@diagrammo/dgmo 0.8.21 → 0.8.22

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.
Files changed (93) hide show
  1. package/AGENTS.md +2 -1
  2. package/README.md +1 -0
  3. package/dist/cli.cjs +143 -93
  4. package/dist/editor.cjs +17 -3
  5. package/dist/editor.cjs.map +1 -1
  6. package/dist/editor.js +17 -3
  7. package/dist/editor.js.map +1 -1
  8. package/dist/highlight.cjs +12 -2
  9. package/dist/highlight.cjs.map +1 -1
  10. package/dist/highlight.js +12 -2
  11. package/dist/highlight.js.map +1 -1
  12. package/dist/index.cjs +19997 -14886
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +331 -8
  15. package/dist/index.d.ts +331 -8
  16. package/dist/index.js +19984 -14889
  17. package/dist/index.js.map +1 -1
  18. package/docs/guide/chart-sitemap.md +18 -1
  19. package/docs/guide/chart-tech-radar.md +219 -0
  20. package/docs/guide/registry.json +1 -0
  21. package/docs/language-reference.md +116 -6
  22. package/gallery/fixtures/boxes-and-lines.dgmo +10 -3
  23. package/gallery/fixtures/c4-full.dgmo +2 -2
  24. package/gallery/fixtures/cycle/ooda-loop.dgmo +25 -0
  25. package/gallery/fixtures/cycle/pdca-circle-nodes.dgmo +12 -0
  26. package/gallery/fixtures/cycle/pdca-minimal.dgmo +6 -0
  27. package/gallery/fixtures/cycle/sprint-cycle-span.dgmo +17 -0
  28. package/gallery/fixtures/gantt-full.dgmo +2 -2
  29. package/gallery/fixtures/gantt.dgmo +2 -2
  30. package/gallery/fixtures/infra-full.dgmo +2 -2
  31. package/gallery/fixtures/infra.dgmo +1 -1
  32. package/gallery/fixtures/sequence-tags-protocols.dgmo +2 -2
  33. package/gallery/fixtures/sequence-tags.dgmo +2 -2
  34. package/gallery/fixtures/tech-radar-dense.dgmo +77 -0
  35. package/gallery/fixtures/tech-radar.dgmo +36 -0
  36. package/gallery/fixtures/timeline.dgmo +1 -1
  37. package/package.json +1 -1
  38. package/src/boxes-and-lines/layout.ts +309 -33
  39. package/src/boxes-and-lines/parser.ts +86 -10
  40. package/src/boxes-and-lines/renderer.ts +250 -91
  41. package/src/boxes-and-lines/types.ts +1 -1
  42. package/src/c4/layout.ts +8 -8
  43. package/src/c4/parser.ts +35 -2
  44. package/src/c4/renderer.ts +19 -3
  45. package/src/c4/types.ts +1 -0
  46. package/src/chart.ts +14 -7
  47. package/src/completion.ts +227 -0
  48. package/src/cycle/layout.ts +732 -0
  49. package/src/cycle/parser.ts +352 -0
  50. package/src/cycle/renderer.ts +539 -0
  51. package/src/cycle/types.ts +77 -0
  52. package/src/d3.ts +87 -8
  53. package/src/dgmo-router.ts +9 -0
  54. package/src/echarts.ts +7 -4
  55. package/src/editor/dgmo.grammar +5 -1
  56. package/src/editor/dgmo.grammar.js +1 -1
  57. package/src/editor/keywords.ts +14 -0
  58. package/src/gantt/parser.ts +2 -8
  59. package/src/graph/flowchart-parser.ts +15 -21
  60. package/src/graph/state-parser.ts +5 -10
  61. package/src/index.ts +50 -0
  62. package/src/infra/layout.ts +218 -74
  63. package/src/infra/parser.ts +30 -6
  64. package/src/infra/renderer.ts +14 -8
  65. package/src/infra/types.ts +10 -3
  66. package/src/journey-map/layout.ts +386 -0
  67. package/src/journey-map/parser.ts +540 -0
  68. package/src/journey-map/renderer.ts +1456 -0
  69. package/src/journey-map/types.ts +47 -0
  70. package/src/kanban/parser.ts +3 -10
  71. package/src/kanban/renderer.ts +31 -15
  72. package/src/mindmap/parser.ts +12 -18
  73. package/src/mindmap/renderer.ts +14 -13
  74. package/src/mindmap/text-wrap.ts +22 -12
  75. package/src/mindmap/types.ts +2 -2
  76. package/src/org/parser.ts +2 -6
  77. package/src/sequence/renderer.ts +144 -38
  78. package/src/sharing.ts +1 -0
  79. package/src/sitemap/layout.ts +21 -6
  80. package/src/sitemap/parser.ts +26 -17
  81. package/src/sitemap/renderer.ts +34 -0
  82. package/src/sitemap/types.ts +1 -0
  83. package/src/tech-radar/index.ts +14 -0
  84. package/src/tech-radar/interactive.ts +1058 -0
  85. package/src/tech-radar/layout.ts +190 -0
  86. package/src/tech-radar/parser.ts +385 -0
  87. package/src/tech-radar/renderer.ts +1159 -0
  88. package/src/tech-radar/shared.ts +187 -0
  89. package/src/tech-radar/types.ts +81 -0
  90. package/src/utils/description-helpers.ts +33 -0
  91. package/src/utils/legend-layout.ts +3 -1
  92. package/src/utils/parsing.ts +46 -7
  93. package/src/utils/tag-groups.ts +46 -60
