Skip to content

Commit f9fa8cf

Browse files
DylanMerigaudclaude
andcommitted
Hold the extraction reveal ~3.5s after the read, so the figures are readable
Matching fires almost the instant intake finishes, so the extracted invoice figures flashed on screen and were gone before you could read them. Keep the full-screen reveal for a short grace window after the read completes, then collapse to the one-line intake node and hand the pane to the graph. Derived from a ref (the read timestamp, keyed on the invoice) plus a one-shot timer, not a stuck-prone boolean state — the first attempt gated `pastIntake` on a state flag that never cleared and left the reveal overlay covering the "View trace" button. Verified: the overlay lifts after the window and the approval e2e (which clicks View trace) passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent b611d22 commit f9fa8cf

1 file changed

Lines changed: 34 additions & 1 deletion

File tree

components/dashboard.tsx

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,40 @@ export const Dashboard = ({
413413
);
414414
const doneIntake =
415415
intake && intake.state.status === "done" && movedPastIntake ? intake : null;
416-
const pastIntake = doneIntake !== null;
416+
417+
// Hold the full-screen extraction reveal for a beat AFTER the read completes, so
418+
// the extracted figures are actually READABLE before the pane collapses to the
419+
// one-line intake node and hands over to the graph. Matching fires almost
420+
// instantly, so without this grace window the reveal flashes and is gone.
421+
//
422+
// `intakeDoneAtRef` records WHEN the read first completed (keyed on the invoice, so
423+
// a re-render mid-hold doesn't reset it); a one-shot timer re-renders once the
424+
// window elapses. `revealHeld` is derived from the ref each render, so React state
425+
// stays a single boolean tick and can't get stuck.
426+
const REVEAL_HOLD_MS = 3500;
427+
const intakeDoneAtRef = useRef<{ key: string; at: number } | null>(null);
428+
const [, forceTick] = useState(0);
429+
const doneKey = doneIntake?.document.invoiceNumber ?? null;
430+
useEffect(() => {
431+
if (doneKey === null) {
432+
intakeDoneAtRef.current = null;
433+
return;
434+
}
435+
if (intakeDoneAtRef.current?.key === doneKey) return; // already timing
436+
intakeDoneAtRef.current = { key: doneKey, at: Date.now() };
437+
const t = setTimeout(() => forceTick((n) => n + 1), REVEAL_HOLD_MS);
438+
return () => clearTimeout(t);
439+
}, [doneKey]);
440+
441+
const holdRec = intakeDoneAtRef.current;
442+
const revealHeld =
443+
holdRec !== null &&
444+
holdRec.key === doneKey &&
445+
Date.now() - holdRec.at < REVEAL_HOLD_MS;
446+
447+
// Past intake once the read is done AND its reveal grace window has elapsed. Until
448+
// then the reveal owns the pane so the figures can be read.
449+
const pastIntake = doneIntake !== null && !revealHeld;
417450

418451
// The gates the paused run is waiting on (joined: live status + the workflow's
419452
// people). Drives the inline per-node Approve/Reject and the submit affordance.

0 commit comments

Comments
 (0)