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", () => {
|
it("renders resume and trace operations as expanded evidence summaries", () => {
|
||||||
const { unmount } = renderBeat("resume", "interrupt-evidence");
|
const { unmount } = renderBeat("resume", "interrupt-evidence");
|
||||||
expect(screen.getByLabelText("workflow.runs.resume operation")).toBeInTheDocument();
|
expect(screen.getByLabelText("workflow.runs.resume operation")).toBeInTheDocument();
|
||||||
|
|||||||
@@ -20,6 +20,16 @@ type DemoWorkflowSceneProps = {
|
|||||||
readonly openEvidence: () => void;
|
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>> = {
|
const operationStageByBeat: Readonly<Record<string, DemoEvent["stage"] | undefined>> = {
|
||||||
operation: "run_start",
|
operation: "run_start",
|
||||||
resume: "run_resume",
|
resume: "run_resume",
|
||||||
@@ -44,6 +54,7 @@ export const DemoWorkflowScene = ({
|
|||||||
const currentEvent = currentStage ? findEvent(demo, currentStage) : null;
|
const currentEvent = currentStage ? findEvent(demo, currentStage) : null;
|
||||||
const contract = runStart ? projectInterruptContract(runStart) : null;
|
const contract = runStart ? projectInterruptContract(runStart) : null;
|
||||||
const execution = graphExecutionForBeat(beat.id);
|
const execution = graphExecutionForBeat(beat.id);
|
||||||
|
const layout = layoutForBeat(beat.id);
|
||||||
|
|
||||||
const showExpandedOperation = beat.id === "operation" || beat.id === "resume" || beat.id === "trace";
|
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";
|
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>
|
<p>{beat.caption}</p>
|
||||||
</StageCaption>
|
</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 && (
|
{showExpandedOperation && currentEvent && (
|
||||||
<OperationBlock
|
<OperationBlock
|
||||||
event={currentEvent}
|
event={currentEvent}
|
||||||
@@ -85,7 +96,11 @@ export const DemoWorkflowScene = ({
|
|||||||
selectNode={selectNode}
|
selectNode={selectNode}
|
||||||
/>
|
/>
|
||||||
{contractMode && contract && (
|
{contractMode && contract && (
|
||||||
<InterruptContractPreview contract={contract} mode={contractMode} />
|
<InterruptContractPreview
|
||||||
|
contract={contract}
|
||||||
|
mode={contractMode}
|
||||||
|
hero={layout === "approval"}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -5,15 +5,18 @@ import { formatJson } from "./format.js";
|
|||||||
type InterruptContractPreviewProps = {
|
type InterruptContractPreviewProps = {
|
||||||
readonly contract: InterruptContractPresentation;
|
readonly contract: InterruptContractPresentation;
|
||||||
readonly mode: "preview" | "approval";
|
readonly mode: "preview" | "approval";
|
||||||
|
readonly hero?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const InterruptContractPreview = ({
|
export const InterruptContractPreview = ({
|
||||||
contract,
|
contract,
|
||||||
mode,
|
mode,
|
||||||
|
hero = false,
|
||||||
}: InterruptContractPreviewProps) => (
|
}: InterruptContractPreviewProps) => (
|
||||||
<m.aside
|
<m.aside
|
||||||
className="interrupt-contract-preview"
|
className="interrupt-contract-preview"
|
||||||
data-mode={mode}
|
data-mode={mode}
|
||||||
|
data-hero={hero ? "true" : "false"}
|
||||||
initial={{ opacity: 0, x: 10 }}
|
initial={{ opacity: 0, x: 10 }}
|
||||||
animate={{ opacity: 1, x: 0 }}
|
animate={{ opacity: 1, x: 0 }}
|
||||||
transition={{ duration: 0.22, ease: [0.16, 1, 0.3, 1] }}
|
transition={{ duration: 0.22, ease: [0.16, 1, 0.3, 1] }}
|
||||||
|
|||||||
@@ -482,6 +482,42 @@
|
|||||||
oklch(0.135 0.025 250);
|
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"] {
|
.interrupt-contract-preview[data-mode="approval"] {
|
||||||
box-shadow: inset 0 3px 0 var(--accent-amber);
|
box-shadow: inset 0 3px 0 var(--accent-amber);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user