@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.cjs CHANGED
@@ -1483,20 +1483,6 @@ function extractColor(label, palette) {
1483
1483
  function parseFirstLine(line10) {
1484
1484
  const trimmed = line10.trim();
1485
1485
  if (!trimmed || trimmed.startsWith("//")) return null;
1486
- const oldMatch = trimmed.match(CHART_TYPE_RE);
1487
- if (oldMatch) {
1488
- const parts = oldMatch[1].trim();
1489
- const spaceIdx2 = parts.indexOf(" ");
1490
- if (spaceIdx2 === -1) {
1491
- const ct2 = parts.toLowerCase();
1492
- return ALL_CHART_TYPES.has(ct2) ? { chartType: ct2, title: void 0 } : null;
1493
- }
1494
- const ct = parts.substring(0, spaceIdx2).toLowerCase();
1495
- if (ALL_CHART_TYPES.has(ct)) {
1496
- return { chartType: ct, title: parts.substring(spaceIdx2 + 1).trim() || void 0 };
1497
- }
1498
- return null;
1499
- }
1500
1486
  const spaceIdx = trimmed.indexOf(" ");
1501
1487
  if (spaceIdx === -1) {
1502
1488
  const ct = trimmed.toLowerCase();
@@ -1580,12 +1566,6 @@ function parseSeriesNames(value, lines, lineIndex, palette) {
1580
1566
  }
1581
1567
  return { series, names, nameColors, nameLineNumbers, newIndex };
1582
1568
  }
1583
- function normalizeDirection(value) {
1584
- const v = value.trim().toLowerCase();
1585
- if (v === "lr" || v === "horizontal") return "LR";
1586
- if (v === "tb" || v === "vertical") return "TB";
1587
- return null;
1588
- }
1589
1569
  function inferArrowColor(label) {
1590
1570
  const lower = label.toLowerCase();
1591
1571
  if (lower === "yes" || lower === "success" || lower === "ok" || lower === "true") return "green";
@@ -1593,9 +1573,10 @@ function inferArrowColor(label) {
1593
1573
  if (lower === "maybe" || lower === "warning") return "orange";
1594
1574
  return void 0;
1595
1575
  }
1596
- function parsePipeMetadata(segments, aliasMap = /* @__PURE__ */ new Map(), warnMultiplePipes) {
1597
- if (segments.length > 2 && warnMultiplePipes) {
1598
- warnMultiplePipes();
1576
+ function parsePipeMetadata(segments, aliasMap = /* @__PURE__ */ new Map(), errorMultiplePipes) {
1577
+ if (segments.length > 2) {
1578
+ if (errorMultiplePipes) errorMultiplePipes();
1579
+ return {};
1599
1580
  }
1600
1581
  const metadata = {};
1601
1582
  const raw = segments.slice(1).join(",");
@@ -1612,7 +1593,7 @@ function parsePipeMetadata(segments, aliasMap = /* @__PURE__ */ new Map(), warnM
1612
1593
  }
1613
1594
  return metadata;
1614
1595
  }
1615
- var ALL_CHART_TYPES, COLOR_SUFFIX_RE, CHART_TYPE_RE, TITLE_RE, OPTION_NOCOLON_RE, GROUP_HASH_RE, DOUBLE_HASH_RE, MULTIPLE_PIPE_WARNING;
1596
+ var ALL_CHART_TYPES, COLOR_SUFFIX_RE, TITLE_RE, OPTION_NOCOLON_RE, MULTIPLE_PIPE_ERROR;
1616
1597
  var init_parsing = __esm({
1617
1598
  "src/utils/parsing.ts"() {
1618
1599
  "use strict";
@@ -1656,12 +1637,9 @@ var init_parsing = __esm({
1656
1637
  "gantt"
1657
1638
  ]);
1658
1639
  COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
1659
- CHART_TYPE_RE = /^chart\s*:\s*(.+)/i;
1660
1640
  TITLE_RE = /^title\s*:\s*(.+)/i;
1661
1641
  OPTION_NOCOLON_RE = /^([a-z][a-z0-9-]*)\s+(.+)$/i;
1662
- GROUP_HASH_RE = /^#\s+(.+)$/;
1663
- DOUBLE_HASH_RE = /^##\s/;
1664
- MULTIPLE_PIPE_WARNING = 'Use a single "|" to start metadata, then separate items with commas.';
1642
+ MULTIPLE_PIPE_ERROR = 'Use a single "|" to start metadata, then separate items with commas.';
1665
1643
  }
1666
1644
  });
1667
1645
 
@@ -1670,7 +1648,7 @@ function isAliasToken(token) {
1670
1648
  return /^[a-z]{1,4}$/.test(token);
1671
1649
  }
1672
1650
  function isTagBlockHeading(trimmed) {
1673
- return TAG_BLOCK_NOCOLON_RE.test(trimmed) || TAG_BLOCK_RE.test(trimmed) || GROUP_HEADING_RE.test(trimmed);
1651
+ return TAG_BLOCK_NOCOLON_RE.test(trimmed);
1674
1652
  }
1675
1653
  function parseTagDeclaration(line10) {
1676
1654
  if (!TAG_BLOCK_NOCOLON_RE.test(line10)) return null;
@@ -1740,7 +1718,6 @@ function parseTagDeclaration(line10) {
1740
1718
  name,
1741
1719
  alias,
1742
1720
  colorHint,
1743
- deprecated: false,
1744
1721
  inlineValues: inlineValues && inlineValues.length > 0 ? inlineValues : void 0
1745
1722
  };
1746
1723
  }
@@ -1805,36 +1782,14 @@ function injectDefaultTagMetadata(entities, tagGroups, skip) {
1805
1782
  }
1806
1783
  }
1807
1784
  function matchTagBlockHeading(trimmed) {
1808
- const nocolonResult = parseTagDeclaration(trimmed);
1809
- if (nocolonResult) return nocolonResult;
1810
- const tagMatch = trimmed.match(TAG_BLOCK_RE);
1811
- if (tagMatch) {
1812
- return {
1813
- name: tagMatch[1].trim(),
1814
- alias: tagMatch[2] || void 0,
1815
- colorHint: tagMatch[3] || void 0,
1816
- deprecated: false
1817
- };
1818
- }
1819
- const groupMatch = trimmed.match(GROUP_HEADING_RE);
1820
- if (groupMatch) {
1821
- return {
1822
- name: groupMatch[1].trim(),
1823
- alias: groupMatch[2] || void 0,
1824
- colorHint: groupMatch[3] || void 0,
1825
- deprecated: true
1826
- };
1827
- }
1828
- return null;
1785
+ return parseTagDeclaration(trimmed);
1829
1786
  }
1830
- var TAG_BLOCK_RE, TAG_BLOCK_NOCOLON_RE, GROUP_HEADING_RE;
1787
+ var TAG_BLOCK_NOCOLON_RE;
1831
1788
  var init_tag_groups = __esm({
1832
1789
  "src/utils/tag-groups.ts"() {
1833
1790
  "use strict";
1834
1791
  init_parsing();
1835
- TAG_BLOCK_RE = /^tag:\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/i;
1836
1792
  TAG_BLOCK_NOCOLON_RE = /^tag\s+/i;
1837
- GROUP_HEADING_RE = /^##\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/;
1838
1793
  }
1839
1794
  });
1840
1795
 
@@ -2319,18 +2274,7 @@ function isSequenceNote(el) {
2319
2274
  function parseNoteLine(trimmed, participants, lastMsgFrom) {
2320
2275
  const lower = trimmed.toLowerCase();
2321
2276
  if (!lower.startsWith("note")) return null;
2322
- if (trimmed.length > 4 && trimmed[4] !== " " && trimmed[4] !== ":") return null;
2323
- const colonMatch = trimmed.match(NOTE_SINGLE_COLON);
2324
- if (colonMatch) {
2325
- const position = colonMatch[1]?.toLowerCase() || "right";
2326
- let participantId = colonMatch[2] || null;
2327
- if (!participantId) {
2328
- if (!lastMsgFrom) return { kind: "skip" };
2329
- participantId = lastMsgFrom;
2330
- }
2331
- if (!participants.some((p) => p.id === participantId)) return { kind: "skip" };
2332
- return { kind: "single", position, participantId, text: colonMatch[3].trim() };
2333
- }
2277
+ if (trimmed.length > 4 && trimmed[4] !== " ") return null;
2334
2278
  const multiMatch = trimmed.match(NOTE_MULTI);
2335
2279
  if (multiMatch) {
2336
2280
  const position = multiMatch[1]?.toLowerCase() || "right";
@@ -2462,7 +2406,7 @@ function parseSequenceDgmo(content) {
2462
2406
  if (idx < 0) return { core: text };
2463
2407
  const core = text.substring(0, idx).trimEnd();
2464
2408
  const segments = text.substring(idx).split("|");
2465
- const warnFn = ln != null ? () => pushWarning(ln, MULTIPLE_PIPE_WARNING) : void 0;
2409
+ const warnFn = ln != null ? () => pushError(ln, MULTIPLE_PIPE_ERROR) : void 0;
2466
2410
  const meta = parsePipeMetadata(segments, aliasMap, warnFn);
2467
2411
  return Object.keys(meta).length > 0 ? { core, meta } : { core };
2468
2412
  };
@@ -2492,7 +2436,7 @@ function parseSequenceDgmo(content) {
2492
2436
  const afterBracket = groupMatch[3]?.trim() || "";
2493
2437
  if (afterBracket.startsWith("|")) {
2494
2438
  const segments = afterBracket.split("|");
2495
- const meta = parsePipeMetadata(segments, aliasMap, () => pushWarning(lineNumber, MULTIPLE_PIPE_WARNING));
2439
+ const meta = parsePipeMetadata(segments, aliasMap, () => pushError(lineNumber, MULTIPLE_PIPE_ERROR));
2496
2440
  if (Object.keys(meta).length > 0) groupMeta = meta;
2497
2441
  }
2498
2442
  if (groupColor) {
@@ -2536,7 +2480,7 @@ function parseSequenceDgmo(content) {
2536
2480
  continue;
2537
2481
  }
2538
2482
  const tagBlockMatch = matchTagBlockHeading(trimmed);
2539
- if (tagBlockMatch && !tagBlockMatch.deprecated) {
2483
+ if (tagBlockMatch) {
2540
2484
  if (contentStarted) {
2541
2485
  pushError(lineNumber, "Tag groups must appear before sequence content");
2542
2486
  continue;
@@ -2597,13 +2541,6 @@ function parseSequenceDgmo(content) {
2597
2541
  if (key === "note" || key.startsWith("note ")) {
2598
2542
  } else {
2599
2543
  const value = trimmed.substring(colonIndex + 1).trim();
2600
- if (key === "chart") {
2601
- hasExplicitChart = true;
2602
- if (value.toLowerCase() !== "sequence") {
2603
- return fail(lineNumber, `Expected chart type "sequence", got "${value}"`);
2604
- }
2605
- continue;
2606
- }
2607
2544
  if (contentStarted) {
2608
2545
  pushError(lineNumber, `Options like '${key}: ${value}' must appear before the first message or declaration`);
2609
2546
  continue;
@@ -2645,7 +2582,7 @@ function parseSequenceDgmo(content) {
2645
2582
  }
2646
2583
  }
2647
2584
  const { core: isACore, meta: isAMeta } = splitPipe(trimmed, lineNumber);
2648
- const isAMatch = isACore.match(IS_A_PATTERN);
2585
+ const isAMatch = !/^note(\s|$)/i.test(trimmed) ? isACore.match(IS_A_PATTERN) : null;
2649
2586
  if (isAMatch) {
2650
2587
  contentStarted = true;
2651
2588
  const id = isAMatch[1];
@@ -3075,7 +3012,7 @@ function looksLikeSequence(content) {
3075
3012
  return ARROW_PATTERN.test(trimmed);
3076
3013
  });
3077
3014
  }
3078
- 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;
3015
+ 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;
3079
3016
  var init_parser = __esm({
3080
3017
  "src/sequence/parser.ts"() {
3081
3018
  "use strict";
@@ -3105,9 +3042,8 @@ var init_parser = __esm({
3105
3042
  LEGACY_GROUP_PATTERN = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
3106
3043
  SECTION_PATTERN = /^==\s+(.+?)(?:\s*==)?\s*$/;
3107
3044
  ARROW_PATTERN = /\S+\s*(?:<-\S+-|<~\S+~|-\S+->|~\S+~>|->|~>|<-|<~)\s*\S+/;
3108
- NOTE_SINGLE_COLON = /^note(?:\s+(right|left)(?:\s+(?:of\s+)?(.+?))?)?\s*:\s*(.+)$/i;
3109
3045
  NOTE_BARE = /^note\s+(.+)$/i;
3110
- NOTE_MULTI = /^note(?:\s+(right|left)(?:\s+(?:of\s+)?(.+?))?)?\s*:?\s*$/i;
3046
+ NOTE_MULTI = /^note(?:\s+(right|left)(?:\s+(?:of\s+)?(.+?))?)?\s*$/i;
3111
3047
  }
3112
3048
  });
3113
3049
 
@@ -3233,7 +3169,7 @@ function parseFlowchart(content, palette) {
3233
3169
  const lines = content.split("\n");
3234
3170
  const result = {
3235
3171
  type: "flowchart",
3236
- direction: "TB",
3172
+ direction: "LR",
3237
3173
  nodes: [],
3238
3174
  edges: [],
3239
3175
  options: {},
@@ -3250,9 +3186,6 @@ function parseFlowchart(content, palette) {
3250
3186
  const indentStack = [];
3251
3187
  let contentStarted = false;
3252
3188
  let firstLineParsed = false;
3253
- let currentGroup = null;
3254
- let groupIndent = -1;
3255
- const groups = [];
3256
3189
  function getOrCreateNode(ref, lineNumber) {
3257
3190
  const existing = nodeMap.get(ref.id);
3258
3191
  if (existing) return existing;
@@ -3261,14 +3194,10 @@ function parseFlowchart(content, palette) {
3261
3194
  label: ref.label,
3262
3195
  shape: ref.shape,
3263
3196
  lineNumber,
3264
- ...ref.color && { color: ref.color },
3265
- ...currentGroup && { group: currentGroup.id }
3197
+ ...ref.color && { color: ref.color }
3266
3198
  };
3267
3199
  nodeMap.set(ref.id, node);
3268
3200
  result.nodes.push(node);
3269
- if (currentGroup && !currentGroup.nodeIds.includes(ref.id)) {
3270
- currentGroup.nodeIds.push(ref.id);
3271
- }
3272
3201
  return node;
3273
3202
  }
3274
3203
  function addEdge(sourceId, targetId, lineNumber, label, color) {
@@ -3363,38 +3292,15 @@ function parseFlowchart(content, palette) {
3363
3292
  continue;
3364
3293
  }
3365
3294
  }
3366
- if (DOUBLE_HASH_RE.test(trimmed)) {
3367
- result.diagnostics.push(
3368
- makeDgmoError(lineNumber, "Use `#` for groups \u2014 nesting is done with indentation.", "error")
3369
- );
3370
- continue;
3371
- }
3372
- const hashGroupMatch = trimmed.match(GROUP_HASH_RE);
3373
- if (hashGroupMatch) {
3374
- const { label, color } = extractColor(hashGroupMatch[1].trim(), palette);
3375
- currentGroup = {
3376
- id: `group:${label.toLowerCase()}`,
3377
- label,
3378
- nodeIds: [],
3379
- lineNumber,
3380
- ...color && { color }
3381
- };
3382
- groupIndent = indent;
3383
- groups.push(currentGroup);
3384
- continue;
3385
- }
3386
3295
  if (!contentStarted) {
3296
+ if (/^direction-tb$/i.test(trimmed)) {
3297
+ result.direction = "TB";
3298
+ continue;
3299
+ }
3387
3300
  const optMatch = trimmed.match(OPTION_NOCOLON_RE);
3388
3301
  if (optMatch && !trimmed.includes("->")) {
3389
3302
  const key = optMatch[1].toLowerCase();
3390
3303
  const value = optMatch[2].trim();
3391
- if (key === "direction" || key === "orientation") {
3392
- const dir = normalizeDirection(value);
3393
- if (dir) {
3394
- result.direction = dir;
3395
- }
3396
- continue;
3397
- }
3398
3304
  if (key === "no-color") {
3399
3305
  result.options["color"] = "off";
3400
3306
  continue;
@@ -3403,13 +3309,8 @@ function parseFlowchart(content, palette) {
3403
3309
  continue;
3404
3310
  }
3405
3311
  }
3406
- if (currentGroup && indent <= groupIndent) {
3407
- currentGroup = null;
3408
- groupIndent = -1;
3409
- }
3410
3312
  processContentLine(trimmed, lineNumber, indent);
3411
3313
  }
3412
- if (groups.length > 0) result.groups = groups;
3413
3314
  if (result.nodes.length === 0 && !result.error) {
3414
3315
  const diag = makeDgmoError(1, "No nodes found. Add flowchart content with shape syntax like [Process] or (Start).");
3415
3316
  result.diagnostics.push(diag);
@@ -3548,7 +3449,7 @@ function parseState(content, palette) {
3548
3449
  const lines = content.split("\n");
3549
3450
  const result = {
3550
3451
  type: "state",
3551
- direction: "TB",
3452
+ direction: "LR",
3552
3453
  nodes: [],
3553
3454
  edges: [],
3554
3455
  options: {},
@@ -3637,17 +3538,14 @@ function parseState(content, palette) {
3637
3538
  continue;
3638
3539
  }
3639
3540
  if (!contentStarted) {
3541
+ if (/^direction-tb$/i.test(trimmed)) {
3542
+ result.direction = "TB";
3543
+ continue;
3544
+ }
3640
3545
  const optMatch = trimmed.match(OPTION_NOCOLON_RE);
3641
3546
  if (optMatch && !trimmed.includes("->")) {
3642
3547
  const key = optMatch[1].toLowerCase();
3643
3548
  const value = optMatch[2].trim();
3644
- if (key === "direction" || key === "orientation") {
3645
- const dir = normalizeDirection(value);
3646
- if (dir) {
3647
- result.direction = dir;
3648
- }
3649
- continue;
3650
- }
3651
3549
  if (key === "no-color") {
3652
3550
  result.options["color"] = "off";
3653
3551
  continue;
@@ -3868,6 +3766,10 @@ function parseClassDiagram(content, palette) {
3868
3766
  }
3869
3767
  }
3870
3768
  if (!contentStarted && indent === 0 && /^[a-z]/.test(trimmed)) {
3769
+ if (trimmed.toLowerCase() === "no-auto-color") {
3770
+ result.options["no-auto-color"] = "on";
3771
+ continue;
3772
+ }
3871
3773
  const optMatch = trimmed.match(OPTION_NOCOLON_RE);
3872
3774
  if (optMatch) {
3873
3775
  const key = optMatch[1].toLowerCase();
@@ -3879,6 +3781,21 @@ function parseClassDiagram(content, palette) {
3879
3781
  }
3880
3782
  }
3881
3783
  if (indent > 0 && currentClass) {
3784
+ const indentRel = trimmed.match(INDENT_REL_ARROW_RE);
3785
+ if (indentRel) {
3786
+ const arrow = indentRel[1];
3787
+ const targetName = indentRel[2];
3788
+ const label = indentRel[3]?.trim();
3789
+ getOrCreateClass(targetName, lineNumber);
3790
+ result.relationships.push({
3791
+ source: currentClass.id,
3792
+ target: classId(targetName),
3793
+ type: ARROW_TO_TYPE[arrow],
3794
+ ...label && { label },
3795
+ lineNumber
3796
+ });
3797
+ continue;
3798
+ }
3882
3799
  const member = parseMember(
3883
3800
  trimmed,
3884
3801
  lineNumber,
@@ -3896,16 +3813,13 @@ function parseClassDiagram(content, palette) {
3896
3813
  const sourceName = relArrow[1];
3897
3814
  const arrow = relArrow[2];
3898
3815
  const targetName = relArrow[3];
3899
- const label = relArrow[4]?.trim();
3900
- getOrCreateClass(sourceName, lineNumber);
3901
- getOrCreateClass(targetName, lineNumber);
3902
- result.relationships.push({
3903
- source: classId(sourceName),
3904
- target: classId(targetName),
3905
- type: ARROW_TO_TYPE[arrow],
3906
- ...label && { label },
3907
- lineNumber
3908
- });
3816
+ result.diagnostics.push(
3817
+ makeDgmoError(
3818
+ lineNumber,
3819
+ `Relationship "${sourceName} ${arrow} ${targetName}" must be indented under the source class "${sourceName}"`,
3820
+ "warning"
3821
+ )
3822
+ );
3909
3823
  continue;
3910
3824
  }
3911
3825
  const classDecl = trimmed.match(CLASS_DECL_RE);
@@ -3989,6 +3903,9 @@ function looksLikeClassDiagram(content) {
3989
3903
  if (/^[+\-#]?\s*\w+.*[:(]/.test(trimmed)) {
3990
3904
  hasIndentedMember = true;
3991
3905
  }
3906
+ if (INDENT_REL_ARROW_RE.test(trimmed)) {
3907
+ hasRelationship = true;
3908
+ }
3992
3909
  }
3993
3910
  }
3994
3911
  if (hasModifier) return true;
@@ -4001,6 +3918,7 @@ function extractSymbols2(docText) {
4001
3918
  for (const rawLine of docText.split("\n")) {
4002
3919
  const line10 = rawLine.trim();
4003
3920
  if (inMetadata && (/^[a-z-]+\s*:/i.test(line10) || /^class(\s|$)/i.test(line10))) continue;
3921
+ if (inMetadata && line10.toLowerCase() === "no-auto-color") continue;
4004
3922
  if (inMetadata && /^[a-z]/.test(line10) && OPTION_NOCOLON_RE.test(line10)) {
4005
3923
  const key = line10.match(OPTION_NOCOLON_RE)[1].toLowerCase();
4006
3924
  if (key !== "abstract" && key !== "interface" && key !== "enum") continue;
@@ -4019,7 +3937,7 @@ function extractSymbols2(docText) {
4019
3937
  keywords: ["extends", "implements", "abstract", "interface", "enum"]
4020
3938
  };
4021
3939
  }
4022
- var CLASS_DECL_RE, REL_ARROW_RE, VISIBILITY_RE, STATIC_SUFFIX_RE, METHOD_RE, FIELD_RE, ARROW_TO_TYPE;
3940
+ var CLASS_DECL_RE, INDENT_REL_ARROW_RE, REL_ARROW_RE, VISIBILITY_RE, STATIC_SUFFIX_RE, METHOD_RE, FIELD_RE, ARROW_TO_TYPE;
4023
3941
  var init_parser2 = __esm({
4024
3942
  "src/class/parser.ts"() {
4025
3943
  "use strict";
@@ -4027,6 +3945,7 @@ var init_parser2 = __esm({
4027
3945
  init_diagnostics();
4028
3946
  init_parsing();
4029
3947
  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*$/;
3948
+ INDENT_REL_ARROW_RE = /^(--\|>|\.\.\|>|\*--|o--|\.\.\>|->)\s*([A-Z][A-Za-z0-9_]*)(?:\s+:?\s*(.+))?$/;
4030
3949
  REL_ARROW_RE = /^([A-Z][A-Za-z0-9_]*)\s*(--\|>|\.\.\|>|\*--|o--|\.\.\>|->)\s*([A-Z][A-Za-z0-9_]*)(?:\s+:?\s*(.+))?$/;
4031
3950
  VISIBILITY_RE = /^([+\-#])\s*/;
4032
3951
  STATIC_SUFFIX_RE = /\{static\}\s*$/;
@@ -4172,13 +4091,6 @@ function parseERDiagram(content, palette) {
4172
4091
  if (!contentStarted && indent === 0) {
4173
4092
  const tagBlockMatch = matchTagBlockHeading(trimmed);
4174
4093
  if (tagBlockMatch) {
4175
- if (tagBlockMatch.deprecated) {
4176
- result.diagnostics.push(makeDgmoError(
4177
- lineNumber,
4178
- `'## ${tagBlockMatch.name}' is no longer supported \u2014 use 'tag: ${tagBlockMatch.name}' instead`
4179
- ));
4180
- continue;
4181
- }
4182
4094
  currentTagGroup = {
4183
4095
  name: tagBlockMatch.name,
4184
4096
  alias: tagBlockMatch.alias,
@@ -4255,15 +4167,13 @@ function parseERDiagram(content, palette) {
4255
4167
  contentStarted = true;
4256
4168
  const rel = parseRelationship(trimmed, lineNumber, pushError);
4257
4169
  if (rel) {
4258
- getOrCreateTable(rel.source, lineNumber);
4259
- getOrCreateTable(rel.target, lineNumber);
4260
- result.relationships.push({
4261
- source: tableId(rel.source),
4262
- target: tableId(rel.target),
4263
- cardinality: { from: rel.from, to: rel.to },
4264
- ...rel.label && { label: rel.label },
4265
- lineNumber
4266
- });
4170
+ result.diagnostics.push(
4171
+ makeDgmoError(
4172
+ lineNumber,
4173
+ `Relationship "${rel.source} \u2192 ${rel.target}" must be indented under the source table "${rel.source}"`,
4174
+ "warning"
4175
+ )
4176
+ );
4267
4177
  continue;
4268
4178
  }
4269
4179
  const tableDecl = trimmed.match(TABLE_DECL_RE);
@@ -4472,6 +4382,12 @@ function parseChart(content, palette) {
4472
4382
  }
4473
4383
  const spaceIdx = trimmed.indexOf(" ");
4474
4384
  const firstToken = (spaceIdx >= 0 ? trimmed.substring(0, spaceIdx) : trimmed).toLowerCase();
4385
+ if (KNOWN_BOOLEANS.has(firstToken) && spaceIdx < 0) {
4386
+ if (firstToken === "orientation-horizontal") {
4387
+ result.orientation = "horizontal";
4388
+ }
4389
+ continue;
4390
+ }
4475
4391
  if (KNOWN_OPTIONS2.has(firstToken) && spaceIdx >= 0) {
4476
4392
  const value = trimmed.substring(spaceIdx + 1).trim();
4477
4393
  if (firstToken === "chart") {
@@ -4506,26 +4422,6 @@ function parseChart(content, palette) {
4506
4422
  result.label = value;
4507
4423
  continue;
4508
4424
  }
4509
- if (firstToken === "labels") {
4510
- const v = value.toLowerCase();
4511
- if (v === "name" || v === "value" || v === "percent" || v === "full") {
4512
- result.labels = v;
4513
- }
4514
- continue;
4515
- }
4516
- if (firstToken === "orientation" || firstToken === "direction") {
4517
- if (result.type === "bar" || result.type === "bar-stacked") {
4518
- const vLower = value.toLowerCase();
4519
- if (vLower === "horizontal" || vLower === "vertical") {
4520
- result.orientation = vLower;
4521
- } else {
4522
- const dir = normalizeDirection(value);
4523
- if (dir === "LR") result.orientation = "horizontal";
4524
- else if (dir === "TB") result.orientation = "vertical";
4525
- }
4526
- }
4527
- continue;
4528
- }
4529
4425
  if (firstToken === "color") {
4530
4426
  result.color = resolveColor(value.trim(), palette) ?? void 0;
4531
4427
  continue;
@@ -4543,6 +4439,18 @@ function parseChart(content, palette) {
4543
4439
  continue;
4544
4440
  }
4545
4441
  }
4442
+ if (firstToken === "no-label-name") {
4443
+ result.noLabelName = true;
4444
+ continue;
4445
+ }
4446
+ if (firstToken === "no-label-value") {
4447
+ result.noLabelValue = true;
4448
+ continue;
4449
+ }
4450
+ if (firstToken === "no-label-percent") {
4451
+ result.noLabelPercent = true;
4452
+ continue;
4453
+ }
4546
4454
  if (firstToken === "series" && spaceIdx === -1) {
4547
4455
  const parsed = parseSeriesNames("", lines, i, palette);
4548
4456
  i = parsed.newIndex;
@@ -4555,7 +4463,8 @@ function parseChart(content, palette) {
4555
4463
  if (parsed.nameColors.some(Boolean)) result.seriesNameColors = parsed.nameColors;
4556
4464
  continue;
4557
4465
  }
4558
- const dataValues = parseDataRowValues(trimmed);
4466
+ const multiValue = (result.seriesNames?.length ?? 0) >= 2;
4467
+ const dataValues = parseDataRowValues(trimmed, { multiValue });
4559
4468
  if (dataValues) {
4560
4469
  const { label: rawLabel, color: pointColor } = extractColor(dataValues.label, palette);
4561
4470
  const [first, ...rest] = dataValues.values;
@@ -4611,7 +4520,7 @@ function parseChart(content, palette) {
4611
4520
  for (const dp of result.data) {
4612
4521
  const actualCount = 1 + (dp.extraValues?.length ?? 0);
4613
4522
  if (actualCount !== expectedCount) {
4614
- warn(dp.lineNumber, `Data point "${dp.label}" has ${actualCount} value(s), but ${expectedCount} series defined. Each row must have ${expectedCount} comma-separated values.`);
4523
+ warn(dp.lineNumber, `Data point "${dp.label}" has ${actualCount} value(s), but ${expectedCount} series defined. Each row must have ${expectedCount} values.`);
4615
4524
  }
4616
4525
  }
4617
4526
  result.data = result.data.filter((dp) => {
@@ -4621,12 +4530,12 @@ function parseChart(content, palette) {
4621
4530
  }
4622
4531
  return result;
4623
4532
  }
4624
- function parseDataRowValues(line10) {
4533
+ function parseDataRowValues(line10, options) {
4625
4534
  const segments = line10.split(",");
4626
4535
  const normalized = [];
4627
4536
  for (let i = 0; i < segments.length; i++) {
4628
4537
  const seg = segments[i].trim();
4629
- if (i > 0 && /^\d{3}$/.test(seg) && !/^\s/.test(segments[i])) {
4538
+ if (i > 0 && /^\d{3}(\.\d+)?$/.test(seg) && !/^\s/.test(segments[i])) {
4630
4539
  const prevSeg = normalized[normalized.length - 1].trimEnd();
4631
4540
  if (/\d{1,3}$/.test(prevSeg)) {
4632
4541
  const prevMatch = prevSeg.match(/(\d{1,3})$/);
@@ -4655,11 +4564,11 @@ function parseDataRowValues(line10) {
4655
4564
  const splitAt = commaParts.length - numericCount;
4656
4565
  const extraValueParts = commaParts.slice(splitAt);
4657
4566
  const firstPart = commaParts.slice(0, splitAt).join(",").trim();
4658
- const lastSpaceIdx2 = firstPart.lastIndexOf(" ");
4659
- if (lastSpaceIdx2 >= 0) {
4660
- const possibleFirstVal = firstPart.substring(lastSpaceIdx2 + 1).trim();
4567
+ const lastSpaceIdx = firstPart.lastIndexOf(" ");
4568
+ if (lastSpaceIdx >= 0) {
4569
+ const possibleFirstVal = firstPart.substring(lastSpaceIdx + 1).trim();
4661
4570
  if (possibleFirstVal && !isNaN(parseFloat(possibleFirstVal)) && isFinite(Number(possibleFirstVal))) {
4662
- const label2 = firstPart.substring(0, lastSpaceIdx2).trim();
4571
+ const label2 = firstPart.substring(0, lastSpaceIdx).trim();
4663
4572
  if (label2) {
4664
4573
  const values = [parseFloat(possibleFirstVal)];
4665
4574
  for (const p of extraValueParts) {
@@ -4671,16 +4580,31 @@ function parseDataRowValues(line10) {
4671
4580
  }
4672
4581
  }
4673
4582
  }
4674
- const lastSpaceIdx = rebuilt.lastIndexOf(" ");
4675
- if (lastSpaceIdx < 0) return null;
4676
- const possibleValue = rebuilt.substring(lastSpaceIdx + 1).trim();
4677
- const num = parseFloat(possibleValue);
4678
- if (isNaN(num) || !isFinite(Number(possibleValue))) return null;
4679
- const label = rebuilt.substring(0, lastSpaceIdx).trim();
4583
+ const tokens = rebuilt.split(/\s+/);
4584
+ if (tokens.length < 2) return null;
4585
+ if (options?.multiValue) {
4586
+ const values = [];
4587
+ let idx = tokens.length - 1;
4588
+ while (idx >= 1) {
4589
+ const tok = tokens[idx];
4590
+ const num2 = parseFloat(tok);
4591
+ if (isNaN(num2) || !isFinite(Number(tok))) break;
4592
+ values.unshift(num2);
4593
+ idx--;
4594
+ }
4595
+ if (values.length === 0) return null;
4596
+ const label2 = tokens.slice(0, idx + 1).join(" ");
4597
+ if (!label2) return null;
4598
+ return { label: label2, values };
4599
+ }
4600
+ const lastToken = tokens[tokens.length - 1];
4601
+ const num = parseFloat(lastToken);
4602
+ if (isNaN(num) || !isFinite(Number(lastToken))) return null;
4603
+ const label = tokens.slice(0, -1).join(" ");
4680
4604
  if (!label) return null;
4681
4605
  return { label, values: [num] };
4682
4606
  }
4683
- var VALID_TYPES, TYPE_ALIASES, KNOWN_OPTIONS2;
4607
+ var VALID_TYPES, TYPE_ALIASES, KNOWN_OPTIONS2, KNOWN_BOOLEANS;
4684
4608
  var init_chart = __esm({
4685
4609
  "src/chart.ts"() {
4686
4610
  "use strict";
@@ -4707,11 +4631,14 @@ var init_chart = __esm({
4707
4631
  "xlabel",
4708
4632
  "ylabel",
4709
4633
  "label",
4710
- "labels",
4711
- "orientation",
4712
- "direction",
4634
+ "no-label-name",
4635
+ "no-label-value",
4636
+ "no-label-percent",
4713
4637
  "color"
4714
4638
  ]);
4639
+ KNOWN_BOOLEANS = /* @__PURE__ */ new Set([
4640
+ "orientation-horizontal"
4641
+ ]);
4715
4642
  }
4716
4643
  });
4717
4644
 
@@ -4805,7 +4732,7 @@ var init_legend_svg = __esm({
4805
4732
 
4806
4733
  // src/echarts.ts
4807
4734
  function parseScatterRow(line10, palette, currentCategory, lineNumber) {
4808
- const dataRow = parseDataRowValues(line10);
4735
+ const dataRow = parseDataRowValues(line10, { multiValue: true });
4809
4736
  if (!dataRow || dataRow.values.length < 2) return null;
4810
4737
  const { label: rawLabel, color: pointColor } = extractColor(dataRow.label, palette);
4811
4738
  return {
@@ -4999,13 +4926,9 @@ function parseExtendedChart(content, palette) {
4999
4926
  result.sizelabel = value;
5000
4927
  continue;
5001
4928
  }
5002
- if (firstToken === "labels") {
5003
- result.showLabels = value.toLowerCase() === "on" || value.toLowerCase() === "true";
5004
- continue;
5005
- }
5006
4929
  if (firstToken === "columns") {
5007
4930
  if (value) {
5008
- result.columns = value.split(",").map((s) => s.trim());
4931
+ result.columns = value.includes(",") ? value.split(",").map((s) => s.trim()) : value.split(/\s+/);
5009
4932
  } else {
5010
4933
  const collected = collectIndentedValues(lines, i);
5011
4934
  i = collected.newIndex;
@@ -5015,7 +4938,7 @@ function parseExtendedChart(content, palette) {
5015
4938
  }
5016
4939
  if (firstToken === "rows") {
5017
4940
  if (value) {
5018
- result.rows = value.split(",").map((s) => s.trim());
4941
+ result.rows = value.includes(",") ? value.split(",").map((s) => s.trim()) : value.split(/\s+/);
5019
4942
  } else {
5020
4943
  const collected = collectIndentedValues(lines, i);
5021
4944
  i = collected.newIndex;
@@ -5034,6 +4957,14 @@ function parseExtendedChart(content, palette) {
5034
4957
  continue;
5035
4958
  }
5036
4959
  }
4960
+ if (firstToken === "no-labels") {
4961
+ result.showLabels = false;
4962
+ continue;
4963
+ }
4964
+ if (firstToken === "shade") {
4965
+ result.shade = true;
4966
+ continue;
4967
+ }
5037
4968
  if (firstToken === "series" && spaceIdx === -1) {
5038
4969
  const parsed = parseSeriesNames("", lines, i, palette);
5039
4970
  i = parsed.newIndex;
@@ -5082,7 +5013,7 @@ function parseExtendedChart(content, palette) {
5082
5013
  }
5083
5014
  }
5084
5015
  if (result.type === "heatmap") {
5085
- const dataRow2 = parseDataRowValues(trimmed);
5016
+ const dataRow2 = parseDataRowValues(trimmed, { multiValue: true });
5086
5017
  if (dataRow2 && dataRow2.values.length > 0) {
5087
5018
  if (!result.heatmapRows) result.heatmapRows = [];
5088
5019
  result.heatmapRows.push({ label: dataRow2.label, values: dataRow2.values, lineNumber });
@@ -5411,6 +5342,12 @@ function buildFunctionOption(parsed, palette, textColor, axisLineColor, gridOpac
5411
5342
  itemStyle: {
5412
5343
  color: fnColor
5413
5344
  },
5345
+ ...parsed.shade && {
5346
+ areaStyle: {
5347
+ color: fnColor,
5348
+ opacity: 0.15
5349
+ }
5350
+ },
5414
5351
  emphasis: EMPHASIS_SELF
5415
5352
  };
5416
5353
  });
@@ -5658,7 +5595,7 @@ function buildScatterOption(parsed, palette, textColor, axisLineColor, gridOpaci
5658
5595
  const defaultSize = 15;
5659
5596
  const hasCategories = points.some((p) => p.category !== void 0);
5660
5597
  const hasSize = points.some((p) => p.size !== void 0);
5661
- const showLabels = parsed.showLabels ?? false;
5598
+ const showLabels = parsed.showLabels ?? true;
5662
5599
  const labelFontSize = 11;
5663
5600
  const labelConfig = {
5664
5601
  show: false,
@@ -6346,17 +6283,19 @@ function buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineCol
6346
6283
  ]
6347
6284
  };
6348
6285
  }
6349
- function segmentLabelFormatter(mode) {
6350
- switch (mode) {
6351
- case "name":
6352
- return "{b}";
6353
- case "value":
6354
- return "{b} \u2014 {c}";
6355
- case "percent":
6356
- return "{b} \u2014 {d}%";
6357
- default:
6358
- return "{b} \u2014 {c} ({d}%)";
6359
- }
6286
+ function segmentLabelFormatter(parsed) {
6287
+ const showName = !parsed.noLabelName;
6288
+ const showValue = !parsed.noLabelValue;
6289
+ const showPercent = !parsed.noLabelPercent;
6290
+ const parts = [];
6291
+ if (showName) parts.push("{b}");
6292
+ if (showValue) parts.push("{c}");
6293
+ if (showPercent) parts.push("{d}%");
6294
+ if (parts.length === 0) return "{b}";
6295
+ if (parts.length === 1) return parts[0];
6296
+ if (showName && showValue && showPercent) return "{b} \u2014 {c} ({d}%)";
6297
+ if (showName) return "{b} \u2014 " + parts.slice(1).join(" ");
6298
+ return parts.join(" ");
6360
6299
  }
6361
6300
  function buildPieOption(parsed, textColor, colors, bg, titleConfig, tooltipTheme, isDoughnut) {
6362
6301
  const HIDE_AXES = { xAxis: { show: false }, yAxis: { show: false } };
@@ -6383,7 +6322,7 @@ function buildPieOption(parsed, textColor, colors, bg, titleConfig, tooltipTheme
6383
6322
  data,
6384
6323
  label: {
6385
6324
  position: "outside",
6386
- formatter: segmentLabelFormatter(parsed.labels),
6325
+ formatter: segmentLabelFormatter(parsed),
6387
6326
  color: textColor,
6388
6327
  fontFamily: FONT_FAMILY
6389
6328
  },
@@ -6478,7 +6417,7 @@ function buildPolarAreaOption(parsed, textColor, colors, bg, titleConfig, toolti
6478
6417
  data,
6479
6418
  label: {
6480
6419
  position: "outside",
6481
- formatter: segmentLabelFormatter(parsed.labels),
6420
+ formatter: segmentLabelFormatter(parsed),
6482
6421
  color: textColor,
6483
6422
  fontFamily: FONT_FAMILY
6484
6423
  },
@@ -6647,7 +6586,7 @@ var init_echarts = __esm({
6647
6586
  "xlabel",
6648
6587
  "ylabel",
6649
6588
  "sizelabel",
6650
- "labels",
6589
+ "no-labels",
6651
6590
  "columns",
6652
6591
  "rows",
6653
6592
  "x"
@@ -6750,10 +6689,6 @@ function parseOrg(content, palette) {
6750
6689
  pushError(lineNumber, "Tag groups must appear before org content");
6751
6690
  continue;
6752
6691
  }
6753
- if (tagBlockMatch.deprecated) {
6754
- pushError(lineNumber, `'## ${tagBlockMatch.name}' is no longer supported \u2014 use 'tag: ${tagBlockMatch.name}' instead`);
6755
- continue;
6756
- }
6757
6692
  currentTagGroup = {
6758
6693
  name: tagBlockMatch.name,
6759
6694
  alias: tagBlockMatch.alias,
@@ -6775,7 +6710,7 @@ function parseOrg(content, palette) {
6775
6710
  continue;
6776
6711
  }
6777
6712
  }
6778
- if (KNOWN_BOOLEANS.has(trimmed.toLowerCase())) {
6713
+ if (KNOWN_BOOLEANS2.has(trimmed.toLowerCase())) {
6779
6714
  result.options[trimmed.toLowerCase()] = "on";
6780
6715
  continue;
6781
6716
  }
@@ -6864,7 +6799,7 @@ function parseNodeLabel(trimmed, _indent, lineNumber, palette, counter, aliasMap
6864
6799
  const segments = trimmed.split("|").map((s) => s.trim());
6865
6800
  let rawLabel = segments[0];
6866
6801
  const { label, color } = extractColor(rawLabel, palette);
6867
- const metadata = parsePipeMetadata(segments, aliasMap, warnFn ? () => warnFn(lineNumber, MULTIPLE_PIPE_WARNING) : void 0);
6802
+ const metadata = parsePipeMetadata(segments, aliasMap, warnFn ? () => warnFn(lineNumber, MULTIPLE_PIPE_ERROR) : void 0);
6868
6803
  return {
6869
6804
  id: `node-${counter}`,
6870
6805
  label,
@@ -6902,7 +6837,7 @@ function findMetadataParent(indent, indentStack) {
6902
6837
  }
6903
6838
  return null;
6904
6839
  }
6905
- var CONTAINER_RE, METADATA_RE, KNOWN_OPTIONS3, KNOWN_BOOLEANS;
6840
+ var CONTAINER_RE, METADATA_RE, KNOWN_OPTIONS3, KNOWN_BOOLEANS2;
6906
6841
  var init_parser4 = __esm({
6907
6842
  "src/org/parser.ts"() {
6908
6843
  "use strict";
@@ -6912,13 +6847,13 @@ var init_parser4 = __esm({
6912
6847
  CONTAINER_RE = /^\[([^\]]+)\]$/;
6913
6848
  METADATA_RE = /^([^:]+):\s*(.+)$/;
6914
6849
  KNOWN_OPTIONS3 = /* @__PURE__ */ new Set([
6915
- "direction",
6916
6850
  "sub-node-label",
6917
6851
  "hide",
6918
6852
  "show-sub-node-count"
6919
6853
  ]);
6920
- KNOWN_BOOLEANS = /* @__PURE__ */ new Set([
6921
- "show-sub-node-count"
6854
+ KNOWN_BOOLEANS2 = /* @__PURE__ */ new Set([
6855
+ "show-sub-node-count",
6856
+ "direction-tb"
6922
6857
  ]);
6923
6858
  }
6924
6859
  });
@@ -6998,10 +6933,6 @@ function parseKanban(content, palette) {
6998
6933
  if (!contentStarted) {
6999
6934
  const tagBlockMatch = matchTagBlockHeading(trimmed);
7000
6935
  if (tagBlockMatch) {
7001
- if (tagBlockMatch.deprecated) {
7002
- result.diagnostics.push(makeDgmoError(lineNumber, `'## ${tagBlockMatch.name}' is no longer supported \u2014 use 'tag: ${tagBlockMatch.name}' instead`));
7003
- continue;
7004
- }
7005
6936
  currentTagGroup = {
7006
6937
  name: tagBlockMatch.name,
7007
6938
  alias: tagBlockMatch.alias,
@@ -7024,7 +6955,7 @@ function parseKanban(content, palette) {
7024
6955
  continue;
7025
6956
  }
7026
6957
  }
7027
- if (KNOWN_BOOLEANS2.has(trimmed.toLowerCase()) && !COLUMN_RE.test(trimmed)) {
6958
+ if (KNOWN_BOOLEANS3.has(trimmed.toLowerCase()) && !COLUMN_RE.test(trimmed)) {
7028
6959
  result.options[trimmed.toLowerCase()] = "on";
7029
6960
  continue;
7030
6961
  }
@@ -7202,7 +7133,7 @@ function parseCardLine(trimmed, lineNumber, counter, aliasMap, palette) {
7202
7133
  color
7203
7134
  };
7204
7135
  }
7205
- var COLUMN_RE, LEGACY_COLUMN_RE, KNOWN_OPTIONS4, KNOWN_BOOLEANS2;
7136
+ var COLUMN_RE, LEGACY_COLUMN_RE, KNOWN_OPTIONS4, KNOWN_BOOLEANS3;
7206
7137
  var init_parser5 = __esm({
7207
7138
  "src/kanban/parser.ts"() {
7208
7139
  "use strict";
@@ -7213,10 +7144,11 @@ var init_parser5 = __esm({
7213
7144
  COLUMN_RE = /^\[(.+?)\](?:\s*\(([^)]+)\))?\s*(?:\|\s*(.+))?$/;
7214
7145
  LEGACY_COLUMN_RE = /^==\s+(.+?)\s*(?:\[wip:\s*(\d+)\])?\s*==$/;
7215
7146
  KNOWN_OPTIONS4 = /* @__PURE__ */ new Set([
7216
- "color-off",
7217
7147
  "hide"
7218
7148
  ]);
7219
- KNOWN_BOOLEANS2 = /* @__PURE__ */ new Set([]);
7149
+ KNOWN_BOOLEANS3 = /* @__PURE__ */ new Set([
7150
+ "no-auto-color"
7151
+ ]);
7220
7152
  }
7221
7153
  });
7222
7154
 
@@ -7329,10 +7261,6 @@ function parseC4(content, palette) {
7329
7261
  pushError(lineNumber, "Tag groups must appear before content");
7330
7262
  continue;
7331
7263
  }
7332
- if (tagBlockMatch.deprecated) {
7333
- pushError(lineNumber, `'## ${tagBlockMatch.name}' is no longer supported \u2014 use 'tag: ${tagBlockMatch.name}' instead`);
7334
- continue;
7335
- }
7336
7264
  currentTagGroup = {
7337
7265
  name: tagBlockMatch.name,
7338
7266
  alias: tagBlockMatch.alias,
@@ -7346,6 +7274,10 @@ function parseC4(content, palette) {
7346
7274
  continue;
7347
7275
  }
7348
7276
  if (!contentStarted && !currentTagGroup && measureIndent(line10) === 0) {
7277
+ if (KNOWN_C4_BOOLEANS.has(trimmed.toLowerCase())) {
7278
+ result.options[trimmed.toLowerCase()] = "on";
7279
+ continue;
7280
+ }
7349
7281
  const optMatch = trimmed.match(OPTION_NOCOLON_RE);
7350
7282
  if (optMatch) {
7351
7283
  const key = optMatch[1].trim().toLowerCase();
@@ -7407,7 +7339,7 @@ function parseC4(content, palette) {
7407
7339
  }
7408
7340
  const segments = trimmed.split("|").map((s) => s.trim());
7409
7341
  const nodeName = segments[0];
7410
- const metadata = parsePipeMetadata(segments, aliasMap, () => pushError(lineNumber, MULTIPLE_PIPE_WARNING, "warning"));
7342
+ const metadata = parsePipeMetadata(segments, aliasMap, () => pushError(lineNumber, MULTIPLE_PIPE_ERROR));
7411
7343
  const shape = inferC4Shape(nodeName, metadata.tech ?? metadata.technology);
7412
7344
  const dNode = {
7413
7345
  name: nodeName,
@@ -7620,7 +7552,7 @@ function parseC4(content, palette) {
7620
7552
  }
7621
7553
  namePart = namePart.substring(0, nameIsAMatch.index).trim();
7622
7554
  }
7623
- const metadata = parsePipeMetadata(segments, aliasMap, () => pushError(lineNumber, MULTIPLE_PIPE_WARNING, "warning"));
7555
+ const metadata = parsePipeMetadata(segments, aliasMap, () => pushError(lineNumber, MULTIPLE_PIPE_ERROR));
7624
7556
  const shape = explicitShape ?? inferC4Shape(namePart, metadata.tech ?? metadata.technology);
7625
7557
  const element = {
7626
7558
  name: namePart,
@@ -7668,7 +7600,7 @@ function parseC4(content, palette) {
7668
7600
  lineNumber,
7669
7601
  `'${elementMatch[1]} ${namePart}' prefix syntax is no longer supported \u2014 use '${namePart} is a ${elementType}' instead`
7670
7602
  );
7671
- const metadata = parsePipeMetadata(segments, aliasMap, () => pushError(lineNumber, MULTIPLE_PIPE_WARNING, "warning"));
7603
+ const metadata = parsePipeMetadata(segments, aliasMap, () => pushError(lineNumber, MULTIPLE_PIPE_ERROR));
7672
7604
  const shape = explicitShape ?? inferC4Shape(namePart, metadata.tech ?? metadata.technology);
7673
7605
  const element = {
7674
7606
  name: namePart,
@@ -7814,7 +7746,7 @@ function validateDeploymentRefs(result, knownNames, pushWarning) {
7814
7746
  }
7815
7747
  walkDeploy(result.deployment);
7816
7748
  }
7817
- 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;
7749
+ 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;
7818
7750
  var init_parser6 = __esm({
7819
7751
  "src/c4/parser.ts"() {
7820
7752
  "use strict";
@@ -7833,7 +7765,7 @@ var init_parser6 = __esm({
7833
7765
  C4_LABELED_BIDI_ASYNC_RE = /^<~(.+)~>\s*(.+)$/;
7834
7766
  SECTION_HEADER_RE = /^(containers|components|deployment)\s*$/i;
7835
7767
  CONTAINER_REF_RE = /^container\s+(.+)$/i;
7836
- METADATA_RE2 = /^([a-z][a-z0-9-]*)\s+(.+)$/i;
7768
+ METADATA_RE2 = /^([a-z][a-z0-9-]*):\s+(.+)$/i;
7837
7769
  VALID_ELEMENT_TYPES = /* @__PURE__ */ new Set([
7838
7770
  "person",
7839
7771
  "system",
@@ -7849,8 +7781,10 @@ var init_parser6 = __esm({
7849
7781
  "external"
7850
7782
  ]);
7851
7783
  KNOWN_C4_OPTIONS = /* @__PURE__ */ new Set([
7852
- "layout",
7853
- "direction"
7784
+ "layout"
7785
+ ]);
7786
+ KNOWN_C4_BOOLEANS = /* @__PURE__ */ new Set([
7787
+ "direction-tb"
7854
7788
  ]);
7855
7789
  ALL_CHART_TYPES2 = [
7856
7790
  "c4",
@@ -8013,10 +7947,10 @@ function parseInitiativeStatus(content) {
8013
7947
  if (hideMatch && !trimmed.match(/^hide\s*\|/)) {
8014
7948
  const pairs = hideMatch[1].split(",");
8015
7949
  for (const pair of pairs) {
8016
- const tokens = pair.trim().split(/\s+/);
8017
- if (tokens.length >= 2) {
8018
- const groupKey = tokens[0].toLowerCase();
8019
- const value = tokens.slice(1).join(" ").toLowerCase();
7950
+ const colonIdx = pair.indexOf(":");
7951
+ if (colonIdx > 0) {
7952
+ const groupKey = pair.substring(0, colonIdx).trim().toLowerCase();
7953
+ const value = pair.substring(colonIdx + 1).trim().toLowerCase();
8020
7954
  if (groupKey && value) {
8021
7955
  if (!result.initialHiddenTagValues.has(groupKey)) {
8022
7956
  result.initialHiddenTagValues.set(groupKey, /* @__PURE__ */ new Set());
@@ -8032,7 +7966,7 @@ function parseInitiativeStatus(content) {
8032
7966
  if (optMatch) {
8033
7967
  const key = optMatch[1].toLowerCase();
8034
7968
  const value = optMatch[2].trim();
8035
- if (key === "active-tag" || key === "sort") {
7969
+ if (key === "active-tag") {
8036
7970
  result.options[key] = value;
8037
7971
  continue;
8038
7972
  }
@@ -8046,12 +7980,6 @@ function parseInitiativeStatus(content) {
8046
7980
  );
8047
7981
  continue;
8048
7982
  }
8049
- if (tagBlockMatch.deprecated) {
8050
- result.diagnostics.push(
8051
- makeDgmoError(lineNum, `'## ${tagBlockMatch.name}' is no longer supported \u2014 use 'tag ${tagBlockMatch.name}' instead`)
8052
- );
8053
- continue;
8054
- }
8055
7983
  currentTagGroup = {
8056
7984
  name: tagBlockMatch.name,
8057
7985
  alias: tagBlockMatch.alias,
@@ -8314,7 +8242,7 @@ function looksLikeSitemap(content) {
8314
8242
  for (const line10 of lines) {
8315
8243
  const trimmed = line10.trim();
8316
8244
  if (!trimmed || trimmed.startsWith("//")) continue;
8317
- if (CHART_TYPE_RE.test(trimmed) || TITLE_RE.test(trimmed)) continue;
8245
+ if (parseFirstLine(trimmed) || TITLE_RE.test(trimmed)) continue;
8318
8246
  if (isTagBlockHeading(trimmed)) continue;
8319
8247
  if (/^-.*->\s*.+/.test(trimmed) || /^->\s*.+/.test(trimmed)) {
8320
8248
  hasArrow = true;
@@ -8331,7 +8259,7 @@ function parseSitemap(content, palette) {
8331
8259
  const result = {
8332
8260
  title: null,
8333
8261
  titleLineNumber: null,
8334
- direction: "TB",
8262
+ direction: "LR",
8335
8263
  roots: [],
8336
8264
  edges: [],
8337
8265
  tagGroups: [],
@@ -8401,10 +8329,6 @@ function parseSitemap(content, palette) {
8401
8329
  pushError(lineNumber, "Tag groups must appear before sitemap content");
8402
8330
  continue;
8403
8331
  }
8404
- if (tagBlockMatch.deprecated) {
8405
- pushError(lineNumber, `'## ${tagBlockMatch.name}' is no longer supported \u2014 use 'tag: ${tagBlockMatch.name}' instead`);
8406
- continue;
8407
- }
8408
8332
  currentTagGroup = {
8409
8333
  name: tagBlockMatch.name,
8410
8334
  alias: tagBlockMatch.alias,
@@ -8418,16 +8342,13 @@ function parseSitemap(content, palette) {
8418
8342
  continue;
8419
8343
  }
8420
8344
  if (!contentStarted && !currentTagGroup && measureIndent(line10) === 0 && !trimmed.includes("|") && !trimmed.includes("->")) {
8345
+ if (/^direction-tb$/i.test(trimmed)) {
8346
+ result.direction = "TB";
8347
+ continue;
8348
+ }
8421
8349
  const optMatch = trimmed.match(OPTION_NOCOLON_RE);
8422
8350
  if (optMatch) {
8423
8351
  const key = optMatch[1].trim().toLowerCase();
8424
- if (key === "direction" || key === "orientation") {
8425
- const dir = normalizeDirection(optMatch[2]);
8426
- if (dir) {
8427
- result.direction = dir;
8428
- }
8429
- continue;
8430
- }
8431
8352
  result.options[key] = optMatch[2].trim();
8432
8353
  continue;
8433
8354
  }
@@ -8562,7 +8483,7 @@ function parseNodeLabel2(trimmed, lineNumber, palette, counter, aliasMap = /* @_
8562
8483
  const segments = trimmed.split("|").map((s) => s.trim());
8563
8484
  const rawLabel = segments[0];
8564
8485
  const { label, color } = extractColor(rawLabel, palette);
8565
- const metadata = parsePipeMetadata(segments, aliasMap, warnFn ? () => warnFn(lineNumber, MULTIPLE_PIPE_WARNING) : void 0);
8486
+ const metadata = parsePipeMetadata(segments, aliasMap, warnFn ? () => warnFn(lineNumber, MULTIPLE_PIPE_ERROR) : void 0);
8566
8487
  return {
8567
8488
  id: `node-${counter}`,
8568
8489
  label,
@@ -8755,14 +8676,8 @@ function parseInfra(content) {
8755
8676
  }
8756
8677
  continue;
8757
8678
  }
8758
- if (/^(?:direction|orientation)\s/i.test(trimmed)) {
8759
- const raw2 = trimmed.replace(/^(?:direction|orientation)\s+/i, "").trim();
8760
- const dir = normalizeDirection(raw2);
8761
- if (dir) {
8762
- result.direction = dir;
8763
- } else {
8764
- warn(lineNumber, `Unknown direction '${raw2}'. Expected 'LR', 'TB', 'horizontal', or 'vertical'.`);
8765
- }
8679
+ if (/^direction-tb$/i.test(trimmed)) {
8680
+ result.direction = "TB";
8766
8681
  continue;
8767
8682
  }
8768
8683
  if (trimmed === "animate") {
@@ -8778,23 +8693,6 @@ function parseInfra(content) {
8778
8693
  result.options[optMatch[1].toLowerCase()] = optMatch[2].trim();
8779
8694
  continue;
8780
8695
  }
8781
- if (/^scenario\s*:/i.test(trimmed)) {
8782
- setError(lineNumber, `'scenario:' syntax is no longer supported`);
8783
- let si = i + 1;
8784
- while (si < lines.length) {
8785
- const sLine = lines[si];
8786
- const sTrimmed = sLine.trim();
8787
- if (!sTrimmed || sTrimmed.startsWith("#")) {
8788
- si++;
8789
- continue;
8790
- }
8791
- const sIndent = sLine.length - sLine.trimStart().length;
8792
- if (sIndent === 0) break;
8793
- si++;
8794
- }
8795
- i = si - 1;
8796
- continue;
8797
- }
8798
8696
  const tagMatch = matchTagBlockHeading(trimmed);
8799
8697
  if (tagMatch) {
8800
8698
  finishCurrentNode();
@@ -8807,21 +8705,6 @@ function parseInfra(content) {
8807
8705
  };
8808
8706
  continue;
8809
8707
  }
8810
- const hashGroupMatch = trimmed.match(GROUP_HASH_RE);
8811
- if (hashGroupMatch) {
8812
- finishCurrentNode();
8813
- finishCurrentTagGroup();
8814
- const gLabel = hashGroupMatch[1].trim();
8815
- const gId = groupId(gLabel);
8816
- currentGroup = {
8817
- id: gId,
8818
- label: gLabel,
8819
- metadata: void 0,
8820
- lineNumber
8821
- };
8822
- result.groups.push(currentGroup);
8823
- continue;
8824
- }
8825
8708
  const groupMatch = trimmed.match(GROUP_RE);
8826
8709
  if (groupMatch) {
8827
8710
  finishCurrentNode();
@@ -8838,28 +8721,6 @@ function parseInfra(content) {
8838
8721
  result.groups.push(currentGroup);
8839
8722
  continue;
8840
8723
  }
8841
- const isaMatch = trimmed.match(IS_A_RE2);
8842
- if (isaMatch) {
8843
- finishCurrentNode();
8844
- finishCurrentTagGroup();
8845
- const name = isaMatch[1].trim();
8846
- const nType = isaMatch[2].toLowerCase();
8847
- const id = nodeId2(name);
8848
- const isEdge = EDGE_NODE_NAMES.has(id.toLowerCase());
8849
- currentNode = {
8850
- id,
8851
- label: name,
8852
- properties: [],
8853
- groupId: null,
8854
- tags: {},
8855
- isEdge,
8856
- nodeType: nType,
8857
- lineNumber
8858
- };
8859
- currentGroup = null;
8860
- baseIndent = 0;
8861
- continue;
8862
- }
8863
8724
  const compMatch = trimmed.match(COMPONENT_RE);
8864
8725
  if (compMatch) {
8865
8726
  finishCurrentNode();
@@ -8917,26 +8778,6 @@ function parseInfra(content) {
8917
8778
  continue;
8918
8779
  }
8919
8780
  }
8920
- const isaMatchG = trimmed.match(IS_A_RE2);
8921
- if (isaMatchG) {
8922
- finishCurrentTagGroup();
8923
- const name = isaMatchG[1].trim();
8924
- const nType = isaMatchG[2].toLowerCase();
8925
- const id = nodeId2(name);
8926
- const tags = currentGroup.metadata ? { ...currentGroup.metadata } : {};
8927
- currentNode = {
8928
- id,
8929
- label: name,
8930
- properties: [],
8931
- groupId: currentGroup.id,
8932
- tags,
8933
- isEdge: false,
8934
- nodeType: nType,
8935
- lineNumber
8936
- };
8937
- baseIndent = indent;
8938
- continue;
8939
- }
8940
8781
  const compMatch = trimmed.match(COMPONENT_RE);
8941
8782
  if (compMatch) {
8942
8783
  finishCurrentTagGroup();
@@ -9101,25 +8942,6 @@ function parseInfra(content) {
9101
8942
  }
9102
8943
  if (currentGroup && indent > 0) {
9103
8944
  finishCurrentNode();
9104
- const isaMatchG2 = trimmed.match(IS_A_RE2);
9105
- if (isaMatchG2) {
9106
- const name = isaMatchG2[1].trim();
9107
- const nType = isaMatchG2[2].toLowerCase();
9108
- const id = nodeId2(name);
9109
- const tags = currentGroup.metadata ? { ...currentGroup.metadata } : {};
9110
- currentNode = {
9111
- id,
9112
- label: name,
9113
- properties: [],
9114
- groupId: currentGroup.id,
9115
- tags,
9116
- isEdge: false,
9117
- nodeType: nType,
9118
- lineNumber
9119
- };
9120
- baseIndent = indent;
9121
- continue;
9122
- }
9123
8945
  const compMatch = trimmed.match(COMPONENT_RE);
9124
8946
  if (compMatch) {
9125
8947
  const name = compMatch[1];
@@ -9208,7 +9030,7 @@ function extractSymbols4(docText) {
9208
9030
  if (!indented) {
9209
9031
  const firstLine = parseFirstLine(line10);
9210
9032
  if (firstLine) continue;
9211
- if (/^(?:direction|orientation|animate|no-animate|slo-|default-)/i.test(line10)) continue;
9033
+ if (/^(?:direction-tb|animate|no-animate|slo-|default-)/i.test(line10)) continue;
9212
9034
  if (/^[a-z-]+\s*:/i.test(line10)) continue;
9213
9035
  inMetadata = false;
9214
9036
  } else {
@@ -9226,7 +9048,6 @@ function extractSymbols4(docText) {
9226
9048
  }
9227
9049
  inTagGroup = false;
9228
9050
  if (/^\[/.test(line10)) continue;
9229
- if (/^#\s/.test(line10)) continue;
9230
9051
  const m = COMPONENT_RE.exec(line10);
9231
9052
  if (m && !entities.includes(m[1])) entities.push(m[1]);
9232
9053
  } else {
@@ -9244,7 +9065,7 @@ function extractSymbols4(docText) {
9244
9065
  }
9245
9066
  return { kind: "infra", entities, keywords: [] };
9246
9067
  }
9247
- 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;
9068
+ 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;
9248
9069
  var init_parser9 = __esm({
9249
9070
  "src/infra/parser.ts"() {
9250
9071
  "use strict";
@@ -9257,7 +9078,6 @@ var init_parser9 = __esm({
9257
9078
  ASYNC_CONNECTION_RE = /^~(?:([^~].*?))?~>\s*(.+?)(?:(?:\s*\|\s*|\s+)split\s*:?\s*(\d+)%)?\s*$/;
9258
9079
  ASYNC_SIMPLE_CONNECTION_RE = /^~>\s*(.+?)(?:(?:\s*\|\s*|\s+)split\s*:?\s*(\d+)%)?\s*$/;
9259
9080
  DEPRECATED_FANOUT_RE = /\bx(\d+)\s*$/;
9260
- IS_A_RE2 = /^(.+?)\s+is\s+an?\s+(database|cache|queue|service|gateway|storage|function|network)\s*$/i;
9261
9081
  GROUP_RE = /^\[([^\]]+)\]\s*(?:\|\s*(.+))?$/;
9262
9082
  TAG_VALUE_RE = /^(\w[\w\s]*?)(?:\(([^)]+)\))?\s*$/;
9263
9083
  COMPONENT_RE = /^([a-zA-Z_][\w-]*)(.*)$/;
@@ -9492,6 +9312,10 @@ function parseGantt(content, palette) {
9492
9312
  let inTagBlock = false;
9493
9313
  let currentTagGroup = null;
9494
9314
  let tagBlockIndent = 0;
9315
+ let inEraBlock = false;
9316
+ let eraBlockIndent = 0;
9317
+ let inMarkerBlock = false;
9318
+ let markerBlockIndent = 0;
9495
9319
  let lastTaskNode = null;
9496
9320
  let taskIdCounter = 0;
9497
9321
  const seriesColors2 = palette ? getSeriesColors(palette) : [];
@@ -9504,6 +9328,12 @@ function parseGantt(content, palette) {
9504
9328
  if (inHolidaysBlock && indent <= holidaysBlockIndent) {
9505
9329
  inHolidaysBlock = false;
9506
9330
  }
9331
+ if (inEraBlock && indent <= eraBlockIndent) {
9332
+ inEraBlock = false;
9333
+ }
9334
+ if (inMarkerBlock && indent <= markerBlockIndent) {
9335
+ inMarkerBlock = false;
9336
+ }
9507
9337
  if (inTagBlock && indent <= tagBlockIndent) {
9508
9338
  inTagBlock = false;
9509
9339
  if (currentTagGroup) {
@@ -9565,6 +9395,49 @@ function parseGantt(content, palette) {
9565
9395
  continue;
9566
9396
  }
9567
9397
  }
9398
+ if (inEraBlock) {
9399
+ if (indent <= eraBlockIndent) {
9400
+ inEraBlock = false;
9401
+ } else {
9402
+ if (COMMENT_RE.test(line10)) continue;
9403
+ const eraEntryMatch = line10.match(ERA_ENTRY_RE);
9404
+ if (eraEntryMatch) {
9405
+ const eraLabelRaw = eraEntryMatch[3].trim();
9406
+ const eraExtracted = extractColor(eraLabelRaw, palette);
9407
+ result.eras.push({
9408
+ startDate: eraEntryMatch[1],
9409
+ endDate: eraEntryMatch[2],
9410
+ label: eraExtracted.label,
9411
+ color: eraExtracted.color || null,
9412
+ lineNumber
9413
+ });
9414
+ } else {
9415
+ warn(lineNumber, `Unrecognized era entry: "${line10}"`);
9416
+ }
9417
+ continue;
9418
+ }
9419
+ }
9420
+ if (inMarkerBlock) {
9421
+ if (indent <= markerBlockIndent) {
9422
+ inMarkerBlock = false;
9423
+ } else {
9424
+ if (COMMENT_RE.test(line10)) continue;
9425
+ const markerEntryMatch = line10.match(MARKER_ENTRY_RE);
9426
+ if (markerEntryMatch) {
9427
+ const markerLabelRaw = markerEntryMatch[2].trim();
9428
+ const markerExtracted = extractColor(markerLabelRaw, palette);
9429
+ result.markers.push({
9430
+ date: markerEntryMatch[1],
9431
+ label: markerExtracted.label,
9432
+ color: markerExtracted.color || null,
9433
+ lineNumber
9434
+ });
9435
+ } else {
9436
+ warn(lineNumber, `Unrecognized marker entry: "${line10}"`);
9437
+ }
9438
+ continue;
9439
+ }
9440
+ }
9568
9441
  if (inTagBlock && currentTagGroup) {
9569
9442
  if (indent <= tagBlockIndent) {
9570
9443
  inTagBlock = false;
@@ -9603,7 +9476,7 @@ function parseGantt(content, palette) {
9603
9476
  const targetName = depParts[0].trim();
9604
9477
  let offset;
9605
9478
  if (depParts.length > 1) {
9606
- const meta = parsePipeMetadata(["", ...depParts.slice(1)], aliasMap, () => warn(lineNumber, MULTIPLE_PIPE_WARNING));
9479
+ const meta = parsePipeMetadata(["", ...depParts.slice(1)], aliasMap, () => warn(lineNumber, MULTIPLE_PIPE_ERROR));
9607
9480
  if (meta.lag || meta.lead) {
9608
9481
  const key = meta.lag ? "lag" : "lead";
9609
9482
  softError(lineNumber, `"${key}" is no longer supported \u2014 use "offset: ${meta[key]}" instead.${key === "lead" ? ' Negate the value for lead behavior: "offset: -...".' : ""}`);
@@ -9655,10 +9528,6 @@ function parseGantt(content, palette) {
9655
9528
  }
9656
9529
  const tagMatch = matchTagBlockHeading(line10);
9657
9530
  if (tagMatch) {
9658
- if (tagMatch.deprecated) {
9659
- softError(lineNumber, `'## ${tagMatch.name}' is no longer supported \u2014 use 'tag ${tagMatch.name}' instead`);
9660
- continue;
9661
- }
9662
9531
  inTagBlock = true;
9663
9532
  tagBlockIndent = indent;
9664
9533
  inHeaderBlock = false;
@@ -9673,6 +9542,23 @@ function parseGantt(content, palette) {
9673
9542
  }
9674
9543
  continue;
9675
9544
  }
9545
+ const topWorkweekMatch = line10.match(WORKWEEK_RE);
9546
+ if (topWorkweekMatch) {
9547
+ const days = parseWorkweek(topWorkweekMatch[1].trim());
9548
+ if (days) {
9549
+ result.holidays.workweek = days;
9550
+ } else {
9551
+ warn(lineNumber, `Invalid workweek format: "${topWorkweekMatch[1]}". Use day range like "sun-thu" or comma-separated days.`);
9552
+ }
9553
+ inHeaderBlock = false;
9554
+ continue;
9555
+ }
9556
+ if (line10.toLowerCase() === "era") {
9557
+ inEraBlock = true;
9558
+ eraBlockIndent = indent;
9559
+ inHeaderBlock = false;
9560
+ continue;
9561
+ }
9676
9562
  const eraMatch = line10.match(ERA_RE);
9677
9563
  if (eraMatch) {
9678
9564
  const eraLabelRaw = eraMatch[3].trim();
@@ -9687,6 +9573,12 @@ function parseGantt(content, palette) {
9687
9573
  inHeaderBlock = false;
9688
9574
  continue;
9689
9575
  }
9576
+ if (line10.toLowerCase() === "marker") {
9577
+ inMarkerBlock = true;
9578
+ markerBlockIndent = indent;
9579
+ inHeaderBlock = false;
9580
+ continue;
9581
+ }
9690
9582
  const markerMatch = line10.match(MARKER_RE);
9691
9583
  if (markerMatch) {
9692
9584
  const markerLabelRaw = markerMatch[2].trim();
@@ -9702,7 +9594,7 @@ function parseGantt(content, palette) {
9702
9594
  }
9703
9595
  const optNoColonMatch = line10.match(/^([a-z][a-z0-9-]*)\s+(.+)$/i);
9704
9596
  const bareKeyword = line10.match(/^([a-z][a-z0-9-]*)$/i);
9705
- if (bareKeyword && KNOWN_BOOLEANS3.has(bareKeyword[1].toLowerCase())) {
9597
+ if (bareKeyword && KNOWN_BOOLEANS4.has(bareKeyword[1].toLowerCase())) {
9706
9598
  const key = bareKeyword[1].toLowerCase();
9707
9599
  result.options.optionLineNumbers[key] = lineNumber;
9708
9600
  switch (key) {
@@ -9717,7 +9609,7 @@ function parseGantt(content, palette) {
9717
9609
  }
9718
9610
  if (bareKeyword && bareKeyword[1].toLowerCase().startsWith("no-")) {
9719
9611
  const base = bareKeyword[1].toLowerCase().substring(3);
9720
- if (KNOWN_BOOLEANS3.has(base)) {
9612
+ if (KNOWN_BOOLEANS4.has(base)) {
9721
9613
  result.options.optionLineNumbers[base] = lineNumber;
9722
9614
  switch (base) {
9723
9615
  case "dependencies":
@@ -9745,9 +9637,6 @@ function parseGantt(content, palette) {
9745
9637
  result.options.title = value;
9746
9638
  result.options.titleLineNumber = lineNumber;
9747
9639
  break;
9748
- case "orientation":
9749
- warn(lineNumber, `'orientation' is not supported for gantt charts`);
9750
- break;
9751
9640
  case "today-marker":
9752
9641
  if (/^\d{4}-\d{2}-\d{2}(?: \d{2}:\d{2})?$/.test(value)) {
9753
9642
  result.options.todayMarker = value;
@@ -9776,26 +9665,6 @@ function parseGantt(content, palette) {
9776
9665
  continue;
9777
9666
  }
9778
9667
  inHeaderBlock = false;
9779
- const hashGroupMatch = line10.match(GROUP_HASH_RE);
9780
- if (hashGroupMatch) {
9781
- const nameExtracted = extractColor(hashGroupMatch[1], palette);
9782
- const group = {
9783
- name: nameExtracted.label,
9784
- color: nameExtracted.color || null,
9785
- metadata: {},
9786
- lineNumber,
9787
- children: []
9788
- };
9789
- const groupNode = { kind: "group", ...group };
9790
- currentContainer().push(groupNode);
9791
- blockStack.push({
9792
- node: groupNode,
9793
- indent,
9794
- containerType: "group"
9795
- });
9796
- lastTaskNode = null;
9797
- continue;
9798
- }
9799
9668
  if (line10 === "parallel") {
9800
9669
  const parallel = {
9801
9670
  kind: "parallel",
@@ -9817,7 +9686,7 @@ function parseGantt(content, palette) {
9817
9686
  const segments = afterBrackets ? afterBrackets.split("|") : [];
9818
9687
  let metadata = {};
9819
9688
  let color = null;
9820
- const pipeWarn = () => warn(lineNumber, MULTIPLE_PIPE_WARNING);
9689
+ const pipeWarn = () => warn(lineNumber, MULTIPLE_PIPE_ERROR);
9821
9690
  if (segments.length > 0 && segments[0].trim()) {
9822
9691
  metadata = parsePipeMetadata(["", ...segments], aliasMap, pipeWarn);
9823
9692
  } else if (segments.length > 1) {
@@ -9898,7 +9767,7 @@ function parseGantt(content, palette) {
9898
9767
  const targetName = depParts[0].trim();
9899
9768
  let offset;
9900
9769
  if (depParts.length > 1) {
9901
- const meta = parsePipeMetadata(["", ...depParts.slice(1)], aliasMap, () => warn(lineNumber, MULTIPLE_PIPE_WARNING));
9770
+ const meta = parsePipeMetadata(["", ...depParts.slice(1)], aliasMap, () => warn(lineNumber, MULTIPLE_PIPE_ERROR));
9902
9771
  if (meta.lag || meta.lead) {
9903
9772
  const key = meta.lag ? "lag" : "lead";
9904
9773
  softError(lineNumber, `"${key}" is no longer supported \u2014 use "offset: ${meta[key]}" instead.${key === "lead" ? ' Negate the value for lead behavior: "offset: -...".' : ""}`);
@@ -9935,7 +9804,7 @@ function parseGantt(content, palette) {
9935
9804
  if (label.toLowerCase() === "parallel") {
9936
9805
  softError(ln, `"parallel" is a reserved keyword and cannot be used as a task name.`);
9937
9806
  }
9938
- const metadata = segments.length > 1 ? parsePipeMetadata(segments, aliasMap, () => warn(ln, MULTIPLE_PIPE_WARNING)) : {};
9807
+ const metadata = segments.length > 1 ? parsePipeMetadata(segments, aliasMap, () => warn(ln, MULTIPLE_PIPE_ERROR)) : {};
9939
9808
  let progress = null;
9940
9809
  if (metadata.progress) {
9941
9810
  progress = parseFloat(metadata.progress);
@@ -10021,7 +9890,7 @@ function parseWorkweek(s) {
10021
9890
  function isKnownOption(key) {
10022
9891
  return KNOWN_OPTIONS5.has(key);
10023
9892
  }
10024
- 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;
9893
+ 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;
10025
9894
  var init_parser10 = __esm({
10026
9895
  "src/gantt/parser.ts"() {
10027
9896
  "use strict";
@@ -10032,15 +9901,17 @@ var init_parser10 = __esm({
10032
9901
  init_palettes();
10033
9902
  DURATION_RE = /^(\d+(?:\.\d+)?)(min|bd|d|w|m|q|y|h)(\?)?\s+(.+)$/;
10034
9903
  EXPLICIT_DATE_RE = /^(\d{4}-\d{2}-\d{2}(?: \d{2}:\d{2})?)\s+(.+)$/;
10035
- 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+(.+)$/;
9904
+ 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+(.+)$/;
10036
9905
  GROUP_RE2 = /^\[(.+?)\]\s*(.*)$/;
10037
9906
  DEPENDENCY_RE = /^(?:-(.+?))?->\s*(.+)$/;
10038
9907
  COMMENT_RE = /^\/\//;
10039
- 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;
9908
+ 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;
10040
9909
  MARKER_RE = /^marker\s+(\d{4}(?:-\d{2}(?:-\d{2}(?: \d{2}:\d{2})?)?)?)\s+(.+)$/i;
10041
9910
  HOLIDAY_DATE_RE = /^(\d{4}-\d{2}-\d{2})\s+(.+)$/;
10042
- HOLIDAY_RANGE_RE = /^(\d{4}-\d{2}-\d{2})\s*->\s*(\d{4}-\d{2}-\d{2})\s+(.+)$/;
9911
+ HOLIDAY_RANGE_RE = /^(\d{4}-\d{2}-\d{2})\s*(?:->|\u2013>)\s*(\d{4}-\d{2}-\d{2})\s+(.+)$/;
10043
9912
  WORKWEEK_RE = /^workweek\s+(.+)$/i;
9913
+ 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+(.+)$/;
9914
+ MARKER_ENTRY_RE = /^(\d{4}(?:-\d{2}(?:-\d{2}(?: \d{2}:\d{2})?)?)?)\s+(.+)$/;
10044
9915
  WEEKDAY_MAP = {
10045
9916
  mon: "mon",
10046
9917
  tue: "tue",
@@ -10060,14 +9931,13 @@ var init_parser10 = __esm({
10060
9931
  KNOWN_OPTIONS5 = /* @__PURE__ */ new Set([
10061
9932
  "start",
10062
9933
  "title",
10063
- "orientation",
10064
9934
  "today-marker",
10065
9935
  "critical-path",
10066
9936
  "dependencies",
10067
9937
  "chart",
10068
9938
  "sort"
10069
9939
  ]);
10070
- KNOWN_BOOLEANS3 = /* @__PURE__ */ new Set([
9940
+ KNOWN_BOOLEANS4 = /* @__PURE__ */ new Set([
10071
9941
  "critical-path",
10072
9942
  "today-marker",
10073
9943
  "dependencies"
@@ -12915,7 +12785,7 @@ function nodeStroke3(palette, modifier, nodeColor2, colorOff) {
12915
12785
  return nodeColor2 ?? modifierColor(modifier, palette, colorOff);
12916
12786
  }
12917
12787
  function collectClassTypes(parsed) {
12918
- if (parsed.options?.color === "off") return [];
12788
+ if (parsed.options?.["no-auto-color"]) return [];
12919
12789
  const present = /* @__PURE__ */ new Set();
12920
12790
  for (const c of parsed.classes) {
12921
12791
  if (c.color) continue;
@@ -13077,7 +12947,7 @@ function renderClassDiagram(container, parsed, layout, palette, isDark, onClickI
13077
12947
  }
13078
12948
  const w = node.width;
13079
12949
  const h = node.height;
13080
- const colorOff = parsed.options?.color === "off";
12950
+ const colorOff = !!parsed.options?.["no-auto-color"];
13081
12951
  const neutralize = hasLegend && !isLegendExpanded && !node.color;
13082
12952
  const effectiveColor = neutralize ? palette.primary : node.color;
13083
12953
  const fill2 = nodeFill3(palette, isDark, node.modifier, effectiveColor, colorOff);
@@ -24248,6 +24118,10 @@ function parseVisualization(content, palette) {
24248
24118
  let currentArcGroup = null;
24249
24119
  let currentTimelineGroup = null;
24250
24120
  let currentTimelineTagGroup = null;
24121
+ let inTimelineEraBlock = false;
24122
+ let timelineEraBlockIndent = 0;
24123
+ let inTimelineMarkerBlock = false;
24124
+ let timelineMarkerBlockIndent = 0;
24251
24125
  const timelineAliasMap = /* @__PURE__ */ new Map();
24252
24126
  const VALID_D3_TYPES = /* @__PURE__ */ new Set(["slope", "wordcloud", "arc", "timeline", "venn", "quadrant", "sequence"]);
24253
24127
  let firstLineParsed = false;
@@ -24273,13 +24147,6 @@ function parseVisualization(content, palette) {
24273
24147
  if (result.type === "timeline" && indent === 0) {
24274
24148
  const tagBlockMatch = matchTagBlockHeading(line10);
24275
24149
  if (tagBlockMatch) {
24276
- if (tagBlockMatch.deprecated) {
24277
- result.diagnostics.push(makeDgmoError(
24278
- lineNumber,
24279
- `'## ${tagBlockMatch.name}' is deprecated for tag groups \u2014 use 'tag: ${tagBlockMatch.name}' instead`,
24280
- "warning"
24281
- ));
24282
- }
24283
24150
  currentTimelineTagGroup = {
24284
24151
  name: tagBlockMatch.name,
24285
24152
  alias: tagBlockMatch.alias,
@@ -24361,9 +24228,64 @@ function parseVisualization(content, palette) {
24361
24228
  continue;
24362
24229
  }
24363
24230
  }
24231
+ if (result.type === "timeline" && inTimelineEraBlock) {
24232
+ if (indent <= timelineEraBlockIndent) {
24233
+ inTimelineEraBlock = false;
24234
+ } else {
24235
+ if (line10.startsWith("//")) continue;
24236
+ const eraEntryMatch = line10.match(
24237
+ /^(\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*$/
24238
+ );
24239
+ if (eraEntryMatch) {
24240
+ const colorAnnotation = eraEntryMatch[4]?.trim() || null;
24241
+ result.timelineEras.push({
24242
+ startDate: eraEntryMatch[1],
24243
+ endDate: eraEntryMatch[2],
24244
+ label: eraEntryMatch[3].trim(),
24245
+ color: colorAnnotation ? resolveColor(colorAnnotation, palette) : null,
24246
+ lineNumber
24247
+ });
24248
+ } else {
24249
+ warn(lineNumber, `Unrecognized era entry: "${line10}"`);
24250
+ }
24251
+ continue;
24252
+ }
24253
+ }
24254
+ if (result.type === "timeline" && inTimelineMarkerBlock) {
24255
+ if (indent <= timelineMarkerBlockIndent) {
24256
+ inTimelineMarkerBlock = false;
24257
+ } else {
24258
+ if (line10.startsWith("//")) continue;
24259
+ const markerEntryMatch = line10.match(
24260
+ /^(\d{4}(?:-\d{2})?(?:-\d{2}(?: \d{2}:\d{2})?)?)\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/
24261
+ );
24262
+ if (markerEntryMatch) {
24263
+ const colorAnnotation = markerEntryMatch[3]?.trim() || null;
24264
+ result.timelineMarkers.push({
24265
+ date: markerEntryMatch[1],
24266
+ label: markerEntryMatch[2].trim(),
24267
+ color: colorAnnotation ? resolveColor(colorAnnotation, palette) : null,
24268
+ lineNumber
24269
+ });
24270
+ } else {
24271
+ warn(lineNumber, `Unrecognized marker entry: "${line10}"`);
24272
+ }
24273
+ continue;
24274
+ }
24275
+ }
24364
24276
  if (result.type === "timeline") {
24277
+ if (line10.toLowerCase() === "era") {
24278
+ inTimelineEraBlock = true;
24279
+ timelineEraBlockIndent = indent;
24280
+ continue;
24281
+ }
24282
+ if (line10.toLowerCase() === "marker") {
24283
+ inTimelineMarkerBlock = true;
24284
+ timelineMarkerBlockIndent = indent;
24285
+ continue;
24286
+ }
24365
24287
  const eraMatch = line10.match(
24366
- /^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*$/
24288
+ /^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*$/
24367
24289
  );
24368
24290
  if (eraMatch) {
24369
24291
  const colorAnnotation = eraMatch[4]?.trim() || null;
@@ -24392,7 +24314,7 @@ function parseVisualization(content, palette) {
24392
24314
  }
24393
24315
  if (result.type === "timeline") {
24394
24316
  const durationMatch = line10.match(
24395
- /^(\d{4}(?:-\d{2})?(?:-\d{2}(?: \d{2}:\d{2})?)?)\s*->\s*(\d+(?:\.\d{1,2})?)(min|[dwmyh])(\?)?(?:\s*:\s*|\s+)(.+)$/
24317
+ /^(\d{4}(?:-\d{2})?(?:-\d{2}(?: \d{2}:\d{2})?)?)\s*(?:->|\u2013>)\s*(\d+(?:\.\d{1,2})?)(min|[dwmyh])(\?)?(?:\s*:\s*|\s+)(.+)$/
24396
24318
  );
24397
24319
  if (durationMatch) {
24398
24320
  const startDate = durationMatch[1];
@@ -24401,7 +24323,7 @@ function parseVisualization(content, palette) {
24401
24323
  const unit = durationMatch[3];
24402
24324
  const endDate = addDurationToDate(startDate, amount, unit);
24403
24325
  const segments = durationMatch[5].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: startDate,
24407
24329
  endDate,
@@ -24414,11 +24336,11 @@ function parseVisualization(content, palette) {
24414
24336
  continue;
24415
24337
  }
24416
24338
  const rangeMatch = line10.match(
24417
- /^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\d{4}(?:-\d{2})?(?:-\d{2})?)(\?)?(?:\s*:\s*|\s+)(.+)$/
24339
+ /^(\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+)(.+)$/
24418
24340
  );
24419
24341
  if (rangeMatch) {
24420
24342
  const segments = rangeMatch[4].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: rangeMatch[1],
24424
24346
  endDate: rangeMatch[2],
@@ -24435,7 +24357,7 @@ function parseVisualization(content, palette) {
24435
24357
  );
24436
24358
  if (pointMatch) {
24437
24359
  const segments = pointMatch[2].split("|");
24438
- const metadata = segments.length > 1 ? parsePipeMetadata(["", ...segments.slice(1)], timelineAliasMap, () => warn(lineNumber, MULTIPLE_PIPE_WARNING)) : {};
24360
+ const metadata = segments.length > 1 ? parsePipeMetadata(["", ...segments.slice(1)], timelineAliasMap, () => warn(lineNumber, MULTIPLE_PIPE_ERROR)) : {};
24439
24361
  result.timelineEvents.push({
24440
24362
  date: pointMatch[1],
24441
24363
  endDate: null,
@@ -24449,18 +24371,40 @@ function parseVisualization(content, palette) {
24449
24371
  }
24450
24372
  if (result.type === "venn") {
24451
24373
  if (/\+/.test(line10)) {
24452
- const colonIdx = line10.indexOf(":");
24453
- let setsPart;
24454
- let label;
24455
- if (colonIdx >= 0) {
24456
- setsPart = line10.substring(0, colonIdx).trim();
24457
- label = line10.substring(colonIdx + 1).trim() || null;
24458
- } else {
24459
- setsPart = line10.trim();
24460
- label = null;
24461
- }
24462
- const rawSets = setsPart.split("+").map((s) => s.trim()).filter(Boolean);
24463
- if (rawSets.length >= 2) {
24374
+ const knownSetRefs = /* @__PURE__ */ new Set();
24375
+ for (const s of result.vennSets) {
24376
+ knownSetRefs.add(s.name.toLowerCase());
24377
+ if (s.alias) knownSetRefs.add(s.alias.toLowerCase());
24378
+ }
24379
+ const segments = line10.split("+").map((s) => s.trim()).filter(Boolean);
24380
+ if (segments.length >= 2) {
24381
+ const rawSets = segments.slice(0, -1);
24382
+ const lastSeg = segments[segments.length - 1];
24383
+ const colonIdx = lastSeg.indexOf(":");
24384
+ let lastSetRef;
24385
+ let label;
24386
+ if (colonIdx >= 0) {
24387
+ lastSetRef = lastSeg.substring(0, colonIdx).trim();
24388
+ label = lastSeg.substring(colonIdx + 1).trim() || null;
24389
+ } else {
24390
+ const words = lastSeg.split(/\s+/);
24391
+ let matchLen = 0;
24392
+ for (let w = words.length; w >= 1; w--) {
24393
+ const candidate = words.slice(0, w).join(" ");
24394
+ if (knownSetRefs.has(candidate.toLowerCase())) {
24395
+ matchLen = w;
24396
+ break;
24397
+ }
24398
+ }
24399
+ if (matchLen > 0) {
24400
+ lastSetRef = words.slice(0, matchLen).join(" ");
24401
+ label = words.length > matchLen ? words.slice(matchLen).join(" ") : null;
24402
+ } else {
24403
+ lastSetRef = words[0];
24404
+ label = words.length > 1 ? words.slice(1).join(" ") : null;
24405
+ }
24406
+ }
24407
+ rawSets.push(lastSetRef);
24464
24408
  result.vennOverlaps.push({ sets: rawSets, label, lineNumber });
24465
24409
  continue;
24466
24410
  }
@@ -24570,19 +24514,6 @@ function parseVisualization(content, palette) {
24570
24514
  }
24571
24515
  continue;
24572
24516
  }
24573
- if (firstToken === "orientation" || firstToken === "direction") {
24574
- if (result.type === "arc" || result.type === "timeline") {
24575
- const vLower = restValue.toLowerCase();
24576
- if (vLower === "horizontal" || vLower === "vertical") {
24577
- result.orientation = vLower;
24578
- } else {
24579
- const dir = normalizeDirection(restValue);
24580
- if (dir === "LR") result.orientation = "horizontal";
24581
- else if (dir === "TB") result.orientation = "vertical";
24582
- }
24583
- }
24584
- continue;
24585
- }
24586
24517
  if (firstToken === "order") {
24587
24518
  const v = restValue.toLowerCase();
24588
24519
  if (v === "name" || v === "group" || v === "degree") {
@@ -24590,27 +24521,6 @@ function parseVisualization(content, palette) {
24590
24521
  }
24591
24522
  continue;
24592
24523
  }
24593
- if (firstToken === "sort") {
24594
- const vLower = restValue.toLowerCase();
24595
- if (vLower === "time" || vLower === "group") {
24596
- result.timelineSort = vLower;
24597
- } else if (vLower === "tag" || vLower.startsWith("tag:")) {
24598
- result.timelineSort = "tag";
24599
- if (vLower.startsWith("tag:")) {
24600
- const groupRef = restValue.substring(4).trim();
24601
- if (groupRef) {
24602
- result.timelineDefaultSwimlaneTG = groupRef;
24603
- }
24604
- }
24605
- }
24606
- continue;
24607
- }
24608
- if (firstToken === "swimlanes") {
24609
- const v = restValue.toLowerCase();
24610
- if (v === "on") result.timelineSwimlanes = true;
24611
- else if (v === "off") result.timelineSwimlanes = false;
24612
- continue;
24613
- }
24614
24524
  if (firstToken === "rotate") {
24615
24525
  const v = restValue.toLowerCase();
24616
24526
  if (v === "none" || v === "mixed" || v === "angled") {
@@ -24639,19 +24549,6 @@ function parseVisualization(content, palette) {
24639
24549
  const rawKey = line10.substring(0, colonIndex).trim();
24640
24550
  const key = rawKey.toLowerCase();
24641
24551
  const colorMatch = rawKey.match(/^(.+?)\(([^)]+)\)\s*$/);
24642
- if (key === "chart") {
24643
- const value = line10.substring(colonIndex + 1).trim().toLowerCase();
24644
- if (VALID_D3_TYPES.has(value)) {
24645
- result.type = value;
24646
- } else {
24647
- const validD3Types = [...VALID_D3_TYPES];
24648
- let msg = `Unsupported chart type: ${value}. Supported types: ${validD3Types.join(", ")}`;
24649
- const hint = suggest(value, validD3Types);
24650
- if (hint) msg += `. ${hint}`;
24651
- return fail(lineNumber, msg);
24652
- }
24653
- continue;
24654
- }
24655
24552
  if (key === "title") {
24656
24553
  result.title = line10.substring(colonIndex + 1).trim();
24657
24554
  result.titleLineNumber = lineNumber;
@@ -24660,20 +24557,6 @@ function parseVisualization(content, palette) {
24660
24557
  }
24661
24558
  continue;
24662
24559
  }
24663
- if (key === "orientation" || key === "direction") {
24664
- if (result.type === "arc" || result.type === "timeline") {
24665
- const raw = line10.substring(colonIndex + 1).trim();
24666
- const vLower = raw.toLowerCase();
24667
- if (vLower === "horizontal" || vLower === "vertical") {
24668
- result.orientation = vLower;
24669
- } else {
24670
- const dir = normalizeDirection(raw);
24671
- if (dir === "LR") result.orientation = "horizontal";
24672
- else if (dir === "TB") result.orientation = "vertical";
24673
- }
24674
- }
24675
- continue;
24676
- }
24677
24560
  if (key === "order") {
24678
24561
  const v = line10.substring(colonIndex + 1).trim().toLowerCase();
24679
24562
  if (v === "name" || v === "group" || v === "degree") {
@@ -24681,31 +24564,6 @@ function parseVisualization(content, palette) {
24681
24564
  }
24682
24565
  continue;
24683
24566
  }
24684
- if (key === "sort") {
24685
- const v = line10.substring(colonIndex + 1).trim();
24686
- const vLower = v.toLowerCase();
24687
- if (vLower === "time" || vLower === "group") {
24688
- result.timelineSort = vLower;
24689
- } else if (vLower === "tag" || vLower.startsWith("tag:")) {
24690
- result.timelineSort = "tag";
24691
- if (vLower.startsWith("tag:")) {
24692
- const groupRef = v.substring(4).trim();
24693
- if (groupRef) {
24694
- result.timelineDefaultSwimlaneTG = groupRef;
24695
- }
24696
- }
24697
- }
24698
- continue;
24699
- }
24700
- if (key === "swimlanes") {
24701
- const v = line10.substring(colonIndex + 1).trim().toLowerCase();
24702
- if (v === "on") {
24703
- result.timelineSwimlanes = true;
24704
- } else if (v === "off") {
24705
- result.timelineSwimlanes = false;
24706
- }
24707
- continue;
24708
- }
24709
24567
  if (key === "rotate") {
24710
24568
  const v = line10.substring(colonIndex + 1).trim().toLowerCase();
24711
24569
  if (v === "none" || v === "mixed" || v === "angled") {
@@ -24744,21 +24602,15 @@ function parseVisualization(content, palette) {
24744
24602
  numericValues.push(num);
24745
24603
  }
24746
24604
  if (allNumeric && numericValues.length > 0) {
24747
- if (result.type === "wordcloud" && numericValues.length === 1) {
24748
- result.words.push({
24749
- text: labelPart,
24750
- weight: numericValues[0],
24751
- lineNumber
24752
- });
24753
- } else {
24605
+ if (result.type !== "wordcloud") {
24754
24606
  result.data.push({
24755
24607
  label: labelPart,
24756
24608
  values: numericValues,
24757
24609
  color: colorPart,
24758
24610
  lineNumber
24759
24611
  });
24612
+ continue;
24760
24613
  }
24761
- continue;
24762
24614
  }
24763
24615
  }
24764
24616
  if (result.type === "wordcloud") {
@@ -24801,7 +24653,7 @@ function parseVisualization(content, palette) {
24801
24653
  result.words = tokenizeFreeformText(freeformLines.join(" "));
24802
24654
  }
24803
24655
  if (result.words.length === 0) {
24804
- warn(1, 'No words found. Add words as "word: weight", one per line, or paste freeform text');
24656
+ warn(1, 'No words found. Add words as "word weight" (space-separated), one per line, or paste freeform text');
24805
24657
  }
24806
24658
  if (result.cloudOptions.max > 0 && result.words.length > result.cloudOptions.max) {
24807
24659
  result.words = result.words.slice().sort((a, b) => b.weight - a.weight).slice(0, result.cloudOptions.max);
@@ -24844,25 +24696,6 @@ function parseVisualization(content, palette) {
24844
24696
  }
24845
24697
  }
24846
24698
  }
24847
- if (result.timelineSort === "tag") {
24848
- if (result.timelineTagGroups.length === 0) {
24849
- warn(1, '"sort: tag" requires at least one tag group definition');
24850
- result.timelineSort = "time";
24851
- } else if (result.timelineDefaultSwimlaneTG) {
24852
- const ref = result.timelineDefaultSwimlaneTG.toLowerCase();
24853
- const match = result.timelineTagGroups.find(
24854
- (g) => g.name.toLowerCase() === ref || g.alias?.toLowerCase() === ref
24855
- );
24856
- if (match) {
24857
- result.timelineDefaultSwimlaneTG = match.name;
24858
- } else {
24859
- warn(1, `"sort: tag:${result.timelineDefaultSwimlaneTG}" \u2014 no tag group matches "${result.timelineDefaultSwimlaneTG}"`);
24860
- result.timelineDefaultSwimlaneTG = result.timelineTagGroups[0].name;
24861
- }
24862
- } else {
24863
- result.timelineDefaultSwimlaneTG = result.timelineTagGroups[0].name;
24864
- }
24865
- }
24866
24699
  return result;
24867
24700
  }
24868
24701
  if (result.type === "venn") {
@@ -25459,6 +25292,18 @@ function formatDateLabel(dateStr) {
25459
25292
  const day = parseInt(parts[2], 10);
25460
25293
  return `${month} ${day}, ${year}${timeSuffix}`;
25461
25294
  }
25295
+ function formatBoundaryLabel(dateStr, otherDateStr) {
25296
+ const spaceIdx = dateStr.indexOf(" ");
25297
+ const otherSpaceIdx = otherDateStr.indexOf(" ");
25298
+ if (spaceIdx !== -1 && otherSpaceIdx !== -1) {
25299
+ const datePart = dateStr.slice(0, spaceIdx);
25300
+ const otherDatePart = otherDateStr.slice(0, otherSpaceIdx);
25301
+ if (datePart === otherDatePart) {
25302
+ return dateStr.slice(spaceIdx + 1);
25303
+ }
25304
+ }
25305
+ return formatDateLabel(dateStr);
25306
+ }
25462
25307
  function computeTimeTicks(domainMin, domainMax, scale, boundaryStart, boundaryEnd, boundaryStartLabel, boundaryEndLabel) {
25463
25308
  const minYear = Math.floor(domainMin);
25464
25309
  const maxYear = Math.floor(domainMax);
@@ -25515,17 +25360,22 @@ function computeTimeTicks(domainMin, domainMax, scale, boundaryStart, boundaryEn
25515
25360
  if (spanHours > 48) stepHour = 6;
25516
25361
  else if (spanHours > 24) stepHour = 3;
25517
25362
  else if (spanHours > 12) stepHour = 2;
25363
+ const singleDay = spanHours <= 24;
25518
25364
  const startDate = fractionalYearToDate(domainMin);
25519
25365
  startDate.setHours(Math.floor(startDate.getHours() / stepHour) * stepHour, 0, 0, 0);
25520
25366
  while (true) {
25521
25367
  const val = dateToFractionalYear2(startDate);
25522
25368
  if (val > domainMax) break;
25523
25369
  if (val >= domainMin) {
25524
- const mon = MONTH_ABBR2[startDate.getMonth()];
25525
- const d = startDate.getDate();
25526
25370
  const hh = String(startDate.getHours()).padStart(2, "0");
25527
25371
  const mm = String(startDate.getMinutes()).padStart(2, "0");
25528
- ticks.push({ pos: scale(val), label: `${mon} ${d} ${hh}:${mm}` });
25372
+ if (singleDay) {
25373
+ ticks.push({ pos: scale(val), label: `${hh}:${mm}` });
25374
+ } else {
25375
+ const mon = MONTH_ABBR2[startDate.getMonth()];
25376
+ const d = startDate.getDate();
25377
+ ticks.push({ pos: scale(val), label: `${mon} ${d} ${hh}:${mm}` });
25378
+ }
25529
25379
  }
25530
25380
  startDate.setHours(startDate.getHours() + stepHour);
25531
25381
  }
@@ -25916,8 +25766,8 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
25916
25766
  textColor,
25917
25767
  minDate,
25918
25768
  maxDate,
25919
- formatDateLabel(earliestStartDateStr),
25920
- formatDateLabel(latestEndDateStr)
25769
+ formatBoundaryLabel(earliestStartDateStr, latestEndDateStr),
25770
+ formatBoundaryLabel(latestEndDateStr, earliestStartDateStr)
25921
25771
  );
25922
25772
  }
25923
25773
  if (timelineSwimlanes || tagLanes) {
@@ -26035,8 +25885,8 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
26035
25885
  textColor,
26036
25886
  minDate,
26037
25887
  maxDate,
26038
- formatDateLabel(earliestStartDateStr),
26039
- formatDateLabel(latestEndDateStr)
25888
+ formatBoundaryLabel(earliestStartDateStr, latestEndDateStr),
25889
+ formatBoundaryLabel(latestEndDateStr, earliestStartDateStr)
26040
25890
  );
26041
25891
  }
26042
25892
  if (timelineGroups.length > 0) {
@@ -26183,8 +26033,8 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
26183
26033
  textColor,
26184
26034
  minDate,
26185
26035
  maxDate,
26186
- formatDateLabel(earliestStartDateStr),
26187
- formatDateLabel(latestEndDateStr)
26036
+ formatBoundaryLabel(earliestStartDateStr, latestEndDateStr),
26037
+ formatBoundaryLabel(latestEndDateStr, earliestStartDateStr)
26188
26038
  );
26189
26039
  }
26190
26040
  let curY = markerMargin;
@@ -26337,8 +26187,8 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
26337
26187
  textColor,
26338
26188
  minDate,
26339
26189
  maxDate,
26340
- formatDateLabel(earliestStartDateStr),
26341
- formatDateLabel(latestEndDateStr)
26190
+ formatBoundaryLabel(earliestStartDateStr, latestEndDateStr),
26191
+ formatBoundaryLabel(latestEndDateStr, earliestStartDateStr)
26342
26192
  );
26343
26193
  }
26344
26194
  if (timelineGroups.length > 0) {
@@ -28374,9 +28224,9 @@ init_tag_groups();
28374
28224
  var MAX_DEPTH = 10;
28375
28225
  var IMPORT_RE = /^(\s+)import:?\s+(.+\.dgmo)\s*$/i;
28376
28226
  var TAGS_RE = /^tags:?\s+(.+\.dgmo)\s*$/i;
28377
- var HEADER_RE = /^(org|kanban|chart\s*:|title\s*:)/i;
28227
+ var HEADER_RE = /^(org|kanban|title\s*:)/i;
28378
28228
  var KNOWN_HEADER_OPTIONS = /* @__PURE__ */ new Set([
28379
- "direction",
28229
+ "direction-tb",
28380
28230
  "sub-node-label",
28381
28231
  "hide",
28382
28232
  "show-sub-node-count",
@@ -28823,41 +28673,44 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
28823
28673
  series: { description: "Series name(s)" },
28824
28674
  xlabel: { description: "X-axis label" },
28825
28675
  ylabel: { description: "Y-axis label" },
28826
- orientation: { description: "Layout direction", values: ["horizontal", "vertical"] },
28827
- labels: { description: "Label format", values: ["name", "value", "percent", "full"] },
28676
+ "orientation-horizontal": { description: "Switch to horizontal bars" },
28828
28677
  color: { description: "Bar color override" }
28829
28678
  })],
28830
28679
  ["line", withGlobals({
28831
28680
  series: { description: "Series name(s)" },
28832
28681
  xlabel: { description: "X-axis label" },
28833
- ylabel: { description: "Y-axis label" },
28834
- labels: { description: "Label format", values: ["name", "value", "percent", "full"] }
28682
+ ylabel: { description: "Y-axis label" }
28835
28683
  })],
28836
28684
  ["pie", withGlobals({
28837
- labels: { description: "Label format", values: ["name", "value", "percent", "full"] }
28685
+ "no-label-name": { description: "Hide name from segment labels" },
28686
+ "no-label-value": { description: "Hide value from segment labels" },
28687
+ "no-label-percent": { description: "Hide percent from segment labels" }
28838
28688
  })],
28839
28689
  ["doughnut", withGlobals({
28840
- labels: { description: "Label format", values: ["name", "value", "percent", "full"] }
28690
+ "no-label-name": { description: "Hide name from segment labels" },
28691
+ "no-label-value": { description: "Hide value from segment labels" },
28692
+ "no-label-percent": { description: "Hide percent from segment labels" }
28841
28693
  })],
28842
28694
  ["area", withGlobals({
28843
28695
  series: { description: "Series name(s)" },
28844
28696
  xlabel: { description: "X-axis label" },
28845
- ylabel: { description: "Y-axis label" },
28846
- labels: { description: "Label format", values: ["name", "value", "percent", "full"] }
28697
+ ylabel: { description: "Y-axis label" }
28847
28698
  })],
28848
28699
  ["polar-area", withGlobals({
28849
- labels: { description: "Label format", values: ["name", "value", "percent", "full"] }
28700
+ "no-label-name": { description: "Hide name from segment labels" },
28701
+ "no-label-value": { description: "Hide value from segment labels" },
28702
+ "no-label-percent": { description: "Hide percent from segment labels" }
28850
28703
  })],
28851
28704
  ["radar", withGlobals()],
28852
28705
  ["bar-stacked", withGlobals({
28853
28706
  series: { description: "Series name(s) (required)" },
28854
28707
  xlabel: { description: "X-axis label" },
28855
28708
  ylabel: { description: "Y-axis label" },
28856
- orientation: { description: "Layout direction", values: ["horizontal", "vertical"] }
28709
+ "orientation-horizontal": { description: "Switch to horizontal bars" }
28857
28710
  })],
28858
28711
  // ── Extended charts ──────────────────────────────────────
28859
28712
  ["scatter", withGlobals({
28860
- labels: { description: "Show labels", values: ["on", "off"] },
28713
+ "no-labels": { description: "Hide point labels" },
28861
28714
  xlabel: { description: "X-axis label" },
28862
28715
  ylabel: { description: "Y-axis label" },
28863
28716
  sizelabel: { description: "Size axis label" }
@@ -28871,11 +28724,12 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
28871
28724
  ["function", withGlobals({
28872
28725
  x: { description: "X-axis range (start to end)" },
28873
28726
  xlabel: { description: "X-axis label" },
28874
- ylabel: { description: "Y-axis label" }
28727
+ ylabel: { description: "Y-axis label" },
28728
+ shade: { description: "Fill area below curves with translucent color" }
28875
28729
  })],
28876
28730
  // ── Visualizations ───────────────────────────────────────
28877
28731
  ["slope", withGlobals({
28878
- orientation: { description: "Layout direction", values: ["horizontal", "vertical"] }
28732
+ "orientation-vertical": { description: "Switch to vertical layout" }
28879
28733
  })],
28880
28734
  ["wordcloud", withGlobals({
28881
28735
  rotate: { description: "Word rotation", values: ["none", "mixed", "angled"] },
@@ -28883,14 +28737,9 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
28883
28737
  size: { description: "Font size range (min, max)" }
28884
28738
  })],
28885
28739
  ["arc", withGlobals({
28886
- order: { description: "Node ordering", values: ["appearance", "name", "group", "degree"] },
28887
- orientation: { description: "Layout direction" }
28888
- })],
28889
- ["timeline", withGlobals({
28890
- scale: { description: "Show time scale", values: ["on", "off"] },
28891
- sort: { description: "Sort order", values: ["time", "group", "tag"] },
28892
- swimlanes: { description: "Show swimlanes", values: ["on", "off"] }
28740
+ order: { description: "Node ordering", values: ["appearance", "name", "group", "degree"] }
28893
28741
  })],
28742
+ ["timeline", withGlobals()],
28894
28743
  ["venn", withGlobals({
28895
28744
  values: { description: "Show values", values: ["on", "off"] }
28896
28745
  })],
@@ -28904,25 +28753,31 @@ var COMPLETION_REGISTRY = /* @__PURE__ */ new Map([
28904
28753
  "collapse-notes": { description: "Collapse note blocks", values: ["yes", "no"] },
28905
28754
  "active-tag": { description: "Active tag group name" }
28906
28755
  })],
28907
- ["flowchart", withGlobals()],
28908
- ["class", withGlobals()],
28756
+ ["flowchart", withGlobals({
28757
+ "direction-tb": { description: "Switch to top-to-bottom layout" }
28758
+ })],
28759
+ ["class", withGlobals({
28760
+ "no-auto-color": { description: "Disable automatic modifier-based coloring" }
28761
+ })],
28909
28762
  ["er", withGlobals()],
28910
28763
  ["org", withGlobals({
28911
28764
  "sub-node-label": { description: "Label for sub-nodes" },
28912
28765
  "show-sub-node-count": { description: "Show sub-node counts" }
28913
28766
  })],
28914
- ["kanban", withGlobals()],
28767
+ ["kanban", withGlobals({
28768
+ "no-auto-color": { description: "Disable automatic card coloring" }
28769
+ })],
28915
28770
  ["c4", withGlobals()],
28916
28771
  ["initiative-status", withGlobals()],
28917
28772
  ["state", withGlobals({
28918
- direction: { description: "Layout direction", values: ["TB", "LR"] },
28773
+ "direction-tb": { description: "Switch to top-to-bottom layout" },
28919
28774
  color: { description: "Color mode", values: ["off"] }
28920
28775
  })],
28921
28776
  ["sitemap", withGlobals({
28922
- direction: { description: "Layout direction", values: ["TB", "LR"] }
28777
+ "direction-tb": { description: "Switch to top-to-bottom layout" }
28923
28778
  })],
28924
28779
  ["infra", withGlobals({
28925
- direction: { description: "Layout direction", values: ["LR", "TB"] },
28780
+ "direction-tb": { description: "Switch to top-to-bottom layout" },
28926
28781
  "default-latency-ms": { description: "Default latency for all nodes" },
28927
28782
  "default-uptime": { description: "Default uptime for all nodes" },
28928
28783
  "default-rps": { description: "Default RPS capacity for all nodes" },
@@ -28982,7 +28837,6 @@ var CHART_TYPES = [...ALL_CHART_TYPES].filter((t) => t !== "multi-line").map((na
28982
28837
  }));
28983
28838
  var ENTITY_TYPES = /* @__PURE__ */ new Map([
28984
28839
  ["sequence", ["service", "database", "actor", "queue", "cache", "gateway", "external", "networking", "frontend"]],
28985
- ["infra", ["database", "cache", "queue", "service", "gateway", "storage", "function", "network"]],
28986
28840
  ["c4", ["person", "system", "container", "component", "external", "database"]]
28987
28841
  ]);
28988
28842
  var PIPE_METADATA = /* @__PURE__ */ new Map([