支持会话响应时添加队列消息

This commit is contained in:
xintaofei
2026-03-12 23:19:24 +08:00
parent 6c26d067fd
commit 1f623c0d6e
16 changed files with 566 additions and 31 deletions

View File

@@ -18,6 +18,7 @@ import { useTabContext } from "@/contexts/tab-context"
import { useSessionStats } from "@/contexts/session-stats-context"
import { cn } from "@/lib/utils"
import { useConnectionLifecycle } from "@/hooks/use-connection-lifecycle"
import { useMessageQueue, type QueuedMessage } from "@/hooks/use-message-queue"
import { MessageListView } from "@/components/message/message-list-view"
import { ConversationShell } from "@/components/chat/conversation-shell"
import { AgentSelector } from "@/components/chat/agent-selector"
@@ -254,6 +255,18 @@ const ConversationTabView = memo(function ConversationTabView({
disconnect: connDisconnect,
sessionId: connSessionId,
} = conn
const messageQueue = useMessageQueue()
const {
queue: msgQueue,
enqueue: mqEnqueue,
dequeue: mqDequeue,
remove: mqRemove,
reorder: mqReorder,
updateItem: mqUpdateItem,
editingItemId: mqEditingItemId,
startEditing: mqStartEditing,
cancelEditing: mqCancelEditing,
} = messageQueue
const connStatusRef = useRef(connStatus)
useEffect(() => {
connStatusRef.current = connStatus
@@ -329,6 +342,32 @@ const ConversationTabView = memo(function ConversationTabView({
syncTurnMetadata,
])
// Auto-send queued messages when agent finishes responding.
// Refs are synced via useEffect; the auto-send effect is declared
// AFTER completeTurn so React runs it second.
const autoSendQueueRef = useRef<() => QueuedMessage | undefined>(mqDequeue)
useEffect(() => {
autoSendQueueRef.current = mqDequeue
}, [mqDequeue])
const handleSendRef = useRef<
(draft: PromptDraft, modeId?: string | null) => void
>(() => {})
const prevAutoSendStatusRef = useRef(connStatus)
useEffect(() => {
const wasPrompting = prevAutoSendStatusRef.current === "prompting"
prevAutoSendStatusRef.current = connStatus
if (!wasPrompting || connStatus !== "connected") return
// Use queueMicrotask to ensure completeTurn effect has fully committed
queueMicrotask(() => {
const next = autoSendQueueRef.current()
if (next) {
handleSendRef.current(next.draft, next.modeId)
}
})
}, [connStatus])
useEffect(() => {
// Only sync non-null liveMessage updates to state. When conn.liveMessage
// goes null (agent finished streaming), don't clear state.liveMessage —
@@ -545,6 +584,11 @@ const ConversationTabView = memo(function ConversationTabView({
]
)
// Sync handleSend ref for auto-send effect (declared before handleSend)
useEffect(() => {
handleSendRef.current = handleSend
}, [handleSend])
const handleOpenAgentsSettings = useCallback(() => {
openSettingsWindow("agents", { agentType: selectedAgent }).catch((err) => {
console.error(
@@ -615,6 +659,33 @@ const ConversationTabView = memo(function ConversationTabView({
]
)
// Queue edit flow: derive editing draft text from queue state
const editingQueueDraftText = useMemo(() => {
if (!mqEditingItemId) return null
const item = msgQueue.find((m) => m.id === mqEditingItemId)
return item?.draft.displayText ?? null
}, [mqEditingItemId, msgQueue])
const handleQueueEdit = useCallback(
(id: string) => {
mqStartEditing(id)
},
[mqStartEditing]
)
const handleQueueCancelEdit = useCallback(() => {
mqCancelEditing()
}, [mqCancelEditing])
const handleSaveQueueEdit = useCallback(
(draft: PromptDraft) => {
if (mqEditingItemId) {
mqUpdateItem(mqEditingItemId, draft)
}
},
[mqEditingItemId, mqUpdateItem]
)
const showDraftHeader = !hasPersistedConversation && !hasSentMessage
const isWelcomeMode = showDraftHeader
@@ -657,6 +728,16 @@ const ConversationTabView = memo(function ConversationTabView({
draftStorageKey={draftStorageKey}
hideInput={isWelcomeMode}
isActive={isActive}
queue={msgQueue}
onEnqueue={mqEnqueue}
onQueueReorder={mqReorder}
onQueueEdit={handleQueueEdit}
onQueueDelete={mqRemove}
editingItemId={mqEditingItemId}
editingDraftText={editingQueueDraftText}
isEditingQueueItem={mqEditingItemId != null}
onSaveQueueEdit={handleSaveQueueEdit}
onCancelQueueEdit={handleQueueCancelEdit}
>
{isWelcomeMode ? (
<div className="flex h-full min-h-0 flex-col items-center justify-center">