@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,197 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react'
2
+
3
+ import { listSessionWorkspaceTree, listWorkspaceTree } from '#~/api'
4
+
5
+ import {
6
+ collectProjectFileTreeDirectoryPaths,
7
+ getProjectFileTreeAncestorDirectoryPaths,
8
+ loadProjectFileTreeDirectoryRecursive,
9
+ replaceProjectFileTreeNodeChildren,
10
+ toProjectFileTreeNodes
11
+ } from './project-file-tree-helpers'
12
+ import type { ProjectFileTreeCommand, ProjectFileTreeNode } from './project-file-tree-types'
13
+
14
+ export const useProjectFileTreeData = ({
15
+ command,
16
+ refreshKey,
17
+ sessionId
18
+ }: {
19
+ command?: ProjectFileTreeCommand | null
20
+ refreshKey: number
21
+ sessionId?: string
22
+ }) => {
23
+ const [treeData, setTreeData] = useState<ProjectFileTreeNode[]>([])
24
+ const [expandedPaths, setExpandedPaths] = useState<Set<string>>(new Set())
25
+ const [loadingPaths, setLoadingPaths] = useState<Set<string>>(new Set())
26
+ const [hasLoadedTree, setHasLoadedTree] = useState(false)
27
+ const [hasTreeError, setHasTreeError] = useState(false)
28
+ const [isRootLoading, setIsRootLoading] = useState(false)
29
+ const treeContainerRef = useRef<HTMLDivElement | null>(null)
30
+ const treeDataRef = useRef(treeData)
31
+ const treeOperationIdRef = useRef(0)
32
+
33
+ const loadWorkspaceTree = useCallback(async (path?: string) => {
34
+ if (sessionId != null && sessionId !== '') {
35
+ return await listSessionWorkspaceTree(sessionId, path)
36
+ }
37
+ return await listWorkspaceTree(path)
38
+ }, [sessionId])
39
+
40
+ const scrollTreePathIntoView = useCallback((path?: string) => {
41
+ if (path == null || path === '') {
42
+ return
43
+ }
44
+
45
+ window.requestAnimationFrame(() => {
46
+ window.requestAnimationFrame(() => {
47
+ const rows = treeContainerRef.current?.querySelectorAll<HTMLElement>('[data-workspace-tree-path]') ?? []
48
+ const target = Array.from(rows).find(row => row.dataset.workspaceTreePath === path)
49
+ target?.scrollIntoView({ block: 'center', inline: 'nearest' })
50
+ })
51
+ })
52
+ }, [])
53
+
54
+ const loadRootTree = useCallback(async () => {
55
+ const operationId = treeOperationIdRef.current + 1
56
+ treeOperationIdRef.current = operationId
57
+ setHasTreeError(false)
58
+ setExpandedPaths(new Set())
59
+ setIsRootLoading(true)
60
+ try {
61
+ const result = await loadWorkspaceTree()
62
+ if (treeOperationIdRef.current !== operationId) {
63
+ return
64
+ }
65
+ setTreeData(toProjectFileTreeNodes(result.entries))
66
+ setHasLoadedTree(true)
67
+ } catch {
68
+ if (treeOperationIdRef.current === operationId) {
69
+ setTreeData([])
70
+ setHasLoadedTree(true)
71
+ setHasTreeError(true)
72
+ }
73
+ } finally {
74
+ if (treeOperationIdRef.current === operationId) {
75
+ setIsRootLoading(false)
76
+ }
77
+ }
78
+ }, [loadWorkspaceTree])
79
+
80
+ useEffect(() => {
81
+ treeDataRef.current = treeData
82
+ }, [treeData])
83
+
84
+ useEffect(() => {
85
+ void loadRootTree()
86
+ }, [loadRootTree, refreshKey])
87
+
88
+ useEffect(() => {
89
+ if (command == null) {
90
+ return
91
+ }
92
+
93
+ if (command.action === 'collapse') {
94
+ treeOperationIdRef.current += 1
95
+ setExpandedPaths(new Set())
96
+ return
97
+ }
98
+
99
+ let isCancelled = false
100
+ const isCurrentOperation = (operationId: number) => !isCancelled && treeOperationIdRef.current === operationId
101
+ const runLocateFile = async () => {
102
+ if (command.path == null || command.path === '') {
103
+ return
104
+ }
105
+ const ancestorPaths = getProjectFileTreeAncestorDirectoryPaths(command.path)
106
+ const operationId = treeOperationIdRef.current + 1
107
+ treeOperationIdRef.current = operationId
108
+ setHasTreeError(false)
109
+ try {
110
+ let nextTreeData = treeDataRef.current
111
+ if (nextTreeData.length === 0) {
112
+ nextTreeData = toProjectFileTreeNodes((await loadWorkspaceTree()).entries)
113
+ }
114
+ for (const path of ancestorPaths) {
115
+ const result = await loadWorkspaceTree(path)
116
+ nextTreeData = replaceProjectFileTreeNodeChildren(
117
+ nextTreeData,
118
+ path,
119
+ toProjectFileTreeNodes(result.entries)
120
+ )
121
+ }
122
+ if (!isCurrentOperation(operationId)) {
123
+ return
124
+ }
125
+ setTreeData(nextTreeData)
126
+ setHasLoadedTree(true)
127
+ setExpandedPaths(prev => new Set([...prev, ...ancestorPaths]))
128
+ scrollTreePathIntoView(command.path)
129
+ } catch {
130
+ if (isCurrentOperation(operationId)) {
131
+ setHasLoadedTree(true)
132
+ setHasTreeError(true)
133
+ }
134
+ }
135
+ }
136
+ const runExpandAll = async () => {
137
+ const operationId = treeOperationIdRef.current + 1
138
+ treeOperationIdRef.current = operationId
139
+ setHasTreeError(false)
140
+ try {
141
+ const nextTreeData = await Promise.all(
142
+ treeDataRef.current.map(node => loadProjectFileTreeDirectoryRecursive(node, loadWorkspaceTree))
143
+ )
144
+ if (!isCurrentOperation(operationId)) {
145
+ return
146
+ }
147
+ setTreeData(nextTreeData)
148
+ setHasLoadedTree(true)
149
+ setExpandedPaths(new Set(collectProjectFileTreeDirectoryPaths(nextTreeData)))
150
+ } catch {
151
+ if (isCurrentOperation(operationId)) {
152
+ setHasLoadedTree(true)
153
+ setHasTreeError(true)
154
+ }
155
+ }
156
+ }
157
+
158
+ void (command.action === 'locate' ? runLocateFile() : runExpandAll())
159
+
160
+ return () => {
161
+ isCancelled = true
162
+ }
163
+ }, [command, loadWorkspaceTree, scrollTreePathIntoView])
164
+
165
+ const handleToggleDirectory = useCallback(async (node: ProjectFileTreeNode) => {
166
+ if (expandedPaths.has(node.path)) {
167
+ setExpandedPaths(prev => new Set(Array.from(prev).filter(path => path !== node.path)))
168
+ return
169
+ }
170
+
171
+ setExpandedPaths(prev => new Set(prev).add(node.path))
172
+ if (node.children != null) {
173
+ return
174
+ }
175
+
176
+ setLoadingPaths(prev => new Set(prev).add(node.path))
177
+ try {
178
+ const result = await loadWorkspaceTree(node.path)
179
+ setTreeData(prev => replaceProjectFileTreeNodeChildren(prev, node.path, toProjectFileTreeNodes(result.entries)))
180
+ } catch {
181
+ setHasTreeError(true)
182
+ } finally {
183
+ setLoadingPaths(prev => new Set(Array.from(prev).filter(path => path !== node.path)))
184
+ }
185
+ }, [expandedPaths, loadWorkspaceTree])
186
+
187
+ return {
188
+ expandedPaths,
189
+ handleToggleDirectory,
190
+ hasLoadedTree,
191
+ hasTreeError,
192
+ isRootLoading,
193
+ loadingPaths,
194
+ treeContainerRef,
195
+ treeData
196
+ }
197
+ }
@@ -0,0 +1,144 @@
1
+ import type { MouseEvent } from 'react'
2
+ import { useCallback, useMemo, useRef, useState } from 'react'
3
+
4
+ import { canSelectProjectFileTreeNode } from './project-file-tree-helpers'
5
+ import type {
6
+ ProjectFileTreeNode,
7
+ ProjectFileTreeSelectableTypes,
8
+ ProjectFileTreeSelection,
9
+ ProjectFileTreeSelectionMode
10
+ } from './project-file-tree-types'
11
+
12
+ const uniqueNodes = (nodes: ProjectFileTreeNode[]) => {
13
+ const seen = new Set<string>()
14
+ const result: ProjectFileTreeNode[] = []
15
+ for (const node of nodes) {
16
+ if (seen.has(node.path)) {
17
+ continue
18
+ }
19
+ seen.add(node.path)
20
+ result.push(node)
21
+ }
22
+ return result
23
+ }
24
+
25
+ const getNodesByPaths = (paths: string[], visibleNodes: ProjectFileTreeNode[]) => {
26
+ const nodeByPath = new Map(visibleNodes.map(node => [node.path, node]))
27
+ return paths
28
+ .map(path => nodeByPath.get(path))
29
+ .filter((node): node is ProjectFileTreeNode => node != null)
30
+ }
31
+
32
+ export const useProjectFileTreeSelection = ({
33
+ activePath,
34
+ onSelectionChange,
35
+ selectableTypes,
36
+ selectedPaths,
37
+ selectionMode,
38
+ visibleNodes
39
+ }: {
40
+ activePath?: string | null
41
+ onSelectionChange?: (selection: ProjectFileTreeSelection) => void
42
+ selectableTypes: ProjectFileTreeSelectableTypes
43
+ selectedPaths?: string[]
44
+ selectionMode: ProjectFileTreeSelectionMode
45
+ visibleNodes: ProjectFileTreeNode[]
46
+ }) => {
47
+ const [internalSelectedPaths, setInternalSelectedPaths] = useState<string[]>([])
48
+ const selectionAnchorPathRef = useRef<string | null>(null)
49
+ const resolvedSelectedPaths = selectedPaths ?? internalSelectedPaths
50
+ const selectedNodes = useMemo(
51
+ () => getNodesByPaths(resolvedSelectedPaths, visibleNodes),
52
+ [resolvedSelectedPaths, visibleNodes]
53
+ )
54
+ const selectedPathSet = useMemo(() => {
55
+ const paths = new Set(resolvedSelectedPaths)
56
+ if (activePath != null && activePath !== '') {
57
+ paths.add(activePath)
58
+ }
59
+ return paths
60
+ }, [activePath, resolvedSelectedPaths])
61
+
62
+ const emitSelectionChange = useCallback((
63
+ nextNodes: ProjectFileTreeNode[]
64
+ ) => {
65
+ const normalizedNodes = uniqueNodes(nextNodes.filter(node => canSelectProjectFileTreeNode(node, selectableTypes)))
66
+ const nextPaths = normalizedNodes.map(node => node.path)
67
+ if (selectedPaths == null) {
68
+ setInternalSelectedPaths(nextPaths)
69
+ }
70
+ onSelectionChange?.({ nodes: normalizedNodes, paths: nextPaths })
71
+ }, [onSelectionChange, selectableTypes, selectedPaths])
72
+
73
+ const handleContextSelect = useCallback((node: ProjectFileTreeNode) => {
74
+ if (selectionMode === 'none' || selectedPathSet.has(node.path)) {
75
+ return
76
+ }
77
+ selectionAnchorPathRef.current = node.path
78
+ emitSelectionChange([node])
79
+ }, [emitSelectionChange, selectedPathSet, selectionMode])
80
+
81
+ const handleSelectNode = useCallback((node: ProjectFileTreeNode, event: MouseEvent<HTMLButtonElement>) => {
82
+ if (selectionMode === 'none' || !canSelectProjectFileTreeNode(node, selectableTypes)) {
83
+ return false
84
+ }
85
+
86
+ const isRangeSelection = event.shiftKey
87
+ const isToggleSelection = event.metaKey || event.ctrlKey
88
+ if (!isRangeSelection && !isToggleSelection) {
89
+ selectionAnchorPathRef.current = node.path
90
+ emitSelectionChange([node])
91
+ return false
92
+ }
93
+
94
+ event.preventDefault()
95
+ event.stopPropagation()
96
+
97
+ const selectableVisibleNodes = visibleNodes.filter(item => canSelectProjectFileTreeNode(item, selectableTypes))
98
+ if (selectableVisibleNodes.length === 0) {
99
+ return true
100
+ }
101
+
102
+ if (isRangeSelection) {
103
+ const anchorPath = selectionAnchorPathRef.current ?? resolvedSelectedPaths.at(-1) ?? node.path
104
+ const anchorIndex = selectableVisibleNodes.findIndex(item => item.path === anchorPath)
105
+ const targetIndex = selectableVisibleNodes.findIndex(item => item.path === node.path)
106
+ if (targetIndex < 0) {
107
+ return true
108
+ }
109
+ if (anchorIndex < 0) {
110
+ emitSelectionChange([node])
111
+ selectionAnchorPathRef.current = node.path
112
+ return true
113
+ }
114
+
115
+ const start = Math.min(anchorIndex, targetIndex)
116
+ const end = Math.max(anchorIndex, targetIndex)
117
+ emitSelectionChange(selectableVisibleNodes.slice(start, end + 1))
118
+ return true
119
+ }
120
+
121
+ selectionAnchorPathRef.current = node.path
122
+ if (selectedPathSet.has(node.path)) {
123
+ emitSelectionChange(selectedNodes.filter(item => item.path !== node.path))
124
+ } else {
125
+ emitSelectionChange([...selectedNodes, node])
126
+ }
127
+ return true
128
+ }, [
129
+ emitSelectionChange,
130
+ resolvedSelectedPaths,
131
+ selectableTypes,
132
+ selectedNodes,
133
+ selectedPathSet,
134
+ selectionMode,
135
+ visibleNodes
136
+ ])
137
+
138
+ return {
139
+ handleContextSelect,
140
+ handleSelectNode,
141
+ selectedNodes,
142
+ selectedPathSet
143
+ }
144
+ }
@@ -0,0 +1,69 @@
1
+ import type { Session } from '@vibe-forge/core'
2
+ import type { SessionPromptType } from '@vibe-forge/types'
3
+
4
+ export type ChatSessionTargetType = 'default' | SessionPromptType
5
+
6
+ export interface ChatSessionTargetDraft {
7
+ type: ChatSessionTargetType
8
+ name?: string
9
+ label?: string
10
+ description?: string
11
+ path?: string
12
+ }
13
+
14
+ export interface ChatSessionTargetResource {
15
+ id: string
16
+ name: string
17
+ description?: string
18
+ path?: string
19
+ }
20
+
21
+ export const DEFAULT_CHAT_SESSION_TARGET_DRAFT: ChatSessionTargetDraft = {
22
+ type: 'default'
23
+ }
24
+
25
+ export const isChatSessionTargetReady = (target?: ChatSessionTargetDraft) => (
26
+ target == null || target.type === 'default' || (target.name?.trim() ?? '') !== ''
27
+ )
28
+
29
+ export const getChatSessionTargetPrompt = (target?: ChatSessionTargetDraft): {
30
+ promptType?: SessionPromptType
31
+ promptName?: string
32
+ } => {
33
+ if (target == null || target.type === 'default') {
34
+ return {}
35
+ }
36
+
37
+ const promptName = target.name?.trim()
38
+ if (promptName == null || promptName === '') {
39
+ return {}
40
+ }
41
+
42
+ return {
43
+ promptType: target.type,
44
+ promptName
45
+ }
46
+ }
47
+
48
+ export const createChatSessionTargetDraft = (
49
+ type: Exclude<ChatSessionTargetType, 'default'>,
50
+ resource: ChatSessionTargetResource
51
+ ): ChatSessionTargetDraft => ({
52
+ type,
53
+ name: type === 'workspace' ? resource.id : resource.name,
54
+ label: resource.name,
55
+ description: resource.description,
56
+ path: resource.path
57
+ })
58
+
59
+ export const getChatSessionTargetDraftFromSession = (session?: Session): ChatSessionTargetDraft => {
60
+ if (session?.promptType == null) {
61
+ return DEFAULT_CHAT_SESSION_TARGET_DRAFT
62
+ }
63
+
64
+ return {
65
+ type: session.promptType,
66
+ name: session.promptName,
67
+ label: session.promptName
68
+ }
69
+ }
@@ -8,18 +8,25 @@ export interface ChatSessionWorkspaceDraftBranch {
8
8
 
9
9
  export interface ChatSessionWorkspaceDraft {
10
10
  createWorktree: boolean
11
+ worktreeEnvironment?: string
11
12
  branch?: ChatSessionWorkspaceDraftBranch
12
13
  }
13
14
 
14
- export const buildChatSessionWorkspaceDraft = (createWorktree = true): ChatSessionWorkspaceDraft => ({
15
- createWorktree
15
+ export const buildChatSessionWorkspaceDraft = (
16
+ createWorktree = true,
17
+ worktreeEnvironment?: string
18
+ ): ChatSessionWorkspaceDraft => ({
19
+ createWorktree,
20
+ ...(worktreeEnvironment != null && worktreeEnvironment.trim() !== ''
21
+ ? { worktreeEnvironment: worktreeEnvironment.trim() }
22
+ : {})
16
23
  })
17
24
 
18
25
  export const getChatSessionWorkspaceDraftFromConfig = (
19
26
  configRes?: ConfigResponse
20
27
  ): ChatSessionWorkspaceDraft => {
21
- const createWorktree = configRes?.sources?.merged?.conversation?.createSessionWorktree
22
- return buildChatSessionWorkspaceDraft(createWorktree ?? true)
28
+ const conversation = configRes?.sources?.merged?.conversation
29
+ return buildChatSessionWorkspaceDraft(conversation?.createSessionWorktree ?? true, conversation?.worktreeEnvironment)
23
30
  }
24
31
 
25
32
  export const DEFAULT_CHAT_SESSION_WORKSPACE_DRAFT: ChatSessionWorkspaceDraft = buildChatSessionWorkspaceDraft()
@@ -8,6 +8,7 @@ export interface InteractionRequestState {
8
8
  }
9
9
 
10
10
  export interface ChatErrorState {
11
+ action?: 'retry-session-creation'
11
12
  kind: 'connection' | 'session'
12
13
  message: string
13
14
  code?: string
@@ -0,0 +1,189 @@
1
+ import { atom } from 'jotai'
2
+
3
+ import type {
4
+ ChatMessage,
5
+ ChatMessageContent,
6
+ EffortLevel,
7
+ Session,
8
+ SessionPermissionMode,
9
+ SessionPromptType
10
+ } from '@vibe-forge/core'
11
+ import type { GitBranchKind } from '@vibe-forge/types'
12
+
13
+ export interface OptimisticSessionCreationOptions {
14
+ start?: boolean
15
+ parentSessionId?: string
16
+ id: string
17
+ promptType?: SessionPromptType
18
+ promptName?: string
19
+ effort?: EffortLevel
20
+ permissionMode?: SessionPermissionMode
21
+ adapter?: string
22
+ account?: string
23
+ workspace?: {
24
+ createWorktree?: boolean
25
+ worktreeEnvironment?: string
26
+ branch?: {
27
+ name: string
28
+ kind?: GitBranchKind
29
+ mode?: 'checkout' | 'create'
30
+ }
31
+ }
32
+ }
33
+
34
+ export interface OptimisticSessionCreationRequest {
35
+ id: string
36
+ title?: string
37
+ initialMessage?: string
38
+ initialContent?: ChatMessageContent[]
39
+ model?: string
40
+ options: OptimisticSessionCreationOptions
41
+ }
42
+
43
+ export interface OptimisticSessionCreation {
44
+ errorMessage?: string
45
+ message: ChatMessage
46
+ request: OptimisticSessionCreationRequest
47
+ session: Session
48
+ status: 'creating' | 'failed'
49
+ }
50
+
51
+ export type OptimisticSessionCreationMap = Record<string, OptimisticSessionCreation>
52
+
53
+ export const optimisticSessionCreationsAtom = atom<OptimisticSessionCreationMap>({})
54
+
55
+ const discardedOptimisticSessionIds = new Set<string>()
56
+
57
+ export const markOptimisticSessionDiscarded = (id: string) => {
58
+ discardedOptimisticSessionIds.add(id)
59
+ }
60
+
61
+ export const clearOptimisticSessionDiscarded = (id: string) => {
62
+ discardedOptimisticSessionIds.delete(id)
63
+ }
64
+
65
+ export const isOptimisticSessionDiscarded = (id: string) => {
66
+ return discardedOptimisticSessionIds.has(id)
67
+ }
68
+
69
+ export const createOptimisticSessionId = () => {
70
+ return globalThis.crypto?.randomUUID != null
71
+ ? globalThis.crypto.randomUUID()
72
+ : `local-session-${Date.now()}-${Math.random().toString(16).slice(2)}`
73
+ }
74
+
75
+ const getContentPreview = (content?: string | ChatMessageContent[]) => {
76
+ if (typeof content === 'string') {
77
+ return content.trim()
78
+ }
79
+
80
+ const textItem = content?.find(
81
+ (item): item is Extract<ChatMessageContent, { type: 'text' }> => item.type === 'text' && item.text.trim() !== ''
82
+ )
83
+ if (textItem != null) {
84
+ return textItem.text.trim()
85
+ }
86
+
87
+ const fileItem = content?.find(
88
+ (item): item is Extract<ChatMessageContent, { type: 'file' }> => item.type === 'file' && item.path.trim() !== ''
89
+ )
90
+ if (fileItem != null) {
91
+ return fileItem.path.trim()
92
+ }
93
+
94
+ const imageItem = content?.find((item): item is Extract<ChatMessageContent, { type: 'image' }> =>
95
+ item.type === 'image'
96
+ )
97
+ if (imageItem != null) {
98
+ return imageItem.name?.trim() ?? ''
99
+ }
100
+
101
+ return ''
102
+ }
103
+
104
+ const getTitlePreview = (contentPreview: string) => {
105
+ const firstLine = contentPreview.split('\n')[0]?.trim() ?? ''
106
+ if (firstLine === '') return undefined
107
+ return firstLine.length > 50 ? `${firstLine.slice(0, 50)}...` : firstLine
108
+ }
109
+
110
+ export const createOptimisticSessionCreation = (
111
+ request: OptimisticSessionCreationRequest,
112
+ now = Date.now()
113
+ ): OptimisticSessionCreation => {
114
+ const content = request.initialContent ?? request.initialMessage ?? ''
115
+ const contentPreview = getContentPreview(content)
116
+ const title = request.title ?? getTitlePreview(contentPreview)
117
+
118
+ return {
119
+ message: {
120
+ id: `${request.id}:optimistic-user-message`,
121
+ role: 'user',
122
+ content,
123
+ createdAt: now
124
+ },
125
+ request,
126
+ session: {
127
+ id: request.id,
128
+ title,
129
+ createdAt: now,
130
+ messageCount: 1,
131
+ lastMessage: contentPreview === '' ? undefined : contentPreview,
132
+ lastUserMessage: contentPreview === '' ? undefined : contentPreview,
133
+ isArchived: false,
134
+ isStarred: false,
135
+ tags: [],
136
+ status: 'running',
137
+ model: request.model,
138
+ adapter: request.options.adapter,
139
+ account: request.options.account,
140
+ permissionMode: request.options.permissionMode,
141
+ effort: request.options.effort,
142
+ promptType: request.options.promptType,
143
+ promptName: request.options.promptName
144
+ },
145
+ status: 'creating'
146
+ }
147
+ }
148
+
149
+ export const markOptimisticSessionCreationCreating = (
150
+ creation: OptimisticSessionCreation
151
+ ): OptimisticSessionCreation => ({
152
+ ...creation,
153
+ errorMessage: undefined,
154
+ session: {
155
+ ...creation.session,
156
+ status: 'running'
157
+ },
158
+ status: 'creating'
159
+ })
160
+
161
+ export const markOptimisticSessionCreationFailed = (
162
+ creation: OptimisticSessionCreation,
163
+ errorMessage: string
164
+ ): OptimisticSessionCreation => ({
165
+ ...creation,
166
+ errorMessage,
167
+ session: {
168
+ ...creation.session,
169
+ status: 'failed'
170
+ },
171
+ status: 'failed'
172
+ })
173
+
174
+ export const mergeOptimisticSessions = (
175
+ sessions: Session[],
176
+ creations: OptimisticSessionCreationMap
177
+ ) => {
178
+ const existingIds = new Set(sessions.map(session => session.id))
179
+ const mergedSessions = sessions.map(session => creations[session.id]?.session ?? session)
180
+ const optimisticSessions = Object.values(creations)
181
+ .map(creation => creation.session)
182
+ .filter(session => session.isArchived !== true && !existingIds.has(session.id))
183
+
184
+ return [...mergedSessions, ...optimisticSessions]
185
+ }
186
+
187
+ export const removeSessionFromList = (sessions: Session[], id: string) => {
188
+ return sessions.filter(session => session.id !== id)
189
+ }