@devfellowship/components 3.2.4 → 3.3.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,6 +227,17 @@ __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,
@@ -312,19 +331,55 @@ __export(src_exports, {
312
331
  buttonVariants: () => buttonVariants,
313
332
  clampPct: () => clampPct,
314
333
  cn: () => cn,
334
+ columnIndex: () => columnIndex,
335
+ columnsCovered: () => columnsCovered,
336
+ columnsUsed: () => columnsUsed,
315
337
  filterPublishableAccounts: () => filterPublishableAccounts,
338
+ firstFreeRow: () => firstFreeRow,
316
339
  getInitials: () => getInitials,
340
+ gridColumnFor: () => gridColumnFor,
341
+ groupColumnRuns: () => groupColumnRuns,
342
+ groupRanges: () => groupRanges,
317
343
  iconButtonVariants: () => iconButtonVariants,
318
344
  isAllowedOrigin: () => isAllowedOrigin,
319
345
  kbdVariants: () => kbdVariants,
320
346
  labelVariants: () => labelVariants,
347
+ maxRowOf: () => maxRowOf,
321
348
  memberHueIndex: () => memberHueIndex,
322
349
  memberHueVar: () => memberHueVar,
323
350
  navigationMenuTriggerStyle: () => navigationMenuTriggerStyle,
351
+ nodesByRow: () => nodesByRow,
352
+ parseRoadmapDocument: () => parseRoadmapDocument,
353
+ parseRoadmapStateOverlay: () => parseRoadmapStateOverlay,
324
354
  parseTags: () => parseTags,
355
+ placeNodes: () => placeNodes,
356
+ resolveEdgeArrow: () => resolveEdgeArrow,
357
+ resolveEdgeRoute: () => resolveEdgeRoute,
358
+ resolveEdgeStyle: () => resolveEdgeStyle,
359
+ resolveGroupColumns: () => resolveGroupColumns,
360
+ resolveGroupTone: () => resolveGroupTone,
361
+ resolveLegendPlacement: () => resolveLegendPlacement,
325
362
  resolveNameColWidth: () => resolveNameColWidth,
363
+ resolveNodeSpan: () => resolveNodeSpan,
364
+ resolveNodeState: () => resolveNodeState,
365
+ resolveNodeTone: () => resolveNodeTone,
326
366
  resolveWeekCount: () => resolveWeekCount,
327
367
  resolveWeekLabels: () => resolveWeekLabels,
368
+ roadmapActionSchema: () => roadmapActionSchema,
369
+ roadmapDocumentSchema: () => roadmapDocumentSchema,
370
+ roadmapEdgeSchema: () => roadmapEdgeSchema,
371
+ roadmapGroupSchema: () => roadmapGroupSchema,
372
+ roadmapIconSchema: () => roadmapIconSchema,
373
+ roadmapLegendEntrySchema: () => roadmapLegendEntrySchema,
374
+ roadmapLegendSchema: () => roadmapLegendSchema,
375
+ roadmapLinkSchema: () => roadmapLinkSchema,
376
+ roadmapNodeSchema: () => roadmapNodeSchema,
377
+ roadmapNodeStateSchema: () => roadmapNodeStateSchema,
378
+ roadmapStateOverlaySchema: () => roadmapStateOverlaySchema,
379
+ roadmapStateSchema: () => roadmapStateSchema,
380
+ rowsOf: () => rowsOf,
381
+ safeParseRoadmapDocument: () => safeParseRoadmapDocument,
382
+ safeParseRoadmapStateOverlay: () => safeParseRoadmapStateOverlay,
328
383
  stageProgress: () => stageProgress,
329
384
  toast: () => toast,
330
385
  toggleVariants: () => toggleVariants,
@@ -338,6 +393,7 @@ __export(src_exports, {
338
393
  useSession: () => useSession,
339
394
  useSidebar: () => useSidebar,
340
395
  useToast: () => useToast,
396
+ validateNoOverlap: () => validateNoOverlap,
341
397
  validatePublishForm: () => validatePublishForm
342
398
  });
343
399
  module.exports = __toCommonJS(src_exports);
@@ -8637,6 +8693,417 @@ function PublishDrawer({
8637
8693
  ) });
8638
8694
  }
8639
8695
 
