@diagrammo/dgmo 0.4.1 → 0.4.3

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.
Files changed (59) hide show
  1. package/.claude/skills/dgmo-chart/SKILL.md +28 -0
  2. package/.claude/skills/dgmo-generate/SKILL.md +1 -0
  3. package/.claude/skills/dgmo-sequence/SKILL.md +24 -1
  4. package/.cursorrules +27 -2
  5. package/.github/copilot-instructions.md +36 -3
  6. package/.windsurfrules +27 -2
  7. package/README.md +12 -3
  8. package/dist/cli.cjs +611 -153
  9. package/dist/index.cjs +8371 -3200
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +502 -58
  12. package/dist/index.d.ts +502 -58
  13. package/dist/index.js +8594 -3444
  14. package/dist/index.js.map +1 -1
  15. package/docs/ai-integration.md +1 -1
  16. package/docs/language-reference.md +336 -17
  17. package/docs/migration-sequence-color-to-tags.md +98 -0
  18. package/package.json +1 -1
  19. package/src/c4/renderer.ts +1 -20
  20. package/src/class/renderer.ts +1 -11
  21. package/src/cli.ts +40 -0
  22. package/src/d3.ts +92 -2
  23. package/src/dgmo-router.ts +11 -0
  24. package/src/echarts.ts +74 -8
  25. package/src/er/parser.ts +29 -3
  26. package/src/er/renderer.ts +1 -15
  27. package/src/graph/flowchart-parser.ts +7 -30
  28. package/src/graph/flowchart-renderer.ts +62 -69
  29. package/src/graph/layout.ts +5 -0
  30. package/src/graph/state-parser.ts +388 -0
  31. package/src/graph/state-renderer.ts +496 -0
  32. package/src/graph/types.ts +4 -2
  33. package/src/index.ts +42 -1
  34. package/src/infra/compute.ts +1113 -0
  35. package/src/infra/layout.ts +575 -0
  36. package/src/infra/parser.ts +559 -0
  37. package/src/infra/renderer.ts +1509 -0
  38. package/src/infra/roles.ts +60 -0
  39. package/src/infra/serialize.ts +67 -0
  40. package/src/infra/types.ts +221 -0
  41. package/src/infra/validation.ts +192 -0
  42. package/src/initiative-status/layout.ts +56 -61
  43. package/src/initiative-status/renderer.ts +13 -13
  44. package/src/kanban/renderer.ts +1 -24
  45. package/src/org/layout.ts +28 -37
  46. package/src/org/parser.ts +16 -1
  47. package/src/org/renderer.ts +159 -121
  48. package/src/org/resolver.ts +90 -23
  49. package/src/palettes/color-utils.ts +30 -0
  50. package/src/render.ts +2 -0
  51. package/src/sequence/parser.ts +202 -42
  52. package/src/sequence/renderer.ts +576 -113
  53. package/src/sequence/tag-resolution.ts +163 -0
  54. package/src/sitemap/collapse.ts +187 -0
  55. package/src/sitemap/layout.ts +738 -0
  56. package/src/sitemap/parser.ts +489 -0
  57. package/src/sitemap/renderer.ts +774 -0
  58. package/src/sitemap/types.ts +42 -0
  59. package/src/utils/tag-groups.ts +119 -0
