@diagrammo/dgmo 0.8.1 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1461,20 +1461,6 @@ function extractColor(label, palette) {
1461
1461
  function parseFirstLine(line10) {
1462
1462
  const trimmed = line10.trim();
1463
1463
  if (!trimmed || trimmed.startsWith("//")) return null;
1464
- const oldMatch = trimmed.match(CHART_TYPE_RE);
1465
- if (oldMatch) {
1466
- const parts = oldMatch[1].trim();
1467
- const spaceIdx2 = parts.indexOf(" ");
1468
- if (spaceIdx2 === -1) {
1469
- const ct2 = parts.toLowerCase();
1470
- return ALL_CHART_TYPES.has(ct2) ? { chartType: ct2, title: void 0 } : null;
1471
- }
1472
- const ct = parts.substring(0, spaceIdx2).toLowerCase();
1473
- if (ALL_CHART_TYPES.has(ct)) {
1474
- return { chartType: ct, title: parts.substring(spaceIdx2 + 1).trim() || void 0 };
1475
- }
1476
- return null;
1477
- }
1478
1464
  const spaceIdx = trimmed.indexOf(" ");
1479
1465
  if (spaceIdx === -1) {
1480
1466
  const ct = trimmed.toLowerCase();
@@ -1558,12 +1544,6 @@ function parseSeriesNames(value, lines, lineIndex, palette) {
1558
1544
  }
1559
1545
  return { series, names, nameColors, nameLineNumbers, newIndex };
1560
1546
  }
1561
- function normalizeDirection(value) {
1562
- const v = value.trim().toLowerCase();
1563
- if (v === "lr" || v === "horizontal") return "LR";
1564
- if (v === "tb" || v === "vertical") return "TB";
1565
- return null;
1566
- }
1567
1547
  function inferArrowColor(label) {
1568
1548
  const lower = label.toLowerCase();
1569
1549
  if (lower === "yes" || lower === "success" || lower === "ok" || lower === "true") return "green";
@@ -1571,9 +1551,10 @@ function inferArrowColor(label) {
1571
1551
  if (lower === "maybe" || lower === "warning") return "orange";
1572
1552
  return void 0;
1573
1553
  }
1574
- function parsePipeMetadata(segments, aliasMap = /* @__PURE__ */ new Map(), warnMultiplePipes) {
1575
- if (segments.length > 2 && warnMultiplePipes) {
1576
- warnMultiplePipes();
1554
+ function parsePipeMetadata(segments, aliasMap = /* @__PURE__ */ new Map(), errorMultiplePipes) {
1555
+ if (segments.length > 2) {
1556
+ if (errorMultiplePipes) errorMultiplePipes();
1557
+ return {};
1577
1558
  }
1578
1559
  const metadata = {};
1579
1560
  const raw = segments.slice(1).join(",");
@@ -1590,7 +1571,7 @@ function parsePipeMetadata(segments, aliasMap = /* @__PURE__ */ new Map(), warnM
1590
1571
  }
1591
1572
  return metadata;
1592
1573
  }
1593
- var ALL_CHART_TYPES, COLOR_SUFFIX_RE, CHART_TYPE_RE, TITLE_RE, OPTION_NOCOLON_RE, GROUP_HASH_RE, DOUBLE_HASH_RE, MULTIPLE_PIPE_WARNING;
1574
+ var ALL_CHART_TYPES, COLOR_SUFFIX_RE, TITLE_RE, OPTION_NOCOLON_RE, MULTIPLE_PIPE_ERROR;
1594
1575
  var init_parsing = __esm({
1595
1576
  "src/utils/parsing.ts"() {
1596
1577
  "use strict";
@@ -1634,12 +1615,9 @@ var init_parsing = __esm({
1634
1615
  "gantt"
1635
1616
  ]);
1636
1617
  COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
1637
- CHART_TYPE_RE = /^chart\s*:\s*(.+)/i;
1638
1618
  TITLE_RE = /^title\s*:\s*(.+)/i;
1639
1619
  OPTION_NOCOLON_RE = /^([a-z][a-z0-9-]*)\s+(.+)$/i;
1640
- GROUP_HASH_RE = /^#\s+(.+)$/;
1641
- DOUBLE_HASH_RE = /^##\s/;
1642
- MULTIPLE_PIPE_WARNING = 'Use a single "|" to start metadata, then separate items with commas.';
1620
+ MULTIPLE_PIPE_ERROR = 'Use a single "|" to start metadata, then separate items with commas.';
1643
1621
  }
1644
1622
  });
1645
1623
 
@@ -1648,7 +1626,7 @@ function isAliasToken(token) {
1648
1626
  return /^[a-z]{1,4}$/.test(token);
1649
1627
  }
1650
1628
  function isTagBlockHeading(trimmed) {
1651
- return TAG_BLOCK_NOCOLON_RE.test(trimmed) || TAG_BLOCK_RE.test(trimmed) || GROUP_HEADING_RE.test(trimmed);
1629
+ return TAG_BLOCK_NOCOLON_RE.test(trimmed);
1652
1630
  }
1653
1631
  function parseTagDeclaration(line10) {
1654
1632
  if (!TAG_BLOCK_NOCOLON_RE.test(line10)) return null;
@@ -1718,7 +1696,6 @@ function parseTagDeclaration(line10) {
1718
1696
  name,
1719
1697
  alias,
1720
1698
  colorHint,
1721
- deprecated: false,
1722
1699
  inlineValues: inlineValues && inlineValues.length > 0 ? inlineValues : void 0
1723
1700
  };
1724
1701
  }
@@ -1783,36 +1760,14 @@ function injectDefaultTagMetadata(entities, tagGroups, skip) {
1783
1760
  }
1784
1761
  }
1785
1762
  function matchTagBlockHeading(trimmed) {
1786
- const nocolonResult = parseTagDeclaration(trimmed);
1787
- if (nocolonResult) return nocolonResult;
1788
- const tagMatch = trimmed.match(TAG_BLOCK_RE);
1789
- if (tagMatch) {
1790
- return {
1791
- name: tagMatch[1].trim(),
1792
- alias: tagMatch[2] || void 0,
1793
- colorHint: tagMatch[3] || void 0,
1794
- deprecated: false
1795
- };
1796
- }
1797
- const groupMatch = trimmed.match(GROUP_HEADING_RE);
1798
- if (groupMatch) {
1799
- return {
1800
- name: groupMatch[1].trim(),
1801
- alias: groupMatch[2] || void 0,
1802
- colorHint: groupMatch[3] || void 0,
1803
- deprecated: true
1804
- };
1805
- }
1806
- return null;
1763
+ return parseTagDeclaration(trimmed);
1807
1764
  }
1808
- var TAG_BLOCK_RE, TAG_BLOCK_NOCOLON_RE, GROUP_HEADING_RE;
1765
+ var TAG_BLOCK_NOCOLON_RE;
1809
1766
  var init_tag_groups = __esm({
1810
1767
  "src/utils/tag-groups.ts"() {
1811
1768
  "use strict";
1812
1769
  init_parsing();
1813
- TAG_BLOCK_RE = /^tag:\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/i;
1814
1770
  TAG_BLOCK_NOCOLON_RE = /^tag\s+/i;
1815
- GROUP_HEADING_RE = /^##\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/;
1816
1771
  }
1817
1772
  });
1818
1773
 
@@ -2297,18 +2252,7 @@ function isSequenceNote(el) {
2297
2252
  function parseNoteLine(trimmed, participants, lastMsgFrom) {
2298
2253
  const lower = trimmed.toLowerCase();
2299
2254
  if (!lower.startsWith("note")) return null;
2300
- if (trimmed.length > 4 && trimmed[4] !== " " && trimmed[4] !== ":") return null;
2301
- const colonMatch = trimmed.match(NOTE_SINGLE_COLON);
2302
- if (colonMatch) {
2303
- const position = colonMatch[1]?.toLowerCase() || "right";
2304
- let participantId = colonMatch[2] || null;
2305
- if (!participantId) {
2306
- if (!lastMsgFrom) return { kind: "skip" };
2307
- participantId = lastMsgFrom;
2308
- }
2309
- if (!participants.some((p) => p.id === participantId)) return { kind: "skip" };
2310
- return { kind: "single", position, participantId, text: colonMatch[3].trim() };
2311
- }
2255
+ if (trimmed.length > 4 && trimmed[4] !== " ") return null;
2312
2256
  const multiMatch = trimmed.match(NOTE_MULTI);
2313
2257
  if (multiMatch) {
2314
2258
  const position = multiMatch[1]?.toLowerCase() || "right";
@@ -2440,7 +2384,7 @@ function parseSequenceDgmo(content) {
2440
2384
  if (idx < 0) return { core: text };
2441
2385
  const core = text.substring(0, idx).trimEnd();
2442
2386
  const segments = text.substring(idx).split("|");
2443
- const warnFn = ln != null ? () => pushWarning(ln, MULTIPLE_PIPE_WARNING) : void 0;
2387
+ const warnFn = ln != null ? () => pushError(ln, MULTIPLE_PIPE_ERROR) : void 0;
2444
2388
  const meta = parsePipeMetadata(segments, aliasMap, warnFn);
2445
2389
  return Object.keys(meta).length > 0 ? { core, meta } : { core };
2446
2390
  };
@@ -2470,7 +2414,7 @@ function parseSequenceDgmo(content) {
2470
2414
  const afterBracket = groupMatch[3]?.trim() || "";
2471
2415
  if (afterBracket.startsWith("|")) {
2472
2416
  const segments = afterBracket.split("|");
2473
- const meta = parsePipeMetadata(segments, aliasMap, () => pushWarning(lineNumber, MULTIPLE_PIPE_WARNING));
2417
+ const meta = parsePipeMetadata(segments, aliasMap, () => pushError(lineNumber, MULTIPLE_PIPE_ERROR));
2474
2418
  if (Object.keys(meta).length > 0) groupMeta = meta;
2475
2419
  }
2476
2420
  if (groupColor) {
@@ -2514,7 +2458,7 @@ function parseSequenceDgmo(content) {
2514
2458
  continue;
2515
2459
  }
2516
2460
  const tagBlockMatch = matchTagBlockHeading(trimmed);
2517
- if (tagBlockMatch && !tagBlockMatch.deprecated) {
2461
+ if (tagBlockMatch) {
2518
2462
  if (contentStarted) {
2519
2463
  pushError(lineNumber, "Tag groups must appear before sequence content");
2520
2464
  continue;
@@ -2575,13 +2519,6 @@ function parseSequenceDgmo(content) {
2575
2519
  if (key === "note" || key.startsWith("note ")) {
2576
2520
  } else {
2577
2521
  const value = trimmed.substring(colonIndex + 1).trim();
2578
- if (key === "chart") {
2579
- hasExplicitChart = true;
2580
- if (value.toLowerCase() !== "sequence") {
2581
- return fail(lineNumber, `Expected chart type "sequence", got "${value}"`);
2582
- }
2583
- continue;
2584
- }
2585
2522
  if (contentStarted) {
2586
2523
  pushError(lineNumber, `Options like '${key}: ${value}' must appear before the first message or declaration`);
2587
2524
  continue;
@@ -2623,7 +2560,7 @@ function parseSequenceDgmo(content) {
2623
2560
  }
2624
2561
  }
2625
2562
  const { core: isACore, meta: isAMeta } = splitPipe(trimmed, lineNumber);
2626
- const isAMatch = isACore.match(IS_A_PATTERN);
2563
+ const isAMatch = !/^note(\s|$)/i.test(trimmed) ? isACore.match(IS_A_PATTERN) : null;
2627
2564
  if (isAMatch) {
2628
2565
  contentStarted = true;
2629
2566
  const id = isAMatch[1];
@@ -3053,7 +2990,7 @@ function looksLikeSequence(content) {
3053
2990
  return ARROW_PATTERN.test(trimmed);
3054
2991
  });
3055
2992
  }
3056
- var KNOWN_SEQ_OPTIONS, KNOWN_SEQ_BOOLEANS, VALID_PARTICIPANT_TYPES, IS_A_PATTERN, POSITION_ONLY_PATTERN, COLORED_PARTICIPANT_PATTERN, GROUP_HEADING_PATTERN, GROUP_HEADING_FALLBACK, LEGACY_GROUP_PATTERN, SECTION_PATTERN, ARROW_PATTERN, NOTE_SINGLE_COLON, NOTE_BARE, NOTE_MULTI;
2993
+ var KNOWN_SEQ_OPTIONS, KNOWN_SEQ_BOOLEANS, VALID_PARTICIPANT_TYPES, IS_A_PATTERN, POSITION_ONLY_PATTERN, COLORED_PARTICIPANT_PATTERN, GROUP_HEADING_PATTERN, GROUP_HEADING_FALLBACK, LEGACY_GROUP_PATTERN, SECTION_PATTERN, ARROW_PATTERN, NOTE_BARE, NOTE_MULTI;
3057
2994
  var init_parser = __esm({
3058
2995
  "src/sequence/parser.ts"() {
3059
2996
  "use strict";
@@ -3083,9 +3020,8 @@ var init_parser = __esm({
3083
3020
  LEGACY_GROUP_PATTERN = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
3084
3021
  SECTION_PATTERN = /^==\s+(.+?)(?:\s*==)?\s*$/;
3085
3022
  ARROW_PATTERN = /\S+\s*(?:<-\S+-|<~\S+~|-\S+->|~\S+~>|->|~>|<-|<~)\s*\S+/;
3086
- NOTE_SINGLE_COLON = /^note(?:\s+(right|left)(?:\s+(?:of\s+)?(.+?))?)?\s*:\s*(.+)$/i;
3087
3023
  NOTE_BARE = /^note\s+(.+)$/i;
3088
- NOTE_MULTI = /^note(?:\s+(right|left)(?:\s+(?:of\s+)?(.+?))?)?\s*:?\s*$/i;
3024
+ NOTE_MULTI = /^note(?:\s+(right|left)(?:\s+(?:of\s+)?(.+?))?)?\s*$/i;
3089
3025
  }
3090
3026
  });
3091
3027
 
@@ -3211,7 +3147,7 @@ function parseFlowchart(content, palette) {
3211
3147
  const lines = content.split("\n");
3212
3148
  const result = {
3213
3149
  type: "flowchart",
3214
- direction: "TB",
3150
+ direction: "LR",
3215
3151
  nodes: [],
3216
3152
  edges: [],
3217
3153
  options: {},
@@ -3228,9 +3164,6 @@ function parseFlowchart(content, palette) {
3228
3164
  const indentStack = [];
3229
3165
  let contentStarted = false;
3230
3166
  let firstLineParsed = false;
3231
- let currentGroup = null;
3232
- let groupIndent = -1;
3233
- const groups = [];
3234
3167
  function getOrCreateNode(ref, lineNumber) {
3235
3168
  const existing = nodeMap.get(ref.id);
3236
3169
  if (existing) return existing;
@@ -3239,14 +3172,10 @@ function parseFlowchart(content, palette) {
3239
3172
  label: ref.label,
3240
3173
  shape: ref.shape,
3241
3174
  lineNumber,
3242
- ...ref.color && { color: ref.color },
3243
- ...currentGroup && { group: currentGroup.id }
3175
+ ...ref.color && { color: ref.color }
3244
3176
  };
3245
3177
  nodeMap.set(ref.id, node);
3246
3178
  result.nodes.push(node);
3247
- if (currentGroup && !currentGroup.nodeIds.includes(ref.id)) {
3248
- currentGroup.nodeIds.push(ref.id);
3249
- }
3250
3179
  return node;
3251
3180
  }
3252
3181
  function addEdge(sourceId, targetId, lineNumber, label, color) {
@@ -3341,38 +3270,15 @@ function parseFlowchart(content, palette) {
3341
3270
  continue;
3342
3271
  }
3343
3272
  }
3344
- if (DOUBLE_HASH_RE.test(trimmed)) {
3345
- result.diagnostics.push(
3346
- makeDgmoError(lineNumber, "Use `#` for groups \u2014 nesting is done with indentation.", "error")
3347
- );
3348
- continue;
3349
- }
3350
- const hashGroupMatch = trimmed.match(GROUP_HASH_RE);
3351
- if (hashGroupMatch) {
3352
- const { label, color } = extractColor(hashGroupMatch[1].trim(), palette);
3353
- currentGroup = {
3354
- id: `group:${label.toLowerCase()}`,
3355
- label,
3356
- nodeIds: [],
3357
- lineNumber,
3358
- ...color && { color }
3359
- };
3360
- groupIndent = indent;
3361
- groups.push(currentGroup);
3362
- continue;
3363
- }
3364
3273
  if (!contentStarted) {
3274
+ if (/^direction-tb$/i.test(trimmed)) {
3275
+ result.direction = "TB";
3276
+ continue;
3277
+ }
3365
3278
  const optMatch = trimmed.match(OPTION_NOCOLON_RE);
3366
3279
  if (optMatch && !trimmed.includes("->")) {
3367
3280
  const key = optMatch[1].toLowerCase();
3368
3281
  const value = optMatch[2].trim();
3369
- if (key === "direction" || key === "orientation") {
3370
- const dir = normalizeDirection(value);
3371
- if (dir) {
3372
- result.direction = dir;
3373
- }
3374
- continue;
3375
- }
3376
3282
  if (key === "no-color") {
3377
3283
  result.options["color"] = "off";
3378
3284
  continue;
@@ -3381,13 +3287,8 @@ function parseFlowchart(content, palette) {
3381
3287
  continue;
3382
3288
  }
3383
3289
  }
3384
- if (currentGroup && indent <= groupIndent) {
3385
- currentGroup = null;
3386
- groupIndent = -1;
3387
- }
3388
3290
  processContentLine(trimmed, lineNumber, indent);
3389
3291
  }
3390
- if (groups.length > 0) result.groups = groups;
3391
3292
  if (result.nodes.length === 0 && !result.error) {
3392
3293
  const diag = makeDgmoError(1, "No nodes found. Add flowchart content with shape syntax like [Process] or (Start).");
3393
3294
  result.diagnostics.push(diag);
@@ -3526,7 +3427,7 @@ function parseState(content, palette) {
3526
3427
  const lines = content.split("\n");
3527
3428
  const result = {
3528
3429
  type: "state",
3529
- direction: "TB",
3430
+ direction: "LR",
3530
3431
  nodes: [],
3531
3432
  edges: [],
3532
3433
  options: {},
@@ -3615,17 +3516,14 @@ function parseState(content, palette) {
3615
3516
  continue;
3616
3517
  }
3617
3518
  if (!contentStarted) {
3519
+ if (/^direction-tb$/i.test(trimmed)) {
3520
+ result.direction = "TB";
3521
+ continue;
3522
+ }
3618
3523
  const optMatch = trimmed.match(OPTION_NOCOLON_RE);
3619
3524
  if (optMatch && !trimmed.includes("->")) {
3620
3525
  const key = optMatch[1].toLowerCase();
3621
3526
  const value = optMatch[2].trim();
3622
- if (key === "direction" || key === "orientation") {
3623
- const dir = normalizeDirection(value);
3624
- if (dir) {
3625
- result.direction = dir;
3626
- }
3627
- continue;
3628
- }
3629
3527
  if (key === "no-color") {
3630
3528
  result.options["color"] = "off";
3631
3529
  continue;
@@ -3846,6 +3744,10 @@ function parseClassDiagram(content, palette) {
3846
3744
  }
3847
3745
  }
3848
3746
  if (!contentStarted && indent === 0 && /^[a-z]/.test(trimmed)) {
3747
+ if (trimmed.toLowerCase() === "no-auto-color") {
3748
+ result.options["no-auto-color"] = "on";
3749
+ continue;
3750
+ }
3849
3751
  const optMatch = trimmed.match(OPTION_NOCOLON_RE);
3850
3752
  if (optMatch) {
3851
3753
  const key = optMatch[1].toLowerCase();
@@ -3857,6 +3759,21 @@ function parseClassDiagram(content, palette) {
3857
3759
  }
3858
3760
  }
3859
3761
  if (indent > 0 && currentClass) {
3762
+ const indentRel = trimmed.match(INDENT_REL_ARROW_RE);
3763
+ if (indentRel) {
3764
+ const arrow = indentRel[1];
3765
+ const targetName = indentRel[2];
3766
+ const label = indentRel[3]?.trim();
3767
+ getOrCreateClass(targetName, lineNumber);
3768
+ result.relationships.push({
3769
+ source: currentClass.id,
3770
+ target: classId(targetName),
3771
+ type: ARROW_TO_TYPE[arrow],
3772
+ ...label && { label },
3773
+ lineNumber
3774
+ });
3775
+ continue;
3776
+ }
3860
3777
  const member = parseMember(
3861
3778
  trimmed,
3862
3779
  lineNumber,
@@ -3874,16 +3791,13 @@ function parseClassDiagram(content, palette) {
3874
3791
  const sourceName = relArrow[1];
3875
3792
  const arrow = relArrow[2];
3876
3793
  const targetName = relArrow[3];
3877
- const label = relArrow[4]?.trim();
3878
- getOrCreateClass(sourceName, lineNumber);
3879
- getOrCreateClass(targetName, lineNumber);
3880
- result.relationships.push({
3881
- source: classId(sourceName),
3882
- target: classId(targetName),
3883
- type: ARROW_TO_TYPE[arrow],
3884
- ...label && { label },
3885
- lineNumber
3886
- });
3794
+ result.diagnostics.push(
3795
+ makeDgmoError(
3796
+ lineNumber,
3797
+ `Relationship "${sourceName} ${arrow} ${targetName}" must be indented under the source class "${sourceName}"`,
3798
+ "warning"
3799
+ )
3800
+ );
3887
3801
  continue;
3888
3802
  }
3889
3803
  const classDecl = trimmed.match(CLASS_DECL_RE);
@@ -3967,6 +3881,9 @@ function looksLikeClassDiagram(content) {
3967
3881
  if (/^[+\-#]?\s*\w+.*[:(]/.test(trimmed)) {
3968
3882
  hasIndentedMember = true;
3969
3883
  }
3884
+ if (INDENT_REL_ARROW_RE.test(trimmed)) {
3885
+ hasRelationship = true;
3886
+ }
3970
3887
  }
3971
3888
  }
3972
3889
  if (hasModifier) return true;
@@ -3979,6 +3896,7 @@ function extractSymbols2(docText) {
3979
3896
  for (const rawLine of docText.split("\n")) {
3980
3897
  const line10 = rawLine.trim();
3981
3898
  if (inMetadata && (/^[a-z-]+\s*:/i.test(line10) || /^class(\s|$)/i.test(line10))) continue;
3899
+ if (inMetadata && line10.toLowerCase() === "no-auto-color") continue;
3982
3900
  if (inMetadata && /^[a-z]/.test(line10) && OPTION_NOCOLON_RE.test(line10)) {
3983
3901
  const key = line10.match(OPTION_NOCOLON_RE)[1].toLowerCase();
3984
3902
  if (key !== "abstract" && key !== "interface" && key !== "enum") continue;
@@ -3997,7 +3915,7 @@ function extractSymbols2(docText) {
3997
3915
  keywords: ["extends", "implements", "abstract", "interface", "enum"]
3998
3916
  };
3999
3917
  }
4000
- var CLASS_DECL_RE, REL_ARROW_RE, VISIBILITY_RE, STATIC_SUFFIX_RE, METHOD_RE, FIELD_RE, ARROW_TO_TYPE;
3918
+ var CLASS_DECL_RE, INDENT_REL_ARROW_RE, REL_ARROW_RE, VISIBILITY_RE, STATIC_SUFFIX_RE, METHOD_RE, FIELD_RE, ARROW_TO_TYPE;
4001
3919
  var init_parser2 = __esm({
4002
3920
  "src/class/parser.ts"() {
4003
3921
  "use strict";
@@ -4005,6 +3923,7 @@ var init_parser2 = __esm({
4005
3923
  init_diagnostics();
4006
3924
  init_parsing();
4007
3925
  CLASS_DECL_RE = /^(?:(abstract|interface|enum)\s+)?([A-Z][A-Za-z0-9_]*)(?:\s+(extends|implements)\s+([A-Z][A-Za-z0-9_]*))?(?:\s+\[(abstract|interface|enum)\])?(?:\s+\(([^)]+)\))?\s*$/;
3926
+ INDENT_REL_ARROW_RE = /^(--\|>|\.\.\|>|\*--|o--|\.\.\>|->)\s*([A-Z][A-Za-z0-9_]*)(?:\s+:?\s*(.+))?$/;
4008
3927
  REL_ARROW_RE = /^([A-Z][A-Za-z0-9_]*)\s*(--\|>|\.\.\|>|\*--|o--|\.\.\>|->)\s*([A-Z][A-Za-z0-9_]*)(?:\s+:?\s*(.+))?$/;
4009
3928
  VISIBILITY_RE = /^([+\-#])\s*/;
4010
3929
  STATIC_SUFFIX_RE = /\{static\}\s*$/;
@@ -4150,13 +4069,6 @@ function parseERDiagram(content, palette) {
4150
4069
  if (!contentStarted && indent === 0) {
4151
4070
  const tagBlockMatch = matchTagBlockHeading(trimmed);
4152
4071
  if (tagBlockMatch) {
4153
- if (tagBlockMatch.deprecated) {
4154
- result.diagnostics.push(makeDgmoError(
4155
- lineNumber,
4156
- `'## ${tagBlockMatch.name}' is no longer supported \u2014 use 'tag: ${tagBlockMatch.name}' instead`
4157
- ));
4158
- continue;
4159
- }
4160
4072
  currentTagGroup = {
4161
4073
  name: tagBlockMatch.name,
4162
4074
  alias: tagBlockMatch.alias,
@@ -4233,15 +4145,13 @@ function parseERDiagram(content, palette) {
4233
4145
  contentStarted = true;
4234
4146
  const rel = parseRelationship(trimmed, lineNumber, pushError);
4235
4147
  if (rel) {
4236
- getOrCreateTable(rel.source, lineNumber);
4237
- getOrCreateTable(rel.target, lineNumber);
4238
- result.relationships.push({
4239
- source: tableId(rel.source),
4240
- target: tableId(rel.target),
4241
- cardinality: { from: rel.from, to: rel.to },
4242
- ...rel.label && { label: rel.label },
4243
- lineNumber
4244
- });
4148
+ result.diagnostics.push(
4149
+ makeDgmoError(
4150
+ lineNumber,
4151
+ `Relationship "${rel.source} \u2192 ${rel.target}" must be indented under the source table "${rel.source}"`,
4152
+ "warning"
4153
+ )
4154
+ );
4245
4155
  continue;
4246
4156
  }
4247
4157
  const tableDecl = trimmed.match(TABLE_DECL_RE);
@@ -4450,6 +4360,12 @@ function parseChart(content, palette) {
4450
4360
  }
4451
4361
  const spaceIdx = trimmed.indexOf(" ");
4452
4362
  const firstToken = (spaceIdx >= 0 ? trimmed.substring(0, spaceIdx) : trimmed).toLowerCase();
4363
+ if (KNOWN_BOOLEANS.has(firstToken) && spaceIdx < 0) {
4364
+ if (firstToken === "orientation-horizontal") {
4365
+ result.orientation = "horizontal";
4366
+ }
4367
+ continue;
4368
+ }
4453
4369
  if (KNOWN_OPTIONS2.has(firstToken) && spaceIdx >= 0) {
4454
4370
  const value = trimmed.substring(spaceIdx + 1).trim();
4455
4371
  if (firstToken === "chart") {
@@ -4484,26 +4400,6 @@ function parseChart(content, palette) {
4484
4400
  result.label = value;
4485
4401
  continue;
4486
4402
  }
4487
- if (firstToken === "labels") {
4488
- const v = value.toLowerCase();
4489
- if (v === "name" || v === "value" || v === "percent" || v === "full") {
4490
- result.labels = v;
4491
- }
4492
- continue;
4493
- }
4494
- if (firstToken === "orientation" || firstToken === "direction") {
4495
- if (result.type === "bar" || result.type === "bar-stacked") {
4496
- const vLower = value.toLowerCase();
4497
- if (vLower === "horizontal" || vLower === "vertical") {
4498
- result.orientation = vLower;
4499
- } else {
4500
- const dir = normalizeDirection(value);
4501
- if (dir === "LR") result.orientation = "horizontal";
4502
- else if (dir === "TB") result.orientation = "vertical";
4503
- }
4504
- }
4505
- continue;
4506
- }
4507
4403
  if (firstToken === "color") {
4508
4404
  result.color = resolveColor(value.trim(), palette) ?? void 0;
4509
4405
  continue;
@@ -4521,6 +4417,18 @@ function parseChart(content, palette) {
4521
4417
  continue;
4522
4418
  }
4523
4419
  }
4420
+ if (firstToken === "no-label-name") {
4421
+ result.noLabelName = true;
4422
+ continue;
4423
+ }
4424
+ if (firstToken === "no-label-value") {
4425
+ result.noLabelValue = true;
4426
+ continue;
4427
+ }
4428
+ if (firstToken === "no-label-percent") {
4429
+ result.noLabelPercent = true;
4430
+ continue;
4431
+ }
4524
4432
  if (firstToken === "series" && spaceIdx === -1) {
4525
4433
  const parsed = parseSeriesNames("", lines, i, palette);
4526
4434
  i = parsed.newIndex;
@@ -4533,7 +4441,8 @@ function parseChart(content, palette) {
4533
4441
  if (parsed.nameColors.some(Boolean)) result.seriesNameColors = parsed.nameColors;
4534
4442
  continue;
4535
4443
  }
4536
- const dataValues = parseDataRowValues(trimmed);
4444
+ const multiValue = (result.seriesNames?.length ?? 0) >= 2;
4445
+ const dataValues = parseDataRowValues(trimmed, { multiValue });
4537
4446
  if (dataValues) {
4538
4447
  const { label: rawLabel, color: pointColor } = extractColor(dataValues.label, palette);
4539
4448
  const [first, ...rest] = dataValues.values;
@@ -4589,7 +4498,7 @@ function parseChart(content, palette) {
4589
4498
  for (const dp of result.data) {
4590
4499
  const actualCount = 1 + (dp.extraValues?.length ?? 0);
4591
4500
  if (actualCount !== expectedCount) {
4592
- warn(dp.lineNumber, `Data point "${dp.label}" has ${actualCount} value(s), but ${expectedCount} series defined. Each row must have ${expectedCount} comma-separated values.`);
4501
+ warn(dp.lineNumber, `Data point "${dp.label}" has ${actualCount} value(s), but ${expectedCount} series defined. Each row must have ${expectedCount} values.`);
4593
4502
  }
4594
4503
  }
4595
4504
  result.data = result.data.filter((dp) => {
@@ -4599,12 +4508,12 @@ function parseChart(content, palette) {
4599
4508
  }
4600
4509
  return result;
4601
4510
  }
4602
- function parseDataRowValues(line10) {
4511
+ function parseDataRowValues(line10, options) {
4603
4512
  const segments = line10.split(",");
4604
4513
  const normalized = [];
4605
4514
  for (let i = 0; i < segments.length; i++) {
4606
4515
  const seg = segments[i].trim();
4607
- if (i > 0 && /^\d{3}$/.test(seg) && !/^\s/.test(segments[i])) {
4516
+ if (i > 0 && /^\d{3}(\.\d+)?$/.test(seg) && !/^\s/.test(segments[i])) {
4608
4517
  const prevSeg = normalized[normalized.length - 1].trimEnd();
4609
4518
  if (/\d{1,3}$/.test(prevSeg)) {
4610
4519
  const prevMatch = prevSeg.match(/(\d{1,3})$/);
@@ -4633,11 +4542,11 @@ function parseDataRowValues(line10) {
4633
4542
  const splitAt = commaParts.length - numericCount;
4634
4543
  const extraValueParts = commaParts.slice(splitAt);
4635
4544
  const firstPart = commaParts.slice(0, splitAt).join(",").trim();
4636
- const lastSpaceIdx2 = firstPart.lastIndexOf(" ");
4637
- if (lastSpaceIdx2 >= 0) {
4638
- const possibleFirstVal = firstPart.substring(lastSpaceIdx2 + 1).trim();
4545
+ const lastSpaceIdx = firstPart.lastIndexOf(" ");
4546
+ if (lastSpaceIdx >= 0) {
4547
+ const possibleFirstVal = firstPart.substring(lastSpaceIdx + 1).trim();
4639
4548
  if (possibleFirstVal && !isNaN(parseFloat(possibleFirstVal)) && isFinite(Number(possibleFirstVal))) {
4640
- const label2 = firstPart.substring(0, lastSpaceIdx2).trim();
4549
+ const label2 = firstPart.substring(0, lastSpaceIdx).trim();
4641
4550
  if (label2) {
4642
4551
  const values = [parseFloat(possibleFirstVal)];
4643
4552
  for (const p of extraValueParts) {
@@ -4649,16 +4558,31 @@ function parseDataRowValues(line10) {
4649
4558
  }
4650
4559
  }
4651
4560
  }
4652
- const lastSpaceIdx = rebuilt.lastIndexOf(" ");
4653
- if (lastSpaceIdx < 0) return null;
4654
- const possibleValue = rebuilt.substring(lastSpaceIdx + 1).trim();
4655
- const num = parseFloat(possibleValue);
4656
- if (isNaN(num) || !isFinite(Number(possibleValue))) return null;
4657
- const label = rebuilt.substring(0, lastSpaceIdx).trim();
4561
+ const tokens = rebuilt.split(/\s+/);
4562
+ if (tokens.length < 2) return null;
4563
+ if (options?.multiValue) {
4564
+ const values = [];
4565
+ let idx = tokens.length - 1;
4566
+ while (idx >= 1) {
4567
+ const tok = tokens[idx];
4568
+ const num2 = parseFloat(tok);
4569
+ if (isNaN(num2) || !isFinite(Number(tok))) break;
4570
+ values.unshift(num2);
4571
+ idx--;
4572
+ }
4573
+ if (values.length === 0) return null;
4574
+ const label2 = tokens.slice(0, idx + 1).join(" ");
4575
+ if (!label2) return null;
4576
+ return { label: label2, values };
4577
+ }
4578
+ const lastToken = tokens[tokens.length - 1];
4579
+ const num = parseFloat(lastToken);
4580
+ if (isNaN(num) || !isFinite(Number(lastToken))) return null;
4581
+ const label = tokens.slice(0, -1).join(" ");
4658
4582
  if (!label) return null;
4659
4583
  return { label, values: [num] };
4660
4584
  }
4661
- var VALID_TYPES, TYPE_ALIASES, KNOWN_OPTIONS2;
4585
+ var VALID_TYPES, TYPE_ALIASES, KNOWN_OPTIONS2, KNOWN_BOOLEANS;
4662
4586
  var init_chart = __esm({
4663
4587
  "src/chart.ts"() {
4664
4588
  "use strict";
@@ -4685,11 +4609,14 @@ var init_chart = __esm({
4685
4609
  "xlabel",
4686
4610
  "ylabel",
4687
4611
  "label",
4688
- "labels",
4689
- "orientation",
4690
- "direction",
4612
+ "no-label-name",
4613
+ "no-label-value",
4614
+ "no-label-percent",
4691
4615
  "color"
4692
4616
  ]);
4617
+ KNOWN_BOOLEANS = /* @__PURE__ */ new Set([
4618
+ "orientation-horizontal"
4619
+ ]);
4693
4620
  }
4694
4621
  });
4695
4622
 
@@ -4784,7 +4711,7 @@ var init_legend_svg = __esm({
4784
4711
  // src/echarts.ts
4785
4712
  import * as echarts from "echarts";
4786
4713
  function parseScatterRow(line10, palette, currentCategory, lineNumber) {
4787
- const dataRow = parseDataRowValues(line10);
4714
+ const dataRow = parseDataRowValues(line10, { multiValue: true });
4788
4715
  if (!dataRow || dataRow.values.length < 2) return null;
4789
4716
  const { label: rawLabel, color: pointColor } = extractColor(dataRow.label, palette);
4790
4717
  return {
@@ -4978,13 +4905,9 @@ function parseExtendedChart(content, palette) {
4978
4905
  result.sizelabel = value;
4979
4906
  continue;
4980
4907
  }
4981
- if (firstToken === "labels") {
4982
- result.showLabels = value.toLowerCase() === "on" || value.toLowerCase() === "true";
4983
- continue;
4984
- }
4985
4908
  if (firstToken === "columns") {
4986
4909
  if (value) {
4987
- result.columns = value.split(",").map((s) => s.trim());
4910
+ result.columns = value.includes(",") ? value.split(",").map((s) => s.trim()) : value.split(/\s+/);
4988
4911
  } else {
4989
4912
  const collected = collectIndentedValues(lines, i);
4990
4913
  i = collected.newIndex;
@@ -4994,7 +4917,7 @@ function parseExtendedChart(content, palette) {
4994
4917
  }
4995
4918
  if (firstToken === "rows") {
4996
4919
  if (value) {
4997
- result.rows = value.split(",").map((s) => s.trim());
4920
+ result.rows = value.includes(",") ? value.split(",").map((s) => s.trim()) : value.split(/\s+/);
4998
4921
  } else {
4999
4922
  const collected = collectIndentedValues(lines, i);
5000
4923
  i = collected.newIndex;
@@ -5013,6 +4936,14 @@ function parseExtendedChart(content, palette) {
5013
4936
  continue;
5014
4937
  }
5015
4938
  }
4939
+ if (firstToken === "no-labels") {
4940
+ result.showLabels = false;
4941
+ continue;
4942
+ }
4943
+ if (firstToken === "shade") {
4944
+ result.shade = true;
4945
+ continue;
4946
+ }
5016
4947
  if (firstToken === "series" && spaceIdx === -1) {
5017
4948
  const parsed = parseSeriesNames("", lines, i, palette);
5018
4949
  i = parsed.newIndex;
@@ -5061,7 +4992,7 @@ function parseExtendedChart(content, palette) {
5061
4992
  }
5062
4993
  }
5063
4994
  if (result.type === "heatmap") {
5064
- const dataRow2 = parseDataRowValues(trimmed);
4995
+ const dataRow2 = parseDataRowValues(trimmed, { multiValue: true });
5065
4996
  if (dataRow2 && dataRow2.values.length > 0) {
5066
4997
  if (!result.heatmapRows) result.heatmapRows = [];
5067
4998
  result.heatmapRows.push({ label: dataRow2.label, values: dataRow2.values, lineNumber });
@@ -5390,6 +5321,12 @@ function buildFunctionOption(parsed, palette, textColor, axisLineColor, gridOpac
5390
5321
  itemStyle: {
5391
5322
  color: fnColor
5392
5323
  },
5324
+ ...parsed.shade && {
5325
+ areaStyle: {
5326
+ color: fnColor,
5327
+ opacity: 0.15
5328
+ }
5329
+ },
5393
5330
  emphasis: EMPHASIS_SELF
5394
5331
  };
5395
5332
  });
@@ -5637,7 +5574,7 @@ function buildScatterOption(parsed, palette, textColor, axisLineColor, gridOpaci
5637
5574
  const defaultSize = 15;
5638
5575
  const hasCategories = points.some((p) => p.category !== void 0);
5639
5576
  const hasSize = points.some((p) => p.size !== void 0);
5640
- const showLabels = parsed.showLabels ?? false;
5577
+ const showLabels = parsed.showLabels ?? true;
5641
5578
  const labelFontSize = 11;
5642
5579
  const labelConfig = {
5643
5580
  show: false,
@@ -6325,17 +6262,19 @@ function buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineCol
6325
6262
  ]
6326
6263
  };
6327
6264
  }
6328
- function segmentLabelFormatter(mode) {
6329
- switch (mode) {
6330
- case "name":
6331
- return "{b}";
6332
- case "value":
6333
- return "{b} \u2014 {c}";
6334
- case "percent":
6335
- return "{b} \u2014 {d}%";
6336
- default:
6337
- return "{b} \u2014 {c} ({d}%)";
6338
- }
6265
+ function segmentLabelFormatter(parsed) {
6266
+ const showName = !parsed.noLabelName;
6267
+ const showValue = !parsed.noLabelValue;
6268
+ const showPercent = !parsed.noLabelPercent;
6269
+ const parts = [];
6270
+ if (showName) parts.push("{b}");
6271
+ if (showValue) parts.push("{c}");
6272
+ if (showPercent) parts.push("{d}%");
6273
+ if (parts.length === 0) return "{b}";
6274
+ if (parts.length === 1) return parts[0];
6275
+ if (showName && showValue && showPercent) return "{b} \u2014 {c} ({d}%)";
6276
+ if (showName) return "{b} \u2014 " + parts.slice(1).join(" ");
6277
+ return parts.join(" ");
6339
6278
  }
6340
6279
  function buildPieOption(parsed, textColor, colors, bg, titleConfig, tooltipTheme, isDoughnut) {
6341
6280
  const HIDE_AXES = { xAxis: { show: false }, yAxis: { show: false } };
@@ -6362,7 +6301,7 @@ function buildPieOption(parsed, textColor, colors, bg, titleConfig, tooltipTheme
6362
6301
  data,
6363
6302
  label: {
6364
6303
  position: "outside",
6365
- formatter: segmentLabelFormatter(parsed.labels),
6304
+ formatter: segmentLabelFormatter(parsed),
6366
6305
  color: textColor,
6367
6306
  fontFamily: FONT_FAMILY
6368
6307
  },
@@ -6457,7 +6396,7 @@ function buildPolarAreaOption(parsed, textColor, colors, bg, titleConfig, toolti
6457
6396
  data,
6458
6397
  label: {
6459
6398
  position: "outside",
6460
- formatter: segmentLabelFormatter(parsed.labels),
6399
+ formatter: segmentLabelFormatter(parsed),
6461
6400
  color: textColor,
6462
6401
  fontFamily: FONT_FAMILY
6463
6402
  },
@@ -6625,7 +6564,7 @@ var init_echarts = __esm({
6625
6564
  "xlabel",
6626
6565
  "ylabel",
6627
6566
  "sizelabel",
6628
- "labels",
6567
+ "no-labels",
6629
6568
  "columns",
6630
6569
  "rows",
6631
6570
  "x"
@@ -6728,10 +6667,6 @@ function parseOrg(content, palette) {
6728
6667
  pushError(lineNumber, "Tag groups must appear before org content");
6729
6668
  continue;
6730
6669
  }
6731
- if (tagBlockMatch.deprecated) {
6732
- pushError(lineNumber, `'## ${tagBlockMatch.name}' is no longer supported \u2014 use 'tag: ${tagBlockMatch.name}' instead`);
6733
- continue;
6734
- }
6735
6670
  currentTagGroup = {
6736
6671
  name: tagBlockMatch.name,
6737
6672
  alias: tagBlockMatch.alias,
@@ -6753,7 +6688,7 @@ function parseOrg(content, palette) {
6753
6688
  continue;
6754
6689
  }
6755
6690
  }
6756
- if (KNOWN_BOOLEANS.has(trimmed.toLowerCase())) {
6691
+ if (KNOWN_BOOLEANS2.has(trimmed.toLowerCase())) {
6757
6692
  result.options[trimmed.toLowerCase()] = "on";
6758
6693
  continue;
6759
6694
  }
@@ -6842,7 +6777,7 @@ function parseNodeLabel(trimmed, _indent, lineNumber, palette, counter, aliasMap
6842
6777
  const segments = trimmed.split("|").map((s) => s.trim());
6843
6778
  let rawLabel = segments[0];
6844
6779
  const { label, color } = extractColor(rawLabel, palette);
6845
- const metadata = parsePipeMetadata(segments, aliasMap, warnFn ? () => warnFn(lineNumber, MULTIPLE_PIPE_WARNING) : void 0);
6780
+ const metadata = parsePipeMetadata(segments, aliasMap, warnFn ? () => warnFn(lineNumber, MULTIPLE_PIPE_ERROR) : void 0);
6846
6781
  return {
6847
6782
  id: `node-${counter}`,
6848
6783
  label,
@@ -6880,7 +6815,7 @@ function findMetadataParent(indent, indentStack) {
6880
6815
  }
6881
6816
  return null;
6882
6817
  }
6883
- var CONTAINER_RE, METADATA_RE, KNOWN_OPTIONS3, KNOWN_BOOLEANS;
6818
+ var CONTAINER_RE, METADATA_RE, KNOWN_OPTIONS3, KNOWN_BOOLEANS2;
6884
6819
  var init_parser4 = __esm({
6885
6820
  "src/org/parser.ts"() {
6886
6821
  "use strict";
@@ -6890,13 +6825,13 @@ var init_parser4 = __esm({
6890
6825
  CONTAINER_RE = /^\[([^\]]+)\]$/;
6891
6826
  METADATA_RE = /^([^:]+):\s*(.+)$/;
6892
6827
  KNOWN_OPTIONS3 = /* @__PURE__ */ new Set([
6893
- "direction",
6894
6828
  "sub-node-label",
6895
6829
  "hide",
6896
6830
  "show-sub-node-count"
6897
6831
  ]);
6898
- KNOWN_BOOLEANS = /* @__PURE__ */ new Set([
6899
- "show-sub-node-count"
6832
+ KNOWN_BOOLEANS2 = /* @__PURE__ */ new Set([
6833
+ "show-sub-node-count",
6834
+ "direction-tb"
6900
6835
  ]);
6901
6836
  }
6902
6837
  });
@@ -6976,10 +6911,6 @@ function parseKanban(content, palette) {
6976
6911
  if (!contentStarted) {
6977
6912
  const tagBlockMatch = matchTagBlockHeading(trimmed);
6978
6913
  if (tagBlockMatch) {
6979
- if (tagBlockMatch.deprecated) {
6980
- result.diagnostics.push(makeDgmoError(lineNumber, `'## ${tagBlockMatch.name}' is no longer supported \u2014 use 'tag: ${tagBlockMatch.name}' instead`));
6981
- continue;
6982
- }
6983
6914
  currentTagGroup = {
6984
6915
  name: tagBlockMatch.name,
6985
6916
  alias: tagBlockMatch.alias,
@@ -7002,7 +6933,7 @@ function parseKanban(content, palette) {
7002
6933
  continue;
7003
6934
  }
7004
6935
  }
7005
- if (KNOWN_BOOLEANS2.has(trimmed.toLowerCase()) && !COLUMN_RE.test(trimmed)) {
6936
+ if (KNOWN_BOOLEANS3.has(trimmed.toLowerCase()) && !COLUMN_RE.test(trimmed)) {
7006
6937
  result.options[trimmed.toLowerCase()] = "on";
7007
6938
  continue;
7008
6939
  }
@@ -7180,7 +7111,7 @@ function parseCardLine(trimmed, lineNumber, counter, aliasMap, palette) {
7180
7111
  color
7181
7112
  };
7182
7113
  }
7183
- var COLUMN_RE, LEGACY_COLUMN_RE, KNOWN_OPTIONS4, KNOWN_BOOLEANS2;
7114
+ var COLUMN_RE, LEGACY_COLUMN_RE, KNOWN_OPTIONS4, KNOWN_BOOLEANS3;
7184
7115
  var init_parser5 = __esm({
7185
7116
  "src/kanban/parser.ts"() {
7186
7117
  "use strict";
@@ -7191,10 +7122,11 @@ var init_parser5 = __esm({
7191
7122
  COLUMN_RE = /^\[(.+?)\](?:\s*\(([^)]+)\))?\s*(?:\|\s*(.+))?$/;
7192
7123
  LEGACY_COLUMN_RE = /^==\s+(.+?)\s*(?:\[wip:\s*(\d+)\])?\s*==$/;
7193
7124
  KNOWN_OPTIONS4 = /* @__PURE__ */ new Set([
7194
- "color-off",
7195
7125
  "hide"
7196
7126
  ]);
7197
- KNOWN_BOOLEANS2 = /* @__PURE__ */ new Set([]);
7127
+ KNOWN_BOOLEANS3 = /* @__PURE__ */ new Set([
7128
+ "no-auto-color"
7129
+ ]);
7198
7130
  }
7199
7131
  });
7200
7132
 
@@ -7307,10 +7239,6 @@ function parseC4(content, palette) {
7307
7239
  pushError(lineNumber, "Tag groups must appear before content");
7308
7240
  continue;
7309
7241
  }
7310
- if (tagBlockMatch.deprecated) {
7311
- pushError(lineNumber, `'## ${tagBlockMatch.name}' is no longer supported \u2014 use 'tag: ${tagBlockMatch.name}' instead`);
7312
- continue;
7313
- }
7314
7242
  currentTagGroup = {
7315
7243
  name: tagBlockMatch.name,
7316
7244
  alias: tagBlockMatch.alias,
@@ -7324,6 +7252,10 @@ function parseC4(content, palette) {
7324
7252
  continue;
7325
7253
  }
7326
7254
  if (!contentStarted && !currentTagGroup && measureIndent(line10) === 0) {
7255
+ if (KNOWN_C4_BOOLEANS.has(trimmed.toLowerCase())) {
7256
+ result.options[trimmed.toLowerCase()] = "on";
7257
+ continue;
7258
+ }
7327
7259
  const optMatch = trimmed.match(OPTION_NOCOLON_RE);
7328
7260
  if (optMatch) {
7329
7261
  const key = optMatch[1].trim().toLowerCase();
@@ -7385,7 +7317,7 @@ function parseC4(content, palette) {
7385
7317
  }
7386
7318
  const segments = trimmed.split("|").map((s) => s.trim());
7387
7319
  const nodeName = segments[0];
7388
- const metadata = parsePipeMetadata(segments, aliasMap, () => pushError(lineNumber, MULTIPLE_PIPE_WARNING, "warning"));
7320
+ const metadata = parsePipeMetadata(segments, aliasMap, () => pushError(lineNumber, MULTIPLE_PIPE_ERROR));
7389
7321
  const shape = inferC4Shape(nodeName, metadata.tech ?? metadata.technology);
7390
7322
  const dNode = {
7391
7323
  name: nodeName,
@@ -7598,7 +7530,7 @@ function parseC4(content, palette) {
7598
7530
  }
7599
7531
  namePart = namePart.substring(0, nameIsAMatch.index).trim();
7600
7532
  }
7601
- const metadata = parsePipeMetadata(segments, aliasMap, () => pushError(lineNumber, MULTIPLE_PIPE_WARNING, "warning"));
7533
+ const metadata = parsePipeMetadata(segments, aliasMap, () => pushError(lineNumber, MULTIPLE_PIPE_ERROR));
7602
7534
  const shape = explicitShape ?? inferC4Shape(namePart, metadata.tech ?? metadata.technology);
7603
7535
  const element = {
7604
7536
  name: namePart,
@@ -7646,7 +7578,7 @@ function parseC4(content, palette) {
7646
7578
  lineNumber,
7647
7579
  `'${elementMatch[1]} ${namePart}' prefix syntax is no longer supported \u2014 use '${namePart} is a ${elementType}' instead`
7648
7580
  );
7649
- const metadata = parsePipeMetadata(segments, aliasMap, () => pushError(lineNumber, MULTIPLE_PIPE_WARNING, "warning"));
7581
+ const metadata = parsePipeMetadata(segments, aliasMap, () => pushError(lineNumber, MULTIPLE_PIPE_ERROR));
7650
7582
  const shape = explicitShape ?? inferC4Shape(namePart, metadata.tech ?? metadata.technology);
7651
7583
  const element = {
7652
7584
  name: namePart,
@@ -7792,7 +7724,7 @@ function validateDeploymentRefs(result, knownNames, pushWarning) {
7792
7724
  }
7793
7725
  walkDeploy(result.deployment);
7794
7726
  }
7795
- var CONTAINER_RE2, ELEMENT_RE, IS_A_RE, C4_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, KNOWN_C4_OPTIONS, ALL_CHART_TYPES2;
7727
+ var CONTAINER_RE2, ELEMENT_RE, IS_A_RE, C4_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, KNOWN_C4_OPTIONS, KNOWN_C4_BOOLEANS, ALL_CHART_TYPES2;
7796
7728
  var init_parser6 = __esm({
7797
7729
  "src/c4/parser.ts"() {
7798
7730
  "use strict";
@@ -7811,7 +7743,7 @@ var init_parser6 = __esm({
7811
7743
  C4_LABELED_BIDI_ASYNC_RE = /^<~(.+)~>\s*(.+)$/;
7812
7744
  SECTION_HEADER_RE = /^(containers|components|deployment)\s*$/i;
7813
7745
  CONTAINER_REF_RE = /^container\s+(.+)$/i;
7814
- METADATA_RE2 = /^([a-z][a-z0-9-]*)\s+(.+)$/i;
7746
+ METADATA_RE2 = /^([a-z][a-z0-9-]*):\s+(.+)$/i;
7815
7747
  VALID_ELEMENT_TYPES = /* @__PURE__ */ new Set([
7816
7748
  "person",
7817
7749
  "system",
@@ -7827,8 +7759,10 @@ var init_parser6 = __esm({
7827
7759
  "external"
7828
7760
  ]);
7829
7761
  KNOWN_C4_OPTIONS = /* @__PURE__ */ new Set([
7830
- "layout",
7831
- "direction"
7762
+ "layout"
7763
+ ]);
7764
+ KNOWN_C4_BOOLEANS = /* @__PURE__ */ new Set([
7765
+ "direction-tb"
7832
7766
  ]);
7833
7767
  ALL_CHART_TYPES2 = [
7834
7768
  "c4",
@@ -7991,10 +7925,10 @@ function parseInitiativeStatus(content) {
7991
7925
  if (hideMatch && !trimmed.match(/^hide\s*\|/)) {
7992
7926
  const pairs = hideMatch[1].split(",");
7993
7927
  for (const pair of pairs) {
7994
- const tokens = pair.trim().split(/\s+/);
7995
- if (tokens.length >= 2) {
7996
- const groupKey = tokens[0].toLowerCase();
7997
- const value = tokens.slice(1).join(" ").toLowerCase();
7928
+ const colonIdx = pair.indexOf(":");
7929
+ if (colonIdx > 0) {
7930
+ const groupKey = pair.substring(0, colonIdx).trim().toLowerCase();
7931
+ const value = pair.substring(colonIdx + 1).trim().toLowerCase();
7998
7932
  if (groupKey && value) {
7999
7933
  if (!result.initialHiddenTagValues.has(groupKey)) {
8000
7934
  result.initialHiddenTagValues.set(groupKey, /* @__PURE__ */ new Set());
@@ -8010,7 +7944,7 @@ function parseInitiativeStatus(content) {
8010
7944
  if (optMatch) {
8011
7945
  const key = optMatch[1].toLowerCase();
8012
7946
  const value = optMatch[2].trim();
8013
- if (key === "active-tag" || key === "sort") {
7947
+ if (key === "active-tag") {
8014
7948
  result.options[key] = value;
8015
7949
  continue;
8016
7950
  }
@@ -8024,12 +7958,6 @@ function parseInitiativeStatus(content) {
8024
7958
  );
8025
7959
  continue;
8026
7960
  }
8027
- if (tagBlockMatch.deprecated) {
8028
- result.diagnostics.push(
8029
- makeDgmoError(lineNum, `'## ${tagBlockMatch.name}' is no longer supported \u2014 use 'tag ${tagBlockMatch.name}' instead`)
8030
- );
8031
- continue;
8032
- }
8033
7961
  currentTagGroup = {
8034
7962
  name: tagBlockMatch.name,
8035
7963
  alias: tagBlockMatch.alias,
@@ -8292,7 +8220,7 @@ function looksLikeSitemap(content) {
8292
8220
  for (const line10 of lines) {
8293
8221
  const trimmed = line10.trim();
8294
8222
  if (!trimmed || trimmed.startsWith("//")) continue;
8295
- if (CHART_TYPE_RE.test(trimmed) || TITLE_RE.test(trimmed)) continue;
8223
+ if (parseFirstLine(trimmed) || TITLE_RE.test(trimmed)) continue;
8296
8224
  if (isTagBlockHeading(trimmed)) continue;
8297
8225
  if (/^-.*->\s*.+/.test(trimmed) || /^->\s*.+/.test(trimmed)) {
8298
8226
  hasArrow = true;
@@ -8309,7 +8237,7 @@ function parseSitemap(content, palette) {
8309
8237
  const result = {
8310
8238
  title: null,
8311
8239
  titleLineNumber: null,
8312
- direction: "TB",
8240
+ direction: "LR",
8313
8241
  roots: [],
8314
8242
  edges: [],
8315
8243
  tagGroups: [],
@@ -8379,10 +8307,6 @@ function parseSitemap(content, palette) {
8379
8307
  pushError(lineNumber, "Tag groups must appear before sitemap content");
8380
8308
  continue;
8381
8309
  }
8382
- if (tagBlockMatch.deprecated) {
8383
- pushError(lineNumber, `'## ${tagBlockMatch.name}' is no longer supported \u2014 use 'tag: ${tagBlockMatch.name}' instead`);
8384
- continue;
8385
- }
8386
8310
  currentTagGroup = {
8387
8311
  name: tagBlockMatch.name,
8388
8312
  alias: tagBlockMatch.alias,
@@ -8396,16 +8320,13 @@ function parseSitemap(content, palette) {
8396
8320
  continue;
8397
8321
  }
8398
8322
  if (!contentStarted && !currentTagGroup && measureIndent(line10) === 0 && !trimmed.includes("|") && !trimmed.includes("->")) {
8323
+ if (/^direction-tb$/i.test(trimmed)) {
8324
+ result.direction = "TB";
8325
+ continue;
8326
+ }
8399
8327
  const optMatch = trimmed.match(OPTION_NOCOLON_RE);
8400
8328
  if (optMatch) {
8401
8329
  const key = optMatch[1].trim().toLowerCase();
8402
- if (key === "direction" || key === "orientation") {
8403
- const dir = normalizeDirection(optMatch[2]);
8404
- if (dir) {
8405
- result.direction = dir;
8406
- }
8407
- continue;
8408
- }
8409
8330
  result.options[key] = optMatch[2].trim();
8410
8331
  continue;
8411
8332
  }
@@ -8540,7 +8461,7 @@ function parseNodeLabel2(trimmed, lineNumber, palette, counter, aliasMap = /* @_
8540
8461
  const segments = trimmed.split("|").map((s) => s.trim());
8541
8462
  const rawLabel = segments[0];
8542
8463
  const { label, color } = extractColor(rawLabel, palette);
8543
- const metadata = parsePipeMetadata(segments, aliasMap, warnFn ? () => warnFn(lineNumber, MULTIPLE_PIPE_WARNING) : void 0);
8464
+ const metadata = parsePipeMetadata(segments, aliasMap, warnFn ? () => warnFn(lineNumber, MULTIPLE_PIPE_ERROR) : void 0);
8544
8465
  return {
8545
8466
  id: `node-${counter}`,
8546
8467
  label,
@@ -8733,14 +8654,8 @@ function parseInfra(content) {
8733
8654
  }
8734
8655
  continue;
8735
8656
  }
8736
- if (/^(?:direction|orientation)\s/i.test(trimmed)) {
8737
- const raw2 = trimmed.replace(/^(?:direction|orientation)\s+/i, "").trim();
8738
- const dir = normalizeDirection(raw2);
8739
- if (dir) {
8740
- result.direction = dir;
8741
- } else {
8742
- warn(lineNumber, `Unknown direction '${raw2}'. Expected 'LR', 'TB', 'horizontal', or 'vertical'.`);
8743
- }
8657
+ if (/^direction-tb$/i.test(trimmed)) {
8658
+ result.direction = "TB";
8744
8659
  continue;
8745
8660
  }
8746
8661
  if (trimmed === "animate") {
@@ -8756,23 +8671,6 @@ function parseInfra(content) {
8756
8671
  result.options[optMatch[1].toLowerCase()] = optMatch[2].trim();
8757
8672
  continue;
8758
8673
  }
8759
- if (/^scenario\s*:/i.test(trimmed)) {
8760
- setError(lineNumber, `'scenario:' syntax is no longer supported`);
8761
- let si = i + 1;
8762
- while (si < lines.length) {
8763
- const sLine = lines[si];
8764
- const sTrimmed = sLine.trim();
8765
- if (!sTrimmed || sTrimmed.startsWith("#")) {
8766
- si++;
8767
- continue;
8768
- }
8769
- const sIndent = sLine.length - sLine.trimStart().length;
8770
- if (sIndent === 0) break;
8771
- si++;
8772
- }
8773
- i = si - 1;
8774
- continue;
8775
- }
8776
8674
  const tagMatch = matchTagBlockHeading(trimmed);
8777
8675
  if (tagMatch) {
8778
8676
  finishCurrentNode();
@@ -8785,21 +8683,6 @@ function parseInfra(content) {
8785
8683
  };
8786
8684
  continue;
8787
8685
  }
8788
- const hashGroupMatch = trimmed.match(GROUP_HASH_RE);
8789
- if (hashGroupMatch) {
8790
- finishCurrentNode();
8791
- finishCurrentTagGroup();
8792
- const gLabel = hashGroupMatch[1].trim();
8793
- const gId = groupId(gLabel);
8794
- currentGroup = {
8795
- id: gId,
8796
- label: gLabel,
8797
- metadata: void 0,
8798
- lineNumber
8799
- };
8800
- result.groups.push(currentGroup);
8801
- continue;
8802
- }
8803
8686
  const groupMatch = trimmed.match(GROUP_RE);
8804
8687
  if (groupMatch) {
8805
8688
  finishCurrentNode();
@@ -8816,28 +8699,6 @@ function parseInfra(content) {
8816
8699
  result.groups.push(currentGroup);
8817
8700
  continue;
8818
8701
  }
8819
- const isaMatch = trimmed.match(IS_A_RE2);
8820
- if (isaMatch) {
8821
- finishCurrentNode();
8822
- finishCurrentTagGroup();
8823
- const name = isaMatch[1].trim();
8824
- const nType = isaMatch[2].toLowerCase();
8825
- const id = nodeId2(name);
8826
- const isEdge = EDGE_NODE_NAMES.has(id.toLowerCase());
8827
- currentNode = {
8828
- id,
8829
- label: name,
8830
- properties: [],
8831
- groupId: null,
8832
- tags: {},
8833
- isEdge,
8834
- nodeType: nType,
8835
- lineNumber
8836
- };
8837
- currentGroup = null;
8838
- baseIndent = 0;
8839
- continue;
8840
- }
8841
8702
  const compMatch = trimmed.match(COMPONENT_RE);
8842
8703
  if (compMatch) {
8843
8704
  finishCurrentNode();
@@ -8895,26 +8756,6 @@ function parseInfra(content) {
8895
8756
  continue;
8896
8757
  }
8897
8758
  }
8898
- const isaMatchG = trimmed.match(IS_A_RE2);
8899
- if (isaMatchG) {
8900
- finishCurrentTagGroup();
8901
- const name = isaMatchG[1].trim();
8902
- const nType = isaMatchG[2].toLowerCase();
8903
- const id = nodeId2(name);
8904
- const tags = currentGroup.metadata ? { ...currentGroup.metadata } : {};
8905
- currentNode = {
8906
- id,
8907
- label: name,
8908
- properties: [],
8909
- groupId: currentGroup.id,
8910
- tags,
8911
- isEdge: false,
8912
- nodeType: nType,
8913
- lineNumber
8914
- };
8915
- baseIndent = indent;
8916
- continue;
8917
- }
8918
8759
  const compMatch = trimmed.match(COMPONENT_RE);
8919
8760
  if (compMatch) {
8920
8761
  finishCurrentTagGroup();
@@ -9079,25 +8920,6 @@ function parseInfra(content) {
9079
8920
  }
9080
8921
  if (currentGroup && indent > 0) {
9081
8922
  finishCurrentNode();
9082
- const isaMatchG2 = trimmed.match(IS_A_RE2);
9083
- if (isaMatchG2) {
9084
- const name = isaMatchG2[1].trim();
9085
- const nType = isaMatchG2[2].toLowerCase();
9086
- const id = nodeId2(name);
9087
- const tags = currentGroup.metadata ? { ...currentGroup.metadata } : {};
9088
- currentNode = {
9089
- id,
9090
- label: name,
9091
- properties: [],
9092
- groupId: currentGroup.id,
9093
- tags,
9094
- isEdge: false,
9095
- nodeType: nType,
9096
- lineNumber
9097
- };
9098
- baseIndent = indent;
9099
- continue;
9100
- }
9101
8923
  const compMatch = trimmed.match(COMPONENT_RE);
9102
8924
  if (compMatch) {
9103
8925
  const name = compMatch[1];
@@ -9186,7 +9008,7 @@ function extractSymbols4(docText) {
9186
9008
  if (!indented) {
9187
9009
  const firstLine = parseFirstLine(line10);
9188
9010
  if (firstLine) continue;
9189
- if (/^(?:direction|orientation|animate|no-animate|slo-|default-)/i.test(line10)) continue;
9011
+ if (/^(?:direction-tb|animate|no-animate|slo-|default-)/i.test(line10)) continue;
9190
9012
  if (/^[a-z-]+\s*:/i.test(line10)) continue;
9191
9013
  inMetadata = false;
9192
9014
  } else {
@@ -9204,7 +9026,6 @@ function extractSymbols4(docText) {
9204
9026
  }
9205
9027
  inTagGroup = false;
9206
9028
  if (/^\[/.test(line10)) continue;
9207
- if (/^#\s/.test(line10)) continue;
9208
9029
  const m = COMPONENT_RE.exec(line10);
9209
9030
  if (m && !entities.includes(m[1])) entities.push(m[1]);
9210
9031
  } else {
@@ -9222,7 +9043,7 @@ function extractSymbols4(docText) {
9222
9043
  }
9223
9044
  return { kind: "infra", entities, keywords: [] };
9224
9045
  }
9225
- var CONNECTION_RE, SIMPLE_CONNECTION_RE, ASYNC_CONNECTION_RE, ASYNC_SIMPLE_CONNECTION_RE, DEPRECATED_FANOUT_RE, IS_A_RE2, GROUP_RE, TAG_VALUE_RE, COMPONENT_RE, PIPE_META_RE, PROPERTY_RE, PERCENT_RE, RANGE_RE, EDGE_NODE_NAMES, TOP_LEVEL_OPTIONS;
9046
+ var CONNECTION_RE, SIMPLE_CONNECTION_RE, ASYNC_CONNECTION_RE, ASYNC_SIMPLE_CONNECTION_RE, DEPRECATED_FANOUT_RE, GROUP_RE, TAG_VALUE_RE, COMPONENT_RE, PIPE_META_RE, PROPERTY_RE, PERCENT_RE, RANGE_RE, EDGE_NODE_NAMES, TOP_LEVEL_OPTIONS;
9226
9047
  var init_parser9 = __esm({
9227
9048
  "src/infra/parser.ts"() {
9228
9049
  "use strict";
@@ -9235,7 +9056,6 @@ var init_parser9 = __esm({
9235
9056
  ASYNC_CONNECTION_RE = /^~(?:([^~].*?))?~>\s*(.+?)(?:(?:\s*\|\s*|\s+)split\s*:?\s*(\d+)%)?\s*$/;
9236
9057
  ASYNC_SIMPLE_CONNECTION_RE = /^~>\s*(.+?)(?:(?:\s*\|\s*|\s+)split\s*:?\s*(\d+)%)?\s*$/;
9237
9058
  DEPRECATED_FANOUT_RE = /\bx(\d+)\s*$/;
9238
- IS_A_RE2 = /^(.+?)\s+is\s+an?\s+(database|cache|queue|service|gateway|storage|function|network)\s*$/i;
9239
9059
  GROUP_RE = /^\[([^\]]+)\]\s*(?:\|\s*(.+))?$/;
9240
9060
  TAG_VALUE_RE = /^(\w[\w\s]*?)(?:\(([^)]+)\))?\s*$/;
9241
9061
  COMPONENT_RE = /^([a-zA-Z_][\w-]*)(.*)$/;
@@ -9470,6 +9290,10 @@ function parseGantt(content, palette) {
9470
9290
  let inTagBlock = false;
9471
9291
  let currentTagGroup = null;
9472
9292
  let tagBlockIndent = 0;
9293
+ let inEraBlock = false;
9294
+ let eraBlockIndent = 0;
9295
+ let inMarkerBlock = false;
9296
+ let markerBlockIndent = 0;
9473
9297
  let lastTaskNode = null;
9474
9298
  let taskIdCounter = 0;
9475
9299
  const seriesColors2 = palette ? getSeriesColors(palette) : [];
@@ -9482,6 +9306,12 @@ function parseGantt(content, palette) {
9482
9306
  if (inHolidaysBlock && indent <= holidaysBlockIndent) {
9483
9307
  inHolidaysBlock = false;
9484
9308
  }
9309
+ if (inEraBlock && indent <= eraBlockIndent) {
9310
+ inEraBlock = false;
9311
+ }
9312
+ if (inMarkerBlock && indent <= markerBlockIndent) {
9313
+ inMarkerBlock = false;
9314
+ }
9485
9315
  if (inTagBlock && indent <= tagBlockIndent) {
9486
9316
  inTagBlock = false;
9487
9317
  if (currentTagGroup) {
@@ -9543,6 +9373,49 @@ function parseGantt(content, palette) {
9543
9373
  continue;
9544
9374
  }
9545
9375
  }
9376
+ if (inEraBlock) {
9377
+ if (indent <= eraBlockIndent) {
9378
+ inEraBlock = false;
9379
+ } else {
9380
+ if (COMMENT_RE.test(line10)) continue;
9381
+ const eraEntryMatch = line10.match(ERA_ENTRY_RE);
9382
+ if (eraEntryMatch) {
9383
+ const eraLabelRaw = eraEntryMatch[3].trim();
9384
+ const eraExtracted = extractColor(eraLabelRaw, palette);
9385
+ result.eras.push({
9386
+ startDate: eraEntryMatch[1],
9387
+ endDate: eraEntryMatch[2],
9388
+ label: eraExtracted.label,
9389
+ color: eraExtracted.color || null,
9390
+ lineNumber
9391
+ });
9392
+ } else {
9393
+ warn(lineNumber, `Unrecognized era entry: "${line10}"`);
9394
+ }
9395
+ continue;
9396
+ }
9397
+ }
9398
+ if (inMarkerBlock) {
9399
+ if (indent <= markerBlockIndent) {
9400
+ inMarkerBlock = false;
9401
+ } else {
9402
+ if (COMMENT_RE.test(line10)) continue;
9403
+ const markerEntryMatch = line10.match(MARKER_ENTRY_RE);
9404
+ if (markerEntryMatch) {
9405
+ const markerLabelRaw = markerEntryMatch[2].trim();
9406
+ const markerExtracted = extractColor(markerLabelRaw, palette);
9407
+ result.markers.push({
9408
+ date: markerEntryMatch[1],
9409
+ label: markerExtracted.label,
9410
+ color: markerExtracted.color || null,
9411
+ lineNumber
9412
+ });
9413
+ } else {
9414
+ warn(lineNumber, `Unrecognized marker entry: "${line10}"`);
9415
+ }
9416
+ continue;
9417
+ }
9418
+ }
9546
9419
  if (inTagBlock && currentTagGroup) {
9547
9420
  if (indent <= tagBlockIndent) {
9548
9421
  inTagBlock = false;
@@ -9581,7 +9454,7 @@ function parseGantt(content, palette) {
9581
9454
  const targetName = depParts[0].trim();
9582
9455
  let offset;
9583
9456
  if (depParts.length > 1) {
9584
- const meta = parsePipeMetadata(["", ...depParts.slice(1)], aliasMap, () => warn(lineNumber, MULTIPLE_PIPE_WARNING));
9457
+ const meta = parsePipeMetadata(["", ...depParts.slice(1)], aliasMap, () => warn(lineNumber, MULTIPLE_PIPE_ERROR));
9585
9458
  if (meta.lag || meta.lead) {
9586
9459
  const key = meta.lag ? "lag" : "lead";
9587
9460
  softError(lineNumber, `"${key}" is no longer supported \u2014 use "offset: ${meta[key]}" instead.${key === "lead" ? ' Negate the value for lead behavior: "offset: -...".' : ""}`);
@@ -9633,10 +9506,6 @@ function parseGantt(content, palette) {
9633
9506
  }
9634
9507
  const tagMatch = matchTagBlockHeading(line10);
9635
9508
  if (tagMatch) {
9636
- if (tagMatch.deprecated) {
9637
- softError(lineNumber, `'## ${tagMatch.name}' is no longer supported \u2014 use 'tag ${tagMatch.name}' instead`);
9638
- continue;
9639
- }
9640
9509
  inTagBlock = true;
9641
9510
  tagBlockIndent = indent;
9642
9511
  inHeaderBlock = false;
@@ -9651,6 +9520,23 @@ function parseGantt(content, palette) {
9651
9520
  }
9652
9521
  continue;
9653
9522
  }
9523
+ const topWorkweekMatch = line10.match(WORKWEEK_RE);
9524
+ if (topWorkweekMatch) {
9525
+ const days = parseWorkweek(topWorkweekMatch[1].trim());
9526
+ if (days) {
9527
+ result.holidays.workweek = days;
9528
+ } else {
9529
+ warn(lineNumber, `Invalid workweek format: "${topWorkweekMatch[1]}". Use day range like "sun-thu" or comma-separated days.`);
9530
+ }
9531
+ inHeaderBlock = false;
9532
+ continue;
9533
+ }
9534
+ if (line10.toLowerCase() === "era") {
9535
+ inEraBlock = true;
9536
+ eraBlockIndent = indent;
9537
+ inHeaderBlock = false;
9538
+ continue;
9539
+ }
9654
9540
  const eraMatch = line10.match(ERA_RE);
9655
9541
  if (eraMatch) {
9656
9542
  const eraLabelRaw = eraMatch[3].trim();
@@ -9665,6 +9551,12 @@ function parseGantt(content, palette) {
9665
9551
  inHeaderBlock = false;
9666
9552
  continue;
9667
9553
  }
9554
+ if (line10.toLowerCase() === "marker") {
9555
+ inMarkerBlock = true;
9556
+ markerBlockIndent = indent;
9557
+ inHeaderBlock = false;
9558
+ continue;
9559
+ }
9668
9560
  const markerMatch = line10.match(MARKER_RE);
9669
9561
  if (markerMatch) {
9670
9562
  const markerLabelRaw = markerMatch[2].trim();
@@ -9680,7 +9572,7 @@ function parseGantt(content, palette) {
9680
9572
  }
9681
9573
  const optNoColonMatch = line10.match(/^([a-z][a-z0-9-]*)\s+(.+)$/i);
9682
9574
  const bareKeyword = line10.match(/^([a-z][a-z0-9-]*)$/i);
9683
- if (bareKeyword && KNOWN_BOOLEANS3.has(bareKeyword[1].toLowerCase())) {
9575
+ if (bareKeyword && KNOWN_BOOLEANS4.has(bareKeyword[1].toLowerCase())) {
9684
9576
  const key = bareKeyword[1].toLowerCase();
9685
9577
  result.options.optionLineNumbers[key] = lineNumber;
9686
9578
  switch (key) {
@@ -9695,7 +9587,7 @@ function parseGantt(content, palette) {
9695
9587
  }
9696
9588
  if (bareKeyword && bareKeyword[1].toLowerCase().startsWith("no-")) {
9697
9589
  const base = bareKeyword[1].toLowerCase().substring(3);
9698
- if (KNOWN_BOOLEANS3.has(base)) {
9590
+ if (KNOWN_BOOLEANS4.has(base)) {
9699
9591
  result.options.optionLineNumbers[base] = lineNumber;
9700
9592
  switch (base) {
9701
9593
  case "dependencies":
@@ -9723,9 +9615,6 @@ function parseGantt(content, palette) {
9723
9615
  result.options.title = value;
9724
9616
  result.options.titleLineNumber = lineNumber;
9725
9617
  break;
9726
- case "orientation":
9727
- warn(lineNumber, `'orientation' is not supported for gantt charts`);
9728
- break;
9729
9618
  case "today-marker":
9730
9619
  if (/^\d{4}-\d{2}-\d{2}(?: \d{2}:\d{2})?$/.test(value)) {
9731
9620
  result.options.todayMarker = value;
@@ -9754,26 +9643,6 @@ function parseGantt(content, palette) {
9754
9643
  continue;
9755
9644
  }
9756
9645
  inHeaderBlock = false;
9757
- const hashGroupMatch = line10.match(GROUP_HASH_RE);
9758
- if (hashGroupMatch) {
9759
- const nameExtracted = extractColor(hashGroupMatch[1], palette);
9760
- const group = {
9761
- name: nameExtracted.label,
9762
- color: nameExtracted.color || null,
9763
- metadata: {},
9764
- lineNumber,
9765
- children: []
9766
- };
9767
- const groupNode = { kind: "group", ...group };
9768
- currentContainer().push(groupNode);
9769
- blockStack.push({
9770
- node: groupNode,
9771
- indent,
9772
- containerType: "group"
9773
- });
9774
- lastTaskNode = null;
9775
- continue;
9776
- }
9777
9646
  if (line10 === "parallel") {
9778
9647
  const parallel = {
9779
9648
  kind: "parallel",
@@ -9795,7 +9664,7 @@ function parseGantt(content, palette) {
9795
9664
  const segments = afterBrackets ? afterBrackets.split("|") : [];
9796
9665
  let metadata = {};
9797
9666
  let color = null;
9798
- const pipeWarn = () => warn(lineNumber, MULTIPLE_PIPE_WARNING);
9667
+ const pipeWarn = () => warn(lineNumber, MULTIPLE_PIPE_ERROR);
9799
9668
  if (segments.length > 0 && segments[0].trim()) {
9800
9669
  metadata = parsePipeMetadata(["", ...segments], aliasMap, pipeWarn);
9801
9670
  } else if (segments.length > 1) {
@@ -9876,7 +9745,7 @@ function parseGantt(content, palette) {
9876
9745
  const targetName = depParts[0].trim();
9877
9746
  let offset;
9878
9747
  if (depParts.length > 1) {
9879
- const meta = parsePipeMetadata(["", ...depParts.slice(1)], aliasMap, () => warn(lineNumber, MULTIPLE_PIPE_WARNING));
9748
+ const meta = parsePipeMetadata(["", ...depParts.slice(1)], aliasMap, () => warn(lineNumber, MULTIPLE_PIPE_ERROR));
9880
9749
  if (meta.lag || meta.lead) {
9881
9750
  const key = meta.lag ? "lag" : "lead";
9882
9751
  softError(lineNumber, `"${key}" is no longer supported \u2014 use "offset: ${meta[key]}" instead.${key === "lead" ? ' Negate the value for lead behavior: "offset: -...".' : ""}`);
@@ -9913,7 +9782,7 @@ function parseGantt(content, palette) {
9913
9782
  if (label.toLowerCase() === "parallel") {
9914
9783
  softError(ln, `"parallel" is a reserved keyword and cannot be used as a task name.`);
9915
9784
  }
9916
- const metadata = segments.length > 1 ? parsePipeMetadata(segments, aliasMap, () => warn(ln, MULTIPLE_PIPE_WARNING)) : {};
9785
+ const metadata = segments.length > 1 ? parsePipeMetadata(segments, aliasMap, () => warn(ln, MULTIPLE_PIPE_ERROR)) : {};
9917
9786
  let progress = null;
9918
9787
  if (metadata.progress) {
9919
9788
  progress = parseFloat(metadata.progress);
@@ -9999,7 +9868,7 @@ function parseWorkweek(s) {
9999
9868
  function isKnownOption(key) {
10000
9869
  return KNOWN_OPTIONS5.has(key);
10001
9870
  }
10002
- var DURATION_RE, EXPLICIT_DATE_RE, TIMELINE_DURATION_RE, GROUP_RE2, DEPENDENCY_RE, COMMENT_RE, ERA_RE, MARKER_RE, HOLIDAY_DATE_RE, HOLIDAY_RANGE_RE, WORKWEEK_RE, WEEKDAY_MAP, KNOWN_OPTIONS5, KNOWN_BOOLEANS3;
9871
+ var DURATION_RE, EXPLICIT_DATE_RE, TIMELINE_DURATION_RE, GROUP_RE2, DEPENDENCY_RE, COMMENT_RE, ERA_RE, MARKER_RE, HOLIDAY_DATE_RE, HOLIDAY_RANGE_RE, WORKWEEK_RE, ERA_ENTRY_RE, MARKER_ENTRY_RE, WEEKDAY_MAP, KNOWN_OPTIONS5, KNOWN_BOOLEANS4;
10003
9872
  var init_parser10 = __esm({
10004
9873
  "src/gantt/parser.ts"() {
10005
9874
  "use strict";
@@ -10010,15 +9879,17 @@ var init_parser10 = __esm({
10010
9879
  init_palettes();
10011
9880
  DURATION_RE = /^(\d+(?:\.\d+)?)(min|bd|d|w|m|q|y|h)(\?)?\s+(.+)$/;
10012
9881
  EXPLICIT_DATE_RE = /^(\d{4}-\d{2}-\d{2}(?: \d{2}:\d{2})?)\s+(.+)$/;
10013
- TIMELINE_DURATION_RE = /^(\d{4}-\d{2}-\d{2}(?: \d{2}:\d{2})?)\s*->\s*(\d+(?:\.\d+)?)(min|bd|d|w|m|q|y|h)(\?)?\s+(.+)$/;
9882
+ TIMELINE_DURATION_RE = /^(\d{4}-\d{2}-\d{2}(?: \d{2}:\d{2})?)\s*(?:->|\u2013>)\s*(\d+(?:\.\d+)?)(min|bd|d|w|m|q|y|h)(\?)?\s+(.+)$/;
10014
9883
  GROUP_RE2 = /^\[(.+?)\]\s*(.*)$/;
10015
9884
  DEPENDENCY_RE = /^(?:-(.+?))?->\s*(.+)$/;
10016
9885
  COMMENT_RE = /^\/\//;
10017
- ERA_RE = /^era\s+(\d{4}(?:-\d{2}(?:-\d{2}(?: \d{2}:\d{2})?)?)?)\s*->\s*(\d{4}(?:-\d{2}(?:-\d{2}(?: \d{2}:\d{2})?)?)?)\s+(.+)$/i;
9886
+ ERA_RE = /^era\s+(\d{4}(?:-\d{2}(?:-\d{2}(?: \d{2}:\d{2})?)?)?)\s*(?:->|\u2013>)\s*(\d{4}(?:-\d{2}(?:-\d{2}(?: \d{2}:\d{2})?)?)?)\s+(.+)$/i;
10018
9887
  MARKER_RE = /^marker\s+(\d{4}(?:-\d{2}(?:-\d{2}(?: \d{2}:\d{2})?)?)?)\s+(.+)$/i;
10019
9888
  HOLIDAY_DATE_RE = /^(\d{4}-\d{2}-\d{2})\s+(.+)$/;
10020
- HOLIDAY_RANGE_RE = /^(\d{4}-\d{2}-\d{2})\s*->\s*(\d{4}-\d{2}-\d{2})\s+(.+)$/;
9889
+ HOLIDAY_RANGE_RE = /^(\d{4}-\d{2}-\d{2})\s*(?:->|\u2013>)\s*(\d{4}-\d{2}-\d{2})\s+(.+)$/;
10021
9890
  WORKWEEK_RE = /^workweek\s+(.+)$/i;
9891
+ ERA_ENTRY_RE = /^(\d{4}(?:-\d{2}(?:-\d{2}(?: \d{2}:\d{2})?)?)?)\s*(?:->|\u2013>)\s*(\d{4}(?:-\d{2}(?:-\d{2}(?: \d{2}:\d{2})?)?)?)\s+(.+)$/;
9892
+ MARKER_ENTRY_RE = /^(\d{4}(?:-\d{2}(?:-\d{2}(?: \d{2}:\d{2})?)?)?)\s+(.+)$/;
10022
9893
  WEEKDAY_MAP = {
10023
9894
  mon: "mon",
10024
9895
  tue: "tue",
@@ -10038,14 +9909,13 @@ var init_parser10 = __esm({
10038
9909
  KNOWN_OPTIONS5 = /* @__PURE__ */ new Set([
10039
9910
  "start",
10040
9911
  "title",
10041
- "orientation",
10042
9912
  "today-marker",
10043
9913
  "critical-path",
10044
9914
  "dependencies",
10045
9915
  "chart",
10046
9916
  "sort"
10047
9917
  ]);
10048
- KNOWN_BOOLEANS3 = /* @__PURE__ */ new Set([
9918
+ KNOWN_BOOLEANS4 = /* @__PURE__ */ new Set([
10049
9919
  "critical-path",
10050
9920
  "today-marker",
10051
9921
  "dependencies"
@@ -12895,7 +12765,7 @@ function nodeStroke3(palette, modifier, nodeColor2, colorOff) {
12895
12765
  return nodeColor2 ?? modifierColor(modifier, palette, colorOff);
12896
12766
  }
12897
12767
  function collectClassTypes(parsed) {
12898
- if (parsed.options?.color === "off") return [];
12768
+ if (parsed.options?.["no-auto-color"]) return [];
12899
12769
  const present = /* @__PURE__ */ new Set();
12900
12770
  for (const c of parsed.classes) {
12901
12771
  if (c.color) continue;
@@ -13057,7 +12927,7 @@ function renderClassDiagram(container, parsed, layout, palette, isDark, onClickI
13057
12927
  }
13058
12928
  const w = node.width;
13059
12929
  const h = node.height;
13060
- const colorOff = parsed.options?.color === "off";
12930
+ const colorOff = !!parsed.options?.["no-auto-color"];
13061
12931
  const neutralize = hasLegend && !isLegendExpanded && !node.color;
13062
12932
  const effectiveColor = neutralize ? palette.primary : node.color;
13063
12933
  const fill2 = nodeFill3(palette, isDark, node.modifier, effectiveColor, colorOff);
@@ -24231,6 +24101,10 @@ function parseVisualization(content, palette) {
24231
24101
  let currentArcGroup = null;
24232
24102
  let currentTimelineGroup = null;
24233
24103
  let currentTimelineTagGroup = null;
24104
+ let inTimelineEraBlock = false;
24105
+ let timelineEraBlockIndent = 0;
24106
+ let inTimelineMarkerBlock = false;
24107
+ let timelineMarkerBlockIndent = 0;
24234
24108
  const timelineAliasMap = /* @__PURE__ */ new Map();
24235
24109
  const VALID_D3_TYPES = /* @__PURE__ */ new Set(["slope", "wordcloud", "arc", "timeline", "venn", "quadrant", "sequence"]);
24236
24110
  let firstLineParsed = false;
@@ -24256,13 +24130,6 @@ function parseVisualization(content, palette) {
24256
24130
  if (result.type === "timeline" && indent === 0) {
24257
24131
  const tagBlockMatch = matchTagBlockHeading(line10);
24258
24132
  if (tagBlockMatch) {
24259
- if (tagBlockMatch.deprecated) {
24260
- result.diagnostics.push(makeDgmoError(
24261
- lineNumber,
24262
- `'## ${tagBlockMatch.name}' is deprecated for tag groups \u2014 use 'tag: ${tagBlockMatch.name}' instead`,
24263
- "warning"
24264
- ));
24265
- }
24266
24133
  currentTimelineTagGroup = {
24267
24134
  name: tagBlockMatch.name,
24268
24135
  alias: tagBlockMatch.alias,
@@ -24344,9 +24211,64 @@ function parseVisualization(content, palette) {
24344
24211
  continue;
24345
24212
  }
24346
24213
  }
24214
+ if (result.type === "timeline" && inTimelineEraBlock) {
24215
+ if (indent <= timelineEraBlockIndent) {
24216
+ inTimelineEraBlock = false;
24217
+ } else {
24218
+ if (line10.startsWith("//")) continue;
24219
+ const eraEntryMatch = line10.match(
24220
+ /^(\d{4}(?:-\d{2})?(?:-\d{2}(?: \d{2}:\d{2})?)?)\s*(?:->|\u2013>)\s*(\d{4}(?:-\d{2})?(?:-\d{2}(?: \d{2}:\d{2})?)?)\s*:?\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/
24221
+ );
24222
+ if (eraEntryMatch) {
24223
+ const colorAnnotation = eraEntryMatch[4]?.trim() || null;
24224
+ result.timelineEras.push({
24225
+ startDate: eraEntryMatch[1],
24226
+ endDate: eraEntryMatch[2],
24227
+ label: eraEntryMatch[3].trim(),
24228
+ color: colorAnnotation ? resolveColor(colorAnnotation, palette) : null,
24229
+ lineNumber
24230
+ });
24231
+ } else {
24232
+ warn(lineNumber, `Unrecognized era entry: "${line10}"`);
24233
+ }
24234
+ continue;
24235
+ }
24236
+ }
24237
+ if (result.type === "timeline" && inTimelineMarkerBlock) {
24238
+ if (indent <= timelineMarkerBlockIndent) {
24239
+ inTimelineMarkerBlock = false;
24240
+ } else {
24241
+ if (line10.startsWith("//")) continue;
24242
+ const markerEntryMatch = line10.match(
24243
+ /^(\d{4}(?:-\d{2})?(?:-\d{2}(?: \d{2}:\d{2})?)?)\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/
24244
+ );
24245
+ if (markerEntryMatch) {
24246
+ const colorAnnotation = markerEntryMatch[3]?.trim() || null;
24247
+ result.timelineMarkers.push({
24248
+ date: markerEntryMatch[1],
24249
+ label: markerEntryMatch[2].trim(),
24250
+ color: colorAnnotation ? resolveColor(colorAnnotation, palette) : null,
24251
+ lineNumber
24252
+ });
24253
+ } else {
24254
+ warn(lineNumber, `Unrecognized marker entry: "${line10}"`);
24255
+ }
24256
+ continue;
24257
+ }
24258
+ }
24347
24259
  if (result.type === "timeline") {
24260
+ if (line10.toLowerCase() === "era") {
24261
+ inTimelineEraBlock = true;
24262
+ timelineEraBlockIndent = indent;
24263
+ continue;
24264
+ }
24265
+ if (line10.toLowerCase() === "marker") {
24266
+ inTimelineMarkerBlock = true;
24267
+ timelineMarkerBlockIndent = indent;
24268
+ continue;
24269
+ }
24348
24270
  const eraMatch = line10.match(
24349
- /^era\s+(\d{4}(?:-\d{2})?(?:-\d{2}(?: \d{2}:\d{2})?)?)\s*->\s*(\d{4}(?:-\d{2})?(?:-\d{2}(?: \d{2}:\d{2})?)?)\s*:?\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/
24271
+ /^era\s+(\d{4}(?:-\d{2})?(?:-\d{2}(?: \d{2}:\d{2})?)?)\s*(?:->|\u2013>)\s*(\d{4}(?:-\d{2})?(?:-\d{2}(?: \d{2}:\d{2})?)?)\s*:?\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/
24350
24272
  );
24351
24273
  if (eraMatch) {
24352
24274
  const colorAnnotation = eraMatch[4]?.trim() || null;
@@ -24375,7 +24297,7 @@ function parseVisualization(content, palette) {
24375
24297
  }
24376
24298
  if (result.type === "timeline") {
24377
24299
  const durationMatch = line10.match(
24378
- /^(\d{4}(?:-\d{2})?(?:-\d{2}(?: \d{2}:\d{2})?)?)\s*->\s*(\d+(?:\.\d{1,2})?)(min|[dwmyh])(\?)?(?:\s*:\s*|\s+)(.+)$/
24300
+ /^(\d{4}(?:-\d{2})?(?:-\d{2}(?: \d{2}:\d{2})?)?)\s*(?:->|\u2013>)\s*(\d+(?:\.\d{1,2})?)(min|[dwmyh])(\?)?(?:\s*:\s*|\s+)(.+)$/
24379
24301
  );
24380
24302
  if (durationMatch) {
24381
24303
  const startDate = durationMatch[1];
@@ -24384,7 +24306,7 @@ function parseVisualization(content, palette) {
24384
24306
  const unit = durationMatch[3];
24385
24307
  const endDate = addDurationToDate(startDate, amount, unit);
24386
24308
  const segments = durationMatch[5].split("|");
24387
- const metadata = segments.length > 1 ? parsePipeMetadata(["", ...segments.slice(1)], timelineAliasMap, () => warn(lineNumber, MULTIPLE_PIPE_WARNING)) : {};
24309
+ const metadata = segments.length > 1 ? parsePipeMetadata(["", ...segments.slice(1)], timelineAliasMap, () => warn(lineNumber, MULTIPLE_PIPE_ERROR)) : {};
24388
24310
  result.timelineEvents.push({
24389
24311
  date: startDate,
24390
24312
  endDate,
@@ -24397,11 +24319,11 @@ function parseVisualization(content, palette) {
24397
24319
  continue;
24398
24320
  }
24399
24321
  const rangeMatch = line10.match(
24400
- /^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\d{4}(?:-\d{2})?(?:-\d{2})?)(\?)?(?:\s*:\s*|\s+)(.+)$/
24322
+ /^(\d{4}(?:-\d{2})?(?:-\d{2}(?: \d{2}:\d{2})?)?)\s*(?:->|\u2013>)\s*(\d{4}(?:-\d{2})?(?:-\d{2}(?: \d{2}:\d{2})?)?)(\?)?(?:\s*:\s*|\s+)(.+)$/
24401
24323
  );
24402
24324
  if (rangeMatch) {
24403
24325
  const segments = rangeMatch[4].split("|");
24404
- const metadata = segments.length > 1 ? parsePipeMetadata(["", ...segments.slice(1)], timelineAliasMap, () => warn(lineNumber, MULTIPLE_PIPE_WARNING)) : {};
24326
+ const metadata = segments.length > 1 ? parsePipeMetadata(["", ...segments.slice(1)], timelineAliasMap, () => warn(lineNumber, MULTIPLE_PIPE_ERROR)) : {};
24405
24327
  result.timelineEvents.push({
24406
24328
  date: rangeMatch[1],
24407
24329
  endDate: rangeMatch[2],
@@ -24418,7 +24340,7 @@ function parseVisualization(content, palette) {
24418
24340
  );
24419
24341
  if (pointMatch) {
24420
24342
  const segments = pointMatch[2].split("|");
24421
- const metadata = segments.length > 1 ? parsePipeMetadata(["", ...segments.slice(1)], timelineAliasMap, () => warn(lineNumber, MULTIPLE_PIPE_WARNING)) : {};
24343
+ const metadata = segments.length > 1 ? parsePipeMetadata(["", ...segments.slice(1)], timelineAliasMap, () => warn(lineNumber, MULTIPLE_PIPE_ERROR)) : {};
24422
24344
  result.timelineEvents.push({
24423
24345
  date: pointMatch[1],
24424
24346
  endDate: null,
@@ -24432,18 +24354,40 @@ function parseVisualization(content, palette) {
24432
24354
  }
24433
24355
  if (result.type === "venn") {
24434
24356
  if (/\+/.test(line10)) {
24435
- const colonIdx = line10.indexOf(":");
24436
- let setsPart;
24437
- let label;
24438
- if (colonIdx >= 0) {
24439
- setsPart = line10.substring(0, colonIdx).trim();
24440
- label = line10.substring(colonIdx + 1).trim() || null;
24441
- } else {
24442
- setsPart = line10.trim();
24443
- label = null;
24444
- }
24445
- const rawSets = setsPart.split("+").map((s) => s.trim()).filter(Boolean);
24446
- if (rawSets.length >= 2) {
24357
+ const knownSetRefs = /* @__PURE__ */ new Set();
24358
+ for (const s of result.vennSets) {
24359
+ knownSetRefs.add(s.name.toLowerCase());
24360
+ if (s.alias) knownSetRefs.add(s.alias.toLowerCase());
24361
+ }
24362
+ const segments = line10.split("+").map((s) => s.trim()).filter(Boolean);
24363
+ if (segments.length >= 2) {
24364
+ const rawSets = segments.slice(0, -1);
24365
+ const lastSeg = segments[segments.length - 1];
24366
+ const colonIdx = lastSeg.indexOf(":");
24367
+ let lastSetRef;
24368
+ let label;
24369
+ if (colonIdx >= 0) {
24370
+ lastSetRef = lastSeg.substring(0, colonIdx).trim();
24371
+ label = lastSeg.substring(colonIdx + 1).trim() || null;
24372
+ } else {
24373
+ const words = lastSeg.split(/\s+/);
24374
+ let matchLen = 0;
24375
+ for (let w = words.length; w >= 1; w--) {
24376
+ const candidate = words.slice(0, w).join(" ");
24377
+ if (knownSetRefs.has(candidate.toLowerCase())) {
24378
+ matchLen = w;
24379
+ break;
24380
+ }
24381
+ }
24382
+ if (matchLen > 0) {
24383
+ lastSetRef = words.slice(0, matchLen).join(" ");
24384
+ label = words.length > matchLen ? words.slice(matchLen).join(" ") : null;
24385
+ } else {
24386
+ lastSetRef = words[0];
24387
+ label = words.length > 1 ? words.slice(1).join(" ") : null;
24388
+ }
24389
+ }
24390
+ rawSets.push(lastSetRef);
24447
24391
  result.vennOverlaps.push({ sets: rawSets, label, lineNumber });
24448
24392
  continue;
24449
24393
  }
@@ -24553,19 +24497,6 @@ function parseVisualization(content, palette) {
24553
24497
  }
24554
24498
  continue;
24555
24499
  }
24556
- if (firstToken === "orientation" || firstToken === "direction") {
24557
- if (result.type === "arc" || result.type === "timeline") {
24558
- const vLower = restValue.toLowerCase();
24559
- if (vLower === "horizontal" || vLower === "vertical") {
24560
- result.orientation = vLower;
24561
- } else {
24562
- const dir = normalizeDirection(restValue);
24563
- if (dir === "LR") result.orientation = "horizontal";
24564
- else if (dir === "TB") result.orientation = "vertical";
24565
- }
24566
- }
24567
- continue;
24568
- }
24569
24500
  if (firstToken === "order") {
24570
24501
  const v = restValue.toLowerCase();
24571
24502
  if (v === "name" || v === "group" || v === "degree") {
@@ -24573,27 +24504,6 @@ function parseVisualization(content, palette) {
24573
24504
  }
24574
24505
  continue;
24575
24506
  }
24576
- if (firstToken === "sort") {
24577
- const vLower = restValue.toLowerCase();
24578
- if (vLower === "time" || vLower === "group") {
24579
- result.timelineSort = vLower;
24580
- } else if (vLower === "tag" || vLower.startsWith("tag:")) {
24581
- result.timelineSort = "tag";
24582
- if (vLower.startsWith("tag:")) {
24583
- const groupRef = restValue.substring(4).trim();
24584
- if (groupRef) {
24585
- result.timelineDefaultSwimlaneTG = groupRef;
24586
- }
24587
- }
24588
- }
24589
- continue;
24590
- }
24591
- if (firstToken === "swimlanes") {
24592
- const v = restValue.toLowerCase();
24593
- if (v === "on") result.timelineSwimlanes = true;
24594
- else if (v === "off") result.timelineSwimlanes = false;
24595
- continue;
24596
- }
24597
24507
  if (firstToken === "rotate") {
24598
24508
  const v = restValue.toLowerCase();
24599
24509
  if (v === "none" || v === "mixed" || v === "angled") {
@@ -24622,19 +24532,6 @@ function parseVisualization(content, palette) {
24622
24532
  const rawKey = line10.substring(0, colonIndex).trim();
24623
24533
  const key = rawKey.toLowerCase();
24624
24534
  const colorMatch = rawKey.match(/^(.+?)\(([^)]+)\)\s*$/);
24625
- if (key === "chart") {
24626
- const value = line10.substring(colonIndex + 1).trim().toLowerCase();
24627
- if (VALID_D3_TYPES.has(value)) {
24628
- result.type = value;
24629
- } else {
24630
- const validD3Types = [...VALID_D3_TYPES];
24631
- let msg = `Unsupported chart type: ${value}. Supported types: ${validD3Types.join(", ")}`;
24632
- const hint = suggest(value, validD3Types);
24633
- if (hint) msg += `. ${hint}`;
24634
- return fail(lineNumber, msg);
24635
- }
24636
- continue;
24637
- }
24638
24535
  if (key === "title") {
24639
24536
  result.title = line10.substring(colonIndex + 1).trim();
24640
24537
  result.titleLineNumber = lineNumber;
@@ -24643,20 +24540,6 @@ function parseVisualization(content, palette) {
24643
24540
  }
24644
24541
  continue;
24645
24542
  }
24646
- if (key === "orientation" || key === "direction") {
24647
- if (result.type === "arc" || result.type === "timeline") {
24648
- const raw = line10.substring(colonIndex + 1).trim();
24649
- const vLower = raw.toLowerCase();
24650
- if (vLower === "horizontal" || vLower === "vertical") {
24651
- result.orientation = vLower;
24652
- } else {
24653
- const dir = normalizeDirection(raw);
24654
- if (dir === "LR") result.orientation = "horizontal";
24655
- else if (dir === "TB") result.orientation = "vertical";
24656
- }
24657
- }
24658
- continue;
24659
- }
24660
24543
  if (key === "order") {
24661
24544
  const v = line10.substring(colonIndex + 1).trim().toLowerCase();
24662
24545
  if (v === "name" || v === "group" || v === "degree") {
@@ -24664,31 +24547,6 @@ function parseVisualization(content, palette) {
24664
24547
  }
24665
24548
  continue;
24666
24549
  }
24667
- if (key === "sort") {
24668
- const v = line10.substring(colonIndex + 1).trim();
24669
- const vLower = v.toLowerCase();
24670
- if (vLower === "time" || vLower === "group") {
24671
- result.timelineSort = vLower;
24672
- } else if (vLower === "tag" || vLower.startsWith("tag:")) {
24673
- result.timelineSort = "tag";
24674
- if (vLower.startsWith("tag:")) {
24675
- const groupRef = v.substring(4).trim();
24676
- if (groupRef) {
24677
- result.timelineDefaultSwimlaneTG = groupRef;
24678
- }
24679
- }
24680
- }
24681
- continue;
24682
- }
24683
- if (key === "swimlanes") {
24684
- const v = line10.substring(colonIndex + 1).trim().toLowerCase();
24685
- if (v === "on") {
24686
- result.timelineSwimlanes = true;
24687
- } else if (v === "off") {
24688
- result.timelineSwimlanes = false;
24689
- }
24690
- continue;
24691
- }
24692
24550
  if (key === "rotate") {
24693
24551
  const v = line10.substring(colonIndex + 1).trim().toLowerCase();
24694
24552
  if (v === "none" || v === "mixed" || v === "angled") {
@@ -24727,21 +24585,15 @@ function parseVisualization(content, palette) {
24727
24585
  numericValues.push(num);
24728
24586
  }
24729
24587
  if (allNumeric && numericValues.length > 0) {
24730
- if (result.type === "wordcloud" && numericValues.length === 1) {
24731
- result.words.push({
24732
- text: labelPart,
24733
- weight: numericValues[0],
24734
- lineNumber
24735
- });
24736
- } else {
24588
+ if (result.type !== "wordcloud") {
24737
24589
  result.data.push({
24738
24590
  label: labelPart,
24739
24591
  values: numericValues,
24740
24592
  color: colorPart,
24741
24593
  lineNumber
24742
24594
  });
24595
+ continue;
24743
24596
  }
24744
- continue;
24745
24597
  }
24746
24598
  }
24747
24599
  if (result.type === "wordcloud") {
@@ -24784,7 +24636,7 @@ function parseVisualization(content, palette) {
24784
24636
  result.words = tokenizeFreeformText(freeformLines.join(" "));
24785
24637
  }
24786
24638
  if (result.words.length === 0) {
24787
- warn(1, 'No words found. Add words as "word: weight", one per line, or paste freeform text');
24639
+ warn(1, 'No words found. Add words as "word weight" (space-separated), one per line, or paste freeform text');
24788
24640
  }
24789
24641
  if (result.cloudOptions.max > 0 && result.words.length > result.cloudOptions.max) {
24790
24642
  result.words = result.words.slice().sort((a, b) => b.weight - a.weight).slice(0, result.cloudOptions.max);
@@ -24827,25 +24679,6 @@ function parseVisualization(content, palette) {
24827
24679
  }
24828
24680
  }
24829
24681
  }
24830
- if (result.timelineSort === "tag") {
24831
- if (result.timelineTagGroups.length === 0) {
24832
- warn(1, '"sort: tag" requires at least one tag group definition');
24833
- result.timelineSort = "time";
24834
- } else if (result.timelineDefaultSwimlaneTG) {
24835
- const ref = result.timelineDefaultSwimlaneTG.toLowerCase();
24836
- const match = result.timelineTagGroups.find(
24837
- (g) => g.name.toLowerCase() === ref || g.alias?.toLowerCase() === ref
24838
- );
24839
- if (match) {
24840
- result.timelineDefaultSwimlaneTG = match.name;
24841
- } else {
24842
- warn(1, `"sort: tag:${result.timelineDefaultSwimlaneTG}" \u2014 no tag group matches "${result.timelineDefaultSwimlaneTG}"`);
24843
- result.timelineDefaultSwimlaneTG = result.timelineTagGroups[0].name;
24844
- }
24845
- } else {
24846
- result.timelineDefaultSwimlaneTG = result.timelineTagGroups[0].name;
24847
- }
24848
- }
24849
24682
  return result;
24850
24683
  }
24851
24684
  if (result.type === "venn") {
@@ -25442,6 +25275,18 @@ function formatDateLabel(dateStr) {
25442
25275
  const day = parseInt(parts[2], 10);
25443
25276
  return `${month} ${day}, ${year}${timeSuffix}`;
25444
25277
  }
25278
+ function formatBoundaryLabel(dateStr, otherDateStr) {
25279
+ const spaceIdx = dateStr.indexOf(" ");
25280
+ const otherSpaceIdx = otherDateStr.indexOf(" ");
25281
+ if (spaceIdx !== -1 && otherSpaceIdx !== -1) {
25282
+ const datePart = dateStr.slice(0, spaceIdx);
25283
+ const otherDatePart = otherDateStr.slice(0, otherSpaceIdx);
25284
+ if (datePart === otherDatePart) {
25285
+ return dateStr.slice(spaceIdx + 1);
25286
+ }
25287
+ }
25288
+ return formatDateLabel(dateStr);
25289
+ }
25445
25290
  function computeTimeTicks(domainMin, domainMax, scale, boundaryStart, boundaryEnd, boundaryStartLabel, boundaryEndLabel) {
25446
25291
  const minYear = Math.floor(domainMin);
25447
25292
  const maxYear = Math.floor(domainMax);
@@ -25498,17 +25343,22 @@ function computeTimeTicks(domainMin, domainMax, scale, boundaryStart, boundaryEn
25498
25343
  if (spanHours > 48) stepHour = 6;
25499
25344
  else if (spanHours > 24) stepHour = 3;
25500
25345
  else if (spanHours > 12) stepHour = 2;
25346
+ const singleDay = spanHours <= 24;
25501
25347
  const startDate = fractionalYearToDate(domainMin);
25502
25348
  startDate.setHours(Math.floor(startDate.getHours() / stepHour) * stepHour, 0, 0, 0);
25503
25349
  while (true) {
25504
25350
  const val = dateToFractionalYear2(startDate);
25505
25351
  if (val > domainMax) break;
25506
25352
  if (val >= domainMin) {
25507
- const mon = MONTH_ABBR2[startDate.getMonth()];
25508
- const d = startDate.getDate();
25509
25353
  const hh = String(startDate.getHours()).padStart(2, "0");
25510
25354
  const mm = String(startDate.getMinutes()).padStart(2, "0");
25511
- ticks.push({ pos: scale(val), label: `${mon} ${d} ${hh}:${mm}` });
25355
+ if (singleDay) {
25356
+ ticks.push({ pos: scale(val), label: `${hh}:${mm}` });
25357
+ } else {
25358
+ const mon = MONTH_ABBR2[startDate.getMonth()];
25359
+ const d = startDate.getDate();
25360
+ ticks.push({ pos: scale(val), label: `${mon} ${d} ${hh}:${mm}` });
25361
+ }
25512
25362
  }
25513
25363
  startDate.setHours(startDate.getHours() + stepHour);
25514
25364
  }
@@ -25899,8 +25749,8 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
25899
25749
  textColor,
25900
25750
  minDate,
25901
25751
  maxDate,
25902
- formatDateLabel(earliestStartDateStr),
25903
- formatDateLabel(latestEndDateStr)
25752
+ formatBoundaryLabel(earliestStartDateStr, latestEndDateStr),
25753
+ formatBoundaryLabel(latestEndDateStr, earliestStartDateStr)
25904
25754
  );
25905
25755
  }
25906
25756
  if (timelineSwimlanes || tagLanes) {
@@ -26018,8 +25868,8 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
26018
25868
  textColor,
26019
25869
  minDate,
26020
25870
  maxDate,
26021
- formatDateLabel(earliestStartDateStr),
26022
- formatDateLabel(latestEndDateStr)
25871
+ formatBoundaryLabel(earliestStartDateStr, latestEndDateStr),
25872
+ formatBoundaryLabel(latestEndDateStr, earliestStartDateStr)
26023
25873
  );
26024
25874
  }
26025
25875
  if (timelineGroups.length > 0) {
@@ -26166,8 +26016,8 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
26166
26016
  textColor,
26167
26017
  minDate,
26168
26018
  maxDate,
26169
- formatDateLabel(earliestStartDateStr),
26170
- formatDateLabel(latestEndDateStr)
26019
+ formatBoundaryLabel(earliestStartDateStr, latestEndDateStr),
26020
+ formatBoundaryLabel(latestEndDateStr, earliestStartDateStr)
26171
26021
  );
26172
26022
  }
26173
26023
  let curY = markerMargin;
@@ -26320,8 +26170,8 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
26320
26170
  textColor,
26321
26171
  minDate,
26322
26172
  maxDate,
26323
- formatDateLabel(earliestStartDateStr),
26324
- formatDateLabel(latestEndDateStr)
26173
+ formatBoundaryLabel(earliestStartDateStr, latestEndDateStr),
26174
+ formatBoundaryLabel(latestEndDateStr, earliestStartDateStr)
26325
26175
  );
26326
26176
  }
26327
26177
  if (timelineGroups.length > 0) {
@@ -28188,9 +28038,9 @@ init_tag_groups();
28188
28038
  var MAX_DEPTH = 10;
28189
28039
  var IMPORT_RE = /^(\s+)import:?\s+(.+\.dgmo)\s*$/i;
28190
28040
  var TAGS_RE = /^tags:?\s+(.+\.dgmo)\s*$/i;
28191
- var HEADER_RE = /^(org|kanban|chart\s*:|title\s*:)/i;
28041
+ var HEADER_RE = /^(org|kanban|title\s*:)/i;
28192
28042
  var KNOWN_HEADER_OPTIONS = /* @__PURE__ */ new Set([
28193
- "direction",
28043
+ "direction-tb",
28194
28044
  "sub-node-label",
28195
28045
  "hide",
28196
28046
  "show-sub-node-count",
@@ -28640,41 +28490,44 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
28640
28490
  series: { description: "Series name(s)" },
28641
28491
  xlabel: { description: "X-axis label" },
28642
28492
  ylabel: { description: "Y-axis label" },
28643
- orientation: { description: "Layout direction", values: ["horizontal", "vertical"] },
28644
- labels: { description: "Label format", values: ["name", "value", "percent", "full"] },
28493
+ "orientation-horizontal": { description: "Switch to horizontal bars" },
28645
28494
  color: { description: "Bar color override" }
28646
28495
  })],
28647
28496
  ["line", withGlobals({
28648
28497
  series: { description: "Series name(s)" },
28649
28498
  xlabel: { description: "X-axis label" },
28650
- ylabel: { description: "Y-axis label" },
28651
- labels: { description: "Label format", values: ["name", "value", "percent", "full"] }
28499
+ ylabel: { description: "Y-axis label" }
28652
28500
  })],
28653
28501
  ["pie", withGlobals({
28654
- labels: { description: "Label format", values: ["name", "value", "percent", "full"] }
28502
+ "no-label-name": { description: "Hide name from segment labels" },
28503
+ "no-label-value": { description: "Hide value from segment labels" },
28504
+ "no-label-percent": { description: "Hide percent from segment labels" }
28655
28505
  })],
28656
28506
  ["doughnut", withGlobals({
28657
- labels: { description: "Label format", values: ["name", "value", "percent", "full"] }
28507
+ "no-label-name": { description: "Hide name from segment labels" },
28508
+ "no-label-value": { description: "Hide value from segment labels" },
28509
+ "no-label-percent": { description: "Hide percent from segment labels" }
28658
28510
  })],
28659
28511
  ["area", withGlobals({
28660
28512
  series: { description: "Series name(s)" },
28661
28513
  xlabel: { description: "X-axis label" },
28662
- ylabel: { description: "Y-axis label" },
28663
- labels: { description: "Label format", values: ["name", "value", "percent", "full"] }
28514
+ ylabel: { description: "Y-axis label" }
28664
28515
  })],
28665
28516
  ["polar-area", withGlobals({
28666
- labels: { description: "Label format", values: ["name", "value", "percent", "full"] }
28517
+ "no-label-name": { description: "Hide name from segment labels" },
28518
+ "no-label-value": { description: "Hide value from segment labels" },
28519
+ "no-label-percent": { description: "Hide percent from segment labels" }
28667
28520
  })],
28668
28521
  ["radar", withGlobals()],
28669
28522
  ["bar-stacked", withGlobals({
28670
28523
  series: { description: "Series name(s) (required)" },
28671
28524
  xlabel: { description: "X-axis label" },
28672
28525
  ylabel: { description: "Y-axis label" },
28673
- orientation: { description: "Layout direction", values: ["horizontal", "vertical"] }
28526
+ "orientation-horizontal": { description: "Switch to horizontal bars" }
28674
28527
  })],
28675
28528
  // ── Extended charts ──────────────────────────────────────
28676
28529
  ["scatter", withGlobals({
28677
- labels: { description: "Show labels", values: ["on", "off"] },
28530
+ "no-labels": { description: "Hide point labels" },
28678
28531
  xlabel: { description: "X-axis label" },
28679
28532
  ylabel: { description: "Y-axis label" },
28680
28533
  sizelabel: { description: "Size axis label" }
@@ -28688,11 +28541,12 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
28688
28541
  ["function", withGlobals({
28689
28542
  x: { description: "X-axis range (start to end)" },
28690
28543
  xlabel: { description: "X-axis label" },
28691
- ylabel: { description: "Y-axis label" }
28544
+ ylabel: { description: "Y-axis label" },
28545
+ shade: { description: "Fill area below curves with translucent color" }
28692
28546
  })],
28693
28547
  // ── Visualizations ───────────────────────────────────────
28694
28548
  ["slope", withGlobals({
28695
- orientation: { description: "Layout direction", values: ["horizontal", "vertical"] }
28549
+ "orientation-vertical": { description: "Switch to vertical layout" }
28696
28550
  })],
28697
28551
  ["wordcloud", withGlobals({
28698
28552
  rotate: { description: "Word rotation", values: ["none", "mixed", "angled"] },
@@ -28700,14 +28554,9 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
28700
28554
  size: { description: "Font size range (min, max)" }
28701
28555
  })],
28702
28556
  ["arc", withGlobals({
28703
- order: { description: "Node ordering", values: ["appearance", "name", "group", "degree"] },
28704
- orientation: { description: "Layout direction" }
28705
- })],
28706
- ["timeline", withGlobals({
28707
- scale: { description: "Show time scale", values: ["on", "off"] },
28708
- sort: { description: "Sort order", values: ["time", "group", "tag"] },
28709
- swimlanes: { description: "Show swimlanes", values: ["on", "off"] }
28557
+ order: { description: "Node ordering", values: ["appearance", "name", "group", "degree"] }
28710
28558
  })],
28559
+ ["timeline", withGlobals()],
28711
28560
  ["venn", withGlobals({
28712
28561
  values: { description: "Show values", values: ["on", "off"] }
28713
28562
  })],
@@ -28721,25 +28570,31 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
28721
28570
  "collapse-notes": { description: "Collapse note blocks", values: ["yes", "no"] },
28722
28571
  "active-tag": { description: "Active tag group name" }
28723
28572
  })],
28724
- ["flowchart", withGlobals()],
28725
- ["class", withGlobals()],
28573
+ ["flowchart", withGlobals({
28574
+ "direction-tb": { description: "Switch to top-to-bottom layout" }
28575
+ })],
28576
+ ["class", withGlobals({
28577
+ "no-auto-color": { description: "Disable automatic modifier-based coloring" }
28578
+ })],
28726
28579
  ["er", withGlobals()],
28727
28580
  ["org", withGlobals({
28728
28581
  "sub-node-label": { description: "Label for sub-nodes" },
28729
28582
  "show-sub-node-count": { description: "Show sub-node counts" }
28730
28583
  })],
28731
- ["kanban", withGlobals()],
28584
+ ["kanban", withGlobals({
28585
+ "no-auto-color": { description: "Disable automatic card coloring" }
28586
+ })],
28732
28587
  ["c4", withGlobals()],
28733
28588
  ["initiative-status", withGlobals()],
28734
28589
  ["state", withGlobals({
28735
- direction: { description: "Layout direction", values: ["TB", "LR"] },
28590
+ "direction-tb": { description: "Switch to top-to-bottom layout" },
28736
28591
  color: { description: "Color mode", values: ["off"] }
28737
28592
  })],
28738
28593
  ["sitemap", withGlobals({
28739
- direction: { description: "Layout direction", values: ["TB", "LR"] }
28594
+ "direction-tb": { description: "Switch to top-to-bottom layout" }
28740
28595
  })],
28741
28596
  ["infra", withGlobals({
28742
- direction: { description: "Layout direction", values: ["LR", "TB"] },
28597
+ "direction-tb": { description: "Switch to top-to-bottom layout" },
28743
28598
  "default-latency-ms": { description: "Default latency for all nodes" },
28744
28599
  "default-uptime": { description: "Default uptime for all nodes" },
28745
28600
  "default-rps": { description: "Default RPS capacity for all nodes" },
@@ -28799,7 +28654,6 @@ var CHART_TYPES = [...ALL_CHART_TYPES].filter((t) => t !== "multi-line").map((na
28799
28654
  }));
28800
28655
  var ENTITY_TYPES = /* @__PURE__ */ new Map([
28801
28656
  ["sequence", ["service", "database", "actor", "queue", "cache", "gateway", "external", "networking", "frontend"]],
28802
- ["infra", ["database", "cache", "queue", "service", "gateway", "storage", "function", "network"]],
28803
28657
  ["c4", ["person", "system", "container", "component", "external", "database"]]
28804
28658
  ]);
28805
28659
  var PIPE_METADATA = /* @__PURE__ */ new Map([