@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
@@ -3,6 +3,7 @@ import type { ReactNode } from 'react'
3
3
  import type { ChatEffort } from '#~/hooks/chat/use-chat-effort'
4
4
  import type { ModelSelectOption } from '#~/hooks/chat/use-chat-model-adapter-selection'
5
5
  import type { PermissionMode } from '#~/hooks/chat/use-chat-permission-mode'
6
+ import type { SessionQueuedMessageMode } from '@vibe-forge/core'
6
7
 
7
8
  import type { SenderToolbarHandlers } from '../@types/sender-toolbar-types'
8
9
 
@@ -19,7 +20,9 @@ export const createSenderToolbarHandlers = ({
19
20
  onModelChange,
20
21
  onToggleRecommendedModel,
21
22
  onPermissionModeChange,
23
+ onQueueModeChange,
22
24
  onCancel,
25
+ onConfirmInteractionOption,
23
26
  onSend,
24
27
  referenceActions,
25
28
  selectOverlays,
@@ -41,8 +44,10 @@ export const createSenderToolbarHandlers = ({
41
44
  onModelChange?: (model: string) => void
42
45
  onToggleRecommendedModel?: (option: ModelSelectOption) => void | Promise<void>
43
46
  onPermissionModeChange?: (mode: PermissionMode) => void
47
+ onQueueModeChange?: (mode: SessionQueuedMessageMode) => void
44
48
  onCancel?: () => void
45
- onSend: () => void
49
+ onConfirmInteractionOption?: () => void
50
+ onSend: (mode?: SessionQueuedMessageMode) => void
46
51
  referenceActions: {
47
52
  setShowReferenceActions: (nextOpen: boolean) => void
48
53
  setShowPermissionActions: (nextOpen: boolean) => void
@@ -108,8 +113,10 @@ export const createSenderToolbarHandlers = ({
108
113
  onToggleRecommendedModel,
109
114
  onEffortChange,
110
115
  onAdapterChange,
116
+ onQueueModeChange,
111
117
  onSend,
112
118
  onInterrupt,
113
- onCancel
119
+ onCancel,
120
+ onConfirmInteractionOption
114
121
  } satisfies SenderToolbarHandlers
115
122
  }
@@ -8,7 +8,7 @@ export const getSenderRuntimeState = (
8
8
  const isThinking = !isInlineEdit && props.sessionStatus === 'running'
9
9
  const isBusy = isThinking || props.submitLoading === true
10
10
  const supportsEffort = props.selectedAdapter === 'codex' || props.selectedAdapter === 'claude-code' ||
11
- props.selectedAdapter === 'opencode'
11
+ props.selectedAdapter === 'copilot' || props.selectedAdapter === 'opencode'
12
12
 
13
13
  return { isInlineEdit, isMac, isThinking, isBusy, supportsEffort }
14
14
  }
@@ -1,5 +1,5 @@
1
1
  import type { AskUserQuestionParams } from '@vibe-forge/core'
2
2
 
3
3
  export const shouldHideSenderForInteraction = (
4
- interactionRequest: { id: string; payload: AskUserQuestionParams } | null | undefined
5
- ) => interactionRequest?.payload.kind === 'permission'
4
+ _interactionRequest: { id: string; payload: AskUserQuestionParams } | null | undefined
5
+ ) => false
@@ -1,5 +1,7 @@
1
1
  import type { ReactNode } from 'react'
2
2
 
3
+ import type { SessionQueuedMessageMode } from '@vibe-forge/core'
4
+
3
5
  import type { ChatEffort } from '#~/hooks/chat/use-chat-effort'
4
6
  import type { ModelSelectMenuGroup, ModelSelectOption } from '#~/hooks/chat/use-chat-model-adapter-selection'
5
7
  import type { PermissionMode } from '#~/hooks/chat/use-chat-permission-mode'
@@ -33,8 +35,10 @@ export const createSenderToolbarBindings = ({
33
35
  onModelChange?: (model: string) => void
34
36
  onToggleRecommendedModel?: (option: ModelSelectOption) => void | Promise<void>
35
37
  onPermissionModeChange?: (mode: PermissionMode) => void
38
+ onQueueModeChange?: (mode: SessionQueuedMessageMode) => void
36
39
  onCancel?: () => void
37
- onSend: () => void
40
+ onConfirmInteractionOption?: () => void
41
+ onSend: (mode?: SessionQueuedMessageMode) => void
38
42
  }
39
43
  composer: { input: string; pendingImageCount: number; pendingFileCount: number }
40
44
  resources: { message: { warning: (content: ReactNode) => unknown }; t: (key: string) => string }
@@ -46,6 +50,8 @@ export const createSenderToolbarBindings = ({
46
50
  modelSearchOptions?: ModelSelectOption[]
47
51
  permissionMode: PermissionMode
48
52
  permissionModeOptions: SenderToolbarData['permissionModeOptions']
53
+ queueMode: SessionQueuedMessageMode
54
+ queuedMessageShortcuts: Pick<SenderToolbarData['composerControlShortcuts'], 'queueNext' | 'queueSteer'>
49
55
  recommendedModelOptions?: ModelSelectOption[]
50
56
  servicePreviewModelOptions?: ModelSelectOption[]
51
57
  resolvedSendShortcut: string
@@ -56,11 +62,17 @@ export const createSenderToolbarBindings = ({
56
62
  ui: {
57
63
  adapterLocked: boolean
58
64
  canOpenReferenceActions: boolean
59
- composerControlShortcuts: SenderToolbarData['composerControlShortcuts']
65
+ composerControlShortcuts: Pick<
66
+ SenderToolbarData['composerControlShortcuts'],
67
+ 'switchEffort' | 'switchModel' | 'switchPermissionMode'
68
+ >
60
69
  focusRestore: { queueEditorFocusRestore: () => void }
61
70
  isInlineEdit: boolean
62
71
  isMac: boolean
63
72
  isThinking: boolean
73
+ sendBlocked: boolean
74
+ sendBlockedTooltip?: string
75
+ showConfirmInteractionAction: boolean
64
76
  modelUnavailable?: boolean
65
77
  referenceActions: {
66
78
  showReferenceActions: boolean
@@ -85,6 +97,7 @@ export const createSenderToolbarBindings = ({
85
97
  openEffortSelector: () => boolean
86
98
  }
87
99
  submitLabel?: string
100
+ confirmInteractionLabel?: string
88
101
  submitLoading: boolean
89
102
  supportsEffort: boolean
90
103
  }
@@ -93,6 +106,9 @@ export const createSenderToolbarBindings = ({
93
106
  isInlineEdit: ui.isInlineEdit,
94
107
  isThinking: ui.isThinking,
95
108
  modelUnavailable: Boolean(ui.modelUnavailable),
109
+ sendBlocked: ui.sendBlocked,
110
+ sendBlockedTooltip: ui.sendBlockedTooltip,
111
+ showConfirmInteractionAction: ui.showConfirmInteractionAction,
96
112
  adapterLocked: ui.adapterLocked,
97
113
  submitLoading: ui.submitLoading,
98
114
  supportsEffort: ui.supportsEffort,
@@ -108,6 +124,8 @@ export const createSenderToolbarBindings = ({
108
124
  selectedAdapter: selection.selectedAdapter,
109
125
  isMac: ui.isMac,
110
126
  resolvedSendShortcut: selection.resolvedSendShortcut,
127
+ queueMode: selection.queueMode,
128
+ showQueueModeControl: ui.isThinking && !ui.isInlineEdit,
111
129
  hasComposerContent: composer.input.trim() !== '' || composer.pendingImageCount > 0 || composer.pendingFileCount > 0,
112
130
  hasSendText: composer.input.trim() !== ''
113
131
  }
@@ -121,8 +139,12 @@ export const createSenderToolbarBindings = ({
121
139
  effortOptions: selection.effortOptions,
122
140
  permissionModeOptions: selection.permissionModeOptions,
123
141
  adapterOptions: selection.adapterOptions,
124
- composerControlShortcuts: ui.composerControlShortcuts,
125
- submitLabel: ui.submitLabel
142
+ composerControlShortcuts: {
143
+ ...ui.composerControlShortcuts,
144
+ ...selection.queuedMessageShortcuts
145
+ },
146
+ submitLabel: ui.submitLabel,
147
+ confirmInteractionLabel: ui.confirmInteractionLabel
126
148
  }
127
149
 
128
150
  const toolbarRefs: SenderToolbarRefs = {
@@ -144,7 +166,9 @@ export const createSenderToolbarBindings = ({
144
166
  onModelChange: callbacks.onModelChange,
145
167
  onToggleRecommendedModel: callbacks.onToggleRecommendedModel,
146
168
  onPermissionModeChange: callbacks.onPermissionModeChange,
169
+ onQueueModeChange: callbacks.onQueueModeChange,
147
170
  onCancel: callbacks.onCancel,
171
+ onConfirmInteractionOption: callbacks.onConfirmInteractionOption,
148
172
  onSend: callbacks.onSend,
149
173
  referenceActions: ui.referenceActions,
150
174
  selectOverlays: ui.selectOverlays,
@@ -4,6 +4,7 @@ import { useCallback, useMemo } from 'react'
4
4
  import { useTranslation } from 'react-i18next'
5
5
 
6
6
  import type { ModelSelectMenuGroup, ModelSelectOption } from '#~/hooks/chat/use-chat-model-adapter-selection'
7
+ import { useResponsiveLayout } from '#~/hooks/use-responsive-layout'
7
8
 
8
9
  import { ModelSelectOptionLabel } from '../@components/model-select/ModelSelectOptionLabel'
9
10
 
@@ -27,6 +28,7 @@ export const useModelSelectBrowser = ({
27
28
  onSelectModel: (value: string) => void
28
29
  }) => {
29
30
  const { t } = useTranslation()
31
+ const { isTouchInteraction } = useResponsiveLayout()
30
32
 
31
33
  const renderCompactModelMenuLabel = useCallback((option: ModelSelectOption) => {
32
34
  return (
@@ -175,13 +177,13 @@ export const useModelSelectBrowser = ({
175
177
  mode='vertical'
176
178
  selectable
177
179
  selectedKeys={selectedModelMenuKeys}
178
- triggerSubMenuAction='hover'
180
+ triggerSubMenuAction={isTouchInteraction ? 'click' : 'hover'}
179
181
  items={modelMenuItems}
180
182
  onClick={handleModelMenuClick}
181
183
  />
182
184
  </div>
183
185
  )
184
- }, [hasModelSearchQuery, handleModelMenuClick, modelMenuItems, selectedModelMenuKeys])
186
+ }, [hasModelSearchQuery, handleModelMenuClick, isTouchInteraction, modelMenuItems, selectedModelMenuKeys])
185
187
 
186
188
  return {
187
189
  renderModelPopup
@@ -1,3 +1,4 @@
1
+ /* eslint-disable max-lines */
1
2
  import { App } from 'antd'
2
3
  import { useTranslation } from 'react-i18next'
3
4
 
@@ -79,16 +80,21 @@ export const useSenderController = (props: SenderProps) => {
79
80
  interactionRequest: props.interactionRequest,
80
81
  isInlineEdit
81
82
  })
82
- const { clearInputShortcut, composerControlShortcuts, resolvedSendShortcut } = useSenderShortcuts({
83
- enabled: !hideSender && !attachments.showContextPicker && !isInlineEdit,
84
- isInlineEdit,
85
- isMac,
86
- isThinking,
87
- modelUnavailable: props.modelUnavailable,
88
- permissionModeOptions: props.permissionModeOptions ?? [],
89
- referenceActions,
90
- selectOverlays
91
- })
83
+ const isPermissionInteraction = !isInlineEdit && props.interactionRequest?.payload.kind === 'permission'
84
+ const showConfirmInteractionAction = isPermissionInteraction &&
85
+ (props.interactionOptionNavigation?.optionCount ?? 0) > 0
86
+ const sendBlockedTooltip = isPermissionInteraction ? t('chat.permissionSendBlockedTooltip') : undefined
87
+ const { clearInputShortcut, composerControlShortcuts, resolvedSendShortcut, queuedMessageShortcuts } =
88
+ useSenderShortcuts({
89
+ enabled: !hideSender && !attachments.showContextPicker && !isInlineEdit,
90
+ isInlineEdit,
91
+ isMac,
92
+ isThinking,
93
+ modelUnavailable: props.modelUnavailable,
94
+ permissionModeOptions: props.permissionModeOptions ?? [],
95
+ referenceActions,
96
+ selectOverlays
97
+ })
92
98
 
93
99
  const resetComposer = () => {
94
100
  composer.resetComposerContent()
@@ -103,6 +109,7 @@ export const useSenderController = (props: SenderProps) => {
103
109
  pendingImages: composer.pendingImages,
104
110
  pendingFiles: composer.pendingFiles,
105
111
  isBusy,
112
+ allowWhileBusy: isThinking,
106
113
  isInlineEdit,
107
114
  modelUnavailable: props.modelUnavailable,
108
115
  interactionRequest: props.interactionRequest,
@@ -113,7 +120,20 @@ export const useSenderController = (props: SenderProps) => {
113
120
  t,
114
121
  resetComposer
115
122
  })
116
- const triggerSend = () => void handleSend()
123
+ const handleBlockedSendAttempt = () => {
124
+ void message.error({
125
+ content: t('chat.permissionSendBlockedError'),
126
+ key: 'chat-permission-send-blocked'
127
+ })
128
+ }
129
+ const triggerSend = (mode?: 'steer' | 'next') => {
130
+ if (isPermissionInteraction) {
131
+ handleBlockedSendAttempt()
132
+ return
133
+ }
134
+
135
+ void handleSend(mode)
136
+ }
117
137
 
118
138
  useSenderAutofocus({ autoFocus: props.autoFocus === true, editorRef })
119
139
  useSenderReferenceFocusRestore({ focusRestore, referenceActions })
@@ -123,11 +143,24 @@ export const useSenderController = (props: SenderProps) => {
123
143
  isMac,
124
144
  clearInputShortcut,
125
145
  isInlineEdit,
146
+ isThinking,
126
147
  input: composer.input,
127
148
  pendingImageCount: composer.pendingImages.length,
128
149
  pendingFileCount: composer.pendingFiles.length,
150
+ interactionOptionCount: props.interactionOptionNavigation?.optionCount ?? 0,
129
151
  onCancel: props.onCancel,
130
152
  onClear: props.onClear,
153
+ onInteractionOptionMove: props.interactionOptionNavigation?.onMove,
154
+ onInteractionOptionSubmit: props.interactionOptionNavigation?.onSubmit,
155
+ onInterrupt: props.onInterrupt,
156
+ onInterruptHint: () => {
157
+ void message.open({
158
+ type: 'info',
159
+ content: t('chat.queue.stopShortcutConfirm'),
160
+ duration: 1.6,
161
+ key: 'chat-stop-shortcut-confirm'
162
+ })
163
+ },
131
164
  onResetComposer: resetComposer,
132
165
  showReferenceActions: referenceActions.showReferenceActions,
133
166
  onCloseReferenceActions: () => referenceActions.closeReferenceActions({ restoreFocus: true }),
@@ -160,10 +193,16 @@ export const useSenderController = (props: SenderProps) => {
160
193
  isInlineEdit,
161
194
  isMac,
162
195
  isThinking,
196
+ sendBlocked: isPermissionInteraction,
197
+ sendBlockedTooltip,
198
+ showConfirmInteractionAction,
199
+ confirmInteractionLabel: showConfirmInteractionAction ? t('chat.permissionConfirmOption') : undefined,
200
+ onConfirmInteractionOption: showConfirmInteractionAction ? props.interactionOptionNavigation?.onSubmit : undefined,
163
201
  message,
164
202
  props,
165
203
  refs: { fileInputRef, modelSelectRef, effortSelectRef },
166
204
  referenceActions,
205
+ queuedMessageShortcuts,
167
206
  resolvedSendShortcut,
168
207
  selectOverlays,
169
208
  supportsEffort,
@@ -186,6 +225,12 @@ export const useSenderController = (props: SenderProps) => {
186
225
  permissionContext,
187
226
  editorRef,
188
227
  placeholder: props.placeholder ?? props.interactionRequest?.payload.question ?? t('chat.inputPlaceholder'),
228
+ secondarySendShortcut: isThinking && !isPermissionInteraction ? queuedMessageShortcuts.queueNext : undefined,
229
+ onSecondarySendShortcut: isThinking && !isPermissionInteraction
230
+ ? () => {
231
+ void handleSend('next')
232
+ }
233
+ : undefined,
189
234
  toolbar
190
235
  })
191
236
  }
@@ -1,4 +1,5 @@
1
1
  import type { RefObject } from 'react'
2
+ import { useEffect, useRef } from 'react'
2
3
 
3
4
  import type { SenderEditorHandle } from '#~/components/chat/sender/@types/sender-editor'
4
5
  import { loadChatHistory } from '#~/components/chat/sender/@utils/sender-utils'
@@ -9,11 +10,17 @@ export const useSenderKeydown = ({
9
10
  isMac,
10
11
  clearInputShortcut,
11
12
  isInlineEdit,
13
+ isThinking,
12
14
  input,
13
15
  pendingImageCount,
14
16
  pendingFileCount,
17
+ interactionOptionCount,
15
18
  onCancel,
16
19
  onClear,
20
+ onInteractionOptionMove,
21
+ onInteractionOptionSubmit,
22
+ onInterrupt,
23
+ onInterruptHint,
17
24
  onResetComposer,
18
25
  showReferenceActions,
19
26
  onCloseReferenceActions,
@@ -30,11 +37,17 @@ export const useSenderKeydown = ({
30
37
  isMac: boolean
31
38
  clearInputShortcut?: string
32
39
  isInlineEdit: boolean
40
+ isThinking: boolean
33
41
  input: string
34
42
  pendingImageCount: number
35
43
  pendingFileCount: number
44
+ interactionOptionCount: number
36
45
  onCancel?: () => void
37
46
  onClear?: () => void
47
+ onInteractionOptionMove?: (delta: number) => void
48
+ onInteractionOptionSubmit?: () => void
49
+ onInterrupt: () => void
50
+ onInterruptHint: () => void
38
51
  onResetComposer: () => void
39
52
  showReferenceActions: boolean
40
53
  onCloseReferenceActions: () => void
@@ -47,6 +60,14 @@ export const useSenderKeydown = ({
47
60
  onHistoryNavigate: (direction: 'up' | 'down') => void
48
61
  onInputClear: () => void
49
62
  }) => {
63
+ const interruptConfirmationExpiresAtRef = useRef<number | null>(null)
64
+
65
+ useEffect(() => {
66
+ if (input !== '' || pendingImageCount > 0 || pendingFileCount > 0 || !isThinking) {
67
+ interruptConfirmationExpiresAtRef.current = null
68
+ }
69
+ }, [input, isThinking, pendingFileCount, pendingImageCount])
70
+
50
71
  return (event: KeyboardEvent) => {
51
72
  if (showReferenceActions && event.key === 'Escape') {
52
73
  event.preventDefault()
@@ -71,6 +92,33 @@ export const useSenderKeydown = ({
71
92
  onInputClear()
72
93
  return
73
94
  }
95
+ const canNavigateInteractionOptions = !isInlineEdit &&
96
+ interactionOptionCount > 0 &&
97
+ input.trim() === '' &&
98
+ pendingImageCount === 0 &&
99
+ pendingFileCount === 0
100
+ if (
101
+ canNavigateInteractionOptions &&
102
+ onInteractionOptionMove != null &&
103
+ (event.key === 'ArrowUp' || event.key === 'ArrowDown')
104
+ ) {
105
+ event.preventDefault()
106
+ onInteractionOptionMove(event.key === 'ArrowUp' ? -1 : 1)
107
+ return
108
+ }
109
+ if (
110
+ canNavigateInteractionOptions &&
111
+ onInteractionOptionSubmit != null &&
112
+ event.key === 'Enter' &&
113
+ !event.metaKey &&
114
+ !event.ctrlKey &&
115
+ !event.altKey &&
116
+ !event.shiftKey
117
+ ) {
118
+ event.preventDefault()
119
+ onInteractionOptionSubmit()
120
+ return
121
+ }
74
122
  if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
75
123
  const selection = editorRef.current?.getSelection()
76
124
  const history = loadChatHistory()
@@ -96,7 +144,23 @@ export const useSenderKeydown = ({
96
144
  }
97
145
  if (input !== '') {
98
146
  event.preventDefault()
147
+ interruptConfirmationExpiresAtRef.current = null
99
148
  onInputClear()
149
+ return
150
+ }
151
+ if (isThinking && pendingImageCount === 0 && pendingFileCount === 0) {
152
+ event.preventDefault()
153
+ const now = Date.now()
154
+ const expiresAt = interruptConfirmationExpiresAtRef.current
155
+
156
+ if (expiresAt != null && expiresAt > now) {
157
+ interruptConfirmationExpiresAtRef.current = null
158
+ onInterrupt()
159
+ return
160
+ }
161
+
162
+ interruptConfirmationExpiresAtRef.current = now + 1800
163
+ onInterruptHint()
100
164
  }
101
165
  return
102
166
  }
@@ -8,6 +8,19 @@ import type { PermissionMode } from '#~/hooks/chat/use-chat-permission-mode'
8
8
  import { useComposerControlShortcuts } from '#~/hooks/chat/use-composer-control-shortcuts'
9
9
  import { resolveSendShortcut } from '#~/utils/shortcutUtils'
10
10
 
11
+ const resolveQueuedMessageShortcuts = (sendShortcut: string, isMac: boolean) => {
12
+ const primary = sendShortcut
13
+ const candidates = isMac
14
+ ? ['cmd+shift+enter', 'cmd+alt+enter', 'cmd+ctrl+enter']
15
+ : ['mod+enter', 'alt+enter', 'ctrl+shift+enter']
16
+ const next = candidates.find(shortcut => shortcut !== primary) ?? candidates[0]
17
+
18
+ return {
19
+ queueSteer: primary,
20
+ queueNext: next
21
+ }
22
+ }
23
+
11
24
  export const useSenderShortcuts = ({
12
25
  enabled,
13
26
  isInlineEdit,
@@ -38,6 +51,7 @@ export const useSenderShortcuts = ({
38
51
  const { data: configRes } = useSWR<ConfigResponse>('/api/config')
39
52
  const mergedShortcuts = configRes?.sources?.merged?.shortcuts
40
53
  const resolvedSendShortcut = resolveSendShortcut(mergedShortcuts?.sendMessage, isMac)
54
+ const queuedMessageShortcuts = resolveQueuedMessageShortcuts(resolvedSendShortcut, isMac)
41
55
 
42
56
  const composerControlShortcuts = useComposerControlShortcuts({
43
57
  enabled,
@@ -73,6 +87,7 @@ export const useSenderShortcuts = ({
73
87
  return {
74
88
  clearInputShortcut: mergedShortcuts?.clearInput,
75
89
  composerControlShortcuts,
76
- resolvedSendShortcut
90
+ resolvedSendShortcut,
91
+ queuedMessageShortcuts
77
92
  }
78
93
  }
@@ -1,3 +1,4 @@
1
+ import type { SessionQueuedMessageMode } from '@vibe-forge/core'
1
2
  import type { TFunction } from 'i18next'
2
3
 
3
4
  import type { MessageInstance } from 'antd/es/message/interface'
@@ -11,6 +12,7 @@ export const useSenderSubmit = ({
11
12
  pendingImages,
12
13
  pendingFiles,
13
14
  isBusy,
15
+ allowWhileBusy,
14
16
  isInlineEdit,
15
17
  modelUnavailable,
16
18
  interactionRequest,
@@ -25,20 +27,26 @@ export const useSenderSubmit = ({
25
27
  pendingImages: Parameters<typeof buildMessageContent>[1]
26
28
  pendingFiles: Parameters<typeof buildMessageContent>[2]
27
29
  isBusy: boolean
30
+ allowWhileBusy: boolean
28
31
  isInlineEdit: boolean
29
32
  modelUnavailable?: boolean
30
33
  interactionRequest?: { id: string } | null
31
34
  onInteractionResponse?: (id: string, data: string | string[]) => void
32
- onSend: (text: string) => SenderSubmitResult | Promise<SenderSubmitResult>
33
- onSendContent: (content: ReturnType<typeof buildMessageContent>) => SenderSubmitResult | Promise<SenderSubmitResult>
35
+ onSend: (text: string, mode?: SessionQueuedMessageMode) => SenderSubmitResult | Promise<SenderSubmitResult>
36
+ onSendContent: (
37
+ content: ReturnType<typeof buildMessageContent>,
38
+ mode?: SessionQueuedMessageMode
39
+ ) => SenderSubmitResult | Promise<SenderSubmitResult>
34
40
  message: MessageInstance
35
41
  t: TFunction
36
42
  resetComposer: () => void
37
43
  }) => {
38
- return async () => {
44
+ return async (mode?: SessionQueuedMessageMode) => {
39
45
  const input = getInput()
40
46
 
41
- if (isBusy || (input.trim() === '' && pendingImages.length === 0 && pendingFiles.length === 0)) {
47
+ if (
48
+ ((isBusy && !allowWhileBusy) || (input.trim() === '' && pendingImages.length === 0 && pendingFiles.length === 0))
49
+ ) {
42
50
  return
43
51
  }
44
52
  if (!isInlineEdit && modelUnavailable) {
@@ -61,14 +69,14 @@ export const useSenderSubmit = ({
61
69
  if (pendingImages.length > 0 || pendingFiles.length > 0) {
62
70
  const content = buildMessageContent(input, pendingImages, pendingFiles)
63
71
  if (isInlineEdit) {
64
- didSubmit = (await onSendContent(content)) !== false
72
+ didSubmit = (await onSendContent(content, mode)) !== false
65
73
  } else {
66
- void onSendContent(content)
74
+ void onSendContent(content, mode)
67
75
  }
68
76
  } else if (isInlineEdit) {
69
- didSubmit = (await onSend(input)) !== false
77
+ didSubmit = (await onSend(input, mode)) !== false
70
78
  } else {
71
- void onSend(input)
79
+ void onSend(input, mode)
72
80
  }
73
81
 
74
82
  if (!didSubmit) {
@@ -1,6 +1,11 @@
1
1
  import type { ReactNode } from 'react'
2
2
 
3
- import type { AskUserQuestionParams, ChatMessageContent, SessionStatus } from '@vibe-forge/core'
3
+ import type {
4
+ AskUserQuestionParams,
5
+ ChatMessageContent,
6
+ SessionQueuedMessageMode,
7
+ SessionStatus
8
+ } from '@vibe-forge/core'
4
9
  import type { SessionInfo } from '@vibe-forge/types'
5
10
 
6
11
  import type { ChatEffort } from '#~/hooks/chat/use-chat-effort'
@@ -10,16 +15,26 @@ import type { PermissionMode } from '#~/hooks/chat/use-chat-permission-mode'
10
15
  import type { SenderInitialContent, SenderSubmitResult, SenderVariant } from './sender-types'
11
16
 
12
17
  export interface SenderProps {
13
- onSend: (text: string) => SenderSubmitResult | Promise<SenderSubmitResult>
14
- onSendContent: (content: ChatMessageContent[]) => SenderSubmitResult | Promise<SenderSubmitResult>
18
+ onSend: (text: string, mode?: SessionQueuedMessageMode) => SenderSubmitResult | Promise<SenderSubmitResult>
19
+ onSendContent: (
20
+ content: ChatMessageContent[],
21
+ mode?: SessionQueuedMessageMode
22
+ ) => SenderSubmitResult | Promise<SenderSubmitResult>
15
23
  variant?: SenderVariant
16
24
  adapterLocked?: boolean
17
25
  sessionStatus?: SessionStatus
18
26
  onInterrupt: () => void
19
27
  onClear?: () => void
28
+ sessionId?: string
20
29
  sessionInfo?: SessionInfo | null
21
30
  interactionRequest?: { id: string; payload: AskUserQuestionParams } | null
22
31
  onInteractionResponse?: (id: string, data: string | string[]) => void
32
+ interactionOptionNavigation?: {
33
+ optionCount: number
34
+ activeIndex: number
35
+ onMove: (delta: number) => void
36
+ onSubmit: () => void
37
+ }
23
38
  placeholder?: string
24
39
  initialContent?: SenderInitialContent
25
40
  onCancel?: () => void
@@ -44,4 +59,6 @@ export interface SenderProps {
44
59
  adapterOptions?: Array<{ value: string; label: ReactNode }>
45
60
  onAdapterChange?: (adapter: string) => void
46
61
  modelUnavailable?: boolean
62
+ queueMode?: SessionQueuedMessageMode
63
+ onQueueModeChange?: (mode: SessionQueuedMessageMode) => void
47
64
  }
@@ -1,5 +1,6 @@
1
1
  import type { ChangeEvent, KeyboardEvent, ReactNode, RefObject } from 'react'
2
2
 
3
+ import type { SessionQueuedMessageMode } from '@vibe-forge/core'
3
4
  import type { RefSelectProps } from 'antd'
4
5
 
5
6
  import type { ChatEffort } from '#~/hooks/chat/use-chat-effort'
@@ -11,12 +12,17 @@ export interface SenderToolbarShortcuts {
11
12
  switchModel: string
12
13
  switchEffort: string
13
14
  switchPermissionMode: string
15
+ queueSteer: string
16
+ queueNext: string
14
17
  }
15
18
 
16
19
  export interface SenderToolbarState {
17
20
  isInlineEdit: boolean
18
21
  isThinking: boolean
19
22
  modelUnavailable: boolean
23
+ sendBlocked: boolean
24
+ sendBlockedTooltip?: string
25
+ showConfirmInteractionAction: boolean
20
26
  adapterLocked: boolean
21
27
  submitLoading: boolean
22
28
  supportsEffort: boolean
@@ -34,6 +40,8 @@ export interface SenderToolbarState {
34
40
  resolvedSendShortcut: string
35
41
  hasComposerContent: boolean
36
42
  hasSendText: boolean
43
+ queueMode: SessionQueuedMessageMode
44
+ showQueueModeControl: boolean
37
45
  }
38
46
 
39
47
  export interface SenderToolbarData {
@@ -47,6 +55,7 @@ export interface SenderToolbarData {
47
55
  adapterOptions?: Array<{ value: string; label: ReactNode }>
48
56
  composerControlShortcuts: SenderToolbarShortcuts
49
57
  submitLabel?: string
58
+ confirmInteractionLabel?: string
50
59
  }
51
60
 
52
61
  export interface SenderToolbarRefs {
@@ -77,7 +86,9 @@ export interface SenderToolbarHandlers {
77
86
  onToggleRecommendedModel?: (option: ModelSelectOption) => void | Promise<void>
78
87
  onEffortChange?: (effort: ChatEffort) => void
79
88
  onAdapterChange?: (adapter: string) => void
80
- onSend: () => void
89
+ onSend: (mode?: SessionQueuedMessageMode) => void
81
90
  onInterrupt: () => void
82
91
  onCancel?: () => void
92
+ onConfirmInteractionOption?: () => void
93
+ onQueueModeChange?: (mode: SessionQueuedMessageMode) => void
83
94
  }
@@ -1,4 +1,6 @@
1
1
  .chat-input-wrapper {
2
+ width: 100%;
3
+ margin: 0;
2
4
  border-top: none;
3
5
  position: relative;
4
6
  background-color: var(--bg-color);
@@ -6,6 +8,7 @@
6
8
 
7
9
  .chat-input-wrapper--inline-edit {
8
10
  width: 100%;
11
+ margin: 0;
9
12
  }
10
13
 
11
14
  .chat-input-container {
@@ -21,7 +24,7 @@
21
24
  &:focus-within {
22
25
  border-color: #3b82f6;
23
26
  background-color: var(--input-focus-bg, #f9fafb);
24
- box-shadow: 0 0 0 2px rgba(59, 130, 246, .1);
27
+ box-shadow: none;
25
28
  }
26
29
 
27
30
  .chat-input-monaco {