@diagrammo/dgmo 0.15.0 → 0.16.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/README.md +23 -10
- package/dist/advanced.cjs +53094 -0
- package/dist/advanced.d.cts +4690 -0
- package/dist/advanced.d.ts +4690 -0
- package/dist/advanced.js +52849 -0
- package/dist/auto.cjs +2298 -2069
- package/dist/auto.js +132 -109
- package/dist/auto.mjs +2294 -2065
- package/dist/cli.cjs +175 -152
- package/dist/editor.cjs +8 -9
- package/dist/editor.js +8 -9
- package/dist/highlight.cjs +8 -9
- package/dist/highlight.js +8 -9
- package/dist/index.cjs +2281 -2048
- package/dist/index.d.cts +45 -1
- package/dist/index.d.ts +45 -1
- package/dist/index.js +2276 -2044
- package/dist/internal.cjs +2064 -1831
- package/dist/internal.d.cts +113 -113
- package/dist/internal.d.ts +113 -113
- package/dist/internal.js +2059 -1826
- package/dist/pert.cjs +325 -0
- package/dist/pert.d.cts +542 -0
- package/dist/pert.d.ts +542 -0
- package/dist/pert.js +294 -0
- package/docs/language-reference.md +83 -66
- package/gallery/fixtures/area.dgmo +3 -3
- package/gallery/fixtures/bar-stacked.dgmo +5 -5
- package/gallery/fixtures/boxes-and-lines.dgmo +2 -2
- package/gallery/fixtures/c4-full.dgmo +8 -8
- package/gallery/fixtures/class-full.dgmo +2 -2
- package/gallery/fixtures/doughnut.dgmo +6 -6
- package/gallery/fixtures/flowchart-colors.dgmo +3 -3
- package/gallery/fixtures/function.dgmo +3 -3
- package/gallery/fixtures/gantt-full.dgmo +9 -9
- package/gallery/fixtures/gantt.dgmo +7 -7
- package/gallery/fixtures/infra-full.dgmo +6 -6
- package/gallery/fixtures/infra.dgmo +2 -2
- package/gallery/fixtures/kanban.dgmo +9 -9
- package/gallery/fixtures/line.dgmo +2 -2
- package/gallery/fixtures/multi-line.dgmo +3 -3
- package/gallery/fixtures/org-full.dgmo +6 -6
- package/gallery/fixtures/quadrant.dgmo +2 -2
- package/gallery/fixtures/sankey.dgmo +9 -9
- package/gallery/fixtures/scatter.dgmo +3 -3
- package/gallery/fixtures/sequence-tags-protocols.dgmo +8 -8
- package/gallery/fixtures/sequence-tags.dgmo +7 -7
- package/gallery/fixtures/sitemap-full.dgmo +7 -7
- package/gallery/fixtures/slope.dgmo +5 -5
- package/gallery/fixtures/spr-eras.dgmo +9 -9
- package/gallery/fixtures/timeline.dgmo +3 -3
- package/gallery/fixtures/venn.dgmo +3 -3
- package/package.json +28 -3
- package/src/advanced.ts +730 -0
- package/src/auto/index.ts +14 -13
- package/src/boxes-and-lines/layout.ts +481 -445
- package/src/boxes-and-lines/renderer.ts +5 -1
- package/src/c4/parser.ts +8 -8
- package/src/c4/renderer.ts +15 -8
- package/src/chart-types.ts +0 -5
- package/src/chart.ts +18 -9
- package/src/class/parser.ts +8 -15
- package/src/class/renderer.ts +17 -6
- package/src/cli.ts +15 -13
- package/src/completion-types.ts +28 -0
- package/src/completion.ts +28 -21
- package/src/cycle/layout.ts +2 -2
- package/src/cycle/parser.ts +14 -0
- package/src/cycle/renderer.ts +6 -3
- package/src/d3.ts +1537 -1164
- package/src/echarts.ts +37 -20
- package/src/editor/dgmo.grammar +1 -3
- package/src/editor/dgmo.grammar.js +8 -8
- package/src/editor/dgmo.grammar.terms.js +11 -12
- package/src/editor/highlight-api.ts +0 -1
- package/src/editor/highlight.ts +0 -1
- package/src/er/parser.ts +19 -20
- package/src/er/renderer.ts +20 -8
- package/src/gantt/calculator.ts +1 -11
- package/src/gantt/parser.ts +17 -17
- package/src/gantt/renderer.ts +9 -6
- package/src/graph/flowchart-parser.ts +19 -85
- package/src/graph/flowchart-renderer.ts +4 -9
- package/src/graph/layout.ts +0 -2
- package/src/graph/state-parser.ts +17 -62
- package/src/graph/state-renderer.ts +4 -9
- package/src/index.ts +17 -1
- package/src/infra/parser.ts +40 -30
- package/src/infra/renderer.ts +9 -6
- package/src/internal.ts +9 -721
- package/src/journey-map/parser.ts +10 -3
- package/src/journey-map/renderer.ts +3 -1
- package/src/kanban/parser.ts +12 -8
- package/src/kanban/renderer.ts +3 -1
- package/src/mindmap/layout.ts +1 -1
- package/src/mindmap/parser.ts +3 -3
- package/src/mindmap/renderer.ts +2 -1
- package/src/org/parser.ts +3 -3
- package/src/org/renderer.ts +5 -4
- package/src/pert/layout.ts +1 -1
- package/src/pert/monte-carlo.ts +2 -2
- package/src/pert/parser.ts +10 -10
- package/src/pert/renderer.ts +7 -2
- package/src/pert/types.ts +1 -1
- package/src/pyramid/parser.ts +12 -0
- package/src/raci/parser.ts +44 -14
- package/src/raci/renderer.ts +3 -2
- package/src/raci/types.ts +4 -3
- package/src/ring/parser.ts +12 -0
- package/src/sequence/parser.ts +15 -9
- package/src/sequence/renderer.ts +2 -5
- package/src/sitemap/layout.ts +0 -2
- package/src/sitemap/parser.ts +12 -38
- package/src/sitemap/renderer.ts +13 -13
- package/src/sitemap/types.ts +0 -1
- package/src/tech-radar/interactive.ts +1 -1
- package/src/tech-radar/renderer.ts +6 -4
- package/src/tech-radar/types.ts +2 -0
- package/src/utils/arrows.ts +3 -28
- package/src/utils/legend-d3.ts +12 -6
- package/src/utils/legend-layout.ts +1 -1
- package/src/utils/legend-types.ts +1 -1
- package/src/utils/parsing.ts +64 -35
- package/src/utils/tag-groups.ts +109 -30
- package/src/wireframe/layout.ts +11 -7
- package/src/wireframe/parser.ts +4 -4
- package/src/wireframe/renderer.ts +5 -2
package/src/echarts.ts
CHANGED
|
@@ -367,12 +367,15 @@ export function parseExtendedChart(
|
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
// [Category] container header with optional color: [Category Name] or [Category Name](color)
|
|
370
|
-
|
|
370
|
+
// Category brackets with optional trailing-token color (§1.5):
|
|
371
|
+
// `[Name]` or `[Name] color`. Per universal rule, color is a bare token.
|
|
372
|
+
const categoryMatch = trimmed.match(/^\[(.+?)\](?:\s+(\S+))?\s*$/);
|
|
371
373
|
if (categoryMatch) {
|
|
372
374
|
const catName = categoryMatch[1].trim();
|
|
373
|
-
const
|
|
375
|
+
const rawCatColor = categoryMatch[2]?.trim();
|
|
376
|
+
const catColor = rawCatColor
|
|
374
377
|
? (resolveColorWithDiagnostic(
|
|
375
|
-
|
|
378
|
+
rawCatColor,
|
|
376
379
|
lineNumber,
|
|
377
380
|
result.diagnostics,
|
|
378
381
|
palette
|
|
@@ -388,9 +391,14 @@ export function parseExtendedChart(
|
|
|
388
391
|
continue;
|
|
389
392
|
}
|
|
390
393
|
|
|
391
|
-
// Sankey/chord link syntax
|
|
394
|
+
// Sankey/chord link syntax (§1.5 universal trailing-token):
|
|
395
|
+
// `Source -> Target value` (directed, no link color)
|
|
396
|
+
// `Source -> Target value linkColor` (directed, trailing-token link color)
|
|
397
|
+
// `Source -- Target value` (undirected)
|
|
398
|
+
// Link color (if present) must be a recognized lowercase palette word.
|
|
399
|
+
// Source/target labels still accept trailing-token color via extractColor.
|
|
392
400
|
const arrowMatch = trimmed.match(
|
|
393
|
-
/^(.+?)\s*(->|--)\s*(.+?)\s+(-?[\d,_]+(?:\.[\d]+)?)
|
|
401
|
+
/^(.+?)\s*(->|--)\s*(.+?)\s+(-?[\d,_]+(?:\.[\d]+)?)(?:\s+(red|orange|yellow|green|blue|purple|teal|cyan|gray|black|white))?\s*$/
|
|
394
402
|
);
|
|
395
403
|
if (arrowMatch) {
|
|
396
404
|
const [, rawSource, arrow, rawTarget, rawVal, rawLinkColor] = arrowMatch;
|
|
@@ -441,13 +449,22 @@ export function parseExtendedChart(
|
|
|
441
449
|
sankeyStack.pop();
|
|
442
450
|
}
|
|
443
451
|
if (sankeyStack.length > 0) {
|
|
444
|
-
//
|
|
445
|
-
//
|
|
452
|
+
// Indented sankey child (§1.5 trailing-token):
|
|
453
|
+
// `TargetName value` — link, no link color
|
|
454
|
+
// `TargetName value linkColor` — link with link color
|
|
455
|
+
// `TargetName nodeColor value` — node-colored child
|
|
456
|
+
// `TargetName nodeColor value linkColor` — both
|
|
457
|
+
// Strategy: peel a trailing recognized color word (after the value)
|
|
458
|
+
// first, then run parseDataRowValues on the remainder. Trailing
|
|
459
|
+
// tokens that aren't recognized colors stay in the data row.
|
|
446
460
|
const valColorMatch = trimmed.match(
|
|
447
|
-
/(-?[\d,_]+(?:\.[\d]+)?)\s
|
|
461
|
+
/(-?[\d,_]+(?:\.[\d]+)?)\s+(red|orange|yellow|green|blue|purple|teal|cyan|gray|black|white)\s*$/
|
|
448
462
|
);
|
|
449
463
|
const strippedLine = valColorMatch
|
|
450
|
-
? trimmed.replace(
|
|
464
|
+
? trimmed.replace(
|
|
465
|
+
/\s+(red|orange|yellow|green|blue|purple|teal|cyan|gray|black|white)\s*$/,
|
|
466
|
+
''
|
|
467
|
+
)
|
|
451
468
|
: trimmed;
|
|
452
469
|
const dataRow = parseDataRowValues(strippedLine);
|
|
453
470
|
if (dataRow && dataRow.values.length === 1) {
|
|
@@ -1026,7 +1043,7 @@ function buildChordOption(
|
|
|
1026
1043
|
isDark: boolean,
|
|
1027
1044
|
textColor: string,
|
|
1028
1045
|
colors: string[],
|
|
1029
|
-
|
|
1046
|
+
_bg: string,
|
|
1030
1047
|
titleConfig: EChartsOption['title']
|
|
1031
1048
|
): EChartsOption {
|
|
1032
1049
|
// Extract unique nodes from links
|
|
@@ -1185,7 +1202,7 @@ function evaluateExpression(expr: string, x: number): number {
|
|
|
1185
1202
|
function buildFunctionOption(
|
|
1186
1203
|
parsed: ParsedExtendedChart,
|
|
1187
1204
|
palette: PaletteColors,
|
|
1188
|
-
|
|
1205
|
+
_isDark: boolean,
|
|
1189
1206
|
textColor: string,
|
|
1190
1207
|
axisLineColor: string,
|
|
1191
1208
|
gridOpacity: number,
|
|
@@ -2092,7 +2109,7 @@ function buildFunnelOption(
|
|
|
2092
2109
|
isDark: boolean,
|
|
2093
2110
|
textColor: string,
|
|
2094
2111
|
colors: string[],
|
|
2095
|
-
|
|
2112
|
+
_bg: string,
|
|
2096
2113
|
titleConfig: EChartsOption['title']
|
|
2097
2114
|
): EChartsOption {
|
|
2098
2115
|
// Sort data descending by value for funnel ordering
|
|
@@ -2464,7 +2481,7 @@ function buildBarOption(
|
|
|
2464
2481
|
splitLineColor: string,
|
|
2465
2482
|
gridOpacity: number,
|
|
2466
2483
|
colors: string[],
|
|
2467
|
-
|
|
2484
|
+
_bg: string,
|
|
2468
2485
|
titleConfig: EChartsOption['title'],
|
|
2469
2486
|
chartWidth?: number
|
|
2470
2487
|
): EChartsOption {
|
|
@@ -2893,7 +2910,7 @@ function buildPieOption(
|
|
|
2893
2910
|
isDark: boolean,
|
|
2894
2911
|
textColor: string,
|
|
2895
2912
|
colors: string[],
|
|
2896
|
-
|
|
2913
|
+
_bg: string,
|
|
2897
2914
|
titleConfig: EChartsOption['title'],
|
|
2898
2915
|
isDoughnut: boolean
|
|
2899
2916
|
): EChartsOption {
|
|
@@ -3021,7 +3038,7 @@ function buildPolarAreaOption(
|
|
|
3021
3038
|
isDark: boolean,
|
|
3022
3039
|
textColor: string,
|
|
3023
3040
|
colors: string[],
|
|
3024
|
-
|
|
3041
|
+
_bg: string,
|
|
3025
3042
|
titleConfig: EChartsOption['title']
|
|
3026
3043
|
): EChartsOption {
|
|
3027
3044
|
const data = parsed.data.map((d, i) => {
|
|
@@ -3079,7 +3096,7 @@ function buildBarStackedOption(
|
|
|
3079
3096
|
splitLineColor: string,
|
|
3080
3097
|
gridOpacity: number,
|
|
3081
3098
|
colors: string[],
|
|
3082
|
-
|
|
3099
|
+
_bg: string,
|
|
3083
3100
|
titleConfig: EChartsOption['title'],
|
|
3084
3101
|
chartWidth?: number
|
|
3085
3102
|
): EChartsOption {
|
|
@@ -3282,11 +3299,11 @@ export async function renderExtendedChartForExport(
|
|
|
3282
3299
|
// In static export, expand the first group so entries are visible
|
|
3283
3300
|
// Extract grid offsets for plot-area-centered legend
|
|
3284
3301
|
const grid = option.grid as Record<string, unknown> | undefined;
|
|
3285
|
-
const gridLeftPct = grid?.left
|
|
3286
|
-
? parseFloat(String(grid
|
|
3302
|
+
const gridLeftPct = grid?.['left']
|
|
3303
|
+
? parseFloat(String(grid['left']))
|
|
3287
3304
|
: undefined;
|
|
3288
|
-
const gridRightPct = grid?.right
|
|
3289
|
-
? parseFloat(String(grid
|
|
3305
|
+
const gridRightPct = grid?.['right']
|
|
3306
|
+
? parseFloat(String(grid['right']))
|
|
3290
3307
|
: undefined;
|
|
3291
3308
|
const { svg: legendSvgStr } = renderLegendSvg(legendGroups, {
|
|
3292
3309
|
palette: effectivePalette,
|
package/src/editor/dgmo.grammar
CHANGED
|
@@ -8,7 +8,6 @@ contentPart {
|
|
|
8
8
|
SectionMarker |
|
|
9
9
|
Url |
|
|
10
10
|
OpenBracket | CloseBracket | OpenParen | CloseParen | OpenAngle | CloseAngle |
|
|
11
|
-
ColorAnnotation |
|
|
12
11
|
Pipe | Colon | Comma | Plus | Dash | Tilde | Star | Question |
|
|
13
12
|
ChartType |
|
|
14
13
|
TagKeyword | DirectiveKeyword | ControlKeyword | ModifierKeyword |
|
|
@@ -38,7 +37,6 @@ contentPart {
|
|
|
38
37
|
|
|
39
38
|
SectionMarker { "==" }
|
|
40
39
|
Url { "http" "s"? "://" ![ \t\n|,)\]>]+ }
|
|
41
|
-
ColorAnnotation { "(" $[a-z\-]+ ")" }
|
|
42
40
|
|
|
43
41
|
Pipe { "|" }
|
|
44
42
|
Colon { ":" }
|
|
@@ -66,7 +64,7 @@ contentPart {
|
|
|
66
64
|
|
|
67
65
|
@precedence {
|
|
68
66
|
Comment, SyncArrow, AsyncArrow,
|
|
69
|
-
Duration, DateLiteral, Percentage, Number, SectionMarker, Url,
|
|
67
|
+
Duration, DateLiteral, Percentage, Number, SectionMarker, Url,
|
|
70
68
|
Pipe, Colon, Comma, Plus, Dash, Tilde, Star, Question,
|
|
71
69
|
OpenBracket, CloseBracket, OpenParen, CloseParen, OpenAngle, CloseAngle,
|
|
72
70
|
QuotedString, Identifier, Punct
|
|
@@ -3,16 +3,16 @@ import {LRParser} from "@lezer/lr"
|
|
|
3
3
|
import {specializeKeyword} from "./tokens"
|
|
4
4
|
export const parser = LRParser.deserialize({
|
|
5
5
|
version: 14,
|
|
6
|
-
states: "!WQVQPOOOOQO'#
|
|
7
|
-
stateData: "&
|
|
8
|
-
goto: "!
|
|
9
|
-
nodeNames: "⚠ ChartType TagKeyword DirectiveKeyword ControlKeyword ModifierKeyword Document Comment ContentLine SyncArrow AsyncArrow Duration DateLiteral Percentage Number SectionMarker Url OpenBracket CloseBracket OpenParen CloseParen OpenAngle CloseAngle
|
|
10
|
-
maxTerm:
|
|
6
|
+
states: "!WQVQPOOOOQO'#DU'#DUOOQO'#DP'#DPO%]QPO'#CdOOQO'#DO'#DOQVQPOOOOQO-E6}-E6}OOQO,59O,59OOOQO-E6|-E6|",
|
|
7
|
+
stateData: "&Q~OvOS~OPPOQPORPOSPOTPOVSOXPOYPOZPO[PO]PO^PO_PO`POaPObPOcPOdPOePOfPOgPOhPOiPOjPOkPOlPOmPOnPOoPOpPOqPOwSO~OPPOQPORPOSPOTPOXPOYPOZPO[PO]PO^PO_PO`POaPObPOcPOdPOePOfPOgPOhPOiPOjPOkPOlPOmPOnPOoPOpPOqPO~OwVO~P#]OVXYZ[]^_`ghijklmnabcdefopqk~",
|
|
8
|
+
goto: "!byPPPPPPPPzPPPPPPPPPPPPPPPPPPPPPPPPP!O!UPPPP!]TSOTQTORWTSROTRURVQORT",
|
|
9
|
+
nodeNames: "⚠ ChartType TagKeyword DirectiveKeyword ControlKeyword ModifierKeyword Document Comment ContentLine SyncArrow AsyncArrow Duration DateLiteral Percentage Number SectionMarker Url OpenBracket CloseBracket OpenParen CloseParen OpenAngle CloseAngle Pipe Colon Comma Plus Dash Tilde Star Question QuotedString Identifier Punct",
|
|
10
|
+
maxTerm: 40,
|
|
11
11
|
skippedNodes: [0],
|
|
12
12
|
repeatNodeCount: 2,
|
|
13
|
-
tokenData: "
|
|
13
|
+
tokenData: "<v~RzOX#uXY#zYZ$VZp#upq#zqr#urs$[st#uux#uxy%eyz%lz{%s{|%z|}&R}!O&Y!O!P#u!P!Q&i!Q!['Y![!]/{!]!^#u!^!_0S!_!`0Z!`!a0h!a!b0o!b!c#u!c!}0v!}#O3|#O#P#u#P#Q4R#Q#R#u#R#S0v#S#T#u#T#[0v#[#]4Y#]#o0v#o#p#u#p#q<Y#q#r#u#r#s<a#s;'S#u;'S;=`<p<%lO#u~#zOq~~$PQv~XY#zpq#z~$[Ow~~$aUq~OY$sZr$srs%Ys;'S$s;'S;=`%_<%lO$s~$vUOY$sZr$srs%Ys;'S$s;'S;=`%_<%lO$s~%_Oo~~%bP;=`<%l$s~%lOc~q~~%sOd~q~~%zOm~q~~&ROj~q~~&YOi~q~~&aPk~q~!`!a&d~&iOX~~&nPq~!P!Q&q~&vSV~OY&qZ;'S&q;'S;=`'S<%lO&q~'VP;=`<%l&q~'a]^~q~uv(Y|}(_!O!P)[!Q![*{#R#S/g#U#V*V#W#X*]#[#]*]#a#b*j#e#f*]#g#h*]#k#l*]#m#n*]~(_O]~~(bP!Q![(e~(hP!Q![(k~(nP!Q![(q~(vQ^~|}(_!O!P(|~)PP!Q![)S~)XP^~!Q![)S~)_P!Q![)b~)gY^~uv(Y!Q![)b#U#V*V#W#X*]#[#]*]#a#b*j#e#f*]#g#h*]#k#l*]#m#n*]~*YP#W#X*]~*bPZ~!a!b*e~*jOZ~~*oQZ~!a!b*e#]#^*u~*xP#b#c*]~+Q]^~uv(Y|}(_!O!P)[!Q![+y#R#S/g#U#V*V#W#X*]#[#]*]#a#b*j#e#f*]#g#h*]#k#l*]#m#n*]~,O]^~uv(Y|}(_!O!P)[!Q![,w#R#S/g#U#V*V#W#X*]#[#]*]#a#b*j#e#f*]#g#h*]#k#l*]#m#n*]~,|]^~uv(Y}!O-u!O!P)[!Q![.l#R#S/g#U#V*V#W#X*]#[#]*]#a#b*j#e#f*]#g#h*]#k#l*]#m#n*]~-xP!Q![-{~.OP!Q![.R~.WP[~}!O.Z~.^P!Q![.a~.dP!Q![.g~.lO[~~.q[^~uv(Y!O!P)[!Q![.l#R#S/g#U#V*V#W#X*]#[#]*]#a#b*j#e#f*]#g#h*]#k#l*]#m#n*]~/jP!Q![/m~/rR^~!O!P(|!Q![/m#R#S/g~0SOh~q~~0ZOe~q~~0`Pq~!_!`0c~0hO_~~0oOf~q~~0vOn~q~~0}_p~q~qr1|st1|vw1|wx1|{|1|}!O3Q!O!P1|!P!Q1|!Q![1|!_!`1|!a!b1|!b!c1|!c!}1|#R#S1|#T#o1|~2R_p~qr1|st1|vw1|wx1|{|1|}!O3Q!O!P1|!P!Q1|!Q![1|!_!`1|!a!b1|!b!c1|!c!}1|#R#S1|#T#o1|~3T]qr1|st1|vw1|wx1|{|1|!O!P1|!P!Q1|!_!`1|!a!b1|!b!c1|!c!}1|#R#S1|#T#o1|~4ROa~~4YOb~q~~4aap~q~qr1|st1|vw1|wx1|{|1|}!O3Q!O!P1|!P!Q1|!Q![1|!_!`1|!a!b1|!b!c1|!c!}1|#R#S1|#T#h1|#h#i5f#i#o1|~5kap~qr1|st1|vw1|wx1|{|1|}!O3Q!O!P1|!P!Q1|!Q![1|!_!`1|!a!b1|!b!c1|!c!}1|#R#S1|#T#h1|#h#i6p#i#o1|~6uap~qr1|st1|vw1|wx1|{|1|}!O3Q!O!P1|!P!Q1|!Q![1|!_!`1|!a!b1|!b!c1|!c!}1|#R#S1|#T#d1|#d#e7z#e#o1|~8Pbp~qr1|st1|vw1|wx1|{|1|}!O3Q!O!P1|!P!Q1|!Q![1|![!]9X!_!`1|!a!b1|!b!c1|!c!}1|#R#S1|#T#g1|#g#h;R#h#o1|~9[P!P!Q9_~9bP!P!Q9e~9hYOX:WZp:Wqy:Wz|:W}!`:W!a#P:W#Q#p:W#q;'S:W;'S;=`:{<%lO:W~:]Y`~OX:WZp:Wqy:Wz|:W}!`:W!a#P:W#Q#p:W#q;'S:W;'S;=`:{<%lO:W~;OP;=`<%l:W~;W`p~qr1|st1|vw1|wx1|{|1|}!O3Q!O!P1|!P!Q1|!Q![1|![!]9X!_!`1|!a!b1|!b!c1|!c!}1|#R#S1|#T#o1|~<aOg~q~~<hPl~q~!`!a<k~<pOY~~<sP;=`<%l#u",
|
|
14
14
|
tokenizers: [0],
|
|
15
15
|
topRules: {"Document":[0,6]},
|
|
16
|
-
specialized: [{term:
|
|
17
|
-
tokenPrec:
|
|
16
|
+
specialized: [{term: 32, get: (value, stack) => (specializeKeyword(value, stack) << 1), external: specializeKeyword}],
|
|
17
|
+
tokenPrec: 204
|
|
18
18
|
})
|
|
@@ -22,15 +22,14 @@ export const
|
|
|
22
22
|
CloseParen = 20,
|
|
23
23
|
OpenAngle = 21,
|
|
24
24
|
CloseAngle = 22,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
Punct = 34
|
|
25
|
+
Pipe = 23,
|
|
26
|
+
Colon = 24,
|
|
27
|
+
Comma = 25,
|
|
28
|
+
Plus = 26,
|
|
29
|
+
Dash = 27,
|
|
30
|
+
Tilde = 28,
|
|
31
|
+
Star = 29,
|
|
32
|
+
Question = 30,
|
|
33
|
+
QuotedString = 31,
|
|
34
|
+
Identifier = 32,
|
|
35
|
+
Punct = 33
|
package/src/editor/highlight.ts
CHANGED
package/src/er/parser.ts
CHANGED
|
@@ -46,15 +46,15 @@ function tableId(name: string): string {
|
|
|
46
46
|
// Regex patterns
|
|
47
47
|
// ============================================================
|
|
48
48
|
|
|
49
|
-
// Table declaration: name
|
|
50
|
-
// Multi-word names allowed; quote `"name with reserved
|
|
51
|
-
// contains pipe / paren / colon. Captures:
|
|
49
|
+
// Table declaration: `name`, `name color` (trailing-token §1.5), or
|
|
50
|
+
// `name | key: value`. Multi-word names allowed; quote `"name with reserved
|
|
51
|
+
// chars"` if the name contains pipe / paren / colon. Captures:
|
|
52
52
|
// 1: quoted-name content (without surrounding quotes), or undefined
|
|
53
53
|
// 2: bare-name (trimmed at call site), or undefined
|
|
54
|
-
// 3: color (
|
|
54
|
+
// 3: trailing-token color (recognized palette word), or undefined
|
|
55
55
|
// 4: pipe metadata (without leading `|`), or undefined
|
|
56
56
|
const TABLE_DECL_RE =
|
|
57
|
-
/^(?:"([^"]+)"|([a-zA-Z_][^|":(]*?))(?:\s
|
|
57
|
+
/^(?:"([^"]+)"|([a-zA-Z_][^|":(]*?))(?:\s+(red|orange|yellow|green|blue|purple|teal|cyan|gray|black|white))?(?:\s*\|(.+))?$/;
|
|
58
58
|
|
|
59
59
|
// Column: name [type] [constraints...] — space-separated, no colon, no brackets
|
|
60
60
|
// First token is always the name. Second token is the type if it's not a constraint keyword.
|
|
@@ -229,14 +229,6 @@ export function parseERDiagram(
|
|
|
229
229
|
error: null,
|
|
230
230
|
};
|
|
231
231
|
|
|
232
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
233
|
-
const _fail = (line: number, message: string): ParsedERDiagram => {
|
|
234
|
-
const diag = makeDgmoError(line, message);
|
|
235
|
-
result.diagnostics.push(diag);
|
|
236
|
-
result.error = formatDgmoError(diag);
|
|
237
|
-
return result;
|
|
238
|
-
};
|
|
239
|
-
|
|
240
232
|
const pushError = (line: number, message: string): void => {
|
|
241
233
|
const diag = makeDgmoError(line, message);
|
|
242
234
|
result.diagnostics.push(diag);
|
|
@@ -358,7 +350,7 @@ export function parseERDiagram(
|
|
|
358
350
|
result.diagnostics.push(
|
|
359
351
|
makeDgmoError(
|
|
360
352
|
lineNumber,
|
|
361
|
-
`Expected 'Value
|
|
353
|
+
`Expected 'Value color' in tag group '${currentTagGroup.name}'`,
|
|
362
354
|
'warning'
|
|
363
355
|
)
|
|
364
356
|
);
|
|
@@ -502,7 +494,7 @@ export function parseERDiagram(
|
|
|
502
494
|
if (result.tables.length === 0 && !result.error) {
|
|
503
495
|
const diag = makeDgmoError(
|
|
504
496
|
1,
|
|
505
|
-
'No tables found. Add table declarations like "users" or "orders
|
|
497
|
+
'No tables found. Add table declarations like "users" or "orders blue".'
|
|
506
498
|
);
|
|
507
499
|
result.diagnostics.push(diag);
|
|
508
500
|
result.error = formatDgmoError(diag);
|
|
@@ -628,7 +620,7 @@ export function looksLikeERDiagram(content: string): boolean {
|
|
|
628
620
|
// Symbol extraction (for completion API)
|
|
629
621
|
// ============================================================
|
|
630
622
|
|
|
631
|
-
import type { DiagramSymbols } from '../completion';
|
|
623
|
+
import type { DiagramSymbols } from '../completion-types';
|
|
632
624
|
|
|
633
625
|
/**
|
|
634
626
|
* Extract table names (entities) and ER keywords from document text.
|
|
@@ -640,15 +632,22 @@ export function extractSymbols(docText: string): DiagramSymbols {
|
|
|
640
632
|
for (const rawLine of docText.split('\n')) {
|
|
641
633
|
const line = rawLine.trim();
|
|
642
634
|
if (inMetadata && /^er(\s|$)/i.test(line)) continue;
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
635
|
+
// Under §1.5 trailing-token, `Users blue` matches OPTION_NOCOLON_RE
|
|
636
|
+
// (key=Users, value=blue) but is actually a table with a color.
|
|
637
|
+
// Detect tables FIRST so they aren't swallowed by the option fallback.
|
|
646
638
|
if (/^\s/.test(rawLine)) continue; // indented = column definition, not table
|
|
639
|
+
if (line.length === 0) continue;
|
|
647
640
|
const m = TABLE_DECL_RE.exec(line);
|
|
648
641
|
if (m) {
|
|
649
642
|
const name = (m[1] ?? m[2] ?? '').trim();
|
|
650
|
-
if (name)
|
|
643
|
+
if (name) {
|
|
644
|
+
inMetadata = false;
|
|
645
|
+
entities.push(name);
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
651
648
|
}
|
|
649
|
+
if (inMetadata && OPTION_NOCOLON_RE.test(line)) continue; // option line
|
|
650
|
+
inMetadata = false;
|
|
652
651
|
}
|
|
653
652
|
return {
|
|
654
653
|
kind: 'er',
|
package/src/er/renderer.ts
CHANGED
|
@@ -225,7 +225,8 @@ export function renderERDiagram(
|
|
|
225
225
|
exportDims?: { width?: number; height?: number },
|
|
226
226
|
activeTagGroup?: string | null,
|
|
227
227
|
/** When false, semantic role colors are suppressed and entities use a neutral color. */
|
|
228
|
-
semanticColorsActive?: boolean
|
|
228
|
+
semanticColorsActive?: boolean,
|
|
229
|
+
exportMode?: boolean
|
|
229
230
|
): void {
|
|
230
231
|
d3Selection.select(container).selectAll(':not([data-d3-tooltip])').remove();
|
|
231
232
|
|
|
@@ -340,7 +341,7 @@ export function renderERDiagram(
|
|
|
340
341
|
semanticRoles !== null && (semanticColorsActive ?? true);
|
|
341
342
|
|
|
342
343
|
// ── Edges (behind nodes) ──
|
|
343
|
-
const useLabels = parsed.options
|
|
344
|
+
const useLabels = parsed.options['notation'] === 'labels';
|
|
344
345
|
|
|
345
346
|
for (const edge of layout.edges) {
|
|
346
347
|
if (edge.points.length < 2) continue;
|
|
@@ -560,7 +561,7 @@ export function renderERDiagram(
|
|
|
560
561
|
const legendConfig: LegendConfig = {
|
|
561
562
|
groups: parsed.tagGroups,
|
|
562
563
|
position: { placement: 'top-center', titleRelation: 'below-title' },
|
|
563
|
-
mode: '
|
|
564
|
+
mode: exportMode ? 'export' : 'preview',
|
|
564
565
|
};
|
|
565
566
|
const legendState: LegendState = { activeGroup: activeTagGroup ?? null };
|
|
566
567
|
const legendG = svg
|
|
@@ -602,7 +603,7 @@ export function renderERDiagram(
|
|
|
602
603
|
const legendConfig: LegendConfig = {
|
|
603
604
|
groups: semanticGroups,
|
|
604
605
|
position: { placement: 'top-center', titleRelation: 'below-title' },
|
|
605
|
-
mode: '
|
|
606
|
+
mode: exportMode ? 'export' : 'preview',
|
|
606
607
|
};
|
|
607
608
|
const legendState: LegendState = {
|
|
608
609
|
activeGroup: semanticActive ? 'Role' : null,
|
|
@@ -653,10 +654,21 @@ export function renderERDiagramForExport(
|
|
|
653
654
|
document.body.appendChild(container);
|
|
654
655
|
|
|
655
656
|
try {
|
|
656
|
-
renderERDiagram(
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
657
|
+
renderERDiagram(
|
|
658
|
+
container,
|
|
659
|
+
parsed,
|
|
660
|
+
layout,
|
|
661
|
+
palette,
|
|
662
|
+
isDark,
|
|
663
|
+
undefined,
|
|
664
|
+
{
|
|
665
|
+
width: exportWidth,
|
|
666
|
+
height: exportHeight,
|
|
667
|
+
},
|
|
668
|
+
undefined,
|
|
669
|
+
undefined,
|
|
670
|
+
true
|
|
671
|
+
);
|
|
660
672
|
|
|
661
673
|
const svgEl = container.querySelector('svg');
|
|
662
674
|
if (!svgEl) return '';
|
package/src/gantt/calculator.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// 3. Forward pass: resolve dates using universal max rule
|
|
10
10
|
// 4. (Optional) Critical path: backward pass to find zero-slack chain
|
|
11
11
|
|
|
12
|
-
import { makeDgmoError
|
|
12
|
+
import { makeDgmoError } from '../diagnostics';
|
|
13
13
|
import type { DgmoError } from '../diagnostics';
|
|
14
14
|
import type {
|
|
15
15
|
ParsedGantt,
|
|
@@ -67,14 +67,6 @@ export function calculateSchedule(parsed: ParsedGantt): ResolvedSchedule {
|
|
|
67
67
|
diagnostics.push(makeDgmoError(line, message, 'warning'));
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
71
|
-
const _fail = (line: number, message: string): ResolvedSchedule => {
|
|
72
|
-
const diag = makeDgmoError(line, message);
|
|
73
|
-
diagnostics.push(diag);
|
|
74
|
-
result.error = formatDgmoError(diag);
|
|
75
|
-
return result;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
70
|
// ── Build holiday set ───────────────────────────────────
|
|
79
71
|
|
|
80
72
|
const holidaySet = buildHolidaySet(parsed.holidays);
|
|
@@ -124,8 +116,6 @@ export function calculateSchedule(parsed: ParsedGantt): ResolvedSchedule {
|
|
|
124
116
|
// ── Resolve explicit -> dependencies ────────────────────
|
|
125
117
|
|
|
126
118
|
for (const task of allTasks) {
|
|
127
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
128
|
-
const _node = taskMap.get(task.id)!;
|
|
129
119
|
for (const dep of task.dependencies) {
|
|
130
120
|
const resolved = resolveTaskName(dep.targetName, allTasks);
|
|
131
121
|
if (isResolverError(resolved)) {
|
package/src/gantt/parser.ts
CHANGED
|
@@ -380,7 +380,7 @@ export function parseGantt(
|
|
|
380
380
|
currentTagGroup = null;
|
|
381
381
|
// fall through to process this line normally
|
|
382
382
|
} else {
|
|
383
|
-
// Parse tag entry: `Value
|
|
383
|
+
// Parse tag entry: `Value color` or `Value`
|
|
384
384
|
// First entry is the default unless another is marked `default`
|
|
385
385
|
if (COMMENT_RE.test(line)) continue;
|
|
386
386
|
const { text: cleanEntry, isDefault } = stripDefaultModifier(line);
|
|
@@ -435,15 +435,15 @@ export function parseGantt(
|
|
|
435
435
|
metaAliasMap,
|
|
436
436
|
() => warn(lineNumber, MULTIPLE_PIPE_ERROR)
|
|
437
437
|
);
|
|
438
|
-
if (meta
|
|
439
|
-
const key = meta
|
|
438
|
+
if (meta['lag'] || meta['lead']) {
|
|
439
|
+
const key = meta['lag'] ? 'lag' : 'lead';
|
|
440
440
|
softError(
|
|
441
441
|
lineNumber,
|
|
442
442
|
`"${key}" is no longer supported — use "offset: ${meta[key]}" instead.${key === 'lead' ? ' Negate the value for lead behavior: "offset: -...".' : ''}`
|
|
443
443
|
);
|
|
444
444
|
}
|
|
445
|
-
if (meta
|
|
446
|
-
const raw = meta
|
|
445
|
+
if (meta['offset']) {
|
|
446
|
+
const raw = meta['offset'];
|
|
447
447
|
if (raw.trim().startsWith('+')) {
|
|
448
448
|
warn(
|
|
449
449
|
lineNumber,
|
|
@@ -903,15 +903,15 @@ export function parseGantt(
|
|
|
903
903
|
metaAliasMap,
|
|
904
904
|
() => warn(lineNumber, MULTIPLE_PIPE_ERROR)
|
|
905
905
|
);
|
|
906
|
-
if (meta
|
|
907
|
-
const key = meta
|
|
906
|
+
if (meta['lag'] || meta['lead']) {
|
|
907
|
+
const key = meta['lag'] ? 'lag' : 'lead';
|
|
908
908
|
softError(
|
|
909
909
|
lineNumber,
|
|
910
910
|
`"${key}" is no longer supported — use "offset: ${meta[key]}" instead.${key === 'lead' ? ' Negate the value for lead behavior: "offset: -...".' : ''}`
|
|
911
911
|
);
|
|
912
912
|
}
|
|
913
|
-
if (meta
|
|
914
|
-
const raw = meta
|
|
913
|
+
if (meta['offset']) {
|
|
914
|
+
const raw = meta['offset'];
|
|
915
915
|
if (raw.trim().startsWith('+')) {
|
|
916
916
|
warn(
|
|
917
917
|
lineNumber,
|
|
@@ -1023,9 +1023,9 @@ export function parseGantt(
|
|
|
1023
1023
|
|
|
1024
1024
|
// Extract progress from metadata or shorthand
|
|
1025
1025
|
let progress: number | null = null;
|
|
1026
|
-
if (metadata
|
|
1027
|
-
progress = parseFloat(metadata
|
|
1028
|
-
delete metadata
|
|
1026
|
+
if (metadata['progress']) {
|
|
1027
|
+
progress = parseFloat(metadata['progress']);
|
|
1028
|
+
delete metadata['progress'];
|
|
1029
1029
|
}
|
|
1030
1030
|
// Check for progress shorthand: `| 80%` or `| t:X, 80%`
|
|
1031
1031
|
for (const part of segments.slice(1).join(',').split(',')) {
|
|
@@ -1037,8 +1037,8 @@ export function parseGantt(
|
|
|
1037
1037
|
}
|
|
1038
1038
|
|
|
1039
1039
|
// Reject lag/lead — use offset instead
|
|
1040
|
-
if (metadata
|
|
1041
|
-
const key = metadata
|
|
1040
|
+
if (metadata['lag'] || metadata['lead']) {
|
|
1041
|
+
const key = metadata['lag'] ? 'lag' : 'lead';
|
|
1042
1042
|
softError(
|
|
1043
1043
|
ln,
|
|
1044
1044
|
`"${key}" is no longer supported — use "offset: ${metadata[key]}" instead.${key === 'lead' ? ' Negate the value for lead behavior: "offset: -...".' : ''}`
|
|
@@ -1047,8 +1047,8 @@ export function parseGantt(
|
|
|
1047
1047
|
|
|
1048
1048
|
// Extract task-level offset from metadata
|
|
1049
1049
|
let taskOffset: Offset | undefined;
|
|
1050
|
-
if (metadata
|
|
1051
|
-
const raw = metadata
|
|
1050
|
+
if (metadata['offset']) {
|
|
1051
|
+
const raw = metadata['offset'];
|
|
1052
1052
|
if (raw.trim().startsWith('+')) {
|
|
1053
1053
|
warn(
|
|
1054
1054
|
ln,
|
|
@@ -1063,7 +1063,7 @@ export function parseGantt(
|
|
|
1063
1063
|
);
|
|
1064
1064
|
}
|
|
1065
1065
|
}
|
|
1066
|
-
delete metadata
|
|
1066
|
+
delete metadata['offset'];
|
|
1067
1067
|
}
|
|
1068
1068
|
|
|
1069
1069
|
// Inherit metadata from parent groups (tag inheritance)
|
package/src/gantt/renderer.ts
CHANGED
|
@@ -208,6 +208,7 @@ export interface GanttInteractiveOptions {
|
|
|
208
208
|
collapsedLanes?: Set<string>;
|
|
209
209
|
onToggleLane?: (laneName: string) => void;
|
|
210
210
|
viewMode?: boolean;
|
|
211
|
+
exportMode?: boolean;
|
|
211
212
|
}
|
|
212
213
|
|
|
213
214
|
// ── Main Renderer ───────────────────────────────────────────
|
|
@@ -439,7 +440,8 @@ export function renderGantt(
|
|
|
439
440
|
).attr('display', active ? null : 'none');
|
|
440
441
|
}
|
|
441
442
|
drawLegend();
|
|
442
|
-
}
|
|
443
|
+
},
|
|
444
|
+
options?.exportMode ?? false
|
|
443
445
|
);
|
|
444
446
|
}
|
|
445
447
|
}
|
|
@@ -1973,7 +1975,7 @@ function renderTagLegend(
|
|
|
1973
1975
|
isDark: boolean,
|
|
1974
1976
|
hasCriticalPath: boolean,
|
|
1975
1977
|
criticalPathActive: boolean,
|
|
1976
|
-
|
|
1978
|
+
_optionLineNumbers: Record<string, number>,
|
|
1977
1979
|
onToggle?: (groupName: string) => void,
|
|
1978
1980
|
onToggleControlsExpand?: () => void,
|
|
1979
1981
|
currentSwimlaneGroup?: string | null,
|
|
@@ -1983,7 +1985,8 @@ function renderTagLegend(
|
|
|
1983
1985
|
controlsExpanded = false,
|
|
1984
1986
|
hasDependencies = false,
|
|
1985
1987
|
dependenciesActive = false,
|
|
1986
|
-
onControlsToggle?: (toggleId: string, active: boolean) => void
|
|
1988
|
+
onControlsToggle?: (toggleId: string, active: boolean) => void,
|
|
1989
|
+
exportMode = false
|
|
1987
1990
|
): void {
|
|
1988
1991
|
// Build visible groups: active group expanded + swimlane group as compact pill
|
|
1989
1992
|
let visibleGroups: TagGroup[];
|
|
@@ -2117,7 +2120,7 @@ function renderTagLegend(
|
|
|
2117
2120
|
placement: 'top-center' as const,
|
|
2118
2121
|
titleRelation: 'below-title' as const,
|
|
2119
2122
|
},
|
|
2120
|
-
mode: '
|
|
2123
|
+
mode: exportMode ? 'export' : 'preview',
|
|
2121
2124
|
capsulePillAddonWidth: iconReserve,
|
|
2122
2125
|
controlsGroup:
|
|
2123
2126
|
controlsToggles.length > 0 ? { toggles: controlsToggles } : undefined,
|
|
@@ -2263,7 +2266,7 @@ function renderTagLegend(
|
|
|
2263
2266
|
placement: 'top-center' as const,
|
|
2264
2267
|
titleRelation: 'below-title' as const,
|
|
2265
2268
|
},
|
|
2266
|
-
mode: '
|
|
2269
|
+
mode: exportMode ? 'export' : 'preview',
|
|
2267
2270
|
controlsGroup: { toggles: controlsToggles },
|
|
2268
2271
|
};
|
|
2269
2272
|
|
|
@@ -3633,7 +3636,7 @@ function resolveTaskColor(
|
|
|
3633
3636
|
function renderTimeScaleHorizontal(
|
|
3634
3637
|
g: d3Selection.Selection<SVGGElement, unknown, null, undefined>,
|
|
3635
3638
|
scale: d3Scale.ScaleLinear<number, number>,
|
|
3636
|
-
|
|
3639
|
+
_innerWidth: number,
|
|
3637
3640
|
innerHeight: number,
|
|
3638
3641
|
textColor: string
|
|
3639
3642
|
): void {
|