@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.cjs CHANGED
@@ -169,6 +169,7 @@ var init_participant_inference = __esm({
169
169
  var parser_exports = {};
170
170
  __export(parser_exports, {
171
171
  isSequenceBlock: () => isSequenceBlock,
172
+ isSequenceNote: () => isSequenceNote,
172
173
  isSequenceSection: () => isSequenceSection,
173
174
  looksLikeSequence: () => looksLikeSequence,
174
175
  parseSequenceDgmo: () => parseSequenceDgmo
@@ -179,6 +180,9 @@ function isSequenceBlock(el) {
179
180
  function isSequenceSection(el) {
180
181
  return "kind" in el && el.kind === "section";
181
182
  }
183
+ function isSequenceNote(el) {
184
+ return "kind" in el && el.kind === "note";
185
+ }
182
186
  function parseReturnLabel(rawLabel) {
183
187
  if (!rawLabel) return { label: "" };
184
188
  const arrowReturn = rawLabel.match(ARROW_RETURN_PATTERN);
@@ -189,19 +193,22 @@ function parseReturnLabel(rawLabel) {
189
193
  if (umlReturn) {
190
194
  return { label: umlReturn[1].trim(), returnLabel: umlReturn[2].trim() };
191
195
  }
192
- const lastSep = rawLabel.lastIndexOf(" : ");
193
- if (lastSep > 0) {
194
- const reqPart = rawLabel.substring(0, lastSep).trim();
195
- const resPart = rawLabel.substring(lastSep + 3).trim();
196
- if (reqPart && resPart) {
197
- return { label: reqPart, returnLabel: resPart };
196
+ const lastColon = rawLabel.lastIndexOf(":");
197
+ if (lastColon > 0 && lastColon < rawLabel.length - 1) {
198
+ const afterColon = rawLabel.substring(lastColon + 1);
199
+ if (!afterColon.startsWith("//")) {
200
+ const reqPart = rawLabel.substring(0, lastColon).trim();
201
+ const resPart = afterColon.trim();
202
+ if (reqPart && resPart) {
203
+ return { label: reqPart, returnLabel: resPart };
204
+ }
198
205
  }
199
206
  }
200
207
  return { label: rawLabel };
201
208
  }
202
- function measureIndent(line2) {
209
+ function measureIndent(line3) {
203
210
  let indent = 0;
204
- for (const ch of line2) {
211
+ for (const ch of line3) {
205
212
  if (ch === " ") indent++;
206
213
  else if (ch === " ") indent += 4;
207
214
  else break;
@@ -225,13 +232,17 @@ function parseSequenceDgmo(content) {
225
232
  }
226
233
  const lines = content.split("\n");
227
234
  let hasExplicitChart = false;
235
+ let contentStarted = false;
228
236
  let activeGroup = null;
237
+ const participantGroupMap = /* @__PURE__ */ new Map();
229
238
  const blockStack = [];
230
239
  const currentContainer = () => {
231
240
  if (blockStack.length === 0) return result.elements;
232
241
  const top = blockStack[blockStack.length - 1];
242
+ if (top.activeElseIfBranch) return top.activeElseIfBranch.children;
233
243
  return top.inElse ? top.block.elseChildren : top.block.children;
234
244
  };
245
+ let lastMsgFrom = null;
235
246
  for (let i = 0; i < lines.length; i++) {
236
247
  const raw = lines[i];
237
248
  const trimmed = raw.trim();
@@ -242,9 +253,15 @@ function parseSequenceDgmo(content) {
242
253
  }
243
254
  const groupMatch = trimmed.match(GROUP_HEADING_PATTERN);
244
255
  if (groupMatch) {
256
+ const groupColor = groupMatch[2]?.trim();
257
+ if (groupColor && groupColor.startsWith("#")) {
258
+ result.error = `Line ${lineNumber}: Use a named color instead of hex (e.g., blue, red, teal)`;
259
+ return result;
260
+ }
261
+ contentStarted = true;
245
262
  activeGroup = {
246
- name: groupMatch[1],
247
- color: groupMatch[2] || void 0,
263
+ name: groupMatch[1].trim(),
264
+ color: groupColor || void 0,
248
265
  participantIds: [],
249
266
  lineNumber
250
267
  };
@@ -254,7 +271,11 @@ function parseSequenceDgmo(content) {
254
271
  if (activeGroup && measureIndent(raw) === 0) {
255
272
  activeGroup = null;
256
273
  }
257
- if (trimmed.startsWith("#") || trimmed.startsWith("//")) continue;
274
+ if (trimmed.startsWith("//")) continue;
275
+ if (trimmed.startsWith("#") && !trimmed.startsWith("##")) {
276
+ result.error = `Line ${lineNumber}: Use // for comments. # is reserved for group headings (##)`;
277
+ return result;
278
+ }
258
279
  const sectionMatch = trimmed.match(SECTION_PATTERN);
259
280
  if (sectionMatch) {
260
281
  const sectionIndent = measureIndent(raw);
@@ -264,11 +285,16 @@ function parseSequenceDgmo(content) {
264
285
  blockStack.pop();
265
286
  }
266
287
  const labelRaw = sectionMatch[1].trim();
267
- const colorMatch = labelRaw.match(/^(.+?)\((\w+)\)$/);
288
+ const colorMatch = labelRaw.match(/^(.+?)\(([^)]+)\)$/);
289
+ if (colorMatch && colorMatch[2].trim().startsWith("#")) {
290
+ result.error = `Line ${lineNumber}: Use a named color instead of hex (e.g., blue, red, teal)`;
291
+ return result;
292
+ }
293
+ contentStarted = true;
268
294
  const section = {
269
295
  kind: "section",
270
296
  label: colorMatch ? colorMatch[1].trim() : labelRaw,
271
- color: colorMatch ? colorMatch[2] : void 0,
297
+ color: colorMatch ? colorMatch[2].trim() : void 0,
272
298
  lineNumber
273
299
  };
274
300
  result.sections.push(section);
@@ -278,24 +304,32 @@ function parseSequenceDgmo(content) {
278
304
  const colonIndex = trimmed.indexOf(":");
279
305
  if (colonIndex > 0 && !trimmed.includes("->") && !trimmed.includes("~>")) {
280
306
  const key = trimmed.substring(0, colonIndex).trim().toLowerCase();
281
- const value = trimmed.substring(colonIndex + 1).trim();
282
- if (key === "chart") {
283
- hasExplicitChart = true;
284
- if (value.toLowerCase() !== "sequence") {
285
- result.error = `Expected chart type "sequence", got "${value}"`;
307
+ if (key === "note" || key.startsWith("note ")) {
308
+ } else {
309
+ const value = trimmed.substring(colonIndex + 1).trim();
310
+ if (key === "chart") {
311
+ hasExplicitChart = true;
312
+ if (value.toLowerCase() !== "sequence") {
313
+ result.error = `Expected chart type "sequence", got "${value}"`;
314
+ return result;
315
+ }
316
+ continue;
317
+ }
318
+ if (contentStarted) {
319
+ result.error = `Line ${lineNumber}: Options like '${key}: ${value}' must appear before the first message or declaration`;
286
320
  return result;
287
321
  }
322
+ if (key === "title") {
323
+ result.title = value;
324
+ continue;
325
+ }
326
+ result.options[key] = value;
288
327
  continue;
289
328
  }
290
- if (key === "title") {
291
- result.title = value;
292
- continue;
293
- }
294
- result.options[key] = value;
295
- continue;
296
329
  }
297
330
  const isAMatch = trimmed.match(IS_A_PATTERN);
298
331
  if (isAMatch) {
332
+ contentStarted = true;
299
333
  const id = isAMatch[1];
300
334
  const typeStr = isAMatch[2].toLowerCase();
301
335
  const remainder = isAMatch[3]?.trim() || "";
@@ -318,12 +352,19 @@ function parseSequenceDgmo(content) {
318
352
  });
319
353
  }
320
354
  if (activeGroup && !activeGroup.participantIds.includes(id)) {
355
+ const existingGroup = participantGroupMap.get(id);
356
+ if (existingGroup) {
357
+ result.error = `Line ${lineNumber}: Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`;
358
+ return result;
359
+ }
321
360
  activeGroup.participantIds.push(id);
361
+ participantGroupMap.set(id, activeGroup.name);
322
362
  }
323
363
  continue;
324
364
  }
325
365
  const posOnlyMatch = trimmed.match(POSITION_ONLY_PATTERN);
326
366
  if (posOnlyMatch) {
367
+ contentStarted = true;
327
368
  const id = posOnlyMatch[1];
328
369
  const position = parseInt(posOnlyMatch[2], 10);
329
370
  if (!result.participants.some((p) => p.id === id)) {
@@ -336,11 +377,18 @@ function parseSequenceDgmo(content) {
336
377
  });
337
378
  }
338
379
  if (activeGroup && !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
+ }
339
385
  activeGroup.participantIds.push(id);
386
+ participantGroupMap.set(id, activeGroup.name);
340
387
  }
341
388
  continue;
342
389
  }
343
390
  if (activeGroup && measureIndent(raw) > 0 && /^\S+$/.test(trimmed)) {
391
+ contentStarted = true;
344
392
  const id = trimmed;
345
393
  if (!result.participants.some((p) => p.id === id)) {
346
394
  result.participants.push({
@@ -351,7 +399,13 @@ function parseSequenceDgmo(content) {
351
399
  });
352
400
  }
353
401
  if (!activeGroup.participantIds.includes(id)) {
402
+ const existingGroup = participantGroupMap.get(id);
403
+ if (existingGroup) {
404
+ result.error = `Line ${lineNumber}: Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`;
405
+ return result;
406
+ }
354
407
  activeGroup.participantIds.push(id);
408
+ participantGroupMap.set(id, activeGroup.name);
355
409
  }
356
410
  continue;
357
411
  }
@@ -359,28 +413,31 @@ function parseSequenceDgmo(content) {
359
413
  while (blockStack.length > 0) {
360
414
  const top = blockStack[blockStack.length - 1];
361
415
  if (indent > top.indent) break;
362
- if (indent === top.indent && trimmed.toLowerCase() === "else" && top.block.type === "if")
363
- break;
416
+ if (indent === top.indent && (top.block.type === "if" || top.block.type === "parallel")) {
417
+ const lower = trimmed.toLowerCase();
418
+ if (lower === "else" || lower.startsWith("else if ")) break;
419
+ }
364
420
  blockStack.pop();
365
421
  }
366
- let isAsync = false;
367
- let arrowLine = trimmed;
368
422
  const asyncPrefixMatch = trimmed.match(/^async\s+(.+)$/i);
369
- if (asyncPrefixMatch) {
370
- isAsync = true;
371
- arrowLine = asyncPrefixMatch[1];
423
+ if (asyncPrefixMatch && ARROW_PATTERN.test(asyncPrefixMatch[1])) {
424
+ result.error = `Line ${lineNumber}: Use ~> for async messages: A ~> B: message`;
425
+ return result;
372
426
  }
373
- const asyncArrowMatch = arrowLine.match(
427
+ let isAsync = false;
428
+ const asyncArrowMatch = trimmed.match(
374
429
  /^(\S+)\s*~>\s*([^\s:]+)\s*(?::\s*(.+))?$/
375
430
  );
376
- const syncArrowMatch = arrowLine.match(
431
+ const syncArrowMatch = trimmed.match(
377
432
  /^(\S+)\s*->\s*([^\s:]+)\s*(?::\s*(.+))?$/
378
433
  );
379
434
  const arrowMatch = asyncArrowMatch || syncArrowMatch;
380
435
  if (asyncArrowMatch) isAsync = true;
381
436
  if (arrowMatch) {
437
+ contentStarted = true;
382
438
  const from = arrowMatch[1];
383
439
  const to = arrowMatch[2];
440
+ lastMsgFrom = from;
384
441
  const rawLabel = arrowMatch[3]?.trim() || "";
385
442
  const { label, returnLabel } = isAsync ? { label: rawLabel, returnLabel: void 0 } : parseReturnLabel(rawLabel);
386
443
  const msg = {
@@ -413,6 +470,7 @@ function parseSequenceDgmo(content) {
413
470
  }
414
471
  const ifMatch = trimmed.match(/^if\s+(.+)$/i);
415
472
  if (ifMatch) {
473
+ contentStarted = true;
416
474
  const block = {
417
475
  kind: "block",
418
476
  type: "if",
@@ -427,6 +485,7 @@ function parseSequenceDgmo(content) {
427
485
  }
428
486
  const loopMatch = trimmed.match(/^loop\s+(.+)$/i);
429
487
  if (loopMatch) {
488
+ contentStarted = true;
430
489
  const block = {
431
490
  kind: "block",
432
491
  type: "loop",
@@ -441,6 +500,7 @@ function parseSequenceDgmo(content) {
441
500
  }
442
501
  const parallelMatch = trimmed.match(/^parallel(?:\s+(.+))?$/i);
443
502
  if (parallelMatch) {
503
+ contentStarted = true;
444
504
  const block = {
445
505
  kind: "block",
446
506
  type: "parallel",
@@ -453,15 +513,97 @@ function parseSequenceDgmo(content) {
453
513
  blockStack.push({ block, indent, inElse: false });
454
514
  continue;
455
515
  }
516
+ const elseIfMatch = trimmed.match(/^else\s+if\s+(.+)$/i);
517
+ if (elseIfMatch) {
518
+ if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent) {
519
+ const top = blockStack[blockStack.length - 1];
520
+ if (top.block.type === "parallel") {
521
+ result.error = `Line ${lineNumber}: parallel blocks don't support else if \u2014 list all concurrent messages directly inside the block`;
522
+ return result;
523
+ }
524
+ if (top.block.type === "if") {
525
+ const branch = { label: elseIfMatch[1].trim(), children: [] };
526
+ if (!top.block.elseIfBranches) top.block.elseIfBranches = [];
527
+ top.block.elseIfBranches.push(branch);
528
+ top.activeElseIfBranch = branch;
529
+ top.inElse = false;
530
+ }
531
+ }
532
+ continue;
533
+ }
456
534
  if (trimmed.toLowerCase() === "else") {
457
- if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent && blockStack[blockStack.length - 1].block.type === "if") {
458
- blockStack[blockStack.length - 1].inElse = true;
535
+ if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent) {
536
+ const top = blockStack[blockStack.length - 1];
537
+ if (top.block.type === "parallel") {
538
+ result.error = `Line ${lineNumber}: parallel blocks don't support else \u2014 list all concurrent messages directly inside the block`;
539
+ return result;
540
+ }
541
+ if (top.block.type === "if") {
542
+ top.inElse = true;
543
+ top.activeElseIfBranch = void 0;
544
+ }
545
+ }
546
+ continue;
547
+ }
548
+ const noteSingleMatch = trimmed.match(NOTE_SINGLE);
549
+ if (noteSingleMatch) {
550
+ const notePosition = noteSingleMatch[1]?.toLowerCase() || "right";
551
+ let noteParticipant = noteSingleMatch[2] || null;
552
+ if (!noteParticipant) {
553
+ if (!lastMsgFrom) continue;
554
+ noteParticipant = lastMsgFrom;
459
555
  }
556
+ if (!result.participants.some((p) => p.id === noteParticipant)) {
557
+ continue;
558
+ }
559
+ const note = {
560
+ kind: "note",
561
+ text: noteSingleMatch[3].trim(),
562
+ position: notePosition,
563
+ participantId: noteParticipant,
564
+ lineNumber,
565
+ endLineNumber: lineNumber
566
+ };
567
+ currentContainer().push(note);
568
+ continue;
569
+ }
570
+ const noteMultiMatch = trimmed.match(NOTE_MULTI);
571
+ if (noteMultiMatch) {
572
+ const notePosition = noteMultiMatch[1]?.toLowerCase() || "right";
573
+ let noteParticipant = noteMultiMatch[2] || null;
574
+ if (!noteParticipant) {
575
+ if (!lastMsgFrom) continue;
576
+ noteParticipant = lastMsgFrom;
577
+ }
578
+ if (!result.participants.some((p) => p.id === noteParticipant)) {
579
+ continue;
580
+ }
581
+ const noteLines = [];
582
+ while (i + 1 < lines.length) {
583
+ const nextRaw = lines[i + 1];
584
+ const nextTrimmed = nextRaw.trim();
585
+ if (!nextTrimmed) break;
586
+ const nextIndent = measureIndent(nextRaw);
587
+ if (nextIndent <= indent) break;
588
+ noteLines.push(nextTrimmed);
589
+ i++;
590
+ }
591
+ if (noteLines.length === 0) continue;
592
+ const note = {
593
+ kind: "note",
594
+ text: noteLines.join("\n"),
595
+ position: notePosition,
596
+ participantId: noteParticipant,
597
+ lineNumber,
598
+ endLineNumber: i + 1
599
+ // i has advanced past the body lines (1-based)
600
+ };
601
+ currentContainer().push(note);
460
602
  continue;
461
603
  }
462
604
  }
463
605
  if (!hasExplicitChart && result.messages.length === 0) {
464
- const hasArrows = lines.some((line2) => ARROW_PATTERN.test(line2.trim()));
606
+ const hasArrows = lines.some((line3) => ARROW_PATTERN.test(line3.trim()));
465
607
  if (!hasArrows) {
466
608
  result.error = 'No "chart: sequence" header and no sequence content detected';
467
609
  return result;
@@ -472,13 +614,13 @@ function parseSequenceDgmo(content) {
472
614
  function looksLikeSequence(content) {
473
615
  if (!content) return false;
474
616
  const lines = content.split("\n");
475
- return lines.some((line2) => {
476
- const trimmed = line2.trim();
477
- if (trimmed.startsWith("#") || trimmed.startsWith("//")) return false;
617
+ return lines.some((line3) => {
618
+ const trimmed = line3.trim();
619
+ if (trimmed.startsWith("//")) return false;
478
620
  return ARROW_PATTERN.test(trimmed);
479
621
  });
480
622
  }
481
- var VALID_PARTICIPANT_TYPES, IS_A_PATTERN, POSITION_ONLY_PATTERN, GROUP_HEADING_PATTERN, SECTION_PATTERN, ARROW_PATTERN, ARROW_RETURN_PATTERN, UML_RETURN_PATTERN;
623
+ 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;
482
624
  var init_parser = __esm({
483
625
  "src/sequence/parser.ts"() {
484
626
  "use strict";
@@ -496,11 +638,13 @@ var init_parser = __esm({
496
638
  ]);
497
639
  IS_A_PATTERN = /^(\S+)\s+is\s+an?\s+(\w+)(?:\s+(.+))?$/i;
498
640
  POSITION_ONLY_PATTERN = /^(\S+)\s+position\s+(-?\d+)$/i;
499
- GROUP_HEADING_PATTERN = /^##\s+(\S+?)(?:\((\w+)\))?$/;
500
- SECTION_PATTERN = /^==\s+(.+?)\s*==$/;
641
+ GROUP_HEADING_PATTERN = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
642
+ SECTION_PATTERN = /^==\s+(.+?)(?:\s*==)?\s*$/;
501
643
  ARROW_PATTERN = /\S+\s*(?:->|~>)\s*\S+/;
502
644
  ARROW_RETURN_PATTERN = /^(.+?)\s*<-\s*(.+)$/;
503
645
  UML_RETURN_PATTERN = /^(\w+\([^)]*\))\s*:\s*(.+)$/;
646
+ NOTE_SINGLE = /^note(?:\s+(right|left)\s+of\s+(\S+))?\s*:\s*(.+)$/i;
647
+ NOTE_MULTI = /^note(?:\s+(right|left)\s+of\s+([^\s:]+))?\s*:?\s*$/i;
504
648
  }
505
649
  });
506
650
 
@@ -579,6 +723,369 @@ var init_colors = __esm({
579
723
  }
580
724
  });
581
725
 
726
+ // src/graph/flowchart-parser.ts
727
+ var flowchart_parser_exports = {};
728
+ __export(flowchart_parser_exports, {
729
+ looksLikeFlowchart: () => looksLikeFlowchart,
730
+ parseFlowchart: () => parseFlowchart
731
+ });
732
+ function measureIndent2(line3) {
733
+ let indent = 0;
734
+ for (const ch of line3) {
735
+ if (ch === " ") indent++;
736
+ else if (ch === " ") indent += 4;
737
+ else break;
738
+ }
739
+ return indent;
740
+ }
741
+ function nodeId(shape, label) {
742
+ return `${shape}:${label.toLowerCase().trim()}`;
743
+ }
744
+ function extractColor(label, palette) {
745
+ const m = label.match(COLOR_SUFFIX_RE);
746
+ if (!m) return { label };
747
+ const colorName = m[1].trim();
748
+ return {
749
+ label: label.substring(0, m.index).trim(),
750
+ color: resolveColor(colorName, palette)
751
+ };
752
+ }
753
+ function parseNodeRef(text, palette) {
754
+ const t = text.trim();
755
+ if (!t) return null;
756
+ let m = t.match(/^\[\[([^\]]+)\]\]$/);
757
+ if (m) {
758
+ const { label, color } = extractColor(m[1].trim(), palette);
759
+ return { id: nodeId("subroutine", label), label, shape: "subroutine", color };
760
+ }
761
+ m = t.match(/^\[([^\]]+)~\]$/);
762
+ if (m) {
763
+ const { label, color } = extractColor(m[1].trim(), palette);
764
+ return { id: nodeId("document", label), label, shape: "document", color };
765
+ }
766
+ m = t.match(/^\[([^\]]+)\]$/);
767
+ if (m) {
768
+ const { label, color } = extractColor(m[1].trim(), palette);
769
+ return { id: nodeId("process", label), label, shape: "process", color };
770
+ }
771
+ m = t.match(/^\((.+)\)$/);
772
+ if (m) {
773
+ const { label, color } = extractColor(m[1].trim(), palette);
774
+ return { id: nodeId("terminal", label), label, shape: "terminal", color };
775
+ }
776
+ m = t.match(/^<([^>]+)>$/);
777
+ if (m) {
778
+ const { label, color } = extractColor(m[1].trim(), palette);
779
+ return { id: nodeId("decision", label), label, shape: "decision", color };
780
+ }
781
+ m = t.match(/^\/([^/]+)\/$/);
782
+ if (m) {
783
+ const { label, color } = extractColor(m[1].trim(), palette);
784
+ return { id: nodeId("io", label), label, shape: "io", color };
785
+ }
786
+ return null;
787
+ }
788
+ function splitArrows(line3) {
789
+ const segments = [];
790
+ const arrowRe = /(?:^|\s)-([^>\s(][^(>]*?)?\s*(?:\(([^)]+)\))?\s*->|(?:^|\s)->/g;
791
+ let lastIndex = 0;
792
+ const arrowPositions = [];
793
+ let searchFrom = 0;
794
+ while (searchFrom < line3.length) {
795
+ const idx = line3.indexOf("->", searchFrom);
796
+ if (idx === -1) break;
797
+ let arrowStart = idx;
798
+ let label;
799
+ let color;
800
+ if (idx > 0 && line3[idx - 1] !== " " && line3[idx - 1] !== " ") {
801
+ let scanBack = idx - 1;
802
+ while (scanBack > 0 && line3[scanBack] !== "-") {
803
+ scanBack--;
804
+ }
805
+ if (line3[scanBack] === "-" && (scanBack === 0 || /\s/.test(line3[scanBack - 1]))) {
806
+ let arrowContent = line3.substring(scanBack + 1, idx);
807
+ if (arrowContent.endsWith("-")) arrowContent = arrowContent.slice(0, -1);
808
+ const colorMatch = arrowContent.match(/\(([^)]+)\)\s*$/);
809
+ if (colorMatch) {
810
+ color = colorMatch[1].trim();
811
+ const labelPart = arrowContent.substring(0, colorMatch.index).trim();
812
+ if (labelPart) label = labelPart;
813
+ } else {
814
+ const labelPart = arrowContent.trim();
815
+ if (labelPart) label = labelPart;
816
+ }
817
+ arrowStart = scanBack;
818
+ }
819
+ }
820
+ arrowPositions.push({ start: arrowStart, end: idx + 2, label, color });
821
+ searchFrom = idx + 2;
822
+ }
823
+ if (arrowPositions.length === 0) {
824
+ return [line3];
825
+ }
826
+ for (let i = 0; i < arrowPositions.length; i++) {
827
+ const arrow = arrowPositions[i];
828
+ const beforeText = line3.substring(lastIndex, arrow.start).trim();
829
+ if (beforeText || i === 0) {
830
+ segments.push(beforeText);
831
+ }
832
+ let arrowToken = "->";
833
+ if (arrow.label && arrow.color) arrowToken = `-${arrow.label}(${arrow.color})->`;
834
+ else if (arrow.label) arrowToken = `-${arrow.label}->`;
835
+ else if (arrow.color) arrowToken = `-(${arrow.color})->`;
836
+ segments.push(arrowToken);
837
+ lastIndex = arrow.end;
838
+ }
839
+ const remaining = line3.substring(lastIndex).trim();
840
+ if (remaining) {
841
+ segments.push(remaining);
842
+ }
843
+ return segments;
844
+ }
845
+ function parseArrowToken(token, palette) {
846
+ if (token === "->") return {};
847
+ const colorOnly = token.match(/^-\(([^)]+)\)->$/);
848
+ if (colorOnly) {
849
+ return { color: resolveColor(colorOnly[1].trim(), palette) };
850
+ }
851
+ const m = token.match(/^-(.+?)(?:\(([^)]+)\))?->$/);
852
+ if (m) {
853
+ const label = m[1]?.trim() || void 0;
854
+ const color = m[2] ? resolveColor(m[2].trim(), palette) : void 0;
855
+ return { label, color };
856
+ }
857
+ return {};
858
+ }
859
+ function parseFlowchart(content, palette) {
860
+ const lines = content.split("\n");
861
+ const result = {
862
+ type: "flowchart",
863
+ direction: "TB",
864
+ nodes: [],
865
+ edges: []
866
+ };
867
+ const nodeMap = /* @__PURE__ */ new Map();
868
+ const indentStack = [];
869
+ let currentGroup = null;
870
+ const groups = [];
871
+ let contentStarted = false;
872
+ function getOrCreateNode(ref, lineNumber) {
873
+ const existing = nodeMap.get(ref.id);
874
+ if (existing) return existing;
875
+ const node = {
876
+ id: ref.id,
877
+ label: ref.label,
878
+ shape: ref.shape,
879
+ lineNumber,
880
+ ...ref.color && { color: ref.color },
881
+ ...currentGroup && { group: currentGroup.id }
882
+ };
883
+ nodeMap.set(ref.id, node);
884
+ result.nodes.push(node);
885
+ if (currentGroup && !currentGroup.nodeIds.includes(ref.id)) {
886
+ currentGroup.nodeIds.push(ref.id);
887
+ }
888
+ return node;
889
+ }
890
+ function addEdge(sourceId, targetId, lineNumber, label, color) {
891
+ const edge = {
892
+ source: sourceId,
893
+ target: targetId,
894
+ lineNumber,
895
+ ...label && { label },
896
+ ...color && { color }
897
+ };
898
+ result.edges.push(edge);
899
+ }
900
+ function processContentLine(trimmed, lineNumber, indent) {
901
+ contentStarted = true;
902
+ while (indentStack.length > 0) {
903
+ const top = indentStack[indentStack.length - 1];
904
+ if (top.indent >= indent) {
905
+ indentStack.pop();
906
+ } else {
907
+ break;
908
+ }
909
+ }
910
+ const implicitSourceId = indentStack.length > 0 ? indentStack[indentStack.length - 1].nodeId : null;
911
+ const segments = splitArrows(trimmed);
912
+ if (segments.length === 1) {
913
+ const ref = parseNodeRef(segments[0], palette);
914
+ if (ref) {
915
+ const node = getOrCreateNode(ref, lineNumber);
916
+ indentStack.push({ nodeId: node.id, indent });
917
+ return node.id;
918
+ }
919
+ return null;
920
+ }
921
+ let lastNodeId = null;
922
+ let pendingArrow = null;
923
+ for (let i = 0; i < segments.length; i++) {
924
+ const seg = segments[i];
925
+ if (seg === "->" || /^-.+->$/.test(seg)) {
926
+ pendingArrow = parseArrowToken(seg, palette);
927
+ continue;
928
+ }
929
+ const ref = parseNodeRef(seg, palette);
930
+ if (!ref) continue;
931
+ const node = getOrCreateNode(ref, lineNumber);
932
+ if (pendingArrow !== null) {
933
+ const sourceId = lastNodeId ?? implicitSourceId;
934
+ if (sourceId) {
935
+ addEdge(
936
+ sourceId,
937
+ node.id,
938
+ lineNumber,
939
+ pendingArrow.label,
940
+ pendingArrow.color
941
+ );
942
+ }
943
+ pendingArrow = null;
944
+ } else if (lastNodeId === null && implicitSourceId === null) {
945
+ }
946
+ lastNodeId = node.id;
947
+ }
948
+ if (pendingArrow !== null && lastNodeId === null && implicitSourceId) {
949
+ }
950
+ if (segments.length >= 2 && segments[0] === "" && implicitSourceId && lastNodeId) {
951
+ }
952
+ if (lastNodeId) {
953
+ indentStack.push({ nodeId: lastNodeId, indent });
954
+ }
955
+ return lastNodeId;
956
+ }
957
+ for (let i = 0; i < lines.length; i++) {
958
+ const raw = lines[i];
959
+ const trimmed = raw.trim();
960
+ const lineNumber = i + 1;
961
+ const indent = measureIndent2(raw);
962
+ if (!trimmed) continue;
963
+ if (trimmed.startsWith("//")) continue;
964
+ const groupMatch = trimmed.match(GROUP_HEADING_RE);
965
+ if (groupMatch) {
966
+ const groupLabel = groupMatch[1].trim();
967
+ const groupColorName = groupMatch[2]?.trim();
968
+ const groupColor = groupColorName ? resolveColor(groupColorName, palette) : void 0;
969
+ currentGroup = {
970
+ id: `group:${groupLabel.toLowerCase()}`,
971
+ label: groupLabel,
972
+ nodeIds: [],
973
+ lineNumber,
974
+ ...groupColor && { color: groupColor }
975
+ };
976
+ groups.push(currentGroup);
977
+ continue;
978
+ }
979
+ if (!contentStarted && trimmed.includes(":") && !trimmed.includes("->")) {
980
+ const colonIdx = trimmed.indexOf(":");
981
+ const key = trimmed.substring(0, colonIdx).trim().toLowerCase();
982
+ const value = trimmed.substring(colonIdx + 1).trim();
983
+ if (key === "chart") {
984
+ if (value.toLowerCase() !== "flowchart") {
985
+ result.error = `Line ${lineNumber}: Expected chart type "flowchart", got "${value}"`;
986
+ return result;
987
+ }
988
+ continue;
989
+ }
990
+ if (key === "title") {
991
+ result.title = value;
992
+ continue;
993
+ }
994
+ if (key === "direction") {
995
+ const dir = value.toUpperCase();
996
+ if (dir === "TB" || dir === "LR") {
997
+ result.direction = dir;
998
+ }
999
+ continue;
1000
+ }
1001
+ continue;
1002
+ }
1003
+ processContentLine(trimmed, lineNumber, indent);
1004
+ }
1005
+ if (groups.length > 0) result.groups = groups;
1006
+ if (result.nodes.length === 0 && !result.error) {
1007
+ result.error = "No nodes found. Add flowchart content with shape syntax like [Process] or (Start).";
1008
+ }
1009
+ return result;
1010
+ }
1011
+ function looksLikeFlowchart(content) {
1012
+ if (!content.includes("->")) return false;
1013
+ const hasShapeDelimiter = /\[[^\]]+\]/.test(content) || /\([^)]+\)/.test(content) || /<[^>]+>/.test(content) || /\/[^/]+\//.test(content);
1014
+ if (!hasShapeDelimiter) return false;
1015
+ const shapeNearArrow = /[\])][ \t]*-.*->/.test(content) || // shape ] or ) followed by arrow
1016
+ /->[ \t]*[\[(<\/]/.test(content);
1017
+ return shapeNearArrow;
1018
+ }
1019
+ var COLOR_SUFFIX_RE, GROUP_HEADING_RE;
1020
+ var init_flowchart_parser = __esm({
1021
+ "src/graph/flowchart-parser.ts"() {
1022
+ "use strict";
1023
+ init_colors();
1024
+ COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
1025
+ GROUP_HEADING_RE = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
1026
+ }
1027
+ });
1028
+
1029
+ // src/dgmo-router.ts
1030
+ var dgmo_router_exports = {};
1031
+ __export(dgmo_router_exports, {
1032
+ DGMO_CHART_TYPE_MAP: () => DGMO_CHART_TYPE_MAP,
1033
+ getDgmoFramework: () => getDgmoFramework,
1034
+ parseDgmoChartType: () => parseDgmoChartType
1035
+ });
1036
+ function getDgmoFramework(chartType) {
1037
+ return DGMO_CHART_TYPE_MAP[chartType.toLowerCase()] ?? null;
1038
+ }
1039
+ function parseDgmoChartType(content) {
1040
+ const lines = content.split("\n");
1041
+ for (const line3 of lines) {
1042
+ const trimmed = line3.trim();
1043
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//"))
1044
+ continue;
1045
+ const match = trimmed.match(/^chart\s*:\s*(.+)/i);
1046
+ if (match) return match[1].trim().toLowerCase();
1047
+ }
1048
+ if (looksLikeSequence(content)) return "sequence";
1049
+ if (looksLikeFlowchart(content)) return "flowchart";
1050
+ return null;
1051
+ }
1052
+ var DGMO_CHART_TYPE_MAP;
1053
+ var init_dgmo_router = __esm({
1054
+ "src/dgmo-router.ts"() {
1055
+ "use strict";
1056
+ init_parser();
1057
+ init_flowchart_parser();
1058
+ DGMO_CHART_TYPE_MAP = {
1059
+ // Standard charts (via ECharts)
1060
+ bar: "echart",
1061
+ line: "echart",
1062
+ "multi-line": "echart",
1063
+ area: "echart",
1064
+ pie: "echart",
1065
+ doughnut: "echart",
1066
+ radar: "echart",
1067
+ "polar-area": "echart",
1068
+ "bar-stacked": "echart",
1069
+ // ECharts
1070
+ scatter: "echart",
1071
+ sankey: "echart",
1072
+ chord: "echart",
1073
+ function: "echart",
1074
+ heatmap: "echart",
1075
+ funnel: "echart",
1076
+ // D3
1077
+ slope: "d3",
1078
+ wordcloud: "d3",
1079
+ arc: "d3",
1080
+ timeline: "d3",
1081
+ venn: "d3",
1082
+ quadrant: "d3",
1083
+ sequence: "d3",
1084
+ flowchart: "d3"
1085
+ };
1086
+ }
1087
+ });
1088
+
582
1089
  // src/fonts.ts
583
1090
  var FONT_FAMILY;
584
1091
  var init_fonts = __esm({
@@ -1634,6 +2141,409 @@ var init_palettes = __esm({
1634
2141
  }
1635
2142
  });
1636
2143
 
2144
+ // src/graph/layout.ts
2145
+ var layout_exports = {};
2146
+ __export(layout_exports, {
2147
+ layoutGraph: () => layoutGraph
2148
+ });
2149
+ function computeNodeWidth(label, shape) {
2150
+ const base = Math.max(120, label.length * 9 + 40);
2151
+ if (shape === "subroutine") return base + 10;
2152
+ return base;
2153
+ }
2154
+ function computeNodeHeight(shape) {
2155
+ return shape === "decision" ? 60 : 50;
2156
+ }
2157
+ function layoutGraph(graph) {
2158
+ if (graph.nodes.length === 0) {
2159
+ return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
2160
+ }
2161
+ const g = new import_dagre.default.graphlib.Graph({ compound: true });
2162
+ g.setGraph({
2163
+ rankdir: graph.direction,
2164
+ nodesep: 50,
2165
+ ranksep: 60,
2166
+ edgesep: 20
2167
+ });
2168
+ g.setDefaultEdgeLabel(() => ({}));
2169
+ const nodeDataMap = /* @__PURE__ */ new Map();
2170
+ for (const node of graph.nodes) {
2171
+ nodeDataMap.set(node.id, node);
2172
+ }
2173
+ if (graph.groups) {
2174
+ for (const group of graph.groups) {
2175
+ g.setNode(group.id, {
2176
+ label: group.label,
2177
+ clusterLabelPos: "top"
2178
+ });
2179
+ }
2180
+ }
2181
+ for (const node of graph.nodes) {
2182
+ const width = computeNodeWidth(node.label, node.shape);
2183
+ const height = computeNodeHeight(node.shape);
2184
+ g.setNode(node.id, { label: node.label, width, height });
2185
+ if (node.group && graph.groups?.some((gr) => gr.id === node.group)) {
2186
+ g.setParent(node.id, node.group);
2187
+ }
2188
+ }
2189
+ const edgeDataMap = /* @__PURE__ */ new Map();
2190
+ for (const edge of graph.edges) {
2191
+ const key = `${edge.source}->${edge.target}`;
2192
+ edgeDataMap.set(key, edge);
2193
+ g.setEdge(edge.source, edge.target, {
2194
+ label: edge.label ?? ""
2195
+ });
2196
+ }
2197
+ import_dagre.default.layout(g);
2198
+ const layoutNodes = graph.nodes.map((node) => {
2199
+ const pos = g.node(node.id);
2200
+ return {
2201
+ id: node.id,
2202
+ label: node.label,
2203
+ shape: node.shape,
2204
+ color: node.color,
2205
+ group: node.group,
2206
+ lineNumber: node.lineNumber,
2207
+ x: pos.x,
2208
+ y: pos.y,
2209
+ width: pos.width,
2210
+ height: pos.height
2211
+ };
2212
+ });
2213
+ const layoutEdges = graph.edges.map((edge) => {
2214
+ const edgeData = g.edge(edge.source, edge.target);
2215
+ return {
2216
+ source: edge.source,
2217
+ target: edge.target,
2218
+ points: edgeData?.points ?? [],
2219
+ label: edge.label,
2220
+ color: edge.color,
2221
+ lineNumber: edge.lineNumber
2222
+ };
2223
+ });
2224
+ const layoutGroups = [];
2225
+ if (graph.groups) {
2226
+ const nodeMap = new Map(layoutNodes.map((n) => [n.id, n]));
2227
+ for (const group of graph.groups) {
2228
+ const members = group.nodeIds.map((id) => nodeMap.get(id)).filter((n) => n !== void 0);
2229
+ if (members.length === 0) {
2230
+ layoutGroups.push({
2231
+ id: group.id,
2232
+ label: group.label,
2233
+ color: group.color,
2234
+ x: 0,
2235
+ y: 0,
2236
+ width: 0,
2237
+ height: 0
2238
+ });
2239
+ continue;
2240
+ }
2241
+ let minX = Infinity;
2242
+ let minY = Infinity;
2243
+ let maxX = -Infinity;
2244
+ let maxY = -Infinity;
2245
+ for (const member of members) {
2246
+ const left = member.x - member.width / 2;
2247
+ const right = member.x + member.width / 2;
2248
+ const top = member.y - member.height / 2;
2249
+ const bottom = member.y + member.height / 2;
2250
+ if (left < minX) minX = left;
2251
+ if (right > maxX) maxX = right;
2252
+ if (top < minY) minY = top;
2253
+ if (bottom > maxY) maxY = bottom;
2254
+ }
2255
+ layoutGroups.push({
2256
+ id: group.id,
2257
+ label: group.label,
2258
+ color: group.color,
2259
+ x: minX - GROUP_PADDING,
2260
+ y: minY - GROUP_PADDING,
2261
+ width: maxX - minX + GROUP_PADDING * 2,
2262
+ height: maxY - minY + GROUP_PADDING * 2
2263
+ });
2264
+ }
2265
+ }
2266
+ let totalWidth = 0;
2267
+ let totalHeight = 0;
2268
+ for (const node of layoutNodes) {
2269
+ const right = node.x + node.width / 2;
2270
+ const bottom = node.y + node.height / 2;
2271
+ if (right > totalWidth) totalWidth = right;
2272
+ if (bottom > totalHeight) totalHeight = bottom;
2273
+ }
2274
+ for (const group of layoutGroups) {
2275
+ const right = group.x + group.width;
2276
+ const bottom = group.y + group.height;
2277
+ if (right > totalWidth) totalWidth = right;
2278
+ if (bottom > totalHeight) totalHeight = bottom;
2279
+ }
2280
+ totalWidth += 40;
2281
+ totalHeight += 40;
2282
+ return {
2283
+ nodes: layoutNodes,
2284
+ edges: layoutEdges,
2285
+ groups: layoutGroups,
2286
+ width: totalWidth,
2287
+ height: totalHeight
2288
+ };
2289
+ }
2290
+ var import_dagre, GROUP_PADDING;
2291
+ var init_layout = __esm({
2292
+ "src/graph/layout.ts"() {
2293
+ "use strict";
2294
+ import_dagre = __toESM(require("@dagrejs/dagre"), 1);
2295
+ GROUP_PADDING = 20;
2296
+ }
2297
+ });
2298
+
2299
+ // src/graph/flowchart-renderer.ts
2300
+ var flowchart_renderer_exports = {};
2301
+ __export(flowchart_renderer_exports, {
2302
+ renderFlowchart: () => renderFlowchart,
2303
+ renderFlowchartForExport: () => renderFlowchartForExport
2304
+ });
2305
+ function mix(a, b, pct) {
2306
+ const parse = (h) => {
2307
+ const r = h.replace("#", "");
2308
+ const f = r.length === 3 ? r[0] + r[0] + r[1] + r[1] + r[2] + r[2] : r;
2309
+ return [parseInt(f.substring(0, 2), 16), parseInt(f.substring(2, 4), 16), parseInt(f.substring(4, 6), 16)];
2310
+ };
2311
+ const [ar, ag, ab] = parse(a), [br, bg, bb] = parse(b), t = pct / 100;
2312
+ const c = (x, y) => Math.round(x * t + y * (1 - t)).toString(16).padStart(2, "0");
2313
+ return `#${c(ar, br)}${c(ag, bg)}${c(ab, bb)}`;
2314
+ }
2315
+ function nodeFill(palette, isDark, nodeColor) {
2316
+ if (nodeColor) {
2317
+ return mix(nodeColor, isDark ? palette.surface : palette.bg, 25);
2318
+ }
2319
+ return mix(palette.primary, isDark ? palette.surface : palette.bg, 15);
2320
+ }
2321
+ function nodeStroke(palette, nodeColor) {
2322
+ return nodeColor ?? palette.textMuted;
2323
+ }
2324
+ function renderTerminal(g, node, palette, isDark) {
2325
+ const w = node.width;
2326
+ const h = node.height;
2327
+ const rx = h / 2;
2328
+ 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);
2329
+ }
2330
+ function renderProcess(g, node, palette, isDark) {
2331
+ const w = node.width;
2332
+ const h = node.height;
2333
+ 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);
2334
+ }
2335
+ function renderDecision(g, node, palette, isDark) {
2336
+ const w = node.width / 2;
2337
+ const h = node.height / 2;
2338
+ const points = [
2339
+ `${0},${-h}`,
2340
+ // top
2341
+ `${w},${0}`,
2342
+ // right
2343
+ `${0},${h}`,
2344
+ // bottom
2345
+ `${-w},${0}`
2346
+ // left
2347
+ ].join(" ");
2348
+ 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);
2349
+ }
2350
+ function renderIO(g, node, palette, isDark) {
2351
+ const w = node.width / 2;
2352
+ const h = node.height / 2;
2353
+ const sk = IO_SKEW;
2354
+ const points = [
2355
+ `${-w + sk},${-h}`,
2356
+ // top-left (shifted right)
2357
+ `${w + sk},${-h}`,
2358
+ // top-right (shifted right)
2359
+ `${w - sk},${h}`,
2360
+ // bottom-right (shifted left)
2361
+ `${-w - sk},${h}`
2362
+ // bottom-left (shifted left)
2363
+ ].join(" ");
2364
+ 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);
2365
+ }
2366
+ function renderSubroutine(g, node, palette, isDark) {
2367
+ const w = node.width;
2368
+ const h = node.height;
2369
+ const s = nodeStroke(palette, node.color);
2370
+ 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);
2371
+ 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);
2372
+ 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);
2373
+ }
2374
+ function renderDocument(g, node, palette, isDark) {
2375
+ const w = node.width;
2376
+ const h = node.height;
2377
+ const waveH = DOC_WAVE_HEIGHT;
2378
+ const left = -w / 2;
2379
+ const right = w / 2;
2380
+ const top = -h / 2;
2381
+ const bottom = h / 2 - waveH;
2382
+ const d = [
2383
+ `M ${left} ${top}`,
2384
+ `L ${right} ${top}`,
2385
+ `L ${right} ${bottom}`,
2386
+ `C ${right - w * 0.25} ${bottom + waveH * 2}, ${left + w * 0.25} ${bottom - waveH}, ${left} ${bottom}`,
2387
+ "Z"
2388
+ ].join(" ");
2389
+ 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);
2390
+ }
2391
+ function renderNodeShape(g, node, palette, isDark) {
2392
+ switch (node.shape) {
2393
+ case "terminal":
2394
+ renderTerminal(g, node, palette, isDark);
2395
+ break;
2396
+ case "process":
2397
+ renderProcess(g, node, palette, isDark);
2398
+ break;
2399
+ case "decision":
2400
+ renderDecision(g, node, palette, isDark);
2401
+ break;
2402
+ case "io":
2403
+ renderIO(g, node, palette, isDark);
2404
+ break;
2405
+ case "subroutine":
2406
+ renderSubroutine(g, node, palette, isDark);
2407
+ break;
2408
+ case "document":
2409
+ renderDocument(g, node, palette, isDark);
2410
+ break;
2411
+ }
2412
+ }
2413
+ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem, exportDims) {
2414
+ d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
2415
+ const width = exportDims?.width ?? container.clientWidth;
2416
+ const height = exportDims?.height ?? container.clientHeight;
2417
+ if (width <= 0 || height <= 0) return;
2418
+ const titleOffset = graph.title ? TITLE_HEIGHT : 0;
2419
+ const diagramW = layout.width;
2420
+ const diagramH = layout.height + titleOffset;
2421
+ const scaleX = (width - DIAGRAM_PADDING * 2) / diagramW;
2422
+ const scaleY = (height - DIAGRAM_PADDING * 2) / diagramH;
2423
+ const scale = Math.min(1, scaleX, scaleY);
2424
+ const scaledW = diagramW * scale;
2425
+ const scaledH = diagramH * scale;
2426
+ const offsetX = (width - scaledW) / 2;
2427
+ const offsetY = (height - scaledH) / 2;
2428
+ const svg = d3Selection.select(container).append("svg").attr("width", width).attr("height", height).style("background", palette.bg).style("font-family", FONT_FAMILY);
2429
+ const defs = svg.append("defs");
2430
+ 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);
2431
+ const edgeColors = /* @__PURE__ */ new Set();
2432
+ for (const edge of layout.edges) {
2433
+ if (edge.color) edgeColors.add(edge.color);
2434
+ }
2435
+ for (const color of edgeColors) {
2436
+ const id = `fc-arrow-${color.replace("#", "")}`;
2437
+ 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);
2438
+ }
2439
+ const mainG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY}) scale(${scale})`);
2440
+ if (graph.title) {
2441
+ 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);
2442
+ }
2443
+ const contentG = mainG.append("g").attr("transform", `translate(0, ${titleOffset})`);
2444
+ for (const group of layout.groups) {
2445
+ if (group.width === 0 && group.height === 0) continue;
2446
+ const gx = group.x - GROUP_EXTRA_PADDING;
2447
+ const gy = group.y - GROUP_EXTRA_PADDING - GROUP_LABEL_FONT_SIZE - 4;
2448
+ const gw = group.width + GROUP_EXTRA_PADDING * 2;
2449
+ const gh = group.height + GROUP_EXTRA_PADDING * 2 + GROUP_LABEL_FONT_SIZE + 4;
2450
+ const fillColor = group.color ? mix(group.color, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
2451
+ const strokeColor = group.color ?? palette.textMuted;
2452
+ 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");
2453
+ 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);
2454
+ }
2455
+ for (const edge of layout.edges) {
2456
+ if (edge.points.length < 2) continue;
2457
+ const edgeG = contentG.append("g").attr("class", "fc-edge-group").attr("data-line-number", String(edge.lineNumber));
2458
+ const edgeColor = edge.color ?? palette.textMuted;
2459
+ const markerId = edge.color ? `fc-arrow-${edge.color.replace("#", "")}` : "fc-arrow";
2460
+ const pathD = lineGenerator(edge.points);
2461
+ if (pathD) {
2462
+ 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");
2463
+ }
2464
+ if (edge.label) {
2465
+ const midIdx = Math.floor(edge.points.length / 2);
2466
+ const midPt = edge.points[midIdx];
2467
+ const labelLen = edge.label.length;
2468
+ const bgW = labelLen * 7 + 8;
2469
+ const bgH = 16;
2470
+ 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");
2471
+ 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);
2472
+ }
2473
+ }
2474
+ for (const node of layout.nodes) {
2475
+ const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "fc-node").attr("data-line-number", String(node.lineNumber));
2476
+ if (onClickItem) {
2477
+ nodeG.style("cursor", "pointer").on("click", () => {
2478
+ onClickItem(node.lineNumber);
2479
+ });
2480
+ }
2481
+ renderNodeShape(nodeG, node, palette, isDark);
2482
+ 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);
2483
+ }
2484
+ }
2485
+ function renderFlowchartForExport(content, theme, palette) {
2486
+ const parsed = parseFlowchart(content, palette);
2487
+ if (parsed.error || parsed.nodes.length === 0) return "";
2488
+ const layout = layoutGraph(parsed);
2489
+ const isDark = theme === "dark";
2490
+ const container = document.createElement("div");
2491
+ container.style.width = `${layout.width + DIAGRAM_PADDING * 2}px`;
2492
+ container.style.height = `${layout.height + DIAGRAM_PADDING * 2 + (parsed.title ? TITLE_HEIGHT : 0)}px`;
2493
+ container.style.position = "absolute";
2494
+ container.style.left = "-9999px";
2495
+ document.body.appendChild(container);
2496
+ const exportWidth = layout.width + DIAGRAM_PADDING * 2;
2497
+ const exportHeight = layout.height + DIAGRAM_PADDING * 2 + (parsed.title ? TITLE_HEIGHT : 0);
2498
+ try {
2499
+ renderFlowchart(
2500
+ container,
2501
+ parsed,
2502
+ layout,
2503
+ palette,
2504
+ isDark,
2505
+ void 0,
2506
+ { width: exportWidth, height: exportHeight }
2507
+ );
2508
+ const svgEl = container.querySelector("svg");
2509
+ if (!svgEl) return "";
2510
+ if (theme === "transparent") {
2511
+ svgEl.style.background = "none";
2512
+ }
2513
+ svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
2514
+ svgEl.style.fontFamily = FONT_FAMILY;
2515
+ return svgEl.outerHTML;
2516
+ } finally {
2517
+ document.body.removeChild(container);
2518
+ }
2519
+ }
2520
+ var d3Selection, d3Shape, 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;
2521
+ var init_flowchart_renderer = __esm({
2522
+ "src/graph/flowchart-renderer.ts"() {
2523
+ "use strict";
2524
+ d3Selection = __toESM(require("d3-selection"), 1);
2525
+ d3Shape = __toESM(require("d3-shape"), 1);
2526
+ init_fonts();
2527
+ init_flowchart_parser();
2528
+ init_layout();
2529
+ DIAGRAM_PADDING = 20;
2530
+ TITLE_HEIGHT = 30;
2531
+ TITLE_FONT_SIZE = 18;
2532
+ NODE_FONT_SIZE = 13;
2533
+ EDGE_LABEL_FONT_SIZE = 11;
2534
+ GROUP_LABEL_FONT_SIZE = 11;
2535
+ EDGE_STROKE_WIDTH = 1.5;
2536
+ NODE_STROKE_WIDTH = 1.5;
2537
+ ARROWHEAD_W = 10;
2538
+ ARROWHEAD_H = 7;
2539
+ IO_SKEW = 15;
2540
+ SUBROUTINE_INSET = 8;
2541
+ DOC_WAVE_HEIGHT = 10;
2542
+ GROUP_EXTRA_PADDING = 12;
2543
+ lineGenerator = d3Shape.line().x((d) => d.x).y((d) => d.y).curve(d3Shape.curveBasis);
2544
+ }
2545
+ });
2546
+
1637
2547
  // src/sequence/renderer.ts
1638
2548
  var renderer_exports = {};
1639
2549
  __export(renderer_exports, {
@@ -1644,7 +2554,44 @@ __export(renderer_exports, {
1644
2554
  groupMessagesBySection: () => groupMessagesBySection,
1645
2555
  renderSequenceDiagram: () => renderSequenceDiagram
1646
2556
  });
1647
- function mix(a, b, pct) {
2557
+ function parseInlineMarkdown(text) {
2558
+ const spans = [];
2559
+ const regex = /\*\*(.+?)\*\*|__(.+?)__|\*(.+?)\*|_(.+?)_|`(.+?)`|\[(.+?)\]\((.+?)\)|([^*_`[]+)/g;
2560
+ let match;
2561
+ while ((match = regex.exec(text)) !== null) {
2562
+ if (match[1]) spans.push({ text: match[1], bold: true });
2563
+ else if (match[2]) spans.push({ text: match[2], bold: true });
2564
+ else if (match[3]) spans.push({ text: match[3], italic: true });
2565
+ else if (match[4]) spans.push({ text: match[4], italic: true });
2566
+ else if (match[5]) spans.push({ text: match[5], code: true });
2567
+ else if (match[6]) spans.push({ text: match[6], href: match[7] });
2568
+ else if (match[8]) spans.push({ text: match[8] });
2569
+ }
2570
+ return spans;
2571
+ }
2572
+ function wrapTextLines(text, maxChars) {
2573
+ const rawLines = text.split("\n");
2574
+ const wrapped = [];
2575
+ for (const line3 of rawLines) {
2576
+ if (line3.length <= maxChars) {
2577
+ wrapped.push(line3);
2578
+ } else {
2579
+ const words = line3.split(" ");
2580
+ let current = "";
2581
+ for (const word of words) {
2582
+ if (current && (current + " " + word).length > maxChars) {
2583
+ wrapped.push(current);
2584
+ current = word;
2585
+ } else {
2586
+ current = current ? current + " " + word : word;
2587
+ }
2588
+ }
2589
+ if (current) wrapped.push(current);
2590
+ }
2591
+ }
2592
+ return wrapped;
2593
+ }
2594
+ function mix2(a, b, pct) {
1648
2595
  const parse = (h) => {
1649
2596
  const r = h.replace("#", "");
1650
2597
  const f = r.length === 3 ? r[0] + r[0] + r[1] + r[1] + r[2] + r[2] : r;
@@ -1750,7 +2697,12 @@ function groupMessagesBySection(elements, messages) {
1750
2697
  ...collectIndices(el.children),
1751
2698
  ...collectIndices(el.elseChildren)
1752
2699
  );
1753
- } else if (isSequenceSection(el)) {
2700
+ if (el.elseIfBranches) {
2701
+ for (const branch of el.elseIfBranches) {
2702
+ indices.push(...collectIndices(branch.children));
2703
+ }
2704
+ }
2705
+ } else if (isSequenceSection(el) || isSequenceNote(el)) {
1754
2706
  continue;
1755
2707
  } else {
1756
2708
  const idx = messages.indexOf(el);
@@ -1766,7 +2718,7 @@ function groupMessagesBySection(elements, messages) {
1766
2718
  } else if (currentGroup) {
1767
2719
  if (isSequenceBlock(el)) {
1768
2720
  currentGroup.messageIndices.push(...collectIndices([el]));
1769
- } else {
2721
+ } else if (!isSequenceNote(el)) {
1770
2722
  const idx = messages.indexOf(el);
1771
2723
  if (idx >= 0) currentGroup.messageIndices.push(idx);
1772
2724
  }
@@ -1923,7 +2875,7 @@ function applyGroupOrdering(participants, groups) {
1923
2875
  return result;
1924
2876
  }
1925
2877
  function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateToLine, options) {
1926
- d3Selection.select(container).selectAll("*").remove();
2878
+ d3Selection2.select(container).selectAll("*").remove();
1927
2879
  const { title, messages, elements, groups, options: parsedOptions } = parsed;
1928
2880
  const collapsedSections = options?.collapsedSections;
1929
2881
  const participants = applyPositionOverrides(
@@ -1964,7 +2916,15 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1964
2916
  if (isSequenceBlock(el)) {
1965
2917
  const idx = findFirstMsgIndex(el.children);
1966
2918
  if (idx >= 0) return idx;
1967
- } else if (!isSequenceSection(el)) {
2919
+ if (el.elseIfBranches) {
2920
+ for (const branch of el.elseIfBranches) {
2921
+ const branchIdx = findFirstMsgIndex(branch.children);
2922
+ if (branchIdx >= 0) return branchIdx;
2923
+ }
2924
+ }
2925
+ const elseIdx = findFirstMsgIndex(el.elseChildren);
2926
+ if (elseIdx >= 0) return elseIdx;
2927
+ } else if (!isSequenceSection(el) && !isSequenceNote(el)) {
1968
2928
  const idx = messages.indexOf(el);
1969
2929
  if (idx >= 0 && !hiddenMsgIndices.has(idx)) return idx;
1970
2930
  }
@@ -1984,6 +2944,13 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1984
2944
  if (!isSequenceBlock(el)) continue;
1985
2945
  const firstIdx = findFirstMsgIndex(el.children);
1986
2946
  if (firstIdx >= 0) addExtra(firstIdx, BLOCK_HEADER_SPACE);
2947
+ if (el.elseIfBranches) {
2948
+ for (const branch of el.elseIfBranches) {
2949
+ const firstBranchIdx = findFirstMsgIndex(branch.children);
2950
+ if (firstBranchIdx >= 0) addExtra(firstBranchIdx, BLOCK_HEADER_SPACE);
2951
+ markBlockSpacing(branch.children);
2952
+ }
2953
+ }
1987
2954
  const firstElseIdx = findFirstMsgIndex(el.elseChildren);
1988
2955
  if (firstElseIdx >= 0) addExtra(firstElseIdx, BLOCK_HEADER_SPACE);
1989
2956
  markBlockSpacing(el.children);
@@ -1997,6 +2964,34 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1997
2964
  if (elements && elements.length > 0) {
1998
2965
  markBlockSpacing(elements);
1999
2966
  }
2967
+ const NOTE_OFFSET_BELOW = 16;
2968
+ const computeNoteHeight = (text) => {
2969
+ const lines = wrapTextLines(text, NOTE_CHARS_PER_LINE);
2970
+ return lines.length * NOTE_LINE_H + NOTE_PAD_V * 2;
2971
+ };
2972
+ const markNoteSpacing = (els) => {
2973
+ for (let i = 0; i < els.length; i++) {
2974
+ const el = els[i];
2975
+ if (isSequenceNote(el)) {
2976
+ const noteH = computeNoteHeight(el.text);
2977
+ const nextIdx = i + 1 < els.length ? findFirstMsgIndex([els[i + 1]]) : -1;
2978
+ if (nextIdx >= 0) {
2979
+ addExtra(nextIdx, noteH + NOTE_OFFSET_BELOW);
2980
+ }
2981
+ } else if (isSequenceBlock(el)) {
2982
+ markNoteSpacing(el.children);
2983
+ if (el.elseIfBranches) {
2984
+ for (const branch of el.elseIfBranches) {
2985
+ markNoteSpacing(branch.children);
2986
+ }
2987
+ }
2988
+ markNoteSpacing(el.elseChildren);
2989
+ }
2990
+ }
2991
+ };
2992
+ if (elements && elements.length > 0) {
2993
+ markNoteSpacing(elements);
2994
+ }
2000
2995
  const preSectionMsgIndices = [];
2001
2996
  const sectionRegions = [];
2002
2997
  {
@@ -2005,15 +3000,27 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2005
3000
  for (const child of block.children) {
2006
3001
  if (isSequenceBlock(child)) {
2007
3002
  indices.push(...collectMsgIndicesFromBlock(child));
2008
- } else if (!isSequenceSection(child)) {
3003
+ } else if (!isSequenceSection(child) && !isSequenceNote(child)) {
2009
3004
  const idx = messages.indexOf(child);
2010
3005
  if (idx >= 0) indices.push(idx);
2011
3006
  }
2012
3007
  }
3008
+ if (block.elseIfBranches) {
3009
+ for (const branch of block.elseIfBranches) {
3010
+ for (const child of branch.children) {
3011
+ if (isSequenceBlock(child)) {
3012
+ indices.push(...collectMsgIndicesFromBlock(child));
3013
+ } else if (!isSequenceSection(child) && !isSequenceNote(child)) {
3014
+ const idx = messages.indexOf(child);
3015
+ if (idx >= 0) indices.push(idx);
3016
+ }
3017
+ }
3018
+ }
3019
+ }
2013
3020
  for (const child of block.elseChildren) {
2014
3021
  if (isSequenceBlock(child)) {
2015
3022
  indices.push(...collectMsgIndicesFromBlock(child));
2016
- } else if (!isSequenceSection(child)) {
3023
+ } else if (!isSequenceSection(child) && !isSequenceNote(child)) {
2017
3024
  const idx = messages.indexOf(child);
2018
3025
  if (idx >= 0) indices.push(idx);
2019
3026
  }
@@ -2088,7 +3095,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2088
3095
  const GROUP_PADDING_TOP = 22;
2089
3096
  const GROUP_PADDING_BOTTOM = 8;
2090
3097
  const GROUP_LABEL_SIZE = 11;
2091
- const titleOffset = title ? TITLE_HEIGHT : 0;
3098
+ const titleOffset = title ? TITLE_HEIGHT2 : 0;
2092
3099
  const groupOffset = groups.length > 0 ? GROUP_PADDING_TOP + GROUP_LABEL_SIZE : 0;
2093
3100
  const participantStartY = TOP_MARGIN + titleOffset + PARTICIPANT_Y_OFFSET + groupOffset;
2094
3101
  const lifelineStartY0 = participantStartY + PARTICIPANT_BOX_HEIGHT;
@@ -2142,7 +3149,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2142
3149
  participants.forEach((p, i) => {
2143
3150
  participantX.set(p.id, offsetX + i * PARTICIPANT_GAP);
2144
3151
  });
2145
- 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);
3152
+ 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);
2146
3153
  const defs = svg.append("defs");
2147
3154
  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(
2148
3155
  "points",
@@ -2168,7 +3175,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2168
3175
  const boxY = participantStartY - GROUP_PADDING_TOP;
2169
3176
  const boxH = PARTICIPANT_BOX_HEIGHT + GROUP_PADDING_TOP + GROUP_PADDING_BOTTOM;
2170
3177
  const resolvedGroupColor = group.color ? resolveColor(group.color, palette) : void 0;
2171
- const fillColor = resolvedGroupColor ? mix(resolvedGroupColor, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : palette.bg;
3178
+ const fillColor = resolvedGroupColor ? mix2(resolvedGroupColor, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : palette.bg;
2172
3179
  const strokeColor = resolvedGroupColor || palette.textMuted;
2173
3180
  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));
2174
3181
  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);
@@ -2193,7 +3200,12 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2193
3200
  ...collectMsgIndices(el.children),
2194
3201
  ...collectMsgIndices(el.elseChildren)
2195
3202
  );
2196
- } else if (!isSequenceSection(el)) {
3203
+ if (el.elseIfBranches) {
3204
+ for (const branch of el.elseIfBranches) {
3205
+ indices.push(...collectMsgIndices(branch.children));
3206
+ }
3207
+ }
3208
+ } else if (!isSequenceSection(el) && !isSequenceNote(el)) {
2197
3209
  const idx = messages.indexOf(el);
2198
3210
  if (idx >= 0) indices.push(idx);
2199
3211
  }
@@ -2206,8 +3218,21 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2206
3218
  for (const el of els) {
2207
3219
  if (!isSequenceBlock(el)) continue;
2208
3220
  const ifIndices = collectMsgIndices(el.children);
3221
+ const elseIfBranchData = [];
3222
+ if (el.elseIfBranches) {
3223
+ for (const branch of el.elseIfBranches) {
3224
+ elseIfBranchData.push({
3225
+ label: branch.label,
3226
+ indices: collectMsgIndices(branch.children)
3227
+ });
3228
+ }
3229
+ }
2209
3230
  const elseIndices = collectMsgIndices(el.elseChildren);
2210
- const allIndices = [...ifIndices, ...elseIndices];
3231
+ const allIndices = [
3232
+ ...ifIndices,
3233
+ ...elseIfBranchData.flatMap((b) => b.indices),
3234
+ ...elseIndices
3235
+ ];
2211
3236
  if (allIndices.length === 0) continue;
2212
3237
  let minStep = Infinity;
2213
3238
  let maxStep = -Infinity;
@@ -2245,6 +3270,32 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2245
3270
  italic: false,
2246
3271
  blockLine: el.lineNumber
2247
3272
  });
3273
+ for (const branchData of elseIfBranchData) {
3274
+ if (branchData.indices.length > 0) {
3275
+ let firstBranchStep = Infinity;
3276
+ for (const mi of branchData.indices) {
3277
+ const first = msgToFirstStep.get(mi);
3278
+ if (first !== void 0)
3279
+ firstBranchStep = Math.min(firstBranchStep, first);
3280
+ }
3281
+ if (firstBranchStep < Infinity) {
3282
+ const dividerY = stepY(firstBranchStep) - stepSpacing / 2;
3283
+ deferredLines.push({
3284
+ x1: frameX,
3285
+ y1: dividerY,
3286
+ x2: frameX + frameW,
3287
+ y2: dividerY
3288
+ });
3289
+ deferredLabels.push({
3290
+ x: frameX + 6,
3291
+ y: dividerY + 14,
3292
+ text: `else if ${branchData.label}`,
3293
+ bold: false,
3294
+ italic: true
3295
+ });
3296
+ }
3297
+ }
3298
+ }
2248
3299
  if (elseIndices.length > 0) {
2249
3300
  let firstElseStep = Infinity;
2250
3301
  for (const mi of elseIndices) {
@@ -2270,6 +3321,11 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2270
3321
  }
2271
3322
  }
2272
3323
  renderBlockFrames(el.children, depth + 1);
3324
+ if (el.elseIfBranches) {
3325
+ for (const branch of el.elseIfBranches) {
3326
+ renderBlockFrames(branch.children, depth + 1);
3327
+ }
3328
+ }
2273
3329
  renderBlockFrames(el.elseChildren, depth + 1);
2274
3330
  }
2275
3331
  };
@@ -2291,7 +3347,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2291
3347
  if (msg) coveredLines.push(msg.lineNumber);
2292
3348
  }
2293
3349
  svg.append("rect").attr("x", x).attr("y", y1).attr("width", ACTIVATION_WIDTH).attr("height", y2 - y1).attr("fill", isDark ? palette.surface : palette.bg);
2294
- const actFill = mix(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
3350
+ const actFill = mix2(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
2295
3351
  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");
2296
3352
  });
2297
3353
  for (const ln of deferredLines) {
@@ -2417,6 +3473,94 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2417
3473
  }
2418
3474
  }
2419
3475
  });
3476
+ const noteFill = isDark ? mix2(palette.surface, palette.bg, 50) : mix2(palette.bg, palette.surface, 15);
3477
+ const findAssociatedStep = (note) => {
3478
+ let bestMsgIndex = -1;
3479
+ let bestLine = -1;
3480
+ for (let mi = 0; mi < messages.length; mi++) {
3481
+ if (messages[mi].lineNumber < note.lineNumber && messages[mi].lineNumber > bestLine && !hiddenMsgIndices.has(mi)) {
3482
+ bestLine = messages[mi].lineNumber;
3483
+ bestMsgIndex = mi;
3484
+ }
3485
+ }
3486
+ if (bestMsgIndex < 0) return -1;
3487
+ return msgToFirstStep.get(bestMsgIndex) ?? -1;
3488
+ };
3489
+ const renderNoteElements = (els) => {
3490
+ for (const el of els) {
3491
+ if (isSequenceNote(el)) {
3492
+ const px = participantX.get(el.participantId);
3493
+ if (px === void 0) continue;
3494
+ const si = findAssociatedStep(el);
3495
+ if (si < 0) continue;
3496
+ const noteY = stepY(si);
3497
+ const wrappedLines = wrapTextLines(el.text, NOTE_CHARS_PER_LINE);
3498
+ const noteH = wrappedLines.length * NOTE_LINE_H + NOTE_PAD_V * 2;
3499
+ const maxLineLen = Math.max(...wrappedLines.map((l) => l.length));
3500
+ const noteW = Math.min(
3501
+ NOTE_MAX_W,
3502
+ Math.max(80, maxLineLen * NOTE_CHAR_W + NOTE_PAD_H * 2 + NOTE_FOLD)
3503
+ );
3504
+ const isRight = el.position === "right";
3505
+ const noteX = isRight ? px + ACTIVATION_WIDTH + NOTE_GAP : px - ACTIVATION_WIDTH - NOTE_GAP - noteW;
3506
+ const noteTopY = noteY + NOTE_OFFSET_BELOW;
3507
+ 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));
3508
+ noteG.append("path").attr(
3509
+ "d",
3510
+ [
3511
+ `M ${noteX} ${noteTopY}`,
3512
+ `L ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
3513
+ `L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`,
3514
+ `L ${noteX + noteW} ${noteTopY + noteH}`,
3515
+ `L ${noteX} ${noteTopY + noteH}`,
3516
+ "Z"
3517
+ ].join(" ")
3518
+ ).attr("fill", noteFill).attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-box");
3519
+ noteG.append("path").attr(
3520
+ "d",
3521
+ [
3522
+ `M ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
3523
+ `L ${noteX + noteW - NOTE_FOLD} ${noteTopY + NOTE_FOLD}`,
3524
+ `L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`
3525
+ ].join(" ")
3526
+ ).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-fold");
3527
+ wrappedLines.forEach((line3, li) => {
3528
+ const textY = noteTopY + NOTE_PAD_V + (li + 1) * NOTE_LINE_H - 3;
3529
+ const isBullet = line3.startsWith("- ");
3530
+ const bulletIndent = isBullet ? 10 : 0;
3531
+ const displayLine = isBullet ? line3.slice(2) : line3;
3532
+ 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");
3533
+ if (isBullet) {
3534
+ noteG.append("text").attr("x", noteX + NOTE_PAD_H).attr("y", textY).attr("fill", palette.text).attr("font-size", NOTE_FONT_SIZE).text("\u2022");
3535
+ }
3536
+ const spans = parseInlineMarkdown(displayLine);
3537
+ for (const span of spans) {
3538
+ if (span.href) {
3539
+ const a = textEl.append("a").attr("href", span.href);
3540
+ a.append("tspan").text(span.text).attr("fill", palette.primary).style("text-decoration", "underline");
3541
+ } else {
3542
+ const tspan = textEl.append("tspan").text(span.text);
3543
+ if (span.bold) tspan.attr("font-weight", "bold");
3544
+ if (span.italic) tspan.attr("font-style", "italic");
3545
+ if (span.code)
3546
+ tspan.attr("font-family", "monospace").attr("font-size", NOTE_FONT_SIZE - 1);
3547
+ }
3548
+ }
3549
+ });
3550
+ } else if (isSequenceBlock(el)) {
3551
+ renderNoteElements(el.children);
3552
+ if (el.elseIfBranches) {
3553
+ for (const branch of el.elseIfBranches) {
3554
+ renderNoteElements(branch.children);
3555
+ }
3556
+ }
3557
+ renderNoteElements(el.elseChildren);
3558
+ }
3559
+ }
3560
+ };
3561
+ if (elements && elements.length > 0) {
3562
+ renderNoteElements(elements);
3563
+ }
2420
3564
  }
