Owner's three rendering refinements (plan-graph round, 2026-09-26): 1. Reading font: the diagram root sets font-family var(--font-prose) — node titles render in the reading font instead of the inherited mono UI font. Deliberately NOT the font-prose utility: @theme inline bakes the literal into utilities (the --font-title trap), which would freeze the graph at Source Sans 3 and ignore the Appearance reading-font override. The runtime var rides it (live-verified: Georgia override propagates, null resets to Source Sans 3). 2. Thinner lines: edges + box outlines 1.5 → 1 (the app's standard border weight). The arrowheads scale by the same 2/3 (HEAD_H 16 → 10.67; the width, the notch overhang, and the DOCK_GAP all derive from the head scale), so the head:shaft LaTeX proportion is preserved exactly. 3. Arrow color: white only when BOTH ends are white — an edge with an upcoming origin OR upcoming terminal stays gray (line + head). Supersedes round 6's target-only rule: arrows leaving a gray box no longer paint white into covered territory. Live-verified on :3001 (native-format fixture: plan_init + answered quizzes; popup): font default + override, stroke-width 1 on all rects and edges, 5-edge graph with exactly one both-white edge white and both upcoming→visited cross edges gray.
78 lines
3.0 KiB
TypeScript
78 lines
3.0 KiB
TypeScript
import { renderToStaticMarkup } from "react-dom/server";
|
|
import { describe, it } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { PlanMarginalia } from "../components/plan-window.tsx";
|
|
import { blockLayout, type PopupMessage } from "../components/plan-block.tsx";
|
|
import type { PlanTree } from "../src/core/contracts.ts";
|
|
|
|
// The plan marginalia: the bare diagram in the dock band's right flank —
|
|
// full plan (padded viewBox, meet-fit), statuses + glow live by derivation,
|
|
// nothing until a tree exists. Positioning and fade live in DockOverlay.
|
|
|
|
const CHAIN: PlanTree = {
|
|
nodes: [
|
|
{ id: "a", title: "Alpha" },
|
|
{ id: "b", title: "Beta" },
|
|
{ id: "c", title: "Gamma" },
|
|
],
|
|
edges: [
|
|
{ from: "a", to: "b" },
|
|
{ from: "b", to: "c" },
|
|
],
|
|
version: 1,
|
|
};
|
|
|
|
/** A transcript with a successful plan_init (carrying `tree`) and
|
|
* optionally a tagged quiz call. */
|
|
function transcript(tree: PlanTree, taggedNodeId?: string): PopupMessage[] {
|
|
return [
|
|
{ id: "m1", role: "user", parts: [] } as never,
|
|
{
|
|
id: "m2",
|
|
role: "assistant",
|
|
parts: [{ type: "tool-plan_init", state: "output-available", output: { ok: true, tree } }],
|
|
} as never,
|
|
...(taggedNodeId
|
|
? [
|
|
{
|
|
id: "m3",
|
|
role: "assistant",
|
|
parts: [{ type: "tool-quiz", state: "input-available", input: { nodeId: taggedNodeId } }],
|
|
} as never,
|
|
]
|
|
: []),
|
|
];
|
|
}
|
|
|
|
describe("PlanMarginalia render", () => {
|
|
it("renders nothing until a tree exists", () => {
|
|
assert.equal(renderToStaticMarkup(<PlanMarginalia messages={[]} />), "");
|
|
});
|
|
|
|
it("renders the FULL plan (padded full-extent viewBox), not a windowed crop", () => {
|
|
const html = renderToStaticMarkup(<PlanMarginalia messages={transcript(CHAIN)} />);
|
|
// The viewBox covers the whole layout plus the glow-clearing pad —
|
|
// never the retired window's 2:1 crop.
|
|
const layout = blockLayout(CHAIN.nodes, CHAIN.edges);
|
|
const pad = 24; // MARGIN_PAD: GRAPH_PAD + 16 (glow halo clearance)
|
|
const vb = `viewBox="${-pad} ${-pad} ${layout.width + 2 * pad} ${layout.height + 2 * pad}"`;
|
|
assert.ok(html.includes(vb), `expected ${vb} in the svg`);
|
|
assert.ok(!/viewBox="-?\d+(\.\d+)? -?\d+(\.\d+)? 360 180"/.test(html), "the windowed viewBox is retired");
|
|
// Meet-fit mode: the svg fills the band box (h-full w-full), letting
|
|
// preserveAspectRatio contain the diagram in the flank.
|
|
assert.ok(html.includes('font-family:var(--font-prose)'), "fill mode classes");
|
|
});
|
|
|
|
it("glows the current node and dims the not-yet-visited (live statuses)", () => {
|
|
const html = renderToStaticMarkup(<PlanMarginalia messages={transcript(CHAIN, "b")} />);
|
|
// The yellow blurred halo marks the tagged check's node…
|
|
assert.ok(html.includes("fill-yellow-400/25"), "glow on the current node");
|
|
assert.ok(html.includes('data-testid="plan-node-b" data-status="current"'), "b is current");
|
|
// …and arrows into upcoming nodes carry the dimmed head.
|
|
assert.ok(
|
|
html.includes("url(#:r0:-dim)") || /url\(#\S*-dim\)/.test(html),
|
|
"dimmed arrowhead into the upcoming node",
|
|
);
|
|
});
|
|
});
|