@vibe-forge/client 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (347) hide show
  1. package/AGENTS.md +4 -1
  2. package/dist/assets/{arc-CbOXL0l9.js → arc-CqviK3HX.js} +1 -1
  3. package/dist/assets/{blockDiagram-c4efeb88-CqxINvsS.js → blockDiagram-c4efeb88-BEp50UHp.js} +1 -1
  4. package/dist/assets/{c4Diagram-c83219d4-BKazU0hb.js → c4Diagram-c83219d4-C5w55JzM.js} +1 -1
  5. package/dist/assets/channel-C6LTxxLg.js +1 -0
  6. package/dist/assets/{classDiagram-beda092f-fAFX5BpB.js → classDiagram-beda092f-CQJVtHEy.js} +1 -1
  7. package/dist/assets/{classDiagram-v2-2358418a-w1VkNGJj.js → classDiagram-v2-2358418a-B37Xl9jB.js} +1 -1
  8. package/dist/assets/clone-CG6ZcokX.js +1 -0
  9. package/dist/assets/{createText-1719965b-CEinakVP.js → createText-1719965b-9YwvWMdV.js} +1 -1
  10. package/dist/assets/{cssMode-DPqRki4y.js → cssMode-BX88r5f4.js} +1 -1
  11. package/dist/assets/{edges-96097737-Cb0F1_3K.js → edges-96097737-CNHoXVrD.js} +1 -1
  12. package/dist/assets/{erDiagram-0228fc6a-C-N2fx-J.js → erDiagram-0228fc6a-BoYldy0g.js} +1 -1
  13. package/dist/assets/{flowDb-c6c81e3f-D1Xz_8Gf.js → flowDb-c6c81e3f-CoPw_R-Q.js} +1 -1
  14. package/dist/assets/{flowDiagram-50d868cf-DyPSZyAj.js → flowDiagram-50d868cf-nCqbSXd-.js} +1 -1
  15. package/dist/assets/flowDiagram-v2-4f6560a1-CSZTI7GQ.js +1 -0
  16. package/dist/assets/{flowchart-elk-definition-6af322e1-Dr1DDXwE.js → flowchart-elk-definition-6af322e1-BwMuPTrV.js} +1 -1
  17. package/dist/assets/{freemarker2-C3DvPFaK.js → freemarker2-DUFDSvgj.js} +1 -1
  18. package/dist/assets/{ganttDiagram-a2739b55-DmvY1GRj.js → ganttDiagram-a2739b55-CLNH3S_C.js} +1 -1
  19. package/dist/assets/{gitGraphDiagram-82fe8481-CoXfPYYi.js → gitGraphDiagram-82fe8481-uDu1ectX.js} +1 -1
  20. package/dist/assets/{graph-BkDQy7Qt.js → graph-DuC4kt4I.js} +1 -1
  21. package/dist/assets/{handlebars-BcTFdqjl.js → handlebars-BSd4a6l9.js} +1 -1
  22. package/dist/assets/{html-Dg-O6XFr.js → html-H48gEjQd.js} +1 -1
  23. package/dist/assets/{htmlMode-B_wqYWvn.js → htmlMode-Nqw7-Nqh.js} +1 -1
  24. package/dist/assets/{index-5325376f-kxPTR3_e.js → index-5325376f-rnz0GXAT.js} +1 -1
  25. package/dist/assets/{index-wkhI4dr6.js → index-DeQLT67a.js} +398 -377
  26. package/dist/assets/index-DiOCtPLP.css +32 -0
  27. package/dist/assets/{infoDiagram-8eee0895-BEvqkwPI.js → infoDiagram-8eee0895-BsGB550b.js} +1 -1
  28. package/dist/assets/{javascript-DhlOH8_z.js → javascript-0g2herYV.js} +1 -1
  29. package/dist/assets/{journeyDiagram-c64418c1-gKtLYmmp.js → journeyDiagram-c64418c1-DLldlz0H.js} +1 -1
  30. package/dist/assets/{jsonMode-DxTbF9OD.js → jsonMode-CN5ZURMh.js} +1 -1
  31. package/dist/assets/{layout-CDaZEk6E.js → layout-QKUiDNJK.js} +1 -1
  32. package/dist/assets/{line-DNRQu8iq.js → line-CeP3XWjD.js} +1 -1
  33. package/dist/assets/{linear-Cph9Z6_j.js → linear-74cQVgWT.js} +1 -1
  34. package/dist/assets/{liquid-ByZ6JgRG.js → liquid-B6cRrfrb.js} +1 -1
  35. package/dist/assets/{lspLanguageFeatures-DzvhkgnM.js → lspLanguageFeatures-C5ogOh5E.js} +1 -1
  36. package/dist/assets/{mdx-D8RGHTl6.js → mdx-BBIy-KRj.js} +1 -1
  37. package/dist/assets/{mermaid.core-BgcryF__.js → mermaid.core-BhdbV0mr.js} +4 -4
  38. package/dist/assets/{mindmap-definition-8da855dc-WrxK0FcB.js → mindmap-definition-8da855dc-B67VKJuD.js} +1 -1
  39. package/dist/assets/{pieDiagram-a8764435-VsZBsiQy.js → pieDiagram-a8764435-Cxv9WY_E.js} +1 -1
  40. package/dist/assets/{python-CXVtk_cg.js → python-CBdGo8__.js} +1 -1
  41. package/dist/assets/{quadrantDiagram-1e28029f-BVlgwOvU.js → quadrantDiagram-1e28029f-BTkj65P_.js} +1 -1
  42. package/dist/assets/{razor-0tind7h2.js → razor-azKH0Dwj.js} +1 -1
  43. package/dist/assets/{requirementDiagram-08caed73-CpPMPoYp.js → requirementDiagram-08caed73-D4jVXpOT.js} +1 -1
  44. package/dist/assets/{sankeyDiagram-a04cb91d-Cm5nnRmc.js → sankeyDiagram-a04cb91d-CXhutIA1.js} +1 -1
  45. package/dist/assets/{sequenceDiagram-c5b8d532-DpMlJvJB.js → sequenceDiagram-c5b8d532-B56TTZlx.js} +1 -1
  46. package/dist/assets/{stateDiagram-1ecb1508-DU1zc7vq.js → stateDiagram-1ecb1508-Cs0plMcS.js} +1 -1
  47. package/dist/assets/{stateDiagram-v2-c2b004d7-D-0RgmAp.js → stateDiagram-v2-c2b004d7-LSJaXPJN.js} +1 -1
  48. package/dist/assets/{styles-b4e223ce-BSO-yNWV.js → styles-b4e223ce-UdXfHMuu.js} +1 -1
  49. package/dist/assets/{styles-ca3715f6-CHnsn2Ro.js → styles-ca3715f6-EuRy_hTu.js} +1 -1
  50. package/dist/assets/{styles-d45a18b0-B-rVGjEq.js → styles-d45a18b0-B24zVoK3.js} +1 -1
  51. package/dist/assets/{svgDrawCommon-b86b1483-CA3Pl89f.js → svgDrawCommon-b86b1483-B2S0NW3K.js} +1 -1
  52. package/dist/assets/{timeline-definition-faaaa080-BcihLR6s.js → timeline-definition-faaaa080-DFWKh9mU.js} +1 -1
  53. package/dist/assets/{tsMode-D9GGa5Ur.js → tsMode-FZsHWiOn.js} +1 -1
  54. package/dist/assets/{typescript-BT9CK_EL.js → typescript-CYdJ3s3D.js} +1 -1
  55. package/dist/assets/{xml-DNO75J-T.js → xml-C16X_hpZ.js} +1 -1
  56. package/dist/assets/{xychartDiagram-f5964ef8-DJTwe32X.js → xychartDiagram-f5964ef8-DyBiBYci.js} +1 -1
  57. package/dist/assets/{yaml-7CVzhiP2.js → yaml-CRjA4-Rj.js} +1 -1
  58. package/dist/index.html +8 -3
  59. package/dist/manifest.webmanifest +30 -0
  60. package/dist/pwa-icon-192.png +0 -0
  61. package/dist/pwa-icon-512.png +0 -0
  62. package/dist/sw.js +105 -0
  63. package/index.html +6 -1
  64. package/package.json +13 -13
  65. package/public/manifest.webmanifest +30 -0
  66. package/public/pwa-icon-192.png +0 -0
  67. package/public/pwa-icon-512.png +0 -0
  68. package/public/sw.js +105 -0
  69. package/src/App.tsx +13 -1
  70. package/src/api/README.md +1 -0
  71. package/src/api/adapters.ts +63 -0
  72. package/src/api/auth-token.ts +51 -0
  73. package/src/api/auth.ts +46 -0
  74. package/src/api/automation.ts +10 -0
  75. package/src/api/base.ts +20 -17
  76. package/src/api/config.ts +5 -1
  77. package/src/api/knowledge.ts +59 -0
  78. package/src/api/sessions.ts +35 -3
  79. package/src/api/skill-hub.ts +126 -0
  80. package/src/api/workspace.ts +33 -1
  81. package/src/api/worktree-environments.ts +53 -0
  82. package/src/api.ts +62 -7
  83. package/src/client-build-info.ts +19 -0
  84. package/src/components/ConfigView.scss +595 -28
  85. package/src/components/ConfigView.tsx +568 -138
  86. package/src/components/NavRail.scss +1 -2
  87. package/src/components/NavRail.tsx +33 -3
  88. package/src/components/Sidebar.scss +0 -44
  89. package/src/components/Sidebar.tsx +109 -37
  90. package/src/components/auth/AuthGate.scss +79 -0
  91. package/src/components/auth/AuthGate.tsx +174 -0
  92. package/src/components/automation-view/@components/AutomationTaskComposer.tsx +218 -0
  93. package/src/components/automation-view/@components/AutomationTriggerRow.tsx +192 -0
  94. package/src/components/automation-view/@hooks/use-automation-startup-options-data.tsx +289 -0
  95. package/src/components/automation-view/@hooks/use-automation-startup-static-options.ts +51 -0
  96. package/src/components/automation-view/@utils/sender-model-options.tsx +52 -0
  97. package/src/components/automation-view/@utils/startup-options.ts +26 -0
  98. package/src/components/automation-view/AutomationEmptyGuide.tsx +61 -0
  99. package/src/components/automation-view/AutomationEmptyLanding.scss +165 -0
  100. package/src/components/automation-view/AutomationEmptyLanding.tsx +199 -0
  101. package/src/components/automation-view/AutomationRuleDetailPreview.tsx +179 -0
  102. package/src/components/automation-view/PanelTitleActions.tsx +66 -0
  103. package/src/components/automation-view/RuleFormPanel.scss +172 -49
  104. package/src/components/automation-view/RuleFormPanel.tsx +196 -91
  105. package/src/components/automation-view/RuleSidebar.scss +128 -41
  106. package/src/components/automation-view/RuleSidebar.tsx +173 -89
  107. package/src/components/automation-view/RunHistoryPanel.scss +307 -72
  108. package/src/components/automation-view/RunHistoryPanel.tsx +185 -165
  109. package/src/components/automation-view/TaskList.scss +126 -64
  110. package/src/components/automation-view/TaskList.tsx +15 -31
  111. package/src/components/automation-view/TriggerList.scss +87 -8
  112. package/src/components/automation-view/TriggerList.tsx +14 -173
  113. package/src/components/automation-view/index.scss +165 -37
  114. package/src/components/automation-view/index.tsx +174 -87
  115. package/src/components/automation-view/types.ts +13 -0
  116. package/src/components/chat/AGENTS.md +3 -1
  117. package/src/components/chat/ChatHeader.tsx +56 -33
  118. package/src/components/chat/ChatHistoryView.tsx +250 -121
  119. package/src/components/chat/NewSessionGuide.scss +274 -204
  120. package/src/components/chat/NewSessionGuide.tsx +40 -111
  121. package/src/components/chat/NewSessionGuideStarterList.tsx +187 -0
  122. package/src/components/chat/NewSessionGuideStarterSection.tsx +120 -0
  123. package/src/components/chat/bottom-dock-constants.ts +4 -0
  124. package/src/components/chat/conversation-starter-apply.ts +181 -0
  125. package/src/components/chat/git-controls/ChatGitControls.scss +65 -0
  126. package/src/components/chat/git-controls/DraftGitControls.tsx +14 -0
  127. package/src/components/chat/git-controls/DraftWorktreeEnvironmentDropdown.tsx +115 -0
  128. package/src/components/chat/messages/MessageItem.tsx +3 -2
  129. package/src/components/chat/messages/MessageStatusNotice.tsx +12 -4
  130. package/src/components/chat/messages/build-chat-history-status-notices.ts +18 -13
  131. package/src/components/chat/messages/message-action-utils.ts +18 -0
  132. package/src/components/chat/new-session-guide-config.ts +19 -0
  133. package/src/components/chat/new-session-guide-items.ts +172 -0
  134. package/src/components/chat/new-session-guide-list-order.ts +58 -0
  135. package/src/components/chat/sender/@components/account-select/AccountSelectControl.scss +112 -0
  136. package/src/components/chat/sender/@components/account-select/AccountSelectControl.tsx +280 -0
  137. package/src/components/chat/sender/@components/account-select/AccountSelectDropdown.scss +155 -0
  138. package/src/components/chat/sender/@components/adapter-select/AdapterSelectControl.scss +51 -12
  139. package/src/components/chat/sender/@components/adapter-select/AdapterSelectDropdown.scss +14 -0
  140. package/src/components/chat/sender/@components/effort-select/EffortSelectControl.scss +36 -0
  141. package/src/components/chat/sender/@components/effort-select/EffortSelectControl.tsx +17 -12
  142. package/src/components/chat/sender/@components/model-select/ModelSelectControl.scss +62 -0
  143. package/src/components/chat/sender/@components/model-select/ModelSelectControl.tsx +17 -12
  144. package/src/components/chat/sender/@components/model-select/ModelSelectMenu.scss +2 -0
  145. package/src/components/chat/sender/@components/model-select/ModelSelectMenuLabels.scss +24 -0
  146. package/src/components/chat/sender/@components/permission-mode-control/PermissionModeControl.scss +199 -0
  147. package/src/components/chat/sender/@components/permission-mode-control/PermissionModeControl.tsx +172 -0
  148. package/src/components/chat/sender/@components/reference-actions/ReferenceActionsControl.scss +1 -10
  149. package/src/components/chat/sender/@components/reference-actions/ReferenceActionsControl.tsx +16 -65
  150. package/src/components/chat/sender/@components/sender-body/SenderBody.tsx +13 -1
  151. package/src/components/chat/sender/@components/sender-header-controls/SenderHeaderControls.tsx +157 -0
  152. package/src/components/chat/sender/@components/sender-monaco-editor/monaco-runtime.ts +1 -17
  153. package/src/components/chat/sender/@components/sender-toolbar/SenderSelectBase.scss +18 -2
  154. package/src/components/chat/sender/@components/sender-toolbar/SenderSelectShared.scss +18 -1
  155. package/src/components/chat/sender/@components/sender-toolbar/SenderToolbar.scss +2 -0
  156. package/src/components/chat/sender/@components/sender-toolbar/SenderToolbar.tsx +40 -40
  157. package/src/components/chat/sender/@components/session-target/SenderSessionTargetBar.scss +215 -0
  158. package/src/components/chat/sender/@components/session-target/SenderSessionTargetBar.tsx +185 -0
  159. package/src/components/chat/sender/@core/build-sender-toolbar.ts +6 -0
  160. package/src/components/chat/sender/@core/create-sender-toolbar-handlers.ts +21 -1
  161. package/src/components/chat/sender/@core/get-sender-runtime-state.ts +3 -2
  162. package/src/components/chat/sender/@core/sender-toolbar-bindings.ts +12 -0
  163. package/src/components/chat/sender/@hooks/use-sender-controller.ts +56 -1
  164. package/src/components/chat/sender/@hooks/use-sender-reference-actions.ts +9 -66
  165. package/src/components/chat/sender/@types/sender-props.ts +18 -0
  166. package/src/components/chat/sender/@types/sender-toolbar-types.ts +8 -1
  167. package/src/components/chat/sender/@types/sender-types.ts +1 -3
  168. package/src/components/chat/sender/@utils/sender-constants.ts +1 -1
  169. package/src/components/chat/sender/Sender.scss +245 -2
  170. package/src/components/chat/sender/Sender.tsx +1 -0
  171. package/src/components/chat/status-bar/ChatStatusBar.scss +85 -0
  172. package/src/components/chat/status-bar/ChatStatusBar.tsx +48 -0
  173. package/src/components/chat/terminal/@components/TerminalManagerList.tsx +191 -0
  174. package/src/components/chat/terminal/@components/TerminalPane.scss +71 -0
  175. package/src/components/chat/terminal/@components/TerminalPane.tsx +137 -0
  176. package/src/components/chat/terminal/@components/TerminalPanelActions.tsx +75 -0
  177. package/src/components/chat/terminal/@hooks/use-terminal-instance.ts +36 -0
  178. package/src/components/chat/terminal/@hooks/use-terminal-session.ts +18 -21
  179. package/src/components/chat/terminal/@hooks/use-terminal-title-editor.ts +72 -0
  180. package/src/components/chat/terminal/@utils/terminal-keyboard.ts +141 -0
  181. package/src/components/chat/terminal/@utils/terminal-panes.ts +123 -0
  182. package/src/components/chat/terminal/ChatTerminalView.scss +310 -38
  183. package/src/components/chat/terminal/ChatTerminalView.tsx +151 -79
  184. package/src/components/chat/tools/core/ToolDiffViewer.tsx +3 -17
  185. package/src/components/chat/workspace-drawer/ChatWorkspaceDrawer.scss +778 -0
  186. package/src/components/chat/workspace-drawer/ChatWorkspaceDrawer.tsx +112 -0
  187. package/src/components/chat/workspace-drawer/ChatWorkspaceDrawerToolbar.tsx +183 -0
  188. package/src/components/chat/workspace-drawer/WorkspaceDrawerChangedFileRow.tsx +75 -0
  189. package/src/components/chat/workspace-drawer/WorkspaceDrawerChangedFiles.tsx +161 -0
  190. package/src/components/chat/workspace-drawer/WorkspaceDrawerChangedFolderTree.tsx +191 -0
  191. package/src/components/chat/workspace-drawer/WorkspaceDrawerTree.tsx +35 -0
  192. package/src/components/chat/workspace-drawer/WorkspaceDrawerTreeState.tsx +17 -0
  193. package/src/components/chat/workspace-drawer/changed-files-model.ts +152 -0
  194. package/src/components/chat/workspace-drawer/workspace-drawer-icons.ts +110 -0
  195. package/src/components/chat/workspace-file-editor/WorkspaceFileBreadcrumb.tsx +17 -0
  196. package/src/components/chat/workspace-file-editor/WorkspaceFileEditorView.scss +283 -0
  197. package/src/components/chat/workspace-file-editor/WorkspaceFileEditorView.tsx +165 -0
  198. package/src/components/chat/workspace-file-editor/WorkspaceFileTabs.tsx +135 -0
  199. package/src/components/chat/workspace-file-editor/use-workspace-file-editor-state.ts +113 -0
  200. package/src/components/chat/workspace-file-editor/workspace-file-editor-language.ts +55 -0
  201. package/src/components/composer-landing/ComposerLanding.scss +75 -0
  202. package/src/components/composer-landing/ComposerLanding.tsx +47 -0
  203. package/src/components/config/AGENTS.md +45 -0
  204. package/src/components/config/AdapterAccountsManager.scss +540 -0
  205. package/src/components/config/AdapterAccountsManager.tsx +846 -0
  206. package/src/components/config/AppSettingsPanel.tsx +24 -2
  207. package/src/components/config/ConfigAboutSection.scss +7 -1
  208. package/src/components/config/ConfigAboutSection.tsx +21 -3
  209. package/src/components/config/ConfigEditors.scss +12 -0
  210. package/src/components/config/ConfigFieldRow.scss +88 -4
  211. package/src/components/config/ConfigSectionForm.scss +88 -3
  212. package/src/components/config/ConfigSectionForm.tsx +948 -138
  213. package/src/components/config/ConfigSectionPanel.tsx +188 -12
  214. package/src/components/config/ConfigSourceSwitch.tsx +32 -18
  215. package/src/components/config/DetailCollectionFieldActions.tsx +63 -0
  216. package/src/components/config/DetailListField.tsx +413 -0
  217. package/src/components/config/McpServerItemEditor.tsx +154 -0
  218. package/src/components/config/RecommendedModelsItemEditor.tsx +146 -0
  219. package/src/components/config/WorktreeEnvironmentDetailView.tsx +126 -0
  220. package/src/components/config/WorktreeEnvironmentListView.tsx +126 -0
  221. package/src/components/config/WorktreeEnvironmentPanel.scss +430 -0
  222. package/src/components/config/WorktreeEnvironmentPanel.tsx +147 -0
  223. package/src/components/config/WorktreeEnvironmentScriptEditorCard.tsx +125 -0
  224. package/src/components/config/WorktreeEnvironmentScriptEditors.tsx +189 -0
  225. package/src/components/config/configConflict.ts +41 -0
  226. package/src/components/config/configDetail.ts +381 -0
  227. package/src/components/config/configSchema.ts +850 -179
  228. package/src/components/config/configUtils.ts +1 -1
  229. package/src/components/config/record-editors/RecordEditors.scss +187 -2
  230. package/src/components/config/record-editors/RecordJsonEditor.tsx +27 -2
  231. package/src/components/config/record-editors/SchemaObjectEditor.tsx +183 -0
  232. package/src/components/config/record-editors/SchemaRecordEditor.tsx +184 -0
  233. package/src/components/config/record-editors/index.tsx +2 -1
  234. package/src/components/config/record-editors/schemaRecordUtils.ts +55 -0
  235. package/src/components/config/use-worktree-environment-auto-save.ts +386 -0
  236. package/src/components/config/worktree-environment-panel-model.ts +108 -0
  237. package/src/components/dock-panel/DockPanel.scss +84 -17
  238. package/src/components/dock-panel/DockPanel.tsx +37 -34
  239. package/src/components/dock-panel/DockPanelHeader.tsx +65 -0
  240. package/src/components/dock-panel/use-dock-panel-fullscreen.ts +51 -0
  241. package/src/components/knowledge-base/KnowledgeBaseView.scss +276 -38
  242. package/src/components/knowledge-base/KnowledgeBaseView.tsx +252 -46
  243. package/src/components/knowledge-base/components/ActionButton.scss +30 -4
  244. package/src/components/knowledge-base/components/ActionButton.tsx +13 -3
  245. package/src/components/knowledge-base/components/CreateSkillModal.tsx +59 -0
  246. package/src/components/knowledge-base/components/EmptyState.scss +4 -2
  247. package/src/components/knowledge-base/components/EntitiesTab.tsx +20 -20
  248. package/src/components/knowledge-base/components/EntityList.scss +3 -0
  249. package/src/components/knowledge-base/components/FilterBar.scss +1 -0
  250. package/src/components/knowledge-base/components/FilterBar.tsx +12 -8
  251. package/src/components/knowledge-base/components/FlowsTab.tsx +20 -20
  252. package/src/components/knowledge-base/components/KnowledgeBaseHeader.tsx +7 -6
  253. package/src/components/knowledge-base/components/KnowledgeContentControls.tsx +35 -0
  254. package/src/components/knowledge-base/components/KnowledgeList.scss +14 -3
  255. package/src/components/knowledge-base/components/KnowledgeMobilePanel.tsx +122 -0
  256. package/src/components/knowledge-base/components/KnowledgeSidebar.tsx +97 -0
  257. package/src/components/knowledge-base/components/LoadingState.scss +2 -1
  258. package/src/components/knowledge-base/components/ProjectSkillsList.tsx +79 -0
  259. package/src/components/knowledge-base/components/RuleList.scss +3 -0
  260. package/src/components/knowledge-base/components/RulesTab.tsx +31 -30
  261. package/src/components/knowledge-base/components/SectionHeader.scss +13 -1
  262. package/src/components/knowledge-base/components/SectionHeader.tsx +5 -3
  263. package/src/components/knowledge-base/components/SkillArchiveInput.tsx +43 -0
  264. package/src/components/knowledge-base/components/SkillHubResultItem.tsx +112 -0
  265. package/src/components/knowledge-base/components/SkillMarketResults.tsx +98 -0
  266. package/src/components/knowledge-base/components/SkillMarketView.tsx +198 -0
  267. package/src/components/knowledge-base/components/SkillMarketView.types.ts +28 -0
  268. package/src/components/knowledge-base/components/SkillRegistryErrors.tsx +21 -0
  269. package/src/components/knowledge-base/components/SkillRegistryModal.tsx +74 -0
  270. package/src/components/knowledge-base/components/SkillsCliModal.tsx +154 -0
  271. package/src/components/knowledge-base/components/SkillsTab.scss +424 -0
  272. package/src/components/knowledge-base/components/SkillsTab.tsx +319 -35
  273. package/src/components/knowledge-base/components/SkillsTabActions.tsx +88 -0
  274. package/src/components/knowledge-base/components/SpecList.scss +3 -0
  275. package/src/components/knowledge-base/components/TabContent.scss +4 -3
  276. package/src/components/knowledge-base/components/skill-hub-utils.ts +108 -0
  277. package/src/components/knowledge-base/components/use-skill-market-filters.ts +37 -0
  278. package/src/components/knowledge-base/components/use-skill-market-query-input.ts +44 -0
  279. package/src/components/knowledge-base/components/use-skill-market-search.ts +49 -0
  280. package/src/components/knowledge-base/components/use-skill-registry-modal.ts +68 -0
  281. package/src/components/monaco/monaco-runtime.ts +44 -0
  282. package/src/components/monaco/use-monaco-theme.ts +63 -0
  283. package/src/components/nav-rail-account-actions.tsx +104 -0
  284. package/src/components/server-connection/ServerConnectionGate.scss +356 -0
  285. package/src/components/server-connection/ServerConnectionGate.tsx +238 -0
  286. package/src/components/server-connection/ServerConnectionProfileModal.tsx +145 -0
  287. package/src/components/server-connection/ServerConnectionProfiles.tsx +113 -0
  288. package/src/components/server-connection/ServerConnectionUrlInput.tsx +85 -0
  289. package/src/components/sidebar/SidebarHeader.scss +5 -41
  290. package/src/components/sidebar/SidebarHeader.tsx +74 -66
  291. package/src/components/sidebar/SidebarHeaderSearchActions.tsx +24 -28
  292. package/src/components/sidebar-list/SidebarListHeader.scss +246 -0
  293. package/src/components/sidebar-list/SidebarListHeader.tsx +146 -0
  294. package/src/components/workspace/ContextFilePicker.scss +9 -26
  295. package/src/components/workspace/ContextFilePicker.tsx +31 -113
  296. package/src/components/workspace/context-file-types.ts +36 -0
  297. package/src/components/workspace/project-file-tree/ProjectFileTree.scss +298 -0
  298. package/src/components/workspace/project-file-tree/ProjectFileTree.tsx +138 -0
  299. package/src/components/workspace/project-file-tree/ProjectFileTreeRow.tsx +167 -0
  300. package/src/components/workspace/project-file-tree/ProjectFileTreeRowContextMenu.tsx +106 -0
  301. package/src/components/workspace/project-file-tree/ProjectFileTreeRows.tsx +139 -0
  302. package/src/components/workspace/project-file-tree/project-file-tree-helpers.ts +101 -0
  303. package/src/components/workspace/project-file-tree/project-file-tree-icons.ts +93 -0
  304. package/src/components/workspace/project-file-tree/project-file-tree-types.ts +27 -0
  305. package/src/components/workspace/project-file-tree/use-project-file-tree-data.ts +197 -0
  306. package/src/components/workspace/project-file-tree/use-project-file-tree-selection.ts +144 -0
  307. package/src/hooks/chat/chat-session-target.ts +69 -0
  308. package/src/hooks/chat/chat-session-workspace-draft.ts +11 -4
  309. package/src/hooks/chat/interaction-state.ts +1 -0
  310. package/src/hooks/chat/optimistic-session-creation.ts +189 -0
  311. package/src/hooks/chat/use-chat-adapter-account-selection.tsx +156 -0
  312. package/src/hooks/chat/use-chat-route-bottom-panel.ts +181 -0
  313. package/src/hooks/chat/use-chat-route-deep-link-view.ts +33 -0
  314. package/src/hooks/chat/use-chat-session-actions.ts +259 -65
  315. package/src/hooks/chat/use-chat-session-messages.ts +71 -4
  316. package/src/hooks/chat/use-chat-session.ts +36 -1
  317. package/src/hooks/chat/workspace-file-panel-state.ts +43 -0
  318. package/src/hooks/session-subscription-cache.ts +25 -0
  319. package/src/hooks/use-chat-layout-query-state.ts +29 -0
  320. package/src/hooks/use-sender-header-query-state.ts +35 -0
  321. package/src/hooks/use-session-subscription.ts +17 -8
  322. package/src/i18n-resources.ts +44 -0
  323. package/src/i18n.ts +21 -6
  324. package/src/main.tsx +8 -0
  325. package/src/pwa.ts +46 -0
  326. package/src/resources/locales/en.json +729 -24
  327. package/src/resources/locales/zh.json +731 -26
  328. package/src/routes/ChatRoute.scss +105 -7
  329. package/src/routes/ChatRoute.tsx +11 -165
  330. package/src/routes/ChatRouteBottomPanel.tsx +47 -0
  331. package/src/routes/ChatRouteView.tsx +199 -0
  332. package/src/runtime-config.ts +155 -2
  333. package/src/server-connection-history.ts +179 -0
  334. package/src/store/index.ts +40 -0
  335. package/src/styles/global.scss +3 -0
  336. package/src/utils/mobile-viewport.ts +67 -0
  337. package/src/version-compatibility.ts +37 -0
  338. package/src/vite-env.d.ts +9 -0
  339. package/src/ws.ts +20 -9
  340. package/vite.config.ts +23 -1
  341. package/dist/assets/channel-Dnopc5A6.js +0 -1
  342. package/dist/assets/clone-sQthahUA.js +0 -1
  343. package/dist/assets/flowDiagram-v2-4f6560a1-OazrdWQO.js +0 -1
  344. package/dist/assets/index-o93dlo92.css +0 -32
  345. package/src/components/chat/NewSessionGuideCompactPanel.tsx +0 -130
  346. package/src/components/chat/NewSessionGuideGrid.tsx +0 -141
  347. package/src/components/chat/sender/@components/reference-actions/ReferencePermissionActionsPopover.tsx +0 -114
