@diagrammo/dgmo 0.7.3 → 0.8.1
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/AGENTS.md +15 -20
- package/README.md +56 -58
- package/dist/cli.cjs +188 -181
- package/dist/index.cjs +3522 -1072
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +196 -43
- package/dist/index.d.ts +196 -43
- package/dist/index.js +3509 -1072
- package/dist/index.js.map +1 -1
- package/docs/language-reference.md +629 -289
- package/package.json +1 -1
- package/src/c4/layout.ts +6 -9
- package/src/c4/parser.ts +189 -83
- package/src/c4/renderer.ts +8 -9
- package/src/chart.ts +296 -83
- package/src/class/parser.ts +54 -37
- package/src/class/renderer.ts +8 -8
- package/src/cli.ts +8 -8
- package/src/colors.ts +4 -1
- package/src/completion.ts +757 -10
- package/src/d3.ts +324 -78
- package/src/dgmo-router.ts +63 -8
- package/src/echarts.ts +735 -241
- package/src/er/parser.ts +94 -76
- package/src/er/renderer.ts +6 -5
- package/src/gantt/parser.ts +144 -69
- package/src/gantt/renderer.ts +50 -14
- package/src/gantt/types.ts +3 -3
- package/src/graph/flowchart-parser.ts +97 -37
- package/src/graph/flowchart-renderer.ts +4 -3
- package/src/graph/state-parser.ts +50 -31
- package/src/graph/state-renderer.ts +4 -3
- package/src/index.ts +14 -5
- package/src/infra/compute.ts +1 -0
- package/src/infra/layout.ts +3 -0
- package/src/infra/parser.ts +291 -92
- package/src/infra/renderer.ts +172 -30
- package/src/infra/types.ts +5 -0
- package/src/initiative-status/layout.ts +1 -1
- package/src/initiative-status/parser.ts +121 -47
- package/src/initiative-status/renderer.ts +42 -23
- package/src/initiative-status/types.ts +10 -2
- package/src/kanban/parser.ts +60 -37
- package/src/kanban/renderer.ts +2 -2
- package/src/kanban/types.ts +1 -0
- package/src/org/layout.ts +9 -9
- package/src/org/parser.ts +39 -40
- package/src/org/renderer.ts +5 -6
- package/src/org/resolver.ts +26 -19
- package/src/render.ts +1 -1
- package/src/sequence/parser.ts +304 -95
- package/src/sequence/renderer.ts +9 -9
- package/src/sitemap/layout.ts +3 -4
- package/src/sitemap/parser.ts +57 -49
- package/src/sitemap/renderer.ts +6 -7
- package/src/utils/arrows.ts +25 -6
- package/src/utils/duration.ts +43 -7
- package/src/utils/legend-constants.ts +26 -0
- package/src/utils/legend-svg.ts +167 -0
- package/src/utils/parsing.ts +247 -7
- package/src/utils/tag-groups.ts +160 -15
- package/src/utils/title-constants.ts +9 -0
package/src/kanban/renderer.ts
CHANGED
|
@@ -35,8 +35,8 @@ const CARD_RADIUS = 6;
|
|
|
35
35
|
const CARD_PADDING_X = 10;
|
|
36
36
|
const CARD_PADDING_Y = 6;
|
|
37
37
|
const CARD_STROKE_WIDTH = 1.5;
|
|
38
|
+
import { TITLE_FONT_SIZE, TITLE_FONT_WEIGHT } from '../utils/title-constants';
|
|
38
39
|
const TITLE_HEIGHT = 30;
|
|
39
|
-
const TITLE_FONT_SIZE = 18;
|
|
40
40
|
const COLUMN_HEADER_FONT_SIZE = 13;
|
|
41
41
|
const CARD_TITLE_FONT_SIZE = 12;
|
|
42
42
|
const CARD_META_FONT_SIZE = 10;
|
|
@@ -235,7 +235,7 @@ export function renderKanban(
|
|
|
235
235
|
.attr('x', DIAGRAM_PADDING)
|
|
236
236
|
.attr('y', DIAGRAM_PADDING + TITLE_FONT_SIZE)
|
|
237
237
|
.attr('font-size', TITLE_FONT_SIZE)
|
|
238
|
-
.attr('font-weight',
|
|
238
|
+
.attr('font-weight', TITLE_FONT_WEIGHT)
|
|
239
239
|
.attr('fill', palette.text)
|
|
240
240
|
.text(parsed.title);
|
|
241
241
|
}
|
package/src/kanban/types.ts
CHANGED
package/src/org/layout.ts
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
// ============================================================
|
|
4
4
|
|
|
5
5
|
import { hierarchy, tree } from 'd3-hierarchy';
|
|
6
|
-
import type { ParsedOrg, OrgNode
|
|
6
|
+
import type { ParsedOrg, OrgNode } from './parser';
|
|
7
|
+
import type { TagGroup } from '../utils/tag-groups';
|
|
7
8
|
import { resolveTagColor, injectDefaultTagMetadata } from '../utils/tag-groups';
|
|
9
|
+
import { LEGEND_PILL_FONT_SIZE, LEGEND_ENTRY_FONT_SIZE, measureLegendText } from '../utils/legend-constants';
|
|
8
10
|
|
|
9
11
|
// ============================================================
|
|
10
12
|
// Types
|
|
@@ -101,10 +103,8 @@ const STACK_V_GAP = 20;
|
|
|
101
103
|
const LEGEND_GAP = 30;
|
|
102
104
|
const LEGEND_HEIGHT = 28;
|
|
103
105
|
const LEGEND_PILL_PAD = 16;
|
|
104
|
-
const LEGEND_PILL_FONT_W = 11 * 0.6;
|
|
105
106
|
const LEGEND_CAPSULE_PAD = 4;
|
|
106
107
|
const LEGEND_DOT_R = 4;
|
|
107
|
-
const LEGEND_ENTRY_FONT_W = 10 * 0.6;
|
|
108
108
|
const LEGEND_ENTRY_DOT_GAP = 4;
|
|
109
109
|
const LEGEND_ENTRY_TRAIL = 8;
|
|
110
110
|
const LEGEND_GROUP_GAP = 12;
|
|
@@ -167,7 +167,7 @@ function computeCardHeight(meta: Record<string, string>): number {
|
|
|
167
167
|
|
|
168
168
|
function resolveNodeColor(
|
|
169
169
|
node: OrgNode,
|
|
170
|
-
tagGroups:
|
|
170
|
+
tagGroups: TagGroup[],
|
|
171
171
|
activeGroupName: string | null
|
|
172
172
|
): string | undefined {
|
|
173
173
|
// Explicit inline (color) always wins — handled before tag resolution
|
|
@@ -262,7 +262,7 @@ function centerHeavyChildren(node: TreeNode): void {
|
|
|
262
262
|
// ============================================================
|
|
263
263
|
|
|
264
264
|
function computeLegendGroups(
|
|
265
|
-
tagGroups:
|
|
265
|
+
tagGroups: TagGroup[],
|
|
266
266
|
showEyeIcons: boolean,
|
|
267
267
|
usedValuesByGroup?: Map<string, Set<string>>
|
|
268
268
|
): OrgLegendGroup[] {
|
|
@@ -279,7 +279,7 @@ function computeLegendGroups(
|
|
|
279
279
|
if (visibleEntries.length === 0) continue;
|
|
280
280
|
|
|
281
281
|
// Pill label shows just the group name (alias is for DSL shorthand only)
|
|
282
|
-
const pillWidth = group.name
|
|
282
|
+
const pillWidth = measureLegendText(group.name, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD;
|
|
283
283
|
const minPillWidth = pillWidth;
|
|
284
284
|
|
|
285
285
|
// Capsule: pad + pill + gap + entries + pad
|
|
@@ -288,7 +288,7 @@ function computeLegendGroups(
|
|
|
288
288
|
entriesWidth +=
|
|
289
289
|
LEGEND_DOT_R * 2 +
|
|
290
290
|
LEGEND_ENTRY_DOT_GAP +
|
|
291
|
-
entry.value
|
|
291
|
+
measureLegendText(entry.value, LEGEND_ENTRY_FONT_SIZE) +
|
|
292
292
|
LEGEND_ENTRY_TRAIL;
|
|
293
293
|
}
|
|
294
294
|
const eyeSpace = showEyeIcons ? LEGEND_EYE_SIZE + LEGEND_EYE_GAP : 0;
|
|
@@ -320,7 +320,7 @@ function computeLegendGroups(
|
|
|
320
320
|
*/
|
|
321
321
|
function injectDefaultMetadata(
|
|
322
322
|
roots: OrgNode[],
|
|
323
|
-
tagGroups:
|
|
323
|
+
tagGroups: TagGroup[]
|
|
324
324
|
): void {
|
|
325
325
|
// Flatten all nodes (recursive) for the shared utility
|
|
326
326
|
const allNodes: OrgNode[] = [];
|
|
@@ -377,7 +377,7 @@ export function layoutOrg(
|
|
|
377
377
|
|
|
378
378
|
// Build tree structure
|
|
379
379
|
const subNodeLabel = parsed.options['sub-node-label'] ?? undefined;
|
|
380
|
-
const showSubNodeCount = parsed.options['show-sub-node-count']?.toLowerCase()
|
|
380
|
+
const showSubNodeCount = ['yes', 'on'].includes(parsed.options['show-sub-node-count']?.toLowerCase() ?? '');
|
|
381
381
|
const treeNodes = buildTreeNodes(parsed.roots, hiddenCounts, hiddenAttributes, subNodeLabel, showSubNodeCount);
|
|
382
382
|
|
|
383
383
|
// Single root or virtual root for multiple roots
|
package/src/org/parser.ts
CHANGED
|
@@ -8,20 +8,14 @@ import {
|
|
|
8
8
|
extractColor,
|
|
9
9
|
parsePipeMetadata,
|
|
10
10
|
MULTIPLE_PIPE_WARNING,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
OPTION_RE,
|
|
11
|
+
parseFirstLine,
|
|
12
|
+
OPTION_NOCOLON_RE,
|
|
14
13
|
} from '../utils/parsing';
|
|
15
14
|
|
|
16
15
|
// ============================================================
|
|
17
16
|
// Types
|
|
18
17
|
// ============================================================
|
|
19
18
|
|
|
20
|
-
/** @deprecated Use `TagEntry` from `utils/tag-groups` */
|
|
21
|
-
export type OrgTagEntry = TagEntry;
|
|
22
|
-
/** @deprecated Use `TagGroup` from `utils/tag-groups` */
|
|
23
|
-
export type OrgTagGroup = TagGroup;
|
|
24
|
-
|
|
25
19
|
export interface OrgNode {
|
|
26
20
|
id: string;
|
|
27
21
|
label: string;
|
|
@@ -37,7 +31,7 @@ export interface ParsedOrg {
|
|
|
37
31
|
title: string | null;
|
|
38
32
|
titleLineNumber: number | null;
|
|
39
33
|
roots: OrgNode[];
|
|
40
|
-
tagGroups:
|
|
34
|
+
tagGroups: TagGroup[];
|
|
41
35
|
options: Record<string, string>;
|
|
42
36
|
diagnostics: DgmoError[];
|
|
43
37
|
error: string | null;
|
|
@@ -50,6 +44,15 @@ export interface ParsedOrg {
|
|
|
50
44
|
const CONTAINER_RE = /^\[([^\]]+)\]$/;
|
|
51
45
|
const METADATA_RE = /^([^:]+):\s*(.+)$/;
|
|
52
46
|
|
|
47
|
+
/** Known org chart options (key-value). */
|
|
48
|
+
const KNOWN_OPTIONS = new Set([
|
|
49
|
+
'direction', 'sub-node-label', 'hide', 'show-sub-node-count',
|
|
50
|
+
]);
|
|
51
|
+
/** Known org chart boolean options (bare keyword = on). */
|
|
52
|
+
const KNOWN_BOOLEANS = new Set([
|
|
53
|
+
'show-sub-node-count',
|
|
54
|
+
]);
|
|
55
|
+
|
|
53
56
|
// ============================================================
|
|
54
57
|
// Inference
|
|
55
58
|
// ============================================================
|
|
@@ -111,7 +114,7 @@ export function parseOrg(
|
|
|
111
114
|
let containerCounter = 0;
|
|
112
115
|
|
|
113
116
|
// Tag group parsing state
|
|
114
|
-
let currentTagGroup:
|
|
117
|
+
let currentTagGroup: TagGroup | null = null;
|
|
115
118
|
|
|
116
119
|
// Alias map: alias (lowercased) → group name (lowercased)
|
|
117
120
|
const aliasMap = new Map<string, string>();
|
|
@@ -139,28 +142,21 @@ export function parseOrg(
|
|
|
139
142
|
|
|
140
143
|
// --- Header phase ---
|
|
141
144
|
|
|
142
|
-
// chart
|
|
145
|
+
// Extract chart type + title from first line (e.g. `org My Org Chart`)
|
|
143
146
|
if (!contentStarted) {
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
if (chartType !== 'org') {
|
|
147
|
+
const firstLine = parseFirstLine(trimmed);
|
|
148
|
+
if (firstLine) {
|
|
149
|
+
if (firstLine.chartType !== 'org') {
|
|
148
150
|
const allTypes = ['org', 'class', 'flowchart', 'sequence', 'er', 'bar', 'line', 'pie', 'scatter', 'sankey', 'venn', 'timeline', 'arc', 'slope'];
|
|
149
|
-
let msg = `Expected chart type "org", got "${chartType}"`;
|
|
150
|
-
const hint = suggest(chartType, allTypes);
|
|
151
|
+
let msg = `Expected chart type "org", got "${firstLine.chartType}"`;
|
|
152
|
+
const hint = suggest(firstLine.chartType, allTypes);
|
|
151
153
|
if (hint) msg += `. ${hint}`;
|
|
152
154
|
return fail(lineNumber, msg);
|
|
153
155
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
// title: value
|
|
159
|
-
if (!contentStarted) {
|
|
160
|
-
const titleMatch = trimmed.match(TITLE_RE);
|
|
161
|
-
if (titleMatch) {
|
|
162
|
-
result.title = titleMatch[1].trim();
|
|
163
|
-
result.titleLineNumber = lineNumber;
|
|
156
|
+
if (firstLine.title) {
|
|
157
|
+
result.title = firstLine.title;
|
|
158
|
+
result.titleLineNumber = lineNumber;
|
|
159
|
+
}
|
|
164
160
|
continue;
|
|
165
161
|
}
|
|
166
162
|
}
|
|
@@ -174,7 +170,8 @@ export function parseOrg(
|
|
|
174
170
|
continue;
|
|
175
171
|
}
|
|
176
172
|
if (tagBlockMatch.deprecated) {
|
|
177
|
-
|
|
173
|
+
pushError(lineNumber, `'## ${tagBlockMatch.name}' is no longer supported — use 'tag: ${tagBlockMatch.name}' instead`);
|
|
174
|
+
continue;
|
|
178
175
|
}
|
|
179
176
|
currentTagGroup = {
|
|
180
177
|
name: tagBlockMatch.name,
|
|
@@ -189,34 +186,36 @@ export function parseOrg(
|
|
|
189
186
|
continue;
|
|
190
187
|
}
|
|
191
188
|
|
|
192
|
-
// Generic header options (
|
|
193
|
-
// Only match non-indented lines with
|
|
189
|
+
// Generic header options (space-separated: `key value` or bare boolean `key`)
|
|
190
|
+
// Only match non-indented lines with known option keys
|
|
194
191
|
if (!contentStarted && !currentTagGroup && measureIndent(line) === 0) {
|
|
195
|
-
const optMatch = trimmed.match(
|
|
192
|
+
const optMatch = trimmed.match(OPTION_NOCOLON_RE);
|
|
196
193
|
if (optMatch) {
|
|
197
194
|
const key = optMatch[1].trim().toLowerCase();
|
|
198
|
-
if (key
|
|
195
|
+
if (KNOWN_OPTIONS.has(key)) {
|
|
199
196
|
result.options[key] = optMatch[2].trim();
|
|
200
197
|
continue;
|
|
201
198
|
}
|
|
202
199
|
}
|
|
200
|
+
// Bare boolean option (single keyword, no value)
|
|
201
|
+
if (KNOWN_BOOLEANS.has(trimmed.toLowerCase())) {
|
|
202
|
+
result.options[trimmed.toLowerCase()] = 'on';
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
203
205
|
}
|
|
204
206
|
|
|
205
|
-
// Tag group entries (indented Value(color)
|
|
207
|
+
// Tag group entries (indented Value(color) under tag heading)
|
|
208
|
+
// First entry is implicitly the default.
|
|
206
209
|
if (currentTagGroup && !contentStarted) {
|
|
207
210
|
const indent = measureIndent(line);
|
|
208
211
|
if (indent > 0) {
|
|
209
|
-
|
|
210
|
-
const isDefault = /\bdefault\s*$/.test(trimmed);
|
|
211
|
-
const entryText = isDefault
|
|
212
|
-
? trimmed.replace(/\s+default\s*$/, '').trim()
|
|
213
|
-
: trimmed;
|
|
214
|
-
const { label, color } = extractColor(entryText, palette);
|
|
212
|
+
const { label, color } = extractColor(trimmed, palette);
|
|
215
213
|
if (!color) {
|
|
216
214
|
pushError(lineNumber, `Expected 'Value(color)' in tag group '${currentTagGroup.name}'`);
|
|
217
215
|
continue;
|
|
218
216
|
}
|
|
219
|
-
|
|
217
|
+
// First entry is the default
|
|
218
|
+
if (currentTagGroup.entries.length === 0) {
|
|
220
219
|
currentTagGroup.defaultValue = label;
|
|
221
220
|
}
|
|
222
221
|
currentTagGroup.entries.push({
|
package/src/org/renderer.ts
CHANGED
|
@@ -15,11 +15,9 @@ import {
|
|
|
15
15
|
LEGEND_HEIGHT,
|
|
16
16
|
LEGEND_PILL_PAD,
|
|
17
17
|
LEGEND_PILL_FONT_SIZE,
|
|
18
|
-
LEGEND_PILL_FONT_W,
|
|
19
18
|
LEGEND_CAPSULE_PAD,
|
|
20
19
|
LEGEND_DOT_R,
|
|
21
20
|
LEGEND_ENTRY_FONT_SIZE,
|
|
22
|
-
LEGEND_ENTRY_FONT_W,
|
|
23
21
|
LEGEND_ENTRY_DOT_GAP,
|
|
24
22
|
LEGEND_ENTRY_TRAIL,
|
|
25
23
|
LEGEND_GROUP_GAP,
|
|
@@ -27,6 +25,7 @@ import {
|
|
|
27
25
|
LEGEND_EYE_GAP,
|
|
28
26
|
EYE_OPEN_PATH,
|
|
29
27
|
EYE_CLOSED_PATH,
|
|
28
|
+
measureLegendText,
|
|
30
29
|
} from '../utils/legend-constants';
|
|
31
30
|
|
|
32
31
|
// ============================================================
|
|
@@ -35,8 +34,8 @@ import {
|
|
|
35
34
|
|
|
36
35
|
const DIAGRAM_PADDING = 20;
|
|
37
36
|
const MAX_SCALE = 3;
|
|
37
|
+
import { TITLE_FONT_SIZE, TITLE_FONT_WEIGHT } from '../utils/title-constants';
|
|
38
38
|
const TITLE_HEIGHT = 30;
|
|
39
|
-
const TITLE_FONT_SIZE = 18;
|
|
40
39
|
const LABEL_FONT_SIZE = 13;
|
|
41
40
|
const META_FONT_SIZE = 11;
|
|
42
41
|
const META_LINE_HEIGHT = 16;
|
|
@@ -179,7 +178,7 @@ export function renderOrg(
|
|
|
179
178
|
.attr('text-anchor', 'middle')
|
|
180
179
|
.attr('fill', palette.text)
|
|
181
180
|
.attr('font-size', TITLE_FONT_SIZE)
|
|
182
|
-
.attr('font-weight',
|
|
181
|
+
.attr('font-weight', TITLE_FONT_WEIGHT)
|
|
183
182
|
.attr('class', 'org-title chart-title')
|
|
184
183
|
.style(
|
|
185
184
|
'cursor',
|
|
@@ -534,7 +533,7 @@ export function renderOrg(
|
|
|
534
533
|
|
|
535
534
|
const pillLabel = group.name;
|
|
536
535
|
const pillWidth =
|
|
537
|
-
pillLabel
|
|
536
|
+
measureLegendText(pillLabel, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD;
|
|
538
537
|
|
|
539
538
|
const gX = fixedPositions?.get(group.name) ?? group.x;
|
|
540
539
|
const gY = fixedPositions ? 0 : group.y;
|
|
@@ -656,7 +655,7 @@ export function renderOrg(
|
|
|
656
655
|
.attr('fill', palette.textMuted)
|
|
657
656
|
.text(entryLabel);
|
|
658
657
|
|
|
659
|
-
entryX = textX + entryLabel
|
|
658
|
+
entryX = textX + measureLegendText(entryLabel, LEGEND_ENTRY_FONT_SIZE) + LEGEND_ENTRY_TRAIL;
|
|
660
659
|
}
|
|
661
660
|
}
|
|
662
661
|
}
|
package/src/org/resolver.ts
CHANGED
|
@@ -34,10 +34,18 @@ export interface ResolveImportsResult {
|
|
|
34
34
|
// ============================================================
|
|
35
35
|
|
|
36
36
|
const MAX_DEPTH = 10;
|
|
37
|
-
const IMPORT_RE = /^(\s+)import
|
|
38
|
-
const TAGS_RE = /^tags
|
|
39
|
-
|
|
40
|
-
const
|
|
37
|
+
const IMPORT_RE = /^(\s+)import:?\s+(.+\.dgmo)\s*$/i;
|
|
38
|
+
const TAGS_RE = /^tags:?\s+(.+\.dgmo)\s*$/i;
|
|
39
|
+
/** Matches new-style first line: `org ...` or old `chart: ...` or `title: ...` */
|
|
40
|
+
const HEADER_RE = /^(org|kanban|chart\s*:|title\s*:)/i;
|
|
41
|
+
/**
|
|
42
|
+
* Known option keys that can appear in org chart headers (space-separated).
|
|
43
|
+
* Only these are stripped from imported files — avoids eating content like "Alice Chen".
|
|
44
|
+
*/
|
|
45
|
+
const KNOWN_HEADER_OPTIONS = new Set([
|
|
46
|
+
'direction', 'sub-node-label', 'hide', 'show-sub-node-count',
|
|
47
|
+
'color-off',
|
|
48
|
+
]);
|
|
41
49
|
|
|
42
50
|
// ============================================================
|
|
43
51
|
// Path Helpers (pure string ops — no Node `path` dependency)
|
|
@@ -116,7 +124,7 @@ interface ParsedHeader {
|
|
|
116
124
|
|
|
117
125
|
/**
|
|
118
126
|
* Separate an imported file into header (stripped) and content body.
|
|
119
|
-
* Also extracts tag groups and tags
|
|
127
|
+
* Also extracts tag groups and tags directive for merging.
|
|
120
128
|
*/
|
|
121
129
|
function parseFileHeader(lines: string[]): ParsedHeader {
|
|
122
130
|
const tagGroups = extractTagGroups(lines);
|
|
@@ -157,11 +165,10 @@ function parseFileHeader(lines: string[]): ParsedHeader {
|
|
|
157
165
|
continue;
|
|
158
166
|
}
|
|
159
167
|
|
|
160
|
-
//
|
|
161
|
-
if (
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (key !== 'chart' && key !== 'title' && !trimmed.includes('|')) {
|
|
168
|
+
// Known option header lines (space-separated `key value` or bare boolean)
|
|
169
|
+
if (!lines[i].match(/^\s/) && !isTagBlockHeading(trimmed) && !trimmed.includes('|')) {
|
|
170
|
+
const firstToken = trimmed.split(/\s/)[0].toLowerCase();
|
|
171
|
+
if (KNOWN_HEADER_OPTIONS.has(firstToken)) {
|
|
165
172
|
continue;
|
|
166
173
|
}
|
|
167
174
|
}
|
|
@@ -181,7 +188,7 @@ function parseFileHeader(lines: string[]): ParsedHeader {
|
|
|
181
188
|
// ============================================================
|
|
182
189
|
|
|
183
190
|
/**
|
|
184
|
-
* Pre-processes org chart content, resolving `tags
|
|
191
|
+
* Pre-processes org chart content, resolving `tags` and `import` directives.
|
|
185
192
|
*
|
|
186
193
|
* @param content - Raw .dgmo file content
|
|
187
194
|
* @param filePath - Absolute path of the file (for relative path resolution)
|
|
@@ -220,7 +227,7 @@ async function resolveFile(
|
|
|
220
227
|
const bodyStartIndex = findBodyStart(lines);
|
|
221
228
|
|
|
222
229
|
// Collect header lines (chart:, title:, options, tags:)
|
|
223
|
-
let tagsLineNumber = 0; // 1-based line number of the tags
|
|
230
|
+
let tagsLineNumber = 0; // 1-based line number of the tags directive
|
|
224
231
|
for (let i = 0; i < bodyStartIndex; i++) {
|
|
225
232
|
const trimmed = lines[i].trim();
|
|
226
233
|
if (trimmed === '' || trimmed.startsWith('//')) {
|
|
@@ -240,7 +247,7 @@ async function resolveFile(
|
|
|
240
247
|
headerLines.push({ text: lines[i], originalLine: i + 1 });
|
|
241
248
|
}
|
|
242
249
|
|
|
243
|
-
// ---- Step 2: Resolve tags
|
|
250
|
+
// ---- Step 2: Resolve tags directive ----
|
|
244
251
|
let tagsFileGroups: TagGroupBlock[] = [];
|
|
245
252
|
if (tagsDirective) {
|
|
246
253
|
const tagsPath = resolvePath(filePath, tagsDirective);
|
|
@@ -255,7 +262,7 @@ async function resolveFile(
|
|
|
255
262
|
}
|
|
256
263
|
}
|
|
257
264
|
|
|
258
|
-
// ---- Step 3: Resolve import
|
|
265
|
+
// ---- Step 3: Resolve import directives in body ----
|
|
259
266
|
const bodyLines = lines.slice(bodyStartIndex);
|
|
260
267
|
const resolvedBodyLines: { text: string; originalLine: number | null; importSource: ImportSource | null }[] = [];
|
|
261
268
|
const importedTagGroups: TagGroupBlock[] = [];
|
|
@@ -381,7 +388,7 @@ async function resolveFile(
|
|
|
381
388
|
const lineMap: (number | null)[] = [null];
|
|
382
389
|
const importSourceMap: (ImportSource | null)[] = [null];
|
|
383
390
|
|
|
384
|
-
// Header lines (chart:, title:, options — no tags
|
|
391
|
+
// Header lines (chart:, title:, options — no tags or tag groups)
|
|
385
392
|
for (const entry of headerLines) {
|
|
386
393
|
outputLines.push(entry.text);
|
|
387
394
|
lineMap.push(entry.originalLine);
|
|
@@ -470,10 +477,10 @@ function findBodyStart(lines: string[]): number {
|
|
|
470
477
|
if (HEADER_RE.test(trimmed)) continue;
|
|
471
478
|
if (TAGS_RE.test(trimmed)) continue;
|
|
472
479
|
|
|
473
|
-
//
|
|
474
|
-
if (
|
|
475
|
-
const
|
|
476
|
-
if (
|
|
480
|
+
// Known option lines (space-separated `key value` or bare boolean before content)
|
|
481
|
+
if (!lines[i].match(/^\s/) && !trimmed.includes('|') && !isTagBlockHeading(trimmed)) {
|
|
482
|
+
const firstToken = trimmed.split(/\s/)[0].toLowerCase();
|
|
483
|
+
if (KNOWN_HEADER_OPTIONS.has(firstToken)) {
|
|
477
484
|
continue;
|
|
478
485
|
}
|
|
479
486
|
}
|
package/src/render.ts
CHANGED
|
@@ -57,7 +57,7 @@ export async function render(
|
|
|
57
57
|
): Promise<string> {
|
|
58
58
|
const theme = options?.theme ?? 'light';
|
|
59
59
|
const paletteName = options?.palette ?? 'nord';
|
|
60
|
-
const branding = options?.branding ??
|
|
60
|
+
const branding = options?.branding ?? false;
|
|
61
61
|
|
|
62
62
|
const paletteColors = getPalette(paletteName)[theme === 'dark' ? 'dark' : 'light'];
|
|
63
63
|
|