@diagrammo/dgmo 0.2.6 → 0.2.7

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,105 @@ 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
+ }
459
545
  }
460
546
  continue;
461
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) {
554
+ result.error = `Line ${lineNumber}: note requires a preceding message`;
555
+ return result;
556
+ }
557
+ noteParticipant = lastMsgFrom;
558
+ }
559
+ if (!result.participants.some((p) => p.id === noteParticipant)) {
560
+ result.error = `Line ${lineNumber}: note references unknown participant '${noteParticipant}'`;
561
+ return result;
562
+ }
563
+ const note = {
564
+ kind: "note",
565
+ text: noteSingleMatch[3].trim(),
566
+ position: notePosition,
567
+ participantId: noteParticipant,
568
+ lineNumber
569
+ };
570
+ currentContainer().push(note);
571
+ continue;
572
+ }
573
+ const noteMultiMatch = trimmed.match(NOTE_MULTI);
574
+ if (noteMultiMatch) {
575
+ const notePosition = noteMultiMatch[1]?.toLowerCase() || "right";
576
+ let noteParticipant = noteMultiMatch[2] || null;
577
+ if (!noteParticipant) {
578
+ if (!lastMsgFrom) {
579
+ result.error = `Line ${lineNumber}: note requires a preceding message`;
580
+ return result;
581
+ }
582
+ noteParticipant = lastMsgFrom;
583
+ }
584
+ if (!result.participants.some((p) => p.id === noteParticipant)) {
585
+ result.error = `Line ${lineNumber}: note references unknown participant '${noteParticipant}'`;
586
+ return result;
587
+ }
588
+ const noteLines = [];
589
+ while (i + 1 < lines.length) {
590
+ const nextRaw = lines[i + 1];
591
+ const nextTrimmed = nextRaw.trim();
592
+ if (!nextTrimmed) break;
593
+ const nextIndent = measureIndent(nextRaw);
594
+ if (nextIndent <= indent) break;
595
+ noteLines.push(nextTrimmed);
596
+ i++;
597
+ }
598
+ if (noteLines.length === 0) {
599
+ result.error = `Line ${lineNumber}: multi-line note has no content \u2014 add indented lines or use 'note: text'`;
600
+ return result;
601
+ }
602
+ const note = {
603
+ kind: "note",
604
+ text: noteLines.join("\n"),
605
+ position: notePosition,
606
+ participantId: noteParticipant,
607
+ lineNumber
608
+ };
609
+ currentContainer().push(note);
610
+ continue;
611
+ }
462
612
  }
