@diagrammo/dgmo 0.8.15 → 0.8.16

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
@@ -322,20 +322,39 @@ var init_label_layout = __esm({
322
322
  });
323
323
 
324
324
  // src/colors.ts
325
+ function isRecognizedColorName(name) {
326
+ return Object.prototype.hasOwnProperty.call(colorNames, name.toLowerCase());
327
+ }
325
328
  function resolveColor(color, palette) {
329
+ if (!color) return null;
326
330
  if (color.startsWith("#")) return null;
327
331
  const lower = color.toLowerCase();
332
+ if (!isRecognizedColorName(lower)) return null;
328
333
  if (palette) {
329
334
  const named = palette.colors[lower];
330
335
  if (named) return named;
331
336
  }
332
- if (colorNames[lower]) return colorNames[lower];
333
- return color;
337
+ return colorNames[lower];
338
+ }
339
+ function resolveColorWithDiagnostic(color, line10, diagnostics, palette) {
340
+ const resolved = resolveColor(color, palette);
341
+ if (resolved !== null) return resolved;
342
+ const hint = suggest(color, RECOGNIZED_COLOR_NAMES);
343
+ const suggestion = hint ? ` ${hint}` : "";
344
+ diagnostics.push(
345
+ makeDgmoError(
346
+ line10,
347
+ `Unknown color "${color}". Allowed: ${RECOGNIZED_COLOR_NAMES.join(", ")}.${suggestion}`,
348
+ "warning"
349
+ )
350
+ );
351
+ return void 0;
334
352
  }
