Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/cli/src/ui/hooks/useGeminiStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,11 @@ export const useGeminiStream = (
};
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The setImmediate yield creates a sub-millisecond window where the user could cancel (press Escape). After the yield, execution falls through to handleAtCommand, applyVisionBridgeIfNeeded, and eventually sendMessageStream without re-checking turnCancelledRef. While the abort signal IS propagated to all downstream calls (so the API call fails cleanly rather than succeeding with stale data), adding an early re-check is consistent with the entry guard at line 922 and avoids unnecessary downstream work.

Suggested change
}
await new Promise((r) => setImmediate(r));
if (turnCancelledRef.current) {
return { queryToSend: null, shouldProceed: false };
}

— qwen3.7-max via Qwen Code /review


// Yield to let React render the user message before continuing
// with @-command processing and API call. Reduces the visual lag
// between input-box clear and the message appearing in history.
await Promise.resolve();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Review Bot] await Promise.resolve() only yields a microtask — it does not give React a chance to render, so this line is effectively a no-op.

React 19.2.4 (Ink 7.0.3) schedules renders via MessageChannel.postMessage, which is a macrotask. The JS event loop drains all microtasks before any macrotask fires, so the continuation after this await runs before React's scheduler has a chance to flush. The comment above claims this yields to React, but that's not what happens.

The pre-patch code already reaches a natural await almost immediately after addItem — either await handleAtCommand(...) (which internally does await resolveAtCommandQuery() on its first line) or await applyVisionBridgeIfNeeded(...) (which early-returns a resolved Promise for non-image messages). Those real awaits already yield to the event loop at the same point a microtask yield would, so adding one extra microtask here changes nothing about when React can render.

The perceived lag reduction in the PR description (200–500ms → 50–100ms) is more likely explained by those subsequent I/O-bound awaits, not this line.

Suggestions:

  • To actually flush a render before continuing, use a macrotask yield: await new Promise(r => setTimeout(r, 0)) (costs ~4ms).
  • Or remove this line entirely and keep the existing behavior — the subsequent await handleAtCommand / await applyVisionBridgeIfNeeded already provide natural render windows.
  • If keeping the line, at least update the comment to accurately describe what it does (one microtask yield, no render flush), so future readers aren't misled.


// Handle @-commands (which might involve tool calls)
if (isAtCommand(trimmedQuery)) {
const atCommandResult = await handleAtCommand({
Expand Down
Loading