@devfellowship/components 3.3.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -244,6 +244,7 @@ __export(src_exports, {
244
244
  ResizableHandle: () => ResizableHandle,
245
245
  ResizablePanel: () => ResizablePanel,
246
246
  ResizablePanelGroup: () => ResizablePanelGroup,
247
+ Roadmap: () => Roadmap,
247
248
  ScrollArea: () => ScrollArea,
248
249
  ScrollBar: () => ScrollBar,
249
250
  Select: () => Select,
@@ -9103,18 +9104,287 @@ function firstFreeRow(taken, columns, from) {
9103
9104
  }
9104
9105
  return row;
9105
9106
  }
9107
+ var pointText = (p) => `${p.x} ${p.y}`;
9108
+ var center = (r) => ({ x: (r.left + r.right) / 2, y: (r.top + r.bottom) / 2 });
9109
+ function segmentHits(a, b, r) {
9110
+ return a.x === b.x ? a.x > r.left && a.x < r.right && Math.max(a.y, b.y) > r.top && Math.min(a.y, b.y) < r.bottom : a.y > r.top && a.y < r.bottom && Math.max(a.x, b.x) > r.left && Math.min(a.x, b.x) < r.right;
9111
+ }
9112
+ function routeEdge(source, target, edge, obstacles = []) {
9113
+ const a = center(source), b = center(target);
9114
+ const sameColumn = Math.abs(a.x - b.x) < 1;
9115
+ const vertical = sameColumn || source.left < target.right && target.left < source.right;
9116
+ const down = b.y >= a.y, right = b.x >= a.x;
9117
+ const start = vertical ? { x: a.x, y: down ? source.bottom : source.top } : { x: right ? source.right : source.left, y: a.y };
9118
+ const end = vertical ? { x: b.x, y: down ? target.top : target.bottom } : { x: right ? target.left : target.right, y: b.y };
9119
+ const route = edge.route ?? "auto";
9120
+ if (route === "straight" || route === "auto" && sameColumn) return { start, end, d: `M ${pointText(start)} L ${pointText(end)}` };
9121
+ if (route !== "elbow") {
9122
+ const mid = vertical ? (start.y + end.y) / 2 : (start.x + end.x) / 2;
9123
+ const c1 = vertical ? { x: start.x, y: mid } : { x: mid, y: start.y };
9124
+ const c2 = vertical ? { x: end.x, y: mid } : { x: mid, y: end.y };
9125
+ return { start, end, d: `M ${pointText(start)} C ${pointText(c1)} ${pointText(c2)} ${pointText(end)}` };
9126
+ }
9127
+ const boxes = [.../* @__PURE__ */ new Set([source, target, ...obstacles])];
9128
+ const clearance = 4;
9129
+ const ys = [...new Set(boxes.flatMap((r) => [r.top - clearance, r.bottom + clearance]))];
9130
+ let best, bestLength = Infinity;
9131
+ for (const sx of [source.left, source.right]) for (const tx of [target.left, target.right]) {
9132
+ const s = { x: sx, y: a.y }, t = { x: tx, y: b.y };
9133
+ const gx = sx + (sx === source.left ? -clearance : clearance);
9134
+ const hx = tx + (tx === target.left ? -clearance : clearance);
9135
+ for (const y of [a.y, b.y, ...ys]) {
9136
+ const points2 = [s, { x: gx, y: a.y }, { x: gx, y }, { x: hx, y }, { x: hx, y: b.y }, t];
9137
+ const length = points2.slice(1).reduce((sum, p, i) => sum + Math.abs(p.x - points2[i].x) + Math.abs(p.y - points2[i].y), 0);
9138
+ if (length >= bestLength || points2.slice(1).some((p, i) => boxes.some((r) => segmentHits(points2[i], p, r)))) continue;
9139
+ best = points2;
9140
+ bestLength = length;
9141
+ }
9142
+ }
9143
+ if (!best) return { start, end, d: "" };
9144
+ const points = best.filter((p, i) => i === 0 || p.x !== best[i - 1].x || p.y !== best[i - 1].y);
9145
+ return { start: points[0], end: points[points.length - 1], points, d: points.map((p, i) => `${i ? "L" : "M"} ${pointText(p)}`).join(" ") };
9146
+ }
9106
9147
 
9107
- // src/hooks/use-iframe-auth.ts
9148
+ // src/components/organisms/roadmap/Roadmap.tsx
9149
+ var import_react8 = require("react");
9150
+
9151
+ // src/components/organisms/roadmap/RoadmapNode.tsx
9152
+ var import_class_variance_authority14 = require("class-variance-authority");
9153
+
9154
+ // src/components/organisms/roadmap/RoadmapLegend.tsx
9155
+ var import_lucide_react22 = require("lucide-react");
9156
+
9157
+ // src/components/organisms/roadmap/appearance.ts
9158
+ function toneStyle(tone) {
9159
+ return {
9160
+ "--c-roadmap-node-bg": `var(--c-roadmap-${tone}-bg)`,
9161
+ "--c-roadmap-node-fg": `var(--c-roadmap-${tone}-fg)`
9162
+ };
9163
+ }
9164
+ var focusClass = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--c-roadmap-focus)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--c-roadmap-surface)]";
9165
+
9166
+ // src/components/organisms/roadmap/RoadmapLegend.tsx
9167
+ var import_jsx_runtime65 = require("react/jsx-runtime");
9168
+ var glyphs = { check: import_lucide_react22.Check, globe: import_lucide_react22.Globe, lock: import_lucide_react22.Lock, star: import_lucide_react22.Star, "book-open": import_lucide_react22.BookOpen, play: import_lucide_react22.Play, circle: import_lucide_react22.Circle, clock: import_lucide_react22.Clock, link: import_lucide_react22.Link, "external-link": import_lucide_react22.ExternalLink };
9169
+ function RoadmapBadge({ name = "check", tone = "neutral", label, className = "" }) {
9170
+ const Glyph = glyphs[name] ?? import_lucide_react22.CircleHelp;
9171
+ return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("span", { "aria-label": label, "aria-hidden": label ? void 0 : true, style: toneStyle(tone), className: `inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full border-2 border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)] text-[var(--c-roadmap-node-fg)] ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Glyph, { size: 12, "aria-hidden": "true" }) });
9172
+ }
9173
+ function RoadmapLegendView({ legend, testIdPrefix = "roadmap" }) {
9174
+ return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("aside", { "data-testid": `${testIdPrefix}-legend`, "aria-label": "Legend", className: "min-w-0 rounded-[var(--radius)] border-2 border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-neutral-bg)] p-3 text-[var(--c-roadmap-neutral-fg)]", children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("ul", { className: "m-0 flex list-none flex-wrap gap-3 p-0", children: legend.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("li", { className: "flex min-w-0 items-center gap-2 text-xs leading-normal", children: [
9175
+ /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(RoadmapBadge, { name: entry.icon, tone: entry.tone }),
9176
+ /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("span", { className: "[overflow-wrap:anywhere]", children: entry.label })
9177
+ ] }, entry.id)) }) });
9178
+ }
9179
+
9180
+ // src/components/organisms/roadmap/RoadmapNode.tsx
9181
+ var import_jsx_runtime66 = require("react/jsx-runtime");
9182
+ var nodeVariants = (0, import_class_variance_authority14.cva)("relative flex min-w-0 flex-col justify-center rounded-[var(--radius)] border-2 px-2 py-3 text-center text-xs leading-normal md:px-4 md:text-base text-[var(--c-roadmap-node-fg)]", {
9183
+ variants: {
9184
+ kind: { topic: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", subtopic: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", button: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", label: "border-transparent bg-transparent", title: "border-transparent bg-transparent font-semibold md:text-[28px]", paragraph: "text-left border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", legend: "border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]" },
9185
+ state: { todo: "", done: "[--c-roadmap-node-bg:var(--c-roadmap-done-bg)] [--c-roadmap-node-fg:var(--c-roadmap-done-fg)]", learning: "[--c-roadmap-node-bg:var(--c-roadmap-learning-bg)] [--c-roadmap-node-fg:var(--c-roadmap-learning-fg)]", skipped: "[--c-roadmap-node-bg:var(--c-roadmap-skipped-bg)] [--c-roadmap-node-fg:var(--c-roadmap-skipped-fg)]", locked: "opacity-60" }
9186
+ }
9187
+ });
9188
+ function RoadmapNodeView({ node, state, badge, testIdPrefix, onNodeClick, onAction }) {
9189
+ const locked = state === "locked";
9190
+ const nodeTone = resolveNodeTone(node);
9191
+ const label = /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { "data-testid": `${testIdPrefix}-node-label`, className: `[overflow-wrap:anywhere] ${state === "done" ? "line-through" : state === "learning" ? "underline" : ""}`, children: node.label });
9192
+ const interactiveClass = `block w-full min-w-0 text-inherit ${focusClass}`;
9193
+ const wholeBox = !locked && !node.action && !node.links?.length;
9194
+ const Box = wholeBox && node.href ? "a" : wholeBox && onNodeClick ? "button" : "div";
9195
+ const primary = Box !== "div" ? label : node.href && !locked ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("a", { href: node.href, className: interactiveClass, onClick: () => onNodeClick?.(node), children: label }) : onNodeClick && !locked ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("button", { type: "button", className: interactiveClass, onClick: () => onNodeClick(node), children: label }) : label;
9196
+ const icon = node.icon ?? (badge ? { name: badge.icon ?? "check", side: "right", tone: badge.tone } : void 0);
9197
+ return /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("div", { style: toneStyle(nodeTone), className: "min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(Box, { style: { borderColor: node.kind === "title" || node.kind === "label" ? "transparent" : "var(--c-roadmap-border)" }, "data-roadmap-node-box": node.id, href: Box === "a" ? node.href : void 0, type: Box === "button" ? "button" : void 0, onClick: Box !== "div" ? () => onNodeClick?.(node) : void 0, className: `${nodeVariants({ kind: node.kind, state })} w-full ${Box !== "div" ? focusClass : ""}`, "aria-disabled": locked || void 0, title: node.kind === "topic" || node.kind === "subtopic" ? node.description : void 0, children: [
9198
+ icon && /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(RoadmapBadge, { name: icon.name, tone: icon.tone ?? nodeTone, label: badge?.label ?? icon.name, className: `absolute top-1/2 -translate-y-1/2 ${icon.side === "left" ? "left-0 -translate-x-1/2" : "right-0 translate-x-1/2"}` }),
9199
+ primary,
9200
+ node.description && node.kind === "paragraph" && /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("p", { className: "mt-2 [overflow-wrap:anywhere]", children: node.description }),
9201
+ node.links && /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("ul", { className: "mt-2 list-none space-y-2 p-0", children: node.links.map((link, index) => /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("li", { children: locked ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { children: link.label }) : /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("a", { href: link.href, className: `underline [overflow-wrap:anywhere] ${focusClass}`, onClick: (e) => e.stopPropagation(), children: link.label }) }, index)) }),
9202
+ node.action && /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("div", { style: toneStyle(node.action.tone ?? "primary"), className: "mt-3 min-w-0", children: node.action.href && !locked ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("a", { href: node.action.href, className: `inline-block rounded px-2 py-2 bg-[var(--c-roadmap-node-bg)] text-[var(--c-roadmap-node-fg)] [overflow-wrap:anywhere] ${focusClass}`, children: node.action.label }) : /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("button", { type: "button", disabled: locked, className: `max-w-full rounded px-2 py-2 bg-[var(--c-roadmap-node-bg)] text-[var(--c-roadmap-node-fg)] [overflow-wrap:anywhere] ${focusClass}`, onClick: (e) => {
9203
+ e.stopPropagation();
9204
+ if (node.action?.actionId) onAction?.(node.action.actionId, node);
9205
+ }, children: node.action.label }) })
9206
+ ] }) });
9207
+ }
9208
+
9209
+ // src/components/organisms/roadmap/RoadmapGroup.tsx
9210
+ var import_jsx_runtime67 = require("react/jsx-runtime");
9211
+ function RoadmapGroupView({ group, testIdPrefix }) {
9212
+ const tone = resolveGroupTone(group);
9213
+ const appearance = tone === "accent" ? { "--c-roadmap-node-bg": "var(--c-roadmap-group-accent-bg)", "--c-roadmap-node-fg": "var(--c-roadmap-group-accent-fg)" } : toneStyle(tone);
9214
+ return /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(import_jsx_runtime67.Fragment, { children: groupColumnRuns(group).map((run, index) => /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(
9215
+ "div",
9216
+ {
9217
+ "data-testid": `${testIdPrefix}-group`,
9218
+ "data-group-id": group.id,
9219
+ style: { borderColor: "var(--c-roadmap-border)", ...appearance, gridColumn: `${run.start} / span ${run.span}`, gridRow: `${group.from + 1} / span ${group.to - group.from + 1}` },
9220
+ className: "pointer-events-none relative z-0 min-w-0 self-stretch rounded-[var(--radius)] border-2 border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)] text-[var(--c-roadmap-node-fg)]",
9221
+ children: [
9222
+ index === 0 && group.title && /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { title: group.title, className: "absolute left-2 right-2 top-0 -translate-y-1/2 text-xs leading-normal", children: /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "inline-block max-w-full overflow-hidden text-ellipsis whitespace-nowrap bg-[var(--c-roadmap-surface)] px-1 align-top text-[var(--c-roadmap-neutral-fg)]", children: group.title }) }),
9223
+ index === 0 && group.description && /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "sr-only", children: group.description })
9224
+ ]
9225
+ },
9226
+ run.start
9227
+ )) });
9228
+ }
9229
+
9230
+ // src/components/organisms/roadmap/RoadmapEdgeLayer.tsx
9108
9231
  var import_react7 = require("react");
9232
+ var import_jsx_runtime68 = require("react/jsx-runtime");
9233
+ function Edge({ edge, path, instance, prefix }) {
9234
+ const pathRef = (0, import_react7.useRef)(null);
9235
+ const [midpoint, setMidpoint] = (0, import_react7.useState)();
9236
+ (0, import_react7.useLayoutEffect)(() => {
9237
+ const element = pathRef.current;
9238
+ if (edge.label && element?.getTotalLength && path.d) {
9239
+ const p = element.getPointAtLength(element.getTotalLength() / 2);
9240
+ setMidpoint({ x: p.x, y: p.y });
9241
+ } else setMidpoint(void 0);
9242
+ }, [path.d, edge.label]);
9243
+ const arrow = resolveEdgeArrow(edge), style = resolveEdgeStyle(edge);
9244
+ const marker = `${instance}-${edge.id}`;
9245
+ const color = edge.tone ? `var(--c-roadmap-${edge.tone}-edge)` : "var(--c-roadmap-edge)";
9246
+ return /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("g", { style: { color }, "data-roadmap-edge": edge.id, children: [
9247
+ arrow !== "none" && /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("marker", { id: marker, viewBox: "0 0 8 8", refX: "8", refY: "4", markerWidth: "8", markerHeight: "8", markerUnits: "userSpaceOnUse", orient: "auto-start-reverse", children: /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("path", { d: "M 0 0 L 8 4 L 0 8 Z", fill: "currentColor" }) }) }),
9248
+ /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
9249
+ "path",
9250
+ {
9251
+ ref: pathRef,
9252
+ "data-testid": `${prefix}-edge`,
9253
+ "data-edge-id": edge.id,
9254
+ "data-source": edge.source,
9255
+ "data-target": edge.target,
9256
+ d: path.d,
9257
+ fill: "none",
9258
+ stroke: "currentColor",
9259
+ strokeLinecap: "round",
9260
+ strokeLinejoin: "round",
9261
+ strokeDasharray: style === "solid" ? void 0 : style === "dotted" ? "0.8 4" : "0.8 8",
9262
+ className: "[stroke-width:2px] md:[stroke-width:3px]",
9263
+ markerStart: arrow === "both" ? `url(#${marker})` : void 0,
9264
+ markerEnd: arrow !== "none" ? `url(#${marker})` : void 0
9265
+ }
9266
+ ),
9267
+ edge.label && midpoint && /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("foreignObject", { "data-testid": `${prefix}-edge-label`, "data-edge-id": edge.id, x: midpoint.x, y: midpoint.y, width: "1", height: "1", overflow: "visible", children: /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
9268
+ "div",
9269
+ {
9270
+ className: "w-max max-w-[160px] -translate-x-1/2 -translate-y-1/2 rounded-full bg-[var(--c-roadmap-edge-label-bg)] px-2 py-0.5 text-center text-xs leading-normal text-[var(--c-roadmap-edge-label-fg)] [overflow-wrap:anywhere]",
9271
+ style: { boxShadow: "0 0 0 2px var(--c-roadmap-surface)" },
9272
+ children: edge.label
9273
+ }
9274
+ ) })
9275
+ ] });
9276
+ }
9277
+ function RoadmapEdgeLayer({ containerRef, document: documentModel, testIdPrefix, debugPerf }) {
9278
+ const instance = `roadmap-${(0, import_react7.useId)().replace(/:/g, "")}`;
9279
+ const [measured, setMeasured] = (0, import_react7.useState)([]);
9280
+ (0, import_react7.useEffect)(() => {
9281
+ const container = containerRef.current;
9282
+ if (!container || !documentModel.edges.length) {
9283
+ setMeasured([]);
9284
+ return;
9285
+ }
9286
+ let disposed = false, frame = 0;
9287
+ const observed = /* @__PURE__ */ new Set();
9288
+ const measure = () => {
9289
+ frame = 0;
9290
+ if (disposed) return;
9291
+ const started = performance.now();
9292
+ const root = container.getBoundingClientRect();
9293
+ const boxes = /* @__PURE__ */ new Map();
9294
+ for (const wrapper of container.querySelectorAll("[data-roadmap-node]")) {
9295
+ if (wrapper.closest("[data-roadmap-grid]") !== container) continue;
9296
+ const id = wrapper.dataset.roadmapNode;
9297
+ const border = Array.from(wrapper.querySelectorAll("[data-roadmap-node-box]")).find((el) => el.dataset.roadmapNodeBox === id);
9298
+ const element = border ?? wrapper.firstElementChild ?? wrapper;
9299
+ for (const target of [wrapper, element]) if (!observed.has(target)) {
9300
+ observer?.observe(target);
9301
+ observed.add(target);
9302
+ }
9303
+ const rect = element.getBoundingClientRect();
9304
+ boxes.set(id, { left: rect.left - root.left, right: rect.right - root.left, top: rect.top - root.top, bottom: rect.bottom - root.top });
9305
+ }
9306
+ for (const element of observed) if (!container.contains(element)) {
9307
+ observer?.unobserve(element);
9308
+ observed.delete(element);
9309
+ }
9310
+ const obstacles = [...boxes.values()];
9311
+ const next = documentModel.edges.flatMap((edge) => {
9312
+ const source = boxes.get(edge.source), target = boxes.get(edge.target);
9313
+ return source && target ? [{ edge, path: routeEdge(source, target, edge, obstacles) }] : [];
9314
+ });
9315
+ setMeasured(next);
9316
+ if (debugPerf) performance.measure("roadmap:edges", { start: started, end: performance.now() });
9317
+ };
9318
+ const schedule = () => {
9319
+ if (!disposed && !frame) frame = requestAnimationFrame(measure);
9320
+ };
9321
+ const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(schedule);
9322
+ observer?.observe(container);
9323
+ const mutations = new MutationObserver((records) => {
9324
+ if (records.some((record) => (record.target instanceof Element ? record.target : record.target.parentElement)?.closest("[data-roadmap-node]"))) schedule();
9325
+ });
9326
+ mutations.observe(container, { childList: true, subtree: true, characterData: true, attributes: true });
9327
+ const fonts = container.ownerDocument.fonts;
9328
+ fonts?.ready.then(schedule);
9329
+ fonts?.addEventListener("loadingdone", schedule);
9330
+ schedule();
9331
+ return () => {
9332
+ disposed = true;
9333
+ cancelAnimationFrame(frame);
9334
+ observer?.disconnect();
9335
+ mutations.disconnect();
9336
+ fonts?.removeEventListener("loadingdone", schedule);
9337
+ };
9338
+ }, [containerRef, documentModel, debugPerf]);
9339
+ return /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("svg", { "data-testid": `${testIdPrefix}-edges`, "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-[1] h-full w-full overflow-visible", children: measured.map((item) => /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(Edge, { ...item, instance, prefix: testIdPrefix }, item.edge.id)) });
9340
+ }
9341
+
9342
+ // src/components/organisms/roadmap/Roadmap.tsx
9343
+ var import_jsx_runtime69 = require("react/jsx-runtime");
9344
+ function Roadmap({ document: document2, state, renderNode, onNodeClick, onAction, testIdPrefix = "roadmap", className = "", ariaLabel, debugPerf }) {
9345
+ const gridRef = (0, import_react8.useRef)(null);
9346
+ const { placements } = (0, import_react8.useMemo)(() => placeNodes(document2), [document2]);
9347
+ const badges = (0, import_react8.useMemo)(() => new Map(document2.legend?.entries.map((entry) => [entry.id, entry])), [document2.legend]);
9348
+ const legendPlacement = document2.legend ? resolveLegendPlacement(document2.legend) : void 0;
9349
+ const legend = document2.legend && /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(RoadmapLegendView, { legend: document2.legend, testIdPrefix });
9350
+ return /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("section", { "data-testid": testIdPrefix, "aria-label": ariaLabel ?? document2.title ?? "Roadmap", className: `min-w-0 bg-[var(--c-roadmap-surface)] p-3 text-[var(--c-roadmap-neutral-fg)] ${className}`, children: [
9351
+ legendPlacement === "top" && /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("div", { className: "mb-6", children: legend }),
9352
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { ref: gridRef, "data-roadmap-grid": "", "data-testid": `${testIdPrefix}-grid`, role: "list", "aria-label": "Roadmap nodes", className: "relative isolate grid min-w-0 grid-cols-3 items-center gap-x-3 gap-y-2 md:gap-x-8", children: [
9353
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(RoadmapEdgeLayer, { containerRef: gridRef, document: document2, testIdPrefix, debugPerf }),
9354
+ document2.groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(RoadmapGroupView, { group, testIdPrefix }, group.id)),
9355
+ placements.map(({ node, row, column }) => {
9356
+ const resolvedState = resolveNodeState(node, state);
9357
+ return /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
9358
+ "div",
9359
+ {
9360
+ role: "listitem",
9361
+ "data-testid": `${testIdPrefix}-node`,
9362
+ "data-node-id": node.id,
9363
+ "data-roadmap-node": node.id,
9364
+ "data-state": resolvedState,
9365
+ style: { gridColumn: `${column.start} / span ${column.span}`, gridRow: `${row.start} / span 1` },
9366
+ className: "relative z-10 min-w-0 px-2 py-2",
9367
+ children: node.kind === "legend" ? legendPlacement === "inline" ? legend : null : renderNode ? renderNode(node, resolvedState) : /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(RoadmapNodeView, { node, state: resolvedState, badge: node.badge ? badges.get(node.badge) : void 0, onNodeClick, onAction, testIdPrefix })
9368
+ },
9369
+ node.id
9370
+ );
9371
+ })
9372
+ ] }),
9373
+ legendPlacement === "bottom" && /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("div", { className: "mt-6", children: legend })
9374
+ ] });
9375
+ }
9376
+
9377
+ // src/hooks/use-iframe-auth.ts
9378
+ var import_react9 = require("react");
9109
9379
  function useIframeAuth() {
9110
- const ctx = (0, import_react7.useContext)(IframeContext);
9380
+ const ctx = (0, import_react9.useContext)(IframeContext);
9111
9381
  return { token: ctx.token, userId: ctx.userId, ready: ctx.ready };
9112
9382
  }
