feat: message queueing [LET-4669] (#171)

This commit is contained in:
Christina Tong
2025-12-10 11:08:55 -08:00
committed by GitHub
parent 9fb0b68a40
commit b6c387b9ec
4 changed files with 146 additions and 23 deletions

View File

@@ -11,6 +11,7 @@ import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
import { InputAssist } from "./InputAssist";
import { PasteAwareTextInput } from "./PasteAwareTextInput";
import { QueuedMessages } from "./QueuedMessages";
import { ShimmerText } from "./ShimmerText";
// Type assertion for ink-spinner compatibility
@@ -23,7 +24,6 @@ const COUNTER_VISIBLE_THRESHOLD = 1000;
export function Input({
visible = true,
streaming,
commandRunning = false,
tokenCount,
thinkingMessage,
onSubmit,
@@ -35,10 +35,11 @@ export function Input({
agentId,
agentName,
currentModel,
messageQueue,
onEnterQueueEditMode,
}: {
visible?: boolean;
streaming: boolean;
commandRunning?: boolean;
tokenCount: number;
thinkingMessage: string;
onSubmit: (message?: string) => Promise<{ submitted: boolean }>;
@@ -50,6 +51,8 @@ export function Input({
agentId?: string;
agentName?: string | null;
currentModel?: string | null;
messageQueue?: string[];
onEnterQueueEditMode?: () => void;
}) {
const [value, setValue] = useState("");
const [escapePressed, setEscapePressed] = useState(false);
@@ -119,6 +122,16 @@ export function Input({
// When streaming, use Esc to interrupt
if (streaming && onInterrupt && !interruptRequested) {
onInterrupt();
// If there are queued messages, load them into the input box
if (messageQueue && messageQueue.length > 0) {
const queueText = messageQueue.join("\n");
setValue(queueText);
// Signal to App.tsx to clear the queue
if (onEnterQueueEditMode) {
onEnterQueueEditMode();
}
}
return;
}
@@ -226,7 +239,7 @@ export function Input({
}
// On first wrapped line
// First press: move to start, second press: navigate history
// First press: move to start, second press: queue edit or history
if (currentCursorPosition > 0 && !atStartBoundary) {
// First press - move cursor to start
setCursorPos(0);
@@ -234,7 +247,25 @@ export function Input({
return;
}
// Second press or already at start - trigger history navigation
// Check if we should load queue (streaming with queued messages)
if (
streaming &&
messageQueue &&
messageQueue.length > 0 &&
atStartBoundary
) {
setAtStartBoundary(false);
// Clear the queue and load into input as one multi-line message
const queueText = messageQueue.join("\n");
setValue(queueText);
// Signal to App.tsx to clear the queue
if (onEnterQueueEditMode) {
onEnterQueueEditMode();
}
return;
}
// Otherwise, trigger history navigation
if (history.length === 0) return;
setAtStartBoundary(false); // Reset for next time
@@ -352,9 +383,6 @@ export function Input({
return;
}
if (streaming || commandRunning) {
return;
}
const previousValue = value;
// Add to history if not empty and not a duplicate of the last entry
@@ -458,6 +486,11 @@ export function Input({
</Box>
)}
{/* Queue display - show when streaming with queued messages */}
{streaming && messageQueue && messageQueue.length > 0 && (
<QueuedMessages messages={messageQueue} />
)}
<Box flexDirection="column">
{/* Top horizontal divider */}
<Text dimColor>{horizontalLine}</Text>

View File

@@ -0,0 +1,36 @@
import { Box, Text } from "ink";
import { memo } from "react";
interface QueuedMessagesProps {
messages: string[];
}
export const QueuedMessages = memo(({ messages }: QueuedMessagesProps) => {
const maxDisplay = 5;
return (
<Box flexDirection="column" marginBottom={1}>
{messages.slice(0, maxDisplay).map((msg) => (
<Box key={msg} flexDirection="row">
<Box width={2} flexShrink={0}>
<Text dimColor>{">"}</Text>
</Box>
<Box flexGrow={1}>
<Text dimColor>{msg}</Text>
</Box>
</Box>
))}
{messages.length > maxDisplay && (
<Box flexDirection="row">
<Box width={2} flexShrink={0} />
<Box flexGrow={1}>
<Text dimColor>...and {messages.length - maxDisplay} more</Text>
</Box>
</Box>
)}
</Box>
);
});
QueuedMessages.displayName = "QueuedMessages";