diff --git a/apps/web/tests/snapshots/workflow-run/ui-live.expected.md b/apps/web/tests/snapshots/workflow-run/ui-live.expected.md index 9e7f7b0ddc..52ac5c75fc 100644 --- a/apps/web/tests/snapshots/workflow-run/ui-live.expected.md +++ b/apps/web/tests/snapshots/workflow-run/ui-live.expected.md @@ -1,23 +1,6 @@ -- text: "Use the workflow tool exactly once, with args omitted, meta set to { \"name\": \"snapshot-flow\", \"description\": \"one child for the snapshot\" }, and this EXACT script body (copy it verbatim): phase('Run') const reply = await agent('Reply with exactly the word WF_CHILD_OK and nothing else.') return { reply } After the workflow returns, reply with the single word WORKFLOW_DONE and stop. Do not use any other tool. {{clock}}" -- button "Copy": - - img -- button "Context injection @deepseek-ai/dsh-system-prompt": - - img - - img - - text: Context injection @deepseek-ai/dsh-system-prompt -- button "Think The user wants me to use the workflow tool exactly once with specific parameters. Let me carefully follow the instructions:": - - img - - img - - text: "Think The user wants me to use the workflow tool exactly once with specific parameters. Let me carefully follow the instructions:" -- text: Running -- button "Tool call workflow ·": - - img - - img - - text: Tool call workflow · - button "snapshot-flow 1 member Running" [expanded]: - img - text: snapshot-flow 1 member Running - button "Run 1 member Running 1": - img - text: Run 1 member Running 1 -- status: Deep diving... diff --git a/apps/web/tests/workflow-run.e2e.ts b/apps/web/tests/workflow-run.e2e.ts index 04f79cccd4..ef787a60da 100644 --- a/apps/web/tests/workflow-run.e2e.ts +++ b/apps/web/tests/workflow-run.e2e.ts @@ -93,7 +93,7 @@ describe.skipIf(MODE === 'record')('web e2e: durable workflow run in Chat', () = await phaseDisclosure.click() expect(await phaseDisclosure.getAttribute('aria-expanded')).toBe('false') expect(await member.count()).toBe(0) - const liveSnapshot = await captureStableAria(page, '[data-chat-flow]', scaffold.workspaceCwd) + const liveSnapshot = await captureStableAria(page, '[data-workflow-run]', scaffold.workspaceCwd) await compareOrRefreshGolden(UI_LIVE_EXPECTED, liveSnapshot, MODE) await phaseDisclosure.press('Enter') await member.waitFor() diff --git a/packages/client/ui-workflow-run/src/client/WorkflowRunPanel.tsx b/packages/client/ui-workflow-run/src/client/WorkflowRunPanel.tsx index a839403266..9df3452775 100644 --- a/packages/client/ui-workflow-run/src/client/WorkflowRunPanel.tsx +++ b/packages/client/ui-workflow-run/src/client/WorkflowRunPanel.tsx @@ -147,6 +147,16 @@ function collapsePending(state: DisclosureState): DisclosureState { return { ...state, open: false, pendingCleanCollapse: false } } +function existingPhaseState( + phases: ReadonlyMap, + key: string, +): DisclosureState { + const phase = phases.get(key) + /* v8 ignore next -- mounted phase callbacks are created from this owner map. */ + if (phase === undefined) throw new Error(`Missing disclosure state for phase ${key}`) + return phase +} + function phaseStatusSummary(members: readonly WorkflowRunMemberData[], t: WorkflowRunPanelProps['t']): string { const counts = new Map() for (const member of members) counts.set(member.status, (counts.get(member.status) ?? 0) + 1) @@ -226,20 +236,8 @@ function MemberRow({ member, navigable, openSession, t }: { readonly t: WorkflowRunPanelProps['t'] }) { const name = readableMember(member.label, t) - const buttonRef = useRef(null) - const [keepFocusedButton, setKeepFocusedButton] = useState(navigable) - const renderButton = navigable || keepFocusedButton - - useLayoutEffect(() => { - if (navigable) { - if (!keepFocusedButton) setKeepFocusedButton(true) - return - } - const button = buttonRef.current - if (button === null || button.ownerDocument.activeElement !== button) { - if (keepFocusedButton) setKeepFocusedButton(false) - } - }, [keepFocusedButton, navigable]) + const [focused, setFocused] = useState(false) + const renderButton = navigable || focused const content = ( <> @@ -253,14 +251,14 @@ function MemberRow({ member, navigable, openSession, t }: { } return (