@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.js CHANGED
@@ -300,20 +300,39 @@ var init_label_layout = __esm({
300
300
  });
301
301
 
302
302
  // src/colors.ts
303
+ function isRecognizedColorName(name) {
304
+ return Object.prototype.hasOwnProperty.call(colorNames, name.toLowerCase());
305
+ }
303
306
  function resolveColor(color, palette) {
307
+ if (!color) return null;
304
308
  if (color.startsWith("#")) return null;
305
309
  const lower = color.toLowerCase();
310
+ if (!isRecognizedColorName(lower)) return null;
306
311
  if (palette) {
307
312
  const named = palette.colors[lower];
308
313
  if (named) return named;
309
314
  }
310
- if (colorNames[lower]) return colorNames[lower];
311
- return color;
315
+ return colorNames[lower];
316
+ }
317
+ function resolveColorWithDiagnostic(color, line10, diagnostics, palette) {
318
+ const resolved = resolveColor(color, palette);
319
+ if (resolved !== null) return resolved;
320
+ const hint = suggest(color, RECOGNIZED_COLOR_NAMES);
321
+ const suggestion = hint ? ` ${hint}` : "";
322
+ diagnostics.push(
323
+ makeDgmoError(
324
+ line10,
325
+ `Unknown color "${color}". Allowed: ${RECOGNIZED_COLOR_NAMES.join(", ")}.${suggestion}`,
326
+ "warning"
327
+ )
328
+ );
329
+ return void 0;
312
330
  }
