@vibe-forge/client 0.11.2 → 0.11.3

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 (111) hide show
  1. package/dist/assets/{arc-De_WjPJ3.js → arc-h39NrT24.js} +1 -1
  2. package/dist/assets/{blockDiagram-c4efeb88-C4aR2zTE.js → blockDiagram-c4efeb88-CaDg46I6.js} +1 -1
  3. package/dist/assets/{c4Diagram-c83219d4-BZH3rq_m.js → c4Diagram-c83219d4-CDqjcF9U.js} +1 -1
  4. package/dist/assets/channel-CBULQbJz.js +1 -0
  5. package/dist/assets/{classDiagram-beda092f-BzJgBrIK.js → classDiagram-beda092f-BDnZm8nO.js} +1 -1
  6. package/dist/assets/{classDiagram-v2-2358418a-5ZtXcnT3.js → classDiagram-v2-2358418a-BUi85KJW.js} +1 -1
  7. package/dist/assets/clone-dkS7LczW.js +1 -0
  8. package/dist/assets/{createText-1719965b-DUVvEtmR.js → createText-1719965b-Ca5dEwfo.js} +1 -1
  9. package/dist/assets/{cssMode-GoTNjuXX.js → cssMode-Ysz7NfYo.js} +1 -1
  10. package/dist/assets/{edges-96097737-Dd7m4Cvs.js → edges-96097737-CdSKqxZt.js} +1 -1
  11. package/dist/assets/{erDiagram-0228fc6a-DxqFlG_f.js → erDiagram-0228fc6a-B-veAUv_.js} +1 -1
  12. package/dist/assets/{flowDb-c6c81e3f-DU0C5kCI.js → flowDb-c6c81e3f-DD8Cx7_9.js} +1 -1
  13. package/dist/assets/{flowDiagram-50d868cf-Di1uDa_X.js → flowDiagram-50d868cf-9f-_x1ET.js} +1 -1
  14. package/dist/assets/flowDiagram-v2-4f6560a1-1miffU4x.js +1 -0
  15. package/dist/assets/{flowchart-elk-definition-6af322e1-CwG8aty5.js → flowchart-elk-definition-6af322e1-5RhpQM4M.js} +1 -1
  16. package/dist/assets/{freemarker2-j39cqTlI.js → freemarker2-SgMdIXw4.js} +1 -1
  17. package/dist/assets/{ganttDiagram-a2739b55-baO_lzL-.js → ganttDiagram-a2739b55-DnxNghZA.js} +1 -1
  18. package/dist/assets/{gitGraphDiagram-82fe8481-COoHjYMf.js → gitGraphDiagram-82fe8481-CBvS3Tf9.js} +1 -1
  19. package/dist/assets/{graph-KxESr4M5.js → graph-CkHF299-.js} +1 -1
  20. package/dist/assets/{handlebars-BgjdZO8G.js → handlebars-C57IyLUe.js} +1 -1
  21. package/dist/assets/{html-Ba7tYObe.js → html-YsDy5wvW.js} +1 -1
  22. package/dist/assets/{htmlMode-Bztvbig1.js → htmlMode-7o_VDODD.js} +1 -1
  23. package/dist/assets/{index-5325376f-BMTAx2mL.js → index-5325376f-BzOVQPu-.js} +1 -1
  24. package/dist/assets/{index-Pm_kLJvG.js → index-BHFpctk6.js} +320 -319
  25. package/dist/assets/index-D_XqxIvp.css +32 -0
  26. package/dist/assets/{infoDiagram-8eee0895-CC74qbHY.js → infoDiagram-8eee0895-DJ-UI1h4.js} +1 -1
  27. package/dist/assets/{javascript-C1e1cllX.js → javascript-BHQ9NEZr.js} +1 -1
  28. package/dist/assets/{journeyDiagram-c64418c1-C4MyOdE6.js → journeyDiagram-c64418c1-DwfykcG9.js} +1 -1
  29. package/dist/assets/{jsonMode-BC98AlvF.js → jsonMode-3QjftkMM.js} +1 -1
  30. package/dist/assets/{layout-CxAyTlr7.js → layout-CbViRb_b.js} +1 -1
  31. package/dist/assets/{line-DhaUfI71.js → line-DBdBvv9D.js} +1 -1
  32. package/dist/assets/{linear-MYukzldK.js → linear-BDAfhcjn.js} +1 -1
  33. package/dist/assets/{liquid-DahfJEYl.js → liquid-B0cPPzIR.js} +1 -1
  34. package/dist/assets/{lspLanguageFeatures-BWDJcswW.js → lspLanguageFeatures-IOxbobOz.js} +1 -1
  35. package/dist/assets/{mdx-BELlF_FD.js → mdx-Dma_RA8P.js} +1 -1
  36. package/dist/assets/{mermaid.core-BrQnSGSY.js → mermaid.core-Cvn8Go4x.js} +4 -4
  37. package/dist/assets/{mindmap-definition-8da855dc-B0FoxTiy.js → mindmap-definition-8da855dc-DEnYq0Lc.js} +1 -1
  38. package/dist/assets/{pieDiagram-a8764435-Ddr2cjSL.js → pieDiagram-a8764435-ZC4j8sHU.js} +1 -1
  39. package/dist/assets/{python--C9if_AD.js → python-Be0WX4q5.js} +1 -1
  40. package/dist/assets/{quadrantDiagram-1e28029f-BlEs7Mrl.js → quadrantDiagram-1e28029f-DUaqHlIB.js} +1 -1
  41. package/dist/assets/{razor-B9U9JxKn.js → razor-Tjhny-uT.js} +1 -1
  42. package/dist/assets/{requirementDiagram-08caed73-kEFOAu2v.js → requirementDiagram-08caed73-DjSal3es.js} +1 -1
  43. package/dist/assets/{sankeyDiagram-a04cb91d-BBghez8I.js → sankeyDiagram-a04cb91d-BMDXMrMz.js} +1 -1
  44. package/dist/assets/{sequenceDiagram-c5b8d532-CJqgzdUE.js → sequenceDiagram-c5b8d532-CQl9YUlH.js} +1 -1
  45. package/dist/assets/{stateDiagram-1ecb1508-BER4XEI6.js → stateDiagram-1ecb1508-DG7mU9jD.js} +1 -1
  46. package/dist/assets/{stateDiagram-v2-c2b004d7-EBV2vSks.js → stateDiagram-v2-c2b004d7-DTbR_azy.js} +1 -1
  47. package/dist/assets/{styles-b4e223ce-k0eswZsE.js → styles-b4e223ce-C9aS3zb8.js} +1 -1
  48. package/dist/assets/{styles-ca3715f6-Ckr7GA-0.js → styles-ca3715f6-Bh3keVTZ.js} +1 -1
  49. package/dist/assets/{styles-d45a18b0-C1bpSwV3.js → styles-d45a18b0-BDcLLa65.js} +1 -1
  50. package/dist/assets/{svgDrawCommon-b86b1483-CDtKpGvy.js → svgDrawCommon-b86b1483-B9H5ZS_9.js} +1 -1
  51. package/dist/assets/{timeline-definition-faaaa080-BeGR-vua.js → timeline-definition-faaaa080-DCMYCBhK.js} +1 -1
  52. package/dist/assets/{tsMode-D_gJXIy3.js → tsMode-DVqLsn98.js} +1 -1
  53. package/dist/assets/{typescript-BoKcNXkN.js → typescript-wMVyXw7G.js} +1 -1
  54. package/dist/assets/{xml-DZvURlJ-.js → xml-w0gzmn0c.js} +1 -1
  55. package/dist/assets/{xychartDiagram-f5964ef8-DxfeLuYV.js → xychartDiagram-f5964ef8-CdxyD3K5.js} +1 -1
  56. package/dist/assets/{yaml-CTC8PAGY.js → yaml-C29TL1ed.js} +1 -1
  57. package/dist/index.html +2 -2
  58. package/package.json +4 -4
  59. package/src/api/sessions.ts +70 -1
  60. package/src/api/types.ts +2 -1
  61. package/src/api.ts +5 -0
  62. package/src/components/chat/AGENTS.md +14 -2
  63. package/src/components/chat/ChatComposerCard.scss +73 -0
  64. package/src/components/chat/ChatComposerCard.tsx +59 -0
  65. package/src/components/chat/ChatHeader.tsx +3 -1
  66. package/src/components/chat/ChatHistoryView.tsx +215 -49
  67. package/src/components/chat/CurrentTodoList.scss +210 -200
  68. package/src/components/chat/CurrentTodoList.tsx +116 -48
  69. package/src/components/chat/QueuedMessagesCard.scss +195 -0
  70. package/src/components/chat/QueuedMessagesCard.tsx +170 -0
  71. package/src/components/chat/sender/@components/adapter-select/AdapterSelectControl.scss +8 -0
  72. package/src/components/chat/sender/@components/sender-attachments/SenderAttachments.scss +152 -28
  73. package/src/components/chat/sender/@components/sender-attachments/SenderAttachments.tsx +95 -23
  74. package/src/components/chat/sender/@components/sender-body/SenderBody.tsx +7 -0
  75. package/src/components/chat/sender/@components/sender-interaction-panel/SenderInteractionPanel.scss +161 -45
  76. package/src/components/chat/sender/@components/sender-interaction-panel/SenderInteractionPanel.tsx +310 -71
  77. package/src/components/chat/sender/@components/sender-monaco-editor/SenderMonacoEditor.tsx +18 -0
  78. package/src/components/chat/sender/@components/sender-monaco-editor/use-sender-monaco-editor.ts +86 -9
  79. package/src/components/chat/sender/@components/sender-submit-action/SenderSubmitAction.scss +98 -1
  80. package/src/components/chat/sender/@components/sender-submit-action/SenderSubmitAction.tsx +137 -17
  81. package/src/components/chat/sender/@components/sender-toolbar/SenderToolbar.tsx +12 -6
  82. package/src/components/chat/sender/@core/build-sender-controller-result.ts +6 -0
  83. package/src/components/chat/sender/@core/build-sender-toolbar.ts +25 -2
  84. package/src/components/chat/sender/@core/create-sender-toolbar-handlers.ts +9 -2
  85. package/src/components/chat/sender/@core/interaction-request.ts +2 -2
  86. package/src/components/chat/sender/@core/sender-toolbar-bindings.ts +28 -4
  87. package/src/components/chat/sender/@hooks/use-sender-controller.ts +56 -11
  88. package/src/components/chat/sender/@hooks/use-sender-keydown.ts +64 -0
  89. package/src/components/chat/sender/@hooks/use-sender-shortcuts.ts +16 -1
  90. package/src/components/chat/sender/@hooks/use-sender-submit.ts +16 -8
  91. package/src/components/chat/sender/@types/sender-props.ts +19 -3
  92. package/src/components/chat/sender/@types/sender-toolbar-types.ts +12 -1
  93. package/src/components/chat/sender/Sender.scss +3 -0
  94. package/src/components/chat/sender/Sender.tsx +2 -12
  95. package/src/hooks/chat/session-view-cache.ts +4 -1
  96. package/src/hooks/chat/use-chat-adapter.ts +5 -1
  97. package/src/hooks/chat/use-chat-model-adapter-selection.tsx +5 -1
  98. package/src/hooks/chat/use-chat-session-actions.ts +99 -4
  99. package/src/hooks/chat/use-chat-session-messages.ts +20 -1
  100. package/src/hooks/chat/use-chat-session.ts +2 -0
  101. package/src/main.tsx +8 -0
  102. package/src/resources/locales/en.json +32 -1
  103. package/src/resources/locales/zh.json +32 -1
  104. package/src/routes/ChatRoute.scss +45 -1
  105. package/src/routes/ChatRoute.tsx +3 -0
  106. package/dist/assets/channel-BvERb8WU.js +0 -1
  107. package/dist/assets/clone-B9_0v-6Y.js +0 -1
  108. package/dist/assets/flowDiagram-v2-4f6560a1-LpS8Kb00.js +0 -1
  109. package/dist/assets/index-C1oh0w9H.css +0 -32
  110. package/src/components/chat/ThinkingStatus.scss +0 -70
  111. package/src/components/chat/ThinkingStatus.tsx +0 -13
