@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
@@ -1,16 +1,21 @@
1
+ /* eslint-disable max-lines */
2
+
1
3
  import { App } from 'antd'
2
4
  import { useEffect, useMemo, useState } from 'react'
3
5
  import { useTranslation } from 'react-i18next'
4
6
  import useSWR from 'swr'
5
7
 
6
- import type { GitBranchListResult, GitBranchSummary, GitRepositoryState } from '@vibe-forge/types'
8
+ import type { GitBranchListResult, GitBranchSummary, GitRepositoryState, SessionWorkspace } from '@vibe-forge/types'
7
9
 
8
10
  import {
9
11
  checkoutSessionGitBranch,
10
12
  createSessionGitBranch,
13
+ createSessionManagedWorktree,
11
14
  getApiErrorMessage,
12
15
  getSessionGitState,
13
- listSessionGitBranches
16
+ getSessionWorkspace,
17
+ listSessionGitBranches,
18
+ transferSessionWorkspaceToLocal
14
19
  } from '#~/api'
15
20
 
16
21
  import { filterGitBranches, getGitBranchViewState, hasExactGitBranchMatch } from './git-branch-utils'
@@ -20,7 +25,15 @@ import { useChatGitCommit } from './use-chat-git-commit'
20
25
  import { useChatGitPushState } from './use-chat-git-push-state'
21
26
  import { useChatGitWorktrees } from './use-chat-git-worktrees'
22
27
 
23
- type GitActionKind = 'branch-create' | 'branch-switch' | 'commit' | 'commit-and-push' | 'push' | 'sync'
28
+ type GitActionKind =
29
+ | 'branch-create'
30
+ | 'branch-switch'
31
+ | 'commit'
32
+ | 'commit-and-push'
33
+ | 'push'
34
+ | 'sync'
35
+ | 'workspace-create'
36
+ | 'workspace-transfer'
24
37
 