2421
3565
  function renderParticipant(svg, participant, cx, cy, palette, isDark) {
2422
3566
  const g = svg.append("g").attr("transform", `translate(${cx}, ${cy})`).attr("class", "participant").attr("data-participant-id", participant.id);
@@ -2458,11 +3602,11 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark) {
2458
3602
  isActor ? PARTICIPANT_BOX_HEIGHT + 14 : PARTICIPANT_BOX_HEIGHT / 2 + 5
2459
3603
  ).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", 13).attr("font-weight", 500).text(participant.label);
2460
3604
  }
2461
- var d3Selection, 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;
3605
+ var d3Selection2, 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;
2462
3606
  var init_renderer = __esm({
2463
3607
  "src/sequence/renderer.ts"() {
2464
3608
  "use strict";
2465
- d3Selection = __toESM(require("d3-selection"), 1);
3609
+ d3Selection2 = __toESM(require("d3-selection"), 1);
2466
3610
  init_colors();
2467
3611
  init_fonts();
2468
3612
  init_parser();
@@ -2470,13 +3614,22 @@ var init_renderer = __esm({
2470
3614
  PARTICIPANT_BOX_WIDTH = 120;
2471
3615
  PARTICIPANT_BOX_HEIGHT = 50;
2472
3616
  TOP_MARGIN = 20;
2473
- TITLE_HEIGHT = 30;
3617
+ TITLE_HEIGHT2 = 30;
2474
3618
  PARTICIPANT_Y_OFFSET = 10;
2475
3619
  SERVICE_BORDER_RADIUS = 10;
2476
3620
  MESSAGE_START_OFFSET = 30;
2477
3621
  LIFELINE_TAIL = 30;
2478
3622
  ARROWHEAD_SIZE = 8;
2479
- fill = (palette, isDark) => mix(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
3623
+ NOTE_MAX_W = 200;
3624
+ NOTE_FOLD = 10;
3625
+ NOTE_PAD_H = 8;
3626
+ NOTE_PAD_V = 6;
3627
+ NOTE_FONT_SIZE = 10;
3628
+ NOTE_LINE_H = 14;
3629
+ NOTE_GAP = 15;
3630
+ NOTE_CHAR_W = 6;
3631
+ NOTE_CHARS_PER_LINE = Math.floor((NOTE_MAX_W - NOTE_PAD_H * 2 - NOTE_FOLD) / NOTE_CHAR_W);
3632
+ fill = (palette, isDark) => mix2(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
2480
3633
  stroke = (palette) => palette.textMuted;
2481
3634
  SW = 1.5;
2482
3635
  W = PARTICIPANT_BOX_WIDTH;
@@ -2516,7 +3669,10 @@ __export(index_exports, {
2516
3669
  hslToHex: () => hslToHex,
2517
3670
  inferParticipantType: () => inferParticipantType,
2518
3671
  isSequenceBlock: () => isSequenceBlock,
3672
+ isSequenceNote: () => isSequenceNote,
2519
3673
  isValidHex: () => isValidHex,
3674
+ layoutGraph: () => layoutGraph,
3675
+ looksLikeFlowchart: () => looksLikeFlowchart,
2520
3676
  looksLikeSequence: () => looksLikeSequence,
2521
3677
  mute: () => mute,
2522
3678
  nord: () => nord,
@@ -2527,6 +3683,7 @@ __export(index_exports, {
2527
3683
  parseD3: () => parseD3,
2528
3684
  parseDgmoChartType: () => parseDgmoChartType,
2529
3685
  parseEChart: () => parseEChart,
3686
+ parseFlowchart: () => parseFlowchart,
2530
3687
  parseQuadrant: () => parseQuadrant,
2531
3688
  parseSequenceDgmo: () => parseSequenceDgmo,
2532
3689
  parseTimelineDate: () => parseTimelineDate,
@@ -2534,6 +3691,8 @@ __export(index_exports, {
2534
3691
  renderArcDiagram: () => renderArcDiagram,
2535
3692
  renderD3ForExport: () => renderD3ForExport,
2536
3693
  renderEChartsForExport: () => renderEChartsForExport,
3694
+ renderFlowchart: () => renderFlowchart,
3695
+ renderFlowchartForExport: () => renderFlowchartForExport,
2537
3696
  renderQuadrant: () => renderQuadrant,
2538
3697
  renderSequenceDiagram: () => renderSequenceDiagram,
2539
3698
  renderSlopeChart: () => renderSlopeChart,
@@ -2549,51 +3708,7 @@ __export(index_exports, {
2549
3708
  tokyoNightPalette: () => tokyoNightPalette
2550
3709
  });
2551
3710
  module.exports = __toCommonJS(index_exports);
2552
-
2553
- // src/dgmo-router.ts
2554
- init_parser();
2555
- var DGMO_CHART_TYPE_MAP = {
2556
- // Standard charts (via ECharts)
2557
- bar: "echart",
2558
- line: "echart",
2559
- "multi-line": "echart",
2560
- area: "echart",
2561
- pie: "echart",
2562
- doughnut: "echart",
2563
- radar: "echart",
2564
- "polar-area": "echart",
2565
- "bar-stacked": "echart",
2566
- // ECharts
2567
- scatter: "echart",
2568
- sankey: "echart",
2569
- chord: "echart",
2570
- function: "echart",
2571
- heatmap: "echart",
2572
- funnel: "echart",
2573
- // D3
2574
- slope: "d3",
2575
- wordcloud: "d3",
2576
- arc: "d3",
2577
- timeline: "d3",
2578
- venn: "d3",
2579
- quadrant: "d3",
2580
- sequence: "d3"
2581
- };
2582
- function getDgmoFramework(chartType) {
2583
- return DGMO_CHART_TYPE_MAP[chartType.toLowerCase()] ?? null;
2584
- }
2585
- function parseDgmoChartType(content) {
2586
- const lines = content.split("\n");
2587
- for (const line2 of lines) {
2588
- const trimmed = line2.trim();
2589
- if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//"))
2590
- continue;
2591
- const match = trimmed.match(/^chart\s*:\s*(.+)/i);
2592
- if (match) return match[1].trim().toLowerCase();
2593
- }
2594
- if (looksLikeSequence(content)) return "sequence";
2595
- return null;
2596
- }
3711
+ init_dgmo_router();
2597
3712
 
2598
3713
  // src/chart.ts
2599
3714
  init_colors();
@@ -2955,7 +4070,7 @@ function parseEChart(content, palette) {
2955
4070
  function buildEChartsOption(parsed, palette, isDark) {
2956
4071
  const textColor = palette.text;
2957
4072
  const axisLineColor = palette.border;
2958
- const gridOpacity = isDark ? 0.7 : 0.4;
4073
+ const gridOpacity = isDark ? 0.7 : 0.55;
2959
4074
  const colors = getSeriesColors(palette);
2960
4075
  if (parsed.error) {
2961
4076
  return {};
@@ -3661,7 +4776,7 @@ function buildEChartsOptionFromChart(parsed, palette, isDark) {
3661
4776
  const textColor = palette.text;
3662
4777
  const axisLineColor = palette.border;
3663
4778
  const splitLineColor = palette.border;
3664
- const gridOpacity = isDark ? 0.7 : 0.4;
4779
+ const gridOpacity = isDark ? 0.7 : 0.55;
3665
4780
  const colors = getSeriesColors(palette);
3666
4781
  const titleConfig = parsed.title ? {
3667
4782
  text: parsed.title,
@@ -4074,8 +5189,8 @@ async function renderEChartsForExport(content, theme, palette) {
4074
5189
 
4075
5190
  // src/d3.ts
4076
5191
  var d3Scale = __toESM(require("d3-scale"), 1);
4077
- var d3Selection2 = __toESM(require("d3-selection"), 1);
4078
- var d3Shape = __toESM(require("d3-shape"), 1);
5192
+ var d3Selection3 = __toESM(require("d3-selection"), 1);
5193
+ var d3Shape2 = __toESM(require("d3-shape"), 1);
4079
5194
  var d3Array = __toESM(require("d3-array"), 1);
4080
5195
  var import_d3_cloud = __toESM(require("d3-cloud"), 1);
4081
5196
  init_fonts();
@@ -4182,10 +5297,10 @@ function parseD3(content, palette) {
4182
5297
  let currentArcGroup = null;
4183
5298
  let currentTimelineGroup = null;
4184
5299
  for (let i = 0; i < lines.length; i++) {
4185
- const line2 = lines[i].trim();
5300
+ const line3 = lines[i].trim();
4186
5301
  const lineNumber = i + 1;
4187
- if (!line2) continue;
4188
- const sectionMatch = line2.match(/^#{2,}\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/);
5302
+ if (!line3) continue;
5303
+ const sectionMatch = line3.match(/^#{2,}\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/);
4189
5304
  if (sectionMatch) {
4190
5305
  if (result.type === "arc") {
4191
5306
  const name = sectionMatch[1].trim();
@@ -4200,11 +5315,11 @@ function parseD3(content, palette) {
4200
5315
  }
4201
5316
  continue;
4202
5317
  }
4203
- if (line2.startsWith("#") || line2.startsWith("//")) {
5318
+ if (line3.startsWith("#") || line3.startsWith("//")) {
4204
5319
  continue;
4205
5320
  }
4206
5321
  if (result.type === "arc") {
4207
- const linkMatch = line2.match(
5322
+ const linkMatch = line3.match(
4208
5323
  /^(.+?)\s*->\s*(.+?)(?:\(([^)]+)\))?\s*(?::\s*(\d+(?:\.\d+)?))?$/
4209
5324
  );
4210
5325
  if (linkMatch) {
@@ -4234,7 +5349,7 @@ function parseD3(content, palette) {
4234
5349
  }
4235
5350
  }
4236
5351
  if (result.type === "timeline") {
4237
- const eraMatch = line2.match(
5352
+ const eraMatch = line3.match(
4238
5353
  /^era\s+(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
4239
5354
  );
4240
5355
  if (eraMatch) {
@@ -4247,7 +5362,7 @@ function parseD3(content, palette) {
4247
5362
  });
4248
5363
  continue;
4249
5364
  }
4250
- const markerMatch = line2.match(
5365
+ const markerMatch = line3.match(
4251
5366
  /^marker\s+(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
4252
5367
  );
4253
5368
  if (markerMatch) {
@@ -4262,7 +5377,7 @@ function parseD3(content, palette) {
4262
5377
  }
4263
5378
  }
4264
5379
  if (result.type === "timeline") {
4265
- const durationMatch = line2.match(
5380
+ const durationMatch = line3.match(
4266
5381
  /^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\?)?(\d+(?:\.\d{1,2})?)([dwmy])\s*:\s*(.+)$/
4267
5382
  );
4268
5383
  if (durationMatch) {
@@ -4281,7 +5396,7 @@ function parseD3(content, palette) {
4281
5396
  });
4282
5397
  continue;
4283
5398
  }
4284
- const rangeMatch = line2.match(
5399
+ const rangeMatch = line3.match(
4285
5400
  /^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\?)?(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
4286
5401
  );
4287
5402
  if (rangeMatch) {
@@ -4295,7 +5410,7 @@ function parseD3(content, palette) {
4295
5410
  });
4296
5411
  continue;
4297
5412
  }
4298
- const pointMatch = line2.match(
5413
+ const pointMatch = line3.match(
4299
5414
  /^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
4300
5415
  );
4301
5416
  if (pointMatch) {
@@ -4310,7 +5425,7 @@ function parseD3(content, palette) {
4310
5425
  }
4311
5426
  }
4312
5427
  if (result.type === "venn") {
4313
- const overlapMatch = line2.match(
5428
+ const overlapMatch = line3.match(
4314
5429
  /^(.+?&.+?)\s*:\s*(\d+(?:\.\d+)?)\s*(?:"([^"]*)")?\s*$/
4315
5430
  );
4316
5431
  if (overlapMatch) {
@@ -4320,7 +5435,7 @@ function parseD3(content, palette) {
4320
5435
  result.vennOverlaps.push({ sets, size, label, lineNumber });
4321
5436
  continue;
4322
5437
  }
4323
- const setMatch = line2.match(
5438
+ const setMatch = line3.match(
4324
5439
  /^(.+?)(?:\(([^)]+)\))?\s*:\s*(\d+(?:\.\d+)?)\s*(?:"([^"]*)")?\s*$/
4325
5440
  );
4326
5441
  if (setMatch) {
@@ -4333,7 +5448,7 @@ function parseD3(content, palette) {
4333
5448
  }
4334
5449
  }
4335
5450
  if (result.type === "quadrant") {
4336
- const xAxisMatch = line2.match(/^x-axis\s*:\s*(.+)/i);
5451
+ const xAxisMatch = line3.match(/^x-axis\s*:\s*(.+)/i);
4337
5452
  if (xAxisMatch) {
4338
5453
  const parts = xAxisMatch[1].split(",").map((s) => s.trim());
4339
5454
  if (parts.length >= 2) {
@@ -4342,7 +5457,7 @@ function parseD3(content, palette) {
4342
5457
  }
4343
5458
  continue;
4344
5459
  }
4345
- const yAxisMatch = line2.match(/^y-axis\s*:\s*(.+)/i);
5460
+ const yAxisMatch = line3.match(/^y-axis\s*:\s*(.+)/i);
4346
5461
  if (yAxisMatch) {
4347
5462
  const parts = yAxisMatch[1].split(",").map((s) => s.trim());
4348
5463
  if (parts.length >= 2) {
@@ -4352,7 +5467,7 @@ function parseD3(content, palette) {
4352
5467
  continue;
4353
5468
  }
4354
5469
  const quadrantLabelRe = /^(top-right|top-left|bottom-left|bottom-right)\s*:\s*(.+)/i;
4355
- const quadrantMatch = line2.match(quadrantLabelRe);
5470
+ const quadrantMatch = line3.match(quadrantLabelRe);
4356
5471
  if (quadrantMatch) {
4357
5472
  const position = quadrantMatch[1].toLowerCase();
4358
5473
  const labelPart = quadrantMatch[2].trim();
@@ -4368,7 +5483,7 @@ function parseD3(content, palette) {
4368
5483
  result.quadrantLabels.bottomRight = label;
4369
5484
  continue;
4370
5485
  }
4371
- const pointMatch = line2.match(
5486
+ const pointMatch = line3.match(
4372
5487
  /^(.+?):\s*([0-9]*\.?[0-9]+)\s*,\s*([0-9]*\.?[0-9]+)\s*$/
4373
5488
  );
4374
5489
  if (pointMatch) {
@@ -4385,13 +5500,13 @@ function parseD3(content, palette) {
4385
5500
  continue;
4386
5501
  }
4387
5502
  }
4388
- const colonIndex = line2.indexOf(":");
5503
+ const colonIndex = line3.indexOf(":");
4389
5504
  if (colonIndex !== -1) {
4390
- const rawKey = line2.substring(0, colonIndex).trim();
5505
+ const rawKey = line3.substring(0, colonIndex).trim();
4391
5506
  const key = rawKey.toLowerCase();
4392
5507
  const colorMatch = rawKey.match(/^(.+?)\(([^)]+)\)\s*$/);
4393
5508
  if (key === "chart") {
4394
- const value = line2.substring(colonIndex + 1).trim().toLowerCase();
5509
+ const value = line3.substring(colonIndex + 1).trim().toLowerCase();
4395
5510
  if (value === "slope" || value === "wordcloud" || value === "arc" || value === "timeline" || value === "venn" || value === "quadrant" || value === "sequence") {
4396
5511
  result.type = value;
4397
5512
  } else {
@@ -4401,35 +5516,35 @@ function parseD3(content, palette) {
4401
5516
  continue;
4402
5517
  }
4403
5518
  if (key === "title") {
4404
- result.title = line2.substring(colonIndex + 1).trim();
5519
+ result.title = line3.substring(colonIndex + 1).trim();
4405
5520
  if (result.type === "quadrant") {
4406
5521
  result.quadrantTitleLineNumber = lineNumber;
4407
5522
  }
4408
5523
  continue;
4409
5524
  }
4410
5525
  if (key === "orientation") {
4411
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5526
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4412
5527
  if (v === "horizontal" || v === "vertical") {
4413
5528
  result.orientation = v;
4414
5529
  }
4415
5530
  continue;
4416
5531
  }
4417
5532
  if (key === "order") {
4418
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5533
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4419
5534
  if (v === "name" || v === "group" || v === "degree") {
4420
5535
  result.arcOrder = v;
4421
5536
  }
4422
5537
  continue;
4423
5538
  }
4424
5539
  if (key === "sort") {
4425
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5540
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4426
5541
  if (v === "time" || v === "group") {
4427
5542
  result.timelineSort = v;
4428
5543
  }
4429
5544
  continue;
4430
5545
  }
4431
5546
  if (key === "swimlanes") {
4432
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5547
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4433
5548
  if (v === "on") {
4434
5549
  result.timelineSwimlanes = true;
4435
5550
  } else if (v === "off") {
@@ -4438,7 +5553,7 @@ function parseD3(content, palette) {
4438
5553
  continue;
4439
5554
  }
4440
5555
  if (key === "values") {
4441
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5556
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4442
5557
  if (v === "off") {
4443
5558
  result.vennShowValues = false;
4444
5559
  } else if (v === "on") {
@@ -4447,21 +5562,21 @@ function parseD3(content, palette) {
4447
5562
  continue;
4448
5563
  }
4449
5564
  if (key === "rotate") {
4450
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5565
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4451
5566
  if (v === "none" || v === "mixed" || v === "angled") {
4452
5567
  result.cloudOptions.rotate = v;
4453
5568
  }
4454
5569
  continue;
4455
5570
  }
4456
5571
  if (key === "max") {
4457
- const v = parseInt(line2.substring(colonIndex + 1).trim(), 10);
5572
+ const v = parseInt(line3.substring(colonIndex + 1).trim(), 10);
4458
5573
  if (!isNaN(v) && v > 0) {
4459
5574
  result.cloudOptions.max = v;
4460
5575
  }
4461
5576
  continue;
4462
5577
  }
4463
5578
  if (key === "size") {
4464
- const v = line2.substring(colonIndex + 1).trim();
5579
+ const v = line3.substring(colonIndex + 1).trim();
4465
5580
  const parts = v.split(",").map((s) => parseInt(s.trim(), 10));
4466
5581
  if (parts.length === 2 && parts.every((n) => !isNaN(n) && n > 0) && parts[0] < parts[1]) {
4467
5582
  result.cloudOptions.minSize = parts[0];
@@ -4471,7 +5586,7 @@ function parseD3(content, palette) {
4471
5586
  }
4472
5587
  const labelPart = colorMatch ? colorMatch[1].trim() : rawKey;
4473
5588
  const colorPart = colorMatch ? resolveColor(colorMatch[2].trim(), palette) : null;
4474
- const valuePart = line2.substring(colonIndex + 1).trim();
5589
+ const valuePart = line3.substring(colonIndex + 1).trim();
4475
5590
  const values = valuePart.split(",").map((v) => v.trim());
4476
5591
  const numericValues = [];
4477
5592
  let allNumeric = true;
@@ -4502,15 +5617,15 @@ function parseD3(content, palette) {
4502
5617
  }
4503
5618
  }
4504
5619
  if (result.type === "wordcloud") {
4505
- if (colonIndex === -1 && !line2.includes(" ")) {
4506
- result.words.push({ text: line2, weight: 10, lineNumber });
5620
+ if (colonIndex === -1 && !line3.includes(" ")) {
5621
+ result.words.push({ text: line3, weight: 10, lineNumber });
4507
5622
  } else {
4508
- freeformLines.push(line2);
5623
+ freeformLines.push(line3);
4509
5624
  }
4510
5625
  continue;
4511
5626
  }
4512
- if (result.periods.length === 0 && line2.includes(",") && !line2.includes(":")) {
4513
- const periods = line2.split(",").map((p) => p.trim()).filter(Boolean);
5627
+ if (result.periods.length === 0 && line3.includes(",") && !line3.includes(":")) {
5628
+ const periods = line3.split(",").map((p) => p.trim()).filter(Boolean);
4514
5629
  if (periods.length >= 2) {
4515
5630
  result.periods = periods;
4516
5631
  continue;
@@ -4733,7 +5848,7 @@ var SLOPE_MARGIN = { top: 80, bottom: 40, left: 80 };
4733
5848
  var SLOPE_LABEL_FONT_SIZE = 14;
4734
5849
  var SLOPE_CHAR_WIDTH = 8;
4735
5850
  function renderSlopeChart(container, parsed, palette, isDark, onClickItem, exportDims) {
4736
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
5851
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
4737
5852
  const { periods, data, title } = parsed;
4738
5853
  if (data.length === 0 || periods.length < 2) return;
4739
5854
  const width = exportDims?.width ?? container.clientWidth;
@@ -4760,7 +5875,7 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
4760
5875
  const valuePadding = (maxVal - minVal) * 0.1 || 1;
4761
5876
  const yScale = d3Scale.scaleLinear().domain([minVal - valuePadding, maxVal + valuePadding]).range([innerHeight, 0]);
4762
5877
  const xScale = d3Scale.scalePoint().domain(periods).range([0, innerWidth]).padding(0);
4763
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5878
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
4764
5879
  const g = svg.append("g").attr("transform", `translate(${SLOPE_MARGIN.left},${SLOPE_MARGIN.top})`);
4765
5880
  const tooltip = createTooltip(container, palette, isDark);
4766
5881
  if (title) {
@@ -4771,7 +5886,7 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
4771
5886
  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);
4772
5887
  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");
4773
5888
  }
4774
- const lineGen = d3Shape.line().x((_d, i) => xScale(periods[i])).y((d) => yScale(d));
5889
+ const lineGen = d3Shape2.line().x((_d, i) => xScale(periods[i])).y((d) => yScale(d));
4775
5890
  data.forEach((item, idx) => {
4776
5891
  const color = item.color ?? colors[idx % colors.length];
4777
5892
  const firstVal = item.values[0];
@@ -4834,11 +5949,11 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
4834
5949
  const lineHeight = SLOPE_LABEL_FONT_SIZE * 1.2;
4835
5950
  const totalHeight = (lines.length - 1) * lineHeight;
4836
5951
  const startDy = -totalHeight / 2;
4837
- lines.forEach((line2, li) => {
5952
+ lines.forEach((line3, li) => {
4838
5953
  labelEl.append("tspan").attr("x", lastX + 10).attr(
4839
5954
  "dy",
4840
5955
  li === 0 ? `${startDy + SLOPE_LABEL_FONT_SIZE * 0.35}px` : `${lineHeight}px`
4841
- ).text(line2);
5956
+ ).text(line3);
4842
5957
  });
4843
5958
  }
4844
5959
  });
@@ -4933,7 +6048,7 @@ function orderArcNodes(links, order, groups) {
4933
6048
  }
4934
6049
  var ARC_MARGIN = { top: 60, right: 40, bottom: 60, left: 40 };
4935
6050
  function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, exportDims) {
4936
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
6051
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
4937
6052
  const { links, title, orientation, arcOrder, arcNodeGroups } = parsed;
4938
6053
  if (links.length === 0) return;
4939
6054
  const width = exportDims?.width ?? container.clientWidth;
@@ -4970,7 +6085,7 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
4970
6085
  const values = links.map((l) => l.value);
4971
6086
  const [minVal, maxVal] = d3Array.extent(values);
4972
6087
  const strokeScale = d3Scale.scaleLinear().domain([minVal, maxVal]).range([1.5, 6]);
4973
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6088
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
4974
6089
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
4975
6090
  if (title) {
4976
6091
  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);
@@ -4985,14 +6100,14 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
4985
6100
  function handleMouseEnter(hovered) {
4986
6101
  const connected = neighbors.get(hovered);
4987
6102
  g.selectAll(".arc-link").each(function() {
4988
- const el = d3Selection2.select(this);
6103
+ const el = d3Selection3.select(this);
4989
6104
  const src = el.attr("data-source");
4990
6105
  const tgt = el.attr("data-target");
4991
6106
  const isRelated = src === hovered || tgt === hovered;
4992
6107
  el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
4993
6108
  });
4994
6109
  g.selectAll(".arc-node").each(function() {
4995
- const el = d3Selection2.select(this);
6110
+ const el = d3Selection3.select(this);
4996
6111
  const name = el.attr("data-node");
4997
6112
  const isRelated = name === hovered || connected.has(name);
4998
6113
  el.attr("opacity", isRelated ? 1 : FADE_OPACITY);
@@ -5017,23 +6132,23 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
5017
6132
  const members = groupNodeSets.get(groupName);
5018
6133
  if (!members) return;
5019
6134
  g.selectAll(".arc-link").each(function() {
5020
- const el = d3Selection2.select(this);
6135
+ const el = d3Selection3.select(this);
5021
6136
  const isRelated = members.has(el.attr("data-source")) || members.has(el.attr("data-target"));
5022
6137
  el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
5023
6138
  });
5024
6139
  g.selectAll(".arc-node").each(function() {
5025
- const el = d3Selection2.select(this);
6140
+ const el = d3Selection3.select(this);
5026
6141
  el.attr("opacity", members.has(el.attr("data-node")) ? 1 : FADE_OPACITY);
5027
6142
  });
5028
6143
  g.selectAll(".arc-group-band").each(function() {
5029
- const el = d3Selection2.select(this);
6144
+ const el = d3Selection3.select(this);
5030
6145
  el.attr(
5031
6146
  "fill-opacity",
5032
6147
  el.attr("data-group") === groupName ? 0.18 : 0.03
5033
6148
  );
5034
6149
  });
5035
6150
  g.selectAll(".arc-group-label").each(function() {
5036
- const el = d3Selection2.select(this);
6151
+ const el = d3Selection3.select(this);
5037
6152
  el.attr("fill-opacity", el.attr("data-group") === groupName ? 1 : 0.2);
5038
6153
  });
5039
6154
  }
@@ -5236,7 +6351,14 @@ function computeTimeTicks(domainMin, domainMax, scale, boundaryStart, boundaryEn
5236
6351
  const firstYear = Math.ceil(domainMin);
5237
6352
  const lastYear = Math.floor(domainMax);
5238
6353
  if (lastYear >= firstYear + 1) {
5239
- for (let y = firstYear; y <= lastYear; y++) {
6354
+ const yearSpan = lastYear - firstYear;
6355
+ let step = 1;
6356
+ if (yearSpan > 80) step = 20;
6357
+ else if (yearSpan > 40) step = 10;
6358
+ else if (yearSpan > 20) step = 5;
6359
+ else if (yearSpan > 10) step = 2;
6360
+ const alignedFirst = Math.ceil(firstYear / step) * step;
6361
+ for (let y = alignedFirst; y <= lastYear; y += step) {
5240
6362
  ticks.push({ pos: scale(y), label: String(y) });
5241
6363
  }
5242
6364
  } else if (span > 0.25) {
@@ -5336,7 +6458,7 @@ function showEventDatesOnScale(g, scale, startDate, endDate, innerHeight, accent
5336
6458
  function hideEventDatesOnScale(g) {
5337
6459
  g.selectAll(".tl-event-date").remove();
5338
6460
  g.selectAll(".tl-scale-tick").each(function() {
5339
- const el = d3Selection2.select(this);
6461
+ const el = d3Selection3.select(this);
5340
6462
  const isDashed = el.attr("stroke-dasharray");
5341
6463
  el.attr("opacity", isDashed ? 0.15 : 0.4);
5342
6464
  });
@@ -5394,7 +6516,7 @@ function buildEraTooltipHtml(era) {
5394
6516
  return `<strong>${era.label}</strong><br>${formatDateLabel(era.startDate)} \u2192 ${formatDateLabel(era.endDate)}`;
5395
6517
  }
5396
6518
  function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims) {
5397
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
6519
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
5398
6520
  const {
5399
6521
  timelineEvents,
5400
6522
  timelineGroups,
@@ -5446,13 +6568,13 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5446
6568
  const FADE_OPACITY = 0.1;
5447
6569
  function fadeToGroup(g, groupName) {
5448
6570
  g.selectAll(".tl-event").each(function() {
5449
- const el = d3Selection2.select(this);
6571
+ const el = d3Selection3.select(this);
5450
6572
  const evGroup = el.attr("data-group");
5451
6573
  el.attr("opacity", evGroup === groupName ? 1 : FADE_OPACITY);
5452
6574
  });
5453
6575
  g.selectAll(".tl-legend-item, .tl-lane-header").each(
5454
6576
  function() {
5455
- const el = d3Selection2.select(this);
6577
+ const el = d3Selection3.select(this);
5456
6578
  const name = el.attr("data-group");
5457
6579
  el.attr("opacity", name === groupName ? 1 : FADE_OPACITY);
5458
6580
  }
@@ -5464,7 +6586,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5464
6586
  }
5465
6587
  function fadeToEra(g, eraStart, eraEnd) {
5466
6588
  g.selectAll(".tl-event").each(function() {
5467
- const el = d3Selection2.select(this);
6589
+ const el = d3Selection3.select(this);
5468
6590
  const date = parseFloat(el.attr("data-date"));
5469
6591
  const endDate = el.attr("data-end-date");
5470
6592
  const evEnd = endDate ? parseFloat(endDate) : date;
@@ -5476,14 +6598,14 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5476
6598
  FADE_OPACITY
5477
6599
  );
5478
6600
  g.selectAll(".tl-era").each(function() {
5479
- const el = d3Selection2.select(this);
6601
+ const el = d3Selection3.select(this);
5480
6602
  const s = parseFloat(el.attr("data-era-start"));
5481
6603
  const e = parseFloat(el.attr("data-era-end"));
5482
6604
  const isSelf = s === eraStart && e === eraEnd;
5483
6605
  el.attr("opacity", isSelf ? 1 : FADE_OPACITY);
5484
6606
  });
5485
6607
  g.selectAll(".tl-marker").each(function() {
5486
- const el = d3Selection2.select(this);
6608
+ const el = d3Selection3.select(this);
5487
6609
  const date = parseFloat(el.attr("data-marker-date"));
5488
6610
  const inside = date >= eraStart && date <= eraEnd;
5489
6611
  el.attr("opacity", inside ? 1 : FADE_OPACITY);
@@ -5515,7 +6637,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5515
6637
  const innerHeight = height - margin.top - margin.bottom;
5516
6638
  const laneWidth = innerWidth / laneCount;
5517
6639
  const yScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
5518
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6640
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5519
6641
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
5520
6642
  if (title) {
5521
6643
  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);
@@ -5609,7 +6731,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5609
6731
  const axisX = 20;
5610
6732
  const yScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
5611
6733
  const sorted = timelineEvents.slice().sort((a, b) => parseTimelineDate(a.date) - parseTimelineDate(b.date));
5612
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6734
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5613
6735
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
5614
6736
  if (title) {
5615
6737
  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);
@@ -5732,7 +6854,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5732
6854
  const totalGaps = (lanes.length - 1) * GROUP_GAP;
5733
6855
  const rowH = Math.min(28, (innerHeight - totalGaps) / totalEventRows);
5734
6856
  const xScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
5735
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6857
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5736
6858
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
5737
6859
  if (title) {
5738
6860
  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);
@@ -5836,7 +6958,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5836
6958
  if (ev.uncertain) {
5837
6959
  const gradientId = `uncertain-${ev.lineNumber}`;
5838
6960
  const defs = svg.select("defs").node() || svg.append("defs").node();
5839
- d3Selection2.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
6961
+ d3Selection3.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
5840
6962
  { offset: "0%", opacity: 1 },
5841
6963
  { offset: "80%", opacity: 1 },
5842
6964
  { offset: "100%", opacity: 0 }
@@ -5877,7 +6999,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5877
6999
  const innerHeight = height - margin.top - margin.bottom;
5878
7000
  const rowH = Math.min(28, innerHeight / sorted.length);
5879
7001
  const xScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
5880
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7002
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5881
7003
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
5882
7004
  if (title) {
5883
7005
  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);
@@ -5975,7 +7097,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5975
7097
  if (ev.uncertain) {
5976
7098
  const gradientId = `uncertain-ts-${ev.lineNumber}`;
5977
7099
  const defs = svg.select("defs").node() || svg.append("defs").node();
5978
- d3Selection2.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
7100
+ d3Selection3.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
5979
7101
  { offset: "0%", opacity: 1 },
5980
7102
  { offset: "80%", opacity: 1 },
5981
7103
  { offset: "100%", opacity: 0 }
@@ -6008,7 +7130,7 @@ function getRotateFn(mode) {
6008
7130
  return () => 0;
6009
7131
  }
6010
7132
  function renderWordCloud(container, parsed, palette, _isDark, onClickItem, exportDims) {
6011
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
7133
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
6012
7134
  const { words, title, cloudOptions } = parsed;
6013
7135
  if (words.length === 0) return;
6014
7136
  const width = exportDims?.width ?? container.clientWidth;
@@ -6029,7 +7151,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
6029
7151
  return minSize + t * (maxSize - minSize);
6030
7152
  };
6031
7153
  const rotateFn = getRotateFn(cloudOptions.rotate);
6032
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7154
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6033
7155
  if (title) {
6034
7156
  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);
6035
7157
  }
@@ -6052,7 +7174,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
6052
7174
  }
6053
7175
  function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
6054
7176
  return new Promise((resolve) => {
6055
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
7177
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
6056
7178
  const { words, title, cloudOptions } = parsed;
6057
7179
  if (words.length === 0) {
6058
7180
  resolve();
@@ -6079,7 +7201,7 @@ function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
6079
7201
  return minSize + t * (maxSize - minSize);
6080
7202
  };
6081
7203
  const rotateFn = getRotateFn(cloudOptions.rotate);
6082
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7204
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6083
7205
  if (title) {
6084
7206
  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);
6085
7207
  }
@@ -6223,7 +7345,7 @@ function circlePathD(cx, cy, r) {
6223
7345
  return `M${cx - r},${cy} A${r},${r} 0 1,0 ${cx + r},${cy} A${r},${r} 0 1,0 ${cx - r},${cy} Z`;
6224
7346
  }
6225
7347
  function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims) {
6226
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
7348
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
6227
7349
  const { vennSets, vennOverlaps, vennShowValues, title } = parsed;
6228
7350
  if (vennSets.length < 2) return;
6229
7351
  const width = exportDims?.width ?? container.clientWidth;
@@ -6288,7 +7410,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
6288
7410
  const setColors = vennSets.map(
6289
7411
  (s, i) => s.color ?? colors[i % colors.length]
6290
7412
  );
6291
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7413
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6292
7414
  const tooltip = createTooltip(container, palette, isDark);
6293
7415
  if (title) {
6294
7416
  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);
@@ -6434,7 +7556,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
6434
7556
  });
6435
7557
  }
6436
7558
  function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportDims) {
6437
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
7559
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
6438
7560
  const {
6439
7561
  title,
6440
7562
  quadrantLabels,
@@ -6466,7 +7588,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6466
7588
  const chartHeight = height - margin.top - margin.bottom;
6467
7589
  const xScale = d3Scale.scaleLinear().domain([0, 1]).range([0, chartWidth]);
6468
7590
  const yScale = d3Scale.scaleLinear().domain([0, 1]).range([chartHeight, 0]);
6469
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7591
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6470
7592
  const tooltip = createTooltip(container, palette, isDark);
6471
7593
  if (title) {
6472
7594
  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(
@@ -6475,9 +7597,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6475
7597
  ).text(title);
6476
7598
  if (onClickItem && quadrantTitleLineNumber) {
6477
7599
  titleText.on("click", () => onClickItem(quadrantTitleLineNumber)).on("mouseenter", function() {
6478
- d3Selection2.select(this).attr("opacity", 0.7);
7600
+ d3Selection3.select(this).attr("opacity", 0.7);
6479
7601
  }).on("mouseleave", function() {
6480
- d3Selection2.select(this).attr("opacity", 1);
7602
+ d3Selection3.select(this).attr("opacity", 1);
6481
7603
  });
6482
7604
  }
6483
7605
  }
@@ -6560,9 +7682,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6560
7682
  quadrantLabelTexts.on("click", (_, d) => {
6561
7683
  if (d.label?.lineNumber) onClickItem(d.label.lineNumber);
6562
7684
  }).on("mouseenter", function() {
6563
- d3Selection2.select(this).attr("opacity", 0.7);
7685
+ d3Selection3.select(this).attr("opacity", 0.7);
6564
7686
  }).on("mouseleave", function() {
6565
- d3Selection2.select(this).attr("opacity", 1);
7687
+ d3Selection3.select(this).attr("opacity", 1);
6566
7688
  });
6567
7689
  }
6568
7690
  if (quadrantXAxis) {
@@ -6577,9 +7699,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6577
7699
  if (onClickItem && quadrantXAxisLineNumber) {
6578
7700
  [xLowLabel, xHighLabel].forEach((label) => {
6579
7701
  label.on("click", () => onClickItem(quadrantXAxisLineNumber)).on("mouseenter", function() {
6580
- d3Selection2.select(this).attr("opacity", 0.7);
7702
+ d3Selection3.select(this).attr("opacity", 0.7);
6581
7703
  }).on("mouseleave", function() {
6582
- d3Selection2.select(this).attr("opacity", 1);
7704
+ d3Selection3.select(this).attr("opacity", 1);
6583
7705
  });
6584
7706
  });
6585
7707
  }
@@ -6598,9 +7720,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6598
7720
  if (onClickItem && quadrantYAxisLineNumber) {
6599
7721
  [yLowLabel, yHighLabel].forEach((label) => {
6600
7722
  label.on("click", () => onClickItem(quadrantYAxisLineNumber)).on("mouseenter", function() {
6601
- d3Selection2.select(this).attr("opacity", 0.7);
7723
+ d3Selection3.select(this).attr("opacity", 0.7);
6602
7724
  }).on("mouseleave", function() {
6603
- d3Selection2.select(this).attr("opacity", 1);
7725
+ d3Selection3.select(this).attr("opacity", 1);
6604
7726
  });
6605
7727
  });
6606
7728
  }
@@ -6648,7 +7770,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6648
7770
  pointsG.selectAll("g.point-group").each(function(_2, i) {
6649
7771
  const pt = quadrantPoints[i];
6650
7772
  const ptQuad = getPointQuadrant(pt.x, pt.y);
6651
- d3Selection2.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
7773
+ d3Selection3.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
6652
7774
  });
6653
7775
  }).on("mouseleave", () => {
6654
7776
  quadrantRects.attr("opacity", 1);
@@ -6663,6 +7785,43 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6663
7785
  var EXPORT_WIDTH = 1200;
6664
7786
  var EXPORT_HEIGHT = 800;
6665
7787
  async function renderD3ForExport(content, theme, palette) {
7788
+ const { parseDgmoChartType: parseDgmoChartType2 } = await Promise.resolve().then(() => (init_dgmo_router(), dgmo_router_exports));
7789
+ const detectedType = parseDgmoChartType2(content);
7790
+ if (detectedType === "flowchart") {
7791
+ const { parseFlowchart: parseFlowchart2 } = await Promise.resolve().then(() => (init_flowchart_parser(), flowchart_parser_exports));
7792
+ const { layoutGraph: layoutGraph2 } = await Promise.resolve().then(() => (init_layout(), layout_exports));
7793
+ const { renderFlowchart: renderFlowchart2 } = await Promise.resolve().then(() => (init_flowchart_renderer(), flowchart_renderer_exports));
7794
+ const isDark2 = theme === "dark";
7795
+ const { getPalette: getPalette3 } = await Promise.resolve().then(() => (init_palettes(), palettes_exports));
7796
+ const effectivePalette2 = palette ?? (isDark2 ? getPalette3("nord").dark : getPalette3("nord").light);
7797
+ const fcParsed = parseFlowchart2(content, effectivePalette2);
7798
+ if (fcParsed.error || fcParsed.nodes.length === 0) return "";
7799
+ const layout = layoutGraph2(fcParsed);
7800
+ const container2 = document.createElement("div");
7801
+ container2.style.width = `${EXPORT_WIDTH}px`;
7802
+ container2.style.height = `${EXPORT_HEIGHT}px`;
7803
+ container2.style.position = "absolute";
7804
+ container2.style.left = "-9999px";
7805
+ document.body.appendChild(container2);
7806
+ try {
7807
+ renderFlowchart2(container2, fcParsed, layout, effectivePalette2, isDark2, void 0, {
7808
+ width: EXPORT_WIDTH,
7809
+ height: EXPORT_HEIGHT
7810
+ });
7811
+ const svgEl = container2.querySelector("svg");
7812
+ if (!svgEl) return "";
7813
+ if (theme === "transparent") {
7814
+ svgEl.style.background = "none";
7815
+ } else if (!svgEl.style.background) {
7816
+ svgEl.style.background = effectivePalette2.bg;
7817
+ }
7818
+ svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
7819
+ svgEl.style.fontFamily = FONT_FAMILY;
7820
+ return svgEl.outerHTML;
7821
+ } finally {
7822
+ document.body.removeChild(container2);
7823
+ }
7824
+ }
6666
7825
  const parsed = parseD3(content, palette);
6667
7826
  if (parsed.error && parsed.type !== "sequence") {
6668
7827
  const looksLikeSequence2 = /->|~>|<-/.test(content);
@@ -6757,17 +7916,17 @@ function parseQuadrant(content) {
6757
7916
  };
6758
7917
  const lines = content.split("\n");
6759
7918
  for (let i = 0; i < lines.length; i++) {
6760
- const line2 = lines[i].trim();
7919
+ const line3 = lines[i].trim();
6761
7920
  const lineNumber = i + 1;
6762
- if (!line2 || line2.startsWith("#") || line2.startsWith("//")) continue;
6763
- if (/^chart\s*:/i.test(line2)) continue;
6764
- const titleMatch = line2.match(/^title\s*:\s*(.+)/i);
7921
+ if (!line3 || line3.startsWith("#") || line3.startsWith("//")) continue;
7922
+ if (/^chart\s*:/i.test(line3)) continue;
7923
+ const titleMatch = line3.match(/^title\s*:\s*(.+)/i);
6765
7924
  if (titleMatch) {
6766
7925
  result.title = titleMatch[1].trim();
6767
7926
  result.titleLineNumber = lineNumber;
6768
7927
  continue;
6769
7928
  }
6770
- const xMatch = line2.match(/^x-axis\s*:\s*(.+)/i);
7929
+ const xMatch = line3.match(/^x-axis\s*:\s*(.+)/i);
6771
7930
  if (xMatch) {
6772
7931
  const parts = xMatch[1].split(",").map((s) => s.trim());
6773
7932
  if (parts.length >= 2) {
@@ -6776,7 +7935,7 @@ function parseQuadrant(content) {
6776
7935
  }
6777
7936
  continue;
6778
7937
  }
6779
- const yMatch = line2.match(/^y-axis\s*:\s*(.+)/i);
7938
+ const yMatch = line3.match(/^y-axis\s*:\s*(.+)/i);
6780
7939
  if (yMatch) {
6781
7940
  const parts = yMatch[1].split(",").map((s) => s.trim());
6782
7941
  if (parts.length >= 2) {
@@ -6785,7 +7944,7 @@ function parseQuadrant(content) {
6785
7944
  }
6786
7945
  continue;
6787
7946
  }
6788
- const posMatch = line2.match(
7947
+ const posMatch = line3.match(
6789
7948
  /^(top-right|top-left|bottom-left|bottom-right)\s*:\s*(.+)/i
6790
7949
  );
6791
7950
  if (posMatch) {
@@ -6806,7 +7965,7 @@ function parseQuadrant(content) {
6806
7965
  }
6807
7966
  continue;
6808
7967
  }
6809
- const pointMatch = line2.match(DATA_POINT_RE);
7968
+ const pointMatch = line3.match(DATA_POINT_RE);
6810
7969
  if (pointMatch) {
6811
7970
  const key = pointMatch[1].trim().toLowerCase();
6812
7971
  if (!QUADRANT_POSITIONS.has(key)) {
@@ -6880,6 +8039,9 @@ function buildMermaidQuadrant(parsed, options = {}) {
6880
8039
  }
6881
8040
 
6882
8041
  // src/index.ts
8042
+ init_flowchart_parser();
8043
+ init_layout();
8044
+ init_flowchart_renderer();
6883
8045
  init_renderer();
6884
8046
  init_colors();
6885
8047
  init_palettes();
@@ -6914,7 +8076,10 @@ init_palettes();
6914
8076
  hslToHex,
6915
8077
  inferParticipantType,
6916
8078
  isSequenceBlock,
8079
+ isSequenceNote,
6917
8080
  isValidHex,
8081
+ layoutGraph,
8082
+ looksLikeFlowchart,
6918
8083
  looksLikeSequence,
6919
8084
  mute,
6920
8085
  nord,
@@ -6925,6 +8090,7 @@ init_palettes();
6925
8090
  parseD3,
6926
8091
  parseDgmoChartType,
6927
8092
  parseEChart,
8093
+ parseFlowchart,
6928
8094
  parseQuadrant,
6929
8095
  parseSequenceDgmo,
6930
8096
  parseTimelineDate,
@@ -6932,6 +8098,8 @@ init_palettes();
6932
8098
  renderArcDiagram,
6933
8099
  renderD3ForExport,
6934
8100
  renderEChartsForExport,
8101
+ renderFlowchart,
8102
+ renderFlowchartForExport,
6935
8103
  renderQuadrant,
6936
8104
  renderSequenceDiagram,
6937
8105
  renderSlopeChart,