@diagrammo/dgmo 0.2.26 → 0.2.28

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 (47) hide show
  1. package/.claude/skills/dgmo-chart/SKILL.md +107 -0
  2. package/.claude/skills/dgmo-flowchart/SKILL.md +61 -0
  3. package/.claude/skills/dgmo-generate/SKILL.md +58 -0
  4. package/.claude/skills/dgmo-sequence/SKILL.md +83 -0
  5. package/.cursorrules +117 -0
  6. package/.github/copilot-instructions.md +117 -0
  7. package/.windsurfrules +117 -0
  8. package/README.md +10 -3
  9. package/dist/cli.cjs +116 -108
  10. package/dist/index.cjs +563 -356
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +39 -24
  13. package/dist/index.d.ts +39 -24
  14. package/dist/index.js +560 -355
  15. package/dist/index.js.map +1 -1
  16. package/docs/ai-integration.md +125 -0
  17. package/docs/language-reference.md +784 -0
  18. package/package.json +10 -3
  19. package/src/c4/parser.ts +90 -74
  20. package/src/c4/renderer.ts +13 -12
  21. package/src/c4/types.ts +6 -4
  22. package/src/chart.ts +3 -2
  23. package/src/class/parser.ts +2 -10
  24. package/src/class/types.ts +1 -1
  25. package/src/cli.ts +135 -19
  26. package/src/d3.ts +1 -1
  27. package/src/dgmo-mermaid.ts +1 -1
  28. package/src/dgmo-router.ts +1 -1
  29. package/src/echarts.ts +33 -13
  30. package/src/er/parser.ts +34 -43
  31. package/src/er/types.ts +1 -1
  32. package/src/graph/flowchart-parser.ts +2 -25
  33. package/src/graph/types.ts +1 -1
  34. package/src/index.ts +5 -0
  35. package/src/initiative-status/parser.ts +57 -11
  36. package/src/initiative-status/types.ts +1 -1
  37. package/src/kanban/parser.ts +32 -53
  38. package/src/kanban/renderer.ts +9 -8
  39. package/src/kanban/types.ts +6 -14
  40. package/src/org/parser.ts +47 -87
  41. package/src/org/resolver.ts +11 -12
  42. package/src/sequence/parser.ts +97 -15
  43. package/src/sequence/renderer.ts +62 -69
  44. package/src/utils/arrows.ts +75 -0
  45. package/src/utils/inline-markdown.ts +75 -0
  46. package/src/utils/parsing.ts +67 -0
  47. package/src/utils/tag-groups.ts +76 -0
package/dist/index.cjs CHANGED
@@ -1549,6 +1549,96 @@ var init_participant_inference = __esm({
1549
1549
  }
1550
1550
  });
1551
1551
 
1552
+ // src/utils/arrows.ts
1553
+ function parseArrow(line7) {
1554
+ const patterns = [
1555
+ { re: BIDI_SYNC_LABELED_RE, async: false, bidirectional: true },
1556
+ { re: BIDI_ASYNC_LABELED_RE, async: true, bidirectional: true },
1557
+ { re: SYNC_LABELED_RE, async: false, bidirectional: false },
1558
+ { re: ASYNC_LABELED_RE, async: true, bidirectional: false }
1559
+ ];
1560
+ for (const { re, async: isAsync, bidirectional } of patterns) {
1561
+ const m = line7.match(re);
1562
+ if (!m) continue;
1563
+ const label = m[2].trim();
1564
+ if (!label) return null;
1565
+ for (const arrow of ARROW_CHARS) {
1566
+ if (label.includes(arrow)) {
1567
+ return {
1568
+ error: "Arrow characters (->, ~>) are not allowed inside labels"
1569
+ };
1570
+ }
1571
+ }
1572
+ return {
1573
+ from: m[1],
1574
+ to: m[3],
1575
+ label,
1576
+ async: isAsync,
1577
+ bidirectional
1578
+ };
1579
+ }
1580
+ return null;
1581
+ }
1582
+ var BIDI_SYNC_LABELED_RE, BIDI_ASYNC_LABELED_RE, SYNC_LABELED_RE, ASYNC_LABELED_RE, ARROW_CHARS;
1583
+ var init_arrows = __esm({
1584
+ "src/utils/arrows.ts"() {
1585
+ "use strict";
1586
+ BIDI_SYNC_LABELED_RE = /^(\S+)\s+<-(.+)->\s+(\S+)$/;
1587
+ BIDI_ASYNC_LABELED_RE = /^(\S+)\s+<~(.+)~>\s+(\S+)$/;
1588
+ SYNC_LABELED_RE = /^(\S+)\s+-(.+)->\s+(\S+)$/;
1589
+ ASYNC_LABELED_RE = /^(\S+)\s+~(.+)~>\s+(\S+)$/;
1590
+ ARROW_CHARS = ["->", "~>", "<->", "<~>"];
1591
+ }
1592
+ });
1593
+
1594
+ // src/utils/parsing.ts
1595
+ function measureIndent(line7) {
1596
+ let indent = 0;
1597
+ for (const ch of line7) {
1598
+ if (ch === " ") indent++;
1599
+ else if (ch === " ") indent += 4;
1600
+ else break;
1601
+ }
1602
+ return indent;
1603
+ }
1604
+ function extractColor(label, palette) {
1605
+ const m = label.match(COLOR_SUFFIX_RE);
1606
+ if (!m) return { label };
1607
+ const colorName = m[1].trim();
1608
+ return {
1609
+ label: label.substring(0, m.index).trim(),
1610
+ color: resolveColor(colorName, palette)
1611
+ };
1612
+ }
1613
+ function parsePipeMetadata(segments, aliasMap = /* @__PURE__ */ new Map()) {
1614
+ const metadata = {};
1615
+ for (let j = 1; j < segments.length; j++) {
1616
+ for (const part of segments[j].split(",")) {
1617
+ const trimmedPart = part.trim();
1618
+ if (!trimmedPart) continue;
1619
+ const colonIdx = trimmedPart.indexOf(":");
1620
+ if (colonIdx > 0) {
1621
+ const rawKey = trimmedPart.substring(0, colonIdx).trim().toLowerCase();
1622
+ const key = aliasMap.get(rawKey) ?? rawKey;
1623
+ const value = trimmedPart.substring(colonIdx + 1).trim();
1624
+ metadata[key] = value;
1625
+ }
1626
+ }
1627
+ }
1628
+ return metadata;
1629
+ }
1630
+ var COLOR_SUFFIX_RE, CHART_TYPE_RE, TITLE_RE, OPTION_RE;
1631
+ var init_parsing = __esm({
1632
+ "src/utils/parsing.ts"() {
1633
+ "use strict";
1634
+ init_colors();
1635
+ COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
1636
+ CHART_TYPE_RE = /^chart\s*:\s*(.+)/i;
1637
+ TITLE_RE = /^title\s*:\s*(.+)/i;
1638
+ OPTION_RE = /^([a-z][a-z0-9-]*)\s*:\s*(.+)$/i;
1639
+ }
1640
+ });
1641
+
1552
1642
  // src/sequence/parser.ts
1553
1643
  var parser_exports = {};