25
38
  export function useChatGitControls(sessionId: string) {
26
39
  const { t } = useTranslation()
@@ -32,6 +45,11 @@ export function useChatGitControls(sessionId: string) {
32
45
  const [pendingAction, setPendingAction] = useState<GitActionKind | null>(null)
33
46
  const push = useChatGitPushState()
34
47
 
48
+ const { data: workspaceData, mutate: mutateWorkspaceData } = useSWR<{ workspace: SessionWorkspace }>(
49
+ ['session-workspace', sessionId],
50
+ () => getSessionWorkspace(sessionId),
51
+ { revalidateOnFocus: false }
52
+ )
35
53
  const { data: repoState, mutate: mutateRepoState } = useSWR<GitRepositoryState>(
36
54
  ['session-git-state', sessionId],
37
55
  () => getSessionGitState(sessionId),
@@ -75,6 +93,15 @@ export function useChatGitControls(sessionId: string) {
75
93
  }
76
94
  }
77
95
 
96
+ const refreshWorkspaceState = async (nextWorkspace?: SessionWorkspace) => {
97
+ if (nextWorkspace != null) {
98
+ await mutateWorkspaceData({ workspace: nextWorkspace }, { revalidate: false })
99
+ return
100
+ }
101
+
102
+ await mutateWorkspaceData()
103
+ }
104
+
78
105
  const commit = useChatGitCommit({
79
106
  closeOperationsMenu: () => setOperationsMenuOpen(false),
80
107
  refreshGitState,
@@ -116,6 +143,26 @@ export function useChatGitControls(sessionId: string) {
116
143
  task
117
144
  })
118
145
 
146
+ const runWorkspaceMutation = async (
147
+ action: Extract<GitActionKind, 'workspace-create' | 'workspace-transfer'>,
148
+ task: () => Promise<{ workspace: SessionWorkspace }>,
149
+ successMessage: string,
150
+ onSuccess?: () => void
151
+ ) => {
152
+ setPendingAction(action)
153
+ try {
154
+ const result = await task()
155
+ await refreshWorkspaceState(result.workspace)
156
+ await refreshGitState()
157
+ onSuccess?.()
158
+ void message.success(successMessage)
159
+ } catch (error) {
160
+ void message.error(getApiErrorMessage(error, t('common.operationFailed')))
161
+ } finally {
162
+ setPendingAction(null)
163
+ }
164
+ }
165
+
119
166
  const closeBranchMenu = () => {
120
167
  setBranchMenuOpen(false)
121
168
  setBranchQuery('')
@@ -163,6 +210,29 @@ export function useChatGitControls(sessionId: string) {
163
210
  })
164
211
  }
165
212
 
213
+ const handleCreateManagedWorktree = () => {
214
+ void runWorkspaceMutation(
215
+ 'workspace-create',
216
+ () => createSessionManagedWorktree(sessionId),
217
+ t('chat.sessionWorkspaceCreateWorktreeSuccess'),
218
+ () => {
219
+ worktree.setWorktreeMenuOpen(false)
220
+ setBranchMenuOpen(false)
221
+ }
222
+ )
223
+ }
224
+
225
+ const handleTransferWorkspaceToLocal = () => {
226
+ void runWorkspaceMutation(
227
+ 'workspace-transfer',
228
+ () => transferSessionWorkspaceToLocal(sessionId),
229
+ t('chat.sessionWorkspaceTransferToLocalSuccess'),
230
+ () => {
231
+ worktree.setWorktreeMenuOpen(false)
232
+ }
233
+ )
234
+ }
235
+
166
236
  return {
167
237
  branchMenuOpen,
168
238
  branchQuery,
@@ -170,8 +240,10 @@ export function useChatGitControls(sessionId: string) {
170
240
  currentBranchLabel,
171
241
  handleBranchSwitch,
172
242
  handleCreateBranch,
243
+ handleCreateManagedWorktree,
173
244
  handleOpenPushModal,
174
245
  handlePush,
246
+ handleTransferWorkspaceToLocal,
175
247
  hasBranchResults,
176
248
  isBranchListLoading,
177
249
  isBusy,
@@ -183,6 +255,7 @@ export function useChatGitControls(sessionId: string) {
183
255
  pushModalOpen: push.pushModalOpen,
184
256
  remoteBranches,
185
257
  repoState,
258
+ workspace: workspaceData?.workspace,
186
259
  runMutation,
187
260
  showWorktreeButton: worktree.showWorktreeButton,
188
261
  worktreeMenuOpen: worktree.worktreeMenuOpen,
@@ -21,6 +21,7 @@ interface MessageContextMenuProps {
21
21
  isEditing: boolean
22
22
  message: ChatMessage
23
23
  sessionId?: string
24
+ trigger?: ('click' | 'contextMenu')[]
24
25
  onFork: () => void
25
26
  onRecall: () => void
26
27
  onStartEditing: () => void
@@ -39,6 +40,7 @@ export function MessageContextMenu({
39
40
  isEditing,
40
41
  message: sourceMessage,
41
42
  sessionId,
43
+ trigger = ['contextMenu'],
42
44
  onFork,
43
45
  onRecall,
44
46
  onStartEditing
@@ -86,7 +88,7 @@ export function MessageContextMenu({
86
88
 
87
89
  return (
88
90
  <Dropdown
89
- trigger={['contextMenu']}
91
+ trigger={trigger}
90
92
  open={open}
91
93
  onOpenChange={(nextOpen) => {
92
94
  setOpen(nextOpen)
@@ -34,10 +34,22 @@
34
34
  margin-top: 4px;
35
35
  display: flex;
36
36
  align-items: center;
37
+ flex-wrap: wrap;
37
38
  gap: 8px;
39
+ min-width: 0;
38
40
  font-size: 11px;
39
41
  color: var(--sub-text-color, #9ca3af);
40
42
 
43
+ > * {
44
+ min-width: 0;
45
+ }
46
+
47
+ .msg-model,
48
+ .msg-usage,
49
+ .timestamp {
50
+ overflow-wrap: anywhere;
51
+ }
52
+
41
53
  .timestamp {
42
54
  user-select: none;
43
55
  cursor: pointer;
@@ -141,6 +153,13 @@
141
153
  }
142
154
  }
143
155
 
156
+ .chat-message-user.has-compact-menu,
157
+ .chat-message-assistant.has-compact-menu {
158
+ .msg-footer {
159
+ opacity: 1;
160
+ }
161
+ }
162
+
144
163
  .msg-action-button__label {
145
164
  font-size: 11px;
146
165
  line-height: 1;
@@ -170,6 +189,12 @@
170
189
  }
171
190
  }
172
191
 
192
+ .msg-action-button--menu {
193
+ width: 40px;
194
+ min-width: 40px;
195
+ height: 40px;
196
+ }
197
+
173
198
  .chat-message-user {
174
199
  flex-direction: row-reverse;
175
200
  gap: 8px;
@@ -246,8 +271,9 @@ html.dark {
246
271
  }
247
272
 
248
273
  .message-image {
249
- display: inline-block;
250
- max-width: 360px;
274
+ display: block;
275
+ width: min(100%, 360px);
276
+ max-width: 100%;
251
277
  border-radius: 10px;
252
278
  overflow: hidden;
253
279
  border: 1px solid var(--border-color);
@@ -255,6 +281,7 @@ html.dark {
255
281
 
256
282
  .message-image img {
257
283
  display: block;
284
+ width: 100%;
258
285
  max-width: 100%;
259
286
  height: auto;
260
287
  }
@@ -292,7 +319,9 @@ html.dark {
292
319
  font-size: 14px;
293
320
  line-height: 1.5;
294
321
  word-wrap: break-word;
322
+ overflow-wrap: anywhere;
295
323
  width: 100%;
324
+ min-width: 0;
296
325
 
297
326
  p {
298
327
  margin: 0 0 4px 0;
@@ -355,14 +384,28 @@ html.dark {
355
384
  color: #6b7280;
356
385
  }
357
386
 
387
+ .markdown-table-wrapper {
388
+ max-width: 100%;
389
+ margin: 12px 0;
390
+ overflow-x: auto;
391
+ overflow-y: hidden;
392
+ border: 1px solid #e5e7eb;
393
+ border-radius: 8px;
394
+ -webkit-overflow-scrolling: touch;
395
+ scrollbar-gutter: stable both-edges;
396
+ }
397
+
358
398
  table {
359
399
  border-collapse: collapse;
360
- width: 100%;
361
- margin: 12px 0;
400
+ width: max-content;
401
+ min-width: 100%;
402
+ margin: 0;
362
403
 
363
404
  th, td {
364
405
  border: 1px solid #e5e7eb;
365
406
  padding: 6px 13px;
407
+ vertical-align: top;
408
+ overflow-wrap: anywhere;
366
409
  }
367
410
 
368
411
  tr:nth-child(2n) {
@@ -410,3 +453,34 @@ html.dark {
410
453
  opacity: 1;
411
454
  }
412
455
  }
456
+
457
+ @media (max-width: 960px) {
458
+ .chat-message-user, .chat-message-assistant {
459
+ scroll-margin-top: 64px;
460
+
461
+ .msg-footer {
462
+ gap: 6px;
463
+ font-size: 10px;
464
+ }
465
+ }
466
+
467
+ .chat-message-user, .chat-message-assistant {
468
+ &.is-targeted {
469
+ scroll-margin-top: 76px;
470
+
471
+ .bubble {
472
+ outline-offset: 4px;
473
+ }
474
+ }
475
+ }
476
+
477
+ .message-context-file {
478
+ width: 100%;
479
+ }
480
+
481
+ .markdown-body {
482
+ .markdown-table-wrapper {
483
+ margin: 8px 0;
484
+ }
485
+ }
486
+ }
@@ -30,6 +30,8 @@ interface MessageItemProps {
30
30
  sessionInfo?: SessionInfo | null
31
31
  isSessionBusy: boolean
32
32
  isEditing: boolean
33
+ isCompactLayout: boolean
34
+ isTouchInteraction: boolean
33
35
  showAssistantActions: boolean
34
36
  onEditMessage: (messageId: string, content: string | ChatMessageContent[]) => Promise<boolean>
35
37
  onRecallMessage: (messageId: string) => Promise<boolean>
@@ -53,6 +55,8 @@ function MessageItemComponent({
53
55
  sessionInfo,
54
56
  isSessionBusy,
55
57
  isEditing,
58
+ isCompactLayout,
59
+ isTouchInteraction,
56
60
  showAssistantActions,
57
61
  onEditMessage,
58
62
  onRecallMessage,
@@ -80,6 +84,7 @@ function MessageItemComponent({
80
84
  const canFork = isPersistedMessage && !isSessionBusy && isUser
81
85
  const canCopy = copyableText != null
82
86
  const shouldShowAssistantActions = !isUser && showAssistantActions
87
+ const showCompactActionMenu = isCompactLayout || isTouchInteraction
83
88
 
84
89
  useEffect(() => {
85
90
  setIsSubmitting(false)
@@ -384,6 +389,36 @@ function MessageItemComponent({
384
389
  </>
385
390
  )
386
391
 
392
+ const compactActionMenu = (
393
+ <MessageContextMenu
394
+ anchorId={anchorId}
395
+ canEdit={canEdit}
396
+ canFork={canFork}
397
+ canRecall={canRecall}
398
+ copyableText={copyableText}
399
+ isDebugMode={isDebugMode}
400
+ isEditing={isEditing}
401
+ message={originalMessage}
402
+ sessionId={sessionId}
403
+ trigger={['click']}
404
+ onFork={handleFork}
405
+ onRecall={handleRecall}
406
+ onStartEditing={handleStartEdit}
407
+ >
408
+ <button
409
+ type='button'
410
+ className='msg-action-button msg-action-button--menu'
411
+ title={t('common.moreActions')}
412
+ aria-label={t('common.moreActions')}
413
+ onClick={(event) => {
414
+ event.stopPropagation()
415
+ }}
416
+ >
417
+ <span className='material-symbols-rounded'>more_horiz</span>
418
+ </button>
419
+ </MessageContextMenu>
420
+ )
421
+
387
422
  return (
388
423
  <MessageContextMenu
389
424
  anchorId={anchorId}
@@ -404,13 +439,15 @@ function MessageItemComponent({
404
439
  id={anchorId}
405
440
  className={`${isUser ? 'chat-message-user' : 'chat-message-assistant'} ${isEditing ? 'is-editing' : ''} ${
406
441
  !isFirstInGroup ? 'consecutive' : ''
407
- } ${isActionsVisible ? 'is-actions-visible' : ''} ${isTargeted ? 'is-targeted' : ''}`}
442
+ } ${isActionsVisible ? 'is-actions-visible' : ''} ${isTargeted ? 'is-targeted' : ''} ${
443
+ showCompactActionMenu ? 'has-compact-menu' : ''
444
+ }`}
408
445
  data-message-id={originalMessage.id}
409
446
  onPointerEnter={handleActionsPointerEnter}
410
447
  onPointerLeave={handleActionsPointerLeave}
411
448
  >
412
449
  <div className={`message-body-container ${isEditing ? 'is-editing' : ''}`}>
413
- {isUser && !isEditing && (
450
+ {isUser && !isEditing && !showCompactActionMenu && (
414
451
  <div className='message-side-actions'>
415
452
  {actionButtons}
416
453
  </div>
@@ -421,6 +458,7 @@ function MessageItemComponent({
421
458
  <div className='message-inline-editor'>
422
459
  <Sender
423
460
  variant='inline-edit'
461
+ sessionId={sessionId}
424
462
  sessionInfo={sessionInfo}
425
463
  initialContent={editableContent}
426
464
  submitLabel={t('chat.send')}
@@ -439,7 +477,11 @@ function MessageItemComponent({
439
477
  </div>
440
478
  {!isEditing && (
441
479
  <MessageFooter msg={originalMessage} isUser={isUser}>
442
- {shouldShowAssistantActions ? actionButtons : undefined}
480
+ {showCompactActionMenu
481
+ ? compactActionMenu
482
+ : shouldShowAssistantActions
483
+ ? actionButtons
484
+ : undefined}
443
485
  </MessageFooter>
444
486
  )}
445
487
  </div>
@@ -454,6 +496,8 @@ const areMessageItemPropsEqual = (prev: MessageItemProps, next: MessageItemProps
454
496
  prev.isTargeted === next.isTargeted &&
455
497
  prev.isSessionBusy === next.isSessionBusy &&
456
498
  prev.isEditing === next.isEditing &&
499
+ prev.isCompactLayout === next.isCompactLayout &&
500
+ prev.isTouchInteraction === next.isTouchInteraction &&
457
501
  prev.sessionId === next.sessionId &&
458
502
  prev.sessionInfo === next.sessionInfo &&
459
503
  prev.showAssistantActions === next.showAssistantActions &&
@@ -92,3 +92,18 @@
92
92
  font-size: 15px;
93
93
  line-height: 1;
94
94
  }
95
+
96
+ @media (max-width: 960px) {
97
+ .adapter-select,
98
+ .adapter-select--locked {
99
+ width: var(--sender-control-height);
100
+ min-width: var(--sender-control-height);
101
+ }
102
+
103
+ .adapter-option__icon,
104
+ .adapter-option__icon--fallback {
105
+ width: 14px;
106
+ height: 14px;
107
+ margin: 0 auto;
108
+ }
109
+ }
@@ -21,6 +21,23 @@
21
21
  }
22
22
  }
23
23
 
24
+ @media (max-width: 960px) {
25
+ .toolbar-btn.toolbar-btn--reference {
26
+ width: var(--sender-control-height);
27
+ min-width: var(--sender-control-height);
28
+ height: var(--sender-control-height);
29
+ padding: 0 !important;
30
+ justify-content: center;
31
+
32
+ .toolbar-btn__icon-shell {
33
+ width: 100%;
34
+ min-width: 100%;
35
+ height: 100%;
36
+ border-radius: var(--sender-control-radius);
37
+ }
38
+ }
39
+ }
40
+
24
41
  .reference-actions-popover,
25
42
  .reference-actions-submenu-popover {
26
43
  .ant-popover-inner {
@@ -1,6 +1,8 @@
1
1
  import { Popover } from 'antd'
2
2
  import { useTranslation } from 'react-i18next'
3
3
 
4
+ import { useResponsiveLayout } from '#~/hooks/use-responsive-layout'
5
+
4
6
  import type {
5
7
  SenderToolbarData,
6
8
  SenderToolbarHandlers,
@@ -27,6 +29,7 @@ export function ReferencePermissionActionsPopover({
27
29
  >
28
30
  }) {
29
31
  const { t } = useTranslation()
32
+ const { isTouchInteraction } = useResponsiveLayout()
30
33
  const { showReferenceActions, showPermissionActions, permissionMode } = state
31
34
  const { permissionModeOptions } = data
32
35
  const { referenceMenuNavigation, permissionMenuNavigation } = refs
@@ -81,7 +84,7 @@ export function ReferencePermissionActionsPopover({
81
84
  onShowPermissionActionsChange(nextOpen)
82
85
  }}
83
86
  placement='rightTop'
84
- trigger={['hover', 'click']}
87
+ trigger={isTouchInteraction ? ['click'] : ['hover', 'click']}
85
88
  classNames={{ root: 'reference-actions-submenu-popover' }}
86
89
  destroyOnHidden
87
90
  arrow={false}
@@ -4,6 +4,7 @@
4
4
  gap: 6px;
5
5
  padding: 2px 0 8px;
6
6
  align-items: flex-start;
7
+ min-width: 0;
7
8
  }
8
9
 
9
10
  .pending-attachments__files,
@@ -12,6 +13,7 @@
12
13
  display: flex;
13
14
  gap: 6px;
14
15
  align-items: flex-start;
16
+ min-width: 0;
15
17
  }
16
18
 
17
19
  .pending-attachments__files {
@@ -77,10 +79,11 @@
77
79
  }
78
80
 
79
81
  .pending-context-file {
80
- display: flex;
82
+ display: grid;
83
+ grid-template-columns: minmax(0, 1fr) auto;
81
84
  width: 100%;
85
+ min-width: 0;
82
86
  align-items: center;
83
- justify-content: space-between;
84
87
  gap: 6px;
85
88
  padding: 5px 8px;
86
89
  border: 1px solid rgba(148, 163, 184, .2);
@@ -142,12 +145,15 @@
142
145
  align-items: center;
143
146
  gap: 8px;
144
147
  flex-shrink: 0;
148
+ min-width: 0;
149
+ justify-self: end;
145
150
  }
146
151
 
147
152
  .pending-context-file__size {
148
153
  font-size: 9px;
149
154
  font-weight: 600;
150
155
  color: var(--sub-text-color, #6b7280);
156
+ white-space: nowrap;
151
157
  }
152
158
 
153
159
  .pending-context-file__remove {
@@ -218,7 +224,14 @@ html.dark {
218
224
 
219
225
  @media (max-width: 640px) {
220
226
  .pending-context-file {
227
+ grid-template-columns: minmax(0, 1fr);
228
+ align-items: stretch;
229
+ }
230
+
231
+ .pending-context-file__actions {
221
232
  width: 100%;
233
+ justify-content: space-between;
234
+ justify-self: stretch;
222
235
  }
223
236
 
224
237
  .pending-attachments__images {
@@ -28,6 +28,7 @@ export function SenderBody({
28
28
  onRemovePendingImage,
29
29
  onRemovePendingFile,
30
30
  editorRef,
31
+ sessionId,
31
32
  sessionInfo,
32
33
  placeholder,
33
34
  input,
@@ -55,6 +56,7 @@ export function SenderBody({
55
56
  onRemovePendingImage: (id: string) => void
56
57
  onRemovePendingFile: (path: string) => void
57
58
  editorRef: MutableRefObject<SenderEditorHandle | null>
59
+ sessionId?: string
58
60
  sessionInfo?: SessionInfo | null
59
61
  placeholder: string
60
62
  input: string
@@ -110,6 +112,7 @@ export function SenderBody({
110
112
  {!isInlineEdit && (
111
113
  <ContextFilePicker
112
114
  open={showContextPicker}
115
+ sessionId={sessionId}
113
116
  selectedPaths={pendingFiles.map(file => file.path)}
114
117
  onCancel={onCancelContextPicker}
115
118
  onConfirm={onConfirmContextPicker}
@@ -1,19 +1,68 @@
1
+ import * as monacoApi from 'monaco-editor'
2
+ import type { editor as MonacoEditorNamespace } from 'monaco-editor'
1
3
  import { useEffect, useState } from 'react'
2
4
 
3
- const getThemeName = () => (document.documentElement.classList.contains('dark') ? 'vs-dark' : 'vs')
5
+ const SENDER_MONACO_LIGHT_THEME = 'vf-sender-light'
6
+ const SENDER_MONACO_DARK_THEME = 'vf-sender-dark'
7
+ const HEX_COLOR_PATTERN = /^#(?:[\da-f]{3}|[\da-f]{4}|[\da-f]{6}|[\da-f]{8})$/i
8
+
9
+ const readCssColor = (styles: CSSStyleDeclaration, name: string, fallback: string) => {
10
+ const value = styles.getPropertyValue(name).trim()
11
+
12
+ if (HEX_COLOR_PATTERN.test(value)) {
13
+ return value
14
+ }
15
+
16
+ return fallback
17
+ }
18
+
19
+ const buildSenderMonacoTheme = (isDark: boolean): MonacoEditorNamespace.IStandaloneThemeData => {
20
+ const styles = window.getComputedStyle(document.documentElement)
21
+ const background = readCssColor(styles, '--bg-color', isDark ? '#141414' : '#ffffff')
22
+ const foreground = readCssColor(styles, '--text-color', isDark ? '#ffffff' : '#000000')
23
+ const placeholder = readCssColor(styles, '--placeholder-color', isDark ? '#575859' : '#9ca3af')
24
+ const primaryColor = readCssColor(styles, '--primary-color', isDark ? '#3b82f6' : '#2563eb')
25
+ const selectionBackground = readCssColor(styles, '--primary-soft-bg', isDark ? '#111b26' : '#eff6ff')
26
+
27
+ return {
28
+ base: isDark ? 'vs-dark' : 'vs',
29
+ colors: {
30
+ 'editor.background': background,
31
+ 'editor.foreground': foreground,
32
+ 'editor.inactiveSelectionBackground': selectionBackground,
33
+ 'editor.placeholder.foreground': placeholder,
34
+ 'editor.selectionBackground': selectionBackground,
35
+ 'editorCursor.foreground': primaryColor
36
+ },
37
+ inherit: false,
38
+ rules: []
39
+ }
40
+ }
41
+
42
+ const defineSenderMonacoTheme = (isDark: boolean) => {
43
+ const themeName = isDark ? SENDER_MONACO_DARK_THEME : SENDER_MONACO_LIGHT_THEME
44
+
45
+ monacoApi.editor.defineTheme(themeName, buildSenderMonacoTheme(isDark))
46
+
47
+ return themeName
48
+ }
49
+
50
+ const getThemeName = () => defineSenderMonacoTheme(document.documentElement.classList.contains('dark'))
4
51
 
5
52
  export const useSenderMonacoTheme = () => {
6
53
  const [themeName, setThemeName] = useState(getThemeName)
7
54
 
8
55
  useEffect(() => {
9
- const observer = new MutationObserver(() => {
56
+ const syncThemeName = () => {
10
57
  setThemeName(getThemeName())
11
- })
58
+ }
59
+ const observer = new MutationObserver(syncThemeName)
12
60
 
13
61
  observer.observe(document.documentElement, {
14
62
  attributes: true,
15
63
  attributeFilter: ['class']
16
64
  })
65
+ syncThemeName()
17
66
 
18
67
  return () => {
19
68
  observer.disconnect()
@@ -149,3 +149,15 @@
149
149
  flex: 0 0 auto;
150
150
  }
151
151
  }
152
+
153
+ @media (max-width: 960px) {
154
+ .chat-send-btn {
155
+ width: 24px;
156
+ min-width: 24px;
157
+ height: 24px;
158
+
159
+ .material-symbols-rounded {
160
+ font-size: 14px;
161
+ }
162
+ }
163
+ }
@@ -69,3 +69,24 @@
69
69
  opacity: .56;
70
70
  }
71
71
  }
72
+
73
+ @media (max-width: 960px) {
74
+ .model-select,
75
+ .effort-select {
76
+ .ant-select-arrow {
77
+ width: 20px;
78
+ min-width: 20px;
79
+ flex: 0 0 20px;
80
+ }
81
+
82
+ &.ant-select-single .ant-select-selector {
83
+ padding-left: 6px !important;
84
+ padding-inline-start: 6px !important;
85
+ }
86
+
87
+ .ant-select-selection-item,
88
+ .ant-select-selection-placeholder {
89
+ font-size: 11px;
90
+ }
91
+ }
92
+ }