@devfellowship/components 3.2.4 → 3.4.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
@@ -118,7 +118,14 @@ __export(src_exports, {
118
118
  ContextMenuSubContent: () => ContextMenuSubContent,
119
119
  ContextMenuSubTrigger: () => ContextMenuSubTrigger,
120
120
  ContextMenuTrigger: () => ContextMenuTrigger,
121
+ DEFAULT_EDGE_ARROW: () => DEFAULT_EDGE_ARROW,
122
+ DEFAULT_EDGE_ROUTE: () => DEFAULT_EDGE_ROUTE,
123
+ DEFAULT_EDGE_STYLE: () => DEFAULT_EDGE_STYLE,
124
+ DEFAULT_GROUP_TONE: () => DEFAULT_GROUP_TONE,
125
+ DEFAULT_LEGEND_PLACEMENT: () => DEFAULT_LEGEND_PLACEMENT,
121
126
  DEFAULT_NAME_COL_WIDTH: () => DEFAULT_NAME_COL_WIDTH,
127
+ DEFAULT_NODE_SPAN: () => DEFAULT_NODE_SPAN,
128
+ DEFAULT_NODE_STATE: () => DEFAULT_NODE_STATE,
122
129
  DflRemote: () => DflRemote,
123
130
  Dialog: () => Dialog,
124
131
  DialogClose: () => DialogClose,
@@ -196,6 +203,7 @@ __export(src_exports, {
196
203
  MenubarSubContent: () => MenubarSubContent,
197
204
  MenubarSubTrigger: () => MenubarSubTrigger,
198
205
  MenubarTrigger: () => MenubarTrigger,
206
+ NODE_KIND_DEFAULT_TONE: () => NODE_KIND_DEFAULT_TONE,
199
207
  NavigationMenu: () => NavigationMenu,
200
208
  NavigationMenuContent: () => NavigationMenuContent,
201
209
  NavigationMenuIndicator: () => NavigationMenuIndicator,
@@ -219,12 +227,24 @@ __export(src_exports, {
219
227
  Progress: () => Progress,
220
228
  ProtectedRoute: () => ProtectedRoute,
221
229
  PublishDrawer: () => PublishDrawer,
230
+ ROADMAP_ARROWS: () => ROADMAP_ARROWS,
231
+ ROADMAP_COLUMNS: () => ROADMAP_COLUMNS,
232
+ ROADMAP_EDGE_LABEL_MAX: () => ROADMAP_EDGE_LABEL_MAX,
233
+ ROADMAP_EDGE_ROUTES: () => ROADMAP_EDGE_ROUTES,
234
+ ROADMAP_EDGE_STYLES: () => ROADMAP_EDGE_STYLES,
235
+ ROADMAP_LABEL_MAX: () => ROADMAP_LABEL_MAX,
236
+ ROADMAP_LEGEND_PLACEMENTS: () => ROADMAP_LEGEND_PLACEMENTS,
237
+ ROADMAP_NODE_ID_PATTERN: () => ROADMAP_NODE_ID_PATTERN,
238
+ ROADMAP_NODE_KINDS: () => ROADMAP_NODE_KINDS,
239
+ ROADMAP_NODE_STATES: () => ROADMAP_NODE_STATES,
240
+ ROADMAP_TONES: () => ROADMAP_TONES,
222
241
  RadioGroup: () => RadioGroup4,
223
242
  RadioGroupItem: () => RadioGroupItem,
224
243
  RadioGroupRow: () => RadioGroupRow,
225
244
  ResizableHandle: () => ResizableHandle,
226
245
  ResizablePanel: () => ResizablePanel,
227
246
  ResizablePanelGroup: () => ResizablePanelGroup,
247
+ Roadmap: () => Roadmap,
228
248
  ScrollArea: () => ScrollArea,
229
249
  ScrollBar: () => ScrollBar,
230
250
  Select: () => Select,
@@ -312,19 +332,55 @@ __export(src_exports, {
312
332
  buttonVariants: () => buttonVariants,
313
333
  clampPct: () => clampPct,
314
334
  cn: () => cn,
335
+ columnIndex: () => columnIndex,
336
+ columnsCovered: () => columnsCovered,
337
+ columnsUsed: () => columnsUsed,
315
338
  filterPublishableAccounts: () => filterPublishableAccounts,
339
+ firstFreeRow: () => firstFreeRow,
316
340
  getInitials: () => getInitials,
341
+ gridColumnFor: () => gridColumnFor,
342
+ groupColumnRuns: () => groupColumnRuns,
343
+ groupRanges: () => groupRanges,
317
344
  iconButtonVariants: () => iconButtonVariants,
318
345
  isAllowedOrigin: () => isAllowedOrigin,
319
346
  kbdVariants: () => kbdVariants,
320
347
  labelVariants: () => labelVariants,
348
+ maxRowOf: () => maxRowOf,
321
349
  memberHueIndex: () => memberHueIndex,
322
350
  memberHueVar: () => memberHueVar,
323
351
  navigationMenuTriggerStyle: () => navigationMenuTriggerStyle,
352
+ nodesByRow: () => nodesByRow,
353
+ parseRoadmapDocument: () => parseRoadmapDocument,
354
+ parseRoadmapStateOverlay: () => parseRoadmapStateOverlay,
324
355
  parseTags: () => parseTags,
356
+ placeNodes: () => placeNodes,
357
+ resolveEdgeArrow: () => resolveEdgeArrow,
358
+ resolveEdgeRoute: () => resolveEdgeRoute,
359
+ resolveEdgeStyle: () => resolveEdgeStyle,
360
+ resolveGroupColumns: () => resolveGroupColumns,
361
+ resolveGroupTone: () => resolveGroupTone,
362
+ resolveLegendPlacement: () => resolveLegendPlacement,
325
363
  resolveNameColWidth: () => resolveNameColWidth,
364
+ resolveNodeSpan: () => resolveNodeSpan,
365
+ resolveNodeState: () => resolveNodeState,
366
+ resolveNodeTone: () => resolveNodeTone,
326
367
  resolveWeekCount: () => resolveWeekCount,
327
368
  resolveWeekLabels: () => resolveWeekLabels,
369
+ roadmapActionSchema: () => roadmapActionSchema,
370
+ roadmapDocumentSchema: () => roadmapDocumentSchema,
371
+ roadmapEdgeSchema: () => roadmapEdgeSchema,
372
+ roadmapGroupSchema: () => roadmapGroupSchema,
373
+ roadmapIconSchema: () => roadmapIconSchema,
374
+ roadmapLegendEntrySchema: () => roadmapLegendEntrySchema,
375
+ roadmapLegendSchema: () => roadmapLegendSchema,
376
+ roadmapLinkSchema: () => roadmapLinkSchema,
377
+ roadmapNodeSchema: () => roadmapNodeSchema,
378
+ roadmapNodeStateSchema: () => roadmapNodeStateSchema,
379
+ roadmapStateOverlaySchema: () => roadmapStateOverlaySchema,
380
+ roadmapStateSchema: () => roadmapStateSchema,
381
+ rowsOf: () => rowsOf,
382
+ safeParseRoadmapDocument: () => safeParseRoadmapDocument,
383
+ safeParseRoadmapStateOverlay: () => safeParseRoadmapStateOverlay,
328
384
  stageProgress: () => stageProgress,
329
385
  toast: () => toast,
330
386
  toggleVariants: () => toggleVariants,
@@ -338,6 +394,7 @@ __export(src_exports, {
338
394
  useSession: () => useSession,
339
395
  useSidebar: () => useSidebar,
340
396
  useToast: () => useToast,
397
+ validateNoOverlap: () => validateNoOverlap,
341
398
  validatePublishForm: () => validatePublishForm
342
399
  });
343
400
  module.exports = __toCommonJS(src_exports);
@@ -8637,17 +8694,540 @@ function PublishDrawer({
8637
8694
  ) });
8638
8695
  }
8639
8696
 
8640
- // src/hooks/use-iframe-auth.ts
8697
+ // src/components/organisms/roadmap/contract.ts
8698
+ var import_zod = require("zod");
8699
+ var ROADMAP_COLUMNS = ["left", "center", "right"];
8700
+ var ROADMAP_TONES = [
8701
+ "primary",
8702
+ "secondary",
8703
+ "accent",
8704
+ "muted",
8705
+ "success",
8706
+ "info",
8707
+ "danger",
8708
+ "neutral"
8709
+ ];
8710
+ var ROADMAP_NODE_KINDS = [
8711
+ "topic",
8712
+ "subtopic",
8713
+ "label",
8714
+ "title",
8715
+ "paragraph",
8716
+ "button",
8717
+ "legend"
8718
+ ];
8719
+ var ROADMAP_NODE_STATES = ["todo", "done", "learning", "skipped", "locked"];
8720
+ var ROADMAP_EDGE_STYLES = ["solid", "dashed", "dotted"];
8721
+ var ROADMAP_EDGE_ROUTES = ["auto", "straight", "elbow", "curve"];
8722
+ var ROADMAP_ARROWS = ["none", "end", "both"];
8723
+ var ROADMAP_LEGEND_PLACEMENTS = ["top", "bottom", "inline"];
8724
+ var DEFAULT_NODE_SPAN = 1;
8725
+ var DEFAULT_EDGE_STYLE = "dashed";
8726
+ var DEFAULT_EDGE_ROUTE = "auto";
8727
+ var DEFAULT_EDGE_ARROW = "none";
8728
+ var DEFAULT_GROUP_TONE = "neutral";
8729
+ var DEFAULT_LEGEND_PLACEMENT = "top";
8730
+ var DEFAULT_NODE_STATE = "todo";
8731
+ var NODE_KIND_DEFAULT_TONE = {
8732
+ topic: "primary",
8733
+ subtopic: "secondary",
8734
+ button: "primary",
8735
+ label: "neutral",
8736
+ title: "neutral",
8737
+ paragraph: "neutral",
8738
+ legend: "neutral"
8739
+ };
8740
+ var resolveNodeSpan = (node) => node.span ?? DEFAULT_NODE_SPAN;
8741
+ var resolveNodeTone = (node) => node.tone ?? NODE_KIND_DEFAULT_TONE[node.kind];
8742
+ var resolveEdgeStyle = (edge) => edge.style ?? DEFAULT_EDGE_STYLE;
8743
+ var resolveEdgeRoute = (edge) => edge.route ?? DEFAULT_EDGE_ROUTE;
8744
+ var resolveEdgeArrow = (edge) => edge.arrow ?? DEFAULT_EDGE_ARROW;
8745
+ var resolveGroupTone = (group) => group.tone ?? DEFAULT_GROUP_TONE;
8746
+ var resolveGroupColumns = (group) => group.columns && group.columns.length > 0 ? group.columns : [...ROADMAP_COLUMNS];
8747
+ var resolveLegendPlacement = (legend) => legend.placement ?? DEFAULT_LEGEND_PLACEMENT;
8748
+ var resolveNodeState = (node, overlay) => overlay?.nodes[node.id] ?? node.state ?? DEFAULT_NODE_STATE;
8749
+ var ROADMAP_NODE_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
8750
+ var ROADMAP_EDGE_LABEL_MAX = 40;
8751
+ var ROADMAP_LABEL_MAX = 200;
8752
+ var columnSchema = import_zod.z.enum(ROADMAP_COLUMNS);
8753
+ var toneSchema = import_zod.z.enum(ROADMAP_TONES);
8754
+ var idSchema = import_zod.z.string().regex(ROADMAP_NODE_ID_PATTERN, "must match /^[A-Za-z0-9_-]{1,64}$/");
8755
+ var metaSchema = import_zod.z.record(import_zod.z.unknown());
8756
+ var roadmapIconSchema = import_zod.z.object({
8757
+ name: import_zod.z.string().min(1).max(64),
8758
+ side: import_zod.z.enum(["left", "right"]),
8759
+ tone: toneSchema.optional()
8760
+ }).strict();
8761
+ var roadmapActionSchema = import_zod.z.object({
8762
+ label: import_zod.z.string().min(1).max(80),
8763
+ href: import_zod.z.string().min(1).optional(),
8764
+ actionId: import_zod.z.string().min(1).optional(),
8765
+ tone: toneSchema.optional()
8766
+ }).strict().superRefine((action, ctx) => {
8767
+ const given = [action.href, action.actionId].filter((v) => v !== void 0).length;
8768
+ if (given !== 1) {
8769
+ ctx.addIssue({
8770
+ code: import_zod.z.ZodIssueCode.custom,
8771
+ path: ["href"],
8772
+ message: "an action needs exactly one of `href` or `actionId`"
8773
+ });
8774
+ }
8775
+ });
8776
+ var roadmapLinkSchema = import_zod.z.object({ label: import_zod.z.string().min(1).max(120), href: import_zod.z.string().min(1) }).strict();
8777
+ var roadmapNodeStateSchema = import_zod.z.enum(ROADMAP_NODE_STATES);
8778
+ var roadmapNodeSchema = import_zod.z.object({
8779
+ id: idSchema,
8780
+ column: columnSchema,
8781
+ order: import_zod.z.number().int().min(0),
8782
+ span: import_zod.z.union([import_zod.z.literal(1), import_zod.z.literal(2), import_zod.z.literal(3)]).optional(),
8783
+ kind: import_zod.z.enum(ROADMAP_NODE_KINDS),
8784
+ label: import_zod.z.string().min(1).max(ROADMAP_LABEL_MAX),
8785
+ description: import_zod.z.string().max(2e3).optional(),
8786
+ tone: toneSchema.optional(),
8787
+ icon: roadmapIconSchema.optional(),
8788
+ badge: import_zod.z.string().min(1).optional(),
8789
+ state: roadmapNodeStateSchema.optional(),
8790
+ action: roadmapActionSchema.optional(),
8791
+ href: import_zod.z.string().min(1).optional(),
8792
+ links: import_zod.z.array(roadmapLinkSchema).optional(),
8793
+ meta: metaSchema.optional()
8794
+ }).strict();
8795
+ var roadmapEdgeSchema = import_zod.z.object({
8796
+ id: idSchema,
8797
+ source: import_zod.z.string().min(1),
8798
+ target: import_zod.z.string().min(1),
8799
+ style: import_zod.z.enum(ROADMAP_EDGE_STYLES).optional(),
8800
+ label: import_zod.z.string().min(1).max(ROADMAP_EDGE_LABEL_MAX).optional(),
8801
+ route: import_zod.z.enum(ROADMAP_EDGE_ROUTES).optional(),
8802
+ arrow: import_zod.z.enum(ROADMAP_ARROWS).optional(),
8803
+ tone: toneSchema.optional()
8804
+ }).strict();
8805
+ var roadmapGroupSchema = import_zod.z.object({
8806
+ id: idSchema,
8807
+ title: import_zod.z.string().min(1).max(ROADMAP_LABEL_MAX).optional(),
8808
+ tone: toneSchema.optional(),
8809
+ from: import_zod.z.number().int().min(0),
8810
+ to: import_zod.z.number().int().min(0),
8811
+ columns: import_zod.z.array(columnSchema).min(1).max(3).optional(),
8812
+ description: import_zod.z.string().max(2e3).optional()
8813
+ }).strict();
8814
+ var roadmapLegendEntrySchema = import_zod.z.object({
8815
+ id: idSchema,
8816
+ label: import_zod.z.string().min(1).max(ROADMAP_LABEL_MAX),
8817
+ tone: toneSchema,
8818
+ icon: import_zod.z.string().min(1).max(64).optional()
8819
+ }).strict();
8820
+ var roadmapLegendSchema = import_zod.z.object({
8821
+ entries: import_zod.z.array(roadmapLegendEntrySchema).min(1),
8822
+ placement: import_zod.z.enum(ROADMAP_LEGEND_PLACEMENTS).optional()
8823
+ }).strict();
8824
+ function columnsCovered(node) {
8825
+ const span = node.span ?? DEFAULT_NODE_SPAN;
8826
+ if (span === 3) return [...ROADMAP_COLUMNS];
8827
+ const start = ROADMAP_COLUMNS.indexOf(node.column);
8828
+ return ROADMAP_COLUMNS.slice(start, start + span);
8829
+ }
8830
+ var roadmapDocumentBaseSchema = import_zod.z.object({
8831
+ version: import_zod.z.literal("roadmap/v1"),
8832
+ title: import_zod.z.string().min(1).max(ROADMAP_LABEL_MAX).optional(),
8833
+ direction: import_zod.z.literal("down"),
8834
+ columns: import_zod.z.literal(3),
8835
+ nodes: import_zod.z.array(roadmapNodeSchema),
8836
+ edges: import_zod.z.array(roadmapEdgeSchema),
8837
+ groups: import_zod.z.array(roadmapGroupSchema),
8838
+ legend: roadmapLegendSchema.optional(),
8839
+ meta: metaSchema.optional()
8840
+ }).strict();
8841
+ var roadmapDocumentSchema = roadmapDocumentBaseSchema.superRefine((doc, ctx) => {
8842
+ const nodeIndexById = /* @__PURE__ */ new Map();
8843
+ doc.nodes.forEach((node, i) => {
8844
+ if (nodeIndexById.has(node.id)) {
8845
+ ctx.addIssue({
8846
+ code: import_zod.z.ZodIssueCode.custom,
8847
+ path: ["nodes", i, "id"],
8848
+ message: `duplicate node id "${node.id}" (first seen at nodes[${nodeIndexById.get(node.id)}])`
8849
+ });
8850
+ return;
8851
+ }
8852
+ nodeIndexById.set(node.id, i);
8853
+ });
8854
+ const cellOwner = /* @__PURE__ */ new Map();
8855
+ doc.nodes.forEach((node, i) => {
8856
+ const span = node.span ?? DEFAULT_NODE_SPAN;
8857
+ if (span === 2 && node.column === "right") {
8858
+ ctx.addIssue({
8859
+ code: import_zod.z.ZodIssueCode.custom,
8860
+ path: ["nodes", i, "span"],
8861
+ message: 'a `span: 2` node at column "right" runs past the last track'
8862
+ });
8863
+ return;
8864
+ }
8865
+ for (const column of columnsCovered(node)) {
8866
+ const key = `${column}:${node.order}`;
8867
+ const owner = cellOwner.get(key);
8868
+ if (owner !== void 0) {
8869
+ ctx.addIssue({
8870
+ code: import_zod.z.ZodIssueCode.custom,
8871
+ path: ["nodes", i, "order"],
8872
+ message: `cell (${column}, ${node.order}) is already taken by nodes[${owner}] ("${doc.nodes[owner].id}")`
8873
+ });
8874
+ continue;
8875
+ }
8876
+ cellOwner.set(key, i);
8877
+ }
8878
+ });
8879
+ const edgeIds = /* @__PURE__ */ new Set();
8880
+ doc.edges.forEach((edge, i) => {
8881
+ if (edgeIds.has(edge.id)) {
8882
+ ctx.addIssue({
8883
+ code: import_zod.z.ZodIssueCode.custom,
8884
+ path: ["edges", i, "id"],
8885
+ message: `duplicate edge id "${edge.id}"`
8886
+ });
8887
+ }
8888
+ edgeIds.add(edge.id);
8889
+ if (!nodeIndexById.has(edge.source)) {
8890
+ ctx.addIssue({
8891
+ code: import_zod.z.ZodIssueCode.custom,
8892
+ path: ["edges", i, "source"],
8893
+ message: `edge source "${edge.source}" is not a node in this document`
8894
+ });
8895
+ }
8896
+ if (!nodeIndexById.has(edge.target)) {
8897
+ ctx.addIssue({
8898
+ code: import_zod.z.ZodIssueCode.custom,
8899
+ path: ["edges", i, "target"],
8900
+ message: `edge target "${edge.target}" is not a node in this document`
8901
+ });
8902
+ }
8903
+ if (edge.source === edge.target) {
8904
+ ctx.addIssue({
8905
+ code: import_zod.z.ZodIssueCode.custom,
8906
+ path: ["edges", i, "target"],
8907
+ message: `edge "${edge.id}" is a self-loop`
8908
+ });
8909
+ }
8910
+ });
8911
+ const groupIds = /* @__PURE__ */ new Set();
8912
+ doc.groups.forEach((group, i) => {
8913
+ if (groupIds.has(group.id)) {
8914
+ ctx.addIssue({
8915
+ code: import_zod.z.ZodIssueCode.custom,
8916
+ path: ["groups", i, "id"],
8917
+ message: `duplicate group id "${group.id}"`
8918
+ });
8919
+ }
8920
+ groupIds.add(group.id);
8921
+ if (group.from > group.to) {
8922
+ ctx.addIssue({
8923
+ code: import_zod.z.ZodIssueCode.custom,
8924
+ path: ["groups", i, "to"],
8925
+ message: `group "${group.id}" has from (${group.from}) greater than to (${group.to})`
8926
+ });
8927
+ }
8928
+ });
8929
+ for (let i = 0; i < doc.groups.length; i += 1) {
8930
+ const a = doc.groups[i];
8931
+ if (a.from > a.to) continue;
8932
+ const aColumns = new Set(resolveGroupColumns(a));
8933
+ for (let j = i + 1; j < doc.groups.length; j += 1) {
8934
+ const b = doc.groups[j];
8935
+ if (b.from > b.to) continue;
8936
+ const rowsOverlap = a.from <= b.to && b.from <= a.to;
8937
+ if (!rowsOverlap) continue;
8938
+ const shared = resolveGroupColumns(b).filter((c) => aColumns.has(c));
8939
+ if (shared.length === 0) continue;
8940
+ ctx.addIssue({
8941
+ code: import_zod.z.ZodIssueCode.custom,
8942
+ path: ["groups", j, "from"],
8943
+ message: `group "${b.id}" overlaps rows ${a.from}..${a.to} of group "${a.id}" on column(s) ${shared.join(", ")}`
8944
+ });
8945
+ }
8946
+ }
8947
+ const legendIds = /* @__PURE__ */ new Set();
8948
+ doc.legend?.entries.forEach((entry, i) => {
8949
+ if (legendIds.has(entry.id)) {
8950
+ ctx.addIssue({
8951
+ code: import_zod.z.ZodIssueCode.custom,
8952
+ path: ["legend", "entries", i, "id"],
8953
+ message: `duplicate legend entry id "${entry.id}"`
8954
+ });
8955
+ }
8956
+ legendIds.add(entry.id);
8957
+ });
8958
+ doc.nodes.forEach((node, i) => {
8959
+ if (node.badge === void 0) return;
8960
+ if (!legendIds.has(node.badge)) {
8961
+ ctx.addIssue({
8962
+ code: import_zod.z.ZodIssueCode.custom,
8963
+ path: ["nodes", i, "badge"],
8964
+ message: `badge "${node.badge}" is not a legend entry of this document`
8965
+ });
8966
+ }
8967
+ });
8968
+ });
8969
+ var roadmapStateOverlaySchema = import_zod.z.object({
8970
+ version: import_zod.z.literal("roadmap-state/v1"),
8971
+ nodes: import_zod.z.record(roadmapNodeStateSchema)
8972
+ }).strict();
8973
+ var roadmapStateSchema = roadmapStateOverlaySchema;
8974
+ function parseRoadmapDocument(input) {
8975
+ return roadmapDocumentSchema.parse(input);
8976
+ }
8977
+ function safeParseRoadmapDocument(input) {
8978
+ return roadmapDocumentSchema.safeParse(input);
8979
+ }
8980
+ function parseRoadmapStateOverlay(input) {
8981
+ return roadmapStateOverlaySchema.parse(input);
8982
+ }
8983
+ function safeParseRoadmapStateOverlay(input) {
8984
+ return roadmapStateOverlaySchema.safeParse(input);
8985
+ }
8986
+
8987
+ // src/components/organisms/roadmap/layout.ts
8988
+ function columnIndex(column) {
8989
+ return ROADMAP_COLUMNS.indexOf(column);
8990
+ }
8991
+ function gridColumnFor(node) {
8992
+ const span = resolveNodeSpan(node);
8993
+ if (span === 3) return { start: 1, span: 3 };
8994
+ return { start: columnIndex(node.column) + 1, span };
8995
+ }
8996
+ function rowsOf(doc) {
8997
+ const rows = /* @__PURE__ */ new Set();
8998
+ for (const node of doc.nodes) rows.add(node.order);
8999
+ return [...rows].sort((a, b) => a - b);
9000
+ }
9001
+ function maxRowOf(doc) {
9002
+ return doc.nodes.reduce((max, node) => Math.max(max, node.order), -1);
9003
+ }
9004
+ function nodesByRow(doc) {
9005
+ const byRow = /* @__PURE__ */ new Map();
9006
+ for (const node of doc.nodes) {
9007
+ const bucket = byRow.get(node.order);
9008
+ if (bucket) bucket.push(node);
9009
+ else byRow.set(node.order, [node]);
9010
+ }
9011
+ for (const bucket of byRow.values()) {
9012
+ bucket.sort((a, b) => {
9013
+ const byColumn = columnIndex(a.column) - columnIndex(b.column);
9014
+ return byColumn !== 0 ? byColumn : a.id.localeCompare(b.id);
9015
+ });
9016
+ }
9017
+ return byRow;
9018
+ }
9019
+ function columnsUsed(doc) {
9020
+ const used = /* @__PURE__ */ new Set();
9021
+ for (const node of doc.nodes) for (const column of columnsCovered(node)) used.add(column);
9022
+ return ROADMAP_COLUMNS.filter((column) => used.has(column));
9023
+ }
9024
+ function groupRanges(doc) {
9025
+ return doc.groups.map((group) => {
9026
+ const columns = resolveGroupColumns(group);
9027
+ const indices = columns.map(columnIndex).sort((a, b) => a - b);
9028
+ const start = indices[0];
9029
+ const end = indices[indices.length - 1];
9030
+ return {
9031
+ id: group.id,
9032
+ from: group.from,
9033
+ to: group.to,
9034
+ columns: ROADMAP_COLUMNS.filter((c) => columns.includes(c)),
9035
+ row: { start: group.from + 1, span: group.to - group.from + 1 },
9036
+ column: { start: start + 1, span: end - start + 1 },
9037
+ fragmented: end - start + 1 !== indices.length
9038
+ };
9039
+ });
9040
+ }
9041
+ function groupColumnRuns(group) {
9042
+ const indices = resolveGroupColumns(group).map(columnIndex).sort((a, b) => a - b);
9043
+ const runs = [];
9044
+ for (const index of indices) {
9045
+ const last = runs[runs.length - 1];
9046
+ if (last && last.start + last.span === index + 1) last.span += 1;
9047
+ else runs.push({ start: index + 1, span: 1 });
9048
+ }
9049
+ return runs;
9050
+ }
9051
+ function validateNoOverlap(doc) {
9052
+ const overlaps = [];
9053
+ const nodeCell = /* @__PURE__ */ new Map();
9054
+ for (const node of doc.nodes) {
9055
+ for (const column of columnsCovered(node)) {
9056
+ const key = `${column}:${node.order}`;
9057
+ const owner = nodeCell.get(key);
9058
+ if (owner !== void 0) {
9059
+ overlaps.push({ kind: "node", column, row: node.order, ids: [owner, node.id] });
9060
+ continue;
9061
+ }
9062
+ nodeCell.set(key, node.id);
9063
+ }
9064
+ }
9065
+ const groupCell = /* @__PURE__ */ new Map();
9066
+ for (const group of doc.groups) {
9067
+ if (group.from > group.to) continue;
9068
+ for (const column of resolveGroupColumns(group)) {
9069
+ for (let row = group.from; row <= group.to; row += 1) {
9070
+ const key = `${column}:${row}`;
9071
+ const owner = groupCell.get(key);
9072
+ if (owner !== void 0) {
9073
+ if (owner !== group.id) {
9074
+ overlaps.push({ kind: "group", column, row, ids: [owner, group.id] });
9075
+ }
9076
+ continue;
9077
+ }
9078
+ groupCell.set(key, group.id);
9079
+ }
9080
+ }
9081
+ }
9082
+ return overlaps;
9083
+ }
9084
+ function placeNodes(doc) {
9085
+ const byRow = nodesByRow(doc);
9086
+ const rows = rowsOf(doc);
9087
+ const placements = [];
9088
+ for (const row of rows) {
9089
+ for (const node of byRow.get(row) ?? []) {
9090
+ placements.push({
9091
+ node,
9092
+ row: { start: node.order + 1, span: 1 },
9093
+ column: gridColumnFor(node)
9094
+ });
9095
+ }
9096
+ }
9097
+ return { placements, rows, rowCount: maxRowOf(doc) + 1 };
9098
+ }
9099
+ function firstFreeRow(taken, columns, from) {
9100
+ let row = from;
9101
+ for (let guard = 0; guard < 1e5; guard += 1) {
9102
+ if (columns.every((column) => !taken.has(`${column}:${row}`))) return row;
9103
+ row += 1;
9104
+ }
9105
+ return row;
9106
+ }
9107
+
9108
+ // src/components/organisms/roadmap/Roadmap.tsx
8641
9109
  var import_react7 = require("react");
9110
+
9111
+ // src/components/organisms/roadmap/RoadmapNode.tsx
9112
+ var import_class_variance_authority14 = require("class-variance-authority");
9113
+
9114
+ // src/components/organisms/roadmap/RoadmapLegend.tsx
9115
+ var import_lucide_react22 = require("lucide-react");
9116
+
9117
+ // src/components/organisms/roadmap/appearance.ts
9118
+ function toneStyle(tone) {
9119
+ return {
9120
+ "--c-roadmap-node-bg": `var(--c-roadmap-${tone}-bg)`,
9121
+ "--c-roadmap-node-fg": `var(--c-roadmap-${tone}-fg)`
9122
+ };
9123
+ }
9124
+ 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)]";
9125
+
9126
+ // src/components/organisms/roadmap/RoadmapLegend.tsx
9127
+ var import_jsx_runtime65 = require("react/jsx-runtime");
9128
+ 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 };
9129
+ function RoadmapBadge({ name = "check", tone = "neutral", label, className = "" }) {
9130
+ const Glyph = glyphs[name] ?? import_lucide_react22.CircleHelp;
9131
+ 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" }) });
9132
+ }
9133
+ function RoadmapLegendView({ legend, testIdPrefix = "roadmap" }) {
9134
+ 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: [
9135
+ /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(RoadmapBadge, { name: entry.icon, tone: entry.tone }),
9136
+ /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("span", { className: "[overflow-wrap:anywhere]", children: entry.label })
9137
+ ] }, entry.id)) }) });
9138
+ }
9139
+
9140
+ // src/components/organisms/roadmap/RoadmapNode.tsx
9141
+ var import_jsx_runtime66 = require("react/jsx-runtime");
9142
+ 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)]", {
9143
+ variants: {
9144
+ 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)]" },
9145
+ 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" }
9146
+ }
9147
+ });
9148
+ function RoadmapNodeView({ node, state, badge, testIdPrefix, onNodeClick, onAction }) {
9149
+ const locked = state === "locked";
9150
+ const nodeTone = resolveNodeTone(node);
9151
+ 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 });
9152
+ const interactiveClass = `block w-full min-w-0 text-inherit ${focusClass}`;
9153
+ const wholeBox = !locked && !node.action && !node.links?.length;
9154
+ const Box = wholeBox && node.href ? "a" : wholeBox && onNodeClick ? "button" : "div";
9155
+ 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;
9156
+ const icon = node.icon ?? (badge ? { name: badge.icon ?? "check", side: "right", tone: badge.tone } : void 0);
9157
+ return /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("div", { style: toneStyle(nodeTone), className: "min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(Box, { "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: [
9158
+ 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"}` }),
9159
+ primary,
9160
+ node.description && node.kind === "paragraph" && /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("p", { className: "mt-2 [overflow-wrap:anywhere]", children: node.description }),
9161
+ 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)) }),
9162
+ 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) => {
9163
+ e.stopPropagation();
9164
+ if (node.action?.actionId) onAction?.(node.action.actionId, node);
9165
+ }, children: node.action.label }) })
9166
+ ] }) });
9167
+ }
9168
+
9169
+ // src/components/organisms/roadmap/RoadmapGroup.tsx
9170
+ var import_jsx_runtime67 = require("react/jsx-runtime");
9171
+ function RoadmapGroupView({ group, testIdPrefix }) {
9172
+ return /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(import_jsx_runtime67.Fragment, { children: groupColumnRuns(group).map((run, index) => /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(
9173
+ "div",
9174
+ {
9175
+ "data-testid": `${testIdPrefix}-group`,
9176
+ "data-group-id": group.id,
9177
+ style: { ...toneStyle(resolveGroupTone(group)), gridColumn: `${run.start} / span ${run.span}`, gridRow: `${group.from + 1} / span ${group.to - group.from + 1}` },
9178
+ 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)]",
9179
+ children: [
9180
+ index === 0 && group.title && /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { 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: "bg-[var(--c-roadmap-surface)] px-1 [overflow-wrap:anywhere]", children: group.title }) }),
9181
+ index === 0 && group.description && /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "sr-only", children: group.description })
9182
+ ]
9183
+ },
9184
+ run.start
9185
+ )) });
9186
+ }
9187
+
9188
+ // src/components/organisms/roadmap/Roadmap.tsx
9189
+ var import_jsx_runtime68 = require("react/jsx-runtime");
9190
+ function Roadmap({ document: document2, state, renderNode, onNodeClick, onAction, testIdPrefix = "roadmap", className = "", ariaLabel }) {
9191
+ const { placements } = (0, import_react7.useMemo)(() => placeNodes(document2), [document2]);
9192
+ const badges = (0, import_react7.useMemo)(() => new Map(document2.legend?.entries.map((entry) => [entry.id, entry])), [document2.legend]);
9193
+ const legendPlacement = document2.legend ? resolveLegendPlacement(document2.legend) : void 0;
9194
+ const legend = document2.legend && /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(RoadmapLegendView, { legend: document2.legend, testIdPrefix });
9195
+ return /* @__PURE__ */ (0, import_jsx_runtime68.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: [
9196
+ legendPlacement === "top" && /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("div", { className: "mb-6", children: legend }),
9197
+ /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("div", { "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-8 md:gap-x-8", children: [
9198
+ document2.groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(RoadmapGroupView, { group, testIdPrefix }, group.id)),
9199
+ placements.map(({ node, row, column }) => {
9200
+ const resolvedState = resolveNodeState(node, state);
9201
+ return /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
9202
+ "div",
9203
+ {
9204
+ role: "listitem",
9205
+ "data-testid": `${testIdPrefix}-node`,
9206
+ "data-node-id": node.id,
9207
+ "data-state": resolvedState,
9208
+ style: { gridColumn: `${column.start} / span ${column.span}`, gridRow: `${row.start} / span 1` },
9209
+ className: "relative z-10 min-w-0 px-2 py-4",
9210
+ children: node.kind === "legend" ? legendPlacement === "inline" ? legend : null : renderNode ? renderNode(node, resolvedState) : /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(RoadmapNodeView, { node, state: resolvedState, badge: node.badge ? badges.get(node.badge) : void 0, onNodeClick, onAction, testIdPrefix })
9211
+ },
9212
+ node.id
9213
+ );
9214
+ })
9215
+ ] }),
9216
+ legendPlacement === "bottom" && /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("div", { className: "mt-6", children: legend })
9217
+ ] });
9218
+ }
9219
+
9220
+ // src/hooks/use-iframe-auth.ts
9221
+ var import_react8 = require("react");
8642
9222
  function useIframeAuth() {
8643
- const ctx = (0, import_react7.useContext)(IframeContext);
9223
+ const ctx = (0, import_react8.useContext)(IframeContext);
8644
9224
  return { token: ctx.token, userId: ctx.userId, ready: ctx.ready };
8645
9225
  }