1554
1644
  __export(parser_exports, {
@@ -1590,15 +1680,6 @@ function parseReturnLabel(rawLabel) {
1590
1680
  }
1591
1681
  return { label: rawLabel };
1592
1682
  }
1593
- function measureIndent(line7) {
1594
- let indent = 0;
1595
- for (const ch of line7) {
1596
- if (ch === " ") indent++;
1597
- else if (ch === " ") indent += 4;
1598
- else break;
1599
- }
1600
- return indent;
1601
- }
1602
1683
  function parseSequenceDgmo(content) {
1603
1684
  const result = {
1604
1685
  title: null,
@@ -1820,6 +1901,86 @@ function parseSequenceDgmo(content) {
1820
1901
  pushError(lineNumber, "Use ~> for async messages: A ~> B: message");
1821
1902
  continue;
1822
1903
  }
1904
+ const labeledArrow = parseArrow(trimmed);
1905
+ if (labeledArrow && "error" in labeledArrow) {
1906
+ pushError(lineNumber, labeledArrow.error);
1907
+ continue;
1908
+ }
1909
+ if (labeledArrow) {
1910
+ contentStarted = true;
1911
+ const { from, to, label, async: isAsync2, bidirectional } = labeledArrow;
1912
+ lastMsgFrom = from;
1913
+ const msg = {
1914
+ from,
1915
+ to,
1916
+ label,
1917
+ returnLabel: void 0,
1918
+ lineNumber,
1919
+ ...isAsync2 ? { async: true } : {},
1920
+ ...bidirectional ? { bidirectional: true } : {}
1921
+ };
1922
+ result.messages.push(msg);
1923
+ currentContainer().push(msg);
1924
+ if (!result.participants.some((p) => p.id === from)) {
1925
+ result.participants.push({
1926
+ id: from,
1927
+ label: from,
1928
+ type: inferParticipantType(from),
1929
+ lineNumber
1930
+ });
1931
+ }
1932
+ if (!result.participants.some((p) => p.id === to)) {
1933
+ result.participants.push({
1934
+ id: to,
1935
+ label: to,
1936
+ type: inferParticipantType(to),
1937
+ lineNumber
1938
+ });
1939
+ }
1940
+ continue;
1941
+ }
1942
+ const bidiSyncMatch = trimmed.match(
1943
+ /^(\S+)\s*<->\s*([^\s:]+)\s*(?::\s*(.+))?$/
1944
+ );
1945
+ const bidiAsyncMatch = trimmed.match(
1946
+ /^(\S+)\s*<~>\s*([^\s:]+)\s*(?::\s*(.+))?$/
1947
+ );
1948
+ const bidiMatch = bidiSyncMatch || bidiAsyncMatch;
1949
+ if (bidiMatch) {
1950
+ contentStarted = true;
1951
+ const from = bidiMatch[1];
1952
+ const to = bidiMatch[2];
1953
+ lastMsgFrom = from;
1954
+ const rawLabel = bidiMatch[3]?.trim() || "";
1955
+ const isBidiAsync = !!bidiAsyncMatch;
1956
+ const msg = {
1957
+ from,
1958
+ to,
1959
+ label: rawLabel,
1960
+ lineNumber,
1961
+ bidirectional: true,
1962
+ ...isBidiAsync ? { async: true } : {}
1963
+ };
1964
+ result.messages.push(msg);
1965
+ currentContainer().push(msg);
1966
+ if (!result.participants.some((p) => p.id === from)) {
1967
+ result.participants.push({
1968
+ id: from,
1969
+ label: from,
1970
+ type: inferParticipantType(from),
1971
+ lineNumber
1972
+ });
1973
+ }
1974
+ if (!result.participants.some((p) => p.id === to)) {
1975
+ result.participants.push({
1976
+ id: to,
1977
+ label: to,
1978
+ type: inferParticipantType(to),
1979
+ lineNumber
1980
+ });
1981
+ }
1982
+ continue;
1983
+ }
1823
1984
  let isAsync = false;
1824
1985
  const asyncArrowMatch = trimmed.match(
1825
1986
  /^(\S+)\s*~>\s*([^\s:]+)\s*(?::\s*(.+))?$/
@@ -2057,6 +2218,8 @@ var init_parser = __esm({
2057
2218
  "use strict";
2058
2219
  init_participant_inference();
2059
2220
  init_diagnostics();
2221
+ init_arrows();
2222
+ init_parsing();
2060
2223
  VALID_PARTICIPANT_TYPES = /* @__PURE__ */ new Set([
2061
2224
  "service",
2062
2225
  "database",
@@ -2072,7 +2235,7 @@ var init_parser = __esm({
2072
2235
  POSITION_ONLY_PATTERN = /^(\S+)\s+position\s+(-?\d+)$/i;
2073
2236
  GROUP_HEADING_PATTERN = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
2074
2237
  SECTION_PATTERN = /^==\s+(.+?)(?:\s*==)?\s*$/;
2075
- ARROW_PATTERN = /\S+\s*(?:->|~>)\s*\S+/;
2238
+ ARROW_PATTERN = /\S+\s*(?:<->|<~>|->|~>|-\S+->|~\S+~>|<-\S+->|<~\S+~>)\s*\S+/;
2076
2239
  ARROW_RETURN_PATTERN = /^(.+?)\s*<-\s*(.+)$/;
2077
2240
  UML_RETURN_PATTERN = /^(\w+\([^)]*\))\s*:\s*(.+)$/;
2078
2241
  NOTE_SINGLE = /^note(?:\s+(right|left)\s+of\s+(\S+))?\s*:\s*(.+)$/i;
@@ -2086,27 +2249,9 @@ __export(flowchart_parser_exports, {
2086
2249
  looksLikeFlowchart: () => looksLikeFlowchart,
2087
2250
  parseFlowchart: () => parseFlowchart
2088
2251
  });
2089
- function measureIndent2(line7) {
2090
- let indent = 0;
2091
- for (const ch of line7) {
2092
- if (ch === " ") indent++;
2093
- else if (ch === " ") indent += 4;
2094
- else break;
2095
- }
2096
- return indent;
2097
- }
2098
2252
  function nodeId(shape, label) {
2099
2253
  return `${shape}:${label.toLowerCase().trim()}`;
2100
2254
  }
2101
- function extractColor(label, palette) {
2102
- const m = label.match(COLOR_SUFFIX_RE);
2103
- if (!m) return { label };
2104
- const colorName = m[1].trim();
2105
- return {
2106
- label: label.substring(0, m.index).trim(),
2107
- color: resolveColor(colorName, palette)
2108
- };
2109
- }
2110
2255
  function parseNodeRef(text, palette) {
2111
2256
  const t = text.trim();
2112
2257
  if (!t) return null;
@@ -2221,7 +2366,8 @@ function parseFlowchart(content, palette) {
2221
2366
  nodes: [],
2222
2367
  edges: [],
2223
2368
  options: {},
2224
- diagnostics: []
2369
+ diagnostics: [],
2370
+ error: null
2225
2371
  };
2226
2372
  const fail = (line7, message) => {
2227
2373
  const diag = makeDgmoError(line7, message);
@@ -2323,7 +2469,7 @@ function parseFlowchart(content, palette) {
2323
2469
  const raw = lines[i];
2324
2470
  const trimmed = raw.trim();
2325
2471
  const lineNumber = i + 1;
2326
- const indent = measureIndent2(raw);
2472
+ const indent = measureIndent(raw);
2327
2473
  if (!trimmed) continue;
2328
2474
  if (trimmed.startsWith("//")) continue;
2329
2475
  const groupMatch = trimmed.match(GROUP_HEADING_RE);
@@ -2400,13 +2546,13 @@ function looksLikeFlowchart(content) {
2400
2546
  /->[ \t]*[\[(<\/]/.test(content);
2401
2547
  return shapeNearArrow;
2402
2548
  }
2403
- var COLOR_SUFFIX_RE, GROUP_HEADING_RE;
2549
+ var GROUP_HEADING_RE;
2404
2550
  var init_flowchart_parser = __esm({
2405
2551
  "src/graph/flowchart-parser.ts"() {
2406
2552
  "use strict";
2407
2553
  init_colors();
2408
2554
  init_diagnostics();
2409
- COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
2555
+ init_parsing();
2410
2556
  GROUP_HEADING_RE = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
2411
2557
  }
2412
2558
  });
@@ -2417,15 +2563,6 @@ __export(parser_exports2, {
2417
2563
  looksLikeClassDiagram: () => looksLikeClassDiagram,
2418
2564
  parseClassDiagram: () => parseClassDiagram
2419
2565
  });
2420
- function measureIndent3(line7) {
2421
- let indent = 0;
2422
- for (const ch of line7) {
2423
- if (ch === " ") indent++;
2424
- else if (ch === " ") indent += 4;
2425
- else break;
2426
- }
2427
- return indent;
2428
- }
2429
2566
  function classId(name) {
2430
2567
  return name.toLowerCase().trim();
2431
2568
  }
@@ -2500,7 +2637,8 @@ function parseClassDiagram(content, palette) {
2500
2637
  classes: [],
2501
2638
  relationships: [],
2502
2639
  options: {},
2503
- diagnostics: []
2640
+ diagnostics: [],
2641
+ error: null
2504
2642
  };
2505
2643
  const fail = (line7, message) => {
2506
2644
  const diag = makeDgmoError(line7, message);
@@ -2529,7 +2667,7 @@ function parseClassDiagram(content, palette) {
2529
2667
  const raw = lines[i];
2530
2668
  const trimmed = raw.trim();
2531
2669
  const lineNumber = i + 1;
2532
- const indent = measureIndent3(raw);
2670
+ const indent = measureIndent(raw);
2533
2671
  if (!trimmed) {
2534
2672
  if (indent === 0) currentClass = null;
2535
2673
  continue;
@@ -2649,7 +2787,7 @@ function looksLikeClassDiagram(content) {
2649
2787
  const trimmed = line7.trim();
2650
2788
  if (!trimmed || trimmed.startsWith("//")) continue;
2651
2789
  if (/^(chart|title)\s*:/i.test(trimmed)) continue;
2652
- const indent = measureIndent3(line7);
2790
+ const indent = measureIndent(line7);
2653
2791
  if (indent === 0) {
2654
2792
  if (/^[A-Z][A-Za-z0-9_]*\s+\[(abstract|interface|enum)\]/i.test(trimmed)) {
2655
2793
  hasModifier = true;
@@ -2680,6 +2818,7 @@ var init_parser2 = __esm({
2680
2818
  "use strict";
2681
2819
  init_colors();
2682
2820
  init_diagnostics();
2821
+ init_parsing();
2683
2822
  CLASS_DECL_RE = /^([A-Z][A-Za-z0-9_]*)(?:\s+\[(abstract|interface|enum)\])?(?:\s+\(([^)]+)\))?\s*$/;
2684
2823
  REL_KEYWORD_RE = /^([A-Z][A-Za-z0-9_]*)\s+(extends|implements|contains|has|uses)\s+([A-Z][A-Za-z0-9_]*)(?:\s*:\s*(.+))?$/;
2685
2824
  REL_ARROW_RE = /^([A-Z][A-Za-z0-9_]*)\s+(--\|>|\.\.\|>|\*--|o--|\.\.\>|->)\s+([A-Z][A-Za-z0-9_]*)(?:\s*:\s*(.+))?$/;
@@ -2711,22 +2850,14 @@ __export(parser_exports3, {
2711
2850
  looksLikeERDiagram: () => looksLikeERDiagram,
2712
2851
  parseERDiagram: () => parseERDiagram
2713
2852
  });
2714
- function measureIndent4(line7) {
2715
- let indent = 0;
2716
- for (const ch of line7) {
2717
- if (ch === " ") indent++;
2718
- else if (ch === " ") indent += 4;
2719
- else break;
2720
- }
2721
- return indent;
2722
- }
2723
2853
  function tableId(name) {
2724
2854
  return name.toLowerCase().trim();
2725
2855
  }
2726
2856
  function parseCardSide(token) {
2727
- return CARD_WORD[token.toLowerCase()] ?? null;
2857
+ if (token === "1" || token === "*" || token === "?") return token;
2858
+ return null;
2728
2859
  }
2729
- function parseRelationship(trimmed) {
2860
+ function parseRelationship(trimmed, lineNumber, pushError) {
2730
2861
  const sym = trimmed.match(REL_SYMBOLIC_RE);
2731
2862
  if (sym) {
2732
2863
  const fromCard = parseCardSide(sym[2]);
@@ -2743,17 +2874,13 @@ function parseRelationship(trimmed) {
2743
2874
  }
2744
2875
  const kw = trimmed.match(REL_KEYWORD_RE2);
2745
2876
  if (kw) {
2746
- const fromCard = parseCardSide(kw[2]);
2747
- const toCard = parseCardSide(kw[3]);
2748
- if (fromCard && toCard) {
2749
- return {
2750
- source: kw[1],
2751
- target: kw[4],
2752
- from: fromCard,
2753
- to: toCard,
2754
- label: kw[5]?.trim()
2755
- };
2756
- }
2877
+ const fromSym = KEYWORD_TO_SYMBOL[kw[2].toLowerCase()] ?? kw[2];
2878
+ const toSym = KEYWORD_TO_SYMBOL[kw[3].toLowerCase()] ?? kw[3];
2879
+ pushError(
2880
+ lineNumber,
2881
+ `Use symbolic cardinality (1--*, ?--1, *--*) instead of "${kw[2]}-to-${kw[3]}". Example: ${kw[1]} ${fromSym}--${toSym} ${kw[4]}`
2882
+ );
2883
+ return null;
2757
2884
  }
2758
2885
  return null;
2759
2886
  }
@@ -2773,7 +2900,8 @@ function parseERDiagram(content, palette) {
2773
2900
  options: {},
2774
2901
  tables: [],
2775
2902
  relationships: [],
2776
- diagnostics: []
2903
+ diagnostics: [],
2904
+ error: null
2777
2905
  };
2778
2906
  const fail = (line7, message) => {
2779
2907
  const diag = makeDgmoError(line7, message);
@@ -2781,6 +2909,11 @@ function parseERDiagram(content, palette) {
2781
2909
  result.error = formatDgmoError(diag);
2782
2910
  return result;
2783
2911
  };
2912
+ const pushError = (line7, message) => {
2913
+ const diag = makeDgmoError(line7, message);
2914
+ result.diagnostics.push(diag);
2915
+ if (!result.error) result.error = formatDgmoError(diag);
2916
+ };
2784
2917
  const tableMap = /* @__PURE__ */ new Map();
2785
2918
  let currentTable = null;
2786
2919
  let contentStarted = false;
@@ -2802,7 +2935,7 @@ function parseERDiagram(content, palette) {
2802
2935
  const raw = lines[i];
2803
2936
  const trimmed = raw.trim();
2804
2937
  const lineNumber = i + 1;
2805
- const indent = measureIndent4(raw);
2938
+ const indent = measureIndent(raw);
2806
2939
  if (!trimmed) {
2807
2940
  if (indent === 0) currentTable = null;
2808
2941
  continue;
@@ -2851,7 +2984,7 @@ function parseERDiagram(content, palette) {
2851
2984
  }
2852
2985
  currentTable = null;
2853
2986
  contentStarted = true;
2854
- const rel = parseRelationship(trimmed);
2987
+ const rel = parseRelationship(trimmed, lineNumber, pushError);
2855
2988
  if (rel) {
2856
2989
  getOrCreateTable(rel.source, lineNumber);
2857
2990
  getOrCreateTable(rel.target, lineNumber);
@@ -2904,7 +3037,7 @@ function looksLikeERDiagram(content) {
2904
3037
  const trimmed = line7.trim();
2905
3038
  if (!trimmed || trimmed.startsWith("//")) continue;
2906
3039
  if (/^(chart|title|notation)\s*:/i.test(trimmed)) continue;
2907
- const indent = measureIndent4(line7);
3040
+ const indent = measureIndent(line7);
2908
3041
  if (indent > 0) {
2909
3042
  if (/\[(pk|fk)\]/i.test(trimmed)) {
2910
3043
  hasConstraint = true;
@@ -2913,7 +3046,7 @@ function looksLikeERDiagram(content) {
2913
3046
  if (TABLE_DECL_RE.test(trimmed)) {
2914
3047
  hasTableDecl = true;
2915
3048
  }
2916
- if (REL_SYMBOLIC_RE.test(trimmed) || REL_KEYWORD_RE2.test(trimmed)) {
3049
+ if (REL_SYMBOLIC_RE.test(trimmed)) {
2917
3050
  hasRelationship = true;
2918
3051
  }
2919
3052
  }
@@ -2922,12 +3055,13 @@ function looksLikeERDiagram(content) {
2922
3055
  if (hasRelationship && hasTableDecl && hasConstraint) return true;
2923
3056
  return false;
2924
3057
  }
2925
- var TABLE_DECL_RE, COLUMN_RE, CONSTRAINT_MAP, CARD_WORD, REL_SYMBOLIC_RE, REL_KEYWORD_RE2;
3058
+ var TABLE_DECL_RE, COLUMN_RE, CONSTRAINT_MAP, REL_SYMBOLIC_RE, REL_KEYWORD_RE2, KEYWORD_TO_SYMBOL;
2926
3059
  var init_parser3 = __esm({
2927
3060
  "src/er/parser.ts"() {
2928
3061
  "use strict";
2929
3062
  init_colors();
2930
3063
  init_diagnostics();
3064
+ init_parsing();
2931
3065
  TABLE_DECL_RE = /^([a-zA-Z_]\w*)(?:\s+\(([^)]+)\))?\s*$/;
2932
3066
  COLUMN_RE = /^(\w+)(?:\s*:\s*(\w[\w()]*(?:\s*\[\])?))?(?:\s+\[([^\]]+)\])?\s*$/;
2933
3067
  CONSTRAINT_MAP = {
@@ -2936,16 +3070,13 @@ var init_parser3 = __esm({
2936
3070
  unique: "unique",
2937
3071
  nullable: "nullable"
2938
3072
  };
2939
- CARD_WORD = {
3073
+ REL_SYMBOLIC_RE = /^([a-zA-Z_]\w*)\s+([1*?])\s*-{1,2}\s*([1*?])\s+([a-zA-Z_]\w*)(?:\s*:\s*(.+))?$/;
3074
+ REL_KEYWORD_RE2 = /^([a-zA-Z_]\w*)\s+(one|many|zero)[- ]to[- ](one|many|zero)\s+([a-zA-Z_]\w*)(?:\s*:\s*(.+))?$/i;
3075
+ KEYWORD_TO_SYMBOL = {
2940
3076
  one: "1",
2941
3077
  many: "*",
2942
- "1": "1",
2943
- "*": "*",
2944
- "?": "?",
2945
3078
  zero: "?"
2946
3079
  };
2947
- REL_SYMBOLIC_RE = /^([a-zA-Z_]\w*)\s+([1*?])\s*-{1,2}\s*([1*?])\s+([a-zA-Z_]\w*)(?:\s*:\s*(.+))?$/;
2948
- REL_KEYWORD_RE2 = /^([a-zA-Z_]\w*)\s+(one|many|zero|1|\*|\?)[- ]to[- ](one|many|zero|1|\*|\?)\s+([a-zA-Z_]\w*)(?:\s*:\s*(.+))?$/i;
2949
3080
  }
2950
3081
  });
2951
3082
 
@@ -2955,7 +3086,8 @@ function parseChart(content, palette) {
2955
3086
  const result = {
2956
3087
  type: "bar",
2957
3088
  data: [],
2958
- diagnostics: []
3089
+ diagnostics: [],
3090
+ error: null
2959
3091
  };
2960
3092
  const fail = (line7, message) => {
2961
3093
  const diag = makeDgmoError(line7, message);
@@ -2968,7 +3100,7 @@ function parseChart(content, palette) {
2968
3100
  const lineNumber = i + 1;
2969
3101
  if (!trimmed) continue;
2970
3102
  if (/^#{2,}\s+/.test(trimmed)) continue;
2971
- if (trimmed.startsWith("#") || trimmed.startsWith("//")) continue;
3103
+ if (trimmed.startsWith("//")) continue;
2972
3104
  const colonIndex = trimmed.indexOf(":");
2973
3105
  if (colonIndex === -1) continue;
2974
3106
  const key = trimmed.substring(0, colonIndex).trim().toLowerCase();
@@ -3126,7 +3258,8 @@ function parseEChart(content, palette) {
3126
3258
  const result = {
3127
3259
  type: "scatter",
3128
3260
  data: [],
3129
- diagnostics: []
3261
+ diagnostics: [],
3262
+ error: null
3130
3263
  };
3131
3264
  let currentCategory = "Default";
3132
3265
  for (let i = 0; i < lines.length; i++) {
@@ -3146,7 +3279,7 @@ function parseEChart(content, palette) {
3146
3279
  currentCategory = catName;
3147
3280
  continue;
3148
3281
  }
3149
- if (trimmed.startsWith("#") || trimmed.startsWith("//")) continue;
3282
+ if (trimmed.startsWith("//")) continue;
3150
3283
  const categoryMatch = trimmed.match(/^\[(.+)\]$/);
3151
3284
  if (categoryMatch) {
3152
3285
  currentCategory = categoryMatch[1].trim();
@@ -3775,7 +3908,7 @@ function buildScatterOption(parsed, palette, textColor, axisLineColor, gridOpaci
3775
3908
  }
3776
3909
  },
3777
3910
  grid: {
3778
- left: parsed.ylabel ? "5%" : "3%",
3911
+ left: parsed.ylabel ? "12%" : "3%",
3779
3912
  right: "4%",
3780
3913
  bottom: hasCategories ? "15%" : parsed.xlabel ? "10%" : "3%",
3781
3914
  top: parsed.title ? "15%" : "5%",
@@ -4047,17 +4180,26 @@ function resolveAxisLabels(parsed) {
4047
4180
  yLabel: parsed.ylabel ?? (isHorizontal ? void 0 : parsed.label)
4048
4181
  };
4049
4182
  }
4050
- function makeGridAxis(type, textColor, axisLineColor, splitLineColor, gridOpacity, label, data) {
4183
+ function makeGridAxis(type, textColor, axisLineColor, splitLineColor, gridOpacity, label, data, nameGapOverride) {
4184
+ const defaultGap = type === "value" ? 75 : 40;
4051
4185
  return {
4052
4186
  type,
4053
4187
  ...data && { data },
4054
4188
  axisLine: { lineStyle: { color: axisLineColor } },
4055
- axisLabel: { color: textColor, fontSize: 16, fontFamily: FONT_FAMILY },
4189
+ axisLabel: {
4190
+ color: textColor,
4191
+ fontSize: type === "category" && data ? data.length > 10 ? 11 : data.length > 5 ? 12 : 16 : 16,
4192
+ fontFamily: FONT_FAMILY,
4193
+ ...type === "category" && {
4194
+ interval: 0,
4195
+ formatter: (value) => value.replace(/([a-z])([A-Z])/g, "$1\n$2").replace(/ /g, "\n")
4196
+ }
4197
+ },
4056
4198
  splitLine: { lineStyle: { color: splitLineColor, opacity: gridOpacity } },
4057
4199
  ...label && {
4058
4200
  name: label,
4059
4201
  nameLocation: "middle",
4060
- nameGap: 40,
4202
+ nameGap: nameGapOverride ?? defaultGap,
4061
4203
  nameTextStyle: { color: textColor, fontSize: 18, fontFamily: FONT_FAMILY }
4062
4204
  }
4063
4205
  };
@@ -4112,7 +4254,8 @@ function buildBarOption(parsed, textColor, axisLineColor, splitLineColor, gridOp
4112
4254
  value: d.value,
4113
4255
  itemStyle: { color: d.color ?? colors[i % colors.length] }
4114
4256
  }));
4115
- const categoryAxis = makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? yLabel : xLabel, labels);
4257
+ const hCatGap = isHorizontal && yLabel ? Math.max(40, Math.max(...labels.map((l) => l.length)) * 8 + 16) : void 0;
4258
+ const categoryAxis = makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? yLabel : xLabel, labels, hCatGap);
4116
4259
  const valueAxis = makeGridAxis("value", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? xLabel : yLabel);
4117
4260
  return {
4118
4261
  backgroundColor: "transparent",
@@ -4124,7 +4267,7 @@ function buildBarOption(parsed, textColor, axisLineColor, splitLineColor, gridOp
4124
4267
  axisPointer: { type: "shadow" }
4125
4268
  },
4126
4269
  grid: {
4127
- left: yLabel ? "5%" : "3%",
4270
+ left: yLabel ? "12%" : "3%",
4128
4271
  right: "4%",
4129
4272
  bottom: xLabel ? "10%" : "3%",
4130
4273
  top: parsed.title ? "15%" : "5%",
@@ -4159,7 +4302,7 @@ function buildLineOption(parsed, palette, textColor, axisLineColor, splitLineCol
4159
4302
  axisPointer: { type: "line" }
4160
4303
  },
4161
4304
  grid: {
4162
- left: yLabel ? "5%" : "3%",
4305
+ left: yLabel ? "12%" : "3%",
4163
4306
  right: "4%",
4164
4307
  bottom: xLabel ? "10%" : "3%",
4165
4308
  top: parsed.title ? "15%" : "5%",
@@ -4221,7 +4364,7 @@ function buildMultiLineOption(parsed, textColor, axisLineColor, splitLineColor,
4221
4364
  textStyle: { color: textColor }
4222
4365
  },
4223
4366
  grid: {
4224
- left: yLabel ? "5%" : "3%",
4367
+ left: yLabel ? "12%" : "3%",
4225
4368
  right: "4%",
4226
4369
  bottom: "15%",
4227
4370
  top: parsed.title ? "15%" : "5%",
@@ -4247,7 +4390,7 @@ function buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineCol
4247
4390
  axisPointer: { type: "line" }
4248
4391
  },
4249
4392
  grid: {
4250
- left: yLabel ? "5%" : "3%",
4393
+ left: yLabel ? "12%" : "3%",
4251
4394
  right: "4%",
4252
4395
  bottom: xLabel ? "10%" : "3%",
4253
4396
  top: parsed.title ? "15%" : "5%",
@@ -4444,7 +4587,8 @@ function buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor,
4444
4587
  }
4445
4588
  };
4446
4589
  });
4447
- const categoryAxis = makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? yLabel : xLabel, labels);
4590
+ const hCatGap = isHorizontal && yLabel ? Math.max(40, Math.max(...labels.map((l) => l.length)) * 8 + 16) : void 0;
4591
+ const categoryAxis = makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? yLabel : xLabel, labels, hCatGap);
4448
4592
  const valueAxis = makeGridAxis("value", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? xLabel : yLabel);
4449
4593
  return {
4450
4594
  backgroundColor: "transparent",
@@ -4461,7 +4605,7 @@ function buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor,
4461
4605
  textStyle: { color: textColor }
4462
4606
  },
4463
4607
  grid: {
4464
- left: yLabel ? "5%" : "3%",
4608
+ left: yLabel ? "12%" : "3%",
4465
4609
  right: "4%",
4466
4610
  bottom: "15%",
4467
4611
  top: parsed.title ? "15%" : "5%",
@@ -4540,35 +4684,51 @@ var init_echarts = __esm({
4540
4684
  }
4541
4685
  });
4542
4686
 
4687
+ // src/utils/tag-groups.ts
4688
+ function isTagBlockHeading(trimmed) {
4689
+ return TAG_BLOCK_RE.test(trimmed) || GROUP_HEADING_RE2.test(trimmed);
4690
+ }
4691
+ function matchTagBlockHeading(trimmed) {
4692
+ const tagMatch = trimmed.match(TAG_BLOCK_RE);
4693
+ if (tagMatch) {
4694
+ return {
4695
+ name: tagMatch[1].trim(),
4696
+ alias: tagMatch[2] || void 0,
4697
+ colorHint: tagMatch[3] || void 0,
4698
+ deprecated: false
4699
+ };
4700
+ }
4701
+ const groupMatch = trimmed.match(GROUP_HEADING_RE2);
4702
+ if (groupMatch) {
4703
+ return {
4704
+ name: groupMatch[1].trim(),
4705
+ alias: groupMatch[2] || void 0,
4706
+ colorHint: groupMatch[3] || void 0,
4707
+ deprecated: true
4708
+ };
4709
+ }
4710
+ return null;
4711
+ }
4712
+ var TAG_BLOCK_RE, GROUP_HEADING_RE2;
4713
+ var init_tag_groups = __esm({
4714
+ "src/utils/tag-groups.ts"() {
4715
+ "use strict";
4716
+ TAG_BLOCK_RE = /^tag:\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/i;
4717
+ GROUP_HEADING_RE2 = /^##\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/;
4718
+ }
4719
+ });
4720
+
4543
4721
  // src/org/parser.ts
4544
4722
  var parser_exports4 = {};
4545
4723
  __export(parser_exports4, {
4546
4724
  looksLikeOrg: () => looksLikeOrg,
4547
4725
  parseOrg: () => parseOrg
4548
4726
  });
4549
- function measureIndent5(line7) {
4550
- let indent = 0;
4551
- for (const ch of line7) {
4552
- if (ch === " ") indent++;
4553
- else if (ch === " ") indent += 4;
4554
- else break;
4555
- }
4556
- return indent;
4557
- }
4558
- function extractColor2(label, palette) {
4559
- const m = label.match(COLOR_SUFFIX_RE2);
4560
- if (!m) return { label };
4561
- const colorName = m[1].trim();
4562
- return {
4563
- label: label.substring(0, m.index).trim(),
4564
- color: resolveColor(colorName, palette)
4565
- };
4566
- }
4567
4727
  function looksLikeOrg(content) {
4568
4728
  for (const line7 of content.split("\n")) {
4569
4729
  const trimmed = line7.trim();
4570
4730
  if (!trimmed || trimmed.startsWith("//")) continue;
4571
- if (GROUP_HEADING_RE2.test(trimmed)) return true;
4731
+ if (isTagBlockHeading(trimmed)) return true;
4572
4732
  }
4573
4733
  return false;
4574
4734
  }
@@ -4593,6 +4753,9 @@ function parseOrg(content, palette) {
4593
4753
  result.diagnostics.push(diag);
4594
4754
  if (!result.error) result.error = formatDgmoError(diag);
4595
4755
  };
4756
+ const pushWarning = (line7, message) => {
4757
+ result.diagnostics.push(makeDgmoError(line7, message, "warning"));
4758
+ };
4596
4759
  if (!content || !content.trim()) {
4597
4760
  return fail(0, "No content provided");
4598
4761
  }
@@ -4636,42 +4799,43 @@ function parseOrg(content, palette) {
4636
4799
  continue;
4637
4800
  }
4638
4801
  }
4639
- if (!contentStarted && !currentTagGroup && measureIndent5(line7) === 0) {
4640
- const optMatch = trimmed.match(OPTION_RE);
4641
- if (optMatch && !trimmed.startsWith("##")) {
4642
- const key = optMatch[1].trim().toLowerCase();
4643
- if (key !== "chart" && key !== "title") {
4644
- result.options[key] = optMatch[2].trim();
4645
- continue;
4646
- }
4647
- }
4648
- }
4649
- const groupMatch = trimmed.match(GROUP_HEADING_RE2);
4650
- if (groupMatch) {
4802
+ const tagBlockMatch = matchTagBlockHeading(trimmed);
4803
+ if (tagBlockMatch) {
4651
4804
  if (contentStarted) {
4652
- pushError(lineNumber, "Tag groups (##) must appear before org content");
4805
+ pushError(lineNumber, "Tag groups must appear before org content");
4653
4806
  continue;
4654
4807
  }
4655
- const groupName = groupMatch[1].trim();
4656
- const alias = groupMatch[2] || void 0;
4808
+ if (tagBlockMatch.deprecated) {
4809
+ pushWarning(lineNumber, `'## ${tagBlockMatch.name}' is deprecated for tag groups \u2014 use 'tag: ${tagBlockMatch.name}' instead`);
4810
+ }
4657
4811
  currentTagGroup = {
4658
- name: groupName,
4659
- alias,
4812
+ name: tagBlockMatch.name,
4813
+ alias: tagBlockMatch.alias,
4660
4814
  entries: [],
4661
4815
  lineNumber
4662
4816
  };
4663
- if (alias) {
4664
- aliasMap.set(alias.toLowerCase(), groupName.toLowerCase());
4817
+ if (tagBlockMatch.alias) {
4818
+ aliasMap.set(tagBlockMatch.alias.toLowerCase(), tagBlockMatch.name.toLowerCase());
4665
4819
  }
4666
4820
  result.tagGroups.push(currentTagGroup);
4667
4821
  continue;
4668
4822
  }
4823
+ if (!contentStarted && !currentTagGroup && measureIndent(line7) === 0) {
4824
+ const optMatch = trimmed.match(OPTION_RE);
4825
+ if (optMatch) {
4826
+ const key = optMatch[1].trim().toLowerCase();
4827
+ if (key !== "chart" && key !== "title") {
4828
+ result.options[key] = optMatch[2].trim();
4829
+ continue;
4830
+ }
4831
+ }
4832
+ }
4669
4833
  if (currentTagGroup && !contentStarted) {
4670
- const indent2 = measureIndent5(line7);
4834
+ const indent2 = measureIndent(line7);
4671
4835
  if (indent2 > 0) {
4672
4836
  const isDefault = /\bdefault\s*$/.test(trimmed);
4673
4837
  const entryText = isDefault ? trimmed.replace(/\s+default\s*$/, "").trim() : trimmed;
4674
- const { label, color } = extractColor2(entryText, palette);
4838
+ const { label, color } = extractColor(entryText, palette);
4675
4839
  if (!color) {
4676
4840
  pushError(lineNumber, `Expected 'Value(color)' in tag group '${currentTagGroup.name}'`);
4677
4841
  continue;
@@ -4690,12 +4854,12 @@ function parseOrg(content, palette) {
4690
4854
  }
4691
4855
  contentStarted = true;
4692
4856
  currentTagGroup = null;
4693
- const indent = measureIndent5(line7);
4857
+ const indent = measureIndent(line7);
4694
4858
  const containerMatch = trimmed.match(CONTAINER_RE);
4695
4859
  const metadataMatch = trimmed.includes("|") ? null : trimmed.match(METADATA_RE);
4696
4860
  if (containerMatch) {
4697
4861
  const rawLabel = containerMatch[1].trim();
4698
- const { label, color } = extractColor2(rawLabel, palette);
4862
+ const { label, color } = extractColor(rawLabel, palette);
4699
4863
  containerCounter++;
4700
4864
  const node = {
4701
4865
  id: `container-${containerCounter}`,
@@ -4740,24 +4904,8 @@ function parseOrg(content, palette) {
4740
4904
  function parseNodeLabel(trimmed, _indent, lineNumber, palette, counter, aliasMap = /* @__PURE__ */ new Map()) {
4741
4905
  const segments = trimmed.split("|").map((s) => s.trim());
4742
4906
  let rawLabel = segments[0];
4743
- const { label, color } = extractColor2(rawLabel, palette);
4744
- const metadata = {};
4745
- const metaParts = [];
4746
- for (let j = 1; j < segments.length; j++) {
4747
- for (const part of segments[j].split(",")) {
4748
- const trimmedPart = part.trim();
4749
- if (trimmedPart) metaParts.push(trimmedPart);
4750
- }
4751
- }
4752
- for (const part of metaParts) {
4753
- const colonIdx = part.indexOf(":");
4754
- if (colonIdx > 0) {
4755
- const rawKey = part.substring(0, colonIdx).trim().toLowerCase();
4756
- const key = aliasMap.get(rawKey) ?? rawKey;
4757
- const value = part.substring(colonIdx + 1).trim();
4758
- metadata[key] = value;
4759
- }
4760
- }
4907
+ const { label, color } = extractColor(rawLabel, palette);
4908
+ const metadata = parsePipeMetadata(segments, aliasMap);
4761
4909
  return {
4762
4910
  id: `node-${counter}`,
4763
4911
  label,
@@ -4795,19 +4943,15 @@ function findMetadataParent(indent, indentStack) {
4795
4943
  }
4796
4944
  return null;
4797
4945
  }
4798
- var COLOR_SUFFIX_RE2, GROUP_HEADING_RE2, CONTAINER_RE, METADATA_RE, CHART_TYPE_RE, TITLE_RE, OPTION_RE;
4946
+ var CONTAINER_RE, METADATA_RE;
4799
4947
  var init_parser4 = __esm({
4800
4948
  "src/org/parser.ts"() {
4801
4949
  "use strict";
4802
- init_colors();
4803
4950
  init_diagnostics();
4804
- COLOR_SUFFIX_RE2 = /\(([^)]+)\)\s*$/;
4805
- GROUP_HEADING_RE2 = /^##\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/;
4951
+ init_tag_groups();
4952
+ init_parsing();
4806
4953
  CONTAINER_RE = /^\[([^\]]+)\]$/;
4807
4954
  METADATA_RE = /^([^:]+):\s*(.+)$/;
4808
- CHART_TYPE_RE = /^chart\s*:\s*(.+)/i;
4809
- TITLE_RE = /^title\s*:\s*(.+)/i;
4810
- OPTION_RE = /^([a-z][a-z0-9-]*)\s*:\s*(.+)$/i;
4811
4955
  }
4812
4956
  });
4813
4957
 
@@ -4816,31 +4960,14 @@ var parser_exports5 = {};
4816
4960
  __export(parser_exports5, {
4817
4961
  parseKanban: () => parseKanban
4818
4962
  });
4819
- function measureIndent6(line7) {
4820
- let indent = 0;
4821
- for (const ch of line7) {
4822
- if (ch === " ") indent++;
4823
- else if (ch === " ") indent += 4;
4824
- else break;
4825
- }
4826
- return indent;
4827
- }
4828
- function extractColor3(label, palette) {
4829
- const m = label.match(COLOR_SUFFIX_RE3);
4830
- if (!m) return { label };
4831
- const colorName = m[1].trim();
4832
- return {
4833
- label: label.substring(0, m.index).trim(),
4834
- color: resolveColor(colorName, palette)
4835
- };
4836
- }
4837
4963
  function parseKanban(content, palette) {
4838
4964
  const result = {
4839
4965
  type: "kanban",
4840
4966
  columns: [],
4841
4967
  tagGroups: [],
4842
4968
  options: {},
4843
- diagnostics: []
4969
+ diagnostics: [],
4970
+ error: null
4844
4971
  };
4845
4972
  const fail = (line7, message) => {
4846
4973
  const diag = makeDgmoError(line7, message);
@@ -4873,7 +5000,7 @@ function parseKanban(content, palette) {
4873
5000
  }
4874
5001
  if (trimmed.startsWith("//")) continue;
4875
5002
  if (!contentStarted && !currentTagGroup) {
4876
- const chartMatch = trimmed.match(CHART_TYPE_RE2);
5003
+ const chartMatch = trimmed.match(CHART_TYPE_RE);
4877
5004
  if (chartMatch) {
4878
5005
  const chartType = chartMatch[1].trim().toLowerCase();
4879
5006
  if (chartType !== "kanban") {
@@ -4897,16 +5024,35 @@ function parseKanban(content, palette) {
4897
5024
  }
4898
5025
  }
4899
5026
  if (!contentStarted && !currentTagGroup) {
4900
- const titleMatch = trimmed.match(TITLE_RE2);
5027
+ const titleMatch = trimmed.match(TITLE_RE);
4901
5028
  if (titleMatch) {
4902
5029
  result.title = titleMatch[1].trim();
4903
5030
  result.titleLineNumber = lineNumber;
4904
5031
  continue;
4905
5032
  }
4906
5033
  }
4907
- if (!contentStarted && !currentTagGroup && measureIndent6(line7) === 0) {
4908
- const optMatch = trimmed.match(OPTION_RE2);
4909
- if (optMatch && !trimmed.startsWith("##") && !COLUMN_RE2.test(trimmed)) {
5034
+ if (!contentStarted) {
5035
+ const tagBlockMatch = matchTagBlockHeading(trimmed);
5036
+ if (tagBlockMatch) {
5037
+ if (tagBlockMatch.deprecated) {
5038
+ warn(lineNumber, `'## ${tagBlockMatch.name}' is deprecated for tag groups \u2014 use 'tag: ${tagBlockMatch.name}' instead`);
5039
+ }
5040
+ currentTagGroup = {
5041
+ name: tagBlockMatch.name,
5042
+ alias: tagBlockMatch.alias,
5043
+ entries: [],
5044
+ lineNumber
5045
+ };
5046
+ if (tagBlockMatch.alias) {
5047
+ aliasMap.set(tagBlockMatch.alias.toLowerCase(), tagBlockMatch.name.toLowerCase());
5048
+ }
5049
+ result.tagGroups.push(currentTagGroup);
5050
+ continue;
5051
+ }
5052
+ }
5053
+ if (!contentStarted && !currentTagGroup && measureIndent(line7) === 0) {
5054
+ const optMatch = trimmed.match(OPTION_RE);
5055
+ if (optMatch && !COLUMN_RE2.test(trimmed)) {
4910
5056
  const key = optMatch[1].trim().toLowerCase();
4911
5057
  if (key !== "chart" && key !== "title") {
4912
5058
  result.options[key] = optMatch[2].trim();
@@ -4914,28 +5060,12 @@ function parseKanban(content, palette) {
4914
5060
  }
4915
5061
  }
4916
5062
  }
4917
- const groupMatch = trimmed.match(GROUP_HEADING_RE3);
4918
- if (groupMatch && !contentStarted) {
4919
- const groupName = groupMatch[1].trim();
4920
- const alias = groupMatch[2] || void 0;
4921
- currentTagGroup = {
4922
- name: groupName,
4923
- alias,
4924
- entries: [],
4925
- lineNumber
4926
- };
4927
- if (alias) {
4928
- aliasMap.set(alias.toLowerCase(), groupName.toLowerCase());
4929
- }
4930
- result.tagGroups.push(currentTagGroup);
4931
- continue;
4932
- }
4933
5063
  if (currentTagGroup && !contentStarted) {
4934
- const indent2 = measureIndent6(line7);
5064
+ const indent2 = measureIndent(line7);
4935
5065
  if (indent2 > 0) {
4936
5066
  const isDefault = /\bdefault\s*$/.test(trimmed);
4937
5067
  const entryText = isDefault ? trimmed.replace(/\s+default\s*$/, "").trim() : trimmed;
4938
- const { label, color } = extractColor3(entryText, palette);
5068
+ const { label, color } = extractColor(entryText, palette);
4939
5069
  if (!color) {
4940
5070
  warn(
4941
5071
  lineNumber,
@@ -4969,7 +5099,7 @@ function parseKanban(content, palette) {
4969
5099
  columnCounter++;
4970
5100
  const rawColName = columnMatch[1].trim();
4971
5101
  const wipStr = columnMatch[2];
4972
- const { label: colName, color: colColor } = extractColor3(
5102
+ const { label: colName, color: colColor } = extractColor(
4973
5103
  rawColName,
4974
5104
  palette
4975
5105
  );
@@ -4991,7 +5121,7 @@ function parseKanban(content, palette) {
4991
5121
  warn(lineNumber, "Card line found before any column");
4992
5122
  continue;
4993
5123
  }
4994
- const indent = measureIndent6(line7);
5124
+ const indent = measureIndent(line7);
4995
5125
  if (indent > 0 && currentCard) {
4996
5126
  currentCard.details.push(trimmed);
4997
5127
  currentCard.endLineNumber = lineNumber;
@@ -5056,7 +5186,7 @@ function parseCardLine(trimmed, lineNumber, counter, aliasMap, palette) {
5056
5186
  } else {
5057
5187
  rawTitle = trimmed;
5058
5188
  }
5059
- const { label: title, color } = extractColor3(rawTitle, palette);
5189
+ const { label: title, color } = extractColor(rawTitle, palette);
5060
5190
  const tags = {};
5061
5191
  if (tagsStr) {
5062
5192
  for (const part of tagsStr.split(",")) {
@@ -5079,18 +5209,14 @@ function parseCardLine(trimmed, lineNumber, counter, aliasMap, palette) {
5079
5209
  color
5080
5210
  };
5081
5211
  }
5082
- var CHART_TYPE_RE2, TITLE_RE2, OPTION_RE2, GROUP_HEADING_RE3, COLUMN_RE2, COLOR_SUFFIX_RE3;
5212
+ var COLUMN_RE2;
5083
5213
  var init_parser5 = __esm({
5084
5214
  "src/kanban/parser.ts"() {
5085
5215
  "use strict";
5086
- init_colors();
5087
5216
  init_diagnostics();
5088
- CHART_TYPE_RE2 = /^chart\s*:\s*(.+)/i;
5089
- TITLE_RE2 = /^title\s*:\s*(.+)/i;
5090
- OPTION_RE2 = /^([a-z][a-z0-9-]*)\s*:\s*(.+)$/i;
5091
- GROUP_HEADING_RE3 = /^##\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/;
5217
+ init_tag_groups();
5218
+ init_parsing();
5092
5219
  COLUMN_RE2 = /^==\s+(.+?)\s*(?:\[wip:\s*(\d+)\])?\s*==$/;
5093
- COLOR_SUFFIX_RE3 = /\(([^)]+)\)\s*$/;
5094
5220
  }
5095
5221
  });
5096
5222
 
@@ -5099,24 +5225,6 @@ var parser_exports6 = {};
5099
5225
  __export(parser_exports6, {
5100
5226
  parseC4: () => parseC4
5101
5227
  });
5102
- function measureIndent7(line7) {
5103
- let indent = 0;
5104
- for (const ch of line7) {
5105
- if (ch === " ") indent++;
5106
- else if (ch === " ") indent += 4;
5107
- else break;
5108
- }
5109
- return indent;
5110
- }
5111
- function extractColor4(label, palette) {
5112
- const m = label.match(COLOR_SUFFIX_RE4);
5113
- if (!m) return { label };
5114
- const colorName = m[1].trim();
5115
- return {
5116
- label: label.substring(0, m.index).trim(),
5117
- color: resolveColor(colorName, palette)
5118
- };
5119
- }
5120
5228
  function participantTypeToC4Shape(pType) {
5121
5229
  switch (pType) {
5122
5230
  case "database":
@@ -5173,23 +5281,6 @@ function parseRelationshipBody(body) {
5173
5281
  }
5174
5282
  return { target, label: rest };
5175
5283
  }
5176
- function parsePipeMetadata(segments, aliasMap) {
5177
- const metadata = {};
5178
- for (let j = 1; j < segments.length; j++) {
5179
- for (const part of segments[j].split(",")) {
5180
- const trimmedPart = part.trim();
5181
- if (!trimmedPart) continue;
5182
- const colonIdx = trimmedPart.indexOf(":");
5183
- if (colonIdx > 0) {
5184
- const rawKey = trimmedPart.substring(0, colonIdx).trim().toLowerCase();
5185
- const key = aliasMap.get(rawKey) ?? rawKey;
5186
- const value = trimmedPart.substring(colonIdx + 1).trim();
5187
- metadata[key] = value;
5188
- }
5189
- }
5190
- }
5191
- return metadata;
5192
- }
5193
5284
  function parseC4(content, palette) {
5194
5285
  const result = {
5195
5286
  title: null,
@@ -5235,7 +5326,7 @@ function parseC4(content, palette) {
5235
5326
  }
5236
5327
  if (trimmed.startsWith("//")) continue;
5237
5328
  if (!contentStarted) {
5238
- const chartMatch = trimmed.match(CHART_TYPE_RE3);
5329
+ const chartMatch = trimmed.match(CHART_TYPE_RE);
5239
5330
  if (chartMatch) {
5240
5331
  const chartType = chartMatch[1].trim().toLowerCase();
5241
5332
  if (chartType !== "c4") {
@@ -5249,49 +5340,50 @@ function parseC4(content, palette) {
5249
5340
  }
5250
5341
  }
5251
5342
  if (!contentStarted) {
5252
- const titleMatch = trimmed.match(TITLE_RE3);
5343
+ const titleMatch = trimmed.match(TITLE_RE);
5253
5344
  if (titleMatch) {
5254
5345
  result.title = titleMatch[1].trim();
5255
5346
  result.titleLineNumber = lineNumber;
5256
5347
  continue;
5257
5348
  }
5258
5349
  }
5259
- if (!contentStarted && !currentTagGroup && measureIndent7(line7) === 0) {
5260
- const optMatch = trimmed.match(OPTION_RE3);
5261
- if (optMatch && !trimmed.startsWith("##")) {
5262
- const key = optMatch[1].trim().toLowerCase();
5263
- if (key !== "chart" && key !== "title") {
5264
- result.options[key] = optMatch[2].trim();
5265
- continue;
5266
- }
5267
- }
5268
- }
5269
- const groupMatch = trimmed.match(GROUP_HEADING_RE4);
5270
- if (groupMatch) {
5350
+ const tagBlockMatch = matchTagBlockHeading(trimmed);
5351
+ if (tagBlockMatch) {
5271
5352
  if (contentStarted) {
5272
- pushError(lineNumber, "Tag groups (##) must appear before content");
5353
+ pushError(lineNumber, "Tag groups must appear before content");
5273
5354
  continue;
5274
5355
  }
5275
- const groupName = groupMatch[1].trim();
5276
- const alias = groupMatch[2] || void 0;
5356
+ if (tagBlockMatch.deprecated) {
5357
+ pushError(lineNumber, `'## ${tagBlockMatch.name}' is deprecated for tag groups \u2014 use 'tag: ${tagBlockMatch.name}' instead`, "warning");
5358
+ }
5277
5359
  currentTagGroup = {
5278
- name: groupName,
5279
- alias,
5360
+ name: tagBlockMatch.name,
5361
+ alias: tagBlockMatch.alias,
5280
5362
  entries: [],
5281
5363
  lineNumber
5282
5364
  };
5283
- if (alias) {
5284
- aliasMap.set(alias.toLowerCase(), groupName.toLowerCase());
5365
+ if (tagBlockMatch.alias) {
5366
+ aliasMap.set(tagBlockMatch.alias.toLowerCase(), tagBlockMatch.name.toLowerCase());
5285
5367
  }
5286
5368
  result.tagGroups.push(currentTagGroup);
5287
5369
  continue;
5288
5370
  }
5371
+ if (!contentStarted && !currentTagGroup && measureIndent(line7) === 0) {
5372
+ const optMatch = trimmed.match(OPTION_RE);
5373
+ if (optMatch) {
5374
+ const key = optMatch[1].trim().toLowerCase();
5375
+ if (key !== "chart" && key !== "title") {
5376
+ result.options[key] = optMatch[2].trim();
5377
+ continue;
5378
+ }
5379
+ }
5380
+ }
5289
5381
  if (currentTagGroup && !contentStarted) {
5290
- const indent2 = measureIndent7(line7);
5382
+ const indent2 = measureIndent(line7);
5291
5383
  if (indent2 > 0) {
5292
5384
  const isDefault = /\bdefault\s*$/.test(trimmed);
5293
5385
  const entryText = isDefault ? trimmed.replace(/\s+default\s*$/, "").trim() : trimmed;
5294
- const { label, color } = extractColor4(entryText, palette);
5386
+ const { label, color } = extractColor(entryText, palette);
5295
5387
  if (!color) {
5296
5388
  pushError(
5297
5389
  lineNumber,
@@ -5316,7 +5408,7 @@ function parseC4(content, palette) {
5316
5408
  if (!sawChartType) {
5317
5409
  return fail(lineNumber, 'Missing "chart: c4" header');
5318
5410
  }
5319
- const indent = measureIndent7(line7);
5411
+ const indent = measureIndent(line7);
5320
5412
  if (inDeployment) {
5321
5413
  while (deployStack.length > 0) {
5322
5414
  const top = deployStack[deployStack.length - 1];
@@ -5411,6 +5503,45 @@ function parseC4(content, palette) {
5411
5503
  }
5412
5504
  continue;
5413
5505
  }
5506
+ {
5507
+ const labeledPatterns = [
5508
+ { re: C4_LABELED_BIDI_SYNC_RE, arrowType: "bidirectional" },
5509
+ { re: C4_LABELED_BIDI_ASYNC_RE, arrowType: "bidirectional-async" },
5510
+ { re: C4_LABELED_SYNC_RE, arrowType: "sync" },
5511
+ { re: C4_LABELED_ASYNC_RE, arrowType: "async" }
5512
+ ];
5513
+ let labeledHandled = false;
5514
+ for (const { re, arrowType } of labeledPatterns) {
5515
+ const m = trimmed.match(re);
5516
+ if (!m) continue;
5517
+ const rawLabel = m[1].trim();
5518
+ const targetBody = m[2].trim();
5519
+ if (!rawLabel) break;
5520
+ let label = rawLabel;
5521
+ let technology;
5522
+ const techMatch = rawLabel.match(/\[([^\]]+)\]\s*$/);
5523
+ if (techMatch) {
5524
+ label = rawLabel.substring(0, techMatch.index).trim() || void 0;
5525
+ technology = techMatch[1].trim();
5526
+ }
5527
+ const rel = {
5528
+ target: targetBody,
5529
+ label,
5530
+ technology,
5531
+ arrowType,
5532
+ lineNumber
5533
+ };
5534
+ const parentEntry = findParentElement(indent, stack);
5535
+ if (parentEntry) {
5536
+ parentEntry.element.relationships.push(rel);
5537
+ } else {
5538
+ result.relationships.push(rel);
5539
+ }
5540
+ labeledHandled = true;
5541
+ break;
5542
+ }
5543
+ if (labeledHandled) continue;
5544
+ }
5414
5545
  const relMatch = trimmed.match(RELATIONSHIP_RE);
5415
5546
  if (relMatch) {
5416
5547
  const arrowType = parseArrowType(relMatch[1]);
@@ -5600,22 +5731,22 @@ function validateDeploymentRefs(result, knownNames, pushWarning) {
5600
5731
  }
5601
5732
  walkDeploy(result.deployment);
5602
5733
  }
5603
- var CHART_TYPE_RE3, TITLE_RE3, OPTION_RE3, GROUP_HEADING_RE4, COLOR_SUFFIX_RE4, CONTAINER_RE2, ELEMENT_RE, IS_A_RE, RELATIONSHIP_RE, SECTION_HEADER_RE, CONTAINER_REF_RE, METADATA_RE2, VALID_ELEMENT_TYPES, VALID_SHAPES, ALL_CHART_TYPES;
5734
+ var CONTAINER_RE2, ELEMENT_RE, IS_A_RE, RELATIONSHIP_RE, C4_LABELED_SYNC_RE, C4_LABELED_ASYNC_RE, C4_LABELED_BIDI_SYNC_RE, C4_LABELED_BIDI_ASYNC_RE, SECTION_HEADER_RE, CONTAINER_REF_RE, METADATA_RE2, VALID_ELEMENT_TYPES, VALID_SHAPES, ALL_CHART_TYPES;
5604
5735
  var init_parser6 = __esm({
5605
5736
  "src/c4/parser.ts"() {
5606
5737
  "use strict";
5607
- init_colors();
5608
5738
  init_diagnostics();
5739
+ init_tag_groups();
5609
5740
  init_participant_inference();
5610
- CHART_TYPE_RE3 = /^chart\s*:\s*(.+)/i;
5611
- TITLE_RE3 = /^title\s*:\s*(.+)/i;
5612
- OPTION_RE3 = /^([a-z][a-z0-9-]*)\s*:\s*(.+)$/i;
5613
- GROUP_HEADING_RE4 = /^##\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/;
5614
- COLOR_SUFFIX_RE4 = /\(([^)]+)\)\s*$/;
5741
+ init_parsing();
5615
5742
  CONTAINER_RE2 = /^\[([^\]]+)\]$/;
5616
5743
  ELEMENT_RE = /^(person|system|container|component)\s+(.+)$/i;
5617
5744
  IS_A_RE = /\s+is\s+a(?:n)?\s+(\w+)\s*$/i;
5618
5745
  RELATIONSHIP_RE = /^(<?-?>|<?~?>)\s+(.+)$/;
5746
+ C4_LABELED_SYNC_RE = /^-(.+)->\s+(.+)$/;
5747
+ C4_LABELED_ASYNC_RE = /^~(.+)~>\s+(.+)$/;
5748
+ C4_LABELED_BIDI_SYNC_RE = /^<-(.+)->\s+(.+)$/;
5749
+ C4_LABELED_BIDI_ASYNC_RE = /^<~(.+)~>\s+(.+)$/;
5619
5750
  SECTION_HEADER_RE = /^(containers|components|deployment)\s*:\s*$/i;
5620
5751
  CONTAINER_REF_RE = /^container\s+(.+)$/i;
5621
5752
  METADATA_RE2 = /^([^:]+):\s*(.+)$/;
@@ -5673,20 +5804,23 @@ function looksLikeInitiativeStatus(content) {
5673
5804
  const lines = content.split("\n");
5674
5805
  let hasArrow = false;
5675
5806
  let hasStatus = false;
5807
+ let hasIndentedArrow = false;
5676
5808
  for (const line7 of lines) {
5677
5809
  const trimmed = line7.trim();
5678
- if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//")) continue;
5810
+ if (!trimmed || trimmed.startsWith("//")) continue;
5679
5811
  if (trimmed.match(/^chart\s*:/i)) continue;
5680
5812
  if (trimmed.match(/^title\s*:/i)) continue;
5681
5813
  if (trimmed.includes("->")) hasArrow = true;
5682
5814
  if (/\|\s*(done|wip|todo|na)\s*$/i.test(trimmed)) hasStatus = true;
5815
+ const isIndented = line7.length > 0 && line7 !== trimmed && /^\s/.test(line7);
5816
+ if (isIndented && (trimmed.startsWith("->") || /^-[^>].*->/.test(trimmed))) hasIndentedArrow = true;
5683
5817
  if (hasArrow && hasStatus) return true;
5684
5818
  }
5685
- return false;
5819
+ return hasIndentedArrow;
5686
5820
  }
5687
5821
  function parseStatus(raw, line7, diagnostics) {
5688
5822
  const trimmed = raw.trim().toLowerCase();
5689
- if (!trimmed) return null;
5823
+ if (!trimmed) return "na";
5690
5824
  if (VALID_STATUSES.includes(trimmed)) return trimmed;
5691
5825
  const hint = suggest(trimmed, VALID_STATUSES);
5692
5826
  const msg = `Unknown status "${raw.trim()}"${hint ? `. ${hint}` : ""}`;
@@ -5703,16 +5837,17 @@ function parseInitiativeStatus(content) {
5703
5837
  groups: [],
5704
5838
  options: {},
5705
5839
  diagnostics: [],
5706
- error: void 0
5840
+ error: null
5707
5841
  };
5708
5842
  const lines = content.split("\n");
5709
5843
  const nodeLabels = /* @__PURE__ */ new Set();
5710
5844
  let currentGroup = null;
5845
+ let lastNodeLabel = null;
5711
5846
  for (let i = 0; i < lines.length; i++) {
5712
5847
  const lineNum = i + 1;
5713
5848
  const raw = lines[i];
5714
5849
  const trimmed = raw.trim();
5715
- if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//")) continue;
5850
+ if (!trimmed || trimmed.startsWith("//")) continue;
5716
5851
  const chartMatch = trimmed.match(/^chart\s*:\s*(.+)/i);
5717
5852
  if (chartMatch) {
5718
5853
  const chartType = chartMatch[1].trim().toLowerCase();
@@ -5744,12 +5879,23 @@ function parseInitiativeStatus(content) {
5744
5879
  currentGroup = null;
5745
5880
  }
5746
5881
  if (trimmed.includes("->")) {
5747
- const edge = parseEdgeLine(trimmed, lineNum, result.diagnostics);
5882
+ let edgeText = trimmed;
5883
+ if (trimmed.startsWith("->") || /^-[^>].*->/.test(trimmed)) {
5884
+ if (!lastNodeLabel) {
5885
+ result.diagnostics.push(
5886
+ makeDgmoError(lineNum, "Indented edge has no preceding node to use as source", "warning")
5887
+ );
5888
+ continue;
5889
+ }
5890
+ edgeText = `${lastNodeLabel} ${trimmed}`;
5891
+ }
5892
+ const edge = parseEdgeLine(edgeText, lineNum, result.diagnostics);
5748
5893
  if (edge) result.edges.push(edge);
5749
5894
  continue;
5750
5895
  }
5751
5896
  const node = parseNodeLine(trimmed, lineNum, result.diagnostics);
5752
5897
  if (node) {
5898
+ lastNodeLabel = node.label;
5753
5899
  if (nodeLabels.has(node.label)) {
5754
5900
  result.diagnostics.push(
5755
5901
  makeDgmoError(lineNum, `Duplicate node "${node.label}"`, "warning")
@@ -5772,7 +5918,7 @@ function parseInitiativeStatus(content) {
5772
5918
  makeDgmoError(edge.lineNumber, `Edge source "${edge.source}" is not a declared node`, "warning")
5773
5919
  );
5774
5920
  if (!result.nodes.some((n) => n.label === edge.source)) {
5775
- result.nodes.push({ label: edge.source, status: null, shape: inferParticipantType(edge.source), lineNumber: edge.lineNumber });
5921
+ result.nodes.push({ label: edge.source, status: "na", shape: inferParticipantType(edge.source), lineNumber: edge.lineNumber });
5776
5922
  nodeLabels.add(edge.source);
5777
5923
  }
5778
5924
  }
@@ -5781,7 +5927,7 @@ function parseInitiativeStatus(content) {
5781
5927
  makeDgmoError(edge.lineNumber, `Edge target "${edge.target}" is not a declared node`, "warning")
5782
5928
  );
5783
5929
  if (!result.nodes.some((n) => n.label === edge.target)) {
5784
- result.nodes.push({ label: edge.target, status: null, shape: inferParticipantType(edge.target), lineNumber: edge.lineNumber });
5930
+ result.nodes.push({ label: edge.target, status: "na", shape: inferParticipantType(edge.target), lineNumber: edge.lineNumber });
5785
5931
  nodeLabels.add(edge.target);
5786
5932
  }
5787
5933
  }
@@ -5797,9 +5943,30 @@ function parseNodeLine(trimmed, lineNum, diagnostics) {
5797
5943
  const status = parseStatus(statusRaw, lineNum, diagnostics);
5798
5944
  return { label, status, shape: inferParticipantType(label), lineNumber: lineNum };
5799
5945
  }
5800
- return { label: trimmed, status: null, shape: inferParticipantType(trimmed), lineNumber: lineNum };
5946
+ return { label: trimmed, status: "na", shape: inferParticipantType(trimmed), lineNumber: lineNum };
5801
5947
  }
5802
5948
  function parseEdgeLine(trimmed, lineNum, diagnostics) {
5949
+ const labeledMatch = trimmed.match(/^(\S+)\s+-(.+)->\s+(.+)$/);
5950
+ if (labeledMatch) {
5951
+ const source2 = labeledMatch[1];
5952
+ const label2 = labeledMatch[2].trim();
5953
+ let targetRest = labeledMatch[3].trim();
5954
+ if (label2) {
5955
+ let status2 = "na";
5956
+ const lastPipe2 = targetRest.lastIndexOf("|");
5957
+ if (lastPipe2 >= 0) {
5958
+ const statusRaw = targetRest.slice(lastPipe2 + 1).trim();
5959
+ status2 = parseStatus(statusRaw, lineNum, diagnostics);
5960
+ targetRest = targetRest.slice(0, lastPipe2).trim();
5961
+ }
5962
+ const target2 = targetRest.trim();
5963
+ if (!target2) {
5964
+ diagnostics.push(makeDgmoError(lineNum, "Edge is missing target"));
5965
+ return null;
5966
+ }
5967
+ return { source: source2, target: target2, label: label2, status: status2, lineNumber: lineNum };
5968
+ }
5969
+ }
5803
5970
  const arrowIdx = trimmed.indexOf("->");
5804
5971
  if (arrowIdx < 0) return null;
5805
5972
  const source = trimmed.slice(0, arrowIdx).trim();
@@ -5808,7 +5975,7 @@ function parseEdgeLine(trimmed, lineNum, diagnostics) {
5808
5975
  diagnostics.push(makeDgmoError(lineNum, "Edge is missing source or target"));
5809
5976
  return null;
5810
5977
  }
5811
- let status = null;
5978
+ let status = "na";
5812
5979
  const lastPipe = rest.lastIndexOf("|");
5813
5980
  if (lastPipe >= 0) {
5814
5981
  const statusRaw = rest.slice(lastPipe + 1).trim();
@@ -5854,7 +6021,7 @@ function parseDgmoChartType(content) {
5854
6021
  const lines = content.split("\n");
5855
6022
  for (const line7 of lines) {
5856
6023
  const trimmed = line7.trim();
5857
- if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//"))
6024
+ if (!trimmed || trimmed.startsWith("//"))
5858
6025
  continue;
5859
6026
  const match = trimmed.match(/^chart\s*:\s*(.+)/i);
5860
6027
  if (match) return match[1].trim().toLowerCase();
@@ -7116,6 +7283,58 @@ var init_renderer = __esm({
7116
7283
  }
7117
7284
  });
7118
7285
 
7286
+ // src/utils/inline-markdown.ts
7287
+ function parseInlineMarkdown(text) {
7288
+ const spans = [];
7289
+ const regex = /\*\*(.+?)\*\*|__(.+?)__|\*(.+?)\*|_(.+?)_|`(.+?)`|\[(.+?)\]\((.+?)\)|(https?:\/\/[^\s)>\]]+|www\.[^\s)>\]]+)|([^*_`[]+?(?=https?:\/\/|www\.|$)|[^*_`[]+)/g;
7290
+ let match;
7291
+ while ((match = regex.exec(text)) !== null) {
7292
+ if (match[1]) spans.push({ text: match[1], bold: true });
7293
+ else if (match[2]) spans.push({ text: match[2], bold: true });
7294
+ else if (match[3]) spans.push({ text: match[3], italic: true });
7295
+ else if (match[4]) spans.push({ text: match[4], italic: true });
7296
+ else if (match[5]) spans.push({ text: match[5], code: true });
7297
+ else if (match[6]) spans.push({ text: match[6], href: match[7] });
7298
+ else if (match[8]) {
7299
+ const url = match[8];
7300
+ const href = url.startsWith("www.") ? `https://${url}` : url;
7301
+ spans.push({ text: url, href });
7302
+ } else if (match[9]) spans.push({ text: match[9] });
7303
+ }
7304
+ return spans;
7305
+ }
7306
+ function truncateBareUrl(url) {
7307
+ const stripped = url.replace(/^https?:\/\//, "").replace(/^www\./, "");
7308
+ if (stripped.length <= BARE_URL_MAX_DISPLAY) return stripped;
7309
+ return stripped.slice(0, BARE_URL_MAX_DISPLAY - 1) + "\u2026";
7310
+ }
7311
+ function renderInlineText(textEl, text, palette, fontSize) {
7312
+ const spans = parseInlineMarkdown(text);
7313
+ for (const span of spans) {
7314
+ if (span.href) {
7315
+ const isBareUrl = span.text === span.href || `https://${span.text}` === span.href;
7316
+ const display = isBareUrl ? truncateBareUrl(span.text) : span.text;
7317
+ const a = textEl.append("a").attr("href", span.href);
7318
+ a.append("tspan").text(display).attr("fill", palette.primary).style("text-decoration", "underline");
7319
+ } else {
7320
+ const tspan = textEl.append("tspan").text(span.text);
7321
+ if (span.bold) tspan.attr("font-weight", "bold");
7322
+ if (span.italic) tspan.attr("font-style", "italic");
7323
+ if (span.code) {
7324
+ tspan.attr("font-family", "monospace");
7325
+ if (fontSize) tspan.attr("font-size", fontSize - 1);
7326
+ }
7327
+ }
7328
+ }
7329
+ }
7330
+ var BARE_URL_MAX_DISPLAY;
7331
+ var init_inline_markdown = __esm({
7332
+ "src/utils/inline-markdown.ts"() {
7333
+ "use strict";
7334
+ BARE_URL_MAX_DISPLAY = 35;
7335
+ }
7336
+ });
7337
+
7119
7338
  // src/kanban/mutations.ts
7120
7339
  function computeCardMove(content, parsed, cardId, targetColumnId, targetIndex) {
7121
7340
  let sourceCard = null;
@@ -7437,7 +7656,8 @@ function renderKanban(container, parsed, palette, isDark, _onNavigateToLine, exp
7437
7656
  const cx = colLayout.x + cardLayout.x;
7438
7657
  const cy = colLayout.y + cardLayout.y;
7439
7658
  cg.append("rect").attr("x", cx).attr("y", cy).attr("width", cardLayout.width).attr("height", cardLayout.height).attr("rx", CARD_RADIUS2).attr("fill", cardFill).attr("stroke", cardStroke).attr("stroke-width", CARD_STROKE_WIDTH);
7440
- cg.append("text").attr("x", cx + CARD_PADDING_X).attr("y", cy + CARD_PADDING_Y + CARD_TITLE_FONT_SIZE).attr("font-size", CARD_TITLE_FONT_SIZE).attr("font-weight", "500").attr("fill", palette.text).text(card.title);
7659
+ const titleEl = cg.append("text").attr("x", cx + CARD_PADDING_X).attr("y", cy + CARD_PADDING_Y + CARD_TITLE_FONT_SIZE).attr("font-size", CARD_TITLE_FONT_SIZE).attr("font-weight", "500").attr("fill", palette.text);
7660
+ renderInlineText(titleEl, card.title, palette, CARD_TITLE_FONT_SIZE);
7441
7661
  if (hasMeta) {
7442
7662
  const separatorY = cy + CARD_HEADER_HEIGHT;
7443
7663
  cg.append("line").attr("x1", cx).attr("y1", separatorY).attr("x2", cx + cardLayout.width).attr("y2", separatorY).attr("stroke", cardStroke).attr("stroke-opacity", 0.3).attr("stroke-width", 1);
@@ -7449,7 +7669,8 @@ function renderKanban(container, parsed, palette, isDark, _onNavigateToLine, exp
7449
7669
  metaY += CARD_META_LINE_HEIGHT;
7450
7670
  }
7451
7671
  for (const detail of card.details) {
7452
- cg.append("text").attr("x", cx + CARD_PADDING_X).attr("y", metaY).attr("font-size", CARD_META_FONT_SIZE).attr("fill", palette.textMuted).text(detail);
7672
+ const detailEl = cg.append("text").attr("x", cx + CARD_PADDING_X).attr("y", metaY).attr("font-size", CARD_META_FONT_SIZE).attr("fill", palette.textMuted);
7673
+ renderInlineText(detailEl, detail, palette, CARD_META_FONT_SIZE);
7453
7674
  metaY += CARD_META_LINE_HEIGHT;
7454
7675
  }
7455
7676
  }
@@ -7475,6 +7696,7 @@ var init_renderer2 = __esm({
7475
7696
  "use strict";
7476
7697
  d3Selection2 = __toESM(require("d3-selection"), 1);
7477
7698
  init_fonts();
7699
+ init_inline_markdown();
7478
7700
  init_parser5();
7479
7701
  init_mutations();
7480
7702
  DIAGRAM_PADDING2 = 20;
@@ -10620,7 +10842,8 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
10620
10842
  const contentWidth = w - CARD_H_PAD3 * 2;
10621
10843
  const lines = wrapText2(node.description, contentWidth, DESC_CHAR_WIDTH2);
10622
10844
  for (const line7 of lines) {
10623
- nodeG.append("text").attr("x", 0).attr("y", yPos + DESC_FONT_SIZE / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", palette.textMuted).attr("font-size", DESC_FONT_SIZE).text(line7);
10845
+ const textEl = nodeG.append("text").attr("x", 0).attr("y", yPos + DESC_FONT_SIZE / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", palette.textMuted).attr("font-size", DESC_FONT_SIZE);
10846
+ renderInlineText(textEl, line7, palette, DESC_FONT_SIZE);
10624
10847
  yPos += DESC_LINE_HEIGHT2;
10625
10848
  }
10626
10849
  }
@@ -11106,7 +11329,8 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
11106
11329
  const contentWidth = w - CARD_H_PAD3 * 2;
11107
11330
  const lines = wrapText2(node.description, contentWidth, DESC_CHAR_WIDTH2);
11108
11331
  for (const line7 of lines) {
11109
- nodeG.append("text").attr("x", 0).attr("y", yPos + DESC_FONT_SIZE / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", palette.textMuted).attr("font-size", DESC_FONT_SIZE).text(line7);
11332
+ const textEl = nodeG.append("text").attr("x", 0).attr("y", yPos + DESC_FONT_SIZE / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", palette.textMuted).attr("font-size", DESC_FONT_SIZE);
11333
+ renderInlineText(textEl, line7, palette, DESC_FONT_SIZE);
11110
11334
  yPos += DESC_LINE_HEIGHT2;
11111
11335
  }
11112
11336
  }
@@ -11129,7 +11353,8 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
11129
11353
  const contentWidth = w - CARD_H_PAD3 * 2;
11130
11354
  const lines = wrapText2(node.description, contentWidth, DESC_CHAR_WIDTH2);
11131
11355
  for (const line7 of lines) {
11132
- nodeG.append("text").attr("x", 0).attr("y", yPos + DESC_FONT_SIZE / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", palette.textMuted).attr("font-size", DESC_FONT_SIZE).text(line7);
11356
+ const textEl = nodeG.append("text").attr("x", 0).attr("y", yPos + DESC_FONT_SIZE / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", palette.textMuted).attr("font-size", DESC_FONT_SIZE);
11357
+ renderInlineText(textEl, line7, palette, DESC_FONT_SIZE);
11133
11358
  yPos += DESC_LINE_HEIGHT2;
11134
11359
  }
11135
11360
  }
@@ -11250,6 +11475,7 @@ var init_renderer6 = __esm({
11250
11475
  d3Selection6 = __toESM(require("d3-selection"), 1);
11251
11476
  d3Shape4 = __toESM(require("d3-shape"), 1);
11252
11477
  init_fonts();
11478
+ init_inline_markdown();
11253
11479
  init_parser6();
11254
11480
  init_layout5();
11255
11481
  DIAGRAM_PADDING6 = 20;
@@ -11747,49 +11973,6 @@ __export(renderer_exports7, {
11747
11973
  renderSequenceDiagram: () => renderSequenceDiagram,
11748
11974
  truncateBareUrl: () => truncateBareUrl
11749
11975
  });
11750
- function parseInlineMarkdown(text) {
11751
- const spans = [];
11752
- const regex = /\*\*(.+?)\*\*|__(.+?)__|\*(.+?)\*|_(.+?)_|`(.+?)`|\[(.+?)\]\((.+?)\)|(https?:\/\/[^\s)>\]]+|www\.[^\s)>\]]+)|([^*_`[]+?(?=https?:\/\/|www\.|$)|[^*_`[]+)/g;
11753
- let match;
11754
- while ((match = regex.exec(text)) !== null) {
11755
- if (match[1]) spans.push({ text: match[1], bold: true });
11756
- else if (match[2]) spans.push({ text: match[2], bold: true });
11757
- else if (match[3]) spans.push({ text: match[3], italic: true });
11758
- else if (match[4]) spans.push({ text: match[4], italic: true });
11759
- else if (match[5]) spans.push({ text: match[5], code: true });
11760
- else if (match[6]) spans.push({ text: match[6], href: match[7] });
11761
- else if (match[8]) {
11762
- const url = match[8];
11763
- const href = url.startsWith("www.") ? `https://${url}` : url;
11764
- spans.push({ text: url, href });
11765
- } else if (match[9]) spans.push({ text: match[9] });
11766
- }
11767
- return spans;
11768
- }
11769
- function truncateBareUrl(url) {
11770
- const stripped = url.replace(/^https?:\/\//, "").replace(/^www\./, "");
11771
- if (stripped.length <= BARE_URL_MAX_DISPLAY) return stripped;
11772
- return stripped.slice(0, BARE_URL_MAX_DISPLAY - 1) + "\u2026";
11773
- }
11774
- function renderInlineText(textEl, text, palette, fontSize) {
11775
- const spans = parseInlineMarkdown(text);
11776
- for (const span of spans) {
11777
- if (span.href) {
11778
- const isBareUrl = span.text === span.href || `https://${span.text}` === span.href;
11779
- const display = isBareUrl ? truncateBareUrl(span.text) : span.text;
11780
- const a = textEl.append("a").attr("href", span.href);
11781
- a.append("tspan").text(display).attr("fill", palette.primary).style("text-decoration", "underline");
11782
- } else {
11783
- const tspan = textEl.append("tspan").text(span.text);
11784
- if (span.bold) tspan.attr("font-weight", "bold");
11785
- if (span.italic) tspan.attr("font-style", "italic");
11786
- if (span.code) {
11787
- tspan.attr("font-family", "monospace");
11788
- if (fontSize) tspan.attr("font-size", fontSize - 1);
11789
- }
11790
- }
11791
- }
11792
- }
11793
11976
  function wrapTextLines(text, maxChars) {
11794
11977
  const rawLines = text.split("\n");
11795
11978
  const wrapped = [];
@@ -11970,8 +12153,12 @@ function buildRenderSequence(messages) {
11970
12153
  to: msg.to,
11971
12154
  label: msg.label,
11972
12155
  messageIndex: mi,
11973
- ...msg.async ? { async: true } : {}
12156
+ ...msg.async ? { async: true } : {},
12157
+ ...msg.bidirectional ? { bidirectional: true } : {}
11974
12158
  });
12159
+ if (msg.bidirectional) {
12160
+ continue;
12161
+ }
11975
12162
  if (msg.async) {
11976
12163
  continue;
11977
12164
  }
@@ -12463,6 +12650,14 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
12463
12650
  "points",
12464
12651
  `0,0 ${ARROWHEAD_SIZE},${ARROWHEAD_SIZE / 2} 0,${ARROWHEAD_SIZE}`
12465
12652
  ).attr("fill", "none").attr("stroke", palette.text).attr("stroke-width", 1.2);
12653
+ defs.append("marker").attr("id", "seq-arrowhead-reverse").attr("viewBox", `0 0 ${ARROWHEAD_SIZE} ${ARROWHEAD_SIZE}`).attr("refX", 0).attr("refY", ARROWHEAD_SIZE / 2).attr("markerWidth", ARROWHEAD_SIZE).attr("markerHeight", ARROWHEAD_SIZE).attr("orient", "auto").append("polygon").attr(
12654
+ "points",
12655
+ `${ARROWHEAD_SIZE},0 0,${ARROWHEAD_SIZE / 2} ${ARROWHEAD_SIZE},${ARROWHEAD_SIZE}`
12656
+ ).attr("fill", palette.text);
12657
+ defs.append("marker").attr("id", "seq-arrowhead-async-reverse").attr("viewBox", `0 0 ${ARROWHEAD_SIZE} ${ARROWHEAD_SIZE}`).attr("refX", 0).attr("refY", ARROWHEAD_SIZE / 2).attr("markerWidth", ARROWHEAD_SIZE).attr("markerHeight", ARROWHEAD_SIZE).attr("orient", "auto").append("polyline").attr(
12658
+ "points",
12659
+ `${ARROWHEAD_SIZE},0 0,${ARROWHEAD_SIZE / 2} ${ARROWHEAD_SIZE},${ARROWHEAD_SIZE}`
12660
+ ).attr("fill", "none").attr("stroke", palette.text).attr("stroke-width", 1.2);
12466
12661
  if (title) {
12467
12662
  const titleEl = svg.append("text").attr("class", "chart-title").attr("x", svgWidth / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", 20).attr("font-weight", "bold").text(title);
12468
12663
  if (parsed.titleLineNumber) {
@@ -12743,10 +12938,17 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
12743
12938
  const x1 = arrowEdgeX(step.from, i, goingRight ? "right" : "left");
12744
12939
  const x2 = arrowEdgeX(step.to, i, goingRight ? "left" : "right");
12745
12940
  const markerRef = step.async ? "url(#seq-arrowhead-async)" : "url(#seq-arrowhead)";
12746
- svg.append("line").attr("x1", x1).attr("y1", y).attr("x2", x2).attr("y2", y).attr("stroke", palette.text).attr("stroke-width", 1.2).attr("marker-end", markerRef).attr("class", "message-arrow").attr(
12941
+ const markerStartRef = step.bidirectional ? step.async ? "url(#seq-arrowhead-async-reverse)" : "url(#seq-arrowhead-reverse)" : null;
12942
+ const line7 = svg.append("line").attr("x1", x1).attr("y1", y).attr("x2", x2).attr("y2", y).attr("stroke", palette.text).attr("stroke-width", 1.2).attr("marker-end", markerRef).attr("class", "message-arrow").attr(
12747
12943
  "data-line-number",
12748
12944
  String(messages[step.messageIndex].lineNumber)
12749
12945
  ).attr("data-msg-index", String(step.messageIndex)).attr("data-step-index", String(i));
12946
+ if (markerStartRef) {
12947
+ line7.attr("marker-start", markerStartRef);
12948
+ }
12949
+ if (step.bidirectional && step.async) {
12950
+ line7.attr("stroke-dasharray", "6 4");
12951
+ }
12750
12952
  if (step.label) {
12751
12953
  const midX = (x1 + x2) / 2;
12752
12954
  const labelEl = svg.append("text").attr("x", midX).attr("y", y - 8).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", 12).attr("class", "message-label").attr(
@@ -12934,12 +13136,13 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark) {
12934
13136
  isActor ? PARTICIPANT_BOX_HEIGHT + 14 : PARTICIPANT_BOX_HEIGHT / 2 + 5
12935
13137
  ).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", 13).attr("font-weight", 500).text(participant.label);
12936
13138
  }
12937
- var d3Selection8, PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, TOP_MARGIN, TITLE_HEIGHT4, PARTICIPANT_Y_OFFSET, SERVICE_BORDER_RADIUS, MESSAGE_START_OFFSET, LIFELINE_TAIL, ARROWHEAD_SIZE, NOTE_MAX_W, NOTE_FOLD, NOTE_PAD_H, NOTE_PAD_V, NOTE_FONT_SIZE, NOTE_LINE_H, NOTE_GAP, NOTE_CHAR_W, NOTE_CHARS_PER_LINE, COLLAPSED_NOTE_H, COLLAPSED_NOTE_W, BARE_URL_MAX_DISPLAY, fill, stroke, SW, W, H;
13139
+ var d3Selection8, PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, TOP_MARGIN, TITLE_HEIGHT4, PARTICIPANT_Y_OFFSET, SERVICE_BORDER_RADIUS, MESSAGE_START_OFFSET, LIFELINE_TAIL, ARROWHEAD_SIZE, NOTE_MAX_W, NOTE_FOLD, NOTE_PAD_H, NOTE_PAD_V, NOTE_FONT_SIZE, NOTE_LINE_H, NOTE_GAP, NOTE_CHAR_W, NOTE_CHARS_PER_LINE, COLLAPSED_NOTE_H, COLLAPSED_NOTE_W, fill, stroke, SW, W, H;
12938
13140
  var init_renderer7 = __esm({
12939
13141
  "src/sequence/renderer.ts"() {
12940
13142
  "use strict";
12941
13143
  d3Selection8 = __toESM(require("d3-selection"), 1);
12942
13144
  init_colors();
13145
+ init_inline_markdown();
12943
13146
  init_fonts();
12944
13147
  init_parser();
12945
13148
  PARTICIPANT_GAP = 160;
@@ -12963,7 +13166,6 @@ var init_renderer7 = __esm({
12963
13166
  NOTE_CHARS_PER_LINE = Math.floor((NOTE_MAX_W - NOTE_PAD_H * 2 - NOTE_FOLD) / NOTE_CHAR_W);
12964
13167
  COLLAPSED_NOTE_H = 20;
12965
13168
  COLLAPSED_NOTE_W = 40;
12966
- BARE_URL_MAX_DISPLAY = 35;
12967
13169
  fill = (palette, isDark) => mix8(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
12968
13170
  stroke = (palette) => palette.textMuted;
12969
13171
  SW = 1.5;
@@ -13093,7 +13295,7 @@ function parseD3(content, palette) {
13093
13295
  }
13094
13296
  continue;
13095
13297
  }
13096
- if (line7.startsWith("#") || line7.startsWith("//")) {
13298
+ if (line7.startsWith("//")) {
13097
13299
  continue;
13098
13300
  }
13099
13301
  if (result.type === "arc") {
@@ -16266,6 +16468,7 @@ __export(index_exports, {
16266
16468
  parseERDiagram: () => parseERDiagram,
16267
16469
  parseFlowchart: () => parseFlowchart,
16268
16470
  parseInitiativeStatus: () => parseInitiativeStatus,
16471
+ parseInlineMarkdown: () => parseInlineMarkdown,
16269
16472
  parseKanban: () => parseKanban,
16270
16473
  parseOrg: () => parseOrg,
16271
16474
  parseQuadrant: () => parseQuadrant,
@@ -16309,7 +16512,8 @@ __export(index_exports, {
16309
16512
  shade: () => shade,
16310
16513
  solarizedPalette: () => solarizedPalette,
16311
16514
  tint: () => tint,
16312
- tokyoNightPalette: () => tokyoNightPalette
16515
+ tokyoNightPalette: () => tokyoNightPalette,
16516
+ truncateBareUrl: () => truncateBareUrl
16313
16517
  });
16314
16518
  module.exports = __toCommonJS(index_exports);
16315
16519
  init_diagnostics();
@@ -16390,7 +16594,7 @@ function parseQuadrant(content) {
16390
16594
  for (let i = 0; i < lines.length; i++) {
16391
16595
  const line7 = lines[i].trim();
16392
16596
  const lineNumber = i + 1;
16393
- if (!line7 || line7.startsWith("#") || line7.startsWith("//")) continue;
16597
+ if (!line7 || line7.startsWith("//")) continue;
16394
16598
  if (/^chart\s*:/i.test(line7)) continue;
16395
16599
  const titleMatch = line7.match(/^title\s*:\s*(.+)/i);
16396
16600
  if (titleMatch) {
@@ -16520,6 +16724,7 @@ init_renderer3();
16520
16724
  init_parser3();
16521
16725
  init_layout3();
16522
16726
  init_renderer4();
16727
+ init_inline_markdown();
16523
16728
  init_parser4();
16524
16729
  init_layout();
16525
16730
  init_renderer();
@@ -16536,12 +16741,12 @@ init_collapse();
16536
16741
 
16537
16742
  // src/org/resolver.ts
16538
16743
  init_diagnostics();
16744
+ init_tag_groups();
16539
16745
  var MAX_DEPTH = 10;
16540
16746
  var IMPORT_RE = /^(\s+)import:\s+(.+\.dgmo)\s*$/i;
16541
16747
  var TAGS_RE = /^tags:\s+(.+\.dgmo)\s*$/i;
16542
16748
  var HEADER_RE = /^(chart|title)\s*:/i;
16543
- var OPTION_RE4 = /^[a-z][a-z0-9-]*\s*:/i;
16544
- var GROUP_HEADING_RE5 = /^##\s+/;
16749
+ var OPTION_RE2 = /^[a-z][a-z0-9-]*\s*:/i;
16545
16750
  function dirname(filePath) {
16546
16751
  const last = filePath.lastIndexOf("/");
16547
16752
  return last > 0 ? filePath.substring(0, last) : "/";
@@ -16562,9 +16767,9 @@ function extractTagGroups(lines) {
16562
16767
  let current = null;
16563
16768
  for (const line7 of lines) {
16564
16769
  const trimmed = line7.trim();
16565
- if (GROUP_HEADING_RE5.test(trimmed)) {
16566
- const nameMatch = trimmed.match(/^##\s+(.+?)(?:\s+alias\s+\w+)?(?:\s*\([^)]+\))?\s*$/);
16567
- const name = nameMatch ? nameMatch[1].trim().toLowerCase() : trimmed.substring(3).trim().toLowerCase();
16770
+ const headingMatch = matchTagBlockHeading(trimmed);
16771
+ if (headingMatch) {
16772
+ const name = headingMatch.name.toLowerCase();
16568
16773
  current = { name, lines: [line7] };
16569
16774
  blocks.push(current);
16570
16775
  } else if (current) {
@@ -16606,7 +16811,7 @@ function parseFileHeader(lines) {
16606
16811
  tagsDirective = tagsMatch[1].trim();
16607
16812
  continue;
16608
16813
  }
16609
- if (OPTION_RE4.test(trimmed) && !trimmed.startsWith("##") && !lines[i].match(/^\s/)) {
16814
+ if (OPTION_RE2.test(trimmed) && !isTagBlockHeading(trimmed) && !lines[i].match(/^\s/)) {
16610
16815
  const key = trimmed.split(":")[0].trim().toLowerCase();
16611
16816
  if (key !== "chart" && key !== "title" && !trimmed.includes("|")) {
16612
16817
  continue;
@@ -16636,7 +16841,7 @@ async function resolveFile(content, filePath, readFileFn, diagnostics, ancestorC
16636
16841
  headerLines.push(lines[i]);
16637
16842
  continue;
16638
16843
  }
16639
- if (GROUP_HEADING_RE5.test(trimmed)) continue;
16844
+ if (isTagBlockHeading(trimmed)) continue;
16640
16845
  if (lines[i] !== trimmed) continue;
16641
16846
  const tagsMatch = trimmed.match(TAGS_RE);
16642
16847
  if (tagsMatch) {
@@ -16668,7 +16873,7 @@ async function resolveFile(content, filePath, readFileFn, diagnostics, ancestorC
16668
16873
  const importMatch = line7.match(IMPORT_RE);
16669
16874
  if (!importMatch) {
16670
16875
  const trimmed = line7.trim();
16671
- if (GROUP_HEADING_RE5.test(trimmed) || inlineTagGroups.length > 0 && isTagGroupEntry(line7, bodyLines, i)) {
16876
+ if (isTagBlockHeading(trimmed) || inlineTagGroups.length > 0 && isTagGroupEntry(line7, bodyLines, i)) {
16672
16877
  continue;
16673
16878
  }
16674
16879
  resolvedBodyLines.push(line7);
@@ -16763,7 +16968,7 @@ function findBodyStart(lines) {
16763
16968
  if (inTagGroup) inTagGroup = false;
16764
16969
  continue;
16765
16970
  }
16766
- if (GROUP_HEADING_RE5.test(trimmed)) {
16971
+ if (isTagBlockHeading(trimmed)) {
16767
16972
  inTagGroup = true;
16768
16973
  continue;
16769
16974
  }
@@ -16775,7 +16980,7 @@ function findBodyStart(lines) {
16775
16980
  }
16776
16981
  if (HEADER_RE.test(trimmed)) continue;
16777
16982
  if (TAGS_RE.test(trimmed)) continue;
16778
- if (OPTION_RE4.test(trimmed) && !lines[i].match(/^\s/) && !trimmed.includes("|")) {
16983
+ if (OPTION_RE2.test(trimmed) && !isTagBlockHeading(trimmed) && !lines[i].match(/^\s/) && !trimmed.includes("|")) {
16779
16984
  const key = trimmed.split(":")[0].trim().toLowerCase();
16780
16985
  if (key !== "chart" && key !== "title") {
16781
16986
  continue;
@@ -16790,7 +16995,7 @@ function isTagGroupEntry(line7, allLines, index) {
16790
16995
  for (let i = index - 1; i >= 0; i--) {
16791
16996
  const prev = allLines[i].trim();
16792
16997
  if (prev === "" || prev.startsWith("//")) continue;
16793
- if (GROUP_HEADING_RE5.test(prev)) return true;
16998
+ if (isTagBlockHeading(prev)) return true;
16794
16999
  if (allLines[i].match(/^\s+/)) continue;
16795
17000
  return false;
16796
17001
  }
@@ -16945,6 +17150,7 @@ init_branding();
16945
17150
  parseERDiagram,
16946
17151
  parseFlowchart,
16947
17152
  parseInitiativeStatus,
17153
+ parseInlineMarkdown,
16948
17154
  parseKanban,
16949
17155
  parseOrg,
16950
17156
  parseQuadrant,
@@ -16988,6 +17194,7 @@ init_branding();
16988
17194
  shade,
16989
17195
  solarizedPalette,
16990
17196
  tint,
16991
- tokyoNightPalette
17197
+ tokyoNightPalette,
17198
+ truncateBareUrl
16992
17199
  });
16993
17200
  //# sourceMappingURL=index.cjs.map