463
613
  if (!hasExplicitChart && result.messages.length === 0) {
464
- const hasArrows = lines.some((line2) => ARROW_PATTERN.test(line2.trim()));
614
+ const hasArrows = lines.some((line3) => ARROW_PATTERN.test(line3.trim()));
465
615
  if (!hasArrows) {
466
616
  result.error = 'No "chart: sequence" header and no sequence content detected';
467
617
  return result;
@@ -472,13 +622,13 @@ function parseSequenceDgmo(content) {
472
622
  function looksLikeSequence(content) {
473
623
  if (!content) return false;
474
624
  const lines = content.split("\n");
475
- return lines.some((line2) => {
476
- const trimmed = line2.trim();
477
- if (trimmed.startsWith("#") || trimmed.startsWith("//")) return false;
625
+ return lines.some((line3) => {
626
+ const trimmed = line3.trim();
627
+ if (trimmed.startsWith("//")) return false;
478
628
  return ARROW_PATTERN.test(trimmed);
479
629
  });
480
630
  }
481
- var VALID_PARTICIPANT_TYPES, IS_A_PATTERN, POSITION_ONLY_PATTERN, GROUP_HEADING_PATTERN, SECTION_PATTERN, ARROW_PATTERN, ARROW_RETURN_PATTERN, UML_RETURN_PATTERN;
631
+ 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
632
  var init_parser = __esm({
483
633
  "src/sequence/parser.ts"() {
484
634
  "use strict";
@@ -496,11 +646,13 @@ var init_parser = __esm({
496
646
  ]);
497
647
  IS_A_PATTERN = /^(\S+)\s+is\s+an?\s+(\w+)(?:\s+(.+))?$/i;
498
648
  POSITION_ONLY_PATTERN = /^(\S+)\s+position\s+(-?\d+)$/i;
499
- GROUP_HEADING_PATTERN = /^##\s+(\S+?)(?:\((\w+)\))?$/;
500
- SECTION_PATTERN = /^==\s+(.+?)\s*==$/;
649
+ GROUP_HEADING_PATTERN = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
650
+ SECTION_PATTERN = /^==\s+(.+?)(?:\s*==)?\s*$/;
501
651
  ARROW_PATTERN = /\S+\s*(?:->|~>)\s*\S+/;
502
652
  ARROW_RETURN_PATTERN = /^(.+?)\s*<-\s*(.+)$/;
503
653
  UML_RETURN_PATTERN = /^(\w+\([^)]*\))\s*:\s*(.+)$/;
654
+ NOTE_SINGLE = /^note(?:\s+(right|left)\s+of\s+(\S+))?\s*:\s*(.+)$/i;
655
+ NOTE_MULTI = /^note(?:\s+(right|left)\s+of\s+(\S+))?\s*$/i;
504
656
  }
505
657
  });
506
658
 
@@ -579,6 +731,369 @@ var init_colors = __esm({
579
731
  }
580
732
  });
581
733
 
734
+ // src/graph/flowchart-parser.ts
735
+ var flowchart_parser_exports = {};
736
+ __export(flowchart_parser_exports, {
737
+ looksLikeFlowchart: () => looksLikeFlowchart,
738
+ parseFlowchart: () => parseFlowchart
739
+ });
740
+ function measureIndent2(line3) {
741
+ let indent = 0;
742
+ for (const ch of line3) {
743
+ if (ch === " ") indent++;
744
+ else if (ch === " ") indent += 4;
745
+ else break;
746
+ }
747
+ return indent;
748
+ }
749
+ function nodeId(shape, label) {
750
+ return `${shape}:${label.toLowerCase().trim()}`;
751
+ }
752
+ function extractColor(label, palette) {
753
+ const m = label.match(COLOR_SUFFIX_RE);
754
+ if (!m) return { label };
755
+ const colorName = m[1].trim();
756
+ return {
757
+ label: label.substring(0, m.index).trim(),
758
+ color: resolveColor(colorName, palette)
759
+ };
760
+ }
761
+ function parseNodeRef(text, palette) {
762
+ const t = text.trim();
763
+ if (!t) return null;
764
+ let m = t.match(/^\[\[([^\]]+)\]\]$/);
765
+ if (m) {
766
+ const { label, color } = extractColor(m[1].trim(), palette);
767
+ return { id: nodeId("subroutine", label), label, shape: "subroutine", color };
768
+ }
769
+ m = t.match(/^\[([^\]]+)~\]$/);
770
+ if (m) {
771
+ const { label, color } = extractColor(m[1].trim(), palette);
772
+ return { id: nodeId("document", label), label, shape: "document", color };
773
+ }
774
+ m = t.match(/^\[([^\]]+)\]$/);
775
+ if (m) {
776
+ const { label, color } = extractColor(m[1].trim(), palette);
777
+ return { id: nodeId("process", label), label, shape: "process", color };
778
+ }
779
+ m = t.match(/^\((.+)\)$/);
780
+ if (m) {
781
+ const { label, color } = extractColor(m[1].trim(), palette);
782
+ return { id: nodeId("terminal", label), label, shape: "terminal", color };
783
+ }
784
+ m = t.match(/^<([^>]+)>$/);
785
+ if (m) {
786
+ const { label, color } = extractColor(m[1].trim(), palette);
787
+ return { id: nodeId("decision", label), label, shape: "decision", color };
788
+ }
789
+ m = t.match(/^\/([^/]+)\/$/);
790
+ if (m) {
791
+ const { label, color } = extractColor(m[1].trim(), palette);
792
+ return { id: nodeId("io", label), label, shape: "io", color };
793
+ }
794
+ return null;
795
+ }
796
+ function splitArrows(line3) {
797
+ const segments = [];
798
+ const arrowRe = /(?:^|\s)-([^>\s(][^(>]*?)?\s*(?:\(([^)]+)\))?\s*->|(?:^|\s)->/g;
799
+ let lastIndex = 0;
800
+ const arrowPositions = [];
801
+ let searchFrom = 0;
802
+ while (searchFrom < line3.length) {
803
+ const idx = line3.indexOf("->", searchFrom);
804
+ if (idx === -1) break;
805
+ let arrowStart = idx;
806
+ let label;
807
+ let color;
808
+ if (idx > 0 && line3[idx - 1] !== " " && line3[idx - 1] !== " ") {
809
+ let scanBack = idx - 1;
810
+ while (scanBack > 0 && line3[scanBack] !== "-") {
811
+ scanBack--;
812
+ }
813
+ if (line3[scanBack] === "-" && (scanBack === 0 || /\s/.test(line3[scanBack - 1]))) {
814
+ let arrowContent = line3.substring(scanBack + 1, idx);
815
+ if (arrowContent.endsWith("-")) arrowContent = arrowContent.slice(0, -1);
816
+ const colorMatch = arrowContent.match(/\(([^)]+)\)\s*$/);
817
+ if (colorMatch) {
818
+ color = colorMatch[1].trim();
819
+ const labelPart = arrowContent.substring(0, colorMatch.index).trim();
820
+ if (labelPart) label = labelPart;
821
+ } else {
822
+ const labelPart = arrowContent.trim();
823
+ if (labelPart) label = labelPart;
824
+ }
825
+ arrowStart = scanBack;
826
+ }
827
+ }
828
+ arrowPositions.push({ start: arrowStart, end: idx + 2, label, color });
829
+ searchFrom = idx + 2;
830
+ }
831
+ if (arrowPositions.length === 0) {
832
+ return [line3];
833
+ }
834
+ for (let i = 0; i < arrowPositions.length; i++) {
835
+ const arrow = arrowPositions[i];
836
+ const beforeText = line3.substring(lastIndex, arrow.start).trim();
837
+ if (beforeText || i === 0) {
838
+ segments.push(beforeText);
839
+ }
840
+ let arrowToken = "->";
841
+ if (arrow.label && arrow.color) arrowToken = `-${arrow.label}(${arrow.color})->`;
842
+ else if (arrow.label) arrowToken = `-${arrow.label}->`;
843
+ else if (arrow.color) arrowToken = `-(${arrow.color})->`;
844
+ segments.push(arrowToken);
845
+ lastIndex = arrow.end;
846
+ }
847
+ const remaining = line3.substring(lastIndex).trim();
848
+ if (remaining) {
849
+ segments.push(remaining);
850
+ }
851
+ return segments;
852
+ }
853
+ function parseArrowToken(token, palette) {
854
+ if (token === "->") return {};
855
+ const colorOnly = token.match(/^-\(([^)]+)\)->$/);
856
+ if (colorOnly) {
857
+ return { color: resolveColor(colorOnly[1].trim(), palette) };
858
+ }
859
+ const m = token.match(/^-(.+?)(?:\(([^)]+)\))?->$/);
860
+ if (m) {
861
+ const label = m[1]?.trim() || void 0;
862
+ const color = m[2] ? resolveColor(m[2].trim(), palette) : void 0;
863
+ return { label, color };
864
+ }
865
+ return {};
866
+ }
867
+ function parseFlowchart(content, palette) {
868
+ const lines = content.split("\n");
869
+ const result = {
870
+ type: "flowchart",
871
+ direction: "TB",
872
+ nodes: [],
873
+ edges: []
874
+ };
875
+ const nodeMap = /* @__PURE__ */ new Map();
876
+ const indentStack = [];
877
+ let currentGroup = null;
878
+ const groups = [];
879
+ let contentStarted = false;
880
+ function getOrCreateNode(ref, lineNumber) {
881
+ const existing = nodeMap.get(ref.id);
882
+ if (existing) return existing;
883
+ const node = {
884
+ id: ref.id,
885
+ label: ref.label,
886
+ shape: ref.shape,
887
+ lineNumber,
888
+ ...ref.color && { color: ref.color },
889
+ ...currentGroup && { group: currentGroup.id }
890
+ };
891
+ nodeMap.set(ref.id, node);
892
+ result.nodes.push(node);
893
+ if (currentGroup && !currentGroup.nodeIds.includes(ref.id)) {
894
+ currentGroup.nodeIds.push(ref.id);
895
+ }
896
+ return node;
897
+ }
898
+ function addEdge(sourceId, targetId, lineNumber, label, color) {
899
+ const edge = {
900
+ source: sourceId,
901
+ target: targetId,
902
+ lineNumber,
903
+ ...label && { label },
904
+ ...color && { color }
905
+ };
906
+ result.edges.push(edge);
907
+ }
908
+ function processContentLine(trimmed, lineNumber, indent) {
909
+ contentStarted = true;
910
+ while (indentStack.length > 0) {
911
+ const top = indentStack[indentStack.length - 1];
912
+ if (top.indent >= indent) {
913
+ indentStack.pop();
914
+ } else {
915
+ break;
916
+ }
917
+ }
918
+ const implicitSourceId = indentStack.length > 0 ? indentStack[indentStack.length - 1].nodeId : null;
919
+ const segments = splitArrows(trimmed);
920
+ if (segments.length === 1) {
921
+ const ref = parseNodeRef(segments[0], palette);
922
+ if (ref) {
923
+ const node = getOrCreateNode(ref, lineNumber);
924
+ indentStack.push({ nodeId: node.id, indent });
925
+ return node.id;
926
+ }
927
+ return null;
928
+ }
929
+ let lastNodeId = null;
930
+ let pendingArrow = null;
931
+ for (let i = 0; i < segments.length; i++) {
932
+ const seg = segments[i];
933
+ if (seg === "->" || /^-.+->$/.test(seg)) {
934
+ pendingArrow = parseArrowToken(seg, palette);
935
+ continue;
936
+ }
937
+ const ref = parseNodeRef(seg, palette);
938
+ if (!ref) continue;
939
+ const node = getOrCreateNode(ref, lineNumber);
940
+ if (pendingArrow !== null) {
941
+ const sourceId = lastNodeId ?? implicitSourceId;
942
+ if (sourceId) {
943
+ addEdge(
944
+ sourceId,
945
+ node.id,
946
+ lineNumber,
947
+ pendingArrow.label,
948
+ pendingArrow.color
949
+ );
950
+ }
951
+ pendingArrow = null;
952
+ } else if (lastNodeId === null && implicitSourceId === null) {
953
+ }
954
+ lastNodeId = node.id;
955
+ }
956
+ if (pendingArrow !== null && lastNodeId === null && implicitSourceId) {
957
+ }
958
+ if (segments.length >= 2 && segments[0] === "" && implicitSourceId && lastNodeId) {
959
+ }
960
+ if (lastNodeId) {
961
+ indentStack.push({ nodeId: lastNodeId, indent });
962
+ }
963
+ return lastNodeId;
964
+ }
965
+ for (let i = 0; i < lines.length; i++) {
966
+ const raw = lines[i];
967
+ const trimmed = raw.trim();
968
+ const lineNumber = i + 1;
969
+ const indent = measureIndent2(raw);
970
+ if (!trimmed) continue;
971
+ if (trimmed.startsWith("//")) continue;
972
+ const groupMatch = trimmed.match(GROUP_HEADING_RE);
973
+ if (groupMatch) {
974
+ const groupLabel = groupMatch[1].trim();
975
+ const groupColorName = groupMatch[2]?.trim();
976
+ const groupColor = groupColorName ? resolveColor(groupColorName, palette) : void 0;
977
+ currentGroup = {
978
+ id: `group:${groupLabel.toLowerCase()}`,
979
+ label: groupLabel,
980
+ nodeIds: [],
981
+ lineNumber,
982
+ ...groupColor && { color: groupColor }
983
+ };
984
+ groups.push(currentGroup);
985
+ continue;
986
+ }
987
+ if (!contentStarted && trimmed.includes(":") && !trimmed.includes("->")) {
988
+ const colonIdx = trimmed.indexOf(":");
989
+ const key = trimmed.substring(0, colonIdx).trim().toLowerCase();
990
+ const value = trimmed.substring(colonIdx + 1).trim();
991
+ if (key === "chart") {
992
+ if (value.toLowerCase() !== "flowchart") {
993
+ result.error = `Line ${lineNumber}: Expected chart type "flowchart", got "${value}"`;
994
+ return result;
995
+ }
996
+ continue;
997
+ }
998
+ if (key === "title") {
999
+ result.title = value;
1000
+ continue;
1001
+ }
1002
+ if (key === "direction") {
1003
+ const dir = value.toUpperCase();
1004
+ if (dir === "TB" || dir === "LR") {
1005
+ result.direction = dir;
1006
+ }
1007
+ continue;
1008
+ }
1009
+ continue;
1010
+ }
1011
+ processContentLine(trimmed, lineNumber, indent);
1012
+ }
1013
+ if (groups.length > 0) result.groups = groups;
1014
+ if (result.nodes.length === 0 && !result.error) {
1015
+ result.error = "No nodes found. Add flowchart content with shape syntax like [Process] or (Start).";
1016
+ }
1017
+ return result;
1018
+ }
1019
+ function looksLikeFlowchart(content) {
1020
+ if (!content.includes("->")) return false;
1021
+ const hasShapeDelimiter = /\[[^\]]+\]/.test(content) || /\([^)]+\)/.test(content) || /<[^>]+>/.test(content) || /\/[^/]+\//.test(content);
1022
+ if (!hasShapeDelimiter) return false;
1023
+ const shapeNearArrow = /[\])][ \t]*-.*->/.test(content) || // shape ] or ) followed by arrow
1024
+ /->[ \t]*[\[(<\/]/.test(content);
1025
+ return shapeNearArrow;
1026
+ }
1027
+ var COLOR_SUFFIX_RE, GROUP_HEADING_RE;
1028
+ var init_flowchart_parser = __esm({
1029
+ "src/graph/flowchart-parser.ts"() {
1030
+ "use strict";
1031
+ init_colors();
1032
+ COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
1033
+ GROUP_HEADING_RE = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
1034
+ }
1035
+ });
1036
+
1037
+ // src/dgmo-router.ts
1038
+ var dgmo_router_exports = {};
1039
+ __export(dgmo_router_exports, {
1040
+ DGMO_CHART_TYPE_MAP: () => DGMO_CHART_TYPE_MAP,
1041
+ getDgmoFramework: () => getDgmoFramework,
1042
+ parseDgmoChartType: () => parseDgmoChartType
1043
+ });
1044
+ function getDgmoFramework(chartType) {
1045
+ return DGMO_CHART_TYPE_MAP[chartType.toLowerCase()] ?? null;
1046
+ }
1047
+ function parseDgmoChartType(content) {
1048
+ const lines = content.split("\n");
1049
+ for (const line3 of lines) {
1050
+ const trimmed = line3.trim();
1051
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//"))
1052
+ continue;
1053
+ const match = trimmed.match(/^chart\s*:\s*(.+)/i);
1054
+ if (match) return match[1].trim().toLowerCase();
1055
+ }
1056
+ if (looksLikeSequence(content)) return "sequence";
1057
+ if (looksLikeFlowchart(content)) return "flowchart";
1058
+ return null;
1059
+ }
1060
+ var DGMO_CHART_TYPE_MAP;
1061
+ var init_dgmo_router = __esm({
1062
+ "src/dgmo-router.ts"() {
1063
+ "use strict";
1064
+ init_parser();
1065
+ init_flowchart_parser();
1066
+ DGMO_CHART_TYPE_MAP = {
1067
+ // Standard charts (via ECharts)
1068
+ bar: "echart",
1069
+ line: "echart",
1070
+ "multi-line": "echart",
1071
+ area: "echart",
1072
+ pie: "echart",
1073
+ doughnut: "echart",
1074
+ radar: "echart",
1075
+ "polar-area": "echart",
1076
+ "bar-stacked": "echart",
1077
+ // ECharts
1078
+ scatter: "echart",
1079
+ sankey: "echart",
1080
+ chord: "echart",
1081
+ function: "echart",
1082
+ heatmap: "echart",
1083
+ funnel: "echart",
1084
+ // D3
1085
+ slope: "d3",
1086
+ wordcloud: "d3",
1087
+ arc: "d3",
1088
+ timeline: "d3",
1089
+ venn: "d3",
1090
+ quadrant: "d3",
1091
+ sequence: "d3",
1092
+ flowchart: "d3"
1093
+ };
1094
+ }
1095
+ });
1096
+
582
1097
  // src/fonts.ts
583
1098
  var FONT_FAMILY;
584
1099
  var init_fonts = __esm({
@@ -1634,6 +2149,409 @@ var init_palettes = __esm({
1634
2149
  }
1635
2150
  });
1636
2151
 
2152
+ // src/graph/layout.ts
2153
+ var layout_exports = {};
2154
+ __export(layout_exports, {
2155
+ layoutGraph: () => layoutGraph
2156
+ });
2157
+ function computeNodeWidth(label, shape) {
2158
+ const base = Math.max(120, label.length * 9 + 40);
2159
+ if (shape === "subroutine") return base + 10;
2160
+ return base;
2161
+ }
2162
+ function computeNodeHeight(shape) {
2163
+ return shape === "decision" ? 60 : 50;
2164
+ }
2165
+ function layoutGraph(graph) {
2166
+ if (graph.nodes.length === 0) {
2167
+ return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
2168
+ }
2169
+ const g = new import_dagre.default.graphlib.Graph({ compound: true });
2170
+ g.setGraph({
2171
+ rankdir: graph.direction,
2172
+ nodesep: 50,
2173
+ ranksep: 60,
2174
+ edgesep: 20
2175
+ });
2176
+ g.setDefaultEdgeLabel(() => ({}));
2177
+ const nodeDataMap = /* @__PURE__ */ new Map();
2178
+ for (const node of graph.nodes) {
2179
+ nodeDataMap.set(node.id, node);
2180
+ }
2181
+ if (graph.groups) {
2182
+ for (const group of graph.groups) {
2183
+ g.setNode(group.id, {
2184
+ label: group.label,
2185
+ clusterLabelPos: "top"
2186
+ });
2187
+ }
2188
+ }
2189
+ for (const node of graph.nodes) {
2190
+ const width = computeNodeWidth(node.label, node.shape);
2191
+ const height = computeNodeHeight(node.shape);
2192
+ g.setNode(node.id, { label: node.label, width, height });
2193
+ if (node.group && graph.groups?.some((gr) => gr.id === node.group)) {
2194
+ g.setParent(node.id, node.group);
2195
+ }
2196
+ }
2197
+ const edgeDataMap = /* @__PURE__ */ new Map();
2198
+ for (const edge of graph.edges) {
2199
+ const key = `${edge.source}->${edge.target}`;
2200
+ edgeDataMap.set(key, edge);
2201
+ g.setEdge(edge.source, edge.target, {
2202
+ label: edge.label ?? ""
2203
+ });
2204
+ }
2205
+ import_dagre.default.layout(g);
2206
+ const layoutNodes = graph.nodes.map((node) => {
2207
+ const pos = g.node(node.id);
2208
+ return {
2209
+ id: node.id,
2210
+ label: node.label,
2211
+ shape: node.shape,
2212
+ color: node.color,
2213
+ group: node.group,
2214
+ lineNumber: node.lineNumber,
2215
+ x: pos.x,
2216
+ y: pos.y,
2217
+ width: pos.width,
2218
+ height: pos.height
2219
+ };
2220
+ });
2221
+ const layoutEdges = graph.edges.map((edge) => {
2222
+ const edgeData = g.edge(edge.source, edge.target);
2223
+ return {
2224
+ source: edge.source,
2225
+ target: edge.target,
2226
+ points: edgeData?.points ?? [],
2227
+ label: edge.label,
2228
+ color: edge.color,
2229
+ lineNumber: edge.lineNumber
2230
+ };
2231
+ });
2232
+ const layoutGroups = [];
2233
+ if (graph.groups) {
2234
+ const nodeMap = new Map(layoutNodes.map((n) => [n.id, n]));
2235
+ for (const group of graph.groups) {
2236
+ const members = group.nodeIds.map((id) => nodeMap.get(id)).filter((n) => n !== void 0);
2237
+ if (members.length === 0) {
2238
+ layoutGroups.push({
2239
+ id: group.id,
2240
+ label: group.label,
2241
+ color: group.color,
2242
+ x: 0,
2243
+ y: 0,
2244
+ width: 0,
2245
+ height: 0
2246
+ });
2247
+ continue;
2248
+ }
2249
+ let minX = Infinity;
2250
+ let minY = Infinity;
2251
+ let maxX = -Infinity;
2252
+ let maxY = -Infinity;
2253
+ for (const member of members) {
2254
+ const left = member.x - member.width / 2;
2255
+ const right = member.x + member.width / 2;
2256
+ const top = member.y - member.height / 2;
2257
+ const bottom = member.y + member.height / 2;
2258
+ if (left < minX) minX = left;
2259
+ if (right > maxX) maxX = right;
2260
+ if (top < minY) minY = top;
2261
+ if (bottom > maxY) maxY = bottom;
2262
+ }
2263
+ layoutGroups.push({
2264
+ id: group.id,
2265
+ label: group.label,
2266
+ color: group.color,
2267
+ x: minX - GROUP_PADDING,
2268
+ y: minY - GROUP_PADDING,
2269
+ width: maxX - minX + GROUP_PADDING * 2,
2270
+ height: maxY - minY + GROUP_PADDING * 2
2271
+ });
2272
+ }
2273
+ }
2274
+ let totalWidth = 0;
2275
+ let totalHeight = 0;
2276
+ for (const node of layoutNodes) {
2277
+ const right = node.x + node.width / 2;
2278
+ const bottom = node.y + node.height / 2;
2279
+ if (right > totalWidth) totalWidth = right;
2280
+ if (bottom > totalHeight) totalHeight = bottom;
2281
+ }
2282
+ for (const group of layoutGroups) {
2283
+ const right = group.x + group.width;
2284
+ const bottom = group.y + group.height;
2285
+ if (right > totalWidth) totalWidth = right;
2286
+ if (bottom > totalHeight) totalHeight = bottom;
2287
+ }
2288
+ totalWidth += 40;
2289
+ totalHeight += 40;
2290
+ return {
2291
+ nodes: layoutNodes,
2292
+ edges: layoutEdges,
2293
+ groups: layoutGroups,
2294
+ width: totalWidth,
2295
+ height: totalHeight
2296
+ };
2297
+ }
2298
+ var import_dagre, GROUP_PADDING;
2299
+ var init_layout = __esm({
2300
+ "src/graph/layout.ts"() {
2301
+ "use strict";
2302
+ import_dagre = __toESM(require("@dagrejs/dagre"), 1);
2303
+ GROUP_PADDING = 20;
2304
+ }
2305
+ });
2306
+
2307
+ // src/graph/flowchart-renderer.ts
2308
+ var flowchart_renderer_exports = {};
2309
+ __export(flowchart_renderer_exports, {
2310
+ renderFlowchart: () => renderFlowchart,
2311
+ renderFlowchartForExport: () => renderFlowchartForExport
2312
+ });
2313
+ function mix(a, b, pct) {
2314
+ const parse = (h) => {
2315
+ const r = h.replace("#", "");
2316
+ const f = r.length === 3 ? r[0] + r[0] + r[1] + r[1] + r[2] + r[2] : r;
2317
+ return [parseInt(f.substring(0, 2), 16), parseInt(f.substring(2, 4), 16), parseInt(f.substring(4, 6), 16)];
2318
+ };
2319
+ const [ar, ag, ab] = parse(a), [br, bg, bb] = parse(b), t = pct / 100;
2320
+ const c = (x, y) => Math.round(x * t + y * (1 - t)).toString(16).padStart(2, "0");
2321
+ return `#${c(ar, br)}${c(ag, bg)}${c(ab, bb)}`;
2322
+ }
2323
+ function nodeFill(palette, isDark, nodeColor) {
2324
+ if (nodeColor) {
2325
+ return mix(nodeColor, isDark ? palette.surface : palette.bg, 25);
2326
+ }
2327
+ return mix(palette.primary, isDark ? palette.surface : palette.bg, 15);
2328
+ }
2329
+ function nodeStroke(palette, nodeColor) {
2330
+ return nodeColor ?? palette.textMuted;
2331
+ }
2332
+ function renderTerminal(g, node, palette, isDark) {
2333
+ const w = node.width;
2334
+ const h = node.height;
2335
+ const rx = h / 2;
2336
+ 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);
2337
+ }
2338
+ function renderProcess(g, node, palette, isDark) {
2339
+ const w = node.width;
2340
+ const h = node.height;
2341
+ 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);
2342
+ }
2343
+ function renderDecision(g, node, palette, isDark) {
2344
+ const w = node.width / 2;
2345
+ const h = node.height / 2;
2346
+ const points = [
2347
+ `${0},${-h}`,
2348
+ // top
2349
+ `${w},${0}`,
2350
+ // right
2351
+ `${0},${h}`,
2352
+ // bottom
2353
+ `${-w},${0}`
2354
+ // left
2355
+ ].join(" ");
2356
+ 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);
2357
+ }
2358
+ function renderIO(g, node, palette, isDark) {
2359
+ const w = node.width / 2;
2360
+ const h = node.height / 2;
2361
+ const sk = IO_SKEW;
2362
+ const points = [
2363
+ `${-w + sk},${-h}`,
2364
+ // top-left (shifted right)
2365
+ `${w + sk},${-h}`,
2366
+ // top-right (shifted right)
2367
+ `${w - sk},${h}`,
2368
+ // bottom-right (shifted left)
2369
+ `${-w - sk},${h}`
2370
+ // bottom-left (shifted left)
2371
+ ].join(" ");
2372
+ 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);
2373
+ }
2374
+ function renderSubroutine(g, node, palette, isDark) {
2375
+ const w = node.width;
2376
+ const h = node.height;
2377
+ const s = nodeStroke(palette, node.color);
2378
+ 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);
2379
+ 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);
2380
+ 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);
2381
+ }
2382
+ function renderDocument(g, node, palette, isDark) {
2383
+ const w = node.width;
2384
+ const h = node.height;
2385
+ const waveH = DOC_WAVE_HEIGHT;
2386
+ const left = -w / 2;
2387
+ const right = w / 2;
2388
+ const top = -h / 2;
2389
+ const bottom = h / 2 - waveH;
2390
+ const d = [
2391
+ `M ${left} ${top}`,
2392
+ `L ${right} ${top}`,
2393
+ `L ${right} ${bottom}`,
2394
+ `C ${right - w * 0.25} ${bottom + waveH * 2}, ${left + w * 0.25} ${bottom - waveH}, ${left} ${bottom}`,
2395
+ "Z"
2396
+ ].join(" ");
2397
+ 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);
2398
+ }
2399
+ function renderNodeShape(g, node, palette, isDark) {
2400
+ switch (node.shape) {
2401
+ case "terminal":
2402
+ renderTerminal(g, node, palette, isDark);
2403
+ break;
2404
+ case "process":
2405
+ renderProcess(g, node, palette, isDark);
2406
+ break;
2407
+ case "decision":
2408
+ renderDecision(g, node, palette, isDark);
2409
+ break;
2410
+ case "io":
2411
+ renderIO(g, node, palette, isDark);
2412
+ break;
2413
+ case "subroutine":
2414
+ renderSubroutine(g, node, palette, isDark);
2415
+ break;
2416
+ case "document":
2417
+ renderDocument(g, node, palette, isDark);
2418
+ break;
2419
+ }
2420
+ }
2421
+ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem, exportDims) {
2422
+ d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
2423
+ const width = exportDims?.width ?? container.clientWidth;
2424
+ const height = exportDims?.height ?? container.clientHeight;
2425
+ if (width <= 0 || height <= 0) return;
2426
+ const titleOffset = graph.title ? TITLE_HEIGHT : 0;
2427
+ const diagramW = layout.width;
2428
+ const diagramH = layout.height + titleOffset;
2429
+ const scaleX = (width - DIAGRAM_PADDING * 2) / diagramW;
2430
+ const scaleY = (height - DIAGRAM_PADDING * 2) / diagramH;
2431
+ const scale = Math.min(1, scaleX, scaleY);
2432
+ const scaledW = diagramW * scale;
2433
+ const scaledH = diagramH * scale;
2434
+ const offsetX = (width - scaledW) / 2;
2435
+ const offsetY = (height - scaledH) / 2;
2436
+ const svg = d3Selection.select(container).append("svg").attr("width", width).attr("height", height).style("background", palette.bg).style("font-family", FONT_FAMILY);
2437
+ const defs = svg.append("defs");
2438
+ 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);
2439
+ const edgeColors = /* @__PURE__ */ new Set();
2440
+ for (const edge of layout.edges) {
2441
+ if (edge.color) edgeColors.add(edge.color);
2442
+ }
2443
+ for (const color of edgeColors) {
2444
+ const id = `fc-arrow-${color.replace("#", "")}`;
2445
+ 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);
2446
+ }
2447
+ const mainG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY}) scale(${scale})`);
2448
+ if (graph.title) {
2449
+ 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);
2450
+ }
2451
+ const contentG = mainG.append("g").attr("transform", `translate(0, ${titleOffset})`);
2452
+ for (const group of layout.groups) {
2453
+ if (group.width === 0 && group.height === 0) continue;
2454
+ const gx = group.x - GROUP_EXTRA_PADDING;
2455
+ const gy = group.y - GROUP_EXTRA_PADDING - GROUP_LABEL_FONT_SIZE - 4;
2456
+ const gw = group.width + GROUP_EXTRA_PADDING * 2;
2457
+ const gh = group.height + GROUP_EXTRA_PADDING * 2 + GROUP_LABEL_FONT_SIZE + 4;
2458
+ const fillColor = group.color ? mix(group.color, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
2459
+ const strokeColor = group.color ?? palette.textMuted;
2460
+ 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");
2461
+ 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);
2462
+ }
2463
+ for (const edge of layout.edges) {
2464
+ if (edge.points.length < 2) continue;
2465
+ const edgeG = contentG.append("g").attr("class", "fc-edge-group").attr("data-line-number", String(edge.lineNumber));
2466
+ const edgeColor = edge.color ?? palette.textMuted;
2467
+ const markerId = edge.color ? `fc-arrow-${edge.color.replace("#", "")}` : "fc-arrow";
2468
+ const pathD = lineGenerator(edge.points);
2469
+ if (pathD) {
2470
+ 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");
2471
+ }
2472
+ if (edge.label) {
2473
+ const midIdx = Math.floor(edge.points.length / 2);
2474
+ const midPt = edge.points[midIdx];
2475
+ const labelLen = edge.label.length;
2476
+ const bgW = labelLen * 7 + 8;
2477
+ const bgH = 16;
2478
+ 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");
2479
+ 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);
2480
+ }
2481
+ }
2482
+ for (const node of layout.nodes) {
2483
+ const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "fc-node").attr("data-line-number", String(node.lineNumber));
2484
+ if (onClickItem) {
2485
+ nodeG.style("cursor", "pointer").on("click", () => {
2486
+ onClickItem(node.lineNumber);
2487
+ });
2488
+ }
2489
+ renderNodeShape(nodeG, node, palette, isDark);
2490
+ 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);
2491
+ }
2492
+ }
2493
+ function renderFlowchartForExport(content, theme, palette) {
2494
+ const parsed = parseFlowchart(content, palette);
2495
+ if (parsed.error || parsed.nodes.length === 0) return "";
2496
+ const layout = layoutGraph(parsed);
2497
+ const isDark = theme === "dark";
2498
+ const container = document.createElement("div");
2499
+ container.style.width = `${layout.width + DIAGRAM_PADDING * 2}px`;
2500
+ container.style.height = `${layout.height + DIAGRAM_PADDING * 2 + (parsed.title ? TITLE_HEIGHT : 0)}px`;
2501
+ container.style.position = "absolute";
2502
+ container.style.left = "-9999px";
2503
+ document.body.appendChild(container);
2504
+ const exportWidth = layout.width + DIAGRAM_PADDING * 2;
2505
+ const exportHeight = layout.height + DIAGRAM_PADDING * 2 + (parsed.title ? TITLE_HEIGHT : 0);
2506
+ try {
2507
+ renderFlowchart(
2508
+ container,
2509
+ parsed,
2510
+ layout,
2511
+ palette,
2512
+ isDark,
2513
+ void 0,
2514
+ { width: exportWidth, height: exportHeight }
2515
+ );
2516
+ const svgEl = container.querySelector("svg");
2517
+ if (!svgEl) return "";
2518
+ if (theme === "transparent") {
2519
+ svgEl.style.background = "none";
2520
+ }
2521
+ svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
2522
+ svgEl.style.fontFamily = FONT_FAMILY;
2523
+ return svgEl.outerHTML;
2524
+ } finally {
2525
+ document.body.removeChild(container);
2526
+ }
2527
+ }
2528
+ 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;
2529
+ var init_flowchart_renderer = __esm({
2530
+ "src/graph/flowchart-renderer.ts"() {
2531
+ "use strict";
2532
+ d3Selection = __toESM(require("d3-selection"), 1);
2533
+ d3Shape = __toESM(require("d3-shape"), 1);
2534
+ init_fonts();
2535
+ init_flowchart_parser();
2536
+ init_layout();
2537
+ DIAGRAM_PADDING = 20;
2538
+ TITLE_HEIGHT = 30;
2539
+ TITLE_FONT_SIZE = 18;
2540
+ NODE_FONT_SIZE = 13;
2541
+ EDGE_LABEL_FONT_SIZE = 11;
2542
+ GROUP_LABEL_FONT_SIZE = 11;
2543
+ EDGE_STROKE_WIDTH = 1.5;
2544
+ NODE_STROKE_WIDTH = 1.5;
2545
+ ARROWHEAD_W = 10;
2546
+ ARROWHEAD_H = 7;
2547
+ IO_SKEW = 15;
2548
+ SUBROUTINE_INSET = 8;
2549
+ DOC_WAVE_HEIGHT = 10;
2550
+ GROUP_EXTRA_PADDING = 12;
2551
+ lineGenerator = d3Shape.line().x((d) => d.x).y((d) => d.y).curve(d3Shape.curveBasis);
2552
+ }
2553
+ });
2554
+
1637
2555
  // src/sequence/renderer.ts
1638
2556
  var renderer_exports = {};
1639
2557
  __export(renderer_exports, {
@@ -1644,7 +2562,42 @@ __export(renderer_exports, {
1644
2562
  groupMessagesBySection: () => groupMessagesBySection,
1645
2563
  renderSequenceDiagram: () => renderSequenceDiagram
1646
2564
  });
1647
- function mix(a, b, pct) {
2565
+ function parseInlineMarkdown(text) {
2566
+ const spans = [];
2567
+ const regex = /\*\*(.+?)\*\*|\*(.+?)\*|`(.+?)`|\[(.+?)\]\((.+?)\)|([^*`[]+)/g;
2568
+ let match;
2569
+ while ((match = regex.exec(text)) !== null) {
2570
+ if (match[1]) spans.push({ text: match[1], bold: true });
2571
+ else if (match[2]) spans.push({ text: match[2], italic: true });
2572
+ else if (match[3]) spans.push({ text: match[3], code: true });
2573
+ else if (match[4]) spans.push({ text: match[4], href: match[5] });
2574
+ else if (match[6]) spans.push({ text: match[6] });
2575
+ }
2576
+ return spans;
2577
+ }
2578
+ function wrapTextLines(text, maxChars) {
2579
+ const rawLines = text.split("\n");
2580
+ const wrapped = [];
2581
+ for (const line3 of rawLines) {
2582
+ if (line3.length <= maxChars) {
2583
+ wrapped.push(line3);
2584
+ } else {
2585
+ const words = line3.split(" ");
2586
+ let current = "";
2587
+ for (const word of words) {
2588
+ if (current && (current + " " + word).length > maxChars) {
2589
+ wrapped.push(current);
2590
+ current = word;
2591
+ } else {
2592
+ current = current ? current + " " + word : word;
2593
+ }
2594
+ }
2595
+ if (current) wrapped.push(current);
2596
+ }
2597
+ }
2598
+ return wrapped;
2599
+ }
2600
+ function mix2(a, b, pct) {
1648
2601
  const parse = (h) => {
1649
2602
  const r = h.replace("#", "");
1650
2603
  const f = r.length === 3 ? r[0] + r[0] + r[1] + r[1] + r[2] + r[2] : r;
@@ -1750,7 +2703,12 @@ function groupMessagesBySection(elements, messages) {
1750
2703
  ...collectIndices(el.children),
1751
2704
  ...collectIndices(el.elseChildren)
1752
2705
  );
1753
- } else if (isSequenceSection(el)) {
2706
+ if (el.elseIfBranches) {
2707
+ for (const branch of el.elseIfBranches) {
2708
+ indices.push(...collectIndices(branch.children));
2709
+ }
2710
+ }
2711
+ } else if (isSequenceSection(el) || isSequenceNote(el)) {
1754
2712
  continue;
1755
2713
  } else {
1756
2714
  const idx = messages.indexOf(el);
@@ -1766,7 +2724,7 @@ function groupMessagesBySection(elements, messages) {
1766
2724
  } else if (currentGroup) {
1767
2725
  if (isSequenceBlock(el)) {
1768
2726
  currentGroup.messageIndices.push(...collectIndices([el]));
1769
- } else {
2727
+ } else if (!isSequenceNote(el)) {
1770
2728
  const idx = messages.indexOf(el);
1771
2729
  if (idx >= 0) currentGroup.messageIndices.push(idx);
1772
2730
  }
@@ -1923,7 +2881,7 @@ function applyGroupOrdering(participants, groups) {
1923
2881
  return result;
1924
2882
  }
1925
2883
  function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateToLine, options) {
1926
- d3Selection.select(container).selectAll("*").remove();
2884
+ d3Selection2.select(container).selectAll("*").remove();
1927
2885
  const { title, messages, elements, groups, options: parsedOptions } = parsed;
1928
2886
  const collapsedSections = options?.collapsedSections;
1929
2887
  const participants = applyPositionOverrides(
@@ -1964,7 +2922,15 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1964
2922
  if (isSequenceBlock(el)) {
1965
2923
  const idx = findFirstMsgIndex(el.children);
1966
2924
  if (idx >= 0) return idx;
1967
- } else if (!isSequenceSection(el)) {
2925
+ if (el.elseIfBranches) {
2926
+ for (const branch of el.elseIfBranches) {
2927
+ const branchIdx = findFirstMsgIndex(branch.children);
2928
+ if (branchIdx >= 0) return branchIdx;
2929
+ }
2930
+ }
2931
+ const elseIdx = findFirstMsgIndex(el.elseChildren);
2932
+ if (elseIdx >= 0) return elseIdx;
2933
+ } else if (!isSequenceSection(el) && !isSequenceNote(el)) {
1968
2934
  const idx = messages.indexOf(el);
1969
2935
  if (idx >= 0 && !hiddenMsgIndices.has(idx)) return idx;
1970
2936
  }
@@ -1984,6 +2950,13 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1984
2950
  if (!isSequenceBlock(el)) continue;
1985
2951
  const firstIdx = findFirstMsgIndex(el.children);
1986
2952
  if (firstIdx >= 0) addExtra(firstIdx, BLOCK_HEADER_SPACE);
2953
+ if (el.elseIfBranches) {
2954
+ for (const branch of el.elseIfBranches) {
2955
+ const firstBranchIdx = findFirstMsgIndex(branch.children);
2956
+ if (firstBranchIdx >= 0) addExtra(firstBranchIdx, BLOCK_HEADER_SPACE);
2957
+ markBlockSpacing(branch.children);
2958
+ }
2959
+ }
1987
2960
  const firstElseIdx = findFirstMsgIndex(el.elseChildren);
1988
2961
  if (firstElseIdx >= 0) addExtra(firstElseIdx, BLOCK_HEADER_SPACE);
1989
2962
  markBlockSpacing(el.children);
@@ -2005,15 +2978,27 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2005
2978
  for (const child of block.children) {
2006
2979
  if (isSequenceBlock(child)) {
2007
2980
  indices.push(...collectMsgIndicesFromBlock(child));
2008
- } else if (!isSequenceSection(child)) {
2981
+ } else if (!isSequenceSection(child) && !isSequenceNote(child)) {
2009
2982
  const idx = messages.indexOf(child);
2010
2983
  if (idx >= 0) indices.push(idx);
2011
2984
  }
2012
2985
  }
2986
+ if (block.elseIfBranches) {
2987
+ for (const branch of block.elseIfBranches) {
2988
+ for (const child of branch.children) {
2989
+ if (isSequenceBlock(child)) {
2990
+ indices.push(...collectMsgIndicesFromBlock(child));
2991
+ } else if (!isSequenceSection(child) && !isSequenceNote(child)) {
2992
+ const idx = messages.indexOf(child);
2993
+ if (idx >= 0) indices.push(idx);
2994
+ }
2995
+ }
2996
+ }
2997
+ }
2013
2998
  for (const child of block.elseChildren) {
2014
2999
  if (isSequenceBlock(child)) {
2015
3000
  indices.push(...collectMsgIndicesFromBlock(child));
2016
- } else if (!isSequenceSection(child)) {
3001
+ } else if (!isSequenceSection(child) && !isSequenceNote(child)) {
2017
3002
  const idx = messages.indexOf(child);
2018
3003
  if (idx >= 0) indices.push(idx);
2019
3004
  }
@@ -2088,7 +3073,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2088
3073
  const GROUP_PADDING_TOP = 22;
2089
3074
  const GROUP_PADDING_BOTTOM = 8;
2090
3075
  const GROUP_LABEL_SIZE = 11;
2091
- const titleOffset = title ? TITLE_HEIGHT : 0;
3076
+ const titleOffset = title ? TITLE_HEIGHT2 : 0;
2092
3077
  const groupOffset = groups.length > 0 ? GROUP_PADDING_TOP + GROUP_LABEL_SIZE : 0;
2093
3078
  const participantStartY = TOP_MARGIN + titleOffset + PARTICIPANT_Y_OFFSET + groupOffset;
2094
3079
  const lifelineStartY0 = participantStartY + PARTICIPANT_BOX_HEIGHT;
@@ -2142,7 +3127,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2142
3127
  participants.forEach((p, i) => {
2143
3128
  participantX.set(p.id, offsetX + i * PARTICIPANT_GAP);
2144
3129
  });
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);
3130
+ 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
3131
  const defs = svg.append("defs");
2147
3132
  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
3133
  "points",
@@ -2168,7 +3153,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2168
3153
  const boxY = participantStartY - GROUP_PADDING_TOP;
2169
3154
  const boxH = PARTICIPANT_BOX_HEIGHT + GROUP_PADDING_TOP + GROUP_PADDING_BOTTOM;
2170
3155
  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;
3156
+ const fillColor = resolvedGroupColor ? mix2(resolvedGroupColor, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : palette.bg;
2172
3157
  const strokeColor = resolvedGroupColor || palette.textMuted;
2173
3158
  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
3159
  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 +3178,12 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2193
3178
  ...collectMsgIndices(el.children),
2194
3179
  ...collectMsgIndices(el.elseChildren)
2195
3180
  );
2196
- } else if (!isSequenceSection(el)) {
3181
+ if (el.elseIfBranches) {
3182
+ for (const branch of el.elseIfBranches) {
3183
+ indices.push(...collectMsgIndices(branch.children));
3184
+ }
3185
+ }
3186
+ } else if (!isSequenceSection(el) && !isSequenceNote(el)) {
2197
3187
  const idx = messages.indexOf(el);
2198
3188
  if (idx >= 0) indices.push(idx);
2199
3189
  }
@@ -2206,8 +3196,21 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2206
3196
  for (const el of els) {
2207
3197
  if (!isSequenceBlock(el)) continue;
2208
3198
  const ifIndices = collectMsgIndices(el.children);
3199
+ const elseIfBranchData = [];
3200
+ if (el.elseIfBranches) {
3201
+ for (const branch of el.elseIfBranches) {
3202
+ elseIfBranchData.push({
3203
+ label: branch.label,
3204
+ indices: collectMsgIndices(branch.children)
3205
+ });
3206
+ }
3207
+ }
2209
3208
  const elseIndices = collectMsgIndices(el.elseChildren);
2210
- const allIndices = [...ifIndices, ...elseIndices];
3209
+ const allIndices = [
3210
+ ...ifIndices,
3211
+ ...elseIfBranchData.flatMap((b) => b.indices),
3212
+ ...elseIndices
3213
+ ];
2211
3214
  if (allIndices.length === 0) continue;
2212
3215
  let minStep = Infinity;
2213
3216
  let maxStep = -Infinity;
@@ -2245,6 +3248,32 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2245
3248
  italic: false,
2246
3249
  blockLine: el.lineNumber
2247
3250
  });
3251
+ for (const branchData of elseIfBranchData) {
3252
+ if (branchData.indices.length > 0) {
3253
+ let firstBranchStep = Infinity;
3254
+ for (const mi of branchData.indices) {
3255
+ const first = msgToFirstStep.get(mi);
3256
+ if (first !== void 0)
3257
+ firstBranchStep = Math.min(firstBranchStep, first);
3258
+ }
3259
+ if (firstBranchStep < Infinity) {
3260
+ const dividerY = stepY(firstBranchStep) - stepSpacing / 2;
3261
+ deferredLines.push({
3262
+ x1: frameX,
3263
+ y1: dividerY,
3264
+ x2: frameX + frameW,
3265
+ y2: dividerY
3266
+ });
3267
+ deferredLabels.push({
3268
+ x: frameX + 6,
3269
+ y: dividerY + 14,
3270
+ text: `else if ${branchData.label}`,
3271
+ bold: false,
3272
+ italic: true
3273
+ });
3274
+ }
3275
+ }
3276
+ }
2248
3277
  if (elseIndices.length > 0) {
2249
3278
  let firstElseStep = Infinity;
2250
3279
  for (const mi of elseIndices) {
@@ -2270,6 +3299,11 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2270
3299
  }
2271
3300
  }
2272
3301
  renderBlockFrames(el.children, depth + 1);
3302
+ if (el.elseIfBranches) {
3303
+ for (const branch of el.elseIfBranches) {
3304
+ renderBlockFrames(branch.children, depth + 1);
3305
+ }
3306
+ }
2273
3307
  renderBlockFrames(el.elseChildren, depth + 1);
2274
3308
  }
2275
3309
  };
