@diagrammo/dgmo 0.6.3 → 0.7.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/dist/cli.cjs +180 -178
- package/dist/index.cjs +5447 -2229
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +236 -16
- package/dist/index.d.ts +236 -16
- package/dist/index.js +5439 -2228
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/c4/parser.ts +3 -2
- package/src/c4/renderer.ts +6 -6
- package/src/class/renderer.ts +183 -7
- package/src/cli.ts +3 -11
- package/src/colors.ts +3 -3
- package/src/d3.ts +132 -29
- package/src/dgmo-router.ts +3 -1
- package/src/er/parser.ts +5 -3
- package/src/er/renderer.ts +11 -5
- package/src/gantt/calculator.ts +717 -0
- package/src/gantt/parser.ts +767 -0
- package/src/gantt/renderer.ts +2251 -0
- package/src/gantt/resolver.ts +144 -0
- package/src/gantt/types.ts +168 -0
- package/src/index.ts +27 -0
- package/src/infra/renderer.ts +48 -12
- package/src/initiative-status/filter.ts +63 -0
- package/src/initiative-status/layout.ts +319 -67
- package/src/initiative-status/parser.ts +200 -25
- package/src/initiative-status/renderer.ts +293 -10
- package/src/initiative-status/types.ts +6 -0
- package/src/org/layout.ts +22 -55
- package/src/org/parser.ts +7 -5
- package/src/org/renderer.ts +4 -8
- package/src/palettes/dracula.ts +60 -0
- package/src/palettes/index.ts +8 -6
- package/src/palettes/monokai.ts +60 -0
- package/src/palettes/registry.ts +4 -2
- package/src/sequence/parser.ts +10 -9
- package/src/sequence/renderer.ts +5 -4
- package/src/sharing.ts +8 -0
- package/src/sitemap/parser.ts +5 -3
- package/src/sitemap/renderer.ts +4 -4
- package/src/utils/duration.ts +212 -0
- package/src/utils/legend-constants.ts +1 -0
- package/src/utils/parsing.ts +23 -12
package/src/d3.ts
CHANGED
|
@@ -181,7 +181,7 @@ import { getSeriesColors } from './palettes';
|
|
|
181
181
|
import { mix } from './palettes/color-utils';
|
|
182
182
|
import type { DgmoError } from './diagnostics';
|
|
183
183
|
import { makeDgmoError, formatDgmoError, suggest } from './diagnostics';
|
|
184
|
-
import { collectIndentedValues, extractColor, parsePipeMetadata } from './utils/parsing';
|
|
184
|
+
import { collectIndentedValues, extractColor, parsePipeMetadata, MULTIPLE_PIPE_WARNING } from './utils/parsing';
|
|
185
185
|
import { matchTagBlockHeading, validateTagValues, resolveTagColor } from './utils/tag-groups';
|
|
186
186
|
import type { TagGroup } from './utils/tag-groups';
|
|
187
187
|
import {
|
|
@@ -545,9 +545,9 @@ export function parseVisualization(content: string, palette?: PaletteColors): Pa
|
|
|
545
545
|
continue;
|
|
546
546
|
}
|
|
547
547
|
|
|
548
|
-
// Timeline marker lines: marker YYYY
|
|
548
|
+
// Timeline marker lines: marker: YYYY Label (color)
|
|
549
549
|
const markerMatch = line.match(
|
|
550
|
-
/^marker
|
|
550
|
+
/^marker:\s+(\d{4}(?:-\d{2})?(?:-\d{2})?)\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/
|
|
551
551
|
);
|
|
552
552
|
if (markerMatch) {
|
|
553
553
|
const colorAnnotation = markerMatch[3]?.trim() || null;
|
|
@@ -579,7 +579,7 @@ export function parseVisualization(content: string, palette?: PaletteColors): Pa
|
|
|
579
579
|
const endDate = addDurationToDate(startDate, amount, unit);
|
|
580
580
|
const segments = durationMatch[5].split('|');
|
|
581
581
|
const metadata = segments.length > 1
|
|
582
|
-
? parsePipeMetadata(['', ...segments.slice(1)], timelineAliasMap)
|
|
582
|
+
? parsePipeMetadata(['', ...segments.slice(1)], timelineAliasMap, () => warn(lineNumber, MULTIPLE_PIPE_WARNING))
|
|
583
583
|
: {};
|
|
584
584
|
result.timelineEvents.push({
|
|
585
585
|
date: startDate,
|
|
@@ -600,7 +600,7 @@ export function parseVisualization(content: string, palette?: PaletteColors): Pa
|
|
|
600
600
|
if (rangeMatch) {
|
|
601
601
|
const segments = rangeMatch[4].split('|');
|
|
602
602
|
const metadata = segments.length > 1
|
|
603
|
-
? parsePipeMetadata(['', ...segments.slice(1)], timelineAliasMap)
|
|
603
|
+
? parsePipeMetadata(['', ...segments.slice(1)], timelineAliasMap, () => warn(lineNumber, MULTIPLE_PIPE_WARNING))
|
|
604
604
|
: {};
|
|
605
605
|
result.timelineEvents.push({
|
|
606
606
|
date: rangeMatch[1],
|
|
@@ -621,7 +621,7 @@ export function parseVisualization(content: string, palette?: PaletteColors): Pa
|
|
|
621
621
|
if (pointMatch) {
|
|
622
622
|
const segments = pointMatch[2].split('|');
|
|
623
623
|
const metadata = segments.length > 1
|
|
624
|
-
? parsePipeMetadata(['', ...segments.slice(1)], timelineAliasMap)
|
|
624
|
+
? parsePipeMetadata(['', ...segments.slice(1)], timelineAliasMap, () => warn(lineNumber, MULTIPLE_PIPE_WARNING))
|
|
625
625
|
: {};
|
|
626
626
|
result.timelineEvents.push({
|
|
627
627
|
date: pointMatch[1],
|
|
@@ -3059,7 +3059,7 @@ export function renderTimeline(
|
|
|
3059
3059
|
}
|
|
3060
3060
|
}
|
|
3061
3061
|
|
|
3062
|
-
// Reserve space for tag legend at the
|
|
3062
|
+
// Reserve space for tag legend at the top of chart content (below title/headers)
|
|
3063
3063
|
const tagLegendReserve = parsed.timelineTagGroups.length > 0 ? 36 : 0;
|
|
3064
3064
|
|
|
3065
3065
|
// ================================================================
|
|
@@ -3099,9 +3099,9 @@ export function renderTimeline(
|
|
|
3099
3099
|
const scaleMargin = timelineScale ? 40 : 0;
|
|
3100
3100
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
3101
3101
|
const margin = {
|
|
3102
|
-
top: 104 + markerMargin,
|
|
3102
|
+
top: 104 + markerMargin + tagLegendReserve,
|
|
3103
3103
|
right: 40 + scaleMargin,
|
|
3104
|
-
bottom: 40
|
|
3104
|
+
bottom: 40,
|
|
3105
3105
|
left: 60 + scaleMargin,
|
|
3106
3106
|
};
|
|
3107
3107
|
const innerWidth = width - margin.left - margin.right;
|
|
@@ -3254,12 +3254,14 @@ export function renderTimeline(
|
|
|
3254
3254
|
const rectH = Math.max(y2 - y, 4);
|
|
3255
3255
|
|
|
3256
3256
|
let fill: string = mix(evColor, bg, 30);
|
|
3257
|
+
let stroke: string = evColor;
|
|
3257
3258
|
if (ev.uncertain) {
|
|
3258
3259
|
const gradientId = `uncertain-vg-${ev.lineNumber}`;
|
|
3260
|
+
const strokeGradientId = `uncertain-vg-s-${ev.lineNumber}`;
|
|
3259
3261
|
const defs =
|
|
3260
3262
|
svg.select('defs').node() || svg.append('defs').node();
|
|
3261
|
-
d3Selection
|
|
3262
|
-
|
|
3263
|
+
const defsEl = d3Selection.select(defs as Element);
|
|
3264
|
+
defsEl
|
|
3263
3265
|
.append('linearGradient')
|
|
3264
3266
|
.attr('id', gradientId)
|
|
3265
3267
|
.attr('x1', '0%')
|
|
@@ -3277,7 +3279,26 @@ export function renderTimeline(
|
|
|
3277
3279
|
.attr('offset', (d) => d.offset)
|
|
3278
3280
|
.attr('stop-color', mix(laneColor, bg, 30))
|
|
3279
3281
|
.attr('stop-opacity', (d) => d.opacity);
|
|
3282
|
+
defsEl
|
|
3283
|
+
.append('linearGradient')
|
|
3284
|
+
.attr('id', strokeGradientId)
|
|
3285
|
+
.attr('x1', '0%')
|
|
3286
|
+
.attr('y1', '0%')
|
|
3287
|
+
.attr('x2', '0%')
|
|
3288
|
+
.attr('y2', '100%')
|
|
3289
|
+
.selectAll('stop')
|
|
3290
|
+
.data([
|
|
3291
|
+
{ offset: '0%', opacity: 1 },
|
|
3292
|
+
{ offset: '80%', opacity: 1 },
|
|
3293
|
+
{ offset: '100%', opacity: 0 },
|
|
3294
|
+
])
|
|
3295
|
+
.enter()
|
|
3296
|
+
.append('stop')
|
|
3297
|
+
.attr('offset', (d) => d.offset)
|
|
3298
|
+
.attr('stop-color', evColor)
|
|
3299
|
+
.attr('stop-opacity', (d) => d.opacity);
|
|
3280
3300
|
fill = `url(#${gradientId})`;
|
|
3301
|
+
stroke = `url(#${strokeGradientId})`;
|
|
3281
3302
|
}
|
|
3282
3303
|
|
|
3283
3304
|
evG
|
|
@@ -3288,7 +3309,7 @@ export function renderTimeline(
|
|
|
3288
3309
|
.attr('height', rectH)
|
|
3289
3310
|
.attr('rx', 4)
|
|
3290
3311
|
.attr('fill', fill)
|
|
3291
|
-
.attr('stroke',
|
|
3312
|
+
.attr('stroke', stroke)
|
|
3292
3313
|
.attr('stroke-width', 2);
|
|
3293
3314
|
evG
|
|
3294
3315
|
.append('text')
|
|
@@ -3323,9 +3344,9 @@ export function renderTimeline(
|
|
|
3323
3344
|
const scaleMargin = timelineScale ? 40 : 0;
|
|
3324
3345
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
3325
3346
|
const margin = {
|
|
3326
|
-
top: 104 + markerMargin,
|
|
3347
|
+
top: 104 + markerMargin + tagLegendReserve,
|
|
3327
3348
|
right: 200,
|
|
3328
|
-
bottom: 40
|
|
3349
|
+
bottom: 40,
|
|
3329
3350
|
left: 60 + scaleMargin,
|
|
3330
3351
|
};
|
|
3331
3352
|
const innerWidth = width - margin.left - margin.right;
|
|
@@ -3474,12 +3495,14 @@ export function renderTimeline(
|
|
|
3474
3495
|
const rectH = Math.max(y2 - y, 4);
|
|
3475
3496
|
|
|
3476
3497
|
let fill: string = mix(color, bg, 30);
|
|
3498
|
+
let stroke: string = color;
|
|
3477
3499
|
if (ev.uncertain) {
|
|
3478
3500
|
const gradientId = `uncertain-v-${ev.lineNumber}`;
|
|
3501
|
+
const strokeGradientId = `uncertain-v-s-${ev.lineNumber}`;
|
|
3479
3502
|
const defs =
|
|
3480
3503
|
svg.select('defs').node() || svg.append('defs').node();
|
|
3481
|
-
d3Selection
|
|
3482
|
-
|
|
3504
|
+
const defsEl = d3Selection.select(defs as Element);
|
|
3505
|
+
defsEl
|
|
3483
3506
|
.append('linearGradient')
|
|
3484
3507
|
.attr('id', gradientId)
|
|
3485
3508
|
.attr('x1', '0%')
|
|
@@ -3497,7 +3520,26 @@ export function renderTimeline(
|
|
|
3497
3520
|
.attr('offset', (d) => d.offset)
|
|
3498
3521
|
.attr('stop-color', mix(color, bg, 30))
|
|
3499
3522
|
.attr('stop-opacity', (d) => d.opacity);
|
|
3523
|
+
defsEl
|
|
3524
|
+
.append('linearGradient')
|
|
3525
|
+
.attr('id', strokeGradientId)
|
|
3526
|
+
.attr('x1', '0%')
|
|
3527
|
+
.attr('y1', '0%')
|
|
3528
|
+
.attr('x2', '0%')
|
|
3529
|
+
.attr('y2', '100%')
|
|
3530
|
+
.selectAll('stop')
|
|
3531
|
+
.data([
|
|
3532
|
+
{ offset: '0%', opacity: 1 },
|
|
3533
|
+
{ offset: '80%', opacity: 1 },
|
|
3534
|
+
{ offset: '100%', opacity: 0 },
|
|
3535
|
+
])
|
|
3536
|
+
.enter()
|
|
3537
|
+
.append('stop')
|
|
3538
|
+
.attr('offset', (d) => d.offset)
|
|
3539
|
+
.attr('stop-color', color)
|
|
3540
|
+
.attr('stop-opacity', (d) => d.opacity);
|
|
3500
3541
|
fill = `url(#${gradientId})`;
|
|
3542
|
+
stroke = `url(#${strokeGradientId})`;
|
|
3501
3543
|
}
|
|
3502
3544
|
|
|
3503
3545
|
evG
|
|
@@ -3508,7 +3550,7 @@ export function renderTimeline(
|
|
|
3508
3550
|
.attr('height', rectH)
|
|
3509
3551
|
.attr('rx', 4)
|
|
3510
3552
|
.attr('fill', fill)
|
|
3511
|
-
.attr('stroke',
|
|
3553
|
+
.attr('stroke', stroke)
|
|
3512
3554
|
.attr('stroke-width', 2);
|
|
3513
3555
|
evG
|
|
3514
3556
|
.append('text')
|
|
@@ -3606,9 +3648,9 @@ export function renderTimeline(
|
|
|
3606
3648
|
// Group-sorted doesn't need legend space (group names shown on left)
|
|
3607
3649
|
const baseTopMargin = title ? 50 : 20;
|
|
3608
3650
|
const margin = {
|
|
3609
|
-
top: baseTopMargin + (timelineScale ? 40 : 0) + markerMargin,
|
|
3651
|
+
top: baseTopMargin + (timelineScale ? 40 : 0) + markerMargin + tagLegendReserve,
|
|
3610
3652
|
right: 40,
|
|
3611
|
-
bottom: 40 + scaleMargin
|
|
3653
|
+
bottom: 40 + scaleMargin,
|
|
3612
3654
|
left: dynamicLeftMargin,
|
|
3613
3655
|
};
|
|
3614
3656
|
const innerWidth = width - margin.left - margin.right;
|
|
@@ -3785,12 +3827,14 @@ export function renderTimeline(
|
|
|
3785
3827
|
const labelFitsInside = rectW >= estLabelWidth;
|
|
3786
3828
|
|
|
3787
3829
|
let fill: string = mix(evColor, bg, 30);
|
|
3830
|
+
let stroke: string = evColor;
|
|
3788
3831
|
if (ev.uncertain) {
|
|
3789
3832
|
// Create gradient for uncertain end - fades last 20%
|
|
3790
3833
|
const gradientId = `uncertain-${ev.lineNumber}`;
|
|
3834
|
+
const strokeGradientId = `uncertain-s-${ev.lineNumber}`;
|
|
3791
3835
|
const defs = svg.select('defs').node() || svg.append('defs').node();
|
|
3792
|
-
d3Selection
|
|
3793
|
-
|
|
3836
|
+
const defsEl = d3Selection.select(defs as Element);
|
|
3837
|
+
defsEl
|
|
3794
3838
|
.append('linearGradient')
|
|
3795
3839
|
.attr('id', gradientId)
|
|
3796
3840
|
.attr('x1', '0%')
|
|
@@ -3808,7 +3852,26 @@ export function renderTimeline(
|
|
|
3808
3852
|
.attr('offset', (d) => d.offset)
|
|
3809
3853
|
.attr('stop-color', mix(evColor, bg, 30))
|
|
3810
3854
|
.attr('stop-opacity', (d) => d.opacity);
|
|
3855
|
+
defsEl
|
|
3856
|
+
.append('linearGradient')
|
|
3857
|
+
.attr('id', strokeGradientId)
|
|
3858
|
+
.attr('x1', '0%')
|
|
3859
|
+
.attr('y1', '0%')
|
|
3860
|
+
.attr('x2', '100%')
|
|
3861
|
+
.attr('y2', '0%')
|
|
3862
|
+
.selectAll('stop')
|
|
3863
|
+
.data([
|
|
3864
|
+
{ offset: '0%', opacity: 1 },
|
|
3865
|
+
{ offset: '80%', opacity: 1 },
|
|
3866
|
+
{ offset: '100%', opacity: 0 },
|
|
3867
|
+
])
|
|
3868
|
+
.enter()
|
|
3869
|
+
.append('stop')
|
|
3870
|
+
.attr('offset', (d) => d.offset)
|
|
3871
|
+
.attr('stop-color', evColor)
|
|
3872
|
+
.attr('stop-opacity', (d) => d.opacity);
|
|
3811
3873
|
fill = `url(#${gradientId})`;
|
|
3874
|
+
stroke = `url(#${strokeGradientId})`;
|
|
3812
3875
|
}
|
|
3813
3876
|
|
|
3814
3877
|
evG
|
|
@@ -3819,7 +3882,7 @@ export function renderTimeline(
|
|
|
3819
3882
|
.attr('height', BAR_H)
|
|
3820
3883
|
.attr('rx', 4)
|
|
3821
3884
|
.attr('fill', fill)
|
|
3822
|
-
.attr('stroke',
|
|
3885
|
+
.attr('stroke', stroke)
|
|
3823
3886
|
.attr('stroke-width', 2);
|
|
3824
3887
|
|
|
3825
3888
|
if (labelFitsInside) {
|
|
@@ -3887,9 +3950,9 @@ export function renderTimeline(
|
|
|
3887
3950
|
const scaleMargin = timelineScale ? 24 : 0;
|
|
3888
3951
|
const markerMargin = timelineMarkers.length > 0 ? 30 : 0;
|
|
3889
3952
|
const margin = {
|
|
3890
|
-
top: 104 + (timelineScale ? 40 : 0) + markerMargin,
|
|
3953
|
+
top: 104 + (timelineScale ? 40 : 0) + markerMargin + tagLegendReserve,
|
|
3891
3954
|
right: 40,
|
|
3892
|
-
bottom: 40 + scaleMargin
|
|
3955
|
+
bottom: 40 + scaleMargin,
|
|
3893
3956
|
left: 60,
|
|
3894
3957
|
};
|
|
3895
3958
|
const innerWidth = width - margin.left - margin.right;
|
|
@@ -4047,12 +4110,14 @@ export function renderTimeline(
|
|
|
4047
4110
|
const labelFitsInside = rectW >= estLabelWidth;
|
|
4048
4111
|
|
|
4049
4112
|
let fill: string = mix(color, bg, 30);
|
|
4113
|
+
let stroke: string = color;
|
|
4050
4114
|
if (ev.uncertain) {
|
|
4051
4115
|
// Create gradient for uncertain end - fades last 20%
|
|
4052
4116
|
const gradientId = `uncertain-ts-${ev.lineNumber}`;
|
|
4117
|
+
const strokeGradientId = `uncertain-ts-s-${ev.lineNumber}`;
|
|
4053
4118
|
const defs = svg.select('defs').node() || svg.append('defs').node();
|
|
4054
|
-
d3Selection
|
|
4055
|
-
|
|
4119
|
+
const defsEl = d3Selection.select(defs as Element);
|
|
4120
|
+
defsEl
|
|
4056
4121
|
.append('linearGradient')
|
|
4057
4122
|
.attr('id', gradientId)
|
|
4058
4123
|
.attr('x1', '0%')
|
|
@@ -4070,7 +4135,26 @@ export function renderTimeline(
|
|
|
4070
4135
|
.attr('offset', (d) => d.offset)
|
|
4071
4136
|
.attr('stop-color', mix(color, bg, 30))
|
|
4072
4137
|
.attr('stop-opacity', (d) => d.opacity);
|
|
4138
|
+
defsEl
|
|
4139
|
+
.append('linearGradient')
|
|
4140
|
+
.attr('id', strokeGradientId)
|
|
4141
|
+
.attr('x1', '0%')
|
|
4142
|
+
.attr('y1', '0%')
|
|
4143
|
+
.attr('x2', '100%')
|
|
4144
|
+
.attr('y2', '0%')
|
|
4145
|
+
.selectAll('stop')
|
|
4146
|
+
.data([
|
|
4147
|
+
{ offset: '0%', opacity: 1 },
|
|
4148
|
+
{ offset: '80%', opacity: 1 },
|
|
4149
|
+
{ offset: '100%', opacity: 0 },
|
|
4150
|
+
])
|
|
4151
|
+
.enter()
|
|
4152
|
+
.append('stop')
|
|
4153
|
+
.attr('offset', (d) => d.offset)
|
|
4154
|
+
.attr('stop-color', color)
|
|
4155
|
+
.attr('stop-opacity', (d) => d.opacity);
|
|
4073
4156
|
fill = `url(#${gradientId})`;
|
|
4157
|
+
stroke = `url(#${strokeGradientId})`;
|
|
4074
4158
|
}
|
|
4075
4159
|
|
|
4076
4160
|
evG
|
|
@@ -4081,7 +4165,7 @@ export function renderTimeline(
|
|
|
4081
4165
|
.attr('height', BAR_H)
|
|
4082
4166
|
.attr('rx', 4)
|
|
4083
4167
|
.attr('fill', fill)
|
|
4084
|
-
.attr('stroke',
|
|
4168
|
+
.attr('stroke', stroke)
|
|
4085
4169
|
.attr('stroke-width', 2);
|
|
4086
4170
|
|
|
4087
4171
|
if (labelFitsInside) {
|
|
@@ -4157,7 +4241,8 @@ export function renderTimeline(
|
|
|
4157
4241
|
const mainSvg = d3Selection.select(container).select<SVGSVGElement>('svg');
|
|
4158
4242
|
const mainG = mainSvg.select<SVGGElement>('g');
|
|
4159
4243
|
if (!mainSvg.empty() && !mainG.empty()) {
|
|
4160
|
-
|
|
4244
|
+
// Position legend at top, below title
|
|
4245
|
+
const legendY = title ? 50 : 10;
|
|
4161
4246
|
|
|
4162
4247
|
const groupBg = isDark
|
|
4163
4248
|
? mix(palette.surface, palette.bg, 50)
|
|
@@ -5914,7 +5999,7 @@ export async function renderForExport(
|
|
|
5914
5999
|
const exportHeight = isLayout.height + PADDING * 2 + titleOffset;
|
|
5915
6000
|
const container = createExportContainer(exportWidth, exportHeight);
|
|
5916
6001
|
|
|
5917
|
-
renderInitiativeStatus(container, isParsed, isLayout, effectivePalette, theme === 'dark',
|
|
6002
|
+
renderInitiativeStatus(container, isParsed, isLayout, effectivePalette, theme === 'dark', { exportDims: { width: exportWidth, height: exportHeight } });
|
|
5918
6003
|
return finalizeSvgExport(container, theme, effectivePalette, options);
|
|
5919
6004
|
}
|
|
5920
6005
|
|
|
@@ -6003,6 +6088,24 @@ export async function renderForExport(
|
|
|
6003
6088
|
return finalizeSvgExport(container, theme, effectivePalette, options);
|
|
6004
6089
|
}
|
|
6005
6090
|
|
|
6091
|
+
if (detectedType === 'gantt') {
|
|
6092
|
+
const { parseGantt } = await import('./gantt/parser');
|
|
6093
|
+
const { calculateSchedule } = await import('./gantt/calculator');
|
|
6094
|
+
const { renderGantt } = await import('./gantt/renderer');
|
|
6095
|
+
|
|
6096
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
6097
|
+
const ganttParsed = parseGantt(content, effectivePalette);
|
|
6098
|
+
const resolved = calculateSchedule(ganttParsed);
|
|
6099
|
+
if (resolved.tasks.length === 0) return '';
|
|
6100
|
+
|
|
6101
|
+
const EXPORT_W = 1200;
|
|
6102
|
+
const EXPORT_H = 800;
|
|
6103
|
+
const container = createExportContainer(EXPORT_W, EXPORT_H);
|
|
6104
|
+
|
|
6105
|
+
renderGantt(container, resolved, effectivePalette, theme === 'dark', undefined, { width: EXPORT_W, height: EXPORT_H });
|
|
6106
|
+
return finalizeSvgExport(container, theme, effectivePalette, options);
|
|
6107
|
+
}
|
|
6108
|
+
|
|
6006
6109
|
if (detectedType === 'state') {
|
|
6007
6110
|
const { parseState } = await import('./graph/state-parser');
|
|
6008
6111
|
const { layoutGraph } = await import('./graph/layout');
|
package/src/dgmo-router.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { parseC4 } from './c4/parser';
|
|
|
16
16
|
import { looksLikeInitiativeStatus, parseInitiativeStatus } from './initiative-status/parser';
|
|
17
17
|
import { looksLikeSitemap, parseSitemap } from './sitemap/parser';
|
|
18
18
|
import { parseInfra } from './infra/parser';
|
|
19
|
+
import { parseGantt } from './gantt/parser';
|
|
19
20
|
import type { DgmoError } from './diagnostics';
|
|
20
21
|
|
|
21
22
|
/**
|
|
@@ -65,7 +66,7 @@ const VISUALIZATION_TYPES = new Set([
|
|
|
65
66
|
]);
|
|
66
67
|
const DIAGRAM_TYPES = new Set([
|
|
67
68
|
'sequence', 'flowchart', 'class', 'er', 'org', 'kanban', 'c4',
|
|
68
|
-
'initiative-status', 'state', 'sitemap', 'infra',
|
|
69
|
+
'initiative-status', 'state', 'sitemap', 'infra', 'gantt',
|
|
69
70
|
]);
|
|
70
71
|
const EXTENDED_CHART_TYPES = new Set([
|
|
71
72
|
'scatter', 'sankey', 'chord', 'function', 'heatmap', 'funnel',
|
|
@@ -128,6 +129,7 @@ const PARSE_DISPATCH = new Map<string, (content: string) => { diagnostics: DgmoE
|
|
|
128
129
|
['state', (c) => parseState(c)],
|
|
129
130
|
['sitemap', (c) => parseSitemap(c)],
|
|
130
131
|
['infra', (c) => parseInfra(c)],
|
|
132
|
+
['gantt', (c) => parseGantt(c)],
|
|
131
133
|
]);
|
|
132
134
|
|
|
133
135
|
/**
|
package/src/er/parser.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolveColor } from '../colors';
|
|
2
2
|
import type { PaletteColors } from '../palettes';
|
|
3
3
|
import { makeDgmoError, formatDgmoError, suggest } from '../diagnostics';
|
|
4
|
-
import { measureIndent, extractColor, parsePipeMetadata } from '../utils/parsing';
|
|
4
|
+
import { measureIndent, extractColor, parsePipeMetadata, MULTIPLE_PIPE_WARNING } from '../utils/parsing';
|
|
5
5
|
import { matchTagBlockHeading, validateTagValues } from '../utils/tag-groups';
|
|
6
6
|
import type { TagGroup } from '../utils/tag-groups';
|
|
7
7
|
import type {
|
|
@@ -350,8 +350,10 @@ export function parseERDiagram(
|
|
|
350
350
|
// Parse pipe metadata: TableName(color) | key: value, key2: value2
|
|
351
351
|
const pipeStr = tableDecl[3]?.trim();
|
|
352
352
|
if (pipeStr) {
|
|
353
|
-
//
|
|
354
|
-
const
|
|
353
|
+
// Split on additional pipes (treated as commas) and warn if found
|
|
354
|
+
const pipeSegments = pipeStr.split('|');
|
|
355
|
+
const meta = parsePipeMetadata(['', ...pipeSegments], aliasMap,
|
|
356
|
+
() => result.diagnostics.push(makeDgmoError(lineNumber, MULTIPLE_PIPE_WARNING, 'warning')));
|
|
355
357
|
Object.assign(table.metadata, meta);
|
|
356
358
|
}
|
|
357
359
|
|
package/src/er/renderer.ts
CHANGED
|
@@ -224,7 +224,13 @@ export function renderERDiagram(
|
|
|
224
224
|
|
|
225
225
|
const useSemanticColors =
|
|
226
226
|
parsed.tagGroups.length === 0 && layout.nodes.every((n) => !n.color);
|
|
227
|
-
const
|
|
227
|
+
const LEGEND_FIXED_GAP = 8;
|
|
228
|
+
const hasTagLegend = parsed.tagGroups.length > 0;
|
|
229
|
+
const legendReserveH = useSemanticColors
|
|
230
|
+
? LEGEND_HEIGHT + LEGEND_FIXED_GAP
|
|
231
|
+
: hasTagLegend
|
|
232
|
+
? LEGEND_HEIGHT + LEGEND_FIXED_GAP
|
|
233
|
+
: 0;
|
|
228
234
|
|
|
229
235
|
const titleHeight = parsed.title ? 40 : 0;
|
|
230
236
|
const diagramW = layout.width;
|
|
@@ -254,13 +260,13 @@ export function renderERDiagram(
|
|
|
254
260
|
scale = Math.min(MAX_SCALE, scaleX, scaleY);
|
|
255
261
|
const scaledW = diagramW * scale;
|
|
256
262
|
offsetX = (viewW - scaledW) / 2;
|
|
257
|
-
offsetY = titleHeight + DIAGRAM_PADDING;
|
|
263
|
+
offsetY = titleHeight + legendReserveH + DIAGRAM_PADDING;
|
|
258
264
|
} else {
|
|
259
265
|
viewW = naturalW;
|
|
260
266
|
viewH = naturalH;
|
|
261
267
|
scale = 1;
|
|
262
268
|
offsetX = DIAGRAM_PADDING;
|
|
263
|
-
offsetY = titleHeight + DIAGRAM_PADDING;
|
|
269
|
+
offsetY = titleHeight + legendReserveH + DIAGRAM_PADDING;
|
|
264
270
|
}
|
|
265
271
|
|
|
266
272
|
if (viewW <= 0 || viewH <= 0) return;
|
|
@@ -521,7 +527,7 @@ export function renderERDiagram(
|
|
|
521
527
|
}
|
|
522
528
|
|
|
523
529
|
let legendX = DIAGRAM_PADDING;
|
|
524
|
-
let legendY =
|
|
530
|
+
let legendY = DIAGRAM_PADDING + titleHeight;
|
|
525
531
|
|
|
526
532
|
for (const group of parsed.tagGroups) {
|
|
527
533
|
const groupG = legendG.append('g')
|
|
@@ -639,7 +645,7 @@ export function renderERDiagram(
|
|
|
639
645
|
}
|
|
640
646
|
|
|
641
647
|
const legendX = (viewW - totalWidth) / 2;
|
|
642
|
-
const legendY =
|
|
648
|
+
const legendY = DIAGRAM_PADDING + titleHeight;
|
|
643
649
|
|
|
644
650
|
const semanticLegendG = svg
|
|
645
651
|
.append('g')
|