fix(ui): keep the input box usable while a response is streaming
While a turn was in flight (isThinking or streamingText), the ChatInput was
unmounted and replaced with a static "Waiting for response…" box — so the user
couldn't type ahead during output, the input field just vanished.
Now ChatInput stays mounted the whole time: the user can type and edit while
output streams in. A one-line status above it ("Waiting… esc to interrupt — type
ahead, Enter sends when done.") replaces the old full-width box. Submitting a
second turn mid-stream is still prevented — handleSubmit now guards on
isThinking/streamingText and returns early, so Enter is a no-op until the
in-flight turn finishes (the typed text stays in the box). The original race this
swap was protecting against (overlapping turns on the same session) is preserved
by that guard.
Verified: typecheck clean, build 274.76 KB, 301 tests pass.
This commit is contained in:
+16
-18
@@ -776,6 +776,11 @@ export function App({
|
||||
}
|
||||
|
||||
async function handleSubmit(raw: string) {
|
||||
// A turn is already in flight (streaming output or waiting for the first chunk). Keep the
|
||||
// input box mounted so the user can type ahead, but ignore Enter so a second turn can't
|
||||
// overlap onto the same session. The in-flight turn is the source of truth; the typed text
|
||||
// stays in the box for when the turn finishes.
|
||||
if (isThinking || streamingText !== null) return;
|
||||
setInputValue("");
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return;
|
||||
@@ -1198,25 +1203,18 @@ export function App({
|
||||
gitInfo={gitInfo}
|
||||
/>
|
||||
)}
|
||||
{isThinking || streamingText !== null ? (
|
||||
// Swapped in for ChatInput while a turn is in flight. Closes a real (if rare) race:
|
||||
// handleSubmit had no guard against firing while a turn was already running, so
|
||||
// typing and hitting Enter mid-stream could submit a second overlapping turn onto
|
||||
// the same session.
|
||||
<Box borderStyle="round" borderColor={ACCENT_HEX} paddingX={1} width="100%">
|
||||
<Text dimColor>Waiting for response… (esc to interrupt)</Text>
|
||||
</Box>
|
||||
) : (
|
||||
<ChatInput
|
||||
value={inputValue}
|
||||
onChange={setInputValue}
|
||||
onSubmit={handleSubmit}
|
||||
cwd={cwd}
|
||||
history={history}
|
||||
availableColumns={chatColumns}
|
||||
isActive={!filePanelFocused}
|
||||
/>
|
||||
{(isThinking || streamingText !== null) && (
|
||||
<Text dimColor> Waiting for response… (esc to interrupt) — type ahead, Enter sends when done.</Text>
|
||||
)}
|
||||
<ChatInput
|
||||
value={inputValue}
|
||||
onChange={setInputValue}
|
||||
onSubmit={handleSubmit}
|
||||
cwd={cwd}
|
||||
history={history}
|
||||
availableColumns={chatColumns}
|
||||
isActive={!filePanelFocused}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user