@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.
- package/.claude/skills/dgmo-chart/SKILL.md +28 -0
- package/.claude/skills/dgmo-generate/SKILL.md +1 -0
- package/.claude/skills/dgmo-sequence/SKILL.md +24 -1
- package/.cursorrules +27 -2
- package/.github/copilot-instructions.md +36 -3
- package/.windsurfrules +27 -2
- package/README.md +12 -3
- package/dist/cli.cjs +611 -153
- package/dist/index.cjs +8371 -3200
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +502 -58
- package/dist/index.d.ts +502 -58
- package/dist/index.js +8594 -3444
- package/dist/index.js.map +1 -1
- package/docs/ai-integration.md +1 -1
- package/docs/language-reference.md +336 -17
- package/docs/migration-sequence-color-to-tags.md +98 -0
- package/package.json +1 -1
- package/src/c4/renderer.ts +1 -20
- package/src/class/renderer.ts +1 -11
- package/src/cli.ts +40 -0
- package/src/d3.ts +92 -2
- package/src/dgmo-router.ts +11 -0
- package/src/echarts.ts +74 -8
- package/src/er/parser.ts +29 -3
- package/src/er/renderer.ts +1 -15
- package/src/graph/flowchart-parser.ts +7 -30
- package/src/graph/flowchart-renderer.ts +62 -69
- package/src/graph/layout.ts +5 -0
- package/src/graph/state-parser.ts +388 -0
- package/src/graph/state-renderer.ts +496 -0
- package/src/graph/types.ts +4 -2
- package/src/index.ts +42 -1
- package/src/infra/compute.ts +1113 -0
- package/src/infra/layout.ts +575 -0
- package/src/infra/parser.ts +559 -0
- package/src/infra/renderer.ts +1509 -0
- package/src/infra/roles.ts +60 -0
- package/src/infra/serialize.ts +67 -0
- package/src/infra/types.ts +221 -0
- package/src/infra/validation.ts +192 -0
- package/src/initiative-status/layout.ts +56 -61
- package/src/initiative-status/renderer.ts +13 -13
- package/src/kanban/renderer.ts +1 -24
- package/src/org/layout.ts +28 -37
- package/src/org/parser.ts +16 -1
- package/src/org/renderer.ts +159 -121
- package/src/org/resolver.ts +90 -23
- package/src/palettes/color-utils.ts +30 -0
- package/src/render.ts +2 -0
- package/src/sequence/parser.ts +202 -42
- package/src/sequence/renderer.ts +576 -113
- package/src/sequence/tag-resolution.ts +163 -0
- package/src/sitemap/collapse.ts +187 -0
- package/src/sitemap/layout.ts +738 -0
- package/src/sitemap/parser.ts +489 -0
- package/src/sitemap/renderer.ts +774 -0
- package/src/sitemap/types.ts +42 -0
- 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
|
+
}
|
package/src/utils/tag-groups.ts
CHANGED
|
@@ -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);
|