@@ -2291,7 +3325,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2291
3325
  if (msg) coveredLines.push(msg.lineNumber);
2292
3326
  }
2293
3327
  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);
3328
+ const actFill = mix2(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
2295
3329
  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
3330
  });
2297
3331
  for (const ln of deferredLines) {
@@ -2417,6 +3451,91 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2417
3451
  }
2418
3452
  }
2419
3453
  });
3454
+ const noteFill = isDark ? mix2(palette.surface, palette.bg, 50) : mix2(palette.bg, palette.surface, 15);
3455
+ const findAssociatedStep = (note) => {
3456
+ let bestMsgIndex = -1;
3457
+ let bestLine = -1;
3458
+ for (let mi = 0; mi < messages.length; mi++) {
3459
+ if (messages[mi].lineNumber < note.lineNumber && messages[mi].lineNumber > bestLine && !hiddenMsgIndices.has(mi)) {
3460
+ bestLine = messages[mi].lineNumber;
3461
+ bestMsgIndex = mi;
3462
+ }
3463
+ }
3464
+ if (bestMsgIndex < 0) return -1;
3465
+ return msgToFirstStep.get(bestMsgIndex) ?? -1;
3466
+ };
3467
+ const renderNoteElements = (els) => {
3468
+ for (const el of els) {
3469
+ if (isSequenceNote(el)) {
3470
+ const px = participantX.get(el.participantId);
3471
+ if (px === void 0) continue;
3472
+ const si = findAssociatedStep(el);
3473
+ if (si < 0) continue;
3474
+ const noteY = stepY(si);
3475
+ const wrappedLines = wrapTextLines(el.text, NOTE_CHARS_PER_LINE);
3476
+ const noteH = wrappedLines.length * NOTE_LINE_H + NOTE_PAD_V * 2;
3477
+ const maxLineLen = Math.max(...wrappedLines.map((l) => l.length));
3478
+ const noteW = Math.min(
3479
+ NOTE_MAX_W,
3480
+ Math.max(80, maxLineLen * NOTE_CHAR_W + NOTE_PAD_H * 2 + NOTE_FOLD)
3481
+ );
3482
+ const isRight = el.position === "right";
3483
+ const noteX = isRight ? px + ACTIVATION_WIDTH + NOTE_GAP : px - ACTIVATION_WIDTH - NOTE_GAP - noteW;
3484
+ const noteTopY = noteY - noteH / 2;
3485
+ const noteG = svg.append("g").attr("class", "note").attr("data-note-toggle", "").attr("data-line-number", String(el.lineNumber));
3486
+ noteG.append("path").attr(
3487
+ "d",
3488
+ [
3489
+ `M ${noteX} ${noteTopY}`,
3490
+ `L ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
3491
+ `L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`,
3492
+ `L ${noteX + noteW} ${noteTopY + noteH}`,
3493
+ `L ${noteX} ${noteTopY + noteH}`,
3494
+ "Z"
3495
+ ].join(" ")
3496
+ ).attr("fill", noteFill).attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-box");
3497
+ noteG.append("path").attr(
3498
+ "d",
3499
+ [
3500
+ `M ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
3501
+ `L ${noteX + noteW - NOTE_FOLD} ${noteTopY + NOTE_FOLD}`,
3502
+ `L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`
3503
+ ].join(" ")
3504
+ ).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-fold");
3505
+ const connectorNoteX = isRight ? noteX : noteX + noteW;
3506
+ const connectorLifeX = isRight ? px + ACTIVATION_WIDTH / 2 : px - ACTIVATION_WIDTH / 2;
3507
+ noteG.append("line").attr("x1", connectorNoteX).attr("y1", noteY).attr("x2", connectorLifeX).attr("y2", noteY).attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("stroke-dasharray", "3 2").attr("class", "note-connector");
3508
+ wrappedLines.forEach((line3, li) => {
3509
+ const textY = noteTopY + NOTE_PAD_V + (li + 1) * NOTE_LINE_H - 3;
3510
+ const textEl = noteG.append("text").attr("x", noteX + NOTE_PAD_H).attr("y", textY).attr("fill", palette.text).attr("font-size", NOTE_FONT_SIZE).attr("class", "note-text");
3511
+ const spans = parseInlineMarkdown(line3);
3512
+ for (const span of spans) {
3513
+ if (span.href) {
3514
+ const a = textEl.append("a").attr("href", span.href);
3515
+ a.append("tspan").text(span.text).attr("fill", palette.primary).style("text-decoration", "underline");
3516
+ } else {
3517
+ const tspan = textEl.append("tspan").text(span.text);
3518
+ if (span.bold) tspan.attr("font-weight", "bold");
3519
+ if (span.italic) tspan.attr("font-style", "italic");
3520
+ if (span.code)
3521
+ tspan.attr("font-family", "monospace").attr("font-size", NOTE_FONT_SIZE - 1);
3522
+ }
3523
+ }
3524
+ });
3525
+ } else if (isSequenceBlock(el)) {
3526
+ renderNoteElements(el.children);
3527
+ if (el.elseIfBranches) {
3528
+ for (const branch of el.elseIfBranches) {
3529
+ renderNoteElements(branch.children);
3530
+ }
3531
+ }
3532
+ renderNoteElements(el.elseChildren);
3533
+ }
3534
+ }
3535
+ };
3536
+ if (elements && elements.length > 0) {
3537
+ renderNoteElements(elements);
3538
+ }
2420
3539
  }
