feat: make interrupt approval the demo hero
This commit is contained in:
@@ -105,6 +105,22 @@ describe("DemoWorkflowScene", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("makes the Scene 10 approval contract the primary visual", () => {
|
||||
renderBeat("approval", "interrupt-evidence");
|
||||
|
||||
const stage = screen.getByLabelText("demo workflow stage");
|
||||
expect(stage).toHaveAttribute("data-demo-layout", "approval");
|
||||
expect(screen.getByLabelText("typed interrupt contract")).toHaveAttribute("data-hero", "true");
|
||||
expect(screen.getByLabelText("workflow graph")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("marks trace beat as evidence layout", () => {
|
||||
renderBeat("trace", "interrupt-evidence");
|
||||
|
||||
expect(screen.getByLabelText("demo workflow stage")).toHaveAttribute("data-demo-layout", "evidence");
|
||||
expect(screen.getByLabelText("workflow.runs.trace operation")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders resume and trace operations as expanded evidence summaries", () => {
|
||||
const { unmount } = renderBeat("resume", "interrupt-evidence");
|
||||
expect(screen.getByLabelText("workflow.runs.resume operation")).toBeInTheDocument();
|
||||
|
||||
@@ -20,6 +20,16 @@ type DemoWorkflowSceneProps = {
|
||||
readonly openEvidence: () => void;
|
||||
};
|
||||
|
||||
type DemoWorkflowLayout = "operation" | "graph" | "interrupt" | "approval" | "evidence";
|
||||
|
||||
const layoutForBeat = (beatId: string): DemoWorkflowLayout => {
|
||||
if (beatId === "operation" || beatId === "resume") return "operation";
|
||||
if (beatId === "interrupt") return "interrupt";
|
||||
if (beatId === "approval") return "approval";
|
||||
if (beatId === "trace" || beatId === "output") return "evidence";
|
||||
return "graph";
|
||||
};
|
||||
|
||||
const operationStageByBeat: Readonly<Record<string, DemoEvent["stage"] | undefined>> = {
|
||||
operation: "run_start",
|
||||
resume: "run_resume",
|
||||
@@ -44,6 +54,7 @@ export const DemoWorkflowScene = ({
|
||||
const currentEvent = currentStage ? findEvent(demo, currentStage) : null;
|
||||
const contract = runStart ? projectInterruptContract(runStart) : null;
|
||||
const execution = graphExecutionForBeat(beat.id);
|
||||
const layout = layoutForBeat(beat.id);
|
||||
|
||||
const showExpandedOperation = beat.id === "operation" || beat.id === "resume" || beat.id === "trace";
|
||||
const showGraph = beat.id === "graph" || beat.id === "interrupt" || beat.id === "approval" || beat.id === "output";
|
||||
@@ -60,7 +71,7 @@ export const DemoWorkflowScene = ({
|
||||
<p>{beat.caption}</p>
|
||||
</StageCaption>
|
||||
|
||||
<div className="demo-workflow-stage" data-beat={beat.id}>
|
||||
<div className="demo-workflow-stage" data-beat={beat.id} data-demo-layout={layout} aria-label="demo workflow stage">
|
||||
{showExpandedOperation && currentEvent && (
|
||||
<OperationBlock
|
||||
event={currentEvent}
|
||||
@@ -85,7 +96,11 @@ export const DemoWorkflowScene = ({
|
||||
selectNode={selectNode}
|
||||
/>
|
||||
{contractMode && contract && (
|
||||
<InterruptContractPreview contract={contract} mode={contractMode} />
|
||||
<InterruptContractPreview
|
||||
contract={contract}
|
||||
mode={contractMode}
|
||||
hero={layout === "approval"}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -5,15 +5,18 @@ import { formatJson } from "./format.js";
|
||||
type InterruptContractPreviewProps = {
|
||||
readonly contract: InterruptContractPresentation;
|
||||
readonly mode: "preview" | "approval";
|
||||
readonly hero?: boolean;
|
||||
};
|
||||
|
||||
export const InterruptContractPreview = ({
|
||||
contract,
|
||||
mode,
|
||||
hero = false,
|
||||
}: InterruptContractPreviewProps) => (
|
||||
<m.aside
|
||||
className="interrupt-contract-preview"
|
||||
data-mode={mode}
|
||||
data-hero={hero ? "true" : "false"}
|
||||
initial={{ opacity: 0, x: 10 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.22, ease: [0.16, 1, 0.3, 1] }}
|
||||
|
||||
@@ -482,6 +482,42 @@
|
||||
oklch(0.135 0.025 250);
|
||||
}
|
||||
|
||||
.demo-workflow-stage[data-demo-layout="approval"] .demo-workflow-stage__graph,
|
||||
.demo-workflow-stage[data-demo-layout="interrupt"] .demo-workflow-stage__graph {
|
||||
grid-template-columns: minmax(19rem, 34%) minmax(0, 1fr);
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.demo-workflow-stage[data-demo-layout="approval"] .interrupt-contract-preview,
|
||||
.demo-workflow-stage[data-demo-layout="interrupt"] .interrupt-contract-preview {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.demo-workflow-stage[data-demo-layout="approval"] .workflow-graph-stage,
|
||||
.demo-workflow-stage[data-demo-layout="interrupt"] .workflow-graph-stage {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.interrupt-contract-preview[data-hero="true"] {
|
||||
border-color: color-mix(in oklch, var(--accent-amber) 72%, white);
|
||||
background:
|
||||
linear-gradient(180deg, color-mix(in oklch, var(--accent-amber) 14%, transparent), transparent 48%),
|
||||
color-mix(in oklch, var(--stage-surface) 92%, black);
|
||||
}
|
||||
|
||||
.interrupt-contract-preview[data-hero="true"] h2 {
|
||||
font-size: clamp(1.45rem, 2.3vw, 2rem);
|
||||
}
|
||||
|
||||
@media (max-width: 1050px) {
|
||||
.demo-workflow-stage[data-demo-layout="approval"] .demo-workflow-stage__graph,
|
||||
.demo-workflow-stage[data-demo-layout="interrupt"] .demo-workflow-stage__graph {
|
||||
grid-template-columns: minmax(17rem, 40%) minmax(0, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.interrupt-contract-preview[data-mode="approval"] {
|
||||
box-shadow: inset 0 3px 0 var(--accent-amber);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user