@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.
Files changed (351) hide show
  1. package/AGENTS.md +4 -1
  2. package/dist/assets/{arc-CbOXL0l9.js → arc-1JbypnRY.js} +1 -1
  3. package/dist/assets/{blockDiagram-c4efeb88-CqxINvsS.js → blockDiagram-c4efeb88-jT5b9nId.js} +1 -1
  4. package/dist/assets/{c4Diagram-c83219d4-BKazU0hb.js → c4Diagram-c83219d4-KNPh-1ta.js} +1 -1
  5. package/dist/assets/channel-pzvjJnfd.js +1 -0
  6. package/dist/assets/{classDiagram-beda092f-fAFX5BpB.js → classDiagram-beda092f-vvXuOT3F.js} +1 -1
  7. package/dist/assets/{classDiagram-v2-2358418a-w1VkNGJj.js → classDiagram-v2-2358418a-vH4j7skr.js} +1 -1
  8. package/dist/assets/clone-CtWwIeUb.js +1 -0
  9. package/dist/assets/{createText-1719965b-CEinakVP.js → createText-1719965b-COeSYnf3.js} +1 -1
  10. package/dist/assets/{cssMode-DPqRki4y.js → cssMode-Doc7QRKn.js} +1 -1
  11. package/dist/assets/{edges-96097737-Cb0F1_3K.js → edges-96097737-D09v6R_r.js} +1 -1
  12. package/dist/assets/{erDiagram-0228fc6a-C-N2fx-J.js → erDiagram-0228fc6a-CbZ6rfxl.js} +1 -1
  13. package/dist/assets/{flowDb-c6c81e3f-D1Xz_8Gf.js → flowDb-c6c81e3f-CnZOzhdk.js} +1 -1
  14. package/dist/assets/{flowDiagram-50d868cf-DyPSZyAj.js → flowDiagram-50d868cf-jv-xWM2p.js} +1 -1
  15. package/dist/assets/flowDiagram-v2-4f6560a1-BiyrkIKs.js +1 -0
  16. package/dist/assets/{flowchart-elk-definition-6af322e1-Dr1DDXwE.js → flowchart-elk-definition-6af322e1-D8pf677G.js} +1 -1
  17. package/dist/assets/{freemarker2-C3DvPFaK.js → freemarker2-CL0o23Gj.js} +1 -1
  18. package/dist/assets/{ganttDiagram-a2739b55-DmvY1GRj.js → ganttDiagram-a2739b55-BAPQzC4u.js} +1 -1
  19. package/dist/assets/{gitGraphDiagram-82fe8481-CoXfPYYi.js → gitGraphDiagram-82fe8481-BI2x71m0.js} +1 -1
  20. package/dist/assets/{graph-BkDQy7Qt.js → graph-CZK4Bjpq.js} +1 -1
  21. package/dist/assets/{handlebars-BcTFdqjl.js → handlebars-CCG38Pg6.js} +1 -1
  22. package/dist/assets/{html-Dg-O6XFr.js → html-BddshTWG.js} +1 -1
  23. package/dist/assets/{htmlMode-B_wqYWvn.js → htmlMode-xKXiYcDz.js} +1 -1
  24. package/dist/assets/{index-5325376f-kxPTR3_e.js → index-5325376f-BWMTD8RU.js} +1 -1
  25. package/dist/assets/{index-wkhI4dr6.js → index-CBe7kDkV.js} +398 -377
  26. package/dist/assets/index-MWOwVzqE.css +32 -0
  27. package/dist/assets/{infoDiagram-8eee0895-BEvqkwPI.js → infoDiagram-8eee0895-CR78btIF.js} +1 -1
  28. package/dist/assets/{javascript-DhlOH8_z.js → javascript-BPlRHPjg.js} +1 -1
  29. package/dist/assets/{journeyDiagram-c64418c1-gKtLYmmp.js → journeyDiagram-c64418c1-DZRv6FKz.js} +1 -1
  30. package/dist/assets/{jsonMode-DxTbF9OD.js → jsonMode-BYLVfdkf.js} +1 -1
  31. package/dist/assets/{layout-CDaZEk6E.js → layout-BtudyPU2.js} +1 -1
  32. package/dist/assets/{line-DNRQu8iq.js → line-DgHXrIhS.js} +1 -1
  33. package/dist/assets/{linear-Cph9Z6_j.js → linear-DtBoKICx.js} +1 -1
  34. package/dist/assets/{liquid-ByZ6JgRG.js → liquid-DxBlJk0W.js} +1 -1
  35. package/dist/assets/{lspLanguageFeatures-DzvhkgnM.js → lspLanguageFeatures-DmKVpmeH.js} +1 -1
  36. package/dist/assets/{mdx-D8RGHTl6.js → mdx-DrScsd-w.js} +1 -1
  37. package/dist/assets/{mermaid.core-BgcryF__.js → mermaid.core-Cj_NJ_lZ.js} +4 -4
  38. package/dist/assets/{mindmap-definition-8da855dc-WrxK0FcB.js → mindmap-definition-8da855dc-CMXbwtsc.js} +1 -1
  39. package/dist/assets/{pieDiagram-a8764435-VsZBsiQy.js → pieDiagram-a8764435-Bd6rfpJv.js} +1 -1
  40. package/dist/assets/{python-CXVtk_cg.js → python-Crbi7B7n.js} +1 -1
  41. package/dist/assets/{quadrantDiagram-1e28029f-BVlgwOvU.js → quadrantDiagram-1e28029f-H-FdBQn6.js} +1 -1
  42. package/dist/assets/{razor-0tind7h2.js → razor-DHyrzIfE.js} +1 -1
  43. package/dist/assets/{requirementDiagram-08caed73-CpPMPoYp.js → requirementDiagram-08caed73-BSyCVDSG.js} +1 -1
  44. package/dist/assets/{sankeyDiagram-a04cb91d-Cm5nnRmc.js → sankeyDiagram-a04cb91d-S6E6BReg.js} +1 -1
  45. package/dist/assets/{sequenceDiagram-c5b8d532-DpMlJvJB.js → sequenceDiagram-c5b8d532-BMHY4KMj.js} +1 -1
  46. package/dist/assets/{stateDiagram-1ecb1508-DU1zc7vq.js → stateDiagram-1ecb1508-BMrMw6XR.js} +1 -1
  47. package/dist/assets/{stateDiagram-v2-c2b004d7-D-0RgmAp.js → stateDiagram-v2-c2b004d7-B5SIFUb_.js} +1 -1
  48. package/dist/assets/{styles-b4e223ce-BSO-yNWV.js → styles-b4e223ce-DqiXwnx3.js} +1 -1
  49. package/dist/assets/{styles-ca3715f6-CHnsn2Ro.js → styles-ca3715f6-jNxW2Db8.js} +1 -1
  50. package/dist/assets/{styles-d45a18b0-B-rVGjEq.js → styles-d45a18b0-C4nlfLcZ.js} +1 -1
  51. package/dist/assets/{svgDrawCommon-b86b1483-CA3Pl89f.js → svgDrawCommon-b86b1483-FIWFuZfb.js} +1 -1
  52. package/dist/assets/{timeline-definition-faaaa080-BcihLR6s.js → timeline-definition-faaaa080-CEQDoqdf.js} +1 -1
  53. package/dist/assets/{tsMode-D9GGa5Ur.js → tsMode-DnNy3rE9.js} +1 -1
  54. package/dist/assets/{typescript-BT9CK_EL.js → typescript-YBZ4vw5B.js} +1 -1
  55. package/dist/assets/{xml-DNO75J-T.js → xml-BJGGYD70.js} +1 -1
  56. package/dist/assets/{xychartDiagram-f5964ef8-DJTwe32X.js → xychartDiagram-f5964ef8-DUNOFOk0.js} +1 -1
  57. package/dist/assets/{yaml-7CVzhiP2.js → yaml-BMY4mu5s.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 +596 -28
  85. package/src/components/ConfigView.tsx +569 -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 +110 -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 +220 -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 +211 -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 +130 -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 +254 -123
  119. package/src/components/chat/NewSessionGuide.scss +274 -204
  120. package/src/components/chat/NewSessionGuide.tsx +46 -111
  121. package/src/components/chat/NewSessionGuideStarterList.tsx +190 -0
  122. package/src/components/chat/NewSessionGuideStarterSection.tsx +121 -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 +21 -6
  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 +29 -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 +257 -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 +85 -9
  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/@hooks/use-skills-cli-modal-controller.ts +157 -0
  244. package/src/components/knowledge-base/components/@hooks/use-skills-tab-actions.ts +63 -0
  245. package/src/components/knowledge-base/components/ActionButton.scss +30 -4
  246. package/src/components/knowledge-base/components/ActionButton.tsx +13 -3
  247. package/src/components/knowledge-base/components/CreateSkillModal.tsx +59 -0
  248. package/src/components/knowledge-base/components/EmptyState.scss +4 -2
  249. package/src/components/knowledge-base/components/EntitiesTab.tsx +20 -20
  250. package/src/components/knowledge-base/components/EntityList.scss +3 -0
  251. package/src/components/knowledge-base/components/FilterBar.scss +1 -0
  252. package/src/components/knowledge-base/components/FilterBar.tsx +12 -8
  253. package/src/components/knowledge-base/components/FlowsTab.tsx +20 -20
  254. package/src/components/knowledge-base/components/KnowledgeBaseHeader.tsx +7 -6
  255. package/src/components/knowledge-base/components/KnowledgeContentControls.tsx +35 -0
  256. package/src/components/knowledge-base/components/KnowledgeList.scss +14 -3
  257. package/src/components/knowledge-base/components/KnowledgeMobilePanel.tsx +122 -0
  258. package/src/components/knowledge-base/components/KnowledgeSidebar.tsx +97 -0
  259. package/src/components/knowledge-base/components/LoadingState.scss +2 -1
  260. package/src/components/knowledge-base/components/ProjectSkillsList.tsx +79 -0
  261. package/src/components/knowledge-base/components/RuleList.scss +3 -0
  262. package/src/components/knowledge-base/components/RulesTab.tsx +31 -30
  263. package/src/components/knowledge-base/components/SectionHeader.scss +13 -1
  264. package/src/components/knowledge-base/components/SectionHeader.tsx +5 -3
  265. package/src/components/knowledge-base/components/SkillArchiveInput.tsx +43 -0
  266. package/src/components/knowledge-base/components/SkillHubResultItem.tsx +112 -0
  267. package/src/components/knowledge-base/components/SkillMarketResults.tsx +98 -0
  268. package/src/components/knowledge-base/components/SkillMarketView.tsx +198 -0
  269. package/src/components/knowledge-base/components/SkillMarketView.types.ts +28 -0
  270. package/src/components/knowledge-base/components/SkillRegistryErrors.tsx +21 -0
  271. package/src/components/knowledge-base/components/SkillRegistryModal.tsx +74 -0
  272. package/src/components/knowledge-base/components/SkillsCliModal.tsx +154 -0
  273. package/src/components/knowledge-base/components/SkillsTab.scss +424 -0
  274. package/src/components/knowledge-base/components/SkillsTab.tsx +166 -35
  275. package/src/components/knowledge-base/components/SkillsTabActions.tsx +88 -0
  276. package/src/components/knowledge-base/components/SpecList.scss +3 -0
  277. package/src/components/knowledge-base/components/TabContent.scss +4 -3
  278. package/src/components/knowledge-base/components/skill-hub-utils.ts +108 -0
  279. package/src/components/knowledge-base/components/use-skill-market-filters.ts +37 -0
  280. package/src/components/knowledge-base/components/use-skill-market-query-input.ts +44 -0
  281. package/src/components/knowledge-base/components/use-skill-market-search.ts +49 -0
  282. package/src/components/knowledge-base/components/use-skill-registry-modal.ts +68 -0
  283. package/src/components/monaco/monaco-runtime.ts +44 -0
  284. package/src/components/monaco/use-monaco-theme.ts +63 -0
  285. package/src/components/nav-rail-account-actions.tsx +104 -0
  286. package/src/components/server-connection/ServerConnectionGate.scss +356 -0
  287. package/src/components/server-connection/ServerConnectionGate.tsx +238 -0
  288. package/src/components/server-connection/ServerConnectionProfileModal.tsx +145 -0
  289. package/src/components/server-connection/ServerConnectionProfiles.tsx +113 -0
  290. package/src/components/server-connection/ServerConnectionUrlInput.tsx +85 -0
  291. package/src/components/sidebar/SidebarHeader.scss +5 -41
  292. package/src/components/sidebar/SidebarHeader.tsx +117 -91
  293. package/src/components/sidebar/SidebarHeaderSearchActions.tsx +24 -28
  294. package/src/components/sidebar/sidebar-search-visibility.ts +18 -0
  295. package/src/components/sidebar-list/SidebarListHeader.scss +246 -0
  296. package/src/components/sidebar-list/SidebarListHeader.tsx +146 -0
  297. package/src/components/workspace/ContextFilePicker.scss +9 -26
  298. package/src/components/workspace/ContextFilePicker.tsx +31 -113
  299. package/src/components/workspace/context-file-types.ts +36 -0
  300. package/src/components/workspace/project-file-tree/ProjectFileTree.scss +298 -0
  301. package/src/components/workspace/project-file-tree/ProjectFileTree.tsx +138 -0
  302. package/src/components/workspace/project-file-tree/ProjectFileTreeRow.tsx +167 -0
  303. package/src/components/workspace/project-file-tree/ProjectFileTreeRowContextMenu.tsx +106 -0
  304. package/src/components/workspace/project-file-tree/ProjectFileTreeRows.tsx +139 -0
  305. package/src/components/workspace/project-file-tree/project-file-tree-helpers.ts +101 -0
  306. package/src/components/workspace/project-file-tree/project-file-tree-icons.ts +93 -0
  307. package/src/components/workspace/project-file-tree/project-file-tree-types.ts +27 -0
  308. package/src/components/workspace/project-file-tree/use-project-file-tree-data.ts +197 -0
  309. package/src/components/workspace/project-file-tree/use-project-file-tree-selection.ts +144 -0
  310. package/src/hooks/chat/chat-session-target.ts +69 -0
  311. package/src/hooks/chat/chat-session-workspace-draft.ts +11 -4
  312. package/src/hooks/chat/interaction-state.ts +1 -0
  313. package/src/hooks/chat/optimistic-session-creation.ts +189 -0
  314. package/src/hooks/chat/use-chat-adapter-account-selection.tsx +156 -0
  315. package/src/hooks/chat/use-chat-route-bottom-panel.ts +181 -0
  316. package/src/hooks/chat/use-chat-route-deep-link-view.ts +33 -0
  317. package/src/hooks/chat/use-chat-session-actions.ts +259 -65
  318. package/src/hooks/chat/use-chat-session-messages.ts +71 -4
  319. package/src/hooks/chat/use-chat-session.ts +36 -1
  320. package/src/hooks/chat/workspace-file-panel-state.ts +43 -0
  321. package/src/hooks/session-subscription-cache.ts +25 -0
  322. package/src/hooks/use-chat-layout-query-state.ts +29 -0
  323. package/src/hooks/use-sender-header-query-state.ts +35 -0
  324. package/src/hooks/use-session-subscription.ts +17 -8
  325. package/src/hooks/useQueryParams.ts +91 -23
  326. package/src/i18n-resources.ts +44 -0
  327. package/src/i18n.ts +21 -6
  328. package/src/main.tsx +8 -0
  329. package/src/pwa.ts +46 -0
  330. package/src/resources/locales/en.json +741 -24
  331. package/src/resources/locales/zh.json +743 -26
  332. package/src/routes/ChatRoute.scss +110 -7
  333. package/src/routes/ChatRoute.tsx +11 -165
  334. package/src/routes/ChatRouteBottomPanel.tsx +47 -0
  335. package/src/routes/ChatRouteView.tsx +199 -0
  336. package/src/runtime-config.ts +155 -2
  337. package/src/server-connection-history.ts +179 -0
  338. package/src/store/index.ts +151 -3
  339. package/src/styles/global.scss +3 -0
  340. package/src/utils/mobile-viewport.ts +67 -0
  341. package/src/version-compatibility.ts +37 -0
  342. package/src/vite-env.d.ts +9 -0
  343. package/src/ws.ts +20 -9
  344. package/vite.config.ts +23 -1
  345. package/dist/assets/channel-Dnopc5A6.js +0 -1
  346. package/dist/assets/clone-sQthahUA.js +0 -1
  347. package/dist/assets/flowDiagram-v2-4f6560a1-OazrdWQO.js +0 -1
  348. package/dist/assets/index-o93dlo92.css +0 -32
  349. package/src/components/chat/NewSessionGuideCompactPanel.tsx +0 -130
  350. package/src/components/chat/NewSessionGuideGrid.tsx +0 -141
  351. package/src/components/chat/sender/@components/reference-actions/ReferencePermissionActionsPopover.tsx +0 -114
@@ -1,3 +1,5 @@
1
+ import type { TranslationFn } from './configUtils'
2
+
1
3
  export type FieldValueType =
2
4
  | 'string'
3
5
  | 'number'
@@ -7,6 +9,7 @@ export type FieldValueType =
7
9
  | 'json'
8
10
  | 'multiline'
9
11
  | 'record'
12
+ | 'detailCollection'
10
13
  | 'shortcut'
11
14
 
12
15
  export type RecordKind = 'json' | 'modelServices' | 'mcpServers' | 'boolean' | 'keyValue' | 'channels'
@@ -26,7 +29,7 @@ export interface FieldSpec {
26
29
  placeholderKey?: string
27
30
  labelKey?: string
28
31
  descriptionKey?: string
29
- group?: 'base' | 'permissions' | 'env' | 'items'
32
+ group?: string
30
33
  recordKind?: RecordKind
31
34
  sensitive?: boolean
32
35
  hidden?: boolean
@@ -36,16 +39,528 @@ export interface FieldSpec {
36
39
  descKey?: string
37
40
  togglePath?: string[]
38
41
  }
42
+ detailCollection?: DetailCollectionSpec
43
+ }
44
+
45
+ export interface DetailCollectionContext {
46
+ mergedModelServices: Record<string, unknown>
47
+ mergedAdapters: Record<string, unknown>
48
+ t: TranslationFn
49
+ }
50
+
51
+ export interface DetailCollectionBooleanControlSpec {
52
+ kind: 'boolean'
53
+ path: string[]
54
+ checkedValue?: boolean
55
+ labelKey?: string
56
+ }
57
+
58
+ interface DetailCollectionBaseSpec {
59
+ detailKind?: 'recommendedModels' | 'mcpServer'
60
+ itemFields?: FieldSpec[]
61
+ summaryControls?: DetailCollectionBooleanControlSpec[]
62
+ getItemTitle: (
63
+ item: Record<string, unknown>,
64
+ itemKey: string,
65
+ itemIndex: number,
66
+ context: DetailCollectionContext
67
+ ) => string
68
+ getItemSubtitle?: (
69
+ item: Record<string, unknown>,
70
+ itemKey: string,
71
+ itemIndex: number,
72
+ context: DetailCollectionContext
73
+ ) => string | undefined
74
+ getItemDescription?: (
75
+ item: Record<string, unknown>,
76
+ itemKey: string,
77
+ itemIndex: number,
78
+ context: DetailCollectionContext
79
+ ) => string | undefined
80
+ getBreadcrumbLabel?: (
81
+ item: Record<string, unknown>,
82
+ itemKey: string,
83
+ itemIndex: number,
84
+ context: DetailCollectionContext
85
+ ) => string
39
86
  }
40
87
 
41
- export const configGroupMeta: Record<string, Record<string, { labelKey: string }>> = {
88
+ export interface DetailListCollectionSpec extends DetailCollectionBaseSpec {
89
+ collectionKind: 'list'
90
+ createItem: () => Record<string, unknown>
91
+ getMergeKey?: (item: Record<string, unknown>) => string | undefined
92
+ keyPlaceholderKey?: never
93
+ }
94
+
95
+ export interface DetailRecordCollectionSpec extends DetailCollectionBaseSpec {
96
+ collectionKind: 'record'
97
+ itemKeys: string[]
98
+ createItem?: (itemKey: string) => Record<string, unknown>
99
+ keyPlaceholderKey?: never
100
+ }
101
+
102
+ export interface DetailRecordMapCollectionSpec extends DetailCollectionBaseSpec {
103
+ collectionKind: 'recordMap'
104
+ keyPlaceholderKey?: string
105
+ createItem?: (itemKey: string, itemKind?: string) => Record<string, unknown>
106
+ }
107
+
108
+ export type DetailCollectionSpec =
109
+ | DetailListCollectionSpec
110
+ | DetailRecordCollectionSpec
111
+ | DetailRecordMapCollectionSpec
112
+
113
+ export interface ConfigGroupMeta {
114
+ labelKey: string
115
+ collapsible?: boolean
116
+ defaultExpanded?: boolean
117
+ }
118
+
119
+ export const configGroupMeta: Record<string, Record<string, ConfigGroupMeta>> = {
42
120
  general: {
121
+ base: {
122
+ labelKey: 'config.sectionGroups.base',
123
+ collapsible: true,
124
+ defaultExpanded: true
125
+ },
126
+ models: {
127
+ labelKey: 'config.sectionGroups.models',
128
+ collapsible: true,
129
+ defaultExpanded: true
130
+ },
131
+ advanced: {
132
+ labelKey: 'config.sectionGroups.advanced',
133
+ collapsible: true,
134
+ defaultExpanded: false
135
+ },
136
+ permissions: {
137
+ labelKey: 'config.sectionGroups.permissions',
138
+ collapsible: true,
139
+ defaultExpanded: true
140
+ },
141
+ env: {
142
+ labelKey: 'config.sectionGroups.env',
143
+ collapsible: true,
144
+ defaultExpanded: false
145
+ },
43
146
  items: {
44
- labelKey: 'config.fields.general.notifications.label'
147
+ labelKey: 'config.fields.general.notifications.label',
148
+ collapsible: true,
149
+ defaultExpanded: true
150
+ }
151
+ },
152
+ conversation: {
153
+ defaults: {
154
+ labelKey: 'config.sectionGroups.base',
155
+ collapsible: true,
156
+ defaultExpanded: true
157
+ },
158
+ presets: {
159
+ labelKey: 'config.fields.conversation.startupPresets.label',
160
+ collapsible: true,
161
+ defaultExpanded: true
162
+ },
163
+ actions: {
164
+ labelKey: 'config.fields.conversation.builtinActions.label',
165
+ collapsible: true,
166
+ defaultExpanded: true
45
167
  }
46
168
  }
47
169
  }
48
170
 
171
+ export const configGroupOrder: Record<string, string[]> = {
172
+ general: ['base', 'models', 'permissions', 'env', 'items', 'advanced', 'default'],
173
+ conversation: ['defaults', 'presets', 'actions', 'default']
174
+ }
175
+
176
+ const notificationEventDetailFields: FieldSpec[] = [
177
+ {
178
+ path: ['title'],
179
+ type: 'string',
180
+ defaultValue: '',
181
+ icon: 'title',
182
+ labelKey: 'config.fields.general.notifications.events.item.title.label',
183
+ descriptionKey: 'config.fields.general.notifications.events.item.title.desc'
184
+ },
185
+ {
186
+ path: ['description'],
187
+ type: 'multiline',
188
+ defaultValue: '',
189
+ icon: 'notes',
190
+ labelKey: 'config.fields.general.notifications.events.item.description.label',
191
+ descriptionKey: 'config.fields.general.notifications.events.item.description.desc'
192
+ },
193
+ {
194
+ path: ['sound'],
195
+ type: 'string',
196
+ defaultValue: '',
197
+ icon: 'volume_up',
198
+ labelKey: 'config.fields.general.notifications.events.item.sound.label',
199
+ descriptionKey: 'config.fields.general.notifications.events.item.sound.desc'
200
+ }
201
+ ]
202
+
203
+ const modelServiceDetailFields: FieldSpec[] = [
204
+ {
205
+ path: ['title'],
206
+ type: 'string',
207
+ defaultValue: '',
208
+ icon: 'title',
209
+ labelKey: 'config.fields.modelServices.item.title.label',
210
+ descriptionKey: 'config.fields.modelServices.item.title.desc'
211
+ },
212
+ {
213
+ path: ['description'],
214
+ type: 'multiline',
215
+ defaultValue: '',
216
+ icon: 'notes',
217
+ labelKey: 'config.fields.modelServices.item.description.label',
218
+ descriptionKey: 'config.fields.modelServices.item.description.desc'
219
+ },
220
+ {
221
+ path: ['apiBaseUrl'],
222
+ type: 'string',
223
+ defaultValue: '',
224
+ icon: 'link',
225
+ labelKey: 'config.fields.modelServices.item.apiBaseUrl.label',
226
+ descriptionKey: 'config.fields.modelServices.item.apiBaseUrl.desc'
227
+ },
228
+ {
229
+ path: ['apiKey'],
230
+ type: 'string',
231
+ defaultValue: '',
232
+ icon: 'key',
233
+ sensitive: true,
234
+ labelKey: 'config.fields.modelServices.item.apiKey.label',
235
+ descriptionKey: 'config.fields.modelServices.item.apiKey.desc'
236
+ },
237
+ {
238
+ path: ['models'],
239
+ type: 'string[]',
240
+ defaultValue: [],
241
+ icon: 'view_list',
242
+ labelKey: 'config.fields.modelServices.item.models.label',
243
+ descriptionKey: 'config.fields.modelServices.item.models.desc'
244
+ },
245
+ {
246
+ path: ['timeoutMs'],
247
+ type: 'number',
248
+ defaultValue: undefined,
249
+ icon: 'timer',
250
+ labelKey: 'config.fields.modelServices.item.timeoutMs.label',
251
+ descriptionKey: 'config.fields.modelServices.item.timeoutMs.desc'
252
+ },
253
+ {
254
+ path: ['maxOutputTokens'],
255
+ type: 'number',
256
+ defaultValue: undefined,
257
+ icon: 'numbers',
258
+ labelKey: 'config.fields.modelServices.item.maxOutputTokens.label',
259
+ descriptionKey: 'config.fields.modelServices.item.maxOutputTokens.desc'
260
+ },
261
+ {
262
+ path: ['extra'],
263
+ type: 'json',
264
+ defaultValue: {},
265
+ icon: 'account_tree',
266
+ labelKey: 'config.fields.modelServices.item.extra.label',
267
+ descriptionKey: 'config.fields.modelServices.item.extra.desc'
268
+ }
269
+ ]
270
+
271
+ const conversationStarterModeOptions: FieldSpec['options'] = [
272
+ { value: 'default', label: 'config.options.conversationStarterMode.default' },
273
+ { value: 'workspace', label: 'config.options.conversationStarterMode.workspace' },
274
+ { value: 'agent', label: 'config.options.conversationStarterMode.agent' },
275
+ { value: 'entity', label: 'config.options.conversationStarterMode.entity' },
276
+ { value: 'spec', label: 'config.options.conversationStarterMode.spec' }
277
+ ]
278
+
279
+ const conversationStarterEffortOptions: FieldSpec['options'] = [
280
+ { value: 'default', label: 'config.options.effort.default' },
281
+ { value: 'low', label: 'config.options.effort.low' },
282
+ { value: 'medium', label: 'config.options.effort.medium' },
283
+ { value: 'high', label: 'config.options.effort.high' },
284
+ { value: 'max', label: 'config.options.effort.max' }
285
+ ]
286
+
287
+ const conversationStarterPermissionModeOptions: FieldSpec['options'] = [
288
+ { value: 'default', label: 'config.options.permissionMode.default' },
289
+ { value: 'acceptEdits', label: 'config.options.permissionMode.acceptEdits' },
290
+ { value: 'plan', label: 'config.options.permissionMode.plan' },
291
+ { value: 'dontAsk', label: 'config.options.permissionMode.dontAsk' },
292
+ { value: 'bypassPermissions', label: 'config.options.permissionMode.bypassPermissions' }
293
+ ]
294
+
295
+ const conversationStarterBranchKindOptions: FieldSpec['options'] = [
296
+ { value: 'local', label: 'config.options.branchKind.local' },
297
+ { value: 'remote', label: 'config.options.branchKind.remote' }
298
+ ]
299
+
300
+ const conversationStarterBranchModeOptions: FieldSpec['options'] = [
301
+ { value: 'checkout', label: 'config.options.branchMode.checkout' },
302
+ { value: 'create', label: 'config.options.branchMode.create' }
303
+ ]
304
+
305
+ const conversationStarterDetailFields: FieldSpec[] = [
306
+ {
307
+ path: ['title'],
308
+ type: 'string',
309
+ defaultValue: '',
310
+ icon: 'title',
311
+ labelKey: 'config.fields.conversation.starterItem.title.label',
312
+ descriptionKey: 'config.fields.conversation.starterItem.title.desc'
313
+ },
314
+ {
315
+ path: ['description'],
316
+ type: 'multiline',
317
+ defaultValue: '',
318
+ icon: 'notes',
319
+ labelKey: 'config.fields.conversation.starterItem.description.label',
320
+ descriptionKey: 'config.fields.conversation.starterItem.description.desc'
321
+ },
322
+ {
323
+ path: ['icon'],
324
+ type: 'string',
325
+ defaultValue: '',
326
+ icon: 'palette',
327
+ labelKey: 'config.fields.conversation.starterItem.icon.label',
328
+ descriptionKey: 'config.fields.conversation.starterItem.icon.desc'
329
+ },
330
+ {
331
+ path: ['mode'],
332
+ type: 'select',
333
+ defaultValue: 'default',
334
+ icon: 'category',
335
+ options: conversationStarterModeOptions,
336
+ labelKey: 'config.fields.conversation.starterItem.mode.label',
337
+ descriptionKey: 'config.fields.conversation.starterItem.mode.desc'
338
+ },
339
+ {
340
+ path: ['target'],
341
+ type: 'string',
342
+ defaultValue: '',
343
+ icon: 'label',
344
+ labelKey: 'config.fields.conversation.starterItem.target.label',
345
+ descriptionKey: 'config.fields.conversation.starterItem.target.desc'
346
+ },
347
+ {
348
+ path: ['targetLabel'],
349
+ type: 'string',
350
+ defaultValue: '',
351
+ icon: 'title',
352
+ labelKey: 'config.fields.conversation.starterItem.targetLabel.label',
353
+ descriptionKey: 'config.fields.conversation.starterItem.targetLabel.desc'
354
+ },
355
+ {
356
+ path: ['targetDescription'],
357
+ type: 'multiline',
358
+ defaultValue: '',
359
+ icon: 'info',
360
+ labelKey: 'config.fields.conversation.starterItem.targetDescription.label',
361
+ descriptionKey: 'config.fields.conversation.starterItem.targetDescription.desc'
362
+ },
363
+ {
364
+ path: ['model'],
365
+ type: 'string',
366
+ defaultValue: '',
367
+ icon: 'model_training',
368
+ labelKey: 'config.fields.conversation.starterItem.model.label',
369
+ descriptionKey: 'config.fields.conversation.starterItem.model.desc'
370
+ },
371
+ {
372
+ path: ['adapter'],
373
+ type: 'string',
374
+ defaultValue: '',
375
+ icon: 'settings_input_component',
376
+ labelKey: 'config.fields.conversation.starterItem.adapter.label',
377
+ descriptionKey: 'config.fields.conversation.starterItem.adapter.desc'
378
+ },
379
+ {
380
+ path: ['account'],
381
+ type: 'string',
382
+ defaultValue: '',
383
+ icon: 'person',
384
+ labelKey: 'config.fields.conversation.starterItem.account.label',
385
+ descriptionKey: 'config.fields.conversation.starterItem.account.desc'
386
+ },
387
+ {
388
+ path: ['effort'],
389
+ type: 'select',
390
+ defaultValue: 'default',
391
+ icon: 'psychology',
392
+ options: conversationStarterEffortOptions,
393
+ labelKey: 'config.fields.conversation.starterItem.effort.label',
394
+ descriptionKey: 'config.fields.conversation.starterItem.effort.desc'
395
+ },
396
+ {
397
+ path: ['permissionMode'],
398
+ type: 'select',
399
+ defaultValue: 'default',
400
+ icon: 'policy',
401
+ options: conversationStarterPermissionModeOptions,
402
+ labelKey: 'config.fields.conversation.starterItem.permissionMode.label',
403
+ descriptionKey: 'config.fields.conversation.starterItem.permissionMode.desc'
404
+ },
405
+ {
406
+ path: ['worktree', 'create'],
407
+ type: 'boolean',
408
+ defaultValue: false,
409
+ icon: 'account_tree',
410
+ labelKey: 'config.fields.conversation.starterItem.worktreeCreate.label',
411
+ descriptionKey: 'config.fields.conversation.starterItem.worktreeCreate.desc'
412
+ },
413
+ {
414
+ path: ['worktree', 'environment'],
415
+ type: 'string',
416
+ defaultValue: '',
417
+ icon: 'deployed_code',
418
+ labelKey: 'config.fields.conversation.starterItem.worktreeEnvironment.label',
419
+ descriptionKey: 'config.fields.conversation.starterItem.worktreeEnvironment.desc'
420
+ },
421
+ {
422
+ path: ['worktree', 'branch', 'name'],
423
+ type: 'string',
424
+ defaultValue: '',
425
+ icon: 'call_split',
426
+ labelKey: 'config.fields.conversation.starterItem.branchName.label',
427
+ descriptionKey: 'config.fields.conversation.starterItem.branchName.desc'
428
+ },
429
+ {
430
+ path: ['worktree', 'branch', 'kind'],
431
+ type: 'select',
432
+ defaultValue: '',
433
+ icon: 'fork_right',
434
+ options: conversationStarterBranchKindOptions,
435
+ labelKey: 'config.fields.conversation.starterItem.branchKind.label',
436
+ descriptionKey: 'config.fields.conversation.starterItem.branchKind.desc'
437
+ },
438
+ {
439
+ path: ['worktree', 'branch', 'mode'],
440
+ type: 'select',
441
+ defaultValue: '',
442
+ icon: 'alt_route',
443
+ options: conversationStarterBranchModeOptions,
444
+ labelKey: 'config.fields.conversation.starterItem.branchMode.label',
445
+ descriptionKey: 'config.fields.conversation.starterItem.branchMode.desc'
446
+ },
447
+ {
448
+ path: ['prompt'],
449
+ type: 'multiline',
450
+ defaultValue: '',
451
+ icon: 'chat',
452
+ labelKey: 'config.fields.conversation.starterItem.prompt.label',
453
+ descriptionKey: 'config.fields.conversation.starterItem.prompt.desc'
454
+ },
455
+ {
456
+ path: ['files'],
457
+ type: 'string[]',
458
+ defaultValue: [],
459
+ icon: 'draft',
460
+ labelKey: 'config.fields.conversation.starterItem.files.label',
461
+ descriptionKey: 'config.fields.conversation.starterItem.files.desc'
462
+ },
463
+ {
464
+ path: ['rules'],
465
+ type: 'string[]',
466
+ defaultValue: [],
467
+ icon: 'gavel',
468
+ labelKey: 'config.fields.conversation.starterItem.rules.label',
469
+ descriptionKey: 'config.fields.conversation.starterItem.rules.desc'
470
+ },
471
+ {
472
+ path: ['skills'],
473
+ type: 'string[]',
474
+ defaultValue: [],
475
+ icon: 'school',
476
+ labelKey: 'config.fields.conversation.starterItem.skills.label',
477
+ descriptionKey: 'config.fields.conversation.starterItem.skills.desc'
478
+ }
479
+ ]
480
+
481
+ const pluginInstanceDetailFields: FieldSpec[] = [
482
+ {
483
+ path: ['id'],
484
+ type: 'string',
485
+ defaultValue: '',
486
+ icon: 'extension'
487
+ },
488
+ {
489
+ path: ['enabled'],
490
+ type: 'boolean',
491
+ defaultValue: true,
492
+ icon: 'toggle_on',
493
+ labelKey: 'config.fields.plugins.enabled.label',
494
+ descriptionKey: 'config.fields.plugins.enabled.desc'
495
+ },
496
+ {
497
+ path: ['scope'],
498
+ type: 'string',
499
+ defaultValue: '',
500
+ icon: 'label'
501
+ },
502
+ {
503
+ path: ['options'],
504
+ type: 'json',
505
+ defaultValue: {},
506
+ icon: 'tune'
507
+ },
508
+ {
509
+ path: ['children'],
510
+ type: 'json',
511
+ defaultValue: [],
512
+ icon: 'device_hub'
513
+ }
514
+ ]
515
+
516
+ const pluginMarketplaceDetailFields: FieldSpec[] = [
517
+ {
518
+ path: ['type'],
519
+ type: 'string',
520
+ defaultValue: 'claude-code',
521
+ icon: 'category'
522
+ },
523
+ {
524
+ path: ['enabled'],
525
+ type: 'boolean',
526
+ defaultValue: true,
527
+ icon: 'toggle_on',
528
+ labelKey: 'config.fields.plugins.enabled.label',
529
+ descriptionKey: 'config.fields.plugins.enabled.desc'
530
+ },
531
+ {
532
+ path: ['syncOnRun'],
533
+ type: 'boolean',
534
+ defaultValue: false,
535
+ icon: 'sync'
536
+ },
537
+ {
538
+ path: ['plugins'],
539
+ type: 'json',
540
+ defaultValue: {},
541
+ icon: 'extension'
542
+ },
543
+ {
544
+ path: ['options'],
545
+ type: 'json',
546
+ defaultValue: {},
547
+ icon: 'store'
548
+ }
549
+ ]
550
+
551
+ const notificationEventKeys = ['completed', 'failed', 'terminated', 'waiting_input']
552
+ const getNotificationEventLabel = (t: TranslationFn, itemKey: string) => {
553
+ const labelKey = `config.fields.general.notifications.events.${itemKey}.label`
554
+ const translated = t(labelKey)
555
+ return translated === labelKey ? itemKey : translated
556
+ }
557
+
558
+ const getNotificationEventDescription = (t: TranslationFn, itemKey: string) => {
559
+ const descKey = `config.fields.general.notifications.events.${itemKey}.desc`
560
+ const translated = t(descKey, { defaultValue: '' })
561
+ return translated === descKey ? '' : translated
562
+ }
563
+
49
564
  export const configSchema: Record<string, FieldSpec[]> = {
50
565
  general: [
51
566
  { path: ['baseDir'], type: 'string', defaultValue: '.ai', icon: 'folder', group: 'base' },
@@ -54,7 +569,7 @@ export const configSchema: Record<string, FieldSpec[]> = {
54
569
  type: 'select',
55
570
  defaultValue: '',
56
571
  icon: 'psychology',
57
- group: 'base',
572
+ group: 'models',
58
573
  options: [
59
574
  { value: 'low', label: 'config.options.effort.low' },
60
575
  { value: 'medium', label: 'config.options.effort.medium' },
@@ -62,10 +577,60 @@ export const configSchema: Record<string, FieldSpec[]> = {
62
577
  { value: 'max', label: 'config.options.effort.max' }
63
578
  ]
64
579
  },
65
- { path: ['defaultAdapter'], type: 'select', defaultValue: '', icon: 'settings_input_component', group: 'base' },
66
- { path: ['defaultModelService'], type: 'select', defaultValue: '', icon: 'hub', group: 'base' },
67
- { path: ['defaultModel'], type: 'select', defaultValue: '', icon: 'model_training', group: 'base' },
68
- { path: ['recommendedModels'], type: 'json', defaultValue: [], icon: 'stars', group: 'base' },
580
+ {
581
+ path: ['defaultAdapter'],
582
+ type: 'select',
583
+ defaultValue: '',
584
+ icon: 'settings_input_component',
585
+ group: 'models'
586
+ },
587
+ { path: ['defaultModelService'], type: 'select', defaultValue: '', icon: 'hub', group: 'models' },
588
+ { path: ['defaultModel'], type: 'select', defaultValue: '', icon: 'model_training', group: 'models' },
589
+ {
590
+ path: ['recommendedModels'],
591
+ type: 'detailCollection',
592
+ defaultValue: [],
593
+ icon: 'stars',
594
+ group: 'models',
595
+ detailCollection: {
596
+ collectionKind: 'list',
597
+ detailKind: 'recommendedModels',
598
+ createItem: () => ({
599
+ model: '',
600
+ placement: 'modelSelector'
601
+ }),
602
+ getItemTitle: (item, _itemKey, itemIndex, { t }) => {
603
+ const title = typeof item.title === 'string' ? item.title.trim() : ''
604
+ if (title !== '') return title
605
+ const model = typeof item.model === 'string' ? item.model.trim() : ''
606
+ if (model !== '') return model
607
+ return `${t('config.fields.general.recommendedModels.label')} #${itemIndex + 1}`
608
+ },
609
+ getItemSubtitle: (item, _itemKey, _itemIndex, { t }) => {
610
+ const parts: string[] = []
611
+ const service = typeof item.service === 'string' ? item.service.trim() : ''
612
+ const model = typeof item.model === 'string' ? item.model.trim() : ''
613
+ const placement = typeof item.placement === 'string'
614
+ ? t(`config.options.recommendedModels.${item.placement}`, { defaultValue: item.placement })
615
+ : ''
616
+ if (service !== '') parts.push(service)
617
+ if (model !== '') parts.push(model)
618
+ if (placement !== '') parts.push(placement)
619
+ return parts.length > 0 ? parts.join(' · ') : undefined
620
+ },
621
+ getItemDescription: (item) => {
622
+ const description = typeof item.description === 'string' ? item.description.trim() : ''
623
+ return description !== '' ? description : undefined
624
+ },
625
+ getBreadcrumbLabel: (item, _itemKey, itemIndex, context) => (
626
+ typeof item.title === 'string' && item.title.trim() !== ''
627
+ ? item.title.trim()
628
+ : typeof item.model === 'string' && item.model.trim() !== ''
629
+ ? item.model.trim()
630
+ : `${context.t('config.fields.general.recommendedModels.label')} #${itemIndex + 1}`
631
+ )
632
+ }
633
+ },
69
634
  {
70
635
  path: ['interfaceLanguage'],
71
636
  type: 'select',
@@ -88,7 +653,7 @@ export const configSchema: Record<string, FieldSpec[]> = {
88
653
  { value: 'en', label: 'config.options.language.en' }
89
654
  ]
90
655
  },
91
- { path: ['announcements'], type: 'string[]', defaultValue: [], icon: 'campaign', group: 'base' },
656
+ { path: ['announcements'], type: 'string[]', defaultValue: [], icon: 'campaign', group: 'advanced' },
92
657
  { path: ['permissions', 'allow'], type: 'string[]', defaultValue: [], icon: 'check_circle', group: 'permissions' },
93
658
  { path: ['permissions', 'deny'], type: 'string[]', defaultValue: [], icon: 'block', group: 'permissions' },
94
659
  { path: ['permissions', 'ask'], type: 'string[]', defaultValue: [], icon: 'help', group: 'permissions' },
@@ -102,159 +667,37 @@ export const configSchema: Record<string, FieldSpec[]> = {
102
667
  },
103
668
  { path: ['notifications', 'volume'], type: 'number', defaultValue: 100, icon: 'volume_down', group: 'items' },
104
669
  {
105
- path: ['notifications', 'events', 'completed', 'title'],
106
- type: 'string',
107
- defaultValue: '',
108
- icon: 'title',
109
- group: 'items',
110
- collapse: {
111
- key: 'notifications.events.completed',
112
- labelKey: 'config.fields.general.notifications.events.completed.label',
113
- descKey: 'config.fields.general.notifications.events.completed.desc',
114
- togglePath: ['notifications', 'events', 'completed', 'disabled']
115
- }
116
- },
117
- {
118
- path: ['notifications', 'events', 'completed', 'description'],
119
- type: 'multiline',
120
- defaultValue: '',
121
- icon: 'notes',
122
- group: 'items',
123
- collapse: {
124
- key: 'notifications.events.completed',
125
- labelKey: 'config.fields.general.notifications.events.completed.label',
126
- descKey: 'config.fields.general.notifications.events.completed.desc',
127
- togglePath: ['notifications', 'events', 'completed', 'disabled']
128
- }
129
- },
130
- {
131
- path: ['notifications', 'events', 'completed', 'sound'],
132
- type: 'string',
133
- defaultValue: '',
134
- icon: 'volume_up',
135
- group: 'items',
136
- collapse: {
137
- key: 'notifications.events.completed',
138
- labelKey: 'config.fields.general.notifications.events.completed.label',
139
- descKey: 'config.fields.general.notifications.events.completed.desc',
140
- togglePath: ['notifications', 'events', 'completed', 'disabled']
141
- }
142
- },
143
- {
144
- path: ['notifications', 'events', 'failed', 'title'],
145
- type: 'string',
146
- defaultValue: '',
147
- icon: 'title',
148
- group: 'items',
149
- collapse: {
150
- key: 'notifications.events.failed',
151
- labelKey: 'config.fields.general.notifications.events.failed.label',
152
- descKey: 'config.fields.general.notifications.events.failed.desc',
153
- togglePath: ['notifications', 'events', 'failed', 'disabled']
154
- }
155
- },
156
- {
157
- path: ['notifications', 'events', 'failed', 'description'],
158
- type: 'multiline',
159
- defaultValue: '',
160
- icon: 'notes',
161
- group: 'items',
162
- collapse: {
163
- key: 'notifications.events.failed',
164
- labelKey: 'config.fields.general.notifications.events.failed.label',
165
- descKey: 'config.fields.general.notifications.events.failed.desc',
166
- togglePath: ['notifications', 'events', 'failed', 'disabled']
167
- }
168
- },
169
- {
170
- path: ['notifications', 'events', 'failed', 'sound'],
171
- type: 'string',
172
- defaultValue: '',
173
- icon: 'volume_up',
174
- group: 'items',
175
- collapse: {
176
- key: 'notifications.events.failed',
177
- labelKey: 'config.fields.general.notifications.events.failed.label',
178
- descKey: 'config.fields.general.notifications.events.failed.desc',
179
- togglePath: ['notifications', 'events', 'failed', 'disabled']
180
- }
181
- },
182
- {
183
- path: ['notifications', 'events', 'terminated', 'title'],
184
- type: 'string',
185
- defaultValue: '',
186
- icon: 'title',
187
- group: 'items',
188
- collapse: {
189
- key: 'notifications.events.terminated',
190
- labelKey: 'config.fields.general.notifications.events.terminated.label',
191
- descKey: 'config.fields.general.notifications.events.terminated.desc',
192
- togglePath: ['notifications', 'events', 'terminated', 'disabled']
193
- }
194
- },
195
- {
196
- path: ['notifications', 'events', 'terminated', 'description'],
197
- type: 'multiline',
198
- defaultValue: '',
199
- icon: 'notes',
200
- group: 'items',
201
- collapse: {
202
- key: 'notifications.events.terminated',
203
- labelKey: 'config.fields.general.notifications.events.terminated.label',
204
- descKey: 'config.fields.general.notifications.events.terminated.desc',
205
- togglePath: ['notifications', 'events', 'terminated', 'disabled']
206
- }
207
- },
208
- {
209
- path: ['notifications', 'events', 'terminated', 'sound'],
210
- type: 'string',
211
- defaultValue: '',
212
- icon: 'volume_up',
213
- group: 'items',
214
- collapse: {
215
- key: 'notifications.events.terminated',
216
- labelKey: 'config.fields.general.notifications.events.terminated.label',
217
- descKey: 'config.fields.general.notifications.events.terminated.desc',
218
- togglePath: ['notifications', 'events', 'terminated', 'disabled']
219
- }
220
- },
221
- {
222
- path: ['notifications', 'events', 'waiting_input', 'title'],
223
- type: 'string',
224
- defaultValue: '',
225
- icon: 'title',
226
- group: 'items',
227
- collapse: {
228
- key: 'notifications.events.waiting_input',
229
- labelKey: 'config.fields.general.notifications.events.waiting_input.label',
230
- descKey: 'config.fields.general.notifications.events.waiting_input.desc',
231
- togglePath: ['notifications', 'events', 'waiting_input', 'disabled']
232
- }
233
- },
234
- {
235
- path: ['notifications', 'events', 'waiting_input', 'description'],
236
- type: 'multiline',
237
- defaultValue: '',
238
- icon: 'notes',
239
- group: 'items',
240
- collapse: {
241
- key: 'notifications.events.waiting_input',
242
- labelKey: 'config.fields.general.notifications.events.waiting_input.label',
243
- descKey: 'config.fields.general.notifications.events.waiting_input.desc',
244
- togglePath: ['notifications', 'events', 'waiting_input', 'disabled']
245
- }
246
- },
247
- {
248
- path: ['notifications', 'events', 'waiting_input', 'sound'],
249
- type: 'string',
250
- defaultValue: '',
251
- icon: 'volume_up',
670
+ path: ['notifications', 'events'],
671
+ type: 'detailCollection',
672
+ defaultValue: {},
673
+ icon: 'notifications_active',
252
674
  group: 'items',
253
- collapse: {
254
- key: 'notifications.events.waiting_input',
255
- labelKey: 'config.fields.general.notifications.events.waiting_input.label',
256
- descKey: 'config.fields.general.notifications.events.waiting_input.desc',
257
- togglePath: ['notifications', 'events', 'waiting_input', 'disabled']
675
+ labelKey: 'config.fields.general.notifications.events.label',
676
+ descriptionKey: 'config.fields.general.notifications.events.desc',
677
+ detailCollection: {
678
+ collectionKind: 'record',
679
+ itemKeys: [...notificationEventKeys],
680
+ createItem: () => ({}),
681
+ itemFields: notificationEventDetailFields,
682
+ summaryControls: [
683
+ {
684
+ kind: 'boolean',
685
+ path: ['disabled'],
686
+ checkedValue: false,
687
+ labelKey: 'config.fields.general.notifications.events.item.enabled.label'
688
+ }
689
+ ],
690
+ getItemTitle: (_item, itemKey, _itemIndex, { t }) => getNotificationEventLabel(t, itemKey),
691
+ getItemSubtitle: (item) => {
692
+ const parts: string[] = []
693
+ const title = typeof item.title === 'string' ? item.title.trim() : ''
694
+ const sound = typeof item.sound === 'string' ? item.sound.trim() : ''
695
+ if (title !== '') parts.push(title)
696
+ if (sound !== '') parts.push(sound)
697
+ return parts.length > 0 ? parts.join(' · ') : undefined
698
+ },
699
+ getItemDescription: (_item, itemKey, _itemIndex, { t }) => getNotificationEventDescription(t, itemKey),
700
+ getBreadcrumbLabel: (_item, itemKey, _itemIndex, { t }) => getNotificationEventLabel(t, itemKey)
258
701
  }
259
702
  }
260
703
  ],
@@ -264,13 +707,114 @@ export const configSchema: Record<string, FieldSpec[]> = {
264
707
  type: 'select',
265
708
  defaultValue: 'friendly',
266
709
  icon: 'forum',
710
+ group: 'defaults',
267
711
  options: [
268
712
  { value: 'friendly', label: 'config.options.conversation.friendly' },
269
713
  { value: 'programmatic', label: 'config.options.conversation.programmatic' }
270
714
  ]
271
715
  },
272
- { path: ['createSessionWorktree'], type: 'boolean', defaultValue: true, icon: 'account_tree' },
273
- { path: ['customInstructions'], type: 'multiline', defaultValue: '', icon: 'description' }
716
+ { path: ['createSessionWorktree'], type: 'boolean', defaultValue: true, icon: 'account_tree', group: 'defaults' },
717
+ { path: ['worktreeEnvironment'], type: 'select', defaultValue: '', icon: 'deployed_code', group: 'defaults' },
718
+ { path: ['customInstructions'], type: 'multiline', defaultValue: '', icon: 'description', group: 'defaults' },
719
+ {
720
+ path: ['startupPresets'],
721
+ type: 'detailCollection',
722
+ defaultValue: [],
723
+ icon: 'bolt',
724
+ group: 'presets',
725
+ labelKey: 'config.fields.conversation.startupPresets.label',
726
+ descriptionKey: 'config.fields.conversation.startupPresets.desc',
727
+ detailCollection: {
728
+ collectionKind: 'list',
729
+ createItem: () => ({
730
+ title: 'Untitled preset',
731
+ mode: 'default',
732
+ prompt: '',
733
+ files: [],
734
+ rules: [],
735
+ skills: []
736
+ }),
737
+ itemFields: conversationStarterDetailFields,
738
+ getItemTitle: (item, _itemKey, itemIndex, { t }) => {
739
+ const title = typeof item.title === 'string' ? item.title.trim() : ''
740
+ if (title !== '') return title
741
+ return `${t('config.fields.conversation.startupPresets.label')} #${itemIndex + 1}`
742
+ },
743
+ getItemSubtitle: (item, _itemKey, _itemIndex, { t }) => {
744
+ const parts: string[] = []
745
+ const mode = typeof item.mode === 'string' ? item.mode : 'default'
746
+ const target = typeof item.target === 'string' ? item.target.trim() : ''
747
+ const model = typeof item.model === 'string' ? item.model.trim() : ''
748
+ const effort = typeof item.effort === 'string' ? item.effort : ''
749
+ const worktreeCreate = item.worktree != null &&
750
+ typeof item.worktree === 'object' &&
751
+ !Array.isArray(item.worktree) &&
752
+ (item.worktree as { create?: unknown }).create === true
753
+ parts.push(t(`config.options.conversationStarterMode.${mode}`, { defaultValue: mode }))
754
+ if (target !== '') parts.push(target)
755
+ if (model !== '') parts.push(model)
756
+ if (effort !== '') parts.push(t(`config.options.effort.${effort}`, { defaultValue: effort }))
757
+ if (worktreeCreate) parts.push(t('config.fields.conversation.starterItem.worktreeEnabledBadge'))
758
+ return parts.join(' · ')
759
+ },
760
+ getItemDescription: (item) => {
761
+ const description = typeof item.description === 'string' ? item.description.trim() : ''
762
+ return description !== '' ? description : undefined
763
+ },
764
+ getBreadcrumbLabel: (item, _itemKey, itemIndex, { t }) => {
765
+ const title = typeof item.title === 'string' ? item.title.trim() : ''
766
+ return title !== ''
767
+ ? title
768
+ : `${t('config.fields.conversation.startupPresets.label')} #${itemIndex + 1}`
769
+ }
770
+ }
771
+ },
772
+ {
773
+ path: ['builtinActions'],
774
+ type: 'detailCollection',
775
+ defaultValue: [],
776
+ icon: 'construction',
777
+ group: 'actions',
778
+ labelKey: 'config.fields.conversation.builtinActions.label',
779
+ descriptionKey: 'config.fields.conversation.builtinActions.desc',
780
+ detailCollection: {
781
+ collectionKind: 'list',
782
+ createItem: () => ({
783
+ title: 'Untitled action',
784
+ mode: 'default',
785
+ prompt: '',
786
+ files: [],
787
+ rules: [],
788
+ skills: []
789
+ }),
790
+ itemFields: conversationStarterDetailFields,
791
+ getItemTitle: (item, _itemKey, itemIndex, { t }) => {
792
+ const title = typeof item.title === 'string' ? item.title.trim() : ''
793
+ if (title !== '') return title
794
+ return `${t('config.fields.conversation.builtinActions.label')} #${itemIndex + 1}`
795
+ },
796
+ getItemSubtitle: (item, _itemKey, _itemIndex, { t }) => {
797
+ const parts: string[] = []
798
+ const mode = typeof item.mode === 'string' ? item.mode : 'default'
799
+ const target = typeof item.target === 'string' ? item.target.trim() : ''
800
+ const prompt = typeof item.prompt === 'string' ? item.prompt.trim() : ''
801
+ parts.push(t(`config.options.conversationStarterMode.${mode}`, { defaultValue: mode }))
802
+ if (target !== '') parts.push(target)
803
+ if (prompt !== '') parts.push(prompt.slice(0, 24))
804
+ return parts.join(' · ')
805
+ },
806
+ getItemDescription: (item) => {
807
+ const description = typeof item.description === 'string' ? item.description.trim() : ''
808
+ return description !== '' ? description : undefined
809
+ },
810
+ getBreadcrumbLabel: (item, _itemKey, itemIndex, { t }) => {
811
+ const title = typeof item.title === 'string' ? item.title.trim() : ''
812
+ return title !== ''
813
+ ? title
814
+ : `${t('config.fields.conversation.builtinActions.label')} #${itemIndex + 1}`
815
+ }
816
+ }
817
+ }
274
818
  ],
275
819
  models: [
276
820
  {
@@ -284,42 +828,149 @@ export const configSchema: Record<string, FieldSpec[]> = {
284
828
  modelServices: [
285
829
  {
286
830
  path: [],
287
- type: 'record',
288
- recordKind: 'modelServices',
831
+ type: 'detailCollection',
289
832
  defaultValue: {},
290
- icon: 'hub'
833
+ icon: 'hub',
834
+ labelKey: 'config.fields.modelServices.items.label',
835
+ descriptionKey: 'config.fields.modelServices.items.desc',
836
+ detailCollection: {
837
+ collectionKind: 'recordMap',
838
+ keyPlaceholderKey: 'config.editor.newModelServiceName',
839
+ createItem: () => ({
840
+ title: '',
841
+ description: '',
842
+ apiBaseUrl: '',
843
+ apiKey: '',
844
+ models: [],
845
+ timeoutMs: undefined,
846
+ maxOutputTokens: undefined,
847
+ extra: {}
848
+ }),
849
+ itemFields: modelServiceDetailFields,
850
+ getItemTitle: (item, itemKey) => {
851
+ const title = typeof item.title === 'string' ? item.title.trim() : ''
852
+ return title !== '' ? title : itemKey
853
+ },
854
+ getItemSubtitle: (item, itemKey) => {
855
+ const title = typeof item.title === 'string' ? item.title.trim() : ''
856
+ return title !== '' ? itemKey : undefined
857
+ },
858
+ getItemDescription: (item) => {
859
+ const description = typeof item.description === 'string' ? item.description.trim() : ''
860
+ return description !== '' ? description : undefined
861
+ },
862
+ getBreadcrumbLabel: (item, itemKey) => {
863
+ const title = typeof item.title === 'string' ? item.title.trim() : ''
864
+ return title !== '' ? title : itemKey
865
+ }
866
+ }
291
867
  }
292
868
  ],
293
869
  channels: [
294
870
  {
295
871
  path: [],
296
- type: 'record',
297
- recordKind: 'channels',
872
+ type: 'detailCollection',
298
873
  defaultValue: {},
299
- icon: 'campaign'
874
+ icon: 'campaign',
875
+ labelKey: 'config.sections.channels',
876
+ detailCollection: {
877
+ collectionKind: 'recordMap',
878
+ keyPlaceholderKey: 'config.editor.newChannelName',
879
+ getItemTitle: (_item, itemKey) => itemKey,
880
+ getItemSubtitle: (item, _itemKey, _itemIndex, { t }) => {
881
+ const type = typeof item.type === 'string' ? item.type : ''
882
+ if (type === '') return undefined
883
+ const translated = t(`config.options.channels.${type}`, { defaultValue: type })
884
+ return translated
885
+ },
886
+ getItemDescription: (item) => {
887
+ const description = typeof item.description === 'string' ? item.description.trim() : ''
888
+ return description !== '' ? description : undefined
889
+ },
890
+ getBreadcrumbLabel: (_item, itemKey) => itemKey
891
+ }
300
892
  }
301
893
  ],
302
894
  adapters: [
303
895
  {
304
896
  path: [],
305
- type: 'record',
306
- recordKind: 'json',
897
+ type: 'detailCollection',
307
898
  defaultValue: {},
308
- icon: 'settings_input_component'
899
+ icon: 'settings_input_component',
900
+ labelKey: 'config.sections.adapters',
901
+ detailCollection: {
902
+ collectionKind: 'recordMap',
903
+ keyPlaceholderKey: 'config.editor.newAdapterName',
904
+ getItemTitle: (_item, itemKey) => itemKey,
905
+ getBreadcrumbLabel: (_item, itemKey) => itemKey
906
+ }
309
907
  }
310
908
  ],
311
909
  plugins: [
312
910
  {
313
911
  path: ['plugins'],
314
- type: 'json',
912
+ type: 'detailCollection',
315
913
  defaultValue: [],
316
- icon: 'extension'
914
+ icon: 'extension',
915
+ labelKey: 'config.fields.plugins.items.label',
916
+ descriptionKey: 'config.fields.plugins.items.desc',
917
+ detailCollection: {
918
+ collectionKind: 'list',
919
+ createItem: () => ({
920
+ id: '',
921
+ enabled: true,
922
+ scope: '',
923
+ options: {},
924
+ children: []
925
+ }),
926
+ getMergeKey: (item) => {
927
+ const id = typeof item.id === 'string' ? item.id.trim() : ''
928
+ if (id === '') return undefined
929
+ const scope = typeof item.scope === 'string' ? item.scope.trim() : ''
930
+ return `${id}::${scope}`
931
+ },
932
+ itemFields: pluginInstanceDetailFields,
933
+ summaryControls: [{ kind: 'boolean', path: ['enabled'], checkedValue: true }],
934
+ getItemTitle: (item, _itemKey, itemIndex) => {
935
+ const id = typeof item.id === 'string' ? item.id.trim() : ''
936
+ return id !== '' ? id : `Plugin #${itemIndex + 1}`
937
+ },
938
+ getItemSubtitle: (item) => {
939
+ const scope = typeof item.scope === 'string' ? item.scope.trim() : ''
940
+ return scope !== '' ? scope : undefined
941
+ },
942
+ getBreadcrumbLabel: (item, _itemKey, itemIndex) => {
943
+ const id = typeof item.id === 'string' ? item.id.trim() : ''
944
+ return id !== '' ? id : `Plugin #${itemIndex + 1}`
945
+ }
946
+ }
317
947
  },
318
948
  {
319
949
  path: ['marketplaces'],
320
- type: 'json',
950
+ type: 'detailCollection',
321
951
  defaultValue: {},
322
- icon: 'store'
952
+ icon: 'store',
953
+ labelKey: 'config.fields.plugins.marketplaces.label',
954
+ descriptionKey: 'config.fields.plugins.marketplaces.desc',
955
+ detailCollection: {
956
+ collectionKind: 'recordMap',
957
+ keyPlaceholderKey: 'config.editor.newMarketplaceName',
958
+ createItem: () => ({
959
+ type: 'claude-code',
960
+ enabled: true,
961
+ syncOnRun: false,
962
+ plugins: {},
963
+ options: {}
964
+ }),
965
+ itemFields: pluginMarketplaceDetailFields,
966
+ summaryControls: [{ kind: 'boolean', path: ['enabled'], checkedValue: true }],
967
+ getItemTitle: (_item, itemKey) => itemKey,
968
+ getItemSubtitle: (item) => {
969
+ const type = typeof item.type === 'string' ? item.type.trim() : ''
970
+ return type !== '' ? type : undefined
971
+ },
972
+ getBreadcrumbLabel: (_item, itemKey) => itemKey
973
+ }
323
974
  }
324
975
  ],
325
976
  mcp: [
@@ -328,10 +979,30 @@ export const configSchema: Record<string, FieldSpec[]> = {
328
979
  { path: ['noDefaultVibeForgeMcpServer'], type: 'boolean', defaultValue: false, group: 'base', icon: 'block' },
329
980
  {
330
981
  path: ['mcpServers'],
331
- type: 'record',
332
- recordKind: 'mcpServers',
982
+ type: 'detailCollection',
333
983
  defaultValue: {},
334
- icon: 'account_tree'
984
+ icon: 'account_tree',
985
+ labelKey: 'config.fields.mcp.mcpServers.label',
986
+ descriptionKey: 'config.fields.mcp.mcpServers.desc',
987
+ detailCollection: {
988
+ collectionKind: 'recordMap',
989
+ detailKind: 'mcpServer',
990
+ keyPlaceholderKey: 'config.editor.newMcpServerName',
991
+ createItem: () => ({
992
+ enabled: true,
993
+ command: '',
994
+ args: []
995
+ }),
996
+ summaryControls: [{ kind: 'boolean', path: ['enabled'], checkedValue: true }],
997
+ getItemTitle: (_item, itemKey) => itemKey,
998
+ getItemSubtitle: (item, _itemKey, _itemIndex, { t }) => {
999
+ const type = typeof item.type === 'string' && item.type.trim() !== ''
1000
+ ? item.type.trim()
1001
+ : 'command'
1002
+ return t(`config.options.mcp.${type}`, { defaultValue: type })
1003
+ },
1004
+ getBreadcrumbLabel: (_item, itemKey) => itemKey
1005
+ }
335
1006
  }
336
1007
  ],
337
1008
  shortcuts: [