@vibe-forge/client 2.0.0 → 3.0.0-alpha.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/AGENTS.md +4 -1
- package/dist/assets/{arc-CbOXL0l9.js → arc-1JbypnRY.js} +1 -1
- package/dist/assets/{blockDiagram-c4efeb88-CqxINvsS.js → blockDiagram-c4efeb88-jT5b9nId.js} +1 -1
- package/dist/assets/{c4Diagram-c83219d4-BKazU0hb.js → c4Diagram-c83219d4-KNPh-1ta.js} +1 -1
- package/dist/assets/channel-pzvjJnfd.js +1 -0
- package/dist/assets/{classDiagram-beda092f-fAFX5BpB.js → classDiagram-beda092f-vvXuOT3F.js} +1 -1
- package/dist/assets/{classDiagram-v2-2358418a-w1VkNGJj.js → classDiagram-v2-2358418a-vH4j7skr.js} +1 -1
- package/dist/assets/clone-CtWwIeUb.js +1 -0
- package/dist/assets/{createText-1719965b-CEinakVP.js → createText-1719965b-COeSYnf3.js} +1 -1
- package/dist/assets/{cssMode-DPqRki4y.js → cssMode-Doc7QRKn.js} +1 -1
- package/dist/assets/{edges-96097737-Cb0F1_3K.js → edges-96097737-D09v6R_r.js} +1 -1
- package/dist/assets/{erDiagram-0228fc6a-C-N2fx-J.js → erDiagram-0228fc6a-CbZ6rfxl.js} +1 -1
- package/dist/assets/{flowDb-c6c81e3f-D1Xz_8Gf.js → flowDb-c6c81e3f-CnZOzhdk.js} +1 -1
- package/dist/assets/{flowDiagram-50d868cf-DyPSZyAj.js → flowDiagram-50d868cf-jv-xWM2p.js} +1 -1
- package/dist/assets/flowDiagram-v2-4f6560a1-BiyrkIKs.js +1 -0
- package/dist/assets/{flowchart-elk-definition-6af322e1-Dr1DDXwE.js → flowchart-elk-definition-6af322e1-D8pf677G.js} +1 -1
- package/dist/assets/{freemarker2-C3DvPFaK.js → freemarker2-CL0o23Gj.js} +1 -1
- package/dist/assets/{ganttDiagram-a2739b55-DmvY1GRj.js → ganttDiagram-a2739b55-BAPQzC4u.js} +1 -1
- package/dist/assets/{gitGraphDiagram-82fe8481-CoXfPYYi.js → gitGraphDiagram-82fe8481-BI2x71m0.js} +1 -1
- package/dist/assets/{graph-BkDQy7Qt.js → graph-CZK4Bjpq.js} +1 -1
- package/dist/assets/{handlebars-BcTFdqjl.js → handlebars-CCG38Pg6.js} +1 -1
- package/dist/assets/{html-Dg-O6XFr.js → html-BddshTWG.js} +1 -1
- package/dist/assets/{htmlMode-B_wqYWvn.js → htmlMode-xKXiYcDz.js} +1 -1
- package/dist/assets/{index-5325376f-kxPTR3_e.js → index-5325376f-BWMTD8RU.js} +1 -1
- package/dist/assets/{index-wkhI4dr6.js → index-CBe7kDkV.js} +398 -377
- package/dist/assets/index-MWOwVzqE.css +32 -0
- package/dist/assets/{infoDiagram-8eee0895-BEvqkwPI.js → infoDiagram-8eee0895-CR78btIF.js} +1 -1
- package/dist/assets/{javascript-DhlOH8_z.js → javascript-BPlRHPjg.js} +1 -1
- package/dist/assets/{journeyDiagram-c64418c1-gKtLYmmp.js → journeyDiagram-c64418c1-DZRv6FKz.js} +1 -1
- package/dist/assets/{jsonMode-DxTbF9OD.js → jsonMode-BYLVfdkf.js} +1 -1
- package/dist/assets/{layout-CDaZEk6E.js → layout-BtudyPU2.js} +1 -1
- package/dist/assets/{line-DNRQu8iq.js → line-DgHXrIhS.js} +1 -1
- package/dist/assets/{linear-Cph9Z6_j.js → linear-DtBoKICx.js} +1 -1
- package/dist/assets/{liquid-ByZ6JgRG.js → liquid-DxBlJk0W.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-DzvhkgnM.js → lspLanguageFeatures-DmKVpmeH.js} +1 -1
- package/dist/assets/{mdx-D8RGHTl6.js → mdx-DrScsd-w.js} +1 -1
- package/dist/assets/{mermaid.core-BgcryF__.js → mermaid.core-Cj_NJ_lZ.js} +4 -4
- package/dist/assets/{mindmap-definition-8da855dc-WrxK0FcB.js → mindmap-definition-8da855dc-CMXbwtsc.js} +1 -1
- package/dist/assets/{pieDiagram-a8764435-VsZBsiQy.js → pieDiagram-a8764435-Bd6rfpJv.js} +1 -1
- package/dist/assets/{python-CXVtk_cg.js → python-Crbi7B7n.js} +1 -1
- package/dist/assets/{quadrantDiagram-1e28029f-BVlgwOvU.js → quadrantDiagram-1e28029f-H-FdBQn6.js} +1 -1
- package/dist/assets/{razor-0tind7h2.js → razor-DHyrzIfE.js} +1 -1
- package/dist/assets/{requirementDiagram-08caed73-CpPMPoYp.js → requirementDiagram-08caed73-BSyCVDSG.js} +1 -1
- package/dist/assets/{sankeyDiagram-a04cb91d-Cm5nnRmc.js → sankeyDiagram-a04cb91d-S6E6BReg.js} +1 -1
- package/dist/assets/{sequenceDiagram-c5b8d532-DpMlJvJB.js → sequenceDiagram-c5b8d532-BMHY4KMj.js} +1 -1
- package/dist/assets/{stateDiagram-1ecb1508-DU1zc7vq.js → stateDiagram-1ecb1508-BMrMw6XR.js} +1 -1
- package/dist/assets/{stateDiagram-v2-c2b004d7-D-0RgmAp.js → stateDiagram-v2-c2b004d7-B5SIFUb_.js} +1 -1
- package/dist/assets/{styles-b4e223ce-BSO-yNWV.js → styles-b4e223ce-DqiXwnx3.js} +1 -1
- package/dist/assets/{styles-ca3715f6-CHnsn2Ro.js → styles-ca3715f6-jNxW2Db8.js} +1 -1
- package/dist/assets/{styles-d45a18b0-B-rVGjEq.js → styles-d45a18b0-C4nlfLcZ.js} +1 -1
- package/dist/assets/{svgDrawCommon-b86b1483-CA3Pl89f.js → svgDrawCommon-b86b1483-FIWFuZfb.js} +1 -1
- package/dist/assets/{timeline-definition-faaaa080-BcihLR6s.js → timeline-definition-faaaa080-CEQDoqdf.js} +1 -1
- package/dist/assets/{tsMode-D9GGa5Ur.js → tsMode-DnNy3rE9.js} +1 -1
- package/dist/assets/{typescript-BT9CK_EL.js → typescript-YBZ4vw5B.js} +1 -1
- package/dist/assets/{xml-DNO75J-T.js → xml-BJGGYD70.js} +1 -1
- package/dist/assets/{xychartDiagram-f5964ef8-DJTwe32X.js → xychartDiagram-f5964ef8-DUNOFOk0.js} +1 -1
- package/dist/assets/{yaml-7CVzhiP2.js → yaml-BMY4mu5s.js} +1 -1
- package/dist/index.html +8 -3
- package/dist/manifest.webmanifest +30 -0
- package/dist/pwa-icon-192.png +0 -0
- package/dist/pwa-icon-512.png +0 -0
- package/dist/sw.js +105 -0
- package/index.html +6 -1
- package/package.json +13 -13
- package/public/manifest.webmanifest +30 -0
- package/public/pwa-icon-192.png +0 -0
- package/public/pwa-icon-512.png +0 -0
- package/public/sw.js +105 -0
- package/src/App.tsx +13 -1
- package/src/api/README.md +1 -0
- package/src/api/adapters.ts +63 -0
- package/src/api/auth-token.ts +51 -0
- package/src/api/auth.ts +46 -0
- package/src/api/automation.ts +10 -0
- package/src/api/base.ts +20 -17
- package/src/api/config.ts +5 -1
- package/src/api/knowledge.ts +59 -0
- package/src/api/sessions.ts +35 -3
- package/src/api/skill-hub.ts +126 -0
- package/src/api/workspace.ts +33 -1
- package/src/api/worktree-environments.ts +53 -0
- package/src/api.ts +62 -7
- package/src/client-build-info.ts +19 -0
- package/src/components/ConfigView.scss +596 -28
- package/src/components/ConfigView.tsx +569 -138
- package/src/components/NavRail.scss +1 -2
- package/src/components/NavRail.tsx +33 -3
- package/src/components/Sidebar.scss +0 -44
- package/src/components/Sidebar.tsx +110 -37
- package/src/components/auth/AuthGate.scss +79 -0
- package/src/components/auth/AuthGate.tsx +174 -0
- package/src/components/automation-view/@components/AutomationTaskComposer.tsx +220 -0
- package/src/components/automation-view/@components/AutomationTriggerRow.tsx +192 -0
- package/src/components/automation-view/@hooks/use-automation-startup-options-data.tsx +289 -0
- package/src/components/automation-view/@hooks/use-automation-startup-static-options.ts +51 -0
- package/src/components/automation-view/@utils/sender-model-options.tsx +52 -0
- package/src/components/automation-view/@utils/startup-options.ts +26 -0
- package/src/components/automation-view/AutomationEmptyGuide.tsx +61 -0
- package/src/components/automation-view/AutomationEmptyLanding.scss +165 -0
- package/src/components/automation-view/AutomationEmptyLanding.tsx +211 -0
- package/src/components/automation-view/AutomationRuleDetailPreview.tsx +179 -0
- package/src/components/automation-view/PanelTitleActions.tsx +66 -0
- package/src/components/automation-view/RuleFormPanel.scss +172 -49
- package/src/components/automation-view/RuleFormPanel.tsx +196 -91
- package/src/components/automation-view/RuleSidebar.scss +128 -41
- package/src/components/automation-view/RuleSidebar.tsx +173 -89
- package/src/components/automation-view/RunHistoryPanel.scss +307 -72
- package/src/components/automation-view/RunHistoryPanel.tsx +185 -165
- package/src/components/automation-view/TaskList.scss +130 -64
- package/src/components/automation-view/TaskList.tsx +15 -31
- package/src/components/automation-view/TriggerList.scss +87 -8
- package/src/components/automation-view/TriggerList.tsx +14 -173
- package/src/components/automation-view/index.scss +165 -37
- package/src/components/automation-view/index.tsx +174 -87
- package/src/components/automation-view/types.ts +13 -0
- package/src/components/chat/AGENTS.md +3 -1
- package/src/components/chat/ChatHeader.tsx +56 -33
- package/src/components/chat/ChatHistoryView.tsx +254 -123
- package/src/components/chat/NewSessionGuide.scss +274 -204
- package/src/components/chat/NewSessionGuide.tsx +46 -111
- package/src/components/chat/NewSessionGuideStarterList.tsx +190 -0
- package/src/components/chat/NewSessionGuideStarterSection.tsx +121 -0
- package/src/components/chat/bottom-dock-constants.ts +4 -0
- package/src/components/chat/conversation-starter-apply.ts +181 -0
- package/src/components/chat/git-controls/ChatGitControls.scss +65 -0
- package/src/components/chat/git-controls/DraftGitControls.tsx +14 -0
- package/src/components/chat/git-controls/DraftWorktreeEnvironmentDropdown.tsx +115 -0
- package/src/components/chat/messages/MessageItem.tsx +21 -6
- package/src/components/chat/messages/MessageStatusNotice.tsx +12 -4
- package/src/components/chat/messages/build-chat-history-status-notices.ts +18 -13
- package/src/components/chat/messages/message-action-utils.ts +29 -0
- package/src/components/chat/new-session-guide-config.ts +19 -0
- package/src/components/chat/new-session-guide-items.ts +172 -0
- package/src/components/chat/new-session-guide-list-order.ts +58 -0
- package/src/components/chat/sender/@components/account-select/AccountSelectControl.scss +112 -0
- package/src/components/chat/sender/@components/account-select/AccountSelectControl.tsx +280 -0
- package/src/components/chat/sender/@components/account-select/AccountSelectDropdown.scss +155 -0
- package/src/components/chat/sender/@components/adapter-select/AdapterSelectControl.scss +51 -12
- package/src/components/chat/sender/@components/adapter-select/AdapterSelectDropdown.scss +14 -0
- package/src/components/chat/sender/@components/effort-select/EffortSelectControl.scss +36 -0
- package/src/components/chat/sender/@components/effort-select/EffortSelectControl.tsx +17 -12
- package/src/components/chat/sender/@components/model-select/ModelSelectControl.scss +62 -0
- package/src/components/chat/sender/@components/model-select/ModelSelectControl.tsx +17 -12
- package/src/components/chat/sender/@components/model-select/ModelSelectMenu.scss +2 -0
- package/src/components/chat/sender/@components/model-select/ModelSelectMenuLabels.scss +24 -0
- package/src/components/chat/sender/@components/permission-mode-control/PermissionModeControl.scss +199 -0
- package/src/components/chat/sender/@components/permission-mode-control/PermissionModeControl.tsx +172 -0
- package/src/components/chat/sender/@components/reference-actions/ReferenceActionsControl.scss +1 -10
- package/src/components/chat/sender/@components/reference-actions/ReferenceActionsControl.tsx +16 -65
- package/src/components/chat/sender/@components/sender-body/SenderBody.tsx +13 -1
- package/src/components/chat/sender/@components/sender-header-controls/SenderHeaderControls.tsx +157 -0
- package/src/components/chat/sender/@components/sender-monaco-editor/monaco-runtime.ts +1 -17
- package/src/components/chat/sender/@components/sender-toolbar/SenderSelectBase.scss +18 -2
- package/src/components/chat/sender/@components/sender-toolbar/SenderSelectShared.scss +18 -1
- package/src/components/chat/sender/@components/sender-toolbar/SenderToolbar.scss +2 -0
- package/src/components/chat/sender/@components/sender-toolbar/SenderToolbar.tsx +40 -40
- package/src/components/chat/sender/@components/session-target/SenderSessionTargetBar.scss +215 -0
- package/src/components/chat/sender/@components/session-target/SenderSessionTargetBar.tsx +185 -0
- package/src/components/chat/sender/@core/build-sender-toolbar.ts +6 -0
- package/src/components/chat/sender/@core/create-sender-toolbar-handlers.ts +21 -1
- package/src/components/chat/sender/@core/get-sender-runtime-state.ts +3 -2
- package/src/components/chat/sender/@core/sender-toolbar-bindings.ts +12 -0
- package/src/components/chat/sender/@hooks/use-sender-controller.ts +56 -1
- package/src/components/chat/sender/@hooks/use-sender-reference-actions.ts +9 -66
- package/src/components/chat/sender/@types/sender-props.ts +18 -0
- package/src/components/chat/sender/@types/sender-toolbar-types.ts +8 -1
- package/src/components/chat/sender/@types/sender-types.ts +1 -3
- package/src/components/chat/sender/@utils/sender-constants.ts +1 -1
- package/src/components/chat/sender/Sender.scss +257 -2
- package/src/components/chat/sender/Sender.tsx +1 -0
- package/src/components/chat/status-bar/ChatStatusBar.scss +85 -0
- package/src/components/chat/status-bar/ChatStatusBar.tsx +48 -0
- package/src/components/chat/terminal/@components/TerminalManagerList.tsx +191 -0
- package/src/components/chat/terminal/@components/TerminalPane.scss +71 -0
- package/src/components/chat/terminal/@components/TerminalPane.tsx +137 -0
- package/src/components/chat/terminal/@components/TerminalPanelActions.tsx +75 -0
- package/src/components/chat/terminal/@hooks/use-terminal-instance.ts +36 -0
- package/src/components/chat/terminal/@hooks/use-terminal-session.ts +18 -21
- package/src/components/chat/terminal/@hooks/use-terminal-title-editor.ts +72 -0
- package/src/components/chat/terminal/@utils/terminal-keyboard.ts +141 -0
- package/src/components/chat/terminal/@utils/terminal-panes.ts +123 -0
- package/src/components/chat/terminal/ChatTerminalView.scss +310 -38
- package/src/components/chat/terminal/ChatTerminalView.tsx +151 -79
- package/src/components/chat/tools/core/ToolDiffViewer.tsx +3 -17
- package/src/components/chat/workspace-drawer/ChatWorkspaceDrawer.scss +778 -0
- package/src/components/chat/workspace-drawer/ChatWorkspaceDrawer.tsx +112 -0
- package/src/components/chat/workspace-drawer/ChatWorkspaceDrawerToolbar.tsx +183 -0
- package/src/components/chat/workspace-drawer/WorkspaceDrawerChangedFileRow.tsx +75 -0
- package/src/components/chat/workspace-drawer/WorkspaceDrawerChangedFiles.tsx +161 -0
- package/src/components/chat/workspace-drawer/WorkspaceDrawerChangedFolderTree.tsx +191 -0
- package/src/components/chat/workspace-drawer/WorkspaceDrawerTree.tsx +35 -0
- package/src/components/chat/workspace-drawer/WorkspaceDrawerTreeState.tsx +17 -0
- package/src/components/chat/workspace-drawer/changed-files-model.ts +152 -0
- package/src/components/chat/workspace-drawer/workspace-drawer-icons.ts +110 -0
- package/src/components/chat/workspace-file-editor/WorkspaceFileBreadcrumb.tsx +17 -0
- package/src/components/chat/workspace-file-editor/WorkspaceFileEditorView.scss +283 -0
- package/src/components/chat/workspace-file-editor/WorkspaceFileEditorView.tsx +165 -0
- package/src/components/chat/workspace-file-editor/WorkspaceFileTabs.tsx +135 -0
- package/src/components/chat/workspace-file-editor/use-workspace-file-editor-state.ts +113 -0
- package/src/components/chat/workspace-file-editor/workspace-file-editor-language.ts +55 -0
- package/src/components/composer-landing/ComposerLanding.scss +75 -0
- package/src/components/composer-landing/ComposerLanding.tsx +47 -0
- package/src/components/config/AGENTS.md +45 -0
- package/src/components/config/AdapterAccountsManager.scss +540 -0
- package/src/components/config/AdapterAccountsManager.tsx +846 -0
- package/src/components/config/AppSettingsPanel.tsx +85 -9
- package/src/components/config/ConfigAboutSection.scss +7 -1
- package/src/components/config/ConfigAboutSection.tsx +21 -3
- package/src/components/config/ConfigEditors.scss +12 -0
- package/src/components/config/ConfigFieldRow.scss +88 -4
- package/src/components/config/ConfigSectionForm.scss +88 -3
- package/src/components/config/ConfigSectionForm.tsx +948 -138
- package/src/components/config/ConfigSectionPanel.tsx +188 -12
- package/src/components/config/ConfigSourceSwitch.tsx +32 -18
- package/src/components/config/DetailCollectionFieldActions.tsx +63 -0
- package/src/components/config/DetailListField.tsx +413 -0
- package/src/components/config/McpServerItemEditor.tsx +154 -0
- package/src/components/config/RecommendedModelsItemEditor.tsx +146 -0
- package/src/components/config/WorktreeEnvironmentDetailView.tsx +126 -0
- package/src/components/config/WorktreeEnvironmentListView.tsx +126 -0
- package/src/components/config/WorktreeEnvironmentPanel.scss +430 -0
- package/src/components/config/WorktreeEnvironmentPanel.tsx +147 -0
- package/src/components/config/WorktreeEnvironmentScriptEditorCard.tsx +125 -0
- package/src/components/config/WorktreeEnvironmentScriptEditors.tsx +189 -0
- package/src/components/config/configConflict.ts +41 -0
- package/src/components/config/configDetail.ts +381 -0
- package/src/components/config/configSchema.ts +850 -179
- package/src/components/config/configUtils.ts +1 -1
- package/src/components/config/record-editors/RecordEditors.scss +187 -2
- package/src/components/config/record-editors/RecordJsonEditor.tsx +27 -2
- package/src/components/config/record-editors/SchemaObjectEditor.tsx +183 -0
- package/src/components/config/record-editors/SchemaRecordEditor.tsx +184 -0
- package/src/components/config/record-editors/index.tsx +2 -1
- package/src/components/config/record-editors/schemaRecordUtils.ts +55 -0
- package/src/components/config/use-worktree-environment-auto-save.ts +386 -0
- package/src/components/config/worktree-environment-panel-model.ts +108 -0
- package/src/components/dock-panel/DockPanel.scss +84 -17
- package/src/components/dock-panel/DockPanel.tsx +37 -34
- package/src/components/dock-panel/DockPanelHeader.tsx +65 -0
- package/src/components/dock-panel/use-dock-panel-fullscreen.ts +51 -0
- package/src/components/knowledge-base/KnowledgeBaseView.scss +276 -38
- package/src/components/knowledge-base/KnowledgeBaseView.tsx +252 -46
- package/src/components/knowledge-base/components/@hooks/use-skills-cli-modal-controller.ts +157 -0
- package/src/components/knowledge-base/components/@hooks/use-skills-tab-actions.ts +63 -0
- package/src/components/knowledge-base/components/ActionButton.scss +30 -4
- package/src/components/knowledge-base/components/ActionButton.tsx +13 -3
- package/src/components/knowledge-base/components/CreateSkillModal.tsx +59 -0
- package/src/components/knowledge-base/components/EmptyState.scss +4 -2
- package/src/components/knowledge-base/components/EntitiesTab.tsx +20 -20
- package/src/components/knowledge-base/components/EntityList.scss +3 -0
- package/src/components/knowledge-base/components/FilterBar.scss +1 -0
- package/src/components/knowledge-base/components/FilterBar.tsx +12 -8
- package/src/components/knowledge-base/components/FlowsTab.tsx +20 -20
- package/src/components/knowledge-base/components/KnowledgeBaseHeader.tsx +7 -6
- package/src/components/knowledge-base/components/KnowledgeContentControls.tsx +35 -0
- package/src/components/knowledge-base/components/KnowledgeList.scss +14 -3
- package/src/components/knowledge-base/components/KnowledgeMobilePanel.tsx +122 -0
- package/src/components/knowledge-base/components/KnowledgeSidebar.tsx +97 -0
- package/src/components/knowledge-base/components/LoadingState.scss +2 -1
- package/src/components/knowledge-base/components/ProjectSkillsList.tsx +79 -0
- package/src/components/knowledge-base/components/RuleList.scss +3 -0
- package/src/components/knowledge-base/components/RulesTab.tsx +31 -30
- package/src/components/knowledge-base/components/SectionHeader.scss +13 -1
- package/src/components/knowledge-base/components/SectionHeader.tsx +5 -3
- package/src/components/knowledge-base/components/SkillArchiveInput.tsx +43 -0
- package/src/components/knowledge-base/components/SkillHubResultItem.tsx +112 -0
- package/src/components/knowledge-base/components/SkillMarketResults.tsx +98 -0
- package/src/components/knowledge-base/components/SkillMarketView.tsx +198 -0
- package/src/components/knowledge-base/components/SkillMarketView.types.ts +28 -0
- package/src/components/knowledge-base/components/SkillRegistryErrors.tsx +21 -0
- package/src/components/knowledge-base/components/SkillRegistryModal.tsx +74 -0
- package/src/components/knowledge-base/components/SkillsCliModal.tsx +154 -0
- package/src/components/knowledge-base/components/SkillsTab.scss +424 -0
- package/src/components/knowledge-base/components/SkillsTab.tsx +166 -35
- package/src/components/knowledge-base/components/SkillsTabActions.tsx +88 -0
- package/src/components/knowledge-base/components/SpecList.scss +3 -0
- package/src/components/knowledge-base/components/TabContent.scss +4 -3
- package/src/components/knowledge-base/components/skill-hub-utils.ts +108 -0
- package/src/components/knowledge-base/components/use-skill-market-filters.ts +37 -0
- package/src/components/knowledge-base/components/use-skill-market-query-input.ts +44 -0
- package/src/components/knowledge-base/components/use-skill-market-search.ts +49 -0
- package/src/components/knowledge-base/components/use-skill-registry-modal.ts +68 -0
- package/src/components/monaco/monaco-runtime.ts +44 -0
- package/src/components/monaco/use-monaco-theme.ts +63 -0
- package/src/components/nav-rail-account-actions.tsx +104 -0
- package/src/components/server-connection/ServerConnectionGate.scss +356 -0
- package/src/components/server-connection/ServerConnectionGate.tsx +238 -0
- package/src/components/server-connection/ServerConnectionProfileModal.tsx +145 -0
- package/src/components/server-connection/ServerConnectionProfiles.tsx +113 -0
- package/src/components/server-connection/ServerConnectionUrlInput.tsx +85 -0
- package/src/components/sidebar/SidebarHeader.scss +5 -41
- package/src/components/sidebar/SidebarHeader.tsx +117 -91
- package/src/components/sidebar/SidebarHeaderSearchActions.tsx +24 -28
- package/src/components/sidebar/sidebar-search-visibility.ts +18 -0
- package/src/components/sidebar-list/SidebarListHeader.scss +246 -0
- package/src/components/sidebar-list/SidebarListHeader.tsx +146 -0
- package/src/components/workspace/ContextFilePicker.scss +9 -26
- package/src/components/workspace/ContextFilePicker.tsx +31 -113
- package/src/components/workspace/context-file-types.ts +36 -0
- package/src/components/workspace/project-file-tree/ProjectFileTree.scss +298 -0
- package/src/components/workspace/project-file-tree/ProjectFileTree.tsx +138 -0
- package/src/components/workspace/project-file-tree/ProjectFileTreeRow.tsx +167 -0
- package/src/components/workspace/project-file-tree/ProjectFileTreeRowContextMenu.tsx +106 -0
- package/src/components/workspace/project-file-tree/ProjectFileTreeRows.tsx +139 -0
- package/src/components/workspace/project-file-tree/project-file-tree-helpers.ts +101 -0
- package/src/components/workspace/project-file-tree/project-file-tree-icons.ts +93 -0
- package/src/components/workspace/project-file-tree/project-file-tree-types.ts +27 -0
- package/src/components/workspace/project-file-tree/use-project-file-tree-data.ts +197 -0
- package/src/components/workspace/project-file-tree/use-project-file-tree-selection.ts +144 -0
- package/src/hooks/chat/chat-session-target.ts +69 -0
- package/src/hooks/chat/chat-session-workspace-draft.ts +11 -4
- package/src/hooks/chat/interaction-state.ts +1 -0
- package/src/hooks/chat/optimistic-session-creation.ts +189 -0
- package/src/hooks/chat/use-chat-adapter-account-selection.tsx +156 -0
- package/src/hooks/chat/use-chat-route-bottom-panel.ts +181 -0
- package/src/hooks/chat/use-chat-route-deep-link-view.ts +33 -0
- package/src/hooks/chat/use-chat-session-actions.ts +259 -65
- package/src/hooks/chat/use-chat-session-messages.ts +71 -4
- package/src/hooks/chat/use-chat-session.ts +36 -1
- package/src/hooks/chat/workspace-file-panel-state.ts +43 -0
- package/src/hooks/session-subscription-cache.ts +25 -0
- package/src/hooks/use-chat-layout-query-state.ts +29 -0
- package/src/hooks/use-sender-header-query-state.ts +35 -0
- package/src/hooks/use-session-subscription.ts +17 -8
- package/src/hooks/useQueryParams.ts +91 -23
- package/src/i18n-resources.ts +44 -0
- package/src/i18n.ts +21 -6
- package/src/main.tsx +8 -0
- package/src/pwa.ts +46 -0
- package/src/resources/locales/en.json +741 -24
- package/src/resources/locales/zh.json +743 -26
- package/src/routes/ChatRoute.scss +110 -7
- package/src/routes/ChatRoute.tsx +11 -165
- package/src/routes/ChatRouteBottomPanel.tsx +47 -0
- package/src/routes/ChatRouteView.tsx +199 -0
- package/src/runtime-config.ts +155 -2
- package/src/server-connection-history.ts +179 -0
- package/src/store/index.ts +151 -3
- package/src/styles/global.scss +3 -0
- package/src/utils/mobile-viewport.ts +67 -0
- package/src/version-compatibility.ts +37 -0
- package/src/vite-env.d.ts +9 -0
- package/src/ws.ts +20 -9
- package/vite.config.ts +23 -1
- package/dist/assets/channel-Dnopc5A6.js +0 -1
- package/dist/assets/clone-sQthahUA.js +0 -1
- package/dist/assets/flowDiagram-v2-4f6560a1-OazrdWQO.js +0 -1
- package/dist/assets/index-o93dlo92.css +0 -32
- package/src/components/chat/NewSessionGuideCompactPanel.tsx +0 -130
- package/src/components/chat/NewSessionGuideGrid.tsx +0 -141
- package/src/components/chat/sender/@components/reference-actions/ReferencePermissionActionsPopover.tsx +0 -114
package/src/runtime-config.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
export type RuntimeEnv = Partial<{
|
|
2
|
+
__VF_PROJECT_AI_SERVER_BASE_URL__: string
|
|
2
3
|
__VF_PROJECT_AI_SERVER_HOST__: string
|
|
3
4
|
__VF_PROJECT_AI_SERVER_PORT__: string
|
|
4
5
|
__VF_PROJECT_AI_SERVER_WS_PATH__: string
|
|
6
|
+
__VF_PROJECT_AI_CLIENT_MODE__: string
|
|
5
7
|
__VF_PROJECT_AI_CLIENT_BASE__: string
|
|
8
|
+
__VF_PROJECT_AI_CLIENT_VERSION__: string
|
|
9
|
+
__VF_PROJECT_AI_CLIENT_COMMIT_HASH__: string
|
|
6
10
|
}>
|
|
7
11
|
|
|
12
|
+
export const SERVER_BASE_URL_STORAGE_KEY = 'vf_server_base_url'
|
|
13
|
+
export const SERVER_CONNECTION_PICKER_STORAGE_KEY = 'vf_server_connection_picker_requested'
|
|
14
|
+
|
|
8
15
|
export const resolveDevDocumentTitle = (
|
|
9
16
|
baseTitle: string,
|
|
10
17
|
input: {
|
|
@@ -57,6 +64,95 @@ const normalizePath = (value?: string) => {
|
|
|
57
64
|
return next
|
|
58
65
|
}
|
|
59
66
|
|
|
67
|
+
const normalizeServerHost = (value?: string) => {
|
|
68
|
+
const next = value?.trim()
|
|
69
|
+
if (next == null || next === '' || next === '0.0.0.0' || next === '::' || next === '[::]') {
|
|
70
|
+
return undefined
|
|
71
|
+
}
|
|
72
|
+
return next
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const hasUrlProtocol = (value: string) => /^[a-z][a-z\d+.-]*:\/\//i.test(value)
|
|
76
|
+
|
|
77
|
+
const getStorage = () => {
|
|
78
|
+
try {
|
|
79
|
+
return globalThis.localStorage
|
|
80
|
+
} catch {
|
|
81
|
+
return undefined
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const getBrowserProtocol = () => (
|
|
86
|
+
globalThis.location?.protocol === 'https:' ? 'https' : 'http'
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
const getClientMode = () => (
|
|
90
|
+
pickNonEmptyValue(
|
|
91
|
+
getRuntimeEnv().__VF_PROJECT_AI_CLIENT_MODE__,
|
|
92
|
+
import.meta.env.__VF_PROJECT_AI_CLIENT_MODE__,
|
|
93
|
+
import.meta.env.__VF_PROJECT_AI_CLIENT_DEPLOY_MODE__
|
|
94
|
+
)?.trim().toLowerCase()
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
export const normalizeServerBaseUrl = (value?: string) => {
|
|
98
|
+
const trimmed = value?.trim() ?? ''
|
|
99
|
+
if (trimmed === '') {
|
|
100
|
+
return undefined
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const rawUrl = hasUrlProtocol(trimmed) ? trimmed : `${getBrowserProtocol()}://${trimmed}`
|
|
104
|
+
try {
|
|
105
|
+
const url = new URL(rawUrl)
|
|
106
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
107
|
+
return undefined
|
|
108
|
+
}
|
|
109
|
+
url.hash = ''
|
|
110
|
+
url.search = ''
|
|
111
|
+
return url.toString().replace(/\/$/, '')
|
|
112
|
+
} catch {
|
|
113
|
+
return undefined
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const createServerUrlFromBase = (baseUrl: string, path: string) => {
|
|
118
|
+
const normalizedBaseUrl = normalizeServerBaseUrl(baseUrl)
|
|
119
|
+
if (normalizedBaseUrl == null) {
|
|
120
|
+
throw new Error('Invalid server base URL')
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const relativePath = path.replace(/^\/+/, '')
|
|
124
|
+
return new URL(relativePath, `${normalizedBaseUrl}/`).toString()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export const getStoredServerBaseUrl = () => (
|
|
128
|
+
normalizeServerBaseUrl(getStorage()?.getItem(SERVER_BASE_URL_STORAGE_KEY) ?? undefined)
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
export const setStoredServerBaseUrl = (value: string) => {
|
|
132
|
+
const normalized = normalizeServerBaseUrl(value)
|
|
133
|
+
if (normalized == null) {
|
|
134
|
+
return undefined
|
|
135
|
+
}
|
|
136
|
+
getStorage()?.setItem(SERVER_BASE_URL_STORAGE_KEY, normalized)
|
|
137
|
+
return normalized
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export const clearStoredServerBaseUrl = () => {
|
|
141
|
+
getStorage()?.removeItem(SERVER_BASE_URL_STORAGE_KEY)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const isStandaloneClientMode = () => {
|
|
145
|
+
const mode = getClientMode()
|
|
146
|
+
return mode === 'standalone' || mode === 'independent'
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const isDesktopClientMode = () => getClientMode() === 'desktop'
|
|
150
|
+
|
|
151
|
+
export const isServerConnectionManagedClientMode = () => {
|
|
152
|
+
const mode = getClientMode()
|
|
153
|
+
return mode === 'standalone' || mode === 'independent' || mode === 'desktop'
|
|
154
|
+
}
|
|
155
|
+
|
|
60
156
|
export const getRuntimeEnv = (): RuntimeEnv => getGlobalRuntimeEnv() ?? {}
|
|
61
157
|
|
|
62
158
|
export const resolveClientBase = (...values: Array<string | undefined>) => (
|
|
@@ -73,14 +169,71 @@ export const getClientBase = () => (
|
|
|
73
169
|
)
|
|
74
170
|
|
|
75
171
|
export const getServerHostEnv = () =>
|
|
76
|
-
|
|
77
|
-
|
|
172
|
+
normalizeServerHost(
|
|
173
|
+
getRuntimeEnv().__VF_PROJECT_AI_SERVER_HOST__ ??
|
|
174
|
+
import.meta.env.__VF_PROJECT_AI_SERVER_HOST__
|
|
175
|
+
)
|
|
78
176
|
|
|
79
177
|
export const getServerPortEnv = () =>
|
|
80
178
|
getRuntimeEnv().__VF_PROJECT_AI_SERVER_PORT__ ??
|
|
81
179
|
import.meta.env.__VF_PROJECT_AI_SERVER_PORT__
|
|
82
180
|
|
|
181
|
+
export const getConfiguredServerBaseUrl = () => {
|
|
182
|
+
const configuredServerBaseUrl = normalizeServerBaseUrl(
|
|
183
|
+
getRuntimeEnv().__VF_PROJECT_AI_SERVER_BASE_URL__ ??
|
|
184
|
+
import.meta.env.__VF_PROJECT_AI_SERVER_BASE_URL__
|
|
185
|
+
)
|
|
186
|
+
if (configuredServerBaseUrl != null) {
|
|
187
|
+
return configuredServerBaseUrl
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const serverHost = getServerHostEnv()
|
|
191
|
+
const serverPort = getServerPortEnv()?.trim()
|
|
192
|
+
if (serverHost == null || serverPort == null || serverPort === '') {
|
|
193
|
+
return undefined
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return normalizeServerBaseUrl(`${serverHost}:${serverPort}`)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export const isServerConnectionPickerRequested = () => (
|
|
200
|
+
getStorage()?.getItem(SERVER_CONNECTION_PICKER_STORAGE_KEY) === 'true'
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
export const requestServerConnectionPicker = (
|
|
204
|
+
{ clearCurrentServer = false }: { clearCurrentServer?: boolean } = {}
|
|
205
|
+
) => {
|
|
206
|
+
if (clearCurrentServer) {
|
|
207
|
+
clearStoredServerBaseUrl()
|
|
208
|
+
}
|
|
209
|
+
getStorage()?.setItem(SERVER_CONNECTION_PICKER_STORAGE_KEY, 'true')
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export const clearServerConnectionPickerRequest = () => {
|
|
213
|
+
getStorage()?.removeItem(SERVER_CONNECTION_PICKER_STORAGE_KEY)
|
|
214
|
+
}
|
|
215
|
+
|
|
83
216
|
export const getServerWsPath = () =>
|
|
84
217
|
normalizePath(
|
|
85
218
|
getRuntimeEnv().__VF_PROJECT_AI_SERVER_WS_PATH__ ?? import.meta.env.__VF_PROJECT_AI_SERVER_WS_PATH__
|
|
86
219
|
)
|
|
220
|
+
|
|
221
|
+
export const getServerBaseUrl = () => {
|
|
222
|
+
if (isServerConnectionManagedClientMode()) {
|
|
223
|
+
const storedServerBaseUrl = getStoredServerBaseUrl()
|
|
224
|
+
if (storedServerBaseUrl != null) {
|
|
225
|
+
return storedServerBaseUrl
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const configuredServerBaseUrl = getConfiguredServerBaseUrl()
|
|
230
|
+
if (configuredServerBaseUrl != null) {
|
|
231
|
+
return configuredServerBaseUrl
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const serverHost = getServerHostEnv() ?? globalThis.location?.hostname ?? 'localhost'
|
|
235
|
+
const serverPort = getServerPortEnv() ?? '8787'
|
|
236
|
+
return normalizeServerBaseUrl(`${serverHost}:${serverPort}`) ?? `${getBrowserProtocol()}://localhost:8787`
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export const createServerUrl = (path: string) => createServerUrlFromBase(getServerBaseUrl(), path)
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { normalizeServerBaseUrl } from '#~/runtime-config'
|
|
2
|
+
|
|
3
|
+
export const SERVER_BASE_URL_HISTORY_STORAGE_KEY = 'vf_server_base_url_history'
|
|
4
|
+
|
|
5
|
+
export interface ServerConnectionProfile {
|
|
6
|
+
serverUrl: string
|
|
7
|
+
alias?: string
|
|
8
|
+
description?: string
|
|
9
|
+
authToken?: string
|
|
10
|
+
serverVersion?: string
|
|
11
|
+
createdAt: number
|
|
12
|
+
lastConnectedAt: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ServerConnectionProfileDetails {
|
|
16
|
+
alias?: string
|
|
17
|
+
description?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const getStorage = () => {
|
|
21
|
+
try {
|
|
22
|
+
return globalThis.localStorage
|
|
23
|
+
} catch {
|
|
24
|
+
return undefined
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const normalizeOptionalText = (value: unknown) => {
|
|
29
|
+
const normalized = typeof value === 'string' ? value.trim() : ''
|
|
30
|
+
return normalized === '' ? undefined : normalized
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const normalizeProfile = (value: unknown, fallbackTime: number): ServerConnectionProfile | undefined => {
|
|
34
|
+
if (typeof value === 'string') {
|
|
35
|
+
const serverUrl = normalizeServerBaseUrl(value)
|
|
36
|
+
return serverUrl == null ? undefined : {
|
|
37
|
+
serverUrl,
|
|
38
|
+
createdAt: fallbackTime,
|
|
39
|
+
lastConnectedAt: fallbackTime
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (value == null || typeof value !== 'object' || Array.isArray(value)) return undefined
|
|
43
|
+
|
|
44
|
+
const record = value as Record<string, unknown>
|
|
45
|
+
const serverUrl = normalizeServerBaseUrl(record.serverUrl as string | undefined)
|
|
46
|
+
if (serverUrl == null) return undefined
|
|
47
|
+
|
|
48
|
+
const createdAt = typeof record.createdAt === 'number' ? record.createdAt : fallbackTime
|
|
49
|
+
const lastConnectedAt = typeof record.lastConnectedAt === 'number' ? record.lastConnectedAt : createdAt
|
|
50
|
+
const alias = normalizeOptionalText(record.alias)
|
|
51
|
+
const description = normalizeOptionalText(record.description)
|
|
52
|
+
const authToken = normalizeOptionalText(record.authToken)
|
|
53
|
+
const serverVersion = normalizeOptionalText(record.serverVersion)
|
|
54
|
+
return {
|
|
55
|
+
serverUrl,
|
|
56
|
+
createdAt,
|
|
57
|
+
lastConnectedAt,
|
|
58
|
+
...(alias != null ? { alias } : {}),
|
|
59
|
+
...(description != null ? { description } : {}),
|
|
60
|
+
...(authToken != null ? { authToken } : {}),
|
|
61
|
+
...(serverVersion != null ? { serverVersion } : {})
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const sortProfiles = (profiles: ServerConnectionProfile[]) => (
|
|
66
|
+
[...profiles].sort((left, right) => right.lastConnectedAt - left.lastConnectedAt)
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
const saveProfiles = (profiles: ServerConnectionProfile[]) => {
|
|
70
|
+
const normalizedProfiles = sortProfiles(profiles)
|
|
71
|
+
getStorage()?.setItem(SERVER_BASE_URL_HISTORY_STORAGE_KEY, JSON.stringify(normalizedProfiles))
|
|
72
|
+
return normalizedProfiles
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const getServerConnectionProfiles = () => {
|
|
76
|
+
const raw = getStorage()?.getItem(SERVER_BASE_URL_HISTORY_STORAGE_KEY)
|
|
77
|
+
if (raw == null || raw.trim() === '') return []
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const parsed = JSON.parse(raw) as unknown
|
|
81
|
+
if (!Array.isArray(parsed)) return []
|
|
82
|
+
|
|
83
|
+
const profileByUrl = new Map<string, ServerConnectionProfile>()
|
|
84
|
+
const fallbackTime = Date.now()
|
|
85
|
+
for (const item of parsed) {
|
|
86
|
+
const profile = normalizeProfile(item, fallbackTime)
|
|
87
|
+
if (profile == null || profileByUrl.has(profile.serverUrl)) continue
|
|
88
|
+
profileByUrl.set(profile.serverUrl, profile)
|
|
89
|
+
}
|
|
90
|
+
return sortProfiles(Array.from(profileByUrl.values()))
|
|
91
|
+
} catch {
|
|
92
|
+
return []
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const getRecentServerBaseUrls = () => (
|
|
97
|
+
getServerConnectionProfiles().map(profile => profile.serverUrl)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
export const rememberServerBaseUrl = (value: string, details: { serverVersion?: string } = {}) => {
|
|
101
|
+
const serverUrl = normalizeServerBaseUrl(value)
|
|
102
|
+
if (serverUrl == null) return getServerConnectionProfiles()
|
|
103
|
+
|
|
104
|
+
const now = Date.now()
|
|
105
|
+
const profiles = getServerConnectionProfiles()
|
|
106
|
+
const existing = profiles.find(profile => profile.serverUrl === serverUrl)
|
|
107
|
+
const nextProfile: ServerConnectionProfile = {
|
|
108
|
+
...existing,
|
|
109
|
+
serverUrl,
|
|
110
|
+
createdAt: existing?.createdAt ?? now,
|
|
111
|
+
lastConnectedAt: now,
|
|
112
|
+
...(normalizeOptionalText(details.serverVersion) != null
|
|
113
|
+
? { serverVersion: normalizeOptionalText(details.serverVersion) }
|
|
114
|
+
: {})
|
|
115
|
+
}
|
|
116
|
+
return saveProfiles([
|
|
117
|
+
nextProfile,
|
|
118
|
+
...profiles.filter(profile => profile.serverUrl !== serverUrl)
|
|
119
|
+
])
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const updateServerConnectionProfile = (serverUrl: string, details: ServerConnectionProfileDetails) => {
|
|
123
|
+
const normalizedServerUrl = normalizeServerBaseUrl(serverUrl)
|
|
124
|
+
if (normalizedServerUrl == null) return getServerConnectionProfiles()
|
|
125
|
+
|
|
126
|
+
const now = Date.now()
|
|
127
|
+
const profiles = getServerConnectionProfiles()
|
|
128
|
+
const existing = profiles.find(profile => profile.serverUrl === normalizedServerUrl)
|
|
129
|
+
const alias = normalizeOptionalText(details.alias)
|
|
130
|
+
const description = normalizeOptionalText(details.description)
|
|
131
|
+
const { alias: _alias, description: _description, ...existingProfile } = existing ?? {}
|
|
132
|
+
return saveProfiles([
|
|
133
|
+
{
|
|
134
|
+
...existingProfile,
|
|
135
|
+
serverUrl: normalizedServerUrl,
|
|
136
|
+
createdAt: existing?.createdAt ?? now,
|
|
137
|
+
lastConnectedAt: existing?.lastConnectedAt ?? now,
|
|
138
|
+
...(alias != null ? { alias } : {}),
|
|
139
|
+
...(description != null ? { description } : {})
|
|
140
|
+
},
|
|
141
|
+
...profiles.filter(profile => profile.serverUrl !== normalizedServerUrl)
|
|
142
|
+
])
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export const removeServerConnectionProfile = (serverUrl: string) => {
|
|
146
|
+
const normalizedServerUrl = normalizeServerBaseUrl(serverUrl)
|
|
147
|
+
if (normalizedServerUrl == null) return getServerConnectionProfiles()
|
|
148
|
+
return saveProfiles(getServerConnectionProfiles().filter(profile => profile.serverUrl !== normalizedServerUrl))
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export const getAuthTokenForServerUrl = (serverUrl: string) => (
|
|
152
|
+
getServerConnectionProfiles().find(profile => profile.serverUrl === normalizeServerBaseUrl(serverUrl))
|
|
153
|
+
?.authToken
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
export const setAuthTokenForServerUrl = (serverUrl: string, token: string) => {
|
|
157
|
+
const normalizedToken = token.trim()
|
|
158
|
+
if (normalizedToken === '') return
|
|
159
|
+
|
|
160
|
+
const profiles = rememberServerBaseUrl(serverUrl)
|
|
161
|
+
const normalizedServerUrl = normalizeServerBaseUrl(serverUrl)
|
|
162
|
+
saveProfiles(
|
|
163
|
+
profiles.map(profile =>
|
|
164
|
+
profile.serverUrl === normalizedServerUrl ? { ...profile, authToken: normalizedToken } : profile
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export const clearAuthTokenForServerUrl = (serverUrl: string) => {
|
|
170
|
+
const normalizedServerUrl = normalizeServerBaseUrl(serverUrl)
|
|
171
|
+
if (normalizedServerUrl == null) return getServerConnectionProfiles()
|
|
172
|
+
return saveProfiles(
|
|
173
|
+
getServerConnectionProfiles().map(profile => {
|
|
174
|
+
if (profile.serverUrl !== normalizedServerUrl) return profile
|
|
175
|
+
const { authToken: _authToken, ...nextProfile } = profile
|
|
176
|
+
return nextProfile
|
|
177
|
+
})
|
|
178
|
+
)
|
|
179
|
+
}
|
package/src/store/index.ts
CHANGED
|
@@ -18,8 +18,156 @@ export const activeSessionIdAtom = atom<string | undefined>(undefined)
|
|
|
18
18
|
|
|
19
19
|
// 主题模式: 'light' | 'dark' | 'system'
|
|
20
20
|
export type ThemeMode = 'light' | 'dark' | 'system'
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
|
|
22
|
+
const THEME_STORAGE_KEY = 'theme'
|
|
23
|
+
|
|
24
|
+
const isThemeMode = (value: string): value is ThemeMode => {
|
|
25
|
+
return value === 'light' || value === 'dark' || value === 'system'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const getStoredThemeMode = (): ThemeMode => {
|
|
29
|
+
try {
|
|
30
|
+
const raw = localStorage.getItem(THEME_STORAGE_KEY)
|
|
31
|
+
if (raw != null && isThemeMode(raw)) {
|
|
32
|
+
return raw
|
|
33
|
+
}
|
|
34
|
+
} catch {}
|
|
35
|
+
|
|
36
|
+
return 'system'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const themeBaseAtom = atom<ThemeMode>(getStoredThemeMode())
|
|
40
|
+
|
|
41
|
+
export const themeAtom = atom(
|
|
42
|
+
get => get(themeBaseAtom),
|
|
43
|
+
(_get, set, value: ThemeMode) => {
|
|
44
|
+
const nextValue = isThemeMode(value)
|
|
45
|
+
? value
|
|
46
|
+
: 'system'
|
|
47
|
+
|
|
48
|
+
set(themeBaseAtom, nextValue)
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
localStorage.setItem(THEME_STORAGE_KEY, nextValue)
|
|
52
|
+
} catch {}
|
|
53
|
+
}
|
|
23
54
|
)
|
|
24
55
|
|
|
25
|
-
|
|
56
|
+
const getStoredBoolean = (key: string, defaultValue: boolean) => {
|
|
57
|
+
try {
|
|
58
|
+
const raw = localStorage.getItem(key)
|
|
59
|
+
if (raw === 'true') return true
|
|
60
|
+
if (raw === 'false') return false
|
|
61
|
+
} catch {}
|
|
62
|
+
|
|
63
|
+
return defaultValue
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const createStoredBooleanAtom = (storageKey: string, defaultValue: boolean) => {
|
|
67
|
+
const baseAtom = atom<boolean>(getStoredBoolean(storageKey, defaultValue))
|
|
68
|
+
|
|
69
|
+
return atom(
|
|
70
|
+
get => get(baseAtom),
|
|
71
|
+
(_get, set, value: boolean) => {
|
|
72
|
+
const nextValue = value === true
|
|
73
|
+
set(baseAtom, nextValue)
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
localStorage.setItem(storageKey, String(nextValue))
|
|
77
|
+
} catch {}
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const getStoredNonNegativeInteger = (key: string, defaultValue: number) => {
|
|
83
|
+
try {
|
|
84
|
+
const raw = localStorage.getItem(key)
|
|
85
|
+
if (raw == null || raw.trim() === '') return defaultValue
|
|
86
|
+
|
|
87
|
+
const parsed = Number(raw)
|
|
88
|
+
if (Number.isInteger(parsed) && parsed >= 0) {
|
|
89
|
+
return parsed
|
|
90
|
+
}
|
|
91
|
+
} catch {}
|
|
92
|
+
|
|
93
|
+
return defaultValue
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const createStoredNonNegativeIntegerAtom = (storageKey: string, defaultValue: number) => {
|
|
97
|
+
const baseAtom = atom<number>(getStoredNonNegativeInteger(storageKey, defaultValue))
|
|
98
|
+
|
|
99
|
+
return atom(
|
|
100
|
+
get => get(baseAtom),
|
|
101
|
+
(_get, set, value: number) => {
|
|
102
|
+
const nextValue = Number.isInteger(value) && value >= 0
|
|
103
|
+
? value
|
|
104
|
+
: defaultValue
|
|
105
|
+
|
|
106
|
+
set(baseAtom, nextValue)
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
localStorage.setItem(storageKey, String(nextValue))
|
|
110
|
+
} catch {}
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export type SenderHeaderDisplayMode = 'expanded' | 'collapsed'
|
|
116
|
+
|
|
117
|
+
const SENDER_HEADER_DISPLAY_STORAGE_KEY = 'vf_sender_header_default_display'
|
|
118
|
+
|
|
119
|
+
const isSenderHeaderDisplayMode = (
|
|
120
|
+
value: string
|
|
121
|
+
): value is SenderHeaderDisplayMode => {
|
|
122
|
+
return value === 'expanded' || value === 'collapsed'
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const getStoredSenderHeaderDisplayMode = (): SenderHeaderDisplayMode => {
|
|
126
|
+
try {
|
|
127
|
+
const raw = localStorage.getItem(SENDER_HEADER_DISPLAY_STORAGE_KEY)
|
|
128
|
+
if (raw != null && isSenderHeaderDisplayMode(raw)) {
|
|
129
|
+
return raw
|
|
130
|
+
}
|
|
131
|
+
} catch {}
|
|
132
|
+
|
|
133
|
+
return 'expanded'
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const senderHeaderDisplayBaseAtom = atom<SenderHeaderDisplayMode>(
|
|
137
|
+
getStoredSenderHeaderDisplayMode()
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
export const senderHeaderDisplayAtom = atom(
|
|
141
|
+
get => get(senderHeaderDisplayBaseAtom),
|
|
142
|
+
(_get, set, value: SenderHeaderDisplayMode) => {
|
|
143
|
+
const nextValue = isSenderHeaderDisplayMode(value)
|
|
144
|
+
? value
|
|
145
|
+
: 'expanded'
|
|
146
|
+
|
|
147
|
+
set(senderHeaderDisplayBaseAtom, nextValue)
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
localStorage.setItem(SENDER_HEADER_DISPLAY_STORAGE_KEY, nextValue)
|
|
151
|
+
} catch {}
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
const SHOW_ANNOUNCEMENTS_STORAGE_KEY = 'vf_show_announcements'
|
|
156
|
+
const SHOW_NEW_SESSION_STARTER_LIST_STORAGE_KEY = 'vf_show_new_session_starter_list'
|
|
157
|
+
const SESSION_LIST_SEARCH_THRESHOLD_STORAGE_KEY = 'vf_session_list_search_threshold'
|
|
158
|
+
const DEFAULT_SESSION_LIST_SEARCH_THRESHOLD = 5
|
|
159
|
+
|
|
160
|
+
export const showAnnouncementsAtom = createStoredBooleanAtom(
|
|
161
|
+
SHOW_ANNOUNCEMENTS_STORAGE_KEY,
|
|
162
|
+
true
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
export const showNewSessionStarterListAtom = createStoredBooleanAtom(
|
|
166
|
+
SHOW_NEW_SESSION_STARTER_LIST_STORAGE_KEY,
|
|
167
|
+
true
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
export const sessionListSearchThresholdAtom = createStoredNonNegativeIntegerAtom(
|
|
171
|
+
SESSION_LIST_SEARCH_THRESHOLD_STORAGE_KEY,
|
|
172
|
+
DEFAULT_SESSION_LIST_SEARCH_THRESHOLD
|
|
173
|
+
)
|
package/src/styles/global.scss
CHANGED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const KEYBOARD_VISIBLE_THRESHOLD = 80
|
|
2
|
+
|
|
3
|
+
const focusableSelector = [
|
|
4
|
+
'input',
|
|
5
|
+
'textarea',
|
|
6
|
+
'select',
|
|
7
|
+
'[contenteditable="true"]',
|
|
8
|
+
'[role="textbox"]'
|
|
9
|
+
].join(',')
|
|
10
|
+
|
|
11
|
+
const getViewport = () => globalThis.visualViewport
|
|
12
|
+
|
|
13
|
+
const getFocusedInput = () => {
|
|
14
|
+
const activeElement = document.activeElement
|
|
15
|
+
if (!(activeElement instanceof HTMLElement)) return undefined
|
|
16
|
+
return activeElement.matches(focusableSelector) ? activeElement : undefined
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const scrollFocusedInputIntoView = () => {
|
|
20
|
+
const focusedInput = getFocusedInput()
|
|
21
|
+
if (focusedInput == null) return
|
|
22
|
+
|
|
23
|
+
focusedInput.scrollIntoView({
|
|
24
|
+
block: 'center',
|
|
25
|
+
inline: 'nearest',
|
|
26
|
+
behavior: 'auto'
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const setupMobileViewport = () => {
|
|
31
|
+
if (typeof window === 'undefined') return
|
|
32
|
+
|
|
33
|
+
let scrollFrame = 0
|
|
34
|
+
|
|
35
|
+
const updateViewportVars = () => {
|
|
36
|
+
const viewport = getViewport()
|
|
37
|
+
const viewportHeight = viewport?.height ?? window.innerHeight
|
|
38
|
+
const viewportOffsetTop = viewport?.offsetTop ?? 0
|
|
39
|
+
const focusedInput = getFocusedInput()
|
|
40
|
+
const keyboardInset = viewport == null || focusedInput == null
|
|
41
|
+
? 0
|
|
42
|
+
: Math.max(0, window.innerHeight - viewportHeight - viewportOffsetTop)
|
|
43
|
+
|
|
44
|
+
document.documentElement.style.setProperty('--vf-visual-viewport-height', `${Math.round(viewportHeight)}px`)
|
|
45
|
+
document.documentElement.style.setProperty('--vf-keyboard-inset-bottom', `${Math.round(keyboardInset)}px`)
|
|
46
|
+
document.documentElement.classList.toggle('has-mobile-keyboard', keyboardInset > KEYBOARD_VISIBLE_THRESHOLD)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const scheduleFocusedInputScroll = () => {
|
|
50
|
+
window.cancelAnimationFrame(scrollFrame)
|
|
51
|
+
scrollFrame = window.requestAnimationFrame(() => {
|
|
52
|
+
updateViewportVars()
|
|
53
|
+
scrollFocusedInputIntoView()
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
updateViewportVars()
|
|
58
|
+
|
|
59
|
+
getViewport()?.addEventListener('resize', scheduleFocusedInputScroll)
|
|
60
|
+
getViewport()?.addEventListener('scroll', scheduleFocusedInputScroll)
|
|
61
|
+
window.addEventListener('resize', scheduleFocusedInputScroll)
|
|
62
|
+
window.addEventListener('orientationchange', scheduleFocusedInputScroll)
|
|
63
|
+
document.addEventListener('focusin', scheduleFocusedInputScroll)
|
|
64
|
+
document.addEventListener('focusout', () => {
|
|
65
|
+
window.setTimeout(updateViewportVars, 120)
|
|
66
|
+
})
|
|
67
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface ParsedSemver {
|
|
2
|
+
major: number
|
|
3
|
+
minor: number
|
|
4
|
+
patch: number
|
|
5
|
+
prerelease?: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const SEMVER_PATTERN = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/
|
|
9
|
+
|
|
10
|
+
export const parseSemverVersion = (value: string | undefined): ParsedSemver | undefined => {
|
|
11
|
+
const match = value?.trim().match(SEMVER_PATTERN)
|
|
12
|
+
if (match == null) return undefined
|
|
13
|
+
return {
|
|
14
|
+
major: Number(match[1]),
|
|
15
|
+
minor: Number(match[2]),
|
|
16
|
+
patch: Number(match[3]),
|
|
17
|
+
...(match[4] != null ? { prerelease: match[4] } : {})
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const areSemverVersionsCompatible = (
|
|
22
|
+
clientVersion: string | undefined,
|
|
23
|
+
serverVersion: string | undefined
|
|
24
|
+
) => {
|
|
25
|
+
const client = parseSemverVersion(clientVersion)
|
|
26
|
+
const server = parseSemverVersion(serverVersion)
|
|
27
|
+
if (client == null || server == null) return false
|
|
28
|
+
if (client.prerelease != null || server.prerelease != null) {
|
|
29
|
+
return client.major === server.major &&
|
|
30
|
+
client.minor === server.minor &&
|
|
31
|
+
client.patch === server.patch &&
|
|
32
|
+
client.prerelease === server.prerelease
|
|
33
|
+
}
|
|
34
|
+
if (client.major !== server.major) return false
|
|
35
|
+
if (client.major === 0) return client.minor === server.minor
|
|
36
|
+
return true
|
|
37
|
+
}
|
package/src/vite-env.d.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
/// <reference types="vite/client" />
|
|
2
2
|
|
|
3
3
|
interface ImportMetaEnv {
|
|
4
|
+
readonly __VF_PROJECT_AI_SERVER_BASE_URL__: string
|
|
4
5
|
readonly __VF_PROJECT_AI_SERVER_HOST__: string
|
|
5
6
|
readonly __VF_PROJECT_AI_SERVER_PORT__: string
|
|
6
7
|
readonly __VF_PROJECT_AI_SERVER_WS_PATH__: string
|
|
7
8
|
readonly __VF_PROJECT_AI_CLIENT_HOST__: string
|
|
9
|
+
readonly __VF_PROJECT_AI_CLIENT_MODE__: string
|
|
10
|
+
readonly __VF_PROJECT_AI_CLIENT_DEPLOY_MODE__: string
|
|
8
11
|
readonly __VF_PROJECT_AI_CLIENT_PORT__: string
|
|
9
12
|
readonly __VF_PROJECT_AI_CLIENT_BASE__: string
|
|
13
|
+
readonly __VF_PROJECT_AI_CLIENT_VERSION__: string
|
|
14
|
+
readonly __VF_PROJECT_AI_CLIENT_COMMIT_HASH__: string
|
|
10
15
|
readonly __VF_PROJECT_AI_DEV_GIT_REF__: string
|
|
11
16
|
}
|
|
12
17
|
|
|
@@ -16,9 +21,13 @@ interface ImportMeta {
|
|
|
16
21
|
|
|
17
22
|
interface Window {
|
|
18
23
|
__VF_PROJECT_AI_RUNTIME_ENV__?: Partial<{
|
|
24
|
+
__VF_PROJECT_AI_SERVER_BASE_URL__: string
|
|
19
25
|
__VF_PROJECT_AI_SERVER_HOST__: string
|
|
20
26
|
__VF_PROJECT_AI_SERVER_PORT__: string
|
|
21
27
|
__VF_PROJECT_AI_SERVER_WS_PATH__: string
|
|
28
|
+
__VF_PROJECT_AI_CLIENT_MODE__: string
|
|
22
29
|
__VF_PROJECT_AI_CLIENT_BASE__: string
|
|
30
|
+
__VF_PROJECT_AI_CLIENT_VERSION__: string
|
|
31
|
+
__VF_PROJECT_AI_CLIENT_COMMIT_HASH__: string
|
|
23
32
|
}>
|
|
24
33
|
}
|