@@ -0,0 +1,59 @@
1
+ import './ChatComposerCard.scss'
2
+
3
+ import type { ReactNode } from 'react'
4
+
5
+ export function ChatComposerCard({
6
+ className,
7
+ summary,
8
+ summaryClassName,
9
+ bodyClassName,
10
+ progress,
11
+ children,
12
+ expanded = true,
13
+ onToggle,
14
+ narrow = false
15
+ }: {
16
+ className?: string
17
+ summary: ReactNode
18
+ summaryClassName?: string
19
+ bodyClassName?: string
20
+ progress?: ReactNode
21
+ children?: ReactNode
22
+ expanded?: boolean
23
+ onToggle?: () => void
24
+ narrow?: boolean
25
+ }) {
26
+ const classes = [
27
+ 'chat-composer-card',
28
+ narrow ? 'chat-composer-card--narrow' : '',
29
+ expanded ? 'is-expanded' : 'is-collapsed',
30
+ className ?? ''
31
+ ].filter(Boolean).join(' ')
32
+ const summaryClasses = ['chat-composer-card__summary', summaryClassName ?? ''].filter(Boolean).join(' ')
33
+ const bodyClasses = ['chat-composer-card__body', bodyClassName ?? ''].filter(Boolean).join(' ')
34
+ const hasBody = children != null
35
+
36
+ return (
37
+ <section className={classes}>
38
+ {onToggle != null
39
+ ? (
40
+ <button type='button' className={summaryClasses} onClick={onToggle}>
41
+ {summary}
42
+ </button>
43
+ )
44
+ : (
45
+ <div className={summaryClasses}>
46
+ {summary}
47
+ </div>
48
+ )}
49
+ {progress}
50
+ {hasBody && (
51
+ <div className='chat-composer-card__content' aria-hidden={!expanded}>
52
+ <div className={bodyClasses}>
53
+ {children}
54
+ </div>
55
+ </div>
56
+ )}
57
+ </section>
58
+ )
59
+ }
@@ -1,6 +1,6 @@
1
1
  import './ChatHeader.scss'
