@diagrammo/dgmo 0.8.20 → 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 (110) hide show
  1. package/AGENTS.md +2 -1
  2. package/README.md +1 -0
  3. package/dist/cli.cjs +142 -90
  4. package/dist/editor.cjs +30 -4
  5. package/dist/editor.cjs.map +1 -1
  6. package/dist/editor.js +30 -4
  7. package/dist/editor.js.map +1 -1
  8. package/dist/highlight.cjs +25 -3
  9. package/dist/highlight.cjs.map +1 -1
  10. package/dist/highlight.js +25 -3
  11. package/dist/highlight.js.map +1 -1
  12. package/dist/index.cjs +21201 -12886
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +646 -89
  15. package/dist/index.d.ts +646 -89
  16. package/dist/index.js +21178 -12889
  17. package/dist/index.js.map +1 -1
  18. package/docs/guide/chart-mindmap.md +198 -0
  19. package/docs/guide/chart-sequence.md +23 -1
  20. package/docs/guide/chart-sitemap.md +18 -1
  21. package/docs/guide/chart-tech-radar.md +219 -0
  22. package/docs/guide/chart-wireframe.md +100 -0
  23. package/docs/guide/index.md +8 -0
  24. package/docs/guide/registry.json +1 -0
  25. package/docs/language-reference.md +249 -4
  26. package/gallery/fixtures/boxes-and-lines.dgmo +10 -3
  27. package/gallery/fixtures/c4-full.dgmo +2 -2
  28. package/gallery/fixtures/cycle/ooda-loop.dgmo +25 -0
  29. package/gallery/fixtures/cycle/pdca-circle-nodes.dgmo +12 -0
  30. package/gallery/fixtures/cycle/pdca-minimal.dgmo +6 -0
  31. package/gallery/fixtures/cycle/sprint-cycle-span.dgmo +17 -0
  32. package/gallery/fixtures/gantt-full.dgmo +2 -2
  33. package/gallery/fixtures/gantt.dgmo +2 -2
  34. package/gallery/fixtures/infra-full.dgmo +2 -2
  35. package/gallery/fixtures/infra.dgmo +1 -1
  36. package/gallery/fixtures/sequence-tags-protocols.dgmo +2 -2
  37. package/gallery/fixtures/sequence-tags.dgmo +2 -2
  38. package/gallery/fixtures/tech-radar-dense.dgmo +77 -0
  39. package/gallery/fixtures/tech-radar.dgmo +36 -0
  40. package/gallery/fixtures/timeline.dgmo +1 -1
  41. package/package.json +1 -1
  42. package/src/boxes-and-lines/collapse.ts +21 -3
  43. package/src/boxes-and-lines/layout.ts +360 -42
  44. package/src/boxes-and-lines/parser.ts +94 -11
  45. package/src/boxes-and-lines/renderer.ts +371 -114
  46. package/src/boxes-and-lines/types.ts +2 -1
  47. package/src/c4/layout.ts +8 -8
  48. package/src/c4/parser.ts +35 -2
  49. package/src/c4/renderer.ts +19 -3
  50. package/src/c4/types.ts +1 -0
  51. package/src/chart.ts +14 -7
  52. package/src/completion.ts +253 -0
  53. package/src/cycle/layout.ts +732 -0
  54. package/src/cycle/parser.ts +352 -0
  55. package/src/cycle/renderer.ts +539 -0
  56. package/src/cycle/types.ts +77 -0
  57. package/src/d3.ts +240 -40
  58. package/src/dgmo-router.ts +15 -0
  59. package/src/echarts.ts +7 -4
  60. package/src/editor/dgmo.grammar +5 -1
  61. package/src/editor/dgmo.grammar.js +1 -1
  62. package/src/editor/keywords.ts +26 -0
  63. package/src/gantt/parser.ts +2 -8
  64. package/src/graph/flowchart-parser.ts +15 -21
  65. package/src/graph/layout.ts +73 -9
  66. package/src/graph/state-collapse.ts +78 -0
  67. package/src/graph/state-parser.ts +5 -10
  68. package/src/graph/state-renderer.ts +139 -34
  69. package/src/index.ts +78 -0
  70. package/src/infra/layout.ts +218 -74
  71. package/src/infra/parser.ts +30 -6
  72. package/src/infra/renderer.ts +14 -8
  73. package/src/infra/types.ts +10 -3
  74. package/src/journey-map/layout.ts +386 -0
  75. package/src/journey-map/parser.ts +540 -0
  76. package/src/journey-map/renderer.ts +1456 -0
  77. package/src/journey-map/types.ts +47 -0
  78. package/src/kanban/parser.ts +3 -10
  79. package/src/kanban/renderer.ts +325 -63
  80. package/src/mindmap/collapse.ts +88 -0
  81. package/src/mindmap/layout.ts +605 -0
  82. package/src/mindmap/parser.ts +373 -0
  83. package/src/mindmap/renderer.ts +544 -0
  84. package/src/mindmap/text-wrap.ts +217 -0
  85. package/src/mindmap/types.ts +55 -0
  86. package/src/org/parser.ts +2 -6
  87. package/src/render.ts +18 -21
  88. package/src/sequence/renderer.ts +273 -56
  89. package/src/sharing.ts +3 -0
  90. package/src/sitemap/layout.ts +56 -18
  91. package/src/sitemap/parser.ts +26 -17
  92. package/src/sitemap/renderer.ts +34 -0
  93. package/src/sitemap/types.ts +1 -0
  94. package/src/tech-radar/index.ts +14 -0
  95. package/src/tech-radar/interactive.ts +1058 -0
  96. package/src/tech-radar/layout.ts +190 -0
  97. package/src/tech-radar/parser.ts +385 -0
  98. package/src/tech-radar/renderer.ts +1159 -0
  99. package/src/tech-radar/shared.ts +187 -0
  100. package/src/tech-radar/types.ts +81 -0
  101. package/src/utils/description-helpers.ts +33 -0
  102. package/src/utils/export-container.ts +3 -2
  103. package/src/utils/legend-d3.ts +1 -0
  104. package/src/utils/legend-layout.ts +5 -3
  105. package/src/utils/parsing.ts +48 -7
  106. package/src/utils/tag-groups.ts +46 -60
  107. package/src/wireframe/layout.ts +460 -0
  108. package/src/wireframe/parser.ts +956 -0
  109. package/src/wireframe/renderer.ts +1293 -0
  110. package/src/wireframe/types.ts +110 -0
