@pheem49/mint 1.15.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.
- package/Cargo.lock +1012 -69
- package/Cargo.toml +5 -2
- package/LICENSE +21 -654
- package/README.md +92 -817
- package/Release_Note.md +53 -560
- package/apps/mint-companion/src-tauri/Cargo.toml +20 -0
- package/apps/mint-companion/src-tauri/build.rs +3 -0
- package/apps/mint-companion/src-tauri/src/main.rs +468 -0
- package/apps/mint-companion/tsconfig.json +20 -0
- package/apps/mint-companion/vite.config.ts +3 -0
- package/components.json +19 -0
- package/crates/mint-cli/Cargo.toml +6 -0
- package/crates/mint-cli/src/agent/live_status.rs +187 -39
- package/crates/mint-cli/src/agent.rs +323 -95
- package/crates/mint-cli/src/commands/agent.rs +69 -55
- package/crates/mint-cli/src/commands/integrations.rs +7 -0
- package/crates/mint-cli/src/commands/knowledge.rs +12 -32
- package/crates/mint-cli/src/commands/mod.rs +44 -0
- package/crates/mint-cli/src/image.rs +31 -33
- package/crates/mint-cli/src/interactive/chat_tui.rs +3594 -483
- package/crates/mint-cli/src/interactive/input_box.rs +56 -6
- package/crates/mint-cli/src/interactive/slash_commands.rs +208 -56
- package/crates/mint-cli/src/interactive/tool_activity.rs +328 -0
- package/crates/mint-cli/src/interactive.rs +64 -40
- package/crates/mint-cli/src/main.rs +13 -7
- package/crates/mint-cli/src/mcp.rs +5 -0
- package/crates/mint-cli/src/mod_host.rs +6 -0
- package/crates/mint-cli/src/mods_cli.rs +62 -0
- package/crates/mint-cli/src/mods_ui.rs +187 -0
- package/crates/mint-cli/src/onboard.rs +5 -31
- package/crates/mint-cli/src/terminal_theme.rs +3 -1
- package/crates/mint-cli/src/updater.rs +56 -2
- package/crates/mint-cli/tests/library_intake.rs +77 -0
- package/crates/mint-cli/tests/mods_testing.rs +133 -0
- package/crates/mint-companion-protocol/Cargo.toml +10 -0
- package/crates/mint-companion-protocol/src/lib.rs +163 -0
- package/crates/mint-core/Cargo.toml +6 -3
- package/crates/mint-core/build.rs +26 -0
- package/crates/mint-core/src/agent/chat.rs +167 -39
- package/crates/mint-core/src/agent/code_tools.rs +15 -0
- package/crates/mint-core/src/agent/memory/sources.rs +240 -0
- package/crates/mint-core/src/agent/memory.rs +147 -13
- package/crates/mint-core/src/agent/skill_inventory.rs +325 -0
- package/crates/mint-core/src/agent/skills.rs +437 -90
- package/crates/mint-core/src/agent.rs +1 -0
- package/crates/mint-core/src/api_server/routes/background_jobs.rs +310 -0
- package/crates/mint-core/src/api_server/routes/chat.rs +216 -199
- package/crates/mint-core/src/api_server/routes/html_preview.rs +77 -0
- package/crates/mint-core/src/api_server/routes/library.rs +97 -0
- package/crates/mint-core/src/api_server/routes/media_gen.rs +10 -57
- package/crates/mint-core/src/api_server/routes/misc.rs +104 -107
- package/crates/mint-core/src/api_server/routes/mod.rs +6 -0
- package/crates/mint-core/src/api_server/routes/mods.rs +31 -0
- package/crates/mint-core/src/api_server/routes/sessions.rs +41 -0
- package/crates/mint-core/src/api_server/routes/skills_subagents.rs +12 -42
- package/crates/mint-core/src/api_server/routes/slash.rs +50 -1
- package/crates/mint-core/src/api_server/routes/sources.rs +101 -0
- package/crates/mint-core/src/api_server.rs +118 -9
- package/crates/mint-core/src/avatar_bridge.rs +11 -2
- package/crates/mint-core/src/browser/session.rs +52 -0
- package/crates/mint-core/src/companion.rs +716 -0
- package/crates/mint-core/src/cron/scheduler.rs +34 -2
- package/crates/mint-core/src/integrations/channels.rs +87 -345
- package/crates/mint-core/src/integrations/mcp.rs +397 -50
- package/crates/mint-core/src/integrations/mcp_calls.rs +823 -0
- package/crates/mint-core/src/integrations/mcp_catalog.rs +267 -0
- package/crates/mint-core/src/integrations/mcp_device.rs +319 -0
- package/crates/mint-core/src/integrations/mcp_result.rs +356 -0
- package/crates/mint-core/src/integrations/webhooks.rs +539 -0
- package/crates/mint-core/src/integrations.rs +2 -0
- package/crates/mint-core/src/lib.rs +85 -26
- package/crates/mint-core/src/media/gemini_live.rs +134 -14
- package/crates/mint-core/src/media/image_models.rs +3 -27
- package/crates/mint-core/src/media/image_workflow.rs +216 -0
- package/crates/mint-core/src/media/library/intake.rs +144 -0
- package/crates/mint-core/src/media/library/migration.rs +56 -0
- package/crates/mint-core/src/media/library/removal.rs +55 -0
- package/crates/mint-core/src/media/library/tests.rs +730 -0
- package/crates/mint-core/src/media/library/thumbnails.rs +72 -0
- package/crates/mint-core/src/media/library.rs +545 -0
- package/crates/mint-core/src/media/model_catalog.rs +375 -0
- package/crates/mint-core/src/media/pictures.rs +79 -222
- package/crates/mint-core/src/media/video_models.rs +3 -22
- package/crates/mint-core/src/media.rs +3 -0
- package/crates/mint-core/src/mods/manager.rs +1124 -0
- package/crates/mint-core/src/mods/mod.rs +569 -0
- package/crates/mint-core/src/mods/operations.rs +204 -0
- package/crates/mint-core/src/mods/session.rs +146 -0
- package/crates/mint-core/src/mods/slash.rs +59 -0
- package/crates/mint-core/src/mods/telemetry.rs +306 -0
- package/crates/mint-core/src/orchestration/approvals.rs +344 -0
- package/crates/mint-core/src/orchestration/context_render.rs +104 -0
- package/crates/mint-core/src/orchestration/mcp_review.rs +153 -0
- package/crates/mint-core/src/orchestration/memory_skill.rs +104 -32
- package/crates/mint-core/src/orchestration/mod.rs +2139 -244
- package/crates/mint-core/src/orchestration/mod_authoring_regressions.rs +209 -0
- package/crates/mint-core/src/orchestration/progress_clock.rs +80 -0
- package/crates/mint-core/src/orchestration/repeated_failures.rs +116 -0
- package/crates/mint-core/src/orchestration/review_regressions.rs +449 -0
- package/crates/mint-core/src/orchestration/run_control.rs +196 -0
- package/crates/mint-core/src/orchestration/tools/files.rs +161 -14
- package/crates/mint-core/src/orchestration/tools/media.rs +37 -41
- package/crates/mint-core/src/orchestration/tools/misc.rs +3 -2
- package/crates/mint-core/src/orchestration/tools/mod.rs +2 -0
- package/crates/mint-core/src/orchestration/tools/mods.rs +161 -0
- package/crates/mint-core/src/orchestration/tools/plugins_mcp.rs +303 -71
- package/crates/mint-core/src/orchestration/tools/shell.rs +95 -2
- package/crates/mint-core/src/orchestration/workspace_helpers.rs +4 -1
- package/crates/mint-core/src/prompts/agent.rs +17 -2
- package/crates/mint-core/src/prompts/tool_catalog.rs +28 -2
- package/crates/mint-core/src/search/knowledge.rs +7 -1
- package/crates/mint-core/src/search/linked_folders/index.rs +177 -13
- package/crates/mint-core/src/search/linked_folders.rs +83 -2
- package/crates/mint-core/src/slash/catalog.rs +1 -1
- package/crates/mint-core/src/slash/mod.rs +174 -11
- package/crates/mint-core/src/slash/mods.rs +155 -0
- package/crates/mint-core/src/system/bg_shell.rs +1129 -95
- package/crates/mint-core/src/system/config.rs +5 -10
- package/crates/mint-core/src/system/html_preview.rs +398 -0
- package/crates/mint-core/src/system/mint_web.rs +164 -0
- package/crates/mint-core/src/system/release_notes.rs +18 -0
- package/crates/mint-core/src/system/shell.rs +21 -6
- package/crates/mint-core/src/system/workspace.rs +72 -8
- package/crates/mint-core/src/system/workspace_history.rs +604 -0
- package/crates/mint-core/src/system.rs +4 -0
- package/crates/mint-core/tests/background_retention.rs +34 -0
- package/crates/mint-core/tests/background_shutdown.rs +24 -0
- package/crates/mint-core/tests/library_http.rs +209 -0
- package/crates/mint-core/tests/mcp_contract.rs +283 -0
- package/crates/mint-core/tests/mcp_stdio.rs +11 -10
- package/crates/mint-core/tests/media_model_catalog_http.rs +403 -0
- package/crates/mint-core/tests/mods_http.rs +215 -0
- package/crates/mint-core/tests/terminal_shutdown.rs +109 -0
- package/crates/mint-mod-runtime/Cargo.toml +18 -0
- package/crates/mint-mod-runtime/src/lib.rs +507 -0
- package/crates/mint-mod-runtime/src/sdk.js +56 -0
- package/crates/mint-mod-runtime/src/test_sdk.js +32 -0
- package/crates/mint-mod-runtime/src/testing.rs +94 -0
- package/crates/mint-mod-runtime/tests/runtime.rs +156 -0
- package/docs/mods/AUTHORING.md +61 -0
- package/docs/mods/README.md +139 -0
- package/docs/mods/TESTING.md +56 -0
- package/docs/mods/mint-mods.d.ts +66 -0
- package/examples/device-mcp/README.md +22 -0
- package/examples/device-mcp/contract.md +49 -0
- package/examples/device-mcp/mock-device.mjs +66 -0
- package/examples/device-mcp/server.mjs +48 -0
- package/examples/device-mcp/server.test.mjs +78 -0
- package/examples/mods/task-checklist/README.md +1 -0
- package/examples/mods/task-checklist/main.ts +17 -0
- package/examples/mods/task-checklist/manifest.json +1 -0
- package/examples/mods/task-checklist/tests/checklist.test.ts +15 -0
- package/examples/mods/token-meter/README.md +1 -0
- package/examples/mods/token-meter/main.ts +8 -0
- package/examples/mods/token-meter/manifest.json +1 -0
- package/examples/mods/tool-guard/README.md +1 -0
- package/examples/mods/tool-guard/main.ts +9 -0
- package/examples/mods/tool-guard/manifest.json +1 -0
- package/out/web/assets/CliSessionsView-CtzUJpK_.css +1 -0
- package/out/web/assets/CliSessionsView-D7hw0Mgj.js +1 -0
- package/out/web/assets/CodeReviewPage-C7bEOJfd.js +4 -0
- package/out/web/assets/CodeReviewPage-Dte9uLiL.css +1 -0
- package/out/web/assets/CommandPalette-0RrF-AOT.js +1 -0
- package/out/web/assets/ImageStudioPanel-CdxkAf_r.js +1 -0
- package/out/web/assets/LibraryPage--Qfisp6D.js +2 -0
- package/out/web/assets/LibraryPage-DAxUTAg7.css +1 -0
- package/out/web/assets/LinkedFoldersView-2nfZbDgN.js +1 -0
- package/out/web/assets/McpRegistryPicker-h9uS8_wT.js +1 -0
- package/out/web/assets/McpServersView-slfQJzNB.js +1 -0
- package/out/web/assets/MintDashboard-BSR5XpXh.js +25 -0
- package/out/web/assets/MintDashboard-CWUcCGhd.css +1 -0
- package/out/web/assets/PluginsView-l7LSm503.js +1 -0
- package/out/web/assets/ScheduledTasksView-CttmLbq1.js +1 -0
- package/out/web/assets/SettingsWindow-BCMLYvEV.js +4 -0
- package/out/web/assets/SettingsWindow-DcMVmU9u.css +1 -0
- package/out/web/assets/SkillsView-rNs1qr_U.js +2 -0
- package/out/web/assets/ToolSurfacePage-B9DzEPWN.js +37 -0
- package/out/web/assets/ToolSurfacePage-CTfgggF2.css +1 -0
- package/out/web/assets/VeoStudioPanel-DiJs_Cg2.js +4 -0
- package/out/web/assets/WorkspaceSelector-B2xIp04D.js +1 -0
- package/out/web/assets/WorkspaceSelector-Ca8uJOsS.css +1 -0
- package/out/web/assets/{abnfDiagram-VCTEODGH-ByMyfanq.js → abnfDiagram-VCTEODGH-CD0CjoGl.js} +1 -1
- package/out/web/assets/{architecture-7GRP2DOG-CLq2kEOB.js → architecture-7GRP2DOG-RKV83OeZ.js} +1 -1
- package/out/web/assets/{architectureDiagram-5GKGNRK7-6PoVSmNG.js → architectureDiagram-5GKGNRK7-nN_HAFct.js} +1 -1
- package/out/web/assets/{blockDiagram-NRAW4CY4-cKw9mAKi.js → blockDiagram-NRAW4CY4-BrrqzHW5.js} +1 -1
- package/out/web/assets/{c4Diagram-UCG6FXSJ-Cqy8N5Ge.js → c4Diagram-UCG6FXSJ-DTdV8_zE.js} +1 -1
- package/out/web/assets/{chunk-75Z2AOVW-3qybijF5.js → chunk-75Z2AOVW-_M0z5olV.js} +1 -1
- package/out/web/assets/chunk-CKBBP62Z-BpQEgeHF.js +2 -0
- package/out/web/assets/{chunk-CLGD4ZFX-CndDQkww.js → chunk-CLGD4ZFX-D54yGvGS.js} +1 -1
- package/out/web/assets/{chunk-DSNCTWBM-Cc4NxvgD.js → chunk-DSNCTWBM-C-gvT2ru.js} +1 -1
- package/out/web/assets/{chunk-DU6HZSFF-drzh6GsU.js → chunk-DU6HZSFF-CnFngIhW.js} +1 -1
- package/out/web/assets/{chunk-F27PBJKO-FiY35l4v.js → chunk-F27PBJKO-BqtWrnEO.js} +1 -1
- package/out/web/assets/{chunk-G27WJ6UU-Bjt9Il4A.js → chunk-G27WJ6UU-Ckoq5mi2.js} +1 -1
- package/out/web/assets/{chunk-GMAD6QVW-Bppi3Ctd.js → chunk-GMAD6QVW-BuhEcgHq.js} +1 -1
- package/out/web/assets/{chunk-JQ64N6SF-DGg5kcEV.js → chunk-JQ64N6SF-CF8T7GiZ.js} +1 -1
- package/out/web/assets/{chunk-LCL6LL3I-CCYcjd7p.js → chunk-LCL6LL3I-BhXV9zyH.js} +1 -1
- package/out/web/assets/{chunk-OBVCFTLP-DJeNil5M.js → chunk-OBVCFTLP-CRIkunUB.js} +1 -1
- package/out/web/assets/{chunk-P2QGCYS3-lRzkRjBB.js → chunk-P2QGCYS3-UFEU6BxZ.js} +1 -1
- package/out/web/assets/{chunk-POPQ4Y6H-DouASMBW.js → chunk-POPQ4Y6H-CQcrNbjr.js} +1 -1
- package/out/web/assets/{chunk-PWAF6VOD-DMmD1vVf.js → chunk-PWAF6VOD-Cc2uVDsG.js} +1 -1
- package/out/web/assets/{chunk-R7TYR2AO-CYLA5UC5.js → chunk-R7TYR2AO-DiCxzdAY.js} +1 -1
- package/out/web/assets/{chunk-RHFEMEQ7-BiXwEcUT.js → chunk-RHFEMEQ7-b7hnPmrR.js} +1 -1
- package/out/web/assets/{chunk-SVP7TREG-BV-zPslF.js → chunk-SVP7TREG-Bu_yYb-V.js} +1 -1
- package/out/web/assets/{chunk-TEH6E4GO-B2RGEohZ.js → chunk-TEH6E4GO-G4Cdlyrt.js} +1 -1
- package/out/web/assets/classDiagram-DTDB5LWJ-RnC2mYO4.js +1 -0
- package/out/web/assets/classDiagram-v2-JRS7N3AN-RnC2mYO4.js +1 -0
- package/out/web/assets/config-CnvCChUM.js +2 -0
- package/out/web/assets/config-DM6oIVO3.css +1 -0
- package/out/web/assets/core-DsKkQ7ig.js +1 -0
- package/out/web/assets/{cynefin-OW5HDTMX-Cas4csHM.js → cynefin-OW5HDTMX-CnzQfOGp.js} +1 -1
- package/out/web/assets/{cynefinDiagram-5FMLGOSQ-Bbpj66Y3.js → cynefinDiagram-5FMLGOSQ-DVtQnfpm.js} +1 -1
- package/out/web/assets/{dagre-3AP2YEHR-B0-2RDEd.js → dagre-3AP2YEHR-DG76_k5_.js} +1 -1
- package/out/web/assets/{diagram-S7CK7UJ4-BdzEjUyH.js → diagram-S7CK7UJ4-CaouUoze.js} +1 -1
- package/out/web/assets/{diagram-UQ7AKVKN-CJDwyPxF.js → diagram-UQ7AKVKN-B4bEc26B.js} +1 -1
- package/out/web/assets/{diagram-VSXAHHWV-BxaG1r_B.js → diagram-VSXAHHWV-BJ86Ov2Y.js} +1 -1
- package/out/web/assets/{diagram-VX7I27RA-BMmUqm8f.js → diagram-VX7I27RA-7hH5IPYf.js} +1 -1
- package/out/web/assets/{diagram-Z3DM3KII-CFtd03eE.js → diagram-Z3DM3KII-CCM4clo8.js} +1 -1
- package/out/web/assets/dist-js-BkYNznHX.js +1 -0
- package/out/web/assets/{ebnfDiagram-PWID7BFC-DwAen1hO.js → ebnfDiagram-PWID7BFC-Da7WAM3k.js} +1 -1
- package/out/web/assets/{erDiagram-SSCWMZ5O-BCZSHACj.js → erDiagram-SSCWMZ5O-CzzmnZWT.js} +1 -1
- package/out/web/assets/{event-ChBVpRy0.js → event-Cdt7Byyj.js} +1 -1
- package/out/web/assets/{eventmodeling-NTZA5JFV-XHStE4E_.js → eventmodeling-NTZA5JFV-xcfsA0XD.js} +1 -1
- package/out/web/assets/flowDiagram-A5DVABFB-JdtH1673.js +1 -0
- package/out/web/assets/{ganttDiagram-EL5Y4UJY-BAEu1Cql.js → ganttDiagram-EL5Y4UJY-B3ClFGmL.js} +1 -1
- package/out/web/assets/{gitGraph-4MIJSDKK-r32Vyper.js → gitGraph-4MIJSDKK-BZ_y_cDU.js} +1 -1
- package/out/web/assets/{gitGraphDiagram-WWUBYQGX-z9o9qt6V.js → gitGraphDiagram-WWUBYQGX-drhRSYm3.js} +1 -1
- package/out/web/assets/index-BZ9xgmMP.css +1 -0
- package/out/web/assets/index-Ca8cCS4h.js +168 -0
- package/out/web/assets/{info-A6RAGUB7-BvLaC2cw.js → info-A6RAGUB7-B7I3K0F1.js} +1 -1
- package/out/web/assets/{infoDiagram-RXCK75RN-Be6WAXXw.js → infoDiagram-RXCK75RN-deX_1MPh.js} +1 -1
- package/out/web/assets/{ishikawaDiagram-5VMMS53U-DZjoHJHC.js → ishikawaDiagram-5VMMS53U-s38yo28y.js} +1 -1
- package/out/web/assets/{journeyDiagram-EYS64GPL-CtVkjkGm.js → journeyDiagram-EYS64GPL-CZn2kB8b.js} +1 -1
- package/out/web/assets/jsx-runtime-DN4NhQUK.js +1 -0
- package/out/web/assets/{kanban-definition-3QL26DDD-CW5FMv1w.js → kanban-definition-3QL26DDD-DodjgySL.js} +1 -1
- package/out/web/assets/markdown-CMhYIcWB.js +39 -0
- package/out/web/assets/{mermaid-parser.core-aUgCqsZ5.js → mermaid-parser.core-Whu2rlJC.js} +3 -3
- package/out/web/assets/{mermaid.core-D57sEiSL.js → mermaid.core-CKyPo8VH.js} +4 -4
- package/out/web/assets/{mindmap-definition-FBJOCRG2-DAmSpM3a.js → mindmap-definition-FBJOCRG2-260WBDxZ.js} +1 -1
- package/out/web/assets/modelManager-CUaGWMWu.js +2 -0
- package/out/web/assets/{oauthManager-9jxicDWJ.js → oauthManager-DlNhXJr6.js} +1 -1
- package/out/web/assets/{packet-AYTQ26CC-DJ27iIoJ.js → packet-AYTQ26CC-CEeIRKRG.js} +1 -1
- package/out/web/assets/{pegDiagram-XKGWAZYB-Bj3ii-gS.js → pegDiagram-XKGWAZYB-BYadI-KO.js} +1 -1
- package/out/web/assets/{pie-WAS4IAKB-DG5mCUbc.js → pie-WAS4IAKB-D8uf4dh5.js} +1 -1
- package/out/web/assets/{pieDiagram-E7YTZNPT-BrJLPnDA.js → pieDiagram-E7YTZNPT-CjSDy4hO.js} +1 -1
- package/out/web/assets/{quadrantDiagram-AXDQQJYC-3x_TmF3j.js → quadrantDiagram-AXDQQJYC-B8D3gU5d.js} +1 -1
- package/out/web/assets/{radar-RG4KPBEZ-CrXaTTuV.js → radar-RG4KPBEZ-DZJxnokY.js} +1 -1
- package/out/web/assets/{railroad-74A4TZTK-BpqMH9BE.js → railroad-74A4TZTK-DBysUiUi.js} +1 -1
- package/out/web/assets/{railroad-abnf-HS5TGJTU-DHI0uLlV.js → railroad-abnf-HS5TGJTU-CHWxoJYg.js} +1 -1
- package/out/web/assets/{railroad-ebnf-LZEXJU2U-CQCb316y.js → railroad-ebnf-LZEXJU2U-DDv7X6bY.js} +1 -1
- package/out/web/assets/{railroad-peg-WCYAUIDC-Ph1giKkH.js → railroad-peg-WCYAUIDC-DpfcYYNJ.js} +1 -1
- package/out/web/assets/{railroadDiagram-O6MQD6OU-BRq6wIxx.js → railroadDiagram-O6MQD6OU-CYtyv0t3.js} +1 -1
- package/out/web/assets/{requirementDiagram-EFPCY7ZU-DIJj6G1g.js → requirementDiagram-EFPCY7ZU-D2X-Ewik.js} +1 -1
- package/out/web/assets/{sankeyDiagram-P5KCCOFB-DZajSryq.js → sankeyDiagram-P5KCCOFB-Bn2x6C4Y.js} +1 -1
- package/out/web/assets/{sequenceDiagram-WJ2MYXX4-DxJCWyZS.js → sequenceDiagram-WJ2MYXX4-BvFLKtPd.js} +1 -1
- package/out/web/assets/{stateDiagram-HBIQ2CUA-k6voi8Vf.js → stateDiagram-HBIQ2CUA-Bx0GWeaY.js} +1 -1
- package/out/web/assets/stateDiagram-v2-4QOOHH4V--hol1yg_.js +1 -0
- package/out/web/assets/{swimlanes-XN3QIQJK-igYhTjXB.js → swimlanes-XN3QIQJK-CCma0T7L.js} +1 -1
- package/out/web/assets/swimlanesDiagram-VK2B7HYN-BkohoVsU.js +8 -0
- package/out/web/assets/{timeline-definition-24CTP7MA-Dke2p93p.js → timeline-definition-24CTP7MA-iCRhhJ7C.js} +1 -1
- package/out/web/assets/{treeView-Q6P3EWNA-hNELnXUs.js → treeView-Q6P3EWNA-DZ-y08qp.js} +1 -1
- package/out/web/assets/{treemap-WGGIJYW6-D2B6wDsx.js → treemap-WGGIJYW6-De0CxzMx.js} +1 -1
- package/out/web/assets/{vennDiagram-4TSXK5OY-CDkW7rUP.js → vennDiagram-4TSXK5OY-w_7aZ6xe.js} +1 -1
- package/out/web/assets/{wardley-WFR3VGLG-BECdXETi.js → wardley-WFR3VGLG-DT0y0xJ4.js} +1 -1
- package/out/web/assets/{wardleyDiagram-VM6X3IG4-B5BycpQ4.js → wardleyDiagram-VM6X3IG4-DAdXELbQ.js} +1 -1
- package/out/web/assets/window-BJCVtyET.js +1 -0
- package/out/web/assets/{xychartDiagram-S5SC5T6Z-CtJS3b--.js → xychartDiagram-S5SC5T6Z-IZIXf3Vo.js} +1 -1
- package/out/web/index-web.html +3 -2
- package/out/web/index.html +3 -2
- package/package.json +71 -22
- package/shared-modules/.github/workflows/ci.yml +120 -0
- package/shared-modules/.github/workflows/update.yaml +28 -0
- package/slash-commands.json +7 -2
- package/src/bin/auto-build.js +66 -0
- package/src/bin/desktop.js +113 -0
- package/src/bin/desktop.test.js +113 -0
- package/src/bin/index.js +18 -25
- package/src/bin/index.test.js +89 -0
- package/src/bin/measure-effects.mjs +53 -0
- package/src/bin/package.test.js +28 -0
- package/src/bin/release-notes.test.js +84 -0
- package/src/bin/styles.test.js +20 -0
- package/src/bin/web.js +5 -0
- package/src/bin/web.test.js +39 -0
- package/src/renderer/index.html +0 -2
- package/src/renderer/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/src/renderer/shared/agentProgress.ts +33 -0
- package/src/renderer/shared/components/AgentActivityDrawer.tsx +3 -2
- package/src/renderer/shared/components/AgentActivityTable.tsx +1 -1
- package/src/renderer/shared/components/ApprovalCard.test.tsx +24 -0
- package/src/renderer/shared/components/ApprovalCard.tsx +10 -3
- package/src/renderer/shared/components/ArtifactPreviewPanel.tsx +88 -37
- package/src/renderer/shared/components/AuthGate.tsx +75 -39
- package/src/renderer/shared/components/BackgroundTerminals.tsx +181 -0
- package/src/renderer/shared/components/ChatCodeBlock.tsx +8 -4
- package/src/renderer/shared/components/ChatMessageItem.test.tsx +68 -0
- package/src/renderer/shared/components/ChatMessageItem.tsx +36 -16
- package/src/renderer/shared/components/ChatPanel.test.tsx +21 -0
- package/src/renderer/shared/components/ChatPanel.tsx +294 -272
- package/src/renderer/shared/components/ChunkErrorBoundary.tsx +4 -102
- package/src/renderer/shared/components/CliSessionsView.test.tsx +125 -0
- package/src/renderer/shared/components/CliSessionsView.tsx +61 -13
- package/src/renderer/shared/components/CodeReviewPage.tsx +111 -8
- package/src/renderer/shared/components/CollapsiblePasteInput.tsx +90 -0
- package/src/renderer/shared/components/ContextCompactionStatus.tsx +17 -0
- package/src/renderer/shared/components/DashboardSidebar.tsx +15 -128
- package/src/renderer/shared/components/DesktopTitlebar.tsx +45 -19
- package/src/renderer/shared/components/ErrorNotice.tsx +29 -0
- package/src/renderer/shared/components/FormattedMessage.tsx +11 -0
- package/src/renderer/shared/components/GeminiLiveOverlay.css +86 -0
- package/src/renderer/shared/components/GeminiLiveOverlay.test.tsx +24 -0
- package/src/renderer/shared/components/GeminiLiveOverlay.tsx +85 -101
- package/src/renderer/shared/components/GlideSelect.css +138 -0
- package/src/renderer/shared/components/GlideSelect.d.ts +31 -0
- package/src/renderer/shared/components/GlideSelect.jsx +266 -0
- package/src/renderer/shared/components/ImageSearchCard.tsx +46 -56
- package/src/renderer/shared/components/ImageStudioPanel.tsx +9 -8
- package/src/renderer/shared/components/LibraryItemCard.tsx +72 -0
- package/src/renderer/shared/components/LibraryPage.tsx +165 -0
- package/src/renderer/shared/components/LibraryPreview.tsx +59 -0
- package/src/renderer/shared/components/McpServersView.tsx +27 -8
- package/src/renderer/shared/components/McpToolAllowlist.tsx +2 -4
- package/src/renderer/shared/components/McpToolResults.tsx +99 -0
- package/src/renderer/shared/components/MintDashboard.tsx +308 -770
- package/src/renderer/shared/components/ModSessionControls.tsx +8 -0
- package/src/renderer/shared/components/ModUi.test.tsx +65 -0
- package/src/renderer/shared/components/ModUi.tsx +26 -0
- package/src/renderer/shared/components/ModelSelectorPopover.tsx +14 -7
- package/src/renderer/shared/components/ModsPanel.tsx +156 -0
- package/src/renderer/shared/components/ProgressActivity.tsx +17 -0
- package/src/renderer/shared/components/ReviewHistory.tsx +42 -0
- package/src/renderer/shared/components/RewindModal.tsx +5 -12
- package/src/renderer/shared/components/RunSummaryDashboard.tsx +6 -12
- package/src/renderer/shared/components/ScheduledTasksView.tsx +7 -6
- package/src/renderer/shared/components/SelectField.test.tsx +27 -0
- package/src/renderer/shared/components/SelectField.tsx +38 -0
- package/src/renderer/shared/components/SessionSources.tsx +48 -0
- package/src/renderer/shared/components/SessionSourcesPopover.tsx +61 -0
- package/src/renderer/shared/components/Settings/AgentsTab.tsx +7 -6
- package/src/renderer/shared/components/Settings/AudioTab.tsx +8 -7
- package/src/renderer/shared/components/Settings/AutomationTab.tsx +2 -2
- package/src/renderer/shared/components/Settings/GeneralTab.tsx +33 -32
- package/src/renderer/shared/components/Settings/MemoryTab.test.tsx +37 -0
- package/src/renderer/shared/components/Settings/MemoryTab.tsx +71 -31
- package/src/renderer/shared/components/Settings/NotificationsTab.test.tsx +15 -0
- package/src/renderer/shared/components/Settings/NotificationsTab.tsx +84 -0
- package/src/renderer/shared/components/Settings/ThemeTab.tsx +19 -11
- package/src/renderer/shared/components/SlashChoicePanel.tsx +88 -0
- package/src/renderer/shared/components/SlashSuggestions.test.tsx +33 -0
- package/src/renderer/shared/components/SlashSuggestions.tsx +38 -10
- package/src/renderer/shared/components/SourcesBlock.tsx +12 -20
- package/src/renderer/shared/components/ToolSurfacePage.test.tsx +32 -0
- package/src/renderer/shared/components/ToolSurfacePage.tsx +59 -11
- package/src/renderer/shared/components/ToolsPanelToggle.tsx +13 -0
- package/src/renderer/shared/components/VeoStudioPanel.tsx +9 -8
- package/src/renderer/shared/components/ViewedImages.tsx +36 -0
- package/src/renderer/shared/components/VirtualChatHistory.tsx +82 -0
- package/src/renderer/shared/components/VoicePill.css +85 -0
- package/src/renderer/shared/components/VoicePill.d.ts +12 -0
- package/src/renderer/shared/components/VoicePill.jsx +190 -0
- package/src/renderer/shared/components/VoicePill.test.tsx +22 -0
- package/src/renderer/shared/components/WorkspaceContextPopover.tsx +20 -15
- package/src/renderer/shared/components/WorkspacePaneResizer.tsx +92 -0
- package/src/renderer/shared/components/WorkspacePreviewActions.test.tsx +44 -0
- package/src/renderer/shared/components/WorkspacePreviewActions.tsx +19 -0
- package/src/renderer/shared/components/WorkspaceSelector.tsx +13 -8
- package/src/renderer/shared/components/background-terminals.css +292 -0
- package/src/renderer/shared/components/ui/README.md +52 -0
- package/src/renderer/shared/components/ui/button.tsx +37 -0
- package/src/renderer/shared/components/ui/demo.tsx +26 -0
- package/src/renderer/shared/components/ui/error-recovery.css +35 -0
- package/src/renderer/shared/components/ui/error-recovery.tsx +35 -0
- package/src/renderer/shared/components/ui/voice-powered-orb.css +6 -0
- package/src/renderer/shared/components/ui/voice-powered-orb.tsx +374 -0
- package/src/renderer/shared/components/workspace-selector.css +248 -0
- package/src/renderer/shared/constants/config.ts +30 -1
- package/src/renderer/shared/constants/slashCommands.ts +1 -1
- package/src/renderer/shared/conversation/lifecycle.test.ts +362 -0
- package/src/renderer/shared/conversation/lifecycle.ts +418 -0
- package/src/renderer/shared/conversation/projectNavigation.test.ts +105 -0
- package/src/renderer/shared/conversation/projectNavigation.ts +39 -0
- package/src/renderer/shared/conversation/sourcesTransport.ts +27 -0
- package/src/renderer/shared/conversation/syncView.test.ts +1 -34
- package/src/renderer/shared/conversation/syncView.ts +0 -37
- package/src/renderer/shared/conversation/transport.test.ts +163 -0
- package/src/renderer/shared/conversation/transport.ts +159 -0
- package/src/renderer/shared/conversation/useConversationCoordinator.ts +30 -72
- package/src/renderer/shared/css/agent-observability.css +20 -21
- package/src/renderer/shared/css/auth-gate.css +112 -13
- package/src/renderer/shared/css/code-review.css +33 -0
- package/src/renderer/shared/css/code-sessions.css +27 -0
- package/src/renderer/shared/css/collapsed-paste.css +35 -0
- package/src/renderer/shared/css/effects.css +15 -0
- package/src/renderer/shared/css/error-notice.css +60 -0
- package/src/renderer/shared/css/library.css +476 -0
- package/src/renderer/shared/css/management-views.css +138 -0
- package/src/renderer/shared/css/mcp-results.css +11 -0
- package/src/renderer/shared/css/model-selector.css +163 -105
- package/src/renderer/shared/css/mods.css +77 -0
- package/src/renderer/shared/css/notification-notice.css +28 -0
- package/src/renderer/shared/css/session-sources-popover.css +9 -0
- package/src/renderer/shared/css/session-sources.css +23 -0
- package/src/renderer/shared/css/settings/memory.css +154 -0
- package/src/renderer/shared/css/settings/notifications.css +11 -0
- package/src/renderer/shared/css/settings/theme.css +5 -0
- package/src/renderer/shared/css/slash-choice.css +95 -0
- package/src/renderer/shared/css/slash-suggestions.css +68 -0
- package/src/renderer/shared/css/sources-block.css +91 -0
- package/src/renderer/shared/css/thinking-status.css +46 -0
- package/src/renderer/shared/css/tools-panel.css +7 -0
- package/src/renderer/shared/css/ui-tailwind.css +19 -0
- package/src/renderer/shared/css/viewed-images.css +15 -0
- package/src/renderer/shared/fonts.css +46 -0
- package/src/renderer/shared/fonts.ts +3 -12
- package/src/renderer/shared/lib/utils.ts +6 -0
- package/src/renderer/shared/library/controller.test.ts +61 -0
- package/src/renderer/shared/library/controller.ts +57 -0
- package/src/renderer/shared/library/transport.test.ts +26 -0
- package/src/renderer/shared/library/transport.ts +60 -0
- package/src/renderer/shared/library/useLibraryFile.ts +38 -0
- package/src/renderer/shared/mods.ts +58 -0
- package/src/renderer/shared/platform.test.ts +57 -1
- package/src/renderer/shared/platform.ts +46 -1
- package/src/renderer/shared/theme/themeManager.test.ts +43 -0
- package/src/renderer/shared/theme/themeManager.ts +17 -4
- package/src/renderer/shared/types.ts +56 -2
- package/src/renderer/shared/utils/agentActivity.test.ts +75 -0
- package/src/renderer/shared/utils/agentActivity.ts +42 -3
- package/src/renderer/shared/utils/approval.tsx +7 -0
- package/src/renderer/shared/utils/artifactBrowser.test.ts +80 -0
- package/src/renderer/shared/utils/artifactBrowser.ts +41 -0
- package/src/renderer/shared/utils/contextCompaction.test.tsx +48 -0
- package/src/renderer/shared/utils/contextCompaction.ts +21 -0
- package/src/renderer/shared/utils/effectVisibility.ts +9 -0
- package/src/renderer/shared/utils/markdown.tsx +3 -3
- package/src/renderer/shared/utils/mcpResults.test.tsx +91 -0
- package/src/renderer/shared/utils/modViewport.ts +7 -0
- package/src/renderer/shared/utils/nativeRecordingSession.test.ts +61 -0
- package/src/renderer/shared/utils/nativeRecordingSession.ts +46 -0
- package/src/renderer/shared/utils/notificationSettings.test.ts +14 -0
- package/src/renderer/shared/utils/notificationSettings.ts +23 -0
- package/src/renderer/shared/utils/notifications.test.ts +51 -0
- package/src/renderer/shared/utils/notifications.ts +54 -0
- package/src/renderer/shared/utils/pastedText.test.ts +20 -0
- package/src/renderer/shared/utils/pastedText.ts +13 -0
- package/src/renderer/shared/utils/progressCache.test.ts +32 -0
- package/src/renderer/shared/utils/sessionSources.test.ts +31 -0
- package/src/renderer/shared/utils/sessionSources.ts +34 -0
- package/src/renderer/shared/utils/slashCommandProcessor.ts +1 -1
- package/src/renderer/shared/utils/sourceAttachments.ts +28 -0
- package/src/renderer/shared/utils/speech.ts +51 -9
- package/src/renderer/shared/utils/syntaxHighlight.test.tsx +22 -0
- package/src/renderer/shared/utils/syntaxHighlight.tsx +26 -0
- package/src/renderer/shared/utils/useGeminiLiveVoice.ts +49 -5
- package/src/renderer/shared/utils/useLatestCallback.ts +9 -0
- package/src/renderer/shared/utils/useNativeVoiceInput.ts +80 -72
- package/src/renderer/shared/utils/useSessionSources.ts +33 -0
- package/src/renderer/shared/utils/viewedImages.ts +26 -0
- package/src/renderer/shared/utils/workspacePane.test.ts +33 -0
- package/src/renderer/shared/utils/workspacePane.ts +20 -0
- package/src/renderer/shared/utils/workspacePreview.test.ts +24 -0
- package/src/renderer/shared/utils/workspacePreview.ts +16 -0
- package/src/renderer/shared/utils/workspaceSnapshot.test.ts +49 -0
- package/src/renderer/shared/utils/workspaceSnapshot.ts +26 -0
- package/src/renderer/shared/utils/workspaces.ts +10 -0
- package/src/renderer/shared/workspaceUndo.ts +88 -0
- package/src/renderer/src/App.tsx +9 -1
- package/src/renderer/src/components/LiveTranslateControls.tsx +65 -0
- package/src/renderer/src/components/PicturesLibrary.tsx +2 -288
- package/src/renderer/src/components/ScreenPicker.css +67 -30
- package/src/renderer/src/components/ScreenPicker.tsx +201 -60
- package/src/renderer/src/components/SettingsWindow.tsx +10 -5
- package/src/renderer/src/components/WorkspacePanel.tsx +222 -32
- package/src/renderer/src/components/browser-shell.css +1 -1
- package/src/renderer/src/components/browser-suggestions-popup.css +1 -1
- package/src/renderer/src/components/liveTranslateFrames.test.ts +26 -5
- package/src/renderer/src/components/liveTranslateFrames.ts +18 -7
- package/src/renderer/src/components/liveTranslateLanguage.ts +6 -0
- package/src/renderer/src/css/app-shell.css +52 -137
- package/src/renderer/src/css/chat.css +21 -495
- package/src/renderer/src/css/desktop-tools.css +199 -1
- package/src/renderer/src/css/misc.css +0 -52
- package/src/renderer/src/css/pictures.css +2 -415
- package/src/renderer/src/css/settings/base.css +50 -47
- package/src/renderer/src/css/sidebar-workspace.css +151 -94
- package/src/renderer/src/css/tool-surfaces.css +65 -9
- package/src/renderer/src/css/widget.css +1 -1
- package/src/renderer/src/env.d.ts +1 -9
- package/src/renderer/src/index.css +1 -1
- package/src/renderer/src/main.tsx +2 -1
- package/src/renderer/src/proactiveSuggestions.ts +29 -0
- package/src/renderer/src/tauri.ts +122 -330
- package/src/renderer/src/voiceInput.ts +2 -1
- package/src/renderer/src-web/components/PicturesLibrary.tsx +2 -285
- package/src/renderer/src-web/components/PullToRefresh.tsx +1 -1
- package/src/renderer/src-web/components/SettingsWindow.tsx +10 -5
- package/src/renderer/src-web/css/app-shell.css +24 -121
- package/src/renderer/src-web/css/chat.css +21 -495
- package/src/renderer/src-web/css/misc.css +0 -52
- package/src/renderer/src-web/css/mobile.css +15 -14
- package/src/renderer/src-web/css/pictures.css +2 -462
- package/src/renderer/src-web/css/settings/base.css +50 -47
- package/src/renderer/src-web/css/sidebar-workspace.css +9 -95
- package/src/renderer/src-web/css/widget.css +1 -1
- package/src/renderer/src-web/env.d.ts +1 -12
- package/src/renderer/src-web/index.css +1 -1
- package/src/renderer/src-web/main.tsx +2 -1
- package/src/renderer/src-web/proactiveSuggestions.ts +3 -0
- package/src/renderer/src-web/tauri.ts +129 -354
- package/src/renderer/tests/backgroundTerminals.browser.html +81 -0
- package/src/renderer/tests/chatPanelFixture.ts +23 -0
- package/src/renderer/tests/chatPerformance.browser.html +71 -0
- package/src/renderer/tests/cliSessions.browser.html +82 -0
- package/src/renderer/tests/collapsedPaste.browser.html +70 -0
- package/src/renderer/tests/composerMenus.browser.html +116 -0
- package/src/renderer/tests/contextCompaction.browser.html +17 -0
- package/src/renderer/tests/conversationLifecycle.browser.html +70 -0
- package/src/renderer/tests/effectsPerformance.browser.html +13 -0
- package/src/renderer/tests/errorRecovery.browser.html +21 -0
- package/src/renderer/tests/glideSelect.browser.html +71 -0
- package/src/renderer/tests/glideSelect.preview.html +27 -0
- package/src/renderer/tests/library.browser.html +1 -0
- package/src/renderer/tests/library.browser.tsx +78 -0
- package/src/renderer/tests/liveOrb.browser.html +49 -0
- package/src/renderer/tests/liveVoice.browser.html +29 -0
- package/src/renderer/tests/modInput.browser.html +22 -0
- package/src/renderer/tests/sessionSources.browser.html +1 -0
- package/src/renderer/tests/sessionSources.browser.tsx +107 -0
- package/src/renderer/tests/skills.browser.html +24 -0
- package/src/renderer/tests/toolsPanel.browser.html +1 -0
- package/src/renderer/tests/toolsPanel.browser.tsx +113 -0
- package/src/renderer/tests/viewedImages.browser.html +1 -0
- package/src/renderer/tests/viewedImages.browser.tsx +93 -0
- package/src/renderer/tests/voicePill.browser.html +80 -0
- package/src/renderer/tests/voicePill.preview.html +34 -0
- package/src/renderer/tests/webSources.browser.html +1 -0
- package/src/renderer/tests/webSources.browser.tsx +98 -0
- package/src/renderer/tests/workspaceUndo.browser.html +92 -0
- package/src-tauri/Cargo.toml +40 -0
- package/src-tauri/build.rs +3 -0
- package/src-tauri/src/app/lifecycle.rs +277 -0
- package/src-tauri/src/app/mod.rs +4 -0
- package/src-tauri/src/app/shortcuts.rs +40 -0
- package/src-tauri/src/app/tray.rs +52 -0
- package/src-tauri/src/background/events.rs +70 -0
- package/src-tauri/src/background/headless.rs +230 -0
- package/src-tauri/src/background/mod.rs +4 -0
- package/src-tauri/src/background/proactive.rs +262 -0
- package/src-tauri/src/commands/approvals.rs +20 -0
- package/src-tauri/src/commands/auth.rs +127 -0
- package/src-tauri/src/commands/conversation.rs +642 -0
- package/src-tauri/src/commands/integrations.rs +157 -0
- package/src-tauri/src/commands/media.rs +179 -0
- package/src-tauri/src/commands/mod.rs +11 -0
- package/src-tauri/src/commands/mods.rs +6 -0
- package/src-tauri/src/commands/settings.rs +141 -0
- package/src-tauri/src/commands/voice.rs +153 -0
- package/src-tauri/src/commands/workspace.rs +501 -0
- package/src-tauri/src/lib.rs +10 -0
- package/src-tauri/src/main.rs +3 -0
- package/src-tauri/src/native/actions.rs +206 -0
- package/src-tauri/src/native/browser.rs +252 -0
- package/src-tauri/src/native/capture.rs +695 -0
- package/src-tauri/src/native/discord_rpc.rs +99 -0
- package/src-tauri/src/native/integrations.rs +126 -0
- package/src-tauri/src/native/mod.rs +12 -0
- package/src-tauri/src/native/notifications.rs +51 -0
- package/src-tauri/src/native/plugins.rs +14 -0
- package/src-tauri/src/native/system.rs +250 -0
- package/src-tauri/src/native/terminal.rs +201 -0
- package/src-tauri/src/native/updater.rs +148 -0
- package/src-tauri/src/native/windows.rs +143 -0
- package/tsconfig.json +2 -0
- package/vite.config.ts +4 -1
- package/vite.config.web.ts +4 -0
- package/.cargo/config.toml +0 -13
- package/.claude/settings.local.json +0 -182
- package/.codex +0 -0
- package/CLAUDE.md +0 -102
- package/ROADMAP.md +0 -30
- package/mint-presentation-demo.mp4 +0 -0
- package/out/web/Live2DCubismCore.js +0 -9
- package/out/web/assets/CliSessionsView-BxUVWEEL.js +0 -1
- package/out/web/assets/CliSessionsView-CBDTXuQv.css +0 -1
- package/out/web/assets/CodeReviewPage-DkMgfcos.css +0 -1
- package/out/web/assets/CodeReviewPage-jKlmLJwT.js +0 -3
- package/out/web/assets/CommandPalette-DnwPA5hy.js +0 -1
- package/out/web/assets/ImageStudioPanel-BR1XFwhR.js +0 -1
- package/out/web/assets/LinkedFoldersView-bi-CWQ-I.js +0 -1
- package/out/web/assets/McpRegistryPicker-D5tEFnyq.js +0 -1
- package/out/web/assets/McpServersView-BGG1dhFM.js +0 -1
- package/out/web/assets/MintDashboard-BIglDEWX.js +0 -20
- package/out/web/assets/MintDashboard-DSMywSgs.css +0 -1
- package/out/web/assets/ModelPanel-CbJ1PFoZ.js +0 -1
- package/out/web/assets/PicturesLibrary-CQP0fLP1.css +0 -1
- package/out/web/assets/PicturesLibrary-DoYN7T1p.js +0 -1
- package/out/web/assets/PluginsView-CU1HWXGk.js +0 -1
- package/out/web/assets/ScheduledTasksView-Bnl36y2N.js +0 -1
- package/out/web/assets/SettingsWindow-DhWLAn9T.css +0 -1
- package/out/web/assets/SettingsWindow-Dw8L01pw.js +0 -2
- package/out/web/assets/SkillsView-cnl8aY5p.js +0 -2
- package/out/web/assets/ToolSurfacePage-CgsQw6l5.css +0 -1
- package/out/web/assets/ToolSurfacePage-Cz2VJAPZ.js +0 -37
- package/out/web/assets/VeoStudioPanel-D4JnERWR.js +0 -4
- package/out/web/assets/chunk-CKBBP62Z-cIrXq6jN.js +0 -2
- package/out/web/assets/classDiagram-DTDB5LWJ-Dq0smRWk.js +0 -1
- package/out/web/assets/classDiagram-v2-JRS7N3AN-Dq0smRWk.js +0 -1
- package/out/web/assets/config-C-ZFtipU.css +0 -1
- package/out/web/assets/config-eEDngJXO.js +0 -1
- package/out/web/assets/core-hQ2VA9Ti.js +0 -1
- package/out/web/assets/dist-js-Bf2bhoku.js +0 -1
- package/out/web/assets/flowDiagram-A5DVABFB-BLg7nEcd.js +0 -1
- package/out/web/assets/index-Btsf4L8M.css +0 -1
- package/out/web/assets/index-D8D9MeRA.js +0 -11
- package/out/web/assets/lucide-react-Ip3L6Jtn.js +0 -1
- package/out/web/assets/markdown-BSeeUbJH.js +0 -38
- package/out/web/assets/modelManager-tMVmJVAW.js +0 -2
- package/out/web/assets/prompt-latin-400-normal-BQ9zjSN8.woff2 +0 -0
- package/out/web/assets/prompt-latin-400-normal-CxU4ec_r.woff +0 -0
- package/out/web/assets/prompt-latin-500-normal-B1edBDQ9.woff +0 -0
- package/out/web/assets/prompt-latin-500-normal-CxzxEHZc.woff2 +0 -0
- package/out/web/assets/prompt-latin-600-normal-CSDDLorv.woff +0 -0
- package/out/web/assets/prompt-latin-600-normal-hKZWXsc1.woff2 +0 -0
- 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-400-normal-BrkKv8cO.woff2 +0 -0
- package/out/web/assets/prompt-thai-400-normal-DJypnfr3.woff +0 -0
- package/out/web/assets/prompt-thai-500-normal-C18pDUoL.woff2 +0 -0
- package/out/web/assets/prompt-thai-500-normal-D7YNqUJD.woff +0 -0
- package/out/web/assets/prompt-thai-600-normal-CYPViXds.woff +0 -0
- package/out/web/assets/prompt-thai-600-normal-MrdfU7zR.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/stateDiagram-v2-4QOOHH4V-DeEc0lGZ.js +0 -1
- package/out/web/assets/swimlanesDiagram-VK2B7HYN-DpVik9v7.js +0 -8
- package/out/web/assets/window-5qYXKoW7.js +0 -1
- package/skills-lock.json +0 -77
- package/src/renderer/public/Live2DCubismCore.js +0 -9
- package/src/renderer/src/companionWidget.ts +0 -95
- package/src/renderer/src/components/Live2DStage.tsx +0 -412
- package/src/renderer/src/components/ModelPanel.tsx +0 -155
- package/src/renderer/src/css/model.css +0 -628
- package/src/renderer/src-web/companionWidget.ts +0 -37
- package/src/renderer/src-web/components/ModelPanel.tsx +0 -12
- package/src/renderer/src-web/css/model.css +0 -628
|
@@ -23,6 +23,7 @@ export function useSpeechToText({
|
|
|
23
23
|
const [voiceAwaitingResponse, setVoiceAwaitingResponse] = useState(false)
|
|
24
24
|
|
|
25
25
|
const recognitionRef = useRef<any>(null)
|
|
26
|
+
const resolveStartRef = useRef<((started: boolean) => void) | null>(null)
|
|
26
27
|
const silenceTimerRef = useRef<number | null>(null)
|
|
27
28
|
const restartTimerRef = useRef<number | null>(null)
|
|
28
29
|
|
|
@@ -75,8 +76,25 @@ export function useSpeechToText({
|
|
|
75
76
|
clearRestartTimer()
|
|
76
77
|
clearSilenceTimer()
|
|
77
78
|
recognitionRef.current?.stop()
|
|
79
|
+
setIsRecording(false)
|
|
80
|
+
setVoiceMode(false)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const cancelRecognition = () => {
|
|
84
|
+
clearRestartTimer()
|
|
85
|
+
clearSilenceTimer()
|
|
86
|
+
const recognition = recognitionRef.current
|
|
78
87
|
recognitionRef.current = null
|
|
88
|
+
resolveStartRef.current?.(false)
|
|
89
|
+
resolveStartRef.current = null
|
|
90
|
+
if (recognition) {
|
|
91
|
+
recognition.onstart = recognition.onresult = recognition.onend = recognition.onerror = null
|
|
92
|
+
try { recognition.abort() } catch {}
|
|
93
|
+
}
|
|
94
|
+
voiceModeRef.current = false
|
|
95
|
+
setVoiceMode(false)
|
|
79
96
|
setIsRecording(false)
|
|
97
|
+
setVoiceTranscript('')
|
|
80
98
|
}
|
|
81
99
|
|
|
82
100
|
const scheduleVoiceListen = (delayMs = 350) => {
|
|
@@ -88,18 +106,19 @@ export function useSpeechToText({
|
|
|
88
106
|
}, delayMs)
|
|
89
107
|
}
|
|
90
108
|
|
|
91
|
-
const startRecognition = (autoSend: boolean
|
|
92
|
-
if (recognitionRef.current || sendingRef.current || voiceAwaitingResponseRef.current || isSpeakingRef.current) return
|
|
109
|
+
const startRecognition = (autoSend = false): Promise<boolean> => {
|
|
110
|
+
if (recognitionRef.current || sendingRef.current || voiceAwaitingResponseRef.current || isSpeakingRef.current) return Promise.resolve(false)
|
|
93
111
|
|
|
94
112
|
const SpeechRecognitionApi = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition
|
|
95
113
|
if (!SpeechRecognitionApi) {
|
|
96
|
-
|
|
114
|
+
setVoiceTranscript('Speech recognition is unavailable in this browser. Use Chrome or Edge.')
|
|
97
115
|
voiceModeRef.current = false
|
|
98
|
-
setVoiceMode(
|
|
99
|
-
return
|
|
116
|
+
setVoiceMode(true)
|
|
117
|
+
return Promise.resolve(false)
|
|
100
118
|
}
|
|
101
119
|
|
|
102
120
|
let accumulatedTranscript = ''
|
|
121
|
+
const started = new Promise<boolean>(resolve => { resolveStartRef.current = resolve })
|
|
103
122
|
clearSilenceTimer()
|
|
104
123
|
|
|
105
124
|
try {
|
|
@@ -118,9 +137,14 @@ export function useSpeechToText({
|
|
|
118
137
|
}
|
|
119
138
|
|
|
120
139
|
recognition.onstart = () => {
|
|
140
|
+
if (recognitionRef.current !== recognition) return
|
|
121
141
|
setIsRecording(true)
|
|
142
|
+
setVoiceMode(true)
|
|
143
|
+
resolveStartRef.current?.(true)
|
|
144
|
+
resolveStartRef.current = null
|
|
122
145
|
}
|
|
123
146
|
recognition.onresult = (event: any) => {
|
|
147
|
+
if (recognitionRef.current !== recognition) return
|
|
124
148
|
let interimText = ''
|
|
125
149
|
let finalText = ''
|
|
126
150
|
for (let index = event.resultIndex; index < event.results.length; index += 1) {
|
|
@@ -141,12 +165,21 @@ export function useSpeechToText({
|
|
|
141
165
|
}
|
|
142
166
|
}
|
|
143
167
|
recognition.onerror = (event: any) => {
|
|
144
|
-
|
|
168
|
+
if (recognitionRef.current !== recognition) return
|
|
169
|
+
resolveStartRef.current?.(false)
|
|
170
|
+
resolveStartRef.current = null
|
|
171
|
+
setVoiceTranscript(event.error === 'not-allowed' ? 'Microphone permission denied' : `Speech recognition error: ${event.error}`)
|
|
172
|
+
voiceModeRef.current = false
|
|
173
|
+
setVoiceMode(false)
|
|
145
174
|
setIsRecording(false)
|
|
146
175
|
clearSilenceTimer()
|
|
147
176
|
}
|
|
148
177
|
recognition.onend = () => {
|
|
178
|
+
if (recognitionRef.current !== recognition) return
|
|
179
|
+
resolveStartRef.current?.(false)
|
|
180
|
+
resolveStartRef.current = null
|
|
149
181
|
recognitionRef.current = null
|
|
182
|
+
if (!autoSend) { voiceModeRef.current = false; setVoiceMode(false) }
|
|
150
183
|
setIsRecording(false)
|
|
151
184
|
clearSilenceTimer()
|
|
152
185
|
const finalText = accumulatedTranscript.trim()
|
|
@@ -170,17 +203,21 @@ export function useSpeechToText({
|
|
|
170
203
|
}
|
|
171
204
|
recognitionRef.current = recognition
|
|
172
205
|
recognition.start()
|
|
206
|
+
return started
|
|
173
207
|
} catch (error) {
|
|
174
|
-
|
|
208
|
+
resolveStartRef.current?.(false)
|
|
209
|
+
resolveStartRef.current = null
|
|
210
|
+
setVoiceTranscript(error instanceof Error ? error.message : String(error))
|
|
175
211
|
recognitionRef.current = null
|
|
176
212
|
setIsRecording(false)
|
|
177
213
|
clearSilenceTimer()
|
|
214
|
+
return Promise.resolve(false)
|
|
178
215
|
}
|
|
179
216
|
}
|
|
180
217
|
|
|
181
218
|
// Auto-listen trigger
|
|
182
219
|
useEffect(() => {
|
|
183
|
-
if (!voiceMode || sending || voiceAwaitingResponse || isSpeaking || isRecording) return
|
|
220
|
+
if (!voiceMode || sending || voiceAwaitingResponse || isSpeaking || isRecording || recognitionRef.current) return
|
|
184
221
|
scheduleVoiceListen()
|
|
185
222
|
}, [voiceMode, sending, voiceAwaitingResponse, isSpeaking, isRecording])
|
|
186
223
|
|
|
@@ -189,9 +226,13 @@ export function useSpeechToText({
|
|
|
189
226
|
return () => {
|
|
190
227
|
clearRestartTimer()
|
|
191
228
|
clearSilenceTimer()
|
|
229
|
+
resolveStartRef.current?.(false)
|
|
230
|
+
resolveStartRef.current = null
|
|
192
231
|
if (recognitionRef.current) {
|
|
193
232
|
try {
|
|
194
|
-
recognitionRef.current
|
|
233
|
+
const recognition = recognitionRef.current
|
|
234
|
+
recognition.onstart = recognition.onresult = recognition.onend = recognition.onerror = null
|
|
235
|
+
recognition.abort()
|
|
195
236
|
} catch (e) {
|
|
196
237
|
// ignore
|
|
197
238
|
}
|
|
@@ -210,6 +251,7 @@ export function useSpeechToText({
|
|
|
210
251
|
voiceModeRef,
|
|
211
252
|
startRecognition,
|
|
212
253
|
stopRecognition,
|
|
254
|
+
cancelRecognition,
|
|
213
255
|
scheduleVoiceListen,
|
|
214
256
|
clearRestartTimer
|
|
215
257
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { renderToStaticMarkup } from 'react-dom/server'
|
|
3
|
+
import { renderHighlightedCode } from './syntaxHighlight'
|
|
4
|
+
|
|
5
|
+
describe('highlight reuse', () => {
|
|
6
|
+
it('reuses unchanged code across rerenders and message remounts', () => {
|
|
7
|
+
const first = renderHighlightedCode('const answer = 42', 'typescript', true)
|
|
8
|
+
expect(renderHighlightedCode('const answer = 42', 'typescript', true)).toBe(first)
|
|
9
|
+
})
|
|
10
|
+
it('does not reuse stale text, language, or line-number settings', () => {
|
|
11
|
+
const first = renderHighlightedCode('const answer = 42', 'typescript', true)
|
|
12
|
+
expect(renderHighlightedCode('const answer = 43', 'typescript', true)).not.toBe(first)
|
|
13
|
+
expect(renderHighlightedCode('const answer = 42', 'plaintext', true)).not.toBe(first)
|
|
14
|
+
const withoutNumbers = renderHighlightedCode('const answer = 42', 'typescript', false)
|
|
15
|
+
expect(renderToStaticMarkup(withoutNumbers)).not.toContain('chat-code-line-num')
|
|
16
|
+
})
|
|
17
|
+
it('evicts old entries instead of retaining every visited code block', () => {
|
|
18
|
+
const first = renderHighlightedCode('const evicted = 0', 'typescript')
|
|
19
|
+
for (let i = 0; i < 100; i++) renderHighlightedCode(`const other = ${i}`, 'typescript')
|
|
20
|
+
expect(renderHighlightedCode('const evicted = 0', 'typescript')).not.toBe(first)
|
|
21
|
+
})
|
|
22
|
+
})
|
|
@@ -335,6 +335,32 @@ export function renderHighlightedCode(
|
|
|
335
335
|
language: string,
|
|
336
336
|
showLineNumbers = true
|
|
337
337
|
): ReactNode {
|
|
338
|
+
const key = `${language}:${showLineNumbers ? 1 : 0}:${code}`
|
|
339
|
+
const cached = highlightedCodeCache.get(key)
|
|
340
|
+
if (cached) {
|
|
341
|
+
highlightedCodeCache.delete(key)
|
|
342
|
+
highlightedCodeCache.set(key, cached)
|
|
343
|
+
return cached.node
|
|
344
|
+
}
|
|
345
|
+
const node = buildHighlightedCode(code, language, showLineNumbers)
|
|
346
|
+
// Bound retained source and React elements when virtualized rows remount.
|
|
347
|
+
// Large blocks still get per-component memoization without global retention.
|
|
348
|
+
if (code.length <= 50_000) {
|
|
349
|
+
highlightedCodeCache.set(key, { node, chars: code.length })
|
|
350
|
+
cachedCodeChars += code.length
|
|
351
|
+
while (highlightedCodeCache.size > 32 || cachedCodeChars > 100_000) {
|
|
352
|
+
const oldest = highlightedCodeCache.keys().next().value!
|
|
353
|
+
cachedCodeChars -= highlightedCodeCache.get(oldest)!.chars
|
|
354
|
+
highlightedCodeCache.delete(oldest)
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return node
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const highlightedCodeCache = new Map<string, { node: ReactNode; chars: number }>()
|
|
361
|
+
let cachedCodeChars = 0
|
|
362
|
+
|
|
363
|
+
function buildHighlightedCode(code: string, language: string, showLineNumbers: boolean): ReactNode {
|
|
338
364
|
const lines = code.split('\n')
|
|
339
365
|
|
|
340
366
|
return (
|
|
@@ -51,12 +51,17 @@ export function useGeminiLiveVoice({ workspacePath, chatId, startSession, sendAu
|
|
|
51
51
|
const [voiceAwaitingResponse, setVoiceAwaitingResponse] = useState(false)
|
|
52
52
|
const [isPaused, setIsPaused] = useState(false)
|
|
53
53
|
const [isSpeaking, setIsSpeaking] = useState(false)
|
|
54
|
+
const [errorMessage, setErrorMessage] = useState('')
|
|
54
55
|
|
|
55
56
|
const voiceModeRef = useRef(false)
|
|
56
57
|
const voiceAwaitingResponseRef = useRef(false)
|
|
57
58
|
const isPausedRef = useRef(false)
|
|
58
59
|
const sessionIdRef = useRef<string | null>(null)
|
|
59
60
|
const audioContextRef = useRef<AudioContext | null>(null)
|
|
61
|
+
const analyserRef = useRef<AnalyserNode | null>(null)
|
|
62
|
+
const generationRef = useRef(0)
|
|
63
|
+
const playbackSourcesRef = useRef(new Set<AudioBufferSourceNode>())
|
|
64
|
+
const stopRef = useRef<() => void>(() => {})
|
|
60
65
|
const micStreamRef = useRef<MediaStream | null>(null)
|
|
61
66
|
const captureNodesRef = useRef<{ source: MediaStreamAudioSourceNode; processor: ScriptProcessorNode } | null>(null)
|
|
62
67
|
const nextPlaybackTimeRef = useRef(0)
|
|
@@ -76,6 +81,11 @@ export function useGeminiLiveVoice({ workspacePath, chatId, startSession, sendAu
|
|
|
76
81
|
const ensureAudioContext = useCallback((): AudioContext => {
|
|
77
82
|
if (!audioContextRef.current || audioContextRef.current.state === 'closed') {
|
|
78
83
|
audioContextRef.current = new AudioContext()
|
|
84
|
+
analyserRef.current = audioContextRef.current.createAnalyser()
|
|
85
|
+
analyserRef.current.fftSize = 512
|
|
86
|
+
analyserRef.current.smoothingTimeConstant = 0.3
|
|
87
|
+
analyserRef.current.minDecibels = -90
|
|
88
|
+
analyserRef.current.maxDecibels = -10
|
|
79
89
|
}
|
|
80
90
|
return audioContextRef.current
|
|
81
91
|
}, [])
|
|
@@ -94,6 +104,9 @@ export function useGeminiLiveVoice({ workspacePath, chatId, startSession, sendAu
|
|
|
94
104
|
const source = ctx.createBufferSource()
|
|
95
105
|
source.buffer = buffer
|
|
96
106
|
source.connect(ctx.destination)
|
|
107
|
+
if (analyserRef.current) source.connect(analyserRef.current)
|
|
108
|
+
playbackSourcesRef.current.add(source)
|
|
109
|
+
source.onended = () => { playbackSourcesRef.current.delete(source); source.disconnect() }
|
|
97
110
|
|
|
98
111
|
const startAt = Math.max(ctx.currentTime, nextPlaybackTimeRef.current)
|
|
99
112
|
source.start(startAt)
|
|
@@ -132,13 +145,16 @@ export function useGeminiLiveVoice({ workspacePath, chatId, startSession, sendAu
|
|
|
132
145
|
console.error('Gemini Live session error', event.message)
|
|
133
146
|
transcriptRoleRef.current = null
|
|
134
147
|
setAssistantTranscript(event.message)
|
|
148
|
+
setErrorMessage(event.message)
|
|
135
149
|
break
|
|
136
150
|
case 'closed':
|
|
151
|
+
stopRef.current()
|
|
137
152
|
setIsRecording(false)
|
|
138
153
|
setVoiceAwaitingResponse(false)
|
|
139
154
|
setIsSpeaking(false)
|
|
140
155
|
sessionIdRef.current = null
|
|
141
156
|
transcriptRoleRef.current = null
|
|
157
|
+
setErrorMessage('Live connection closed')
|
|
142
158
|
break
|
|
143
159
|
}
|
|
144
160
|
}, [playAudioChunk])
|
|
@@ -152,7 +168,17 @@ export function useGeminiLiveVoice({ workspacePath, chatId, startSession, sendAu
|
|
|
152
168
|
}, [])
|
|
153
169
|
|
|
154
170
|
const stop = useCallback(() => {
|
|
171
|
+
++generationRef.current
|
|
155
172
|
stopCapture()
|
|
173
|
+
for (const source of playbackSourcesRef.current) {
|
|
174
|
+
try { source.stop(); source.disconnect() } catch { /* Already ended. */ }
|
|
175
|
+
}
|
|
176
|
+
playbackSourcesRef.current.clear()
|
|
177
|
+
analyserRef.current?.disconnect()
|
|
178
|
+
analyserRef.current = null
|
|
179
|
+
const context = audioContextRef.current
|
|
180
|
+
audioContextRef.current = null
|
|
181
|
+
if (context && context.state !== 'closed') void context.close().catch(error => console.error('Failed to close Live audio', error))
|
|
156
182
|
const sessionId = sessionIdRef.current
|
|
157
183
|
sessionIdRef.current = null
|
|
158
184
|
setIsRecording(false)
|
|
@@ -167,6 +193,7 @@ export function useGeminiLiveVoice({ workspacePath, chatId, startSession, sendAu
|
|
|
167
193
|
nextPlaybackTimeRef.current = 0
|
|
168
194
|
if (sessionId) stopSession(sessionId).catch((error) => console.error('Failed to stop Gemini Live session', error))
|
|
169
195
|
}, [stopCapture, stopSession])
|
|
196
|
+
stopRef.current = stop
|
|
170
197
|
|
|
171
198
|
const togglePause = useCallback(() => {
|
|
172
199
|
setIsPaused((current) => {
|
|
@@ -177,7 +204,9 @@ export function useGeminiLiveVoice({ workspacePath, chatId, startSession, sendAu
|
|
|
177
204
|
}, [])
|
|
178
205
|
|
|
179
206
|
const start = useCallback(async () => {
|
|
207
|
+
const generation = ++generationRef.current
|
|
180
208
|
try {
|
|
209
|
+
setErrorMessage('')
|
|
181
210
|
transcriptRoleRef.current = null
|
|
182
211
|
setUserTranscript('')
|
|
183
212
|
setAssistantTranscript('')
|
|
@@ -193,15 +222,27 @@ export function useGeminiLiveVoice({ workspacePath, chatId, startSession, sendAu
|
|
|
193
222
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
194
223
|
audio: { channelCount: 1, echoCancellation: true, noiseSuppression: true, autoGainControl: true }
|
|
195
224
|
})
|
|
225
|
+
if (generation !== generationRef.current) {
|
|
226
|
+
stream.getTracks().forEach(track => track.stop())
|
|
227
|
+
return
|
|
228
|
+
}
|
|
196
229
|
console.log('[gemini-live] microphone granted, starting session...')
|
|
197
230
|
micStreamRef.current = stream
|
|
198
231
|
|
|
199
|
-
const sessionId = await startSession(
|
|
232
|
+
const sessionId = await startSession(event => {
|
|
233
|
+
if (generation === generationRef.current) handleEvent(event)
|
|
234
|
+
}, workspacePath, chatId)
|
|
235
|
+
if (generation !== generationRef.current) {
|
|
236
|
+
await stopSession(sessionId)
|
|
237
|
+
return
|
|
238
|
+
}
|
|
200
239
|
console.log('[gemini-live] session started', sessionId)
|
|
201
240
|
sessionIdRef.current = sessionId
|
|
202
241
|
|
|
203
242
|
const ctx = ensureAudioContext()
|
|
204
243
|
const source = ctx.createMediaStreamSource(stream)
|
|
244
|
+
if (ctx.state === 'suspended') await ctx.resume()
|
|
245
|
+
if (generation !== generationRef.current) { source.disconnect(); return }
|
|
205
246
|
const processor = ctx.createScriptProcessor(CAPTURE_BUFFER_SIZE, 1, 1)
|
|
206
247
|
const ratio = ctx.sampleRate / MIC_SAMPLE_RATE
|
|
207
248
|
|
|
@@ -223,20 +264,21 @@ export function useGeminiLiveVoice({ workspacePath, chatId, startSession, sendAu
|
|
|
223
264
|
}
|
|
224
265
|
|
|
225
266
|
source.connect(processor)
|
|
267
|
+
if (analyserRef.current) source.connect(analyserRef.current)
|
|
226
268
|
processor.connect(ctx.destination)
|
|
227
269
|
captureNodesRef.current = { source, processor }
|
|
228
270
|
setIsRecording(true)
|
|
229
271
|
setVoiceAwaitingResponse(true)
|
|
230
272
|
} catch (error) {
|
|
273
|
+
if (generation !== generationRef.current) return
|
|
231
274
|
console.error('Failed to start Gemini Live voice session', error)
|
|
232
275
|
// Keep voiceMode on so the error stays visible in the voice-mode bar instead of
|
|
233
276
|
// vanishing the instant it appears; the user can dismiss it with the mic button.
|
|
234
277
|
setAssistantTranscript(`Error: ${error instanceof Error ? error.message : String(error)}`)
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
setVoiceAwaitingResponse(false)
|
|
278
|
+
setErrorMessage(error instanceof Error ? error.message : String(error))
|
|
279
|
+
stop()
|
|
238
280
|
}
|
|
239
|
-
}, [handleEvent, workspacePath, chatId, ensureAudioContext,
|
|
281
|
+
}, [handleEvent, workspacePath, chatId, ensureAudioContext, stop, startSession, stopSession, sendAudioChunk])
|
|
240
282
|
|
|
241
283
|
useEffect(() => {
|
|
242
284
|
if (voiceMode) {
|
|
@@ -271,6 +313,8 @@ export function useGeminiLiveVoice({ workspacePath, chatId, startSession, sendAu
|
|
|
271
313
|
isPaused,
|
|
272
314
|
togglePause,
|
|
273
315
|
isSpeaking,
|
|
316
|
+
analyserRef,
|
|
317
|
+
errorMessage,
|
|
274
318
|
restart,
|
|
275
319
|
startRecognition: noop,
|
|
276
320
|
stopRecognition: noop,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { useCallback, useLayoutEffect, useRef } from 'react'
|
|
2
|
+
|
|
3
|
+
// Memoized messages keep stable event handlers while their actions/settings
|
|
4
|
+
// can change. Events always call the last committed implementation.
|
|
5
|
+
export function useLatestCallback<T extends (...args: any[]) => any>(callback: T): T {
|
|
6
|
+
const latest = useRef(callback)
|
|
7
|
+
useLayoutEffect(() => { latest.current = callback }, [callback])
|
|
8
|
+
return useCallback(((...args: Parameters<T>) => latest.current(...args)) as T, [])
|
|
9
|
+
}
|
|
@@ -1,99 +1,107 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
2
|
+
import { createNativeRecordingSession, type NativeRecordingBackend } from './nativeRecordingSession'
|
|
2
3
|
|
|
3
|
-
export interface NativeVoiceInputOptions {
|
|
4
|
+
export interface NativeVoiceInputOptions extends NativeRecordingBackend {
|
|
4
5
|
onSendVoiceMessage: (text: string) => Promise<any>
|
|
5
|
-
startRecording: () => Promise<void>
|
|
6
|
-
stopRecordingAndTranscribe: () => Promise<string>
|
|
7
6
|
}
|
|
8
7
|
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
* whichever chat provider is configured for transcription — desktop only.
|
|
12
|
-
* Replaces the old browser `window.SpeechRecognition`-based `useSpeechToText`,
|
|
13
|
-
* which only worked in Chrome/Edge and not at all in the Tauri desktop
|
|
14
|
-
* webview on Linux.
|
|
15
|
-
*
|
|
16
|
-
* Exposes the same field names `useSpeechToText` did (isRecording/voiceMode/
|
|
17
|
-
* startRecognition/stopRecognition/scheduleVoiceListen/clearRestartTimer) so
|
|
18
|
-
* it drops into a chat panel's mic button without reworking call sites.
|
|
19
|
-
* Unlike the old hook, this is explicit push-to-talk with no continuous
|
|
20
|
-
* auto-relisten loop (there's no mid-recording silence detection — audio only
|
|
21
|
-
* reaches the transcription step after the user presses stop), so
|
|
22
|
-
* `scheduleVoiceListen`/`clearRestartTimer` are no-ops here.
|
|
23
|
-
*/
|
|
24
|
-
export function useNativeVoiceInput({ onSendVoiceMessage, startRecording, stopRecordingAndTranscribe }: NativeVoiceInputOptions) {
|
|
8
|
+
/** Explicit push-to-talk, with cancel/stop requests serialized behind device startup. */
|
|
9
|
+
export function useNativeVoiceInput(options: NativeVoiceInputOptions) {
|
|
25
10
|
const [isRecording, setIsRecording] = useState(false)
|
|
26
11
|
const [voiceMode, setVoiceMode] = useState(false)
|
|
27
12
|
const [voiceTranscript, setVoiceTranscript] = useState('')
|
|
28
13
|
const [voiceAwaitingResponse, setVoiceAwaitingResponse] = useState(false)
|
|
29
|
-
|
|
30
14
|
const voiceAwaitingResponseRef = useRef(false)
|
|
31
15
|
const voiceModeRef = useRef(false)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
16
|
+
const latest = useRef(options)
|
|
17
|
+
latest.current = options
|
|
18
|
+
const mounted = useRef(true)
|
|
19
|
+
const epoch = useRef(0)
|
|
20
|
+
const stopping = useRef<Promise<void> | null>(null)
|
|
21
|
+
const session = useRef<ReturnType<typeof createNativeRecordingSession> | null>(null)
|
|
22
|
+
session.current ??= createNativeRecordingSession({
|
|
23
|
+
startRecording: () => latest.current.startRecording(),
|
|
24
|
+
stopRecordingAndTranscribe: () => latest.current.stopRecordingAndTranscribe(),
|
|
25
|
+
cancelRecording: () => latest.current.cancelRecording(),
|
|
26
|
+
})
|
|
27
|
+
useEffect(() => { voiceAwaitingResponseRef.current = voiceAwaitingResponse }, [voiceAwaitingResponse])
|
|
28
|
+
useEffect(() => { voiceModeRef.current = voiceMode }, [voiceMode])
|
|
39
29
|
|
|
40
30
|
const startRecognition = useCallback(async () => {
|
|
41
|
-
if (
|
|
31
|
+
if (stopping.current) return false
|
|
32
|
+
const request = ++epoch.current
|
|
42
33
|
setVoiceTranscript('')
|
|
43
34
|
try {
|
|
44
|
-
await
|
|
45
|
-
|
|
46
|
-
|
|
35
|
+
const started = await session.current!.start()
|
|
36
|
+
if (started && mounted.current && request === epoch.current) {
|
|
37
|
+
setIsRecording(true)
|
|
38
|
+
setVoiceMode(true)
|
|
39
|
+
}
|
|
40
|
+
return started && mounted.current && request === epoch.current
|
|
47
41
|
} catch (error) {
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
if (mounted.current && request === epoch.current) {
|
|
43
|
+
setVoiceTranscript(error instanceof Error ? error.message : String(error))
|
|
44
|
+
setVoiceMode(true)
|
|
45
|
+
}
|
|
46
|
+
return false
|
|
50
47
|
}
|
|
51
|
-
}, [
|
|
48
|
+
}, [])
|
|
52
49
|
|
|
53
|
-
const stopRecognition = useCallback(
|
|
54
|
-
if (
|
|
50
|
+
const stopRecognition = useCallback(() => {
|
|
51
|
+
if (stopping.current) return stopping.current
|
|
52
|
+
const request = epoch.current
|
|
55
53
|
setIsRecording(false)
|
|
56
54
|
setVoiceAwaitingResponse(true)
|
|
57
55
|
setVoiceTranscript('Transcribing...')
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
56
|
+
const finish = async () => {
|
|
57
|
+
try {
|
|
58
|
+
const text = await session.current!.stop()
|
|
59
|
+
if (!mounted.current || request !== epoch.current || text === null) return
|
|
60
|
+
const trimmed = text.trim()
|
|
61
|
+
setVoiceTranscript(trimmed || 'No speech detected')
|
|
62
|
+
if (trimmed) await latest.current.onSendVoiceMessage(trimmed)
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (mounted.current && request === epoch.current) {
|
|
65
|
+
setVoiceTranscript(error instanceof Error ? error.message : String(error))
|
|
66
|
+
}
|
|
67
|
+
} finally {
|
|
68
|
+
stopping.current = null
|
|
69
|
+
if (mounted.current) {
|
|
70
|
+
setVoiceAwaitingResponse(false)
|
|
71
|
+
setVoiceMode(false)
|
|
72
|
+
}
|
|
66
73
|
}
|
|
67
|
-
} catch (error) {
|
|
68
|
-
console.error('Native voice transcription failed', error)
|
|
69
|
-
// Surfaces MicTranscribeError's message verbatim, e.g. "provider 'anthropic'
|
|
70
|
-
// does not support audio input for voice transcription — switch to a
|
|
71
|
-
// provider that does..."
|
|
72
|
-
setVoiceTranscript(error instanceof Error ? error.message : String(error))
|
|
73
|
-
} finally {
|
|
74
|
-
setVoiceAwaitingResponse(false)
|
|
75
|
-
setVoiceMode(false)
|
|
76
74
|
}
|
|
77
|
-
|
|
75
|
+
const task = finish()
|
|
76
|
+
stopping.current = task
|
|
77
|
+
return task
|
|
78
|
+
}, [])
|
|
78
79
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
const cancelRecognition = useCallback(async () => {
|
|
81
|
+
++epoch.current
|
|
82
|
+
setIsRecording(false)
|
|
83
|
+
setVoiceMode(false)
|
|
84
|
+
setVoiceTranscript('')
|
|
85
|
+
try { await session.current!.cancel() } catch (error) {
|
|
86
|
+
if (mounted.current) {
|
|
87
|
+
setVoiceTranscript(error instanceof Error ? error.message : String(error))
|
|
88
|
+
setVoiceMode(true)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}, [])
|
|
92
|
+
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
mounted.current = true
|
|
95
|
+
return () => {
|
|
96
|
+
mounted.current = false
|
|
97
|
+
++epoch.current
|
|
98
|
+
void session.current!.cancel().catch(error => console.error('Failed to close microphone', error))
|
|
99
|
+
}
|
|
100
|
+
}, [])
|
|
101
|
+
// Native recording is one-shot; the legacy voice-conversation scheduling API stays inert.
|
|
82
102
|
const scheduleVoiceListen = useCallback((_delayMs?: number) => {}, [])
|
|
83
103
|
const clearRestartTimer = useCallback(() => {}, [])
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
voiceMode,
|
|
88
|
-
setVoiceMode,
|
|
89
|
-
voiceTranscript,
|
|
90
|
-
setVoiceTranscript,
|
|
91
|
-
voiceAwaitingResponse,
|
|
92
|
-
voiceAwaitingResponseRef,
|
|
93
|
-
voiceModeRef,
|
|
94
|
-
startRecognition,
|
|
95
|
-
stopRecognition,
|
|
96
|
-
scheduleVoiceListen,
|
|
97
|
-
clearRestartTimer
|
|
98
|
-
}
|
|
104
|
+
return { isRecording, voiceMode, setVoiceMode, voiceTranscript, setVoiceTranscript,
|
|
105
|
+
voiceAwaitingResponse, voiceAwaitingResponseRef, voiceModeRef, startRecognition,
|
|
106
|
+
stopRecognition, cancelRecognition, scheduleVoiceListen, clearRestartTimer }
|
|
99
107
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
2
|
+
import { conversationPlatform } from '../platform'
|
|
3
|
+
import type { AgentProgress, ConversationSources } from '../types'
|
|
4
|
+
import { collectSessionSources } from './sessionSources'
|
|
5
|
+
|
|
6
|
+
/** Shared session ownership and polling for the Desktop and Web source panels. */
|
|
7
|
+
export function useSessionSources(chatId: string, liveProgress: AgentProgress[], enabled: boolean) {
|
|
8
|
+
const [state, setState] = useState<{ chatId: string; value: ConversationSources | null; error: string; loading: boolean } | null>(null)
|
|
9
|
+
const [attempt, setAttempt] = useState(0)
|
|
10
|
+
const retry = useCallback(() => setAttempt(value => value + 1), [])
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (!enabled) return
|
|
13
|
+
let disposed = false
|
|
14
|
+
let busy = false
|
|
15
|
+
setState({ chatId, value: null, error: '', loading: true })
|
|
16
|
+
const load = async () => {
|
|
17
|
+
if (busy || document.hidden) return
|
|
18
|
+
busy = true
|
|
19
|
+
try {
|
|
20
|
+
const value = await conversationPlatform.getConversationSources(chatId)
|
|
21
|
+
if (!disposed) setState({ chatId, value, error: '', loading: false })
|
|
22
|
+
} catch (error) {
|
|
23
|
+
if (!disposed) setState(previous => ({ chatId, value: previous?.chatId === chatId ? previous.value : null, error: String(error), loading: false }))
|
|
24
|
+
} finally { busy = false }
|
|
25
|
+
}
|
|
26
|
+
void load()
|
|
27
|
+
const timer = window.setInterval(() => { void load() }, 2000)
|
|
28
|
+
return () => { disposed = true; window.clearInterval(timer) }
|
|
29
|
+
}, [enabled, chatId, attempt])
|
|
30
|
+
const current = state?.chatId === chatId ? state : null
|
|
31
|
+
const sources = useMemo(() => enabled ? collectSessionSources(current?.value ?? { attachments: [], webSearchProgress: [] }, liveProgress) : [], [enabled, current, liveProgress])
|
|
32
|
+
return { sources, loading: enabled && (!current || current.loading), error: current?.error || '', retry }
|
|
33
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AgentProgress } from '../types'
|
|
2
|
+
|
|
3
|
+
/** view_image returns an inline image, optionally followed by post-tool hook notes. */
|
|
4
|
+
export function viewedImageDataUri(result: string): string | null {
|
|
5
|
+
const firstLine = result.trim().split('\n', 1)[0].trim()
|
|
6
|
+
return /^data:image\/(?:png|jpeg|gif|webp|svg\+xml);base64,[A-Za-z0-9+/]+={0,2}$/.test(firstLine)
|
|
7
|
+
? firstLine : null
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function viewedImagesFrom(progress: AgentProgress[]) {
|
|
11
|
+
const images = new Map<string, { id: string; src: string; path: string; name: string }>()
|
|
12
|
+
let anonymousCount = 0
|
|
13
|
+
progress.forEach(event => {
|
|
14
|
+
if (event.type !== 'ToolEnd' || event.data.action !== 'view_image'
|
|
15
|
+
|| (event.data.status && event.data.status !== 'success')) return
|
|
16
|
+
const src = viewedImageDataUri(event.data.result)
|
|
17
|
+
if (!src) return
|
|
18
|
+
const path = typeof event.data.input.path === 'string' ? event.data.input.path : ''
|
|
19
|
+
const name = path.replaceAll('\\', '/').split('/').at(-1) || 'Image'
|
|
20
|
+
// Legacy/parallel results may omit callId. Count only retained images so
|
|
21
|
+
// trimming unrelated progress cannot remount an image and close its dialog.
|
|
22
|
+
const id = event.data.callId || `view-image-${anonymousCount++}`
|
|
23
|
+
images.set(id, { id, src, path, name })
|
|
24
|
+
})
|
|
25
|
+
return [...images.values()]
|
|
26
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { workspacePaneLayout, workspacePaneKeyWidth, readWorkspacePaneWidth } from './workspacePane'
|
|
3
|
+
|
|
4
|
+
describe('Workspace pane sizing', () => {
|
|
5
|
+
it('enforces the minimum and maximum on a wide window', () => {
|
|
6
|
+
expect(workspacePaneLayout(100, 1600).width).toBe(280)
|
|
7
|
+
expect(workspacePaneLayout(1000, 1600).width).toBe(720)
|
|
8
|
+
})
|
|
9
|
+
it('reserves space for the conversation and restores the preferred width after shrinking', () => {
|
|
10
|
+
expect(workspacePaneLayout(650, 1000)).toMatchObject({ width: 522, max: 522, stacked: false })
|
|
11
|
+
expect(workspacePaneLayout(650, 1500).width).toBe(650)
|
|
12
|
+
})
|
|
13
|
+
it('stacks the panes when their minimum widths cannot fit', () => {
|
|
14
|
+
expect(workspacePaneLayout(400, 757).stacked).toBe(true)
|
|
15
|
+
expect(workspacePaneLayout(400, 758).stacked).toBe(false)
|
|
16
|
+
})
|
|
17
|
+
it('validates persisted sizes and tolerates unavailable storage', () => {
|
|
18
|
+
for (const value of [null, '', 'oops', 'Infinity']) expect(readWorkspacePaneWidth(() => value)).toBe(400)
|
|
19
|
+
expect(readWorkspacePaneWidth(() => '560')).toBe(560)
|
|
20
|
+
expect(readWorkspacePaneWidth(() => '9999')).toBe(720)
|
|
21
|
+
expect(readWorkspacePaneWidth(() => { throw new Error('blocked') })).toBe(400)
|
|
22
|
+
})
|
|
23
|
+
it('supports keyboard resizing, bounded Home/End and default reset', () => {
|
|
24
|
+
const layout = workspacePaneLayout(400, 1000)
|
|
25
|
+
expect(workspacePaneKeyWidth('ArrowLeft', layout)).toBe(376)
|
|
26
|
+
expect(workspacePaneKeyWidth('ArrowRight', layout)).toBe(424)
|
|
27
|
+
expect(workspacePaneKeyWidth('Home', layout)).toBe(280)
|
|
28
|
+
expect(workspacePaneKeyWidth('End', layout)).toBe(522)
|
|
29
|
+
expect(workspacePaneKeyWidth('Enter', layout)).toBe(400)
|
|
30
|
+
expect(workspacePaneKeyWidth('Tab', layout)).toBeNull()
|
|
31
|
+
expect(workspacePaneKeyWidth('ArrowRight', workspacePaneLayout(720, 1600))).toBe(720)
|
|
32
|
+
})
|
|
33
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const WORKSPACE_PANE_STORAGE_KEY = 'mint:workspace-panel-width'
|
|
2
|
+
const MIN = 280
|
|
3
|
+
const MAX = 720
|
|
4
|
+
const DEFAULT = 400
|
|
5
|
+
const clamp = (width: number, max = MAX) => Math.min(max, Math.max(MIN, Number.isFinite(width) ? width : DEFAULT))
|
|
6
|
+
|
|
7
|
+
export function workspacePaneLayout(preferred: number, available: number) {
|
|
8
|
+
const max = Math.max(MIN, Math.min(MAX, available - 460 - 18))
|
|
9
|
+
return { width: clamp(preferred, max), min: MIN, max, stacked: available < MIN + 460 + 18 }
|
|
10
|
+
}
|
|
11
|
+
export function workspacePaneKeyWidth(key: string, layout: ReturnType<typeof workspacePaneLayout>): number | null {
|
|
12
|
+
const width = { ArrowLeft: layout.width - 24, ArrowRight: layout.width + 24, Home: layout.min, End: layout.max, Enter: DEFAULT }[key]
|
|
13
|
+
return width === undefined ? null : clamp(width, layout.max)
|
|
14
|
+
}
|
|
15
|
+
export function readWorkspacePaneWidth(read: () => string | null) {
|
|
16
|
+
try {
|
|
17
|
+
const value = read()
|
|
18
|
+
return value?.trim() && Number.isFinite(Number(value)) ? clamp(Number(value)) : DEFAULT
|
|
19
|
+
} catch { return DEFAULT }
|
|
20
|
+
}
|