9113
9383
 
9114
9384
  // src/hooks/use-iframe-navigate.ts
9115
- var import_react8 = require("react");
9385
+ var import_react10 = require("react");
9116
9386
  function useIframeNavigate() {
9117
- return (0, import_react8.useCallback)((path) => {
9387
+ return (0, import_react10.useCallback)((path) => {
9118
9388
  if (!window.parent || window.parent === window) return;
9119
9389
  window.parent.postMessage(
9120
9390
  { type: "DFL_NAVIGATE", path },
@@ -9124,9 +9394,9 @@ function useIframeNavigate() {
9124
9394
  }
9125
9395
 
9126
9396
  // src/providers/feature-flag-provider.tsx
9127
- var import_react9 = require("react");
9128
- var import_jsx_runtime65 = require("react/jsx-runtime");
9129
- var FeatureFlagContext = (0, import_react9.createContext)({
9397
+ var import_react11 = require("react");
9398
+ var import_jsx_runtime70 = require("react/jsx-runtime");
9399
+ var FeatureFlagContext = (0, import_react11.createContext)({
9130
9400
  flags: {},
9131
9401
  isEnabled: () => false
9132
9402
  });
@@ -9134,21 +9404,21 @@ var FeatureFlagProvider = ({
9134
9404
  children,
9135
9405
  flags
9136
9406
  }) => {
9137
- const value = (0, import_react9.useMemo)(
9407
+ const value = (0, import_react11.useMemo)(
9138
9408
  () => ({
9139
9409
  flags,
9140
9410
  isEnabled: (flag) => Boolean(flags[flag])
9141
9411
  }),
9142
9412
  [flags]
9143
9413
  );
9144
- return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(FeatureFlagContext.Provider, { value, children });
9414
+ return /* @__PURE__ */ (0, import_jsx_runtime70.jsx)(FeatureFlagContext.Provider, { value, children });
9145
9415
  };
9146
9416
  var useFeatureFlag = (flag) => {
9147
- const { isEnabled } = (0, import_react9.useContext)(FeatureFlagContext);
9417
+ const { isEnabled } = (0, import_react11.useContext)(FeatureFlagContext);
9148
9418
  return isEnabled(flag);
9149
9419
  };
9150
9420
  var useFeatureFlags = () => {
9151
- const { flags } = (0, import_react9.useContext)(FeatureFlagContext);
9421
+ const { flags } = (0, import_react11.useContext)(FeatureFlagContext);
9152
9422
  return flags;
9153
9423
  };
9154
9424
  // Annotate the CommonJS export names for ESM import in node:
@@ -9367,6 +9637,7 @@ var useFeatureFlags = () => {
9367
9637
  ResizableHandle,
9368
9638
  ResizablePanel,
9369
9639
  ResizablePanelGroup,
9640
+ Roadmap,
9370
9641
  ScrollArea,
9371
9642
  ScrollBar,
9372
9643
  Select,
package/dist/index.d.cts CHANGED
@@ -2219,7 +2219,7 @@ declare function PublishDrawer({ open, onOpenChange, supabase, videoUrl, transcr
2219
2219
  *
2220
2220
  * Colour is a TOKEN, never a hex (plan ADR-6). `tone` is one of eight names and
2221
2221
  * the schema REJECTS any other string, `"#fdff00"` included. That closed
2222
- * vocabulary is what keeps the dark design system, the editorial surface and a
2222
+ * vocabulary is what keeps the dark design system and a
2223
2223
  * downstream `--s-*` rebrand all working from one document.
2224
2224
  *
2225
2225
  * The live progress state does NOT live in the document (plan ADR-8). A document
@@ -3262,4 +3262,18 @@ declare function placeNodes(doc: Pick<RoadmapDocument, "nodes">): {
3262
3262
  */
3263
3263
  declare function firstFreeRow(taken: ReadonlySet<string>, columns: readonly RoadmapColumn[], from: number): number;
3264
3264
 
3265
- export { ALLOWED_ORIGINS, AVATAR_MEMBER_PALETTE_SIZE, Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, type AlertDialogActionProps, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppNavbar, type AppNavbarProps, AppSidebar, type AppSidebarProps, AspectRatio, Avatar, AvatarFallback, AvatarGroup, AvatarImage, type AvatarSize, AvatarStatus, type AvatarStatusValue, type AvatarTone, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, type BreadcrumbEntry, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, Calendar, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, type CarouselApi, CarouselContent, CarouselDots, CarouselItem, CarouselNext, CarouselPrevious, type ChartConfig, ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, Checkbox, Collapsible, CollapsibleContent, CollapsibleHeader, CollapsibleItem, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ConfirmDialog, type ConfirmDialogProps, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DEFAULT_EDGE_ARROW, DEFAULT_EDGE_ROUTE, DEFAULT_EDGE_STYLE, DEFAULT_GROUP_TONE, DEFAULT_LEGEND_PLACEMENT, DEFAULT_NAME_COL_WIDTH, DEFAULT_NODE_SPAN, DEFAULT_NODE_STATE, type DflIframeMessage, type DflNavigateMessage, type DflReadyMessage, DflRemote, type DflRemoteProps, type DflResizeMessage, type DflSetTokenMessage, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, Gantt, type GanttDependency, type GanttMilestone, type GanttProps, type GanttStage, HoverCard, HoverCardContent, HoverCardTrigger, IconButton, type IconButtonProps, IframeAware, type IframeAwareProps, IframeContext, type IframeContextValue, type IframeMessageType, Input, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Kbd, type KbdProps, Label, LoginPage, type LoginPageProps, LoginScreen, type LoginScreenProps, MEMBER_PALETTE_SIZE, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NODE_KIND_DEFAULT_TONE, type NavGroup, type NavItem, type NavbarUserInfo, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, type OTPStatus, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PasswordInput, type PasswordInputProps, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, type ProgressProps, ProtectedRoute, type ProtectedRouteProps, PublishDrawer, type PublishDrawerProps, type PublishDrawerSupabase, type PublishResult, type PublishStatus, type PublisherAccount, ROADMAP_ARROWS, ROADMAP_COLUMNS, ROADMAP_EDGE_LABEL_MAX, ROADMAP_EDGE_ROUTES, ROADMAP_EDGE_STYLES, ROADMAP_LABEL_MAX, ROADMAP_LEGEND_PLACEMENTS, ROADMAP_NODE_ID_PATTERN, ROADMAP_NODE_KINDS, ROADMAP_NODE_STATES, ROADMAP_TONES, RadioGroup, RadioGroupItem, RadioGroupRow, ResizableHandle, ResizablePanel, ResizablePanelGroup, type RoadmapAction, type RoadmapArrow, type RoadmapColumn, type RoadmapDocument, type RoadmapEdge, type RoadmapEdgeRoute, type RoadmapEdgeStyle, type RoadmapGroup, type RoadmapGroupRange, type RoadmapIcon, type RoadmapLegend, type RoadmapLegendEntry, type RoadmapLegendPlacement, type RoadmapLink, type RoadmapNode, type RoadmapNodeKind, type RoadmapNodeState, type RoadmapOverlap, type RoadmapPlacement, type RoadmapStateOverlay, type RoadmapTone, ScrollArea, type ScrollAreaScrollbars, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SeparatorWithLabel, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, SonnerToaster, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, type TextareaProps, Toast, ToastAction, type ToastActionElement, ToastClose, type ToastComponentProps, ToastDescription, ToastProvider, ToastTitle, ToastViewport, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserAvatar, type UserAvatarProps, type UserInfo, UserMenu, type UserMenuItem, type UserMenuProps, avatarMemberColor, badgeVariants, barGridColumn, buttonVariants, clampPct, columnIndex, columnsCovered, columnsUsed, filterPublishableAccounts, firstFreeRow, getInitials, gridColumnFor, groupColumnRuns, groupRanges, iconButtonVariants, isAllowedOrigin, kbdVariants, labelVariants, maxRowOf, memberHueIndex, memberHueVar, navigationMenuTriggerStyle, nodesByRow, parseRoadmapDocument, parseRoadmapStateOverlay, parseTags, placeNodes, resolveEdgeArrow, resolveEdgeRoute, resolveEdgeStyle, resolveGroupColumns, resolveGroupTone, resolveLegendPlacement, resolveNameColWidth, resolveNodeSpan, resolveNodeState, resolveNodeTone, resolveWeekCount, resolveWeekLabels, roadmapActionSchema, roadmapDocumentSchema, roadmapEdgeSchema, roadmapGroupSchema, roadmapIconSchema, roadmapLegendEntrySchema, roadmapLegendSchema, roadmapLinkSchema, roadmapNodeSchema, roadmapNodeStateSchema, roadmapStateOverlaySchema, roadmapStateSchema, rowsOf, safeParseRoadmapDocument, safeParseRoadmapStateOverlay, stageProgress, toggleVariants, useFormField, useSidebar, validateNoOverlap, validatePublishForm };
3265
+ interface RoadmapProps {
3266
+ document: RoadmapDocument;
3267
+ state?: RoadmapStateOverlay;
3268
+ renderNode?: (node: RoadmapNode, state: RoadmapNodeState) => ReactNode;
3269
+ onNodeClick?: (node: RoadmapNode) => void;
3270
+ onAction?: (actionId: string, node: RoadmapNode) => void;
3271
+ debugPerf?: boolean;
3272
+ testIdPrefix?: string;
3273
+ className?: string;
3274
+ ariaLabel?: string;
3275
+ }
3276
+ /** Dark-only, ordinary document scroll. The JSON owns placement; CSS owns the pixels. */
3277
+ declare function Roadmap({ document, state, renderNode, onNodeClick, onAction, testIdPrefix, className, ariaLabel, debugPerf }: RoadmapProps): React__default.JSX.Element;
3278
+
3279
+ export { ALLOWED_ORIGINS, AVATAR_MEMBER_PALETTE_SIZE, Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, type AlertDialogActionProps, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppNavbar, type AppNavbarProps, AppSidebar, type AppSidebarProps, AspectRatio, Avatar, AvatarFallback, AvatarGroup, AvatarImage, type AvatarSize, AvatarStatus, type AvatarStatusValue, type AvatarTone, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, type BreadcrumbEntry, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, Calendar, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, type CarouselApi, CarouselContent, CarouselDots, CarouselItem, CarouselNext, CarouselPrevious, type ChartConfig, ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, Checkbox, Collapsible, CollapsibleContent, CollapsibleHeader, CollapsibleItem, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ConfirmDialog, type ConfirmDialogProps, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DEFAULT_EDGE_ARROW, DEFAULT_EDGE_ROUTE, DEFAULT_EDGE_STYLE, DEFAULT_GROUP_TONE, DEFAULT_LEGEND_PLACEMENT, DEFAULT_NAME_COL_WIDTH, DEFAULT_NODE_SPAN, DEFAULT_NODE_STATE, type DflIframeMessage, type DflNavigateMessage, type DflReadyMessage, DflRemote, type DflRemoteProps, type DflResizeMessage, type DflSetTokenMessage, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, Gantt, type GanttDependency, type GanttMilestone, type GanttProps, type GanttStage, HoverCard, HoverCardContent, HoverCardTrigger, IconButton, type IconButtonProps, IframeAware, type IframeAwareProps, IframeContext, type IframeContextValue, type IframeMessageType, Input, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Kbd, type KbdProps, Label, LoginPage, type LoginPageProps, LoginScreen, type LoginScreenProps, MEMBER_PALETTE_SIZE, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NODE_KIND_DEFAULT_TONE, type NavGroup, type NavItem, type NavbarUserInfo, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, type OTPStatus, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PasswordInput, type PasswordInputProps, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, type ProgressProps, ProtectedRoute, type ProtectedRouteProps, PublishDrawer, type PublishDrawerProps, type PublishDrawerSupabase, type PublishResult, type PublishStatus, type PublisherAccount, ROADMAP_ARROWS, ROADMAP_COLUMNS, ROADMAP_EDGE_LABEL_MAX, ROADMAP_EDGE_ROUTES, ROADMAP_EDGE_STYLES, ROADMAP_LABEL_MAX, ROADMAP_LEGEND_PLACEMENTS, ROADMAP_NODE_ID_PATTERN, ROADMAP_NODE_KINDS, ROADMAP_NODE_STATES, ROADMAP_TONES, RadioGroup, RadioGroupItem, RadioGroupRow, ResizableHandle, ResizablePanel, ResizablePanelGroup, Roadmap, type RoadmapAction, type RoadmapArrow, type RoadmapColumn, type RoadmapDocument, type RoadmapEdge, type RoadmapEdgeRoute, type RoadmapEdgeStyle, type RoadmapGroup, type RoadmapGroupRange, type RoadmapIcon, type RoadmapLegend, type RoadmapLegendEntry, type RoadmapLegendPlacement, type RoadmapLink, type RoadmapNode, type RoadmapNodeKind, type RoadmapNodeState, type RoadmapOverlap, type RoadmapPlacement, type RoadmapProps, type RoadmapStateOverlay, type RoadmapTone, ScrollArea, type ScrollAreaScrollbars, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SeparatorWithLabel, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, SonnerToaster, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, type TextareaProps, Toast, ToastAction, type ToastActionElement, ToastClose, type ToastComponentProps, ToastDescription, ToastProvider, ToastTitle, ToastViewport, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserAvatar, type UserAvatarProps, type UserInfo, UserMenu, type UserMenuItem, type UserMenuProps, avatarMemberColor, badgeVariants, barGridColumn, buttonVariants, clampPct, columnIndex, columnsCovered, columnsUsed, filterPublishableAccounts, firstFreeRow, getInitials, gridColumnFor, groupColumnRuns, groupRanges, iconButtonVariants, isAllowedOrigin, kbdVariants, labelVariants, maxRowOf, memberHueIndex, memberHueVar, navigationMenuTriggerStyle, nodesByRow, parseRoadmapDocument, parseRoadmapStateOverlay, parseTags, placeNodes, resolveEdgeArrow, resolveEdgeRoute, resolveEdgeStyle, resolveGroupColumns, resolveGroupTone, resolveLegendPlacement, resolveNameColWidth, resolveNodeSpan, resolveNodeState, resolveNodeTone, resolveWeekCount, resolveWeekLabels, roadmapActionSchema, roadmapDocumentSchema, roadmapEdgeSchema, roadmapGroupSchema, roadmapIconSchema, roadmapLegendEntrySchema, roadmapLegendSchema, roadmapLinkSchema, roadmapNodeSchema, roadmapNodeStateSchema, roadmapStateOverlaySchema, roadmapStateSchema, rowsOf, safeParseRoadmapDocument, safeParseRoadmapStateOverlay, stageProgress, toggleVariants, useFormField, useSidebar, validateNoOverlap, validatePublishForm };
package/dist/index.d.ts CHANGED
@@ -2219,7 +2219,7 @@ declare function PublishDrawer({ open, onOpenChange, supabase, videoUrl, transcr
2219
2219
  *
2220
2220
  * Colour is a TOKEN, never a hex (plan ADR-6). `tone` is one of eight names and
2221
2221
  * the schema REJECTS any other string, `"#fdff00"` included. That closed
2222
- * vocabulary is what keeps the dark design system, the editorial surface and a
2222
+ * vocabulary is what keeps the dark design system and a
2223
2223
  * downstream `--s-*` rebrand all working from one document.
2224
2224
  *
2225
2225
  * The live progress state does NOT live in the document (plan ADR-8). A document
@@ -3262,4 +3262,18 @@ declare function placeNodes(doc: Pick<RoadmapDocument, "nodes">): {
3262
3262
  */
3263
3263
  declare function firstFreeRow(taken: ReadonlySet<string>, columns: readonly RoadmapColumn[], from: number): number;
3264
3264
 
3265
- export { ALLOWED_ORIGINS, AVATAR_MEMBER_PALETTE_SIZE, Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, type AlertDialogActionProps, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppNavbar, type AppNavbarProps, AppSidebar, type AppSidebarProps, AspectRatio, Avatar, AvatarFallback, AvatarGroup, AvatarImage, type AvatarSize, AvatarStatus, type AvatarStatusValue, type AvatarTone, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, type BreadcrumbEntry, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, Calendar, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, type CarouselApi, CarouselContent, CarouselDots, CarouselItem, CarouselNext, CarouselPrevious, type ChartConfig, ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, Checkbox, Collapsible, CollapsibleContent, CollapsibleHeader, CollapsibleItem, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ConfirmDialog, type ConfirmDialogProps, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DEFAULT_EDGE_ARROW, DEFAULT_EDGE_ROUTE, DEFAULT_EDGE_STYLE, DEFAULT_GROUP_TONE, DEFAULT_LEGEND_PLACEMENT, DEFAULT_NAME_COL_WIDTH, DEFAULT_NODE_SPAN, DEFAULT_NODE_STATE, type DflIframeMessage, type DflNavigateMessage, type DflReadyMessage, DflRemote, type DflRemoteProps, type DflResizeMessage, type DflSetTokenMessage, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, Gantt, type GanttDependency, type GanttMilestone, type GanttProps, type GanttStage, HoverCard, HoverCardContent, HoverCardTrigger, IconButton, type IconButtonProps, IframeAware, type IframeAwareProps, IframeContext, type IframeContextValue, type IframeMessageType, Input, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Kbd, type KbdProps, Label, LoginPage, type LoginPageProps, LoginScreen, type LoginScreenProps, MEMBER_PALETTE_SIZE, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NODE_KIND_DEFAULT_TONE, type NavGroup, type NavItem, type NavbarUserInfo, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, type OTPStatus, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PasswordInput, type PasswordInputProps, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, type ProgressProps, ProtectedRoute, type ProtectedRouteProps, PublishDrawer, type PublishDrawerProps, type PublishDrawerSupabase, type PublishResult, type PublishStatus, type PublisherAccount, ROADMAP_ARROWS, ROADMAP_COLUMNS, ROADMAP_EDGE_LABEL_MAX, ROADMAP_EDGE_ROUTES, ROADMAP_EDGE_STYLES, ROADMAP_LABEL_MAX, ROADMAP_LEGEND_PLACEMENTS, ROADMAP_NODE_ID_PATTERN, ROADMAP_NODE_KINDS, ROADMAP_NODE_STATES, ROADMAP_TONES, RadioGroup, RadioGroupItem, RadioGroupRow, ResizableHandle, ResizablePanel, ResizablePanelGroup, type RoadmapAction, type RoadmapArrow, type RoadmapColumn, type RoadmapDocument, type RoadmapEdge, type RoadmapEdgeRoute, type RoadmapEdgeStyle, type RoadmapGroup, type RoadmapGroupRange, type RoadmapIcon, type RoadmapLegend, type RoadmapLegendEntry, type RoadmapLegendPlacement, type RoadmapLink, type RoadmapNode, type RoadmapNodeKind, type RoadmapNodeState, type RoadmapOverlap, type RoadmapPlacement, type RoadmapStateOverlay, type RoadmapTone, ScrollArea, type ScrollAreaScrollbars, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SeparatorWithLabel, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, SonnerToaster, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, type TextareaProps, Toast, ToastAction, type ToastActionElement, ToastClose, type ToastComponentProps, ToastDescription, ToastProvider, ToastTitle, ToastViewport, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserAvatar, type UserAvatarProps, type UserInfo, UserMenu, type UserMenuItem, type UserMenuProps, avatarMemberColor, badgeVariants, barGridColumn, buttonVariants, clampPct, columnIndex, columnsCovered, columnsUsed, filterPublishableAccounts, firstFreeRow, getInitials, gridColumnFor, groupColumnRuns, groupRanges, iconButtonVariants, isAllowedOrigin, kbdVariants, labelVariants, maxRowOf, memberHueIndex, memberHueVar, navigationMenuTriggerStyle, nodesByRow, parseRoadmapDocument, parseRoadmapStateOverlay, parseTags, placeNodes, resolveEdgeArrow, resolveEdgeRoute, resolveEdgeStyle, resolveGroupColumns, resolveGroupTone, resolveLegendPlacement, resolveNameColWidth, resolveNodeSpan, resolveNodeState, resolveNodeTone, resolveWeekCount, resolveWeekLabels, roadmapActionSchema, roadmapDocumentSchema, roadmapEdgeSchema, roadmapGroupSchema, roadmapIconSchema, roadmapLegendEntrySchema, roadmapLegendSchema, roadmapLinkSchema, roadmapNodeSchema, roadmapNodeStateSchema, roadmapStateOverlaySchema, roadmapStateSchema, rowsOf, safeParseRoadmapDocument, safeParseRoadmapStateOverlay, stageProgress, toggleVariants, useFormField, useSidebar, validateNoOverlap, validatePublishForm };
3265
+ interface RoadmapProps {
3266
+ document: RoadmapDocument;
3267
+ state?: RoadmapStateOverlay;
3268
+ renderNode?: (node: RoadmapNode, state: RoadmapNodeState) => ReactNode;
3269
+ onNodeClick?: (node: RoadmapNode) => void;
3270
+ onAction?: (actionId: string, node: RoadmapNode) => void;
3271
+ debugPerf?: boolean;
3272
+ testIdPrefix?: string;
3273
+ className?: string;
3274
+ ariaLabel?: string;
3275
+ }
3276
+ /** Dark-only, ordinary document scroll. The JSON owns placement; CSS owns the pixels. */
3277
+ declare function Roadmap({ document, state, renderNode, onNodeClick, onAction, testIdPrefix, className, ariaLabel, debugPerf }: RoadmapProps): React__default.JSX.Element;
3278
+
3279
+ export { ALLOWED_ORIGINS, AVATAR_MEMBER_PALETTE_SIZE, Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, type AlertDialogActionProps, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppNavbar, type AppNavbarProps, AppSidebar, type AppSidebarProps, AspectRatio, Avatar, AvatarFallback, AvatarGroup, AvatarImage, type AvatarSize, AvatarStatus, type AvatarStatusValue, type AvatarTone, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, type BreadcrumbEntry, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, Calendar, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, type CarouselApi, CarouselContent, CarouselDots, CarouselItem, CarouselNext, CarouselPrevious, type ChartConfig, ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, Checkbox, Collapsible, CollapsibleContent, CollapsibleHeader, CollapsibleItem, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ConfirmDialog, type ConfirmDialogProps, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DEFAULT_EDGE_ARROW, DEFAULT_EDGE_ROUTE, DEFAULT_EDGE_STYLE, DEFAULT_GROUP_TONE, DEFAULT_LEGEND_PLACEMENT, DEFAULT_NAME_COL_WIDTH, DEFAULT_NODE_SPAN, DEFAULT_NODE_STATE, type DflIframeMessage, type DflNavigateMessage, type DflReadyMessage, DflRemote, type DflRemoteProps, type DflResizeMessage, type DflSetTokenMessage, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, Gantt, type GanttDependency, type GanttMilestone, type GanttProps, type GanttStage, HoverCard, HoverCardContent, HoverCardTrigger, IconButton, type IconButtonProps, IframeAware, type IframeAwareProps, IframeContext, type IframeContextValue, type IframeMessageType, Input, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Kbd, type KbdProps, Label, LoginPage, type LoginPageProps, LoginScreen, type LoginScreenProps, MEMBER_PALETTE_SIZE, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NODE_KIND_DEFAULT_TONE, type NavGroup, type NavItem, type NavbarUserInfo, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, type OTPStatus, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PasswordInput, type PasswordInputProps, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, type ProgressProps, ProtectedRoute, type ProtectedRouteProps, PublishDrawer, type PublishDrawerProps, type PublishDrawerSupabase, type PublishResult, type PublishStatus, type PublisherAccount, ROADMAP_ARROWS, ROADMAP_COLUMNS, ROADMAP_EDGE_LABEL_MAX, ROADMAP_EDGE_ROUTES, ROADMAP_EDGE_STYLES, ROADMAP_LABEL_MAX, ROADMAP_LEGEND_PLACEMENTS, ROADMAP_NODE_ID_PATTERN, ROADMAP_NODE_KINDS, ROADMAP_NODE_STATES, ROADMAP_TONES, RadioGroup, RadioGroupItem, RadioGroupRow, ResizableHandle, ResizablePanel, ResizablePanelGroup, Roadmap, type RoadmapAction, type RoadmapArrow, type RoadmapColumn, type RoadmapDocument, type RoadmapEdge, type RoadmapEdgeRoute, type RoadmapEdgeStyle, type RoadmapGroup, type RoadmapGroupRange, type RoadmapIcon, type RoadmapLegend, type RoadmapLegendEntry, type RoadmapLegendPlacement, type RoadmapLink, type RoadmapNode, type RoadmapNodeKind, type RoadmapNodeState, type RoadmapOverlap, type RoadmapPlacement, type RoadmapProps, type RoadmapStateOverlay, type RoadmapTone, ScrollArea, type ScrollAreaScrollbars, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SeparatorWithLabel, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, SonnerToaster, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, type TextareaProps, Toast, ToastAction, type ToastActionElement, ToastClose, type ToastComponentProps, ToastDescription, ToastProvider, ToastTitle, ToastViewport, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserAvatar, type UserAvatarProps, type UserInfo, UserMenu, type UserMenuItem, type UserMenuProps, avatarMemberColor, badgeVariants, barGridColumn, buttonVariants, clampPct, columnIndex, columnsCovered, columnsUsed, filterPublishableAccounts, firstFreeRow, getInitials, gridColumnFor, groupColumnRuns, groupRanges, iconButtonVariants, isAllowedOrigin, kbdVariants, labelVariants, maxRowOf, memberHueIndex, memberHueVar, navigationMenuTriggerStyle, nodesByRow, parseRoadmapDocument, parseRoadmapStateOverlay, parseTags, placeNodes, resolveEdgeArrow, resolveEdgeRoute, resolveEdgeStyle, resolveGroupColumns, resolveGroupTone, resolveLegendPlacement, resolveNameColWidth, resolveNodeSpan, resolveNodeState, resolveNodeTone, resolveWeekCount, resolveWeekLabels, roadmapActionSchema, roadmapDocumentSchema, roadmapEdgeSchema, roadmapGroupSchema, roadmapIconSchema, roadmapLegendEntrySchema, roadmapLegendSchema, roadmapLinkSchema, roadmapNodeSchema, roadmapNodeStateSchema, roadmapStateOverlaySchema, roadmapStateSchema, rowsOf, safeParseRoadmapDocument, safeParseRoadmapStateOverlay, stageProgress, toggleVariants, useFormField, useSidebar, validateNoOverlap, validatePublishForm };
package/dist/index.js CHANGED
@@ -8715,6 +8715,275 @@ function firstFreeRow(taken, columns, from) {
8715
8715
  }
8716
8716
  return row;
8717
8717
  }
8718
+ var pointText = (p) => `${p.x} ${p.y}`;
8719
+ var center = (r) => ({ x: (r.left + r.right) / 2, y: (r.top + r.bottom) / 2 });
8720
+ function segmentHits(a, b, r) {
8721
+ return a.x === b.x ? a.x > r.left && a.x < r.right && Math.max(a.y, b.y) > r.top && Math.min(a.y, b.y) < r.bottom : a.y > r.top && a.y < r.bottom && Math.max(a.x, b.x) > r.left && Math.min(a.x, b.x) < r.right;
8722
+ }
8723
+ function routeEdge(source, target, edge, obstacles = []) {
8724
+ const a = center(source), b = center(target);
8725
+ const sameColumn = Math.abs(a.x - b.x) < 1;
8726
+ const vertical = sameColumn || source.left < target.right && target.left < source.right;
8727
+ const down = b.y >= a.y, right = b.x >= a.x;
8728
+ const start = vertical ? { x: a.x, y: down ? source.bottom : source.top } : { x: right ? source.right : source.left, y: a.y };
8729
+ const end = vertical ? { x: b.x, y: down ? target.top : target.bottom } : { x: right ? target.left : target.right, y: b.y };
8730
+ const route = edge.route ?? "auto";
8731
+ if (route === "straight" || route === "auto" && sameColumn) return { start, end, d: `M ${pointText(start)} L ${pointText(end)}` };
8732
+ if (route !== "elbow") {
8733
+ const mid = vertical ? (start.y + end.y) / 2 : (start.x + end.x) / 2;
8734
+ const c1 = vertical ? { x: start.x, y: mid } : { x: mid, y: start.y };
8735
+ const c2 = vertical ? { x: end.x, y: mid } : { x: mid, y: end.y };
8736
+ return { start, end, d: `M ${pointText(start)} C ${pointText(c1)} ${pointText(c2)} ${pointText(end)}` };
8737
+ }
8738
+ const boxes = [.../* @__PURE__ */ new Set([source, target, ...obstacles])];
8739
+ const clearance = 4;
8740
+ const ys = [...new Set(boxes.flatMap((r) => [r.top - clearance, r.bottom + clearance]))];
8741
+ let best, bestLength = Infinity;
8742
+ for (const sx of [source.left, source.right]) for (const tx of [target.left, target.right]) {
8743
+ const s = { x: sx, y: a.y }, t = { x: tx, y: b.y };
8744
+ const gx = sx + (sx === source.left ? -clearance : clearance);
8745
+ const hx = tx + (tx === target.left ? -clearance : clearance);
8746
+ for (const y of [a.y, b.y, ...ys]) {
8747
+ const points2 = [s, { x: gx, y: a.y }, { x: gx, y }, { x: hx, y }, { x: hx, y: b.y }, t];
8748
+ const length = points2.slice(1).reduce((sum, p, i) => sum + Math.abs(p.x - points2[i].x) + Math.abs(p.y - points2[i].y), 0);
8749
+ if (length >= bestLength || points2.slice(1).some((p, i) => boxes.some((r) => segmentHits(points2[i], p, r)))) continue;
8750
+ best = points2;
8751
+ bestLength = length;
8752
+ }
8753
+ }
8754
+ if (!best) return { start, end, d: "" };
8755
+ const points = best.filter((p, i) => i === 0 || p.x !== best[i - 1].x || p.y !== best[i - 1].y);
8756
+ return { start: points[0], end: points[points.length - 1], points, d: points.map((p, i) => `${i ? "L" : "M"} ${pointText(p)}`).join(" ") };
8757
+ }
8758
+
8759
+ // src/components/organisms/roadmap/Roadmap.tsx
8760
+ import { useMemo as useMemo5, useRef as useRef6 } from "react";
8761
+
8762
+ // src/components/organisms/roadmap/RoadmapNode.tsx
8763
+ import { cva as cva14 } from "class-variance-authority";
8764
+
8765
+ // src/components/organisms/roadmap/RoadmapLegend.tsx
8766
+ import { Check as Check4, Globe, Lock, Star, BookOpen, Play, Circle as Circle2, CircleHelp, Clock, Link as Link2, ExternalLink } from "lucide-react";
8767
+
8768
+ // src/components/organisms/roadmap/appearance.ts
8769
+ function toneStyle(tone) {
8770
+ return {
8771
+ "--c-roadmap-node-bg": `var(--c-roadmap-${tone}-bg)`,
8772
+ "--c-roadmap-node-fg": `var(--c-roadmap-${tone}-fg)`
8773
+ };
8774
+ }
8775
+ var focusClass = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--c-roadmap-focus)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--c-roadmap-surface)]";
8776
+
8777
+ // src/components/organisms/roadmap/RoadmapLegend.tsx
8778
+ import { jsx as jsx65, jsxs as jsxs40 } from "react/jsx-runtime";
8779
+ var glyphs = { check: Check4, globe: Globe, lock: Lock, star: Star, "book-open": BookOpen, play: Play, circle: Circle2, clock: Clock, link: Link2, "external-link": ExternalLink };
8780
+ function RoadmapBadge({ name = "check", tone = "neutral", label, className = "" }) {
8781
+ const Glyph = glyphs[name] ?? CircleHelp;
8782
+ return /* @__PURE__ */ jsx65("span", { "aria-label": label, "aria-hidden": label ? void 0 : true, style: toneStyle(tone), className: `inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full border-2 border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)] text-[var(--c-roadmap-node-fg)] ${className}`, children: /* @__PURE__ */ jsx65(Glyph, { size: 12, "aria-hidden": "true" }) });
8783
+ }
8784
+ function RoadmapLegendView({ legend, testIdPrefix = "roadmap" }) {
8785
+ return /* @__PURE__ */ jsx65("aside", { "data-testid": `${testIdPrefix}-legend`, "aria-label": "Legend", className: "min-w-0 rounded-[var(--radius)] border-2 border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-neutral-bg)] p-3 text-[var(--c-roadmap-neutral-fg)]", children: /* @__PURE__ */ jsx65("ul", { className: "m-0 flex list-none flex-wrap gap-3 p-0", children: legend.entries.map((entry) => /* @__PURE__ */ jsxs40("li", { className: "flex min-w-0 items-center gap-2 text-xs leading-normal", children: [
8786
+ /* @__PURE__ */ jsx65(RoadmapBadge, { name: entry.icon, tone: entry.tone }),
8787
+ /* @__PURE__ */ jsx65("span", { className: "[overflow-wrap:anywhere]", children: entry.label })
8788
+ ] }, entry.id)) }) });
8789
+ }
8790
+
8791
+ // src/components/organisms/roadmap/RoadmapNode.tsx
8792
+ import { jsx as jsx66, jsxs as jsxs41 } from "react/jsx-runtime";
8793
+ var nodeVariants = cva14("relative flex min-w-0 flex-col justify-center rounded-[var(--radius)] border-2 px-2 py-3 text-center text-xs leading-normal md:px-4 md:text-base text-[var(--c-roadmap-node-fg)]", {
8794
+ variants: {
8795
+ kind: { topic: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", subtopic: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", button: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", label: "border-transparent bg-transparent", title: "border-transparent bg-transparent font-semibold md:text-[28px]", paragraph: "text-left border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", legend: "border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]" },
8796
+ state: { todo: "", done: "[--c-roadmap-node-bg:var(--c-roadmap-done-bg)] [--c-roadmap-node-fg:var(--c-roadmap-done-fg)]", learning: "[--c-roadmap-node-bg:var(--c-roadmap-learning-bg)] [--c-roadmap-node-fg:var(--c-roadmap-learning-fg)]", skipped: "[--c-roadmap-node-bg:var(--c-roadmap-skipped-bg)] [--c-roadmap-node-fg:var(--c-roadmap-skipped-fg)]", locked: "opacity-60" }
8797
+ }
8798
+ });
8799
+ function RoadmapNodeView({ node, state, badge, testIdPrefix, onNodeClick, onAction }) {
8800
+ const locked = state === "locked";
8801
+ const nodeTone = resolveNodeTone(node);
8802
+ const label = /* @__PURE__ */ jsx66("span", { "data-testid": `${testIdPrefix}-node-label`, className: `[overflow-wrap:anywhere] ${state === "done" ? "line-through" : state === "learning" ? "underline" : ""}`, children: node.label });
8803
+ const interactiveClass = `block w-full min-w-0 text-inherit ${focusClass}`;
8804
+ const wholeBox = !locked && !node.action && !node.links?.length;
8805
+ const Box = wholeBox && node.href ? "a" : wholeBox && onNodeClick ? "button" : "div";
8806
+ const primary = Box !== "div" ? label : node.href && !locked ? /* @__PURE__ */ jsx66("a", { href: node.href, className: interactiveClass, onClick: () => onNodeClick?.(node), children: label }) : onNodeClick && !locked ? /* @__PURE__ */ jsx66("button", { type: "button", className: interactiveClass, onClick: () => onNodeClick(node), children: label }) : label;
8807
+ const icon = node.icon ?? (badge ? { name: badge.icon ?? "check", side: "right", tone: badge.tone } : void 0);
8808
+ return /* @__PURE__ */ jsx66("div", { style: toneStyle(nodeTone), className: "min-w-0", children: /* @__PURE__ */ jsxs41(Box, { style: { borderColor: node.kind === "title" || node.kind === "label" ? "transparent" : "var(--c-roadmap-border)" }, "data-roadmap-node-box": node.id, href: Box === "a" ? node.href : void 0, type: Box === "button" ? "button" : void 0, onClick: Box !== "div" ? () => onNodeClick?.(node) : void 0, className: `${nodeVariants({ kind: node.kind, state })} w-full ${Box !== "div" ? focusClass : ""}`, "aria-disabled": locked || void 0, title: node.kind === "topic" || node.kind === "subtopic" ? node.description : void 0, children: [
8809
+ icon && /* @__PURE__ */ jsx66(RoadmapBadge, { name: icon.name, tone: icon.tone ?? nodeTone, label: badge?.label ?? icon.name, className: `absolute top-1/2 -translate-y-1/2 ${icon.side === "left" ? "left-0 -translate-x-1/2" : "right-0 translate-x-1/2"}` }),
8810
+ primary,
8811
+ node.description && node.kind === "paragraph" && /* @__PURE__ */ jsx66("p", { className: "mt-2 [overflow-wrap:anywhere]", children: node.description }),
8812
+ node.links && /* @__PURE__ */ jsx66("ul", { className: "mt-2 list-none space-y-2 p-0", children: node.links.map((link, index) => /* @__PURE__ */ jsx66("li", { children: locked ? /* @__PURE__ */ jsx66("span", { children: link.label }) : /* @__PURE__ */ jsx66("a", { href: link.href, className: `underline [overflow-wrap:anywhere] ${focusClass}`, onClick: (e) => e.stopPropagation(), children: link.label }) }, index)) }),
8813
+ node.action && /* @__PURE__ */ jsx66("div", { style: toneStyle(node.action.tone ?? "primary"), className: "mt-3 min-w-0", children: node.action.href && !locked ? /* @__PURE__ */ jsx66("a", { href: node.action.href, className: `inline-block rounded px-2 py-2 bg-[var(--c-roadmap-node-bg)] text-[var(--c-roadmap-node-fg)] [overflow-wrap:anywhere] ${focusClass}`, children: node.action.label }) : /* @__PURE__ */ jsx66("button", { type: "button", disabled: locked, className: `max-w-full rounded px-2 py-2 bg-[var(--c-roadmap-node-bg)] text-[var(--c-roadmap-node-fg)] [overflow-wrap:anywhere] ${focusClass}`, onClick: (e) => {
8814
+ e.stopPropagation();
8815
+ if (node.action?.actionId) onAction?.(node.action.actionId, node);
8816
+ }, children: node.action.label }) })
8817
+ ] }) });
8818
+ }
8819
+
8820
+ // src/components/organisms/roadmap/RoadmapGroup.tsx
8821
+ import { Fragment as Fragment10, jsx as jsx67, jsxs as jsxs42 } from "react/jsx-runtime";
8822
+ function RoadmapGroupView({ group, testIdPrefix }) {
8823
+ const tone = resolveGroupTone(group);
8824
+ const appearance = tone === "accent" ? { "--c-roadmap-node-bg": "var(--c-roadmap-group-accent-bg)", "--c-roadmap-node-fg": "var(--c-roadmap-group-accent-fg)" } : toneStyle(tone);
8825
+ return /* @__PURE__ */ jsx67(Fragment10, { children: groupColumnRuns(group).map((run, index) => /* @__PURE__ */ jsxs42(
8826
+ "div",
8827
+ {
8828
+ "data-testid": `${testIdPrefix}-group`,
8829
+ "data-group-id": group.id,
8830
+ style: { borderColor: "var(--c-roadmap-border)", ...appearance, gridColumn: `${run.start} / span ${run.span}`, gridRow: `${group.from + 1} / span ${group.to - group.from + 1}` },
8831
+ className: "pointer-events-none relative z-0 min-w-0 self-stretch rounded-[var(--radius)] border-2 border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)] text-[var(--c-roadmap-node-fg)]",
8832
+ children: [
8833
+ index === 0 && group.title && /* @__PURE__ */ jsx67("span", { title: group.title, className: "absolute left-2 right-2 top-0 -translate-y-1/2 text-xs leading-normal", children: /* @__PURE__ */ jsx67("span", { className: "inline-block max-w-full overflow-hidden text-ellipsis whitespace-nowrap bg-[var(--c-roadmap-surface)] px-1 align-top text-[var(--c-roadmap-neutral-fg)]", children: group.title }) }),
8834
+ index === 0 && group.description && /* @__PURE__ */ jsx67("span", { className: "sr-only", children: group.description })
8835
+ ]
8836
+ },
8837
+ run.start
8838
+ )) });
8839
+ }
8840
+
8841
+ // src/components/organisms/roadmap/RoadmapEdgeLayer.tsx
8842
+ import { useId as useId4, useEffect as useEffect10, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState13 } from "react";
8843
+ import { jsx as jsx68, jsxs as jsxs43 } from "react/jsx-runtime";
8844
+ function Edge({ edge, path, instance, prefix }) {
8845
+ const pathRef = useRef5(null);
8846
+ const [midpoint, setMidpoint] = useState13();
8847
+ useLayoutEffect2(() => {
8848
+ const element = pathRef.current;
8849
+ if (edge.label && element?.getTotalLength && path.d) {
8850
+ const p = element.getPointAtLength(element.getTotalLength() / 2);
8851
+ setMidpoint({ x: p.x, y: p.y });
8852
+ } else setMidpoint(void 0);
8853
+ }, [path.d, edge.label]);
8854
+ const arrow = resolveEdgeArrow(edge), style = resolveEdgeStyle(edge);
8855
+ const marker = `${instance}-${edge.id}`;
8856
+ const color = edge.tone ? `var(--c-roadmap-${edge.tone}-edge)` : "var(--c-roadmap-edge)";
8857
+ return /* @__PURE__ */ jsxs43("g", { style: { color }, "data-roadmap-edge": edge.id, children: [
8858
+ arrow !== "none" && /* @__PURE__ */ jsx68("defs", { children: /* @__PURE__ */ jsx68("marker", { id: marker, viewBox: "0 0 8 8", refX: "8", refY: "4", markerWidth: "8", markerHeight: "8", markerUnits: "userSpaceOnUse", orient: "auto-start-reverse", children: /* @__PURE__ */ jsx68("path", { d: "M 0 0 L 8 4 L 0 8 Z", fill: "currentColor" }) }) }),
8859
+ /* @__PURE__ */ jsx68(
8860
+ "path",
8861
+ {
8862
+ ref: pathRef,
8863
+ "data-testid": `${prefix}-edge`,
8864
+ "data-edge-id": edge.id,
8865
+ "data-source": edge.source,
8866
+ "data-target": edge.target,
8867
+ d: path.d,
8868
+ fill: "none",
8869
+ stroke: "currentColor",
8870
+ strokeLinecap: "round",
8871
+ strokeLinejoin: "round",
8872
+ strokeDasharray: style === "solid" ? void 0 : style === "dotted" ? "0.8 4" : "0.8 8",
8873
+ className: "[stroke-width:2px] md:[stroke-width:3px]",
8874
+ markerStart: arrow === "both" ? `url(#${marker})` : void 0,
8875
+ markerEnd: arrow !== "none" ? `url(#${marker})` : void 0
8876
+ }
8877
+ ),
8878
+ edge.label && midpoint && /* @__PURE__ */ jsx68("foreignObject", { "data-testid": `${prefix}-edge-label`, "data-edge-id": edge.id, x: midpoint.x, y: midpoint.y, width: "1", height: "1", overflow: "visible", children: /* @__PURE__ */ jsx68(
8879
+ "div",
8880
+ {
8881
+ className: "w-max max-w-[160px] -translate-x-1/2 -translate-y-1/2 rounded-full bg-[var(--c-roadmap-edge-label-bg)] px-2 py-0.5 text-center text-xs leading-normal text-[var(--c-roadmap-edge-label-fg)] [overflow-wrap:anywhere]",
8882
+ style: { boxShadow: "0 0 0 2px var(--c-roadmap-surface)" },
8883
+ children: edge.label
8884
+ }
8885
+ ) })
8886
+ ] });
8887
+ }
8888
+ function RoadmapEdgeLayer({ containerRef, document: documentModel, testIdPrefix, debugPerf }) {
8889
+ const instance = `roadmap-${useId4().replace(/:/g, "")}`;
8890
+ const [measured, setMeasured] = useState13([]);
8891
+ useEffect10(() => {
8892
+ const container = containerRef.current;
8893
+ if (!container || !documentModel.edges.length) {
8894
+ setMeasured([]);
8895
+ return;
8896
+ }
8897
+ let disposed = false, frame = 0;
8898
+ const observed = /* @__PURE__ */ new Set();
8899
+ const measure = () => {
8900
+ frame = 0;
8901
+ if (disposed) return;
8902
+ const started = performance.now();
8903
+ const root = container.getBoundingClientRect();
8904
+ const boxes = /* @__PURE__ */ new Map();
8905
+ for (const wrapper of container.querySelectorAll("[data-roadmap-node]")) {
8906
+ if (wrapper.closest("[data-roadmap-grid]") !== container) continue;
8907
+ const id = wrapper.dataset.roadmapNode;
8908
+ const border = Array.from(wrapper.querySelectorAll("[data-roadmap-node-box]")).find((el) => el.dataset.roadmapNodeBox === id);
8909
+ const element = border ?? wrapper.firstElementChild ?? wrapper;
8910
+ for (const target of [wrapper, element]) if (!observed.has(target)) {
8911
+ observer?.observe(target);
8912
+ observed.add(target);
8913
+ }
8914
+ const rect = element.getBoundingClientRect();
8915
+ boxes.set(id, { left: rect.left - root.left, right: rect.right - root.left, top: rect.top - root.top, bottom: rect.bottom - root.top });
8916
+ }
8917
+ for (const element of observed) if (!container.contains(element)) {
8918
+ observer?.unobserve(element);
8919
+ observed.delete(element);
8920
+ }
8921
+ const obstacles = [...boxes.values()];
8922
+ const next = documentModel.edges.flatMap((edge) => {
8923
+ const source = boxes.get(edge.source), target = boxes.get(edge.target);
8924
+ return source && target ? [{ edge, path: routeEdge(source, target, edge, obstacles) }] : [];
8925
+ });
8926
+ setMeasured(next);
8927
+ if (debugPerf) performance.measure("roadmap:edges", { start: started, end: performance.now() });
8928
+ };
8929
+ const schedule = () => {
8930
+ if (!disposed && !frame) frame = requestAnimationFrame(measure);
8931
+ };
8932
+ const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(schedule);
8933
+ observer?.observe(container);
8934
+ const mutations = new MutationObserver((records) => {
8935
+ if (records.some((record) => (record.target instanceof Element ? record.target : record.target.parentElement)?.closest("[data-roadmap-node]"))) schedule();
8936
+ });
8937
+ mutations.observe(container, { childList: true, subtree: true, characterData: true, attributes: true });
8938
+ const fonts = container.ownerDocument.fonts;
8939
+ fonts?.ready.then(schedule);
8940
+ fonts?.addEventListener("loadingdone", schedule);
8941
+ schedule();
8942
+ return () => {
8943
+ disposed = true;
8944
+ cancelAnimationFrame(frame);
8945
+ observer?.disconnect();
8946
+ mutations.disconnect();
8947
+ fonts?.removeEventListener("loadingdone", schedule);
8948
+ };
8949
+ }, [containerRef, documentModel, debugPerf]);
8950
+ return /* @__PURE__ */ jsx68("svg", { "data-testid": `${testIdPrefix}-edges`, "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-[1] h-full w-full overflow-visible", children: measured.map((item) => /* @__PURE__ */ jsx68(Edge, { ...item, instance, prefix: testIdPrefix }, item.edge.id)) });
8951
+ }
8952
+
8953
+ // src/components/organisms/roadmap/Roadmap.tsx
8954
+ import { jsx as jsx69, jsxs as jsxs44 } from "react/jsx-runtime";
8955
+ function Roadmap({ document: document2, state, renderNode, onNodeClick, onAction, testIdPrefix = "roadmap", className = "", ariaLabel, debugPerf }) {
8956
+ const gridRef = useRef6(null);
8957
+ const { placements } = useMemo5(() => placeNodes(document2), [document2]);
8958
+ const badges = useMemo5(() => new Map(document2.legend?.entries.map((entry) => [entry.id, entry])), [document2.legend]);
8959
+ const legendPlacement = document2.legend ? resolveLegendPlacement(document2.legend) : void 0;
8960
+ const legend = document2.legend && /* @__PURE__ */ jsx69(RoadmapLegendView, { legend: document2.legend, testIdPrefix });
8961
+ return /* @__PURE__ */ jsxs44("section", { "data-testid": testIdPrefix, "aria-label": ariaLabel ?? document2.title ?? "Roadmap", className: `min-w-0 bg-[var(--c-roadmap-surface)] p-3 text-[var(--c-roadmap-neutral-fg)] ${className}`, children: [
8962
+ legendPlacement === "top" && /* @__PURE__ */ jsx69("div", { className: "mb-6", children: legend }),
8963
+ /* @__PURE__ */ jsxs44("div", { ref: gridRef, "data-roadmap-grid": "", "data-testid": `${testIdPrefix}-grid`, role: "list", "aria-label": "Roadmap nodes", className: "relative isolate grid min-w-0 grid-cols-3 items-center gap-x-3 gap-y-2 md:gap-x-8", children: [
8964
+ /* @__PURE__ */ jsx69(RoadmapEdgeLayer, { containerRef: gridRef, document: document2, testIdPrefix, debugPerf }),
8965
+ document2.groups.map((group) => /* @__PURE__ */ jsx69(RoadmapGroupView, { group, testIdPrefix }, group.id)),
8966
+ placements.map(({ node, row, column }) => {
8967
+ const resolvedState = resolveNodeState(node, state);
8968
+ return /* @__PURE__ */ jsx69(
8969
+ "div",
8970
+ {
8971
+ role: "listitem",
8972
+ "data-testid": `${testIdPrefix}-node`,
8973
+ "data-node-id": node.id,
8974
+ "data-roadmap-node": node.id,
8975
+ "data-state": resolvedState,
8976
+ style: { gridColumn: `${column.start} / span ${column.span}`, gridRow: `${row.start} / span 1` },
8977
+ className: "relative z-10 min-w-0 px-2 py-2",
8978
+ children: node.kind === "legend" ? legendPlacement === "inline" ? legend : null : renderNode ? renderNode(node, resolvedState) : /* @__PURE__ */ jsx69(RoadmapNodeView, { node, state: resolvedState, badge: node.badge ? badges.get(node.badge) : void 0, onNodeClick, onAction, testIdPrefix })
8979
+ },
8980
+ node.id
8981
+ );
8982
+ })
8983
+ ] }),
8984
+ legendPlacement === "bottom" && /* @__PURE__ */ jsx69("div", { className: "mt-6", children: legend })
8985
+ ] });
8986
+ }
8718
8987
 
8719
8988
  // src/hooks/use-iframe-auth.ts
8720
8989
  import { useContext as useContext9 } from "react";
@@ -8739,9 +9008,9 @@ function useIframeNavigate() {
8739
9008
  import {
8740
9009
  createContext as createContext10,
8741
9010
  useContext as useContext10,
8742
- useMemo as useMemo5
9011
+ useMemo as useMemo6
8743
9012
  } from "react";
8744
- import { jsx as jsx65 } from "react/jsx-runtime";
9013
+ import { jsx as jsx70 } from "react/jsx-runtime";
8745
9014
  var FeatureFlagContext = createContext10({
8746
9015
  flags: {},
8747
9016
  isEnabled: () => false
@@ -8750,14 +9019,14 @@ var FeatureFlagProvider = ({
8750
9019
  children,
8751
9020
  flags
8752
9021
  }) => {
8753
- const value = useMemo5(
9022
+ const value = useMemo6(
8754
9023
  () => ({
8755
9024
  flags,
8756
9025
  isEnabled: (flag) => Boolean(flags[flag])
8757
9026
  }),
8758
9027
  [flags]
8759
9028
  );
8760
- return /* @__PURE__ */ jsx65(FeatureFlagContext.Provider, { value, children });
9029
+ return /* @__PURE__ */ jsx70(FeatureFlagContext.Provider, { value, children });
8761
9030
  };
8762
9031
  var useFeatureFlag = (flag) => {
8763
9032
  const { isEnabled } = useContext10(FeatureFlagContext);
@@ -8982,6 +9251,7 @@ export {
8982
9251
  ResizableHandle,
8983
9252
  ResizablePanel,
8984
9253
  ResizablePanelGroup,
9254
+ Roadmap,
8985
9255
  ScrollArea,
8986
9256
  ScrollBar,
8987
9257
  Select,
@@ -19,6 +19,33 @@
19
19
  */
20
20
 
21
21
  @theme inline {
22
+ /* Origin: agent — Roadmap token mappings. */
23
+ --color-c-roadmap-surface: var(--c-roadmap-surface);
24
+ --color-c-roadmap-border: var(--c-roadmap-border);
25
+ --color-c-roadmap-focus: var(--c-roadmap-focus);
26
+ --color-c-roadmap-done-bg: var(--c-roadmap-done-bg);
27
+ --color-c-roadmap-done-fg: var(--c-roadmap-done-fg);
28
+ --color-c-roadmap-learning-bg: var(--c-roadmap-learning-bg);
29
+ --color-c-roadmap-learning-fg: var(--c-roadmap-learning-fg);
30
+ --color-c-roadmap-skipped-bg: var(--c-roadmap-skipped-bg);
31
+ --color-c-roadmap-skipped-fg: var(--c-roadmap-skipped-fg);
32
+ --color-c-roadmap-primary-bg: var(--c-roadmap-primary-bg);
33
+ --color-c-roadmap-primary-fg: var(--c-roadmap-primary-fg);
34
+ --color-c-roadmap-secondary-bg: var(--c-roadmap-secondary-bg);
35
+ --color-c-roadmap-secondary-fg: var(--c-roadmap-secondary-fg);
36
+ --color-c-roadmap-accent-bg: var(--c-roadmap-accent-bg);
37
+ --color-c-roadmap-accent-fg: var(--c-roadmap-accent-fg);
38
+ --color-c-roadmap-muted-bg: var(--c-roadmap-muted-bg);
39
+ --color-c-roadmap-muted-fg: var(--c-roadmap-muted-fg);
40
+ --color-c-roadmap-neutral-bg: var(--c-roadmap-neutral-bg);
41
+ --color-c-roadmap-neutral-fg: var(--c-roadmap-neutral-fg);
42
+ --color-c-roadmap-success-bg: var(--c-roadmap-success-bg);
43
+ --color-c-roadmap-success-fg: var(--c-roadmap-success-fg);
44
+ --color-c-roadmap-info-bg: var(--c-roadmap-info-bg);
45
+ --color-c-roadmap-info-fg: var(--c-roadmap-info-fg);
46
+ --color-c-roadmap-danger-bg: var(--c-roadmap-danger-bg);
47
+ --color-c-roadmap-danger-fg: var(--c-roadmap-danger-fg);
48
+
22
49
  /* ─── Colours ──────────────────────────────────────────────────────────── */
23
50
  --color-background: var(--background);
24
51
  --color-foreground: var(--foreground);
@@ -1706,3 +1706,45 @@
1706
1706
  box-shadow: var(--c-appshell-focus-ring);
1707
1707
  transition: none;
1708
1708
  }
1709
+
1710
+ /* Origin: agent — Roadmap. Q1: dark default and dark only. */
1711
+ :root {
1712
+ --c-roadmap-group-accent-bg: var(--s-surface-elevated);
1713
+ --c-roadmap-group-accent-fg: var(--s-ink-primary);
1714
+ --c-roadmap-edge: var(--s-ink-muted);
1715
+ --c-roadmap-edge-label-bg: var(--s-surface-elevated);
1716
+ --c-roadmap-edge-label-fg: var(--s-ink-secondary);
1717
+ --c-roadmap-primary-edge: var(--s-brand-solid);
1718
+ --c-roadmap-secondary-edge: var(--s-ink-secondary);
1719
+ --c-roadmap-accent-edge: var(--s-ink-primary);
1720
+ --c-roadmap-muted-edge: var(--s-ink-muted);
1721
+ --c-roadmap-neutral-edge: var(--s-ink-secondary);
1722
+ --c-roadmap-success-edge: var(--s-success-fg);
1723
+ --c-roadmap-info-edge: var(--s-info-fg);
1724
+ --c-roadmap-danger-edge: var(--s-danger-fg);
1725
+ --c-roadmap-surface: var(--s-surface-page);
1726
+ --c-roadmap-border: var(--s-border-strong);
1727
+ --c-roadmap-focus: var(--s-border-focus);
1728
+ --c-roadmap-done-bg: var(--s-surface-raised);
1729
+ --c-roadmap-done-fg: var(--s-ink-secondary);
1730
+ --c-roadmap-learning-bg: var(--s-info-subtle);
1731
+ --c-roadmap-learning-fg: var(--s-info-fg);
1732
+ --c-roadmap-skipped-bg: var(--s-surface-elevated);
1733
+ --c-roadmap-skipped-fg: var(--s-ink-muted);
1734
+ --c-roadmap-primary-bg: var(--s-brand-solid);
1735
+ --c-roadmap-primary-fg: var(--s-ink-inverse);
1736
+ --c-roadmap-secondary-bg: var(--s-brand-subtle);
1737
+ --c-roadmap-secondary-fg: var(--s-ink-primary);
1738
+ --c-roadmap-accent-bg: var(--s-ink-primary);
1739
+ --c-roadmap-accent-fg: var(--s-ink-inverse);
1740
+ --c-roadmap-muted-bg: var(--s-surface-raised);
1741
+ --c-roadmap-muted-fg: var(--s-ink-muted);
1742
+ --c-roadmap-neutral-bg: var(--s-surface-panel);
1743
+ --c-roadmap-neutral-fg: var(--s-ink-primary);
1744
+ --c-roadmap-success-bg: var(--s-success-subtle);
1745
+ --c-roadmap-success-fg: var(--s-success-fg);
1746
+ --c-roadmap-info-bg: var(--s-info-subtle);
1747
+ --c-roadmap-info-fg: var(--s-info-fg);
1748
+ --c-roadmap-danger-bg: var(--s-danger-subtle);
1749
+ --c-roadmap-danger-fg: var(--s-danger-fg);
1750
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devfellowship/components",
3
- "version": "3.3.0",
3
+ "version": "3.5.0",
4
4
  "description": "DFL Design System — UI components, hooks, utils and providers",
5
5
  "type": "module",
6
6
  "sideEffects": [