feat: message queueing [LET-4669] (#171)
This commit is contained in:
@@ -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>
|
||||
|
||||
36
src/cli/components/QueuedMessages.tsx
Normal file
36
src/cli/components/QueuedMessages.tsx
Normal 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";
|
||||
Reference in New Issue
Block a user