8646
9226
 
8647
9227
  // src/hooks/use-iframe-navigate.ts
8648
- var import_react8 = require("react");
9228
+ var import_react9 = require("react");
8649
9229
  function useIframeNavigate() {
8650
- return (0, import_react8.useCallback)((path) => {
9230
+ return (0, import_react9.useCallback)((path) => {
8651
9231
  if (!window.parent || window.parent === window) return;
8652
9232
  window.parent.postMessage(
8653
9233
  { type: "DFL_NAVIGATE", path },
@@ -8657,9 +9237,9 @@ function useIframeNavigate() {
8657
9237
  }
8658
9238
 
8659
9239
  // src/providers/feature-flag-provider.tsx
8660
- var import_react9 = require("react");
8661
- var import_jsx_runtime65 = require("react/jsx-runtime");
8662
- var FeatureFlagContext = (0, import_react9.createContext)({
9240
+ var import_react10 = require("react");
9241
+ var import_jsx_runtime69 = require("react/jsx-runtime");
9242
+ var FeatureFlagContext = (0, import_react10.createContext)({
8663
9243
  flags: {},
8664
9244
  isEnabled: () => false
8665
9245
  });
@@ -8667,21 +9247,21 @@ var FeatureFlagProvider = ({
8667
9247
  children,
8668
9248
  flags
8669
9249
  }) => {
8670
- const value = (0, import_react9.useMemo)(
9250
+ const value = (0, import_react10.useMemo)(
8671
9251
  () => ({
8672
9252
  flags,
8673
9253
  isEnabled: (flag) => Boolean(flags[flag])
8674
9254
  }),
8675
9255
  [flags]
8676
9256
  );
8677
- return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(FeatureFlagContext.Provider, { value, children });
9257
+ return /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(FeatureFlagContext.Provider, { value, children });
8678
9258
  };
8679
9259
  var useFeatureFlag = (flag) => {
8680
- const { isEnabled } = (0, import_react9.useContext)(FeatureFlagContext);
9260
+ const { isEnabled } = (0, import_react10.useContext)(FeatureFlagContext);
8681
9261
  return isEnabled(flag);
8682
9262
  };
8683
9263
  var useFeatureFlags = () => {
8684
- const { flags } = (0, import_react9.useContext)(FeatureFlagContext);
9264
+ const { flags } = (0, import_react10.useContext)(FeatureFlagContext);
8685
9265
  return flags;
8686
9266
  };
8687
9267
  // Annotate the CommonJS export names for ESM import in node:
@@ -8774,7 +9354,14 @@ var useFeatureFlags = () => {
8774
9354
  ContextMenuSubContent,
8775
9355
  ContextMenuSubTrigger,
8776
9356
  ContextMenuTrigger,
9357
+ DEFAULT_EDGE_ARROW,
9358
+ DEFAULT_EDGE_ROUTE,
9359
+ DEFAULT_EDGE_STYLE,
9360
+ DEFAULT_GROUP_TONE,
9361
+ DEFAULT_LEGEND_PLACEMENT,
8777
9362
  DEFAULT_NAME_COL_WIDTH,
9363
+ DEFAULT_NODE_SPAN,
9364
+ DEFAULT_NODE_STATE,
8778
9365
  DflRemote,
8779
9366
  Dialog,
8780
9367
  DialogClose,
@@ -8852,6 +9439,7 @@ var useFeatureFlags = () => {
8852
9439
  MenubarSubContent,
8853
9440
  MenubarSubTrigger,
8854
9441
  MenubarTrigger,
9442
+ NODE_KIND_DEFAULT_TONE,
8855
9443
  NavigationMenu,
8856
9444
  NavigationMenuContent,
8857
9445
  NavigationMenuIndicator,
@@ -8875,12 +9463,24 @@ var useFeatureFlags = () => {
8875
9463
  Progress,
8876
9464
  ProtectedRoute,
8877
9465
  PublishDrawer,
9466
+ ROADMAP_ARROWS,
9467
+ ROADMAP_COLUMNS,
9468
+ ROADMAP_EDGE_LABEL_MAX,
9469
+ ROADMAP_EDGE_ROUTES,
9470
+ ROADMAP_EDGE_STYLES,
9471
+ ROADMAP_LABEL_MAX,
9472
+ ROADMAP_LEGEND_PLACEMENTS,
9473
+ ROADMAP_NODE_ID_PATTERN,
9474
+ ROADMAP_NODE_KINDS,
9475
+ ROADMAP_NODE_STATES,
9476
+ ROADMAP_TONES,
8878
9477
  RadioGroup,
8879
9478
  RadioGroupItem,
8880
9479
  RadioGroupRow,
8881
9480
  ResizableHandle,
8882
9481
  ResizablePanel,
8883
9482
  ResizablePanelGroup,
9483
+ Roadmap,
8884
9484
  ScrollArea,
8885
9485
  ScrollBar,
8886
9486
  Select,
@@ -8968,19 +9568,55 @@ var useFeatureFlags = () => {
8968
9568
  buttonVariants,
8969
9569
  clampPct,
8970
9570
  cn,
9571
+ columnIndex,
9572
+ columnsCovered,
9573
+ columnsUsed,
8971
9574
  filterPublishableAccounts,
9575
+ firstFreeRow,
8972
9576
  getInitials,
9577
+ gridColumnFor,
9578
+ groupColumnRuns,
9579
+ groupRanges,
8973
9580
  iconButtonVariants,
8974
9581
  isAllowedOrigin,
8975
9582
  kbdVariants,
8976
9583
  labelVariants,
9584
+ maxRowOf,
8977
9585
  memberHueIndex,
8978
9586
  memberHueVar,
8979
9587
  navigationMenuTriggerStyle,
9588
+ nodesByRow,
9589
+ parseRoadmapDocument,
9590
+ parseRoadmapStateOverlay,
8980
9591
  parseTags,
9592
+ placeNodes,
9593
+ resolveEdgeArrow,
9594
+ resolveEdgeRoute,
9595
+ resolveEdgeStyle,
9596
+ resolveGroupColumns,
9597
+ resolveGroupTone,
9598
+ resolveLegendPlacement,
8981
9599
  resolveNameColWidth,
9600
+ resolveNodeSpan,
9601
+ resolveNodeState,
9602
+ resolveNodeTone,
8982
9603
  resolveWeekCount,
8983
9604
  resolveWeekLabels,
9605
+ roadmapActionSchema,
9606
+ roadmapDocumentSchema,
9607
+ roadmapEdgeSchema,
9608
+ roadmapGroupSchema,
9609
+ roadmapIconSchema,
9610
+ roadmapLegendEntrySchema,
9611
+ roadmapLegendSchema,
9612
+ roadmapLinkSchema,
9613
+ roadmapNodeSchema,
9614
+ roadmapNodeStateSchema,
9615
+ roadmapStateOverlaySchema,
9616
+ roadmapStateSchema,
9617
+ rowsOf,
9618
+ safeParseRoadmapDocument,
9619
+ safeParseRoadmapStateOverlay,
8984
9620
  stageProgress,
8985
9621
  toast,
8986
9622
  toggleVariants,
@@ -8994,5 +9630,6 @@ var useFeatureFlags = () => {
8994
9630
  useSession,
8995
9631
  useSidebar,
8996
9632
  useToast,
9633
+ validateNoOverlap,
8997
9634
  validatePublishForm
8998
9635
  });