feat: compose demo climax scenes
This commit is contained in:
@@ -131,4 +131,35 @@ describe("DemoWorkflowScene", () => {
|
|||||||
renderBeat("trace", "interrupt-evidence");
|
renderBeat("trace", "interrupt-evidence");
|
||||||
expect(screen.getByLabelText("workflow.runs.trace operation")).toBeInTheDocument();
|
expect(screen.getByLabelText("workflow.runs.trace operation")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("shows the continuity rail across Scene 9 operation, graph, and interrupt beats", () => {
|
||||||
|
const { unmount } = renderBeat("operation");
|
||||||
|
expect(screen.getByLabelText("demo continuity")).toHaveTextContent("Request becomes a workflow run");
|
||||||
|
expect(screen.getByLabelText("demo continuity")).toHaveTextContent("workflow.runs.start");
|
||||||
|
unmount();
|
||||||
|
|
||||||
|
const graph = renderBeat("graph");
|
||||||
|
expect(screen.getByLabelText("demo continuity")).toHaveTextContent("The product owns the reusable workflow shape");
|
||||||
|
graph.unmount();
|
||||||
|
|
||||||
|
renderBeat("interrupt");
|
||||||
|
expect(screen.getByLabelText("demo continuity")).toHaveTextContent("Execution stops at a declared human boundary");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("adds outcome proof to approval, resume, output, and trace beats", () => {
|
||||||
|
const approval = renderBeat("approval", "interrupt-evidence");
|
||||||
|
expect(screen.getByLabelText("demo outcome proof")).toHaveTextContent("schema-backed");
|
||||||
|
approval.unmount();
|
||||||
|
|
||||||
|
const resume = renderBeat("resume", "interrupt-evidence");
|
||||||
|
expect(screen.getByLabelText("demo outcome proof")).toHaveTextContent("Same persisted run");
|
||||||
|
resume.unmount();
|
||||||
|
|
||||||
|
const output = renderBeat("output", "interrupt-evidence");
|
||||||
|
expect(screen.getByLabelText("demo outcome proof")).toHaveTextContent("Report markdown");
|
||||||
|
output.unmount();
|
||||||
|
|
||||||
|
renderBeat("trace", "interrupt-evidence");
|
||||||
|
expect(screen.getByLabelText("demo outcome proof")).toHaveTextContent("Trace frames");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import type { DemoEvent } from "../demo/timeline/models.js";
|
import type { DemoEvent } from "../demo/timeline/models.js";
|
||||||
import type { DemoTimelineController } from "../demo/useDemoTimeline.js";
|
import type { DemoTimelineController } from "../demo/useDemoTimeline.js";
|
||||||
import {
|
import {
|
||||||
|
demoBeatLensForBeat,
|
||||||
graphExecutionForBeat,
|
graphExecutionForBeat,
|
||||||
projectInterruptContract,
|
projectInterruptContract,
|
||||||
|
projectOperationPresentation,
|
||||||
} from "./demo-workflow-model.js";
|
} from "./demo-workflow-model.js";
|
||||||
|
import { DemoContinuityRail } from "./DemoContinuityRail.js";
|
||||||
|
import { DemoOutcomePanel } from "./DemoOutcomePanel.js";
|
||||||
import { InterruptContractPreview } from "./InterruptContractPreview.js";
|
import { InterruptContractPreview } from "./InterruptContractPreview.js";
|
||||||
import { NodeSpotlight } from "./NodeSpotlight.js";
|
import { NodeSpotlight } from "./NodeSpotlight.js";
|
||||||
import { OperationBlock } from "./OperationBlock.js";
|
import { OperationBlock } from "./OperationBlock.js";
|
||||||
@@ -56,6 +60,10 @@ export const DemoWorkflowScene = ({
|
|||||||
const execution = graphExecutionForBeat(beat.id);
|
const execution = graphExecutionForBeat(beat.id);
|
||||||
const layout = layoutForBeat(beat.id);
|
const layout = layoutForBeat(beat.id);
|
||||||
|
|
||||||
|
const lens = demoBeatLensForBeat(beat.id);
|
||||||
|
const currentOperation = currentEvent ? projectOperationPresentation(currentEvent) : null;
|
||||||
|
const showOutcomePanel = beat.id === "approval" || beat.id === "resume" || beat.id === "output" || beat.id === "trace";
|
||||||
|
|
||||||
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";
|
||||||
const showReceipt = showGraph;
|
const showReceipt = showGraph;
|
||||||
@@ -71,6 +79,8 @@ export const DemoWorkflowScene = ({
|
|||||||
<p>{beat.caption}</p>
|
<p>{beat.caption}</p>
|
||||||
</StageCaption>
|
</StageCaption>
|
||||||
|
|
||||||
|
<DemoContinuityRail lens={lens} />
|
||||||
|
|
||||||
<div className="demo-workflow-stage" data-beat={beat.id} data-demo-layout={layout} aria-label="demo workflow stage">
|
<div className="demo-workflow-stage" data-beat={beat.id} data-demo-layout={layout} aria-label="demo workflow stage">
|
||||||
{showExpandedOperation && currentEvent && (
|
{showExpandedOperation && currentEvent && (
|
||||||
<OperationBlock
|
<OperationBlock
|
||||||
@@ -105,6 +115,15 @@ export const DemoWorkflowScene = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{showOutcomePanel && (
|
||||||
|
<DemoOutcomePanel
|
||||||
|
beatId={beat.id}
|
||||||
|
lens={lens}
|
||||||
|
operation={currentOperation}
|
||||||
|
contract={contract}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{showExpandedOperation && !currentEvent && (
|
{showExpandedOperation && !currentEvent && (
|
||||||
<div className="demo-workflow-stage__pending" role="status">
|
<div className="demo-workflow-stage__pending" role="status">
|
||||||
Replay operation is not available yet.
|
Replay operation is not available yet.
|
||||||
|
|||||||
@@ -72,7 +72,8 @@
|
|||||||
|
|
||||||
.demo-workflow-stage {
|
.demo-workflow-stage {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -629,6 +630,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.demo-workflow-stage:has(.demo-outcome-panel) {
|
||||||
|
grid-template-columns: minmax(0, 1fr) minmax(15rem, 23%);
|
||||||
|
gap: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-workflow-stage:has(.demo-outcome-panel) .demo-workflow-stage__graph {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-workflow-stage[data-demo-layout="approval"] {
|
||||||
|
grid-template-columns: minmax(0, 1.2fr) minmax(17rem, 0.55fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-workflow-stage[data-demo-layout="approval"] .demo-workflow-stage__graph {
|
||||||
|
grid-template-columns: minmax(0, 1fr) minmax(17rem, 32%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-workflow-stage[data-demo-layout="evidence"] {
|
||||||
|
grid-template-columns: minmax(0, 1fr) minmax(16rem, 24%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@container presentation-canvas (max-width: 1050px) {
|
||||||
|
.demo-workflow-stage:has(.demo-outcome-panel),
|
||||||
|
.demo-workflow-stage[data-demo-layout="approval"],
|
||||||
|
.demo-workflow-stage[data-demo-layout="evidence"] {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-height: 760px) {
|
@media (max-height: 760px) {
|
||||||
.presentation-stage[data-scene-view="demo"] .presentation-stage__primary {
|
.presentation-stage[data-scene-view="demo"] .presentation-stage__primary {
|
||||||
gap: 0.55rem;
|
gap: 0.55rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user