fix: keep demo workflow graph in frame
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { cleanup, render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { WorkflowGraphStage } from "./WorkflowGraphStage.js";
|
||||
import { presentationNodes, WorkflowGraphStage } from "./WorkflowGraphStage.js";
|
||||
|
||||
afterEach(() => cleanup());
|
||||
|
||||
@@ -58,4 +58,28 @@ describe("WorkflowGraphStage", () => {
|
||||
expect(connectors).toHaveLength(4);
|
||||
expect(connectors.filter((connector) => connector.dataset.active === "true")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("keeps all graph nodes inside the visible percentage frame", () => {
|
||||
for (const node of presentationNodes) {
|
||||
expect(node.x).toBeGreaterThanOrEqual(14);
|
||||
expect(node.x).toBeLessThanOrEqual(86);
|
||||
expect(node.y).toBeGreaterThanOrEqual(28);
|
||||
expect(node.y).toBeLessThanOrEqual(72);
|
||||
}
|
||||
});
|
||||
|
||||
it("renders compact run proof inside the graph", () => {
|
||||
render(
|
||||
<WorkflowGraphStage
|
||||
execution={{ completedNodeIds: ["read_docs"], currentNodeId: "build_report" }}
|
||||
selectedNodeId={null}
|
||||
selectNode={vi.fn()}
|
||||
proof={{ runId: "run_recorded_lda_report", traceLabel: "5 nodes", evidenceLabel: "JSON-RPC captured" }}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByLabelText("workflow graph proof")).toHaveTextContent("run_recorded_lda_report");
|
||||
expect(screen.getByLabelText("workflow graph proof")).toHaveTextContent("5 nodes");
|
||||
expect(screen.getByLabelText("workflow graph proof")).toHaveTextContent("JSON-RPC captured");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,11 +12,11 @@ export type PresentationNode = {
|
||||
};
|
||||
|
||||
export const presentationNodes: ReadonlyArray<PresentationNode> = [
|
||||
{ id: "read_docs", label: "Read documents", detail: "5 selected", kind: "node", x: 9, y: 57 },
|
||||
{ id: "build_report", label: "Build report", detail: "Markdown", kind: "node", x: 30, y: 35 },
|
||||
{ id: "review_issues", label: "Issue review", detail: "Typed interrupt", kind: "interrupt", x: 52, y: 57 },
|
||||
{ id: "create_issues", label: "Create issues", detail: "Selected only", kind: "node", x: 74, y: 35 },
|
||||
{ id: "end_completed", label: "Completed", detail: "Persisted run", kind: "end", x: 92, y: 57 },
|
||||
{ id: "read_docs", label: "Read documents", detail: "5 selected", kind: "node", x: 14, y: 58 },
|
||||
{ id: "build_report", label: "Build report", detail: "Markdown", kind: "node", x: 34, y: 36 },
|
||||
{ id: "review_issues", label: "Issue review", detail: "Typed interrupt", kind: "interrupt", x: 52, y: 58 },
|
||||
{ id: "create_issues", label: "Create issues", detail: "Selected only", kind: "node", x: 70, y: 36 },
|
||||
{ id: "end_completed", label: "Completed", detail: "Persisted run", kind: "end", x: 86, y: 58 },
|
||||
];
|
||||
|
||||
type PresentationEdge = readonly [from: string, to: string];
|
||||
@@ -30,10 +30,17 @@ const presentationEdges: ReadonlyArray<PresentationEdge> = [
|
||||
|
||||
type NodeExecutionState = "completed" | "current" | "future";
|
||||
|
||||
export type WorkflowGraphProof = {
|
||||
readonly runId: string | null;
|
||||
readonly traceLabel: string;
|
||||
readonly evidenceLabel: string;
|
||||
};
|
||||
|
||||
type WorkflowGraphStageProps = {
|
||||
readonly execution: GraphExecutionPresentation;
|
||||
readonly selectedNodeId: string | null;
|
||||
readonly selectNode: (nodeId: string) => void;
|
||||
readonly proof?: WorkflowGraphProof;
|
||||
};
|
||||
|
||||
const executionStateForNode = (
|
||||
@@ -55,6 +62,7 @@ export const WorkflowGraphStage = ({
|
||||
execution,
|
||||
selectedNodeId,
|
||||
selectNode,
|
||||
proof,
|
||||
}: WorkflowGraphStageProps) => {
|
||||
const markerPrefix = useId().replaceAll(":", "");
|
||||
const arrowMarkerId = `${markerPrefix}-workflow-arrow`;
|
||||
@@ -68,6 +76,14 @@ export const WorkflowGraphStage = ({
|
||||
<span><i data-state="interrupt" />Human boundary</span>
|
||||
</div>
|
||||
|
||||
{proof && (
|
||||
<div className="workflow-graph-stage__proof" aria-label="workflow graph proof">
|
||||
<span><b>Run</b><code>{proof.runId ?? "run unavailable"}</code></span>
|
||||
<span><b>Trace</b>{proof.traceLabel}</span>
|
||||
<span><b>Evidence</b>{proof.evidenceLabel}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<svg className="workflow-graph-stage__connectors" aria-hidden="true">
|
||||
<defs>
|
||||
<marker className="workflow-graph-stage__arrow-marker" id={arrowMarkerId} markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
|
||||
|
||||
@@ -400,16 +400,53 @@
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.workflow-graph-stage__proof {
|
||||
position: absolute;
|
||||
z-index: 4;
|
||||
left: 0.8rem;
|
||||
bottom: 0.7rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.55rem;
|
||||
max-width: calc(100% - 1.6rem);
|
||||
color: var(--text-secondary);
|
||||
font: 600 0.58rem/1.2 var(--font-mono, monospace);
|
||||
}
|
||||
|
||||
.workflow-graph-stage__proof span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
border: 1px solid oklch(0.36 0.04 250 / 0.7);
|
||||
border-radius: 999px;
|
||||
padding: 0.18rem 0.45rem;
|
||||
background: oklch(0.09 0.018 250 / 0.78);
|
||||
}
|
||||
|
||||
.workflow-graph-stage__proof b {
|
||||
color: var(--accent-cyan);
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.workflow-graph-stage__proof code {
|
||||
max-width: 14rem;
|
||||
overflow: hidden;
|
||||
color: var(--text-primary);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.presentation-route .workflow-graph-stage__node {
|
||||
position: relative;
|
||||
display: grid;
|
||||
gap: 0.25rem;
|
||||
width: clamp(7.2rem, 11vw, 9.5rem);
|
||||
min-height: 4.35rem;
|
||||
width: clamp(7rem, 10vw, 8.75rem);
|
||||
min-height: 4.15rem;
|
||||
transform: none;
|
||||
border: 1px solid var(--stage-line);
|
||||
border-radius: 0.7rem;
|
||||
padding: 0.65rem 0.75rem;
|
||||
padding: 0.58rem 0.68rem;
|
||||
background: oklch(0.15 0.025 250 / 0.96);
|
||||
color: var(--text-primary);
|
||||
text-align: left;
|
||||
|
||||
Reference in New Issue
Block a user