@@ -0,0 +1,42 @@
1
+ // ============================================================
2
+ // Sitemap Diagram Types
3
+ // ============================================================
4
+
5
+ import type { DgmoError } from '../diagnostics';
6
+ import type { TagGroup } from '../utils/tag-groups';
7
+
8
+ export interface SitemapNode {
9
+ id: string;
10
+ label: string;
11
+ metadata: Record<string, string>;
12
+ children: SitemapNode[];
13
+ parentId: string | null;
14
+ /** True for [Group Name] container nodes */
15
+ isContainer: boolean;
16
+ lineNumber: number;
17
+ color?: string;
18
+ }
19
+
20
+ export interface SitemapEdge {
21
+ sourceId: string;
22
+ targetId: string;
23
+ label?: string;
24
+ color?: string;
25
+ lineNumber: number;
26
+ }
27
+
28
+ export type SitemapDirection = 'TB' | 'LR';
29
+
30
+ export interface ParsedSitemap {
31
+ title: string | null;
32
+ titleLineNumber: number | null;
33
+ direction: SitemapDirection;
34
+ /** Top-level nodes (roots of the hierarchy) */
35
+ roots: SitemapNode[];
36
+ /** All cross-link edges */
37
+ edges: SitemapEdge[];
38
+ tagGroups: TagGroup[];
39
+ options: Record<string, string>;
40
+ diagnostics: DgmoError[];
41
+ error: string | null;
42
+ }
@@ -49,6 +49,125 @@ export function isTagBlockHeading(trimmed: string): boolean {
49
49
  * Parse a tag block heading line into structured data.
50
50
  * Returns `null` if the line is not a tag block heading.
51
51
  */
52
+ // ── Tag Resolution ────────────────────────────────────────
53
+
54
+ /**
55
+ * Resolve a tag color for an entity given its metadata, available tag groups,
56
+ * and the currently active group.
57
+ *
58
+ * Returns the hex color from the matching tag entry, `'#999999'` for
59
+ * untagged/unknown values, or `undefined` when no group is active.
60
+ *
61
+ * @param metadata The entity's key-value metadata (keys already lowercased)
62
+ * @param tagGroups All declared tag groups
63
+ * @param activeGroupName The currently selected tag group (null = no group active)
64
+ * @param isContainer When true, `defaultValue` is NOT applied (containers are structural, not data)
65
+ */
66
+ export function resolveTagColor(
67
+ metadata: Record<string, string>,
68
+ tagGroups: TagGroup[],
69
+ activeGroupName: string | null,
70
+ isContainer?: boolean
71
+ ): string | undefined {
72
+ if (!activeGroupName) return undefined;
73
+
74
+ const group = tagGroups.find(
75
+ (g) => g.name.toLowerCase() === activeGroupName.toLowerCase()
76
+ );
77
+ if (!group) return undefined;
78
+
79
+ const metaValue =
80
+ metadata[group.name.toLowerCase()] ??
81
+ (isContainer ? undefined : group.defaultValue);
82
+ if (!metaValue) return '#999999';
83
+
84
+ return (
85
+ group.entries.find(
86
+ (e) => e.value.toLowerCase() === metaValue.toLowerCase()
87
+ )?.color ?? '#999999'
88
+ );
89
+ }
90
+
91
+ // ── Tag Validation ────────────────────────────────────────
92
+
93
+ /**
94
+ * Validate tag metadata values on a collection of entities against declared
95
+ * tag groups. Emits warnings (via `pushWarning`) for unknown values with
96
+ * did-you-mean suggestions.
97
+ *
98
+ * @param entities Objects with `metadata` and `lineNumber`
99
+ * @param tagGroups Declared tag groups to validate against
100
+ * @param pushWarning Callback to emit a warning at a given line
101
+ * @param suggestFn Optional did-you-mean suggestion function
102
+ */
103
+ export function validateTagValues(
104
+ entities: ReadonlyArray<{ metadata: Record<string, string>; lineNumber: number }>,
105
+ tagGroups: ReadonlyArray<TagGroup>,
106
+ pushWarning: (lineNumber: number, message: string) => void,
107
+ suggestFn?: (input: string, candidates: readonly string[]) => string | null
108
+ ): void {
109
+ if (tagGroups.length === 0) return;
110
+
111
+ const groupMap = new Map<string, TagGroup>();
112
+ for (const g of tagGroups) groupMap.set(g.name.toLowerCase(), g);
113
+
114
+ for (const entity of entities) {
115
+ for (const [key, value] of Object.entries(entity.metadata)) {
116
+ const group = groupMap.get(key);
117
+ if (!group) continue;
118
+ const match = group.entries.some(
119
+ (e) => e.value.toLowerCase() === value.toLowerCase()
120
+ );
121
+ if (!match) {
122
+ const defined = group.entries.map((e) => e.value);
123
+ let msg = `Unknown value '${value}' for tag group '${group.name}'`;
124
+ const hint = suggestFn?.(value, defined);
125
+ if (hint) {
126
+ msg += `. ${hint}`;
127
+ } else {
128
+ msg += ` — defined values: ${defined.join(', ')}`;
129
+ }
130
+ pushWarning(entity.lineNumber, msg);
131
+ }
132
+ }
133
+ }
134
+ }
135
+
136
+ // ── Default Metadata Injection ────────────────────────────
137
+
138
+ /**
139
+ * Inject default tag group values into entity metadata.
140
+ * Only sets keys not already present. Idempotent.
141
+ *
142
+ * @param entities Objects with mutable `metadata`
143
+ * @param tagGroups Tag groups (only those with `defaultValue` matter)
144
+ * @param skip Optional predicate — entities matching this are skipped (e.g. containers)
145
+ */
146
+ export function injectDefaultTagMetadata(
147
+ entities: Array<{ metadata: Record<string, string> }>,
148
+ tagGroups: ReadonlyArray<TagGroup>,
149
+ skip?: (entity: { metadata: Record<string, string> }) => boolean
150
+ ): void {
151
+ const defaults: { key: string; value: string }[] = [];
152
+ for (const group of tagGroups) {
153
+ if (group.defaultValue) {
154
+ defaults.push({ key: group.name.toLowerCase(), value: group.defaultValue });
155
+ }
156
+ }
157
+ if (defaults.length === 0) return;
158
+
159
+ for (const entity of entities) {
160
+ if (skip?.(entity)) continue;
161
+ for (const { key, value } of defaults) {
162
+ if (!(key in entity.metadata)) {
163
+ entity.metadata[key] = value;
164
+ }
165
+ }
166
+ }
167
+ }
168
+
169
+ // ── Matchers ────────────────────────────────────────────────
170
+
52
171
  export function matchTagBlockHeading(trimmed: string): TagBlockMatch | null {
53
172
  // Try new syntax first
54
173
  const tagMatch = trimmed.match(TAG_BLOCK_RE);