fix: make footer sole workflow run control

This commit is contained in:
lda
2026-07-12 15:48:17 +07:00 Verified
parent ed2ee043bd
commit 1359a7f8e3
8 changed files with 9 additions and 59 deletions
@@ -64,8 +64,8 @@ const renderBeat = (
demo={demo} demo={demo}
selectedNodeId={null} selectedNodeId={null}
selectNode={noop} selectNode={noop}
openEvidence={openEvidence} openEvidence={openEvidence}
approvalActions={options.approvalActions} approvalActions={options.approvalActions}
/>, />,
); );
return { ...rendered, openEvidence }; return { ...rendered, openEvidence };
@@ -181,8 +181,7 @@ describe("OperatorChat", () => {
expect(screen.getByRole("button", { name: /presentation.selectWorkflowNode/i })).toBeInTheDocument(); expect(screen.getByRole("button", { name: /presentation.selectWorkflowNode/i })).toBeInTheDocument();
}); });
it("shows a chat-owned run prepared workflow action", async () => { it("does not render the run prepared workflow action", () => {
const user = userEvent.setup();
const runPreparedWorkflow = vi.fn(async () => {}); const runPreparedWorkflow = vi.fn(async () => {});
render( render(
@@ -200,8 +199,8 @@ describe("OperatorChat", () => {
/>, />,
); );
await user.click(screen.getByRole("button", { name: /run prepared workflow/i })); expect(screen.queryByRole("button", { name: /run prepared workflow/i })).not.toBeInTheDocument();
expect(runPreparedWorkflow).toHaveBeenCalledTimes(1); expect(runPreparedWorkflow).not.toHaveBeenCalled();
}); });
it("routes schema approval submit and revision request through the timeline agent when present", async () => { it("routes schema approval submit and revision request through the timeline agent when present", async () => {
@@ -43,7 +43,6 @@ export const OperatorChat = ({ state, messages, timelineAgent, onApprove, onRequ
? () => { timelineAgent.requestRevision().catch(console.error); } ? () => { timelineAgent.requestRevision().catch(console.error); }
: onRequestRevision; : onRequestRevision;
const composition = compositionForState(state); const composition = compositionForState(state);
const isScene8 = state.location.kind === "main" && state.location.sceneId === "agent-handoff";
const presentationSurface = composition.chatTheme === "light" ? "editorial" : "night"; const presentationSurface = composition.chatTheme === "light" ? "editorial" : "night";
return ( return (
<aside <aside
@@ -56,11 +55,6 @@ export const OperatorChat = ({ state, messages, timelineAgent, onApprove, onRequ
<AssistantOperatorThread <AssistantOperatorThread
mode={composition.chatMode} mode={composition.chatMode}
messages={visibleMessages} messages={visibleMessages}
runAction={timelineAgent && !isScene8 ? {
label: timelineAgent.runLabel,
disabled: !timelineAgent.canRun,
run: () => void timelineAgent.runPreparedWorkflow(),
} : undefined}
submitApproval={submit} submitApproval={submit}
requestRevision={requestRevision} requestRevision={requestRevision}
/> />
@@ -286,7 +286,7 @@ describe("PresentationRoute", () => {
const { PresentationRoute } = await import("./PresentationRoute.js"); const { PresentationRoute } = await import("./PresentationRoute.js");
render(<PresentationRoute />); render(<PresentationRoute />);
expect(await screen.findAllByRole("button", { name: /run prepared workflow/i })).toHaveLength(2); expect(await screen.findAllByRole("button", { name: /run prepared workflow/i })).toHaveLength(1);
}); });
it("owns the live run action in the footer rail", async () => { it("owns the live run action in the footer rail", async () => {
@@ -13,7 +13,6 @@ type AuthoringConversationProps = {
readonly requestOverride?: string | undefined; readonly requestOverride?: string | undefined;
readonly requestOverrides?: Scene9SubmittedOverrides | undefined; readonly requestOverrides?: Scene9SubmittedOverrides | undefined;
readonly scrollMode?: "active" | "start" | undefined; readonly scrollMode?: "active" | "start" | undefined;
readonly runAction?: { readonly label: string; readonly disabled: boolean; readonly run: () => void } | undefined;
}; };
/** Renders the same prepared conversation at full-stage or compact-dock scale. */ /** Renders the same prepared conversation at full-stage or compact-dock scale. */
@@ -24,7 +23,6 @@ export const AuthoringConversation = ({
requestOverride, requestOverride,
requestOverrides, requestOverrides,
scrollMode, scrollMode,
runAction,
}: AuthoringConversationProps) => ( }: AuthoringConversationProps) => (
<AssistantOperatorThread <AssistantOperatorThread
mode={surface === "stage" ? "full" : "dock"} mode={surface === "stage" ? "full" : "dock"}
@@ -33,6 +31,5 @@ export const AuthoringConversation = ({
activeToolGroupId={authoringToolGroupId(activePhase)} activeToolGroupId={authoringToolGroupId(activePhase)}
scrollMode={scrollMode} scrollMode={scrollMode}
ariaLabel="prepared authoring conversation" ariaLabel="prepared authoring conversation"
runAction={runAction}
/> />
); );
@@ -99,19 +99,10 @@ describe("AssistantOperatorThread", () => {
expect(requestRevision).toHaveBeenCalledOnce(); expect(requestRevision).toHaveBeenCalledOnce();
}); });
it("renders a chat-owned run action", async () => { it("does not render a run action", () => {
const user = userEvent.setup(); render(<AssistantOperatorThread mode="dock" messages={[]} />);
const run = vi.fn();
render(
<AssistantOperatorThread
mode="dock"
messages={[]}
runAction={{ label: "Run prepared workflow", disabled: false, run }}
/>,
);
await user.click(screen.getByRole("button", { name: /run prepared workflow/i })); expect(screen.queryByRole("button", { name: /run prepared workflow/i })).not.toBeInTheDocument();
expect(run).toHaveBeenCalledOnce();
}); });
it("labels and opens the synchronized authoring phase group", () => { it("labels and opens the synchronized authoring phase group", () => {
@@ -23,7 +23,6 @@ import {
type AssistantOperatorThreadProps = { type AssistantOperatorThreadProps = {
readonly mode: "hidden" | "full" | "rail" | "dock"; readonly mode: "hidden" | "full" | "rail" | "dock";
readonly messages: ReadonlyArray<AgentMessage>; readonly messages: ReadonlyArray<AgentMessage>;
readonly runAction?: { readonly label: string; readonly disabled: boolean; readonly run: () => void } | undefined;
readonly scrollMode?: "active" | "start" | undefined; readonly scrollMode?: "active" | "start" | undefined;
readonly submitApproval?: (() => void) | undefined; readonly submitApproval?: (() => void) | undefined;
readonly requestRevision?: (() => void) | undefined; readonly requestRevision?: (() => void) | undefined;
@@ -250,7 +249,6 @@ const MessageBubble = ({
export const AssistantOperatorThread = ({ export const AssistantOperatorThread = ({
mode, mode,
messages, messages,
runAction,
scrollMode = "active", scrollMode = "active",
submitApproval, submitApproval,
requestRevision, requestRevision,
@@ -304,11 +302,6 @@ export const AssistantOperatorThread = ({
}); });
}, []); }, []);
const handleRun = useCallback(() => {
if (!runAction || runAction.disabled) return;
runAction.run();
}, [runAction]);
return ( return (
<section <section
className="assistant-operator-thread" className="assistant-operator-thread"
@@ -345,13 +338,6 @@ export const AssistantOperatorThread = ({
})} })}
</div> </div>
</div> </div>
{runAction ? (
<div className="assistant-operator-thread__action">
<button type="button" disabled={runAction.disabled} onClick={handleRun}>
{runAction.label}
</button>
</div>
) : null}
</section> </section>
); );
}; };
@@ -2505,16 +2505,6 @@
} }
} }
.assistant-operator-thread__action button {
width: 100%;
border: 1px solid var(--accent-cyan);
border-radius: 0.65rem;
background: color-mix(in oklch, var(--accent-cyan) 13%, var(--stage-surface));
color: var(--text-primary);
padding: 0.55rem 0.7rem;
font: 700 0.8rem/1 var(--font-interface);
}
.assistant-thread, .assistant-thread,
.assistant-thread__viewport { .assistant-thread__viewport {
min-height: 0; min-height: 0;
@@ -3287,13 +3277,6 @@
color: var(--authoring-accent); color: var(--authoring-accent);
} }
.agent-handoff-scene[data-presentation-surface="editorial"] .assistant-operator-thread__action button {
border-color: var(--authoring-accent);
border-radius: 0.2rem;
background: var(--authoring-accent);
color: var(--authoring-paper);
}
/* Keep the compact Scene 8 request surface centered inside the presentation canvas. */ /* Keep the compact Scene 8 request surface centered inside the presentation canvas. */
@media (max-width: 1100px) { @media (max-width: 1100px) {
.presentation-stage[data-scene-view="agent"] .presentation-stage__primary { .presentation-stage[data-scene-view="agent"] .presentation-stage__primary {