@diagrammo/dgmo 0.2.6 → 0.2.8

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
@@ -147,6 +147,7 @@ var init_participant_inference = __esm({
147
147
  var parser_exports = {};
148
148
  __export(parser_exports, {
149
149
  isSequenceBlock: () => isSequenceBlock,
150
+ isSequenceNote: () => isSequenceNote,
150
151
  isSequenceSection: () => isSequenceSection,
151
152
  looksLikeSequence: () => looksLikeSequence,
152
153
  parseSequenceDgmo: () => parseSequenceDgmo
@@ -157,6 +158,9 @@ function isSequenceBlock(el) {
157
158
  function isSequenceSection(el) {
158
159
  return "kind" in el && el.kind === "section";
159
160
  }
161
+ function isSequenceNote(el) {
162
+ return "kind" in el && el.kind === "note";
163
+ }
160
164
  function parseReturnLabel(rawLabel) {
161
165
  if (!rawLabel) return { label: "" };
162
166
  const arrowReturn = rawLabel.match(ARROW_RETURN_PATTERN);
@@ -167,19 +171,22 @@ function parseReturnLabel(rawLabel) {
167
171
  if (umlReturn) {
168
172
  return { label: umlReturn[1].trim(), returnLabel: umlReturn[2].trim() };
169
173
  }
170
- const lastSep = rawLabel.lastIndexOf(" : ");
171
- if (lastSep > 0) {
172
- const reqPart = rawLabel.substring(0, lastSep).trim();
173
- const resPart = rawLabel.substring(lastSep + 3).trim();
174
- if (reqPart && resPart) {
175
- return { label: reqPart, returnLabel: resPart };
174
+ const lastColon = rawLabel.lastIndexOf(":");
175
+ if (lastColon > 0 && lastColon < rawLabel.length - 1) {
176
+ const afterColon = rawLabel.substring(lastColon + 1);
177
+ if (!afterColon.startsWith("//")) {
178
+ const reqPart = rawLabel.substring(0, lastColon).trim();
179
+ const resPart = afterColon.trim();
180
+ if (reqPart && resPart) {
181
+ return { label: reqPart, returnLabel: resPart };
182
+ }
176
183
  }
177
184
  }
178
185
  return { label: rawLabel };
179
186
  }
180
- function measureIndent(line2) {
187
+ function measureIndent(line3) {
181
188
  let indent = 0;
182
- for (const ch of line2) {
189
+ for (const ch of line3) {
183
190
  if (ch === " ") indent++;
184
191
  else if (ch === " ") indent += 4;
185
192
  else break;
@@ -203,13 +210,17 @@ function parseSequenceDgmo(content) {
203
210
  }
204
211
  const lines = content.split("\n");
205
212
  let hasExplicitChart = false;
213
+ let contentStarted = false;
206
214
  let activeGroup = null;
215
+ const participantGroupMap = /* @__PURE__ */ new Map();
207
216
  const blockStack = [];
208
217
  const currentContainer = () => {
209
218
  if (blockStack.length === 0) return result.elements;
210
219
  const top = blockStack[blockStack.length - 1];
220
+ if (top.activeElseIfBranch) return top.activeElseIfBranch.children;
211
221
  return top.inElse ? top.block.elseChildren : top.block.children;
212
222
  };
223
+ let lastMsgFrom = null;
213
224
  for (let i = 0; i < lines.length; i++) {
214
225
  const raw = lines[i];
215
226
  const trimmed = raw.trim();
@@ -220,9 +231,15 @@ function parseSequenceDgmo(content) {
220
231
  }
221
232
  const groupMatch = trimmed.match(GROUP_HEADING_PATTERN);
222
233
  if (groupMatch) {
234
+ const groupColor = groupMatch[2]?.trim();
235
+ if (groupColor && groupColor.startsWith("#")) {
236
+ result.error = `Line ${lineNumber}: Use a named color instead of hex (e.g., blue, red, teal)`;
237
+ return result;
238
+ }
239
+ contentStarted = true;
223
240
  activeGroup = {
224
- name: groupMatch[1],
225
- color: groupMatch[2] || void 0,
241
+ name: groupMatch[1].trim(),
242
+ color: groupColor || void 0,
226
243
  participantIds: [],
227
244
  lineNumber
228
245
  };
@@ -232,7 +249,11 @@ function parseSequenceDgmo(content) {
232
249
  if (activeGroup && measureIndent(raw) === 0) {
233
250
  activeGroup = null;
234
251
  }
235
- if (trimmed.startsWith("#") || trimmed.startsWith("//")) continue;
252
+ if (trimmed.startsWith("//")) continue;
253
+ if (trimmed.startsWith("#") && !trimmed.startsWith("##")) {
254
+ result.error = `Line ${lineNumber}: Use // for comments. # is reserved for group headings (##)`;
255
+ return result;
256
+ }
236
257
  const sectionMatch = trimmed.match(SECTION_PATTERN);
237
258
  if (sectionMatch) {
238
259
  const sectionIndent = measureIndent(raw);
@@ -242,11 +263,16 @@ function parseSequenceDgmo(content) {
242
263
  blockStack.pop();
243
264
  }
244
265
  const labelRaw = sectionMatch[1].trim();
245
- const colorMatch = labelRaw.match(/^(.+?)\((\w+)\)$/);
266
+ const colorMatch = labelRaw.match(/^(.+?)\(([^)]+)\)$/);
267
+ if (colorMatch && colorMatch[2].trim().startsWith("#")) {
268
+ result.error = `Line ${lineNumber}: Use a named color instead of hex (e.g., blue, red, teal)`;
269
+ return result;
270
+ }
271
+ contentStarted = true;
246
272
  const section = {
247
273
  kind: "section",
248
274
  label: colorMatch ? colorMatch[1].trim() : labelRaw,
249
- color: colorMatch ? colorMatch[2] : void 0,
275
+ color: colorMatch ? colorMatch[2].trim() : void 0,
250
276
  lineNumber
251
277
  };
252
278
  result.sections.push(section);
@@ -256,24 +282,32 @@ function parseSequenceDgmo(content) {
256
282
  const colonIndex = trimmed.indexOf(":");
257
283
  if (colonIndex > 0 && !trimmed.includes("->") && !trimmed.includes("~>")) {
258
284
  const key = trimmed.substring(0, colonIndex).trim().toLowerCase();
259
- const value = trimmed.substring(colonIndex + 1).trim();
260
- if (key === "chart") {
261
- hasExplicitChart = true;
262
- if (value.toLowerCase() !== "sequence") {
263
- result.error = `Expected chart type "sequence", got "${value}"`;
285
+ if (key === "note" || key.startsWith("note ")) {
286
+ } else {
287
+ const value = trimmed.substring(colonIndex + 1).trim();
288
+ if (key === "chart") {
289
+ hasExplicitChart = true;
290
+ if (value.toLowerCase() !== "sequence") {
291
+ result.error = `Expected chart type "sequence", got "${value}"`;
292
+ return result;
293
+ }
294
+ continue;
295
+ }
296
+ if (contentStarted) {
297
+ result.error = `Line ${lineNumber}: Options like '${key}: ${value}' must appear before the first message or declaration`;
264
298
  return result;
265
299
  }
300
+ if (key === "title") {
301
+ result.title = value;
302
+ continue;
303
+ }
304
+ result.options[key] = value;
266
305
  continue;
267
306
  }
268
- if (key === "title") {
269
- result.title = value;
270
- continue;
271
- }
272
- result.options[key] = value;
273
- continue;
274
307
  }
275
308
  const isAMatch = trimmed.match(IS_A_PATTERN);
276
309
  if (isAMatch) {
310
+ contentStarted = true;
277
311
  const id = isAMatch[1];
278
312
  const typeStr = isAMatch[2].toLowerCase();
279
313
  const remainder = isAMatch[3]?.trim() || "";
@@ -296,12 +330,19 @@ function parseSequenceDgmo(content) {
296
330
  });
297
331
  }
298
332
  if (activeGroup && !activeGroup.participantIds.includes(id)) {
333
+ const existingGroup = participantGroupMap.get(id);
334
+ if (existingGroup) {
335
+ result.error = `Line ${lineNumber}: Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`;
336
+ return result;
337
+ }
299
338
  activeGroup.participantIds.push(id);
339
+ participantGroupMap.set(id, activeGroup.name);
300
340
  }
301
341
  continue;
302
342
  }
303
343
  const posOnlyMatch = trimmed.match(POSITION_ONLY_PATTERN);
304
344
  if (posOnlyMatch) {
345
+ contentStarted = true;
305
346
  const id = posOnlyMatch[1];
306
347
  const position = parseInt(posOnlyMatch[2], 10);
307
348
  if (!result.participants.some((p) => p.id === id)) {
@@ -314,11 +355,18 @@ function parseSequenceDgmo(content) {
314
355
  });
315
356
  }
316
357
  if (activeGroup && !activeGroup.participantIds.includes(id)) {
358
+ const existingGroup = participantGroupMap.get(id);
359
+ if (existingGroup) {
360
+ result.error = `Line ${lineNumber}: Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`;
361
+ return result;
362
+ }
317
363
  activeGroup.participantIds.push(id);
364
+ participantGroupMap.set(id, activeGroup.name);
318
365
  }
319
366
  continue;
320
367
  }
321
368
  if (activeGroup && measureIndent(raw) > 0 && /^\S+$/.test(trimmed)) {
369
+ contentStarted = true;
322
370
  const id = trimmed;
323
371
  if (!result.participants.some((p) => p.id === id)) {
324
372
  result.participants.push({
@@ -329,7 +377,13 @@ function parseSequenceDgmo(content) {
329
377
  });
330
378
  }
331
379
  if (!activeGroup.participantIds.includes(id)) {
380
+ const existingGroup = participantGroupMap.get(id);
381
+ if (existingGroup) {
382
+ result.error = `Line ${lineNumber}: Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`;
383
+ return result;
384
+ }
332
385
  activeGroup.participantIds.push(id);
386
+ participantGroupMap.set(id, activeGroup.name);
333
387
  }
334
388
  continue;
335
389
  }
@@ -337,28 +391,31 @@ function parseSequenceDgmo(content) {
337
391
  while (blockStack.length > 0) {
338
392
  const top = blockStack[blockStack.length - 1];
339
393
  if (indent > top.indent) break;
340
- if (indent === top.indent && trimmed.toLowerCase() === "else" && top.block.type === "if")
341
- break;
394
+ if (indent === top.indent && (top.block.type === "if" || top.block.type === "parallel")) {
395
+ const lower = trimmed.toLowerCase();
396
+ if (lower === "else" || lower.startsWith("else if ")) break;
397
+ }
342
398
  blockStack.pop();
343
399
  }
344
- let isAsync = false;
345
- let arrowLine = trimmed;
346
400
  const asyncPrefixMatch = trimmed.match(/^async\s+(.+)$/i);
347
- if (asyncPrefixMatch) {
348
- isAsync = true;
349
- arrowLine = asyncPrefixMatch[1];
401
+ if (asyncPrefixMatch && ARROW_PATTERN.test(asyncPrefixMatch[1])) {
402
+ result.error = `Line ${lineNumber}: Use ~> for async messages: A ~> B: message`;
403
+ return result;
350
404
  }
351
- const asyncArrowMatch = arrowLine.match(
405
+ let isAsync = false;
406
+ const asyncArrowMatch = trimmed.match(
352
407
  /^(\S+)\s*~>\s*([^\s:]+)\s*(?::\s*(.+))?$/
353
408
  );
354
- const syncArrowMatch = arrowLine.match(
409
+ const syncArrowMatch = trimmed.match(
355
410
  /^(\S+)\s*->\s*([^\s:]+)\s*(?::\s*(.+))?$/
356
411
  );
357
412
  const arrowMatch = asyncArrowMatch || syncArrowMatch;
358
413
  if (asyncArrowMatch) isAsync = true;
359
414
  if (arrowMatch) {
415
+ contentStarted = true;
360
416
  const from = arrowMatch[1];
361
417
  const to = arrowMatch[2];
418
+ lastMsgFrom = from;
362
419
  const rawLabel = arrowMatch[3]?.trim() || "";
363
420
  const { label, returnLabel } = isAsync ? { label: rawLabel, returnLabel: void 0 } : parseReturnLabel(rawLabel);
364
421
  const msg = {
@@ -391,6 +448,7 @@ function parseSequenceDgmo(content) {
391
448
  }
392
449
  const ifMatch = trimmed.match(/^if\s+(.+)$/i);
393
450
  if (ifMatch) {
451
+ contentStarted = true;
394
452
  const block = {
395
453
  kind: "block",
396
454
  type: "if",
@@ -405,6 +463,7 @@ function parseSequenceDgmo(content) {
405
463
  }
406
464
  const loopMatch = trimmed.match(/^loop\s+(.+)$/i);
407
465
  if (loopMatch) {
466
+ contentStarted = true;
408
467
  const block = {
409
468
  kind: "block",
410
469
  type: "loop",
@@ -419,6 +478,7 @@ function parseSequenceDgmo(content) {
419
478
  }
420
479
  const parallelMatch = trimmed.match(/^parallel(?:\s+(.+))?$/i);
421
480
  if (parallelMatch) {
481
+ contentStarted = true;
422
482
  const block = {
423
483
  kind: "block",
424
484
  type: "parallel",
@@ -431,15 +491,97 @@ function parseSequenceDgmo(content) {
431
491
  blockStack.push({ block, indent, inElse: false });
432
492
  continue;
433
493
  }
494
+ const elseIfMatch = trimmed.match(/^else\s+if\s+(.+)$/i);
495
+ if (elseIfMatch) {
496
+ if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent) {
497
+ const top = blockStack[blockStack.length - 1];
498
+ if (top.block.type === "parallel") {
499
+ result.error = `Line ${lineNumber}: parallel blocks don't support else if \u2014 list all concurrent messages directly inside the block`;
500
+ return result;
501
+ }
502
+ if (top.block.type === "if") {
503
+ const branch = { label: elseIfMatch[1].trim(), children: [] };
504
+ if (!top.block.elseIfBranches) top.block.elseIfBranches = [];
505
+ top.block.elseIfBranches.push(branch);
506
+ top.activeElseIfBranch = branch;
507
+ top.inElse = false;
508
+ }
509
+ }
510
+ continue;
511
+ }
434
512
  if (trimmed.toLowerCase() === "else") {
435
- if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent && blockStack[blockStack.length - 1].block.type === "if") {
436
- blockStack[blockStack.length - 1].inElse = true;
513
+ if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent) {
514
+ const top = blockStack[blockStack.length - 1];
515
+ if (top.block.type === "parallel") {
516
+ result.error = `Line ${lineNumber}: parallel blocks don't support else \u2014 list all concurrent messages directly inside the block`;
517
+ return result;
518
+ }
519
+ if (top.block.type === "if") {
520
+ top.inElse = true;
521
+ top.activeElseIfBranch = void 0;
522
+ }
437
523
  }
438
524
  continue;
439
525
  }
526
+ const noteSingleMatch = trimmed.match(NOTE_SINGLE);
527
+ if (noteSingleMatch) {
528
+ const notePosition = noteSingleMatch[1]?.toLowerCase() || "right";
529
+ let noteParticipant = noteSingleMatch[2] || null;
530
+ if (!noteParticipant) {
531
+ if (!lastMsgFrom) continue;
532
+ noteParticipant = lastMsgFrom;
533
+ }
534
+ if (!result.participants.some((p) => p.id === noteParticipant)) {
535
+ continue;
536
+ }
537
+ const note = {
538
+ kind: "note",
539
+ text: noteSingleMatch[3].trim(),
540
+ position: notePosition,
541
+ participantId: noteParticipant,
542
+ lineNumber,
543
+ endLineNumber: lineNumber
544
+ };
545
+ currentContainer().push(note);
546
+ continue;
547
+ }
548
+ const noteMultiMatch = trimmed.match(NOTE_MULTI);
549
+ if (noteMultiMatch) {
550
+ const notePosition = noteMultiMatch[1]?.toLowerCase() || "right";
551
+ let noteParticipant = noteMultiMatch[2] || null;
552
+ if (!noteParticipant) {
553
+ if (!lastMsgFrom) continue;
554
+ noteParticipant = lastMsgFrom;
555
+ }
556
+ if (!result.participants.some((p) => p.id === noteParticipant)) {
557
+ continue;
558
+ }
559
+ const noteLines = [];
560
+ while (i + 1 < lines.length) {
561
+ const nextRaw = lines[i + 1];
562
+ const nextTrimmed = nextRaw.trim();
563
+ if (!nextTrimmed) break;
564
+ const nextIndent = measureIndent(nextRaw);
565
+ if (nextIndent <= indent) break;
566
+ noteLines.push(nextTrimmed);
567
+ i++;
568
+ }
569
+ if (noteLines.length === 0) continue;
570
+ const note = {
571
+ kind: "note",
572
+ text: noteLines.join("\n"),
573
+ position: notePosition,
574
+ participantId: noteParticipant,
575
+ lineNumber,
576
+ endLineNumber: i + 1
577
+ // i has advanced past the body lines (1-based)
578
+ };
579
+ currentContainer().push(note);
580
+ continue;
581
+ }
440
582
  }
441
583
  if (!hasExplicitChart && result.messages.length === 0) {
442
- const hasArrows = lines.some((line2) => ARROW_PATTERN.test(line2.trim()));
584
+ const hasArrows = lines.some((line3) => ARROW_PATTERN.test(line3.trim()));
443
585
  if (!hasArrows) {
444
586
  result.error = 'No "chart: sequence" header and no sequence content detected';
445
587
  return result;
@@ -450,13 +592,13 @@ function parseSequenceDgmo(content) {
450
592
  function looksLikeSequence(content) {
451
593
  if (!content) return false;
452
594
  const lines = content.split("\n");
453
- return lines.some((line2) => {
454
- const trimmed = line2.trim();
455
- if (trimmed.startsWith("#") || trimmed.startsWith("//")) return false;
595
+ return lines.some((line3) => {
596
+ const trimmed = line3.trim();
597
+ if (trimmed.startsWith("//")) return false;
456
598
  return ARROW_PATTERN.test(trimmed);
457
599
  });
458
600
  }
459
- var VALID_PARTICIPANT_TYPES, IS_A_PATTERN, POSITION_ONLY_PATTERN, GROUP_HEADING_PATTERN, SECTION_PATTERN, ARROW_PATTERN, ARROW_RETURN_PATTERN, UML_RETURN_PATTERN;
601
+ var VALID_PARTICIPANT_TYPES, IS_A_PATTERN, POSITION_ONLY_PATTERN, GROUP_HEADING_PATTERN, SECTION_PATTERN, ARROW_PATTERN, ARROW_RETURN_PATTERN, UML_RETURN_PATTERN, NOTE_SINGLE, NOTE_MULTI;
460
602
  var init_parser = __esm({
461
603
  "src/sequence/parser.ts"() {
462
604
  "use strict";
@@ -474,11 +616,13 @@ var init_parser = __esm({
474
616
  ]);
475
617
  IS_A_PATTERN = /^(\S+)\s+is\s+an?\s+(\w+)(?:\s+(.+))?$/i;
476
618
  POSITION_ONLY_PATTERN = /^(\S+)\s+position\s+(-?\d+)$/i;
477
- GROUP_HEADING_PATTERN = /^##\s+(\S+?)(?:\((\w+)\))?$/;
478
- SECTION_PATTERN = /^==\s+(.+?)\s*==$/;
619
+ GROUP_HEADING_PATTERN = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
620
+ SECTION_PATTERN = /^==\s+(.+?)(?:\s*==)?\s*$/;
479
621
  ARROW_PATTERN = /\S+\s*(?:->|~>)\s*\S+/;
480
622
  ARROW_RETURN_PATTERN = /^(.+?)\s*<-\s*(.+)$/;
481
623
  UML_RETURN_PATTERN = /^(\w+\([^)]*\))\s*:\s*(.+)$/;
624
+ NOTE_SINGLE = /^note(?:\s+(right|left)\s+of\s+(\S+))?\s*:\s*(.+)$/i;
625
+ NOTE_MULTI = /^note(?:\s+(right|left)\s+of\s+([^\s:]+))?\s*:?\s*$/i;
482
626
  }
483
627
  });
484
628
 
@@ -557,6 +701,369 @@ var init_colors = __esm({
557
701
  }
558
702
  });
559
703
 
704
+ // src/graph/flowchart-parser.ts
705
+ var flowchart_parser_exports = {};
706
+ __export(flowchart_parser_exports, {
707
+ looksLikeFlowchart: () => looksLikeFlowchart,
708
+ parseFlowchart: () => parseFlowchart
709
+ });
710
+ function measureIndent2(line3) {
711
+ let indent = 0;
712
+ for (const ch of line3) {
713
+ if (ch === " ") indent++;
714
+ else if (ch === " ") indent += 4;
715
+ else break;
716
+ }
717
+ return indent;
718
+ }
719
+ function nodeId(shape, label) {
720
+ return `${shape}:${label.toLowerCase().trim()}`;
721
+ }
722
+ function extractColor(label, palette) {
723
+ const m = label.match(COLOR_SUFFIX_RE);
724
+ if (!m) return { label };
725
+ const colorName = m[1].trim();
726
+ return {
727
+ label: label.substring(0, m.index).trim(),
728
+ color: resolveColor(colorName, palette)
729
+ };
730
+ }
731
+ function parseNodeRef(text, palette) {
732
+ const t = text.trim();
733
+ if (!t) return null;
734
+ let m = t.match(/^\[\[([^\]]+)\]\]$/);
735
+ if (m) {
736
+ const { label, color } = extractColor(m[1].trim(), palette);
737
+ return { id: nodeId("subroutine", label), label, shape: "subroutine", color };
738
+ }
739
+ m = t.match(/^\[([^\]]+)~\]$/);
740
+ if (m) {
741
+ const { label, color } = extractColor(m[1].trim(), palette);
742
+ return { id: nodeId("document", label), label, shape: "document", color };
743
+ }
744
+ m = t.match(/^\[([^\]]+)\]$/);
745
+ if (m) {
746
+ const { label, color } = extractColor(m[1].trim(), palette);
747
+ return { id: nodeId("process", label), label, shape: "process", color };
748
+ }
749
+ m = t.match(/^\((.+)\)$/);
750
+ if (m) {
751
+ const { label, color } = extractColor(m[1].trim(), palette);
752
+ return { id: nodeId("terminal", label), label, shape: "terminal", color };
753
+ }
754
+ m = t.match(/^<([^>]+)>$/);
755
+ if (m) {
756
+ const { label, color } = extractColor(m[1].trim(), palette);
757
+ return { id: nodeId("decision", label), label, shape: "decision", color };
758
+ }
759
+ m = t.match(/^\/([^/]+)\/$/);
760
+ if (m) {
761
+ const { label, color } = extractColor(m[1].trim(), palette);
762
+ return { id: nodeId("io", label), label, shape: "io", color };
763
+ }
764
+ return null;
765
+ }
766
+ function splitArrows(line3) {
767
+ const segments = [];
768
+ const arrowRe = /(?:^|\s)-([^>\s(][^(>]*?)?\s*(?:\(([^)]+)\))?\s*->|(?:^|\s)->/g;
769
+ let lastIndex = 0;
770
+ const arrowPositions = [];
771
+ let searchFrom = 0;
772
+ while (searchFrom < line3.length) {
773
+ const idx = line3.indexOf("->", searchFrom);
774
+ if (idx === -1) break;
775
+ let arrowStart = idx;
776
+ let label;
777
+ let color;
778
+ if (idx > 0 && line3[idx - 1] !== " " && line3[idx - 1] !== " ") {
779
+ let scanBack = idx - 1;
780
+ while (scanBack > 0 && line3[scanBack] !== "-") {
781
+ scanBack--;
782
+ }
783
+ if (line3[scanBack] === "-" && (scanBack === 0 || /\s/.test(line3[scanBack - 1]))) {
784
+ let arrowContent = line3.substring(scanBack + 1, idx);
785
+ if (arrowContent.endsWith("-")) arrowContent = arrowContent.slice(0, -1);
786
+ const colorMatch = arrowContent.match(/\(([^)]+)\)\s*$/);
787
+ if (colorMatch) {
788
+ color = colorMatch[1].trim();
789
+ const labelPart = arrowContent.substring(0, colorMatch.index).trim();
790
+ if (labelPart) label = labelPart;
791
+ } else {
792
+ const labelPart = arrowContent.trim();
793
+ if (labelPart) label = labelPart;
794
+ }
795
+ arrowStart = scanBack;
796
+ }
797
+ }
798
+ arrowPositions.push({ start: arrowStart, end: idx + 2, label, color });
799
+ searchFrom = idx + 2;
800
+ }
801
+ if (arrowPositions.length === 0) {
802
+ return [line3];
803
+ }
804
+ for (let i = 0; i < arrowPositions.length; i++) {
805
+ const arrow = arrowPositions[i];
806
+ const beforeText = line3.substring(lastIndex, arrow.start).trim();
807
+ if (beforeText || i === 0) {
808
+ segments.push(beforeText);
809
+ }
810
+ let arrowToken = "->";
811
+ if (arrow.label && arrow.color) arrowToken = `-${arrow.label}(${arrow.color})->`;
812
+ else if (arrow.label) arrowToken = `-${arrow.label}->`;
813
+ else if (arrow.color) arrowToken = `-(${arrow.color})->`;
814
+ segments.push(arrowToken);
815
+ lastIndex = arrow.end;
816
+ }
817
+ const remaining = line3.substring(lastIndex).trim();
818
+ if (remaining) {
819
+ segments.push(remaining);
820
+ }
821
+ return segments;
822
+ }
823
+ function parseArrowToken(token, palette) {
824
+ if (token === "->") return {};
825
+ const colorOnly = token.match(/^-\(([^)]+)\)->$/);
826
+ if (colorOnly) {
827
+ return { color: resolveColor(colorOnly[1].trim(), palette) };
828
+ }
829
+ const m = token.match(/^-(.+?)(?:\(([^)]+)\))?->$/);
830
+ if (m) {
831
+ const label = m[1]?.trim() || void 0;
832
+ const color = m[2] ? resolveColor(m[2].trim(), palette) : void 0;
833
+ return { label, color };
834
+ }
835
+ return {};
836
+ }
837
+ function parseFlowchart(content, palette) {
838
+ const lines = content.split("\n");
839
+ const result = {
840
+ type: "flowchart",
841
+ direction: "TB",
842
+ nodes: [],
843
+ edges: []
844
+ };
845
+ const nodeMap = /* @__PURE__ */ new Map();
846
+ const indentStack = [];
847
+ let currentGroup = null;
848
+ const groups = [];
849
+ let contentStarted = false;
850
+ function getOrCreateNode(ref, lineNumber) {
851
+ const existing = nodeMap.get(ref.id);
852
+ if (existing) return existing;
853
+ const node = {
854
+ id: ref.id,
855
+ label: ref.label,
856
+ shape: ref.shape,
857
+ lineNumber,
858
+ ...ref.color && { color: ref.color },
859
+ ...currentGroup && { group: currentGroup.id }
860
+ };
861
+ nodeMap.set(ref.id, node);
862
+ result.nodes.push(node);
863
+ if (currentGroup && !currentGroup.nodeIds.includes(ref.id)) {
864
+ currentGroup.nodeIds.push(ref.id);
865
+ }
866
+ return node;
867
+ }
868
+ function addEdge(sourceId, targetId, lineNumber, label, color) {
869
+ const edge = {
870
+ source: sourceId,
871
+ target: targetId,
872
+ lineNumber,
873
+ ...label && { label },
874
+ ...color && { color }
875
+ };
876
+ result.edges.push(edge);
877
+ }
878
+ function processContentLine(trimmed, lineNumber, indent) {
879
+ contentStarted = true;
880
+ while (indentStack.length > 0) {
881
+ const top = indentStack[indentStack.length - 1];
882
+ if (top.indent >= indent) {
883
+ indentStack.pop();
884
+ } else {
885
+ break;
886
+ }
887
+ }
888
+ const implicitSourceId = indentStack.length > 0 ? indentStack[indentStack.length - 1].nodeId : null;
889
+ const segments = splitArrows(trimmed);
890
+ if (segments.length === 1) {
891
+ const ref = parseNodeRef(segments[0], palette);
892
+ if (ref) {
893
+ const node = getOrCreateNode(ref, lineNumber);
894
+ indentStack.push({ nodeId: node.id, indent });
895
+ return node.id;
896
+ }
897
+ return null;
898
+ }
899
+ let lastNodeId = null;
900
+ let pendingArrow = null;
901
+ for (let i = 0; i < segments.length; i++) {
902
+ const seg = segments[i];
903
+ if (seg === "->" || /^-.+->$/.test(seg)) {
904
+ pendingArrow = parseArrowToken(seg, palette);
905
+ continue;
906
+ }
907
+ const ref = parseNodeRef(seg, palette);
908
+ if (!ref) continue;
909
+ const node = getOrCreateNode(ref, lineNumber);
910
+ if (pendingArrow !== null) {
911
+ const sourceId = lastNodeId ?? implicitSourceId;
912
+ if (sourceId) {
913
+ addEdge(
914
+ sourceId,
915
+ node.id,
916
+ lineNumber,
917
+ pendingArrow.label,
918
+ pendingArrow.color
919
+ );
920
+ }
921
+ pendingArrow = null;
922
+ } else if (lastNodeId === null && implicitSourceId === null) {
923
+ }
924
+ lastNodeId = node.id;
925
+ }
926
+ if (pendingArrow !== null && lastNodeId === null && implicitSourceId) {
927
+ }
928
+ if (segments.length >= 2 && segments[0] === "" && implicitSourceId && lastNodeId) {
929
+ }
930
+ if (lastNodeId) {
931
+ indentStack.push({ nodeId: lastNodeId, indent });
932
+ }
933
+ return lastNodeId;
934
+ }
935
+ for (let i = 0; i < lines.length; i++) {
936
+ const raw = lines[i];
937
+ const trimmed = raw.trim();
938
+ const lineNumber = i + 1;
939
+ const indent = measureIndent2(raw);
940
+ if (!trimmed) continue;
941
+ if (trimmed.startsWith("//")) continue;
942
+ const groupMatch = trimmed.match(GROUP_HEADING_RE);
943
+ if (groupMatch) {
944
+ const groupLabel = groupMatch[1].trim();
945
+ const groupColorName = groupMatch[2]?.trim();
946
+ const groupColor = groupColorName ? resolveColor(groupColorName, palette) : void 0;
947
+ currentGroup = {
948
+ id: `group:${groupLabel.toLowerCase()}`,
949
+ label: groupLabel,
950
+ nodeIds: [],
951
+ lineNumber,
952
+ ...groupColor && { color: groupColor }
953
+ };
954
+ groups.push(currentGroup);
955
+ continue;
956
+ }
957
+ if (!contentStarted && trimmed.includes(":") && !trimmed.includes("->")) {
958
+ const colonIdx = trimmed.indexOf(":");
959
+ const key = trimmed.substring(0, colonIdx).trim().toLowerCase();
960
+ const value = trimmed.substring(colonIdx + 1).trim();
961
+ if (key === "chart") {
962
+ if (value.toLowerCase() !== "flowchart") {
963
+ result.error = `Line ${lineNumber}: Expected chart type "flowchart", got "${value}"`;
964
+ return result;
965
+ }
966
+ continue;
967
+ }
968
+ if (key === "title") {
969
+ result.title = value;
970
+ continue;
971
+ }
972
+ if (key === "direction") {
973
+ const dir = value.toUpperCase();
974
+ if (dir === "TB" || dir === "LR") {
975
+ result.direction = dir;
976
+ }
977
+ continue;
978
+ }
979
+ continue;
980
+ }
981
+ processContentLine(trimmed, lineNumber, indent);
982
+ }
983
+ if (groups.length > 0) result.groups = groups;
984
+ if (result.nodes.length === 0 && !result.error) {
985
+ result.error = "No nodes found. Add flowchart content with shape syntax like [Process] or (Start).";
986
+ }
987
+ return result;
988
+ }
989
+ function looksLikeFlowchart(content) {
990
+ if (!content.includes("->")) return false;
991
+ const hasShapeDelimiter = /\[[^\]]+\]/.test(content) || /\([^)]+\)/.test(content) || /<[^>]+>/.test(content) || /\/[^/]+\//.test(content);
992
+ if (!hasShapeDelimiter) return false;
993
+ const shapeNearArrow = /[\])][ \t]*-.*->/.test(content) || // shape ] or ) followed by arrow
994
+ /->[ \t]*[\[(<\/]/.test(content);
995
+ return shapeNearArrow;
996
+ }
997
+ var COLOR_SUFFIX_RE, GROUP_HEADING_RE;
998
+ var init_flowchart_parser = __esm({
999
+ "src/graph/flowchart-parser.ts"() {
1000
+ "use strict";
1001
+ init_colors();
1002
+ COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
1003
+ GROUP_HEADING_RE = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
1004
+ }
1005
+ });
1006
+
1007
+ // src/dgmo-router.ts
1008
+ var dgmo_router_exports = {};
1009
+ __export(dgmo_router_exports, {
1010
+ DGMO_CHART_TYPE_MAP: () => DGMO_CHART_TYPE_MAP,
1011
+ getDgmoFramework: () => getDgmoFramework,
1012
+ parseDgmoChartType: () => parseDgmoChartType
1013
+ });
1014
+ function getDgmoFramework(chartType) {
1015
+ return DGMO_CHART_TYPE_MAP[chartType.toLowerCase()] ?? null;
1016
+ }
1017
+ function parseDgmoChartType(content) {
1018
+ const lines = content.split("\n");
1019
+ for (const line3 of lines) {
1020
+ const trimmed = line3.trim();
1021
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//"))
1022
+ continue;
1023
+ const match = trimmed.match(/^chart\s*:\s*(.+)/i);
1024
+ if (match) return match[1].trim().toLowerCase();
1025
+ }
1026
+ if (looksLikeSequence(content)) return "sequence";
1027
+ if (looksLikeFlowchart(content)) return "flowchart";
1028
+ return null;
1029
+ }
1030
+ var DGMO_CHART_TYPE_MAP;
1031
+ var init_dgmo_router = __esm({
1032
+ "src/dgmo-router.ts"() {
1033
+ "use strict";
1034
+ init_parser();
1035
+ init_flowchart_parser();
1036
+ DGMO_CHART_TYPE_MAP = {
1037
+ // Standard charts (via ECharts)
1038
+ bar: "echart",
1039
+ line: "echart",
1040
+ "multi-line": "echart",
1041
+ area: "echart",
1042
+ pie: "echart",
1043
+ doughnut: "echart",
1044
+ radar: "echart",
1045
+ "polar-area": "echart",
1046
+ "bar-stacked": "echart",
1047
+ // ECharts
1048
+ scatter: "echart",
1049
+ sankey: "echart",
1050
+ chord: "echart",
1051
+ function: "echart",
1052
+ heatmap: "echart",
1053
+ funnel: "echart",
1054
+ // D3
1055
+ slope: "d3",
1056
+ wordcloud: "d3",
1057
+ arc: "d3",
1058
+ timeline: "d3",
1059
+ venn: "d3",
1060
+ quadrant: "d3",
1061
+ sequence: "d3",
1062
+ flowchart: "d3"
1063
+ };
1064
+ }
1065
+ });
1066
+
560
1067
  // src/fonts.ts
561
1068
  var FONT_FAMILY;
562
1069
  var init_fonts = __esm({
@@ -1612,6 +2119,409 @@ var init_palettes = __esm({
1612
2119
  }
1613
2120
  });
1614
2121
 
2122
+ // src/graph/layout.ts
2123
+ var layout_exports = {};
2124
+ __export(layout_exports, {
2125
+ layoutGraph: () => layoutGraph
2126
+ });
2127
+ import dagre from "@dagrejs/dagre";
2128
+ function computeNodeWidth(label, shape) {
2129
+ const base = Math.max(120, label.length * 9 + 40);
2130
+ if (shape === "subroutine") return base + 10;
2131
+ return base;
2132
+ }
2133
+ function computeNodeHeight(shape) {
2134
+ return shape === "decision" ? 60 : 50;
2135
+ }
2136
+ function layoutGraph(graph) {
2137
+ if (graph.nodes.length === 0) {
2138
+ return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
2139
+ }
2140
+ const g = new dagre.graphlib.Graph({ compound: true });
2141
+ g.setGraph({
2142
+ rankdir: graph.direction,
2143
+ nodesep: 50,
2144
+ ranksep: 60,
2145
+ edgesep: 20
2146
+ });
2147
+ g.setDefaultEdgeLabel(() => ({}));
2148
+ const nodeDataMap = /* @__PURE__ */ new Map();
2149
+ for (const node of graph.nodes) {
2150
+ nodeDataMap.set(node.id, node);
2151
+ }
2152
+ if (graph.groups) {
2153
+ for (const group of graph.groups) {
2154
+ g.setNode(group.id, {
2155
+ label: group.label,
2156
+ clusterLabelPos: "top"
2157
+ });
2158
+ }
2159
+ }
2160
+ for (const node of graph.nodes) {
2161
+ const width = computeNodeWidth(node.label, node.shape);
2162
+ const height = computeNodeHeight(node.shape);
2163
+ g.setNode(node.id, { label: node.label, width, height });
2164
+ if (node.group && graph.groups?.some((gr) => gr.id === node.group)) {
2165
+ g.setParent(node.id, node.group);
2166
+ }
2167
+ }
2168
+ const edgeDataMap = /* @__PURE__ */ new Map();
2169
+ for (const edge of graph.edges) {
2170
+ const key = `${edge.source}->${edge.target}`;
2171
+ edgeDataMap.set(key, edge);
2172
+ g.setEdge(edge.source, edge.target, {
2173
+ label: edge.label ?? ""
2174
+ });
2175
+ }
2176
+ dagre.layout(g);
2177
+ const layoutNodes = graph.nodes.map((node) => {
2178
+ const pos = g.node(node.id);
2179
+ return {
2180
+ id: node.id,
2181
+ label: node.label,
2182
+ shape: node.shape,
2183
+ color: node.color,
2184
+ group: node.group,
2185
+ lineNumber: node.lineNumber,
2186
+ x: pos.x,
2187
+ y: pos.y,
2188
+ width: pos.width,
2189
+ height: pos.height
2190
+ };
2191
+ });
2192
+ const layoutEdges = graph.edges.map((edge) => {
2193
+ const edgeData = g.edge(edge.source, edge.target);
2194
+ return {
2195
+ source: edge.source,
2196
+ target: edge.target,
2197
+ points: edgeData?.points ?? [],
2198
+ label: edge.label,
2199
+ color: edge.color,
2200
+ lineNumber: edge.lineNumber
2201
+ };
2202
+ });
2203
+ const layoutGroups = [];
2204
+ if (graph.groups) {
2205
+ const nodeMap = new Map(layoutNodes.map((n) => [n.id, n]));
2206
+ for (const group of graph.groups) {
2207
+ const members = group.nodeIds.map((id) => nodeMap.get(id)).filter((n) => n !== void 0);
2208
+ if (members.length === 0) {
2209
+ layoutGroups.push({
2210
+ id: group.id,
2211
+ label: group.label,
2212
+ color: group.color,
2213
+ x: 0,
2214
+ y: 0,
2215
+ width: 0,
2216
+ height: 0
2217
+ });
2218
+ continue;
2219
+ }
2220
+ let minX = Infinity;
2221
+ let minY = Infinity;
2222
+ let maxX = -Infinity;
2223
+ let maxY = -Infinity;
2224
+ for (const member of members) {
2225
+ const left = member.x - member.width / 2;
2226
+ const right = member.x + member.width / 2;
2227
+ const top = member.y - member.height / 2;
2228
+ const bottom = member.y + member.height / 2;
2229
+ if (left < minX) minX = left;
2230
+ if (right > maxX) maxX = right;
2231
+ if (top < minY) minY = top;
2232
+ if (bottom > maxY) maxY = bottom;
2233
+ }
2234
+ layoutGroups.push({
2235
+ id: group.id,
2236
+ label: group.label,
2237
+ color: group.color,
2238
+ x: minX - GROUP_PADDING,
2239
+ y: minY - GROUP_PADDING,
2240
+ width: maxX - minX + GROUP_PADDING * 2,
2241
+ height: maxY - minY + GROUP_PADDING * 2
2242
+ });
2243
+ }
2244
+ }
2245
+ let totalWidth = 0;
2246
+ let totalHeight = 0;
2247
+ for (const node of layoutNodes) {
2248
+ const right = node.x + node.width / 2;
2249
+ const bottom = node.y + node.height / 2;
2250
+ if (right > totalWidth) totalWidth = right;
2251
+ if (bottom > totalHeight) totalHeight = bottom;
2252
+ }
2253
+ for (const group of layoutGroups) {
2254
+ const right = group.x + group.width;
2255
+ const bottom = group.y + group.height;
2256
+ if (right > totalWidth) totalWidth = right;
2257
+ if (bottom > totalHeight) totalHeight = bottom;
2258
+ }
2259
+ totalWidth += 40;
2260
+ totalHeight += 40;
2261
+ return {
2262
+ nodes: layoutNodes,
2263
+ edges: layoutEdges,
2264
+ groups: layoutGroups,
2265
+ width: totalWidth,
2266
+ height: totalHeight
2267
+ };
2268
+ }
2269
+ var GROUP_PADDING;
2270
+ var init_layout = __esm({
2271
+ "src/graph/layout.ts"() {
2272
+ "use strict";
2273
+ GROUP_PADDING = 20;
2274
+ }
2275
+ });
2276
+
2277
+ // src/graph/flowchart-renderer.ts
2278
+ var flowchart_renderer_exports = {};
2279
+ __export(flowchart_renderer_exports, {
2280
+ renderFlowchart: () => renderFlowchart,
2281
+ renderFlowchartForExport: () => renderFlowchartForExport
2282
+ });
2283
+ import * as d3Selection from "d3-selection";
2284
+ import * as d3Shape from "d3-shape";
2285
+ function mix(a, b, pct) {
2286
+ const parse = (h) => {
2287
+ const r = h.replace("#", "");
2288
+ const f = r.length === 3 ? r[0] + r[0] + r[1] + r[1] + r[2] + r[2] : r;
2289
+ return [parseInt(f.substring(0, 2), 16), parseInt(f.substring(2, 4), 16), parseInt(f.substring(4, 6), 16)];
2290
+ };
2291
+ const [ar, ag, ab] = parse(a), [br, bg, bb] = parse(b), t = pct / 100;
2292
+ const c = (x, y) => Math.round(x * t + y * (1 - t)).toString(16).padStart(2, "0");
2293
+ return `#${c(ar, br)}${c(ag, bg)}${c(ab, bb)}`;
2294
+ }
2295
+ function nodeFill(palette, isDark, nodeColor) {
2296
+ if (nodeColor) {
2297
+ return mix(nodeColor, isDark ? palette.surface : palette.bg, 25);
2298
+ }
2299
+ return mix(palette.primary, isDark ? palette.surface : palette.bg, 15);
2300
+ }
2301
+ function nodeStroke(palette, nodeColor) {
2302
+ return nodeColor ?? palette.textMuted;
2303
+ }
2304
+ function renderTerminal(g, node, palette, isDark) {
2305
+ const w = node.width;
2306
+ const h = node.height;
2307
+ const rx = h / 2;
2308
+ g.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", rx).attr("ry", rx).attr("fill", nodeFill(palette, isDark, node.color)).attr("stroke", nodeStroke(palette, node.color)).attr("stroke-width", NODE_STROKE_WIDTH);
2309
+ }
2310
+ function renderProcess(g, node, palette, isDark) {
2311
+ const w = node.width;
2312
+ const h = node.height;
2313
+ g.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", 3).attr("ry", 3).attr("fill", nodeFill(palette, isDark, node.color)).attr("stroke", nodeStroke(palette, node.color)).attr("stroke-width", NODE_STROKE_WIDTH);
2314
+ }
2315
+ function renderDecision(g, node, palette, isDark) {
2316
+ const w = node.width / 2;
2317
+ const h = node.height / 2;
2318
+ const points = [
2319
+ `${0},${-h}`,
2320
+ // top
2321
+ `${w},${0}`,
2322
+ // right
2323
+ `${0},${h}`,
2324
+ // bottom
2325
+ `${-w},${0}`
2326
+ // left
2327
+ ].join(" ");
2328
+ g.append("polygon").attr("points", points).attr("fill", nodeFill(palette, isDark, node.color)).attr("stroke", nodeStroke(palette, node.color)).attr("stroke-width", NODE_STROKE_WIDTH);
2329
+ }
2330
+ function renderIO(g, node, palette, isDark) {
2331
+ const w = node.width / 2;
2332
+ const h = node.height / 2;
2333
+ const sk = IO_SKEW;
2334
+ const points = [
2335
+ `${-w + sk},${-h}`,
2336
+ // top-left (shifted right)
2337
+ `${w + sk},${-h}`,
2338
+ // top-right (shifted right)
2339
+ `${w - sk},${h}`,
2340
+ // bottom-right (shifted left)
2341
+ `${-w - sk},${h}`
2342
+ // bottom-left (shifted left)
2343
+ ].join(" ");
2344
+ g.append("polygon").attr("points", points).attr("fill", nodeFill(palette, isDark, node.color)).attr("stroke", nodeStroke(palette, node.color)).attr("stroke-width", NODE_STROKE_WIDTH);
2345
+ }
2346
+ function renderSubroutine(g, node, palette, isDark) {
2347
+ const w = node.width;
2348
+ const h = node.height;
2349
+ const s = nodeStroke(palette, node.color);
2350
+ g.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", 3).attr("ry", 3).attr("fill", nodeFill(palette, isDark, node.color)).attr("stroke", s).attr("stroke-width", NODE_STROKE_WIDTH);
2351
+ g.append("line").attr("x1", -w / 2 + SUBROUTINE_INSET).attr("y1", -h / 2).attr("x2", -w / 2 + SUBROUTINE_INSET).attr("y2", h / 2).attr("stroke", s).attr("stroke-width", NODE_STROKE_WIDTH);
2352
+ g.append("line").attr("x1", w / 2 - SUBROUTINE_INSET).attr("y1", -h / 2).attr("x2", w / 2 - SUBROUTINE_INSET).attr("y2", h / 2).attr("stroke", s).attr("stroke-width", NODE_STROKE_WIDTH);
2353
+ }
2354
+ function renderDocument(g, node, palette, isDark) {
2355
+ const w = node.width;
2356
+ const h = node.height;
2357
+ const waveH = DOC_WAVE_HEIGHT;
2358
+ const left = -w / 2;
2359
+ const right = w / 2;
2360
+ const top = -h / 2;
2361
+ const bottom = h / 2 - waveH;
2362
+ const d = [
2363
+ `M ${left} ${top}`,
2364
+ `L ${right} ${top}`,
2365
+ `L ${right} ${bottom}`,
2366
+ `C ${right - w * 0.25} ${bottom + waveH * 2}, ${left + w * 0.25} ${bottom - waveH}, ${left} ${bottom}`,
2367
+ "Z"
2368
+ ].join(" ");
2369
+ g.append("path").attr("d", d).attr("fill", nodeFill(palette, isDark, node.color)).attr("stroke", nodeStroke(palette, node.color)).attr("stroke-width", NODE_STROKE_WIDTH);
2370
+ }
2371
+ function renderNodeShape(g, node, palette, isDark) {
2372
+ switch (node.shape) {
2373
+ case "terminal":
2374
+ renderTerminal(g, node, palette, isDark);
2375
+ break;
2376
+ case "process":
2377
+ renderProcess(g, node, palette, isDark);
2378
+ break;
2379
+ case "decision":
2380
+ renderDecision(g, node, palette, isDark);
2381
+ break;
2382
+ case "io":
2383
+ renderIO(g, node, palette, isDark);
2384
+ break;
2385
+ case "subroutine":
2386
+ renderSubroutine(g, node, palette, isDark);
2387
+ break;
2388
+ case "document":
2389
+ renderDocument(g, node, palette, isDark);
2390
+ break;
2391
+ }
2392
+ }
2393
+ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem, exportDims) {
2394
+ d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
2395
+ const width = exportDims?.width ?? container.clientWidth;
2396
+ const height = exportDims?.height ?? container.clientHeight;
2397
+ if (width <= 0 || height <= 0) return;
2398
+ const titleOffset = graph.title ? TITLE_HEIGHT : 0;
2399
+ const diagramW = layout.width;
2400
+ const diagramH = layout.height + titleOffset;
2401
+ const scaleX = (width - DIAGRAM_PADDING * 2) / diagramW;
2402
+ const scaleY = (height - DIAGRAM_PADDING * 2) / diagramH;
2403
+ const scale = Math.min(1, scaleX, scaleY);
2404
+ const scaledW = diagramW * scale;
2405
+ const scaledH = diagramH * scale;
2406
+ const offsetX = (width - scaledW) / 2;
2407
+ const offsetY = (height - scaledH) / 2;
2408
+ const svg = d3Selection.select(container).append("svg").attr("width", width).attr("height", height).style("background", palette.bg).style("font-family", FONT_FAMILY);
2409
+ const defs = svg.append("defs");
2410
+ defs.append("marker").attr("id", "fc-arrow").attr("viewBox", `0 0 ${ARROWHEAD_W} ${ARROWHEAD_H}`).attr("refX", ARROWHEAD_W).attr("refY", ARROWHEAD_H / 2).attr("markerWidth", ARROWHEAD_W).attr("markerHeight", ARROWHEAD_H).attr("orient", "auto").append("polygon").attr("points", `0,0 ${ARROWHEAD_W},${ARROWHEAD_H / 2} 0,${ARROWHEAD_H}`).attr("fill", palette.textMuted);
2411
+ const edgeColors = /* @__PURE__ */ new Set();
2412
+ for (const edge of layout.edges) {
2413
+ if (edge.color) edgeColors.add(edge.color);
2414
+ }
2415
+ for (const color of edgeColors) {
2416
+ const id = `fc-arrow-${color.replace("#", "")}`;
2417
+ defs.append("marker").attr("id", id).attr("viewBox", `0 0 ${ARROWHEAD_W} ${ARROWHEAD_H}`).attr("refX", ARROWHEAD_W).attr("refY", ARROWHEAD_H / 2).attr("markerWidth", ARROWHEAD_W).attr("markerHeight", ARROWHEAD_H).attr("orient", "auto").append("polygon").attr("points", `0,0 ${ARROWHEAD_W},${ARROWHEAD_H / 2} 0,${ARROWHEAD_H}`).attr("fill", color);
2418
+ }
2419
+ const mainG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY}) scale(${scale})`);
2420
+ if (graph.title) {
2421
+ mainG.append("text").attr("x", diagramW / 2).attr("y", TITLE_FONT_SIZE).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", "bold").attr("class", "fc-title").text(graph.title);
2422
+ }
2423
+ const contentG = mainG.append("g").attr("transform", `translate(0, ${titleOffset})`);
2424
+ for (const group of layout.groups) {
2425
+ if (group.width === 0 && group.height === 0) continue;
2426
+ const gx = group.x - GROUP_EXTRA_PADDING;
2427
+ const gy = group.y - GROUP_EXTRA_PADDING - GROUP_LABEL_FONT_SIZE - 4;
2428
+ const gw = group.width + GROUP_EXTRA_PADDING * 2;
2429
+ const gh = group.height + GROUP_EXTRA_PADDING * 2 + GROUP_LABEL_FONT_SIZE + 4;
2430
+ const fillColor = group.color ? mix(group.color, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
2431
+ const strokeColor = group.color ?? palette.textMuted;
2432
+ contentG.append("rect").attr("x", gx).attr("y", gy).attr("width", gw).attr("height", gh).attr("rx", 6).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("class", "fc-group");
2433
+ contentG.append("text").attr("x", gx + 8).attr("y", gy + GROUP_LABEL_FONT_SIZE + 4).attr("fill", strokeColor).attr("font-size", GROUP_LABEL_FONT_SIZE).attr("font-weight", "bold").attr("opacity", 0.7).attr("class", "fc-group-label").text(group.label);
2434
+ }
2435
+ for (const edge of layout.edges) {
2436
+ if (edge.points.length < 2) continue;
2437
+ const edgeG = contentG.append("g").attr("class", "fc-edge-group").attr("data-line-number", String(edge.lineNumber));
2438
+ const edgeColor = edge.color ?? palette.textMuted;
2439
+ const markerId = edge.color ? `fc-arrow-${edge.color.replace("#", "")}` : "fc-arrow";
2440
+ const pathD = lineGenerator(edge.points);
2441
+ if (pathD) {
2442
+ edgeG.append("path").attr("d", pathD).attr("fill", "none").attr("stroke", edgeColor).attr("stroke-width", EDGE_STROKE_WIDTH).attr("marker-end", `url(#${markerId})`).attr("class", "fc-edge");
2443
+ }
2444
+ if (edge.label) {
2445
+ const midIdx = Math.floor(edge.points.length / 2);
2446
+ const midPt = edge.points[midIdx];
2447
+ const labelLen = edge.label.length;
2448
+ const bgW = labelLen * 7 + 8;
2449
+ const bgH = 16;
2450
+ edgeG.append("rect").attr("x", midPt.x - bgW / 2).attr("y", midPt.y - bgH / 2 - 1).attr("width", bgW).attr("height", bgH).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.85).attr("class", "fc-edge-label-bg");
2451
+ edgeG.append("text").attr("x", midPt.x).attr("y", midPt.y + 4).attr("text-anchor", "middle").attr("fill", edgeColor).attr("font-size", EDGE_LABEL_FONT_SIZE).attr("class", "fc-edge-label").text(edge.label);
2452
+ }
2453
+ }
2454
+ for (const node of layout.nodes) {
2455
+ const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "fc-node").attr("data-line-number", String(node.lineNumber));
2456
+ if (onClickItem) {
2457
+ nodeG.style("cursor", "pointer").on("click", () => {
2458
+ onClickItem(node.lineNumber);
2459
+ });
2460
+ }
2461
+ renderNodeShape(nodeG, node, palette, isDark);
2462
+ nodeG.append("text").attr("x", 0).attr("y", 0).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", palette.text).attr("font-size", NODE_FONT_SIZE).text(node.label);
2463
+ }
2464
+ }
2465
+ function renderFlowchartForExport(content, theme, palette) {
2466
+ const parsed = parseFlowchart(content, palette);
2467
+ if (parsed.error || parsed.nodes.length === 0) return "";
2468
+ const layout = layoutGraph(parsed);
2469
+ const isDark = theme === "dark";
2470
+ const container = document.createElement("div");
2471
+ container.style.width = `${layout.width + DIAGRAM_PADDING * 2}px`;
2472
+ container.style.height = `${layout.height + DIAGRAM_PADDING * 2 + (parsed.title ? TITLE_HEIGHT : 0)}px`;
2473
+ container.style.position = "absolute";
2474
+ container.style.left = "-9999px";
2475
+ document.body.appendChild(container);
2476
+ const exportWidth = layout.width + DIAGRAM_PADDING * 2;
2477
+ const exportHeight = layout.height + DIAGRAM_PADDING * 2 + (parsed.title ? TITLE_HEIGHT : 0);
2478
+ try {
2479
+ renderFlowchart(
2480
+ container,
2481
+ parsed,
2482
+ layout,
2483
+ palette,
2484
+ isDark,
2485
+ void 0,
2486
+ { width: exportWidth, height: exportHeight }
2487
+ );
2488
+ const svgEl = container.querySelector("svg");
2489
+ if (!svgEl) return "";
2490
+ if (theme === "transparent") {
2491
+ svgEl.style.background = "none";
2492
+ }
2493
+ svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
2494
+ svgEl.style.fontFamily = FONT_FAMILY;
2495
+ return svgEl.outerHTML;
2496
+ } finally {
2497
+ document.body.removeChild(container);
2498
+ }
2499
+ }
2500
+ var DIAGRAM_PADDING, TITLE_HEIGHT, TITLE_FONT_SIZE, NODE_FONT_SIZE, EDGE_LABEL_FONT_SIZE, GROUP_LABEL_FONT_SIZE, EDGE_STROKE_WIDTH, NODE_STROKE_WIDTH, ARROWHEAD_W, ARROWHEAD_H, IO_SKEW, SUBROUTINE_INSET, DOC_WAVE_HEIGHT, GROUP_EXTRA_PADDING, lineGenerator;
2501
+ var init_flowchart_renderer = __esm({
2502
+ "src/graph/flowchart-renderer.ts"() {
2503
+ "use strict";
2504
+ init_fonts();
2505
+ init_flowchart_parser();
2506
+ init_layout();
2507
+ DIAGRAM_PADDING = 20;
2508
+ TITLE_HEIGHT = 30;
2509
+ TITLE_FONT_SIZE = 18;
2510
+ NODE_FONT_SIZE = 13;
2511
+ EDGE_LABEL_FONT_SIZE = 11;
2512
+ GROUP_LABEL_FONT_SIZE = 11;
2513
+ EDGE_STROKE_WIDTH = 1.5;
2514
+ NODE_STROKE_WIDTH = 1.5;
2515
+ ARROWHEAD_W = 10;
2516
+ ARROWHEAD_H = 7;
2517
+ IO_SKEW = 15;
2518
+ SUBROUTINE_INSET = 8;
2519
+ DOC_WAVE_HEIGHT = 10;
2520
+ GROUP_EXTRA_PADDING = 12;
2521
+ lineGenerator = d3Shape.line().x((d) => d.x).y((d) => d.y).curve(d3Shape.curveBasis);
2522
+ }
2523
+ });
2524
+
1615
2525
  // src/sequence/renderer.ts
1616
2526
  var renderer_exports = {};
1617
2527
  __export(renderer_exports, {
@@ -1622,8 +2532,45 @@ __export(renderer_exports, {
1622
2532
  groupMessagesBySection: () => groupMessagesBySection,
1623
2533
  renderSequenceDiagram: () => renderSequenceDiagram
1624
2534
  });
1625
- import * as d3Selection from "d3-selection";
1626
- function mix(a, b, pct) {
2535
+ import * as d3Selection2 from "d3-selection";
2536
+ function parseInlineMarkdown(text) {
2537
+ const spans = [];
2538
+ const regex = /\*\*(.+?)\*\*|__(.+?)__|\*(.+?)\*|_(.+?)_|`(.+?)`|\[(.+?)\]\((.+?)\)|([^*_`[]+)/g;
2539
+ let match;
2540
+ while ((match = regex.exec(text)) !== null) {
2541
+ if (match[1]) spans.push({ text: match[1], bold: true });
2542
+ else if (match[2]) spans.push({ text: match[2], bold: true });
2543
+ else if (match[3]) spans.push({ text: match[3], italic: true });
2544
+ else if (match[4]) spans.push({ text: match[4], italic: true });
2545
+ else if (match[5]) spans.push({ text: match[5], code: true });
2546
+ else if (match[6]) spans.push({ text: match[6], href: match[7] });
2547
+ else if (match[8]) spans.push({ text: match[8] });
2548
+ }
2549
+ return spans;
2550
+ }
2551
+ function wrapTextLines(text, maxChars) {
2552
+ const rawLines = text.split("\n");
2553
+ const wrapped = [];
2554
+ for (const line3 of rawLines) {
2555
+ if (line3.length <= maxChars) {
2556
+ wrapped.push(line3);
2557
+ } else {
2558
+ const words = line3.split(" ");
2559
+ let current = "";
2560
+ for (const word of words) {
2561
+ if (current && (current + " " + word).length > maxChars) {
2562
+ wrapped.push(current);
2563
+ current = word;
2564
+ } else {
2565
+ current = current ? current + " " + word : word;
2566
+ }
2567
+ }
2568
+ if (current) wrapped.push(current);
2569
+ }
2570
+ }
2571
+ return wrapped;
2572
+ }
2573
+ function mix2(a, b, pct) {
1627
2574
  const parse = (h) => {
1628
2575
  const r = h.replace("#", "");
1629
2576
  const f = r.length === 3 ? r[0] + r[0] + r[1] + r[1] + r[2] + r[2] : r;
@@ -1729,7 +2676,12 @@ function groupMessagesBySection(elements, messages) {
1729
2676
  ...collectIndices(el.children),
1730
2677
  ...collectIndices(el.elseChildren)
1731
2678
  );
1732
- } else if (isSequenceSection(el)) {
2679
+ if (el.elseIfBranches) {
2680
+ for (const branch of el.elseIfBranches) {
2681
+ indices.push(...collectIndices(branch.children));
2682
+ }
2683
+ }
2684
+ } else if (isSequenceSection(el) || isSequenceNote(el)) {
1733
2685
  continue;
1734
2686
  } else {
1735
2687
  const idx = messages.indexOf(el);
@@ -1745,7 +2697,7 @@ function groupMessagesBySection(elements, messages) {
1745
2697
  } else if (currentGroup) {
1746
2698
  if (isSequenceBlock(el)) {
1747
2699
  currentGroup.messageIndices.push(...collectIndices([el]));
1748
- } else {
2700
+ } else if (!isSequenceNote(el)) {
1749
2701
  const idx = messages.indexOf(el);
1750
2702
  if (idx >= 0) currentGroup.messageIndices.push(idx);
1751
2703
  }
@@ -1902,7 +2854,7 @@ function applyGroupOrdering(participants, groups) {
1902
2854
  return result;
1903
2855
  }
1904
2856
  function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateToLine, options) {
1905
- d3Selection.select(container).selectAll("*").remove();
2857
+ d3Selection2.select(container).selectAll("*").remove();
1906
2858
  const { title, messages, elements, groups, options: parsedOptions } = parsed;
1907
2859
  const collapsedSections = options?.collapsedSections;
1908
2860
  const participants = applyPositionOverrides(
@@ -1943,7 +2895,15 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1943
2895
  if (isSequenceBlock(el)) {
1944
2896
  const idx = findFirstMsgIndex(el.children);
1945
2897
  if (idx >= 0) return idx;
1946
- } else if (!isSequenceSection(el)) {
2898
+ if (el.elseIfBranches) {
2899
+ for (const branch of el.elseIfBranches) {
2900
+ const branchIdx = findFirstMsgIndex(branch.children);
2901
+ if (branchIdx >= 0) return branchIdx;
2902
+ }
2903
+ }
2904
+ const elseIdx = findFirstMsgIndex(el.elseChildren);
2905
+ if (elseIdx >= 0) return elseIdx;
2906
+ } else if (!isSequenceSection(el) && !isSequenceNote(el)) {
1947
2907
  const idx = messages.indexOf(el);
1948
2908
  if (idx >= 0 && !hiddenMsgIndices.has(idx)) return idx;
1949
2909
  }
@@ -1963,6 +2923,13 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1963
2923
  if (!isSequenceBlock(el)) continue;
1964
2924
  const firstIdx = findFirstMsgIndex(el.children);
1965
2925
  if (firstIdx >= 0) addExtra(firstIdx, BLOCK_HEADER_SPACE);
2926
+ if (el.elseIfBranches) {
2927
+ for (const branch of el.elseIfBranches) {
2928
+ const firstBranchIdx = findFirstMsgIndex(branch.children);
2929
+ if (firstBranchIdx >= 0) addExtra(firstBranchIdx, BLOCK_HEADER_SPACE);
2930
+ markBlockSpacing(branch.children);
2931
+ }
2932
+ }
1966
2933
  const firstElseIdx = findFirstMsgIndex(el.elseChildren);
1967
2934
  if (firstElseIdx >= 0) addExtra(firstElseIdx, BLOCK_HEADER_SPACE);
1968
2935
  markBlockSpacing(el.children);
@@ -1976,6 +2943,34 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1976
2943
  if (elements && elements.length > 0) {
1977
2944
  markBlockSpacing(elements);
1978
2945
  }
2946
+ const NOTE_OFFSET_BELOW = 16;
2947
+ const computeNoteHeight = (text) => {
2948
+ const lines = wrapTextLines(text, NOTE_CHARS_PER_LINE);
2949
+ return lines.length * NOTE_LINE_H + NOTE_PAD_V * 2;
2950
+ };
2951
+ const markNoteSpacing = (els) => {
2952
+ for (let i = 0; i < els.length; i++) {
2953
+ const el = els[i];
2954
+ if (isSequenceNote(el)) {
2955
+ const noteH = computeNoteHeight(el.text);
2956
+ const nextIdx = i + 1 < els.length ? findFirstMsgIndex([els[i + 1]]) : -1;
2957
+ if (nextIdx >= 0) {
2958
+ addExtra(nextIdx, noteH + NOTE_OFFSET_BELOW);
2959
+ }
2960
+ } else if (isSequenceBlock(el)) {
2961
+ markNoteSpacing(el.children);
2962
+ if (el.elseIfBranches) {
2963
+ for (const branch of el.elseIfBranches) {
2964
+ markNoteSpacing(branch.children);
2965
+ }
2966
+ }
2967
+ markNoteSpacing(el.elseChildren);
2968
+ }
2969
+ }
2970
+ };
2971
+ if (elements && elements.length > 0) {
2972
+ markNoteSpacing(elements);
2973
+ }
1979
2974
  const preSectionMsgIndices = [];
1980
2975
  const sectionRegions = [];
1981
2976
  {
@@ -1984,15 +2979,27 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1984
2979
  for (const child of block.children) {
1985
2980
  if (isSequenceBlock(child)) {
1986
2981
  indices.push(...collectMsgIndicesFromBlock(child));
1987
- } else if (!isSequenceSection(child)) {
2982
+ } else if (!isSequenceSection(child) && !isSequenceNote(child)) {
1988
2983
  const idx = messages.indexOf(child);
1989
2984
  if (idx >= 0) indices.push(idx);
1990
2985
  }
1991
2986
  }
2987
+ if (block.elseIfBranches) {
2988
+ for (const branch of block.elseIfBranches) {
2989
+ for (const child of branch.children) {
2990
+ if (isSequenceBlock(child)) {
2991
+ indices.push(...collectMsgIndicesFromBlock(child));
2992
+ } else if (!isSequenceSection(child) && !isSequenceNote(child)) {
2993
+ const idx = messages.indexOf(child);
2994
+ if (idx >= 0) indices.push(idx);
2995
+ }
2996
+ }
2997
+ }
2998
+ }
1992
2999
  for (const child of block.elseChildren) {
1993
3000
  if (isSequenceBlock(child)) {
1994
3001
  indices.push(...collectMsgIndicesFromBlock(child));
1995
- } else if (!isSequenceSection(child)) {
3002
+ } else if (!isSequenceSection(child) && !isSequenceNote(child)) {
1996
3003
  const idx = messages.indexOf(child);
1997
3004
  if (idx >= 0) indices.push(idx);
1998
3005
  }
@@ -2067,7 +3074,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2067
3074
  const GROUP_PADDING_TOP = 22;
2068
3075
  const GROUP_PADDING_BOTTOM = 8;
2069
3076
  const GROUP_LABEL_SIZE = 11;
2070
- const titleOffset = title ? TITLE_HEIGHT : 0;
3077
+ const titleOffset = title ? TITLE_HEIGHT2 : 0;
2071
3078
  const groupOffset = groups.length > 0 ? GROUP_PADDING_TOP + GROUP_LABEL_SIZE : 0;
2072
3079
  const participantStartY = TOP_MARGIN + titleOffset + PARTICIPANT_Y_OFFSET + groupOffset;
2073
3080
  const lifelineStartY0 = participantStartY + PARTICIPANT_BOX_HEIGHT;
@@ -2121,7 +3128,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2121
3128
  participants.forEach((p, i) => {
2122
3129
  participantX.set(p.id, offsetX + i * PARTICIPANT_GAP);
2123
3130
  });
2124
- const svg = d3Selection.select(container).append("svg").attr("width", "100%").attr("height", totalHeight).attr("viewBox", `0 0 ${svgWidth} ${totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("class", "sequence-diagram").style("font-family", FONT_FAMILY);
3131
+ const svg = d3Selection2.select(container).append("svg").attr("width", "100%").attr("height", totalHeight).attr("viewBox", `0 0 ${svgWidth} ${totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("class", "sequence-diagram").style("font-family", FONT_FAMILY);
2125
3132
  const defs = svg.append("defs");
2126
3133
  defs.append("marker").attr("id", "seq-arrowhead").attr("viewBox", `0 0 ${ARROWHEAD_SIZE} ${ARROWHEAD_SIZE}`).attr("refX", ARROWHEAD_SIZE).attr("refY", ARROWHEAD_SIZE / 2).attr("markerWidth", ARROWHEAD_SIZE).attr("markerHeight", ARROWHEAD_SIZE).attr("orient", "auto").append("polygon").attr(
2127
3134
  "points",
@@ -2147,7 +3154,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2147
3154
  const boxY = participantStartY - GROUP_PADDING_TOP;
2148
3155
  const boxH = PARTICIPANT_BOX_HEIGHT + GROUP_PADDING_TOP + GROUP_PADDING_BOTTOM;
2149
3156
  const resolvedGroupColor = group.color ? resolveColor(group.color, palette) : void 0;
2150
- const fillColor = resolvedGroupColor ? mix(resolvedGroupColor, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : palette.bg;
3157
+ const fillColor = resolvedGroupColor ? mix2(resolvedGroupColor, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : palette.bg;
2151
3158
  const strokeColor = resolvedGroupColor || palette.textMuted;
2152
3159
  svg.append("rect").attr("x", minX).attr("y", boxY).attr("width", maxX - minX).attr("height", boxH).attr("rx", 6).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("class", "group-box").attr("data-group-line", String(group.lineNumber));
2153
3160
  svg.append("text").attr("x", minX + 8).attr("y", boxY + GROUP_LABEL_SIZE + 4).attr("fill", strokeColor).attr("font-size", GROUP_LABEL_SIZE).attr("font-weight", "bold").attr("opacity", 0.7).attr("class", "group-label").attr("data-group-line", String(group.lineNumber)).text(group.name);
@@ -2172,7 +3179,12 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2172
3179
  ...collectMsgIndices(el.children),
2173
3180
  ...collectMsgIndices(el.elseChildren)
2174
3181
  );
2175
- } else if (!isSequenceSection(el)) {
3182
+ if (el.elseIfBranches) {
3183
+ for (const branch of el.elseIfBranches) {
3184
+ indices.push(...collectMsgIndices(branch.children));
3185
+ }
3186
+ }
3187
+ } else if (!isSequenceSection(el) && !isSequenceNote(el)) {
2176
3188
  const idx = messages.indexOf(el);
2177
3189
  if (idx >= 0) indices.push(idx);
2178
3190
  }
@@ -2185,8 +3197,21 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2185
3197
  for (const el of els) {
2186
3198
  if (!isSequenceBlock(el)) continue;
2187
3199
  const ifIndices = collectMsgIndices(el.children);
3200
+ const elseIfBranchData = [];
3201
+ if (el.elseIfBranches) {
3202
+ for (const branch of el.elseIfBranches) {
3203
+ elseIfBranchData.push({
3204
+ label: branch.label,
3205
+ indices: collectMsgIndices(branch.children)
3206
+ });
3207
+ }
3208
+ }
2188
3209
  const elseIndices = collectMsgIndices(el.elseChildren);
2189
- const allIndices = [...ifIndices, ...elseIndices];
3210
+ const allIndices = [
3211
+ ...ifIndices,
3212
+ ...elseIfBranchData.flatMap((b) => b.indices),
3213
+ ...elseIndices
3214
+ ];
2190
3215
  if (allIndices.length === 0) continue;
2191
3216
  let minStep = Infinity;
2192
3217
  let maxStep = -Infinity;
@@ -2224,6 +3249,32 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2224
3249
  italic: false,
2225
3250
  blockLine: el.lineNumber
2226
3251
  });
3252
+ for (const branchData of elseIfBranchData) {
3253
+ if (branchData.indices.length > 0) {
3254
+ let firstBranchStep = Infinity;
3255
+ for (const mi of branchData.indices) {
3256
+ const first = msgToFirstStep.get(mi);
3257
+ if (first !== void 0)
3258
+ firstBranchStep = Math.min(firstBranchStep, first);
3259
+ }
3260
+ if (firstBranchStep < Infinity) {
3261
+ const dividerY = stepY(firstBranchStep) - stepSpacing / 2;
3262
+ deferredLines.push({
3263
+ x1: frameX,
3264
+ y1: dividerY,
3265
+ x2: frameX + frameW,
3266
+ y2: dividerY
3267
+ });
3268
+ deferredLabels.push({
3269
+ x: frameX + 6,
3270
+ y: dividerY + 14,
3271
+ text: `else if ${branchData.label}`,
3272
+ bold: false,
3273
+ italic: true
3274
+ });
3275
+ }
3276
+ }
3277
+ }
2227
3278
  if (elseIndices.length > 0) {
2228
3279
  let firstElseStep = Infinity;
2229
3280
  for (const mi of elseIndices) {
@@ -2249,6 +3300,11 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2249
3300
  }
2250
3301
  }
2251
3302
  renderBlockFrames(el.children, depth + 1);
3303
+ if (el.elseIfBranches) {
3304
+ for (const branch of el.elseIfBranches) {
3305
+ renderBlockFrames(branch.children, depth + 1);
3306
+ }
3307
+ }
2252
3308
  renderBlockFrames(el.elseChildren, depth + 1);
2253
3309
  }
2254
3310
  };
@@ -2270,7 +3326,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2270
3326
  if (msg) coveredLines.push(msg.lineNumber);
2271
3327
  }
2272
3328
  svg.append("rect").attr("x", x).attr("y", y1).attr("width", ACTIVATION_WIDTH).attr("height", y2 - y1).attr("fill", isDark ? palette.surface : palette.bg);
2273
- const actFill = mix(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
3329
+ const actFill = mix2(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
2274
3330
  svg.append("rect").attr("x", x).attr("y", y1).attr("width", ACTIVATION_WIDTH).attr("height", y2 - y1).attr("fill", actFill).attr("stroke", palette.primary).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("data-participant-id", act.participantId).attr("data-msg-lines", coveredLines.join(",")).attr("class", "activation");
2275
3331
  });
2276
3332
  for (const ln of deferredLines) {
@@ -2396,6 +3452,94 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2396
3452
  }
2397
3453
  }
2398
3454
  });
3455
+ const noteFill = isDark ? mix2(palette.surface, palette.bg, 50) : mix2(palette.bg, palette.surface, 15);
3456
+ const findAssociatedStep = (note) => {
3457
+ let bestMsgIndex = -1;
3458
+ let bestLine = -1;
3459
+ for (let mi = 0; mi < messages.length; mi++) {
3460
+ if (messages[mi].lineNumber < note.lineNumber && messages[mi].lineNumber > bestLine && !hiddenMsgIndices.has(mi)) {
3461
+ bestLine = messages[mi].lineNumber;
3462
+ bestMsgIndex = mi;
3463
+ }
3464
+ }
3465
+ if (bestMsgIndex < 0) return -1;
3466
+ return msgToFirstStep.get(bestMsgIndex) ?? -1;
3467
+ };
3468
+ const renderNoteElements = (els) => {
3469
+ for (const el of els) {
3470
+ if (isSequenceNote(el)) {
3471
+ const px = participantX.get(el.participantId);
3472
+ if (px === void 0) continue;
3473
+ const si = findAssociatedStep(el);
3474
+ if (si < 0) continue;
3475
+ const noteY = stepY(si);
3476
+ const wrappedLines = wrapTextLines(el.text, NOTE_CHARS_PER_LINE);
3477
+ const noteH = wrappedLines.length * NOTE_LINE_H + NOTE_PAD_V * 2;
3478
+ const maxLineLen = Math.max(...wrappedLines.map((l) => l.length));
3479
+ const noteW = Math.min(
3480
+ NOTE_MAX_W,
3481
+ Math.max(80, maxLineLen * NOTE_CHAR_W + NOTE_PAD_H * 2 + NOTE_FOLD)
3482
+ );
3483
+ const isRight = el.position === "right";
3484
+ const noteX = isRight ? px + ACTIVATION_WIDTH + NOTE_GAP : px - ACTIVATION_WIDTH - NOTE_GAP - noteW;
3485
+ const noteTopY = noteY + NOTE_OFFSET_BELOW;
3486
+ const noteG = svg.append("g").attr("class", "note").attr("data-note-toggle", "").attr("data-line-number", String(el.lineNumber)).attr("data-line-end", String(el.endLineNumber));
3487
+ noteG.append("path").attr(
3488
+ "d",
3489
+ [
3490
+ `M ${noteX} ${noteTopY}`,
3491
+ `L ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
3492
+ `L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`,
3493
+ `L ${noteX + noteW} ${noteTopY + noteH}`,
3494
+ `L ${noteX} ${noteTopY + noteH}`,
3495
+ "Z"
3496
+ ].join(" ")
3497
+ ).attr("fill", noteFill).attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-box");
3498
+ noteG.append("path").attr(
3499
+ "d",
3500
+ [
3501
+ `M ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
3502
+ `L ${noteX + noteW - NOTE_FOLD} ${noteTopY + NOTE_FOLD}`,
3503
+ `L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`
3504
+ ].join(" ")
3505
+ ).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-fold");
3506
+ wrappedLines.forEach((line3, li) => {
3507
+ const textY = noteTopY + NOTE_PAD_V + (li + 1) * NOTE_LINE_H - 3;
3508
+ const isBullet = line3.startsWith("- ");
3509
+ const bulletIndent = isBullet ? 10 : 0;
3510
+ const displayLine = isBullet ? line3.slice(2) : line3;
3511
+ const textEl = noteG.append("text").attr("x", noteX + NOTE_PAD_H + bulletIndent).attr("y", textY).attr("fill", palette.text).attr("font-size", NOTE_FONT_SIZE).attr("class", "note-text");
3512
+ if (isBullet) {
3513
+ noteG.append("text").attr("x", noteX + NOTE_PAD_H).attr("y", textY).attr("fill", palette.text).attr("font-size", NOTE_FONT_SIZE).text("\u2022");
3514
+ }
3515
+ const spans = parseInlineMarkdown(displayLine);
3516
+ for (const span of spans) {
3517
+ if (span.href) {
3518
+ const a = textEl.append("a").attr("href", span.href);
3519
+ a.append("tspan").text(span.text).attr("fill", palette.primary).style("text-decoration", "underline");
3520
+ } else {
3521
+ const tspan = textEl.append("tspan").text(span.text);
3522
+ if (span.bold) tspan.attr("font-weight", "bold");
3523
+ if (span.italic) tspan.attr("font-style", "italic");
3524
+ if (span.code)
3525
+ tspan.attr("font-family", "monospace").attr("font-size", NOTE_FONT_SIZE - 1);
3526
+ }
3527
+ }
3528
+ });
3529
+ } else if (isSequenceBlock(el)) {
3530
+ renderNoteElements(el.children);
3531
+ if (el.elseIfBranches) {
3532
+ for (const branch of el.elseIfBranches) {
3533
+ renderNoteElements(branch.children);
3534
+ }
3535
+ }
3536
+ renderNoteElements(el.elseChildren);
3537
+ }
3538
+ }
3539
+ };
3540
+ if (elements && elements.length > 0) {
3541
+ renderNoteElements(elements);
3542
+ }
2399
3543
  }
2400
3544
  function renderParticipant(svg, participant, cx, cy, palette, isDark) {
2401
3545
  const g = svg.append("g").attr("transform", `translate(${cx}, ${cy})`).attr("class", "participant").attr("data-participant-id", participant.id);
@@ -2437,7 +3581,7 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark) {
2437
3581
  isActor ? PARTICIPANT_BOX_HEIGHT + 14 : PARTICIPANT_BOX_HEIGHT / 2 + 5
2438
3582
  ).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", 13).attr("font-weight", 500).text(participant.label);
2439
3583
  }
2440
- var PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, TOP_MARGIN, TITLE_HEIGHT, PARTICIPANT_Y_OFFSET, SERVICE_BORDER_RADIUS, MESSAGE_START_OFFSET, LIFELINE_TAIL, ARROWHEAD_SIZE, fill, stroke, SW, W, H;
3584
+ var PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, TOP_MARGIN, TITLE_HEIGHT2, PARTICIPANT_Y_OFFSET, SERVICE_BORDER_RADIUS, MESSAGE_START_OFFSET, LIFELINE_TAIL, ARROWHEAD_SIZE, NOTE_MAX_W, NOTE_FOLD, NOTE_PAD_H, NOTE_PAD_V, NOTE_FONT_SIZE, NOTE_LINE_H, NOTE_GAP, NOTE_CHAR_W, NOTE_CHARS_PER_LINE, fill, stroke, SW, W, H;
2441
3585
  var init_renderer = __esm({
2442
3586
  "src/sequence/renderer.ts"() {
2443
3587
  "use strict";
@@ -2448,13 +3592,22 @@ var init_renderer = __esm({
2448
3592
  PARTICIPANT_BOX_WIDTH = 120;
2449
3593
  PARTICIPANT_BOX_HEIGHT = 50;
2450
3594
  TOP_MARGIN = 20;
2451
- TITLE_HEIGHT = 30;
3595
+ TITLE_HEIGHT2 = 30;
2452
3596
  PARTICIPANT_Y_OFFSET = 10;
2453
3597
  SERVICE_BORDER_RADIUS = 10;
2454
3598
  MESSAGE_START_OFFSET = 30;
2455
3599
  LIFELINE_TAIL = 30;
2456
3600
  ARROWHEAD_SIZE = 8;
2457
- fill = (palette, isDark) => mix(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
3601
+ NOTE_MAX_W = 200;
3602
+ NOTE_FOLD = 10;
3603
+ NOTE_PAD_H = 8;
3604
+ NOTE_PAD_V = 6;
3605
+ NOTE_FONT_SIZE = 10;
3606
+ NOTE_LINE_H = 14;
3607
+ NOTE_GAP = 15;
3608
+ NOTE_CHAR_W = 6;
3609
+ NOTE_CHARS_PER_LINE = Math.floor((NOTE_MAX_W - NOTE_PAD_H * 2 - NOTE_FOLD) / NOTE_CHAR_W);
3610
+ fill = (palette, isDark) => mix2(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
2458
3611
  stroke = (palette) => palette.textMuted;
2459
3612
  SW = 1.5;
2460
3613
  W = PARTICIPANT_BOX_WIDTH;
@@ -2462,50 +3615,8 @@ var init_renderer = __esm({
2462
3615
  }
2463
3616
  });
2464
3617
 
2465
- // src/dgmo-router.ts
2466
- init_parser();
2467
- var DGMO_CHART_TYPE_MAP = {
2468
- // Standard charts (via ECharts)
2469
- bar: "echart",
2470
- line: "echart",
2471
- "multi-line": "echart",
2472
- area: "echart",
2473
- pie: "echart",
2474
- doughnut: "echart",
2475
- radar: "echart",
2476
- "polar-area": "echart",
2477
- "bar-stacked": "echart",
2478
- // ECharts
2479
- scatter: "echart",
2480
- sankey: "echart",
2481
- chord: "echart",
2482
- function: "echart",
2483
- heatmap: "echart",
2484
- funnel: "echart",
2485
- // D3
2486
- slope: "d3",
2487
- wordcloud: "d3",
2488
- arc: "d3",
2489
- timeline: "d3",
2490
- venn: "d3",
2491
- quadrant: "d3",
2492
- sequence: "d3"
2493
- };
2494
- function getDgmoFramework(chartType) {
2495
- return DGMO_CHART_TYPE_MAP[chartType.toLowerCase()] ?? null;
2496
- }
2497
- function parseDgmoChartType(content) {
2498
- const lines = content.split("\n");
2499
- for (const line2 of lines) {
2500
- const trimmed = line2.trim();
2501
- if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//"))
2502
- continue;
2503
- const match = trimmed.match(/^chart\s*:\s*(.+)/i);
2504
- if (match) return match[1].trim().toLowerCase();
2505
- }
2506
- if (looksLikeSequence(content)) return "sequence";
2507
- return null;
2508
- }
3618
+ // src/index.ts
3619
+ init_dgmo_router();
2509
3620
 
2510
3621
  // src/chart.ts
2511
3622
  init_colors();
@@ -2867,7 +3978,7 @@ function parseEChart(content, palette) {
2867
3978
  function buildEChartsOption(parsed, palette, isDark) {
2868
3979
  const textColor = palette.text;
2869
3980
  const axisLineColor = palette.border;
2870
- const gridOpacity = isDark ? 0.7 : 0.4;
3981
+ const gridOpacity = isDark ? 0.7 : 0.55;
2871
3982
  const colors = getSeriesColors(palette);
2872
3983
  if (parsed.error) {
2873
3984
  return {};
@@ -3573,7 +4684,7 @@ function buildEChartsOptionFromChart(parsed, palette, isDark) {
3573
4684
  const textColor = palette.text;
3574
4685
  const axisLineColor = palette.border;
3575
4686
  const splitLineColor = palette.border;
3576
- const gridOpacity = isDark ? 0.7 : 0.4;
4687
+ const gridOpacity = isDark ? 0.7 : 0.55;
3577
4688
  const colors = getSeriesColors(palette);
3578
4689
  const titleConfig = parsed.title ? {
3579
4690
  text: parsed.title,
@@ -3989,8 +5100,8 @@ init_fonts();
3989
5100
  init_colors();
3990
5101
  init_palettes();
3991
5102
  import * as d3Scale from "d3-scale";
3992
- import * as d3Selection2 from "d3-selection";
3993
- import * as d3Shape from "d3-shape";
5103
+ import * as d3Selection3 from "d3-selection";
5104
+ import * as d3Shape2 from "d3-shape";
3994
5105
  import * as d3Array from "d3-array";
3995
5106
  import cloud from "d3-cloud";
3996
5107
  var DEFAULT_CLOUD_OPTIONS = {
@@ -4094,10 +5205,10 @@ function parseD3(content, palette) {
4094
5205
  let currentArcGroup = null;
4095
5206
  let currentTimelineGroup = null;
4096
5207
  for (let i = 0; i < lines.length; i++) {
4097
- const line2 = lines[i].trim();
5208
+ const line3 = lines[i].trim();
4098
5209
  const lineNumber = i + 1;
4099
- if (!line2) continue;
4100
- const sectionMatch = line2.match(/^#{2,}\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/);
5210
+ if (!line3) continue;
5211
+ const sectionMatch = line3.match(/^#{2,}\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/);
4101
5212
  if (sectionMatch) {
4102
5213
  if (result.type === "arc") {
4103
5214
  const name = sectionMatch[1].trim();
@@ -4112,11 +5223,11 @@ function parseD3(content, palette) {
4112
5223
  }
4113
5224
  continue;
4114
5225
  }
4115
- if (line2.startsWith("#") || line2.startsWith("//")) {
5226
+ if (line3.startsWith("#") || line3.startsWith("//")) {
4116
5227
  continue;
4117
5228
  }
4118
5229
  if (result.type === "arc") {
4119
- const linkMatch = line2.match(
5230
+ const linkMatch = line3.match(
4120
5231
  /^(.+?)\s*->\s*(.+?)(?:\(([^)]+)\))?\s*(?::\s*(\d+(?:\.\d+)?))?$/
4121
5232
  );
4122
5233
  if (linkMatch) {
@@ -4146,7 +5257,7 @@ function parseD3(content, palette) {
4146
5257
  }
4147
5258
  }
4148
5259
  if (result.type === "timeline") {
4149
- const eraMatch = line2.match(
5260
+ const eraMatch = line3.match(
4150
5261
  /^era\s+(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
4151
5262
  );
4152
5263
  if (eraMatch) {
@@ -4159,7 +5270,7 @@ function parseD3(content, palette) {
4159
5270
  });
4160
5271
  continue;
4161
5272
  }
4162
- const markerMatch = line2.match(
5273
+ const markerMatch = line3.match(
4163
5274
  /^marker\s+(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
4164
5275
  );
4165
5276
  if (markerMatch) {
@@ -4174,7 +5285,7 @@ function parseD3(content, palette) {
4174
5285
  }
4175
5286
  }
4176
5287
  if (result.type === "timeline") {
4177
- const durationMatch = line2.match(
5288
+ const durationMatch = line3.match(
4178
5289
  /^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\?)?(\d+(?:\.\d{1,2})?)([dwmy])\s*:\s*(.+)$/
4179
5290
  );
4180
5291
  if (durationMatch) {
@@ -4193,7 +5304,7 @@ function parseD3(content, palette) {
4193
5304
  });
4194
5305
  continue;
4195
5306
  }
4196
- const rangeMatch = line2.match(
5307
+ const rangeMatch = line3.match(
4197
5308
  /^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\?)?(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
4198
5309
  );
4199
5310
  if (rangeMatch) {
@@ -4207,7 +5318,7 @@ function parseD3(content, palette) {
4207
5318
  });
4208
5319
  continue;
4209
5320
  }
4210
- const pointMatch = line2.match(
5321
+ const pointMatch = line3.match(
4211
5322
  /^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
4212
5323
  );
4213
5324
  if (pointMatch) {
@@ -4222,7 +5333,7 @@ function parseD3(content, palette) {
4222
5333
  }
4223
5334
  }
4224
5335
  if (result.type === "venn") {
4225
- const overlapMatch = line2.match(
5336
+ const overlapMatch = line3.match(
4226
5337
  /^(.+?&.+?)\s*:\s*(\d+(?:\.\d+)?)\s*(?:"([^"]*)")?\s*$/
4227
5338
  );
4228
5339
  if (overlapMatch) {
@@ -4232,7 +5343,7 @@ function parseD3(content, palette) {
4232
5343
  result.vennOverlaps.push({ sets, size, label, lineNumber });
4233
5344
  continue;
4234
5345
  }
4235
- const setMatch = line2.match(
5346
+ const setMatch = line3.match(
4236
5347
  /^(.+?)(?:\(([^)]+)\))?\s*:\s*(\d+(?:\.\d+)?)\s*(?:"([^"]*)")?\s*$/
4237
5348
  );
4238
5349
  if (setMatch) {
@@ -4245,7 +5356,7 @@ function parseD3(content, palette) {
4245
5356
  }
4246
5357
  }
4247
5358
  if (result.type === "quadrant") {
4248
- const xAxisMatch = line2.match(/^x-axis\s*:\s*(.+)/i);
5359
+ const xAxisMatch = line3.match(/^x-axis\s*:\s*(.+)/i);
4249
5360
  if (xAxisMatch) {
4250
5361
  const parts = xAxisMatch[1].split(",").map((s) => s.trim());
4251
5362
  if (parts.length >= 2) {
@@ -4254,7 +5365,7 @@ function parseD3(content, palette) {
4254
5365
  }
4255
5366
  continue;
4256
5367
  }
4257
- const yAxisMatch = line2.match(/^y-axis\s*:\s*(.+)/i);
5368
+ const yAxisMatch = line3.match(/^y-axis\s*:\s*(.+)/i);
4258
5369
  if (yAxisMatch) {
4259
5370
  const parts = yAxisMatch[1].split(",").map((s) => s.trim());
4260
5371
  if (parts.length >= 2) {
@@ -4264,7 +5375,7 @@ function parseD3(content, palette) {
4264
5375
  continue;
4265
5376
  }
4266
5377
  const quadrantLabelRe = /^(top-right|top-left|bottom-left|bottom-right)\s*:\s*(.+)/i;
4267
- const quadrantMatch = line2.match(quadrantLabelRe);
5378
+ const quadrantMatch = line3.match(quadrantLabelRe);
4268
5379
  if (quadrantMatch) {
4269
5380
  const position = quadrantMatch[1].toLowerCase();
4270
5381
  const labelPart = quadrantMatch[2].trim();
@@ -4280,7 +5391,7 @@ function parseD3(content, palette) {
4280
5391
  result.quadrantLabels.bottomRight = label;
4281
5392
  continue;
4282
5393
  }
4283
- const pointMatch = line2.match(
5394
+ const pointMatch = line3.match(
4284
5395
  /^(.+?):\s*([0-9]*\.?[0-9]+)\s*,\s*([0-9]*\.?[0-9]+)\s*$/
4285
5396
  );
4286
5397
  if (pointMatch) {
@@ -4297,13 +5408,13 @@ function parseD3(content, palette) {
4297
5408
  continue;
4298
5409
  }
4299
5410
  }
4300
- const colonIndex = line2.indexOf(":");
5411
+ const colonIndex = line3.indexOf(":");
4301
5412
  if (colonIndex !== -1) {
4302
- const rawKey = line2.substring(0, colonIndex).trim();
5413
+ const rawKey = line3.substring(0, colonIndex).trim();
4303
5414
  const key = rawKey.toLowerCase();
4304
5415
  const colorMatch = rawKey.match(/^(.+?)\(([^)]+)\)\s*$/);
4305
5416
  if (key === "chart") {
4306
- const value = line2.substring(colonIndex + 1).trim().toLowerCase();
5417
+ const value = line3.substring(colonIndex + 1).trim().toLowerCase();
4307
5418
  if (value === "slope" || value === "wordcloud" || value === "arc" || value === "timeline" || value === "venn" || value === "quadrant" || value === "sequence") {
4308
5419
  result.type = value;
4309
5420
  } else {
@@ -4313,35 +5424,35 @@ function parseD3(content, palette) {
4313
5424
  continue;
4314
5425
  }
4315
5426
  if (key === "title") {
4316
- result.title = line2.substring(colonIndex + 1).trim();
5427
+ result.title = line3.substring(colonIndex + 1).trim();
4317
5428
  if (result.type === "quadrant") {
4318
5429
  result.quadrantTitleLineNumber = lineNumber;
4319
5430
  }
4320
5431
  continue;
4321
5432
  }
4322
5433
  if (key === "orientation") {
4323
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5434
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4324
5435
  if (v === "horizontal" || v === "vertical") {
4325
5436
  result.orientation = v;
4326
5437
  }
4327
5438
  continue;
4328
5439
  }
4329
5440
  if (key === "order") {
4330
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5441
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4331
5442
  if (v === "name" || v === "group" || v === "degree") {
4332
5443
  result.arcOrder = v;
4333
5444
  }
4334
5445
  continue;
4335
5446
  }
4336
5447
  if (key === "sort") {
4337
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5448
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4338
5449
  if (v === "time" || v === "group") {
4339
5450
  result.timelineSort = v;
4340
5451
  }
4341
5452
  continue;
4342
5453
  }
4343
5454
  if (key === "swimlanes") {
4344
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5455
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4345
5456
  if (v === "on") {
4346
5457
  result.timelineSwimlanes = true;
4347
5458
  } else if (v === "off") {
@@ -4350,7 +5461,7 @@ function parseD3(content, palette) {
4350
5461
  continue;
4351
5462
  }
4352
5463
  if (key === "values") {
4353
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5464
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4354
5465
  if (v === "off") {
4355
5466
  result.vennShowValues = false;
4356
5467
  } else if (v === "on") {
@@ -4359,21 +5470,21 @@ function parseD3(content, palette) {
4359
5470
  continue;
4360
5471
  }
4361
5472
  if (key === "rotate") {
4362
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5473
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4363
5474
  if (v === "none" || v === "mixed" || v === "angled") {
4364
5475
  result.cloudOptions.rotate = v;
4365
5476
  }
4366
5477
  continue;
4367
5478
  }
4368
5479
  if (key === "max") {
4369
- const v = parseInt(line2.substring(colonIndex + 1).trim(), 10);
5480
+ const v = parseInt(line3.substring(colonIndex + 1).trim(), 10);
4370
5481
  if (!isNaN(v) && v > 0) {
4371
5482
  result.cloudOptions.max = v;
4372
5483
  }
4373
5484
  continue;
4374
5485
  }
4375
5486
  if (key === "size") {
4376
- const v = line2.substring(colonIndex + 1).trim();
5487
+ const v = line3.substring(colonIndex + 1).trim();
4377
5488
  const parts = v.split(",").map((s) => parseInt(s.trim(), 10));
4378
5489
  if (parts.length === 2 && parts.every((n) => !isNaN(n) && n > 0) && parts[0] < parts[1]) {
4379
5490
  result.cloudOptions.minSize = parts[0];
@@ -4383,7 +5494,7 @@ function parseD3(content, palette) {
4383
5494
  }
4384
5495
  const labelPart = colorMatch ? colorMatch[1].trim() : rawKey;
4385
5496
  const colorPart = colorMatch ? resolveColor(colorMatch[2].trim(), palette) : null;
4386
- const valuePart = line2.substring(colonIndex + 1).trim();
5497
+ const valuePart = line3.substring(colonIndex + 1).trim();
4387
5498
  const values = valuePart.split(",").map((v) => v.trim());
4388
5499
  const numericValues = [];
4389
5500
  let allNumeric = true;
@@ -4414,15 +5525,15 @@ function parseD3(content, palette) {
4414
5525
  }
4415
5526
  }
4416
5527
  if (result.type === "wordcloud") {
4417
- if (colonIndex === -1 && !line2.includes(" ")) {
4418
- result.words.push({ text: line2, weight: 10, lineNumber });
5528
+ if (colonIndex === -1 && !line3.includes(" ")) {
5529
+ result.words.push({ text: line3, weight: 10, lineNumber });
4419
5530
  } else {
4420
- freeformLines.push(line2);
5531
+ freeformLines.push(line3);
4421
5532
  }
4422
5533
  continue;
4423
5534
  }
4424
- if (result.periods.length === 0 && line2.includes(",") && !line2.includes(":")) {
4425
- const periods = line2.split(",").map((p) => p.trim()).filter(Boolean);
5535
+ if (result.periods.length === 0 && line3.includes(",") && !line3.includes(":")) {
5536
+ const periods = line3.split(",").map((p) => p.trim()).filter(Boolean);
4426
5537
  if (periods.length >= 2) {
4427
5538
  result.periods = periods;
4428
5539
  continue;
@@ -4645,7 +5756,7 @@ var SLOPE_MARGIN = { top: 80, bottom: 40, left: 80 };
4645
5756
  var SLOPE_LABEL_FONT_SIZE = 14;
4646
5757
  var SLOPE_CHAR_WIDTH = 8;
4647
5758
  function renderSlopeChart(container, parsed, palette, isDark, onClickItem, exportDims) {
4648
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
5759
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
4649
5760
  const { periods, data, title } = parsed;
4650
5761
  if (data.length === 0 || periods.length < 2) return;
4651
5762
  const width = exportDims?.width ?? container.clientWidth;
@@ -4672,7 +5783,7 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
4672
5783
  const valuePadding = (maxVal - minVal) * 0.1 || 1;
4673
5784
  const yScale = d3Scale.scaleLinear().domain([minVal - valuePadding, maxVal + valuePadding]).range([innerHeight, 0]);
4674
5785
  const xScale = d3Scale.scalePoint().domain(periods).range([0, innerWidth]).padding(0);
4675
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5786
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
4676
5787
  const g = svg.append("g").attr("transform", `translate(${SLOPE_MARGIN.left},${SLOPE_MARGIN.top})`);
4677
5788
  const tooltip = createTooltip(container, palette, isDark);
4678
5789
  if (title) {
@@ -4683,7 +5794,7 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
4683
5794
  g.append("text").attr("x", x).attr("y", -15).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "18px").attr("font-weight", "600").text(period);
4684
5795
  g.append("line").attr("x1", x).attr("y1", 0).attr("x2", x).attr("y2", innerHeight).attr("stroke", mutedColor).attr("stroke-width", 1).attr("stroke-dasharray", "4,4");
4685
5796
  }
4686
- const lineGen = d3Shape.line().x((_d, i) => xScale(periods[i])).y((d) => yScale(d));
5797
+ const lineGen = d3Shape2.line().x((_d, i) => xScale(periods[i])).y((d) => yScale(d));
4687
5798
  data.forEach((item, idx) => {
4688
5799
  const color = item.color ?? colors[idx % colors.length];
4689
5800
  const firstVal = item.values[0];
@@ -4746,11 +5857,11 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
4746
5857
  const lineHeight = SLOPE_LABEL_FONT_SIZE * 1.2;
4747
5858
  const totalHeight = (lines.length - 1) * lineHeight;
4748
5859
  const startDy = -totalHeight / 2;
4749
- lines.forEach((line2, li) => {
5860
+ lines.forEach((line3, li) => {
4750
5861
  labelEl.append("tspan").attr("x", lastX + 10).attr(
4751
5862
  "dy",
4752
5863
  li === 0 ? `${startDy + SLOPE_LABEL_FONT_SIZE * 0.35}px` : `${lineHeight}px`
4753
- ).text(line2);
5864
+ ).text(line3);
4754
5865
  });
4755
5866
  }
4756
5867
  });
@@ -4845,7 +5956,7 @@ function orderArcNodes(links, order, groups) {
4845
5956
  }
4846
5957
  var ARC_MARGIN = { top: 60, right: 40, bottom: 60, left: 40 };
4847
5958
  function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, exportDims) {
4848
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
5959
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
4849
5960
  const { links, title, orientation, arcOrder, arcNodeGroups } = parsed;
4850
5961
  if (links.length === 0) return;
4851
5962
  const width = exportDims?.width ?? container.clientWidth;
@@ -4882,7 +5993,7 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
4882
5993
  const values = links.map((l) => l.value);
4883
5994
  const [minVal, maxVal] = d3Array.extent(values);
4884
5995
  const strokeScale = d3Scale.scaleLinear().domain([minVal, maxVal]).range([1.5, 6]);
4885
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5996
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
4886
5997
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
4887
5998
  if (title) {
4888
5999
  svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
@@ -4897,14 +6008,14 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
4897
6008
  function handleMouseEnter(hovered) {
4898
6009
  const connected = neighbors.get(hovered);
4899
6010
  g.selectAll(".arc-link").each(function() {
4900
- const el = d3Selection2.select(this);
6011
+ const el = d3Selection3.select(this);
4901
6012
  const src = el.attr("data-source");
4902
6013
  const tgt = el.attr("data-target");
4903
6014
  const isRelated = src === hovered || tgt === hovered;
4904
6015
  el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
4905
6016
  });
4906
6017
  g.selectAll(".arc-node").each(function() {
4907
- const el = d3Selection2.select(this);
6018
+ const el = d3Selection3.select(this);
4908
6019
  const name = el.attr("data-node");
4909
6020
  const isRelated = name === hovered || connected.has(name);
4910
6021
  el.attr("opacity", isRelated ? 1 : FADE_OPACITY);
@@ -4929,23 +6040,23 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
4929
6040
  const members = groupNodeSets.get(groupName);
4930
6041
  if (!members) return;
4931
6042
  g.selectAll(".arc-link").each(function() {
4932
- const el = d3Selection2.select(this);
6043
+ const el = d3Selection3.select(this);
4933
6044
  const isRelated = members.has(el.attr("data-source")) || members.has(el.attr("data-target"));
4934
6045
  el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
4935
6046
  });
4936
6047
  g.selectAll(".arc-node").each(function() {
4937
- const el = d3Selection2.select(this);
6048
+ const el = d3Selection3.select(this);
4938
6049
  el.attr("opacity", members.has(el.attr("data-node")) ? 1 : FADE_OPACITY);
4939
6050
  });
4940
6051
  g.selectAll(".arc-group-band").each(function() {
4941
- const el = d3Selection2.select(this);
6052
+ const el = d3Selection3.select(this);
4942
6053
  el.attr(
4943
6054
  "fill-opacity",
4944
6055
  el.attr("data-group") === groupName ? 0.18 : 0.03
4945
6056
  );
4946
6057
  });
4947
6058
  g.selectAll(".arc-group-label").each(function() {
4948
- const el = d3Selection2.select(this);
6059
+ const el = d3Selection3.select(this);
4949
6060
  el.attr("fill-opacity", el.attr("data-group") === groupName ? 1 : 0.2);
4950
6061
  });
4951
6062
  }
@@ -5148,7 +6259,14 @@ function computeTimeTicks(domainMin, domainMax, scale, boundaryStart, boundaryEn
5148
6259
  const firstYear = Math.ceil(domainMin);
5149
6260
  const lastYear = Math.floor(domainMax);
5150
6261
  if (lastYear >= firstYear + 1) {
5151
- for (let y = firstYear; y <= lastYear; y++) {
6262
+ const yearSpan = lastYear - firstYear;
6263
+ let step = 1;
6264
+ if (yearSpan > 80) step = 20;
6265
+ else if (yearSpan > 40) step = 10;
6266
+ else if (yearSpan > 20) step = 5;
6267
+ else if (yearSpan > 10) step = 2;
6268
+ const alignedFirst = Math.ceil(firstYear / step) * step;
6269
+ for (let y = alignedFirst; y <= lastYear; y += step) {
5152
6270
  ticks.push({ pos: scale(y), label: String(y) });
5153
6271
  }
5154
6272
  } else if (span > 0.25) {
@@ -5248,7 +6366,7 @@ function showEventDatesOnScale(g, scale, startDate, endDate, innerHeight, accent
5248
6366
  function hideEventDatesOnScale(g) {
5249
6367
  g.selectAll(".tl-event-date").remove();
5250
6368
  g.selectAll(".tl-scale-tick").each(function() {
5251
- const el = d3Selection2.select(this);
6369
+ const el = d3Selection3.select(this);
5252
6370
  const isDashed = el.attr("stroke-dasharray");
5253
6371
  el.attr("opacity", isDashed ? 0.15 : 0.4);
5254
6372
  });
@@ -5306,7 +6424,7 @@ function buildEraTooltipHtml(era) {
5306
6424
  return `<strong>${era.label}</strong><br>${formatDateLabel(era.startDate)} \u2192 ${formatDateLabel(era.endDate)}`;
5307
6425
  }
5308
6426
  function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims) {
5309
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
6427
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
5310
6428
  const {
5311
6429
  timelineEvents,
5312
6430
  timelineGroups,
@@ -5358,13 +6476,13 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5358
6476
  const FADE_OPACITY = 0.1;
5359
6477
  function fadeToGroup(g, groupName) {
5360
6478
  g.selectAll(".tl-event").each(function() {
5361
- const el = d3Selection2.select(this);
6479
+ const el = d3Selection3.select(this);
5362
6480
  const evGroup = el.attr("data-group");
5363
6481
  el.attr("opacity", evGroup === groupName ? 1 : FADE_OPACITY);
5364
6482
  });
5365
6483
  g.selectAll(".tl-legend-item, .tl-lane-header").each(
5366
6484
  function() {
5367
- const el = d3Selection2.select(this);
6485
+ const el = d3Selection3.select(this);
5368
6486
  const name = el.attr("data-group");
5369
6487
  el.attr("opacity", name === groupName ? 1 : FADE_OPACITY);
5370
6488
  }
@@ -5376,7 +6494,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5376
6494
  }
5377
6495
  function fadeToEra(g, eraStart, eraEnd) {
5378
6496
  g.selectAll(".tl-event").each(function() {
5379
- const el = d3Selection2.select(this);
6497
+ const el = d3Selection3.select(this);
5380
6498
  const date = parseFloat(el.attr("data-date"));
5381
6499
  const endDate = el.attr("data-end-date");
5382
6500
  const evEnd = endDate ? parseFloat(endDate) : date;
@@ -5388,14 +6506,14 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5388
6506
  FADE_OPACITY
5389
6507
  );
5390
6508
  g.selectAll(".tl-era").each(function() {
5391
- const el = d3Selection2.select(this);
6509
+ const el = d3Selection3.select(this);
5392
6510
  const s = parseFloat(el.attr("data-era-start"));
5393
6511
  const e = parseFloat(el.attr("data-era-end"));
5394
6512
  const isSelf = s === eraStart && e === eraEnd;
5395
6513
  el.attr("opacity", isSelf ? 1 : FADE_OPACITY);
5396
6514
  });
5397
6515
  g.selectAll(".tl-marker").each(function() {
5398
- const el = d3Selection2.select(this);
6516
+ const el = d3Selection3.select(this);
5399
6517
  const date = parseFloat(el.attr("data-marker-date"));
5400
6518
  const inside = date >= eraStart && date <= eraEnd;
5401
6519
  el.attr("opacity", inside ? 1 : FADE_OPACITY);
@@ -5427,7 +6545,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5427
6545
  const innerHeight = height - margin.top - margin.bottom;
5428
6546
  const laneWidth = innerWidth / laneCount;
5429
6547
  const yScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
5430
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6548
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5431
6549
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
5432
6550
  if (title) {
5433
6551
  svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
@@ -5521,7 +6639,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5521
6639
  const axisX = 20;
5522
6640
  const yScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
5523
6641
  const sorted = timelineEvents.slice().sort((a, b) => parseTimelineDate(a.date) - parseTimelineDate(b.date));
5524
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6642
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5525
6643
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
5526
6644
  if (title) {
5527
6645
  svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
@@ -5644,7 +6762,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5644
6762
  const totalGaps = (lanes.length - 1) * GROUP_GAP;
5645
6763
  const rowH = Math.min(28, (innerHeight - totalGaps) / totalEventRows);
5646
6764
  const xScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
5647
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6765
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5648
6766
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
5649
6767
  if (title) {
5650
6768
  svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
@@ -5748,7 +6866,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5748
6866
  if (ev.uncertain) {
5749
6867
  const gradientId = `uncertain-${ev.lineNumber}`;
5750
6868
  const defs = svg.select("defs").node() || svg.append("defs").node();
5751
- d3Selection2.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
6869
+ d3Selection3.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
5752
6870
  { offset: "0%", opacity: 1 },
5753
6871
  { offset: "80%", opacity: 1 },
5754
6872
  { offset: "100%", opacity: 0 }
@@ -5789,7 +6907,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5789
6907
  const innerHeight = height - margin.top - margin.bottom;
5790
6908
  const rowH = Math.min(28, innerHeight / sorted.length);
5791
6909
  const xScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
5792
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6910
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5793
6911
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
5794
6912
  if (title) {
5795
6913
  svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
@@ -5887,7 +7005,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5887
7005
  if (ev.uncertain) {
5888
7006
  const gradientId = `uncertain-ts-${ev.lineNumber}`;
5889
7007
  const defs = svg.select("defs").node() || svg.append("defs").node();
5890
- d3Selection2.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
7008
+ d3Selection3.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
5891
7009
  { offset: "0%", opacity: 1 },
5892
7010
  { offset: "80%", opacity: 1 },
5893
7011
  { offset: "100%", opacity: 0 }
@@ -5920,7 +7038,7 @@ function getRotateFn(mode) {
5920
7038
  return () => 0;
5921
7039
  }
5922
7040
  function renderWordCloud(container, parsed, palette, _isDark, onClickItem, exportDims) {
5923
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
7041
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
5924
7042
  const { words, title, cloudOptions } = parsed;
5925
7043
  if (words.length === 0) return;
5926
7044
  const width = exportDims?.width ?? container.clientWidth;
@@ -5941,7 +7059,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
5941
7059
  return minSize + t * (maxSize - minSize);
5942
7060
  };
5943
7061
  const rotateFn = getRotateFn(cloudOptions.rotate);
5944
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7062
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5945
7063
  if (title) {
5946
7064
  svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
5947
7065
  }
@@ -5964,7 +7082,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
5964
7082
  }
5965
7083
  function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
5966
7084
  return new Promise((resolve) => {
5967
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
7085
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
5968
7086
  const { words, title, cloudOptions } = parsed;
5969
7087
  if (words.length === 0) {
5970
7088
  resolve();
@@ -5991,7 +7109,7 @@ function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
5991
7109
  return minSize + t * (maxSize - minSize);
5992
7110
  };
5993
7111
  const rotateFn = getRotateFn(cloudOptions.rotate);
5994
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7112
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5995
7113
  if (title) {
5996
7114
  svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
5997
7115
  }
@@ -6135,7 +7253,7 @@ function circlePathD(cx, cy, r) {
6135
7253
  return `M${cx - r},${cy} A${r},${r} 0 1,0 ${cx + r},${cy} A${r},${r} 0 1,0 ${cx - r},${cy} Z`;
6136
7254
  }
6137
7255
  function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims) {
6138
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
7256
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
6139
7257
  const { vennSets, vennOverlaps, vennShowValues, title } = parsed;
6140
7258
  if (vennSets.length < 2) return;
6141
7259
  const width = exportDims?.width ?? container.clientWidth;
@@ -6200,7 +7318,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
6200
7318
  const setColors = vennSets.map(
6201
7319
  (s, i) => s.color ?? colors[i % colors.length]
6202
7320
  );
6203
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7321
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6204
7322
  const tooltip = createTooltip(container, palette, isDark);
6205
7323
  if (title) {
6206
7324
  svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
@@ -6346,7 +7464,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
6346
7464
  });
6347
7465
  }
6348
7466
  function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportDims) {
6349
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
7467
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
6350
7468
  const {
6351
7469
  title,
6352
7470
  quadrantLabels,
@@ -6378,7 +7496,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6378
7496
  const chartHeight = height - margin.top - margin.bottom;
6379
7497
  const xScale = d3Scale.scaleLinear().domain([0, 1]).range([0, chartWidth]);
6380
7498
  const yScale = d3Scale.scaleLinear().domain([0, 1]).range([chartHeight, 0]);
6381
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7499
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6382
7500
  const tooltip = createTooltip(container, palette, isDark);
6383
7501
  if (title) {
6384
7502
  const titleText = svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").style(
@@ -6387,9 +7505,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6387
7505
  ).text(title);
6388
7506
  if (onClickItem && quadrantTitleLineNumber) {
6389
7507
  titleText.on("click", () => onClickItem(quadrantTitleLineNumber)).on("mouseenter", function() {
6390
- d3Selection2.select(this).attr("opacity", 0.7);
7508
+ d3Selection3.select(this).attr("opacity", 0.7);
6391
7509
  }).on("mouseleave", function() {
6392
- d3Selection2.select(this).attr("opacity", 1);
7510
+ d3Selection3.select(this).attr("opacity", 1);
6393
7511
  });
6394
7512
  }
6395
7513
  }
@@ -6472,9 +7590,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6472
7590
  quadrantLabelTexts.on("click", (_, d) => {
6473
7591
  if (d.label?.lineNumber) onClickItem(d.label.lineNumber);
6474
7592
  }).on("mouseenter", function() {
6475
- d3Selection2.select(this).attr("opacity", 0.7);
7593
+ d3Selection3.select(this).attr("opacity", 0.7);
6476
7594
  }).on("mouseleave", function() {
6477
- d3Selection2.select(this).attr("opacity", 1);
7595
+ d3Selection3.select(this).attr("opacity", 1);
6478
7596
  });
6479
7597
  }
6480
7598
  if (quadrantXAxis) {
@@ -6489,9 +7607,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6489
7607
  if (onClickItem && quadrantXAxisLineNumber) {
6490
7608
  [xLowLabel, xHighLabel].forEach((label) => {
6491
7609
  label.on("click", () => onClickItem(quadrantXAxisLineNumber)).on("mouseenter", function() {
6492
- d3Selection2.select(this).attr("opacity", 0.7);
7610
+ d3Selection3.select(this).attr("opacity", 0.7);
6493
7611
  }).on("mouseleave", function() {
6494
- d3Selection2.select(this).attr("opacity", 1);
7612
+ d3Selection3.select(this).attr("opacity", 1);
6495
7613
  });
6496
7614
  });
6497
7615
  }
@@ -6510,9 +7628,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6510
7628
  if (onClickItem && quadrantYAxisLineNumber) {
6511
7629
  [yLowLabel, yHighLabel].forEach((label) => {
6512
7630
  label.on("click", () => onClickItem(quadrantYAxisLineNumber)).on("mouseenter", function() {
6513
- d3Selection2.select(this).attr("opacity", 0.7);
7631
+ d3Selection3.select(this).attr("opacity", 0.7);
6514
7632
  }).on("mouseleave", function() {
6515
- d3Selection2.select(this).attr("opacity", 1);
7633
+ d3Selection3.select(this).attr("opacity", 1);
6516
7634
  });
6517
7635
  });
6518
7636
  }
@@ -6560,7 +7678,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6560
7678
  pointsG.selectAll("g.point-group").each(function(_2, i) {
6561
7679
  const pt = quadrantPoints[i];
6562
7680
  const ptQuad = getPointQuadrant(pt.x, pt.y);
6563
- d3Selection2.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
7681
+ d3Selection3.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
6564
7682
  });
6565
7683
  }).on("mouseleave", () => {
6566
7684
  quadrantRects.attr("opacity", 1);
@@ -6575,6 +7693,43 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6575
7693
  var EXPORT_WIDTH = 1200;
6576
7694
  var EXPORT_HEIGHT = 800;
6577
7695
  async function renderD3ForExport(content, theme, palette) {
7696
+ const { parseDgmoChartType: parseDgmoChartType2 } = await Promise.resolve().then(() => (init_dgmo_router(), dgmo_router_exports));
7697
+ const detectedType = parseDgmoChartType2(content);
7698
+ if (detectedType === "flowchart") {
7699
+ const { parseFlowchart: parseFlowchart2 } = await Promise.resolve().then(() => (init_flowchart_parser(), flowchart_parser_exports));
7700
+ const { layoutGraph: layoutGraph2 } = await Promise.resolve().then(() => (init_layout(), layout_exports));
7701
+ const { renderFlowchart: renderFlowchart2 } = await Promise.resolve().then(() => (init_flowchart_renderer(), flowchart_renderer_exports));
7702
+ const isDark2 = theme === "dark";
7703
+ const { getPalette: getPalette3 } = await Promise.resolve().then(() => (init_palettes(), palettes_exports));
7704
+ const effectivePalette2 = palette ?? (isDark2 ? getPalette3("nord").dark : getPalette3("nord").light);
7705
+ const fcParsed = parseFlowchart2(content, effectivePalette2);
7706
+ if (fcParsed.error || fcParsed.nodes.length === 0) return "";
7707
+ const layout = layoutGraph2(fcParsed);
7708
+ const container2 = document.createElement("div");
7709
+ container2.style.width = `${EXPORT_WIDTH}px`;
7710
+ container2.style.height = `${EXPORT_HEIGHT}px`;
7711
+ container2.style.position = "absolute";
7712
+ container2.style.left = "-9999px";
7713
+ document.body.appendChild(container2);
7714
+ try {
7715
+ renderFlowchart2(container2, fcParsed, layout, effectivePalette2, isDark2, void 0, {
7716
+ width: EXPORT_WIDTH,
7717
+ height: EXPORT_HEIGHT
7718
+ });
7719
+ const svgEl = container2.querySelector("svg");
7720
+ if (!svgEl) return "";
7721
+ if (theme === "transparent") {
7722
+ svgEl.style.background = "none";
7723
+ } else if (!svgEl.style.background) {
7724
+ svgEl.style.background = effectivePalette2.bg;
7725
+ }
7726
+ svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
7727
+ svgEl.style.fontFamily = FONT_FAMILY;
7728
+ return svgEl.outerHTML;
7729
+ } finally {
7730
+ document.body.removeChild(container2);
7731
+ }
7732
+ }
6578
7733
  const parsed = parseD3(content, palette);
6579
7734
  if (parsed.error && parsed.type !== "sequence") {
6580
7735
  const looksLikeSequence2 = /->|~>|<-/.test(content);
@@ -6669,17 +7824,17 @@ function parseQuadrant(content) {
6669
7824
  };
6670
7825
  const lines = content.split("\n");
6671
7826
  for (let i = 0; i < lines.length; i++) {
6672
- const line2 = lines[i].trim();
7827
+ const line3 = lines[i].trim();
6673
7828
  const lineNumber = i + 1;
6674
- if (!line2 || line2.startsWith("#") || line2.startsWith("//")) continue;
6675
- if (/^chart\s*:/i.test(line2)) continue;
6676
- const titleMatch = line2.match(/^title\s*:\s*(.+)/i);
7829
+ if (!line3 || line3.startsWith("#") || line3.startsWith("//")) continue;
7830
+ if (/^chart\s*:/i.test(line3)) continue;
7831
+ const titleMatch = line3.match(/^title\s*:\s*(.+)/i);
6677
7832
  if (titleMatch) {
6678
7833
  result.title = titleMatch[1].trim();
6679
7834
  result.titleLineNumber = lineNumber;
6680
7835
  continue;
6681
7836
  }
6682
- const xMatch = line2.match(/^x-axis\s*:\s*(.+)/i);
7837
+ const xMatch = line3.match(/^x-axis\s*:\s*(.+)/i);
6683
7838
  if (xMatch) {
6684
7839
  const parts = xMatch[1].split(",").map((s) => s.trim());
6685
7840
  if (parts.length >= 2) {
@@ -6688,7 +7843,7 @@ function parseQuadrant(content) {
6688
7843
  }
6689
7844
  continue;
6690
7845
  }
6691
- const yMatch = line2.match(/^y-axis\s*:\s*(.+)/i);
7846
+ const yMatch = line3.match(/^y-axis\s*:\s*(.+)/i);
6692
7847
  if (yMatch) {
6693
7848
  const parts = yMatch[1].split(",").map((s) => s.trim());
6694
7849
  if (parts.length >= 2) {
@@ -6697,7 +7852,7 @@ function parseQuadrant(content) {
6697
7852
  }
6698
7853
  continue;
6699
7854
  }
6700
- const posMatch = line2.match(
7855
+ const posMatch = line3.match(
6701
7856
  /^(top-right|top-left|bottom-left|bottom-right)\s*:\s*(.+)/i
6702
7857
  );
6703
7858
  if (posMatch) {
@@ -6718,7 +7873,7 @@ function parseQuadrant(content) {
6718
7873
  }
6719
7874
  continue;
6720
7875
  }
6721
- const pointMatch = line2.match(DATA_POINT_RE);
7876
+ const pointMatch = line3.match(DATA_POINT_RE);
6722
7877
  if (pointMatch) {
6723
7878
  const key = pointMatch[1].trim().toLowerCase();
6724
7879
  if (!QUADRANT_POSITIONS.has(key)) {
@@ -6792,6 +7947,9 @@ function buildMermaidQuadrant(parsed, options = {}) {
6792
7947
  }
6793
7948
 
6794
7949
  // src/index.ts
7950
+ init_flowchart_parser();
7951
+ init_layout();
7952
+ init_flowchart_renderer();
6795
7953
  init_renderer();
6796
7954
  init_colors();
6797
7955
  init_palettes();
@@ -6825,7 +7983,10 @@ export {
6825
7983
  hslToHex,
6826
7984
  inferParticipantType,
6827
7985
  isSequenceBlock,
7986
+ isSequenceNote,
6828
7987
  isValidHex,
7988
+ layoutGraph,
7989
+ looksLikeFlowchart,
6829
7990
  looksLikeSequence,
6830
7991
  mute,
6831
7992
  nord,
@@ -6836,6 +7997,7 @@ export {
6836
7997
  parseD3,
6837
7998
  parseDgmoChartType,
6838
7999
  parseEChart,
8000
+ parseFlowchart,
6839
8001
  parseQuadrant,
6840
8002
  parseSequenceDgmo,
6841
8003
  parseTimelineDate,
@@ -6843,6 +8005,8 @@ export {
6843
8005
  renderArcDiagram,
6844
8006
  renderD3ForExport,
6845
8007
  renderEChartsForExport,
8008
+ renderFlowchart,
8009
+ renderFlowchartForExport,
6846
8010
  renderQuadrant,
6847
8011
  renderSequenceDiagram,
6848
8012
  renderSlopeChart,