@vibe-forge/client 0.11.3 → 1.0.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 (200) hide show
  1. package/cli.cjs +6 -11
  2. package/dist/assets/{arc-h39NrT24.js → arc-CbOXL0l9.js} +1 -1
  3. package/dist/assets/{blockDiagram-c4efeb88-CaDg46I6.js → blockDiagram-c4efeb88-CqxINvsS.js} +1 -1
  4. package/dist/assets/{c4Diagram-c83219d4-CDqjcF9U.js → c4Diagram-c83219d4-BKazU0hb.js} +1 -1
  5. package/dist/assets/channel-Dnopc5A6.js +1 -0
  6. package/dist/assets/{classDiagram-beda092f-BDnZm8nO.js → classDiagram-beda092f-fAFX5BpB.js} +1 -1
  7. package/dist/assets/{classDiagram-v2-2358418a-BUi85KJW.js → classDiagram-v2-2358418a-w1VkNGJj.js} +1 -1
  8. package/dist/assets/clone-sQthahUA.js +1 -0
  9. package/dist/assets/{createText-1719965b-Ca5dEwfo.js → createText-1719965b-CEinakVP.js} +1 -1
  10. package/dist/assets/{cssMode-Ysz7NfYo.js → cssMode-DPqRki4y.js} +1 -1
  11. package/dist/assets/{edges-96097737-CdSKqxZt.js → edges-96097737-Cb0F1_3K.js} +1 -1
  12. package/dist/assets/{erDiagram-0228fc6a-B-veAUv_.js → erDiagram-0228fc6a-C-N2fx-J.js} +1 -1
  13. package/dist/assets/{flowDb-c6c81e3f-DD8Cx7_9.js → flowDb-c6c81e3f-D1Xz_8Gf.js} +1 -1
  14. package/dist/assets/{flowDiagram-50d868cf-9f-_x1ET.js → flowDiagram-50d868cf-DyPSZyAj.js} +1 -1
  15. package/dist/assets/flowDiagram-v2-4f6560a1-OazrdWQO.js +1 -0
  16. package/dist/assets/{flowchart-elk-definition-6af322e1-5RhpQM4M.js → flowchart-elk-definition-6af322e1-Dr1DDXwE.js} +1 -1
  17. package/dist/assets/{freemarker2-SgMdIXw4.js → freemarker2-C3DvPFaK.js} +1 -1
  18. package/dist/assets/{ganttDiagram-a2739b55-DnxNghZA.js → ganttDiagram-a2739b55-DmvY1GRj.js} +1 -1
  19. package/dist/assets/{gitGraphDiagram-82fe8481-CBvS3Tf9.js → gitGraphDiagram-82fe8481-CoXfPYYi.js} +1 -1
  20. package/dist/assets/{graph-CkHF299-.js → graph-BkDQy7Qt.js} +1 -1
  21. package/dist/assets/{handlebars-C57IyLUe.js → handlebars-BcTFdqjl.js} +1 -1
  22. package/dist/assets/{html-YsDy5wvW.js → html-Dg-O6XFr.js} +1 -1
  23. package/dist/assets/{htmlMode-7o_VDODD.js → htmlMode-B_wqYWvn.js} +1 -1
  24. package/dist/assets/{index-5325376f-BzOVQPu-.js → index-5325376f-kxPTR3_e.js} +1 -1
  25. package/dist/assets/index-o93dlo92.css +32 -0
  26. package/dist/assets/{index-BHFpctk6.js → index-wkhI4dr6.js} +357 -337
  27. package/dist/assets/{infoDiagram-8eee0895-DJ-UI1h4.js → infoDiagram-8eee0895-BEvqkwPI.js} +1 -1
  28. package/dist/assets/{javascript-BHQ9NEZr.js → javascript-DhlOH8_z.js} +1 -1
  29. package/dist/assets/{journeyDiagram-c64418c1-DwfykcG9.js → journeyDiagram-c64418c1-gKtLYmmp.js} +1 -1
  30. package/dist/assets/{jsonMode-3QjftkMM.js → jsonMode-DxTbF9OD.js} +1 -1
  31. package/dist/assets/{layout-CbViRb_b.js → layout-CDaZEk6E.js} +1 -1
  32. package/dist/assets/{line-DBdBvv9D.js → line-DNRQu8iq.js} +1 -1
  33. package/dist/assets/{linear-BDAfhcjn.js → linear-Cph9Z6_j.js} +1 -1
  34. package/dist/assets/{liquid-B0cPPzIR.js → liquid-ByZ6JgRG.js} +1 -1
  35. package/dist/assets/{lspLanguageFeatures-IOxbobOz.js → lspLanguageFeatures-DzvhkgnM.js} +1 -1
  36. package/dist/assets/{mdx-Dma_RA8P.js → mdx-D8RGHTl6.js} +1 -1
  37. package/dist/assets/{mermaid.core-Cvn8Go4x.js → mermaid.core-BgcryF__.js} +4 -4
  38. package/dist/assets/{mindmap-definition-8da855dc-DEnYq0Lc.js → mindmap-definition-8da855dc-WrxK0FcB.js} +1 -1
  39. package/dist/assets/{pieDiagram-a8764435-ZC4j8sHU.js → pieDiagram-a8764435-VsZBsiQy.js} +1 -1
  40. package/dist/assets/{python-Be0WX4q5.js → python-CXVtk_cg.js} +1 -1
  41. package/dist/assets/{quadrantDiagram-1e28029f-DUaqHlIB.js → quadrantDiagram-1e28029f-BVlgwOvU.js} +1 -1
  42. package/dist/assets/{razor-Tjhny-uT.js → razor-0tind7h2.js} +1 -1
  43. package/dist/assets/{requirementDiagram-08caed73-DjSal3es.js → requirementDiagram-08caed73-CpPMPoYp.js} +1 -1
  44. package/dist/assets/{sankeyDiagram-a04cb91d-BMDXMrMz.js → sankeyDiagram-a04cb91d-Cm5nnRmc.js} +1 -1
  45. package/dist/assets/{sequenceDiagram-c5b8d532-CQl9YUlH.js → sequenceDiagram-c5b8d532-DpMlJvJB.js} +1 -1
  46. package/dist/assets/{stateDiagram-1ecb1508-DG7mU9jD.js → stateDiagram-1ecb1508-DU1zc7vq.js} +1 -1
  47. package/dist/assets/{stateDiagram-v2-c2b004d7-DTbR_azy.js → stateDiagram-v2-c2b004d7-D-0RgmAp.js} +1 -1
  48. package/dist/assets/{styles-b4e223ce-C9aS3zb8.js → styles-b4e223ce-BSO-yNWV.js} +1 -1
  49. package/dist/assets/{styles-ca3715f6-Bh3keVTZ.js → styles-ca3715f6-CHnsn2Ro.js} +1 -1
  50. package/dist/assets/{styles-d45a18b0-BDcLLa65.js → styles-d45a18b0-B-rVGjEq.js} +1 -1
  51. package/dist/assets/{svgDrawCommon-b86b1483-B9H5ZS_9.js → svgDrawCommon-b86b1483-CA3Pl89f.js} +1 -1
  52. package/dist/assets/{timeline-definition-faaaa080-DCMYCBhK.js → timeline-definition-faaaa080-BcihLR6s.js} +1 -1
  53. package/dist/assets/{tsMode-DVqLsn98.js → tsMode-D9GGa5Ur.js} +1 -1
  54. package/dist/assets/{typescript-wMVyXw7G.js → typescript-BT9CK_EL.js} +1 -1
  55. package/dist/assets/{xml-w0gzmn0c.js → xml-DNO75J-T.js} +1 -1
  56. package/dist/assets/{xychartDiagram-f5964ef8-CdxyD3K5.js → xychartDiagram-f5964ef8-DJTwe32X.js} +1 -1
  57. package/dist/assets/{yaml-C29TL1ed.js → yaml-7CVzhiP2.js} +1 -1
  58. package/dist/index.html +2 -2
  59. package/package.json +13 -10
  60. package/src/api/git.ts +12 -0
  61. package/src/api/sessions.ts +61 -3
  62. package/src/api.ts +8 -0
  63. package/src/components/ArchiveView.scss +143 -54
  64. package/src/components/ArchiveView.tsx +181 -167
  65. package/src/components/CodeBlock.scss +5 -0
  66. package/src/components/ConfigView.scss +142 -31
  67. package/src/components/ConfigView.tsx +161 -86
  68. package/src/components/MarkdownContent.tsx +7 -0
  69. package/src/components/NavRail.scss +248 -0
  70. package/src/components/NavRail.tsx +80 -107
  71. package/src/components/NavRailCompact.tsx +107 -0
  72. package/src/components/NavRailCompactMoreSheet.tsx +141 -0
  73. package/src/components/ShortcutTooltip.tsx +4 -2
  74. package/src/components/Sidebar.scss +51 -0
  75. package/src/components/Sidebar.tsx +43 -16
  76. package/src/components/automation-view/RuleFormPanel.scss +40 -13
  77. package/src/components/automation-view/RuleSidebar.scss +73 -47
  78. package/src/components/automation-view/RuleSidebar.tsx +9 -13
  79. package/src/components/automation-view/RunHistoryPanel.scss +141 -13
  80. package/src/components/automation-view/RunHistoryPanel.tsx +203 -161
  81. package/src/components/automation-view/TaskList.scss +44 -13
  82. package/src/components/automation-view/TriggerList.scss +46 -14
  83. package/src/components/automation-view/index.scss +82 -10
  84. package/src/components/automation-view/index.tsx +108 -55
  85. package/src/components/benchmark-view/BenchmarkCasePanel.scss +36 -16
  86. package/src/components/benchmark-view/BenchmarkSidebar.scss +44 -22
  87. package/src/components/benchmark-view/BenchmarkSidebar.tsx +0 -6
  88. package/src/components/benchmark-view/BenchmarkView.scss +63 -20
  89. package/src/components/benchmark-view/index.tsx +71 -34
  90. package/src/components/chat/ChatComposerCard.scss +4 -0
  91. package/src/components/chat/ChatHeader.scss +187 -0
  92. package/src/components/chat/ChatHeader.tsx +206 -56
  93. package/src/components/chat/ChatHistoryView.tsx +63 -2
  94. package/src/components/chat/ChatTimelineView.scss +94 -1
  95. package/src/components/chat/ChatTimelineView.tsx +42 -0
  96. package/src/components/chat/NewSessionGuide.scss +139 -1
  97. package/src/components/chat/NewSessionGuide.tsx +57 -100
  98. package/src/components/chat/NewSessionGuideCompactPanel.tsx +130 -0
  99. package/src/components/chat/NewSessionGuideGrid.tsx +141 -0
  100. package/src/components/chat/git-controls/BranchSwitcherDropdown.tsx +61 -56
  101. package/src/components/chat/git-controls/BranchSwitcherResults.tsx +167 -0
  102. package/src/components/chat/git-controls/BranchTreeEntries.tsx +99 -0
  103. package/src/components/chat/git-controls/ChatGitControls.scss +437 -5
  104. package/src/components/chat/git-controls/ChatGitControls.tsx +136 -109
  105. package/src/components/chat/git-controls/DraftGitControls.tsx +91 -0
  106. package/src/components/chat/git-controls/GitOperationsDropdown.tsx +10 -2
  107. package/src/components/chat/git-controls/GitWorktreeDropdown.tsx +301 -28
  108. package/src/components/chat/git-controls/git-branch-tree.ts +148 -0
  109. package/src/components/chat/git-controls/use-chat-draft-git-controls.ts +168 -0
  110. package/src/components/chat/git-controls/use-chat-git-controls.ts +76 -3
  111. package/src/components/chat/messages/MessageContextMenu.tsx +3 -1
  112. package/src/components/chat/messages/MessageItem.scss +78 -4
  113. package/src/components/chat/messages/MessageItem.tsx +47 -3
  114. package/src/components/chat/sender/@components/adapter-select/AdapterSelectControl.scss +15 -0
  115. package/src/components/chat/sender/@components/reference-actions/ReferenceActionsControl.scss +17 -0
  116. package/src/components/chat/sender/@components/reference-actions/ReferencePermissionActionsPopover.tsx +4 -1
  117. package/src/components/chat/sender/@components/sender-attachments/SenderAttachments.scss +15 -2
  118. package/src/components/chat/sender/@components/sender-body/SenderBody.tsx +3 -0
  119. package/src/components/chat/sender/@components/sender-monaco-editor/use-sender-monaco-theme.ts +52 -3
  120. package/src/components/chat/sender/@components/sender-submit-action/SenderSubmitAction.scss +12 -0
  121. package/src/components/chat/sender/@components/sender-toolbar/SenderSelectBase.scss +21 -0
  122. package/src/components/chat/sender/@components/sender-toolbar/SenderSelectShared.scss +21 -0
  123. package/src/components/chat/sender/@components/sender-toolbar/SenderToolbar.scss +63 -0
  124. package/src/components/chat/sender/@core/get-sender-runtime-state.ts +1 -1
  125. package/src/components/chat/sender/@hooks/use-model-select-browser.tsx +4 -2
  126. package/src/components/chat/sender/@types/sender-props.ts +1 -0
  127. package/src/components/chat/sender/Sender.scss +1 -1
  128. package/src/components/chat/sender/Sender.tsx +1 -0
  129. package/src/components/chat/session-timeline-panel/EventList.scss +88 -0
  130. package/src/components/chat/session-timeline-panel/EventList.tsx +99 -47
  131. package/src/components/chat/session-timeline-panel/gantt.ts +23 -7
  132. package/src/components/chat/session-timeline-panel/git-graph.ts +6 -1
  133. package/src/components/chat/session-timeline-panel/index.scss +14 -1
  134. package/src/components/chat/session-timeline-panel/index.tsx +86 -10
  135. package/src/components/chat/session-timeline-panel/types.ts +4 -0
  136. package/src/components/chat/status-bar/ChatStatusBar.scss +27 -0
  137. package/src/components/chat/status-bar/ChatStatusBar.tsx +39 -0
  138. package/src/components/chat/terminal/ChatTerminalView.tsx +6 -0
  139. package/src/components/chat/tools/core/ToolCallBox.scss +19 -0
  140. package/src/components/chat/tools/core/ToolGroup.scss +32 -0
  141. package/src/components/chat/tools/task/components/TaskToolCard.scss +59 -1
  142. package/src/components/config/ConfigEditors.scss +20 -6
  143. package/src/components/config/ConfigFieldRow.scss +57 -17
  144. package/src/components/config/ConfigSectionForm.scss +10 -4
  145. package/src/components/config/ConfigSectionPanel.tsx +18 -11
  146. package/src/components/config/configSchema.ts +1 -0
  147. package/src/components/config/record-editors/RecordEditors.scss +42 -9
  148. package/src/components/dock-panel/DockPanel.scss +6 -2
  149. package/src/components/dock-panel/DockPanel.tsx +12 -16
  150. package/src/components/knowledge-base/KnowledgeBaseView.scss +180 -6
  151. package/src/components/knowledge-base/KnowledgeBaseView.tsx +98 -26
  152. package/src/components/knowledge-base/components/ActionButton.scss +4 -0
  153. package/src/components/knowledge-base/components/EmptyState.scss +5 -8
  154. package/src/components/knowledge-base/components/EntitiesTab.tsx +8 -2
  155. package/src/components/knowledge-base/components/EntityItem.scss +10 -3
  156. package/src/components/knowledge-base/components/FilterBar.scss +13 -2
  157. package/src/components/knowledge-base/components/FlowsTab.tsx +8 -2
  158. package/src/components/knowledge-base/components/KnowledgeBaseHeader.scss +2 -23
  159. package/src/components/knowledge-base/components/KnowledgeBaseHeader.tsx +0 -5
  160. package/src/components/knowledge-base/components/KnowledgeList.scss +15 -6
  161. package/src/components/knowledge-base/components/LoadingState.scss +4 -0
  162. package/src/components/knowledge-base/components/RuleItem.scss +86 -0
  163. package/src/components/knowledge-base/components/RuleItem.tsx +2 -0
  164. package/src/components/knowledge-base/components/RulesTab.tsx +8 -2
  165. package/src/components/knowledge-base/components/SectionHeader.scss +3 -18
  166. package/src/components/knowledge-base/components/SectionHeader.tsx +3 -7
  167. package/src/components/knowledge-base/components/SkillsTab.tsx +8 -3
  168. package/src/components/knowledge-base/components/SpecItem.scss +16 -7
  169. package/src/components/layout/@hooks/use-mobile-sidebar-modal.ts +190 -0
  170. package/src/components/layout/AppShell.scss +106 -6
  171. package/src/components/layout/AppShell.tsx +118 -10
  172. package/src/components/layout/PageShell.scss +41 -0
  173. package/src/components/layout/PageShell.tsx +32 -0
  174. package/src/components/layout/mobile-sidebar-constants.ts +1 -0
  175. package/src/components/nav-rail-compact-config.ts +114 -0
  176. package/src/components/nav-rail-items.tsx +181 -0
  177. package/src/components/sidebar/SessionContextMenu.tsx +3 -1
  178. package/src/components/sidebar/SessionItem.scss +62 -0
  179. package/src/components/sidebar/SessionItem.tsx +97 -52
  180. package/src/components/sidebar/SessionList.tsx +6 -0
  181. package/src/components/sidebar/SidebarHeader.scss +49 -0
  182. package/src/components/sidebar/SidebarHeader.tsx +27 -5
  183. package/src/components/sidebar/SidebarHeaderBatchActions.tsx +8 -4
  184. package/src/components/sidebar/SidebarHeaderSearchActions.tsx +6 -3
  185. package/src/components/sidebar/SidebarUtilityFooter.tsx +69 -0
  186. package/src/components/workspace/ContextFilePicker.tsx +12 -4
  187. package/src/hooks/chat/chat-session-workspace-draft.ts +25 -0
  188. package/src/hooks/chat/use-chat-scroll.ts +24 -7
  189. package/src/hooks/chat/use-chat-session-actions.ts +19 -2
  190. package/src/hooks/use-responsive-layout.ts +115 -0
  191. package/src/resources/adapters.ts +15 -0
  192. package/src/resources/locales/en.json +52 -0
  193. package/src/resources/locales/zh.json +52 -0
  194. package/src/routes/ChatRoute.scss +113 -14
  195. package/src/routes/ChatRoute.tsx +29 -35
  196. package/src/store/index.ts +2 -0
  197. package/dist/assets/channel-CBULQbJz.js +0 -1
  198. package/dist/assets/clone-dkS7LczW.js +0 -1
  199. package/dist/assets/flowDiagram-v2-4f6560a1-1miffU4x.js +0 -1
  200. package/dist/assets/index-D_XqxIvp.css +0 -32
