@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
@@ -1,25 +1,43 @@
1
1
  import { App } from 'antd'
2
- import React, { useEffect, useMemo, useRef, useState } from 'react'
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
-
6
- import type { AskUserQuestionParams, ChatMessage, ChatMessageContent, Session } from '@vibe-forge/core'
7
- import type { SessionInfo } from '@vibe-forge/types'
8
-
5
+ import useSWR from 'swr'
6
+
7
+ import type {
8
+ AskUserQuestionParams,
9
+ ChatMessage,
10
+ ChatMessageContent,
11
+ Session,
12
+ SessionMessageQueueState,
13
+ SessionQueuedMessage,
14
+ SessionQueuedMessageMode
15
+ } from '@vibe-forge/core'
16
+ import type { ConfigResponse, SessionInfo } from '@vibe-forge/types'
17
+
18
+ import { getConfig } from '#~/api'
19
+ import {
20
+ DEFAULT_CHAT_SESSION_WORKSPACE_DRAFT,
21
+ getChatSessionWorkspaceDraftFromConfig
22
+ } from '#~/hooks/chat/chat-session-workspace-draft'
9
23
  import type { ChatEffort } from '#~/hooks/chat/use-chat-effort'
10
24
  import type { ModelSelectMenuGroup, ModelSelectOption } from '#~/hooks/chat/use-chat-model-adapter-selection'
11
25
  import type { PermissionMode } from '#~/hooks/chat/use-chat-permission-mode'
12
26
  import { useChatScroll } from '#~/hooks/chat/use-chat-scroll'
13
27
  import { useChatSessionActions } from '#~/hooks/chat/use-chat-session-actions'
14
-
28
+ import { useResponsiveLayout } from '#~/hooks/use-responsive-layout'
29
+ import { getLoopedIndex } from '#~/hooks/use-roving-focus-list'
15
30
  import { CurrentTodoList } from './CurrentTodoList'
16
31
  import { NewSessionGuide } from './NewSessionGuide'
32
+ import { QueuedMessagesCard } from './QueuedMessagesCard'
17
33
  import { MessageItem } from './messages/MessageItem'
18
34
  import { MessageStatusNotice } from './messages/MessageStatusNotice'
19
35
  import type { ChatHistoryStatusNotice } from './messages/build-chat-history-status-notices'
20
36
  import { buildMessageTurns } from './messages/message-turns'
21
37
  import { processMessages } from './messages/message-utils'
38
+ import { SenderInteractionPanel } from './sender/@components/sender-interaction-panel/SenderInteractionPanel'
22
39
  import { Sender } from './sender/Sender'
40
+ import { ChatStatusBar } from './status-bar/ChatStatusBar'
23
41
  import { ToolGroup } from './tools/core/ToolGroup'
24
42
 
