@diagrammo/dgmo 0.30.0 → 0.32.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/.cursorrules +4 -1
- package/.github/copilot-instructions.md +4 -1
- package/.windsurfrules +4 -1
- package/README.md +21 -3
- package/SKILL.md +4 -1
- package/dist/advanced.cjs +1853 -623
- package/dist/advanced.d.cts +143 -16
- package/dist/advanced.d.ts +143 -16
- package/dist/advanced.js +1846 -623
- package/dist/auto.cjs +1640 -581
- package/dist/auto.js +99 -99
- package/dist/auto.mjs +1640 -581
- package/dist/cli.cjs +148 -147
- package/dist/index.cjs +1643 -662
- package/dist/index.js +1643 -662
- package/docs/ai-integration.md +4 -1
- package/docs/language-reference.md +282 -27
- package/gallery/fixtures/boxes-and-lines.dgmo +2 -2
- package/gallery/fixtures/c4-full.dgmo +4 -5
- package/gallery/fixtures/c4.dgmo +2 -3
- package/package.json +7 -1
- package/src/advanced.ts +10 -0
- package/src/boxes-and-lines/focus.ts +257 -0
- package/src/boxes-and-lines/layout-search.ts +345 -65
- package/src/boxes-and-lines/layout.ts +11 -1
- package/src/boxes-and-lines/parser.ts +97 -4
- package/src/boxes-and-lines/renderer.ts +111 -8
- package/src/boxes-and-lines/types.ts +9 -0
- package/src/c4/parser.ts +8 -7
- package/src/c4/renderer.ts +7 -5
- package/src/chart-type-registry.ts +129 -4
- package/src/chart-types.ts +3 -3
- package/src/chart.ts +18 -1
- package/src/class/renderer.ts +4 -2
- package/src/cli-banner.ts +107 -0
- package/src/cli.ts +13 -0
- package/src/colors.ts +247 -2
- package/src/cycle/parser.ts +2 -7
- package/src/d3.ts +67 -54
- package/src/diagnostics.ts +17 -0
- package/src/dimensions.ts +9 -13
- package/src/echarts.ts +42 -14
- package/src/er/parser.ts +6 -1
- package/src/er/renderer.ts +4 -2
- package/src/gantt/parser.ts +44 -7
- package/src/graph/flowchart-parser.ts +77 -3
- package/src/graph/flowchart-renderer.ts +4 -2
- package/src/graph/state-renderer.ts +6 -4
- package/src/infra/parser.ts +80 -0
- package/src/infra/renderer.ts +8 -4
- package/src/journey-map/parser.ts +23 -8
- package/src/journey-map/renderer.ts +1 -1
- package/src/kanban/parser.ts +8 -7
- package/src/kanban/renderer.ts +1 -1
- package/src/map/context-labels.ts +134 -27
- package/src/map/geo.ts +10 -2
- package/src/map/layout.ts +259 -4
- package/src/map/parser.ts +2 -0
- package/src/map/renderer.ts +49 -25
- package/src/map/resolver.ts +68 -19
- package/src/mindmap/parser.ts +15 -7
- package/src/mindmap/renderer.ts +55 -15
- package/src/org/parser.ts +8 -7
- package/src/org/renderer.ts +89 -127
- package/src/palettes/color-utils.ts +19 -4
- package/src/palettes/index.ts +1 -0
- package/src/pert/renderer.ts +15 -10
- package/src/pyramid/parser.ts +2 -7
- package/src/quadrant/renderer.ts +2 -2
- package/src/raci/parser.ts +2 -7
- package/src/raci/renderer.ts +5 -5
- package/src/ring/parser.ts +2 -7
- package/src/sequence/parser.ts +18 -7
- package/src/sequence/renderer.ts +4 -4
- package/src/sitemap/parser.ts +8 -7
- package/src/sitemap/renderer.ts +37 -39
- package/src/tech-radar/parser.ts +2 -7
- package/src/timeline/renderer.ts +15 -5
- package/src/utils/card.ts +183 -0
- package/src/utils/parsing.ts +13 -1
- package/src/utils/scaling.ts +38 -81
- package/src/utils/tag-groups.ts +48 -10
- package/src/utils/visual-conventions.ts +61 -0
- package/src/visualizations/parse.ts +6 -1
- package/src/wireframe/parser.ts +6 -1
package/src/d3.ts
CHANGED
|
@@ -83,6 +83,8 @@ interface ExportContext {
|
|
|
83
83
|
viewState: import('./sharing').CompactViewState | undefined;
|
|
84
84
|
options: RenderForExportOptions | undefined;
|
|
85
85
|
exportMode: boolean;
|
|
86
|
+
/** Whether the theme is dark, resolved once in renderForExport (Story 111.2). */
|
|
87
|
+
isDark: boolean;
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
type DiagramExportHandler = (ctx: ExportContext) => Promise<string>;
|
|
@@ -145,6 +147,7 @@ export async function renderForExport(
|
|
|
145
147
|
viewState,
|
|
146
148
|
options,
|
|
147
149
|
exportMode,
|
|
150
|
+
isDark: theme === 'dark',
|
|
148
151
|
};
|
|
149
152
|
// Generic dispatch: every structured diagram AND every D3 visualization now
|
|
150
153
|
// resolves through the handler table. Only `sequence` — which has no chart
|
|
@@ -155,14 +158,24 @@ export async function renderForExport(
|
|
|
155
158
|
return (handler ?? exportVisualization)(ctx);
|
|
156
159
|
}
|
|
157
160
|
|
|
161
|
+
/**
|
|
162
|
+
* The tag-group override threaded into every handler: an explicit viewState tag
|
|
163
|
+
* (app toggle / share link) wins, else the options.tagGroup fallback. Resolved
|
|
164
|
+
* from ctx so handlers stop repeating the viewState/options fallback
|
|
165
|
+
* shape (Story 111.2).
|
|
166
|
+
*/
|
|
167
|
+
function ctxTagOverride(ctx: ExportContext): string | undefined {
|
|
168
|
+
return ctx.viewState?.tag ?? ctx.options?.tagGroup;
|
|
169
|
+
}
|
|
170
|
+
|
|
158
171
|
async function exportOrg(ctx: ExportContext): Promise<string> {
|
|
159
|
-
const { content, theme, palette, viewState,
|
|
172
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
160
173
|
const { parseOrg } = await import('./org/parser');
|
|
161
174
|
const { layoutOrg } = await import('./org/layout');
|
|
162
175
|
const { collapseOrgTree } = await import('./org/collapse');
|
|
163
176
|
const { renderOrg } = await import('./org/renderer');
|
|
164
177
|
|
|
165
|
-
const isDark =
|
|
178
|
+
const isDark = ctx.isDark;
|
|
166
179
|
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
167
180
|
|
|
168
181
|
const orgParsed = parseOrg(content, effectivePalette);
|
|
@@ -173,7 +186,7 @@ async function exportOrg(ctx: ExportContext): Promise<string> {
|
|
|
173
186
|
const activeTagGroup = resolveActiveTagGroup(
|
|
174
187
|
orgParsed.tagGroups,
|
|
175
188
|
orgParsed.options['active-tag'],
|
|
176
|
-
|
|
189
|
+
ctxTagOverride(ctx)
|
|
177
190
|
);
|
|
178
191
|
const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : undefined;
|
|
179
192
|
|
|
@@ -213,13 +226,13 @@ async function exportOrg(ctx: ExportContext): Promise<string> {
|
|
|
213
226
|
}
|
|
214
227
|
|
|
215
228
|
async function exportSitemap(ctx: ExportContext): Promise<string> {
|
|
216
|
-
const { content, theme, palette, viewState,
|
|
229
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
217
230
|
const { parseSitemap } = await import('./sitemap/parser');
|
|
218
231
|
const { layoutSitemap } = await import('./sitemap/layout');
|
|
219
232
|
const { collapseSitemapTree } = await import('./sitemap/collapse');
|
|
220
233
|
const { renderSitemap } = await import('./sitemap/renderer');
|
|
221
234
|
|
|
222
|
-
const isDark =
|
|
235
|
+
const isDark = ctx.isDark;
|
|
223
236
|
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
224
237
|
|
|
225
238
|
const sitemapParsed = parseSitemap(content, effectivePalette);
|
|
@@ -230,7 +243,7 @@ async function exportSitemap(ctx: ExportContext): Promise<string> {
|
|
|
230
243
|
const activeTagGroup = resolveActiveTagGroup(
|
|
231
244
|
sitemapParsed.tagGroups,
|
|
232
245
|
sitemapParsed.options['active-tag'],
|
|
233
|
-
|
|
246
|
+
ctxTagOverride(ctx)
|
|
234
247
|
);
|
|
235
248
|
const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : undefined;
|
|
236
249
|
|
|
@@ -269,7 +282,7 @@ async function exportSitemap(ctx: ExportContext): Promise<string> {
|
|
|
269
282
|
}
|
|
270
283
|
|
|
271
284
|
async function exportKanban(ctx: ExportContext): Promise<string> {
|
|
272
|
-
const { content, theme, palette, viewState,
|
|
285
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
273
286
|
const { parseKanban } = await import('./kanban/parser');
|
|
274
287
|
const { renderKanban } = await import('./kanban/renderer');
|
|
275
288
|
|
|
@@ -289,11 +302,11 @@ async function exportKanban(ctx: ExportContext): Promise<string> {
|
|
|
289
302
|
const kanbanCollapsedColumns = viewState?.cc
|
|
290
303
|
? new Set(viewState.cc)
|
|
291
304
|
: undefined;
|
|
292
|
-
renderKanban(container, kanbanParsed, effectivePalette,
|
|
305
|
+
renderKanban(container, kanbanParsed, effectivePalette, ctx.isDark, {
|
|
293
306
|
activeTagGroup: resolveActiveTagGroup(
|
|
294
307
|
kanbanParsed.tagGroups,
|
|
295
308
|
kanbanParsed.options['active-tag'],
|
|
296
|
-
|
|
309
|
+
ctxTagOverride(ctx)
|
|
297
310
|
),
|
|
298
311
|
currentSwimlaneGroup: viewState?.swim ?? null,
|
|
299
312
|
...(kanbanCollapsedLanes !== undefined && {
|
|
@@ -330,7 +343,7 @@ async function exportClass(ctx: ExportContext): Promise<string> {
|
|
|
330
343
|
classParsed,
|
|
331
344
|
classLayout,
|
|
332
345
|
effectivePalette,
|
|
333
|
-
|
|
346
|
+
ctx.isDark,
|
|
334
347
|
undefined,
|
|
335
348
|
{ width: exportWidth, height: exportHeight },
|
|
336
349
|
undefined,
|
|
@@ -340,7 +353,7 @@ async function exportClass(ctx: ExportContext): Promise<string> {
|
|
|
340
353
|
}
|
|
341
354
|
|
|
342
355
|
async function exportEr(ctx: ExportContext): Promise<string> {
|
|
343
|
-
const { content, theme, palette, viewState,
|
|
356
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
344
357
|
const { parseERDiagram } = await import('./er/parser');
|
|
345
358
|
const { layoutERDiagram } = await import('./er/layout');
|
|
346
359
|
const { renderERDiagram } = await import('./er/renderer');
|
|
@@ -361,13 +374,13 @@ async function exportEr(ctx: ExportContext): Promise<string> {
|
|
|
361
374
|
erParsed,
|
|
362
375
|
erLayout,
|
|
363
376
|
effectivePalette,
|
|
364
|
-
|
|
377
|
+
ctx.isDark,
|
|
365
378
|
undefined,
|
|
366
379
|
{ width: exportWidth, height: exportHeight },
|
|
367
380
|
resolveActiveTagGroup(
|
|
368
381
|
erParsed.tagGroups,
|
|
369
382
|
erParsed.options['active-tag'],
|
|
370
|
-
|
|
383
|
+
ctxTagOverride(ctx)
|
|
371
384
|
),
|
|
372
385
|
viewState?.sem,
|
|
373
386
|
exportMode
|
|
@@ -376,7 +389,7 @@ async function exportEr(ctx: ExportContext): Promise<string> {
|
|
|
376
389
|
}
|
|
377
390
|
|
|
378
391
|
async function exportBoxesAndLines(ctx: ExportContext): Promise<string> {
|
|
379
|
-
const { content, theme, palette, viewState,
|
|
392
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
380
393
|
const { parseBoxesAndLines } = await import('./boxes-and-lines/parser');
|
|
381
394
|
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
382
395
|
const blParsed = parseBoxesAndLines(content, effectivePalette);
|
|
@@ -401,13 +414,13 @@ async function exportBoxesAndLines(ctx: ExportContext): Promise<string> {
|
|
|
401
414
|
const exportHeight = blLayout.height + PADDING * 2 + titleOffset;
|
|
402
415
|
const container = createExportContainer(exportWidth, exportHeight);
|
|
403
416
|
|
|
404
|
-
const blActiveTagGroup =
|
|
417
|
+
const blActiveTagGroup = ctxTagOverride(ctx);
|
|
405
418
|
renderBoxesAndLinesForExport(
|
|
406
419
|
container,
|
|
407
420
|
blParsed,
|
|
408
421
|
blLayout,
|
|
409
422
|
effectivePalette,
|
|
410
|
-
|
|
423
|
+
ctx.isDark,
|
|
411
424
|
{
|
|
412
425
|
exportDims: { width: exportWidth, height: exportHeight },
|
|
413
426
|
...(blActiveTagGroup !== undefined && {
|
|
@@ -423,13 +436,13 @@ async function exportBoxesAndLines(ctx: ExportContext): Promise<string> {
|
|
|
423
436
|
}
|
|
424
437
|
|
|
425
438
|
async function exportMindmap(ctx: ExportContext): Promise<string> {
|
|
426
|
-
const { content, theme, palette, viewState,
|
|
439
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
427
440
|
const { parseMindmap } = await import('./mindmap/parser');
|
|
428
441
|
const { layoutMindmap } = await import('./mindmap/layout');
|
|
429
442
|
const { collapseMindmapTree } = await import('./mindmap/collapse');
|
|
430
443
|
const { renderMindmap } = await import('./mindmap/renderer');
|
|
431
444
|
|
|
432
|
-
const isDark =
|
|
445
|
+
const isDark = ctx.isDark;
|
|
433
446
|
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
434
447
|
|
|
435
448
|
const mmParsed = parseMindmap(content, effectivePalette);
|
|
@@ -439,7 +452,7 @@ async function exportMindmap(ctx: ExportContext): Promise<string> {
|
|
|
439
452
|
const activeTagGroup = resolveActiveTagGroup(
|
|
440
453
|
mmParsed.tagGroups,
|
|
441
454
|
mmParsed.options['active-tag'],
|
|
442
|
-
|
|
455
|
+
ctxTagOverride(ctx)
|
|
443
456
|
);
|
|
444
457
|
const hideDescriptions =
|
|
445
458
|
mmParsed.options['no-descriptions'] === 'true' || viewState?.hd === true;
|
|
@@ -507,7 +520,7 @@ async function exportWireframe(ctx: ExportContext): Promise<string> {
|
|
|
507
520
|
wireframeParsed,
|
|
508
521
|
wireframeLayout,
|
|
509
522
|
effectivePalette,
|
|
510
|
-
|
|
523
|
+
ctx.isDark,
|
|
511
524
|
undefined,
|
|
512
525
|
{ width: exportWidth, height: exportHeight },
|
|
513
526
|
theme
|
|
@@ -516,7 +529,7 @@ async function exportWireframe(ctx: ExportContext): Promise<string> {
|
|
|
516
529
|
}
|
|
517
530
|
|
|
518
531
|
async function exportC4(ctx: ExportContext): Promise<string> {
|
|
519
|
-
const { content, theme, palette, viewState,
|
|
532
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
520
533
|
const { parseC4 } = await import('./c4/parser');
|
|
521
534
|
const {
|
|
522
535
|
layoutC4Context,
|
|
@@ -532,7 +545,7 @@ async function exportC4(ctx: ExportContext): Promise<string> {
|
|
|
532
545
|
|
|
533
546
|
// Container/component-level rendering (viewState fallback for share links)
|
|
534
547
|
const c4Level =
|
|
535
|
-
options?.c4Level ??
|
|
548
|
+
ctx.options?.c4Level ??
|
|
536
549
|
(viewState?.c4l as
|
|
537
550
|
| 'context'
|
|
538
551
|
| 'containers'
|
|
@@ -540,8 +553,8 @@ async function exportC4(ctx: ExportContext): Promise<string> {
|
|
|
540
553
|
| 'deployment'
|
|
541
554
|
| undefined) ??
|
|
542
555
|
'context';
|
|
543
|
-
const c4System = options?.c4System ?? viewState?.c4s;
|
|
544
|
-
const c4Container = options?.c4Container ?? viewState?.c4c;
|
|
556
|
+
const c4System = ctx.options?.c4System ?? viewState?.c4s;
|
|
557
|
+
const c4Container = ctx.options?.c4Container ?? viewState?.c4c;
|
|
545
558
|
|
|
546
559
|
const c4Layout =
|
|
547
560
|
c4Level === 'deployment'
|
|
@@ -572,13 +585,13 @@ async function exportC4(ctx: ExportContext): Promise<string> {
|
|
|
572
585
|
c4Parsed,
|
|
573
586
|
c4Layout,
|
|
574
587
|
effectivePalette,
|
|
575
|
-
|
|
588
|
+
ctx.isDark,
|
|
576
589
|
undefined,
|
|
577
590
|
{ width: exportWidth, height: exportHeight },
|
|
578
591
|
resolveActiveTagGroup(
|
|
579
592
|
c4Parsed.tagGroups,
|
|
580
593
|
c4Parsed.options['active-tag'],
|
|
581
|
-
|
|
594
|
+
ctxTagOverride(ctx)
|
|
582
595
|
),
|
|
583
596
|
exportMode
|
|
584
597
|
);
|
|
@@ -603,7 +616,7 @@ async function exportFlowchart(ctx: ExportContext): Promise<string> {
|
|
|
603
616
|
fcParsed,
|
|
604
617
|
layout,
|
|
605
618
|
effectivePalette,
|
|
606
|
-
|
|
619
|
+
ctx.isDark,
|
|
607
620
|
undefined,
|
|
608
621
|
{ width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
|
|
609
622
|
);
|
|
@@ -611,7 +624,7 @@ async function exportFlowchart(ctx: ExportContext): Promise<string> {
|
|
|
611
624
|
}
|
|
612
625
|
|
|
613
626
|
async function exportInfra(ctx: ExportContext): Promise<string> {
|
|
614
|
-
const { content, theme, palette, viewState
|
|
627
|
+
const { content, theme, palette, viewState } = ctx;
|
|
615
628
|
const { parseInfra } = await import('./infra/parser');
|
|
616
629
|
const { computeInfra } = await import('./infra/compute');
|
|
617
630
|
const { layoutInfra } = await import('./infra/layout');
|
|
@@ -627,7 +640,7 @@ async function exportInfra(ctx: ExportContext): Promise<string> {
|
|
|
627
640
|
const activeTagGroup = resolveActiveTagGroup(
|
|
628
641
|
infraParsed.tagGroups,
|
|
629
642
|
infraParsed.options['active-tag'],
|
|
630
|
-
|
|
643
|
+
ctxTagOverride(ctx)
|
|
631
644
|
);
|
|
632
645
|
|
|
633
646
|
const showInfraTitle =
|
|
@@ -648,7 +661,7 @@ async function exportInfra(ctx: ExportContext): Promise<string> {
|
|
|
648
661
|
container,
|
|
649
662
|
infraLayout,
|
|
650
663
|
effectivePalette,
|
|
651
|
-
|
|
664
|
+
ctx.isDark,
|
|
652
665
|
showInfraTitle ? infraParsed.title : null,
|
|
653
666
|
showInfraTitle ? infraParsed.titleLineNumber : null,
|
|
654
667
|
infraTagGroups,
|
|
@@ -713,7 +726,7 @@ async function exportPert(ctx: ExportContext): Promise<string> {
|
|
|
713
726
|
pertResolved,
|
|
714
727
|
pertLayout,
|
|
715
728
|
effectivePalette,
|
|
716
|
-
|
|
729
|
+
ctx.isDark,
|
|
717
730
|
{
|
|
718
731
|
title: pertParsed.title,
|
|
719
732
|
exportDims: { width: exportW, height: exportH },
|
|
@@ -727,7 +740,7 @@ async function exportPert(ctx: ExportContext): Promise<string> {
|
|
|
727
740
|
}
|
|
728
741
|
|
|
729
742
|
async function exportGantt(ctx: ExportContext): Promise<string> {
|
|
730
|
-
const { content, theme, palette, viewState,
|
|
743
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
731
744
|
const { parseGantt } = await import('./gantt/parser');
|
|
732
745
|
const { calculateSchedule } = await import('./gantt/calculator');
|
|
733
746
|
const { renderGantt } = await import('./gantt/renderer');
|
|
@@ -750,7 +763,7 @@ async function exportGantt(ctx: ExportContext): Promise<string> {
|
|
|
750
763
|
container,
|
|
751
764
|
resolved,
|
|
752
765
|
effectivePalette,
|
|
753
|
-
|
|
766
|
+
ctx.isDark,
|
|
754
767
|
{
|
|
755
768
|
...(ganttCollapsedGroups !== undefined && {
|
|
756
769
|
collapsedGroups: ganttCollapsedGroups,
|
|
@@ -764,7 +777,7 @@ async function exportGantt(ctx: ExportContext): Promise<string> {
|
|
|
764
777
|
currentActiveGroup: resolveActiveTagGroup(
|
|
765
778
|
resolved.tagGroups,
|
|
766
779
|
resolved.options.activeTag ?? undefined,
|
|
767
|
-
|
|
780
|
+
ctxTagOverride(ctx)
|
|
768
781
|
),
|
|
769
782
|
exportMode,
|
|
770
783
|
},
|
|
@@ -791,7 +804,7 @@ async function exportState(ctx: ExportContext): Promise<string> {
|
|
|
791
804
|
stateParsed,
|
|
792
805
|
layout,
|
|
793
806
|
effectivePalette,
|
|
794
|
-
|
|
807
|
+
ctx.isDark,
|
|
795
808
|
undefined,
|
|
796
809
|
{ width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
|
|
797
810
|
);
|
|
@@ -814,7 +827,7 @@ async function exportTechRadar(ctx: ExportContext): Promise<string> {
|
|
|
814
827
|
container,
|
|
815
828
|
radarParsed,
|
|
816
829
|
effectivePalette,
|
|
817
|
-
|
|
830
|
+
ctx.isDark,
|
|
818
831
|
{ width: RADAR_EXPORT_W, height: RADAR_EXPORT_H },
|
|
819
832
|
viewState,
|
|
820
833
|
exportMode
|
|
@@ -837,13 +850,13 @@ async function exportJourneyMap(ctx: ExportContext): Promise<string> {
|
|
|
837
850
|
return '';
|
|
838
851
|
|
|
839
852
|
const jmLayout = layoutJourneyMap(jmParsed, effectivePalette, {
|
|
840
|
-
isDark:
|
|
853
|
+
isDark: ctx.isDark,
|
|
841
854
|
});
|
|
842
855
|
const container = createExportContainer(
|
|
843
856
|
jmLayout.totalWidth,
|
|
844
857
|
jmLayout.totalHeight
|
|
845
858
|
);
|
|
846
|
-
renderJourneyMap(container, jmParsed, effectivePalette,
|
|
859
|
+
renderJourneyMap(container, jmParsed, effectivePalette, ctx.isDark, {
|
|
847
860
|
exportDims: { width: jmLayout.totalWidth, height: jmLayout.totalHeight },
|
|
848
861
|
exportMode,
|
|
849
862
|
});
|
|
@@ -864,7 +877,7 @@ async function exportCycle(ctx: ExportContext): Promise<string> {
|
|
|
864
877
|
container,
|
|
865
878
|
cycleParsed,
|
|
866
879
|
effectivePalette,
|
|
867
|
-
|
|
880
|
+
ctx.isDark,
|
|
868
881
|
{ width: EXPORT_WIDTH, height: EXPORT_HEIGHT },
|
|
869
882
|
viewState,
|
|
870
883
|
exportMode
|
|
@@ -913,7 +926,7 @@ async function exportMap(ctx: ExportContext): Promise<string> {
|
|
|
913
926
|
mapResolved,
|
|
914
927
|
mapData,
|
|
915
928
|
effectivePalette,
|
|
916
|
-
|
|
929
|
+
ctx.isDark,
|
|
917
930
|
dims
|
|
918
931
|
);
|
|
919
932
|
return finalizeSvgExport(container, theme, effectivePalette);
|
|
@@ -933,7 +946,7 @@ async function exportPyramid(ctx: ExportContext): Promise<string> {
|
|
|
933
946
|
container,
|
|
934
947
|
pyramidParsed,
|
|
935
948
|
effectivePalette,
|
|
936
|
-
|
|
949
|
+
ctx.isDark,
|
|
937
950
|
{ width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
|
|
938
951
|
);
|
|
939
952
|
return finalizeSvgExport(container, theme, effectivePalette);
|
|
@@ -953,7 +966,7 @@ async function exportRing(ctx: ExportContext): Promise<string> {
|
|
|
953
966
|
container,
|
|
954
967
|
ringParsed,
|
|
955
968
|
effectivePalette,
|
|
956
|
-
|
|
969
|
+
ctx.isDark,
|
|
957
970
|
{ width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
|
|
958
971
|
);
|
|
959
972
|
return finalizeSvgExport(container, theme, effectivePalette);
|
|
@@ -973,7 +986,7 @@ async function exportRaci(ctx: ExportContext): Promise<string> {
|
|
|
973
986
|
container,
|
|
974
987
|
raciParsed,
|
|
975
988
|
effectivePalette,
|
|
976
|
-
|
|
989
|
+
ctx.isDark,
|
|
977
990
|
{ width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
|
|
978
991
|
);
|
|
979
992
|
return finalizeSvgExport(container, theme, effectivePalette);
|
|
@@ -1006,7 +1019,7 @@ async function exportSlope(ctx: ExportContext): Promise<string> {
|
|
|
1006
1019
|
container,
|
|
1007
1020
|
parsed,
|
|
1008
1021
|
effectivePalette,
|
|
1009
|
-
|
|
1022
|
+
ctx.isDark,
|
|
1010
1023
|
undefined,
|
|
1011
1024
|
dims
|
|
1012
1025
|
);
|
|
@@ -1023,7 +1036,7 @@ async function exportArc(ctx: ExportContext): Promise<string> {
|
|
|
1023
1036
|
container,
|
|
1024
1037
|
parsed,
|
|
1025
1038
|
effectivePalette,
|
|
1026
|
-
|
|
1039
|
+
ctx.isDark,
|
|
1027
1040
|
undefined,
|
|
1028
1041
|
dims
|
|
1029
1042
|
);
|
|
@@ -1031,7 +1044,7 @@ async function exportArc(ctx: ExportContext): Promise<string> {
|
|
|
1031
1044
|
}
|
|
1032
1045
|
|
|
1033
1046
|
async function exportTimeline(ctx: ExportContext): Promise<string> {
|
|
1034
|
-
const { content, theme, palette, viewState,
|
|
1047
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
1035
1048
|
const parsed = parseTimeline(content, palette);
|
|
1036
1049
|
if (parsed.error || parsed.timelineEvents.length === 0) return '';
|
|
1037
1050
|
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
@@ -1040,13 +1053,13 @@ async function exportTimeline(ctx: ExportContext): Promise<string> {
|
|
|
1040
1053
|
container,
|
|
1041
1054
|
parsed,
|
|
1042
1055
|
effectivePalette,
|
|
1043
|
-
|
|
1056
|
+
ctx.isDark,
|
|
1044
1057
|
undefined,
|
|
1045
1058
|
dims,
|
|
1046
1059
|
resolveActiveTagGroup(
|
|
1047
1060
|
parsed.timelineTagGroups,
|
|
1048
1061
|
parsed.timelineActiveTag,
|
|
1049
|
-
|
|
1062
|
+
ctxTagOverride(ctx)
|
|
1050
1063
|
),
|
|
1051
1064
|
viewState?.swim,
|
|
1052
1065
|
undefined,
|
|
@@ -1066,7 +1079,7 @@ async function exportWordcloud(ctx: ExportContext): Promise<string> {
|
|
|
1066
1079
|
container,
|
|
1067
1080
|
parsed,
|
|
1068
1081
|
effectivePalette,
|
|
1069
|
-
|
|
1082
|
+
ctx.isDark,
|
|
1070
1083
|
dims
|
|
1071
1084
|
);
|
|
1072
1085
|
return finalizeSvgExport(container, theme, effectivePalette);
|
|
@@ -1082,7 +1095,7 @@ async function exportVenn(ctx: ExportContext): Promise<string> {
|
|
|
1082
1095
|
container,
|
|
1083
1096
|
parsed,
|
|
1084
1097
|
effectivePalette,
|
|
1085
|
-
|
|
1098
|
+
ctx.isDark,
|
|
1086
1099
|
undefined,
|
|
1087
1100
|
dims
|
|
1088
1101
|
);
|
|
@@ -1099,7 +1112,7 @@ async function exportQuadrant(ctx: ExportContext): Promise<string> {
|
|
|
1099
1112
|
container,
|
|
1100
1113
|
parsed,
|
|
1101
1114
|
effectivePalette,
|
|
1102
|
-
|
|
1115
|
+
ctx.isDark,
|
|
1103
1116
|
undefined,
|
|
1104
1117
|
dims
|
|
1105
1118
|
);
|
|
@@ -1112,7 +1125,7 @@ async function exportQuadrant(ctx: ExportContext): Promise<string> {
|
|
|
1112
1125
|
* D3 visualizations now have their own handler in DIAGRAM_EXPORT_HANDLERS.
|
|
1113
1126
|
*/
|
|
1114
1127
|
async function exportVisualization(ctx: ExportContext): Promise<string> {
|
|
1115
|
-
const { content, theme, palette, viewState
|
|
1128
|
+
const { content, theme, palette, viewState } = ctx;
|
|
1116
1129
|
const parsed = parseVisualization(content, palette);
|
|
1117
1130
|
// Allow sequence diagrams through even if parseVisualization errors —
|
|
1118
1131
|
// sequence is parsed by its own dedicated parser (parseSequenceDgmo)
|
|
@@ -1127,7 +1140,7 @@ async function exportVisualization(ctx: ExportContext): Promise<string> {
|
|
|
1127
1140
|
}
|
|
1128
1141
|
|
|
1129
1142
|
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
1130
|
-
const isDark =
|
|
1143
|
+
const isDark = ctx.isDark;
|
|
1131
1144
|
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
1132
1145
|
|
|
1133
1146
|
const { parseSequenceDgmo } = await import('./sequence/parser');
|
|
@@ -1141,7 +1154,7 @@ async function exportVisualization(ctx: ExportContext): Promise<string> {
|
|
|
1141
1154
|
const collapsedGroups = viewState?.cg
|
|
1142
1155
|
? new Set(viewState.cg.map(Number).filter((n) => Number.isFinite(n)))
|
|
1143
1156
|
: undefined;
|
|
1144
|
-
const seqActiveTagGroup =
|
|
1157
|
+
const seqActiveTagGroup = ctxTagOverride(ctx);
|
|
1145
1158
|
renderSequenceDiagram(
|
|
1146
1159
|
container,
|
|
1147
1160
|
seqParsed,
|
package/src/diagnostics.ts
CHANGED
|
@@ -32,6 +32,23 @@ export function formatDgmoError(err: DgmoError): string {
|
|
|
32
32
|
return err.line > 0 ? `Line ${err.line}: ${err.message}` : err.message;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* The fatal-error accumulator every structured parser re-declared identically
|
|
37
|
+
* (Story 111.4): push a fresh error diagnostic, set `result.error`, and return
|
|
38
|
+
* the partial result so callers can `return fail(line, msg)`. Generic over any
|
|
39
|
+
* result carrying `diagnostics` + `error`.
|
|
40
|
+
*/
|
|
41
|
+
export function makeFail<
|
|
42
|
+
T extends { diagnostics: DgmoError[]; error?: string | null },
|
|
43
|
+
>(result: T): (line: number, message: string) => T {
|
|
44
|
+
return (line: number, message: string): T => {
|
|
45
|
+
const diag = makeDgmoError(line, message);
|
|
46
|
+
result.diagnostics.push(diag);
|
|
47
|
+
result.error = formatDgmoError(diag);
|
|
48
|
+
return result;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
35
52
|
// ============================================================
|
|
36
53
|
// "Did you mean?" Suggestions
|
|
37
54
|
// ============================================================
|
package/src/dimensions.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseDgmo } from './dgmo-router';
|
|
2
|
-
import {
|
|
2
|
+
import type { ContentCounts } from './utils/scaling';
|
|
3
3
|
import { REGISTRY_BY_ID } from './chart-type-registry';
|
|
4
4
|
|
|
5
5
|
export function getMinDimensions(content: string): {
|
|
@@ -9,16 +9,12 @@ export function getMinDimensions(content: string): {
|
|
|
9
9
|
const { chartType } = parseDgmo(content);
|
|
10
10
|
if (!chartType) return { width: 300, height: 200 };
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
content: string,
|
|
21
|
-
chartType: string
|
|
22
|
-
): ContentCounts {
|
|
23
|
-
return REGISTRY_BY_ID.get(chartType)?.measure?.(content) ?? {};
|
|
12
|
+
// Both halves of sizing are owned by the chart type's descriptor in
|
|
13
|
+
// chart-type-registry.ts: `measure` (content → counts) and `minDims`
|
|
14
|
+
// (counts → min size). One lookup serves both. Types without a `minDims`
|
|
15
|
+
// fall back to {300,200} — the previous silent `default:` arm of
|
|
16
|
+
// computeMinDimensions; `measure`-less types contribute `{}` counts.
|
|
17
|
+
const descriptor = REGISTRY_BY_ID.get(chartType);
|
|
18
|
+
const counts: ContentCounts = descriptor?.measure?.(content) ?? {};
|
|
19
|
+
return descriptor?.minDims?.(counts) ?? { width: 300, height: 200 };
|
|
24
20
|
}
|
package/src/echarts.ts
CHANGED
|
@@ -217,6 +217,7 @@ import {
|
|
|
217
217
|
shapeFill,
|
|
218
218
|
hexToHSL,
|
|
219
219
|
hslToHex,
|
|
220
|
+
themeBaseBg,
|
|
220
221
|
} from './palettes/color-utils';
|
|
221
222
|
import { parseChart } from './chart';
|
|
222
223
|
import type { ParsedChart, ChartEra } from './chart';
|
|
@@ -330,7 +331,9 @@ function parseScatterRow(
|
|
|
330
331
|
if (!dataRow || dataRow.values.length < 2) return null;
|
|
331
332
|
const { label: rawLabel, color: pointColor } = extractColor(
|
|
332
333
|
dataRow.label,
|
|
333
|
-
palette
|
|
334
|
+
palette,
|
|
335
|
+
diagnostics,
|
|
336
|
+
lineNumber
|
|
334
337
|
);
|
|
335
338
|
return {
|
|
336
339
|
name: rawLabel,
|
|
@@ -577,11 +580,15 @@ function parseExtendedChartFull(
|
|
|
577
580
|
const targetResolved = resolveSlot(rawTarget!);
|
|
578
581
|
const { label: source, color: sourceColor } = extractColor(
|
|
579
582
|
sourceResolved,
|
|
580
|
-
palette
|
|
583
|
+
palette,
|
|
584
|
+
result.diagnostics,
|
|
585
|
+
lineNumber
|
|
581
586
|
);
|
|
582
587
|
const { label: target, color: targetColor } = extractColor(
|
|
583
588
|
targetResolved,
|
|
584
|
-
palette
|
|
589
|
+
palette,
|
|
590
|
+
result.diagnostics,
|
|
591
|
+
lineNumber
|
|
585
592
|
);
|
|
586
593
|
if (sourceColor || targetColor) {
|
|
587
594
|
if (!result.nodeColors) result.nodeColors = {};
|
|
@@ -653,7 +660,9 @@ function parseExtendedChartFull(
|
|
|
653
660
|
const targetResolved = resolveSlot(dataRow.label);
|
|
654
661
|
const { label: target, color: targetColor } = extractColor(
|
|
655
662
|
targetResolved,
|
|
656
|
-
palette
|
|
663
|
+
palette,
|
|
664
|
+
result.diagnostics,
|
|
665
|
+
lineNumber
|
|
657
666
|
);
|
|
658
667
|
if (targetColor) {
|
|
659
668
|
if (!result.nodeColors) result.nodeColors = {};
|
|
@@ -695,7 +704,9 @@ function parseExtendedChartFull(
|
|
|
695
704
|
const trimmedResolved = resolveSlot(trimmed);
|
|
696
705
|
const { label: nodeName, color: nodeColor } = extractColor(
|
|
697
706
|
trimmedResolved,
|
|
698
|
-
palette
|
|
707
|
+
palette,
|
|
708
|
+
result.diagnostics,
|
|
709
|
+
lineNumber
|
|
699
710
|
);
|
|
700
711
|
if (nodeColor) {
|
|
701
712
|
if (!result.nodeColors) result.nodeColors = {};
|
|
@@ -802,8 +813,15 @@ function parseExtendedChartFull(
|
|
|
802
813
|
min: parseFloat(rangeMatch[1]!),
|
|
803
814
|
max: parseFloat(rangeMatch[2]!),
|
|
804
815
|
};
|
|
816
|
+
continue;
|
|
817
|
+
}
|
|
818
|
+
// The `x` keyword owns ONLY the `x <min> to <max>` range form. A
|
|
819
|
+
// function curve can legitimately start with `x` (e.g. `x / 2: x / 2`);
|
|
820
|
+
// such a colon-bearing line must fall through to the function-curve
|
|
821
|
+
// handler below instead of being silently swallowed here.
|
|
822
|
+
if (!(result.type === 'function' && trimmed.includes(':'))) {
|
|
823
|
+
continue;
|
|
805
824
|
}
|
|
806
|
-
continue;
|
|
807
825
|
}
|
|
808
826
|
}
|
|
809
827
|
|
|
@@ -873,7 +891,9 @@ function parseExtendedChartFull(
|
|
|
873
891
|
if (colonIndex >= 0) {
|
|
874
892
|
const { label: fnName, color: fnColor } = extractColor(
|
|
875
893
|
trimmed.substring(0, colonIndex).trim(),
|
|
876
|
-
palette
|
|
894
|
+
palette,
|
|
895
|
+
result.diagnostics,
|
|
896
|
+
lineNumber
|
|
877
897
|
);
|
|
878
898
|
const fnValue = trimmed.substring(colonIndex + 1).trim();
|
|
879
899
|
if (!result.functions) result.functions = [];
|
|
@@ -928,7 +948,9 @@ function parseExtendedChartFull(
|
|
|
928
948
|
if (dataRow?.values.length === 1) {
|
|
929
949
|
const { label: rawLabel, color: pointColor } = extractColor(
|
|
930
950
|
dataRow.label,
|
|
931
|
-
palette
|
|
951
|
+
palette,
|
|
952
|
+
result.diagnostics,
|
|
953
|
+
lineNumber
|
|
932
954
|
);
|
|
933
955
|
result.data.push({
|
|
934
956
|
label: rawLabel,
|
|
@@ -1064,13 +1086,13 @@ export function buildExtendedChartOption(
|
|
|
1064
1086
|
|
|
1065
1087
|
// Sankey chart has different structure
|
|
1066
1088
|
if (parsed.type === 'sankey') {
|
|
1067
|
-
const bg =
|
|
1089
|
+
const bg = themeBaseBg(palette, isDark);
|
|
1068
1090
|
return buildSankeyOption(parsed, textColor, colors, bg, titleConfig, sc);
|
|
1069
1091
|
}
|
|
1070
1092
|
|
|
1071
1093
|
// Chord diagram
|
|
1072
1094
|
if (parsed.type === 'chord') {
|
|
1073
|
-
const bg =
|
|
1095
|
+
const bg = themeBaseBg(palette, isDark);
|
|
1074
1096
|
return buildChordOption(
|
|
1075
1097
|
parsed,
|
|
1076
1098
|
palette,
|
|
@@ -1119,7 +1141,7 @@ export function buildExtendedChartOption(
|
|
|
1119
1141
|
|
|
1120
1142
|
// Funnel chart
|
|
1121
1143
|
if (parsed.type === 'funnel') {
|
|
1122
|
-
const bg =
|
|
1144
|
+
const bg = themeBaseBg(palette, isDark);
|
|
1123
1145
|
return buildFunnelOption(
|
|
1124
1146
|
parsed,
|
|
1125
1147
|
palette,
|
|
@@ -1960,7 +1982,13 @@ function buildScatterOption(
|
|
|
1960
1982
|
const gridLeft = parsed.ylabel ? 12 : 3;
|
|
1961
1983
|
const gridRight = 4;
|
|
1962
1984
|
const gridBottom = hasCategories ? 15 : parsed.xlabel ? 10 : 3;
|
|
1963
|
-
|
|
1985
|
+
// The categorized legend is hoisted to the TOP in both the app preview and
|
|
1986
|
+
// the static export (the `bottom` legend config below is stripped by both
|
|
1987
|
+
// render paths). So the top inset must reserve room for it — matching the
|
|
1988
|
+
// shared makeChartGrid rule — otherwise the topmost point labels collide
|
|
1989
|
+
// with the legend when there is no title to absorb the space.
|
|
1990
|
+
const hasTitle = !!(parsed.title && !parsed.noTitle);
|
|
1991
|
+
const gridTop = hasTitle ? (hasCategories ? 22 : 15) : hasCategories ? 12 : 5;
|
|
1964
1992
|
|
|
1965
1993
|
// Compute custom label graphics for SSR when labels are enabled
|
|
1966
1994
|
let graphic: Record<string, unknown>[] | undefined;
|
|
@@ -2137,7 +2165,7 @@ function buildHeatmapOption(
|
|
|
2137
2165
|
ctx?: ScaleContext
|
|
2138
2166
|
): EChartsOption {
|
|
2139
2167
|
const sc = ctx ?? ScaleContext.identity();
|
|
2140
|
-
const bg =
|
|
2168
|
+
const bg = themeBaseBg(palette, isDark);
|
|
2141
2169
|
const heatmapRows = parsed.heatmapRows ?? [];
|
|
2142
2170
|
const columns = parsed.columns ?? [];
|
|
2143
2171
|
const rowLabels = heatmapRows.map((r) => r.label);
|
|
@@ -2566,7 +2594,7 @@ export function buildSimpleChartOption(
|
|
|
2566
2594
|
colors,
|
|
2567
2595
|
titleConfig,
|
|
2568
2596
|
} = buildChartCommons(parsed, palette, isDark, sc);
|
|
2569
|
-
const bg =
|
|
2597
|
+
const bg = themeBaseBg(palette, isDark);
|
|
2570
2598
|
|
|
2571
2599
|
switch (parsed.type) {
|
|
2572
2600
|
case 'bar':
|