@diagrammo/dgmo 0.8.14 → 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,18 +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
- color = inferArrowColor(label);
4045
+ const inferred = inferArrowColor(label);
4046
+ if (inferred)
4047
+ color = resolveColorWithDiagnostic(
4048
+ inferred,
4049
+ lineNumber,
4050
+ diagnostics,
4051
+ palette
4052
+ );
3944
4053
  }
3945
4054
  return { label, color };
3946
4055
  }
@@ -4017,7 +4126,12 @@ function parseFlowchart(content, palette) {
4017
4126
  for (let i = 0; i < segments.length; i++) {
4018
4127
  const seg = segments[i];
4019
4128
  if (seg === "->" || /^-.+->$/.test(seg)) {
4020
- pendingArrow = parseArrowToken(seg, palette);
4129
+ pendingArrow = parseArrowToken(
4130
+ seg,
4131
+ palette,
4132
+ lineNumber,
4133
+ result.diagnostics
4134
+ );
4021
4135
  continue;
4022
4136
  }
4023
4137
  const ref = parseNodeRef(seg, palette);
@@ -4211,15 +4325,27 @@ function splitArrows2(line10) {
4211
4325
  if (remaining) segments.push(remaining);
4212
4326
  return segments;
4213
4327
  }
4214
- function parseArrowToken2(token, palette) {
4328
+ function parseArrowToken2(token, palette, lineNumber, diagnostics) {
4215
4329
  if (token === "->") return {};
4216
4330
  const colorOnly = token.match(/^-\(([^)]+)\)->$/);
4217
4331
  if (colorOnly)
4218
- 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
+ };
4219
4340
  const m = token.match(/^-(.+?)(?:\(([^)]+)\))?->$/);
4220
4341
  if (m) {
4221
4342
  const label = m[1]?.trim() || void 0;
4222
- 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;
4223
4349
  return { label, color };
4224
4350
  }
4225
4351
  return {};
@@ -4323,7 +4449,12 @@ function parseState(content, palette) {
4323
4449
  if (groupMatch && groupMatch[1].trim() !== "*") {
4324
4450
  const groupLabel = groupMatch[1].trim();
4325
4451
  const groupColorName = groupMatch[2]?.trim();
4326
- 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;
4327
4458
  currentGroup = {
4328
4459
  id: `group:${groupLabel.toLowerCase()}`,
4329
4460
  label: groupLabel,
@@ -4381,7 +4512,12 @@ function parseState(content, palette) {
4381
4512
  for (let j = 0; j < segments.length; j++) {
4382
4513
  const seg = segments[j];
4383
4514
  if (seg === "->" || /^-.+->$/.test(seg)) {
4384
- pendingArrow = parseArrowToken2(seg, palette);
4515
+ pendingArrow = parseArrowToken2(
4516
+ seg,
4517
+ palette,
4518
+ lineNumber,
4519
+ result.diagnostics
4520
+ );
4385
4521
  continue;
4386
4522
  }
4387
4523
  const ref = parseStateNodeRef(seg, palette);
@@ -4644,7 +4780,12 @@ function parseClassDiagram(content, palette) {
4644
4780
  const bracketModifier = classDecl[5];
4645
4781
  const modifier = prefixModifier ?? bracketModifier;
4646
4782
  const colorName = classDecl[6]?.trim();
4647
- 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;
4648
4789
  const node = getOrCreateClass(name, lineNumber);
4649
4790
  if (modifier) node.modifier = modifier;
4650
4791
  if (color) node.color = color;
@@ -5014,7 +5155,12 @@ function parseERDiagram(content, palette) {
5014
5155
  if (tableDecl) {
5015
5156
  const name = tableDecl[1];
5016
5157
  const colorName = tableDecl[2]?.trim();
5017
- 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;
5018
5164
  const table = getOrCreateTable(name, lineNumber);
5019
5165
  if (color) table.color = color;
5020
5166
  table.lineNumber = lineNumber;
@@ -5142,7 +5288,7 @@ var init_parser3 = __esm({
5142
5288
  init_parsing();
5143
5289
  init_tag_groups();
5144
5290
  TABLE_DECL_RE = /^([a-zA-Z_]\w*)(?:\s*\(([^)]+)\))?(?:\s*\|(.+))?$/;
5145
- 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*$/;
5146
5292
  CONSTRAINT_MAP = {
5147
5293
  pk: "pk",
5148
5294
  fk: "fk",
@@ -5231,7 +5377,12 @@ function parseChart(content, palette) {
5231
5377
  rawEras.push({
5232
5378
  start: eraMatch[1].trim(),
5233
5379
  afterArrow,
5234
- 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,
5235
5386
  lineNumber
5236
5387
  });
5237
5388
  }
@@ -5280,11 +5431,22 @@ function parseChart(content, palette) {
5280
5431
  continue;
5281
5432
  }
5282
5433
  if (firstToken === "color") {
5283
- result.color = resolveColor(value.trim(), palette) ?? void 0;
5434
+ result.color = resolveColorWithDiagnostic(
5435
+ value.trim(),
5436
+ lineNumber,
5437
+ result.diagnostics,
5438
+ palette
5439
+ );
5284
5440
  continue;
5285
5441
  }
5286
5442
  if (firstToken === "series") {
5287
- const parsed = parseSeriesNames(value, lines, i, palette);
5443
+ const parsed = parseSeriesNames(
5444
+ value,
5445
+ lines,
5446
+ i,
5447
+ palette,
5448
+ result.diagnostics
5449
+ );
5288
5450
  i = parsed.newIndex;
5289
5451
  result.series = parsed.series;
5290
5452
  result.seriesLineNumber = lineNumber;
@@ -5706,7 +5868,12 @@ function parseExtendedChart(content, palette) {
5706
5868
  const categoryMatch = trimmed.match(/^\[(.+?)\](?:\s*\(([^)]+)\))?\s*$/);
5707
5869
  if (categoryMatch) {
5708
5870
  const catName = categoryMatch[1].trim();
5709
- 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;
5710
5877
  if (catColor) {
5711
5878
  if (!result.categoryColors) result.categoryColors = {};
5712
5879
  result.categoryColors[catName] = catColor;
@@ -5734,7 +5901,12 @@ function parseExtendedChart(content, palette) {
5734
5901
  if (sourceColor) result.nodeColors[source] = sourceColor;
5735
5902
  if (targetColor) result.nodeColors[target] = targetColor;
5736
5903
  }
5737
- 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;
5738
5910
  if (!result.links) result.links = [];
5739
5911
  result.links.push({
5740
5912
  source,
@@ -5760,7 +5932,12 @@ function parseExtendedChart(content, palette) {
5760
5932
  const dataRow2 = parseDataRowValues(strippedLine);
5761
5933
  if (dataRow2 && dataRow2.values.length === 1) {
5762
5934
  const source = sankeyStack.at(-1).name;
5763
- 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;
5764
5941
  const { label: target, color: targetColor } = extractColor(
5765
5942
  dataRow2.label,
5766
5943
  palette
@@ -8228,7 +8405,12 @@ function parseKanban(content, palette) {
8228
8405
  currentCard = null;
8229
8406
  columnCounter++;
8230
8407
  const colName = columnMatch[1].trim();
8231
- 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;
8232
8414
  let wipLimit;
8233
8415
  const columnMetadata = {};
8234
8416
  const pipeStr = columnMatch[3];
@@ -8276,7 +8458,8 @@ function parseKanban(content, palette) {
8276
8458
  lineNumber,
8277
8459
  cardCounter,
8278
8460
  aliasMap,
8279
- palette
8461
+ palette,
8462
+ result.diagnostics
8280
8463
  );
8281
8464
  if (currentColumn.metadata) {
8282
8465
  for (const [key, value] of Object.entries(currentColumn.metadata)) {
@@ -8330,7 +8513,7 @@ function parseKanban(content, palette) {
8330
8513
  validateTagGroupNames(result.tagGroups, warn);
8331
8514
  return result;
8332
8515
  }
8333
- function parseCardLine(trimmed, lineNumber, counter, aliasMap, palette) {
8516
+ function parseCardLine(trimmed, lineNumber, counter, aliasMap, palette, diagnostics) {
8334
8517
  const pipeIdx = trimmed.indexOf("|");
8335
8518
  let rawTitle;
8336
8519
  let tagsStr = null;
@@ -8340,7 +8523,12 @@ function parseCardLine(trimmed, lineNumber, counter, aliasMap, palette) {
8340
8523
  } else {
8341
8524
  rawTitle = trimmed;
8342
8525
  }
8343
- const { label: title, color } = extractColor(rawTitle, palette);
8526
+ const { label: title, color } = extractColor(
8527
+ rawTitle,
8528
+ palette,
8529
+ diagnostics,
8530
+ lineNumber
8531
+ );
8344
8532
  const tags = {};
8345
8533
  if (tagsStr) {
8346
8534
  for (const part of tagsStr.split(",")) {
@@ -9075,7 +9263,7 @@ __export(parser_exports7, {
9075
9263
  looksLikeSitemap: () => looksLikeSitemap,
9076
9264
  parseSitemap: () => parseSitemap
9077
9265
  });
9078
- function parseArrowLine(trimmed, palette) {
9266
+ function parseArrowLine(trimmed, palette, lineNumber, diagnostics) {
9079
9267
  const bareMatch = trimmed.match(BARE_ARROW_RE);
9080
9268
  if (bareMatch) {
9081
9269
  const rawTarget = bareMatch[1].trim();
@@ -9088,9 +9276,21 @@ function parseArrowLine(trimmed, palette) {
9088
9276
  const arrowMatch = trimmed.match(ARROW_RE);
9089
9277
  if (arrowMatch) {
9090
9278
  const label = arrowMatch[1]?.trim() || void 0;
9091
- 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;
9092
9285
  if (label && !color) {
9093
- color = inferArrowColor(label);
9286
+ const inferred = inferArrowColor(label);
9287
+ if (inferred)
9288
+ color = resolveColorWithDiagnostic(
9289
+ inferred,
9290
+ lineNumber,
9291
+ diagnostics,
9292
+ palette
9293
+ );
9094
9294
  }
9095
9295
  const rawTarget = arrowMatch[3].trim();
9096
9296
  const groupMatch = rawTarget.match(/^\[(.+)\]$/);
@@ -9254,7 +9454,12 @@ function parseSitemap(content, palette) {
9254
9454
  contentStarted = true;
9255
9455
  currentTagGroup = null;
9256
9456
  const indent = measureIndent(line10);
9257
- const arrowInfo = parseArrowLine(trimmed, palette);
9457
+ const arrowInfo = parseArrowLine(
9458
+ trimmed,
9459
+ palette,
9460
+ lineNumber,
9461
+ result.diagnostics
9462
+ );
9258
9463
  if (arrowInfo) {
9259
9464
  const source = findParentNode(indent, indentStack);
9260
9465
  if (!source) {
@@ -9275,7 +9480,12 @@ function parseSitemap(content, palette) {
9275
9480
  const metadataMatch = trimmed.includes("|") ? null : trimmed.match(METADATA_RE3);
9276
9481
  if (containerMatch) {
9277
9482
  const rawLabel = containerMatch[1].trim();
9278
- const { label, color } = extractColor(rawLabel, palette);
9483
+ const { label, color } = extractColor(
9484
+ rawLabel,
9485
+ palette,
9486
+ result.diagnostics,
9487
+ lineNumber
9488
+ );
9279
9489
  const pipeStr = containerMatch[2];
9280
9490
  const containerMetadata = {};
9281
9491
  if (pipeStr) {
@@ -9316,7 +9526,8 @@ function parseSitemap(content, palette) {
9316
9526
  palette,
9317
9527
  ++nodeCounter,
9318
9528
  aliasMap,
9319
- pushWarning
9529
+ pushWarning,
9530
+ result.diagnostics
9320
9531
  );
9321
9532
  attachNode2(node, indent, indentStack, result);
9322
9533
  labelToNode.set(node.label.toLowerCase(), node);
@@ -9330,7 +9541,8 @@ function parseSitemap(content, palette) {
9330
9541
  palette,
9331
9542
  ++nodeCounter,
9332
9543
  aliasMap,
9333
- pushWarning
9544
+ pushWarning,
9545
+ result.diagnostics
9334
9546
  );
9335
9547
  attachNode2(node, indent, indentStack, result);
9336
9548
  labelToNode.set(node.label.toLowerCase(), node);
@@ -9393,10 +9605,15 @@ function parseSitemap(content, palette) {
9393
9605
  }
9394
9606
  return result;
9395
9607
  }
9396
- function parseNodeLabel2(trimmed, lineNumber, palette, counter, aliasMap = /* @__PURE__ */ new Map(), warnFn) {
9608
+ function parseNodeLabel2(trimmed, lineNumber, palette, counter, aliasMap = /* @__PURE__ */ new Map(), warnFn, diagnostics) {
9397
9609
  const segments = trimmed.split("|").map((s) => s.trim());
9398
9610
  const rawLabel = segments[0];
9399
- const { label, color } = extractColor(rawLabel, palette);
9611
+ const { label, color } = extractColor(
9612
+ rawLabel,
9613
+ palette,
9614
+ diagnostics,
9615
+ lineNumber
9616
+ );
9400
9617
  const metadata = parsePipeMetadata(
9401
9618
  segments,
9402
9619
  aliasMap,
@@ -9688,9 +9905,13 @@ function parseInfra(content) {
9688
9905
  const tvMatch = cleanEntry.match(TAG_VALUE_RE);
9689
9906
  if (tvMatch) {
9690
9907
  const valueName = tvMatch[1].trim();
9908
+ const rawColor = tvMatch[2]?.trim();
9909
+ if (rawColor) {
9910
+ resolveColorWithDiagnostic(rawColor, lineNumber, result.diagnostics);
9911
+ }
9691
9912
  currentTagGroup.values.push({
9692
9913
  name: valueName,
9693
- color: tvMatch[2]?.trim()
9914
+ color: rawColor
9694
9915
  });
9695
9916
  if (isDefault) {
9696
9917
  currentTagGroup.defaultValue = valueName;
@@ -10042,6 +10263,7 @@ var init_parser8 = __esm({
10042
10263
  "src/infra/parser.ts"() {
10043
10264
  "use strict";
10044
10265
  init_diagnostics();
10266
+ init_colors();
10045
10267
  init_parsing();
10046
10268
  init_tag_groups();
10047
10269
  init_types();
@@ -10103,9 +10325,11 @@ function buildHolidaySet(holidays) {
10103
10325
  function addBusinessDays(startDate, count, workweek, holidaySet, direction = 1) {
10104
10326
  const days = Math.round(Math.abs(count));
10105
10327
  if (days === 0) return new Date(startDate);
10328
+ if (Number.isNaN(startDate.getTime())) return new Date(startDate);
10106
10329
  const current = new Date(startDate);
10107
10330
  let remaining = days;
10108
- while (remaining > 0) {
10331
+ let safety = days * 14 + 14;
10332
+ while (remaining > 0 && safety-- > 0) {
10109
10333
  current.setDate(current.getDate() + direction);
10110
10334
  if (isWorkday(current, workweek, holidaySet)) {
10111
10335
  remaining--;
@@ -14210,6 +14434,7 @@ var init_mutations = __esm({
14210
14434
  // src/kanban/renderer.ts
14211
14435
  var renderer_exports3 = {};
14212
14436
  __export(renderer_exports3, {
14437
+ bucketCardsBySwimlane: () => bucketCardsBySwimlane,
14213
14438
  renderKanban: () => renderKanban,
14214
14439
  renderKanbanForExport: () => renderKanbanForExport
14215
14440
  });
@@ -14305,7 +14530,14 @@ function computeLayout(parsed, _palette) {
14305
14530
  const totalHeight = startY + maxColumnHeight + DIAGRAM_PADDING3;
14306
14531
  return { columns: columnLayouts, totalWidth, totalHeight };
14307
14532
  }
14308
- 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;
14309
14541
  const layout = computeLayout(parsed, palette);
14310
14542
  const width = exportDims?.width ?? layout.totalWidth;
14311
14543
  const height = exportDims?.height ?? layout.totalHeight;
@@ -14325,16 +14557,49 @@ function renderKanban(container, parsed, palette, isDark, _onNavigateToLine, exp
14325
14557
  };
14326
14558
  const legendState = { activeGroup: activeTagGroup ?? null };
14327
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;
14328
14581
  renderLegendD3(
14329
14582
  legendG,
14330
14583
  legendConfig,
14331
14584
  legendState,
14332
14585
  palette,
14333
14586
  isDark,
14334
- void 0,
14587
+ legendCallbacks,
14335
14588
  width - legendX - DIAGRAM_PADDING3
14336
14589
  );
14337
14590
  }
14591
+ if (swimlaneGroup) {
14592
+ renderSwimlaneBoard(
14593
+ svg,
14594
+ parsed,
14595
+ layout,
14596
+ swimlaneGroup,
14597
+ palette,
14598
+ isDark,
14599
+ activeTagGroup
14600
+ );
14601
+ return;
14602
+ }
14338
14603
  const defaultColBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
14339
14604
  const defaultColHeaderBg = isDark ? mix(palette.surface, palette.bg, 70) : mix(palette.surface, palette.bg, 50);
14340
14605
  const cardBaseBg = isDark ? palette.surface : palette.bg;
@@ -14411,14 +14676,204 @@ function renderKanbanForExport(content, theme, palette) {
14411
14676
  const isDark = theme === "dark";
14412
14677
  const layout = computeLayout(parsed, palette);
14413
14678
  const container = document.createElement("div");
14414
- renderKanban(container, parsed, palette, isDark, void 0, {
14415
- width: layout.totalWidth,
14416
- height: layout.totalHeight
14679
+ renderKanban(container, parsed, palette, isDark, {
14680
+ exportDims: { width: layout.totalWidth, height: layout.totalHeight }
14417
14681
  });
14418
14682
  const svgEl = container.querySelector("svg");
14419
14683
  return svgEl?.outerHTML ?? "";
14420
14684
  }
14421
- 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;
14422
14877
  var init_renderer3 = __esm({
14423
14878
  "src/kanban/renderer.ts"() {
14424
14879
  "use strict";
@@ -14450,6 +14905,8 @@ var init_renderer3 = __esm({
14450
14905
  WIP_FONT_SIZE = 10;
14451
14906
  COLUMN_RADIUS = 8;
14452
14907
  COLUMN_HEADER_RADIUS = 8;
14908
+ LANE_HEADER_WIDTH = 140;
14909
+ LANE_GAP = 14;
14453
14910
  }
14454
14911
  });
14455
14912
 
@@ -23594,7 +24051,7 @@ function resetHighlightAll(svg, chartG) {
23594
24051
  chartG.selectAll(".gantt-lane-band, .gantt-lane-accent").attr("opacity", 1);
23595
24052
  chartG.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").attr("opacity", 0.5);
23596
24053
  }
23597
- function drawSwimlaneIcon(parent, x, y, isActive, palette) {
24054
+ function drawSwimlaneIcon2(parent, x, y, isActive, palette) {
23598
24055
  const iconG = parent.append("g").attr("class", "gantt-swimlane-icon").attr("transform", `translate(${x}, ${y})`);
23599
24056
  const color = isActive ? palette.primary : palette.textMuted;
23600
24057
  const opacity = isActive ? 1 : 0.35;
@@ -23751,7 +24208,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
23751
24208
  const pillXOff = LEGEND_CAPSULE_PAD;
23752
24209
  const iconX = pillXOff + textW + 3;
23753
24210
  const iconY = (LEGEND_HEIGHT - 10) / 2;
23754
- const iconEl = drawSwimlaneIcon(
24211
+ const iconEl = drawSwimlaneIcon2(
23755
24212
  groupEl,
23756
24213
  iconX,
23757
24214
  iconY,
@@ -25178,10 +25635,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
25178
25635
  );
25179
25636
  if (tg) {
25180
25637
  for (const entry of tg.entries) {
25181
- tagValueToColor.set(
25182
- entry.value.toLowerCase(),
25183
- resolveColor(entry.color) ?? entry.color
25184
- );
25638
+ tagValueToColor.set(entry.value.toLowerCase(), entry.color);
25185
25639
  }
25186
25640
  }
25187
25641
  }
@@ -25587,7 +26041,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
25587
26041
  name: tg.name,
25588
26042
  entries: tg.entries.map((e) => ({
25589
26043
  value: e.value,
25590
- color: resolveColor(e.color) ?? e.color
26044
+ color: e.color
25591
26045
  }))
25592
26046
  }));
25593
26047
  const legendConfig = {
@@ -26167,7 +26621,6 @@ var init_renderer10 = __esm({
26167
26621
  init_color_utils();
26168
26622
  init_inline_markdown();
26169
26623
  init_fonts();
26170
- init_colors();
26171
26624
  init_parser();
26172
26625
  init_tag_resolution();
26173
26626
  init_tag_groups();
@@ -26478,12 +26931,22 @@ function parseVisualization(content, palette) {
26478
26931
  if (groupMatch) {
26479
26932
  if (result.type === "arc") {
26480
26933
  const name = groupMatch[1].trim();
26481
- 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;
26482
26940
  result.arcNodeGroups.push({ name, nodes: [], color, lineNumber });
26483
26941
  currentArcGroup = name;
26484
26942
  } else if (result.type === "timeline") {
26485
26943
  const name = groupMatch[1].trim();
26486
- 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;
26487
26950
  result.timelineGroups.push({ name, color, lineNumber });
26488
26951
  currentTimelineGroup = name;
26489
26952
  }
@@ -26511,7 +26974,12 @@ function parseVisualization(content, palette) {
26511
26974
  if (linkMatch) {
26512
26975
  const source = linkMatch[1].trim();
26513
26976
  const target = linkMatch[2].trim();
26514
- 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;
26515
26983
  result.links.push({
26516
26984
  source,
26517
26985
  target,
@@ -26548,7 +27016,12 @@ function parseVisualization(content, palette) {
26548
27016
  startDate: eraEntryMatch[1],
26549
27017
  endDate: eraEntryMatch[2],
26550
27018
  label: eraEntryMatch[3].trim(),
26551
- color: colorAnnotation ? resolveColor(colorAnnotation, palette) : null,
27019
+ color: colorAnnotation ? resolveColorWithDiagnostic(
27020
+ colorAnnotation,
27021
+ lineNumber,
27022
+ result.diagnostics,
27023
+ palette
27024
+ ) ?? null : null,
26552
27025
  lineNumber
26553
27026
  });
26554
27027
  } else {
@@ -26570,7 +27043,12 @@ function parseVisualization(content, palette) {
26570
27043
  result.timelineMarkers.push({
26571
27044
  date: markerEntryMatch[1],
26572
27045
  label: markerEntryMatch[2].trim(),
26573
- color: colorAnnotation ? resolveColor(colorAnnotation, palette) : null,
27046
+ color: colorAnnotation ? resolveColorWithDiagnostic(
27047
+ colorAnnotation,
27048
+ lineNumber,
27049
+ result.diagnostics,
27050
+ palette
27051
+ ) ?? null : null,
26574
27052
  lineNumber
26575
27053
  });
26576
27054
  } else {
@@ -26599,7 +27077,12 @@ function parseVisualization(content, palette) {
26599
27077
  startDate: eraMatch[1],
26600
27078
  endDate: eraMatch[2],
26601
27079
  label: eraMatch[3].trim(),
26602
- color: colorAnnotation ? resolveColor(colorAnnotation, palette) : null,
27080
+ color: colorAnnotation ? resolveColorWithDiagnostic(
27081
+ colorAnnotation,
27082
+ lineNumber,
27083
+ result.diagnostics,
27084
+ palette
27085
+ ) ?? null : null,
26603
27086
  lineNumber
26604
27087
  });
26605
27088
  continue;
@@ -26612,7 +27095,12 @@ function parseVisualization(content, palette) {
26612
27095
  result.timelineMarkers.push({
26613
27096
  date: markerMatch[1],
26614
27097
  label: markerMatch[2].trim(),
26615
- color: colorAnnotation ? resolveColor(colorAnnotation, palette) : null,
27098
+ color: colorAnnotation ? resolveColorWithDiagnostic(
27099
+ colorAnnotation,
27100
+ lineNumber,
27101
+ result.diagnostics,
27102
+ palette
27103
+ ) ?? null : null,
26616
27104
  lineNumber
26617
27105
  });
26618
27106
  continue;
@@ -26727,20 +27215,12 @@ function parseVisualization(content, palette) {
26727
27215
  const colorName = setDeclMatch[2]?.trim() ?? null;
26728
27216
  let color = null;
26729
27217
  if (colorName) {
26730
- const resolved = resolveColor(colorName, palette);
26731
- if (resolved === null) {
26732
- warn(
26733
- lineNumber,
26734
- `Hex colors are not supported \u2014 use named colors (blue, red, green, etc.)`
26735
- );
26736
- } else if (resolved.startsWith("#")) {
26737
- color = resolved;
26738
- } else {
26739
- warn(
26740
- lineNumber,
26741
- `Unknown color "${colorName}" on set "${name}". Using auto-assigned color.`
26742
- );
26743
- }
27218
+ color = resolveColorWithDiagnostic(
27219
+ colorName,
27220
+ lineNumber,
27221
+ result.diagnostics,
27222
+ palette
27223
+ ) ?? null;
26744
27224
  }
26745
27225
  const alias = setDeclMatch[3]?.trim() ?? null;
26746
27226
  result.vennSets.push({ name, alias, color, lineNumber });
@@ -26789,7 +27269,12 @@ function parseVisualization(content, palette) {
26789
27269
  const labelPart = quadrantMatch[2].trim();
26790
27270
  const labelColorMatch = labelPart.match(/^(.+?)\s*\(([^)]+)\)\s*$/);
26791
27271
  const text = labelColorMatch ? labelColorMatch[1].trim() : labelPart;
26792
- 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;
26793
27278
  const label = { text, color, lineNumber };
26794
27279
  if (position === "top-right") result.quadrantLabels.topRight = label;
26795
27280
  else if (position === "top-left") result.quadrantLabels.topLeft = label;
@@ -26947,7 +27432,12 @@ function parseVisualization(content, palette) {
26947
27432
  }
26948
27433
  const colorMatch = joinedLabel.match(/^(.+?)\(([^)]+)\)\s*$/);
26949
27434
  const labelPart = colorMatch ? colorMatch[1].trim() : joinedLabel;
26950
- 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;
26951
27441
  if (!labelPart) {
26952
27442
  warn(
26953
27443
  lineNumber,
@@ -27018,7 +27508,12 @@ function parseVisualization(content, palette) {
27018
27508
  continue;
27019
27509
  }
27020
27510
  const labelPart = colorMatch ? colorMatch[1].trim() : rawKey;
27021
- 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;
27022
27517
  const valuePart = line10.substring(colonIndex + 1).trim();
27023
27518
  const values = valuePart.split(",").map((v) => v.trim());
27024
27519
  const numericValues = [];
@@ -28866,7 +29361,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
28866
29361
  const mainSvg = d3Selection13.select(container).select("svg");
28867
29362
  const mainG = mainSvg.select("g");
28868
29363
  if (!mainSvg.empty() && !mainG.empty()) {
28869
- let drawSwimlaneIcon3 = function(parent, x, y, isSwimActive) {
29364
+ let drawSwimlaneIcon4 = function(parent, x, y, isSwimActive) {
28870
29365
  const iconG = parent.append("g").attr("class", "tl-swimlane-icon").attr("transform", `translate(${x}, ${y})`).style("cursor", "pointer");
28871
29366
  const barColor = isSwimActive ? palette.primary : palette.textMuted;
28872
29367
  const barOpacity = isSwimActive ? 1 : 0.35;
@@ -28958,7 +29453,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
28958
29453
  const pillXOff = LG_CAPSULE_PAD;
28959
29454
  const iconX = pillXOff + pillWidth3 + 5;
28960
29455
  const iconY = (LG_HEIGHT - 10) / 2;
28961
- const iconEl = drawSwimlaneIcon3(
29456
+ const iconEl = drawSwimlaneIcon4(
28962
29457
  groupEl,
28963
29458
  iconX,
28964
29459
  iconY,
@@ -29008,7 +29503,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
29008
29503
  el.selectAll("circle:not(.tl-event-point-outline)").attr("fill", mix(color, bg, 30)).attr("stroke", color);
29009
29504
  });
29010
29505
  };
29011
- var drawSwimlaneIcon2 = drawSwimlaneIcon3, relayout = relayout2, drawLegend = drawLegend2, recolorEvents = recolorEvents2;
29506
+ var drawSwimlaneIcon3 = drawSwimlaneIcon4, relayout = relayout2, drawLegend = drawLegend2, recolorEvents = recolorEvents2;
29012
29507
  const legendY = title ? 50 : 10;
29013
29508
  const legendGroups = parsed.timelineTagGroups.map((g) => {
29014
29509
  const pillW = measureLegendText(g.name, LG_PILL_FONT_SIZE) + LG_PILL_PAD;
@@ -29968,19 +30463,13 @@ async function renderForExport(content, theme, palette, orgExportState, options)
29968
30463
  container2.style.position = "absolute";
29969
30464
  container2.style.left = "-9999px";
29970
30465
  document.body.appendChild(container2);
29971
- renderKanban2(
29972
- container2,
29973
- kanbanParsed,
29974
- effectivePalette2,
29975
- theme === "dark",
29976
- void 0,
29977
- void 0,
29978
- resolveActiveTagGroup(
30466
+ renderKanban2(container2, kanbanParsed, effectivePalette2, theme === "dark", {
30467
+ activeTagGroup: resolveActiveTagGroup(
29979
30468
  kanbanParsed.tagGroups,
29980
30469
  kanbanParsed.options["active-tag"],
29981
30470
  options?.tagGroup
29982
30471
  )
29983
- );
30472
+ });
29984
30473
  return finalizeSvgExport(container2, theme, effectivePalette2, options);
29985
30474
  }
29986
30475
  if (detectedType === "class") {
@@ -30592,7 +31081,11 @@ function parseQuadrant(content) {
30592
31081
  if (labelMatch) {
30593
31082
  const label = {
30594
31083
  text: labelMatch[1].trim(),
30595
- 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,
30596
31089
  lineNumber
30597
31090
  };
30598
31091
  if (position === "top-right") result.quadrants.topRight = label;
@@ -31272,10 +31765,14 @@ function encodeDiagramUrl(dsl, options) {
31272
31765
  if (options?.viewState?.theme && options.viewState.theme !== "dark") {
31273
31766
  hash += `&th=${encodeURIComponent(options.viewState.theme)}`;
31274
31767
  }
31768
+ if (options?.filename) {
31769
+ hash += `&fn=${encodeURIComponent(options.filename)}`;
31770
+ }
31275
31771
  return { url: `${baseUrl}?${hash}#${hash}` };
31276
31772
  }
31277
31773
  function decodeDiagramUrl(hash) {
31278
31774
  const empty = { dsl: "", viewState: {} };
31775
+ let filename;
31279
31776
  if (!hash) return empty;
31280
31777
  let raw = hash;
31281
31778
  if (raw.startsWith("#") || raw.startsWith("?")) {
@@ -31304,16 +31801,17 @@ function decodeDiagramUrl(hash) {
31304
31801
  if (key === "pal" && val) viewState.palette = val;
31305
31802
  if (key === "th" && (val === "light" || val === "dark"))
31306
31803
  viewState.theme = val;
31804
+ if (key === "fn" && val) filename = val;
31307
31805
  }
31308
31806
  if (payload.startsWith("dgmo=")) {
31309
31807
  payload = payload.slice(5);
31310
31808
  }
31311
- if (!payload) return { dsl: "", viewState };
31809
+ if (!payload) return { dsl: "", viewState, filename };
31312
31810
  try {
31313
31811
  const result = decompressFromEncodedURIComponent(payload);
31314
- return { dsl: result ?? "", viewState };
31812
+ return { dsl: result ?? "", viewState, filename };
31315
31813
  } catch {
31316
- return { dsl: "", viewState };
31814
+ return { dsl: "", viewState, filename };
31317
31815
  }
31318
31816
  }
31319
31817
 
@@ -32070,6 +32568,7 @@ export {
32070
32568
  LEGEND_HEIGHT,
32071
32569
  METADATA_KEY_SET,
32072
32570
  PIPE_METADATA,
32571
+ RECOGNIZED_COLOR_NAMES,
32073
32572
  RULE_COUNT,
32074
32573
  addDurationToDate,
32075
32574
  applyGroupOrdering,
@@ -32124,6 +32623,7 @@ export {
32124
32623
  injectBranding,
32125
32624
  isArchiveColumn,
32126
32625
  isExtendedChartType,
32626
+ isRecognizedColorName,
32127
32627
  isSequenceBlock,
32128
32628
  isSequenceNote,
32129
32629
  isValidHex,
@@ -32215,6 +32715,7 @@ export {
32215
32715
  renderVenn,
32216
32716
  renderWordCloud,
32217
32717
  resolveColor,
32718
+ resolveColorWithDiagnostic,
32218
32719
  resolveOrgImports,
32219
32720
  resolveTaskName,
32220
32721
  rollUpContextRelationships,