25
43
  export function ChatHistoryView({
@@ -30,6 +48,7 @@ export function ChatHistoryView({
30
48
  targetToolUseId,
31
49
  sessionInfo,
32
50
  historyStatusNotices,
51
+ queuedMessages,
33
52
  onRetryConnection,
34
53
  interactionRequest,
35
54
  onInteractionResponse,
@@ -64,6 +83,7 @@ export function ChatHistoryView({
64
83
  targetToolUseId?: string
65
84
  sessionInfo: SessionInfo | null
66
85
  historyStatusNotices: ChatHistoryStatusNotice[]
86
+ queuedMessages: SessionMessageQueueState
67
87
  onRetryConnection: () => void
68
88
  interactionRequest: { id: string; payload: AskUserQuestionParams } | null
69
89
  onInteractionResponse: (id: string, data: string | string[]) => void
@@ -94,6 +114,16 @@ export function ChatHistoryView({
94
114
  const { t } = useTranslation()
95
115
  const { message } = App.useApp()
96
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
+ }))
97
127
  const historyRenderCount = messages.length + historyStatusNotices.length
98
128
  const { messagesEndRef, messagesContainerRef, messagesContentRef, showScrollBottom, scrollToBottom } = useChatScroll({
99
129
  contentVersion: historyRenderCount
@@ -102,6 +132,10 @@ export function ChatHistoryView({
102
132
  isCreating,
103
133
  send,
104
134
  sendContent,
135
+ enqueueContent,
136
+ removeQueuedContent,
137
+ moveQueuedContent,
138
+ reorderQueuedContent,
105
139
  editMessage,
106
140
  forkMessage,
107
141
  interrupt,
@@ -114,6 +148,7 @@ export function ChatHistoryView({
114
148
  effort,
115
149
  permissionMode,
116
150
  adapter: selectedAdapter,
151
+ workspaceDraft,
117
152
  onClearMessages
118
153
  })
119
154
  const initialScrollDoneRef = useRef(false)
@@ -121,6 +156,10 @@ export function ChatHistoryView({
121
156
  const handledTargetScrollKeyRef = useRef('')
122
157
  const [editingMessageId, setEditingMessageId] = useState<string | null>(null)
123
158
  const [expandedTurnIds, setExpandedTurnIds] = useState<Set<string>>(new Set())
159
+ const [queueMode, setQueueMode] = useState<SessionQueuedMessageMode>('steer')
160
+ const [queuedDraft, setQueuedDraft] = useState<{ content: ChatMessageContent[] } | null>(null)
161
+ const [activeInteractionOptionIndex, setActiveInteractionOptionIndex] = useState(0)
162
+ const interactionOptions = interactionRequest?.payload.options ?? []
124
163
  const buildUserMessage = (content: string | ChatMessageContent[]): ChatMessage => {
125
164
  const id = globalThis.crypto?.randomUUID
126
165
  ? globalThis.crypto.randomUUID()
@@ -133,37 +172,78 @@ export function ChatHistoryView({
133
172
  }
134
173
  }
135
174
 
136
- const handleSend = async (text: string) => {
175
+ const handleSendContent = async (content: ChatMessageContent[], mode?: SessionQueuedMessageMode) => {
176
+ const resolvedMode = mode ?? queueMode
177
+
178
+ if (session?.id && session.status === 'running') {
179
+ const didQueue = await enqueueContent(resolvedMode, content)
180
+ if (didQueue && queuedDraft != null) {
181
+ setQueuedDraft(null)
182
+ setQueueMode('steer')
183
+ }
184
+ return didQueue
185
+ }
186
+
137
187
  if (!session?.id) {
138
- const optimisticMessage = buildUserMessage(text)
188
+ const optimisticMessage = buildUserMessage(content)
139
189
  setMessages((prev) => [...prev, optimisticMessage])
140
- const didSend = await send(text)
190
+ const didSend = await sendContent(content, mode)
141
191
  if (!didSend) {
142
192
  setMessages((prev) => prev.filter((message) => message.id !== optimisticMessage.id))
143
193
  }
144
- return
194
+ if (didSend && queuedDraft != null) {
195
+ setQueuedDraft(null)
196
+ setQueueMode('steer')
197
+ }
198
+ return didSend
145
199
  }
146
200
 
147
- const didSend = await send(text)
201
+ const didSend = await sendContent(content, mode)
148
202
  if (didSend) {
149
- setMessages((prev) => [...prev, buildUserMessage(text)])
203
+ setMessages((prev) => [...prev, buildUserMessage(content)])
204
+ if (queuedDraft != null) {
205
+ setQueuedDraft(null)
206
+ setQueueMode('steer')
207
+ }
150
208
  }
209
+ return didSend
151
210
  }
152
- const handleSendContent = async (content: ChatMessageContent[]) => {
211
+
212
+ const handleSend = async (text: string, mode?: SessionQueuedMessageMode) => {
213
+ const resolvedMode = mode ?? queueMode
214
+
215
+ if (session?.id && session.status === 'running') {
216
+ const didQueue = await enqueueContent(resolvedMode, [{ type: 'text', text: text.trim() }])
217
+ if (didQueue && queuedDraft != null) {
218
+ setQueuedDraft(null)
219
+ setQueueMode('steer')
220
+ }
221
+ return didQueue
222
+ }
223
+
153
224
  if (!session?.id) {
154
- const optimisticMessage = buildUserMessage(content)
225
+ const optimisticMessage = buildUserMessage(text)
155
226
  setMessages((prev) => [...prev, optimisticMessage])
156
- const didSend = await sendContent(content)
227
+ const didSend = await send(text, mode)
157
228
  if (!didSend) {
158
229
  setMessages((prev) => prev.filter((message) => message.id !== optimisticMessage.id))
159
230
  }
160
- return
231
+ if (didSend && queuedDraft != null) {
232
+ setQueuedDraft(null)
233
+ setQueueMode('steer')
234
+ }
235
+ return didSend
161
236
  }
162
237
 
163
- const didSend = await sendContent(content)
238
+ const didSend = await send(text, mode)
164
239
  if (didSend) {
165
- setMessages((prev) => [...prev, buildUserMessage(content)])
240
+ setMessages((prev) => [...prev, buildUserMessage(text)])
241
+ if (queuedDraft != null) {
242
+ setQueuedDraft(null)
243
+ setQueueMode('steer')
244
+ }
166
245
  }
246
+ return didSend
167
247
  }
168
248
  useEffect(() => {
169
249
  initialScrollDoneRef.current = false
@@ -171,7 +251,65 @@ export function ChatHistoryView({
171
251
  handledTargetScrollKeyRef.current = ''
172
252
  setEditingMessageId(null)
173
253
  setExpandedTurnIds(new Set())
254
+ setQueuedDraft(null)
255
+ setQueueMode('steer')
256
+ }, [session?.id])
257
+ useEffect(() => {
258
+ if (session?.id != null) {
259
+ return
260
+ }
261
+
262
+ workspaceDraftDirtyRef.current = false
263
+ setWorkspaceDraft({
264
+ ...configWorkspaceDraft
265
+ })
174
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])
280
+ useEffect(() => {
281
+ setActiveInteractionOptionIndex(0)
282
+ }, [interactionRequest?.id])
283
+ useEffect(() => {
284
+ if (interactionOptions.length === 0) {
285
+ setActiveInteractionOptionIndex(0)
286
+ return
287
+ }
288
+
289
+ setActiveInteractionOptionIndex((current) => Math.min(current, interactionOptions.length - 1))
290
+ }, [interactionOptions.length])
291
+
292
+ const handleMoveInteractionOption = useCallback((delta: number) => {
293
+ if (interactionOptions.length === 0) {
294
+ return
295
+ }
296
+
297
+ setActiveInteractionOptionIndex((current) => getLoopedIndex(current, delta, interactionOptions.length))
298
+ }, [interactionOptions.length])
299
+
300
+ const handleSubmitActiveInteractionOption = useCallback(() => {
301
+ if (interactionRequest == null) {
302
+ return
303
+ }
304
+
305
+ const option = interactionOptions[activeInteractionOptionIndex] ?? interactionOptions[0]
306
+ if (option == null) {
307
+ return
308
+ }
309
+
310
+ onInteractionResponse(interactionRequest.id, option.value ?? option.label)
311
+ }, [activeInteractionOptionIndex, interactionOptions, interactionRequest, onInteractionResponse])
312
+
175
313
  useEffect(() => {
176
314
  if (!initialScrollDoneRef.current && isReady && location.hash === '') {
177
315
  scrollToBottom('auto')
@@ -200,6 +338,7 @@ export function ChatHistoryView({
200
338
  }
201
339
  }
202
340
  const isInlineEditing = editingMessageId != null
341
+ const shouldShowNewSessionGuide = !session?.id && messages.length === 0 && historyStatusNotices.length === 0
203
342
  const renderItems = useMemo(() => processMessages(messages), [messages])
204
343
  const hashAnchorId = useMemo(() => decodeURIComponent(location.hash.replace(/^#/, '')), [location.hash])
205
344
  const targetAnchorId = useMemo(() => {
@@ -236,6 +375,38 @@ export function ChatHistoryView({
236
375
  }
237
376
  return null
238
377
  }, [renderItems])
378
+ const handleEditQueuedMessage = async (item: SessionQueuedMessage) => {
379
+ const removed = await removeQueuedContent(item.id)
380
+ if (!removed) {
381
+ return
382
+ }
383
+ setQueuedDraft({ content: item.content })
384
+ setQueueMode('steer')
385
+ }
386
+ const handleMoveQueuedMessage = async (item: SessionQueuedMessage, targetMode: SessionQueuedMessageMode) => {
387
+ await moveQueuedContent(item.id, targetMode)
388
+ }
389
+ const isPermissionInteraction = interactionRequest?.payload.kind === 'permission'
390
+ const interactionPanel = !isInlineEditing && interactionRequest != null
391
+ ? (
392
+ <SenderInteractionPanel
393
+ interactionRequest={interactionRequest}
394
+ activeOptionIndex={activeInteractionOptionIndex}
395
+ permissionContext={interactionRequest.payload.kind === 'permission'
396
+ ? interactionRequest.payload.permissionContext
397
+ : undefined}
398
+ deniedTools={interactionRequest.payload.kind === 'permission'
399
+ ? (interactionRequest.payload.permissionContext?.deniedTools ?? [])
400
+ : []}
401
+ reasons={interactionRequest.payload.kind === 'permission'
402
+ ? (interactionRequest.payload.permissionContext?.reasons ?? [])
403
+ : []}
404
+ onActiveOptionIndexChange={setActiveInteractionOptionIndex}
405
+ onMoveActiveOption={handleMoveInteractionOption}
406
+ onInteractionResponse={onInteractionResponse}
407
+ />
408
+ )
409
+ : null
239
410
 
240
411
  useEffect(() => {
241
412
  const hash = hashAnchorId
@@ -403,6 +574,8 @@ export function ChatHistoryView({
403
574
  originalMessage={item.originalMessage}
404
575
  sessionInfo={sessionInfo}
405
576
  isEditing={editingMessageId === item.originalMessage.id}
577
+ isCompactLayout={isCompactLayout}
578
+ isTouchInteraction={isTouchInteraction}
406
579
  isSessionBusy={isCreating || session?.status === 'running' ||
407
580
  session?.status === 'waiting_input'}
408
581
  showAssistantActions={item.anchorId === lastAssistantActionAnchorId}
@@ -483,47 +656,101 @@ export function ChatHistoryView({
483
656
  )}
484
657
  </div>
485
658
 
486
- {!session?.id && messages.length === 0 && historyStatusNotices.length === 0 && (
659
+ {shouldShowNewSessionGuide && !isCompactLayout && (
487
660
  <div className='new-session-guide-wrapper'>
488
661
  <NewSessionGuide />
489
662
  </div>
490
663
  )}
491
664
 
492
- <CurrentTodoList messages={messages} />
493
- {!isInlineEditing && (
494
- <div className='sender-container'>
495
- <Sender
496
- onSend={handleSend}
497
- onSendContent={handleSendContent}
498
- adapterLocked={session?.id != null}
499
- sessionStatus={isCreating ? 'running' : session?.status}
500
- onInterrupt={interrupt}
501
- onClear={clearMessages}
502
- sessionInfo={sessionInfo}
503
- interactionRequest={interactionRequest}
504
- onInteractionResponse={onInteractionResponse}
505
- placeholder={placeholder}
506
- modelMenuGroups={modelMenuGroups}
507
- modelSearchOptions={modelSearchOptions}
508
- recommendedModelOptions={recommendedModelOptions}
509
- servicePreviewModelOptions={servicePreviewModelOptions}
510
- onToggleRecommendedModel={onToggleRecommendedModel}
511
- updatingRecommendedModelValue={updatingRecommendedModelValue}
512
- selectedModel={selectedModel}
513
- onModelChange={onModelChange}
514
- effort={effort}
515
- effortOptions={effortOptions}
516
- onEffortChange={onEffortChange}
517
- permissionMode={permissionMode}
518
- permissionModeOptions={permissionModeOptions}
519
- onPermissionModeChange={onPermissionModeChange}
520
- selectedAdapter={selectedAdapter}
521
- adapterOptions={adapterOptions}
522
- onAdapterChange={onAdapterChange}
523
- modelUnavailable={modelUnavailable}
524
- />
665
+ {shouldShowNewSessionGuide && isCompactLayout && (
666
+ <div className='new-session-guide-wrapper is-compact-layout'>
667
+ <NewSessionGuide />
525
668
  </div>
526
669
  )}
670
+
671
+ <div className='chat-composer-stack'>
672
+ <div className='chat-composer-stack__inner'>
673
+ {isPermissionInteraction && interactionPanel}
674
+ <CurrentTodoList messages={messages} />
675
+ {!isInlineEditing && (
676
+ <QueuedMessagesCard
677
+ mode='next'
678
+ items={queuedMessages.next}
679
+ onMove={(item, targetMode) => void handleMoveQueuedMessage(item, targetMode)}
680
+ onDelete={(item) => void removeQueuedContent(item.id)}
681
+ onEdit={(item) => void handleEditQueuedMessage(item)}
682
+ onReorder={(ids) => reorderQueuedContent('next', ids)}
683
+ />
684
+ )}
685
+ {!isInlineEditing && (
686
+ <QueuedMessagesCard
687
+ mode='steer'
688
+ items={queuedMessages.steer}
689
+ onMove={(item, targetMode) => void handleMoveQueuedMessage(item, targetMode)}
690
+ onDelete={(item) => void removeQueuedContent(item.id)}
691
+ onEdit={(item) => void handleEditQueuedMessage(item)}
692
+ onReorder={(ids) => reorderQueuedContent('steer', ids)}
693
+ />
694
+ )}
695
+ {!isPermissionInteraction && interactionPanel}
696
+ {!isInlineEditing && (
697
+ <div className='sender-container'>
698
+ <Sender
699
+ onSend={handleSend}
700
+ onSendContent={handleSendContent}
701
+ adapterLocked={session?.id != null}
702
+ sessionId={session?.id}
703
+ sessionStatus={isCreating ? 'running' : session?.status}
704
+ onInterrupt={interrupt}
705
+ onClear={clearMessages}
706
+ sessionInfo={sessionInfo}
707
+ interactionRequest={interactionRequest}
708
+ onInteractionResponse={onInteractionResponse}
709
+ interactionOptionNavigation={interactionRequest != null && interactionOptions.length > 0
710
+ ? {
711
+ optionCount: interactionOptions.length,
712
+ activeIndex: activeInteractionOptionIndex,
713
+ onMove: handleMoveInteractionOption,
714
+ onSubmit: handleSubmitActiveInteractionOption
715
+ }
716
+ : undefined}
717
+ initialContent={queuedDraft?.content}
718
+ placeholder={placeholder}
719
+ submitLabel={queuedDraft != null ? t('chat.queue.requeueMessage') : undefined}
720
+ modelMenuGroups={modelMenuGroups}
721
+ modelSearchOptions={modelSearchOptions}
722
+ recommendedModelOptions={recommendedModelOptions}
723
+ servicePreviewModelOptions={servicePreviewModelOptions}
724
+ onToggleRecommendedModel={onToggleRecommendedModel}
725
+ updatingRecommendedModelValue={updatingRecommendedModelValue}
726
+ selectedModel={selectedModel}
727
+ onModelChange={onModelChange}
728
+ effort={effort}
729
+ effortOptions={effortOptions}
730
+ onEffortChange={onEffortChange}
731
+ permissionMode={permissionMode}
732
+ permissionModeOptions={permissionModeOptions}
733
+ onPermissionModeChange={onPermissionModeChange}
734
+ selectedAdapter={selectedAdapter}
735
+ adapterOptions={adapterOptions}
736
+ onAdapterChange={onAdapterChange}
737
+ modelUnavailable={modelUnavailable}
738
+ queueMode={queueMode}
739
+ onQueueModeChange={setQueueMode}
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
+ />
750
+ </div>
751
+ )}
752
+ </div>
753
+ </div>
527
754
  </>
528
755
  )
529
756
  }
@@ -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'>