313
- var nord, colorNames, seriesColors;
331
+ var nord, colorNames, RECOGNIZED_COLOR_NAMES, seriesColors;
314
332
  var init_colors = __esm({
315
333
  "src/colors.ts"() {
316
334
  "use strict";
335
+ init_diagnostics();
317
336
  nord = {
318
337
  // Polar Night (dark)
319
338
  nord0: "#2e3440",
@@ -350,9 +369,23 @@ var init_colors = __esm({
350
369
  purple: nord.nord15,
351
370
  teal: nord.nord7,
352
371
  cyan: nord.nord8,
353
- lightblue: nord.nord8,
354
- gray: nord.nord3
372
+ gray: nord.nord3,
373
+ black: nord.nord0,
374
+ white: nord.nord6
355
375
  };
376
+ RECOGNIZED_COLOR_NAMES = Object.freeze([
377
+ "red",
378
+ "orange",
379
+ "yellow",
380
+ "green",
381
+ "blue",
382
+ "purple",
383
+ "teal",
384
+ "cyan",
385
+ "gray",
386
+ "black",
387
+ "white"
388
+ ]);
356
389
  seriesColors = [
357
390
  nord.nord10,
358
391
  // blue
@@ -609,7 +642,9 @@ var init_bold = __esm({
609
642
  purple: "#cc00cc",
610
643
  teal: "#008080",
611
644
  cyan: "#00cccc",
612
- gray: "#808080"
645
+ gray: "#808080",
646
+ black: "#000000",
647
+ white: "#f0f0f0"
613
648
  }
614
649
  },
615
650
  dark: {
@@ -632,7 +667,9 @@ var init_bold = __esm({
632
667
  purple: "#ff00ff",
633
668
  teal: "#00cccc",
634
669
  cyan: "#00ffff",
635
- gray: "#808080"
670
+ gray: "#808080",
671
+ black: "#111111",
672
+ white: "#ffffff"
636
673
  }
637
674
  }
638
675
  };
@@ -679,8 +716,10 @@ var init_catppuccin = __esm({
679
716
  purple: "#8839ef",
680
717
  teal: "#179299",
681
718
  cyan: "#209fb5",
682
- gray: "#9ca0b0"
719
+ gray: "#9ca0b0",
683
720
  // Latte Overlay0
721
+ black: "#4c4f69",
722
+ white: "#e6e9ef"
684
723
  }
685
724
  },
686
725
  dark: {
@@ -713,8 +752,10 @@ var init_catppuccin = __esm({
713
752
  purple: "#cba6f7",
714
753
  teal: "#94e2d5",
715
754
  cyan: "#74c7ec",
716
- gray: "#6c7086"
755
+ gray: "#6c7086",
717
756
  // Mocha Overlay0
757
+ black: "#313244",
758
+ white: "#cdd6f4"
718
759
  }
719
760
  }
720
761
  };
@@ -769,7 +810,9 @@ var init_gruvbox = __esm({
769
810
  // faded aqua
770
811
  cyan: "#427b58",
771
812
  // faded aqua
772
- gray: "#928374"
813
+ gray: "#928374",
814
+ black: "#3c3836",
815
+ white: "#ebdbb2"
773
816
  }
774
817
  },
775
818
  dark: {
@@ -810,7 +853,9 @@ var init_gruvbox = __esm({
810
853
  // bright aqua
811
854
  cyan: "#8ec07c",
812
855
  // bright aqua
813
- gray: "#928374"
856
+ gray: "#928374",
857
+ black: "#3c3836",
858
+ white: "#ebdbb2"
814
859
  }
815
860
  }
816
861
  };
@@ -865,8 +910,10 @@ var init_nord = __esm({
865
910
  // nord7
866
911
  cyan: "#88c0d0",
867
912
  // nord8
868
- gray: "#4c566a"
913
+ gray: "#4c566a",
869
914
  // nord3
915
+ black: "#2e3440",
916
+ white: "#e5e9f0"
870
917
  }
871
918
  },
872
919
  dark: {
@@ -907,8 +954,10 @@ var init_nord = __esm({
907
954
  // nord7
908
955
  cyan: "#88c0d0",
909
956
  // nord8
910
- gray: "#4c566a"
957
+ gray: "#4c566a",
911
958
  // nord3
959
+ black: "#3b4252",
960
+ white: "#eceff4"
912
961
  }
913
962
  }
914
963
  };
@@ -946,7 +995,9 @@ var init_one_dark = __esm({
946
995
  purple: "#a626a4",
947
996
  teal: "#0184bc",
948
997
  cyan: "#0997b3",
949
- gray: "#696c77"
998
+ gray: "#696c77",
999
+ black: "#383a42",
1000
+ white: "#f0f0f0"
950
1001
  }
951
1002
  },
952
1003
  dark: {
@@ -970,7 +1021,9 @@ var init_one_dark = __esm({
970
1021
  purple: "#c678dd",
971
1022
  teal: "#56b6c2",
972
1023
  cyan: "#56b6c2",
973
- gray: "#5c6370"
1024
+ gray: "#5c6370",
1025
+ black: "#21252b",
1026
+ white: "#abb2bf"
974
1027
  }
975
1028
  }
976
1029
  };
@@ -1025,8 +1078,10 @@ var init_rose_pine = __esm({
1025
1078
  // Pine
1026
1079
  cyan: "#56949f",
1027
1080
  // Foam
1028
- gray: "#9893a5"
1081
+ gray: "#9893a5",
1029
1082
  // Muted
1083
+ black: "#575279",
1084
+ white: "#fffaf3"
1030
1085
  }
1031
1086
  },
1032
1087
  dark: {
@@ -1067,8 +1122,10 @@ var init_rose_pine = __esm({
1067
1122
  // Pine
1068
1123
  cyan: "#9ccfd8",
1069
1124
  // Foam
1070
- gray: "#6e6a86"
1125
+ gray: "#6e6a86",
1071
1126
  // Muted
1127
+ black: "#2a273f",
1128
+ white: "#e0def4"
1072
1129
  }
1073
1130
  }
1074
1131
  };
@@ -1116,8 +1173,10 @@ var init_solarized = __esm({
1116
1173
  teal: "#2aa198",
1117
1174
  cyan: "#2aa198",
1118
1175
  // Solarized has no separate cyan — reuse teal
1119
- gray: "#586e75"
1176
+ gray: "#586e75",
1120
1177
  // base01
1178
+ black: "#657b83",
1179
+ white: "#eee8d5"
1121
1180
  }
1122
1181
  },
1123
1182
  dark: {
@@ -1150,8 +1209,10 @@ var init_solarized = __esm({
1150
1209
  purple: "#6c71c4",
1151
1210
  teal: "#2aa198",
1152
1211
  cyan: "#2aa198",
1153
- gray: "#586e75"
1212
+ gray: "#586e75",
1154
1213
  // base01
1214
+ black: "#073642",
1215
+ white: "#839496"
1155
1216
  }
1156
1217
  }
1157
1218
  };
@@ -1198,8 +1259,10 @@ var init_tokyo_night = __esm({
1198
1259
  purple: "#7847bd",
1199
1260
  teal: "#118c74",
1200
1261
  cyan: "#007197",
1201
- gray: "#8990b3"
1262
+ gray: "#8990b3",
1202
1263
  // Day dark3
1264
+ black: "#1a1b26",
1265
+ white: "#d0d5e3"
1203
1266
  }
1204
1267
  },
1205
1268
  dark: {
@@ -1232,8 +1295,10 @@ var init_tokyo_night = __esm({
1232
1295
  purple: "#bb9af7",
1233
1296
  teal: "#1abc9c",
1234
1297
  cyan: "#7dcfff",
1235
- gray: "#565f89"
1298
+ gray: "#565f89",
1236
1299
  // Night comment
1300
+ black: "#292e42",
1301
+ white: "#c0caf5"
1237
1302
  }
1238
1303
  }
1239
1304
  };
@@ -1279,7 +1344,9 @@ var init_dracula = __esm({
1279
1344
  teal: "#5ac8b8",
1280
1345
  // muted cyan toward green
1281
1346
  cyan: "#8be9fd",
1282
- gray: "#6272a4"
1347
+ gray: "#6272a4",
1348
+ black: "#282a36",
1349
+ white: "#f0f0ec"
1283
1350
  }
1284
1351
  },
1285
1352
  dark: {
@@ -1314,7 +1381,9 @@ var init_dracula = __esm({
1314
1381
  teal: "#5ac8b8",
1315
1382
  // muted cyan toward green
1316
1383
  cyan: "#8be9fd",
1317
- gray: "#6272a4"
1384
+ gray: "#6272a4",
1385
+ black: "#343746",
1386
+ white: "#f8f8f2"
1318
1387
  }
1319
1388
  }
1320
1389
  };
@@ -1360,8 +1429,10 @@ var init_monokai = __esm({
1360
1429
  teal: "#4ea8a6",
1361
1430
  // muted from cyan
1362
1431
  cyan: "#66d9ef",
1363
- gray: "#75715e"
1432
+ gray: "#75715e",
1364
1433
  // comment
1434
+ black: "#272822",
1435
+ white: "#f0efe8"
1365
1436
  }
1366
1437
  },
1367
1438
  dark: {
@@ -1394,8 +1465,10 @@ var init_monokai = __esm({
1394
1465
  teal: "#4ea8a6",
1395
1466
  // muted from cyan
1396
1467
  cyan: "#66d9ef",
1397
- gray: "#75715e"
1468
+ gray: "#75715e",
1398
1469
  // comment
1470
+ black: "#2d2e27",
1471
+ white: "#f8f8f2"
1399
1472
  }
1400
1473
  }
1401
1474
  };
@@ -1626,13 +1699,19 @@ function measureIndent(line10) {
1626
1699
  }
1627
1700
  return indent;
1628
1701
  }
1629
- function extractColor(label, palette) {
1702
+ function extractColor(label, palette, diagnostics, line10) {
1630
1703
  const m = label.match(COLOR_SUFFIX_RE);
1631
1704
  if (!m) return { label };
1632
1705
  const colorName = m[1].trim();
1706
+ let color;
1707
+ if (diagnostics && line10 !== void 0) {
1708
+ color = resolveColorWithDiagnostic(colorName, line10, diagnostics, palette);
1709
+ } else {
1710
+ color = resolveColor(colorName, palette) ?? void 0;
1711
+ }
1633
1712
  return {
1634
1713
  label: label.substring(0, m.index).trim(),
1635
- color: resolveColor(colorName, palette) ?? void 0
1714
+ color
1636
1715
  };
1637
1716
  }
1638
1717
  function parseFirstLine(line10) {
@@ -1696,7 +1775,7 @@ function collectIndentedValues(lines, startIndex) {
1696
1775
  }
1697
1776
  return { values, lineNumbers, newIndex: j - 1 };
1698
1777
  }
1699
- function parseSeriesNames(value, lines, lineIndex, palette) {
1778
+ function parseSeriesNames(value, lines, lineIndex, palette, diagnostics) {
1700
1779
  let rawNames;
1701
1780
  let series;
1702
1781
  let newIndex = lineIndex;
@@ -1714,8 +1793,14 @@ function parseSeriesNames(value, lines, lineIndex, palette) {
1714
1793
  }
1715
1794
  const names = [];
1716
1795
  const nameColors = [];
1717
- for (const raw of rawNames) {
1718
- const extracted = extractColor(raw, palette);
1796
+ for (let i = 0; i < rawNames.length; i++) {
1797
+ const raw = rawNames[i];
1798
+ const extracted = extractColor(
1799
+ raw,
1800
+ palette,
1801
+ diagnostics,
1802
+ nameLineNumbers[i]
1803
+ );
1719
1804
  nameColors.push(extracted.color);
1720
1805
  names.push(extracted.label);
1721
1806
  }
@@ -3216,7 +3301,12 @@ function parseSequenceDgmo(content) {
3216
3301
  }
3217
3302
  if (currentTagGroup && !contentStarted && measureIndent(raw) > 0) {
3218
3303
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
3219
- const { label, color } = extractColor(cleanEntry);
3304
+ const { label, color } = extractColor(
3305
+ cleanEntry,
3306
+ void 0,
3307
+ result.diagnostics,
3308
+ lineNumber
3309
+ );
3220
3310
  if (!color) {
3221
3311
  pushError(
3222
3312
  lineNumber,
@@ -3929,19 +4019,37 @@ function splitArrows(line10) {
3929
4019
  }
3930
4020
  return segments;
3931
4021
  }
3932
- function parseArrowToken(token, palette) {
4022
+ function parseArrowToken(token, palette, lineNumber, diagnostics) {
3933
4023
  if (token === "->") return {};
3934
4024
  const colorOnly = token.match(/^-\(([^)]+)\)->$/);
3935
4025
  if (colorOnly) {
3936
- return { color: resolveColor(colorOnly[1].trim(), palette) ?? void 0 };
4026
+ return {
4027
+ color: resolveColorWithDiagnostic(
4028
+ colorOnly[1].trim(),
4029
+ lineNumber,
4030
+ diagnostics,
4031
+ palette
4032
+ )
4033
+ };
3937
4034
  }
3938
4035
  const m = token.match(/^-(.+?)(?:\(([^)]+)\))?->$/);
3939
4036
  if (m) {
3940
4037
  const label = m[1]?.trim() || void 0;
3941
- let color = m[2] ? resolveColor(m[2].trim(), palette) ?? void 0 : void 0;
4038
+ let color = m[2] ? resolveColorWithDiagnostic(
4039
+ m[2].trim(),
4040
+ lineNumber,
4041
+ diagnostics,
4042
+ palette
4043
+ ) : void 0;
3942
4044
  if (label && !color) {
3943
4045
  const inferred = inferArrowColor(label);
3944
- if (inferred) color = resolveColor(inferred, palette) ?? void 0;
4046
+ if (inferred)
4047
+ color = resolveColorWithDiagnostic(
4048
+ inferred,
4049
+ lineNumber,
4050
+ diagnostics,
4051
+ palette
4052
+ );
3945
4053
  }
3946
4054
  return { label, color };
3947
4055
  }
@@ -4018,7 +4126,12 @@ function parseFlowchart(content, palette) {
4018
4126
  for (let i = 0; i < segments.length; i++) {
4019
4127
  const seg = segments[i];
4020
4128
  if (seg === "->" || /^-.+->$/.test(seg)) {
4021
- pendingArrow = parseArrowToken(seg, palette);
4129
+ pendingArrow = parseArrowToken(
4130
+ seg,
4131
+ palette,
4132
+ lineNumber,
4133
+ result.diagnostics
4134
+ );
4022
4135
  continue;
4023
4136
  }
4024
4137
  const ref = parseNodeRef(seg, palette);
@@ -4212,15 +4325,27 @@ function splitArrows2(line10) {
4212
4325
  if (remaining) segments.push(remaining);
4213
4326
  return segments;
4214
4327
  }
4215
- function parseArrowToken2(token, palette) {
4328
+ function parseArrowToken2(token, palette, lineNumber, diagnostics) {
4216
4329
  if (token === "->") return {};
4217
4330
  const colorOnly = token.match(/^-\(([^)]+)\)->$/);
4218
4331
  if (colorOnly)
4219
- return { color: resolveColor(colorOnly[1].trim(), palette) ?? void 0 };
4332
+ return {
4333
+ color: resolveColorWithDiagnostic(
4334
+ colorOnly[1].trim(),
4335
+ lineNumber,
4336
+ diagnostics,
4337
+ palette
4338
+ )
4339
+ };
4220
4340
  const m = token.match(/^-(.+?)(?:\(([^)]+)\))?->$/);
4221
4341
  if (m) {
4222
4342
  const label = m[1]?.trim() || void 0;
4223
- const color = m[2] ? resolveColor(m[2].trim(), palette) ?? void 0 : void 0;
4343
+ const color = m[2] ? resolveColorWithDiagnostic(
4344
+ m[2].trim(),
4345
+ lineNumber,
4346
+ diagnostics,
4347
+ palette
4348
+ ) : void 0;
4224
4349
  return { label, color };
4225
4350
  }
4226
4351
  return {};
@@ -4324,7 +4449,12 @@ function parseState(content, palette) {
4324
4449
  if (groupMatch && groupMatch[1].trim() !== "*") {
4325
4450
  const groupLabel = groupMatch[1].trim();
4326
4451
  const groupColorName = groupMatch[2]?.trim();
4327
- const groupColor = groupColorName ? resolveColor(groupColorName, palette) : void 0;
4452
+ const groupColor = groupColorName ? resolveColorWithDiagnostic(
4453
+ groupColorName,
4454
+ lineNumber,
4455
+ result.diagnostics,
4456
+ palette
4457
+ ) : void 0;
4328
4458
  currentGroup = {
4329
4459
  id: `group:${groupLabel.toLowerCase()}`,
4330
4460
  label: groupLabel,
@@ -4382,7 +4512,12 @@ function parseState(content, palette) {
4382
4512
  for (let j = 0; j < segments.length; j++) {
4383
4513
  const seg = segments[j];
4384
4514
  if (seg === "->" || /^-.+->$/.test(seg)) {
4385
- pendingArrow = parseArrowToken2(seg, palette);
4515
+ pendingArrow = parseArrowToken2(
4516
+ seg,
4517
+ palette,
4518
+ lineNumber,
4519
+ result.diagnostics
4520
+ );
4386
4521
  continue;
4387
4522
  }
4388
4523
  const ref = parseStateNodeRef(seg, palette);
@@ -4645,7 +4780,12 @@ function parseClassDiagram(content, palette) {
4645
4780
  const bracketModifier = classDecl[5];
4646
4781
  const modifier = prefixModifier ?? bracketModifier;
4647
4782
  const colorName = classDecl[6]?.trim();
4648
- const color = colorName ? resolveColor(colorName, palette) : void 0;
4783
+ const color = colorName ? resolveColorWithDiagnostic(
4784
+ colorName,
4785
+ lineNumber,
4786
+ result.diagnostics,
4787
+ palette
4788
+ ) : void 0;
4649
4789
  const node = getOrCreateClass(name, lineNumber);
4650
4790
  if (modifier) node.modifier = modifier;
4651
4791
  if (color) node.color = color;
@@ -5015,7 +5155,12 @@ function parseERDiagram(content, palette) {
5015
5155
  if (tableDecl) {
5016
5156
  const name = tableDecl[1];
5017
5157
  const colorName = tableDecl[2]?.trim();
5018
- const color = colorName ? resolveColor(colorName, palette) : void 0;
5158
+ const color = colorName ? resolveColorWithDiagnostic(
5159
+ colorName,
5160
+ lineNumber,
5161
+ result.diagnostics,
5162
+ palette
5163
+ ) : void 0;
5019
5164
  const table = getOrCreateTable(name, lineNumber);
5020
5165
  if (color) table.color = color;
5021
5166
  table.lineNumber = lineNumber;
@@ -5143,7 +5288,7 @@ var init_parser3 = __esm({
5143
5288
  init_parsing();
5144
5289
  init_tag_groups();
5145
5290
  TABLE_DECL_RE = /^([a-zA-Z_]\w*)(?:\s*\(([^)]+)\))?(?:\s*\|(.+))?$/;
5146
- INDENT_REL_RE = /^([1*?])-(?:(.+)-)?([1*?])\s+([a-zA-Z_]\w*)\s*$/;
5291
+ INDENT_REL_RE = /^([1*?])-{1,2}(?:(.+?)-{1,2})?([1*?])\s+([a-zA-Z_]\w*)\s*$/;
5147
5292
  CONSTRAINT_MAP = {
5148
5293
  pk: "pk",
5149
5294
  fk: "fk",
@@ -5232,7 +5377,12 @@ function parseChart(content, palette) {
5232
5377
  rawEras.push({
5233
5378
  start: eraMatch[1].trim(),
5234
5379
  afterArrow,
5235
- color: eraMatch[3] ? resolveColor(eraMatch[3].trim(), palette) : null,
5380
+ color: eraMatch[3] ? resolveColorWithDiagnostic(
5381
+ eraMatch[3].trim(),
5382
+ lineNumber,
5383
+ result.diagnostics,
5384
+ palette
5385
+ ) ?? null : null,
5236
5386
  lineNumber
5237
5387
  });
5238
5388
  }
@@ -5281,11 +5431,22 @@ function parseChart(content, palette) {
5281
5431
  continue;
5282
5432
  }
5283
5433
  if (firstToken === "color") {
5284
- result.color = resolveColor(value.trim(), palette) ?? void 0;
5434
+ result.color = resolveColorWithDiagnostic(
5435
+ value.trim(),
5436
+ lineNumber,
5437
+ result.diagnostics,
5438
+ palette
5439
+ );
5285
5440
  continue;
5286
5441
  }
5287
5442
  if (firstToken === "series") {
5288
- const parsed = parseSeriesNames(value, lines, i, palette);
5443
+ const parsed = parseSeriesNames(
5444
+ value,
5445
+ lines,
5446
+ i,
5447
+ palette,
5448
+ result.diagnostics
5449
+ );
5289
5450
  i = parsed.newIndex;
5290
5451
  result.series = parsed.series;
5291
5452
  result.seriesLineNumber = lineNumber;
@@ -5707,7 +5868,12 @@ function parseExtendedChart(content, palette) {
5707
5868
  const categoryMatch = trimmed.match(/^\[(.+?)\](?:\s*\(([^)]+)\))?\s*$/);
5708
5869
  if (categoryMatch) {
5709
5870
  const catName = categoryMatch[1].trim();
5710
- const catColor = categoryMatch[2] ? resolveColor(categoryMatch[2].trim(), palette) : null;
5871
+ const catColor = categoryMatch[2] ? resolveColorWithDiagnostic(
5872
+ categoryMatch[2].trim(),
5873
+ lineNumber,
5874
+ result.diagnostics,
5875
+ palette
5876
+ ) ?? null : null;
5711
5877
  if (catColor) {
5712
5878
  if (!result.categoryColors) result.categoryColors = {};
5713
5879
  result.categoryColors[catName] = catColor;
@@ -5735,7 +5901,12 @@ function parseExtendedChart(content, palette) {
5735
5901
  if (sourceColor) result.nodeColors[source] = sourceColor;
5736
5902
  if (targetColor) result.nodeColors[target] = targetColor;
5737
5903
  }
5738
- const linkColor = rawLinkColor ? resolveColor(rawLinkColor.trim(), palette) : void 0;
5904
+ const linkColor = rawLinkColor ? resolveColorWithDiagnostic(
5905
+ rawLinkColor.trim(),
5906
+ lineNumber,
5907
+ result.diagnostics,
5908
+ palette
5909
+ ) : void 0;
5739
5910
  if (!result.links) result.links = [];
5740
5911
  result.links.push({
5741
5912
  source,
@@ -5761,7 +5932,12 @@ function parseExtendedChart(content, palette) {
5761
5932
  const dataRow2 = parseDataRowValues(strippedLine);
5762
5933
  if (dataRow2 && dataRow2.values.length === 1) {
5763
5934
  const source = sankeyStack.at(-1).name;
5764
- const linkColor = valColorMatch?.[2] ? resolveColor(valColorMatch[2].trim(), palette) : void 0;
5935
+ const linkColor = valColorMatch?.[2] ? resolveColorWithDiagnostic(
5936
+ valColorMatch[2].trim(),
5937
+ lineNumber,
5938
+ result.diagnostics,
5939
+ palette
5940
+ ) : void 0;
5765
5941
  const { label: target, color: targetColor } = extractColor(
5766
5942
  dataRow2.label,
5767
5943
  palette
@@ -8229,7 +8405,12 @@ function parseKanban(content, palette) {
8229
8405
  currentCard = null;
8230
8406
  columnCounter++;
8231
8407
  const colName = columnMatch[1].trim();
8232
- const colColor = columnMatch[2] ? resolveColor(columnMatch[2].trim(), palette) ?? void 0 : void 0;
8408
+ const colColor = columnMatch[2] ? resolveColorWithDiagnostic(
8409
+ columnMatch[2].trim(),
8410
+ lineNumber,
8411
+ result.diagnostics,
8412
+ palette
8413
+ ) : void 0;
8233
8414
  let wipLimit;
8234
8415
  const columnMetadata = {};
8235
8416
  const pipeStr = columnMatch[3];
@@ -8277,7 +8458,8 @@ function parseKanban(content, palette) {
8277
8458
  lineNumber,
8278
8459
  cardCounter,
8279
8460
  aliasMap,
8280
- palette
8461
+ palette,
8462
+ result.diagnostics
8281
8463
  );
8282
8464
  if (currentColumn.metadata) {
8283
8465
  for (const [key, value] of Object.entries(currentColumn.metadata)) {
@@ -8331,7 +8513,7 @@ function parseKanban(content, palette) {
8331
8513
  validateTagGroupNames(result.tagGroups, warn);
8332
8514
  return result;
8333
8515
  }
8334
- function parseCardLine(trimmed, lineNumber, counter, aliasMap, palette) {
8516
+ function parseCardLine(trimmed, lineNumber, counter, aliasMap, palette, diagnostics) {
8335
8517
  const pipeIdx = trimmed.indexOf("|");
8336
8518
  let rawTitle;
8337
8519
  let tagsStr = null;
@@ -8341,7 +8523,12 @@ function parseCardLine(trimmed, lineNumber, counter, aliasMap, palette) {
8341
8523
  } else {
8342
8524
  rawTitle = trimmed;
8343
8525
  }
8344
- const { label: title, color } = extractColor(rawTitle, palette);
8526
+ const { label: title, color } = extractColor(
8527
+ rawTitle,
8528
+ palette,
8529
+ diagnostics,
8530
+ lineNumber
8531
+ );
8345
8532
  const tags = {};
8346
8533
  if (tagsStr) {
8347
8534
  for (const part of tagsStr.split(",")) {
@@ -9076,7 +9263,7 @@ __export(parser_exports7, {
9076
9263
  looksLikeSitemap: () => looksLikeSitemap,
9077
9264
  parseSitemap: () => parseSitemap
9078
9265
  });
9079
- function parseArrowLine(trimmed, palette) {
9266
+ function parseArrowLine(trimmed, palette, lineNumber, diagnostics) {
9080
9267
  const bareMatch = trimmed.match(BARE_ARROW_RE);
9081
9268
  if (bareMatch) {
9082
9269
  const rawTarget = bareMatch[1].trim();
@@ -9089,10 +9276,21 @@ function parseArrowLine(trimmed, palette) {
9089
9276
  const arrowMatch = trimmed.match(ARROW_RE);
9090
9277
  if (arrowMatch) {
9091
9278
  const label = arrowMatch[1]?.trim() || void 0;
9092
- let color = arrowMatch[2] ? resolveColor(arrowMatch[2].trim(), palette) ?? void 0 : void 0;
9279
+ let color = arrowMatch[2] ? resolveColorWithDiagnostic(
9280
+ arrowMatch[2].trim(),
9281
+ lineNumber,
9282
+ diagnostics,
9283
+ palette
9284
+ ) : void 0;
9093
9285
  if (label && !color) {
9094
9286
  const inferred = inferArrowColor(label);
9095
- if (inferred) color = resolveColor(inferred, palette) ?? void 0;
9287
+ if (inferred)
9288
+ color = resolveColorWithDiagnostic(
9289
+ inferred,
9290
+ lineNumber,
9291
+ diagnostics,
9292
+ palette
9293
+ );
9096
9294
  }
9097
9295
  const rawTarget = arrowMatch[3].trim();
9098
9296
  const groupMatch = rawTarget.match(/^\[(.+)\]$/);
@@ -9256,7 +9454,12 @@ function parseSitemap(content, palette) {
9256
9454
  contentStarted = true;
9257
9455
  currentTagGroup = null;
9258
9456
  const indent = measureIndent(line10);
9259
- const arrowInfo = parseArrowLine(trimmed, palette);
9457
+ const arrowInfo = parseArrowLine(
9458
+ trimmed,
9459
+ palette,
9460
+ lineNumber,
9461
+ result.diagnostics
9462
+ );
9260
9463
  if (arrowInfo) {
9261
9464
  const source = findParentNode(indent, indentStack);
9262
9465
  if (!source) {
@@ -9277,7 +9480,12 @@ function parseSitemap(content, palette) {
9277
9480
  const metadataMatch = trimmed.includes("|") ? null : trimmed.match(METADATA_RE3);
9278
9481
  if (containerMatch) {
9279
9482
  const rawLabel = containerMatch[1].trim();
9280
- const { label, color } = extractColor(rawLabel, palette);
9483
+ const { label, color } = extractColor(
9484
+ rawLabel,
9485
+ palette,
9486
+ result.diagnostics,
9487
+ lineNumber
9488
+ );
9281
9489
  const pipeStr = containerMatch[2];
9282
9490
  const containerMetadata = {};
9283
9491
  if (pipeStr) {
@@ -9318,7 +9526,8 @@ function parseSitemap(content, palette) {
9318
9526
  palette,
9319
9527
  ++nodeCounter,
9320
9528
  aliasMap,
9321
- pushWarning
9529
+ pushWarning,
9530
+ result.diagnostics
9322
9531
  );
9323
9532
  attachNode2(node, indent, indentStack, result);
9324
9533
  labelToNode.set(node.label.toLowerCase(), node);
@@ -9332,7 +9541,8 @@ function parseSitemap(content, palette) {
9332
9541
  palette,
9333
9542
  ++nodeCounter,
9334
9543
  aliasMap,
9335
- pushWarning
9544
+ pushWarning,
9545
+ result.diagnostics
9336
9546
  );
9337
9547
  attachNode2(node, indent, indentStack, result);
9338
9548
  labelToNode.set(node.label.toLowerCase(), node);
@@ -9395,10 +9605,15 @@ function parseSitemap(content, palette) {
9395
9605
  }
9396
9606
  return result;
9397
9607
  }
9398
- function parseNodeLabel2(trimmed, lineNumber, palette, counter, aliasMap = /* @__PURE__ */ new Map(), warnFn) {
9608
+ function parseNodeLabel2(trimmed, lineNumber, palette, counter, aliasMap = /* @__PURE__ */ new Map(), warnFn, diagnostics) {
9399
9609
  const segments = trimmed.split("|").map((s) => s.trim());
9400
9610
  const rawLabel = segments[0];
9401
- const { label, color } = extractColor(rawLabel, palette);
9611
+ const { label, color } = extractColor(
9612
+ rawLabel,
9613
+ palette,
9614
+ diagnostics,
9615
+ lineNumber
9616
+ );
9402
9617
  const metadata = parsePipeMetadata(
9403
9618
  segments,
9404
9619
  aliasMap,
@@ -9690,9 +9905,13 @@ function parseInfra(content) {
9690
9905
  const tvMatch = cleanEntry.match(TAG_VALUE_RE);
9691
9906
  if (tvMatch) {
9692
9907
  const valueName = tvMatch[1].trim();
9908
+ const rawColor = tvMatch[2]?.trim();
9909
+ if (rawColor) {
9910
+ resolveColorWithDiagnostic(rawColor, lineNumber, result.diagnostics);
9911
+ }
9693
9912
  currentTagGroup.values.push({
9694
9913
  name: valueName,
9695
- color: tvMatch[2]?.trim()
9914
+ color: rawColor
9696
9915
  });
9697
9916
  if (isDefault) {
9698
9917
  currentTagGroup.defaultValue = valueName;
@@ -10044,6 +10263,7 @@ var init_parser8 = __esm({
10044
10263
  "src/infra/parser.ts"() {
10045
10264
  "use strict";
10046
10265
  init_diagnostics();
10266
+ init_colors();
10047
10267
  init_parsing();
10048
10268
  init_tag_groups();
10049
10269
  init_types();
@@ -10105,9 +10325,11 @@ function buildHolidaySet(holidays) {
10105
10325
  function addBusinessDays(startDate, count, workweek, holidaySet, direction = 1) {
10106
10326
  const days = Math.round(Math.abs(count));
10107
10327
  if (days === 0) return new Date(startDate);
10328
+ if (Number.isNaN(startDate.getTime())) return new Date(startDate);
10108
10329
  const current = new Date(startDate);
10109
10330
  let remaining = days;
10110
- while (remaining > 0) {
10331
+ let safety = days * 14 + 14;
10332
+ while (remaining > 0 && safety-- > 0) {
10111
10333
  current.setDate(current.getDate() + direction);
10112
10334
  if (isWorkday(current, workweek, holidaySet)) {
10113
10335
  remaining--;
@@ -14212,6 +14434,7 @@ var init_mutations = __esm({
14212
14434
  // src/kanban/renderer.ts
14213
14435
  var renderer_exports3 = {};
14214
14436
  __export(renderer_exports3, {
14437
+ bucketCardsBySwimlane: () => bucketCardsBySwimlane,
14215
14438
  renderKanban: () => renderKanban,
14216
14439
  renderKanbanForExport: () => renderKanbanForExport
14217
14440
  });
@@ -14307,7 +14530,14 @@ function computeLayout(parsed, _palette) {
14307
14530
  const totalHeight = startY + maxColumnHeight + DIAGRAM_PADDING3;
14308
14531
  return { columns: columnLayouts, totalWidth, totalHeight };
14309
14532
  }
14310
- function renderKanban(container, parsed, palette, isDark, _onNavigateToLine, exportDims, activeTagGroup) {
14533
+ function renderKanban(container, parsed, palette, isDark, options) {
14534
+ const exportDims = options?.exportDims;
14535
+ const activeTagGroup = options?.activeTagGroup ?? null;
14536
+ const onSwimlaneChange = options?.onSwimlaneChange;
14537
+ const requestedSwimlane = options?.currentSwimlaneGroup ?? null;
14538
+ const swimlaneGroup = requestedSwimlane ? parsed.tagGroups.find(
14539
+ (g) => g.name.toLowerCase() === requestedSwimlane.toLowerCase()
14540
+ ) ?? null : null;
14311
14541
  const layout = computeLayout(parsed, palette);
14312
14542
  const width = exportDims?.width ?? layout.totalWidth;
14313
14543
  const height = exportDims?.height ?? layout.totalHeight;
@@ -14327,16 +14557,49 @@ function renderKanban(container, parsed, palette, isDark, _onNavigateToLine, exp
14327
14557
  };
14328
14558
  const legendState = { activeGroup: activeTagGroup ?? null };
14329
14559
  const legendG = svg.append("g").attr("class", "kanban-legend").attr("transform", `translate(${legendX},${legendY})`);
14560
+ const showSwimlaneIcon = !!onSwimlaneChange && !exportDims;
14561
+ const legendCallbacks = showSwimlaneIcon ? {
14562
+ onGroupRendered: (groupName, groupEl, isActive) => {
14563
+ const isCurrent = swimlaneGroup?.name.toLowerCase() === groupName.toLowerCase();
14564
+ const textW = measureLegendText(groupName, 11);
14565
+ const iconX = isActive ? 4 + 6 + textW + 6 : 8 + textW + 4;
14566
+ const iconY = (LEGEND_HEIGHT - 7) / 2;
14567
+ const iconEl = drawSwimlaneIcon(
14568
+ groupEl,
14569
+ iconX,
14570
+ iconY,
14571
+ isCurrent,
14572
+ palette
14573
+ );
14574
+ iconEl.append("title").text(`Group by ${groupName}`);
14575
+ iconEl.style("cursor", "pointer").on("click", (event) => {
14576
+ event.stopPropagation();
14577
+ onSwimlaneChange?.(isCurrent ? null : groupName);
14578
+ });
14579
+ }
14580
+ } : void 0;
14330
14581
  renderLegendD3(
14331
14582
  legendG,
14332
14583
  legendConfig,
14333
14584
  legendState,
14334
14585
  palette,
14335
14586
  isDark,
14336
- void 0,
14587
+ legendCallbacks,
14337
14588
  width - legendX - DIAGRAM_PADDING3
14338
14589
  );
14339
14590
  }
14591
+ if (swimlaneGroup) {
14592
+ renderSwimlaneBoard(
14593
+ svg,
14594
+ parsed,
14595
+ layout,
14596
+ swimlaneGroup,
14597
+ palette,
14598
+ isDark,
14599
+ activeTagGroup
14600
+ );
14601
+ return;
14602
+ }
14340
14603
  const defaultColBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
14341
14604
  const defaultColHeaderBg = isDark ? mix(palette.surface, palette.bg, 70) : mix(palette.surface, palette.bg, 50);
14342
14605
  const cardBaseBg = isDark ? palette.surface : palette.bg;
@@ -14413,14 +14676,204 @@ function renderKanbanForExport(content, theme, palette) {
14413
14676
  const isDark = theme === "dark";
14414
14677
  const layout = computeLayout(parsed, palette);
14415
14678
  const container = document.createElement("div");
14416
- renderKanban(container, parsed, palette, isDark, void 0, {
14417
- width: layout.totalWidth,
14418
- height: layout.totalHeight
14679
+ renderKanban(container, parsed, palette, isDark, {
14680
+ exportDims: { width: layout.totalWidth, height: layout.totalHeight }
14419
14681
  });
14420
14682
  const svgEl = container.querySelector("svg");
14421
14683
  return svgEl?.outerHTML ?? "";
14422
14684
  }
14423
- var 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;
14685
+ function drawSwimlaneIcon(parent, x, y, isActive, palette) {
14686
+ const iconG = parent.append("g").attr("class", "kanban-swimlane-icon").attr("transform", `translate(${x}, ${y})`);
14687
+ const color = isActive ? palette.primary : palette.textMuted;
14688
+ const opacity = isActive ? 1 : 0.35;
14689
+ const barWidths = [8, 12, 6];
14690
+ const barH = 2;
14691
+ const gap = 3;
14692
+ for (let i = 0; i < barWidths.length; i++) {
14693
+ 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);
14694
+ }
14695
+ return iconG;
14696
+ }
14697
+ function bucketCardsBySwimlane(columns, swimlaneGroup) {
14698
+ const tagKey = swimlaneGroup.name.toLowerCase();
14699
+ const buckets = /* @__PURE__ */ new Map();
14700
+ for (const entry of swimlaneGroup.entries) {
14701
+ buckets.set(entry.value.toLowerCase(), {
14702
+ laneName: entry.value,
14703
+ laneColor: entry.color,
14704
+ isFallback: false,
14705
+ cellsByColumn: {}
14706
+ });
14707
+ }
14708
+ const fallbackKey = `__no_${tagKey}__`;
14709
+ for (const col of columns) {
14710
+ for (const card of col.cards) {
14711
+ const raw = card.tags[tagKey] ?? swimlaneGroup.defaultValue;
14712
+ let bucketKey;
14713
+ if (raw && buckets.has(raw.toLowerCase())) {
14714
+ bucketKey = raw.toLowerCase();
14715
+ } else {
14716
+ if (!buckets.has(fallbackKey)) {
14717
+ buckets.set(fallbackKey, {
14718
+ laneName: `No ${swimlaneGroup.name}`,
14719
+ isFallback: true,
14720
+ cellsByColumn: {}
14721
+ });
14722
+ }
14723
+ bucketKey = fallbackKey;
14724
+ }
14725
+ const bucket = buckets.get(bucketKey);
14726
+ (bucket.cellsByColumn[col.id] ??= []).push(card);
14727
+ }
14728
+ }
14729
+ return Array.from(buckets.values());
14730
+ }
14731
+ function computeCardHeight3(card, tagGroups) {
14732
+ const tagMeta = resolveCardTagMeta(card, tagGroups);
14733
+ const metaCount = tagMeta.length + card.details.length;
14734
+ const metaHeight = metaCount > 0 ? CARD_SEPARATOR_GAP + 1 + CARD_PADDING_Y + metaCount * CARD_META_LINE_HEIGHT : 0;
14735
+ return CARD_HEADER_HEIGHT + CARD_PADDING_Y + metaHeight;
14736
+ }
14737
+ function computeSwimlaneLayout(parsed, buckets, baseLayout) {
14738
+ const headerHeight = parsed.title ? TITLE_HEIGHT3 + 8 : 0;
14739
+ const startY = DIAGRAM_PADDING3 + headerHeight;
14740
+ const columnXs = [];
14741
+ let currentX = DIAGRAM_PADDING3 + LANE_HEADER_WIDTH;
14742
+ for (const col of baseLayout.columns) {
14743
+ columnXs.push({ column: col.column, x: currentX, width: col.width });
14744
+ currentX += col.width + COLUMN_GAP;
14745
+ }
14746
+ const totalWidth = currentX - COLUMN_GAP + DIAGRAM_PADDING3;
14747
+ const lanes = [];
14748
+ let laneY = startY + COLUMN_HEADER_HEIGHT + COLUMN_PADDING;
14749
+ const minCellH = CARD_HEADER_HEIGHT + CARD_PADDING_Y + CARD_GAP;
14750
+ for (const bucket of buckets) {
14751
+ let maxCellH = minCellH;
14752
+ const cellsTmp = [];
14753
+ for (const colInfo of columnXs) {
14754
+ const cards = bucket.cellsByColumn[colInfo.column.id] ?? [];
14755
+ let h = 0;
14756
+ for (const c of cards) {
14757
+ h += computeCardHeight3(c, parsed.tagGroups) + CARD_GAP;
14758
+ }
14759
+ h = Math.max(h - (cards.length > 0 ? CARD_GAP : 0), 0);
14760
+ cellsTmp.push({ column: colInfo.column, cards, h });
14761
+ if (h > maxCellH) maxCellH = h;
14762
+ }
14763
+ const laneHeight = Math.max(maxCellH, minCellH);
14764
+ const cells = cellsTmp.map((tmp, i) => {
14765
+ const colInfo = columnXs[i];
14766
+ const cardLayouts = [];
14767
+ let cy = 0;
14768
+ for (const card of tmp.cards) {
14769
+ const ch = computeCardHeight3(card, parsed.tagGroups);
14770
+ cardLayouts.push({
14771
+ x: colInfo.x + COLUMN_PADDING,
14772
+ y: laneY + cy,
14773
+ width: colInfo.width - COLUMN_PADDING * 2,
14774
+ height: ch,
14775
+ card
14776
+ });
14777
+ cy += ch + CARD_GAP;
14778
+ }
14779
+ return { column: tmp.column, cards: tmp.cards, cardLayouts };
14780
+ });
14781
+ lanes.push({ bucket, y: laneY, height: laneHeight, cells });
14782
+ laneY += laneHeight + LANE_GAP;
14783
+ }
14784
+ const totalHeight = laneY - LANE_GAP + COLUMN_PADDING + DIAGRAM_PADDING3;
14785
+ return { columnXs, lanes, totalWidth, totalHeight, startY };
14786
+ }
14787
+ function renderSwimlaneBoard(svg, parsed, baseLayout, swimlaneGroup, palette, isDark, activeTagGroup) {
14788
+ const visibleColumns = parsed.columns.filter((c) => !isArchiveColumn(c.name));
14789
+ const buckets = bucketCardsBySwimlane(visibleColumns, swimlaneGroup);
14790
+ const grid = computeSwimlaneLayout(parsed, buckets, baseLayout);
14791
+ const currentW = parseFloat(svg.attr("width") || "0");
14792
+ const currentH = parseFloat(svg.attr("height") || "0");
14793
+ if (grid.totalWidth > currentW) svg.attr("width", grid.totalWidth);
14794
+ if (grid.totalHeight > currentH) svg.attr("height", grid.totalHeight);
14795
+ const defaultColBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
14796
+ const defaultColHeaderBg = isDark ? mix(palette.surface, palette.bg, 70) : mix(palette.surface, palette.bg, 50);
14797
+ const cardBaseBg = isDark ? palette.surface : palette.bg;
14798
+ for (const colInfo of grid.columnXs) {
14799
+ const col = colInfo.column;
14800
+ const headerG = svg.append("g").attr("class", "kanban-column kanban-column-header").attr("data-column-id", col.id).attr("data-line-number", col.lineNumber);
14801
+ const colHeaderBg = col.color ? mix(col.color, palette.bg, 25) : defaultColHeaderBg;
14802
+ 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);
14803
+ headerG.append("text").attr("x", colInfo.x + COLUMN_PADDING).attr(
14804
+ "y",
14805
+ grid.startY + COLUMN_HEADER_HEIGHT / 2 + COLUMN_HEADER_FONT_SIZE / 2 - 2
14806
+ ).attr("font-size", COLUMN_HEADER_FONT_SIZE).attr("font-weight", "bold").attr("fill", palette.text).text(col.name);
14807
+ }
14808
+ for (const lane of grid.lanes) {
14809
+ const laneG = svg.append("g").attr("class", "kanban-lane").attr("data-lane-name", lane.bucket.laneName);
14810
+ const headerG = laneG.append("g").attr("class", "kanban-lane-header").attr(
14811
+ "transform",
14812
+ `translate(${DIAGRAM_PADDING3}, ${lane.y - COLUMN_PADDING})`
14813
+ );
14814
+ 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);
14815
+ let labelX = 10;
14816
+ if (lane.bucket.laneColor) {
14817
+ headerG.append("circle").attr("cx", labelX + 4).attr("cy", 16).attr("r", 4).attr("fill", lane.bucket.laneColor);
14818
+ labelX += 14;
14819
+ }
14820
+ 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);
14821
+ const totalCards = lane.cells.reduce((s, c) => s + c.cards.length, 0);
14822
+ headerG.append("text").attr("x", labelX).attr("y", 36).attr("font-size", 10).attr("fill", palette.textMuted).text(`(${totalCards})`);
14823
+ 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);
14824
+ for (const cell of lane.cells) {
14825
+ for (const cardLayout of cell.cardLayouts) {
14826
+ renderSwimlaneCard(
14827
+ laneG,
14828
+ cardLayout,
14829
+ parsed.tagGroups,
14830
+ activeTagGroup,
14831
+ palette,
14832
+ cardBaseBg
14833
+ );
14834
+ }
14835
+ }
14836
+ }
14837
+ }
14838
+ function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palette, cardBaseBg) {
14839
+ const card = cardLayout.card;
14840
+ const resolvedColor = resolveCardTagColor(card, tagGroups, activeTagGroup);
14841
+ const tagMeta = resolveCardTagMeta(card, tagGroups);
14842
+ const hasMeta = tagMeta.length > 0 || card.details.length > 0;
14843
+ const cardFill = resolvedColor ? mix(resolvedColor, cardBaseBg, 15) : mix(palette.primary, cardBaseBg, 15);
14844
+ const cardStroke = resolvedColor ?? palette.textMuted;
14845
+ const cg = parent.append("g").attr("class", "kanban-card").attr("data-card-id", card.id).attr("data-line-number", card.lineNumber);
14846
+ if (activeTagGroup) {
14847
+ const tagKey = activeTagGroup.toLowerCase();
14848
+ const group = tagGroups.find((tg) => tg.name.toLowerCase() === tagKey);
14849
+ const value = card.tags[tagKey] ?? group?.defaultValue;
14850
+ if (value) {
14851
+ cg.attr(`data-tag-${tagKey}`, value.toLowerCase());
14852
+ }
14853
+ }
14854
+ const cx = cardLayout.x;
14855
+ const cy = cardLayout.y;
14856
+ 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);
14857
+ 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);
14858
+ renderInlineText(titleEl, card.title, palette, CARD_TITLE_FONT_SIZE);
14859
+ if (hasMeta) {
14860
+ const separatorY = cy + CARD_HEADER_HEIGHT;
14861
+ 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);
14862
+ let metaY = separatorY + CARD_SEPARATOR_GAP + CARD_META_FONT_SIZE;
14863
+ for (const meta of tagMeta) {
14864
+ 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}: `);
14865
+ const labelWidth = (meta.label.length + 2) * CARD_META_FONT_SIZE * 0.6;
14866
+ 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);
14867
+ metaY += CARD_META_LINE_HEIGHT;
14868
+ }
14869
+ for (const detail of card.details) {
14870
+ 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);
14871
+ renderInlineText(detailEl, detail, palette, CARD_META_FONT_SIZE);
14872
+ metaY += CARD_META_LINE_HEIGHT;
14873
+ }
14874
+ }
14875
+ }
14876
+ var 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;
14424
14877
  var init_renderer3 = __esm({
14425
14878
  "src/kanban/renderer.ts"() {
14426
14879
  "use strict";
@@ -14452,6 +14905,8 @@ var init_renderer3 = __esm({
14452
14905
  WIP_FONT_SIZE = 10;
14453
14906
  COLUMN_RADIUS = 8;
14454
14907
  COLUMN_HEADER_RADIUS = 8;
14908
+ LANE_HEADER_WIDTH = 140;
14909
+ LANE_GAP = 14;
14455
14910
  }
14456
14911
  });
14457
14912
 
@@ -23596,7 +24051,7 @@ function resetHighlightAll(svg, chartG) {
23596
24051
  chartG.selectAll(".gantt-lane-band, .gantt-lane-accent").attr("opacity", 1);
23597
24052
  chartG.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").attr("opacity", 0.5);
23598
24053
  }
23599
- function drawSwimlaneIcon(parent, x, y, isActive, palette) {
24054
+ function drawSwimlaneIcon2(parent, x, y, isActive, palette) {
23600
24055
  const iconG = parent.append("g").attr("class", "gantt-swimlane-icon").attr("transform", `translate(${x}, ${y})`);
23601
24056
  const color = isActive ? palette.primary : palette.textMuted;
23602
24057
  const opacity = isActive ? 1 : 0.35;
@@ -23753,7 +24208,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
23753
24208
  const pillXOff = LEGEND_CAPSULE_PAD;
23754
24209
  const iconX = pillXOff + textW + 3;
23755
24210
  const iconY = (LEGEND_HEIGHT - 10) / 2;
23756
- const iconEl = drawSwimlaneIcon(
24211
+ const iconEl = drawSwimlaneIcon2(
23757
24212
  groupEl,
23758
24213
  iconX,
23759
24214
  iconY,
@@ -25180,10 +25635,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
25180
25635
  );
25181
25636
  if (tg) {
25182
25637
  for (const entry of tg.entries) {
25183
- tagValueToColor.set(
25184
- entry.value.toLowerCase(),
25185
- resolveColor(entry.color) ?? entry.color
25186
- );
25638
+ tagValueToColor.set(entry.value.toLowerCase(), entry.color);
25187
25639
  }
25188
25640
  }
25189
25641
  }
@@ -25589,7 +26041,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
25589
26041
  name: tg.name,
25590
26042
  entries: tg.entries.map((e) => ({
25591
26043
  value: e.value,
25592
- color: resolveColor(e.color) ?? e.color
26044
+ color: e.color
25593
26045
  }))
25594
26046
  }));
25595
26047
  const legendConfig = {
@@ -26169,7 +26621,6 @@ var init_renderer10 = __esm({
26169
26621
  init_color_utils();
26170
26622
  init_inline_markdown();
26171
26623
  init_fonts();
26172
- init_colors();
26173
26624
  init_parser();
26174
26625
  init_tag_resolution();
26175
26626
  init_tag_groups();
@@ -26480,12 +26931,22 @@ function parseVisualization(content, palette) {
26480
26931
  if (groupMatch) {
26481
26932
  if (result.type === "arc") {
26482
26933
  const name = groupMatch[1].trim();
26483
- const color = groupMatch[2] ? resolveColor(groupMatch[2].trim(), palette) : null;
26934
+ const color = groupMatch[2] ? resolveColorWithDiagnostic(
26935
+ groupMatch[2].trim(),
26936
+ lineNumber,
26937
+ result.diagnostics,
26938
+ palette
26939
+ ) ?? null : null;
26484
26940
  result.arcNodeGroups.push({ name, nodes: [], color, lineNumber });
26485
26941
  currentArcGroup = name;
26486
26942
  } else if (result.type === "timeline") {
26487
26943
  const name = groupMatch[1].trim();
26488
- const color = groupMatch[2] ? resolveColor(groupMatch[2].trim(), palette) : null;
26944
+ const color = groupMatch[2] ? resolveColorWithDiagnostic(
26945
+ groupMatch[2].trim(),
26946
+ lineNumber,
26947
+ result.diagnostics,
26948
+ palette
26949
+ ) ?? null : null;
26489
26950
  result.timelineGroups.push({ name, color, lineNumber });
26490
26951
  currentTimelineGroup = name;
26491
26952
  }
@@ -26513,7 +26974,12 @@ function parseVisualization(content, palette) {
26513
26974
  if (linkMatch) {
26514
26975
  const source = linkMatch[1].trim();
26515
26976
  const target = linkMatch[2].trim();
26516
- const linkColor = linkMatch[3] ? resolveColor(linkMatch[3].trim(), palette) : null;
26977
+ const linkColor = linkMatch[3] ? resolveColorWithDiagnostic(
26978
+ linkMatch[3].trim(),
26979
+ lineNumber,
26980
+ result.diagnostics,
26981
+ palette
26982
+ ) ?? null : null;
26517
26983
  result.links.push({
26518
26984
  source,
26519
26985
  target,
@@ -26550,7 +27016,12 @@ function parseVisualization(content, palette) {
26550
27016
  startDate: eraEntryMatch[1],
26551
27017
  endDate: eraEntryMatch[2],
26552
27018
  label: eraEntryMatch[3].trim(),
26553
- color: colorAnnotation ? resolveColor(colorAnnotation, palette) : null,
27019
+ color: colorAnnotation ? resolveColorWithDiagnostic(
27020
+ colorAnnotation,
27021
+ lineNumber,
27022
+ result.diagnostics,
27023
+ palette
27024
+ ) ?? null : null,
26554
27025
  lineNumber
26555
27026
  });
26556
27027
  } else {
@@ -26572,7 +27043,12 @@ function parseVisualization(content, palette) {
26572
27043
  result.timelineMarkers.push({
26573
27044
  date: markerEntryMatch[1],
26574
27045
  label: markerEntryMatch[2].trim(),
26575
- color: colorAnnotation ? resolveColor(colorAnnotation, palette) : null,
27046
+ color: colorAnnotation ? resolveColorWithDiagnostic(
27047
+ colorAnnotation,
27048
+ lineNumber,
27049
+ result.diagnostics,
27050
+ palette
27051
+ ) ?? null : null,
26576
27052
  lineNumber
26577
27053
  });
26578
27054
  } else {
@@ -26601,7 +27077,12 @@ function parseVisualization(content, palette) {
26601
27077
  startDate: eraMatch[1],
26602
27078
  endDate: eraMatch[2],
26603
27079
  label: eraMatch[3].trim(),
26604
- color: colorAnnotation ? resolveColor(colorAnnotation, palette) : null,
27080
+ color: colorAnnotation ? resolveColorWithDiagnostic(
27081
+ colorAnnotation,
27082
+ lineNumber,
27083
+ result.diagnostics,
27084
+ palette
27085
+ ) ?? null : null,
26605
27086
  lineNumber
26606
27087
  });
26607
27088
  continue;
@@ -26614,7 +27095,12 @@ function parseVisualization(content, palette) {
26614
27095
  result.timelineMarkers.push({
26615
27096
  date: markerMatch[1],
26616
27097
  label: markerMatch[2].trim(),
26617
- color: colorAnnotation ? resolveColor(colorAnnotation, palette) : null,
27098
+ color: colorAnnotation ? resolveColorWithDiagnostic(
27099
+ colorAnnotation,
27100
+ lineNumber,
27101
+ result.diagnostics,
27102
+ palette
27103
+ ) ?? null : null,
26618
27104
  lineNumber
26619
27105
  });
26620
27106
  continue;
@@ -26729,20 +27215,12 @@ function parseVisualization(content, palette) {
26729
27215
  const colorName = setDeclMatch[2]?.trim() ?? null;
26730
27216
  let color = null;
26731
27217
  if (colorName) {
26732
- const resolved = resolveColor(colorName, palette);
26733
- if (resolved === null) {
26734
- warn(
26735
- lineNumber,
26736
- `Hex colors are not supported \u2014 use named colors (blue, red, green, etc.)`
26737
- );
26738
- } else if (resolved.startsWith("#")) {
26739
- color = resolved;
26740
- } else {
26741
- warn(
26742
- lineNumber,
26743
- `Unknown color "${colorName}" on set "${name}". Using auto-assigned color.`
26744
- );
26745
- }
27218
+ color = resolveColorWithDiagnostic(
27219
+ colorName,
27220
+ lineNumber,
27221
+ result.diagnostics,
27222
+ palette
27223
+ ) ?? null;
26746
27224
  }
26747
27225
  const alias = setDeclMatch[3]?.trim() ?? null;
26748
27226
  result.vennSets.push({ name, alias, color, lineNumber });
@@ -26791,7 +27269,12 @@ function parseVisualization(content, palette) {
26791
27269
  const labelPart = quadrantMatch[2].trim();
26792
27270
  const labelColorMatch = labelPart.match(/^(.+?)\s*\(([^)]+)\)\s*$/);
26793
27271
  const text = labelColorMatch ? labelColorMatch[1].trim() : labelPart;
26794
- const color = labelColorMatch ? resolveColor(labelColorMatch[2].trim(), palette) : null;
27272
+ const color = labelColorMatch ? resolveColorWithDiagnostic(
27273
+ labelColorMatch[2].trim(),
27274
+ lineNumber,
27275
+ result.diagnostics,
27276
+ palette
27277
+ ) ?? null : null;
26795
27278
  const label = { text, color, lineNumber };
26796
27279
  if (position === "top-right") result.quadrantLabels.topRight = label;
26797
27280
  else if (position === "top-left") result.quadrantLabels.topLeft = label;
@@ -26949,7 +27432,12 @@ function parseVisualization(content, palette) {
26949
27432
  }
26950
27433
  const colorMatch = joinedLabel.match(/^(.+?)\(([^)]+)\)\s*$/);
26951
27434
  const labelPart = colorMatch ? colorMatch[1].trim() : joinedLabel;
26952
- const colorPart = colorMatch ? resolveColor(colorMatch[2].trim(), palette) : null;
27435
+ const colorPart = colorMatch ? resolveColorWithDiagnostic(
27436
+ colorMatch[2].trim(),
27437
+ lineNumber,
27438
+ result.diagnostics,
27439
+ palette
27440
+ ) ?? null : null;
26953
27441
  if (!labelPart) {
26954
27442
  warn(
26955
27443
  lineNumber,
@@ -27020,7 +27508,12 @@ function parseVisualization(content, palette) {
27020
27508
  continue;
27021
27509
  }
27022
27510
  const labelPart = colorMatch ? colorMatch[1].trim() : rawKey;
27023
- const colorPart = colorMatch ? resolveColor(colorMatch[2].trim(), palette) : null;
27511
+ const colorPart = colorMatch ? resolveColorWithDiagnostic(
27512
+ colorMatch[2].trim(),
27513
+ lineNumber,
27514
+ result.diagnostics,
27515
+ palette
27516
+ ) ?? null : null;
27024
27517
  const valuePart = line10.substring(colonIndex + 1).trim();
27025
27518
  const values = valuePart.split(",").map((v) => v.trim());
27026
27519
  const numericValues = [];
@@ -28868,7 +29361,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
28868
29361
  const mainSvg = d3Selection13.select(container).select("svg");
28869
29362
  const mainG = mainSvg.select("g");
28870
29363
  if (!mainSvg.empty() && !mainG.empty()) {
28871
- let drawSwimlaneIcon3 = function(parent, x, y, isSwimActive) {
29364
+ let drawSwimlaneIcon4 = function(parent, x, y, isSwimActive) {
28872
29365
  const iconG = parent.append("g").attr("class", "tl-swimlane-icon").attr("transform", `translate(${x}, ${y})`).style("cursor", "pointer");
28873
29366
  const barColor = isSwimActive ? palette.primary : palette.textMuted;
28874
29367
  const barOpacity = isSwimActive ? 1 : 0.35;
@@ -28960,7 +29453,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
28960
29453
  const pillXOff = LG_CAPSULE_PAD;
28961
29454
  const iconX = pillXOff + pillWidth3 + 5;
28962
29455
  const iconY = (LG_HEIGHT - 10) / 2;
28963
- const iconEl = drawSwimlaneIcon3(
29456
+ const iconEl = drawSwimlaneIcon4(
28964
29457
  groupEl,
28965
29458
  iconX,
28966
29459
  iconY,
@@ -29010,7 +29503,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
29010
29503
  el.selectAll("circle:not(.tl-event-point-outline)").attr("fill", mix(color, bg, 30)).attr("stroke", color);
29011
29504
  });
29012
29505
  };
29013
- var drawSwimlaneIcon2 = drawSwimlaneIcon3, relayout = relayout2, drawLegend = drawLegend2, recolorEvents = recolorEvents2;
29506
+ var drawSwimlaneIcon3 = drawSwimlaneIcon4, relayout = relayout2, drawLegend = drawLegend2, recolorEvents = recolorEvents2;
29014
29507
  const legendY = title ? 50 : 10;
29015
29508
  const legendGroups = parsed.timelineTagGroups.map((g) => {
29016
29509
  const pillW = measureLegendText(g.name, LG_PILL_FONT_SIZE) + LG_PILL_PAD;
@@ -29970,19 +30463,13 @@ async function renderForExport(content, theme, palette, orgExportState, options)
29970
30463
  container2.style.position = "absolute";
29971
30464
  container2.style.left = "-9999px";
29972
30465
  document.body.appendChild(container2);
29973
- renderKanban2(
29974
- container2,
29975
- kanbanParsed,
29976
- effectivePalette2,
29977
- theme === "dark",
29978
- void 0,
29979
- void 0,
29980
- resolveActiveTagGroup(
30466
+ renderKanban2(container2, kanbanParsed, effectivePalette2, theme === "dark", {
30467
+ activeTagGroup: resolveActiveTagGroup(
29981
30468
  kanbanParsed.tagGroups,
29982
30469
  kanbanParsed.options["active-tag"],
29983
30470
  options?.tagGroup
29984
30471
  )
29985
- );
30472
+ });
29986
30473
  return finalizeSvgExport(container2, theme, effectivePalette2, options);
29987
30474
  }
29988
30475
  if (detectedType === "class") {
@@ -30594,7 +31081,11 @@ function parseQuadrant(content) {
30594
31081
  if (labelMatch) {
30595
31082
  const label = {
30596
31083
  text: labelMatch[1].trim(),
30597
- color: labelMatch[2] ? resolveColor(labelMatch[2].trim()) : null,
31084
+ color: labelMatch[2] ? resolveColorWithDiagnostic(
31085
+ labelMatch[2].trim(),
31086
+ lineNumber,
31087
+ result.diagnostics
31088
+ ) ?? null : null,
30598
31089
  lineNumber
30599
31090
  };
30600
31091
  if (position === "top-right") result.quadrants.topRight = label;
@@ -31274,10 +31765,14 @@ function encodeDiagramUrl(dsl, options) {
31274
31765
  if (options?.viewState?.theme && options.viewState.theme !== "dark") {
31275
31766
  hash += `&th=${encodeURIComponent(options.viewState.theme)}`;
31276
31767
  }
31768
+ if (options?.filename) {
31769
+ hash += `&fn=${encodeURIComponent(options.filename)}`;
31770
+ }
31277
31771
  return { url: `${baseUrl}?${hash}#${hash}` };
31278
31772
  }
31279
31773
  function decodeDiagramUrl(hash) {
31280
31774
  const empty = { dsl: "", viewState: {} };
31775
+ let filename;
31281
31776
  if (!hash) return empty;
31282
31777
  let raw = hash;
31283
31778
  if (raw.startsWith("#") || raw.startsWith("?")) {
@@ -31306,16 +31801,17 @@ function decodeDiagramUrl(hash) {
31306
31801
  if (key === "pal" && val) viewState.palette = val;
31307
31802
  if (key === "th" && (val === "light" || val === "dark"))
31308
31803
  viewState.theme = val;
31804
+ if (key === "fn" && val) filename = val;
31309
31805
  }
31310
31806
  if (payload.startsWith("dgmo=")) {
31311
31807
  payload = payload.slice(5);
31312
31808
  }
31313
- if (!payload) return { dsl: "", viewState };
31809
+ if (!payload) return { dsl: "", viewState, filename };
31314
31810
  try {
31315
31811
  const result = decompressFromEncodedURIComponent(payload);
31316
- return { dsl: result ?? "", viewState };
31812
+ return { dsl: result ?? "", viewState, filename };
31317
31813
  } catch {
31318
- return { dsl: "", viewState };
31814
+ return { dsl: "", viewState, filename };
31319
31815
  }
31320
31816
  }
31321
31817
 
@@ -32072,6 +32568,7 @@ export {
32072
32568
  LEGEND_HEIGHT,
32073
32569
  METADATA_KEY_SET,
32074
32570
  PIPE_METADATA,
32571
+ RECOGNIZED_COLOR_NAMES,
32075
32572
  RULE_COUNT,
32076
32573
  addDurationToDate,
32077
32574
  applyGroupOrdering,
@@ -32126,6 +32623,7 @@ export {
32126
32623
  injectBranding,
32127
32624
  isArchiveColumn,
32128
32625
  isExtendedChartType,
32626
+ isRecognizedColorName,
32129
32627
  isSequenceBlock,
32130
32628
  isSequenceNote,
32131
32629
  isValidHex,
@@ -32217,6 +32715,7 @@ export {
32217
32715
  renderVenn,
32218
32716
  renderWordCloud,
32219
32717
  resolveColor,
32718
+ resolveColorWithDiagnostic,
32220
32719
  resolveOrgImports,
32221
32720
  resolveTaskName,
32222
32721
  rollUpContextRelationships,