@@ -20,7 +20,7 @@ import {
20
20
  OPTION_NOCOLON_RE,
21
21
  } from '../utils/parsing';
22
22
 
23
- const MAX_GROUP_DEPTH = 1;
23
+ const MAX_GROUP_DEPTH = 2;
24
24
 
25
25
  /** Boxes-and-lines requires explicit first line — no heuristic detection. */
26
26
  export function looksLikeBoxesAndLines(_content: string): boolean {
@@ -45,9 +45,9 @@ function measureIndent(line: string): number {
45
45
  function parsePipeMetadata(
46
46
  segment: string,
47
47
  aliasMap: Map<string, string>
48
- ): { metadata: Record<string, string>; description?: string } {
48
+ ): { metadata: Record<string, string>; description?: string[] } {
49
49
  const metadata: Record<string, string> = {};
50
- let description: string | undefined;
50
+ let description: string[] | undefined;
51
51
 
52
52
  const items = segment.split(',');
53
53
  for (const item of items) {
@@ -59,7 +59,7 @@ function parsePipeMetadata(
59
59
  const rawKey = trimmed.slice(0, colonIdx).trim().toLowerCase();
60
60
  const value = trimmed.slice(colonIdx + 1).trim();
61
61
  if (rawKey === 'description') {
62
- description = value;
62
+ description = [value];
63
63
  } else {
64
64
  const resolvedKey = aliasMap.get(rawKey) ?? rawKey;
65
65
  metadata[resolvedKey] = value;
@@ -98,6 +98,25 @@ export function parseBoxesAndLines(content: string): ParsedBoxesAndLines {
98
98
  let lastNodeLabel: string | null = null;
99
99
  let lastSourceIsGroup = false;
100
100
 
101
+ // Description collection state
102
+ let descState: {
103
+ nodeLabel: string;
104
+ indent: number;
105
+ lines: string[];
106
+ edgeSeen: boolean;
107
+ } | null = null;
108
+
109
+ function flushDescription() {
110
+ if (descState && descState.lines.length > 0) {
111
+ const node = result.nodes.find((n) => n.label === descState!.nodeLabel);
112
+ if (node) {
113
+ const existing = node.description ?? [];
114
+ node.description = [...existing, ...descState!.lines];
115
+ }
116
+ }
117
+ descState = null;
118
+ }
119
+
101
120
  // Group stack for nesting
102
121
  interface GroupState {
103
122
  group: BLGroup;
@@ -283,7 +302,51 @@ export function parseBoxesAndLines(content: string): ParsedBoxesAndLines {
283
302
 
284
303
  // Non-indented line closes tag group
285
304
  if (currentTagGroup && indent === 0) {
286
- currentTagGroup = null; // eslint-disable-line no-useless-assignment
305
+ currentTagGroup = null;
306
+ }
307
+
308
+ // Description collection: indented non-edge lines under a node
309
+ if (descState !== null) {
310
+ if (indent > descState.indent) {
311
+ // Check if this is an edge line
312
+ if (trimmed.includes('->') || trimmed.includes('<->')) {
313
+ descState.edgeSeen = true;
314
+ // Fall through to normal edge processing
315
+ } else if (descState.edgeSeen) {
316
+ // Text after edges — emit warning
317
+ result.diagnostics.push(
318
+ makeDgmoError(
319
+ lineNum,
320
+ `Move description lines above edges for '${descState.nodeLabel}' — descriptions must come before -> lines`,
321
+ 'warning'
322
+ )
323
+ );
324
+ continue;
325
+ } else if (
326
+ /^-\s*\w/.test(trimmed) &&
327
+ !trimmed.startsWith('- ') &&
328
+ !trimmed.includes('->') &&
329
+ !trimmed.includes('<->')
330
+ ) {
331
+ // Looks like a malformed edge (e.g. "-Target" but not "- list item")
332
+ result.diagnostics.push(
333
+ makeDgmoError(
334
+ lineNum,
335
+ `Looks like an incomplete edge — did you mean "-> ${trimmed.slice(1).trim()}"?`,
336
+ 'warning'
337
+ )
338
+ );
339
+ descState.lines.push(trimmed);
340
+ continue;
341
+ } else {
342
+ // Collect as description
343
+ descState.lines.push(trimmed);
344
+ continue;
345
+ }
346
+ } else {
347
+ // Indent decreased — flush description
348
+ flushDescription();
349
+ }
287
350
  }
288
351
 
289
352
  // Close groups that are no longer scoped by indent
@@ -358,6 +421,7 @@ export function parseBoxesAndLines(content: string): ParsedBoxesAndLines {
358
421
  if (groupMatch && !trimmed.includes('->') && !trimmed.includes('<->')) {
359
422
  contentStarted = true;
360
423
  currentTagGroup = null;
424
+ flushDescription();
361
425
  const label = groupMatch[1];
362
426
 
363
427
  // Check nesting depth
@@ -387,13 +451,20 @@ export function parseBoxesAndLines(content: string): ParsedBoxesAndLines {
387
451
  }
388
452
  }
389
453
 
454
+ const parentGs = currentGroupState();
390
455
  const group: BLGroup = {
391
456
  label,
392
457
  children: [],
393
458
  lineNumber: lineNum,
394
459
  metadata: groupMeta,
460
+ ...(parentGs ? { parentGroup: parentGs.group.label } : {}),
395
461
  };
396
462
 
463
+ // Add nested group as child of parent group
464
+ if (parentGs && indent > parentGs.indent) {
465
+ parentGs.group.children.push(label);
466
+ }
467
+
397
468
  groupLabels.add(label);
398
469
  groupStack.push({ group, indent, depth: currentDepth });
399
470
  lastNodeLabel = label;
@@ -409,7 +480,18 @@ export function parseBoxesAndLines(content: string): ParsedBoxesAndLines {
409
480
 
410
481
  // Indented shorthand: `-> Target` or `-label-> Target`
411
482
  if (trimmed.startsWith('->') || /^-[^>].*->/.test(trimmed)) {
412
- if (!lastNodeLabel) {
483
+ // If the edge is at group-child indent level, use the containing group
484
+ const gs = currentGroupState();
485
+ const inGroup = gs && indent > gs.indent;
486
+ if (inGroup) {
487
+ const sourcePrefix = `[${gs.group.label}]`;
488
+ edgeText = `${sourcePrefix} ${trimmed}`;
489
+ } else if (lastNodeLabel) {
490
+ const sourcePrefix = lastSourceIsGroup
491
+ ? `[${lastNodeLabel}]`
492
+ : lastNodeLabel;
493
+ edgeText = `${sourcePrefix} ${trimmed}`;
494
+ } else {
413
495
  result.diagnostics.push(
414
496
  makeDgmoError(
415
497
  lineNum,
@@ -419,10 +501,6 @@ export function parseBoxesAndLines(content: string): ParsedBoxesAndLines {
419
501
  );
420
502
  continue;
421
503
  }
422
- const sourcePrefix = lastSourceIsGroup
423
- ? `[${lastNodeLabel}]`
424
- : lastNodeLabel;
425
- edgeText = `${sourcePrefix} ${trimmed}`;
426
504
  }
427
505
 
428
506
  const edge = parseEdgeLine(
@@ -442,6 +520,7 @@ export function parseBoxesAndLines(content: string): ParsedBoxesAndLines {
442
520
  // Node: everything else
443
521
  contentStarted = true;
444
522
  currentTagGroup = null;
523
+ flushDescription(); // Flush any pending description from previous node
445
524
  const node = parseNodeLine(trimmed, lineNum, aliasMap, result.diagnostics);
446
525
  if (!node) {
447
526
  result.diagnostics.push(
@@ -479,8 +558,12 @@ export function parseBoxesAndLines(content: string): ParsedBoxesAndLines {
479
558
  }
480
559
 
481
560
  result.nodes.push(node);
561
+ descState = { nodeLabel: node.label, indent, lines: [], edgeSeen: false };
482
562
  }
483
563
 
564
+ // Flush any remaining description
565
+ flushDescription();
566
+
484
567
  // Close any remaining groups
485
568
  while (groupStack.length > 0) {
486
569
  const gs = groupStack.pop()!;
@@ -555,7 +638,7 @@ function parseNodeLine(
555
638
  _diagnostics: DgmoError[]
556
639
  ): BLNode | null {
557
640
  let metadata: Record<string, string> = {};
558
- let description: string | undefined;
641
+ let description: string[] | undefined;
559
642
 
560
643
  // Split on pipe for metadata
561
644
  const pipeIdx = trimmed.indexOf('|');