335
- var nord, colorNames, seriesColors;
353
+ var nord, colorNames, RECOGNIZED_COLOR_NAMES, seriesColors;
336
354
  var init_colors = __esm({
337
355
  "src/colors.ts"() {
338
356
  "use strict";
357
+ init_diagnostics();
339
358
  nord = {
340
359
  // Polar Night (dark)
341
360
  nord0: "#2e3440",
@@ -372,9 +391,23 @@ var init_colors = __esm({
372
391
  purple: nord.nord15,
373
392
  teal: nord.nord7,
374
393
  cyan: nord.nord8,
375
- lightblue: nord.nord8,
376
- gray: nord.nord3
394
+ gray: nord.nord3,
395
+ black: nord.nord0,
396
+ white: nord.nord6
377
397
  };
398
+ RECOGNIZED_COLOR_NAMES = Object.freeze([
399
+ "red",
400
+ "orange",
401
+ "yellow",
402
+ "green",
403
+ "blue",
404
+ "purple",
405
+ "teal",
406
+ "cyan",
407
+ "gray",
408
+ "black",
409
+ "white"
410
+ ]);
378
411
  seriesColors = [
379
412
  nord.nord10,
380
413
  // blue
@@ -631,7 +664,9 @@ var init_bold = __esm({
631
664
  purple: "#cc00cc",
632
665
  teal: "#008080",
633
666
  cyan: "#00cccc",
634
- gray: "#808080"
667
+ gray: "#808080",
668
+ black: "#000000",
669
+ white: "#f0f0f0"
635
670
  }
636
671
  },
637
672
  dark: {
@@ -654,7 +689,9 @@ var init_bold = __esm({
654
689
  purple: "#ff00ff",
655
690
  teal: "#00cccc",
656
691
  cyan: "#00ffff",
657
- gray: "#808080"
692
+ gray: "#808080",
693
+ black: "#111111",
694
+ white: "#ffffff"
658
695
  }
659
696
  }
660
697
  };
@@ -701,8 +738,10 @@ var init_catppuccin = __esm({
701
738
  purple: "#8839ef",
702
739
  teal: "#179299",
703
740
  cyan: "#209fb5",
704
- gray: "#9ca0b0"
741
+ gray: "#9ca0b0",
705
742
  // Latte Overlay0
743
+ black: "#4c4f69",
744
+ white: "#e6e9ef"
706
745
  }
707
746
  },
708
747
  dark: {
@@ -735,8 +774,10 @@ var init_catppuccin = __esm({
735
774
  purple: "#cba6f7",
736
775
  teal: "#94e2d5",
737
776
  cyan: "#74c7ec",
738
- gray: "#6c7086"
777
+ gray: "#6c7086",
739
778
  // Mocha Overlay0
779
+ black: "#313244",
780
+ white: "#cdd6f4"
740
781
  }
741
782
  }
742
783
  };
@@ -791,7 +832,9 @@ var init_gruvbox = __esm({
791
832
  // faded aqua
792
833
  cyan: "#427b58",
793
834
  // faded aqua
794
- gray: "#928374"
835
+ gray: "#928374",
836
+ black: "#3c3836",
837
+ white: "#ebdbb2"
795
838
  }
796
839
  },
797
840
  dark: {
@@ -832,7 +875,9 @@ var init_gruvbox = __esm({
832
875
  // bright aqua
833
876
  cyan: "#8ec07c",
834
877
  // bright aqua
835
- gray: "#928374"
878
+ gray: "#928374",
879
+ black: "#3c3836",
880
+ white: "#ebdbb2"
836
881
  }
837
882
  }
838
883
  };
@@ -887,8 +932,10 @@ var init_nord = __esm({
887
932
  // nord7
888
933
  cyan: "#88c0d0",
889
934
  // nord8
890
- gray: "#4c566a"
935
+ gray: "#4c566a",
891
936
  // nord3
937
+ black: "#2e3440",
938
+ white: "#e5e9f0"
892
939
  }
893
940
  },
894
941
  dark: {
@@ -929,8 +976,10 @@ var init_nord = __esm({
929
976
  // nord7
930
977
  cyan: "#88c0d0",
931
978
  // nord8
932
- gray: "#4c566a"
979
+ gray: "#4c566a",
933
980
  // nord3
981
+ black: "#3b4252",
982
+ white: "#eceff4"
934
983
  }
935
984
  }
936
985
  };
@@ -968,7 +1017,9 @@ var init_one_dark = __esm({
968
1017
  purple: "#a626a4",
969
1018
  teal: "#0184bc",
970
1019
  cyan: "#0997b3",
971
- gray: "#696c77"
1020
+ gray: "#696c77",
1021
+ black: "#383a42",
1022
+ white: "#f0f0f0"
972
1023
  }
973
1024
  },
974
1025
  dark: {
@@ -992,7 +1043,9 @@ var init_one_dark = __esm({
992
1043
  purple: "#c678dd",
993
1044
  teal: "#56b6c2",
994
1045
  cyan: "#56b6c2",
995
- gray: "#5c6370"
1046
+ gray: "#5c6370",
1047
+ black: "#21252b",
1048
+ white: "#abb2bf"
996
1049
  }
997
1050
  }
998
1051
  };
@@ -1047,8 +1100,10 @@ var init_rose_pine = __esm({
1047
1100
  // Pine
1048
1101
  cyan: "#56949f",
1049
1102
  // Foam
1050
- gray: "#9893a5"
1103
+ gray: "#9893a5",
1051
1104
  // Muted
1105
+ black: "#575279",
1106
+ white: "#fffaf3"
1052
1107
  }
1053
1108
  },
1054
1109
  dark: {
@@ -1089,8 +1144,10 @@ var init_rose_pine = __esm({
1089
1144
  // Pine
1090
1145
  cyan: "#9ccfd8",
1091
1146
  // Foam
1092
- gray: "#6e6a86"
1147
+ gray: "#6e6a86",
1093
1148
  // Muted
1149
+ black: "#2a273f",
1150
+ white: "#e0def4"
1094
1151
  }
1095
1152
  }
1096
1153
  };
@@ -1138,8 +1195,10 @@ var init_solarized = __esm({
1138
1195
  teal: "#2aa198",
1139
1196
  cyan: "#2aa198",
1140
1197
  // Solarized has no separate cyan — reuse teal
1141
- gray: "#586e75"
1198
+ gray: "#586e75",
1142
1199
  // base01
1200
+ black: "#657b83",
1201
+ white: "#eee8d5"
1143
1202
  }
1144
1203
  },
1145
1204
  dark: {
@@ -1172,8 +1231,10 @@ var init_solarized = __esm({
1172
1231
  purple: "#6c71c4",
1173
1232
  teal: "#2aa198",
1174
1233
  cyan: "#2aa198",
1175
- gray: "#586e75"
1234
+ gray: "#586e75",
1176
1235
  // base01
1236
+ black: "#073642",
1237
+ white: "#839496"
1177
1238
  }
1178
1239
  }
1179
1240
  };
@@ -1220,8 +1281,10 @@ var init_tokyo_night = __esm({
1220
1281
  purple: "#7847bd",
1221
1282
  teal: "#118c74",
1222
1283
  cyan: "#007197",
1223
- gray: "#8990b3"
1284
+ gray: "#8990b3",
1224
1285
  // Day dark3
1286
+ black: "#1a1b26",
1287
+ white: "#d0d5e3"
1225
1288
  }
1226
1289
  },
1227
1290
  dark: {
@@ -1254,8 +1317,10 @@ var init_tokyo_night = __esm({
1254
1317
  purple: "#bb9af7",
1255
1318
  teal: "#1abc9c",
1256
1319
  cyan: "#7dcfff",
1257
- gray: "#565f89"
1320
+ gray: "#565f89",
1258
1321
  // Night comment
1322
+ black: "#292e42",
1323
+ white: "#c0caf5"
1259
1324
  }
1260
1325
  }
1261
1326
  };
@@ -1301,7 +1366,9 @@ var init_dracula = __esm({
1301
1366
  teal: "#5ac8b8",
1302
1367
  // muted cyan toward green
1303
1368
  cyan: "#8be9fd",
1304
- gray: "#6272a4"
1369
+ gray: "#6272a4",
1370
+ black: "#282a36",
1371
+ white: "#f0f0ec"
1305
1372
  }
1306
1373
  },
1307
1374
  dark: {
@@ -1336,7 +1403,9 @@ var init_dracula = __esm({
1336
1403
  teal: "#5ac8b8",
1337
1404
  // muted cyan toward green
1338
1405
  cyan: "#8be9fd",
1339
- gray: "#6272a4"
1406
+ gray: "#6272a4",
1407
+ black: "#343746",
1408
+ white: "#f8f8f2"
1340
1409
  }
1341
1410
  }
1342
1411
  };
@@ -1382,8 +1451,10 @@ var init_monokai = __esm({
1382
1451
  teal: "#4ea8a6",
1383
1452
  // muted from cyan
1384
1453
  cyan: "#66d9ef",
1385
- gray: "#75715e"
1454
+ gray: "#75715e",
1386
1455
  // comment
1456
+ black: "#272822",
1457
+ white: "#f0efe8"
1387
1458
  }
1388
1459
  },
1389
1460
  dark: {
@@ -1416,8 +1487,10 @@ var init_monokai = __esm({
1416
1487
  teal: "#4ea8a6",
1417
1488
  // muted from cyan
1418
1489
  cyan: "#66d9ef",
1419
- gray: "#75715e"
1490
+ gray: "#75715e",
1420
1491
  // comment
1492
+ black: "#2d2e27",
1493
+ white: "#f8f8f2"
1421
1494
  }
1422
1495
  }
1423
1496
  };
@@ -1648,13 +1721,19 @@ function measureIndent(line10) {
1648
1721
  }
1649
1722
  return indent;
1650
1723
  }
1651
- function extractColor(label, palette) {
1724
+ function extractColor(label, palette, diagnostics, line10) {
1652
1725
  const m = label.match(COLOR_SUFFIX_RE);
1653
1726
  if (!m) return { label };
1654
1727
  const colorName = m[1].trim();
1728
+ let color;
1729
+ if (diagnostics && line10 !== void 0) {
1730
+ color = resolveColorWithDiagnostic(colorName, line10, diagnostics, palette);
1731
+ } else {
1732
+ color = resolveColor(colorName, palette) ?? void 0;
1733
+ }
1655
1734
  return {
1656
1735
  label: label.substring(0, m.index).trim(),
1657
- color: resolveColor(colorName, palette) ?? void 0
1736
+ color
1658
1737
  };
1659
1738
  }
1660
1739
  function parseFirstLine(line10) {
@@ -1718,7 +1797,7 @@ function collectIndentedValues(lines, startIndex) {
1718
1797
  }
1719
1798
  return { values, lineNumbers, newIndex: j - 1 };
1720
1799
  }
1721
- function parseSeriesNames(value, lines, lineIndex, palette) {
1800
+ function parseSeriesNames(value, lines, lineIndex, palette, diagnostics) {
1722
1801
  let rawNames;
1723
1802
  let series;
1724
1803
  let newIndex = lineIndex;
@@ -1736,8 +1815,14 @@ function parseSeriesNames(value, lines, lineIndex, palette) {
1736
1815
  }
1737
1816
  const names = [];
1738
1817
  const nameColors = [];
1739
- for (const raw of rawNames) {
1740
- const extracted = extractColor(raw, palette);
1818
+ for (let i = 0; i < rawNames.length; i++) {
1819
+ const raw = rawNames[i];
1820
+ const extracted = extractColor(
1821
+ raw,
1822
+ palette,
1823
+ diagnostics,
1824
+ nameLineNumbers[i]
1825
+ );
1741
1826
  nameColors.push(extracted.color);
1742
1827
  names.push(extracted.label);
1743
1828
  }
@@ -3238,7 +3323,12 @@ function parseSequenceDgmo(content) {
3238
3323
  }
3239
3324
  if (currentTagGroup && !contentStarted && measureIndent(raw) > 0) {
3240
3325
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
3241
- const { label, color } = extractColor(cleanEntry);
3326
+ const { label, color } = extractColor(
3327
+ cleanEntry,
3328
+ void 0,
3329
+ result.diagnostics,
3330
+ lineNumber
3331
+ );
3242
3332
  if (!color) {
3243
3333
  pushError(
3244
3334
  lineNumber,
@@ -3951,19 +4041,37 @@ function splitArrows(line10) {
3951
4041
  }
3952
4042
  return segments;
3953
4043
  }
3954
- function parseArrowToken(token, palette) {
4044
+ function parseArrowToken(token, palette, lineNumber, diagnostics) {
3955
4045
  if (token === "->") return {};
3956
4046
  const colorOnly = token.match(/^-\(([^)]+)\)->$/);
3957
4047
  if (colorOnly) {
3958
- return { color: resolveColor(colorOnly[1].trim(), palette) ?? void 0 };
4048
+ return {
4049
+ color: resolveColorWithDiagnostic(
4050
+ colorOnly[1].trim(),
4051
+ lineNumber,
4052
+ diagnostics,
4053
+ palette
4054
+ )
4055
+ };
3959
4056
  }
3960
4057
  const m = token.match(/^-(.+?)(?:\(([^)]+)\))?->$/);
3961
4058
  if (m) {
3962
4059
  const label = m[1]?.trim() || void 0;
3963
- let color = m[2] ? resolveColor(m[2].trim(), palette) ?? void 0 : void 0;
4060
+ let color = m[2] ? resolveColorWithDiagnostic(
4061
+ m[2].trim(),
4062
+ lineNumber,
4063
+ diagnostics,
4064
+ palette
4065
+ ) : void 0;
3964
4066
  if (label && !color) {
3965
4067
  const inferred = inferArrowColor(label);
3966
- if (inferred) color = resolveColor(inferred, palette) ?? void 0;
4068
+ if (inferred)
4069
+ color = resolveColorWithDiagnostic(
4070
+ inferred,
4071
+ lineNumber,
4072
+ diagnostics,
4073
+ palette
4074
+ );
3967
4075
  }
3968
4076
  return { label, color };
3969
4077
  }
@@ -4040,7 +4148,12 @@ function parseFlowchart(content, palette) {
4040
4148
  for (let i = 0; i < segments.length; i++) {
4041
4149
  const seg = segments[i];
4042
4150
  if (seg === "->" || /^-.+->$/.test(seg)) {
4043
- pendingArrow = parseArrowToken(seg, palette);
4151
+ pendingArrow = parseArrowToken(
4152
+ seg,
4153
+ palette,
4154
+ lineNumber,
4155
+ result.diagnostics
4156
+ );
4044
4157
  continue;
4045
4158
  }
4046
4159
  const ref = parseNodeRef(seg, palette);
@@ -4234,15 +4347,27 @@ function splitArrows2(line10) {
4234
4347
  if (remaining) segments.push(remaining);
4235
4348
  return segments;
4236
4349
  }
4237
- function parseArrowToken2(token, palette) {
4350
+ function parseArrowToken2(token, palette, lineNumber, diagnostics) {
4238
4351
  if (token === "->") return {};
4239
4352
  const colorOnly = token.match(/^-\(([^)]+)\)->$/);
4240
4353
  if (colorOnly)
4241
- return { color: resolveColor(colorOnly[1].trim(), palette) ?? void 0 };
4354
+ return {
4355
+ color: resolveColorWithDiagnostic(
4356
+ colorOnly[1].trim(),
4357
+ lineNumber,
4358
+ diagnostics,
4359
+ palette
4360
+ )
4361
+ };
4242
4362
  const m = token.match(/^-(.+?)(?:\(([^)]+)\))?->$/);
4243
4363
  if (m) {
4244
4364
  const label = m[1]?.trim() || void 0;
4245
- const color = m[2] ? resolveColor(m[2].trim(), palette) ?? void 0 : void 0;
4365
+ const color = m[2] ? resolveColorWithDiagnostic(
4366
+ m[2].trim(),
4367
+ lineNumber,
4368
+ diagnostics,
4369
+ palette
4370
+ ) : void 0;
4246
4371
  return { label, color };
4247
4372
  }
4248
4373
  return {};
@@ -4346,7 +4471,12 @@ function parseState(content, palette) {
4346
4471
  if (groupMatch && groupMatch[1].trim() !== "*") {
4347
4472
  const groupLabel = groupMatch[1].trim();
4348
4473
  const groupColorName = groupMatch[2]?.trim();
4349
- const groupColor = groupColorName ? resolveColor(groupColorName, palette) : void 0;
4474
+ const groupColor = groupColorName ? resolveColorWithDiagnostic(
4475
+ groupColorName,
4476
+ lineNumber,
4477
+ result.diagnostics,
4478
+ palette
4479
+ ) : void 0;
4350
4480
  currentGroup = {
4351
4481
  id: `group:${groupLabel.toLowerCase()}`,
4352
4482
  label: groupLabel,
@@ -4404,7 +4534,12 @@ function parseState(content, palette) {
4404
4534
  for (let j = 0; j < segments.length; j++) {
4405
4535
  const seg = segments[j];
4406
4536
  if (seg === "->" || /^-.+->$/.test(seg)) {
4407
- pendingArrow = parseArrowToken2(seg, palette);
4537
+ pendingArrow = parseArrowToken2(
4538
+ seg,
4539
+ palette,
4540
+ lineNumber,
4541
+ result.diagnostics
4542
+ );
4408
4543
  continue;
4409
4544
  }
4410
4545
  const ref = parseStateNodeRef(seg, palette);
@@ -4667,7 +4802,12 @@ function parseClassDiagram(content, palette) {
4667
4802
  const bracketModifier = classDecl[5];
4668
4803
  const modifier = prefixModifier ?? bracketModifier;
4669
4804
  const colorName = classDecl[6]?.trim();
4670
- const color = colorName ? resolveColor(colorName, palette) : void 0;
4805
+ const color = colorName ? resolveColorWithDiagnostic(
4806
+ colorName,
4807
+ lineNumber,
4808
+ result.diagnostics,
4809
+ palette
4810
+ ) : void 0;
4671
4811
  const node = getOrCreateClass(name, lineNumber);
4672
4812
  if (modifier) node.modifier = modifier;
4673
4813
  if (color) node.color = color;
@@ -5037,7 +5177,12 @@ function parseERDiagram(content, palette) {
5037
5177
  if (tableDecl) {
5038
5178
  const name = tableDecl[1];
5039
5179
  const colorName = tableDecl[2]?.trim();
5040
- const color = colorName ? resolveColor(colorName, palette) : void 0;
5180
+ const color = colorName ? resolveColorWithDiagnostic(
5181
+ colorName,
5182
+ lineNumber,
5183
+ result.diagnostics,
5184
+ palette
5185
+ ) : void 0;
5041
5186
  const table = getOrCreateTable(name, lineNumber);
5042
5187
  if (color) table.color = color;
5043
5188
  table.lineNumber = lineNumber;
@@ -5165,7 +5310,7 @@ var init_parser3 = __esm({
5165
5310
  init_parsing();
5166
5311
  init_tag_groups();
5167
5312
  TABLE_DECL_RE = /^([a-zA-Z_]\w*)(?:\s*\(([^)]+)\))?(?:\s*\|(.+))?$/;
5168
- INDENT_REL_RE = /^([1*?])-(?:(.+)-)?([1*?])\s+([a-zA-Z_]\w*)\s*$/;
5313
+ INDENT_REL_RE = /^([1*?])-{1,2}(?:(.+?)-{1,2})?([1*?])\s+([a-zA-Z_]\w*)\s*$/;
5169
5314
  CONSTRAINT_MAP = {
5170
5315
  pk: "pk",
5171
5316
  fk: "fk",
@@ -5254,7 +5399,12 @@ function parseChart(content, palette) {
5254
5399
  rawEras.push({
5255
5400
  start: eraMatch[1].trim(),
5256
5401
  afterArrow,
5257
- color: eraMatch[3] ? resolveColor(eraMatch[3].trim(), palette) : null,
5402
+ color: eraMatch[3] ? resolveColorWithDiagnostic(
5403
+ eraMatch[3].trim(),
5404
+ lineNumber,
5405
+ result.diagnostics,
5406
+ palette
5407
+ ) ?? null : null,
5258
5408
  lineNumber
5259
5409
  });
5260
5410
  }
@@ -5303,11 +5453,22 @@ function parseChart(content, palette) {
5303
5453
  continue;
5304
5454
  }
5305
5455
  if (firstToken === "color") {
5306
- result.color = resolveColor(value.trim(), palette) ?? void 0;
5456
+ result.color = resolveColorWithDiagnostic(
5457
+ value.trim(),
5458
+ lineNumber,
5459
+ result.diagnostics,
5460
+ palette
5461
+ );
5307
5462
  continue;
5308
5463
  }
5309
5464
  if (firstToken === "series") {
5310
- const parsed = parseSeriesNames(value, lines, i, palette);
5465
+ const parsed = parseSeriesNames(
5466
+ value,
5467
+ lines,
5468
+ i,
5469
+ palette,
5470
+ result.diagnostics
5471
+ );
5311
5472
  i = parsed.newIndex;
5312
5473
  result.series = parsed.series;
5313
5474
  result.seriesLineNumber = lineNumber;
@@ -5728,7 +5889,12 @@ function parseExtendedChart(content, palette) {
5728
5889
  const categoryMatch = trimmed.match(/^\[(.+?)\](?:\s*\(([^)]+)\))?\s*$/);
5729
5890
  if (categoryMatch) {
5730
5891
  const catName = categoryMatch[1].trim();
5731
- const catColor = categoryMatch[2] ? resolveColor(categoryMatch[2].trim(), palette) : null;
5892
+ const catColor = categoryMatch[2] ? resolveColorWithDiagnostic(
5893
+ categoryMatch[2].trim(),
5894
+ lineNumber,
5895
+ result.diagnostics,
5896
+ palette
5897
+ ) ?? null : null;
5732
5898
  if (catColor) {
5733
5899
  if (!result.categoryColors) result.categoryColors = {};
5734
5900
  result.categoryColors[catName] = catColor;
@@ -5756,7 +5922,12 @@ function parseExtendedChart(content, palette) {
5756
5922
  if (sourceColor) result.nodeColors[source] = sourceColor;
5757
5923
  if (targetColor) result.nodeColors[target] = targetColor;
5758
5924
  }
5759
- const linkColor = rawLinkColor ? resolveColor(rawLinkColor.trim(), palette) : void 0;
5925
+ const linkColor = rawLinkColor ? resolveColorWithDiagnostic(
5926
+ rawLinkColor.trim(),
5927
+ lineNumber,
5928
+ result.diagnostics,
5929
+ palette
5930
+ ) : void 0;
5760
5931
  if (!result.links) result.links = [];
5761
5932
  result.links.push({
5762
5933
  source,
@@ -5782,7 +5953,12 @@ function parseExtendedChart(content, palette) {
5782
5953
  const dataRow2 = parseDataRowValues(strippedLine);
5783
5954
  if (dataRow2 && dataRow2.values.length === 1) {
5784
5955
  const source = sankeyStack.at(-1).name;
5785
- const linkColor = valColorMatch?.[2] ? resolveColor(valColorMatch[2].trim(), palette) : void 0;
5956
+ const linkColor = valColorMatch?.[2] ? resolveColorWithDiagnostic(
5957
+ valColorMatch[2].trim(),
5958
+ lineNumber,
5959
+ result.diagnostics,
5960
+ palette
5961
+ ) : void 0;
5786
5962
  const { label: target, color: targetColor } = extractColor(
5787
5963
  dataRow2.label,
5788
5964
  palette
@@ -8251,7 +8427,12 @@ function parseKanban(content, palette) {
8251
8427
  currentCard = null;
8252
8428
  columnCounter++;
8253
8429
  const colName = columnMatch[1].trim();
8254
- const colColor = columnMatch[2] ? resolveColor(columnMatch[2].trim(), palette) ?? void 0 : void 0;
8430
+ const colColor = columnMatch[2] ? resolveColorWithDiagnostic(
8431
+ columnMatch[2].trim(),
8432
+ lineNumber,
8433
+ result.diagnostics,
8434
+ palette
8435
+ ) : void 0;
8255
8436
  let wipLimit;
8256
8437
  const columnMetadata = {};
8257
8438
  const pipeStr = columnMatch[3];
@@ -8299,7 +8480,8 @@ function parseKanban(content, palette) {
8299
8480
  lineNumber,
8300
8481
  cardCounter,
8301
8482
  aliasMap,
8302
- palette
8483
+ palette,
8484
+ result.diagnostics
8303
8485
  );
8304
8486
  if (currentColumn.metadata) {
8305
8487
  for (const [key, value] of Object.entries(currentColumn.metadata)) {
@@ -8353,7 +8535,7 @@ function parseKanban(content, palette) {
8353
8535
  validateTagGroupNames(result.tagGroups, warn);
8354
8536
  return result;
8355
8537
  }
8356
- function parseCardLine(trimmed, lineNumber, counter, aliasMap, palette) {
8538
+ function parseCardLine(trimmed, lineNumber, counter, aliasMap, palette, diagnostics) {
8357
8539
  const pipeIdx = trimmed.indexOf("|");
8358
8540
  let rawTitle;
8359
8541
  let tagsStr = null;
@@ -8363,7 +8545,12 @@ function parseCardLine(trimmed, lineNumber, counter, aliasMap, palette) {
8363
8545
  } else {
8364
8546
  rawTitle = trimmed;
8365
8547
  }
8366
- const { label: title, color } = extractColor(rawTitle, palette);
8548
+ const { label: title, color } = extractColor(
8549
+ rawTitle,
8550
+ palette,
8551
+ diagnostics,
8552
+ lineNumber
8553
+ );
8367
8554
  const tags = {};
8368
8555
  if (tagsStr) {
8369
8556
  for (const part of tagsStr.split(",")) {
@@ -9098,7 +9285,7 @@ __export(parser_exports7, {
9098
9285
  looksLikeSitemap: () => looksLikeSitemap,
9099
9286
  parseSitemap: () => parseSitemap
9100
9287
  });
9101
- function parseArrowLine(trimmed, palette) {
9288
+ function parseArrowLine(trimmed, palette, lineNumber, diagnostics) {
9102
9289
  const bareMatch = trimmed.match(BARE_ARROW_RE);
9103
9290
  if (bareMatch) {
9104
9291
  const rawTarget = bareMatch[1].trim();
@@ -9111,10 +9298,21 @@ function parseArrowLine(trimmed, palette) {
9111
9298
  const arrowMatch = trimmed.match(ARROW_RE);
9112
9299
  if (arrowMatch) {
9113
9300
  const label = arrowMatch[1]?.trim() || void 0;
9114
- let color = arrowMatch[2] ? resolveColor(arrowMatch[2].trim(), palette) ?? void 0 : void 0;
9301
+ let color = arrowMatch[2] ? resolveColorWithDiagnostic(
9302
+ arrowMatch[2].trim(),
9303
+ lineNumber,
9304
+ diagnostics,
9305
+ palette
9306
+ ) : void 0;
9115
9307
  if (label && !color) {
9116
9308
  const inferred = inferArrowColor(label);
9117
- if (inferred) color = resolveColor(inferred, palette) ?? void 0;
9309
+ if (inferred)
9310
+ color = resolveColorWithDiagnostic(
9311
+ inferred,
9312
+ lineNumber,
9313
+ diagnostics,
9314
+ palette
9315
+ );
9118
9316
  }
9119
9317
  const rawTarget = arrowMatch[3].trim();
9120
9318
  const groupMatch = rawTarget.match(/^\[(.+)\]$/);
@@ -9278,7 +9476,12 @@ function parseSitemap(content, palette) {
9278
9476
  contentStarted = true;
9279
9477
  currentTagGroup = null;
9280
9478
  const indent = measureIndent(line10);
9281
- const arrowInfo = parseArrowLine(trimmed, palette);
9479
+ const arrowInfo = parseArrowLine(
9480
+ trimmed,
9481
+ palette,
9482
+ lineNumber,
9483
+ result.diagnostics
9484
+ );
9282
9485
  if (arrowInfo) {
9283
9486
  const source = findParentNode(indent, indentStack);
9284
9487
  if (!source) {
@@ -9299,7 +9502,12 @@ function parseSitemap(content, palette) {
9299
9502
  const metadataMatch = trimmed.includes("|") ? null : trimmed.match(METADATA_RE3);
9300
9503
  if (containerMatch) {
9301
9504
  const rawLabel = containerMatch[1].trim();
9302
- const { label, color } = extractColor(rawLabel, palette);
9505
+ const { label, color } = extractColor(
9506
+ rawLabel,
9507
+ palette,
9508
+ result.diagnostics,
9509
+ lineNumber
9510
+ );
9303
9511
  const pipeStr = containerMatch[2];
9304
9512
  const containerMetadata = {};
9305
9513
  if (pipeStr) {
@@ -9340,7 +9548,8 @@ function parseSitemap(content, palette) {
9340
9548
  palette,
9341
9549
  ++nodeCounter,
9342
9550
  aliasMap,
9343
- pushWarning
9551
+ pushWarning,
9552
+ result.diagnostics
9344
9553
  );
9345
9554
  attachNode2(node, indent, indentStack, result);
9346
9555
  labelToNode.set(node.label.toLowerCase(), node);
@@ -9354,7 +9563,8 @@ function parseSitemap(content, palette) {
9354
9563
  palette,
9355
9564
  ++nodeCounter,
9356
9565
  aliasMap,
9357
- pushWarning
9566
+ pushWarning,
9567
+ result.diagnostics
9358
9568
  );
9359
9569
  attachNode2(node, indent, indentStack, result);
9360
9570
  labelToNode.set(node.label.toLowerCase(), node);
@@ -9417,10 +9627,15 @@ function parseSitemap(content, palette) {
9417
9627
  }
9418
9628
  return result;
9419
9629
  }
9420
- function parseNodeLabel2(trimmed, lineNumber, palette, counter, aliasMap = /* @__PURE__ */ new Map(), warnFn) {
9630
+ function parseNodeLabel2(trimmed, lineNumber, palette, counter, aliasMap = /* @__PURE__ */ new Map(), warnFn, diagnostics) {
9421
9631
  const segments = trimmed.split("|").map((s) => s.trim());
9422
9632
  const rawLabel = segments[0];
9423
- const { label, color } = extractColor(rawLabel, palette);
9633
+ const { label, color } = extractColor(
9634
+ rawLabel,
9635
+ palette,
9636
+ diagnostics,
9637
+ lineNumber
9638
+ );
9424
9639
  const metadata = parsePipeMetadata(
9425
9640
  segments,
9426
9641
  aliasMap,
@@ -9712,9 +9927,13 @@ function parseInfra(content) {
9712
9927
  const tvMatch = cleanEntry.match(TAG_VALUE_RE);
9713
9928
  if (tvMatch) {
9714
9929
  const valueName = tvMatch[1].trim();
9930
+ const rawColor = tvMatch[2]?.trim();
9931
+ if (rawColor) {
9932
+ resolveColorWithDiagnostic(rawColor, lineNumber, result.diagnostics);
9933
+ }
9715
9934
  currentTagGroup.values.push({
9716
9935
  name: valueName,
9717
- color: tvMatch[2]?.trim()
9936
+ color: rawColor
9718
9937
  });
9719
9938
  if (isDefault) {
9720
9939
  currentTagGroup.defaultValue = valueName;
@@ -10066,6 +10285,7 @@ var init_parser8 = __esm({
10066
10285
  "src/infra/parser.ts"() {
10067
10286
  "use strict";
10068
10287
  init_diagnostics();
10288
+ init_colors();
10069
10289
  init_parsing();
10070
10290
  init_tag_groups();
10071
10291
  init_types();
@@ -10127,9 +10347,11 @@ function buildHolidaySet(holidays) {
10127
10347
  function addBusinessDays(startDate, count, workweek, holidaySet, direction = 1) {
10128
10348
  const days = Math.round(Math.abs(count));
10129
10349
  if (days === 0) return new Date(startDate);
10350
+ if (Number.isNaN(startDate.getTime())) return new Date(startDate);
10130
10351
  const current = new Date(startDate);
10131
10352
  let remaining = days;
10132
- while (remaining > 0) {
10353
+ let safety = days * 14 + 14;
10354
+ while (remaining > 0 && safety-- > 0) {
10133
10355
  current.setDate(current.getDate() + direction);
10134
10356
  if (isWorkday(current, workweek, holidaySet)) {
10135
10357
  remaining--;
@@ -14234,6 +14456,7 @@ var init_mutations = __esm({
14234
14456
  // src/kanban/renderer.ts
14235
14457
  var renderer_exports3 = {};
14236
14458
  __export(renderer_exports3, {
14459
+ bucketCardsBySwimlane: () => bucketCardsBySwimlane,
14237
14460
  renderKanban: () => renderKanban,
14238
14461
  renderKanbanForExport: () => renderKanbanForExport
14239
14462
  });
@@ -14328,7 +14551,14 @@ function computeLayout(parsed, _palette) {
14328
14551
  const totalHeight = startY + maxColumnHeight + DIAGRAM_PADDING3;
14329
14552
  return { columns: columnLayouts, totalWidth, totalHeight };
14330
14553
  }
14331
- function renderKanban(container, parsed, palette, isDark, _onNavigateToLine, exportDims, activeTagGroup) {
14554
+ function renderKanban(container, parsed, palette, isDark, options) {
14555
+ const exportDims = options?.exportDims;
14556
+ const activeTagGroup = options?.activeTagGroup ?? null;
14557
+ const onSwimlaneChange = options?.onSwimlaneChange;
14558
+ const requestedSwimlane = options?.currentSwimlaneGroup ?? null;
14559
+ const swimlaneGroup = requestedSwimlane ? parsed.tagGroups.find(
14560
+ (g) => g.name.toLowerCase() === requestedSwimlane.toLowerCase()
14561
+ ) ?? null : null;
14332
14562
  const layout = computeLayout(parsed, palette);
14333
14563
  const width = exportDims?.width ?? layout.totalWidth;
14334
14564
  const height = exportDims?.height ?? layout.totalHeight;
@@ -14348,16 +14578,49 @@ function renderKanban(container, parsed, palette, isDark, _onNavigateToLine, exp
14348
14578
  };
14349
14579
  const legendState = { activeGroup: activeTagGroup ?? null };
14350
14580
  const legendG = svg.append("g").attr("class", "kanban-legend").attr("transform", `translate(${legendX},${legendY})`);
14581
+ const showSwimlaneIcon = !!onSwimlaneChange && !exportDims;
14582
+ const legendCallbacks = showSwimlaneIcon ? {
14583
+ onGroupRendered: (groupName, groupEl, isActive) => {
14584
+ const isCurrent = swimlaneGroup?.name.toLowerCase() === groupName.toLowerCase();
14585
+ const textW = measureLegendText(groupName, 11);
14586
+ const iconX = isActive ? 4 + 6 + textW + 6 : 8 + textW + 4;
14587
+ const iconY = (LEGEND_HEIGHT - 7) / 2;
14588
+ const iconEl = drawSwimlaneIcon(
14589
+ groupEl,
14590
+ iconX,
14591
+ iconY,
14592
+ isCurrent,
14593
+ palette
14594
+ );
14595
+ iconEl.append("title").text(`Group by ${groupName}`);
14596
+ iconEl.style("cursor", "pointer").on("click", (event) => {
14597
+ event.stopPropagation();
14598
+ onSwimlaneChange?.(isCurrent ? null : groupName);
14599
+ });
14600
+ }
14601
+ } : void 0;
14351
14602
  renderLegendD3(
14352
14603
  legendG,
14353
14604
  legendConfig,
14354
14605
  legendState,
14355
14606
  palette,
14356
14607
  isDark,
14357
- void 0,
14608
+ legendCallbacks,
14358
14609
  width - legendX - DIAGRAM_PADDING3
14359
14610
  );
14360
14611
  }
14612
+ if (swimlaneGroup) {
14613
+ renderSwimlaneBoard(
14614
+ svg,
14615
+ parsed,
14616
+ layout,
14617
+ swimlaneGroup,
14618
+ palette,
14619
+ isDark,
14620
+ activeTagGroup
14621
+ );
14622
+ return;
14623
+ }
14361
14624
  const defaultColBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
14362
14625
  const defaultColHeaderBg = isDark ? mix(palette.surface, palette.bg, 70) : mix(palette.surface, palette.bg, 50);
14363
14626
  const cardBaseBg = isDark ? palette.surface : palette.bg;
@@ -14434,14 +14697,204 @@ function renderKanbanForExport(content, theme, palette) {
14434
14697
  const isDark = theme === "dark";
14435
14698
  const layout = computeLayout(parsed, palette);
14436
14699
  const container = document.createElement("div");
14437
- renderKanban(container, parsed, palette, isDark, void 0, {
14438
- width: layout.totalWidth,
14439
- height: layout.totalHeight
14700
+ renderKanban(container, parsed, palette, isDark, {
14701
+ exportDims: { width: layout.totalWidth, height: layout.totalHeight }
14440
14702
  });
14441
14703
  const svgEl = container.querySelector("svg");
14442
14704
  return svgEl?.outerHTML ?? "";
14443
14705
  }
14444
- var d3Selection3, DIAGRAM_PADDING3, COLUMN_GAP, COLUMN_HEADER_HEIGHT, COLUMN_PADDING, COLUMN_MIN_WIDTH, CARD_HEADER_HEIGHT, CARD_META_LINE_HEIGHT, CARD_SEPARATOR_GAP, CARD_GAP, CARD_RADIUS3, CARD_PADDING_X, CARD_PADDING_Y, CARD_STROKE_WIDTH, TITLE_HEIGHT3, COLUMN_HEADER_FONT_SIZE, CARD_TITLE_FONT_SIZE, CARD_META_FONT_SIZE, WIP_FONT_SIZE, COLUMN_RADIUS, COLUMN_HEADER_RADIUS;
14706
+ function drawSwimlaneIcon(parent, x, y, isActive, palette) {
14707
+ const iconG = parent.append("g").attr("class", "kanban-swimlane-icon").attr("transform", `translate(${x}, ${y})`);
14708
+ const color = isActive ? palette.primary : palette.textMuted;
14709
+ const opacity = isActive ? 1 : 0.35;
14710
+ const barWidths = [8, 12, 6];
14711
+ const barH = 2;
14712
+ const gap = 3;
14713
+ for (let i = 0; i < barWidths.length; i++) {
14714
+ iconG.append("rect").attr("x", 0).attr("y", i * gap).attr("width", barWidths[i]).attr("height", barH).attr("rx", 1).attr("fill", color).attr("opacity", opacity);
14715
+ }
14716
+ return iconG;
14717
+ }
14718
+ function bucketCardsBySwimlane(columns, swimlaneGroup) {
14719
+ const tagKey = swimlaneGroup.name.toLowerCase();
14720
+ const buckets = /* @__PURE__ */ new Map();
14721
+ for (const entry of swimlaneGroup.entries) {
14722
+ buckets.set(entry.value.toLowerCase(), {
14723
+ laneName: entry.value,
14724
+ laneColor: entry.color,
14725
+ isFallback: false,
14726
+ cellsByColumn: {}
14727
+ });
14728
+ }
14729
+ const fallbackKey = `__no_${tagKey}__`;
14730
+ for (const col of columns) {
14731
+ for (const card of col.cards) {
14732
+ const raw = card.tags[tagKey] ?? swimlaneGroup.defaultValue;
14733
+ let bucketKey;
14734
+ if (raw && buckets.has(raw.toLowerCase())) {
14735
+ bucketKey = raw.toLowerCase();
14736
+ } else {
14737
+ if (!buckets.has(fallbackKey)) {
14738
+ buckets.set(fallbackKey, {
14739
+ laneName: `No ${swimlaneGroup.name}`,
14740
+ isFallback: true,
14741
+ cellsByColumn: {}
14742
+ });
14743
+ }
14744
+ bucketKey = fallbackKey;
14745
+ }
14746
+ const bucket = buckets.get(bucketKey);
14747
+ (bucket.cellsByColumn[col.id] ??= []).push(card);
14748
+ }
14749
+ }
14750
+ return Array.from(buckets.values());
14751
+ }
14752
+ function computeCardHeight3(card, tagGroups) {
14753
+ const tagMeta = resolveCardTagMeta(card, tagGroups);
14754
+ const metaCount = tagMeta.length + card.details.length;
14755
+ const metaHeight = metaCount > 0 ? CARD_SEPARATOR_GAP + 1 + CARD_PADDING_Y + metaCount * CARD_META_LINE_HEIGHT : 0;
14756
+ return CARD_HEADER_HEIGHT + CARD_PADDING_Y + metaHeight;
14757
+ }
14758
+ function computeSwimlaneLayout(parsed, buckets, baseLayout) {
14759
+ const headerHeight = parsed.title ? TITLE_HEIGHT3 + 8 : 0;
14760
+ const startY = DIAGRAM_PADDING3 + headerHeight;
14761
+ const columnXs = [];
14762
+ let currentX = DIAGRAM_PADDING3 + LANE_HEADER_WIDTH;
14763
+ for (const col of baseLayout.columns) {
14764
+ columnXs.push({ column: col.column, x: currentX, width: col.width });
14765
+ currentX += col.width + COLUMN_GAP;
14766
+ }
14767
+ const totalWidth = currentX - COLUMN_GAP + DIAGRAM_PADDING3;
14768
+ const lanes = [];
14769
+ let laneY = startY + COLUMN_HEADER_HEIGHT + COLUMN_PADDING;
14770
+ const minCellH = CARD_HEADER_HEIGHT + CARD_PADDING_Y + CARD_GAP;
14771
+ for (const bucket of buckets) {
14772
+ let maxCellH = minCellH;
14773
+ const cellsTmp = [];
14774
+ for (const colInfo of columnXs) {
14775
+ const cards = bucket.cellsByColumn[colInfo.column.id] ?? [];
14776
+ let h = 0;
14777
+ for (const c of cards) {
14778
+ h += computeCardHeight3(c, parsed.tagGroups) + CARD_GAP;
14779
+ }
14780
+ h = Math.max(h - (cards.length > 0 ? CARD_GAP : 0), 0);
14781
+ cellsTmp.push({ column: colInfo.column, cards, h });
14782
+ if (h > maxCellH) maxCellH = h;
14783
+ }
14784
+ const laneHeight = Math.max(maxCellH, minCellH);
14785
+ const cells = cellsTmp.map((tmp, i) => {
14786
+ const colInfo = columnXs[i];
14787
+ const cardLayouts = [];
14788
+ let cy = 0;
14789
+ for (const card of tmp.cards) {
14790
+ const ch = computeCardHeight3(card, parsed.tagGroups);
14791
+ cardLayouts.push({
14792
+ x: colInfo.x + COLUMN_PADDING,
14793
+ y: laneY + cy,
14794
+ width: colInfo.width - COLUMN_PADDING * 2,
14795
+ height: ch,
14796
+ card
14797
+ });
14798
+ cy += ch + CARD_GAP;
14799
+ }
14800
+ return { column: tmp.column, cards: tmp.cards, cardLayouts };
14801
+ });
14802
+ lanes.push({ bucket, y: laneY, height: laneHeight, cells });
14803
+ laneY += laneHeight + LANE_GAP;
14804
+ }
14805
+ const totalHeight = laneY - LANE_GAP + COLUMN_PADDING + DIAGRAM_PADDING3;
14806
+ return { columnXs, lanes, totalWidth, totalHeight, startY };
14807
+ }
14808
+ function renderSwimlaneBoard(svg, parsed, baseLayout, swimlaneGroup, palette, isDark, activeTagGroup) {
14809
+ const visibleColumns = parsed.columns.filter((c) => !isArchiveColumn(c.name));
14810
+ const buckets = bucketCardsBySwimlane(visibleColumns, swimlaneGroup);
14811
+ const grid = computeSwimlaneLayout(parsed, buckets, baseLayout);
14812
+ const currentW = parseFloat(svg.attr("width") || "0");
14813
+ const currentH = parseFloat(svg.attr("height") || "0");
14814
+ if (grid.totalWidth > currentW) svg.attr("width", grid.totalWidth);
14815
+ if (grid.totalHeight > currentH) svg.attr("height", grid.totalHeight);
14816
+ const defaultColBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
14817
+ const defaultColHeaderBg = isDark ? mix(palette.surface, palette.bg, 70) : mix(palette.surface, palette.bg, 50);
14818
+ const cardBaseBg = isDark ? palette.surface : palette.bg;
14819
+ for (const colInfo of grid.columnXs) {
14820
+ const col = colInfo.column;
14821
+ const headerG = svg.append("g").attr("class", "kanban-column kanban-column-header").attr("data-column-id", col.id).attr("data-line-number", col.lineNumber);
14822
+ const colHeaderBg = col.color ? mix(col.color, palette.bg, 25) : defaultColHeaderBg;
14823
+ headerG.append("rect").attr("x", colInfo.x).attr("y", grid.startY).attr("width", colInfo.width).attr("height", COLUMN_HEADER_HEIGHT).attr("rx", COLUMN_HEADER_RADIUS).attr("fill", colHeaderBg);
14824
+ headerG.append("text").attr("x", colInfo.x + COLUMN_PADDING).attr(
14825
+ "y",
14826
+ grid.startY + COLUMN_HEADER_HEIGHT / 2 + COLUMN_HEADER_FONT_SIZE / 2 - 2
14827
+ ).attr("font-size", COLUMN_HEADER_FONT_SIZE).attr("font-weight", "bold").attr("fill", palette.text).text(col.name);
14828
+ }
14829
+ for (const lane of grid.lanes) {
14830
+ const laneG = svg.append("g").attr("class", "kanban-lane").attr("data-lane-name", lane.bucket.laneName);
14831
+ const headerG = laneG.append("g").attr("class", "kanban-lane-header").attr(
14832
+ "transform",
14833
+ `translate(${DIAGRAM_PADDING3}, ${lane.y - COLUMN_PADDING})`
14834
+ );
14835
+ headerG.append("rect").attr("x", 0).attr("y", 0).attr("width", LANE_HEADER_WIDTH - 8).attr("height", lane.height + COLUMN_PADDING * 2).attr("rx", COLUMN_RADIUS).attr("fill", defaultColBg);
14836
+ let labelX = 10;
14837
+ if (lane.bucket.laneColor) {
14838
+ headerG.append("circle").attr("cx", labelX + 4).attr("cy", 16).attr("r", 4).attr("fill", lane.bucket.laneColor);
14839
+ labelX += 14;
14840
+ }
14841
+ headerG.append("text").attr("x", labelX).attr("y", 20).attr("font-size", 12).attr("font-weight", "bold").attr("fill", lane.bucket.isFallback ? palette.textMuted : palette.text).text(lane.bucket.laneName);
14842
+ const totalCards = lane.cells.reduce((s, c) => s + c.cards.length, 0);
14843
+ headerG.append("text").attr("x", labelX).attr("y", 36).attr("font-size", 10).attr("fill", palette.textMuted).text(`(${totalCards})`);
14844
+ laneG.append("line").attr("x1", DIAGRAM_PADDING3 + LANE_HEADER_WIDTH).attr("x2", grid.totalWidth - DIAGRAM_PADDING3).attr("y1", lane.y + lane.height + COLUMN_PADDING).attr("y2", lane.y + lane.height + COLUMN_PADDING).attr("stroke", palette.border ?? palette.textMuted).attr("stroke-opacity", 0.4).attr("stroke-width", 1);
14845
+ for (const cell of lane.cells) {
14846
+ for (const cardLayout of cell.cardLayouts) {
14847
+ renderSwimlaneCard(
14848
+ laneG,
14849
+ cardLayout,
14850
+ parsed.tagGroups,
14851
+ activeTagGroup,
14852
+ palette,
14853
+ cardBaseBg
14854
+ );
14855
+ }
14856
+ }
14857
+ }
14858
+ }
14859
+ function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palette, cardBaseBg) {
14860
+ const card = cardLayout.card;
14861
+ const resolvedColor = resolveCardTagColor(card, tagGroups, activeTagGroup);
14862
+ const tagMeta = resolveCardTagMeta(card, tagGroups);
14863
+ const hasMeta = tagMeta.length > 0 || card.details.length > 0;
14864
+ const cardFill = resolvedColor ? mix(resolvedColor, cardBaseBg, 15) : mix(palette.primary, cardBaseBg, 15);
14865
+ const cardStroke = resolvedColor ?? palette.textMuted;
14866
+ const cg = parent.append("g").attr("class", "kanban-card").attr("data-card-id", card.id).attr("data-line-number", card.lineNumber);
14867
+ if (activeTagGroup) {
14868
+ const tagKey = activeTagGroup.toLowerCase();
14869
+ const group = tagGroups.find((tg) => tg.name.toLowerCase() === tagKey);
14870
+ const value = card.tags[tagKey] ?? group?.defaultValue;
14871
+ if (value) {
14872
+ cg.attr(`data-tag-${tagKey}`, value.toLowerCase());
14873
+ }
14874
+ }
14875
+ const cx = cardLayout.x;
14876
+ const cy = cardLayout.y;
14877
+ cg.append("rect").attr("x", cx).attr("y", cy).attr("width", cardLayout.width).attr("height", cardLayout.height).attr("rx", CARD_RADIUS3).attr("fill", cardFill).attr("stroke", cardStroke).attr("stroke-width", CARD_STROKE_WIDTH);
14878
+ const titleEl = cg.append("text").attr("x", cx + CARD_PADDING_X).attr("y", cy + CARD_PADDING_Y + CARD_TITLE_FONT_SIZE).attr("font-size", CARD_TITLE_FONT_SIZE).attr("font-weight", "500").attr("fill", palette.text);
14879
+ renderInlineText(titleEl, card.title, palette, CARD_TITLE_FONT_SIZE);
14880
+ if (hasMeta) {
14881
+ const separatorY = cy + CARD_HEADER_HEIGHT;
14882
+ cg.append("line").attr("x1", cx).attr("y1", separatorY).attr("x2", cx + cardLayout.width).attr("y2", separatorY).attr("stroke", cardStroke).attr("stroke-opacity", 0.3).attr("stroke-width", 1);
14883
+ let metaY = separatorY + CARD_SEPARATOR_GAP + CARD_META_FONT_SIZE;
14884
+ for (const meta of tagMeta) {
14885
+ cg.append("text").attr("x", cx + CARD_PADDING_X).attr("y", metaY).attr("font-size", CARD_META_FONT_SIZE).attr("fill", palette.textMuted).text(`${meta.label}: `);
14886
+ const labelWidth = (meta.label.length + 2) * CARD_META_FONT_SIZE * 0.6;
14887
+ cg.append("text").attr("x", cx + CARD_PADDING_X + labelWidth).attr("y", metaY).attr("font-size", CARD_META_FONT_SIZE).attr("fill", palette.text).text(meta.value);
14888
+ metaY += CARD_META_LINE_HEIGHT;
14889
+ }
14890
+ for (const detail of card.details) {
14891
+ const detailEl = cg.append("text").attr("x", cx + CARD_PADDING_X).attr("y", metaY).attr("font-size", CARD_META_FONT_SIZE).attr("fill", palette.textMuted);
14892
+ renderInlineText(detailEl, detail, palette, CARD_META_FONT_SIZE);
14893
+ metaY += CARD_META_LINE_HEIGHT;
14894
+ }
14895
+ }
14896
+ }
14897
+ var d3Selection3, DIAGRAM_PADDING3, COLUMN_GAP, COLUMN_HEADER_HEIGHT, COLUMN_PADDING, COLUMN_MIN_WIDTH, CARD_HEADER_HEIGHT, CARD_META_LINE_HEIGHT, CARD_SEPARATOR_GAP, CARD_GAP, CARD_RADIUS3, CARD_PADDING_X, CARD_PADDING_Y, CARD_STROKE_WIDTH, TITLE_HEIGHT3, COLUMN_HEADER_FONT_SIZE, CARD_TITLE_FONT_SIZE, CARD_META_FONT_SIZE, WIP_FONT_SIZE, COLUMN_RADIUS, COLUMN_HEADER_RADIUS, LANE_HEADER_WIDTH, LANE_GAP;
14445
14898
  var init_renderer3 = __esm({
14446
14899
  "src/kanban/renderer.ts"() {
14447
14900
  "use strict";
@@ -14474,6 +14927,8 @@ var init_renderer3 = __esm({
14474
14927
  WIP_FONT_SIZE = 10;
14475
14928
  COLUMN_RADIUS = 8;
14476
14929
  COLUMN_HEADER_RADIUS = 8;
14930
+ LANE_HEADER_WIDTH = 140;
14931
+ LANE_GAP = 14;
14477
14932
  }
14478
14933
  });
14479
14934
 
@@ -23616,7 +24071,7 @@ function resetHighlightAll(svg, chartG) {
23616
24071
  chartG.selectAll(".gantt-lane-band, .gantt-lane-accent").attr("opacity", 1);
23617
24072
  chartG.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").attr("opacity", 0.5);
23618
24073
  }
23619
- function drawSwimlaneIcon(parent, x, y, isActive, palette) {
24074
+ function drawSwimlaneIcon2(parent, x, y, isActive, palette) {
23620
24075
  const iconG = parent.append("g").attr("class", "gantt-swimlane-icon").attr("transform", `translate(${x}, ${y})`);
23621
24076
  const color = isActive ? palette.primary : palette.textMuted;
23622
24077
  const opacity = isActive ? 1 : 0.35;
@@ -23773,7 +24228,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
23773
24228
  const pillXOff = LEGEND_CAPSULE_PAD;
23774
24229
  const iconX = pillXOff + textW + 3;
23775
24230
  const iconY = (LEGEND_HEIGHT - 10) / 2;
23776
- const iconEl = drawSwimlaneIcon(
24231
+ const iconEl = drawSwimlaneIcon2(
23777
24232
  groupEl,
23778
24233
  iconX,
23779
24234
  iconY,
@@ -25201,10 +25656,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
25201
25656
  );
25202
25657
  if (tg) {
25203
25658
  for (const entry of tg.entries) {
25204
- tagValueToColor.set(
25205
- entry.value.toLowerCase(),
25206
- resolveColor(entry.color) ?? entry.color
25207
- );
25659
+ tagValueToColor.set(entry.value.toLowerCase(), entry.color);
25208
25660
  }
25209
25661
  }
25210
25662
  }
@@ -25610,7 +26062,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
25610
26062
  name: tg.name,
25611
26063
  entries: tg.entries.map((e) => ({
25612
26064
  value: e.value,
25613
- color: resolveColor(e.color) ?? e.color
26065
+ color: e.color
25614
26066
  }))
25615
26067
  }));
25616
26068
  const legendConfig = {
@@ -26191,7 +26643,6 @@ var init_renderer10 = __esm({
26191
26643
  init_color_utils();
26192
26644
  init_inline_markdown();
26193
26645
  init_fonts();
26194
- init_colors();
26195
26646
  init_parser();
26196
26647
  init_tag_resolution();
26197
26648
  init_tag_groups();
@@ -26497,12 +26948,22 @@ function parseVisualization(content, palette) {
26497
26948
  if (groupMatch) {
26498
26949
  if (result.type === "arc") {
26499
26950
  const name = groupMatch[1].trim();
26500
- const color = groupMatch[2] ? resolveColor(groupMatch[2].trim(), palette) : null;
26951
+ const color = groupMatch[2] ? resolveColorWithDiagnostic(
26952
+ groupMatch[2].trim(),
26953
+ lineNumber,
26954
+ result.diagnostics,
26955
+ palette
26956
+ ) ?? null : null;
26501
26957
  result.arcNodeGroups.push({ name, nodes: [], color, lineNumber });
26502
26958
  currentArcGroup = name;
26503
26959
  } else if (result.type === "timeline") {
26504
26960
  const name = groupMatch[1].trim();
26505
- const color = groupMatch[2] ? resolveColor(groupMatch[2].trim(), palette) : null;
26961
+ const color = groupMatch[2] ? resolveColorWithDiagnostic(
26962
+ groupMatch[2].trim(),
26963
+ lineNumber,
26964
+ result.diagnostics,
26965
+ palette
26966
+ ) ?? null : null;
26506
26967
  result.timelineGroups.push({ name, color, lineNumber });
26507
26968
  currentTimelineGroup = name;
26508
26969
  }
@@ -26530,7 +26991,12 @@ function parseVisualization(content, palette) {
26530
26991
  if (linkMatch) {
26531
26992
  const source = linkMatch[1].trim();
26532
26993
  const target = linkMatch[2].trim();
26533
- const linkColor = linkMatch[3] ? resolveColor(linkMatch[3].trim(), palette) : null;
26994
+ const linkColor = linkMatch[3] ? resolveColorWithDiagnostic(
26995
+ linkMatch[3].trim(),
26996
+ lineNumber,
26997
+ result.diagnostics,
26998
+ palette
26999
+ ) ?? null : null;
26534
27000
  result.links.push({
26535
27001
  source,
26536
27002
  target,
@@ -26567,7 +27033,12 @@ function parseVisualization(content, palette) {
26567
27033
  startDate: eraEntryMatch[1],
26568
27034
  endDate: eraEntryMatch[2],
26569
27035
  label: eraEntryMatch[3].trim(),
26570
- color: colorAnnotation ? resolveColor(colorAnnotation, palette) : null,
27036
+ color: colorAnnotation ? resolveColorWithDiagnostic(
27037
+ colorAnnotation,
27038
+ lineNumber,
27039
+ result.diagnostics,
27040
+ palette
27041
+ ) ?? null : null,
26571
27042
  lineNumber
26572
27043
  });
26573
27044
  } else {
@@ -26589,7 +27060,12 @@ function parseVisualization(content, palette) {
26589
27060
  result.timelineMarkers.push({
26590
27061
  date: markerEntryMatch[1],
26591
27062
  label: markerEntryMatch[2].trim(),
26592
- color: colorAnnotation ? resolveColor(colorAnnotation, palette) : null,
27063
+ color: colorAnnotation ? resolveColorWithDiagnostic(
27064
+ colorAnnotation,
27065
+ lineNumber,
27066
+ result.diagnostics,
27067
+ palette
27068
+ ) ?? null : null,
26593
27069
  lineNumber
26594
27070
  });
26595
27071
  } else {
@@ -26618,7 +27094,12 @@ function parseVisualization(content, palette) {
26618
27094
  startDate: eraMatch[1],
26619
27095
  endDate: eraMatch[2],
26620
27096
  label: eraMatch[3].trim(),
26621
- color: colorAnnotation ? resolveColor(colorAnnotation, palette) : null,
27097
+ color: colorAnnotation ? resolveColorWithDiagnostic(
27098
+ colorAnnotation,
27099
+ lineNumber,
27100
+ result.diagnostics,
27101
+ palette
27102
+ ) ?? null : null,
26622
27103
  lineNumber
26623
27104
  });
26624
27105
  continue;
@@ -26631,7 +27112,12 @@ function parseVisualization(content, palette) {
26631
27112
  result.timelineMarkers.push({
26632
27113
  date: markerMatch[1],
26633
27114
  label: markerMatch[2].trim(),
26634
- color: colorAnnotation ? resolveColor(colorAnnotation, palette) : null,
27115
+ color: colorAnnotation ? resolveColorWithDiagnostic(
27116
+ colorAnnotation,
27117
+ lineNumber,
27118
+ result.diagnostics,
27119
+ palette
27120
+ ) ?? null : null,
26635
27121
  lineNumber
26636
27122
  });
26637
27123
  continue;
@@ -26746,20 +27232,12 @@ function parseVisualization(content, palette) {
26746
27232
  const colorName = setDeclMatch[2]?.trim() ?? null;
26747
27233
  let color = null;
26748
27234
  if (colorName) {
26749
- const resolved = resolveColor(colorName, palette);
26750
- if (resolved === null) {
26751
- warn(
26752
- lineNumber,
26753
- `Hex colors are not supported \u2014 use named colors (blue, red, green, etc.)`
26754
- );
26755
- } else if (resolved.startsWith("#")) {
26756
- color = resolved;
26757
- } else {
26758
- warn(
26759
- lineNumber,
26760
- `Unknown color "${colorName}" on set "${name}". Using auto-assigned color.`
26761
- );
26762
- }
27235
+ color = resolveColorWithDiagnostic(
27236
+ colorName,
27237
+ lineNumber,
27238
+ result.diagnostics,
27239
+ palette
27240
+ ) ?? null;
26763
27241
  }
26764
27242
  const alias = setDeclMatch[3]?.trim() ?? null;
26765
27243
  result.vennSets.push({ name, alias, color, lineNumber });
@@ -26808,7 +27286,12 @@ function parseVisualization(content, palette) {
26808
27286
  const labelPart = quadrantMatch[2].trim();
26809
27287
  const labelColorMatch = labelPart.match(/^(.+?)\s*\(([^)]+)\)\s*$/);
26810
27288
  const text = labelColorMatch ? labelColorMatch[1].trim() : labelPart;
26811
- const color = labelColorMatch ? resolveColor(labelColorMatch[2].trim(), palette) : null;
27289
+ const color = labelColorMatch ? resolveColorWithDiagnostic(
27290
+ labelColorMatch[2].trim(),
27291
+ lineNumber,
27292
+ result.diagnostics,
27293
+ palette
27294
+ ) ?? null : null;
26812
27295
  const label = { text, color, lineNumber };
26813
27296
  if (position === "top-right") result.quadrantLabels.topRight = label;
26814
27297
  else if (position === "top-left") result.quadrantLabels.topLeft = label;
@@ -26966,7 +27449,12 @@ function parseVisualization(content, palette) {
26966
27449
  }
26967
27450
  const colorMatch = joinedLabel.match(/^(.+?)\(([^)]+)\)\s*$/);
26968
27451
  const labelPart = colorMatch ? colorMatch[1].trim() : joinedLabel;
26969
- const colorPart = colorMatch ? resolveColor(colorMatch[2].trim(), palette) : null;
27452
+ const colorPart = colorMatch ? resolveColorWithDiagnostic(
27453
+ colorMatch[2].trim(),
27454
+ lineNumber,
27455
+ result.diagnostics,
27456
+ palette
27457
+ ) ?? null : null;
26970
27458
  if (!labelPart) {
26971
27459
  warn(
26972
27460
  lineNumber,
@@ -27037,7 +27525,12 @@ function parseVisualization(content, palette) {
27037
27525
  continue;
27038
27526
  }
27039
27527
  const labelPart = colorMatch ? colorMatch[1].trim() : rawKey;
27040
- const colorPart = colorMatch ? resolveColor(colorMatch[2].trim(), palette) : null;
27528
+ const colorPart = colorMatch ? resolveColorWithDiagnostic(
27529
+ colorMatch[2].trim(),
27530
+ lineNumber,
27531
+ result.diagnostics,
27532
+ palette
27533
+ ) ?? null : null;
27041
27534
  const valuePart = line10.substring(colonIndex + 1).trim();
27042
27535
  const values = valuePart.split(",").map((v) => v.trim());
27043
27536
  const numericValues = [];
@@ -28885,7 +29378,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
28885
29378
  const mainSvg = d3Selection13.select(container).select("svg");
28886
29379
  const mainG = mainSvg.select("g");
28887
29380
  if (!mainSvg.empty() && !mainG.empty()) {
28888
- let drawSwimlaneIcon3 = function(parent, x, y, isSwimActive) {
29381
+ let drawSwimlaneIcon4 = function(parent, x, y, isSwimActive) {
28889
29382
  const iconG = parent.append("g").attr("class", "tl-swimlane-icon").attr("transform", `translate(${x}, ${y})`).style("cursor", "pointer");
28890
29383
  const barColor = isSwimActive ? palette.primary : palette.textMuted;
28891
29384
  const barOpacity = isSwimActive ? 1 : 0.35;
@@ -28977,7 +29470,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
28977
29470
  const pillXOff = LG_CAPSULE_PAD;
28978
29471
  const iconX = pillXOff + pillWidth3 + 5;
28979
29472
  const iconY = (LG_HEIGHT - 10) / 2;
28980
- const iconEl = drawSwimlaneIcon3(
29473
+ const iconEl = drawSwimlaneIcon4(
28981
29474
  groupEl,
28982
29475
  iconX,
28983
29476
  iconY,
@@ -29027,7 +29520,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
29027
29520
  el.selectAll("circle:not(.tl-event-point-outline)").attr("fill", mix(color, bg, 30)).attr("stroke", color);
29028
29521
  });
29029
29522
  };
29030
- var drawSwimlaneIcon2 = drawSwimlaneIcon3, relayout = relayout2, drawLegend = drawLegend2, recolorEvents = recolorEvents2;
29523
+ var drawSwimlaneIcon3 = drawSwimlaneIcon4, relayout = relayout2, drawLegend = drawLegend2, recolorEvents = recolorEvents2;
29031
29524
  const legendY = title ? 50 : 10;
29032
29525
  const legendGroups = parsed.timelineTagGroups.map((g) => {
29033
29526
  const pillW = measureLegendText(g.name, LG_PILL_FONT_SIZE) + LG_PILL_PAD;
@@ -29987,19 +30480,13 @@ async function renderForExport(content, theme, palette, orgExportState, options)
29987
30480
  container2.style.position = "absolute";
29988
30481
  container2.style.left = "-9999px";
29989
30482
  document.body.appendChild(container2);
29990
- renderKanban2(
29991
- container2,
29992
- kanbanParsed,
29993
- effectivePalette2,
29994
- theme === "dark",
29995
- void 0,
29996
- void 0,
29997
- resolveActiveTagGroup(
30483
+ renderKanban2(container2, kanbanParsed, effectivePalette2, theme === "dark", {
30484
+ activeTagGroup: resolveActiveTagGroup(
29998
30485
  kanbanParsed.tagGroups,
29999
30486
  kanbanParsed.options["active-tag"],
30000
30487
  options?.tagGroup
30001
30488
  )
30002
- );
30489
+ });
30003
30490
  return finalizeSvgExport(container2, theme, effectivePalette2, options);
30004
30491
  }
30005
30492
  if (detectedType === "class") {
@@ -30491,6 +30978,7 @@ __export(index_exports, {
30491
30978
  LEGEND_HEIGHT: () => LEGEND_HEIGHT,
30492
30979
  METADATA_KEY_SET: () => METADATA_KEY_SET,
30493
30980
  PIPE_METADATA: () => PIPE_METADATA,
30981
+ RECOGNIZED_COLOR_NAMES: () => RECOGNIZED_COLOR_NAMES,
30494
30982
  RULE_COUNT: () => RULE_COUNT,
30495
30983
  addDurationToDate: () => addDurationToDate,
30496
30984
  applyGroupOrdering: () => applyGroupOrdering,
@@ -30545,6 +31033,7 @@ __export(index_exports, {
30545
31033
  injectBranding: () => injectBranding,
30546
31034
  isArchiveColumn: () => isArchiveColumn,
30547
31035
  isExtendedChartType: () => isExtendedChartType,
31036
+ isRecognizedColorName: () => isRecognizedColorName,
30548
31037
  isSequenceBlock: () => isSequenceBlock,
30549
31038
  isSequenceNote: () => isSequenceNote,
30550
31039
  isValidHex: () => isValidHex,
@@ -30636,6 +31125,7 @@ __export(index_exports, {
30636
31125
  renderVenn: () => renderVenn,
30637
31126
  renderWordCloud: () => renderWordCloud,
30638
31127
  resolveColor: () => resolveColor,
31128
+ resolveColorWithDiagnostic: () => resolveColorWithDiagnostic,
30639
31129
  resolveOrgImports: () => resolveOrgImports,
30640
31130
  resolveTaskName: () => resolveTaskName,
30641
31131
  rollUpContextRelationships: () => rollUpContextRelationships,
@@ -30785,7 +31275,11 @@ function parseQuadrant(content) {
30785
31275
  if (labelMatch) {
30786
31276
  const label = {
30787
31277
  text: labelMatch[1].trim(),
30788
- color: labelMatch[2] ? resolveColor(labelMatch[2].trim()) : null,
31278
+ color: labelMatch[2] ? resolveColorWithDiagnostic(
31279
+ labelMatch[2].trim(),
31280
+ lineNumber,
31281
+ result.diagnostics
31282
+ ) ?? null : null,
30789
31283
  lineNumber
30790
31284
  };
30791
31285
  if (position === "top-right") result.quadrants.topRight = label;
@@ -31465,10 +31959,14 @@ function encodeDiagramUrl(dsl, options) {
31465
31959
  if (options?.viewState?.theme && options.viewState.theme !== "dark") {
31466
31960
  hash += `&th=${encodeURIComponent(options.viewState.theme)}`;
31467
31961
  }
31962
+ if (options?.filename) {
31963
+ hash += `&fn=${encodeURIComponent(options.filename)}`;
31964
+ }
31468
31965
  return { url: `${baseUrl}?${hash}#${hash}` };
31469
31966
  }
31470
31967
  function decodeDiagramUrl(hash) {
31471
31968
  const empty = { dsl: "", viewState: {} };
31969
+ let filename;
31472
31970
  if (!hash) return empty;
31473
31971
  let raw = hash;
31474
31972
  if (raw.startsWith("#") || raw.startsWith("?")) {
@@ -31497,16 +31995,17 @@ function decodeDiagramUrl(hash) {
31497
31995
  if (key === "pal" && val) viewState.palette = val;
31498
31996
  if (key === "th" && (val === "light" || val === "dark"))
31499
31997
  viewState.theme = val;
31998
+ if (key === "fn" && val) filename = val;
31500
31999
  }
31501
32000
  if (payload.startsWith("dgmo=")) {
31502
32001
  payload = payload.slice(5);
31503
32002
  }
31504
- if (!payload) return { dsl: "", viewState };
32003
+ if (!payload) return { dsl: "", viewState, filename };
31505
32004
  try {
31506
32005
  const result = decompressFromEncodedURIComponent(payload);
31507
- return { dsl: result ?? "", viewState };
32006
+ return { dsl: result ?? "", viewState, filename };
31508
32007
  } catch {
31509
- return { dsl: "", viewState };
32008
+ return { dsl: "", viewState, filename };
31510
32009
  }
31511
32010
  }
31512
32011
 
@@ -32264,6 +32763,7 @@ init_branding();
32264
32763
  LEGEND_HEIGHT,
32265
32764
  METADATA_KEY_SET,
32266
32765
  PIPE_METADATA,
32766
+ RECOGNIZED_COLOR_NAMES,
32267
32767
  RULE_COUNT,
32268
32768
  addDurationToDate,
32269
32769
  applyGroupOrdering,
@@ -32318,6 +32818,7 @@ init_branding();
32318
32818
  injectBranding,
32319
32819
  isArchiveColumn,
32320
32820
  isExtendedChartType,
32821
+ isRecognizedColorName,
32321
32822
  isSequenceBlock,
32322
32823
  isSequenceNote,
32323
32824
  isValidHex,
@@ -32409,6 +32910,7 @@ init_branding();
32409
32910
  renderVenn,
32410
32911
  renderWordCloud,
32411
32912
  resolveColor,
32913
+ resolveColorWithDiagnostic,
32412
32914
  resolveOrgImports,
32413
32915
  resolveTaskName,
32414
32916
  rollUpContextRelationships,