agent ui: reset cancel-turn busy state via .finally(), not a thinking-flip effect
argus (PR review): the thinking-flip useEffect left the button stuck
disabled if the /api/cancel POST itself failed while the turn was
still genuinely thinking, since nothing re-fires the effect. Switch
onCancelTurn's signature to () => Promise<void> and reset cancelBusy
in a .finally() on that promise instead — fires on success or
failure alike, matching app.js's original
postCancelTurn().finally(() => { btn.disabled = false; }).
This commit is contained in:
parent
b753ec2093
commit
49c3b86dfd
2 changed files with 14 additions and 14 deletions
|
|
@ -155,7 +155,7 @@ export function Root() {
|
||||||
paused={false}
|
paused={false}
|
||||||
onTogglePause={() => {}}
|
onTogglePause={() => {}}
|
||||||
thinking={false}
|
thinking={false}
|
||||||
onCancelTurn={() => {}}
|
onCancelTurn={() => Promise.resolve()}
|
||||||
/>
|
/>
|
||||||
</Header>
|
</Header>
|
||||||
<main className="agent-main">
|
<main className="agent-main">
|
||||||
|
|
@ -210,12 +210,12 @@ export function Root() {
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
thinking={effectiveTurnState === 'thinking'}
|
thinking={effectiveTurnState === 'thinking'}
|
||||||
onCancelTurn={() => {
|
onCancelTurn={() =>
|
||||||
postCancelTurn().then((r) => {
|
postCancelTurn().then((r) => {
|
||||||
if (!r.ok) liveStreamRef.current?.pushNote(`✗ /cancel failed${r.detail ? ': ' + r.detail : ''}`);
|
if (!r.ok) liveStreamRef.current?.pushNote(`✗ /cancel failed${r.detail ? ': ' + r.detail : ''}`);
|
||||||
refresh();
|
refresh();
|
||||||
});
|
})
|
||||||
}}
|
}
|
||||||
/>
|
/>
|
||||||
</Header>
|
</Header>
|
||||||
<main className="agent-main">
|
<main className="agent-main">
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
// selection state live in the caller (`Root.tsx`, via the
|
// selection state live in the caller (`Root.tsx`, via the
|
||||||
// `useAgentState` hook), so this component can still be demoed and
|
// `useAgentState` hook), so this component can still be demoed and
|
||||||
// reviewed against plain sample data independent of live `/api/state`.
|
// reviewed against plain sample data independent of live `/api/state`.
|
||||||
import { useEffect, useState } from 'preact/hooks';
|
import { useState } from 'preact/hooks';
|
||||||
import { Badge, type BadgeTone } from '@hive/shared/badge.js';
|
import { Badge, type BadgeTone } from '@hive/shared/badge.js';
|
||||||
import { Dropdown, type DropdownOption } from '@hive/shared/dropdown.js';
|
import { Dropdown, type DropdownOption } from '@hive/shared/dropdown.js';
|
||||||
import './StatusChips.css';
|
import './StatusChips.css';
|
||||||
|
|
@ -32,7 +32,7 @@ export interface StatusChipsProps {
|
||||||
onTogglePause: () => void;
|
onTogglePause: () => void;
|
||||||
lastTurnLabel?: string;
|
lastTurnLabel?: string;
|
||||||
thinking: boolean;
|
thinking: boolean;
|
||||||
onCancelTurn: () => void;
|
onCancelTurn: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MODEL_DESCRIPTIONS: Record<string, string> = {
|
const MODEL_DESCRIPTIONS: Record<string, string> = {
|
||||||
|
|
@ -105,14 +105,14 @@ export function StatusChips({
|
||||||
thinking,
|
thinking,
|
||||||
onCancelTurn,
|
onCancelTurn,
|
||||||
}: StatusChipsProps) {
|
}: StatusChipsProps) {
|
||||||
|
// `.finally()` on the caller's promise (not a `thinking`-flip effect):
|
||||||
|
// re-enables the button once the request actually settles, success or
|
||||||
|
// failure alike — ported from app.js's original
|
||||||
|
// `postCancelTurn().finally(() => { btn.disabled = false; })`. A
|
||||||
|
// `thinking`-only reset would leave the button stuck disabled on a
|
||||||
|
// failed request (argus, PR review): the turn is still in flight, so
|
||||||
|
// `thinking` never flips to trigger a reset.
|
||||||
const [cancelBusy, setCancelBusy] = useState(false);
|
const [cancelBusy, setCancelBusy] = useState(false);
|
||||||
// The button unmounts (not just hides) once `thinking` flips false, but
|
|
||||||
// `cancelBusy` lives in this component's own state across re-renders —
|
|
||||||
// reset it here so a *later* turn doesn't inherit a stale "busy" from a
|
|
||||||
// previous cancel click.
|
|
||||||
useEffect(() => {
|
|
||||||
if (!thinking) setCancelBusy(false);
|
|
||||||
}, [thinking]);
|
|
||||||
return (
|
return (
|
||||||
<div class="status-chips">
|
<div class="status-chips">
|
||||||
<Badge value={aliveLabel} tone={aliveTone} />
|
<Badge value={aliveLabel} tone={aliveTone} />
|
||||||
|
|
@ -160,7 +160,7 @@ export function StatusChips({
|
||||||
disabled={cancelBusy}
|
disabled={cancelBusy}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCancelBusy(true);
|
setCancelBusy(true);
|
||||||
onCancelTurn();
|
onCancelTurn().finally(() => setCancelBusy(false));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
■ cancel turn
|
■ cancel turn
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue