@vibe-forge/client 0.11.2 → 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 (232) hide show
  1. package/cli.cjs +6 -11
  2. package/dist/assets/{arc-De_WjPJ3.js → arc-CbOXL0l9.js} +1 -1
  3. package/dist/assets/{blockDiagram-c4efeb88-C4aR2zTE.js → blockDiagram-c4efeb88-CqxINvsS.js} +1 -1
  4. package/dist/assets/{c4Diagram-c83219d4-BZH3rq_m.js → c4Diagram-c83219d4-BKazU0hb.js} +1 -1
  5. package/dist/assets/channel-Dnopc5A6.js +1 -0
  6. package/dist/assets/{classDiagram-beda092f-BzJgBrIK.js → classDiagram-beda092f-fAFX5BpB.js} +1 -1
  7. package/dist/assets/{classDiagram-v2-2358418a-5ZtXcnT3.js → classDiagram-v2-2358418a-w1VkNGJj.js} +1 -1
  8. package/dist/assets/clone-sQthahUA.js +1 -0
  9. package/dist/assets/{createText-1719965b-DUVvEtmR.js → createText-1719965b-CEinakVP.js} +1 -1
  10. package/dist/assets/{cssMode-GoTNjuXX.js → cssMode-DPqRki4y.js} +1 -1
  11. package/dist/assets/{edges-96097737-Dd7m4Cvs.js → edges-96097737-Cb0F1_3K.js} +1 -1
  12. package/dist/assets/{erDiagram-0228fc6a-DxqFlG_f.js → erDiagram-0228fc6a-C-N2fx-J.js} +1 -1
  13. package/dist/assets/{flowDb-c6c81e3f-DU0C5kCI.js → flowDb-c6c81e3f-D1Xz_8Gf.js} +1 -1
  14. package/dist/assets/{flowDiagram-50d868cf-Di1uDa_X.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-CwG8aty5.js → flowchart-elk-definition-6af322e1-Dr1DDXwE.js} +1 -1
  17. package/dist/assets/{freemarker2-j39cqTlI.js → freemarker2-C3DvPFaK.js} +1 -1
  18. package/dist/assets/{ganttDiagram-a2739b55-baO_lzL-.js → ganttDiagram-a2739b55-DmvY1GRj.js} +1 -1
  19. package/dist/assets/{gitGraphDiagram-82fe8481-COoHjYMf.js → gitGraphDiagram-82fe8481-CoXfPYYi.js} +1 -1
  20. package/dist/assets/{graph-KxESr4M5.js → graph-BkDQy7Qt.js} +1 -1
  21. package/dist/assets/{handlebars-BgjdZO8G.js → handlebars-BcTFdqjl.js} +1 -1
  22. package/dist/assets/{html-Ba7tYObe.js → html-Dg-O6XFr.js} +1 -1
  23. package/dist/assets/{htmlMode-Bztvbig1.js → htmlMode-B_wqYWvn.js} +1 -1
  24. package/dist/assets/{index-5325376f-BMTAx2mL.js → index-5325376f-kxPTR3_e.js} +1 -1
  25. package/dist/assets/index-o93dlo92.css +32 -0
  26. package/dist/assets/{index-Pm_kLJvG.js → index-wkhI4dr6.js} +350 -329
  27. package/dist/assets/{infoDiagram-8eee0895-CC74qbHY.js → infoDiagram-8eee0895-BEvqkwPI.js} +1 -1
  28. package/dist/assets/{javascript-C1e1cllX.js → javascript-DhlOH8_z.js} +1 -1
  29. package/dist/assets/{journeyDiagram-c64418c1-C4MyOdE6.js → journeyDiagram-c64418c1-gKtLYmmp.js} +1 -1
  30. package/dist/assets/{jsonMode-BC98AlvF.js → jsonMode-DxTbF9OD.js} +1 -1
  31. package/dist/assets/{layout-CxAyTlr7.js → layout-CDaZEk6E.js} +1 -1
  32. package/dist/assets/{line-DhaUfI71.js → line-DNRQu8iq.js} +1 -1
  33. package/dist/assets/{linear-MYukzldK.js → linear-Cph9Z6_j.js} +1 -1
  34. package/dist/assets/{liquid-DahfJEYl.js → liquid-ByZ6JgRG.js} +1 -1
  35. package/dist/assets/{lspLanguageFeatures-BWDJcswW.js → lspLanguageFeatures-DzvhkgnM.js} +1 -1
  36. package/dist/assets/{mdx-BELlF_FD.js → mdx-D8RGHTl6.js} +1 -1
  37. package/dist/assets/{mermaid.core-BrQnSGSY.js → mermaid.core-BgcryF__.js} +4 -4
  38. package/dist/assets/{mindmap-definition-8da855dc-B0FoxTiy.js → mindmap-definition-8da855dc-WrxK0FcB.js} +1 -1
  39. package/dist/assets/{pieDiagram-a8764435-Ddr2cjSL.js → pieDiagram-a8764435-VsZBsiQy.js} +1 -1
  40. package/dist/assets/{python--C9if_AD.js → python-CXVtk_cg.js} +1 -1
  41. package/dist/assets/{quadrantDiagram-1e28029f-BlEs7Mrl.js → quadrantDiagram-1e28029f-BVlgwOvU.js} +1 -1
  42. package/dist/assets/{razor-B9U9JxKn.js → razor-0tind7h2.js} +1 -1
  43. package/dist/assets/{requirementDiagram-08caed73-kEFOAu2v.js → requirementDiagram-08caed73-CpPMPoYp.js} +1 -1
  44. package/dist/assets/{sankeyDiagram-a04cb91d-BBghez8I.js → sankeyDiagram-a04cb91d-Cm5nnRmc.js} +1 -1
  45. package/dist/assets/{sequenceDiagram-c5b8d532-CJqgzdUE.js → sequenceDiagram-c5b8d532-DpMlJvJB.js} +1 -1
  46. package/dist/assets/{stateDiagram-1ecb1508-BER4XEI6.js → stateDiagram-1ecb1508-DU1zc7vq.js} +1 -1
  47. package/dist/assets/{stateDiagram-v2-c2b004d7-EBV2vSks.js → stateDiagram-v2-c2b004d7-D-0RgmAp.js} +1 -1
  48. package/dist/assets/{styles-b4e223ce-k0eswZsE.js → styles-b4e223ce-BSO-yNWV.js} +1 -1
  49. package/dist/assets/{styles-ca3715f6-Ckr7GA-0.js → styles-ca3715f6-CHnsn2Ro.js} +1 -1
  50. package/dist/assets/{styles-d45a18b0-C1bpSwV3.js → styles-d45a18b0-B-rVGjEq.js} +1 -1
  51. package/dist/assets/{svgDrawCommon-b86b1483-CDtKpGvy.js → svgDrawCommon-b86b1483-CA3Pl89f.js} +1 -1
  52. package/dist/assets/{timeline-definition-faaaa080-BeGR-vua.js → timeline-definition-faaaa080-BcihLR6s.js} +1 -1
  53. package/dist/assets/{tsMode-D_gJXIy3.js → tsMode-D9GGa5Ur.js} +1 -1
  54. package/dist/assets/{typescript-BoKcNXkN.js → typescript-BT9CK_EL.js} +1 -1
  55. package/dist/assets/{xml-DZvURlJ-.js → xml-DNO75J-T.js} +1 -1
  56. package/dist/assets/{xychartDiagram-f5964ef8-DxfeLuYV.js → xychartDiagram-f5964ef8-DJTwe32X.js} +1 -1
  57. package/dist/assets/{yaml-CTC8PAGY.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 +131 -4
  62. package/src/api/types.ts +2 -1
  63. package/src/api.ts +13 -0
  64. package/src/components/ArchiveView.scss +143 -54
  65. package/src/components/ArchiveView.tsx +181 -167
  66. package/src/components/CodeBlock.scss +5 -0
  67. package/src/components/ConfigView.scss +142 -31
  68. package/src/components/ConfigView.tsx +161 -86
  69. package/src/components/MarkdownContent.tsx +7 -0
  70. package/src/components/NavRail.scss +248 -0
  71. package/src/components/NavRail.tsx +80 -107
  72. package/src/components/NavRailCompact.tsx +107 -0
  73. package/src/components/NavRailCompactMoreSheet.tsx +141 -0
  74. package/src/components/ShortcutTooltip.tsx +4 -2
  75. package/src/components/Sidebar.scss +51 -0
  76. package/src/components/Sidebar.tsx +43 -16
  77. package/src/components/automation-view/RuleFormPanel.scss +40 -13
  78. package/src/components/automation-view/RuleSidebar.scss +73 -47
  79. package/src/components/automation-view/RuleSidebar.tsx +9 -13
  80. package/src/components/automation-view/RunHistoryPanel.scss +141 -13
  81. package/src/components/automation-view/RunHistoryPanel.tsx +203 -161
  82. package/src/components/automation-view/TaskList.scss +44 -13
  83. package/src/components/automation-view/TriggerList.scss +46 -14
  84. package/src/components/automation-view/index.scss +82 -10
  85. package/src/components/automation-view/index.tsx +108 -55
  86. package/src/components/benchmark-view/BenchmarkCasePanel.scss +36 -16
  87. package/src/components/benchmark-view/BenchmarkSidebar.scss +44 -22
  88. package/src/components/benchmark-view/BenchmarkSidebar.tsx +0 -6
  89. package/src/components/benchmark-view/BenchmarkView.scss +63 -20
  90. package/src/components/benchmark-view/index.tsx +71 -34
  91. package/src/components/chat/AGENTS.md +14 -2
  92. package/src/components/chat/ChatComposerCard.scss +77 -0
  93. package/src/components/chat/ChatComposerCard.tsx +59 -0
  94. package/src/components/chat/ChatHeader.scss +187 -0
  95. package/src/components/chat/ChatHeader.tsx +209 -57
  96. package/src/components/chat/ChatHistoryView.tsx +279 -52
  97. package/src/components/chat/ChatTimelineView.scss +94 -1
  98. package/src/components/chat/ChatTimelineView.tsx +42 -0
  99. package/src/components/chat/CurrentTodoList.scss +210 -200
  100. package/src/components/chat/CurrentTodoList.tsx +116 -48
  101. package/src/components/chat/NewSessionGuide.scss +139 -1
  102. package/src/components/chat/NewSessionGuide.tsx +57 -100
  103. package/src/components/chat/NewSessionGuideCompactPanel.tsx +130 -0
  104. package/src/components/chat/NewSessionGuideGrid.tsx +141 -0
  105. package/src/components/chat/QueuedMessagesCard.scss +195 -0
  106. package/src/components/chat/QueuedMessagesCard.tsx +170 -0
  107. package/src/components/chat/git-controls/BranchSwitcherDropdown.tsx +61 -56
  108. package/src/components/chat/git-controls/BranchSwitcherResults.tsx +167 -0
  109. package/src/components/chat/git-controls/BranchTreeEntries.tsx +99 -0
  110. package/src/components/chat/git-controls/ChatGitControls.scss +437 -5
  111. package/src/components/chat/git-controls/ChatGitControls.tsx +136 -109
  112. package/src/components/chat/git-controls/DraftGitControls.tsx +91 -0
  113. package/src/components/chat/git-controls/GitOperationsDropdown.tsx +10 -2
  114. package/src/components/chat/git-controls/GitWorktreeDropdown.tsx +301 -28
  115. package/src/components/chat/git-controls/git-branch-tree.ts +148 -0
  116. package/src/components/chat/git-controls/use-chat-draft-git-controls.ts +168 -0
  117. package/src/components/chat/git-controls/use-chat-git-controls.ts +76 -3
  118. package/src/components/chat/messages/MessageContextMenu.tsx +3 -1
  119. package/src/components/chat/messages/MessageItem.scss +78 -4
  120. package/src/components/chat/messages/MessageItem.tsx +47 -3
  121. package/src/components/chat/sender/@components/adapter-select/AdapterSelectControl.scss +23 -0
  122. package/src/components/chat/sender/@components/reference-actions/ReferenceActionsControl.scss +17 -0
  123. package/src/components/chat/sender/@components/reference-actions/ReferencePermissionActionsPopover.tsx +4 -1
  124. package/src/components/chat/sender/@components/sender-attachments/SenderAttachments.scss +167 -30
  125. package/src/components/chat/sender/@components/sender-attachments/SenderAttachments.tsx +95 -23
  126. package/src/components/chat/sender/@components/sender-body/SenderBody.tsx +10 -0
  127. package/src/components/chat/sender/@components/sender-interaction-panel/SenderInteractionPanel.scss +161 -45
  128. package/src/components/chat/sender/@components/sender-interaction-panel/SenderInteractionPanel.tsx +310 -71
  129. package/src/components/chat/sender/@components/sender-monaco-editor/SenderMonacoEditor.tsx +18 -0
  130. package/src/components/chat/sender/@components/sender-monaco-editor/use-sender-monaco-editor.ts +86 -9
  131. package/src/components/chat/sender/@components/sender-monaco-editor/use-sender-monaco-theme.ts +52 -3
  132. package/src/components/chat/sender/@components/sender-submit-action/SenderSubmitAction.scss +110 -1
  133. package/src/components/chat/sender/@components/sender-submit-action/SenderSubmitAction.tsx +137 -17
  134. package/src/components/chat/sender/@components/sender-toolbar/SenderSelectBase.scss +21 -0
  135. package/src/components/chat/sender/@components/sender-toolbar/SenderSelectShared.scss +21 -0
  136. package/src/components/chat/sender/@components/sender-toolbar/SenderToolbar.scss +63 -0
  137. package/src/components/chat/sender/@components/sender-toolbar/SenderToolbar.tsx +12 -6
  138. package/src/components/chat/sender/@core/build-sender-controller-result.ts +6 -0
  139. package/src/components/chat/sender/@core/build-sender-toolbar.ts +25 -2
  140. package/src/components/chat/sender/@core/create-sender-toolbar-handlers.ts +9 -2
  141. package/src/components/chat/sender/@core/get-sender-runtime-state.ts +1 -1
  142. package/src/components/chat/sender/@core/interaction-request.ts +2 -2
  143. package/src/components/chat/sender/@core/sender-toolbar-bindings.ts +28 -4
  144. package/src/components/chat/sender/@hooks/use-model-select-browser.tsx +4 -2
  145. package/src/components/chat/sender/@hooks/use-sender-controller.ts +56 -11
  146. package/src/components/chat/sender/@hooks/use-sender-keydown.ts +64 -0
  147. package/src/components/chat/sender/@hooks/use-sender-shortcuts.ts +16 -1
  148. package/src/components/chat/sender/@hooks/use-sender-submit.ts +16 -8
  149. package/src/components/chat/sender/@types/sender-props.ts +20 -3
  150. package/src/components/chat/sender/@types/sender-toolbar-types.ts +12 -1
  151. package/src/components/chat/sender/Sender.scss +4 -1
  152. package/src/components/chat/sender/Sender.tsx +3 -12
  153. package/src/components/chat/session-timeline-panel/EventList.scss +88 -0
  154. package/src/components/chat/session-timeline-panel/EventList.tsx +99 -47
  155. package/src/components/chat/session-timeline-panel/gantt.ts +23 -7
  156. package/src/components/chat/session-timeline-panel/git-graph.ts +6 -1
  157. package/src/components/chat/session-timeline-panel/index.scss +14 -1
  158. package/src/components/chat/session-timeline-panel/index.tsx +86 -10
  159. package/src/components/chat/session-timeline-panel/types.ts +4 -0
  160. package/src/components/chat/status-bar/ChatStatusBar.scss +27 -0
  161. package/src/components/chat/status-bar/ChatStatusBar.tsx +39 -0
  162. package/src/components/chat/terminal/ChatTerminalView.tsx +6 -0
  163. package/src/components/chat/tools/core/ToolCallBox.scss +19 -0
  164. package/src/components/chat/tools/core/ToolGroup.scss +32 -0
  165. package/src/components/chat/tools/task/components/TaskToolCard.scss +59 -1
  166. package/src/components/config/ConfigEditors.scss +20 -6
  167. package/src/components/config/ConfigFieldRow.scss +57 -17
  168. package/src/components/config/ConfigSectionForm.scss +10 -4
  169. package/src/components/config/ConfigSectionPanel.tsx +18 -11
  170. package/src/components/config/configSchema.ts +1 -0
  171. package/src/components/config/record-editors/RecordEditors.scss +42 -9
  172. package/src/components/dock-panel/DockPanel.scss +6 -2
  173. package/src/components/dock-panel/DockPanel.tsx +12 -16
  174. package/src/components/knowledge-base/KnowledgeBaseView.scss +180 -6
  175. package/src/components/knowledge-base/KnowledgeBaseView.tsx +98 -26
  176. package/src/components/knowledge-base/components/ActionButton.scss +4 -0
  177. package/src/components/knowledge-base/components/EmptyState.scss +5 -8
  178. package/src/components/knowledge-base/components/EntitiesTab.tsx +8 -2
  179. package/src/components/knowledge-base/components/EntityItem.scss +10 -3
  180. package/src/components/knowledge-base/components/FilterBar.scss +13 -2
  181. package/src/components/knowledge-base/components/FlowsTab.tsx +8 -2
  182. package/src/components/knowledge-base/components/KnowledgeBaseHeader.scss +2 -23
  183. package/src/components/knowledge-base/components/KnowledgeBaseHeader.tsx +0 -5
  184. package/src/components/knowledge-base/components/KnowledgeList.scss +15 -6
  185. package/src/components/knowledge-base/components/LoadingState.scss +4 -0
  186. package/src/components/knowledge-base/components/RuleItem.scss +86 -0
  187. package/src/components/knowledge-base/components/RuleItem.tsx +2 -0
  188. package/src/components/knowledge-base/components/RulesTab.tsx +8 -2
  189. package/src/components/knowledge-base/components/SectionHeader.scss +3 -18
  190. package/src/components/knowledge-base/components/SectionHeader.tsx +3 -7
  191. package/src/components/knowledge-base/components/SkillsTab.tsx +8 -3
  192. package/src/components/knowledge-base/components/SpecItem.scss +16 -7
  193. package/src/components/layout/@hooks/use-mobile-sidebar-modal.ts +190 -0
  194. package/src/components/layout/AppShell.scss +106 -6
  195. package/src/components/layout/AppShell.tsx +118 -10
  196. package/src/components/layout/PageShell.scss +41 -0
  197. package/src/components/layout/PageShell.tsx +32 -0
  198. package/src/components/layout/mobile-sidebar-constants.ts +1 -0
  199. package/src/components/nav-rail-compact-config.ts +114 -0
  200. package/src/components/nav-rail-items.tsx +181 -0
  201. package/src/components/sidebar/SessionContextMenu.tsx +3 -1
  202. package/src/components/sidebar/SessionItem.scss +62 -0
  203. package/src/components/sidebar/SessionItem.tsx +97 -52
  204. package/src/components/sidebar/SessionList.tsx +6 -0
  205. package/src/components/sidebar/SidebarHeader.scss +49 -0
  206. package/src/components/sidebar/SidebarHeader.tsx +27 -5
  207. package/src/components/sidebar/SidebarHeaderBatchActions.tsx +8 -4
  208. package/src/components/sidebar/SidebarHeaderSearchActions.tsx +6 -3
  209. package/src/components/sidebar/SidebarUtilityFooter.tsx +69 -0
  210. package/src/components/workspace/ContextFilePicker.tsx +12 -4
  211. package/src/hooks/chat/chat-session-workspace-draft.ts +25 -0
  212. package/src/hooks/chat/session-view-cache.ts +4 -1
  213. package/src/hooks/chat/use-chat-adapter.ts +5 -1
  214. package/src/hooks/chat/use-chat-model-adapter-selection.tsx +5 -1
  215. package/src/hooks/chat/use-chat-scroll.ts +24 -7
  216. package/src/hooks/chat/use-chat-session-actions.ts +118 -6
  217. package/src/hooks/chat/use-chat-session-messages.ts +20 -1
  218. package/src/hooks/chat/use-chat-session.ts +2 -0
  219. package/src/hooks/use-responsive-layout.ts +115 -0
  220. package/src/main.tsx +8 -0
  221. package/src/resources/adapters.ts +15 -0
  222. package/src/resources/locales/en.json +84 -1
  223. package/src/resources/locales/zh.json +84 -1
  224. package/src/routes/ChatRoute.scss +152 -9
  225. package/src/routes/ChatRoute.tsx +31 -34
  226. package/src/store/index.ts +2 -0
  227. package/dist/assets/channel-BvERb8WU.js +0 -1
  228. package/dist/assets/clone-B9_0v-6Y.js +0 -1
  229. package/dist/assets/flowDiagram-v2-4f6560a1-LpS8Kb00.js +0 -1
  230. package/dist/assets/index-C1oh0w9H.css +0 -32
  231. package/src/components/chat/ThinkingStatus.scss +0 -70
  232. package/src/components/chat/ThinkingStatus.tsx +0 -13
@@ -17,7 +17,9 @@ interface SessionItemProps {
17
17
  session: Session
18
18
  isActive: boolean
19
19
  isBatchMode: boolean
20
+ isCompactLayout: boolean
20
21
  isSelected: boolean
22
+ isTouchInteraction: boolean
21
23
  onSelect: (session: Session) => void
22
24
  onArchive: (id: string) => void | Promise<void>
23
25
  onDelete: (id: string) => void | Promise<void>
@@ -32,7 +34,9 @@ export function SessionItem({
32
34
  session,
33
35
  isActive,
34
36
  isBatchMode,
37
+ isCompactLayout,
35
38
  isSelected,
39
+ isTouchInteraction,
36
40
  onSelect,
37
41
  onArchive,
38
42
  onDelete,
@@ -44,6 +48,8 @@ export function SessionItem({
44
48
  const automationPrefix = 'automation:'
45
49
  const itemContentRef = useRef<HTMLDivElement | null>(null)
46
50
  const [pendingAction, setPendingAction] = useState<PendingSessionAction>(null)
51
+ const showCompactActionMenu = isCompactLayout || isTouchInteraction
52
+ const resolveTooltipTitle = (title: string) => isTouchInteraction ? undefined : title
47
53
 
48
54
  useEffect(() => {
49
55
  if (pendingAction == null) {
@@ -83,6 +89,9 @@ export function SessionItem({
83
89
 
84
90
  const archiveActionLabel = session.isArchived ? t('common.restore') : t('common.archive')
85
91
  const archiveConfirmLabel = t('common.confirmAction', { action: archiveActionLabel })
92
+ const sessionTags = session.tags ?? []
93
+ const visibleTags = isCompactLayout ? sessionTags.slice(0, 1) : sessionTags
94
+ const hiddenTagCount = Math.max(sessionTags.length - visibleTags.length, 0)
86
95
 
87
96
  const displayTitle = (session.title != null && session.title !== '')
88
97
  ? session.title
@@ -138,7 +147,7 @@ export function SessionItem({
138
147
  break
139
148
  case 'waiting_input':
140
149
  return (
141
- <Tooltip title={title}>
150
+ <Tooltip title={resolveTooltipTitle(title)}>
142
151
  <div className='waiting-input-indicator' />
143
152
  </Tooltip>
144
153
  )
@@ -147,7 +156,7 @@ export function SessionItem({
147
156
  }
148
157
 
149
158
  return (
150
- <Tooltip title={title}>
159
+ <Tooltip title={resolveTooltipTitle(title)}>
151
160
  <span
152
161
  className={`material-symbols-rounded status-icon ${status === 'running' ? 'spin' : ''}`}
153
162
  style={{
@@ -192,7 +201,7 @@ export function SessionItem({
192
201
  }}
193
202
  className={`session-item ${isActive ? 'active' : ''} ${isSelected ? 'selected' : ''} ${
194
203
  session.isStarred ? 'starred' : ''
195
- }`}
204
+ } ${isCompactLayout ? 'session-item--compact' : ''} ${showCompactActionMenu ? 'session-item--touch' : ''}`}
196
205
  >
197
206
  <div ref={itemContentRef} className='session-item-content'>
198
207
  <div className={`session-leading ${adapterDisplay?.icon != null ? 'has-adapter' : ''}`}>
@@ -218,71 +227,102 @@ export function SessionItem({
218
227
  <div className='session-header-side'>
219
228
  {!isBatchMode && (
220
229
  <>
221
- <Tooltip title={timeDisplay.full}>
222
- <span className='time-display'>
223
- {timeDisplay.relative}
224
- </span>
225
- </Tooltip>
226
- <div className='session-item-actions'>
227
- <Tooltip title={session.isStarred ? t('common.unstar') : t('common.star')}>
228
- <Button
229
- type='text'
230
- size='small'
231
- className={`action-btn star-btn ${session.isStarred ? 'starred' : ''}`}
232
- onClick={(e) => {
233
- e.stopPropagation()
234
- setPendingAction(null)
235
- void onStar(session.id, !session.isStarred)
236
- }}
237
- icon={
238
- <span
239
- className={`material-symbols-rounded ${session.isStarred ? 'filled' : ''}`}
240
- >
241
- star
242
- </span>
243
- }
244
- />
230
+ {!isCompactLayout && (
231
+ <Tooltip title={resolveTooltipTitle(timeDisplay.full)}>
232
+ <span className='time-display'>
233
+ {timeDisplay.relative}
234
+ </span>
245
235
  </Tooltip>
246
- <Tooltip
247
- title={pendingAction === 'archive' ? archiveConfirmLabel : archiveActionLabel}
248
- >
249
- <Button
250
- type='text'
251
- size='small'
252
- className={`action-btn archive-btn ${pendingAction === 'archive' ? 'is-confirming' : ''}`}
253
- onClick={(e) => {
254
- e.stopPropagation()
255
- handleConfirmableActionClick('archive')
256
- }}
236
+ )}
237
+ {showCompactActionMenu
238
+ ? (
239
+ <SessionContextMenu
240
+ session={session}
241
+ trigger={['click']}
242
+ onArchive={onArchive}
243
+ onDelete={onDelete}
244
+ onRename={onRename}
245
+ onStar={onStar}
257
246
  >
258
- <span className='material-symbols-rounded'>
259
- {session.isArchived ? 'unarchive' : 'archive'}
260
- </span>
261
- {pendingAction === 'archive' && (
262
- <span className='action-btn__label'>{archiveConfirmLabel}</span>
263
- )}
264
- </Button>
265
- </Tooltip>
266
- </div>
247
+ <Button
248
+ type='text'
249
+ size='small'
250
+ className='action-btn action-btn--more'
251
+ title={t('common.moreActions')}
252
+ aria-label={t('common.moreActions')}
253
+ onClick={(event) => {
254
+ event.stopPropagation()
255
+ }}
256
+ icon={<span className='material-symbols-rounded'>more_horiz</span>}
257
+ />
258
+ </SessionContextMenu>
259
+ )
260
+ : (
261
+ <div className='session-item-actions'>
262
+ <Tooltip
263
+ title={resolveTooltipTitle(session.isStarred ? t('common.unstar') : t('common.star'))}
264
+ >
265
+ <Button
266
+ type='text'
267
+ size='small'
268
+ className={`action-btn star-btn ${session.isStarred ? 'starred' : ''}`}
269
+ onClick={(e) => {
270
+ e.stopPropagation()
271
+ setPendingAction(null)
272
+ void onStar(session.id, !session.isStarred)
273
+ }}
274
+ icon={
275
+ <span
276
+ className={`material-symbols-rounded ${session.isStarred ? 'filled' : ''}`}
277
+ >
278
+ star
279
+ </span>
280
+ }
281
+ />
282
+ </Tooltip>
283
+ <Tooltip
284
+ title={resolveTooltipTitle(
285
+ pendingAction === 'archive' ? archiveConfirmLabel : archiveActionLabel
286
+ )}
287
+ >
288
+ <Button
289
+ type='text'
290
+ size='small'
291
+ className={`action-btn archive-btn ${pendingAction === 'archive' ? 'is-confirming' : ''}`}
292
+ onClick={(e) => {
293
+ e.stopPropagation()
294
+ handleConfirmableActionClick('archive')
295
+ }}
296
+ >
297
+ <span className='material-symbols-rounded'>
298
+ {session.isArchived ? 'unarchive' : 'archive'}
299
+ </span>
300
+ {pendingAction === 'archive' && (
301
+ <span className='action-btn__label'>{archiveConfirmLabel}</span>
302
+ )}
303
+ </Button>
304
+ </Tooltip>
305
+ </div>
306
+ )}
267
307
  </>
268
308
  )}
269
309
  </div>
270
310
  </div>
271
- {lastMessageSnippet != null && (
311
+ {!isCompactLayout && lastMessageSnippet != null && (
272
312
  <div className='last-message'>
273
313
  {lastMessageSnippet}
274
314
  </div>
275
315
  )}
276
- {session.tags != null && session.tags.length > 0 && (
316
+ {visibleTags.length > 0 && (
277
317
  <div className='tags-container'>
278
- {session.tags.map((tag: string) => {
318
+ {visibleTags.map((tag: string) => {
279
319
  const automationTag = parseAutomationTag(tag)
280
320
  if (automationTag) {
281
321
  const href = `/automation?rule=${encodeURIComponent(automationTag.ruleId)}`
282
322
  return (
283
323
  <Tooltip
284
324
  key={tag}
285
- title={automationTag.ruleTitle}
325
+ title={resolveTooltipTitle(automationTag.ruleTitle)}
286
326
  >
287
327
  <Tag
288
328
  className='session-tag session-tag--automation'
@@ -302,7 +342,7 @@ export function SessionItem({
302
342
  return (
303
343
  <Tooltip
304
344
  key={tag}
305
- title={tag}
345
+ title={resolveTooltipTitle(tag)}
306
346
  >
307
347
  <Tag className='session-tag'>
308
348
  <span className='session-tag__text'>
@@ -312,6 +352,11 @@ export function SessionItem({
312
352
  </Tooltip>
313
353
  )
314
354
  })}
355
+ {hiddenTagCount > 0 && (
356
+ <Tag className='session-tag session-tag--count'>
357
+ +{hiddenTagCount}
358
+ </Tag>
359
+ )}
315
360
  </div>
316
361
  )}
317
362
  </div>
@@ -11,7 +11,9 @@ interface SessionListProps {
11
11
  activeId?: string
12
12
  hasActiveFilters: boolean
13
13
  isBatchMode: boolean
14
+ isCompactLayout: boolean
14
15
  selectedIds: Set<string>
16
+ isTouchInteraction: boolean
15
17
  searchQuery?: string
16
18
  onSelectSession: (session: Session) => void
17
19
  onArchiveSession: (id: string) => void | Promise<void>
@@ -26,7 +28,9 @@ export function SessionList({
26
28
  activeId,
27
29
  hasActiveFilters,
28
30
  isBatchMode,
31
+ isCompactLayout,
29
32
  selectedIds,
33
+ isTouchInteraction,
30
34
  searchQuery,
31
35
  onSelectSession,
32
36
  onArchiveSession,
@@ -142,7 +146,9 @@ export function SessionList({
142
146
  session={s}
143
147
  isActive={activeId === s.id}
144
148
  isBatchMode={isBatchMode}
149
+ isCompactLayout={isCompactLayout}
145
150
  isSelected={selectedIds.has(s.id)}
151
+ isTouchInteraction={isTouchInteraction}
146
152
  onSelect={onSelectSession}
147
153
  onArchive={onArchiveSession}
148
154
  onDelete={onDeleteSession}
@@ -357,3 +357,52 @@
357
357
  }
358
358
  }
359
359
  }
360
+
361
+ .sidebar-header--compact {
362
+ padding-top: 12px;
363
+
364
+ .header-top {
365
+ align-items: stretch;
366
+ }
367
+
368
+ .new-chat-btn {
369
+ height: 40px;
370
+ min-height: 40px;
371
+ padding: 0 12px;
372
+ font-size: 13px;
373
+ }
374
+
375
+ .sidebar-collapse-btn,
376
+ .sidebar-new-chat-btn {
377
+ width: 40px;
378
+ min-width: 40px;
379
+ height: 40px;
380
+ min-height: 40px;
381
+ border-radius: 8px;
382
+ }
383
+
384
+ .header-search-row {
385
+ margin-top: 8px;
386
+ }
387
+
388
+ .search-input {
389
+ height: 40px;
390
+ min-height: 40px;
391
+ padding: 0 10px !important;
392
+
393
+ .ant-input {
394
+ font-size: 13px;
395
+ }
396
+
397
+ .search-toggle-button {
398
+ width: 24px;
399
+ height: 24px;
400
+ border-radius: 6px;
401
+ }
402
+ }
403
+
404
+ .toolbar-filter-select .ant-select-selector,
405
+ .batch-select-toggle {
406
+ min-height: 40px;
407
+ }
408
+ }
@@ -4,6 +4,7 @@ import { Button, Tooltip } from 'antd'
4
4
  import React, { useState } from 'react'
5
5
  import { useTranslation } from 'react-i18next'
6
6
 
7
+ import { useResponsiveLayout } from '#~/hooks/use-responsive-layout'
7
8
  import type { SidebarSessionSortOrder } from '#~/hooks/use-sidebar-query-state'
8
9
  import { SidebarHeaderSearchActions } from './SidebarHeaderSearchActions'
9
10
 
@@ -14,6 +15,7 @@ interface SidebarHeaderProps {
14
15
  createButtonRef: React.RefObject<HTMLButtonElement | null>
15
16
  hasActiveSearchControls: boolean
16
17
  isBatchMode: boolean
18
+ isCompactLayout: boolean
17
19
  isCreatingSession: boolean
18
20
  isSidebarCollapsed: boolean
19
21
  searchQuery: string
@@ -27,6 +29,7 @@ interface SidebarHeaderProps {
27
29
  onBatchDelete: () => void
28
30
  onBatchStar: () => void
29
31
  onAdapterFilterChange: (filters: string[]) => void
32
+ onCloseSidebar?: () => void
30
33
  onCreateSession: () => void
31
34
  onSearchChange: (query: string) => void
32
35
  onSortOrderChange: (sort?: SidebarSessionSortOrder) => void
@@ -43,6 +46,7 @@ export function SidebarHeader({
43
46
  createButtonRef,
44
47
  hasActiveSearchControls,
45
48
  isBatchMode,
49
+ isCompactLayout,
46
50
  isCreatingSession,
47
51
  isSidebarCollapsed,
48
52
  searchQuery,
@@ -56,6 +60,7 @@ export function SidebarHeader({
56
60
  onBatchDelete,
57
61
  onBatchStar,
58
62
  onAdapterFilterChange,
63
+ onCloseSidebar,
59
64
  onCreateSession,
60
65
  onSearchChange,
61
66
  onSortOrderChange,
@@ -65,11 +70,12 @@ export function SidebarHeader({
65
70
  onToggleSidebarCollapsed
66
71
  }: SidebarHeaderProps) {
67
72
  const { t } = useTranslation()
73
+ const { isTouchInteraction } = useResponsiveLayout()
68
74
  const [isSearchActionsOpen, setIsSearchActionsOpen] = useState(false)
69
75
  const shouldShowSearchActions = !isSidebarCollapsed && (isSearchActionsOpen || isBatchMode)
70
76
 
71
77
  return (
72
- <div className='sidebar-header'>
78
+ <div className={`sidebar-header ${isCompactLayout ? 'sidebar-header--compact' : ''}`.trim()}>
73
79
  <div className='header-top'>
74
80
  {!isSidebarCollapsed
75
81
  ? (
@@ -93,7 +99,12 @@ export function SidebarHeader({
93
99
  </Button>
94
100
  )
95
101
  : (
96
- <Tooltip title={isCreatingSession ? t('common.alreadyInNewChat') : t('common.newChat')} placement='right'>
102
+ <Tooltip
103
+ title={isTouchInteraction
104
+ ? undefined
105
+ : (isCreatingSession ? t('common.alreadyInNewChat') : t('common.newChat'))}
106
+ placement='right'
107
+ >
97
108
  <Button
98
109
  className={`sidebar-new-chat-btn ${isCreatingSession ? 'active' : ''}`}
99
110
  type='text'
@@ -106,14 +117,25 @@ export function SidebarHeader({
106
117
  </Button>
107
118
  </Tooltip>
108
119
  )}
109
- <Tooltip title={isSidebarCollapsed ? t('common.expand') : t('common.collapse')}>
120
+ <Tooltip
121
+ title={isTouchInteraction ? undefined : (
122
+ isCompactLayout ? t('common.close') : (
123
+ isSidebarCollapsed ? t('common.expand') : t('common.collapse')
124
+ )
125
+ )}
126
+ >
110
127
  <Button
111
128
  className='sidebar-collapse-btn'
112
129
  type='text'
113
- onClick={onToggleSidebarCollapsed}
130
+ aria-label={isCompactLayout ? t('common.close') : (
131
+ isSidebarCollapsed ? t('common.expand') : t('common.collapse')
132
+ )}
133
+ onClick={isCompactLayout
134
+ ? onCloseSidebar
135
+ : onToggleSidebarCollapsed}
114
136
  >
115
137
  <span className='material-symbols-rounded'>
116
- {isSidebarCollapsed ? 'dock_to_right' : 'left_panel_close'}
138
+ {isCompactLayout ? 'close' : isSidebarCollapsed ? 'dock_to_right' : 'left_panel_close'}
117
139
  </span>
118
140
  </Button>
119
141
  </Tooltip>
@@ -1,6 +1,8 @@
1
1
  import { Button, Checkbox, Popconfirm, Tooltip } from 'antd'
2
2
  import { useTranslation } from 'react-i18next'
3
3
 
4
+ import { useResponsiveLayout } from '#~/hooks/use-responsive-layout'
5
+
4
6
  interface SidebarHeaderBatchActionsProps {
5
7
  isAllSelected: boolean
6
8
  selectedCount: number
@@ -21,10 +23,12 @@ export function SidebarHeaderBatchActions({
21
23
  onSelectAll
22
24
  }: SidebarHeaderBatchActionsProps) {
23
25
  const { t } = useTranslation()
26
+ const { isTouchInteraction } = useResponsiveLayout()
27
+ const resolveTooltipTitle = (title: string) => isTouchInteraction ? undefined : title
24
28
 
25
29
  return (
26
30
  <div className='header-batch-actions'>
27
- <Tooltip title={isAllSelected ? t('common.deselectAll') : t('common.selectAll')}>
31
+ <Tooltip title={resolveTooltipTitle(isAllSelected ? t('common.deselectAll') : t('common.selectAll'))}>
28
32
  <label className='batch-select-toggle'>
29
33
  <Checkbox
30
34
  checked={isAllSelected}
@@ -33,7 +37,7 @@ export function SidebarHeaderBatchActions({
33
37
  />
34
38
  </label>
35
39
  </Tooltip>
36
- <Tooltip title={t('common.star')}>
40
+ <Tooltip title={resolveTooltipTitle(t('common.star'))}>
37
41
  <Button
38
42
  className='sidebar-tool-btn is-icon-only'
39
43
  type='text'
@@ -50,7 +54,7 @@ export function SidebarHeaderBatchActions({
50
54
  okButtonProps={{ danger: false }}
51
55
  disabled={selectedCount === 0}
52
56
  >
53
- <Tooltip title={t('common.archive')}>
57
+ <Tooltip title={resolveTooltipTitle(t('common.archive'))}>
54
58
  <Button
55
59
  className='sidebar-tool-btn is-icon-only'
56
60
  type='text'
@@ -67,7 +71,7 @@ export function SidebarHeaderBatchActions({
67
71
  okButtonProps={{ danger: true }}
68
72
  disabled={selectedCount === 0}
69
73
  >
70
- <Tooltip title={t('common.delete')}>
74
+ <Tooltip title={resolveTooltipTitle(t('common.delete'))}>
71
75
  <Button
72
76
  className='sidebar-tool-btn is-icon-only is-danger'
73
77
  type='text'
@@ -2,6 +2,7 @@ import { Button, Input, Tooltip } from 'antd'
2
2
  import { useMemo } from 'react'
3
3
  import { useTranslation } from 'react-i18next'
4
4
 
5
+ import { useResponsiveLayout } from '#~/hooks/use-responsive-layout'
5
6
  import type { SidebarSessionSortOrder } from '#~/hooks/use-sidebar-query-state'
6
7
  import { SidebarHeaderBatchActions } from './SidebarHeaderBatchActions'
7
8
  import { SidebarHeaderSelectField } from './SidebarHeaderSelectField'
@@ -56,9 +57,11 @@ export function SidebarHeaderSearchActions({
56
57
  onToggleSearchActions
57
58
  }: SidebarHeaderSearchActionsProps) {
58
59
  const { t } = useTranslation()
60
+ const { isTouchInteraction } = useResponsiveLayout()
59
61
  const isAllSelected = totalCount > 0 && selectedCount === totalCount
60
62
  const toOptions = useMemo(() => (values: string[]) => values.map((value) => ({ label: value, value })), [])
61
63
  const filterSuffixIcon = <span className='material-symbols-rounded toolbar-filter-chevron'>expand_more</span>
64
+ const resolveTooltipTitle = (title: string) => isTouchInteraction ? undefined : title
62
65
  const sortOptions = useMemo(
63
66
  () => [
64
67
  { label: t('automation.sortDesc'), value: 'desc' },
@@ -78,7 +81,7 @@ export function SidebarHeaderSearchActions({
78
81
  onChange={(e) => onSearchChange(e.target.value)}
79
82
  prefix={<span className='material-symbols-rounded search-icon'>search</span>}
80
83
  suffix={
81
- <Tooltip title={t('common.searchActions')}>
84
+ <Tooltip title={resolveTooltipTitle(t('common.searchActions'))}>
82
85
  <button
83
86
  type='button'
84
87
  className={`search-toggle-button ${shouldShowSearchActions ? 'is-open' : ''} ${
@@ -102,7 +105,7 @@ export function SidebarHeaderSearchActions({
102
105
  <div className='header-toolbar-leading'>
103
106
  {isBatchMode
104
107
  ? (
105
- <Tooltip title={t('common.cancelBatch')}>
108
+ <Tooltip title={resolveTooltipTitle(t('common.cancelBatch'))}>
106
109
  <Button
107
110
  className='sidebar-tool-btn is-icon-only'
108
111
  type='text'
@@ -112,7 +115,7 @@ export function SidebarHeaderSearchActions({
112
115
  </Tooltip>
113
116
  )
114
117
  : (
115
- <Tooltip title={t('common.batchMode')}>
118
+ <Tooltip title={resolveTooltipTitle(t('common.batchMode'))}>
116
119
  <Button
117
120
  className='sidebar-tool-btn is-icon-only'
118
121
  type='text'
@@ -0,0 +1,69 @@
1
+ import { Button, Dropdown } from 'antd'
2
+ import type { MenuProps } from 'antd'
3
+ import { useAtom } from 'jotai'
4
+ import { useMemo } from 'react'
5
+ import { useTranslation } from 'react-i18next'
6
+ import { useNavigate } from 'react-router-dom'
7
+
8
+ import { themeAtom } from '#~/store'
9
+ import { buildLanguageItems, buildThemeItems } from '../nav-rail-items'
10
+
11
+ export function SidebarUtilityFooter() {
12
+ const { t, i18n } = useTranslation()
13
+ const [themeMode, setThemeMode] = useAtom(themeAtom)
14
+ const navigate = useNavigate()
15
+
16
+ const languageItems: MenuProps['items'] = useMemo(() =>
17
+ buildLanguageItems({
18
+ currentLanguage: i18n.language,
19
+ onChangeLanguage: (language) => i18n.changeLanguage(language)
20
+ }), [i18n.language])
21
+
22
+ const themeItems: MenuProps['items'] = useMemo(() =>
23
+ buildThemeItems({
24
+ setThemeMode,
25
+ t,
26
+ themeMode
27
+ }), [setThemeMode, t, themeMode])
28
+
29
+ return (
30
+ <div className='sidebar-utility-footer'>
31
+ <Dropdown menu={{ items: themeItems, triggerSubMenuAction: 'click' }} placement='topLeft' trigger={['click']}>
32
+ <Button
33
+ type='text'
34
+ className='sidebar-utility-footer__action'
35
+ icon={
36
+ <span className='material-symbols-rounded'>
37
+ {themeMode === 'light' ? 'light_mode' : themeMode === 'dark' ? 'dark_mode' : 'desktop_windows'}
38
+ </span>
39
+ }
40
+ >
41
+ <span>{t('common.theme')}</span>
42
+ </Button>
43
+ </Dropdown>
44
+
45
+ <Dropdown
46
+ menu={{ items: languageItems, triggerSubMenuAction: 'click' }}
47
+ placement='topLeft'
48
+ trigger={['click']}
49
+ >
50
+ <Button
51
+ type='text'
52
+ className='sidebar-utility-footer__action'
53
+ icon={<span className='material-symbols-rounded'>language</span>}
54
+ >
55
+ <span>{t('common.language')}</span>
56
+ </Button>
57
+ </Dropdown>
58
+
59
+ <Button
60
+ type='text'
61
+ className='sidebar-utility-footer__action'
62
+ icon={<span className='material-symbols-rounded'>settings</span>}
63
+ onClick={() => void navigate('/config')}
64
+ >
65
+ <span>{t('common.settings')}</span>
66
+ </Button>
67
+ </div>
68
+ )
69
+ }
@@ -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()