@diagrammo/dgmo 0.47.0 → 0.48.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/auto.js +1 -1
- package/dist/auto.mjs +1 -1
- package/dist/element.js +1 -1
- package/dist/element.mjs +1 -1
- package/package.json +19 -70
- package/dist/advanced.cjs +0 -71648
- package/dist/advanced.d.cts +0 -5719
- package/dist/auto.cjs +0 -70648
- package/dist/auto.d.cts +0 -54
- package/dist/block.cjs +0 -70208
- package/dist/block.d.cts +0 -115
- package/dist/editor.cjs +0 -526
- package/dist/editor.d.cts +0 -26
- package/dist/element.cjs +0 -70530
- package/dist/element.d.cts +0 -62
- package/dist/highlight.cjs +0 -852
- package/dist/highlight.d.cts +0 -49
- package/dist/index.cjs +0 -70491
- package/dist/index.d.cts +0 -160
- package/dist/pert.cjs +0 -325
- package/dist/pert.d.cts +0 -559
- package/dist/themes-1-CuKpeH.d.cts +0 -908
- package/src/advanced.ts +0 -873
- package/src/arc/parser.ts +0 -15
- package/src/arc/renderer.ts +0 -633
- package/src/auto/index.ts +0 -621
- package/src/auto/shared.ts +0 -370
- package/src/auto/styles.ts +0 -73
- package/src/block/layout.ts +0 -198
- package/src/block/parser.ts +0 -427
- package/src/block/renderer.ts +0 -484
- package/src/block/types.ts +0 -67
- package/src/boxes-and-lines/collapse.ts +0 -96
- package/src/boxes-and-lines/focus.ts +0 -257
- package/src/boxes-and-lines/label-placement.ts +0 -339
- package/src/boxes-and-lines/layout-grouped.ts +0 -912
- package/src/boxes-and-lines/layout-layered.ts +0 -722
- package/src/boxes-and-lines/layout-search.ts +0 -1678
- package/src/boxes-and-lines/layout.ts +0 -398
- package/src/boxes-and-lines/parser.ts +0 -1181
- package/src/boxes-and-lines/renderer.ts +0 -1465
- package/src/boxes-and-lines/types.ts +0 -66
- package/src/c4/layout.ts +0 -2441
- package/src/c4/parser.ts +0 -1158
- package/src/c4/renderer.ts +0 -2008
- package/src/c4/types.ts +0 -89
- package/src/chart-type-registry.ts +0 -529
- package/src/chart-types.ts +0 -225
- package/src/chart.ts +0 -685
- package/src/charts-d3/bar.ts +0 -338
- package/src/charts-d3/chord.ts +0 -140
- package/src/charts-d3/function.ts +0 -147
- package/src/charts-d3/funnel.ts +0 -104
- package/src/charts-d3/heatmap.ts +0 -145
- package/src/charts-d3/index.ts +0 -337
- package/src/charts-d3/interactions.ts +0 -477
- package/src/charts-d3/line.ts +0 -344
- package/src/charts-d3/mount.ts +0 -151
- package/src/charts-d3/pie.ts +0 -117
- package/src/charts-d3/polar.ts +0 -89
- package/src/charts-d3/radar.ts +0 -0
- package/src/charts-d3/sankey.ts +0 -0
- package/src/charts-d3/scatter.ts +0 -185
- package/src/charts-d3/shared.ts +0 -208
- package/src/class/layout.ts +0 -326
- package/src/class/parser.ts +0 -635
- package/src/class/renderer.ts +0 -793
- package/src/class/types.ts +0 -58
- package/src/cli-banner.ts +0 -106
- package/src/cli.ts +0 -1080
- package/src/colors-diagnostics.ts +0 -35
- package/src/colors.ts +0 -379
- package/src/completion-types.ts +0 -27
- package/src/completion.ts +0 -2668
- package/src/cycle/layout.ts +0 -1026
- package/src/cycle/parser.ts +0 -387
- package/src/cycle/renderer.ts +0 -653
- package/src/cycle/types.ts +0 -101
- package/src/d3.ts +0 -1434
- package/src/data-chart-parser.ts +0 -1007
- package/src/dgmo-router.ts +0 -296
- package/src/diagnostics-registry.ts +0 -108
- package/src/diagnostics.ts +0 -0
- package/src/dimensions.ts +0 -20
- package/src/directives-registry.ts +0 -396
- package/src/editor/dgmo.grammar +0 -76
- package/src/editor/dgmo.grammar.d.ts +0 -2
- package/src/editor/dgmo.grammar.js +0 -18
- package/src/editor/dgmo.grammar.terms.d.ts +0 -5
- package/src/editor/dgmo.grammar.terms.js +0 -35
- package/src/editor/highlight-api.ts +0 -596
- package/src/editor/highlight.ts +0 -36
- package/src/editor/index.ts +0 -27
- package/src/editor/keywords.ts +0 -128
- package/src/editor/tokens.ts +0 -30
- package/src/element/index.ts +0 -432
- package/src/embed/css.ts +0 -312
- package/src/embed/escape.ts +0 -15
- package/src/embed/index.ts +0 -280
- package/src/er/classify.ts +0 -228
- package/src/er/layout.ts +0 -530
- package/src/er/parser.ts +0 -750
- package/src/er/renderer.ts +0 -725
- package/src/er/types.ts +0 -52
- package/src/error-card.ts +0 -269
- package/src/event-line/diagnostics.ts +0 -117
- package/src/event-line/parser.ts +0 -643
- package/src/event-line/renderer.ts +0 -1742
- package/src/event-line/types.ts +0 -74
- package/src/fonts.ts +0 -3
- package/src/gantt/calculator.ts +0 -979
- package/src/gantt/parser.ts +0 -1641
- package/src/gantt/renderer.ts +0 -3787
- package/src/gantt/resolver.ts +0 -172
- package/src/gantt/types.ts +0 -206
- package/src/graph/diagnostics.ts +0 -64
- package/src/graph/edge-spline.ts +0 -29
- package/src/graph/flowchart-parser.ts +0 -759
- package/src/graph/flowchart-renderer.ts +0 -817
- package/src/graph/layout.ts +0 -472
- package/src/graph/notes.ts +0 -21
- package/src/graph/state-collapse.ts +0 -78
- package/src/graph/state-parser.ts +0 -574
- package/src/graph/state-renderer.ts +0 -653
- package/src/graph/types.ts +0 -65
- package/src/index.ts +0 -261
- package/src/infra/compute.ts +0 -1385
- package/src/infra/layout.ts +0 -888
- package/src/infra/parser.ts +0 -1234
- package/src/infra/renderer.ts +0 -2534
- package/src/infra/roles.ts +0 -75
- package/src/infra/types.ts +0 -216
- package/src/infra/validation.ts +0 -201
- package/src/journey-map/layout.ts +0 -490
- package/src/journey-map/parser.ts +0 -557
- package/src/journey-map/renderer.ts +0 -1580
- package/src/journey-map/types.ts +0 -47
- package/src/kanban/mutations.ts +0 -175
- package/src/kanban/parser.ts +0 -509
- package/src/kanban/renderer.ts +0 -1449
- package/src/kanban/types.ts +0 -39
- package/src/label-layout.ts +0 -368
- package/src/map/colorize.ts +0 -54
- package/src/map/completion.ts +0 -316
- package/src/map/context-labels.ts +0 -609
- package/src/map/data/PROVENANCE.json +0 -1
- package/src/map/data/README.md +0 -40
- package/src/map/data/airport-collisions.json +0 -1
- package/src/map/data/airports.json +0 -1
- package/src/map/data/gazetteer.json +0 -1
- package/src/map/data/lakes.json +0 -1
- package/src/map/data/mountain-ranges.json +0 -1
- package/src/map/data/na-lakes.json +0 -1
- package/src/map/data/na-land.json +0 -1
- package/src/map/data/region-names.json +0 -1
- package/src/map/data/rivers.json +0 -1
- package/src/map/data/types.ts +0 -121
- package/src/map/data/us-states.json +0 -1
- package/src/map/data/water-bodies.json +0 -1
- package/src/map/data/world-coarse.json +0 -1
- package/src/map/data/world-detail.json +0 -1
- package/src/map/diagnostics.ts +0 -134
- package/src/map/dimensions.ts +0 -150
- package/src/map/geo-query.ts +0 -295
- package/src/map/geo.ts +0 -438
- package/src/map/invert.ts +0 -111
- package/src/map/layout.ts +0 -5069
- package/src/map/legend-band.ts +0 -127
- package/src/map/load-data.ts +0 -186
- package/src/map/parser.ts +0 -783
- package/src/map/renderer.ts +0 -1445
- package/src/map/resolved-types.ts +0 -160
- package/src/map/resolver.ts +0 -1146
- package/src/map/types.ts +0 -194
- package/src/mindmap/collapse.ts +0 -67
- package/src/mindmap/layout.ts +0 -649
- package/src/mindmap/parser.ts +0 -430
- package/src/mindmap/renderer.ts +0 -637
- package/src/mindmap/text-wrap.ts +0 -218
- package/src/mindmap/types.ts +0 -56
- package/src/org/collapse.ts +0 -151
- package/src/org/layout.ts +0 -1262
- package/src/org/parser.ts +0 -519
- package/src/org/renderer.ts +0 -866
- package/src/org/resolver.ts +0 -636
- package/src/palettes/atlas.ts +0 -77
- package/src/palettes/blueprint.ts +0 -73
- package/src/palettes/catppuccin.ts +0 -84
- package/src/palettes/color-utils.ts +0 -443
- package/src/palettes/index.ts +0 -67
- package/src/palettes/nord.ts +0 -67
- package/src/palettes/registry.ts +0 -114
- package/src/palettes/slate.ts +0 -73
- package/src/palettes/tidewater.ts +0 -73
- package/src/palettes/tokyo-night.ts +0 -86
- package/src/palettes/types.ts +0 -88
- package/src/pert/analyzer.ts +0 -1183
- package/src/pert/diagnostics.ts +0 -110
- package/src/pert/index.ts +0 -43
- package/src/pert/internal.ts +0 -252
- package/src/pert/layout.ts +0 -971
- package/src/pert/monte-carlo.ts +0 -446
- package/src/pert/parser.ts +0 -1606
- package/src/pert/renderer.ts +0 -4009
- package/src/pert/share-normalize.ts +0 -62
- package/src/pert/types.ts +0 -604
- package/src/pyramid/parser.ts +0 -185
- package/src/pyramid/renderer.ts +0 -768
- package/src/pyramid/types.ts +0 -28
- package/src/quadrant/parser.ts +0 -18
- package/src/quadrant/renderer.ts +0 -638
- package/src/raci/diagnostics.ts +0 -213
- package/src/raci/index.ts +0 -23
- package/src/raci/mutations.ts +0 -321
- package/src/raci/parser.ts +0 -699
- package/src/raci/renderer.ts +0 -1627
- package/src/raci/types.ts +0 -82
- package/src/raci/variants.ts +0 -320
- package/src/render.ts +0 -268
- package/src/ring/parser.ts +0 -212
- package/src/ring/renderer.ts +0 -490
- package/src/ring/types.ts +0 -27
- package/src/sequence/collapse.ts +0 -174
- package/src/sequence/parser.ts +0 -1596
- package/src/sequence/participant-inference.ts +0 -152
- package/src/sequence/renderer.ts +0 -3168
- package/src/sequence/tag-resolution.ts +0 -164
- package/src/sharing.ts +0 -211
- package/src/sitemap/collapse.ts +0 -100
- package/src/sitemap/layout.ts +0 -925
- package/src/sitemap/parser.ts +0 -755
- package/src/sitemap/renderer.ts +0 -828
- package/src/sitemap/types.ts +0 -42
- package/src/slope/parser.ts +0 -18
- package/src/slope/renderer.ts +0 -378
- package/src/swimlane/diagnostics.ts +0 -94
- package/src/swimlane/layout.ts +0 -597
- package/src/swimlane/parser.ts +0 -613
- package/src/swimlane/renderer.ts +0 -681
- package/src/swimlane/types.ts +0 -151
- package/src/tech-radar/interactive.ts +0 -1129
- package/src/tech-radar/layout.ts +0 -197
- package/src/tech-radar/parser.ts +0 -436
- package/src/tech-radar/renderer.ts +0 -1239
- package/src/tech-radar/shared.ts +0 -151
- package/src/tech-radar/types.ts +0 -86
- package/src/themes.ts +0 -22
- package/src/timeline/parser.ts +0 -472
- package/src/timeline/renderer.ts +0 -3024
- package/src/timeline/types.ts +0 -33
- package/src/timeline/viz-parser.ts +0 -21
- package/src/treemap/diagnostics.ts +0 -62
- package/src/treemap/layout-radial.ts +0 -233
- package/src/treemap/layout.ts +0 -189
- package/src/treemap/parser.ts +0 -463
- package/src/treemap/renderer-radial.ts +0 -438
- package/src/treemap/renderer.ts +0 -572
- package/src/treemap/treemap-shared.ts +0 -309
- package/src/treemap/types.ts +0 -60
- package/src/utils/arrow-markers.ts +0 -51
- package/src/utils/arrows-diagnostics.ts +0 -62
- package/src/utils/arrows.ts +0 -227
- package/src/utils/brand.ts +0 -61
- package/src/utils/card.ts +0 -183
- package/src/utils/collapse-engine/tree-edges.ts +0 -115
- package/src/utils/collapse-engine/tree.ts +0 -117
- package/src/utils/d3-helpers.ts +0 -219
- package/src/utils/d3-types.ts +0 -9
- package/src/utils/description-helpers.ts +0 -38
- package/src/utils/duration.ts +0 -312
- package/src/utils/export-container.ts +0 -41
- package/src/utils/expr-eval.ts +0 -27
- package/src/utils/fit-canvas.ts +0 -64
- package/src/utils/hover-styles.ts +0 -673
- package/src/utils/inline-markdown.ts +0 -96
- package/src/utils/legend-constants.ts +0 -48
- package/src/utils/legend-d3.ts +0 -618
- package/src/utils/legend-integration.ts +0 -107
- package/src/utils/legend-layout.ts +0 -776
- package/src/utils/legend-svg.ts +0 -214
- package/src/utils/legend-types.ts +0 -272
- package/src/utils/name-normalize.ts +0 -163
- package/src/utils/note-box/constants.ts +0 -25
- package/src/utils/note-box/index.ts +0 -11
- package/src/utils/note-box/metrics.ts +0 -90
- package/src/utils/note-box/svg.ts +0 -331
- package/src/utils/notes/bounds.ts +0 -30
- package/src/utils/notes/build.ts +0 -131
- package/src/utils/notes/index.ts +0 -18
- package/src/utils/notes/model.ts +0 -19
- package/src/utils/notes/parse.ts +0 -131
- package/src/utils/notes/place.ts +0 -177
- package/src/utils/notes/resolve.ts +0 -88
- package/src/utils/number-format.ts +0 -36
- package/src/utils/parsing.ts +0 -953
- package/src/utils/reserved-key-registry.ts +0 -239
- package/src/utils/safe-href.ts +0 -38
- package/src/utils/scaling.ts +0 -97
- package/src/utils/svg-embed.ts +0 -251
- package/src/utils/tag-groups.ts +0 -607
- package/src/utils/text-measure.ts +0 -122
- package/src/utils/time-ticks.ts +0 -253
- package/src/utils/title-constants.ts +0 -24
- package/src/utils/visual-conventions.ts +0 -61
- package/src/utils/wrapped-desc.ts +0 -87
- package/src/venn/parser.ts +0 -18
- package/src/venn/renderer.ts +0 -1047
- package/src/version-control/diagnostics.ts +0 -67
- package/src/version-control/parser.ts +0 -544
- package/src/version-control/renderer.ts +0 -724
- package/src/version-control/types.ts +0 -100
- package/src/visualizations/parse.ts +0 -1560
- package/src/visualizations/types.ts +0 -262
- package/src/wireframe/layout.ts +0 -479
- package/src/wireframe/parser.ts +0 -1114
- package/src/wireframe/renderer.ts +0 -1318
- package/src/wireframe/types.ts +0 -110
- package/src/wordcloud/parser.ts +0 -18
- package/src/wordcloud/renderer.ts +0 -357
|
@@ -1,574 +0,0 @@
|
|
|
1
|
-
import { resolveColorWithDiagnostic } from '../colors';
|
|
2
|
-
import type { DgmoError } from '../diagnostics';
|
|
3
|
-
import type { PaletteColors } from '../palettes';
|
|
4
|
-
import {
|
|
5
|
-
makeDgmoError,
|
|
6
|
-
formatDgmoError,
|
|
7
|
-
suggest,
|
|
8
|
-
NAME_DIAGNOSTIC_CODES,
|
|
9
|
-
nameMergedMessage,
|
|
10
|
-
} from '../diagnostics';
|
|
11
|
-
import { parseInArrowLabel } from '../utils/arrows';
|
|
12
|
-
import {
|
|
13
|
-
measureIndent,
|
|
14
|
-
parseFirstLine,
|
|
15
|
-
OPTION_NOCOLON_RE,
|
|
16
|
-
ALL_CHART_TYPES,
|
|
17
|
-
tryParseSharedOption,
|
|
18
|
-
} from '../utils/parsing';
|
|
19
|
-
import { normalizeName, displayName } from '../utils/name-normalize';
|
|
20
|
-
import type { Writable } from '../utils/brand';
|
|
21
|
-
import type { ParsedGraph, GraphNode, GraphGroup, GraphNote } from './types';
|
|
22
|
-
import { tryCollectNote, resolveNotes } from './notes';
|
|
23
|
-
|
|
24
|
-
// ============================================================
|
|
25
|
-
// Constants
|
|
26
|
-
// ============================================================
|
|
27
|
-
|
|
28
|
-
const PSEUDOSTATE_ID = 'pseudostate:[*]';
|
|
29
|
-
const PSEUDOSTATE_LABEL = '[*]';
|
|
30
|
-
|
|
31
|
-
// `[Group]`, `[Group] color`, and an optional trailing `collapsed: <val>`
|
|
32
|
-
// view-state marker (§1 "Collapsing groups"). Color (group 2) must be a
|
|
33
|
-
// recognized lowercase palette word; group 3 is the raw collapsed value.
|
|
34
|
-
const GROUP_BRACKET_RE =
|
|
35
|
-
/^\[([^\]]+)\](?:\s+(red|orange|yellow|green|blue|purple|teal|cyan|gray|black|white))?(?:,?\s+collapsed:\s*(\S+))?\s*$/;
|
|
36
|
-
|
|
37
|
-
// ============================================================
|
|
38
|
-
// Arrow splitter
|
|
39
|
-
// ============================================================
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Split a line on `->` arrows, returning alternating segments:
|
|
43
|
-
* [nodeText, arrowToken, nodeText, ...]
|
|
44
|
-
*
|
|
45
|
-
* Arrows: `->`, `-label->`. Edges have no color slot (spec §1.7).
|
|
46
|
-
*/
|
|
47
|
-
function splitArrows(line: string): string[] {
|
|
48
|
-
// Mirrors flowchart-parser.ts splitArrows. TD-9 longest-match: arrow token
|
|
49
|
-
// is the maximal run of `-+>`. See that file for the full algorithm rationale.
|
|
50
|
-
const segments: string[] = [];
|
|
51
|
-
const arrowPositions: {
|
|
52
|
-
start: number;
|
|
53
|
-
end: number;
|
|
54
|
-
label?: string;
|
|
55
|
-
}[] = [];
|
|
56
|
-
|
|
57
|
-
let searchFrom = 0;
|
|
58
|
-
let scanFloor = 0;
|
|
59
|
-
while (searchFrom < line.length) {
|
|
60
|
-
const idx = line.indexOf('->', searchFrom);
|
|
61
|
-
if (idx === -1) break;
|
|
62
|
-
|
|
63
|
-
let runStart = idx;
|
|
64
|
-
while (runStart > scanFloor && line[runStart - 1] === '-') runStart--;
|
|
65
|
-
const arrowEnd = idx + 2;
|
|
66
|
-
|
|
67
|
-
let arrowStart: number;
|
|
68
|
-
let label: string | undefined;
|
|
69
|
-
|
|
70
|
-
let openingStart = -1;
|
|
71
|
-
for (let i = scanFloor; i < runStart; i++) {
|
|
72
|
-
if (line[i] !== '-') continue;
|
|
73
|
-
const prevIsWsOrFloor =
|
|
74
|
-
i === 0 || i === scanFloor || /\s/.test(line.charAt(i - 1));
|
|
75
|
-
if (prevIsWsOrFloor) {
|
|
76
|
-
openingStart = i;
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (openingStart !== -1) {
|
|
82
|
-
let openingEnd = openingStart;
|
|
83
|
-
while (openingEnd < runStart && line[openingEnd] === '-') openingEnd++;
|
|
84
|
-
|
|
85
|
-
const labelPart = line.substring(openingEnd, runStart).trim();
|
|
86
|
-
if (labelPart) label = labelPart;
|
|
87
|
-
arrowStart = openingStart;
|
|
88
|
-
} else {
|
|
89
|
-
arrowStart = runStart;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
arrowPositions.push({
|
|
93
|
-
start: arrowStart,
|
|
94
|
-
end: arrowEnd,
|
|
95
|
-
...(label !== undefined && { label }),
|
|
96
|
-
});
|
|
97
|
-
searchFrom = arrowEnd;
|
|
98
|
-
scanFloor = arrowEnd;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (arrowPositions.length === 0) return [line];
|
|
102
|
-
|
|
103
|
-
let lastIndex = 0;
|
|
104
|
-
for (let i = 0; i < arrowPositions.length; i++) {
|
|
105
|
-
// In-bounds by loop guard.
|
|
106
|
-
const arrow = arrowPositions[i]!;
|
|
107
|
-
const beforeText = line.substring(lastIndex, arrow.start).trim();
|
|
108
|
-
if (beforeText || i === 0) segments.push(beforeText);
|
|
109
|
-
|
|
110
|
-
const arrowToken = arrow.label ? `-${arrow.label}->` : '->';
|
|
111
|
-
segments.push(arrowToken);
|
|
112
|
-
lastIndex = arrow.end;
|
|
113
|
-
}
|
|
114
|
-
const remaining = line.substring(lastIndex).trim();
|
|
115
|
-
if (remaining) segments.push(remaining);
|
|
116
|
-
|
|
117
|
-
return segments;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
interface ArrowInfo {
|
|
121
|
-
label?: string;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function parseArrowToken(
|
|
125
|
-
token: string,
|
|
126
|
-
_palette: PaletteColors | undefined,
|
|
127
|
-
lineNumber: number,
|
|
128
|
-
diagnostics: DgmoError[]
|
|
129
|
-
): ArrowInfo {
|
|
130
|
-
if (token === '->') return {};
|
|
131
|
-
// Edges have no color slot (§1.7); arrow content between `-` and `->`
|
|
132
|
-
// is pure label text.
|
|
133
|
-
const m = token.match(/^-(.+?)->$/);
|
|
134
|
-
if (m) {
|
|
135
|
-
const rawLabel = m[1] ?? '';
|
|
136
|
-
const labelResult = parseInArrowLabel(rawLabel, lineNumber);
|
|
137
|
-
diagnostics.push(...labelResult.diagnostics);
|
|
138
|
-
return {
|
|
139
|
-
...(labelResult.label !== undefined && { label: labelResult.label }),
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
return {};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// ============================================================
|
|
146
|
-
// Node ref parser
|
|
147
|
-
// ============================================================
|
|
148
|
-
|
|
149
|
-
interface NodeRef {
|
|
150
|
-
id: string;
|
|
151
|
-
label: string;
|
|
152
|
-
shape: 'state' | 'pseudostate';
|
|
153
|
-
color?: string;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function parseStateNodeRef(text: string): NodeRef | null {
|
|
157
|
-
const t = text.trim();
|
|
158
|
-
if (!t) return null;
|
|
159
|
-
|
|
160
|
-
// Pseudostate: [*]
|
|
161
|
-
if (t === '[*]') {
|
|
162
|
-
return {
|
|
163
|
-
id: PSEUDOSTATE_ID,
|
|
164
|
-
label: PSEUDOSTATE_LABEL,
|
|
165
|
-
shape: 'pseudostate',
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// State: bare text
|
|
170
|
-
const label = t;
|
|
171
|
-
if (!label) return null;
|
|
172
|
-
return {
|
|
173
|
-
id: `state:${normalizeName(label)}`,
|
|
174
|
-
label,
|
|
175
|
-
shape: 'state',
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// ============================================================
|
|
180
|
-
// Main parser
|
|
181
|
-
// ============================================================
|
|
182
|
-
|
|
183
|
-
export function parseState(
|
|
184
|
-
content: string,
|
|
185
|
-
palette?: PaletteColors
|
|
186
|
-
): ParsedGraph {
|
|
187
|
-
const lines = content.split('\n');
|
|
188
|
-
const options: Record<string, string> = {};
|
|
189
|
-
const result: Writable<ParsedGraph> = {
|
|
190
|
-
type: 'state',
|
|
191
|
-
direction: 'LR',
|
|
192
|
-
nodes: [],
|
|
193
|
-
edges: [],
|
|
194
|
-
options,
|
|
195
|
-
diagnostics: [],
|
|
196
|
-
error: null,
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
const fail = (line: number, message: string): ParsedGraph => {
|
|
200
|
-
const diag = makeDgmoError(line, message);
|
|
201
|
-
result.diagnostics.push(diag);
|
|
202
|
-
result.error = formatDgmoError(diag);
|
|
203
|
-
return result;
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
const nodeMap = new Map<string, Writable<GraphNode>>();
|
|
207
|
-
const indentStack: { nodeId: string; indent: number }[] = [];
|
|
208
|
-
const notes: GraphNote[] = [];
|
|
209
|
-
let currentGroup: Writable<GraphGroup> | null = null;
|
|
210
|
-
let groupIndent = -1;
|
|
211
|
-
const groups: Writable<GraphGroup>[] = [];
|
|
212
|
-
let contentStarted = false;
|
|
213
|
-
let firstLineParsed = false;
|
|
214
|
-
|
|
215
|
-
// Per-parse alias literal → canonical node id (TD-18). Per C8.
|
|
216
|
-
const nameAliasMap = new Map<string, string>();
|
|
217
|
-
function peelAlias(seg: string): { seg: string; alias?: string } {
|
|
218
|
-
const trimmed = seg.trim();
|
|
219
|
-
const m = trimmed.match(/^(.*?)\s+as\s+([A-Za-z][A-Za-z0-9_]{0,11})\s*$/);
|
|
220
|
-
if (!m) return { seg: trimmed };
|
|
221
|
-
// Regex capture groups 1 and 2 are guaranteed by the match above.
|
|
222
|
-
return { seg: m[1]!.trim(), alias: m[2]! };
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function getOrCreateNode(
|
|
226
|
-
ref: NodeRef,
|
|
227
|
-
lineNumber: number
|
|
228
|
-
): Writable<GraphNode> {
|
|
229
|
-
const key = ref.id;
|
|
230
|
-
const existing = nodeMap.get(key);
|
|
231
|
-
if (existing) {
|
|
232
|
-
const incomingDisplay = displayName(ref.label);
|
|
233
|
-
const existingDisplay = displayName(existing.label);
|
|
234
|
-
if (incomingDisplay !== existingDisplay) {
|
|
235
|
-
result.diagnostics.push(
|
|
236
|
-
makeDgmoError(
|
|
237
|
-
lineNumber,
|
|
238
|
-
nameMergedMessage({
|
|
239
|
-
incomingDisplay,
|
|
240
|
-
incomingLine: lineNumber,
|
|
241
|
-
existingDisplay,
|
|
242
|
-
existingLine: existing.lineNumber,
|
|
243
|
-
}),
|
|
244
|
-
'warning',
|
|
245
|
-
NAME_DIAGNOSTIC_CODES.NAME_MERGED
|
|
246
|
-
)
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
return existing;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const node: Writable<GraphNode> = {
|
|
253
|
-
id: key,
|
|
254
|
-
label: ref.label,
|
|
255
|
-
shape: ref.shape,
|
|
256
|
-
lineNumber,
|
|
257
|
-
...(ref.color && { color: ref.color }),
|
|
258
|
-
...(currentGroup && { group: currentGroup.id }),
|
|
259
|
-
};
|
|
260
|
-
nodeMap.set(key, node);
|
|
261
|
-
result.nodes.push(node);
|
|
262
|
-
|
|
263
|
-
if (currentGroup && !currentGroup.nodeIds.includes(key)) {
|
|
264
|
-
currentGroup.nodeIds.push(key);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
return node;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
function addEdge(
|
|
271
|
-
sourceId: string,
|
|
272
|
-
targetId: string,
|
|
273
|
-
lineNumber: number,
|
|
274
|
-
label?: string
|
|
275
|
-
): void {
|
|
276
|
-
result.edges.push({
|
|
277
|
-
source: sourceId,
|
|
278
|
-
target: targetId,
|
|
279
|
-
lineNumber,
|
|
280
|
-
...(label && { label }),
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// === Main loop ===
|
|
285
|
-
for (let i = 0; i < lines.length; i++) {
|
|
286
|
-
// In-bounds by loop guard.
|
|
287
|
-
const raw = lines[i]!;
|
|
288
|
-
const trimmed = raw.trim();
|
|
289
|
-
const lineNumber = i + 1;
|
|
290
|
-
const indent = measureIndent(raw);
|
|
291
|
-
|
|
292
|
-
if (!trimmed) continue;
|
|
293
|
-
if (trimmed.startsWith('//')) continue;
|
|
294
|
-
|
|
295
|
-
// First line: try parseFirstLine for `state [Title]`
|
|
296
|
-
if (!firstLineParsed && !contentStarted) {
|
|
297
|
-
const firstLineResult = parseFirstLine(trimmed);
|
|
298
|
-
if (firstLineResult) {
|
|
299
|
-
firstLineParsed = true;
|
|
300
|
-
if (firstLineResult.chartType !== 'state') {
|
|
301
|
-
const allTypes = Array.from(ALL_CHART_TYPES);
|
|
302
|
-
let msg = `Expected chart type "state", got "${firstLineResult.chartType}"`;
|
|
303
|
-
const hint = suggest(firstLineResult.chartType, allTypes);
|
|
304
|
-
if (hint) msg += `. ${hint}`;
|
|
305
|
-
return fail(lineNumber, msg);
|
|
306
|
-
}
|
|
307
|
-
if (firstLineResult.title) {
|
|
308
|
-
result.title = firstLineResult.title;
|
|
309
|
-
result.titleLineNumber = lineNumber;
|
|
310
|
-
}
|
|
311
|
-
continue;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Note annotation: `note <ref> [inline body]` + optional indented
|
|
316
|
-
// body. Only `note -> X` (arrow immediately after `note`) is excluded
|
|
317
|
-
// so a transition FROM a state named "note" still parses; arrows are
|
|
318
|
-
// allowed inside a note body.
|
|
319
|
-
const noteResult = tryCollectNote(
|
|
320
|
-
lines,
|
|
321
|
-
i,
|
|
322
|
-
indent,
|
|
323
|
-
palette,
|
|
324
|
-
result.diagnostics
|
|
325
|
-
);
|
|
326
|
-
if (noteResult) {
|
|
327
|
-
if (noteResult.note) notes.push(noteResult.note);
|
|
328
|
-
i = noteResult.lastIndex;
|
|
329
|
-
continue;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Group brackets: [Name] or [Name](color)
|
|
333
|
-
const groupMatch = trimmed.match(GROUP_BRACKET_RE);
|
|
334
|
-
// Regex capture group 1 is mandatory in GROUP_BRACKET_RE.
|
|
335
|
-
if (groupMatch && groupMatch[1]!.trim() !== '*') {
|
|
336
|
-
const groupLabel = groupMatch[1]!.trim();
|
|
337
|
-
const groupColorName = groupMatch[2]?.trim();
|
|
338
|
-
const groupColor = groupColorName
|
|
339
|
-
? resolveColorWithDiagnostic(
|
|
340
|
-
groupColorName,
|
|
341
|
-
lineNumber,
|
|
342
|
-
result.diagnostics,
|
|
343
|
-
palette
|
|
344
|
-
)
|
|
345
|
-
: undefined;
|
|
346
|
-
|
|
347
|
-
const groupCollapsed = groupMatch[3]?.toLowerCase() === 'true';
|
|
348
|
-
currentGroup = {
|
|
349
|
-
id: `group:${groupLabel.toLowerCase()}`,
|
|
350
|
-
label: groupLabel,
|
|
351
|
-
nodeIds: [],
|
|
352
|
-
lineNumber,
|
|
353
|
-
...(groupColor && { color: groupColor }),
|
|
354
|
-
...(groupCollapsed && { collapsed: true }),
|
|
355
|
-
};
|
|
356
|
-
groupIndent = indent;
|
|
357
|
-
groups.push(currentGroup);
|
|
358
|
-
continue;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Options (space-separated, before content)
|
|
362
|
-
if (!contentStarted) {
|
|
363
|
-
// Bare boolean: direction-tb
|
|
364
|
-
if (/^direction-tb$/i.test(trimmed)) {
|
|
365
|
-
result.direction = 'TB';
|
|
366
|
-
continue;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// Bare boolean: solid-fill
|
|
370
|
-
if (/^solid-fill$/i.test(trimmed)) {
|
|
371
|
-
options['solid-fill'] = 'on';
|
|
372
|
-
continue;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
if (/^no-color$/i.test(trimmed)) {
|
|
376
|
-
result.diagnostics.push(
|
|
377
|
-
makeDgmoError(lineNumber, '"no-color" has been removed.', 'warning')
|
|
378
|
-
);
|
|
379
|
-
continue;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// Cross-chart-type bare booleans (no-title, etc.)
|
|
383
|
-
if (tryParseSharedOption(trimmed, options)) {
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
const optMatch = trimmed.match(OPTION_NOCOLON_RE);
|
|
388
|
-
if (optMatch && !trimmed.includes('->')) {
|
|
389
|
-
// Regex capture groups 1 and 2 are mandatory in OPTION_NOCOLON_RE.
|
|
390
|
-
const key = optMatch[1]!.toLowerCase();
|
|
391
|
-
const value = optMatch[2]!.trim();
|
|
392
|
-
|
|
393
|
-
if (key === 'no-color') {
|
|
394
|
-
result.diagnostics.push(
|
|
395
|
-
makeDgmoError(
|
|
396
|
-
lineNumber,
|
|
397
|
-
'"no-color" was removed — state colors are assigned automatically; delete this line.',
|
|
398
|
-
'warning'
|
|
399
|
-
)
|
|
400
|
-
);
|
|
401
|
-
continue;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
options[key] = value;
|
|
405
|
-
continue;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// Content line — nodes and edges
|
|
410
|
-
contentStarted = true;
|
|
411
|
-
|
|
412
|
-
// Close current group when indent returns to or below the bracket level
|
|
413
|
-
if (currentGroup && indent <= groupIndent) {
|
|
414
|
-
currentGroup = null;
|
|
415
|
-
groupIndent = -1;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// Pop indent stack entries at same or deeper indent
|
|
419
|
-
while (indentStack.length > 0) {
|
|
420
|
-
// In-bounds by length check above.
|
|
421
|
-
const top = indentStack[indentStack.length - 1]!;
|
|
422
|
-
if (top.indent >= indent) {
|
|
423
|
-
indentStack.pop();
|
|
424
|
-
} else {
|
|
425
|
-
break;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
const implicitSourceId =
|
|
430
|
-
indentStack.length > 0
|
|
431
|
-
? // In-bounds by length check above.
|
|
432
|
-
indentStack[indentStack.length - 1]!.nodeId
|
|
433
|
-
: null;
|
|
434
|
-
|
|
435
|
-
const segments = splitArrows(trimmed);
|
|
436
|
-
|
|
437
|
-
if (segments.length === 1) {
|
|
438
|
-
// Single state reference, no arrows — this is the canonical definition
|
|
439
|
-
// In-bounds by length check above.
|
|
440
|
-
const peeled = peelAlias(segments[0]!);
|
|
441
|
-
const ref = parseStateNodeRef(peeled.seg);
|
|
442
|
-
if (ref) {
|
|
443
|
-
const node = getOrCreateNode(ref, lineNumber);
|
|
444
|
-
if (peeled.alias) nameAliasMap.set(peeled.alias, node.id);
|
|
445
|
-
// Standalone heading is the "definition" — update lineNumber so
|
|
446
|
-
// clicking the node in the preview navigates here, not to the
|
|
447
|
-
// first edge mention.
|
|
448
|
-
node.lineNumber = lineNumber;
|
|
449
|
-
indentStack.push({ nodeId: node.id, indent });
|
|
450
|
-
} else {
|
|
451
|
-
// Bare alias-only line: `os` resolves to a previously declared node.
|
|
452
|
-
const aliasResolved = nameAliasMap.get(peeled.seg.trim());
|
|
453
|
-
if (aliasResolved !== undefined) {
|
|
454
|
-
indentStack.push({ nodeId: aliasResolved, indent });
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
continue;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
// Process chain: alternating nodeText / arrowToken / nodeText / ...
|
|
461
|
-
let lastNodeId: string | null = null;
|
|
462
|
-
let pendingArrow: ArrowInfo | null = null;
|
|
463
|
-
|
|
464
|
-
for (let j = 0; j < segments.length; j++) {
|
|
465
|
-
// In-bounds by loop guard.
|
|
466
|
-
const seg = segments[j]!;
|
|
467
|
-
|
|
468
|
-
if (seg === '->' || /^-.+->$/.test(seg)) {
|
|
469
|
-
pendingArrow = parseArrowToken(
|
|
470
|
-
seg,
|
|
471
|
-
palette,
|
|
472
|
-
lineNumber,
|
|
473
|
-
result.diagnostics
|
|
474
|
-
);
|
|
475
|
-
continue;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
const peeled = peelAlias(seg);
|
|
479
|
-
let ref = parseStateNodeRef(peeled.seg);
|
|
480
|
-
if (!ref) {
|
|
481
|
-
const aliasResolved = nameAliasMap.get(peeled.seg.trim());
|
|
482
|
-
if (aliasResolved !== undefined) {
|
|
483
|
-
const existing = nodeMap.get(aliasResolved);
|
|
484
|
-
if (existing) {
|
|
485
|
-
// Narrow to state shapes — alias-resolution can only point
|
|
486
|
-
// at nodes the parser itself created, so the shape is
|
|
487
|
-
// guaranteed to be 'state' or 'pseudostate'.
|
|
488
|
-
const shape = existing.shape as 'state' | 'pseudostate';
|
|
489
|
-
ref = {
|
|
490
|
-
id: aliasResolved,
|
|
491
|
-
label: existing.label,
|
|
492
|
-
shape,
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
if (!ref) continue;
|
|
498
|
-
|
|
499
|
-
const node = getOrCreateNode(ref, lineNumber);
|
|
500
|
-
if (peeled.alias) nameAliasMap.set(peeled.alias, node.id);
|
|
501
|
-
|
|
502
|
-
if (pendingArrow !== null) {
|
|
503
|
-
// Use explicit source if available, else implicit from indent
|
|
504
|
-
const sourceId = lastNodeId ?? implicitSourceId;
|
|
505
|
-
if (sourceId) {
|
|
506
|
-
addEdge(sourceId, node.id, lineNumber, pendingArrow.label);
|
|
507
|
-
}
|
|
508
|
-
pendingArrow = null;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
lastNodeId = node.id;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
if (lastNodeId) {
|
|
515
|
-
indentStack.push({ nodeId: lastNodeId, indent });
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
if (groups.length > 0) result.groups = groups;
|
|
520
|
-
|
|
521
|
-
// Resolve note refs against the state node map (forward refs OK).
|
|
522
|
-
if (notes.length > 0) {
|
|
523
|
-
result.notes = notes;
|
|
524
|
-
resolveNotes(notes, result.nodes, result.diagnostics);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// Validation: no nodes found
|
|
528
|
-
if (result.nodes.length === 0 && !result.error) {
|
|
529
|
-
const diag = makeDgmoError(
|
|
530
|
-
1,
|
|
531
|
-
'No states found. Add state transitions like: Idle -> Active'
|
|
532
|
-
);
|
|
533
|
-
result.diagnostics.push(diag);
|
|
534
|
-
result.error = formatDgmoError(diag);
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
// Warn about orphaned states
|
|
538
|
-
if (result.nodes.length >= 2 && result.edges.length >= 1 && !result.error) {
|
|
539
|
-
const connectedIds = new Set<string>();
|
|
540
|
-
for (const edge of result.edges) {
|
|
541
|
-
// edge endpoints are already-normalized node IDs (set via getOrCreateNode)
|
|
542
|
-
// eslint-disable-next-line name-normalize/required-at-insertion
|
|
543
|
-
connectedIds.add(edge.source);
|
|
544
|
-
// eslint-disable-next-line name-normalize/required-at-insertion
|
|
545
|
-
connectedIds.add(edge.target);
|
|
546
|
-
}
|
|
547
|
-
for (const node of result.nodes) {
|
|
548
|
-
if (!connectedIds.has(node.id)) {
|
|
549
|
-
result.diagnostics.push(
|
|
550
|
-
makeDgmoError(
|
|
551
|
-
node.lineNumber,
|
|
552
|
-
`State "${node.label}" is not connected to any other state`,
|
|
553
|
-
'warning'
|
|
554
|
-
)
|
|
555
|
-
);
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
return result;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
// ============================================================
|
|
564
|
-
// Detection helper
|
|
565
|
-
// ============================================================
|
|
566
|
-
|
|
567
|
-
/**
|
|
568
|
-
* Detect if content looks like a state diagram (without explicit `chart: state` header).
|
|
569
|
-
* Only matches if `[*]` token is present — too ambiguous to infer from bare names alone.
|
|
570
|
-
*/
|
|
571
|
-
export function looksLikeState(content: string): boolean {
|
|
572
|
-
// Must have [*] token (start/end pseudostate) and -> arrows
|
|
573
|
-
return content.includes('[*]') && content.includes('->');
|
|
574
|
-
}
|