2421
3540
  function renderParticipant(svg, participant, cx, cy, palette, isDark) {
2422
3541
  const g = svg.append("g").attr("transform", `translate(${cx}, ${cy})`).attr("class", "participant").attr("data-participant-id", participant.id);
@@ -2458,11 +3577,11 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark) {
2458
3577
  isActor ? PARTICIPANT_BOX_HEIGHT + 14 : PARTICIPANT_BOX_HEIGHT / 2 + 5
2459
3578
  ).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", 13).attr("font-weight", 500).text(participant.label);
2460
3579
  }
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;
3580
+ 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
3581
  var init_renderer = __esm({
2463
3582
  "src/sequence/renderer.ts"() {
2464
3583
  "use strict";
2465
- d3Selection = __toESM(require("d3-selection"), 1);
3584
+ d3Selection2 = __toESM(require("d3-selection"), 1);
2466
3585
  init_colors();
2467
3586
  init_fonts();
2468
3587
  init_parser();
@@ -2470,13 +3589,22 @@ var init_renderer = __esm({
2470
3589
  PARTICIPANT_BOX_WIDTH = 120;
2471
3590
  PARTICIPANT_BOX_HEIGHT = 50;
2472
3591
  TOP_MARGIN = 20;
2473
- TITLE_HEIGHT = 30;
3592
+ TITLE_HEIGHT2 = 30;
2474
3593
  PARTICIPANT_Y_OFFSET = 10;
2475
3594
  SERVICE_BORDER_RADIUS = 10;
2476
3595
  MESSAGE_START_OFFSET = 30;
2477
3596
  LIFELINE_TAIL = 30;
2478
3597
  ARROWHEAD_SIZE = 8;
2479
- fill = (palette, isDark) => mix(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
3598
+ NOTE_MAX_W = 200;
3599
+ NOTE_FOLD = 10;
3600
+ NOTE_PAD_H = 8;
3601
+ NOTE_PAD_V = 6;
3602
+ NOTE_FONT_SIZE = 10;
3603
+ NOTE_LINE_H = 14;
3604
+ NOTE_GAP = 15;
3605
+ NOTE_CHAR_W = 6;
3606
+ NOTE_CHARS_PER_LINE = Math.floor((NOTE_MAX_W - NOTE_PAD_H * 2 - NOTE_FOLD) / NOTE_CHAR_W);
3607
+ fill = (palette, isDark) => mix2(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
2480
3608
  stroke = (palette) => palette.textMuted;
2481
3609
  SW = 1.5;
2482
3610
  W = PARTICIPANT_BOX_WIDTH;
@@ -2516,7 +3644,10 @@ __export(index_exports, {
2516
3644
  hslToHex: () => hslToHex,
2517
3645
  inferParticipantType: () => inferParticipantType,
2518
3646
  isSequenceBlock: () => isSequenceBlock,
3647
+ isSequenceNote: () => isSequenceNote,
2519
3648
  isValidHex: () => isValidHex,
3649
+ layoutGraph: () => layoutGraph,
3650
+ looksLikeFlowchart: () => looksLikeFlowchart,
2520
3651
  looksLikeSequence: () => looksLikeSequence,
2521
3652
  mute: () => mute,
2522
3653
  nord: () => nord,
@@ -2527,6 +3658,7 @@ __export(index_exports, {
2527
3658
  parseD3: () => parseD3,
2528
3659
  parseDgmoChartType: () => parseDgmoChartType,
2529
3660
  parseEChart: () => parseEChart,
3661
+ parseFlowchart: () => parseFlowchart,
2530
3662
  parseQuadrant: () => parseQuadrant,
2531
3663
  parseSequenceDgmo: () => parseSequenceDgmo,
2532
3664
  parseTimelineDate: () => parseTimelineDate,
@@ -2534,6 +3666,8 @@ __export(index_exports, {
2534
3666
  renderArcDiagram: () => renderArcDiagram,
2535
3667
  renderD3ForExport: () => renderD3ForExport,
2536
3668
  renderEChartsForExport: () => renderEChartsForExport,
3669
+ renderFlowchart: () => renderFlowchart,
3670
+ renderFlowchartForExport: () => renderFlowchartForExport,
2537
3671
  renderQuadrant: () => renderQuadrant,
2538
3672
  renderSequenceDiagram: () => renderSequenceDiagram,
2539
3673
  renderSlopeChart: () => renderSlopeChart,
@@ -2549,51 +3683,7 @@ __export(index_exports, {
2549
3683
  tokyoNightPalette: () => tokyoNightPalette
2550
3684
  });
2551
3685
  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
- }
3686
+ init_dgmo_router();
2597
3687
 
2598
3688
  // src/chart.ts
2599
3689
  init_colors();
@@ -2955,7 +4045,7 @@ function parseEChart(content, palette) {
2955
4045
  function buildEChartsOption(parsed, palette, isDark) {
2956
4046
  const textColor = palette.text;
2957
4047
  const axisLineColor = palette.border;
2958
- const gridOpacity = isDark ? 0.7 : 0.4;
4048
+ const gridOpacity = isDark ? 0.7 : 0.55;
2959
4049
  const colors = getSeriesColors(palette);
2960
4050
  if (parsed.error) {
2961
4051
  return {};
@@ -3661,7 +4751,7 @@ function buildEChartsOptionFromChart(parsed, palette, isDark) {
3661
4751
  const textColor = palette.text;
3662
4752
  const axisLineColor = palette.border;
3663
4753
  const splitLineColor = palette.border;
3664
- const gridOpacity = isDark ? 0.7 : 0.4;
4754
+ const gridOpacity = isDark ? 0.7 : 0.55;
3665
4755
  const colors = getSeriesColors(palette);
3666
4756
  const titleConfig = parsed.title ? {
3667
4757
  text: parsed.title,
@@ -4074,8 +5164,8 @@ async function renderEChartsForExport(content, theme, palette) {
4074
5164
 
4075
5165
  // src/d3.ts
4076
5166
  var d3Scale = __toESM(require("d3-scale"), 1);
4077
- var d3Selection2 = __toESM(require("d3-selection"), 1);
4078
- var d3Shape = __toESM(require("d3-shape"), 1);
5167
+ var d3Selection3 = __toESM(require("d3-selection"), 1);
5168
+ var d3Shape2 = __toESM(require("d3-shape"), 1);
4079
5169
  var d3Array = __toESM(require("d3-array"), 1);
4080
5170
  var import_d3_cloud = __toESM(require("d3-cloud"), 1);
4081
5171
  init_fonts();
@@ -4182,10 +5272,10 @@ function parseD3(content, palette) {
4182
5272
  let currentArcGroup = null;
4183
5273
  let currentTimelineGroup = null;
4184
5274
  for (let i = 0; i < lines.length; i++) {
4185
- const line2 = lines[i].trim();
5275
+ const line3 = lines[i].trim();
4186
5276
  const lineNumber = i + 1;
4187
- if (!line2) continue;
4188
- const sectionMatch = line2.match(/^#{2,}\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/);
5277
+ if (!line3) continue;
5278
+ const sectionMatch = line3.match(/^#{2,}\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/);
4189
5279
  if (sectionMatch) {
4190
5280
  if (result.type === "arc") {
4191
5281
  const name = sectionMatch[1].trim();
@@ -4200,11 +5290,11 @@ function parseD3(content, palette) {
4200
5290
  }
4201
5291
  continue;
4202
5292
  }
4203
- if (line2.startsWith("#") || line2.startsWith("//")) {
5293
+ if (line3.startsWith("#") || line3.startsWith("//")) {
4204
5294
  continue;
4205
5295
  }
4206
5296
  if (result.type === "arc") {
4207
- const linkMatch = line2.match(
5297
+ const linkMatch = line3.match(
4208
5298
  /^(.+?)\s*->\s*(.+?)(?:\(([^)]+)\))?\s*(?::\s*(\d+(?:\.\d+)?))?$/
4209
5299
  );
4210
5300
  if (linkMatch) {
@@ -4234,7 +5324,7 @@ function parseD3(content, palette) {
4234
5324
  }
4235
5325
  }
4236
5326
  if (result.type === "timeline") {
4237
- const eraMatch = line2.match(
5327
+ const eraMatch = line3.match(
4238
5328
  /^era\s+(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
4239
5329
  );
4240
5330
  if (eraMatch) {
@@ -4247,7 +5337,7 @@ function parseD3(content, palette) {
4247
5337
  });
4248
5338
  continue;
4249
5339
  }
4250
- const markerMatch = line2.match(
5340
+ const markerMatch = line3.match(
4251
5341
  /^marker\s+(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
4252
5342
  );
4253
5343
  if (markerMatch) {
@@ -4262,7 +5352,7 @@ function parseD3(content, palette) {
4262
5352
  }
4263
5353
  }
4264
5354
  if (result.type === "timeline") {
4265
- const durationMatch = line2.match(
5355
+ const durationMatch = line3.match(
4266
5356
  /^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\?)?(\d+(?:\.\d{1,2})?)([dwmy])\s*:\s*(.+)$/
4267
5357
  );
4268
5358
  if (durationMatch) {
@@ -4281,7 +5371,7 @@ function parseD3(content, palette) {
4281
5371
  });
4282
5372
  continue;
4283
5373
  }
4284
- const rangeMatch = line2.match(
5374
+ const rangeMatch = line3.match(
4285
5375
  /^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\?)?(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
4286
5376
  );
4287
5377
  if (rangeMatch) {
@@ -4295,7 +5385,7 @@ function parseD3(content, palette) {
4295
5385
  });
4296
5386
  continue;
4297
5387
  }
4298
- const pointMatch = line2.match(
5388
+ const pointMatch = line3.match(
4299
5389
  /^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
4300
5390
  );
4301
5391
  if (pointMatch) {
@@ -4310,7 +5400,7 @@ function parseD3(content, palette) {
4310
5400
  }
4311
5401
  }
4312
5402
  if (result.type === "venn") {
4313
- const overlapMatch = line2.match(
5403
+ const overlapMatch = line3.match(
4314
5404
  /^(.+?&.+?)\s*:\s*(\d+(?:\.\d+)?)\s*(?:"([^"]*)")?\s*$/
4315
5405
  );
4316
5406
  if (overlapMatch) {
@@ -4320,7 +5410,7 @@ function parseD3(content, palette) {
4320
5410
  result.vennOverlaps.push({ sets, size, label, lineNumber });
4321
5411
  continue;
4322
5412
  }
4323
- const setMatch = line2.match(
5413
+ const setMatch = line3.match(
4324
5414
  /^(.+?)(?:\(([^)]+)\))?\s*:\s*(\d+(?:\.\d+)?)\s*(?:"([^"]*)")?\s*$/
4325
5415
  );
4326
5416
  if (setMatch) {
@@ -4333,7 +5423,7 @@ function parseD3(content, palette) {
4333
5423
  }
4334
5424
  }
4335
5425
  if (result.type === "quadrant") {
4336
- const xAxisMatch = line2.match(/^x-axis\s*:\s*(.+)/i);
5426
+ const xAxisMatch = line3.match(/^x-axis\s*:\s*(.+)/i);
4337
5427
  if (xAxisMatch) {
4338
5428
  const parts = xAxisMatch[1].split(",").map((s) => s.trim());
4339
5429
  if (parts.length >= 2) {
@@ -4342,7 +5432,7 @@ function parseD3(content, palette) {
4342
5432
  }
4343
5433
  continue;
4344
5434
  }
4345
- const yAxisMatch = line2.match(/^y-axis\s*:\s*(.+)/i);
5435
+ const yAxisMatch = line3.match(/^y-axis\s*:\s*(.+)/i);
4346
5436
  if (yAxisMatch) {
4347
5437
  const parts = yAxisMatch[1].split(",").map((s) => s.trim());
4348
5438
  if (parts.length >= 2) {
@@ -4352,7 +5442,7 @@ function parseD3(content, palette) {
4352
5442
  continue;
4353
5443
  }
4354
5444
  const quadrantLabelRe = /^(top-right|top-left|bottom-left|bottom-right)\s*:\s*(.+)/i;
4355
- const quadrantMatch = line2.match(quadrantLabelRe);
5445
+ const quadrantMatch = line3.match(quadrantLabelRe);
4356
5446
  if (quadrantMatch) {
4357
5447
  const position = quadrantMatch[1].toLowerCase();
4358
5448
  const labelPart = quadrantMatch[2].trim();
@@ -4368,7 +5458,7 @@ function parseD3(content, palette) {
4368
5458
  result.quadrantLabels.bottomRight = label;
4369
5459
  continue;
4370
5460
  }
4371
- const pointMatch = line2.match(
5461
+ const pointMatch = line3.match(
4372
5462
  /^(.+?):\s*([0-9]*\.?[0-9]+)\s*,\s*([0-9]*\.?[0-9]+)\s*$/
4373
5463
  );
4374
5464
  if (pointMatch) {
@@ -4385,13 +5475,13 @@ function parseD3(content, palette) {
4385
5475
  continue;
4386
5476
  }
4387
5477
  }
4388
- const colonIndex = line2.indexOf(":");
5478
+ const colonIndex = line3.indexOf(":");
4389
5479
  if (colonIndex !== -1) {
4390
- const rawKey = line2.substring(0, colonIndex).trim();
5480
+ const rawKey = line3.substring(0, colonIndex).trim();
4391
5481
  const key = rawKey.toLowerCase();
4392
5482
  const colorMatch = rawKey.match(/^(.+?)\(([^)]+)\)\s*$/);
4393
5483
  if (key === "chart") {
4394
- const value = line2.substring(colonIndex + 1).trim().toLowerCase();
5484
+ const value = line3.substring(colonIndex + 1).trim().toLowerCase();
4395
5485
  if (value === "slope" || value === "wordcloud" || value === "arc" || value === "timeline" || value === "venn" || value === "quadrant" || value === "sequence") {
4396
5486
  result.type = value;
4397
5487
  } else {
@@ -4401,35 +5491,35 @@ function parseD3(content, palette) {
4401
5491
  continue;
4402
5492
  }
4403
5493
  if (key === "title") {
4404
- result.title = line2.substring(colonIndex + 1).trim();
5494
+ result.title = line3.substring(colonIndex + 1).trim();
4405
5495
  if (result.type === "quadrant") {
4406
5496
  result.quadrantTitleLineNumber = lineNumber;
4407
5497
  }
4408
5498
  continue;
4409
5499
  }
4410
5500
  if (key === "orientation") {
4411
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5501
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4412
5502
  if (v === "horizontal" || v === "vertical") {
4413
5503
  result.orientation = v;
4414
5504
  }
4415
5505
  continue;
4416
5506
  }
4417
5507
  if (key === "order") {
4418
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5508
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4419
5509
  if (v === "name" || v === "group" || v === "degree") {
4420
5510
  result.arcOrder = v;
4421
5511
  }
4422
5512
  continue;
4423
5513
  }
4424
5514
  if (key === "sort") {
4425
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5515
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4426
5516
  if (v === "time" || v === "group") {
4427
5517
  result.timelineSort = v;
4428
5518
  }
4429
5519
  continue;
4430
5520
  }
4431
5521
  if (key === "swimlanes") {
4432
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5522
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4433
5523
  if (v === "on") {
4434
5524
  result.timelineSwimlanes = true;
4435
5525
  } else if (v === "off") {
@@ -4438,7 +5528,7 @@ function parseD3(content, palette) {
4438
5528
  continue;
4439
5529
  }
4440
5530
  if (key === "values") {
4441
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5531
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4442
5532
  if (v === "off") {
4443
5533
  result.vennShowValues = false;
4444
5534
  } else if (v === "on") {
@@ -4447,21 +5537,21 @@ function parseD3(content, palette) {
4447
5537
  continue;
4448
5538
  }
4449
5539
  if (key === "rotate") {
4450
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5540
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4451
5541
  if (v === "none" || v === "mixed" || v === "angled") {
4452
5542
  result.cloudOptions.rotate = v;
4453
5543
  }
4454
5544
  continue;
4455
5545
  }
4456
5546
  if (key === "max") {
4457
- const v = parseInt(line2.substring(colonIndex + 1).trim(), 10);
5547
+ const v = parseInt(line3.substring(colonIndex + 1).trim(), 10);
4458
5548
  if (!isNaN(v) && v > 0) {
4459
5549
  result.cloudOptions.max = v;
4460
5550
  }
4461
5551
  continue;
4462
5552
  }
4463
5553
  if (key === "size") {
4464
- const v = line2.substring(colonIndex + 1).trim();
5554
+ const v = line3.substring(colonIndex + 1).trim();
4465
5555
  const parts = v.split(",").map((s) => parseInt(s.trim(), 10));
4466
5556
  if (parts.length === 2 && parts.every((n) => !isNaN(n) && n > 0) && parts[0] < parts[1]) {
4467
5557
  result.cloudOptions.minSize = parts[0];
@@ -4471,7 +5561,7 @@ function parseD3(content, palette) {
4471
5561
  }
4472
5562
  const labelPart = colorMatch ? colorMatch[1].trim() : rawKey;
4473
5563
  const colorPart = colorMatch ? resolveColor(colorMatch[2].trim(), palette) : null;
4474
- const valuePart = line2.substring(colonIndex + 1).trim();
5564
+ const valuePart = line3.substring(colonIndex + 1).trim();
4475
5565
  const values = valuePart.split(",").map((v) => v.trim());
4476
5566
  const numericValues = [];
4477
5567
  let allNumeric = true;
@@ -4502,15 +5592,15 @@ function parseD3(content, palette) {
4502
5592
  }
4503
5593
  }
4504
5594
  if (result.type === "wordcloud") {
4505
- if (colonIndex === -1 && !line2.includes(" ")) {
4506
- result.words.push({ text: line2, weight: 10, lineNumber });
5595
+ if (colonIndex === -1 && !line3.includes(" ")) {
5596
+ result.words.push({ text: line3, weight: 10, lineNumber });
4507
5597
  } else {
4508
- freeformLines.push(line2);
5598
+ freeformLines.push(line3);
4509
5599
  }
4510
5600
  continue;
4511
5601
  }
4512
- if (result.periods.length === 0 && line2.includes(",") && !line2.includes(":")) {
4513
- const periods = line2.split(",").map((p) => p.trim()).filter(Boolean);
5602
+ if (result.periods.length === 0 && line3.includes(",") && !line3.includes(":")) {
5603
+ const periods = line3.split(",").map((p) => p.trim()).filter(Boolean);
4514
5604
  if (periods.length >= 2) {
4515
5605
  result.periods = periods;
4516
5606
  continue;
@@ -4733,7 +5823,7 @@ var SLOPE_MARGIN = { top: 80, bottom: 40, left: 80 };
4733
5823
  var SLOPE_LABEL_FONT_SIZE = 14;
4734
5824
  var SLOPE_CHAR_WIDTH = 8;
4735
5825
  function renderSlopeChart(container, parsed, palette, isDark, onClickItem, exportDims) {
4736
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
5826
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
4737
5827
  const { periods, data, title } = parsed;
4738
5828
  if (data.length === 0 || periods.length < 2) return;
4739
5829
  const width = exportDims?.width ?? container.clientWidth;
@@ -4760,7 +5850,7 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
4760
5850
  const valuePadding = (maxVal - minVal) * 0.1 || 1;
4761
5851
  const yScale = d3Scale.scaleLinear().domain([minVal - valuePadding, maxVal + valuePadding]).range([innerHeight, 0]);
4762
5852
  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);
5853
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
4764
5854
  const g = svg.append("g").attr("transform", `translate(${SLOPE_MARGIN.left},${SLOPE_MARGIN.top})`);
4765
5855
  const tooltip = createTooltip(container, palette, isDark);
4766
5856
  if (title) {
@@ -4771,7 +5861,7 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
4771
5861
  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
5862
  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
5863
  }
4774
- const lineGen = d3Shape.line().x((_d, i) => xScale(periods[i])).y((d) => yScale(d));
5864
+ const lineGen = d3Shape2.line().x((_d, i) => xScale(periods[i])).y((d) => yScale(d));
4775
5865
  data.forEach((item, idx) => {
4776
5866
  const color = item.color ?? colors[idx % colors.length];
4777
5867
  const firstVal = item.values[0];
@@ -4834,11 +5924,11 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
4834
5924
  const lineHeight = SLOPE_LABEL_FONT_SIZE * 1.2;
4835
5925
  const totalHeight = (lines.length - 1) * lineHeight;
4836
5926
  const startDy = -totalHeight / 2;
4837
- lines.forEach((line2, li) => {
5927
+ lines.forEach((line3, li) => {
4838
5928
  labelEl.append("tspan").attr("x", lastX + 10).attr(
4839
5929
  "dy",
4840
5930
  li === 0 ? `${startDy + SLOPE_LABEL_FONT_SIZE * 0.35}px` : `${lineHeight}px`
4841
- ).text(line2);
5931
+ ).text(line3);
4842
5932
  });
4843
5933
  }
4844
5934
  });
@@ -4933,7 +6023,7 @@ function orderArcNodes(links, order, groups) {
4933
6023
  }
4934
6024
  var ARC_MARGIN = { top: 60, right: 40, bottom: 60, left: 40 };
4935
6025
  function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, exportDims) {
4936
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
6026
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
4937
6027
  const { links, title, orientation, arcOrder, arcNodeGroups } = parsed;
4938
6028
  if (links.length === 0) return;
4939
6029
  const width = exportDims?.width ?? container.clientWidth;
@@ -4970,7 +6060,7 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
4970
6060
  const values = links.map((l) => l.value);
4971
6061
  const [minVal, maxVal] = d3Array.extent(values);
4972
6062
  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);
6063
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
4974
6064
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
4975
6065
  if (title) {
4976
6066
  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 +6075,14 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
4985
6075
  function handleMouseEnter(hovered) {
4986
6076
  const connected = neighbors.get(hovered);
4987
6077
  g.selectAll(".arc-link").each(function() {
4988
- const el = d3Selection2.select(this);
6078
+ const el = d3Selection3.select(this);
4989
6079
  const src = el.attr("data-source");
4990
6080
  const tgt = el.attr("data-target");
4991
6081
  const isRelated = src === hovered || tgt === hovered;
4992
6082
  el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
4993
6083
  });
4994
6084
  g.selectAll(".arc-node").each(function() {
4995
- const el = d3Selection2.select(this);
6085
+ const el = d3Selection3.select(this);
4996
6086
  const name = el.attr("data-node");
4997
6087
  const isRelated = name === hovered || connected.has(name);
4998
6088
  el.attr("opacity", isRelated ? 1 : FADE_OPACITY);
@@ -5017,23 +6107,23 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
5017
6107
  const members = groupNodeSets.get(groupName);
5018
6108
  if (!members) return;
5019
6109
  g.selectAll(".arc-link").each(function() {
5020
- const el = d3Selection2.select(this);
6110
+ const el = d3Selection3.select(this);
5021
6111
  const isRelated = members.has(el.attr("data-source")) || members.has(el.attr("data-target"));
5022
6112
  el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
5023
6113
  });
5024
6114
  g.selectAll(".arc-node").each(function() {
5025
- const el = d3Selection2.select(this);
6115
+ const el = d3Selection3.select(this);
5026
6116
  el.attr("opacity", members.has(el.attr("data-node")) ? 1 : FADE_OPACITY);
5027
6117
  });
5028
6118
  g.selectAll(".arc-group-band").each(function() {
5029
- const el = d3Selection2.select(this);
6119
+ const el = d3Selection3.select(this);
5030
6120
  el.attr(
5031
6121
  "fill-opacity",
5032
6122
  el.attr("data-group") === groupName ? 0.18 : 0.03
5033
6123
  );
5034
6124
  });
5035
6125
  g.selectAll(".arc-group-label").each(function() {
5036
- const el = d3Selection2.select(this);
6126
+ const el = d3Selection3.select(this);
5037
6127
  el.attr("fill-opacity", el.attr("data-group") === groupName ? 1 : 0.2);
5038
6128
  });
5039
6129
  }
@@ -5236,7 +6326,14 @@ function computeTimeTicks(domainMin, domainMax, scale, boundaryStart, boundaryEn
5236
6326
  const firstYear = Math.ceil(domainMin);
5237
6327
  const lastYear = Math.floor(domainMax);
5238
6328
  if (lastYear >= firstYear + 1) {
5239
- for (let y = firstYear; y <= lastYear; y++) {
6329
+ const yearSpan = lastYear - firstYear;
6330
+ let step = 1;
6331
+ if (yearSpan > 80) step = 20;
6332
+ else if (yearSpan > 40) step = 10;
6333
+ else if (yearSpan > 20) step = 5;
6334
+ else if (yearSpan > 10) step = 2;
6335
+ const alignedFirst = Math.ceil(firstYear / step) * step;
6336
+ for (let y = alignedFirst; y <= lastYear; y += step) {
5240
6337
  ticks.push({ pos: scale(y), label: String(y) });
5241
6338
  }
5242
6339
  } else if (span > 0.25) {
@@ -5336,7 +6433,7 @@ function showEventDatesOnScale(g, scale, startDate, endDate, innerHeight, accent
5336
6433
  function hideEventDatesOnScale(g) {
5337
6434
  g.selectAll(".tl-event-date").remove();
5338
6435
  g.selectAll(".tl-scale-tick").each(function() {
5339
- const el = d3Selection2.select(this);
6436
+ const el = d3Selection3.select(this);
5340
6437
  const isDashed = el.attr("stroke-dasharray");
5341
6438
  el.attr("opacity", isDashed ? 0.15 : 0.4);
5342
6439
  });
@@ -5394,7 +6491,7 @@ function buildEraTooltipHtml(era) {
5394
6491
  return `<strong>${era.label}</strong><br>${formatDateLabel(era.startDate)} \u2192 ${formatDateLabel(era.endDate)}`;
5395
6492
  }
5396
6493
  function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims) {
5397
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
6494
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
5398
6495
  const {
5399
6496
  timelineEvents,
5400
6497
  timelineGroups,
@@ -5446,13 +6543,13 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5446
6543
  const FADE_OPACITY = 0.1;
5447
6544
  function fadeToGroup(g, groupName) {
5448
6545
  g.selectAll(".tl-event").each(function() {
5449
- const el = d3Selection2.select(this);
6546
+ const el = d3Selection3.select(this);
5450
6547
  const evGroup = el.attr("data-group");
5451
6548
  el.attr("opacity", evGroup === groupName ? 1 : FADE_OPACITY);
5452
6549
  });
5453
6550
  g.selectAll(".tl-legend-item, .tl-lane-header").each(
5454
6551
  function() {
5455
- const el = d3Selection2.select(this);
6552
+ const el = d3Selection3.select(this);
5456
6553
  const name = el.attr("data-group");
5457
6554
  el.attr("opacity", name === groupName ? 1 : FADE_OPACITY);
5458
6555
  }
@@ -5464,7 +6561,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5464
6561
  }
5465
6562
  function fadeToEra(g, eraStart, eraEnd) {
5466
6563
  g.selectAll(".tl-event").each(function() {
5467
- const el = d3Selection2.select(this);
6564
+ const el = d3Selection3.select(this);
5468
6565
  const date = parseFloat(el.attr("data-date"));
5469
6566
  const endDate = el.attr("data-end-date");
5470
6567
  const evEnd = endDate ? parseFloat(endDate) : date;
@@ -5476,14 +6573,14 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5476
6573
  FADE_OPACITY
5477
6574
  );
5478
6575
  g.selectAll(".tl-era").each(function() {
5479
- const el = d3Selection2.select(this);
6576
+ const el = d3Selection3.select(this);
5480
6577
  const s = parseFloat(el.attr("data-era-start"));
5481
6578
  const e = parseFloat(el.attr("data-era-end"));
5482
6579
  const isSelf = s === eraStart && e === eraEnd;
5483
6580
  el.attr("opacity", isSelf ? 1 : FADE_OPACITY);
5484
6581
  });
5485
6582
  g.selectAll(".tl-marker").each(function() {
5486
- const el = d3Selection2.select(this);
6583
+ const el = d3Selection3.select(this);
5487
6584
  const date = parseFloat(el.attr("data-marker-date"));
5488
6585
  const inside = date >= eraStart && date <= eraEnd;
5489
6586
  el.attr("opacity", inside ? 1 : FADE_OPACITY);
@@ -5515,7 +6612,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5515
6612
  const innerHeight = height - margin.top - margin.bottom;
5516
6613
  const laneWidth = innerWidth / laneCount;
5517
6614
  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);
6615
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5519
6616
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
5520
6617
  if (title) {
5521
6618
  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 +6706,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5609
6706
  const axisX = 20;
5610
6707
  const yScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
5611
6708
  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);
6709
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5613
6710
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
5614
6711
  if (title) {
5615
6712
  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 +6829,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5732
6829
  const totalGaps = (lanes.length - 1) * GROUP_GAP;
5733
6830
  const rowH = Math.min(28, (innerHeight - totalGaps) / totalEventRows);
5734
6831
  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);
6832
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5736
6833
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
5737
6834
  if (title) {
5738
6835
  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 +6933,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5836
6933
  if (ev.uncertain) {
5837
6934
  const gradientId = `uncertain-${ev.lineNumber}`;
5838
6935
  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([
6936
+ d3Selection3.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
5840
6937
  { offset: "0%", opacity: 1 },
5841
6938
  { offset: "80%", opacity: 1 },
5842
6939
  { offset: "100%", opacity: 0 }
@@ -5877,7 +6974,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5877
6974
  const innerHeight = height - margin.top - margin.bottom;
5878
6975
  const rowH = Math.min(28, innerHeight / sorted.length);
5879
6976
  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);
6977
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5881
6978
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
5882
6979
  if (title) {
5883
6980
  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 +7072,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5975
7072
  if (ev.uncertain) {
5976
7073
  const gradientId = `uncertain-ts-${ev.lineNumber}`;
5977
7074
  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([
7075
+ d3Selection3.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
5979
7076
  { offset: "0%", opacity: 1 },
5980
7077
  { offset: "80%", opacity: 1 },
5981
7078
  { offset: "100%", opacity: 0 }
@@ -6008,7 +7105,7 @@ function getRotateFn(mode) {
6008
7105
  return () => 0;
6009
7106
  }
6010
7107
  function renderWordCloud(container, parsed, palette, _isDark, onClickItem, exportDims) {
6011
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
7108
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
6012
7109
  const { words, title, cloudOptions } = parsed;
6013
7110
  if (words.length === 0) return;
6014
7111
  const width = exportDims?.width ?? container.clientWidth;
@@ -6029,7 +7126,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
6029
7126
  return minSize + t * (maxSize - minSize);
6030
7127
  };
6031
7128
  const rotateFn = getRotateFn(cloudOptions.rotate);
6032
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7129
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6033
7130
  if (title) {
6034
7131
  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
7132
  }
@@ -6052,7 +7149,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
6052
7149
  }
6053
7150
  function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
6054
7151
  return new Promise((resolve) => {
6055
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
7152
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
6056
7153
  const { words, title, cloudOptions } = parsed;
6057
7154
  if (words.length === 0) {
6058
7155
  resolve();
@@ -6079,7 +7176,7 @@ function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
6079
7176
  return minSize + t * (maxSize - minSize);
6080
7177
  };
6081
7178
  const rotateFn = getRotateFn(cloudOptions.rotate);
6082
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7179
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6083
7180
  if (title) {
6084
7181
  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
7182
  }
@@ -6223,7 +7320,7 @@ function circlePathD(cx, cy, r) {
6223
7320
  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
7321
  }
6225
7322
  function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims) {
6226
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
7323
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
6227
7324
  const { vennSets, vennOverlaps, vennShowValues, title } = parsed;
6228
7325
  if (vennSets.length < 2) return;
6229
7326
  const width = exportDims?.width ?? container.clientWidth;
@@ -6288,7 +7385,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
6288
7385
  const setColors = vennSets.map(
6289
7386
  (s, i) => s.color ?? colors[i % colors.length]
6290
7387
  );
6291
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7388
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6292
7389
  const tooltip = createTooltip(container, palette, isDark);
6293
7390
  if (title) {
6294
7391
  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 +7531,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
6434
7531
  });
6435
7532
  }
6436
7533
  function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportDims) {
6437
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
7534
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
6438
7535
  const {
6439
7536
  title,
6440
7537
  quadrantLabels,
@@ -6466,7 +7563,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6466
7563
  const chartHeight = height - margin.top - margin.bottom;
6467
7564
  const xScale = d3Scale.scaleLinear().domain([0, 1]).range([0, chartWidth]);
6468
7565
  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);
7566
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6470
7567
  const tooltip = createTooltip(container, palette, isDark);
6471
7568
  if (title) {
6472
7569
  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 +7572,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6475
7572
  ).text(title);
6476
7573
  if (onClickItem && quadrantTitleLineNumber) {
6477
7574
  titleText.on("click", () => onClickItem(quadrantTitleLineNumber)).on("mouseenter", function() {
6478
- d3Selection2.select(this).attr("opacity", 0.7);
7575
+ d3Selection3.select(this).attr("opacity", 0.7);
6479
7576
  }).on("mouseleave", function() {
6480
- d3Selection2.select(this).attr("opacity", 1);
7577
+ d3Selection3.select(this).attr("opacity", 1);
6481
7578
  });
6482
7579
  }
6483
7580
  }
@@ -6560,9 +7657,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6560
7657
  quadrantLabelTexts.on("click", (_, d) => {
6561
7658
  if (d.label?.lineNumber) onClickItem(d.label.lineNumber);
6562
7659
  }).on("mouseenter", function() {
6563
- d3Selection2.select(this).attr("opacity", 0.7);
7660
+ d3Selection3.select(this).attr("opacity", 0.7);
6564
7661
  }).on("mouseleave", function() {
6565
- d3Selection2.select(this).attr("opacity", 1);
7662
+ d3Selection3.select(this).attr("opacity", 1);
6566
7663
  });
6567
7664
  }
6568
7665
  if (quadrantXAxis) {
@@ -6577,9 +7674,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6577
7674
  if (onClickItem && quadrantXAxisLineNumber) {
6578
7675
  [xLowLabel, xHighLabel].forEach((label) => {
6579
7676
  label.on("click", () => onClickItem(quadrantXAxisLineNumber)).on("mouseenter", function() {
6580
- d3Selection2.select(this).attr("opacity", 0.7);
7677
+ d3Selection3.select(this).attr("opacity", 0.7);
6581
7678
  }).on("mouseleave", function() {
6582
- d3Selection2.select(this).attr("opacity", 1);
7679
+ d3Selection3.select(this).attr("opacity", 1);
6583
7680
  });
6584
7681
  });
6585
7682
  }
@@ -6598,9 +7695,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6598
7695
  if (onClickItem && quadrantYAxisLineNumber) {
6599
7696
  [yLowLabel, yHighLabel].forEach((label) => {
6600
7697
  label.on("click", () => onClickItem(quadrantYAxisLineNumber)).on("mouseenter", function() {
6601
- d3Selection2.select(this).attr("opacity", 0.7);
7698
+ d3Selection3.select(this).attr("opacity", 0.7);
6602
7699
  }).on("mouseleave", function() {
6603
- d3Selection2.select(this).attr("opacity", 1);
7700
+ d3Selection3.select(this).attr("opacity", 1);
6604
7701
  });
6605
7702
  });
6606
7703
  }
@@ -6648,7 +7745,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6648
7745
  pointsG.selectAll("g.point-group").each(function(_2, i) {
6649
7746
  const pt = quadrantPoints[i];
6650
7747
  const ptQuad = getPointQuadrant(pt.x, pt.y);
6651
- d3Selection2.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
7748
+ d3Selection3.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
6652
7749
  });
6653
7750
  }).on("mouseleave", () => {
6654
7751
  quadrantRects.attr("opacity", 1);
@@ -6663,6 +7760,43 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6663
7760
  var EXPORT_WIDTH = 1200;
6664
7761
  var EXPORT_HEIGHT = 800;
6665
7762
  async function renderD3ForExport(content, theme, palette) {
7763
+ const { parseDgmoChartType: parseDgmoChartType2 } = await Promise.resolve().then(() => (init_dgmo_router(), dgmo_router_exports));
7764
+ const detectedType = parseDgmoChartType2(content);
7765
+ if (detectedType === "flowchart") {
7766
+ const { parseFlowchart: parseFlowchart2 } = await Promise.resolve().then(() => (init_flowchart_parser(), flowchart_parser_exports));
7767
+ const { layoutGraph: layoutGraph2 } = await Promise.resolve().then(() => (init_layout(), layout_exports));
7768
+ const { renderFlowchart: renderFlowchart2 } = await Promise.resolve().then(() => (init_flowchart_renderer(), flowchart_renderer_exports));
7769
+ const isDark2 = theme === "dark";
7770
+ const { getPalette: getPalette3 } = await Promise.resolve().then(() => (init_palettes(), palettes_exports));
7771
+ const effectivePalette2 = palette ?? (isDark2 ? getPalette3("nord").dark : getPalette3("nord").light);
7772
+ const fcParsed = parseFlowchart2(content, effectivePalette2);
7773
+ if (fcParsed.error || fcParsed.nodes.length === 0) return "";
7774
+ const layout = layoutGraph2(fcParsed);
7775
+ const container2 = document.createElement("div");
7776
+ container2.style.width = `${EXPORT_WIDTH}px`;
7777
+ container2.style.height = `${EXPORT_HEIGHT}px`;
7778
+ container2.style.position = "absolute";
7779
+ container2.style.left = "-9999px";
7780
+ document.body.appendChild(container2);
7781
+ try {
7782
+ renderFlowchart2(container2, fcParsed, layout, effectivePalette2, isDark2, void 0, {
7783
+ width: EXPORT_WIDTH,
7784
+ height: EXPORT_HEIGHT
7785
+ });
7786
+ const svgEl = container2.querySelector("svg");
7787
+ if (!svgEl) return "";
7788
+ if (theme === "transparent") {
7789
+ svgEl.style.background = "none";
7790
+ } else if (!svgEl.style.background) {
7791
+ svgEl.style.background = effectivePalette2.bg;
7792
+ }
7793
+ svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
7794
+ svgEl.style.fontFamily = FONT_FAMILY;
7795
+ return svgEl.outerHTML;
7796
+ } finally {
7797
+ document.body.removeChild(container2);
7798
+ }
7799
+ }
6666
7800
  const parsed = parseD3(content, palette);
6667
7801
  if (parsed.error && parsed.type !== "sequence") {
6668
7802
  const looksLikeSequence2 = /->|~>|<-/.test(content);
@@ -6757,17 +7891,17 @@ function parseQuadrant(content) {
6757
7891
  };
6758
7892
  const lines = content.split("\n");
6759
7893
  for (let i = 0; i < lines.length; i++) {
6760
- const line2 = lines[i].trim();
7894
+ const line3 = lines[i].trim();
6761
7895
  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);
7896
+ if (!line3 || line3.startsWith("#") || line3.startsWith("//")) continue;
7897
+ if (/^chart\s*:/i.test(line3)) continue;
7898
+ const titleMatch = line3.match(/^title\s*:\s*(.+)/i);
6765
7899
  if (titleMatch) {
6766
7900
  result.title = titleMatch[1].trim();
6767
7901
  result.titleLineNumber = lineNumber;
6768
7902
  continue;
6769
7903
  }
6770
- const xMatch = line2.match(/^x-axis\s*:\s*(.+)/i);
7904
+ const xMatch = line3.match(/^x-axis\s*:\s*(.+)/i);
6771
7905
  if (xMatch) {
6772
7906
  const parts = xMatch[1].split(",").map((s) => s.trim());
6773
7907
  if (parts.length >= 2) {
@@ -6776,7 +7910,7 @@ function parseQuadrant(content) {
6776
7910
  }
6777
7911
  continue;
6778
7912
  }
6779
- const yMatch = line2.match(/^y-axis\s*:\s*(.+)/i);
7913
+ const yMatch = line3.match(/^y-axis\s*:\s*(.+)/i);
6780
7914
  if (yMatch) {
6781
7915
  const parts = yMatch[1].split(",").map((s) => s.trim());
6782
7916
  if (parts.length >= 2) {
@@ -6785,7 +7919,7 @@ function parseQuadrant(content) {
6785
7919
  }
6786
7920
  continue;
6787
7921
  }
6788
- const posMatch = line2.match(
7922
+ const posMatch = line3.match(
6789
7923
  /^(top-right|top-left|bottom-left|bottom-right)\s*:\s*(.+)/i
6790
7924
  );
6791
7925
  if (posMatch) {
@@ -6806,7 +7940,7 @@ function parseQuadrant(content) {
6806
7940
  }
6807
7941
  continue;
6808
7942
  }
6809
- const pointMatch = line2.match(DATA_POINT_RE);
7943
+ const pointMatch = line3.match(DATA_POINT_RE);
6810
7944
  if (pointMatch) {
6811
7945
  const key = pointMatch[1].trim().toLowerCase();
6812
7946
  if (!QUADRANT_POSITIONS.has(key)) {
@@ -6880,6 +8014,9 @@ function buildMermaidQuadrant(parsed, options = {}) {
6880
8014
  }
6881
8015
 
6882
8016
  // src/index.ts
8017
+ init_flowchart_parser();
8018
+ init_layout();
8019
+ init_flowchart_renderer();
6883
8020
  init_renderer();
6884
8021
  init_colors();
6885
8022
  init_palettes();
@@ -6914,7 +8051,10 @@ init_palettes();
6914
8051
  hslToHex,
6915
8052
  inferParticipantType,
6916
8053
  isSequenceBlock,
8054
+ isSequenceNote,
6917
8055
  isValidHex,
8056
+ layoutGraph,
8057
+ looksLikeFlowchart,
6918
8058
  looksLikeSequence,
6919
8059
  mute,
6920
8060
  nord,
@@ -6925,6 +8065,7 @@ init_palettes();
6925
8065
  parseD3,
6926
8066
  parseDgmoChartType,
6927
8067
  parseEChart,
8068
+ parseFlowchart,
6928
8069
  parseQuadrant,
6929
8070
  parseSequenceDgmo,
6930
8071
  parseTimelineDate,
@@ -6932,6 +8073,8 @@ init_palettes();
6932
8073
  renderArcDiagram,
6933
8074
  renderD3ForExport,
6934
8075
  renderEChartsForExport,
8076
+ renderFlowchart,
8077
+ renderFlowchartForExport,
6935
8078
  renderQuadrant,
6936
8079
  renderSequenceDiagram,
6937
8080
  renderSlopeChart,