2
2
 
3
- import type { Session } from '@vibe-forge/core'
3
+ import type { Session, SessionStatus } from '@vibe-forge/core'
4
4
  import type { SessionInfo } from '@vibe-forge/types'
5
5
  import { App, Button, Dropdown, Tooltip } from 'antd'
6
6
  import type { MenuProps } from 'antd'
@@ -44,6 +44,7 @@ export function ChatHeader({
44
44
  sessionInfo,
45
45
  sessionId,
46
46
  sessionTitle,
47
+ sessionStatus,
47
48
  isStarred,
48
49
  isArchived,
49
50
  tags,
@@ -57,6 +58,7 @@ export function ChatHeader({
57
58
  sessionInfo: SessionInfo | null
58
59
  sessionId?: string
59
60
  sessionTitle?: string
61
+ sessionStatus?: SessionStatus
60
62
  isStarred?: boolean
61
63
  isArchived?: boolean
62
64
  tags?: string[]
@@ -1,9 +1,17 @@
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
5
 
6
- import type { AskUserQuestionParams, ChatMessage, ChatMessageContent, Session } from '@vibe-forge/core'
6
+ import type {
7
+ AskUserQuestionParams,
8
+ ChatMessage,
9
+ ChatMessageContent,
10
+ Session,
11
+ SessionMessageQueueState,
12
+ SessionQueuedMessage,
13
+ SessionQueuedMessageMode
14
+ } from '@vibe-forge/core'
7
15
  import type { SessionInfo } from '@vibe-forge/types'
8
16
 
9
17
  import type { ChatEffort } from '#~/hooks/chat/use-chat-effort'
@@ -11,14 +19,16 @@ import type { ModelSelectMenuGroup, ModelSelectOption } from '#~/hooks/chat/use-
11
19
  import type { PermissionMode } from '#~/hooks/chat/use-chat-permission-mode'
12
20
  import { useChatScroll } from '#~/hooks/chat/use-chat-scroll'
13
21
  import { useChatSessionActions } from '#~/hooks/chat/use-chat-session-actions'
14
-
22
+ import { getLoopedIndex } from '#~/hooks/use-roving-focus-list'
15
23
  import { CurrentTodoList } from './CurrentTodoList'
16
24
  import { NewSessionGuide } from './NewSessionGuide'
25
+ import { QueuedMessagesCard } from './QueuedMessagesCard'
17
26
  import { MessageItem } from './messages/MessageItem'
18
27
  import { MessageStatusNotice } from './messages/MessageStatusNotice'
19
28
  import type { ChatHistoryStatusNotice } from './messages/build-chat-history-status-notices'
20
29
  import { buildMessageTurns } from './messages/message-turns'
21
30
  import { processMessages } from './messages/message-utils'
31
+ import { SenderInteractionPanel } from './sender/@components/sender-interaction-panel/SenderInteractionPanel'
22
32
  import { Sender } from './sender/Sender'
23
33
  import { ToolGroup } from './tools/core/ToolGroup'
24
34
 
@@ -30,6 +40,7 @@ export function ChatHistoryView({
30
40
  targetToolUseId,
31
41
  sessionInfo,
32
42
  historyStatusNotices,
43
+ queuedMessages,
33
44
  onRetryConnection,
34
45
  interactionRequest,
35
46
  onInteractionResponse,
@@ -64,6 +75,7 @@ export function ChatHistoryView({
64
75
  targetToolUseId?: string
65
76
  sessionInfo: SessionInfo | null
66
77
  historyStatusNotices: ChatHistoryStatusNotice[]
78
+ queuedMessages: SessionMessageQueueState
67
79
  onRetryConnection: () => void
68
80
  interactionRequest: { id: string; payload: AskUserQuestionParams } | null
69
81
  onInteractionResponse: (id: string, data: string | string[]) => void
@@ -102,6 +114,10 @@ export function ChatHistoryView({
102
114
  isCreating,
103
115
  send,
104
116
  sendContent,
117
+ enqueueContent,
118
+ removeQueuedContent,
119
+ moveQueuedContent,
120
+ reorderQueuedContent,
105
121
  editMessage,
106
122
  forkMessage,
107
123
  interrupt,
@@ -121,6 +137,10 @@ export function ChatHistoryView({
121
137
  const handledTargetScrollKeyRef = useRef('')
122
138
  const [editingMessageId, setEditingMessageId] = useState<string | null>(null)
123
139
  const [expandedTurnIds, setExpandedTurnIds] = useState<Set<string>>(new Set())
140
+ const [queueMode, setQueueMode] = useState<SessionQueuedMessageMode>('steer')
141
+ const [queuedDraft, setQueuedDraft] = useState<{ content: ChatMessageContent[] } | null>(null)
142
+ const [activeInteractionOptionIndex, setActiveInteractionOptionIndex] = useState(0)
143
+ const interactionOptions = interactionRequest?.payload.options ?? []
124
144
  const buildUserMessage = (content: string | ChatMessageContent[]): ChatMessage => {
125
145
  const id = globalThis.crypto?.randomUUID
126
146
  ? globalThis.crypto.randomUUID()
@@ -133,37 +153,78 @@ export function ChatHistoryView({
133
153
  }
134
154
  }
135
155
 
136
- const handleSend = async (text: string) => {
156
+ const handleSendContent = async (content: ChatMessageContent[], mode?: SessionQueuedMessageMode) => {
157
+ const resolvedMode = mode ?? queueMode
158
+
159
+ if (session?.id && session.status === 'running') {
160
+ const didQueue = await enqueueContent(resolvedMode, content)
161
+ if (didQueue && queuedDraft != null) {
162
+ setQueuedDraft(null)
163
+ setQueueMode('steer')
164
+ }
165
+ return didQueue
166
+ }
167
+
137
168
  if (!session?.id) {
138
- const optimisticMessage = buildUserMessage(text)
169
+ const optimisticMessage = buildUserMessage(content)
139
170
  setMessages((prev) => [...prev, optimisticMessage])
140
- const didSend = await send(text)
171
+ const didSend = await sendContent(content, mode)
141
172
  if (!didSend) {
142
173
  setMessages((prev) => prev.filter((message) => message.id !== optimisticMessage.id))
143
174
  }
144
- return
175
+ if (didSend && queuedDraft != null) {
176
+ setQueuedDraft(null)
177
+ setQueueMode('steer')
178
+ }
179
+ return didSend
145
180
  }
146
181
 
147
- const didSend = await send(text)
182
+ const didSend = await sendContent(content, mode)
148
183
  if (didSend) {
149
- setMessages((prev) => [...prev, buildUserMessage(text)])
184
+ setMessages((prev) => [...prev, buildUserMessage(content)])
185
+ if (queuedDraft != null) {
186
+ setQueuedDraft(null)
187
+ setQueueMode('steer')
188
+ }
150
189
  }
190
+ return didSend
151
191
  }
152
- const handleSendContent = async (content: ChatMessageContent[]) => {
192
+
193
+ const handleSend = async (text: string, mode?: SessionQueuedMessageMode) => {
194
+ const resolvedMode = mode ?? queueMode
195
+
196
+ if (session?.id && session.status === 'running') {
197
+ const didQueue = await enqueueContent(resolvedMode, [{ type: 'text', text: text.trim() }])
198
+ if (didQueue && queuedDraft != null) {
199
+ setQueuedDraft(null)
200
+ setQueueMode('steer')
201
+ }
202
+ return didQueue
203
+ }
204
+
153
205
  if (!session?.id) {
154
- const optimisticMessage = buildUserMessage(content)
206
+ const optimisticMessage = buildUserMessage(text)
155
207
  setMessages((prev) => [...prev, optimisticMessage])
156
- const didSend = await sendContent(content)
208
+ const didSend = await send(text, mode)
157
209
  if (!didSend) {
158
210
  setMessages((prev) => prev.filter((message) => message.id !== optimisticMessage.id))
159
211
  }
160
- return
212
+ if (didSend && queuedDraft != null) {
213
+ setQueuedDraft(null)
214
+ setQueueMode('steer')
215
+ }
216
+ return didSend
161
217
  }
162
218
 
163
- const didSend = await sendContent(content)
219
+ const didSend = await send(text, mode)
164
220
  if (didSend) {
165
- setMessages((prev) => [...prev, buildUserMessage(content)])
221
+ setMessages((prev) => [...prev, buildUserMessage(text)])
222
+ if (queuedDraft != null) {
223
+ setQueuedDraft(null)
224
+ setQueueMode('steer')
225
+ }
166
226
  }
227
+ return didSend
167
228
  }
168
229
  useEffect(() => {
169
230
  initialScrollDoneRef.current = false
@@ -171,7 +232,42 @@ export function ChatHistoryView({
171
232
  handledTargetScrollKeyRef.current = ''
172
233
  setEditingMessageId(null)
173
234
  setExpandedTurnIds(new Set())
235
+ setQueuedDraft(null)
236
+ setQueueMode('steer')
174
237
  }, [session?.id])
238
+ useEffect(() => {
239
+ setActiveInteractionOptionIndex(0)
240
+ }, [interactionRequest?.id])
241
+ useEffect(() => {
242
+ if (interactionOptions.length === 0) {
243
+ setActiveInteractionOptionIndex(0)
244
+ return
245
+ }
246
+
247
+ setActiveInteractionOptionIndex((current) => Math.min(current, interactionOptions.length - 1))
248
+ }, [interactionOptions.length])
249
+
250
+ const handleMoveInteractionOption = useCallback((delta: number) => {
251
+ if (interactionOptions.length === 0) {
252
+ return
253
+ }
254
+
255
+ setActiveInteractionOptionIndex((current) => getLoopedIndex(current, delta, interactionOptions.length))
256
+ }, [interactionOptions.length])
257
+
258
+ const handleSubmitActiveInteractionOption = useCallback(() => {
259
+ if (interactionRequest == null) {
260
+ return
261
+ }
262
+
263
+ const option = interactionOptions[activeInteractionOptionIndex] ?? interactionOptions[0]
264
+ if (option == null) {
265
+ return
266
+ }
267
+
268
+ onInteractionResponse(interactionRequest.id, option.value ?? option.label)
269
+ }, [activeInteractionOptionIndex, interactionOptions, interactionRequest, onInteractionResponse])
270
+
175
271
  useEffect(() => {
176
272
  if (!initialScrollDoneRef.current && isReady && location.hash === '') {
177
273
  scrollToBottom('auto')
@@ -236,6 +332,38 @@ export function ChatHistoryView({
236
332
  }
237
333
  return null
238
334
  }, [renderItems])
335
+ const handleEditQueuedMessage = async (item: SessionQueuedMessage) => {
336
+ const removed = await removeQueuedContent(item.id)
337
+ if (!removed) {
338
+ return
339
+ }
340
+ setQueuedDraft({ content: item.content })
341
+ setQueueMode('steer')
342
+ }
343
+ const handleMoveQueuedMessage = async (item: SessionQueuedMessage, targetMode: SessionQueuedMessageMode) => {
344
+ await moveQueuedContent(item.id, targetMode)
345
+ }
346
+ const isPermissionInteraction = interactionRequest?.payload.kind === 'permission'
347
+ const interactionPanel = !isInlineEditing && interactionRequest != null
348
+ ? (
349
+ <SenderInteractionPanel
350
+ interactionRequest={interactionRequest}
351
+ activeOptionIndex={activeInteractionOptionIndex}
352
+ permissionContext={interactionRequest.payload.kind === 'permission'
353
+ ? interactionRequest.payload.permissionContext
354
+ : undefined}
355
+ deniedTools={interactionRequest.payload.kind === 'permission'
356
+ ? (interactionRequest.payload.permissionContext?.deniedTools ?? [])
357
+ : []}
358
+ reasons={interactionRequest.payload.kind === 'permission'
359
+ ? (interactionRequest.payload.permissionContext?.reasons ?? [])
360
+ : []}
361
+ onActiveOptionIndexChange={setActiveInteractionOptionIndex}
362
+ onMoveActiveOption={handleMoveInteractionOption}
363
+ onInteractionResponse={onInteractionResponse}
364
+ />
365
+ )
366
+ : null
239
367
 
240
368
  useEffect(() => {
241
369
  const hash = hashAnchorId
@@ -489,41 +617,79 @@ export function ChatHistoryView({
489
617
  </div>
490
618
  )}
491
619
 
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
- />
620
+ <div className='chat-composer-stack'>
621
+ <div className='chat-composer-stack__inner'>
622
+ {isPermissionInteraction && interactionPanel}
623
+ <CurrentTodoList messages={messages} />
624
+ {!isInlineEditing && (
625
+ <QueuedMessagesCard
626
+ mode='next'
627
+ items={queuedMessages.next}
628
+ onMove={(item, targetMode) => void handleMoveQueuedMessage(item, targetMode)}
629
+ onDelete={(item) => void removeQueuedContent(item.id)}
630
+ onEdit={(item) => void handleEditQueuedMessage(item)}
631
+ onReorder={(ids) => reorderQueuedContent('next', ids)}
632
+ />
633
+ )}
634
+ {!isInlineEditing && (
635
+ <QueuedMessagesCard
636
+ mode='steer'
637
+ items={queuedMessages.steer}
638
+ onMove={(item, targetMode) => void handleMoveQueuedMessage(item, targetMode)}
639
+ onDelete={(item) => void removeQueuedContent(item.id)}
640
+ onEdit={(item) => void handleEditQueuedMessage(item)}
641
+ onReorder={(ids) => reorderQueuedContent('steer', ids)}
642
+ />
643
+ )}
644
+ {!isPermissionInteraction && interactionPanel}
645
+ {!isInlineEditing && (
646
+ <div className='sender-container'>
647
+ <Sender
648
+ onSend={handleSend}
649
+ onSendContent={handleSendContent}
650
+ adapterLocked={session?.id != null}
651
+ sessionStatus={isCreating ? 'running' : session?.status}
652
+ onInterrupt={interrupt}
653
+ onClear={clearMessages}
654
+ sessionInfo={sessionInfo}
655
+ interactionRequest={interactionRequest}
656
+ onInteractionResponse={onInteractionResponse}
657
+ interactionOptionNavigation={interactionRequest != null && interactionOptions.length > 0
658
+ ? {
659
+ optionCount: interactionOptions.length,
660
+ activeIndex: activeInteractionOptionIndex,
661
+ onMove: handleMoveInteractionOption,
662
+ onSubmit: handleSubmitActiveInteractionOption
663
+ }
664
+ : undefined}
665
+ initialContent={queuedDraft?.content}
666
+ placeholder={placeholder}
667
+ submitLabel={queuedDraft != null ? t('chat.queue.requeueMessage') : undefined}
668
+ modelMenuGroups={modelMenuGroups}
669
+ modelSearchOptions={modelSearchOptions}
670
+ recommendedModelOptions={recommendedModelOptions}
671
+ servicePreviewModelOptions={servicePreviewModelOptions}
672
+ onToggleRecommendedModel={onToggleRecommendedModel}
673
+ updatingRecommendedModelValue={updatingRecommendedModelValue}
674
+ selectedModel={selectedModel}
675
+ onModelChange={onModelChange}
676
+ effort={effort}
677
+ effortOptions={effortOptions}
678
+ onEffortChange={onEffortChange}
679
+ permissionMode={permissionMode}
680
+ permissionModeOptions={permissionModeOptions}
681
+ onPermissionModeChange={onPermissionModeChange}
682
+ selectedAdapter={selectedAdapter}
683
+ adapterOptions={adapterOptions}
684
+ onAdapterChange={onAdapterChange}
685
+ modelUnavailable={modelUnavailable}
686
+ queueMode={queueMode}
687
+ onQueueModeChange={setQueueMode}
688
+ />
689
+ </div>
690
+ )}
525
691
  </div>
526
- )}
692
+ </div>
527
693
  </>
528
694
  )
529
695
  }