8696
+ // src/components/organisms/roadmap/contract.ts
8697
+ var import_zod = require("zod");
8698
+ var ROADMAP_COLUMNS = ["left", "center", "right"];
8699
+ var ROADMAP_TONES = [
8700
+ "primary",
8701
+ "secondary",
8702
+ "accent",
8703
+ "muted",
8704
+ "success",
8705
+ "info",
8706
+ "danger",
8707
+ "neutral"
8708
+ ];
8709
+ var ROADMAP_NODE_KINDS = [
8710
+ "topic",
8711
+ "subtopic",
8712
+ "label",
8713
+ "title",
8714
+ "paragraph",
8715
+ "button",
8716
+ "legend"
8717
+ ];
8718
+ var ROADMAP_NODE_STATES = ["todo", "done", "learning", "skipped", "locked"];
8719
+ var ROADMAP_EDGE_STYLES = ["solid", "dashed", "dotted"];
8720
+ var ROADMAP_EDGE_ROUTES = ["auto", "straight", "elbow", "curve"];
8721
+ var ROADMAP_ARROWS = ["none", "end", "both"];
8722
+ var ROADMAP_LEGEND_PLACEMENTS = ["top", "bottom", "inline"];
8723
+ var DEFAULT_NODE_SPAN = 1;
8724
+ var DEFAULT_EDGE_STYLE = "dashed";
8725
+ var DEFAULT_EDGE_ROUTE = "auto";
8726
+ var DEFAULT_EDGE_ARROW = "none";
8727
+ var DEFAULT_GROUP_TONE = "neutral";
8728
+ var DEFAULT_LEGEND_PLACEMENT = "top";
8729
+ var DEFAULT_NODE_STATE = "todo";
8730
+ var NODE_KIND_DEFAULT_TONE = {
8731
+ topic: "primary",
8732
+ subtopic: "secondary",
8733
+ button: "primary",
8734
+ label: "neutral",
8735
+ title: "neutral",
8736
+ paragraph: "neutral",
8737
+ legend: "neutral"
8738
+ };
8739
+ var resolveNodeSpan = (node) => node.span ?? DEFAULT_NODE_SPAN;
8740
+ var resolveNodeTone = (node) => node.tone ?? NODE_KIND_DEFAULT_TONE[node.kind];
8741
+ var resolveEdgeStyle = (edge) => edge.style ?? DEFAULT_EDGE_STYLE;
8742
+ var resolveEdgeRoute = (edge) => edge.route ?? DEFAULT_EDGE_ROUTE;
8743
+ var resolveEdgeArrow = (edge) => edge.arrow ?? DEFAULT_EDGE_ARROW;
8744
+ var resolveGroupTone = (group) => group.tone ?? DEFAULT_GROUP_TONE;
8745
+ var resolveGroupColumns = (group) => group.columns && group.columns.length > 0 ? group.columns : [...ROADMAP_COLUMNS];
8746
+ var resolveLegendPlacement = (legend) => legend.placement ?? DEFAULT_LEGEND_PLACEMENT;
8747
+ var resolveNodeState = (node, overlay) => overlay?.nodes[node.id] ?? node.state ?? DEFAULT_NODE_STATE;
8748
+ var ROADMAP_NODE_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
8749
+ var ROADMAP_EDGE_LABEL_MAX = 40;
8750
+ var ROADMAP_LABEL_MAX = 200;
8751
+ var columnSchema = import_zod.z.enum(ROADMAP_COLUMNS);
8752
+ var toneSchema = import_zod.z.enum(ROADMAP_TONES);
8753
+ var idSchema = import_zod.z.string().regex(ROADMAP_NODE_ID_PATTERN, "must match /^[A-Za-z0-9_-]{1,64}$/");
8754
+ var metaSchema = import_zod.z.record(import_zod.z.unknown());
8755
+ var roadmapIconSchema = import_zod.z.object({
8756
+ name: import_zod.z.string().min(1).max(64),
8757
+ side: import_zod.z.enum(["left", "right"]),
8758
+ tone: toneSchema.optional()
8759
+ }).strict();
8760
+ var roadmapActionSchema = import_zod.z.object({
8761
+ label: import_zod.z.string().min(1).max(80),
8762
+ href: import_zod.z.string().min(1).optional(),
8763
+ actionId: import_zod.z.string().min(1).optional(),
8764
+ tone: toneSchema.optional()
8765
+ }).strict().superRefine((action, ctx) => {
8766
+ const given = [action.href, action.actionId].filter((v) => v !== void 0).length;
8767
+ if (given !== 1) {
8768
+ ctx.addIssue({
8769
+ code: import_zod.z.ZodIssueCode.custom,
8770
+ path: ["href"],
8771
+ message: "an action needs exactly one of `href` or `actionId`"
8772
+ });
8773
+ }
8774
+ });
8775
+ var roadmapLinkSchema = import_zod.z.object({ label: import_zod.z.string().min(1).max(120), href: import_zod.z.string().min(1) }).strict();
8776
+ var roadmapNodeStateSchema = import_zod.z.enum(ROADMAP_NODE_STATES);
8777
+ var roadmapNodeSchema = import_zod.z.object({
8778
+ id: idSchema,
8779
+ column: columnSchema,
8780
+ order: import_zod.z.number().int().min(0),
8781
+ span: import_zod.z.union([import_zod.z.literal(1), import_zod.z.literal(2), import_zod.z.literal(3)]).optional(),
8782
+ kind: import_zod.z.enum(ROADMAP_NODE_KINDS),
8783
+ label: import_zod.z.string().min(1).max(ROADMAP_LABEL_MAX),
8784
+ description: import_zod.z.string().max(2e3).optional(),
8785
+ tone: toneSchema.optional(),
8786
+ icon: roadmapIconSchema.optional(),
8787
+ badge: import_zod.z.string().min(1).optional(),
8788
+ state: roadmapNodeStateSchema.optional(),
8789
+ action: roadmapActionSchema.optional(),
8790
+ href: import_zod.z.string().min(1).optional(),
8791
+ links: import_zod.z.array(roadmapLinkSchema).optional(),
8792
+ meta: metaSchema.optional()
8793
+ }).strict();
8794
+ var roadmapEdgeSchema = import_zod.z.object({
8795
+ id: idSchema,
8796
+ source: import_zod.z.string().min(1),
8797
+ target: import_zod.z.string().min(1),
8798
+ style: import_zod.z.enum(ROADMAP_EDGE_STYLES).optional(),
8799
+ label: import_zod.z.string().min(1).max(ROADMAP_EDGE_LABEL_MAX).optional(),
8800
+ route: import_zod.z.enum(ROADMAP_EDGE_ROUTES).optional(),
8801
+ arrow: import_zod.z.enum(ROADMAP_ARROWS).optional(),
8802
+ tone: toneSchema.optional()
8803
+ }).strict();
8804
+ var roadmapGroupSchema = import_zod.z.object({
8805
+ id: idSchema,
8806
+ title: import_zod.z.string().min(1).max(ROADMAP_LABEL_MAX).optional(),
8807
+ tone: toneSchema.optional(),
8808
+ from: import_zod.z.number().int().min(0),
8809
+ to: import_zod.z.number().int().min(0),
8810
+ columns: import_zod.z.array(columnSchema).min(1).max(3).optional(),
8811
+ description: import_zod.z.string().max(2e3).optional()
8812
+ }).strict();
8813
+ var roadmapLegendEntrySchema = import_zod.z.object({
8814
+ id: idSchema,
8815
+ label: import_zod.z.string().min(1).max(ROADMAP_LABEL_MAX),
8816
+ tone: toneSchema,
8817
+ icon: import_zod.z.string().min(1).max(64).optional()
8818
+ }).strict();
8819
+ var roadmapLegendSchema = import_zod.z.object({
8820
+ entries: import_zod.z.array(roadmapLegendEntrySchema).min(1),
8821
+ placement: import_zod.z.enum(ROADMAP_LEGEND_PLACEMENTS).optional()
8822
+ }).strict();
8823
+ function columnsCovered(node) {
8824
+ const span = node.span ?? DEFAULT_NODE_SPAN;
8825
+ if (span === 3) return [...ROADMAP_COLUMNS];
8826
+ const start = ROADMAP_COLUMNS.indexOf(node.column);
8827
+ return ROADMAP_COLUMNS.slice(start, start + span);
8828
+ }
8829
+ var roadmapDocumentBaseSchema = import_zod.z.object({
8830
+ version: import_zod.z.literal("roadmap/v1"),
8831
+ title: import_zod.z.string().min(1).max(ROADMAP_LABEL_MAX).optional(),
8832
+ direction: import_zod.z.literal("down"),
8833
+ columns: import_zod.z.literal(3),
8834
+ nodes: import_zod.z.array(roadmapNodeSchema),
8835
+ edges: import_zod.z.array(roadmapEdgeSchema),
8836
+ groups: import_zod.z.array(roadmapGroupSchema),
8837
+ legend: roadmapLegendSchema.optional(),
8838
+ meta: metaSchema.optional()
8839
+ }).strict();
8840
+ var roadmapDocumentSchema = roadmapDocumentBaseSchema.superRefine((doc, ctx) => {
8841
+ const nodeIndexById = /* @__PURE__ */ new Map();
8842
+ doc.nodes.forEach((node, i) => {
8843
+ if (nodeIndexById.has(node.id)) {
8844
+ ctx.addIssue({
8845
+ code: import_zod.z.ZodIssueCode.custom,
8846
+ path: ["nodes", i, "id"],
8847
+ message: `duplicate node id "${node.id}" (first seen at nodes[${nodeIndexById.get(node.id)}])`
8848
+ });
8849
+ return;
8850
+ }
8851
+ nodeIndexById.set(node.id, i);
8852
+ });
8853
+ const cellOwner = /* @__PURE__ */ new Map();
8854
+ doc.nodes.forEach((node, i) => {
8855
+ const span = node.span ?? DEFAULT_NODE_SPAN;
8856
+ if (span === 2 && node.column === "right") {
8857
+ ctx.addIssue({
8858
+ code: import_zod.z.ZodIssueCode.custom,
8859
+ path: ["nodes", i, "span"],
8860
+ message: 'a `span: 2` node at column "right" runs past the last track'
8861
+ });
8862
+ return;
8863
+ }
8864
+ for (const column of columnsCovered(node)) {
8865
+ const key = `${column}:${node.order}`;
8866
+ const owner = cellOwner.get(key);
8867
+ if (owner !== void 0) {
8868
+ ctx.addIssue({
8869
+ code: import_zod.z.ZodIssueCode.custom,
8870
+ path: ["nodes", i, "order"],
8871
+ message: `cell (${column}, ${node.order}) is already taken by nodes[${owner}] ("${doc.nodes[owner].id}")`
8872
+ });
8873
+ continue;
8874
+ }
8875
+ cellOwner.set(key, i);
8876
+ }
8877
+ });
8878
+ const edgeIds = /* @__PURE__ */ new Set();
8879
+ doc.edges.forEach((edge, i) => {
8880
+ if (edgeIds.has(edge.id)) {
8881
+ ctx.addIssue({
8882
+ code: import_zod.z.ZodIssueCode.custom,
8883
+ path: ["edges", i, "id"],
8884
+ message: `duplicate edge id "${edge.id}"`
8885
+ });
8886
+ }
8887
+ edgeIds.add(edge.id);
8888
+ if (!nodeIndexById.has(edge.source)) {
8889
+ ctx.addIssue({
8890
+ code: import_zod.z.ZodIssueCode.custom,
8891
+ path: ["edges", i, "source"],
8892
+ message: `edge source "${edge.source}" is not a node in this document`
8893
+ });
8894
+ }
8895
+ if (!nodeIndexById.has(edge.target)) {
8896
+ ctx.addIssue({
8897
+ code: import_zod.z.ZodIssueCode.custom,
8898
+ path: ["edges", i, "target"],
8899
+ message: `edge target "${edge.target}" is not a node in this document`
8900
+ });
8901
+ }
8902
+ if (edge.source === edge.target) {
8903
+ ctx.addIssue({
8904
+ code: import_zod.z.ZodIssueCode.custom,
8905
+ path: ["edges", i, "target"],
8906
+ message: `edge "${edge.id}" is a self-loop`
8907
+ });
8908
+ }
8909
+ });
8910
+ const groupIds = /* @__PURE__ */ new Set();
8911
+ doc.groups.forEach((group, i) => {
8912
+ if (groupIds.has(group.id)) {
8913
+ ctx.addIssue({
8914
+ code: import_zod.z.ZodIssueCode.custom,
8915
+ path: ["groups", i, "id"],
8916
+ message: `duplicate group id "${group.id}"`
8917
+ });
8918
+ }
8919
+ groupIds.add(group.id);
8920
+ if (group.from > group.to) {
8921
+ ctx.addIssue({
8922
+ code: import_zod.z.ZodIssueCode.custom,
8923
+ path: ["groups", i, "to"],
8924
+ message: `group "${group.id}" has from (${group.from}) greater than to (${group.to})`
8925
+ });
8926
+ }
8927
+ });
8928
+ for (let i = 0; i < doc.groups.length; i += 1) {
8929
+ const a = doc.groups[i];
8930
+ if (a.from > a.to) continue;
8931
+ const aColumns = new Set(resolveGroupColumns(a));
8932
+ for (let j = i + 1; j < doc.groups.length; j += 1) {
8933
+ const b = doc.groups[j];
8934
+ if (b.from > b.to) continue;
8935
+ const rowsOverlap = a.from <= b.to && b.from <= a.to;
8936
+ if (!rowsOverlap) continue;
8937
+ const shared = resolveGroupColumns(b).filter((c) => aColumns.has(c));
8938
+ if (shared.length === 0) continue;
8939
+ ctx.addIssue({
8940
+ code: import_zod.z.ZodIssueCode.custom,
8941
+ path: ["groups", j, "from"],
8942
+ message: `group "${b.id}" overlaps rows ${a.from}..${a.to} of group "${a.id}" on column(s) ${shared.join(", ")}`
8943
+ });
8944
+ }
8945
+ }
8946
+ const legendIds = /* @__PURE__ */ new Set();
8947
+ doc.legend?.entries.forEach((entry, i) => {
8948
+ if (legendIds.has(entry.id)) {
8949
+ ctx.addIssue({
8950
+ code: import_zod.z.ZodIssueCode.custom,
8951
+ path: ["legend", "entries", i, "id"],
8952
+ message: `duplicate legend entry id "${entry.id}"`
8953
+ });
8954
+ }
8955
+ legendIds.add(entry.id);
8956
+ });
8957
+ doc.nodes.forEach((node, i) => {
8958
+ if (node.badge === void 0) return;
8959
+ if (!legendIds.has(node.badge)) {
8960
+ ctx.addIssue({
8961
+ code: import_zod.z.ZodIssueCode.custom,
8962
+ path: ["nodes", i, "badge"],
8963
+ message: `badge "${node.badge}" is not a legend entry of this document`
8964
+ });
8965
+ }
8966
+ });
8967
+ });
8968
+ var roadmapStateOverlaySchema = import_zod.z.object({
8969
+ version: import_zod.z.literal("roadmap-state/v1"),
8970
+ nodes: import_zod.z.record(roadmapNodeStateSchema)
8971
+ }).strict();
8972
+ var roadmapStateSchema = roadmapStateOverlaySchema;
8973
+ function parseRoadmapDocument(input) {
8974
+ return roadmapDocumentSchema.parse(input);
8975
+ }
8976
+ function safeParseRoadmapDocument(input) {
8977
+ return roadmapDocumentSchema.safeParse(input);
8978
+ }
8979
+ function parseRoadmapStateOverlay(input) {
8980
+ return roadmapStateOverlaySchema.parse(input);
8981
+ }
8982
+ function safeParseRoadmapStateOverlay(input) {
8983
+ return roadmapStateOverlaySchema.safeParse(input);
8984
+ }
8985
+
8986
+ // src/components/organisms/roadmap/layout.ts
8987
+ function columnIndex(column) {
8988
+ return ROADMAP_COLUMNS.indexOf(column);
8989
+ }
8990
+ function gridColumnFor(node) {
8991
+ const span = resolveNodeSpan(node);
8992
+ if (span === 3) return { start: 1, span: 3 };
8993
+ return { start: columnIndex(node.column) + 1, span };
8994
+ }
8995
+ function rowsOf(doc) {
8996
+ const rows = /* @__PURE__ */ new Set();
8997
+ for (const node of doc.nodes) rows.add(node.order);
8998
+ return [...rows].sort((a, b) => a - b);
8999
+ }
9000
+ function maxRowOf(doc) {
9001
+ return doc.nodes.reduce((max, node) => Math.max(max, node.order), -1);
9002
+ }
9003
+ function nodesByRow(doc) {
9004
+ const byRow = /* @__PURE__ */ new Map();
9005
+ for (const node of doc.nodes) {
9006
+ const bucket = byRow.get(node.order);
9007
+ if (bucket) bucket.push(node);
9008
+ else byRow.set(node.order, [node]);
9009
+ }
9010
+ for (const bucket of byRow.values()) {
9011
+ bucket.sort((a, b) => {
9012
+ const byColumn = columnIndex(a.column) - columnIndex(b.column);
9013
+ return byColumn !== 0 ? byColumn : a.id.localeCompare(b.id);
9014
+ });
9015
+ }
9016
+ return byRow;
9017
+ }
9018
+ function columnsUsed(doc) {
9019
+ const used = /* @__PURE__ */ new Set();
9020
+ for (const node of doc.nodes) for (const column of columnsCovered(node)) used.add(column);
9021
+ return ROADMAP_COLUMNS.filter((column) => used.has(column));
9022
+ }
9023
+ function groupRanges(doc) {
9024
+ return doc.groups.map((group) => {
9025
+ const columns = resolveGroupColumns(group);
9026
+ const indices = columns.map(columnIndex).sort((a, b) => a - b);
9027
+ const start = indices[0];
9028
+ const end = indices[indices.length - 1];
9029
+ return {
9030
+ id: group.id,
9031
+ from: group.from,
9032
+ to: group.to,
9033
+ columns: ROADMAP_COLUMNS.filter((c) => columns.includes(c)),
9034
+ row: { start: group.from + 1, span: group.to - group.from + 1 },
9035
+ column: { start: start + 1, span: end - start + 1 },
9036
+ fragmented: end - start + 1 !== indices.length
9037
+ };
9038
+ });
9039
+ }
9040
+ function groupColumnRuns(group) {
9041
+ const indices = resolveGroupColumns(group).map(columnIndex).sort((a, b) => a - b);
9042
+ const runs = [];
9043
+ for (const index of indices) {
9044
+ const last = runs[runs.length - 1];
9045
+ if (last && last.start + last.span === index + 1) last.span += 1;
9046
+ else runs.push({ start: index + 1, span: 1 });
9047
+ }
9048
+ return runs;
9049
+ }
9050
+ function validateNoOverlap(doc) {
9051
+ const overlaps = [];
9052
+ const nodeCell = /* @__PURE__ */ new Map();
9053
+ for (const node of doc.nodes) {
9054
+ for (const column of columnsCovered(node)) {
9055
+ const key = `${column}:${node.order}`;
9056
+ const owner = nodeCell.get(key);
9057
+ if (owner !== void 0) {
9058
+ overlaps.push({ kind: "node", column, row: node.order, ids: [owner, node.id] });
9059
+ continue;
9060
+ }
9061
+ nodeCell.set(key, node.id);
9062
+ }
9063
+ }
9064
+ const groupCell = /* @__PURE__ */ new Map();
9065
+ for (const group of doc.groups) {
9066
+ if (group.from > group.to) continue;
9067
+ for (const column of resolveGroupColumns(group)) {
9068
+ for (let row = group.from; row <= group.to; row += 1) {
9069
+ const key = `${column}:${row}`;
9070
+ const owner = groupCell.get(key);
9071
+ if (owner !== void 0) {
9072
+ if (owner !== group.id) {
9073
+ overlaps.push({ kind: "group", column, row, ids: [owner, group.id] });
9074
+ }
9075
+ continue;
9076
+ }
9077
+ groupCell.set(key, group.id);
9078
+ }
9079
+ }
9080
+ }
9081
+ return overlaps;
9082
+ }
9083
+ function placeNodes(doc) {
9084
+ const byRow = nodesByRow(doc);
9085
+ const rows = rowsOf(doc);
9086
+ const placements = [];
9087
+ for (const row of rows) {
9088
+ for (const node of byRow.get(row) ?? []) {
9089
+ placements.push({
9090
+ node,
9091
+ row: { start: node.order + 1, span: 1 },
9092
+ column: gridColumnFor(node)
9093
+ });
9094
+ }
9095
+ }
9096
+ return { placements, rows, rowCount: maxRowOf(doc) + 1 };
9097
+ }
9098
+ function firstFreeRow(taken, columns, from) {
9099
+ let row = from;
9100
+ for (let guard = 0; guard < 1e5; guard += 1) {
9101
+ if (columns.every((column) => !taken.has(`${column}:${row}`))) return row;
9102
+ row += 1;
9103
+ }
9104
+ return row;
9105
+ }
9106
+
8640
9107
  // src/hooks/use-iframe-auth.ts
