@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
@@ -8,12 +8,12 @@ import { useAtomValue } from 'jotai'
8
8
  import { useEffect, useMemo, useRef, useState } from 'react'
9
9
  import { useTranslation } from 'react-i18next'
10
10
 
11
- import { deleteSession, getApiErrorMessage, updateSession } from '../../api'
11
+ import { ApiError, deleteSession, getApiErrorMessage, updateSession } from '../../api'
12
+ import { useResponsiveLayout } from '../../hooks/use-responsive-layout'
12
13
  import { useQueryParams } from '../../hooks/useQueryParams'
13
14
  import { isSidebarCollapsedAtom, isSidebarResizingAtom } from '../../store/index'
14
15
  import { ConfigSectionPanel } from '../config'
15
16
  import type { FieldSpec } from '../config/configSchema'
16
- import { ChatGitControls } from './git-controls/ChatGitControls'
17
17
  import {
18
18
  formatToolLabel,
19
19
  getSessionAssetWarnings,
@@ -52,6 +52,8 @@ export function ChatHeader({
52
52
  lastUserMessage,
53
53
  activeView,
54
54
  isTerminalOpen,
55
+ onCreateSession,
56
+ onOpenSidebar,
55
57
  onViewChange,
56
58
  onToggleTerminal
57
59
  }: {
@@ -66,11 +68,14 @@ export function ChatHeader({
66
68
  lastUserMessage?: string
67
69
  activeView: ChatHeaderView
68
70
  isTerminalOpen: boolean
71
+ onCreateSession?: () => void
72
+ onOpenSidebar?: () => void
69
73
  onViewChange: (view: ChatHeaderView) => void
70
74
  onToggleTerminal: () => void
71
75
  }) {
72
76
  const { t } = useTranslation()
73
77
  const { message } = App.useApp()
78
+ const { isCompactLayout, isTouchInteraction } = useResponsiveLayout()
74
79
  const isSidebarCollapsed = useAtomValue(isSidebarCollapsedAtom)
75
80
  const isResizing = useAtomValue(isSidebarResizingAtom)
76
81
  const { searchParams, update: updateQuery } = useQueryParams<{ debug: string }>({
@@ -84,6 +89,8 @@ export function ChatHeader({
84
89
  const hasDebugQuery = searchParams.has('debug')
85
90
  const isDebugMode = searchParams.get('debug') === 'true'
86
91
  const shouldShowDebugButton = hasDebugQuery
92
+ const hasSession = sessionId != null && sessionId !== ''
93
+ const resolveTooltipTitle = (title: string) => isTouchInteraction ? undefined : title
87
94
 
88
95
  const summary = sessionInfo?.type === 'summary' ? sessionInfo.summary : null
89
96
  const title = (sessionInfo?.type === 'init' ? sessionInfo.title : null) ?? sessionTitle
@@ -105,6 +112,7 @@ export function ChatHeader({
105
112
  { value: 'timeline' as const, icon: 'timeline', title: t('chat.viewTimeline') },
106
113
  { value: 'settings' as const, icon: 'tune', title: t('chat.viewSettings') }
107
114
  ]
115
+ const activeViewItem = viewItems.find((item) => item.value === activeView) ?? viewItems[0]!
108
116
 
109
117
  const handleToggleStar = async () => {
110
118
  if (sessionId == null || sessionId === '') return
@@ -148,6 +156,37 @@ export function ChatHeader({
148
156
  }
149
157
  }
150
158
  ]
159
+ const compactViewItems: MenuProps['items'] = viewItems.map((item) => ({
160
+ key: `view:${item.value}`,
161
+ label: item.title,
162
+ icon: <span className={`material-symbols-rounded chat-header-icon ${activeView === item.value ? 'is-filled' : ''}`}>
163
+ {item.icon}
164
+ </span>,
165
+ onClick: () => {
166
+ onViewChange(item.value)
167
+ }
168
+ }))
169
+ const compactMoreItems: MenuProps['items'] = [
170
+ {
171
+ key: 'terminal',
172
+ label: t('chat.viewTerminal'),
173
+ icon: <span className={`material-symbols-rounded chat-header-icon ${isTerminalOpen ? 'is-filled' : ''}`}>
174
+ terminal
175
+ </span>,
176
+ onClick: onToggleTerminal
177
+ },
178
+ ...moreItems,
179
+ ...(shouldShowDebugButton
180
+ ? [{
181
+ key: 'debug',
182
+ label: isDebugMode ? t('chat.debugDisable') : t('chat.debugEnable'),
183
+ icon: <span className={`material-symbols-rounded chat-header-icon ${isDebugMode ? 'is-filled' : ''}`}>
184
+ bug_report
185
+ </span>,
186
+ onClick: toggleDebugMode
187
+ }]
188
+ : [])
189
+ ]
151
190
 
152
191
  useEffect(() => {
153
192
  return () => {
@@ -189,8 +228,40 @@ export function ChatHeader({
189
228
  }
190
229
 
191
230
  return (
192
- <div className={`chat-header ${isSidebarCollapsed ? 'is-collapsed' : ''} ${isResizing ? 'is-resizing' : ''}`}>
231
+ <div
232
+ className={`chat-header ${isSidebarCollapsed ? 'is-collapsed' : ''} ${isResizing ? 'is-resizing' : ''} ${
233
+ isCompactLayout ? 'is-compact' : ''
234
+ }`}
235
+ >
193
236
  <div className='chat-header-main'>
237
+ {isCompactLayout && (
238
+ <div className='chat-header-leading-actions'>
239
+ {onOpenSidebar != null && (
240
+ <Tooltip title={resolveTooltipTitle(t('common.sessions'))}>
241
+ <Button
242
+ type='text'
243
+ className='chat-header-action-button'
244
+ title={t('common.sessions')}
245
+ aria-label={t('common.sessions')}
246
+ onClick={onOpenSidebar}
247
+ icon={<span className='chat-header-view-option material-symbols-rounded'>menu</span>}
248
+ />
249
+ </Tooltip>
250
+ )}
251
+ {onCreateSession != null && (
252
+ <Tooltip title={resolveTooltipTitle(t('common.newChat'))}>
253
+ <Button
254
+ type='text'
255
+ className={`chat-header-action-button ${!hasSession ? 'is-active' : ''}`}
256
+ title={t('common.newChat')}
257
+ aria-label={t('common.newChat')}
258
+ onClick={onCreateSession}
259
+ icon={<span className='chat-header-view-option material-symbols-rounded'>edit_square</span>}
260
+ />
261
+ </Tooltip>
262
+ )}
263
+ </div>
264
+ )}
194
265
  <div className='chat-header-info'>
195
266
  <div
196
267
  className='chat-header-title'
@@ -202,56 +273,92 @@ export function ChatHeader({
202
273
  </div>
203
274
 
204
275
  <div className='chat-header-actions'>
205
- {sessionId != null && sessionId !== '' && (
206
- <ChatGitControls sessionId={sessionId} />
207
- )}
208
- {viewItems.map(item => (
209
- <Tooltip key={item.value} title={item.title}>
210
- <Button
211
- type='text'
212
- className={`chat-header-action-button ${activeView === item.value ? 'is-active' : ''}`}
213
- title={item.title}
214
- aria-label={item.title}
215
- onClick={() => {
216
- onViewChange(item.value)
217
- }}
218
- icon={<span className='chat-header-view-option material-symbols-rounded'>{item.icon}</span>}
219
- />
220
- </Tooltip>
221
- ))}
222
- <Tooltip title={t('chat.viewTerminal')}>
223
- <Button
224
- type='text'
225
- className={`chat-header-action-button ${isTerminalOpen ? 'is-active' : ''}`}
226
- title={t('chat.viewTerminal')}
227
- aria-label={t('chat.viewTerminal')}
228
- onClick={onToggleTerminal}
229
- icon={<span className='chat-header-view-option material-symbols-rounded'>terminal</span>}
230
- />
231
- </Tooltip>
232
- {shouldShowDebugButton && (
233
- <Tooltip title={isDebugMode ? t('chat.debugDisable') : t('chat.debugEnable')}>
234
- <Button
235
- type='text'
236
- className={`chat-header-action-button ${isDebugMode ? 'is-debug-active' : ''}`}
237
- title={isDebugMode ? t('chat.debugDisable') : t('chat.debugEnable')}
238
- aria-label={isDebugMode ? t('chat.debugDisable') : t('chat.debugEnable')}
239
- onClick={toggleDebugMode}
240
- icon={<span className='chat-header-view-option material-symbols-rounded'>bug_report</span>}
241
- />
242
- </Tooltip>
243
- )}
244
- <Tooltip title={t('common.moreActions')}>
245
- <Dropdown menu={{ items: moreItems }} placement='bottomRight' trigger={['click']}>
246
- <Button
247
- type='text'
248
- className='chat-header-action-button'
249
- title={t('common.moreActions')}
250
- aria-label={t('common.moreActions')}
251
- icon={<span className='chat-header-view-option material-symbols-rounded'>more_vert</span>}
252
- />
253
- </Dropdown>
254
- </Tooltip>
276
+ {isCompactLayout
277
+ ? (
278
+ <>
279
+ {hasSession && (
280
+ <Tooltip title={resolveTooltipTitle(activeViewItem.title)}>
281
+ <Dropdown menu={{ items: compactViewItems }} placement='bottomRight' trigger={['click']}>
282
+ <Button
283
+ type='text'
284
+ className='chat-header-action-button'
285
+ title={activeViewItem.title}
286
+ aria-label={activeViewItem.title}
287
+ icon={
288
+ <span className='chat-header-view-option material-symbols-rounded'>
289
+ {activeViewItem.icon}
290
+ </span>
291
+ }
292
+ />
293
+ </Dropdown>
294
+ </Tooltip>
295
+ )}
296
+ {hasSession && (
297
+ <Tooltip title={resolveTooltipTitle(t('common.moreActions'))}>
298
+ <Dropdown menu={{ items: compactMoreItems }} placement='bottomRight' trigger={['click']}>
299
+ <Button
300
+ type='text'
301
+ className='chat-header-action-button'
302
+ title={t('common.moreActions')}
303
+ aria-label={t('common.moreActions')}
304
+ icon={<span className='chat-header-view-option material-symbols-rounded'>more_vert</span>}
305
+ />
306
+ </Dropdown>
307
+ </Tooltip>
308
+ )}
309
+ </>
310
+ )
311
+ : (
312
+ <>
313
+ {viewItems.map(item => (
314
+ <Tooltip key={item.value} title={resolveTooltipTitle(item.title)}>
315
+ <Button
316
+ type='text'
317
+ className={`chat-header-action-button ${activeView === item.value ? 'is-active' : ''}`}
318
+ title={item.title}
319
+ aria-label={item.title}
320
+ onClick={() => {
321
+ onViewChange(item.value)
322
+ }}
323
+ icon={<span className='chat-header-view-option material-symbols-rounded'>{item.icon}</span>}
324
+ />
325
+ </Tooltip>
326
+ ))}
327
+ <Tooltip title={resolveTooltipTitle(t('chat.viewTerminal'))}>
328
+ <Button
329
+ type='text'
330
+ className={`chat-header-action-button ${isTerminalOpen ? 'is-active' : ''}`}
331
+ title={t('chat.viewTerminal')}
332
+ aria-label={t('chat.viewTerminal')}
333
+ onClick={onToggleTerminal}
334
+ icon={<span className='chat-header-view-option material-symbols-rounded'>terminal</span>}
335
+ />
336
+ </Tooltip>
337
+ {shouldShowDebugButton && (
338
+ <Tooltip title={resolveTooltipTitle(isDebugMode ? t('chat.debugDisable') : t('chat.debugEnable'))}>
339
+ <Button
340
+ type='text'
341
+ className={`chat-header-action-button ${isDebugMode ? 'is-debug-active' : ''}`}
342
+ title={isDebugMode ? t('chat.debugDisable') : t('chat.debugEnable')}
343
+ aria-label={isDebugMode ? t('chat.debugDisable') : t('chat.debugEnable')}
344
+ onClick={toggleDebugMode}
345
+ icon={<span className='chat-header-view-option material-symbols-rounded'>bug_report</span>}
346
+ />
347
+ </Tooltip>
348
+ )}
349
+ <Tooltip title={resolveTooltipTitle(t('common.moreActions'))}>
350
+ <Dropdown menu={{ items: moreItems }} placement='bottomRight' trigger={['click']}>
351
+ <Button
352
+ type='text'
353
+ className='chat-header-action-button'
354
+ title={t('common.moreActions')}
355
+ aria-label={t('common.moreActions')}
356
+ icon={<span className='chat-header-view-option material-symbols-rounded'>more_vert</span>}
357
+ />
358
+ </Dropdown>
359
+ </Tooltip>
360
+ </>
361
+ )}
255
362
  </div>
256
363
  </div>
257
364
  )
@@ -267,6 +374,7 @@ export function SessionSettingsPanel({
267
374
  onClose: () => void
268
375
  }) {
269
376
  const { t } = useTranslation()
377
+ const { isCompactLayout } = useResponsiveLayout()
270
378
  const { message, modal } = App.useApp()
271
379
  const { searchParams } = useQueryParams<{ debug: string }>({ keys: ['debug'] })
272
380
  const isDebugMode = searchParams.get('debug') === 'true'
@@ -318,6 +426,27 @@ export function SessionSettingsPanel({
318
426
  const toolGroups = useMemo(() => getSessionToolGroups(sessionInfo), [sessionInfo])
319
427
  const assetWarnings = useMemo(() => getSessionAssetWarnings(sessionInfo), [sessionInfo])
320
428
  const selectionWarnings = useMemo(() => getSessionSelectionWarnings(sessionInfo), [sessionInfo])
429
+
430
+ useEffect(() => {
431
+ if (!isCompactLayout || toolGroups.length === 0) {
432
+ return
433
+ }
434
+
435
+ setCollapsedToolGroupKeys((prev) => {
436
+ const next = { ...prev }
437
+ let changed = false
438
+
439
+ for (const group of toolGroups) {
440
+ if (!(group.key in next)) {
441
+ next[group.key] = true
442
+ changed = true
443
+ }
444
+ }
445
+
446
+ return changed ? next : prev
447
+ })
448
+ }, [isCompactLayout, toolGroups])
449
+
321
450
  const debugItems = useMemo<SessionDebugItem[]>(() => {
322
451
  const emptyValue = t('chat.timelineEmptyValue')
323
452
  const booleanValue = (value: boolean | undefined) =>
@@ -491,6 +620,12 @@ export function SessionSettingsPanel({
491
620
  }
492
621
 
493
622
  const handleDelete = () => {
623
+ const runDelete = async (force = false) => {
624
+ await deleteSession(sessionId, { force })
625
+ void message.success(t('common.deleteSuccess'))
626
+ onClose()
627
+ }
628
+
494
629
  modal.confirm({
495
630
  title: t('common.deleteSession'),
496
631
  content: t('common.deleteSessionConfirm'),
@@ -499,10 +634,25 @@ export function SessionSettingsPanel({
499
634
  cancelText: t('common.cancel'),
500
635
  onOk: async () => {
501
636
  try {
502
- await deleteSession(sessionId)
503
- void message.success(t('common.deleteSuccess'))
504
- onClose()
637
+ await runDelete()
505
638
  } catch (err) {
639
+ if (err instanceof ApiError && err.code === 'session_worktree_not_clean') {
640
+ modal.confirm({
641
+ title: t('chat.sessionWorkspaceForceDeleteTitle'),
642
+ content: t('chat.sessionWorkspaceForceDeleteDescription'),
643
+ okText: t('common.delete'),
644
+ okType: 'danger',
645
+ cancelText: t('common.cancel'),
646
+ onOk: async () => {
647
+ try {
648
+ await runDelete(true)
649
+ } catch (forceError) {
650
+ void message.error(getApiErrorMessage(forceError, t('common.deleteFailed')))
651
+ }
652
+ }
653
+ })
654
+ return
655
+ }
506
656
  void message.error(getApiErrorMessage(err, t('common.deleteFailed')))
507
657
  }
508
658
  }
@@ -2,6 +2,7 @@ import { App } from 'antd'
2
2
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
3
3
  import { useTranslation } from 'react-i18next'
4
4
  import { useLocation } from 'react-router-dom'
5
+ import useSWR from 'swr'
5
6
 
6
7
  import type {
7
8
  AskUserQuestionParams,
@@ -12,13 +13,19 @@ import type {
12
13
  SessionQueuedMessage,
13
14
  SessionQueuedMessageMode
14
15
  } from '@vibe-forge/core'
15
- import type { SessionInfo } from '@vibe-forge/types'
16
+ import type { ConfigResponse, SessionInfo } from '@vibe-forge/types'
16
17
 
18
+ import { getConfig } from '#~/api'
19
+ import {
20
+ DEFAULT_CHAT_SESSION_WORKSPACE_DRAFT,
21
+ getChatSessionWorkspaceDraftFromConfig
22
+ } from '#~/hooks/chat/chat-session-workspace-draft'
17
23
  import type { ChatEffort } from '#~/hooks/chat/use-chat-effort'
18
24
  import type { ModelSelectMenuGroup, ModelSelectOption } from '#~/hooks/chat/use-chat-model-adapter-selection'
19
25
  import type { PermissionMode } from '#~/hooks/chat/use-chat-permission-mode'
20
26
  import { useChatScroll } from '#~/hooks/chat/use-chat-scroll'
21
27
  import { useChatSessionActions } from '#~/hooks/chat/use-chat-session-actions'
28
+ import { useResponsiveLayout } from '#~/hooks/use-responsive-layout'
22
29
  import { getLoopedIndex } from '#~/hooks/use-roving-focus-list'
23
30
  import { CurrentTodoList } from './CurrentTodoList'
24
31
  import { NewSessionGuide } from './NewSessionGuide'
@@ -30,6 +37,7 @@ import { buildMessageTurns } from './messages/message-turns'
30
37
  import { processMessages } from './messages/message-utils'
31
38
  import { SenderInteractionPanel } from './sender/@components/sender-interaction-panel/SenderInteractionPanel'
32
39
  import { Sender } from './sender/Sender'
40
+ import { ChatStatusBar } from './status-bar/ChatStatusBar'
33
41
  import { ToolGroup } from './tools/core/ToolGroup'
34
42
 
35
43
  export function ChatHistoryView({
@@ -106,6 +114,16 @@ export function ChatHistoryView({
106
114
  const { t } = useTranslation()
107
115
  const { message } = App.useApp()
108
116
  const location = useLocation()
117
+ const { isCompactLayout, isTouchInteraction } = useResponsiveLayout()
118
+ const { data: configRes } = useSWR<ConfigResponse>('/api/config', getConfig)
119
+ const configWorkspaceDraft = useMemo(
120
+ () => getChatSessionWorkspaceDraftFromConfig(configRes),
121
+ [configRes]
122
+ )
123
+ const workspaceDraftDirtyRef = useRef(false)
124
+ const [workspaceDraft, setWorkspaceDraft] = useState(() => ({
125
+ ...DEFAULT_CHAT_SESSION_WORKSPACE_DRAFT
126
+ }))
109
127
  const historyRenderCount = messages.length + historyStatusNotices.length
110
128
  const { messagesEndRef, messagesContainerRef, messagesContentRef, showScrollBottom, scrollToBottom } = useChatScroll({
111
129
  contentVersion: historyRenderCount
@@ -130,6 +148,7 @@ export function ChatHistoryView({
130
148
  effort,
131
149
  permissionMode,
132
150
  adapter: selectedAdapter,
151
+ workspaceDraft,
133
152
  onClearMessages
134
153
  })
135
154
  const initialScrollDoneRef = useRef(false)
@@ -235,6 +254,29 @@ export function ChatHistoryView({
235
254
  setQueuedDraft(null)
236
255
  setQueueMode('steer')
237
256
  }, [session?.id])
257
+ useEffect(() => {
258
+ if (session?.id != null) {
259
+ return
260
+ }
261
+
262
+ workspaceDraftDirtyRef.current = false
263
+ setWorkspaceDraft({
264
+ ...configWorkspaceDraft
265
+ })
266
+ }, [session?.id])
267
+ useEffect(() => {
268
+ if (session?.id != null) {
269
+ return
270
+ }
271
+
272
+ if (workspaceDraftDirtyRef.current) {
273
+ return
274
+ }
275
+
276
+ setWorkspaceDraft({
277
+ ...configWorkspaceDraft
278
+ })
279
+ }, [configWorkspaceDraft, session?.id])
238
280
  useEffect(() => {
239
281
  setActiveInteractionOptionIndex(0)
240
282
  }, [interactionRequest?.id])
@@ -296,6 +338,7 @@ export function ChatHistoryView({
296
338
  }
297
339
  }
298
340
  const isInlineEditing = editingMessageId != null
341
+ const shouldShowNewSessionGuide = !session?.id && messages.length === 0 && historyStatusNotices.length === 0
299
342
  const renderItems = useMemo(() => processMessages(messages), [messages])
300
343
  const hashAnchorId = useMemo(() => decodeURIComponent(location.hash.replace(/^#/, '')), [location.hash])
301
344
  const targetAnchorId = useMemo(() => {
@@ -531,6 +574,8 @@ export function ChatHistoryView({
531
574
  originalMessage={item.originalMessage}
532
575
  sessionInfo={sessionInfo}
533
576
  isEditing={editingMessageId === item.originalMessage.id}
577
+ isCompactLayout={isCompactLayout}
578
+ isTouchInteraction={isTouchInteraction}
534
579
  isSessionBusy={isCreating || session?.status === 'running' ||
535
580
  session?.status === 'waiting_input'}
536
581
  showAssistantActions={item.anchorId === lastAssistantActionAnchorId}
@@ -611,12 +656,18 @@ export function ChatHistoryView({
611
656
  )}
612
657
  </div>
613
658
 
614
- {!session?.id && messages.length === 0 && historyStatusNotices.length === 0 && (
659
+ {shouldShowNewSessionGuide && !isCompactLayout && (
615
660
  <div className='new-session-guide-wrapper'>
616
661
  <NewSessionGuide />
617
662
  </div>
618
663
  )}
619
664
 
665
+ {shouldShowNewSessionGuide && isCompactLayout && (
666
+ <div className='new-session-guide-wrapper is-compact-layout'>
667
+ <NewSessionGuide />
668
+ </div>
669
+ )}
670
+
620
671
  <div className='chat-composer-stack'>
621
672
  <div className='chat-composer-stack__inner'>
622
673
  {isPermissionInteraction && interactionPanel}
@@ -648,6 +699,7 @@ export function ChatHistoryView({
648
699
  onSend={handleSend}
649
700
  onSendContent={handleSendContent}
650
701
  adapterLocked={session?.id != null}
702
+ sessionId={session?.id}
651
703
  sessionStatus={isCreating ? 'running' : session?.status}
652
704
  onInterrupt={interrupt}
653
705
  onClear={clearMessages}
@@ -686,6 +738,15 @@ export function ChatHistoryView({
686
738
  queueMode={queueMode}
687
739
  onQueueModeChange={setQueueMode}
688
740
  />
741
+ <ChatStatusBar
742
+ draftWorkspace={workspaceDraft}
743
+ isCreating={isCreating}
744
+ sessionId={session?.id}
745
+ onDraftWorkspaceChange={(nextDraft) => {
746
+ workspaceDraftDirtyRef.current = true
747
+ setWorkspaceDraft(nextDraft)
748
+ }}
749
+ />
689
750
  </div>
690
751
  )}
691
752
  </div>
@@ -5,13 +5,26 @@
5
5
  min-height: 0;
6
6
  padding: 6px 12px 12px;
7
7
  overflow: auto;
8
+ min-width: 0;
9
+ overscroll-behavior: contain;
10
+ }
11
+
12
+ .chat-timeline-view__tabs {
13
+ display: none;
14
+ }
15
+
16
+ .chat-timeline-view__panel {
17
+ min-width: 0;
18
+ min-height: 0;
8
19
  }
9
20
 
10
21
  .session-timeline-section {
11
22
  display: flex;
12
23
  flex-direction: column;
13
24
  min-height: 0;
14
- overflow: scroll;
25
+ min-width: 0;
26
+ overflow: auto;
27
+ overscroll-behavior: contain;
15
28
  }
16
29
 
17
30
  .session-timeline-section--fixed {
@@ -31,11 +44,13 @@
31
44
  display: flex;
32
45
  flex-direction: column;
33
46
  min-height: 0;
47
+ min-width: 0;
34
48
  overflow: hidden;
35
49
  }
36
50
 
37
51
  .session-timeline-column--graph {
38
52
  overflow: auto;
53
+ overscroll-behavior: contain;
39
54
  }
40
55
 
41
56
  .session-timeline-column--events {
@@ -52,3 +67,81 @@
52
67
  .session-timeline-column > .session-timeline-event-table {
53
68
  flex: 1 1 auto;
54
69
  }
70
+
71
+ @media (max-width: 960px) {
72
+ .chat-timeline-view {
73
+ padding: 6px 8px 8px;
74
+ gap: 12px;
75
+ }
76
+
77
+ .chat-timeline-view--compact {
78
+ gap: 8px;
79
+ }
80
+
81
+ .chat-timeline-view__tabs {
82
+ display: grid;
83
+ grid-template-columns: repeat(3, minmax(0, 1fr));
84
+ width: 100%;
85
+ border: 1px solid var(--border-color);
86
+ border-radius: 8px;
87
+ overflow: hidden;
88
+ background: var(--bg-color);
89
+ }
90
+
91
+ .chat-timeline-view__tab {
92
+ min-width: 0;
93
+ min-height: 34px;
94
+ padding: 7px 6px;
95
+ border: 0;
96
+ border-right: 1px solid var(--border-color);
97
+ background: transparent;
98
+ color: var(--sub-text-color);
99
+ font-size: 12px;
100
+ font-weight: 500;
101
+ line-height: 1.2;
102
+ text-align: center;
103
+
104
+ &:last-child {
105
+ border-right: 0;
106
+ }
107
+
108
+ &.is-active {
109
+ background: var(--nav-active-bg, #eff6ff);
110
+ color: var(--nav-active-text, #2563eb);
111
+ }
112
+ }
113
+
114
+ .chat-timeline-view__panel {
115
+ display: flex;
116
+ flex: 1 1 auto;
117
+ }
118
+
119
+ .chat-timeline-view__panel--diagram {
120
+ min-height: 320px;
121
+ }
122
+
123
+ .chat-timeline-view__panel > .session-timeline-panel,
124
+ .chat-timeline-view__panel > .session-timeline-event-table {
125
+ flex: 1 1 auto;
126
+ min-width: 0;
127
+ min-height: 0;
128
+ }
129
+
130
+ .session-timeline-section {
131
+ overflow: visible;
132
+ }
133
+
134
+ .session-timeline-section--fixed {
135
+ flex-basis: auto;
136
+ }
137
+
138
+ .session-timeline-section--row {
139
+ flex-direction: column;
140
+ gap: 12px;
141
+ }
142
+
143
+ .session-timeline-column--graph,
144
+ .session-timeline-column--events {
145
+ flex: 1 1 auto;
146
+ }
147
+ }
@@ -1,9 +1,11 @@
1
1
  import './ChatTimelineView.scss'
2
2
 
3
3
  import React from 'react'
4
+ import { useTranslation } from 'react-i18next'
4
5
 
5
6
  import type { ChatMessage } from '@vibe-forge/core'
6
7
 
8
+ import { useResponsiveLayout } from '#~/hooks/use-responsive-layout'
7
9
  import { SessionTimelinePanel } from './session-timeline-panel'
8
10
  import { SessionTimelineEventList } from './session-timeline-panel/EventList'
9
11
  import type { Task } from './session-timeline-panel/types'
@@ -134,6 +136,46 @@ export function ChatTimelineView({
134
136
  }: {
135
137
  messages: ChatMessage[]
136
138
  }) {
139
+ const { t } = useTranslation()
140
+ const { isCompactLayout } = useResponsiveLayout()
141
+ const [compactView, setCompactView] = React.useState<'events' | 'gantt' | 'git'>('events')
142
+
143
+ if (isCompactLayout) {
144
+ const compactViews = [
145
+ { key: 'events' as const, label: t('chat.timeline.eventListTitle') },
146
+ { key: 'gantt' as const, label: t('chat.timeline.viewGantt') },
147
+ { key: 'git' as const, label: t('chat.timeline.viewGit') }
148
+ ]
149
+ return (
150
+ <div className='chat-timeline-view chat-timeline-view--compact'>
151
+ <div className='chat-timeline-view__tabs' role='tablist' aria-label={t('chat.timelineTiming')}>
152
+ {compactViews.map((item) => (
153
+ <button
154
+ key={item.key}
155
+ type='button'
156
+ role='tab'
157
+ aria-selected={compactView === item.key}
158
+ className={`chat-timeline-view__tab ${compactView === item.key ? 'is-active' : ''}`}
159
+ onClick={() => setCompactView(item.key)}
160
+ >
161
+ {item.label}
162
+ </button>
163
+ ))}
164
+ </div>
165
+
166
+ <section
167
+ className={`chat-timeline-view__panel ${
168
+ compactView === 'events' ? 'chat-timeline-view__panel--events' : 'chat-timeline-view__panel--diagram'
169
+ }`}
170
+ >
171
+ {compactView === 'events' && <SessionTimelineEventList task={mockTimelineTask} />}
172
+ {compactView === 'gantt' && <SessionTimelinePanel task={mockTimelineTask} viewMode='gantt' />}
173
+ {compactView === 'git' && <SessionTimelinePanel task={mockTimelineTask} viewMode='git' />}
174
+ </section>
175
+ </div>
176
+ )
177
+ }
178
+
137
179
  return (
138
180
  <div className='chat-timeline-view'>
139
181
  <section className='session-timeline-section session-timeline-section--fixed'>