@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.
- package/dist/assets/{arc-De_WjPJ3.js → arc-h39NrT24.js} +1 -1
- package/dist/assets/{blockDiagram-c4efeb88-C4aR2zTE.js → blockDiagram-c4efeb88-CaDg46I6.js} +1 -1
- package/dist/assets/{c4Diagram-c83219d4-BZH3rq_m.js → c4Diagram-c83219d4-CDqjcF9U.js} +1 -1
- package/dist/assets/channel-CBULQbJz.js +1 -0
- package/dist/assets/{classDiagram-beda092f-BzJgBrIK.js → classDiagram-beda092f-BDnZm8nO.js} +1 -1
- package/dist/assets/{classDiagram-v2-2358418a-5ZtXcnT3.js → classDiagram-v2-2358418a-BUi85KJW.js} +1 -1
- package/dist/assets/clone-dkS7LczW.js +1 -0
- package/dist/assets/{createText-1719965b-DUVvEtmR.js → createText-1719965b-Ca5dEwfo.js} +1 -1
- package/dist/assets/{cssMode-GoTNjuXX.js → cssMode-Ysz7NfYo.js} +1 -1
- package/dist/assets/{edges-96097737-Dd7m4Cvs.js → edges-96097737-CdSKqxZt.js} +1 -1
- package/dist/assets/{erDiagram-0228fc6a-DxqFlG_f.js → erDiagram-0228fc6a-B-veAUv_.js} +1 -1
- package/dist/assets/{flowDb-c6c81e3f-DU0C5kCI.js → flowDb-c6c81e3f-DD8Cx7_9.js} +1 -1
- package/dist/assets/{flowDiagram-50d868cf-Di1uDa_X.js → flowDiagram-50d868cf-9f-_x1ET.js} +1 -1
- package/dist/assets/flowDiagram-v2-4f6560a1-1miffU4x.js +1 -0
- package/dist/assets/{flowchart-elk-definition-6af322e1-CwG8aty5.js → flowchart-elk-definition-6af322e1-5RhpQM4M.js} +1 -1
- package/dist/assets/{freemarker2-j39cqTlI.js → freemarker2-SgMdIXw4.js} +1 -1
- package/dist/assets/{ganttDiagram-a2739b55-baO_lzL-.js → ganttDiagram-a2739b55-DnxNghZA.js} +1 -1
- package/dist/assets/{gitGraphDiagram-82fe8481-COoHjYMf.js → gitGraphDiagram-82fe8481-CBvS3Tf9.js} +1 -1
- package/dist/assets/{graph-KxESr4M5.js → graph-CkHF299-.js} +1 -1
- package/dist/assets/{handlebars-BgjdZO8G.js → handlebars-C57IyLUe.js} +1 -1
- package/dist/assets/{html-Ba7tYObe.js → html-YsDy5wvW.js} +1 -1
- package/dist/assets/{htmlMode-Bztvbig1.js → htmlMode-7o_VDODD.js} +1 -1
- package/dist/assets/{index-5325376f-BMTAx2mL.js → index-5325376f-BzOVQPu-.js} +1 -1
- package/dist/assets/{index-Pm_kLJvG.js → index-BHFpctk6.js} +320 -319
- package/dist/assets/index-D_XqxIvp.css +32 -0
- package/dist/assets/{infoDiagram-8eee0895-CC74qbHY.js → infoDiagram-8eee0895-DJ-UI1h4.js} +1 -1
- package/dist/assets/{javascript-C1e1cllX.js → javascript-BHQ9NEZr.js} +1 -1
- package/dist/assets/{journeyDiagram-c64418c1-C4MyOdE6.js → journeyDiagram-c64418c1-DwfykcG9.js} +1 -1
- package/dist/assets/{jsonMode-BC98AlvF.js → jsonMode-3QjftkMM.js} +1 -1
- package/dist/assets/{layout-CxAyTlr7.js → layout-CbViRb_b.js} +1 -1
- package/dist/assets/{line-DhaUfI71.js → line-DBdBvv9D.js} +1 -1
- package/dist/assets/{linear-MYukzldK.js → linear-BDAfhcjn.js} +1 -1
- package/dist/assets/{liquid-DahfJEYl.js → liquid-B0cPPzIR.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-BWDJcswW.js → lspLanguageFeatures-IOxbobOz.js} +1 -1
- package/dist/assets/{mdx-BELlF_FD.js → mdx-Dma_RA8P.js} +1 -1
- package/dist/assets/{mermaid.core-BrQnSGSY.js → mermaid.core-Cvn8Go4x.js} +4 -4
- package/dist/assets/{mindmap-definition-8da855dc-B0FoxTiy.js → mindmap-definition-8da855dc-DEnYq0Lc.js} +1 -1
- package/dist/assets/{pieDiagram-a8764435-Ddr2cjSL.js → pieDiagram-a8764435-ZC4j8sHU.js} +1 -1
- package/dist/assets/{python--C9if_AD.js → python-Be0WX4q5.js} +1 -1
- package/dist/assets/{quadrantDiagram-1e28029f-BlEs7Mrl.js → quadrantDiagram-1e28029f-DUaqHlIB.js} +1 -1
- package/dist/assets/{razor-B9U9JxKn.js → razor-Tjhny-uT.js} +1 -1
- package/dist/assets/{requirementDiagram-08caed73-kEFOAu2v.js → requirementDiagram-08caed73-DjSal3es.js} +1 -1
- package/dist/assets/{sankeyDiagram-a04cb91d-BBghez8I.js → sankeyDiagram-a04cb91d-BMDXMrMz.js} +1 -1
- package/dist/assets/{sequenceDiagram-c5b8d532-CJqgzdUE.js → sequenceDiagram-c5b8d532-CQl9YUlH.js} +1 -1
- package/dist/assets/{stateDiagram-1ecb1508-BER4XEI6.js → stateDiagram-1ecb1508-DG7mU9jD.js} +1 -1
- package/dist/assets/{stateDiagram-v2-c2b004d7-EBV2vSks.js → stateDiagram-v2-c2b004d7-DTbR_azy.js} +1 -1
- package/dist/assets/{styles-b4e223ce-k0eswZsE.js → styles-b4e223ce-C9aS3zb8.js} +1 -1
- package/dist/assets/{styles-ca3715f6-Ckr7GA-0.js → styles-ca3715f6-Bh3keVTZ.js} +1 -1
- package/dist/assets/{styles-d45a18b0-C1bpSwV3.js → styles-d45a18b0-BDcLLa65.js} +1 -1
- package/dist/assets/{svgDrawCommon-b86b1483-CDtKpGvy.js → svgDrawCommon-b86b1483-B9H5ZS_9.js} +1 -1
- package/dist/assets/{timeline-definition-faaaa080-BeGR-vua.js → timeline-definition-faaaa080-DCMYCBhK.js} +1 -1
- package/dist/assets/{tsMode-D_gJXIy3.js → tsMode-DVqLsn98.js} +1 -1
- package/dist/assets/{typescript-BoKcNXkN.js → typescript-wMVyXw7G.js} +1 -1
- package/dist/assets/{xml-DZvURlJ-.js → xml-w0gzmn0c.js} +1 -1
- package/dist/assets/{xychartDiagram-f5964ef8-DxfeLuYV.js → xychartDiagram-f5964ef8-CdxyD3K5.js} +1 -1
- package/dist/assets/{yaml-CTC8PAGY.js → yaml-C29TL1ed.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +4 -4
- package/src/api/sessions.ts +70 -1
- package/src/api/types.ts +2 -1
- package/src/api.ts +5 -0
- package/src/components/chat/AGENTS.md +14 -2
- package/src/components/chat/ChatComposerCard.scss +73 -0
- package/src/components/chat/ChatComposerCard.tsx +59 -0
- package/src/components/chat/ChatHeader.tsx +3 -1
- package/src/components/chat/ChatHistoryView.tsx +215 -49
- package/src/components/chat/CurrentTodoList.scss +210 -200
- package/src/components/chat/CurrentTodoList.tsx +116 -48
- package/src/components/chat/QueuedMessagesCard.scss +195 -0
- package/src/components/chat/QueuedMessagesCard.tsx +170 -0
- package/src/components/chat/sender/@components/adapter-select/AdapterSelectControl.scss +8 -0
- package/src/components/chat/sender/@components/sender-attachments/SenderAttachments.scss +152 -28
- package/src/components/chat/sender/@components/sender-attachments/SenderAttachments.tsx +95 -23
- package/src/components/chat/sender/@components/sender-body/SenderBody.tsx +7 -0
- package/src/components/chat/sender/@components/sender-interaction-panel/SenderInteractionPanel.scss +161 -45
- package/src/components/chat/sender/@components/sender-interaction-panel/SenderInteractionPanel.tsx +310 -71
- package/src/components/chat/sender/@components/sender-monaco-editor/SenderMonacoEditor.tsx +18 -0
- package/src/components/chat/sender/@components/sender-monaco-editor/use-sender-monaco-editor.ts +86 -9
- package/src/components/chat/sender/@components/sender-submit-action/SenderSubmitAction.scss +98 -1
- package/src/components/chat/sender/@components/sender-submit-action/SenderSubmitAction.tsx +137 -17
- package/src/components/chat/sender/@components/sender-toolbar/SenderToolbar.tsx +12 -6
- package/src/components/chat/sender/@core/build-sender-controller-result.ts +6 -0
- package/src/components/chat/sender/@core/build-sender-toolbar.ts +25 -2
- package/src/components/chat/sender/@core/create-sender-toolbar-handlers.ts +9 -2
- package/src/components/chat/sender/@core/interaction-request.ts +2 -2
- package/src/components/chat/sender/@core/sender-toolbar-bindings.ts +28 -4
- package/src/components/chat/sender/@hooks/use-sender-controller.ts +56 -11
- package/src/components/chat/sender/@hooks/use-sender-keydown.ts +64 -0
- package/src/components/chat/sender/@hooks/use-sender-shortcuts.ts +16 -1
- package/src/components/chat/sender/@hooks/use-sender-submit.ts +16 -8
- package/src/components/chat/sender/@types/sender-props.ts +19 -3
- package/src/components/chat/sender/@types/sender-toolbar-types.ts +12 -1
- package/src/components/chat/sender/Sender.scss +3 -0
- package/src/components/chat/sender/Sender.tsx +2 -12
- package/src/hooks/chat/session-view-cache.ts +4 -1
- package/src/hooks/chat/use-chat-adapter.ts +5 -1
- package/src/hooks/chat/use-chat-model-adapter-selection.tsx +5 -1
- package/src/hooks/chat/use-chat-session-actions.ts +99 -4
- package/src/hooks/chat/use-chat-session-messages.ts +20 -1
- package/src/hooks/chat/use-chat-session.ts +2 -0
- package/src/main.tsx +8 -0
- package/src/resources/locales/en.json +32 -1
- package/src/resources/locales/zh.json +32 -1
- package/src/routes/ChatRoute.scss +45 -1
- package/src/routes/ChatRoute.tsx +3 -0
- package/dist/assets/channel-BvERb8WU.js +0 -1
- package/dist/assets/clone-B9_0v-6Y.js +0 -1
- package/dist/assets/flowDiagram-v2-4f6560a1-LpS8Kb00.js +0 -1
- package/dist/assets/index-C1oh0w9H.css +0 -32
- package/src/components/chat/ThinkingStatus.scss +0 -70
- package/src/components/chat/ThinkingStatus.tsx +0 -13
|
@@ -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
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
|
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: (
|
|
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 (
|
|
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 {
|
|
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,8 +15,11 @@ 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: (
|
|
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
|
|
@@ -20,6 +28,12 @@ export interface SenderProps {
|
|
|
20
28
|
sessionInfo?: SessionInfo | null
|
|
21
29
|
interactionRequest?: { id: string; payload: AskUserQuestionParams } | null
|
|
22
30
|
onInteractionResponse?: (id: string, data: string | string[]) => void
|
|
31
|
+
interactionOptionNavigation?: {
|
|
32
|
+
optionCount: number
|
|
33
|
+
activeIndex: number
|
|
34
|
+
onMove: (delta: number) => void
|
|
35
|
+
onSubmit: () => void
|
|
36
|
+
}
|
|
23
37
|
placeholder?: string
|
|
24
38
|
initialContent?: SenderInitialContent
|
|
25
39
|
onCancel?: () => void
|
|
@@ -44,4 +58,6 @@ export interface SenderProps {
|
|
|
44
58
|
adapterOptions?: Array<{ value: string; label: ReactNode }>
|
|
45
59
|
onAdapterChange?: (adapter: string) => void
|
|
46
60
|
modelUnavailable?: boolean
|
|
61
|
+
queueMode?: SessionQueuedMessageMode
|
|
62
|
+
onQueueModeChange?: (mode: SessionQueuedMessageMode) => void
|
|
47
63
|
}
|
|
@@ -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,10 +1,8 @@
|
|
|
1
1
|
import './Sender.scss'
|
|
2
2
|
|
|
3
|
-
import { ThinkingStatus } from '#~/components/chat/ThinkingStatus'
|
|
4
3
|
import { useSenderController } from '#~/components/chat/sender/@hooks/use-sender-controller'
|
|
5
4
|
|
|
6
5
|
import { SenderBody } from './@components/sender-body/SenderBody'
|
|
7
|
-
import { SenderInteractionPanel } from './@components/sender-interaction-panel/SenderInteractionPanel'
|
|
8
6
|
import type { SenderProps } from './@types/sender-props'
|
|
9
7
|
|
|
10
8
|
export function Sender(props: SenderProps) {
|
|
@@ -18,16 +16,6 @@ export function Sender(props: SenderProps) {
|
|
|
18
16
|
controller.isInlineEdit ? 'chat-input-wrapper--inline-edit' : ''
|
|
19
17
|
].filter(Boolean).join(' ')}
|
|
20
18
|
>
|
|
21
|
-
{controller.isThinking && <ThinkingStatus />}
|
|
22
|
-
{!controller.isInlineEdit && controller.interactionRequest != null && (
|
|
23
|
-
<SenderInteractionPanel
|
|
24
|
-
interactionRequest={controller.interactionRequest}
|
|
25
|
-
permissionContext={controller.permissionContext}
|
|
26
|
-
deniedTools={controller.deniedTools}
|
|
27
|
-
reasons={controller.reasons}
|
|
28
|
-
onInteractionResponse={controller.interactionResponse}
|
|
29
|
-
/>
|
|
30
|
-
)}
|
|
31
19
|
{!controller.hideSender && (
|
|
32
20
|
<SenderBody
|
|
33
21
|
isInlineEdit={controller.isInlineEdit}
|
|
@@ -47,6 +35,8 @@ export function Sender(props: SenderProps) {
|
|
|
47
35
|
onCursorChange={controller.onCursorChange}
|
|
48
36
|
onKeyDown={controller.handleKeyDown}
|
|
49
37
|
onPaste={controller.attachments.handlePaste}
|
|
38
|
+
secondarySendShortcut={controller.secondarySendShortcut}
|
|
39
|
+
onSecondarySendShortcut={controller.onSecondarySendShortcut}
|
|
50
40
|
resolveCompletionMatch={controller.completion.resolveCompletionMatch}
|
|
51
41
|
resolveTokenDecorations={controller.completion.resolveTokenDecorations}
|
|
52
42
|
toolbarState={controller.toolbar.toolbarState}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChatMessage } from '@vibe-forge/core'
|
|
1
|
+
import type { ChatMessage, SessionMessageQueueState } from '@vibe-forge/core'
|
|
2
2
|
import type { SessionInfo } from '@vibe-forge/types'
|
|
3
3
|
|
|
4
4
|
import type { ChatErrorState, InteractionRequestState } from './interaction-state'
|
|
@@ -6,6 +6,7 @@ import type { ChatErrorState, InteractionRequestState } from './interaction-stat
|
|
|
6
6
|
export interface ChatSessionViewSnapshot {
|
|
7
7
|
messages: ChatMessage[]
|
|
8
8
|
sessionInfo: SessionInfo | null
|
|
9
|
+
queuedMessages: SessionMessageQueueState
|
|
9
10
|
errorState: ChatErrorState | null
|
|
10
11
|
interactionRequest: InteractionRequestState | null
|
|
11
12
|
isHydrated: boolean
|
|
@@ -18,6 +19,7 @@ export const createChatSessionViewSnapshot = (
|
|
|
18
19
|
): ChatSessionViewSnapshot => ({
|
|
19
20
|
messages: value?.messages ?? [],
|
|
20
21
|
sessionInfo: value?.sessionInfo ?? null,
|
|
22
|
+
queuedMessages: value?.queuedMessages ?? { steer: [], next: [] },
|
|
21
23
|
errorState: value?.errorState ?? null,
|
|
22
24
|
interactionRequest: value?.interactionRequest ?? null,
|
|
23
25
|
isHydrated: value?.isHydrated ?? false
|
|
@@ -42,6 +44,7 @@ export const restoreChatSessionViewSnapshot = (snapshot?: ChatSessionViewSnapsho
|
|
|
42
44
|
return {
|
|
43
45
|
messages: restorable.messages,
|
|
44
46
|
sessionInfo: restorable.sessionInfo,
|
|
47
|
+
queuedMessages: restorable.queuedMessages,
|
|
45
48
|
errorState: restorable.errorState,
|
|
46
49
|
interactionRequest: restorable.interactionRequest,
|
|
47
50
|
isReady: restorable.isHydrated
|
|
@@ -57,7 +57,11 @@ export function useChatAdapter() {
|
|
|
57
57
|
alt: '',
|
|
58
58
|
'aria-hidden': true
|
|
59
59
|
})
|
|
60
|
-
:
|
|
60
|
+
: createElement('span', {
|
|
61
|
+
key: 'fallback-icon',
|
|
62
|
+
className: 'adapter-option__icon adapter-option__icon--fallback material-symbols-rounded',
|
|
63
|
+
'aria-hidden': true
|
|
64
|
+
}, 'deployed_code'),
|
|
61
65
|
createElement('span', { key: 'text', className: 'adapter-option__text' }, display.title)
|
|
62
66
|
])
|
|
63
67
|
}
|
|
@@ -410,7 +410,11 @@ export function useChatModelAdapterSelection({
|
|
|
410
410
|
alt: '',
|
|
411
411
|
'aria-hidden': true
|
|
412
412
|
})
|
|
413
|
-
:
|
|
413
|
+
: createElement('span', {
|
|
414
|
+
key: 'fallback-icon',
|
|
415
|
+
className: 'adapter-option__icon adapter-option__icon--fallback material-symbols-rounded',
|
|
416
|
+
'aria-hidden': true
|
|
417
|
+
}, 'deployed_code'),
|
|
414
418
|
createElement('span', { key: 'text', className: 'adapter-option__text' }, display.title)
|
|
415
419
|
])
|
|
416
420
|
}
|
|
@@ -4,9 +4,18 @@ import { useTranslation } from 'react-i18next'
|
|
|
4
4
|
import { useLocation, useNavigate } from 'react-router-dom'
|
|
5
5
|
import { useSWRConfig } from 'swr'
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
branchSessionFromMessage,
|
|
9
|
+
createQueuedMessage,
|
|
10
|
+
createSession,
|
|
11
|
+
deleteQueuedMessage,
|
|
12
|
+
getApiErrorMessage,
|
|
13
|
+
moveQueuedMessage,
|
|
14
|
+
reorderQueuedMessages,
|
|
15
|
+
updateQueuedMessage
|
|
16
|
+
} from '#~/api.js'
|
|
8
17
|
import { connectionManager } from '#~/connectionManager.js'
|
|
9
|
-
import type { ChatMessageContent, Session } from '@vibe-forge/core'
|
|
18
|
+
import type { ChatMessageContent, Session, SessionQueuedMessageMode } from '@vibe-forge/core'
|
|
10
19
|
import type { ChatEffort } from './use-chat-effort'
|
|
11
20
|
import type { PermissionMode } from './use-chat-permission-mode'
|
|
12
21
|
|
|
@@ -61,7 +70,7 @@ export function useChatSessionActions({
|
|
|
61
70
|
setIsCreating(false)
|
|
62
71
|
}, [isCreating, session?.id])
|
|
63
72
|
|
|
64
|
-
const send = useCallback(async (text: string) => {
|
|
73
|
+
const send = useCallback(async (text: string, _mode?: SessionQueuedMessageMode) => {
|
|
65
74
|
if (text.trim() === '' || isThinking) return false
|
|
66
75
|
if (!hasAvailableModels) {
|
|
67
76
|
void message.warning(t('chat.modelConfigRequired'))
|
|
@@ -108,7 +117,7 @@ export function useChatSessionActions({
|
|
|
108
117
|
t
|
|
109
118
|
])
|
|
110
119
|
|
|
111
|
-
const sendContent = useCallback(async (content: ChatMessageContent[]) => {
|
|
120
|
+
const sendContent = useCallback(async (content: ChatMessageContent[], _mode?: SessionQueuedMessageMode) => {
|
|
112
121
|
if (content.length === 0 || isThinking) return false
|
|
113
122
|
if (!hasAvailableModels) {
|
|
114
123
|
void message.warning(t('chat.modelConfigRequired'))
|
|
@@ -200,11 +209,97 @@ export function useChatSessionActions({
|
|
|
200
209
|
return runMessageAction(messageId, 'edit', { content })
|
|
201
210
|
}, [runMessageAction])
|
|
202
211
|
|
|
212
|
+
const enqueueContent = useCallback(async (mode: SessionQueuedMessageMode, content: ChatMessageContent[]) => {
|
|
213
|
+
if (session?.id == null || session.id === '') {
|
|
214
|
+
return false
|
|
215
|
+
}
|
|
216
|
+
if (content.length === 0) {
|
|
217
|
+
return false
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
await createQueuedMessage(session.id, mode, content)
|
|
222
|
+
return true
|
|
223
|
+
} catch (err) {
|
|
224
|
+
console.error(err)
|
|
225
|
+
void message.error(getApiErrorMessage(err, t('common.operationFailed')))
|
|
226
|
+
return false
|
|
227
|
+
}
|
|
228
|
+
}, [message, session?.id, t])
|
|
229
|
+
|
|
230
|
+
const updateQueuedContent = useCallback(async (queueId: string, content: ChatMessageContent[]) => {
|
|
231
|
+
if (session?.id == null || session.id === '') {
|
|
232
|
+
return false
|
|
233
|
+
}
|
|
234
|
+
if (content.length === 0) {
|
|
235
|
+
return false
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
await updateQueuedMessage(session.id, queueId, content)
|
|
240
|
+
return true
|
|
241
|
+
} catch (err) {
|
|
242
|
+
console.error(err)
|
|
243
|
+
void message.error(getApiErrorMessage(err, t('common.operationFailed')))
|
|
244
|
+
return false
|
|
245
|
+
}
|
|
246
|
+
}, [message, session?.id, t])
|
|
247
|
+
|
|
248
|
+
const removeQueuedContent = useCallback(async (queueId: string) => {
|
|
249
|
+
if (session?.id == null || session.id === '') {
|
|
250
|
+
return false
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
try {
|
|
254
|
+
await deleteQueuedMessage(session.id, queueId)
|
|
255
|
+
return true
|
|
256
|
+
} catch (err) {
|
|
257
|
+
console.error(err)
|
|
258
|
+
void message.error(getApiErrorMessage(err, t('common.operationFailed')))
|
|
259
|
+
return false
|
|
260
|
+
}
|
|
261
|
+
}, [message, session?.id, t])
|
|
262
|
+
|
|
263
|
+
const moveQueuedContent = useCallback(async (queueId: string, mode: SessionQueuedMessageMode) => {
|
|
264
|
+
if (session?.id == null || session.id === '') {
|
|
265
|
+
return false
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
await moveQueuedMessage(session.id, queueId, mode)
|
|
270
|
+
return true
|
|
271
|
+
} catch (err) {
|
|
272
|
+
console.error(err)
|
|
273
|
+
void message.error(getApiErrorMessage(err, t('common.operationFailed')))
|
|
274
|
+
return false
|
|
275
|
+
}
|
|
276
|
+
}, [message, session?.id, t])
|
|
277
|
+
|
|
278
|
+
const reorderQueuedContent = useCallback(async (mode: SessionQueuedMessageMode, ids: string[]) => {
|
|
279
|
+
if (session?.id == null || session.id === '') {
|
|
280
|
+
return false
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
await reorderQueuedMessages(session.id, mode, ids)
|
|
285
|
+
return true
|
|
286
|
+
} catch (err) {
|
|
287
|
+
console.error(err)
|
|
288
|
+
void message.error(getApiErrorMessage(err, t('common.operationFailed')))
|
|
289
|
+
return false
|
|
290
|
+
}
|
|
291
|
+
}, [message, session?.id, t])
|
|
292
|
+
|
|
203
293
|
return {
|
|
204
294
|
isCreating,
|
|
205
295
|
isThinking,
|
|
206
296
|
send,
|
|
207
297
|
sendContent,
|
|
298
|
+
enqueueContent,
|
|
299
|
+
updateQueuedContent,
|
|
300
|
+
removeQueuedContent,
|
|
301
|
+
moveQueuedContent,
|
|
302
|
+
reorderQueuedContent,
|
|
208
303
|
editMessage,
|
|
209
304
|
forkMessage,
|
|
210
305
|
interrupt,
|