@@ -22,6 +22,7 @@ export interface SitemapLayoutNode {
22
22
  metadata: Record<string, string>;
23
23
  /** Original (unfiltered) metadata for tag-based coloring and hover dimming */
24
24
  tagMetadata: Record<string, string>;
25
+ description?: string[];
25
26
  isContainer: boolean;
26
27
  lineNumber: number;
27
28
  color?: string;
@@ -161,23 +162,36 @@ function filterMetadata(
161
162
  return filtered;
162
163
  }
163
164
 
164
- function computeCardWidth(label: string, meta: Record<string, string>): number {
165
+ function computeCardWidth(
166
+ label: string,
167
+ meta: Record<string, string>,
168
+ descLines?: string[]
169
+ ): number {
165
170
  let maxChars = label.length;
166
171
  for (const [key, value] of Object.entries(meta)) {
167
172
  const lineChars = key.length + 2 + value.length;
168
173
  if (lineChars > maxChars) maxChars = lineChars;
169
174
  }
175
+ if (descLines) {
176
+ for (const dl of descLines) {
177
+ if (dl.length > maxChars) maxChars = dl.length;
178
+ }
179
+ }
170
180
  return Math.max(
171
181
  MIN_CARD_WIDTH,
172
182
  Math.ceil(maxChars * CHAR_WIDTH) + CARD_H_PAD * 2
173
183
  );
174
184
  }
175
185
 
176
- function computeCardHeight(meta: Record<string, string>): number {
186
+ function computeCardHeight(
187
+ meta: Record<string, string>,
188
+ descLineCount = 0
189
+ ): number {
177
190
  const metaCount = Object.keys(meta).length;
178
- if (metaCount === 0) return HEADER_HEIGHT + CARD_V_PAD;
191
+ const contentCount = metaCount + descLineCount;
192
+ if (contentCount === 0) return HEADER_HEIGHT + CARD_V_PAD;
179
193
  return (
180
- HEADER_HEIGHT + SEPARATOR_GAP + metaCount * META_LINE_HEIGHT + CARD_V_PAD
194
+ HEADER_HEIGHT + SEPARATOR_GAP + contentCount * META_LINE_HEIGHT + CARD_V_PAD
181
195
  );
182
196
  }
183
197
 
@@ -307,8 +321,8 @@ function flattenNodes(
307
321
  parentPageId,
308
322
  meta,
309
323
  fullMeta: { ...node.metadata },
310
- width: computeCardWidth(node.label, meta),
311
- height: computeCardHeight(meta),
324
+ width: computeCardWidth(node.label, meta, node.description),
325
+ height: computeCardHeight(meta, node.description?.length ?? 0),
312
326
  });
313
327
  // Pages can have children too (nested pages) — this page becomes the parentPageId
314
328
  if (node.children.length > 0) {
@@ -519,6 +533,7 @@ export function layoutSitemap(
519
533
  label: node.label,
520
534
  metadata: flat.meta,
521
535
  tagMetadata: flat.fullMeta,
536
+ description: node.description,
522
537
  isContainer: false,
523
538
  lineNumber: node.lineNumber,
524
539
  color: resolveNodeColor(node, parsed.tagGroups, activeTagGroup ?? null),
@@ -25,6 +25,7 @@ import {
25
25
  ALL_CHART_TYPES,
26
26
  } from '../utils/parsing';
27
27
  import type { SitemapNode, ParsedSitemap } from './types';
28
+ import { tryStripDescriptionKeyword } from '../utils/description-helpers';
28
29
 
29
30
  // ============================================================
30
31
  // Regexes
@@ -370,13 +371,7 @@ export function parseSitemap(
370
371
  : trimmed.match(METADATA_RE);
371
372
 
372
373
  if (containerMatch) {
373
- const rawLabel = containerMatch[1].trim();
374
- const { label, color } = extractColor(
375
- rawLabel,
376
- palette,
377
- result.diagnostics,
378
- lineNumber
379
- );
374
+ const label = containerMatch[1].trim();
380
375
 
381
376
  // Parse optional pipe metadata on the container line
382
377
  const pipeStr = containerMatch[2];
@@ -399,7 +394,6 @@ export function parseSitemap(
399
394
  parentId: null,
400
395
  isContainer: true,
401
396
  lineNumber,
402
- color,
403
397
  };
404
398
 
405
399
  attachNode(node, indent, indentStack, result);
@@ -435,6 +429,17 @@ export function parseSitemap(
435
429
  pushError(lineNumber, 'Metadata has no parent node');
436
430
  }
437
431
  } else {
432
+ // Check if this is a description line for a parent node
433
+ const descResult = tryStripDescriptionKeyword(trimmed);
434
+ if (descResult.isKeyword && indentStack.length > 0) {
435
+ const parent = findParentNode(indent, indentStack);
436
+ if (parent) {
437
+ if (!parent.description) parent.description = [];
438
+ parent.description.push(descResult.text.trim());
439
+ continue;
440
+ }
441
+ }
442
+
438
443
  // Node label — possibly with pipe-delimited metadata
439
444
  const node = parseNodeLabel(
440
445
  trimmed,
@@ -531,31 +536,35 @@ function parseNodeLabel(
531
536
  counter: number,
532
537
  aliasMap: Map<string, string> = new Map(),
533
538
  warnFn?: (line: number, msg: string) => void,
534
- diagnostics?: DgmoError[]
539
+ _diagnostics?: DgmoError[]
535
540
  ): SitemapNode {
536
541
  const segments = trimmed.split('|').map((s) => s.trim());
537
- const rawLabel = segments[0];
538
- const { label, color } = extractColor(
539
- rawLabel,
540
- palette,
541
- diagnostics,
542
- lineNumber
543
- );
542
+ const label = segments[0];
544
543
  const metadata = parsePipeMetadata(
545
544
  segments,
546
545
  aliasMap,
547
546
  warnFn ? () => warnFn(lineNumber, MULTIPLE_PIPE_ERROR) : undefined
548
547
  );
549
548
 
549
+ // Extract description from pipe metadata into dedicated field
550
+ let description: string[] | undefined;
551
+ if ('description' in metadata) {
552
+ const descVal = metadata['description'].trim();
553
+ if (descVal) {
554
+ description = [descVal];
555
+ }
556
+ delete metadata['description'];
557
+ }
558
+
550
559
  return {
551
560
  id: `node-${counter}`,
552
561
  label,
553
562
  metadata,
563
+ description,
554
564
  children: [],
555
565
  parentId: null,
556
566
  isContainer: false,
557
567
  lineNumber,
558
- color,
559
568
  };
560
569
  }
561
570
 
@@ -9,6 +9,8 @@ import type { PaletteColors } from '../palettes';
9
9
  import { mix } from '../palettes/color-utils';
10
10
  import type { ParsedSitemap } from './types';
11
11
  import type { SitemapLayoutResult, SitemapLegendGroup } from './layout';
12
+ import { renderInlineText } from '../utils/inline-markdown';
13
+ import { preprocessDescriptionLine } from '../utils/description-helpers';
12
14
  import {
13
15
  LEGEND_HEIGHT,
14
16
  LEGEND_GROUP_GAP,
@@ -522,6 +524,38 @@ export function renderSitemap(
522
524
  }
523
525
  }
524
526
 
527
+ // Description lines (after metadata)
528
+ if (node.description && node.description.length > 0) {
529
+ const metaCount = Object.keys(node.metadata).length;
530
+ // Separator line before descriptions
531
+ const sepY =
532
+ metaCount > 0
533
+ ? HEADER_HEIGHT + SEPARATOR_GAP + metaCount * META_LINE_HEIGHT
534
+ : HEADER_HEIGHT;
535
+ nodeG
536
+ .append('line')
537
+ .attr('x1', 0)
538
+ .attr('y1', sepY)
539
+ .attr('x2', node.width)
540
+ .attr('y2', sepY)
541
+ .attr('stroke', stroke)
542
+ .attr('stroke-opacity', 0.3);
543
+
544
+ const descStartY =
545
+ HEADER_HEIGHT + SEPARATOR_GAP + metaCount * META_LINE_HEIGHT;
546
+ for (let di = 0; di < node.description.length; di++) {
547
+ const processed = preprocessDescriptionLine(node.description[di]);
548
+ const rowY = descStartY + (di + 1) * META_LINE_HEIGHT - 4;
549
+ const textEl = nodeG
550
+ .append('text')
551
+ .attr('x', 10)
552
+ .attr('y', rowY)
553
+ .attr('fill', palette.textMuted)
554
+ .attr('font-size', META_FONT_SIZE);
555
+ renderInlineText(textEl, processed, palette, META_FONT_SIZE);
556
+ }
557
+ }
558
+
525
559
  // Collapsed accent bar
526
560
  if (!exportDims && node.hiddenCount && node.hiddenCount > 0) {
527
561
  const clipId = `clip-${node.id}`;
@@ -11,6 +11,7 @@ export interface SitemapNode {
11
11
  metadata: Record<string, string>;
12
12
  children: SitemapNode[];
13
13
  parentId: string | null;
14
+ description?: string[];
14
15
  /** True for [Group Name] container nodes */
15
16
  isContainer: boolean;
16
17
  lineNumber: number;
@@ -0,0 +1,14 @@
1
+ export { parseTechRadar } from './parser';
2
+ export { computeRadarLayout, getRadarGeometry } from './layout';
3
+ export { renderTechRadar, renderTechRadarForExport } from './renderer';
4
+ export { renderQuadrantFocus } from './interactive';
5
+ export type {
6
+ ParsedTechRadar,
7
+ TechRadarRing,
8
+ TechRadarQuadrant,
9
+ TechRadarBlip,
10
+ TechRadarLayoutPoint,
11
+ QuadrantPosition,
12
+ BlipTrend,
13
+ TechRadarRenderOptions,
14
+ } from './types';