@@ -5,7 +5,7 @@ import type { DataNode } from 'antd/es/tree'
5
5
  import { useEffect, useState } from 'react'
6
6
  import { useTranslation } from 'react-i18next'
7
7
 
8
- import { listWorkspaceTree } from '#~/api/workspace'
8
+ import { listSessionWorkspaceTree, listWorkspaceTree } from '#~/api'
9
9
 
10
10
  export interface ContextPickerFile {
11
11
  path: string
@@ -60,12 +60,14 @@ const toPendingFiles = (paths: string[]): ContextPickerFile[] =>
60
60
 
61
61
  export function ContextFilePicker({
62
62
  open,
63
+ sessionId,
63
64
  selectedPaths,
64
65
  variant = 'modal',
65
66
  onCancel,
66
67
  onConfirm
67
68
  }: {
68
69
  open: boolean
70
+ sessionId?: string
69
71
  selectedPaths: string[]
70
72
  variant?: 'inline' | 'modal'
71
73
  onCancel: () => void
@@ -76,6 +78,12 @@ export function ContextFilePicker({
76
78
  const [checkedKeys, setCheckedKeys] = useState<string[]>(selectedPaths)
77
79
  const [treeData, setTreeData] = useState<ContextFileTreeNode[]>([])
78
80
  const [loadingRoot, setLoadingRoot] = useState(false)
81
+ const loadWorkspaceTree = async (path?: string) => {
82
+ if (sessionId != null && sessionId !== '') {
83
+ return await listSessionWorkspaceTree(sessionId, path)
84
+ }
85
+ return await listWorkspaceTree(path)
86
+ }
79
87
 
80
88
  useEffect(() => {
81
89
  if (!open) {
@@ -84,7 +92,7 @@ export function ContextFilePicker({
84
92
 
85
93
  setCheckedKeys(selectedPaths)
86
94
  setLoadingRoot(true)
87
- void listWorkspaceTree()
95
+ void loadWorkspaceTree()
88
96
  .then((result) => {
89
97
  setTreeData(toTreeNodes(result.entries))
90
98
  })
@@ -94,11 +102,11 @@ export function ContextFilePicker({
94
102
  .finally(() => {
95
103
  setLoadingRoot(false)
96
104
  })
97
- }, [message, open, selectedPaths, t])
105
+ }, [message, open, selectedPaths, sessionId, t])
98
106
 
99
107
  const loadData = async (node: DataNode) => {
100
108
  const path = String(node.key)
101
- const result = await listWorkspaceTree(path)
109
+ const result = await loadWorkspaceTree(path)
102
110
  setTreeData(prev => replaceNodeChildren(prev, path, toTreeNodes(result.entries)))
103
111
  }
104
112
 
@@ -0,0 +1,25 @@
1
+ import type { ConfigResponse, GitBranchKind } from '@vibe-forge/types'
2
+
3
+ export interface ChatSessionWorkspaceDraftBranch {
4
+ name: string
5
+ kind?: GitBranchKind
6
+ mode: 'checkout' | 'create'
7
+ }
8
+
9
+ export interface ChatSessionWorkspaceDraft {
10
+ createWorktree: boolean
11
+ branch?: ChatSessionWorkspaceDraftBranch
12
+ }
13
+
14
+ export const buildChatSessionWorkspaceDraft = (createWorktree = true): ChatSessionWorkspaceDraft => ({
15
+ createWorktree
16
+ })
17
+
18
+ export const getChatSessionWorkspaceDraftFromConfig = (
19
+ configRes?: ConfigResponse
20
+ ): ChatSessionWorkspaceDraft => {
21
+ const createWorktree = configRes?.sources?.merged?.conversation?.createSessionWorktree
22
+ return buildChatSessionWorkspaceDraft(createWorktree ?? true)
23
+ }
24
+
25
+ export const DEFAULT_CHAT_SESSION_WORKSPACE_DRAFT: ChatSessionWorkspaceDraft = buildChatSessionWorkspaceDraft()
@@ -6,6 +6,7 @@ export function useChatScroll({ contentVersion }: { contentVersion: number }) {
6
6
  const messagesEndRef = useRef<HTMLDivElement>(null)
7
7
  const messagesContainerRef = useRef<HTMLDivElement>(null)
8
8
  const messagesContentRef = useRef<HTMLDivElement>(null)
9
+ const scrollTimeoutRef = useRef<number | null>(null)
9
10
  const [showScrollBottom, setShowScrollBottom] = useState(false)
10
11
 
11
12
  const updateScrollState = useCallback(() => {
@@ -15,16 +16,30 @@ export function useChatScroll({ contentVersion }: { contentVersion: number }) {
15
16
  setShowScrollBottom(distanceToBottom > SCROLL_THRESHOLD)
16
17
  }, [])
17
18
 
19
+ const clearScrollTimeout = useCallback(() => {
20
+ if (scrollTimeoutRef.current == null) {
21
+ return
22
+ }
23
+
24
+ window.clearTimeout(scrollTimeoutRef.current)
25
+ scrollTimeoutRef.current = null
26
+ }, [])
27
+
18
28
  const scrollToBottom = useCallback((behavior: ScrollBehavior = 'smooth') => {
19
- setTimeout(() => {
20
- if (messagesContainerRef.current) {
21
- messagesContainerRef.current.scrollTo({
22
- top: messagesContainerRef.current.scrollHeight,
23
- behavior
24
- })
29
+ clearScrollTimeout()
30
+ scrollTimeoutRef.current = window.setTimeout(() => {
31
+ const container = messagesContainerRef.current
32
+ scrollTimeoutRef.current = null
33
+ if (!container) {
34
+ return
25
35
  }
36
+
37
+ container.scrollTo({
38
+ top: container.scrollHeight,
39
+ behavior
40
+ })
26
41
  }, 50)
27
- }, [])
42
+ }, [clearScrollTimeout])
28
43
 
29
44
  useEffect(() => {
30
45
  const container = messagesContainerRef.current
@@ -41,6 +56,8 @@ export function useChatScroll({ contentVersion }: { contentVersion: number }) {
41
56
  updateScrollState()
42
57
  }, [contentVersion, updateScrollState])
43
58
 
59
+ useEffect(() => clearScrollTimeout, [clearScrollTimeout])
60
+
44
61
  return {
45
62
  messagesEndRef,
46
63
  messagesContainerRef,
@@ -16,6 +16,7 @@ import {
16
16
  } from '#~/api.js'
17
17
  import { connectionManager } from '#~/connectionManager.js'
18
18
  import type { ChatMessageContent, Session, SessionQueuedMessageMode } from '@vibe-forge/core'
19
+ import type { ChatSessionWorkspaceDraft } from './chat-session-workspace-draft'
19
20
  import type { ChatEffort } from './use-chat-effort'
20
21
  import type { PermissionMode } from './use-chat-permission-mode'
21
22
 
@@ -26,6 +27,7 @@ export function useChatSessionActions({
26
27
  effort,
27
28
  permissionMode,
28
29
  adapter,
30
+ workspaceDraft,
29
31
  onClearMessages
30
32
  }: {
31
33
  session?: Session
@@ -34,6 +36,7 @@ export function useChatSessionActions({
34
36
  effort: ChatEffort
35
37
  permissionMode: PermissionMode
36
38
  adapter?: string
39
+ workspaceDraft?: ChatSessionWorkspaceDraft
37
40
  onClearMessages: () => void
38
41
  }) {
39
42
  const { message } = App.useApp()
@@ -83,7 +86,13 @@ export function useChatSessionActions({
83
86
  const { session: newSession } = await createSession(undefined, text.trim(), undefined, modelForQuery, {
84
87
  effort: effort === 'default' ? undefined : effort,
85
88
  permissionMode,
86
- adapter
89
+ adapter,
90
+ workspace: workspaceDraft == null
91
+ ? undefined
92
+ : {
93
+ createWorktree: workspaceDraft.createWorktree,
94
+ branch: workspaceDraft.branch
95
+ }
87
96
  })
88
97
 
89
98
  await insertSessionIntoCache(newSession)
@@ -112,6 +121,7 @@ export function useChatSessionActions({
112
121
  navigateWithCurrentSearch,
113
122
  effort,
114
123
  permissionMode,
124
+ workspaceDraft,
115
125
  modelForQuery,
116
126
  session?.id,
117
127
  t
@@ -130,7 +140,13 @@ export function useChatSessionActions({
130
140
  const { session: newSession } = await createSession(undefined, undefined, content, modelForQuery, {
131
141
  effort: effort === 'default' ? undefined : effort,
132
142
  permissionMode,
133
- adapter
143
+ adapter,
144
+ workspace: workspaceDraft == null
145
+ ? undefined
146
+ : {
147
+ createWorktree: workspaceDraft.createWorktree,
148
+ branch: workspaceDraft.branch
149
+ }
134
150
  })
135
151
 
136
152
  await insertSessionIntoCache(newSession)
@@ -159,6 +175,7 @@ export function useChatSessionActions({
159
175
  message,
160
176
  effort,
161
177
  permissionMode,
178
+ workspaceDraft,
162
179
  modelForQuery,
163
180
  session?.id,
164
181
  t
@@ -0,0 +1,115 @@
1
+ import { useSyncExternalStore } from 'react'
2
+
3
+ const COMPACT_LAYOUT_QUERY = '(max-width: 960px)'
4
+ const TOUCH_INTERACTION_QUERY = '(hover: none), (pointer: coarse)'
5
+
6
+ export interface ResponsiveLayoutState {
7
+ isCompactLayout: boolean
8
+ isTouchInteraction: boolean
9
+ }
10
+
11
+ const DEFAULT_RESPONSIVE_LAYOUT: ResponsiveLayoutState = {
12
+ isCompactLayout: false,
13
+ isTouchInteraction: false
14
+ }
15
+
16
+ const listeners = new Set<() => void>()
17
+
18
+ let compactMediaQuery: MediaQueryList | null = null
19
+ let touchMediaQuery: MediaQueryList | null = null
20
+ let currentSnapshot = DEFAULT_RESPONSIVE_LAYOUT
21
+ let mediaListenersInstalled = false
22
+ let removeMediaQueryListeners: (() => void) | null = null
23
+
24
+ const getMediaQueryMatch = (query: string) => {
25
+ if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
26
+ return false
27
+ }
28
+
29
+ return window.matchMedia(query).matches
30
+ }
31
+
32
+ const readResponsiveLayoutSnapshot = (): ResponsiveLayoutState => ({
33
+ isCompactLayout: compactMediaQuery?.matches ?? getMediaQueryMatch(COMPACT_LAYOUT_QUERY),
34
+ isTouchInteraction: touchMediaQuery?.matches ?? getMediaQueryMatch(TOUCH_INTERACTION_QUERY)
35
+ })
36
+
37
+ const isSameResponsiveLayoutSnapshot = (
38
+ left: ResponsiveLayoutState,
39
+ right: ResponsiveLayoutState
40
+ ) => left.isCompactLayout === right.isCompactLayout && left.isTouchInteraction === right.isTouchInteraction
41
+
42
+ const refreshResponsiveLayoutSnapshot = () => {
43
+ const nextSnapshot = readResponsiveLayoutSnapshot()
44
+ if (!isSameResponsiveLayoutSnapshot(currentSnapshot, nextSnapshot)) {
45
+ currentSnapshot = nextSnapshot
46
+ }
47
+
48
+ return currentSnapshot
49
+ }
50
+
51
+ const emitIfChanged = () => {
52
+ const nextSnapshot = readResponsiveLayoutSnapshot()
53
+ if (isSameResponsiveLayoutSnapshot(currentSnapshot, nextSnapshot)) {
54
+ return
55
+ }
56
+
57
+ currentSnapshot = nextSnapshot
58
+ listeners.forEach(listener => listener())
59
+ }
60
+
61
+ const addMediaQueryListener = (mediaQuery: MediaQueryList, listener: () => void) => {
62
+ if (typeof mediaQuery.addEventListener === 'function') {
63
+ mediaQuery.addEventListener('change', listener)
64
+ return () => mediaQuery.removeEventListener('change', listener)
65
+ }
66
+
67
+ mediaQuery.addListener(listener)
68
+ return () => mediaQuery.removeListener(listener)
69
+ }
70
+
71
+ const ensureResponsiveLayoutListeners = () => {
72
+ if (
73
+ mediaListenersInstalled ||
74
+ typeof window === 'undefined' ||
75
+ typeof window.matchMedia !== 'function'
76
+ ) {
77
+ return
78
+ }
79
+
80
+ compactMediaQuery = window.matchMedia(COMPACT_LAYOUT_QUERY)
81
+ touchMediaQuery = window.matchMedia(TOUCH_INTERACTION_QUERY)
82
+ currentSnapshot = refreshResponsiveLayoutSnapshot()
83
+ const removeCompactMediaQueryListener = addMediaQueryListener(compactMediaQuery, emitIfChanged)
84
+ const removeTouchMediaQueryListener = addMediaQueryListener(touchMediaQuery, emitIfChanged)
85
+ removeMediaQueryListeners = () => {
86
+ removeCompactMediaQueryListener()
87
+ removeTouchMediaQueryListener()
88
+ compactMediaQuery = null
89
+ touchMediaQuery = null
90
+ mediaListenersInstalled = false
91
+ }
92
+ mediaListenersInstalled = true
93
+ }
94
+
95
+ const subscribeResponsiveLayout = (listener: () => void) => {
96
+ ensureResponsiveLayoutListeners()
97
+ listeners.add(listener)
98
+ return () => {
99
+ listeners.delete(listener)
100
+ if (listeners.size === 0) {
101
+ removeMediaQueryListeners?.()
102
+ removeMediaQueryListeners = null
103
+ }
104
+ }
105
+ }
106
+
107
+ const getResponsiveLayoutSnapshot = () => refreshResponsiveLayoutSnapshot()
108
+
109
+ export function useResponsiveLayout() {
110
+ return useSyncExternalStore(
111
+ subscribeResponsiveLayout,
112
+ getResponsiveLayoutSnapshot,
113
+ () => DEFAULT_RESPONSIVE_LAYOUT
114
+ )
115
+ }
@@ -3,6 +3,9 @@ import {
3
3
  adapterIcon as claudeCodeIcon
4
4
  } from '@vibe-forge/adapter-claude-code/icon'
5
5
  import { adapterDisplayName as codexDisplayName, adapterIcon as codexIcon } from '@vibe-forge/adapter-codex/icon'
6
+ import { adapterDisplayName as copilotDisplayName, adapterIcon as copilotIcon } from '@vibe-forge/adapter-copilot/icon'
7
+ import { adapterDisplayName as geminiDisplayName, adapterIcon as geminiIcon } from '@vibe-forge/adapter-gemini/icon'
8
+ import { adapterDisplayName as kimiDisplayName, adapterIcon as kimiIcon } from '@vibe-forge/adapter-kimi/icon'
6
9
  import {
7
10
  adapterDisplayName as opencodeDisplayName,
8
11
  adapterIcon as opencodeIcon
@@ -17,6 +20,18 @@ export const adapterDisplayMap = {
17
20
  title: codexDisplayName,
18
21
  icon: codexIcon
19
22
  },
23
+ copilot: {
24
+ title: copilotDisplayName,
25
+ icon: copilotIcon
26
+ },
27
+ gemini: {
28
+ title: geminiDisplayName,
29
+ icon: geminiIcon
30
+ },
31
+ kimi: {
32
+ title: kimiDisplayName,
33
+ icon: kimiIcon
34
+ },
20
35
  opencode: {
21
36
  title: opencodeDisplayName,
22
37
  icon: opencodeIcon
@@ -165,6 +165,8 @@
165
165
  "lastRunAt": "Last run: {{time}}",
166
166
  "noRunYet": "No runs yet",
167
167
  "emptyRules": "No automation rules",
168
+ "mobileRules": "Rules",
169
+ "mobileDetails": "Details",
168
170
  "selectRule": "Select an automation rule",
169
171
  "ruleId": "Rule ID",
170
172
  "createdAt": "Created At",
@@ -209,6 +211,8 @@
209
211
  "caseId": "Case:",
210
212
  "searchPlaceholder": "Search category, title, or summary",
211
213
  "emptyCases": "No benchmark cases",
214
+ "mobileCases": "Cases",
215
+ "mobileDetails": "Details",
212
216
  "noSummary": "No summary",
213
217
  "configTitle": "Case Config",
214
218
  "baseCommit": "Base Commit",
@@ -467,8 +471,51 @@
467
471
  "gitBranchSwitcher": "Switch branch",
468
472
  "gitOperations": "Git actions",
469
473
  "gitWorktree": "Worktree",
474
+ "statusBarTitle": "Session status",
475
+ "statusBarSessionSubtitle": "Track the current session workspace and git context here.",
476
+ "statusBarDraftSubtitle": "Set up the next session workspace before sending the first message.",
477
+ "sessionWorkspace": "Session workspace",
478
+ "sessionWorkspaceManaged": "Managed worktree",
479
+ "sessionWorkspaceShared": "Shared workspace",
480
+ "sessionWorkspaceExternal": "External workspace",
481
+ "sessionWorkspaceDraftCreateWorktree": "Create worktree",
482
+ "sessionWorkspaceDraftStrategyLocal": "Local",
483
+ "sessionWorkspaceDraftStrategyManaged": "New worktree",
484
+ "sessionWorkspaceMenuCurrentWorktree": "Current worktree",
485
+ "sessionWorkspaceMenuWorktreeList": "Worktree list",
486
+ "sessionWorkspaceMenuLaunchInWorktree": "Start in worktree mode",
487
+ "sessionWorkspaceMenuTransferToLocal": "Transfer to local",
488
+ "sessionWorkspaceMenuCreateWorktree": "Create worktree",
489
+ "sessionWorkspaceDraftCreateWorktreeEnabled": "New sessions get an isolated managed worktree.",
490
+ "sessionWorkspaceDraftCreateWorktreeDisabled": "New sessions reuse the shared workspace directly.",
491
+ "sessionWorkspaceDraftCreateBranchLabel": "New branch: {{branch}}",
492
+ "sessionWorkspaceDraftGitUnavailable": "Git is unavailable for the current workspace.",
493
+ "sessionWorkspaceSearchWorktrees": "Search worktrees",
494
+ "sessionWorkspaceNoWorktrees": "No worktrees",
495
+ "sessionWorkspaceMode": "Mode",
496
+ "sessionWorkspaceState": "State",
497
+ "sessionWorkspaceBranch": "Branch",
498
+ "sessionWorkspacePath": "Path",
499
+ "sessionWorkspaceCleanup": "Cleanup",
500
+ "sessionWorkspaceCleanupDelete": "Deletes with session",
501
+ "sessionWorkspaceCleanupRetain": "Retained on delete",
502
+ "sessionWorkspaceBaseRef": "Base ref",
503
+ "sessionWorkspaceCurrentSession": "Current session",
504
+ "sessionWorkspaceRepositoryWorktrees": "Repository worktrees",
505
+ "sessionWorkspaceStateReady": "Ready",
506
+ "sessionWorkspaceStateProvisioning": "Provisioning",
507
+ "sessionWorkspaceStateDeleting": "Deleting",
508
+ "sessionWorkspaceStateDeleted": "Deleted",
509
+ "sessionWorkspaceStateBroken": "Broken",
510
+ "sessionWorkspaceCreateWorktreeSuccess": "Created a managed worktree for this session.",
511
+ "sessionWorkspaceTransferToLocalSuccess": "This worktree is now retained locally.",
512
+ "sessionWorkspaceForceDeleteTitle": "Delete dirty workspace?",
513
+ "sessionWorkspaceForceDeleteDescription": "This session workspace has uncommitted changes. Force delete the session and remove its bound worktree anyway?",
470
514
  "gitDetachedHead": "DETACHED",
471
515
  "gitSearchBranches": "Search local or remote branches",
516
+ "gitBranchViewMode": "Branch view mode",
517
+ "gitBranchViewTree": "Tree",
518
+ "gitBranchViewFlat": "Flat",
472
519
  "gitBranchesLocal": "Local branches",
473
520
  "gitBranchesWorktrees": "Other worktrees",
474
521
  "gitBranchesRemote": "Remote branches",
@@ -611,6 +658,7 @@
611
658
  "announcements": {
612
659
  "title": "Announcements"
613
660
  },
661
+ "moreCount": "{{count}} more",
614
662
  "specs": {
615
663
  "title": "Existing Flows",
616
664
  "params": "{{count}} params",
@@ -989,6 +1037,10 @@
989
1037
  "label": "Conversation Style",
990
1038
  "desc": "Controls the assistant’s interaction style"
991
1039
  },
1040
+ "createSessionWorktree": {
1041
+ "label": "Create worktree for new sessions",
1042
+ "desc": "Use a managed worktree by default when creating a new session"
1043
+ },
992
1044
  "customInstructions": {
993
1045
  "label": "Custom Instructions",
994
1046
  "desc": "Additional guidance for the assistant"
@@ -166,6 +166,8 @@
166
166
  "lastRunAt": "最近运行:{{time}}",
167
167
  "noRunYet": "暂无运行记录",
168
168
  "emptyRules": "暂无自动化任务",
169
+ "mobileRules": "规则",
170
+ "mobileDetails": "详情",
169
171
  "selectRule": "请选择一个自动化任务",
170
172
  "ruleId": "规则 ID",
171
173
  "createdAt": "创建时间",
@@ -210,6 +212,8 @@
210
212
  "caseId": "用例:",
211
213
  "searchPlaceholder": "搜索分类、标题或摘要",
212
214
  "emptyCases": "暂无 benchmark 用例",
215
+ "mobileCases": "用例",
216
+ "mobileDetails": "详情",
213
217
  "noSummary": "暂无摘要",
214
218
  "configTitle": "用例配置",
215
219
  "baseCommit": "基线 Commit",
@@ -468,8 +472,51 @@
468
472
  "gitBranchSwitcher": "切换分支",
469
473
  "gitOperations": "Git 操作",
470
474
  "gitWorktree": "Worktree",
475
+ "statusBarTitle": "会话状态",
476
+ "statusBarSessionSubtitle": "这里统一展示当前会话绑定的工作区和 Git 上下文。",
477
+ "statusBarDraftSubtitle": "发送第一条消息前,先把新会话的工作区准备好。",
478
+ "sessionWorkspace": "会话工作区",
479
+ "sessionWorkspaceManaged": "托管 worktree",
480
+ "sessionWorkspaceShared": "共享工作区",
481
+ "sessionWorkspaceExternal": "外部工作区",
482
+ "sessionWorkspaceDraftCreateWorktree": "创建工作树",
483
+ "sessionWorkspaceDraftStrategyLocal": "本地",
484
+ "sessionWorkspaceDraftStrategyManaged": "新工作树",
485
+ "sessionWorkspaceMenuCurrentWorktree": "当前工作树",
486
+ "sessionWorkspaceMenuWorktreeList": "工作树列表",
487
+ "sessionWorkspaceMenuLaunchInWorktree": "以工作树模式启动",
488
+ "sessionWorkspaceMenuTransferToLocal": "转移到本地",
489
+ "sessionWorkspaceMenuCreateWorktree": "创建工作树",
490
+ "sessionWorkspaceDraftCreateWorktreeEnabled": "新会话会自动拿到一个隔离的托管 worktree。",
491
+ "sessionWorkspaceDraftCreateWorktreeDisabled": "新会话会直接复用当前共享工作区。",
492
+ "sessionWorkspaceDraftCreateBranchLabel": "新分支:{{branch}}",
493
+ "sessionWorkspaceDraftGitUnavailable": "当前工作区不可用 Git。",
494
+ "sessionWorkspaceSearchWorktrees": "搜索工作树",
495
+ "sessionWorkspaceNoWorktrees": "暂无工作树",
496
+ "sessionWorkspaceMode": "模式",
497
+ "sessionWorkspaceState": "状态",
498
+ "sessionWorkspaceBranch": "分支",
499
+ "sessionWorkspacePath": "路径",
500
+ "sessionWorkspaceCleanup": "清理策略",
501
+ "sessionWorkspaceCleanupDelete": "删除会话时一并删除",
502
+ "sessionWorkspaceCleanupRetain": "删除会话时保留",
503
+ "sessionWorkspaceBaseRef": "基线引用",
504
+ "sessionWorkspaceCurrentSession": "当前会话",
505
+ "sessionWorkspaceRepositoryWorktrees": "仓库 worktree",
506
+ "sessionWorkspaceStateReady": "就绪",
507
+ "sessionWorkspaceStateProvisioning": "创建中",
508
+ "sessionWorkspaceStateDeleting": "删除中",
509
+ "sessionWorkspaceStateDeleted": "已删除",
510
+ "sessionWorkspaceStateBroken": "异常",
511
+ "sessionWorkspaceCreateWorktreeSuccess": "已为当前会话创建托管工作树。",
512
+ "sessionWorkspaceTransferToLocalSuccess": "当前工作树已转为本地保留。",
513
+ "sessionWorkspaceForceDeleteTitle": "删除有改动的工作区?",
514
+ "sessionWorkspaceForceDeleteDescription": "这个会话工作区里还有未提交的改动。是否仍然强制删除会话,并移除它绑定的 worktree?",
471
515
  "gitDetachedHead": "DETACHED",
472
516
  "gitSearchBranches": "搜索本地或远程分支",
517
+ "gitBranchViewMode": "分支展示模式",
518
+ "gitBranchViewTree": "树状",
519
+ "gitBranchViewFlat": "平铺",
473
520
  "gitBranchesLocal": "本地分支",
474
521
  "gitBranchesWorktrees": "其他 worktree",
475
522
  "gitBranchesRemote": "远程分支",
@@ -612,6 +659,7 @@
612
659
  "announcements": {
613
660
  "title": "最新公告"
614
661
  },
662
+ "moreCount": "还有 {{count}} 项",
615
663
  "specs": {
616
664
  "title": "已有流程",
617
665
  "params": "{{count}} 个参数",
@@ -990,6 +1038,10 @@
990
1038
  "label": "对话风格",
991
1039
  "desc": "控制助手的对话风格"
992
1040
  },
1041
+ "createSessionWorktree": {
1042
+ "label": "新建会话时创建工作树",
1043
+ "desc": "默认给新会话分配一个托管 worktree"
1044
+ },
993
1045
  "customInstructions": {
994
1046
  "label": "自定义指令",
995
1047
  "desc": "为对话提供额外的提示与约束"