8641
9108
  var import_react7 = require("react");
8642
9109
  function useIframeAuth() {
@@ -8774,7 +9241,14 @@ var useFeatureFlags = () => {
8774
9241
  ContextMenuSubContent,
8775
9242
  ContextMenuSubTrigger,
8776
9243
  ContextMenuTrigger,
9244
+ DEFAULT_EDGE_ARROW,
9245
+ DEFAULT_EDGE_ROUTE,
9246
+ DEFAULT_EDGE_STYLE,
9247
+ DEFAULT_GROUP_TONE,
9248
+ DEFAULT_LEGEND_PLACEMENT,
8777
9249
  DEFAULT_NAME_COL_WIDTH,
9250
+ DEFAULT_NODE_SPAN,
9251
+ DEFAULT_NODE_STATE,
8778
9252
  DflRemote,
8779
9253
  Dialog,
8780
9254
  DialogClose,
@@ -8852,6 +9326,7 @@ var useFeatureFlags = () => {
8852
9326
  MenubarSubContent,
8853
9327
  MenubarSubTrigger,
8854
9328
  MenubarTrigger,
9329
+ NODE_KIND_DEFAULT_TONE,
8855
9330
  NavigationMenu,
8856
9331
  NavigationMenuContent,
8857
9332
  NavigationMenuIndicator,
@@ -8875,6 +9350,17 @@ var useFeatureFlags = () => {
8875
9350
  Progress,
8876
9351
  ProtectedRoute,
8877
9352
  PublishDrawer,
9353
+ ROADMAP_ARROWS,
9354
+ ROADMAP_COLUMNS,
9355
+ ROADMAP_EDGE_LABEL_MAX,
9356
+ ROADMAP_EDGE_ROUTES,
9357
+ ROADMAP_EDGE_STYLES,
9358
+ ROADMAP_LABEL_MAX,
9359
+ ROADMAP_LEGEND_PLACEMENTS,
9360
+ ROADMAP_NODE_ID_PATTERN,
9361
+ ROADMAP_NODE_KINDS,
9362
+ ROADMAP_NODE_STATES,
9363
+ ROADMAP_TONES,
8878
9364
  RadioGroup,
8879
9365
  RadioGroupItem,
8880
9366
  RadioGroupRow,
@@ -8968,19 +9454,55 @@ var useFeatureFlags = () => {
8968
9454
  buttonVariants,
8969
9455
  clampPct,
8970
9456
  cn,
9457
+ columnIndex,
9458
+ columnsCovered,
9459
+ columnsUsed,
8971
9460
  filterPublishableAccounts,
9461
+ firstFreeRow,
8972
9462
  getInitials,
9463
+ gridColumnFor,
9464
+ groupColumnRuns,
9465
+ groupRanges,
8973
9466
  iconButtonVariants,
8974
9467
  isAllowedOrigin,
8975
9468
  kbdVariants,
8976
9469
  labelVariants,
9470
+ maxRowOf,
8977
9471
  memberHueIndex,
8978
9472
  memberHueVar,
8979
9473
  navigationMenuTriggerStyle,
9474
+ nodesByRow,
9475
+ parseRoadmapDocument,
9476
+ parseRoadmapStateOverlay,
8980
9477
  parseTags,
9478
+ placeNodes,
9479
+ resolveEdgeArrow,
9480
+ resolveEdgeRoute,
9481
+ resolveEdgeStyle,
9482
+ resolveGroupColumns,
9483
+ resolveGroupTone,
9484
+ resolveLegendPlacement,
8981
9485
  resolveNameColWidth,
9486
+ resolveNodeSpan,
9487
+ resolveNodeState,
9488
+ resolveNodeTone,
8982
9489
  resolveWeekCount,
8983
9490
  resolveWeekLabels,
9491
+ roadmapActionSchema,
9492
+ roadmapDocumentSchema,
9493
+ roadmapEdgeSchema,
9494
+ roadmapGroupSchema,
9495
+ roadmapIconSchema,
9496
+ roadmapLegendEntrySchema,
9497
+ roadmapLegendSchema,
9498
+ roadmapLinkSchema,
9499
+ roadmapNodeSchema,
9500
+ roadmapNodeStateSchema,
9501
+ roadmapStateOverlaySchema,
9502
+ roadmapStateSchema,
9503
+ rowsOf,
9504
+ safeParseRoadmapDocument,
9505
+ safeParseRoadmapStateOverlay,
8984
9506
  stageProgress,
8985
9507
  toast,
8986
9508
  toggleVariants,
@@ -8994,5 +9516,6 @@ var useFeatureFlags = () => {
8994
9516
  useSession,
8995
9517
  useSidebar,
8996
9518
  useToast,
9519
+ validateNoOverlap,
8997
9520
  validatePublishForm
8998
9521
  });