@@ -0,0 +1,846 @@
1
+ /* eslint-disable max-lines -- account management keeps list, detail, and action flows in one surface. */
2
+ import './AdapterAccountsManager.scss'
3
+
4
+ import { App, Button, Empty, Input, Popconfirm, Spin, Tooltip } from 'antd'
5
+ import { useMemo, useState } from 'react'
6
+ import useSWR from 'swr'
7
+
8
+ import type { AdapterAccountActionDescriptor, AdapterAccountInfo, ConfigUiObjectSchema } from '@vibe-forge/types'
9
+
10
+ import { getAdapterAccountDetail, getAdapterAccounts, getApiErrorMessage, manageAdapterAccount } from '#~/api'
11
+
12
+ import { getFieldDescription, getFieldLabel, getValueByPath, setValueByPath } from './configUtils'
13
+ import type { TranslationFn } from './configUtils'
14
+ import { SchemaObjectEditor } from './record-editors/SchemaObjectEditor'
15
+
16
+ const isRecord = (value: unknown): value is Record<string, unknown> => (
17
+ value != null && typeof value === 'object' && !Array.isArray(value)
18
+ )
19
+
20
+ const ACCOUNT_ACTION_ICON: Record<AdapterAccountActionDescriptor['key'], string> = {
21
+ add: 'person_add',
22
+ refresh: 'refresh',
23
+ remove: 'delete'
24
+ }
25
+
26
+ const ACCOUNT_STATUS_ICON: Record<NonNullable<AdapterAccountInfo['status']>, string> = {
27
+ ready: 'check_circle',
28
+ missing: 'warning',
29
+ error: 'error'
30
+ }
31
+
32
+ const getConfiguredAccounts = (value: Record<string, unknown>) => {
33
+ const configured = getValueByPath(value, ['accounts'])
34
+ return isRecord(configured) ? configured : {}
35
+ }
36
+
37
+ const getConfiguredAccountEntry = (value: Record<string, unknown>, accountKey: string) => {
38
+ const configured = getConfiguredAccounts(value)[accountKey]
39
+ return isRecord(configured) ? configured : {}
40
+ }
41
+
42
+ const setConfiguredAccountEntry = (
43
+ value: Record<string, unknown>,
44
+ accountKey: string,
45
+ nextEntry: Record<string, unknown>
46
+ ) => setValueByPath(value, ['accounts', accountKey], nextEntry) as Record<string, unknown>
47
+
48
+ const formatStatus = (status: AdapterAccountInfo['status'], t: TranslationFn) => {
49
+ switch (status) {
50
+ case 'missing':
51
+ return {
52
+ label: t('config.accounts.status.missing'),
53
+ color: 'default' as const,
54
+ icon: ACCOUNT_STATUS_ICON.missing
55
+ }
56
+ case 'error':
57
+ return {
58
+ label: t('config.accounts.status.error'),
59
+ color: 'error' as const,
60
+ icon: ACCOUNT_STATUS_ICON.error
61
+ }
62
+ case 'ready':
63
+ default:
64
+ return {
65
+ label: t('config.accounts.status.ready'),
66
+ color: 'success' as const,
67
+ icon: ACCOUNT_STATUS_ICON.ready
68
+ }
69
+ }
70
+ }
71
+
72
+ const getActionLabel = (action: AdapterAccountActionDescriptor, t: TranslationFn) => (
73
+ t(`config.accounts.actions.${action.key}.label`, { defaultValue: action.label })
74
+ )
75
+
76
+ const getActionDescription = (action: AdapterAccountActionDescriptor, t: TranslationFn) => (
77
+ t(`config.accounts.actions.${action.key}.description`, {
78
+ defaultValue: action.description ?? action.label
79
+ })
80
+ )
81
+
82
+ const normalizeText = (value: string | undefined) => value?.trim().toLowerCase() ?? ''
83
+ const normalizeDisplayText = (value: string | undefined) => value?.trim() ?? ''
84
+
85
+ const dedupeDisplayTexts = (...values: Array<string | undefined>) => {
86
+ const uniqueValues = new Set<string>()
87
+
88
+ return values
89
+ .map(normalizeDisplayText)
90
+ .filter((value) => {
91
+ if (value === '' || uniqueValues.has(value)) return false
92
+ uniqueValues.add(value)
93
+ return true
94
+ })
95
+ }
96
+
97
+ const parsePercentMetricValue = (value: string | undefined) => {
98
+ if (value == null) return undefined
99
+
100
+ const normalized = value.trim()
101
+ if (!normalized.endsWith('%')) return undefined
102
+
103
+ const parsed = Number(normalized.slice(0, -1))
104
+ if (!Number.isFinite(parsed)) return undefined
105
+
106
+ return Math.min(100, Math.max(0, parsed))
107
+ }
108
+
109
+ const getPercentRingColor = (percent: number) => {
110
+ if (percent >= 85) return 'var(--error-color, #ff4d4f)'
111
+ if (percent >= 60) return 'var(--warning-color, #faad14)'
112
+ return 'var(--success-color, #52c41a)'
113
+ }
114
+
115
+ const compareAccountInfo = (
116
+ left: Pick<AdapterAccountInfo, 'key' | 'title' | 'status' | 'isDefault'>,
117
+ right: Pick<AdapterAccountInfo, 'key' | 'title' | 'status' | 'isDefault'>
118
+ ) => {
119
+ if (left.isDefault === true && right.isDefault !== true) return -1
120
+ if (right.isDefault === true && left.isDefault !== true) return 1
121
+
122
+ if (left.status !== right.status) {
123
+ if (left.status === 'ready') return -1
124
+ if (right.status === 'ready') return 1
125
+ }
126
+
127
+ const titleOrder = normalizeText(left.title).localeCompare(normalizeText(right.title))
128
+ if (titleOrder !== 0) return titleOrder
129
+
130
+ return left.key.localeCompare(right.key)
131
+ }
132
+
133
+ const renderTooltipContent = (label: string, description?: string) => {
134
+ const normalizedDescription = description?.trim()
135
+ if (normalizedDescription == null || normalizedDescription === '' || normalizedDescription === label) {
136
+ return label
137
+ }
138
+
139
+ return (
140
+ <div className='adapter-account-manager__tooltip'>
141
+ <div className='adapter-account-manager__tooltip-title'>{label}</div>
142
+ <div className='adapter-account-manager__tooltip-description'>{normalizedDescription}</div>
143
+ </div>
144
+ )
145
+ }
146
+
147
+ const IconTag = ({
148
+ color,
149
+ icon,
150
+ label,
151
+ description
152
+ }: {
153
+ color?: 'default' | 'success' | 'error'
154
+ icon: string
155
+ label: string
156
+ description?: string
157
+ }) => {
158
+ const colorStyle = color === 'success'
159
+ ? {
160
+ color: 'var(--success-color, #52c41a)'
161
+ }
162
+ : color === 'error'
163
+ ? {
164
+ color: 'var(--error-color, #ff4d4f)'
165
+ }
166
+ : undefined
167
+
168
+ return (
169
+ <Tooltip title={renderTooltipContent(label, description)}>
170
+ <span className='adapter-account-manager__icon-tag' style={colorStyle} aria-label={label}>
171
+ <span className='material-symbols-rounded' aria-hidden='true'>{icon}</span>
172
+ </span>
173
+ </Tooltip>
174
+ )
175
+ }
176
+
177
+ const AccountActionButtons = ({
178
+ actions,
179
+ loadingAction,
180
+ onRunAction,
181
+ t
182
+ }: {
183
+ actions: AdapterAccountActionDescriptor[]
184
+ loadingAction?: string
185
+ onRunAction: (action: AdapterAccountActionDescriptor) => Promise<void>
186
+ t: TranslationFn
187
+ }) => {
188
+ if (actions.length === 0) return null
189
+
190
+ return (
191
+ <div className='adapter-account-manager__actions'>
192
+ {actions.map((action) => {
193
+ const label = getActionLabel(action, t)
194
+ const description = getActionDescription(action, t)
195
+ const icon = ACCOUNT_ACTION_ICON[action.key]
196
+
197
+ if (action.key === 'remove') {
198
+ return (
199
+ <Popconfirm
200
+ key={action.key}
201
+ title={t('config.accounts.deleteConfirmTitle', {
202
+ defaultValue: 'Delete the stored snapshot for {{account}}?',
203
+ account: label
204
+ })}
205
+ okText={t('common.confirm')}
206
+ cancelText={t('common.cancel')}
207
+ onConfirm={async () => {
208
+ await onRunAction(action)
209
+ }}
210
+ >
211
+ <Tooltip title={renderTooltipContent(label, description)}>
212
+ <Button
213
+ type='default'
214
+ size='small'
215
+ danger
216
+ loading={loadingAction === action.key}
217
+ aria-label={label}
218
+ className='adapter-account-manager__icon-button adapter-account-manager__header-action'
219
+ icon={<span className='material-symbols-rounded'>{icon}</span>}
220
+ />
221
+ </Tooltip>
222
+ </Popconfirm>
223
+ )
224
+ }
225
+
226
+ return (
227
+ <Tooltip key={action.key} title={renderTooltipContent(label, description)}>
228
+ <Button
229
+ type='default'
230
+ size='small'
231
+ loading={loadingAction === action.key}
232
+ aria-label={label}
233
+ className='adapter-account-manager__icon-button adapter-account-manager__header-action'
234
+ icon={<span className='material-symbols-rounded'>{icon}</span>}
235
+ onClick={async () => {
236
+ await onRunAction(action)
237
+ }}
238
+ />
239
+ </Tooltip>
240
+ )
241
+ })}
242
+ </div>
243
+ )
244
+ }
245
+
246
+ export const mergeAccounts = (
247
+ configured: Record<string, unknown>,
248
+ discovered: AdapterAccountInfo[],
249
+ defaultAccountKey?: string
250
+ ) => {
251
+ const merged = new Map<string, AdapterAccountInfo>()
252
+
253
+ Object.entries(configured).forEach(([key, entry]) => {
254
+ const configuredEntry = isRecord(entry) ? entry : {}
255
+ const title = typeof configuredEntry.title === 'string' ? configuredEntry.title.trim() : ''
256
+ const description = typeof configuredEntry.description === 'string' ? configuredEntry.description.trim() : ''
257
+ merged.set(key, {
258
+ key,
259
+ title: title !== '' ? title : key,
260
+ ...(description !== '' ? { description } : {}),
261
+ status: 'missing'
262
+ })
263
+ })
264
+
265
+ discovered.forEach((account) => {
266
+ const existing = merged.get(account.key)
267
+ merged.set(account.key, {
268
+ ...existing,
269
+ ...account
270
+ })
271
+ })
272
+
273
+ return [...merged.values()]
274
+ .map(account => ({
275
+ ...account,
276
+ isDefault: defaultAccountKey != null && defaultAccountKey !== ''
277
+ ? account.key === defaultAccountKey
278
+ : account.isDefault
279
+ }))
280
+ .sort(compareAccountInfo)
281
+ }
282
+
283
+ const AccountEditor = ({
284
+ adapterKey,
285
+ accountKey,
286
+ accountItemSchema,
287
+ value,
288
+ onChange,
289
+ t
290
+ }: {
291
+ adapterKey: string
292
+ accountKey: string
293
+ accountItemSchema?: ConfigUiObjectSchema
294
+ value: Record<string, unknown>
295
+ onChange: (nextValue: Record<string, unknown>) => void
296
+ t: TranslationFn
297
+ }) => {
298
+ if (accountItemSchema == null || accountItemSchema.fields.length === 0) {
299
+ return null
300
+ }
301
+
302
+ const editorSchema: ConfigUiObjectSchema = {
303
+ ...accountItemSchema,
304
+ fields: accountItemSchema.fields.map((field) => {
305
+ if (field.path.length === 1 && field.path[0] === 'description') {
306
+ return {
307
+ ...field,
308
+ type: 'multiline'
309
+ }
310
+ }
311
+
312
+ return field
313
+ })
314
+ }
315
+ const defaultAuthFilePath = `.ai/.local/adapters/${adapterKey}/accounts/${accountKey}/auth.json`
316
+
317
+ return (
318
+ <div className='adapter-account-manager__editor'>
319
+ <div className='adapter-account-manager__section-title'>
320
+ <span className='material-symbols-rounded'>tune</span>
321
+ <span>{t('config.accounts.settingsTitle', { defaultValue: 'Account settings' })}</span>
322
+ </div>
323
+ <SchemaObjectEditor
324
+ value={getConfiguredAccountEntry(value, accountKey)}
325
+ schema={editorSchema}
326
+ onChange={(nextEntry) => onChange(setConfiguredAccountEntry(value, accountKey, nextEntry))}
327
+ t={t}
328
+ resolveFieldLabel={(field, fallback) => getFieldLabel(t, 'adapterAccount', field.path, fallback)}
329
+ resolveFieldDescription={(field, fallback) => {
330
+ const translated = getFieldDescription(t, 'adapterAccount', field.path)
331
+ const baseDescription = translated !== '' ? translated : fallback
332
+ if (field.path.length === 1 && field.path[0] === 'authFile') {
333
+ const defaultLookupHint = t('config.accounts.authFileDefaultLookup', {
334
+ defaultValue: 'Leave empty to use {{path}}.',
335
+ path: defaultAuthFilePath
336
+ })
337
+
338
+ return [baseDescription, defaultLookupHint]
339
+ .map(item => item.trim())
340
+ .filter(item => item !== '')
341
+ .join(' ')
342
+ }
343
+
344
+ return baseDescription
345
+ }}
346
+ />
347
+ </div>
348
+ )
349
+ }
350
+
351
+ const AccountDetailView = ({
352
+ adapterKey,
353
+ accountKey,
354
+ accountItemSchema,
355
+ value,
356
+ onChange,
357
+ onChanged,
358
+ onRemoved,
359
+ t
360
+ }: {
361
+ adapterKey: string
362
+ accountKey: string
363
+ accountItemSchema?: ConfigUiObjectSchema
364
+ value: Record<string, unknown>
365
+ onChange: (nextValue: Record<string, unknown>) => void
366
+ onChanged: () => Promise<void>
367
+ onRemoved: () => void
368
+ t: TranslationFn
369
+ }) => {
370
+ const { message } = App.useApp()
371
+ const { data, isLoading, mutate } = useSWR(
372
+ `/api/adapters/${adapterKey}/accounts/${accountKey}`,
373
+ () => getAdapterAccountDetail(adapterKey, accountKey)
374
+ )
375
+ const [loadingAction, setLoadingAction] = useState<string>()
376
+ const detail = data?.account
377
+ const statusMeta = formatStatus(detail?.status, t)
378
+ const detailActions = (detail?.actions ?? []).filter(action => action.key !== 'refresh')
379
+ const quotaMetrics = detail?.quota?.metrics?.filter((metric) => {
380
+ if (typeof metric.value === 'string') return metric.value.trim() !== ''
381
+ return metric.value != null
382
+ }) ?? []
383
+
384
+ const handleRunAction = async (action: AdapterAccountActionDescriptor) => {
385
+ setLoadingAction(action.key)
386
+ try {
387
+ const result = await manageAdapterAccount(adapterKey, {
388
+ action: action.key,
389
+ account: accountKey,
390
+ refresh: action.key === 'refresh'
391
+ })
392
+ await onChanged()
393
+ if (action.key === 'remove') {
394
+ void message.success(result.message ?? t('config.accounts.actionSuccess.remove'))
395
+ onRemoved()
396
+ return
397
+ }
398
+
399
+ if (result.account != null) {
400
+ await mutate({ account: result.account }, { revalidate: false })
401
+ } else {
402
+ const next = await getAdapterAccountDetail(adapterKey, accountKey, { refresh: true })
403
+ await mutate(next, { revalidate: false })
404
+ }
405
+ void message.success(result.message ?? t(`config.accounts.actionSuccess.${action.key}`))
406
+ } catch (error) {
407
+ void message.error(getApiErrorMessage(error, t(`config.accounts.actionFailed.${action.key}`)))
408
+ } finally {
409
+ setLoadingAction(undefined)
410
+ }
411
+ }
412
+
413
+ return (
414
+ <div className='adapter-account-manager__detail'>
415
+ {isLoading && (
416
+ <div className='adapter-account-manager__state'>
417
+ <Spin size='small' />
418
+ </div>
419
+ )}
420
+
421
+ {!isLoading && detail == null && (
422
+ <Empty image={null} description={t('config.accounts.detailMissing')} />
423
+ )}
424
+
425
+ {detail != null && (
426
+ <div className='adapter-account-manager__detail-body'>
427
+ <div className='adapter-account-manager__hero'>
428
+ <div className='adapter-account-manager__hero-body'>
429
+ <div className='adapter-account-manager__hero-title-row'>
430
+ <div className='adapter-account-manager__hero-title'>{detail.title}</div>
431
+ <div className='adapter-account-manager__hero-meta'>
432
+ <div className='adapter-account-manager__hero-badges'>
433
+ <IconTag
434
+ color={statusMeta.color}
435
+ icon={statusMeta.icon}
436
+ label={statusMeta.label}
437
+ />
438
+ {detail.isDefault === true && (
439
+ <IconTag
440
+ icon='star'
441
+ label={t('config.accounts.default')}
442
+ />
443
+ )}
444
+ </div>
445
+ {detailActions.length > 0 && (
446
+ <AccountActionButtons
447
+ actions={detailActions}
448
+ loadingAction={loadingAction}
449
+ onRunAction={handleRunAction}
450
+ t={t}
451
+ />
452
+ )}
453
+ </div>
454
+ </div>
455
+ </div>
456
+ </div>
457
+
458
+ {quotaMetrics.length > 0 && (
459
+ <div className='adapter-account-manager__section'>
460
+ <div className='adapter-account-manager__section-title'>
461
+ <span className='material-symbols-rounded'>query_stats</span>
462
+ <span>{t('config.accounts.quotaTitle', { defaultValue: 'Quota' })}</span>
463
+ </div>
464
+ <div className='adapter-account-manager__metrics'>
465
+ {quotaMetrics.map(metric => (
466
+ <div key={metric.id} className='adapter-account-manager__metric'>
467
+ <div className='adapter-account-manager__metric-label'>
468
+ {metric.label}
469
+ </div>
470
+ <div className='adapter-account-manager__metric-value'>
471
+ {metric.value ?? '-'}
472
+ {(() => {
473
+ const percent = parsePercentMetricValue(metric.value)
474
+ if (percent == null) return null
475
+
476
+ return (
477
+ <span
478
+ className='adapter-account-manager__metric-ring'
479
+ aria-hidden='true'
480
+ style={{
481
+ background: `conic-gradient(${
482
+ getPercentRingColor(percent)
483
+ } ${percent}%, color-mix(in srgb, var(--border-color) 72%, transparent) 0)`
484
+ }}
485
+ >
486
+ <span className='adapter-account-manager__metric-ring-inner' />
487
+ </span>
488
+ )
489
+ })()}
490
+ </div>
491
+ {metric.description != null && metric.description.trim() !== '' && (
492
+ <div className='adapter-account-manager__metric-description'>{metric.description}</div>
493
+ )}
494
+ </div>
495
+ ))}
496
+ </div>
497
+ </div>
498
+ )}
499
+
500
+ <AccountEditor
501
+ adapterKey={adapterKey}
502
+ accountKey={accountKey}
503
+ accountItemSchema={accountItemSchema}
504
+ value={value}
505
+ onChange={onChange}
506
+ t={t}
507
+ />
508
+ </div>
509
+ )}
510
+ </div>
511
+ )
512
+ }
513
+
514
+ const AccountsOverviewCard = ({
515
+ accounts,
516
+ onOpenAccounts,
517
+ t
518
+ }: {
519
+ accounts: AdapterAccountInfo[]
520
+ onOpenAccounts: () => void
521
+ t: TranslationFn
522
+ }) => {
523
+ const readyCount = accounts.filter(account => account.status !== 'missing' && account.status !== 'error').length
524
+ const defaultAccount = accounts.find(account => account.isDefault === true)
525
+
526
+ return (
527
+ <div className='adapter-account-manager__overview'>
528
+ <button
529
+ type='button'
530
+ className='adapter-account-manager__overview-card config-view__field-row'
531
+ onClick={onOpenAccounts}
532
+ >
533
+ <div className='config-view__field-meta'>
534
+ <span className='material-symbols-rounded config-view__field-icon'>manage_accounts</span>
535
+ <div className='config-view__field-text'>
536
+ <div className='config-view__field-title'>{t('config.accounts.title')}</div>
537
+ <div className='config-view__field-desc adapter-account-manager__overview-meta'>
538
+ <span>{t('config.accounts.count', { count: accounts.length })}</span>
539
+ <span>{t('config.accounts.readyCount', { count: readyCount })}</span>
540
+ {defaultAccount != null && (
541
+ <span>{t('config.accounts.defaultHint', { account: defaultAccount.title })}</span>
542
+ )}
543
+ </div>
544
+ </div>
545
+ </div>
546
+ <div className='config-view__field-control adapter-account-manager__overview-control'>
547
+ <span className='material-symbols-rounded adapter-account-manager__overview-arrow'>chevron_right</span>
548
+ </div>
549
+ </button>
550
+ </div>
551
+ )
552
+ }
553
+
554
+ const AccountsListView = ({
555
+ accounts,
556
+ actions,
557
+ loadingAction,
558
+ onOpenAccount,
559
+ onRunAction,
560
+ currentDefaultAccount,
561
+ deletingAccountKey,
562
+ onToggleDefaultAccount,
563
+ onDeleteAccount,
564
+ t
565
+ }: {
566
+ accounts: AdapterAccountInfo[]
567
+ actions: AdapterAccountActionDescriptor[]
568
+ loadingAction?: string
569
+ onOpenAccount: (accountKey: string) => void
570
+ onRunAction: (action: AdapterAccountActionDescriptor) => void
571
+ currentDefaultAccount?: string
572
+ deletingAccountKey?: string
573
+ onToggleDefaultAccount: (accountKey: string) => void
574
+ onDeleteAccount: (accountKey: string) => Promise<void>
575
+ t: TranslationFn
576
+ }) => {
577
+ const [searchValue, setSearchValue] = useState('')
578
+ const normalizedSearch = normalizeText(searchValue)
579
+ const addAction = actions.find(action => action.key === 'add')
580
+ const filteredAccounts = useMemo(() => {
581
+ if (normalizedSearch === '') return accounts
582
+ return accounts.filter(account => {
583
+ const haystacks = [
584
+ account.title,
585
+ account.key,
586
+ account.description,
587
+ account.quota?.summary
588
+ ]
589
+ return haystacks.some(value => normalizeText(value).includes(normalizedSearch))
590
+ })
591
+ }, [accounts, normalizedSearch])
592
+
593
+ return (
594
+ <div className='adapter-account-manager'>
595
+ <div className='adapter-account-manager__header'>
596
+ <Input
597
+ allowClear
598
+ value={searchValue}
599
+ onChange={(event) => setSearchValue(event.target.value)}
600
+ className='adapter-account-manager__search'
601
+ placeholder={t('config.accounts.searchPlaceholder', { defaultValue: 'Search accounts' })}
602
+ prefix={<span className='material-symbols-rounded'>search</span>}
603
+ />
604
+ {addAction != null && (
605
+ <Tooltip
606
+ title={renderTooltipContent(getActionLabel(addAction, t), getActionDescription(addAction, t))}
607
+ >
608
+ <Button
609
+ size='small'
610
+ type='default'
611
+ loading={loadingAction === addAction.key}
612
+ aria-label={getActionLabel(addAction, t)}
613
+ className='adapter-account-manager__icon-button adapter-account-manager__header-action'
614
+ icon={<span className='material-symbols-rounded'>{ACCOUNT_ACTION_ICON[addAction.key]}</span>}
615
+ onClick={() => onRunAction(addAction)}
616
+ />
617
+ </Tooltip>
618
+ )}
619
+ </div>
620
+
621
+ {filteredAccounts.length === 0 && (
622
+ <Empty
623
+ image={null}
624
+ description={accounts.length === 0
625
+ ? t('config.accounts.empty')
626
+ : t('config.accounts.searchEmpty', { defaultValue: 'No matching accounts' })}
627
+ />
628
+ )}
629
+
630
+ {filteredAccounts.length > 0 && (
631
+ <div className='adapter-account-manager__list'>
632
+ {filteredAccounts.map((account) => {
633
+ const isDefault = currentDefaultAccount === account.key || account.isDefault === true
634
+ const showDeleteAction = account.status !== 'missing'
635
+
636
+ return (
637
+ <div key={account.key} className='adapter-account-manager__item'>
638
+ <button
639
+ type='button'
640
+ className='adapter-account-manager__item-trigger'
641
+ onClick={() => onOpenAccount(account.key)}
642
+ >
643
+ <div className='adapter-account-manager__item-main'>
644
+ <div className='adapter-account-manager__item-title'>{account.title}</div>
645
+ {account.quota?.summary != null && account.quota.summary !== '' && (
646
+ <div className='adapter-account-manager__item-description'>
647
+ <span className='material-symbols-rounded'>speed</span>
648
+ <span>{account.quota.summary}</span>
649
+ </div>
650
+ )}
651
+ </div>
652
+ </button>
653
+ <div className='adapter-account-manager__item-actions'>
654
+ <Tooltip
655
+ title={isDefault
656
+ ? t('config.accounts.rowActions.clearDefault', { defaultValue: 'Clear default account' })
657
+ : t('config.accounts.rowActions.setDefault', { defaultValue: 'Set as default account' })}
658
+ >
659
+ <Button
660
+ type='text'
661
+ size='small'
662
+ aria-label={isDefault
663
+ ? t('config.accounts.rowActions.clearDefault', { defaultValue: 'Clear default account' })
664
+ : t('config.accounts.rowActions.setDefault', { defaultValue: 'Set as default account' })}
665
+ className={`adapter-account-manager__row-action ${
666
+ isDefault ? 'adapter-account-manager__row-action--active' : ''
667
+ }`}
668
+ icon={<span className='material-symbols-rounded'>star</span>}
669
+ onClick={(event) => {
670
+ event.stopPropagation()
671
+ onToggleDefaultAccount(account.key)
672
+ }}
673
+ />
674
+ </Tooltip>
675
+ {showDeleteAction && (
676
+ <Popconfirm
677
+ title={t('config.accounts.deleteConfirmTitle', {
678
+ defaultValue: 'Delete the stored snapshot for {{account}}?',
679
+ account: account.title
680
+ })}
681
+ okText={t('common.confirm')}
682
+ cancelText={t('common.cancel')}
683
+ onConfirm={async (event) => {
684
+ event?.stopPropagation?.()
685
+ await onDeleteAccount(account.key)
686
+ }}
687
+ >
688
+ <Button
689
+ type='text'
690
+ size='small'
691
+ danger
692
+ loading={deletingAccountKey === account.key}
693
+ aria-label={t('config.accounts.rowActions.delete', { defaultValue: 'Delete account snapshot' })}
694
+ className='adapter-account-manager__row-action'
695
+ icon={<span className='material-symbols-rounded'>delete</span>}
696
+ onClick={(event) => {
697
+ event.stopPropagation()
698
+ }}
699
+ />
700
+ </Popconfirm>
701
+ )}
702
+ </div>
703
+ </div>
704
+ )
705
+ })}
706
+ </div>
707
+ )}
708
+ </div>
709
+ )
710
+ }
711
+
712
+ export const AdapterAccountsManager = ({
713
+ adapterKey,
714
+ value,
715
+ accountItemSchema,
716
+ nestedPath = [],
717
+ onChange,
718
+ onOpenNestedPath,
719
+ t
720
+ }: {
721
+ adapterKey: string
722
+ value: Record<string, unknown>
723
+ accountItemSchema?: ConfigUiObjectSchema
724
+ nestedPath?: string[]
725
+ onChange: (nextValue: Record<string, unknown>) => void
726
+ onOpenNestedPath: (nextPath: string[]) => void
727
+ t: TranslationFn
728
+ }) => {
729
+ const { message } = App.useApp()
730
+ const configuredDefaultAccount = typeof value.defaultAccount === 'string' && value.defaultAccount.trim() !== ''
731
+ ? value.defaultAccount.trim()
732
+ : undefined
733
+ const { data, isLoading, mutate } = useSWR(
734
+ `/api/adapters/${adapterKey}/accounts`,
735
+ () => getAdapterAccounts(adapterKey)
736
+ )
737
+ const [loadingAction, setLoadingAction] = useState<string>()
738
+ const [deletingAccountKey, setDeletingAccountKey] = useState<string>()
739
+ const configuredAccounts = useMemo(() => getConfiguredAccounts(value), [value])
740
+ const accounts = useMemo(
741
+ () => mergeAccounts(configuredAccounts, data?.accounts ?? [], configuredDefaultAccount),
742
+ [configuredAccounts, configuredDefaultAccount, data?.accounts]
743
+ )
744
+ const actionDescriptors = data?.actions ?? []
745
+ const isAccountsView = nestedPath[0] === 'accounts'
746
+ const activeAccountKey = isAccountsView ? nestedPath[1] : undefined
747
+
748
+ const refreshAccounts = async () => {
749
+ await mutate()
750
+ }
751
+
752
+ const handleToggleDefaultAccount = (accountKey: string) => {
753
+ const nextValue = { ...value }
754
+ if (configuredDefaultAccount === accountKey) {
755
+ delete nextValue.defaultAccount
756
+ } else {
757
+ nextValue.defaultAccount = accountKey
758
+ }
759
+ onChange(nextValue)
760
+ }
761
+
762
+ const handleRunListAction = async (action: AdapterAccountActionDescriptor) => {
763
+ if (action.key !== 'add') return
764
+
765
+ setLoadingAction(action.key)
766
+ try {
767
+ const result = await manageAdapterAccount(adapterKey, { action: 'add' })
768
+ await refreshAccounts()
769
+ void message.success(result.message ?? t('config.accounts.actionSuccess.add'))
770
+ if (result.accountKey != null && result.accountKey.trim() !== '') {
771
+ onOpenNestedPath(['accounts', result.accountKey])
772
+ } else {
773
+ onOpenNestedPath(['accounts'])
774
+ }
775
+ } catch (error) {
776
+ void message.error(getApiErrorMessage(error, t('config.accounts.actionFailed.add')))
777
+ } finally {
778
+ setLoadingAction(undefined)
779
+ }
780
+ }
781
+
782
+ const handleDeleteAccount = async (accountKey: string) => {
783
+ setDeletingAccountKey(accountKey)
784
+ try {
785
+ const result = await manageAdapterAccount(adapterKey, {
786
+ action: 'remove',
787
+ account: accountKey,
788
+ refresh: true
789
+ })
790
+ await refreshAccounts()
791
+ void message.success(result.message ?? t('config.accounts.actionSuccess.remove'))
792
+ } catch (error) {
793
+ void message.error(getApiErrorMessage(error, t('config.accounts.actionFailed.remove')))
794
+ } finally {
795
+ setDeletingAccountKey(undefined)
796
+ }
797
+ }
798
+
799
+ if (isLoading && accounts.length === 0) {
800
+ return (
801
+ <div className='adapter-account-manager__state'>
802
+ <Spin size='small' />
803
+ </div>
804
+ )
805
+ }
806
+
807
+ if (isAccountsView && activeAccountKey != null && activeAccountKey !== '') {
808
+ return (
809
+ <AccountDetailView
810
+ adapterKey={adapterKey}
811
+ accountKey={activeAccountKey}
812
+ accountItemSchema={accountItemSchema}
813
+ value={value}
814
+ onChange={onChange}
815
+ onChanged={refreshAccounts}
816
+ onRemoved={() => onOpenNestedPath(['accounts'])}
817
+ t={t}
818
+ />
819
+ )
820
+ }
821
+
822
+ if (isAccountsView) {
823
+ return (
824
+ <AccountsListView
825
+ accounts={accounts}
826
+ actions={actionDescriptors}
827
+ loadingAction={loadingAction}
828
+ onOpenAccount={(accountKey) => onOpenNestedPath(['accounts', accountKey])}
829
+ onRunAction={handleRunListAction}
830
+ currentDefaultAccount={configuredDefaultAccount}
831
+ deletingAccountKey={deletingAccountKey}
832
+ onToggleDefaultAccount={handleToggleDefaultAccount}
833
+ onDeleteAccount={handleDeleteAccount}
834
+ t={t}
835
+ />
836
+ )
837
+ }
838
+
839
+ return (
840
+ <AccountsOverviewCard
841
+ accounts={accounts}
842
+ onOpenAccounts={() => onOpenNestedPath(['accounts'])}
843
+ t={t}
844
+ />
845
+ )
846
+ }