@diagrammo/dgmo 0.8.19 → 0.8.20

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
@@ -32,8 +32,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
32
32
  ));
33
33
 
34
34
  // src/diagnostics.ts
35
- function makeDgmoError(line10, message, severity = "error") {
36
- return { line: line10, message, severity };
35
+ function makeDgmoError(line10, message, severity = "error", code) {
36
+ return code !== void 0 ? { line: line10, message, severity, code } : { line: line10, message, severity };
37
37
  }
38
38
  function formatDgmoError(err) {
39
39
  return err.line > 0 ? `Line ${err.line}: ${err.message}` : err.message;
@@ -74,74 +74,227 @@ var init_diagnostics = __esm({
74
74
  }
75
75
  });
76
76
 
77
- // src/fonts.ts
78
- var FONT_FAMILY;
79
- var init_fonts = __esm({
80
- "src/fonts.ts"() {
77
+ // src/colors.ts
78
+ function isRecognizedColorName(name) {
79
+ return Object.prototype.hasOwnProperty.call(colorNames, name.toLowerCase());
80
+ }
81
+ function resolveColor(color, palette) {
82
+ if (!color) return null;
83
+ if (color.startsWith("#")) return null;
84
+ const lower = color.toLowerCase();
85
+ if (!isRecognizedColorName(lower)) return null;
86
+ if (palette) {
87
+ const named = palette.colors[lower];
88
+ if (named) return named;
89
+ }
90
+ return colorNames[lower];
91
+ }
92
+ function resolveColorWithDiagnostic(color, line10, diagnostics, palette) {
93
+ const resolved = resolveColor(color, palette);
94
+ if (resolved !== null) return resolved;
95
+ const hint = suggest(color, RECOGNIZED_COLOR_NAMES);
96
+ const suggestion = hint ? ` ${hint}` : "";
97
+ diagnostics.push(
98
+ makeDgmoError(
99
+ line10,
100
+ `Unknown color "${color}". Allowed: ${RECOGNIZED_COLOR_NAMES.join(", ")}.${suggestion}`,
101
+ "warning"
102
+ )
103
+ );
104
+ return void 0;
105
+ }
106
+ var nord, colorNames, RECOGNIZED_COLOR_NAMES, seriesColors;
107
+ var init_colors = __esm({
108
+ "src/colors.ts"() {
81
109
  "use strict";
82
- FONT_FAMILY = "Inter, system-ui, Avenir, Helvetica, Arial, sans-serif";
110
+ init_diagnostics();
111
+ nord = {
112
+ // Polar Night (dark)
113
+ nord0: "#2e3440",
114
+ nord1: "#3b4252",
115
+ nord2: "#434c5e",
116
+ nord3: "#4c566a",
117
+ // Snow Storm (light)
118
+ nord4: "#d8dee9",
119
+ nord5: "#e5e9f0",
120
+ nord6: "#eceff4",
121
+ // Frost (accent blues)
122
+ nord7: "#8fbcbb",
123
+ nord8: "#88c0d0",
124
+ nord9: "#81a1c1",
125
+ nord10: "#5e81ac",
126
+ // Aurora (colors)
127
+ nord11: "#bf616a",
128
+ // red
129
+ nord12: "#d08770",
130
+ // orange
131
+ nord13: "#ebcb8b",
132
+ // yellow
133
+ nord14: "#a3be8c",
134
+ // green
135
+ nord15: "#b48ead"
136
+ // purple
137
+ };
138
+ colorNames = {
139
+ red: nord.nord11,
140
+ orange: nord.nord12,
141
+ yellow: nord.nord13,
142
+ green: nord.nord14,
143
+ blue: nord.nord10,
144
+ purple: nord.nord15,
145
+ teal: nord.nord7,
146
+ cyan: nord.nord8,
147
+ gray: nord.nord3,
148
+ black: nord.nord0,
149
+ white: nord.nord6
150
+ };
151
+ RECOGNIZED_COLOR_NAMES = Object.freeze([
152
+ "red",
153
+ "orange",
154
+ "yellow",
155
+ "green",
156
+ "blue",
157
+ "purple",
158
+ "teal",
159
+ "cyan",
160
+ "gray",
161
+ "black",
162
+ "white"
163
+ ]);
164
+ seriesColors = [
165
+ nord.nord10,
166
+ // blue
167
+ nord.nord14,
168
+ // green
169
+ nord.nord13,
170
+ // yellow
171
+ nord.nord12,
172
+ // orange
173
+ nord.nord15,
174
+ // purple
175
+ nord.nord11,
176
+ // red
177
+ nord.nord7,
178
+ // teal
179
+ nord.nord8
180
+ // light blue
181
+ ];
83
182
  }
84
183
  });
85
184
 
86
- // src/branding.ts
87
- var branding_exports = {};
88
- __export(branding_exports, {
89
- injectBranding: () => injectBranding
90
- });
91
- function injectBranding(svgHtml, mutedColor) {
92
- if (!svgHtml) return svgHtml;
93
- const brandingText = `<text x="0" y="0" font-size="10" font-family="${FONT_FAMILY}" fill="${mutedColor}" opacity="0.5" text-anchor="end">diagrammo.app</text>`;
94
- const vbMatch = svgHtml.match(/viewBox="([^"]+)"/);
95
- const heightAttrMatch = svgHtml.match(/height="([^"]+)"/);
96
- if (vbMatch) {
97
- const parts = vbMatch[1].split(/\s+/).map(Number);
98
- if (parts.length === 4) {
99
- const [vx, vy, vw, vh] = parts;
100
- const newVh = vh + BRANDING_HEIGHT;
101
- const textX = vx + vw - 4;
102
- const textY = vy + vh + BRANDING_HEIGHT - 6;
103
- const positioned = brandingText.replace('x="0" y="0"', `x="${textX}" y="${textY}"`);
104
- let result = svgHtml.replace(
105
- /viewBox="[^"]+"/,
106
- `viewBox="${vx} ${vy} ${vw} ${newVh}"`
107
- );
108
- if (heightAttrMatch) {
109
- const oldH = parseFloat(heightAttrMatch[1]);
110
- if (!isNaN(oldH)) {
111
- result = result.replace(
112
- `height="${heightAttrMatch[1]}"`,
113
- `height="${oldH + BRANDING_HEIGHT}"`
114
- );
115
- }
116
- }
117
- result = result.replace("</svg>", `${positioned}</svg>`);
118
- return result;
119
- }
185
+ // src/utils/arrows.ts
186
+ function validateLabelCharacters(label, lineNumber) {
187
+ const out = [];
188
+ if (label.includes("->") || label.includes("~>")) {
189
+ out.push(
190
+ makeDgmoError(
191
+ lineNumber,
192
+ 'Arrow symbols (-> or ~>) are not allowed inside a label. Move the label after the arrow: "A -> B: uses -> to chain". See "In-Arrow Message Labels" \u2192 Forbidden.',
193
+ "error",
194
+ ARROW_DIAGNOSTIC_CODES.ARROW_SUBSTRING_IN_LABEL
195
+ )
196
+ );
120
197
  }
121
- if (heightAttrMatch) {
122
- const widthMatch = svgHtml.match(/width="([^"]+)"/);
123
- const w = widthMatch ? parseFloat(widthMatch[1]) : 800;
124
- const h = parseFloat(heightAttrMatch[1]);
125
- if (!isNaN(h) && !isNaN(w)) {
126
- const textX = w - 4;
127
- const textY = h + BRANDING_HEIGHT - 6;
128
- const positioned = brandingText.replace('x="0" y="0"', `x="${textX}" y="${textY}"`);
129
- let result = svgHtml.replace(
130
- `height="${heightAttrMatch[1]}"`,
131
- `height="${h + BRANDING_HEIGHT}"`
198
+ for (const ch of label) {
199
+ const cp = ch.codePointAt(0);
200
+ const isC0 = cp >= 0 && cp <= 31 && cp !== 9;
201
+ const isDel = cp === 127;
202
+ if (isC0 || isDel) {
203
+ const hex = cp.toString(16).toUpperCase().padStart(4, "0");
204
+ out.push(
205
+ makeDgmoError(
206
+ lineNumber,
207
+ `Label contains a control character (U+${hex}). Remove it and use plain text.`,
208
+ "error",
209
+ ARROW_DIAGNOSTIC_CODES.CONTROL_CHAR_IN_LABEL
210
+ )
132
211
  );
133
- result = result.replace("</svg>", `${positioned}</svg>`);
134
- return result;
212
+ break;
135
213
  }
136
214
  }
137
- return svgHtml;
215
+ return out;
216
+ }
217
+ function parseInArrowLabel(rawLabel, lineNumber) {
218
+ const trimmed = rawLabel.trim();
219
+ if (trimmed.length === 0) {
220
+ return { label: void 0, diagnostics: [] };
221
+ }
222
+ const diagnostics = validateLabelCharacters(trimmed, lineNumber);
223
+ return { label: trimmed, diagnostics };
138
224
  }
139
- var BRANDING_HEIGHT;
140
- var init_branding = __esm({
141
- "src/branding.ts"() {
225
+ function matchColorParens(content) {
226
+ const m = content.match(/^\(([A-Za-z]+)\)$/);
227
+ if (!m) return null;
228
+ const candidate = m[1].toLowerCase();
229
+ if (RECOGNIZED_COLOR_NAMES.includes(candidate)) {
230
+ return candidate;
231
+ }
232
+ return null;
233
+ }
234
+ function parseArrow(line10) {
235
+ if (BIDI_SYNC_RE.test(line10) || BIDI_ASYNC_RE.test(line10)) {
236
+ return {
237
+ error: "Bidirectional arrows are no longer supported. Use two separate lines: 'A -msg-> B' and 'B -msg-> A'"
238
+ };
239
+ }
240
+ if (RETURN_SYNC_LABELED_RE.test(line10) || RETURN_ASYNC_LABELED_RE.test(line10)) {
241
+ const m = line10.match(RETURN_SYNC_LABELED_RE) ?? line10.match(RETURN_ASYNC_LABELED_RE);
242
+ const from = m[3];
243
+ const to = m[1];
244
+ const label = m[2].trim();
245
+ return {
246
+ error: `Left-pointing arrows are no longer supported. Write '${from} -${label}-> ${to}' instead`
247
+ };
248
+ }
249
+ const patterns = [
250
+ { re: SYNC_LABELED_RE, async: false },
251
+ { re: ASYNC_LABELED_RE, async: true }
252
+ ];
253
+ for (const { re, async: isAsync } of patterns) {
254
+ const m = line10.match(re);
255
+ if (!m) continue;
256
+ const label = m[2].trim();
257
+ if (!label) return null;
258
+ return {
259
+ from: m[1],
260
+ to: m[3],
261
+ label,
262
+ async: isAsync
263
+ };
264
+ }
265
+ return null;
266
+ }
267
+ var ARROW_DIAGNOSTIC_CODES, SYNC_LABELED_RE, ASYNC_LABELED_RE, RETURN_SYNC_LABELED_RE, RETURN_ASYNC_LABELED_RE, BIDI_SYNC_RE, BIDI_ASYNC_RE;
268
+ var init_arrows = __esm({
269
+ "src/utils/arrows.ts"() {
142
270
  "use strict";
143
- init_fonts();
144
- BRANDING_HEIGHT = 20;
271
+ init_diagnostics();
272
+ init_colors();
273
+ ARROW_DIAGNOSTIC_CODES = {
274
+ /** Active: label contains `->` or `~>` substring (TD-13). */
275
+ ARROW_SUBSTRING_IN_LABEL: "E_ARROW_SUBSTRING_IN_LABEL",
276
+ /** Active: label contains a forbidden control character (TD-14). */
277
+ CONTROL_CHAR_IN_LABEL: "E_CONTROL_CHAR_IN_LABEL",
278
+ /** Reserved: not currently emitted by any parser. See JSDoc above. */
279
+ TRAILING_ARROW_TEXT: "E_TRAILING_ARROW_TEXT",
280
+ /** Reserved: not currently emitted by any parser. See JSDoc above. */
281
+ MIXED_ARROW_DELIMITERS: "E_MIXED_ARROW_DELIMITERS"
282
+ };
283
+ SYNC_LABELED_RE = /^(.+?)\s*-(.+)->\s*(.+)$/;
284
+ ASYNC_LABELED_RE = /^(.+?)\s*~(.+)~>\s*(.+)$/;
285
+ RETURN_SYNC_LABELED_RE = /^(.+?)\s*<-(.+)-\s*(.+)$/;
286
+ RETURN_ASYNC_LABELED_RE = /^(.+?)\s*<~(.+)~\s*(.+)$/;
287
+ BIDI_SYNC_RE = /^(.+?)\s*<-(.+)->\s*(.+)$/;
288
+ BIDI_ASYNC_RE = /^(.+?)\s*<~(.+)~>\s*(.+)$/;
289
+ }
290
+ });
291
+
292
+ // src/fonts.ts
293
+ var FONT_FAMILY;
294
+ var init_fonts = __esm({
295
+ "src/fonts.ts"() {
296
+ "use strict";
297
+ FONT_FAMILY = "Inter, system-ui, Avenir, Helvetica, Arial, sans-serif";
145
298
  }
146
299
  });
147
300
 
@@ -322,110 +475,158 @@ var init_label_layout = __esm({
322
475
  }
323
476
  });
324
477
 
325
- // src/colors.ts
326
- function isRecognizedColorName(name) {
327
- return Object.prototype.hasOwnProperty.call(colorNames, name.toLowerCase());
478
+ // src/utils/time-ticks.ts
479
+ function fractionalYearToDate(frac) {
480
+ const year = Math.floor(frac);
481
+ const remainder = frac - year;
482
+ const monthFrac = remainder * 12;
483
+ const month = Math.floor(monthFrac);
484
+ const monthRemainder = remainder - month / 12;
485
+ const dayFrac = monthRemainder * 365;
486
+ const day = Math.floor(dayFrac) + 1;
487
+ const dayRemainder = dayFrac - Math.floor(dayFrac);
488
+ const hourFrac = dayRemainder * 24;
489
+ const hour = Math.floor(hourFrac);
490
+ const minute = Math.round((hourFrac - hour) * 60);
491
+ return new Date(year, month, day, hour, minute);
328
492
  }
329
- function resolveColor(color, palette) {
330
- if (!color) return null;
331
- if (color.startsWith("#")) return null;
332
- const lower = color.toLowerCase();
333
- if (!isRecognizedColorName(lower)) return null;
334
- if (palette) {
335
- const named = palette.colors[lower];
336
- if (named) return named;
337
- }
338
- return colorNames[lower];
493
+ function dateToFractionalYear(d) {
494
+ return d.getFullYear() + d.getMonth() / 12 + (d.getDate() - 1) / 365 + d.getHours() / 8760 + d.getMinutes() / 525600;
339
495
  }
340
- function resolveColorWithDiagnostic(color, line10, diagnostics, palette) {
341
- const resolved = resolveColor(color, palette);
342
- if (resolved !== null) return resolved;
343
- const hint = suggest(color, RECOGNIZED_COLOR_NAMES);
344
- const suggestion = hint ? ` ${hint}` : "";
345
- diagnostics.push(
346
- makeDgmoError(
347
- line10,
348
- `Unknown color "${color}". Allowed: ${RECOGNIZED_COLOR_NAMES.join(", ")}.${suggestion}`,
349
- "warning"
350
- )
351
- );
352
- return void 0;
496
+ function computeTimeTicks(domainMin, domainMax, scale, boundaryStart, boundaryEnd, boundaryStartLabel, boundaryEndLabel) {
497
+ const minYear = Math.floor(domainMin);
498
+ const maxYear = Math.floor(domainMax);
499
+ const span = domainMax - domainMin;
500
+ let ticks = [];
501
+ const firstYear = Math.ceil(domainMin);
502
+ const lastYear = Math.floor(domainMax);
503
+ if (lastYear >= firstYear + 1) {
504
+ const yearSpan = lastYear - firstYear;
505
+ let step = 1;
506
+ if (yearSpan > 80) step = 20;
507
+ else if (yearSpan > 40) step = 10;
508
+ else if (yearSpan > 20) step = 5;
509
+ else if (yearSpan > 10) step = 2;
510
+ const alignedFirst = Math.ceil(firstYear / step) * step;
511
+ for (let y = alignedFirst; y <= lastYear; y += step) {
512
+ ticks.push({ pos: scale(y), label: String(y) });
513
+ }
514
+ } else if (span > 0.25) {
515
+ const crossesYear = maxYear > minYear;
516
+ for (let y = minYear; y <= maxYear + 1; y++) {
517
+ for (let m = 1; m <= 12; m++) {
518
+ const val = y + (m - 1) / 12;
519
+ if (val > domainMax) break;
520
+ if (val >= domainMin) {
521
+ ticks.push({
522
+ pos: scale(val),
523
+ label: crossesYear ? `${MONTH_ABBR[m - 1]} '${String(y).slice(-2)}` : MONTH_ABBR[m - 1]
524
+ });
525
+ }
526
+ }
527
+ }
528
+ } else if (span <= 685e-6) {
529
+ let stepMin = 5;
530
+ const spanHours = span * 8760;
531
+ if (spanHours > 3) stepMin = 30;
532
+ else if (spanHours > 1) stepMin = 15;
533
+ else if (spanHours > 0.5) stepMin = 10;
534
+ const startDate = fractionalYearToDate(domainMin);
535
+ startDate.setMinutes(
536
+ Math.floor(startDate.getMinutes() / stepMin) * stepMin,
537
+ 0,
538
+ 0
539
+ );
540
+ while (true) {
541
+ const val = dateToFractionalYear(startDate);
542
+ if (val > domainMax) break;
543
+ if (val >= domainMin) {
544
+ const hh = String(startDate.getHours()).padStart(2, "0");
545
+ const mm = String(startDate.getMinutes()).padStart(2, "0");
546
+ ticks.push({ pos: scale(val), label: `${hh}:${mm}` });
547
+ }
548
+ startDate.setMinutes(startDate.getMinutes() + stepMin);
549
+ }
550
+ } else if (span <= 822e-5) {
551
+ let stepHour = 1;
552
+ const spanHours = span * 8760;
553
+ if (spanHours > 48) stepHour = 6;
554
+ else if (spanHours > 24) stepHour = 3;
555
+ else if (spanHours > 12) stepHour = 2;
556
+ const singleDay = spanHours <= 24;
557
+ const startDate = fractionalYearToDate(domainMin);
558
+ startDate.setHours(
559
+ Math.floor(startDate.getHours() / stepHour) * stepHour,
560
+ 0,
561
+ 0,
562
+ 0
563
+ );
564
+ while (true) {
565
+ const val = dateToFractionalYear(startDate);
566
+ if (val > domainMax) break;
567
+ if (val >= domainMin) {
568
+ const hh = String(startDate.getHours()).padStart(2, "0");
569
+ const mm = String(startDate.getMinutes()).padStart(2, "0");
570
+ if (singleDay) {
571
+ ticks.push({ pos: scale(val), label: `${hh}:${mm}` });
572
+ } else {
573
+ const mon = MONTH_ABBR[startDate.getMonth()];
574
+ const d = startDate.getDate();
575
+ ticks.push({ pos: scale(val), label: `${mon} ${d} ${hh}:${mm}` });
576
+ }
577
+ }
578
+ startDate.setHours(startDate.getHours() + stepHour);
579
+ }
580
+ } else {
581
+ for (let y = minYear; y <= maxYear + 1; y++) {
582
+ for (let m = 1; m <= 12; m++) {
583
+ for (const d of [1, 8, 15, 22]) {
584
+ const val = y + (m - 1) / 12 + (d - 1) / 365;
585
+ if (val > domainMax) break;
586
+ if (val >= domainMin) {
587
+ ticks.push({
588
+ pos: scale(val),
589
+ label: `${MONTH_ABBR[m - 1]} ${d}`
590
+ });
591
+ }
592
+ }
593
+ }
594
+ }
595
+ }
596
+ const collisionThreshold = 40;
597
+ if (boundaryStart !== void 0 && boundaryStartLabel) {
598
+ const boundaryPos = scale(boundaryStart);
599
+ ticks = ticks.filter(
600
+ (t) => Math.abs(t.pos - boundaryPos) >= collisionThreshold
601
+ );
602
+ ticks.unshift({ pos: boundaryPos, label: boundaryStartLabel });
603
+ }
604
+ if (boundaryEnd !== void 0 && boundaryEndLabel) {
605
+ const boundaryPos = scale(boundaryEnd);
606
+ ticks = ticks.filter(
607
+ (t) => Math.abs(t.pos - boundaryPos) >= collisionThreshold
608
+ );
609
+ ticks.push({ pos: boundaryPos, label: boundaryEndLabel });
610
+ }
611
+ return ticks;
353
612
  }
354
- var nord, colorNames, RECOGNIZED_COLOR_NAMES, seriesColors;
355
- var init_colors = __esm({
356
- "src/colors.ts"() {
613
+ var MONTH_ABBR;
614
+ var init_time_ticks = __esm({
615
+ "src/utils/time-ticks.ts"() {
357
616
  "use strict";
358
- init_diagnostics();
359
- nord = {
360
- // Polar Night (dark)
361
- nord0: "#2e3440",
362
- nord1: "#3b4252",
363
- nord2: "#434c5e",
364
- nord3: "#4c566a",
365
- // Snow Storm (light)
366
- nord4: "#d8dee9",
367
- nord5: "#e5e9f0",
368
- nord6: "#eceff4",
369
- // Frost (accent blues)
370
- nord7: "#8fbcbb",
371
- nord8: "#88c0d0",
372
- nord9: "#81a1c1",
373
- nord10: "#5e81ac",
374
- // Aurora (colors)
375
- nord11: "#bf616a",
376
- // red
377
- nord12: "#d08770",
378
- // orange
379
- nord13: "#ebcb8b",
380
- // yellow
381
- nord14: "#a3be8c",
382
- // green
383
- nord15: "#b48ead"
384
- // purple
385
- };
386
- colorNames = {
387
- red: nord.nord11,
388
- orange: nord.nord12,
389
- yellow: nord.nord13,
390
- green: nord.nord14,
391
- blue: nord.nord10,
392
- purple: nord.nord15,
393
- teal: nord.nord7,
394
- cyan: nord.nord8,
395
- gray: nord.nord3,
396
- black: nord.nord0,
397
- white: nord.nord6
398
- };
399
- RECOGNIZED_COLOR_NAMES = Object.freeze([
400
- "red",
401
- "orange",
402
- "yellow",
403
- "green",
404
- "blue",
405
- "purple",
406
- "teal",
407
- "cyan",
408
- "gray",
409
- "black",
410
- "white"
411
- ]);
412
- seriesColors = [
413
- nord.nord10,
414
- // blue
415
- nord.nord14,
416
- // green
417
- nord.nord13,
418
- // yellow
419
- nord.nord12,
420
- // orange
421
- nord.nord15,
422
- // purple
423
- nord.nord11,
424
- // red
425
- nord.nord7,
426
- // teal
427
- nord.nord8
428
- // light blue
617
+ MONTH_ABBR = [
618
+ "Jan",
619
+ "Feb",
620
+ "Mar",
621
+ "Apr",
622
+ "May",
623
+ "Jun",
624
+ "Jul",
625
+ "Aug",
626
+ "Sep",
627
+ "Oct",
628
+ "Nov",
629
+ "Dec"
429
630
  ];
430
631
  }
431
632
  });
@@ -554,10 +755,6 @@ function hexToHSLString(hex) {
554
755
  const { h, s, l } = hexToHSL(hex);
555
756
  return `${h} ${s}% ${l}%`;
556
757
  }
557
- function mute(hex) {
558
- const { h, s, l } = hexToHSL(hex);
559
- return hslToHex(h, Math.min(s, 35), Math.min(l, 36));
560
- }
561
758
  function tint(hex, amount) {
562
759
  const raw = hex.replace("#", "");
563
760
  const full = raw.length === 3 ? raw[0] + raw[0] + raw[1] + raw[1] + raw[2] + raw[2] : raw;
@@ -1499,177 +1696,10 @@ var init_monokai = __esm({
1499
1696
  }
1500
1697
  });
1501
1698
 
1502
- // src/palettes/mermaid-bridge.ts
1503
- function buildMermaidThemeVars(colors, isDark) {
1504
- const c = colors.colors;
1505
- const accentOrder = [
1506
- c.blue,
1507
- c.red,
1508
- c.green,
1509
- c.yellow,
1510
- c.purple,
1511
- c.orange,
1512
- c.teal,
1513
- c.cyan,
1514
- colors.secondary
1515
- ];
1516
- const fills = isDark ? accentOrder.map(mute) : accentOrder;
1517
- return {
1518
- // ── Backgrounds ──
1519
- background: isDark ? colors.overlay : colors.border,
1520
- mainBkg: colors.surface,
1521
- // ── Primary/Secondary/Tertiary nodes ──
1522
- primaryColor: isDark ? colors.primary : colors.surface,
1523
- primaryTextColor: colors.text,
1524
- primaryBorderColor: isDark ? colors.secondary : colors.border,
1525
- secondaryColor: colors.secondary,
1526
- secondaryTextColor: contrastText(colors.secondary, colors.text, colors.bg),
1527
- secondaryBorderColor: colors.primary,
1528
- tertiaryColor: colors.accent,
1529
- tertiaryTextColor: contrastText(colors.accent, colors.text, colors.bg),
1530
- tertiaryBorderColor: colors.border,
1531
- // ── Lines & text ──
1532
- lineColor: colors.textMuted,
1533
- textColor: colors.text,
1534
- // ── Clusters ──
1535
- clusterBkg: colors.bg,
1536
- clusterBorder: isDark ? colors.border : colors.textMuted,
1537
- titleColor: colors.text,
1538
- // ── Labels ──
1539
- edgeLabelBackground: "transparent",
1540
- // ── Notes (sequence diagrams) ──
1541
- noteBkgColor: colors.bg,
1542
- noteTextColor: colors.text,
1543
- noteBorderColor: isDark ? colors.border : colors.textMuted,
1544
- // ── Actors (sequence diagrams) ──
1545
- actorBkg: colors.surface,
1546
- actorTextColor: colors.text,
1547
- actorBorder: isDark ? colors.border : colors.textMuted,
1548
- actorLineColor: colors.textMuted,
1549
- // ── Signals (sequence diagrams) ──
1550
- signalColor: colors.textMuted,
1551
- signalTextColor: colors.text,
1552
- // ── Labels ──
1553
- labelColor: colors.text,
1554
- labelTextColor: colors.text,
1555
- labelBoxBkgColor: colors.surface,
1556
- labelBoxBorderColor: isDark ? colors.border : colors.textMuted,
1557
- // ── Loop boxes ──
1558
- loopTextColor: colors.text,
1559
- // ── Activation (sequence diagrams) ──
1560
- activationBkgColor: isDark ? colors.overlay : colors.border,
1561
- activationBorderColor: isDark ? colors.border : colors.textMuted,
1562
- // ── Sequence numbers ──
1563
- sequenceNumberColor: isDark ? colors.text : colors.bg,
1564
- // ── State diagrams ──
1565
- labelBackgroundColor: colors.surface,
1566
- // ── Pie chart (9 slices) ──
1567
- // Dark mode: use muted fills so light pieSectionTextColor stays readable
1568
- ...Object.fromEntries(
1569
- (isDark ? fills : accentOrder).map((col, i) => [`pie${i + 1}`, col])
1570
- ),
1571
- pieTitleTextColor: colors.text,
1572
- pieSectionTextColor: isDark ? colors.text : colors.bg,
1573
- pieLegendTextColor: colors.text,
1574
- pieStrokeColor: "transparent",
1575
- pieOuterStrokeWidth: "0px",
1576
- pieOuterStrokeColor: "transparent",
1577
- // ── cScale (9 tiers) — muted in dark mode ──
1578
- ...Object.fromEntries(fills.map((f, i) => [`cScale${i}`, f])),
1579
- ...Object.fromEntries(
1580
- fills.map((_, i) => [
1581
- `cScaleLabel${i}`,
1582
- isDark ? colors.text : i < 2 || i > 6 ? colors.bg : colors.text
1583
- ])
1584
- ),
1585
- // ── fillType (8 slots) ──
1586
- ...Object.fromEntries(
1587
- [0, 1, 2, 3, 4, 5, 6, 7].map((i) => [
1588
- `fillType${i}`,
1589
- fills[i % fills.length]
1590
- ])
1591
- ),
1592
- // ── Journey actors (6 slots) ──
1593
- ...Object.fromEntries(
1594
- [c.red, c.green, c.yellow, c.purple, c.orange, c.teal].map((color, i) => [
1595
- `actor${i}`,
1596
- color
1597
- ])
1598
- ),
1599
- // ── Flowchart ──
1600
- nodeBorder: isDark ? colors.border : colors.textMuted,
1601
- nodeTextColor: colors.text,
1602
- // ── Gantt ──
1603
- gridColor: isDark ? colors.textMuted : colors.border,
1604
- doneTaskBkgColor: c.green,
1605
- doneTaskBorderColor: isDark ? colors.border : colors.textMuted,
1606
- activeTaskBkgColor: colors.secondary,
1607
- activeTaskBorderColor: colors.primary,
1608
- critBkgColor: c.orange,
1609
- critBorderColor: c.red,
1610
- taskBkgColor: colors.surface,
1611
- taskBorderColor: isDark ? colors.border : colors.textMuted,
1612
- taskTextColor: contrastText(colors.surface, colors.text, colors.bg),
1613
- taskTextDarkColor: colors.bg,
1614
- taskTextLightColor: colors.text,
1615
- taskTextOutsideColor: colors.text,
1616
- doneTaskTextColor: contrastText(c.green, colors.text, colors.bg),
1617
- activeTaskTextColor: contrastText(colors.secondary, colors.text, colors.bg),
1618
- critTaskTextColor: contrastText(c.orange, colors.text, colors.bg),
1619
- sectionBkgColor: isDark ? shade(colors.primary, colors.bg, 0.6) : tint(colors.primary, 0.6),
1620
- altSectionBkgColor: colors.bg,
1621
- sectionBkgColor2: isDark ? shade(colors.primary, colors.bg, 0.6) : tint(colors.primary, 0.6),
1622
- todayLineColor: c.yellow,
1623
- // ── Quadrant ──
1624
- quadrant1Fill: isDark ? shade(c.green, colors.bg, 0.75) : tint(c.green, 0.75),
1625
- quadrant2Fill: isDark ? shade(c.blue, colors.bg, 0.75) : tint(c.blue, 0.75),
1626
- quadrant3Fill: isDark ? shade(c.red, colors.bg, 0.75) : tint(c.red, 0.75),
1627
- quadrant4Fill: isDark ? shade(c.yellow, colors.bg, 0.75) : tint(c.yellow, 0.75),
1628
- quadrant1TextFill: colors.text,
1629
- quadrant2TextFill: colors.text,
1630
- quadrant3TextFill: colors.text,
1631
- quadrant4TextFill: colors.text,
1632
- quadrantPointFill: isDark ? c.cyan : c.blue,
1633
- quadrantPointTextFill: colors.text,
1634
- quadrantXAxisTextFill: colors.text,
1635
- quadrantYAxisTextFill: colors.text,
1636
- quadrantTitleFill: colors.text,
1637
- quadrantInternalBorderStrokeFill: colors.border,
1638
- quadrantExternalBorderStrokeFill: colors.border
1639
- };
1640
- }
1641
- function buildThemeCSS(palette, isDark) {
1642
- const base = `
1643
- .branchLabelBkg { fill: transparent !important; stroke: transparent !important; }
1644
- .commit-label-bkg { fill: transparent !important; stroke: transparent !important; }
1645
- .tag-label-bkg { fill: transparent !important; stroke: transparent !important; }
1646
-
1647
- /* GitGraph: ensure commit and branch label text matches palette */
1648
- .commit-label { fill: ${palette.text} !important; }
1649
- .branch-label { fill: ${palette.text} !important; }
1650
- .tag-label { fill: ${palette.text} !important; }
1651
- `;
1652
- if (!isDark) return base;
1653
- return base + `
1654
- /* Flowchart: ensure node and edge label text is readable */
1655
- .nodeLabel, .label { color: ${palette.text} !important; fill: ${palette.text} !important; }
1656
- .edgeLabel { color: ${palette.text} !important; fill: ${palette.text} !important; }
1657
- .edgeLabel .label { color: ${palette.text} !important; fill: ${palette.text} !important; }
1658
- `;
1659
- }
1660
- var init_mermaid_bridge = __esm({
1661
- "src/palettes/mermaid-bridge.ts"() {
1662
- "use strict";
1663
- init_color_utils();
1664
- }
1665
- });
1666
-
1667
1699
  // src/palettes/index.ts
1668
1700
  var palettes_exports = {};
1669
1701
  __export(palettes_exports, {
1670
1702
  boldPalette: () => boldPalette,
1671
- buildMermaidThemeVars: () => buildMermaidThemeVars,
1672
- buildThemeCSS: () => buildThemeCSS,
1673
1703
  catppuccinPalette: () => catppuccinPalette,
1674
1704
  contrastText: () => contrastText,
1675
1705
  draculaPalette: () => draculaPalette,
@@ -1683,7 +1713,6 @@ __export(palettes_exports, {
1683
1713
  hslToHex: () => hslToHex,
1684
1714
  isValidHex: () => isValidHex,
1685
1715
  monokaiPalette: () => monokaiPalette,
1686
- mute: () => mute,
1687
1716
  nordPalette: () => nordPalette,
1688
1717
  oneDarkPalette: () => oneDarkPalette,
1689
1718
  registerPalette: () => registerPalette,
@@ -1708,7 +1737,6 @@ var init_palettes = __esm({
1708
1737
  init_tokyo_night();
1709
1738
  init_dracula();
1710
1739
  init_monokai();
1711
- init_mermaid_bridge();
1712
1740
  }
1713
1741
  });
1714
1742
 
@@ -3133,61 +3161,6 @@ var init_participant_inference = __esm({
3133
3161
  }
3134
3162
  });
3135
3163
 
3136
- // src/utils/arrows.ts
3137
- function parseArrow(line10) {
3138
- if (BIDI_SYNC_RE.test(line10) || BIDI_ASYNC_RE.test(line10)) {
3139
- return {
3140
- error: "Bidirectional arrows are no longer supported. Use two separate lines: 'A -msg-> B' and 'B -msg-> A'"
3141
- };
3142
- }
3143
- if (RETURN_SYNC_LABELED_RE.test(line10) || RETURN_ASYNC_LABELED_RE.test(line10)) {
3144
- const m = line10.match(RETURN_SYNC_LABELED_RE) ?? line10.match(RETURN_ASYNC_LABELED_RE);
3145
- const from = m[3];
3146
- const to = m[1];
3147
- const label = m[2].trim();
3148
- return {
3149
- error: `Left-pointing arrows are no longer supported. Write '${from} -${label}-> ${to}' instead`
3150
- };
3151
- }
3152
- const patterns = [
3153
- { re: SYNC_LABELED_RE, async: false },
3154
- { re: ASYNC_LABELED_RE, async: true }
3155
- ];
3156
- for (const { re, async: isAsync } of patterns) {
3157
- const m = line10.match(re);
3158
- if (!m) continue;
3159
- const label = m[2].trim();
3160
- if (!label) return null;
3161
- for (const arrow of ARROW_CHARS) {
3162
- if (label.includes(arrow)) {
3163
- return {
3164
- error: "Arrow characters (->, ~>) are not allowed inside labels"
3165
- };
3166
- }
3167
- }
3168
- return {
3169
- from: m[1],
3170
- to: m[3],
3171
- label,
3172
- async: isAsync
3173
- };
3174
- }
3175
- return null;
3176
- }
3177
- var SYNC_LABELED_RE, ASYNC_LABELED_RE, RETURN_SYNC_LABELED_RE, RETURN_ASYNC_LABELED_RE, BIDI_SYNC_RE, BIDI_ASYNC_RE, ARROW_CHARS;
3178
- var init_arrows = __esm({
3179
- "src/utils/arrows.ts"() {
3180
- "use strict";
3181
- SYNC_LABELED_RE = /^(.+?)\s*-(.+)->\s*(.+)$/;
3182
- ASYNC_LABELED_RE = /^(.+?)\s*~(.+)~>\s*(.+)$/;
3183
- RETURN_SYNC_LABELED_RE = /^(.+?)\s*<-(.+)-\s*(.+)$/;
3184
- RETURN_ASYNC_LABELED_RE = /^(.+?)\s*<~(.+)~\s*(.+)$/;
3185
- BIDI_SYNC_RE = /^(.+?)\s*<-(.+)->\s*(.+)$/;
3186
- BIDI_ASYNC_RE = /^(.+?)\s*<~(.+)~>\s*(.+)$/;
3187
- ARROW_CHARS = ["->", "~>"];
3188
- }
3189
- });
3190
-
3191
3164
  // src/sequence/parser.ts
3192
3165
  var parser_exports = {};
3193
3166
  __export(parser_exports, {
@@ -3740,8 +3713,11 @@ function parseSequenceDgmo(content) {
3740
3713
  }
3741
3714
  if (labeledArrow) {
3742
3715
  contentStarted = true;
3743
- const { from, to, label, async: isAsync } = labeledArrow;
3716
+ const { from, to, label: rawLabel, async: isAsync } = labeledArrow;
3744
3717
  lastMsgFrom = from;
3718
+ const labelResult = parseInArrowLabel(rawLabel, lineNumber);
3719
+ labelResult.diagnostics.forEach((d) => result.diagnostics.push(d));
3720
+ const label = labelResult.label ?? rawLabel;
3745
3721
  const msg = {
3746
3722
  from,
3747
3723
  to,
@@ -4141,42 +4117,52 @@ function parseNodeRef(text, palette) {
4141
4117
  }
4142
4118
  function splitArrows(line10) {
4143
4119
  const segments = [];
4144
- let lastIndex = 0;
4145
4120
  const arrowPositions = [];
4146
4121
  let searchFrom = 0;
4122
+ let scanFloor = 0;
4147
4123
  while (searchFrom < line10.length) {
4148
4124
  const idx = line10.indexOf("->", searchFrom);
4149
4125
  if (idx === -1) break;
4150
- let arrowStart = idx;
4126
+ let runStart = idx;
4127
+ while (runStart > scanFloor && line10[runStart - 1] === "-") runStart--;
4128
+ const arrowEnd = idx + 2;
4129
+ let arrowStart;
4151
4130
  let label;
4152
4131
  let color;
4153
- if (idx > 0 && line10[idx - 1] !== " " && line10[idx - 1] !== " ") {
4154
- let scanBack = idx - 1;
4155
- while (scanBack > 0 && line10[scanBack] !== "-") {
4156
- scanBack--;
4157
- }
4158
- if (line10[scanBack] === "-" && (scanBack === 0 || /\s/.test(line10[scanBack - 1]))) {
4159
- let arrowContent = line10.substring(scanBack + 1, idx);
4160
- if (arrowContent.endsWith("-"))
4161
- arrowContent = arrowContent.slice(0, -1);
4162
- const colorMatch = arrowContent.match(/\(([^)]+)\)\s*$/);
4163
- if (colorMatch) {
4164
- color = colorMatch[1].trim();
4165
- const labelPart = arrowContent.substring(0, colorMatch.index).trim();
4166
- if (labelPart) label = labelPart;
4167
- } else {
4168
- const labelPart = arrowContent.trim();
4169
- if (labelPart) label = labelPart;
4170
- }
4171
- arrowStart = scanBack;
4132
+ let openingStart = -1;
4133
+ for (let i = scanFloor; i < runStart; i++) {
4134
+ if (line10[i] !== "-") continue;
4135
+ const prevIsWsOrFloor = i === 0 || i === scanFloor || /\s/.test(line10[i - 1]);
4136
+ if (prevIsWsOrFloor) {
4137
+ openingStart = i;
4138
+ break;
4172
4139
  }
4173
4140
  }
4174
- arrowPositions.push({ start: arrowStart, end: idx + 2, label, color });
4175
- searchFrom = idx + 2;
4141
+ if (openingStart !== -1) {
4142
+ let openingEnd = openingStart;
4143
+ while (openingEnd < runStart && line10[openingEnd] === "-") openingEnd++;
4144
+ const arrowContent = line10.substring(openingEnd, runStart);
4145
+ const colorMatch = arrowContent.match(/\(([^)]+)\)\s*$/);
4146
+ if (colorMatch) {
4147
+ color = colorMatch[1].trim();
4148
+ const labelPart = arrowContent.substring(0, colorMatch.index).trim();
4149
+ if (labelPart) label = labelPart;
4150
+ } else {
4151
+ const labelPart = arrowContent.trim();
4152
+ if (labelPart) label = labelPart;
4153
+ }
4154
+ arrowStart = openingStart;
4155
+ } else {
4156
+ arrowStart = runStart;
4157
+ }
4158
+ arrowPositions.push({ start: arrowStart, end: arrowEnd, label, color });
4159
+ searchFrom = arrowEnd;
4160
+ scanFloor = arrowEnd;
4176
4161
  }
4177
4162
  if (arrowPositions.length === 0) {
4178
4163
  return [line10];
4179
4164
  }
4165
+ let lastIndex = 0;
4180
4166
  for (let i = 0; i < arrowPositions.length; i++) {
4181
4167
  const arrow = arrowPositions[i];
4182
4168
  const beforeText = line10.substring(lastIndex, arrow.start).trim();
@@ -4199,20 +4185,26 @@ function splitArrows(line10) {
4199
4185
  }
4200
4186
  function parseArrowToken(token, palette, lineNumber, diagnostics) {
4201
4187
  if (token === "->") return {};
4202
- const colorOnly = token.match(/^-\(([^)]+)\)->$/);
4203
- if (colorOnly) {
4204
- return {
4205
- color: resolveColorWithDiagnostic(
4206
- colorOnly[1].trim(),
4207
- lineNumber,
4208
- diagnostics,
4209
- palette
4210
- )
4211
- };
4188
+ const bareParen = token.match(/^-(\([A-Za-z]+\))->$/);
4189
+ if (bareParen) {
4190
+ const colorName = matchColorParens(bareParen[1]);
4191
+ if (colorName) {
4192
+ return {
4193
+ color: resolveColorWithDiagnostic(
4194
+ colorName,
4195
+ lineNumber,
4196
+ diagnostics,
4197
+ palette
4198
+ )
4199
+ };
4200
+ }
4212
4201
  }
4213
4202
  const m = token.match(/^-(.+?)(?:\(([^)]+)\))?->$/);
4214
4203
  if (m) {
4215
- const label = m[1]?.trim() || void 0;
4204
+ const rawLabel = m[1] ?? "";
4205
+ const labelResult = parseInArrowLabel(rawLabel, lineNumber);
4206
+ diagnostics.push(...labelResult.diagnostics);
4207
+ const label = labelResult.label;
4216
4208
  let color = m[2] ? resolveColorWithDiagnostic(
4217
4209
  m[2].trim(),
4218
4210
  lineNumber,
@@ -4440,6 +4432,7 @@ var init_flowchart_parser = __esm({
4440
4432
  "use strict";
4441
4433
  init_colors();
4442
4434
  init_diagnostics();
4435
+ init_arrows();
4443
4436
  init_parsing();
4444
4437
  NODE_ID_RE = /^([a-zA-Z_][\w-]*)[\s([</{]/;
4445
4438
  }
@@ -4455,35 +4448,45 @@ function splitArrows2(line10) {
4455
4448
  const segments = [];
4456
4449
  const arrowPositions = [];
4457
4450
  let searchFrom = 0;
4451
+ let scanFloor = 0;
4458
4452
  while (searchFrom < line10.length) {
4459
4453
  const idx = line10.indexOf("->", searchFrom);
4460
4454
  if (idx === -1) break;
4461
- let arrowStart = idx;
4455
+ let runStart = idx;
4456
+ while (runStart > scanFloor && line10[runStart - 1] === "-") runStart--;
4457
+ const arrowEnd = idx + 2;
4458
+ let arrowStart;
4462
4459
  let label;
4463
4460
  let color;
4464
- if (idx > 0 && line10[idx - 1] !== " " && line10[idx - 1] !== " ") {
4465
- let scanBack = idx - 1;
4466
- while (scanBack > 0 && line10[scanBack] !== "-") {
4467
- scanBack--;
4468
- }
4469
- if (line10[scanBack] === "-" && (scanBack === 0 || /\s/.test(line10[scanBack - 1]))) {
4470
- let arrowContent = line10.substring(scanBack + 1, idx);
4471
- if (arrowContent.endsWith("-"))
4472
- arrowContent = arrowContent.slice(0, -1);
4473
- const colorMatch = arrowContent.match(/\(([^)]+)\)\s*$/);
4474
- if (colorMatch) {
4475
- color = colorMatch[1].trim();
4476
- const labelPart = arrowContent.substring(0, colorMatch.index).trim();
4477
- if (labelPart) label = labelPart;
4478
- } else {
4479
- const labelPart = arrowContent.trim();
4480
- if (labelPart) label = labelPart;
4481
- }
4482
- arrowStart = scanBack;
4461
+ let openingStart = -1;
4462
+ for (let i = scanFloor; i < runStart; i++) {
4463
+ if (line10[i] !== "-") continue;
4464
+ const prevIsWsOrFloor = i === 0 || i === scanFloor || /\s/.test(line10[i - 1]);
4465
+ if (prevIsWsOrFloor) {
4466
+ openingStart = i;
4467
+ break;
4483
4468
  }
4484
4469
  }
4485
- arrowPositions.push({ start: arrowStart, end: idx + 2, label, color });
4486
- searchFrom = idx + 2;
4470
+ if (openingStart !== -1) {
4471
+ let openingEnd = openingStart;
4472
+ while (openingEnd < runStart && line10[openingEnd] === "-") openingEnd++;
4473
+ const arrowContent = line10.substring(openingEnd, runStart);
4474
+ const colorMatch = arrowContent.match(/\(([^)]+)\)\s*$/);
4475
+ if (colorMatch) {
4476
+ color = colorMatch[1].trim();
4477
+ const labelPart = arrowContent.substring(0, colorMatch.index).trim();
4478
+ if (labelPart) label = labelPart;
4479
+ } else {
4480
+ const labelPart = arrowContent.trim();
4481
+ if (labelPart) label = labelPart;
4482
+ }
4483
+ arrowStart = openingStart;
4484
+ } else {
4485
+ arrowStart = runStart;
4486
+ }
4487
+ arrowPositions.push({ start: arrowStart, end: arrowEnd, label, color });
4488
+ searchFrom = arrowEnd;
4489
+ scanFloor = arrowEnd;
4487
4490
  }
4488
4491
  if (arrowPositions.length === 0) return [line10];
4489
4492
  let lastIndex = 0;
@@ -4505,19 +4508,26 @@ function splitArrows2(line10) {
4505
4508
  }
4506
4509
  function parseArrowToken2(token, palette, lineNumber, diagnostics) {
4507
4510
  if (token === "->") return {};
4508
- const colorOnly = token.match(/^-\(([^)]+)\)->$/);
4509
- if (colorOnly)
4510
- return {
4511
- color: resolveColorWithDiagnostic(
4512
- colorOnly[1].trim(),
4513
- lineNumber,
4514
- diagnostics,
4515
- palette
4516
- )
4517
- };
4511
+ const bareParen = token.match(/^-(\([A-Za-z]+\))->$/);
4512
+ if (bareParen) {
4513
+ const colorName = matchColorParens(bareParen[1]);
4514
+ if (colorName) {
4515
+ return {
4516
+ color: resolveColorWithDiagnostic(
4517
+ colorName,
4518
+ lineNumber,
4519
+ diagnostics,
4520
+ palette
4521
+ )
4522
+ };
4523
+ }
4524
+ }
4518
4525
  const m = token.match(/^-(.+?)(?:\(([^)]+)\))?->$/);
4519
4526
  if (m) {
4520
- const label = m[1]?.trim() || void 0;
4527
+ const rawLabel = m[1] ?? "";
4528
+ const labelResult = parseInArrowLabel(rawLabel, lineNumber);
4529
+ diagnostics.push(...labelResult.diagnostics);
4530
+ const label = labelResult.label;
4521
4531
  const color = m[2] ? resolveColorWithDiagnostic(
4522
4532
  m[2].trim(),
4523
4533
  lineNumber,
@@ -4758,6 +4768,7 @@ var init_state_parser = __esm({
4758
4768
  "use strict";
4759
4769
  init_colors();
4760
4770
  init_diagnostics();
4771
+ init_arrows();
4761
4772
  init_parsing();
4762
4773
  PSEUDOSTATE_ID = "pseudostate:[*]";
4763
4774
  PSEUDOSTATE_LABEL = "[*]";
@@ -4914,6 +4925,11 @@ function parseClassDiagram(content, palette) {
4914
4925
  const targetName = indentRel[2];
4915
4926
  const label = indentRel[3]?.trim();
4916
4927
  getOrCreateClass(targetName, lineNumber);
4928
+ if (label) {
4929
+ result.diagnostics.push(
4930
+ ...validateLabelCharacters(label, lineNumber)
4931
+ );
4932
+ }
4917
4933
  result.relationships.push({
4918
4934
  source: currentClass.id,
4919
4935
  target: classId(targetName),
@@ -5088,6 +5104,7 @@ var init_parser2 = __esm({
5088
5104
  "use strict";
5089
5105
  init_colors();
5090
5106
  init_diagnostics();
5107
+ init_arrows();
5091
5108
  init_parsing();
5092
5109
  CLASS_DECL_RE = /^(?:(abstract|interface|enum)\s+)?([A-Z][A-Za-z0-9_]*)(?:\s+(extends|implements)\s+([A-Z][A-Za-z0-9_]*))?(?:\s+\[(abstract|interface|enum)\])?(?:\s+\(([^)]+)\))?\s*$/;
5093
5110
  INDENT_REL_ARROW_RE = /^(--\|>|\.\.\|>|\*--|o--|\.\.>|->)\s*([A-Z][A-Za-z0-9_]*)(?:\s+:?\s*(.+))?$/;
@@ -5127,12 +5144,18 @@ function parseRelationship(trimmed, lineNumber, pushError) {
5127
5144
  const fromCard = parseCardSide(sym[2]);
5128
5145
  const toCard = parseCardSide(sym[3]);
5129
5146
  if (fromCard && toCard) {
5147
+ const label = sym[5]?.trim();
5148
+ if (label) {
5149
+ validateLabelCharacters(label, lineNumber).forEach(
5150
+ (d) => pushError(d.line, d.message)
5151
+ );
5152
+ }
5130
5153
  return {
5131
5154
  source: sym[1],
5132
5155
  target: sym[4],
5133
5156
  from: fromCard,
5134
5157
  to: toCard,
5135
- label: sym[5]?.trim()
5158
+ label
5136
5159
  };
5137
5160
  }
5138
5161
  }
@@ -5295,11 +5318,17 @@ function parseERDiagram(content, palette) {
5295
5318
  if (fromCard && toCard) {
5296
5319
  const targetName = indentRel[4];
5297
5320
  getOrCreateTable(targetName, lineNumber);
5321
+ const rawLabel = indentRel[2]?.trim();
5322
+ if (rawLabel) {
5323
+ result.diagnostics.push(
5324
+ ...validateLabelCharacters(rawLabel, lineNumber)
5325
+ );
5326
+ }
5298
5327
  result.relationships.push({
5299
5328
  source: currentTable.id,
5300
5329
  target: tableId(targetName),
5301
5330
  cardinality: { from: fromCard, to: toCard },
5302
- ...indentRel[2]?.trim() && { label: indentRel[2].trim() },
5331
+ ...rawLabel && { label: rawLabel },
5303
5332
  lineNumber
5304
5333
  });
5305
5334
  }
@@ -5463,6 +5492,7 @@ var init_parser3 = __esm({
5463
5492
  "use strict";
5464
5493
  init_colors();
5465
5494
  init_diagnostics();
5495
+ init_arrows();
5466
5496
  init_parsing();
5467
5497
  init_tag_groups();
5468
5498
  TABLE_DECL_RE = /^([a-zA-Z_]\w*)(?:\s*\(([^)]+)\))?(?:\s*\|(.+))?$/;
@@ -5965,7 +5995,28 @@ var init_legend_svg = __esm({
5965
5995
  });
5966
5996
 
5967
5997
  // src/echarts.ts
5968
- import * as echarts from "echarts";
5998
+ import * as echarts from "echarts/core";
5999
+ import {
6000
+ BarChart,
6001
+ LineChart,
6002
+ PieChart,
6003
+ ScatterChart,
6004
+ RadarChart,
6005
+ SankeyChart,
6006
+ GraphChart,
6007
+ HeatmapChart,
6008
+ FunnelChart
6009
+ } from "echarts/charts";
6010
+ import {
6011
+ GridComponent,
6012
+ TitleComponent,
6013
+ TooltipComponent,
6014
+ LegendComponent,
6015
+ RadarComponent,
6016
+ VisualMapComponent,
6017
+ GraphicComponent
6018
+ } from "echarts/components";
6019
+ import { SVGRenderer } from "echarts/renderers";
5969
6020
  function parseScatterRow(line10, palette, currentCategory, lineNumber) {
5970
6021
  const dataRow = parseDataRowValues(line10, { multiValue: true });
5971
6022
  if (!dataRow || dataRow.values.length < 2) return null;
@@ -7984,7 +8035,7 @@ function buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor,
7984
8035
  series
7985
8036
  };
7986
8037
  }
7987
- async function renderExtendedChartForExport(content, theme, palette, options) {
8038
+ async function renderExtendedChartForExport(content, theme, palette) {
7988
8039
  const isDark = theme === "dark";
7989
8040
  const { getPalette: getPalette2 } = await Promise.resolve().then(() => (init_palettes(), palettes_exports));
7990
8041
  const effectivePalette = palette ?? (isDark ? getPalette2("nord").dark : getPalette2("nord").light);
@@ -8055,10 +8106,6 @@ async function renderExtendedChartForExport(content, theme, palette, options) {
8055
8106
  `$1<g transform="translate(0,${legendY})">${legendSvgStr}</g>`
8056
8107
  );
8057
8108
  }
8058
- if (options?.branding !== false) {
8059
- const brandColor = theme === "transparent" ? "#888" : effectivePalette.textMuted;
8060
- result = injectBranding(result, brandColor);
8061
- }
8062
8109
  return result;
8063
8110
  } finally {
8064
8111
  chart.dispose();
@@ -8069,7 +8116,6 @@ var init_echarts = __esm({
8069
8116
  "src/echarts.ts"() {
8070
8117
  "use strict";
8071
8118
  init_fonts();
8072
- init_branding();
8073
8119
  init_legend_svg();
8074
8120
  init_label_layout();
8075
8121
  init_palettes();
@@ -8079,6 +8125,25 @@ var init_echarts = __esm({
8079
8125
  init_colors();
8080
8126
  init_parsing();
8081
8127
  init_chart();
8128
+ echarts.use([
8129
+ BarChart,
8130
+ LineChart,
8131
+ PieChart,
8132
+ ScatterChart,
8133
+ RadarChart,
8134
+ SankeyChart,
8135
+ GraphChart,
8136
+ HeatmapChart,
8137
+ FunnelChart,
8138
+ GridComponent,
8139
+ TitleComponent,
8140
+ TooltipComponent,
8141
+ LegendComponent,
8142
+ RadarComponent,
8143
+ VisualMapComponent,
8144
+ GraphicComponent,
8145
+ SVGRenderer
8146
+ ]);
8082
8147
  EMPHASIS_SELF = {
8083
8148
  focus: "self",
8084
8149
  blurScope: "global",
@@ -9041,13 +9106,10 @@ function parseC4(content, palette) {
9041
9106
  labeledHandled = true;
9042
9107
  break;
9043
9108
  }
9044
- let label = rawLabel;
9109
+ const labelResult = parseInArrowLabel(rawLabel, lineNumber);
9110
+ labelResult.diagnostics.forEach((d) => result.diagnostics.push(d));
9111
+ const label = labelResult.label;
9045
9112
  let technology;
9046
- const techMatch = rawLabel.match(/\[([^\]]+)\]\s*$/);
9047
- if (techMatch) {
9048
- label = rawLabel.substring(0, techMatch.index).trim() || void 0;
9049
- technology = techMatch[1].trim();
9050
- }
9051
9113
  let target = targetBody;
9052
9114
  const pipeIdx = targetBody.indexOf("|");
9053
9115
  if (pipeIdx !== -1) {
@@ -9383,6 +9445,7 @@ var init_parser6 = __esm({
9383
9445
  "src/c4/parser.ts"() {
9384
9446
  "use strict";
9385
9447
  init_diagnostics();
9448
+ init_arrows();
9386
9449
  init_tag_groups();
9387
9450
  init_participant_inference();
9388
9451
  init_parsing();
@@ -10183,7 +10246,10 @@ function parseInfra(content) {
10183
10246
  }
10184
10247
  const asyncConnMatch = trimmed.match(ASYNC_CONNECTION_RE);
10185
10248
  if (asyncConnMatch) {
10186
- const label = asyncConnMatch[1]?.trim() || "";
10249
+ const rawLabel = asyncConnMatch[1] ?? "";
10250
+ const labelResult = parseInArrowLabel(rawLabel, lineNumber);
10251
+ result.diagnostics.push(...labelResult.diagnostics);
10252
+ const label = labelResult.label ?? "";
10187
10253
  const targetRaw = asyncConnMatch[2].trim();
10188
10254
  const pipeMeta = extractPipeMetadata(targetRaw);
10189
10255
  const targetName = pipeMeta.clean || targetRaw;
@@ -10243,7 +10309,10 @@ function parseInfra(content) {
10243
10309
  }
10244
10310
  const connMatch = trimmed.match(CONNECTION_RE);
10245
10311
  if (connMatch) {
10246
- const label = connMatch[1]?.trim() || "";
10312
+ const rawLabel = connMatch[1] ?? "";
10313
+ const labelResult = parseInArrowLabel(rawLabel, lineNumber);
10314
+ result.diagnostics.push(...labelResult.diagnostics);
10315
+ const label = labelResult.label ?? "";
10247
10316
  const targetRaw = connMatch[2].trim();
10248
10317
  const pipeMeta = extractPipeMetadata(targetRaw);
10249
10318
  const targetName = pipeMeta.clean || targetRaw;
@@ -10442,6 +10511,7 @@ var init_parser8 = __esm({
10442
10511
  "use strict";
10443
10512
  init_diagnostics();
10444
10513
  init_colors();
10514
+ init_arrows();
10445
10515
  init_parsing();
10446
10516
  init_tag_groups();
10447
10517
  init_types();
@@ -11973,7 +12043,9 @@ function parseEdgeLine(trimmed, lineNum, aliasMap, diagnostics) {
11973
12043
  const biLabeledMatch = trimmed.match(/^(.+?)\s*<-(.+)->\s*(.+)$/);
11974
12044
  if (biLabeledMatch) {
11975
12045
  const source2 = resolveEndpoint(biLabeledMatch[1].trim());
11976
- const label = biLabeledMatch[2].trim();
12046
+ const labelResult = parseInArrowLabel(biLabeledMatch[2], lineNum);
12047
+ diagnostics.push(...labelResult.diagnostics);
12048
+ const label = labelResult.label;
11977
12049
  let rest2 = biLabeledMatch[3].trim();
11978
12050
  let metadata2 = {};
11979
12051
  const pipeIdx2 = rest2.indexOf("|");
@@ -11994,7 +12066,7 @@ function parseEdgeLine(trimmed, lineNum, aliasMap, diagnostics) {
11994
12066
  return {
11995
12067
  source: source2,
11996
12068
  target: resolveEndpoint(rest2),
11997
- label: label || void 0,
12069
+ label,
11998
12070
  bidirectional: true,
11999
12071
  lineNumber: lineNum,
12000
12072
  metadata: metadata2
@@ -12031,7 +12103,9 @@ function parseEdgeLine(trimmed, lineNum, aliasMap, diagnostics) {
12031
12103
  const labeledMatch = trimmed.match(/^(.+?)\s+-(.+)->\s*(.+)$/);
12032
12104
  if (labeledMatch) {
12033
12105
  const source2 = resolveEndpoint(labeledMatch[1].trim());
12034
- const label = labeledMatch[2].trim();
12106
+ const labelResult = parseInArrowLabel(labeledMatch[2], lineNum);
12107
+ diagnostics.push(...labelResult.diagnostics);
12108
+ const label = labelResult.label;
12035
12109
  let rest2 = labeledMatch[3].trim();
12036
12110
  if (label) {
12037
12111
  let metadata2 = {};
@@ -12094,6 +12168,7 @@ var init_parser10 = __esm({
12094
12168
  "src/boxes-and-lines/parser.ts"() {
12095
12169
  "use strict";
12096
12170
  init_diagnostics();
12171
+ init_arrows();
12097
12172
  init_tag_groups();
12098
12173
  init_parsing();
12099
12174
  MAX_GROUP_DEPTH = 1;
@@ -12169,19 +12244,78 @@ function getAllChartTypes() {
12169
12244
  function parseDgmo(content) {
12170
12245
  const chartType = parseDgmoChartType(content);
12171
12246
  if (!chartType) {
12247
+ const colonDiag = detectColonChartType(content);
12248
+ if (colonDiag) {
12249
+ const fallback = parseVisualization(content).diagnostics;
12250
+ return { diagnostics: [colonDiag, ...fallback] };
12251
+ }
12172
12252
  return { diagnostics: parseVisualization(content).diagnostics };
12173
12253
  }
12174
12254
  const directParser = PARSE_DISPATCH.get(chartType);
12175
- if (directParser) return { diagnostics: directParser(content).diagnostics };
12255
+ if (directParser) {
12256
+ const result2 = directParser(content);
12257
+ return {
12258
+ diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)]
12259
+ };
12260
+ }
12176
12261
  if (STANDARD_CHART_TYPES2.has(chartType)) {
12177
- return { diagnostics: parseChart(content).diagnostics };
12262
+ const result2 = parseChart(content);
12263
+ return {
12264
+ diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)]
12265
+ };
12178
12266
  }
12179
12267
  if (ECHART_TYPES.has(chartType)) {
12180
- return { diagnostics: parseExtendedChart(content).diagnostics };
12268
+ const result2 = parseExtendedChart(content);
12269
+ return {
12270
+ diagnostics: [...result2.diagnostics, ...detectEmptyContent(content)]
12271
+ };
12181
12272
  }
12182
- return { diagnostics: parseVisualization(content).diagnostics };
12273
+ const result = parseVisualization(content);
12274
+ return {
12275
+ diagnostics: [...result.diagnostics, ...detectEmptyContent(content)]
12276
+ };
12183
12277
  }
12184
- var GANTT_DURATION_RE, GANTT_DATE_RE, C4_TYPE_RE, DATA_CHART_TYPES, VISUALIZATION_TYPES, DIAGRAM_TYPES, EXTENDED_CHART_TYPES, STANDARD_CHART_TYPES2, ECHART_TYPES, PARSE_DISPATCH;
12278
+ function detectColonChartType(content) {
12279
+ const lines = content.split("\n");
12280
+ for (let i = 0; i < lines.length; i++) {
12281
+ const trimmed = lines[i].trim();
12282
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//"))
12283
+ continue;
12284
+ const match = trimmed.match(/^(\w[\w-]*)\s*:\s*(.*)$/);
12285
+ if (!match) return null;
12286
+ const word = match[1].toLowerCase();
12287
+ const rest = match[2].trim();
12288
+ if (ALL_KNOWN_TYPES.has(word)) {
12289
+ const example = rest ? `${word} ${rest}` : word;
12290
+ return makeDgmoError(
12291
+ i + 1,
12292
+ `Remove the colon \u2014 use '${example}' instead of '${trimmed}'. DGMO chart types don't use colons.`
12293
+ );
12294
+ }
12295
+ const hint = suggest(word, [...ALL_KNOWN_TYPES]);
12296
+ if (hint) {
12297
+ return makeDgmoError(
12298
+ i + 1,
12299
+ `Unknown chart type: ${word}. ${hint} Also, DGMO chart types don't use colons.`
12300
+ );
12301
+ }
12302
+ return null;
12303
+ }
12304
+ return null;
12305
+ }
12306
+ function detectEmptyContent(content) {
12307
+ const lines = content.split("\n");
12308
+ const nonEmpty = lines.filter(
12309
+ (l) => l.trim() && !l.trim().startsWith("#") && !l.trim().startsWith("//")
12310
+ );
12311
+ if (nonEmpty.length <= 1) {
12312
+ return [
12313
+ makeDgmoError(1, "No content after chart type declaration.", "warning")
12314
+ ];
12315
+ }
12316
+ return [];
12317
+ }
12318
+ var GANTT_DURATION_RE, GANTT_DATE_RE, C4_TYPE_RE, DATA_CHART_TYPES, VISUALIZATION_TYPES, DIAGRAM_TYPES, EXTENDED_CHART_TYPES, STANDARD_CHART_TYPES2, ECHART_TYPES, PARSE_DISPATCH, ALL_KNOWN_TYPES;
12185
12319
  var init_dgmo_router = __esm({
12186
12320
  "src/dgmo-router.ts"() {
12187
12321
  "use strict";
@@ -12201,6 +12335,7 @@ var init_dgmo_router = __esm({
12201
12335
  init_parser9();
12202
12336
  init_parser10();
12203
12337
  init_parsing();
12338
+ init_diagnostics();
12204
12339
  GANTT_DURATION_RE = /^\d+(?:\.\d+)?(?:min|bd|d|w|m|q|y|h)(?:\?)?\s+/;
12205
12340
  GANTT_DATE_RE = /^\d{4}-\d{2}-\d{2}(?:\s\d{2}:\d{2})?\s+/;
12206
12341
  C4_TYPE_RE = /\bis\s+an?\s+(person|system|container|component)\b/i;
@@ -12284,6 +12419,11 @@ var init_dgmo_router = __esm({
12284
12419
  ["gantt", (c) => parseGantt(c)],
12285
12420
  ["boxes-and-lines", (c) => parseBoxesAndLines(c)]
12286
12421
  ]);
12422
+ ALL_KNOWN_TYPES = /* @__PURE__ */ new Set([
12423
+ ...DATA_CHART_TYPES,
12424
+ ...VISUALIZATION_TYPES,
12425
+ ...DIAGRAM_TYPES
12426
+ ]);
12287
12427
  }
12288
12428
  });
12289
12429
 
@@ -14458,7 +14598,6 @@ async function renderSitemapForExport(content, theme, palette) {
14458
14598
  const { parseSitemap: parseSitemap2 } = await Promise.resolve().then(() => (init_parser7(), parser_exports7));
14459
14599
  const { layoutSitemap: layoutSitemap2 } = await Promise.resolve().then(() => (init_layout2(), layout_exports2));
14460
14600
  const { getPalette: getPalette2 } = await Promise.resolve().then(() => (init_palettes(), palettes_exports));
14461
- const { injectBranding: injectBranding2 } = await Promise.resolve().then(() => (init_branding(), branding_exports));
14462
14601
  const isDark = theme === "dark";
14463
14602
  const effectivePalette = palette ?? (isDark ? getPalette2("nord").dark : getPalette2("nord").light);
14464
14603
  const parsed = parseSitemap2(content, effectivePalette);
@@ -14500,8 +14639,7 @@ async function renderSitemapForExport(content, theme, palette) {
14500
14639
  svgEl.style.fontFamily = FONT_FAMILY;
14501
14640
  const svgHtml = svgEl.outerHTML;
14502
14641
  document.body.removeChild(container);
14503
- const brandColor = theme === "transparent" ? "#888" : effectivePalette.textMuted;
14504
- return injectBranding2(svgHtml, brandColor);
14642
+ return svgHtml;
14505
14643
  }
14506
14644
  var DIAGRAM_PADDING2, MAX_SCALE2, TITLE_HEIGHT2, LABEL_FONT_SIZE2, META_FONT_SIZE2, META_LINE_HEIGHT4, HEADER_HEIGHT4, SEPARATOR_GAP4, EDGE_STROKE_WIDTH2, NODE_STROKE_WIDTH2, CARD_RADIUS2, CONTAINER_RADIUS2, CONTAINER_LABEL_FONT_SIZE2, CONTAINER_META_FONT_SIZE2, CONTAINER_META_LINE_HEIGHT4, CONTAINER_HEADER_HEIGHT2, ARROWHEAD_W, ARROWHEAD_H, EDGE_LABEL_FONT_SIZE, COLLAPSE_BAR_HEIGHT2, LEGEND_FIXED_GAP2, lineGenerator, lineGeneratorLinear;
14507
14645
  var init_renderer2 = __esm({
@@ -14705,7 +14843,6 @@ var init_mutations = __esm({
14705
14843
  // src/kanban/renderer.ts
14706
14844
  var renderer_exports3 = {};
14707
14845
  __export(renderer_exports3, {
14708
- bucketCardsBySwimlane: () => bucketCardsBySwimlane,
14709
14846
  renderKanban: () => renderKanban,
14710
14847
  renderKanbanForExport: () => renderKanbanForExport
14711
14848
  });
@@ -23718,8 +23855,8 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
23718
23855
  });
23719
23856
  }
23720
23857
  drawLegend();
23721
- const startTime = dateToFractionalYear(resolved.startDate);
23722
- const endTime = dateToFractionalYear(resolved.endDate);
23858
+ const startTime = dateToFractionalYear2(resolved.startDate);
23859
+ const endTime = dateToFractionalYear2(resolved.endDate);
23723
23860
  const domainPad = Math.max((endTime - startTime) * 0.02, 0.01);
23724
23861
  const domainMin = startTime - domainPad;
23725
23862
  const domainMax = endTime + domainPad;
@@ -23750,7 +23887,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
23750
23887
  } else {
23751
23888
  todayDate = /* @__PURE__ */ new Date(resolved.options.todayMarker + "T00:00:00");
23752
23889
  }
23753
- todayX = xScale(dateToFractionalYear(todayDate));
23890
+ todayX = xScale(dateToFractionalYear2(todayDate));
23754
23891
  if (todayX >= 0 && todayX <= innerWidth) {
23755
23892
  const todayLine = g.append("line").attr("class", "gantt-today").attr("x1", todayX).attr("y1", 0).attr("x2", todayX).attr("y2", innerHeight + 10).attr("stroke", todayColor).attr("stroke-width", 2).attr("stroke-dasharray", "6 4").attr("opacity", 0.7).attr("pointer-events", "none");
23756
23893
  if (todayMarkerLineNum)
@@ -23792,8 +23929,8 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
23792
23929
  let lx2 = innerWidth;
23793
23930
  let laneBarWidth = innerWidth;
23794
23931
  if (row.laneStartDate && row.laneEndDate) {
23795
- lx1 = xScale(dateToFractionalYear(row.laneStartDate));
23796
- lx2 = xScale(dateToFractionalYear(row.laneEndDate));
23932
+ lx1 = xScale(dateToFractionalYear2(row.laneStartDate));
23933
+ lx2 = xScale(dateToFractionalYear2(row.laneEndDate));
23797
23934
  laneBarWidth = Math.max(lx2 - lx1, 2);
23798
23935
  }
23799
23936
  lanePositions.set(row.laneName, {
@@ -23899,8 +24036,8 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
23899
24036
  labelG.append("text").attr("x", labelX).attr("y", marginTop + yOffset + BAR_H / 2).attr("dy", "0.35em").attr("text-anchor", "start").attr("font-size", "11px").attr("font-weight", "bold").attr("fill", palette.text).text(
23900
24037
  toggleIcon + " " + group.name + (group.progress !== null ? ` ${Math.round(group.progress)}%` : "")
23901
24038
  );
23902
- const gStart = dateToFractionalYear(group.startDate);
23903
- const gEnd = dateToFractionalYear(group.endDate);
24039
+ const gStart = dateToFractionalYear2(group.startDate);
24040
+ const gEnd = dateToFractionalYear2(group.endDate);
23904
24041
  const gx1 = xScale(gStart);
23905
24042
  const gx2 = xScale(gEnd);
23906
24043
  if (gx2 > gx1) {
@@ -24014,7 +24151,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
24014
24151
  taskLabel.attr("data-critical-path", "true");
24015
24152
  }
24016
24153
  if (rt.isMilestone) {
24017
- const mx = xScale(dateToFractionalYear(rt.startDate));
24154
+ const mx = xScale(dateToFractionalYear2(rt.startDate));
24018
24155
  const my = yOffset + BAR_H / 2;
24019
24156
  g.append("polygon").attr("class", "gantt-milestone").attr("points", diamondPoints(mx, my, MILESTONE_SIZE)).attr("fill", barColor).attr("stroke", barColor).attr("stroke-width", 1.5).attr("data-line-number", String(task.lineNumber)).attr("data-task-name", task.label).attr("data-task-id", task.id).attr("data-group", topGroup).style("cursor", onClickItem ? "pointer" : "default").on("click", () => {
24020
24157
  if (onClickItem) onClickItem(task.lineNumber);
@@ -24036,8 +24173,8 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
24036
24173
  });
24037
24174
  taskPositions.set(task.id, { x1: mx, x2: mx, y: my });
24038
24175
  } else {
24039
- const tStart = dateToFractionalYear(rt.startDate);
24040
- const tEnd = dateToFractionalYear(rt.endDate);
24176
+ const tStart = dateToFractionalYear2(rt.startDate);
24177
+ const tEnd = dateToFractionalYear2(rt.endDate);
24041
24178
  const x1 = xScale(tStart);
24042
24179
  const x2 = xScale(tEnd);
24043
24180
  const barWidth = Math.max(x2 - x1, 2);
@@ -24272,14 +24409,14 @@ function renderHolidayBands(g, svg, resolved, xScale, innerHeight, palette, isDa
24272
24409
  }
24273
24410
  }
24274
24411
  function drawBand(g, xScale, start, end, height, palette, _isDark, className, opacity) {
24275
- const x1 = xScale(dateToFractionalYear(start));
24276
- const x2 = xScale(dateToFractionalYear(end));
24412
+ const x1 = xScale(dateToFractionalYear2(start));
24413
+ const x2 = xScale(dateToFractionalYear2(end));
24277
24414
  if (x2 <= x1) return;
24278
24415
  g.append("rect").attr("class", className).attr("x", x1).attr("y", 0).attr("width", x2 - x1).attr("height", height).attr("fill", palette.text).attr("opacity", opacity).attr("pointer-events", "none");
24279
24416
  }
24280
24417
  function drawHolidayBand(g, svg, xScale, start, end, height, palette, _isDark, label, lineNumber, headerY, chartLeftMargin, onClickItem) {
24281
- const x1 = xScale(dateToFractionalYear(start));
24282
- const x2 = xScale(dateToFractionalYear(end));
24418
+ const x1 = xScale(dateToFractionalYear2(start));
24419
+ const x2 = xScale(dateToFractionalYear2(end));
24283
24420
  if (x2 <= x1) return;
24284
24421
  const bandW = Math.max(x2 - x1, 4);
24285
24422
  const baseOpacity = 0.08;
@@ -24801,8 +24938,8 @@ function renderSprintBands(g, svg, resolved, xScale, innerHeight, palette) {
24801
24938
  const chartMinX = 0;
24802
24939
  for (let i = 0; i < resolved.sprints.length; i++) {
24803
24940
  const sprint = resolved.sprints[i];
24804
- const rawSx = xScale(dateToFractionalYear(sprint.startDate));
24805
- const rawEx = xScale(dateToFractionalYear(sprint.endDate));
24941
+ const rawSx = xScale(dateToFractionalYear2(sprint.startDate));
24942
+ const rawEx = xScale(dateToFractionalYear2(sprint.endDate));
24806
24943
  if (rawEx <= rawSx) continue;
24807
24944
  const sx = Math.max(rawSx, chartMinX);
24808
24945
  const ex = rawEx;
@@ -24924,7 +25061,7 @@ function renderSprintBands(g, svg, resolved, xScale, innerHeight, palette) {
24924
25061
  });
24925
25062
  }
24926
25063
  const lastSprint = resolved.sprints[resolved.sprints.length - 1];
24927
- const lastEx = xScale(dateToFractionalYear(lastSprint.endDate));
25064
+ const lastEx = xScale(dateToFractionalYear2(lastSprint.endDate));
24928
25065
  g.append("line").attr("class", "gantt-sprint-boundary").attr("x1", lastEx).attr("y1", -6).attr("x2", lastEx).attr("y2", innerHeight).attr("stroke", bandColor).attr("stroke-width", 1).attr("stroke-dasharray", "3 3").attr("opacity", SPRINT_BOUNDARY_OPACITY);
24929
25066
  }
24930
25067
  function parseDateStringToDate(s) {
@@ -24935,7 +25072,7 @@ function parseDateStringToDate(s) {
24935
25072
  return new Date(year, month, day);
24936
25073
  }
24937
25074
  function parseDateToFractionalYear(s) {
24938
- return dateToFractionalYear(parseDateStringToDate(s));
25075
+ return dateToFractionalYear2(parseDateStringToDate(s));
24939
25076
  }
24940
25077
  function highlightDeps(g, svg, taskId, resolved) {
24941
25078
  const related = /* @__PURE__ */ new Set([taskId]);
@@ -25302,7 +25439,7 @@ function durationWeightedProgress(tasks) {
25302
25439
  }
25303
25440
  return hasProgress && totalDuration > 0 ? totalProgress / totalDuration : null;
25304
25441
  }
25305
- function dateToFractionalYear(d) {
25442
+ function dateToFractionalYear2(d) {
25306
25443
  const y = d.getFullYear();
25307
25444
  const startOfYear = new Date(y, 0, 1);
25308
25445
  const endOfYear = new Date(y + 1, 0, 1);
@@ -25314,7 +25451,7 @@ function diamondPoints(cx, cy, size) {
25314
25451
  return `${cx},${cy - half} ${cx + half},${cy} ${cx},${cy + half} ${cx - half},${cy}`;
25315
25452
  }
25316
25453
  function formatGanttDate(d) {
25317
- const base = `${MONTH_ABBR[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`;
25454
+ const base = `${MONTH_ABBR2[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`;
25318
25455
  if (d.getHours() === 0 && d.getMinutes() === 0) return base;
25319
25456
  const hh = String(d.getHours()).padStart(2, "0");
25320
25457
  const mm = String(d.getMinutes()).padStart(2, "0");
@@ -25325,7 +25462,7 @@ function showGanttDateIndicators(g, xScale, startDate, endDate, innerHeight, col
25325
25462
  g.selectAll(".gantt-today").attr("opacity", 0.05);
25326
25463
  const hg = g.append("g").attr("class", "gantt-hover-date").attr("pointer-events", "none");
25327
25464
  const tickLen = 6;
25328
- const startPos = xScale(dateToFractionalYear(startDate));
25465
+ const startPos = xScale(dateToFractionalYear2(startDate));
25329
25466
  const startLabel = formatGanttDate(startDate);
25330
25467
  if (!options?.skipStartLine) {
25331
25468
  hg.append("line").attr("class", "gantt-hover-date").attr("x1", startPos).attr("y1", -tickLen).attr("x2", startPos).attr("y2", innerHeight).attr("stroke", color).attr("stroke-width", 1.5).attr("stroke-dasharray", "4 4").attr("opacity", 0.6);
@@ -25333,7 +25470,7 @@ function showGanttDateIndicators(g, xScale, startDate, endDate, innerHeight, col
25333
25470
  hg.append("text").attr("class", "gantt-hover-date").attr("x", startPos).attr("y", -tickLen - 4).attr("text-anchor", "middle").attr("fill", color).attr("font-size", "10px").attr("font-weight", "600").text(startLabel);
25334
25471
  hg.append("text").attr("class", "gantt-hover-date").attr("x", startPos).attr("y", innerHeight + tickLen + 12).attr("text-anchor", "middle").attr("fill", color).attr("font-size", "10px").attr("font-weight", "600").text(startLabel);
25335
25472
  if (endDate && endDate.getTime() !== startDate.getTime()) {
25336
- const endPos = xScale(dateToFractionalYear(endDate));
25473
+ const endPos = xScale(dateToFractionalYear2(endDate));
25337
25474
  const endLabel = formatGanttDate(endDate);
25338
25475
  const minLabelGap = 90;
25339
25476
  const gap = endPos - startPos;
@@ -25403,7 +25540,7 @@ function renderTimeScaleHorizontal(g, scale, innerWidth, innerHeight, textColor)
25403
25540
  g.append("text").attr("class", "gantt-scale-tick").attr("x", tick.pos).attr("y", innerHeight + tickLen + 12).attr("text-anchor", "middle").attr("font-size", "10px").attr("fill", textColor).attr("opacity", opacity).text(tick.label);
25404
25541
  }
25405
25542
  }
25406
- var BAR_H, ROW_GAP, GROUP_GAP2, MILESTONE_SIZE, MIN_LEFT_MARGIN, BOTTOM_MARGIN, RIGHT_MARGIN, CHAR_W2, LABEL_PAD, LABEL_GAP, BAND_ACCENT_W, BAND_RADIUS, bandClipCounter, JS_DAY_TO_WEEKDAY2, ERA_COLORS, SPRINT_BAND_OPACITY, SPRINT_HOVER_OPACITY, SPRINT_BOUNDARY_OPACITY, FADE_OPACITY, MONTH_ABBR;
25543
+ var BAR_H, ROW_GAP, GROUP_GAP2, MILESTONE_SIZE, MIN_LEFT_MARGIN, BOTTOM_MARGIN, RIGHT_MARGIN, CHAR_W2, LABEL_PAD, LABEL_GAP, BAND_ACCENT_W, BAND_RADIUS, bandClipCounter, JS_DAY_TO_WEEKDAY2, ERA_COLORS, SPRINT_BAND_OPACITY, SPRINT_HOVER_OPACITY, SPRINT_BOUNDARY_OPACITY, FADE_OPACITY, MONTH_ABBR2;
25407
25544
  var init_renderer9 = __esm({
25408
25545
  "src/gantt/renderer.ts"() {
25409
25546
  "use strict";
@@ -25411,7 +25548,7 @@ var init_renderer9 = __esm({
25411
25548
  init_palettes();
25412
25549
  init_color_utils();
25413
25550
  init_tag_groups();
25414
- init_d3();
25551
+ init_time_ticks();
25415
25552
  init_legend_constants();
25416
25553
  init_legend_d3();
25417
25554
  init_legend_layout();
@@ -25443,7 +25580,7 @@ var init_renderer9 = __esm({
25443
25580
  SPRINT_HOVER_OPACITY = 0.12;
25444
25581
  SPRINT_BOUNDARY_OPACITY = 0.3;
25445
25582
  FADE_OPACITY = 0.1;
25446
- MONTH_ABBR = [
25583
+ MONTH_ABBR2 = [
25447
25584
  "Jan",
25448
25585
  "Feb",
25449
25586
  "Mar",
@@ -27088,7 +27225,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
27088
27225
  if (tagKey && msgTagValue) {
27089
27226
  labelEl.attr(`data-tag-${tagKey}`, msgTagValue.toLowerCase());
27090
27227
  }
27091
- renderInlineText(labelEl, step.label, palette);
27228
+ labelEl.text(step.label);
27092
27229
  }
27093
27230
  } else {
27094
27231
  const goingRight = fromX < toX;
@@ -27115,7 +27252,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
27115
27252
  if (tagKey && msgTagValue) {
27116
27253
  labelEl.attr(`data-tag-${tagKey}`, msgTagValue.toLowerCase());
27117
27254
  }
27118
- renderInlineText(labelEl, step.label, palette);
27255
+ labelEl.text(step.label);
27119
27256
  }
27120
27257
  }
27121
27258
  } else {
@@ -27146,7 +27283,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
27146
27283
  if (tagKey && msgTagValue) {
27147
27284
  labelEl.attr(`data-tag-${tagKey}`, msgTagValue.toLowerCase());
27148
27285
  }
27149
- renderInlineText(labelEl, step.label, palette);
27286
+ labelEl.text(step.label);
27150
27287
  }
27151
27288
  }
27152
27289
  });
@@ -27423,23 +27560,6 @@ function parseTimelineDate(s) {
27423
27560
  const day = parts.length >= 3 ? parts[2] : 1;
27424
27561
  return year + (month - 1) / 12 + (day - 1) / 365 + hour / 8760 + minute / 525600;
27425
27562
  }
27426
- function fractionalYearToDate(frac) {
27427
- const year = Math.floor(frac);
27428
- const remainder = frac - year;
27429
- const monthFrac = remainder * 12;
27430
- const month = Math.floor(monthFrac);
27431
- const monthRemainder = remainder - month / 12;
27432
- const dayFrac = monthRemainder * 365;
27433
- const day = Math.floor(dayFrac) + 1;
27434
- const dayRemainder = dayFrac - Math.floor(dayFrac);
27435
- const hourFrac = dayRemainder * 24;
27436
- const hour = Math.floor(hourFrac);
27437
- const minute = Math.round((hourFrac - hour) * 60);
27438
- return new Date(year, month, day, hour, minute);
27439
- }
27440
- function dateToFractionalYear2(d) {
27441
- return d.getFullYear() + d.getMonth() / 12 + (d.getDate() - 1) / 365 + d.getHours() / 8760 + d.getMinutes() / 525600;
27442
- }
27443
27563
  function addDurationToDate(startDate, amount, unit) {
27444
27564
  const spaceIdx = startDate.indexOf(" ");
27445
27565
  let datePart = startDate;
@@ -28994,7 +29114,7 @@ function formatDateLabel(dateStr) {
28994
29114
  const parts = datePart.split("-");
28995
29115
  const year = parts[0];
28996
29116
  if (parts.length === 1) return year + timeSuffix;
28997
- const month = MONTH_ABBR2[parseInt(parts[1], 10) - 1];
29117
+ const month = MONTH_ABBR[parseInt(parts[1], 10) - 1];
28998
29118
  if (parts.length === 2) return `${month} ${year}${timeSuffix}`;
28999
29119
  const day = parseInt(parts[2], 10);
29000
29120
  return `${month} ${day}, ${year}${timeSuffix}`;
@@ -29011,123 +29131,6 @@ function formatBoundaryLabel(dateStr, otherDateStr) {
29011
29131
  }
29012
29132
  return formatDateLabel(dateStr);
29013
29133
  }
29014
- function computeTimeTicks(domainMin, domainMax, scale, boundaryStart, boundaryEnd, boundaryStartLabel, boundaryEndLabel) {
29015
- const minYear = Math.floor(domainMin);
29016
- const maxYear = Math.floor(domainMax);
29017
- const span = domainMax - domainMin;
29018
- let ticks = [];
29019
- const firstYear = Math.ceil(domainMin);
29020
- const lastYear = Math.floor(domainMax);
29021
- if (lastYear >= firstYear + 1) {
29022
- const yearSpan = lastYear - firstYear;
29023
- let step = 1;
29024
- if (yearSpan > 80) step = 20;
29025
- else if (yearSpan > 40) step = 10;
29026
- else if (yearSpan > 20) step = 5;
29027
- else if (yearSpan > 10) step = 2;
29028
- const alignedFirst = Math.ceil(firstYear / step) * step;
29029
- for (let y = alignedFirst; y <= lastYear; y += step) {
29030
- ticks.push({ pos: scale(y), label: String(y) });
29031
- }
29032
- } else if (span > 0.25) {
29033
- const crossesYear = maxYear > minYear;
29034
- for (let y = minYear; y <= maxYear + 1; y++) {
29035
- for (let m = 1; m <= 12; m++) {
29036
- const val = y + (m - 1) / 12;
29037
- if (val > domainMax) break;
29038
- if (val >= domainMin) {
29039
- ticks.push({
29040
- pos: scale(val),
29041
- label: crossesYear ? `${MONTH_ABBR2[m - 1]} '${String(y).slice(-2)}` : MONTH_ABBR2[m - 1]
29042
- });
29043
- }
29044
- }
29045
- }
29046
- } else if (span <= 685e-6) {
29047
- let stepMin = 5;
29048
- const spanHours = span * 8760;
29049
- if (spanHours > 3) stepMin = 30;
29050
- else if (spanHours > 1) stepMin = 15;
29051
- else if (spanHours > 0.5) stepMin = 10;
29052
- const startDate = fractionalYearToDate(domainMin);
29053
- startDate.setMinutes(
29054
- Math.floor(startDate.getMinutes() / stepMin) * stepMin,
29055
- 0,
29056
- 0
29057
- );
29058
- while (true) {
29059
- const val = dateToFractionalYear2(startDate);
29060
- if (val > domainMax) break;
29061
- if (val >= domainMin) {
29062
- const hh = String(startDate.getHours()).padStart(2, "0");
29063
- const mm = String(startDate.getMinutes()).padStart(2, "0");
29064
- ticks.push({ pos: scale(val), label: `${hh}:${mm}` });
29065
- }
29066
- startDate.setMinutes(startDate.getMinutes() + stepMin);
29067
- }
29068
- } else if (span <= 822e-5) {
29069
- let stepHour = 1;
29070
- const spanHours = span * 8760;
29071
- if (spanHours > 48) stepHour = 6;
29072
- else if (spanHours > 24) stepHour = 3;
29073
- else if (spanHours > 12) stepHour = 2;
29074
- const singleDay = spanHours <= 24;
29075
- const startDate = fractionalYearToDate(domainMin);
29076
- startDate.setHours(
29077
- Math.floor(startDate.getHours() / stepHour) * stepHour,
29078
- 0,
29079
- 0,
29080
- 0
29081
- );
29082
- while (true) {
29083
- const val = dateToFractionalYear2(startDate);
29084
- if (val > domainMax) break;
29085
- if (val >= domainMin) {
29086
- const hh = String(startDate.getHours()).padStart(2, "0");
29087
- const mm = String(startDate.getMinutes()).padStart(2, "0");
29088
- if (singleDay) {
29089
- ticks.push({ pos: scale(val), label: `${hh}:${mm}` });
29090
- } else {
29091
- const mon = MONTH_ABBR2[startDate.getMonth()];
29092
- const d = startDate.getDate();
29093
- ticks.push({ pos: scale(val), label: `${mon} ${d} ${hh}:${mm}` });
29094
- }
29095
- }
29096
- startDate.setHours(startDate.getHours() + stepHour);
29097
- }
29098
- } else {
29099
- for (let y = minYear; y <= maxYear + 1; y++) {
29100
- for (let m = 1; m <= 12; m++) {
29101
- for (const d of [1, 8, 15, 22]) {
29102
- const val = y + (m - 1) / 12 + (d - 1) / 365;
29103
- if (val > domainMax) break;
29104
- if (val >= domainMin) {
29105
- ticks.push({
29106
- pos: scale(val),
29107
- label: `${MONTH_ABBR2[m - 1]} ${d}`
29108
- });
29109
- }
29110
- }
29111
- }
29112
- }
29113
- }
29114
- const collisionThreshold = 40;
29115
- if (boundaryStart !== void 0 && boundaryStartLabel) {
29116
- const boundaryPos = scale(boundaryStart);
29117
- ticks = ticks.filter(
29118
- (t) => Math.abs(t.pos - boundaryPos) >= collisionThreshold
29119
- );
29120
- ticks.unshift({ pos: boundaryPos, label: boundaryStartLabel });
29121
- }
29122
- if (boundaryEnd !== void 0 && boundaryEndLabel) {
29123
- const boundaryPos = scale(boundaryEnd);
29124
- ticks = ticks.filter(
29125
- (t) => Math.abs(t.pos - boundaryPos) >= collisionThreshold
29126
- );
29127
- ticks.push({ pos: boundaryPos, label: boundaryEndLabel });
29128
- }
29129
- return ticks;
29130
- }
29131
29134
  function renderTimeScale(g, scale, isVertical, innerWidth, innerHeight, textColor, boundaryStart, boundaryEnd, boundaryStartLabel, boundaryEndLabel) {
29132
29135
  const [domainMin, domainMax] = scale.domain();
29133
29136
  const ticks = computeTimeTicks(
@@ -31055,7 +31058,7 @@ function createExportContainer(width, height) {
31055
31058
  document.body.appendChild(container);
31056
31059
  return container;
31057
31060
  }
31058
- function finalizeSvgExport(container, theme, palette, options) {
31061
+ function finalizeSvgExport(container, theme, palette) {
31059
31062
  const svgEl = container.querySelector("svg");
31060
31063
  if (!svgEl) return "";
31061
31064
  if (theme === "transparent") {
@@ -31068,10 +31071,6 @@ function finalizeSvgExport(container, theme, palette, options) {
31068
31071
  svgEl.querySelectorAll("[data-export-ignore]").forEach((el) => el.remove());
31069
31072
  const svgHtml = svgEl.outerHTML;
31070
31073
  document.body.removeChild(container);
31071
- if (options?.branding !== false) {
31072
- const brandColor = theme === "transparent" ? "#888" : palette.textMuted;
31073
- return injectBranding(svgHtml, brandColor);
31074
- }
31075
31074
  return svgHtml;
31076
31075
  }
31077
31076
  async function renderForExport(content, theme, palette, orgExportState, options) {
@@ -31118,7 +31117,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
31118
31117
  activeTagGroup,
31119
31118
  hiddenAttributes
31120
31119
  );
31121
- return finalizeSvgExport(container2, theme, effectivePalette2, options);
31120
+ return finalizeSvgExport(container2, theme, effectivePalette2);
31122
31121
  }
31123
31122
  if (detectedType === "sitemap") {
31124
31123
  const { parseSitemap: parseSitemap2 } = await Promise.resolve().then(() => (init_parser7(), parser_exports7));
@@ -31160,7 +31159,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
31160
31159
  activeTagGroup,
31161
31160
  hiddenAttributes
31162
31161
  );
31163
- return finalizeSvgExport(container2, theme, effectivePalette2, options);
31162
+ return finalizeSvgExport(container2, theme, effectivePalette2);
31164
31163
  }
31165
31164
  if (detectedType === "kanban") {
31166
31165
  const { parseKanban: parseKanban2 } = await Promise.resolve().then(() => (init_parser5(), parser_exports5));
@@ -31179,7 +31178,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
31179
31178
  options?.tagGroup
31180
31179
  )
31181
31180
  });
31182
- return finalizeSvgExport(container2, theme, effectivePalette2, options);
31181
+ return finalizeSvgExport(container2, theme, effectivePalette2);
31183
31182
  }
31184
31183
  if (detectedType === "class") {
31185
31184
  const { parseClassDiagram: parseClassDiagram2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports2));
@@ -31203,7 +31202,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
31203
31202
  void 0,
31204
31203
  { width: exportWidth, height: exportHeight }
31205
31204
  );
31206
- return finalizeSvgExport(container2, theme, effectivePalette2, options);
31205
+ return finalizeSvgExport(container2, theme, effectivePalette2);
31207
31206
  }
31208
31207
  if (detectedType === "er") {
31209
31208
  const { parseERDiagram: parseERDiagram2 } = await Promise.resolve().then(() => (init_parser3(), parser_exports3));
@@ -31232,7 +31231,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
31232
31231
  options?.tagGroup
31233
31232
  )
31234
31233
  );
31235
- return finalizeSvgExport(container2, theme, effectivePalette2, options);
31234
+ return finalizeSvgExport(container2, theme, effectivePalette2);
31236
31235
  }
31237
31236
  if (detectedType === "boxes-and-lines") {
31238
31237
  const { parseBoxesAndLines: parseBoxesAndLines2 } = await Promise.resolve().then(() => (init_parser10(), parser_exports10));
@@ -31258,7 +31257,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
31258
31257
  activeTagGroup: options?.tagGroup
31259
31258
  }
31260
31259
  );
31261
- return finalizeSvgExport(container2, theme, effectivePalette2, options);
31260
+ return finalizeSvgExport(container2, theme, effectivePalette2);
31262
31261
  }
31263
31262
  if (detectedType === "c4") {
31264
31263
  const { parseC4: parseC42 } = await Promise.resolve().then(() => (init_parser6(), parser_exports6));
@@ -31297,7 +31296,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
31297
31296
  options?.tagGroup
31298
31297
  )
31299
31298
  );
31300
- return finalizeSvgExport(container2, theme, effectivePalette2, options);
31299
+ return finalizeSvgExport(container2, theme, effectivePalette2);
31301
31300
  }
31302
31301
  if (detectedType === "flowchart") {
31303
31302
  const { parseFlowchart: parseFlowchart2 } = await Promise.resolve().then(() => (init_flowchart_parser(), flowchart_parser_exports));
@@ -31317,7 +31316,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
31317
31316
  void 0,
31318
31317
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
31319
31318
  );
31320
- return finalizeSvgExport(container2, theme, effectivePalette2, options);
31319
+ return finalizeSvgExport(container2, theme, effectivePalette2);
31321
31320
  }
31322
31321
  if (detectedType === "infra") {
31323
31322
  const { parseInfra: parseInfra2 } = await Promise.resolve().then(() => (init_parser8(), parser_exports8));
@@ -31363,7 +31362,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
31363
31362
  infraSvg.setAttribute("width", String(exportWidth));
31364
31363
  infraSvg.setAttribute("height", String(exportHeight));
31365
31364
  }
31366
- return finalizeSvgExport(container2, theme, effectivePalette2, options);
31365
+ return finalizeSvgExport(container2, theme, effectivePalette2);
31367
31366
  }
31368
31367
  if (detectedType === "gantt") {
31369
31368
  const { parseGantt: parseGantt2 } = await Promise.resolve().then(() => (init_parser9(), parser_exports9));
@@ -31384,7 +31383,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
31384
31383
  void 0,
31385
31384
  { width: EXPORT_W, height: EXPORT_H }
31386
31385
  );
31387
- return finalizeSvgExport(container2, theme, effectivePalette2, options);
31386
+ return finalizeSvgExport(container2, theme, effectivePalette2);
31388
31387
  }
31389
31388
  if (detectedType === "state") {
31390
31389
  const { parseState: parseState2 } = await Promise.resolve().then(() => (init_state_parser(), state_parser_exports));
@@ -31404,7 +31403,7 @@ async function renderForExport(content, theme, palette, orgExportState, options)
31404
31403
  void 0,
31405
31404
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
31406
31405
  );
31407
- return finalizeSvgExport(container2, theme, effectivePalette2, options);
31406
+ return finalizeSvgExport(container2, theme, effectivePalette2);
31408
31407
  }
31409
31408
  const parsed = parseVisualization(content, palette);
31410
31409
  if (parsed.error && parsed.type !== "sequence") {
@@ -31496,15 +31495,15 @@ async function renderForExport(content, theme, palette, orgExportState, options)
31496
31495
  dims
31497
31496
  );
31498
31497
  }
31499
- return finalizeSvgExport(container, theme, effectivePalette, options);
31498
+ return finalizeSvgExport(container, theme, effectivePalette);
31500
31499
  }
31501
- var DEFAULT_CLOUD_OPTIONS, STOP_WORDS, SLOPE_MARGIN, SLOPE_LABEL_FONT_SIZE, SLOPE_CHAR_WIDTH, ARC_MARGIN, MONTH_ABBR2, EXPORT_WIDTH, EXPORT_HEIGHT;
31500
+ var DEFAULT_CLOUD_OPTIONS, STOP_WORDS, SLOPE_MARGIN, SLOPE_LABEL_FONT_SIZE, SLOPE_CHAR_WIDTH, ARC_MARGIN, EXPORT_WIDTH, EXPORT_HEIGHT;
31502
31501
  var init_d3 = __esm({
31503
31502
  "src/d3.ts"() {
31504
31503
  "use strict";
31505
31504
  init_fonts();
31506
- init_branding();
31507
31505
  init_label_layout();
31506
+ init_time_ticks();
31508
31507
  init_colors();
31509
31508
  init_palettes();
31510
31509
  init_color_utils();
@@ -31635,20 +31634,6 @@ var init_d3 = __esm({
31635
31634
  SLOPE_LABEL_FONT_SIZE = 14;
31636
31635
  SLOPE_CHAR_WIDTH = 8;
31637
31636
  ARC_MARGIN = { top: 60, right: 40, bottom: 60, left: 40 };
31638
- MONTH_ABBR2 = [
31639
- "Jan",
31640
- "Feb",
31641
- "Mar",
31642
- "Apr",
31643
- "May",
31644
- "Jun",
31645
- "Jul",
31646
- "Aug",
31647
- "Sep",
31648
- "Oct",
31649
- "Nov",
31650
- "Dec"
31651
- ];
31652
31637
  EXPORT_WIDTH = 1200;
31653
31638
  EXPORT_HEIGHT = 800;
31654
31639
  }
@@ -32113,6 +32098,7 @@ var require_lz_string = __commonJS({
32113
32098
 
32114
32099
  // src/index.ts
32115
32100
  init_diagnostics();
32101
+ init_arrows();
32116
32102
 
32117
32103
  // src/render.ts
32118
32104
  init_d3();
@@ -32148,8 +32134,8 @@ async function ensureDom() {
32148
32134
  async function render(content, options) {
32149
32135
  const theme = options?.theme ?? "light";
32150
32136
  const paletteName = options?.palette ?? "nord";
32151
- const branding = options?.branding ?? false;
32152
32137
  const paletteColors = getPalette(paletteName)[theme === "dark" ? "dark" : "light"];
32138
+ const { diagnostics } = parseDgmo(content);
32153
32139
  const chartType = parseDgmoChartType(content);
32154
32140
  const category = chartType ? getRenderCategory(chartType) : null;
32155
32141
  const legendExportState = options?.legendState ? {
@@ -32157,18 +32143,27 @@ async function render(content, options) {
32157
32143
  hiddenAttributes: options.legendState.hiddenAttributes ? new Set(options.legendState.hiddenAttributes) : void 0
32158
32144
  } : void 0;
32159
32145
  if (category === "data-chart") {
32160
- return renderExtendedChartForExport(content, theme, paletteColors, {
32161
- branding
32162
- });
32146
+ const svg2 = await renderExtendedChartForExport(
32147
+ content,
32148
+ theme,
32149
+ paletteColors
32150
+ );
32151
+ return { svg: svg2, diagnostics };
32163
32152
  }
32164
32153
  await ensureDom();
32165
- return renderForExport(content, theme, paletteColors, legendExportState, {
32166
- branding,
32167
- c4Level: options?.c4Level,
32168
- c4System: options?.c4System,
32169
- c4Container: options?.c4Container,
32170
- tagGroup: options?.tagGroup
32171
- });
32154
+ const svg = await renderForExport(
32155
+ content,
32156
+ theme,
32157
+ paletteColors,
32158
+ legendExportState,
32159
+ {
32160
+ c4Level: options?.c4Level,
32161
+ c4System: options?.c4System,
32162
+ c4Container: options?.c4Container,
32163
+ tagGroup: options?.tagGroup
32164
+ }
32165
+ );
32166
+ return { svg, diagnostics };
32172
32167
  }
32173
32168
 
32174
32169
  // src/index.ts
@@ -32176,172 +32171,9 @@ init_dgmo_router();
32176
32171
  init_chart();
32177
32172
  init_echarts();
32178
32173
  init_d3();
32174
+ init_time_ticks();
32179
32175
  init_parser();
32180
32176
  init_participant_inference();
32181
-
32182
- // src/dgmo-mermaid.ts
32183
- init_colors();
32184
- init_diagnostics();
32185
- var QUADRANT_LABEL_RE = /^(.+?)(?:\s*\(([^)]+)\))?\s*$/;
32186
- var DATA_POINT_RE = /^(.+?)\s+([0-9]*\.?[0-9]+)\s*,\s*([0-9]*\.?[0-9]+)\s*$/;
32187
- var QUADRANT_POSITIONS = /* @__PURE__ */ new Set([
32188
- "top-right",
32189
- "top-left",
32190
- "bottom-left",
32191
- "bottom-right"
32192
- ]);
32193
- function parseQuadrant(content) {
32194
- const result = {
32195
- title: null,
32196
- titleLineNumber: null,
32197
- xAxis: null,
32198
- xAxisLineNumber: null,
32199
- yAxis: null,
32200
- yAxisLineNumber: null,
32201
- quadrants: {
32202
- topRight: null,
32203
- topLeft: null,
32204
- bottomLeft: null,
32205
- bottomRight: null
32206
- },
32207
- points: [],
32208
- diagnostics: [],
32209
- error: null
32210
- };
32211
- const lines = content.split("\n");
32212
- for (let i = 0; i < lines.length; i++) {
32213
- const line10 = lines[i].trim();
32214
- const lineNumber = i + 1;
32215
- if (!line10 || line10.startsWith("//")) continue;
32216
- if (/^chart\s*:/i.test(line10)) continue;
32217
- const titleMatch = line10.match(/^title\s+(.+)/i);
32218
- if (titleMatch) {
32219
- result.title = titleMatch[1].trim();
32220
- result.titleLineNumber = lineNumber;
32221
- continue;
32222
- }
32223
- const xMatch = line10.match(/^x-label\s+(.+)/i);
32224
- if (xMatch) {
32225
- const parts = xMatch[1].split(",").map((s) => s.trim());
32226
- if (parts.length >= 2) {
32227
- result.xAxis = [parts[0], parts[1]];
32228
- result.xAxisLineNumber = lineNumber;
32229
- }
32230
- continue;
32231
- }
32232
- const yMatch = line10.match(/^y-label\s+(.+)/i);
32233
- if (yMatch) {
32234
- const parts = yMatch[1].split(",").map((s) => s.trim());
32235
- if (parts.length >= 2) {
32236
- result.yAxis = [parts[0], parts[1]];
32237
- result.yAxisLineNumber = lineNumber;
32238
- }
32239
- continue;
32240
- }
32241
- const posMatch = line10.match(
32242
- /^(top-right|top-left|bottom-left|bottom-right)\s+(.+)/i
32243
- );
32244
- if (posMatch) {
32245
- const position = posMatch[1].toLowerCase();
32246
- const labelMatch = posMatch[2].match(QUADRANT_LABEL_RE);
32247
- if (labelMatch) {
32248
- const label = {
32249
- text: labelMatch[1].trim(),
32250
- color: labelMatch[2] ? resolveColorWithDiagnostic(
32251
- labelMatch[2].trim(),
32252
- lineNumber,
32253
- result.diagnostics
32254
- ) ?? null : null,
32255
- lineNumber
32256
- };
32257
- if (position === "top-right") result.quadrants.topRight = label;
32258
- else if (position === "top-left") result.quadrants.topLeft = label;
32259
- else if (position === "bottom-left")
32260
- result.quadrants.bottomLeft = label;
32261
- else if (position === "bottom-right")
32262
- result.quadrants.bottomRight = label;
32263
- }
32264
- continue;
32265
- }
32266
- const pointMatch = line10.match(DATA_POINT_RE);
32267
- if (pointMatch) {
32268
- const key = pointMatch[1].trim().toLowerCase();
32269
- if (!QUADRANT_POSITIONS.has(key)) {
32270
- result.points.push({
32271
- label: pointMatch[1].trim(),
32272
- x: parseFloat(pointMatch[2]),
32273
- y: parseFloat(pointMatch[3]),
32274
- lineNumber
32275
- });
32276
- }
32277
- continue;
32278
- }
32279
- }
32280
- if (result.points.length === 0) {
32281
- const diag = makeDgmoError(
32282
- 1,
32283
- "No data points found. Add lines like: Label 0.5, 0.7"
32284
- );
32285
- result.diagnostics.push(diag);
32286
- result.error = formatDgmoError(diag);
32287
- }
32288
- return result;
32289
- }
32290
- function buildMermaidQuadrant(parsed, options = {}) {
32291
- const { isDark = false, textColor, mutedTextColor } = options;
32292
- const lines = [];
32293
- const fillAlpha = isDark ? "30" : "55";
32294
- const primaryText = textColor ?? (isDark ? "#d0d0d0" : "#333333");
32295
- const quadrantLabelText = mutedTextColor ?? (isDark ? "#888888" : "#666666");
32296
- const colorMap = {};
32297
- if (parsed.quadrants.topRight?.color)
32298
- colorMap.quadrant1Fill = parsed.quadrants.topRight.color + fillAlpha;
32299
- if (parsed.quadrants.topLeft?.color)
32300
- colorMap.quadrant2Fill = parsed.quadrants.topLeft.color + fillAlpha;
32301
- if (parsed.quadrants.bottomLeft?.color)
32302
- colorMap.quadrant3Fill = parsed.quadrants.bottomLeft.color + fillAlpha;
32303
- if (parsed.quadrants.bottomRight?.color)
32304
- colorMap.quadrant4Fill = parsed.quadrants.bottomRight.color + fillAlpha;
32305
- colorMap.quadrant1TextFill = quadrantLabelText;
32306
- colorMap.quadrant2TextFill = quadrantLabelText;
32307
- colorMap.quadrant3TextFill = quadrantLabelText;
32308
- colorMap.quadrant4TextFill = quadrantLabelText;
32309
- colorMap.quadrantPointTextFill = primaryText;
32310
- colorMap.quadrantXAxisTextFill = primaryText;
32311
- colorMap.quadrantYAxisTextFill = primaryText;
32312
- colorMap.quadrantTitleFill = primaryText;
32313
- const vars = JSON.stringify(colorMap);
32314
- lines.push(`%%{init: {"themeVariables": ${vars}}}%%`);
32315
- lines.push("quadrantChart");
32316
- if (parsed.title) {
32317
- lines.push(` title ${parsed.title}`);
32318
- }
32319
- if (parsed.xAxis) {
32320
- lines.push(` x-axis ${parsed.xAxis[0]} --> ${parsed.xAxis[1]}`);
32321
- }
32322
- if (parsed.yAxis) {
32323
- lines.push(` y-axis ${parsed.yAxis[0]} --> ${parsed.yAxis[1]}`);
32324
- }
32325
- const quote = (s) => /[\s,:[\]]/.test(s) ? `"${s}"` : s;
32326
- if (parsed.quadrants.topRight) {
32327
- lines.push(` quadrant-1 ${quote(parsed.quadrants.topRight.text)}`);
32328
- }
32329
- if (parsed.quadrants.topLeft) {
32330
- lines.push(` quadrant-2 ${quote(parsed.quadrants.topLeft.text)}`);
32331
- }
32332
- if (parsed.quadrants.bottomLeft) {
32333
- lines.push(` quadrant-3 ${quote(parsed.quadrants.bottomLeft.text)}`);
32334
- }
32335
- if (parsed.quadrants.bottomRight) {
32336
- lines.push(` quadrant-4 ${quote(parsed.quadrants.bottomRight.text)}`);
32337
- }
32338
- for (const point of parsed.points) {
32339
- lines.push(` ${quote(point.label)}: [${point.x}, ${point.y}]`);
32340
- }
32341
- return lines.join("\n");
32342
- }
32343
-
32344
- // src/index.ts
32345
32177
  init_flowchart_parser();
32346
32178
  init_state_parser();
32347
32179
  init_state_renderer();
@@ -33731,9 +33563,9 @@ registerExtractor("boxes-and-lines", extractBoxesAndLinesSymbols);
33731
33563
 
33732
33564
  // src/index.ts
33733
33565
  init_parsing();
33734
- init_branding();
33735
33566
  export {
33736
33567
  ALL_CHART_TYPES,
33568
+ ARROW_DIAGNOSTIC_CODES,
33737
33569
  CHART_TYPES,
33738
33570
  COMPLETION_REGISTRY,
33739
33571
  ENTITY_TYPES,
@@ -33749,13 +33581,10 @@ export {
33749
33581
  applyPositionOverrides,
33750
33582
  boldPalette,
33751
33583
  buildExtendedChartOption,
33752
- buildMermaidQuadrant,
33753
- buildMermaidThemeVars,
33754
33584
  buildNoteMessageMap,
33755
33585
  buildRenderSequence,
33756
33586
  buildSimpleChartOption,
33757
33587
  buildTagLaneRowList,
33758
- buildThemeCSS,
33759
33588
  calculateSchedule,
33760
33589
  catppuccinPalette,
33761
33590
  collapseBoxesAndLines,
@@ -33796,7 +33625,6 @@ export {
33796
33625
  hslToHex,
33797
33626
  inferParticipantType,
33798
33627
  inferRoles,
33799
- injectBranding,
33800
33628
  isArchiveColumn,
33801
33629
  isExtendedChartType,
33802
33630
  isRecognizedColorName,
@@ -33821,8 +33649,8 @@ export {
33821
33649
  looksLikeSitemap,
33822
33650
  looksLikeState,
33823
33651
  makeDgmoError,
33652
+ matchColorParens,
33824
33653
  monokaiPalette,
33825
- mute,
33826
33654
  nord,
33827
33655
  nordPalette,
33828
33656
  oneDarkPalette,
@@ -33840,11 +33668,11 @@ export {
33840
33668
  parseFirstLine,
33841
33669
  parseFlowchart,
33842
33670
  parseGantt,
33671
+ parseInArrowLabel,
33843
33672
  parseInfra,
33844
33673
  parseInlineMarkdown,
33845
33674
  parseKanban,
33846
33675
  parseOrg,
33847
- parseQuadrant,
33848
33676
  parseSequenceDgmo,
33849
33677
  parseSitemap,
33850
33678
  parseState,
@@ -33903,6 +33731,7 @@ export {
33903
33731
  tokyoNightPalette,
33904
33732
  truncateBareUrl,
33905
33733
  validateComputed,
33906
- validateInfra
33734
+ validateInfra,
33735
+ validateLabelCharacters
33907
33736
  };
33908
33737
  //# sourceMappingURL=index.js.map