Files
gitmost/apps/client/src/features/ai-chat/utils/queue-helpers.ts
claude_code e423c35676 feat(ai-chat): queue messages typed while the agent is streaming
Previously a message composed while the AI agent was streaming a reply was
silently dropped (the composer early-returned on isStreaming). Now such
messages are queued FIFO and sent automatically once the current turn
finishes cleanly.

- chat-input: submit() enqueues while streaming (via new onQueue prop) and
  sends otherwise; during streaming show a queue Send button (when text is
  present) alongside the Stop button; the textarea stays usable.
- chat-thread: per-conversation queue in local state (mirrored in a ref);
  flush the next message in onFinish ONLY on a clean finish - ai@6 useChat
  fires onFinish from a finally on Stop/disconnect/error too, where the queue
  must be preserved. Pending messages render as removable chips above the
  composer. Queue is cleared on chat switch (parent remount) and survives
  in-place new-chat id adoption.
- queue-helpers: pure FIFO helpers (enqueue/dequeue/removeQueuedById) + tests.
- i18n: add en-US/ru-RU keys (Queue message, Remove queued message,
  Send when the agent finishes).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 18:53:31 +03:00

35 lines
1013 B
TypeScript

// Pure FIFO helpers for the AI-chat "send while the agent is busy" queue.
// Kept side-effect free so they can be unit-tested without React.
export interface QueuedMessage {
id: string;
text: string;
}
/** Append a message to the end of the queue (returns a new array). */
export function enqueueMessage(
queue: QueuedMessage[],
message: QueuedMessage,
): QueuedMessage[] {
return [...queue, message];
}
/** Split the queue into its first item (`head`) and the remainder (`rest`).
* `head` is null when the queue is empty. Does not mutate the input. */
export function dequeue(queue: QueuedMessage[]): {
head: QueuedMessage | null;
rest: QueuedMessage[];
} {
if (queue.length === 0) return { head: null, rest: [] };
const [head, ...rest] = queue;
return { head, rest };
}
/** Remove the queued message with the given id (returns a new array). */
export function removeQueuedById(
queue: QueuedMessage[],
id: string,
): QueuedMessage[] {
return queue.filter((m) => m.id !== id);
}