steering demoable: the Enter branch keys on BUSY (own-stream clears turnInFlight — Enter was swallowed during our own turn), and the button follows the text (owner live report 2026-10-03)

Root cause: doSteerOrSend branched on turnInFlight, but the local dedup
(chat.tsx ~370) clears that flag the moment the own stream takes over —
during our own turn busy was true and turnInFlight false, so Enter fell to
the plain-send path whose busy guard returned silently. Branch on busy:
own stream AND foreign/wake turns both have a live agent registered by the
time the send's POST resolved, so the steer lands (a raced settle 409s and
falls back to the plain send).

The button now follows the text: busy + typed text = Send (click steers —
steering must be clickable, not Enter-only); busy + empty = Stop. ESC keeps
its confirm-popup abort in every state, so stop is never unreachable.
This commit is contained in:
amosblomqvist
2026-10-03 03:07:49 +03:00
parent 5271ca0e4a
commit 571cc94053
3 changed files with 25 additions and 15 deletions
+10 -6
View File
@@ -98,21 +98,25 @@ export function Composer({
rows={1}
className="field-sizing-content lh-input max-h-48 min-h-10 flex-1 resize-none bg-transparent px-2.5 py-2 outline-none placeholder:text-muted-foreground"
/>
{/* No tooltip on the send square — the ↵ glyph is the
affordance and Enter is obvious (owner, subtlety
round 2026-09-25). */}
{/* The button follows the text (owner report 2026-10-03): while
a turn runs WITH text typed it stays the SEND affordance
(click = steer — steering must be clickable, not Enter-only);
only an empty composer morphs it to Stop. ESC keeps its
confirm-popup abort in every state, so stop is never
unreachable. No tooltip — the ↵ glyph is the affordance
(owner, subtlety round 2026-09-25). */}
<button
type="submit"
aria-label={busy ? "Stop" : "Send"}
aria-label={busy && !canSend ? "Stop" : "Send"}
disabled={!busy && !canSend}
className={cn(
"flex size-10 shrink-0 items-center justify-center rounded-[10px] transition-colors",
busy
busy && !canSend
? "bg-secondary text-foreground hover:bg-accent"
: "bg-primary text-primary-foreground hover:opacity-90 disabled:opacity-40",
)}
>
{busy ? <SquareIcon className="size-4" /> : <CornerDownLeftIcon className="size-5" />}
{busy && !canSend ? <SquareIcon className="size-4" /> : <CornerDownLeftIcon className="size-5" />}
</button>
</form>
);