@diagrammo/dgmo 0.2.5 → 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.js CHANGED
@@ -147,6 +147,7 @@ var init_participant_inference = __esm({
147
147
  var parser_exports = {};
148
148
  __export(parser_exports, {
149
149
  isSequenceBlock: () => isSequenceBlock,
150
+ isSequenceNote: () => isSequenceNote,
150
151
  isSequenceSection: () => isSequenceSection,
151
152
  looksLikeSequence: () => looksLikeSequence,
152
153
  parseSequenceDgmo: () => parseSequenceDgmo
@@ -157,6 +158,9 @@ function isSequenceBlock(el) {
157
158
  function isSequenceSection(el) {
158
159
  return "kind" in el && el.kind === "section";
159
160
  }
161
+ function isSequenceNote(el) {
162
+ return "kind" in el && el.kind === "note";
163
+ }
160
164
  function parseReturnLabel(rawLabel) {
161
165
  if (!rawLabel) return { label: "" };
162
166
  const arrowReturn = rawLabel.match(ARROW_RETURN_PATTERN);
@@ -167,19 +171,22 @@ function parseReturnLabel(rawLabel) {
167
171
  if (umlReturn) {
168
172
  return { label: umlReturn[1].trim(), returnLabel: umlReturn[2].trim() };
169
173
  }
170
- const lastSep = rawLabel.lastIndexOf(" : ");
171
- if (lastSep > 0) {
172
- const reqPart = rawLabel.substring(0, lastSep).trim();
173
- const resPart = rawLabel.substring(lastSep + 3).trim();
174
- if (reqPart && resPart) {
175
- return { label: reqPart, returnLabel: resPart };
174
+ const lastColon = rawLabel.lastIndexOf(":");
175
+ if (lastColon > 0 && lastColon < rawLabel.length - 1) {
176
+ const afterColon = rawLabel.substring(lastColon + 1);
177
+ if (!afterColon.startsWith("//")) {
178
+ const reqPart = rawLabel.substring(0, lastColon).trim();
179
+ const resPart = afterColon.trim();
180
+ if (reqPart && resPart) {
181
+ return { label: reqPart, returnLabel: resPart };
182
+ }
176
183
  }
177
184
  }
178
185
  return { label: rawLabel };
179
186
  }
180
- function measureIndent(line2) {
187
+ function measureIndent(line3) {
181
188
  let indent = 0;
182
- for (const ch of line2) {
189
+ for (const ch of line3) {
183
190
  if (ch === " ") indent++;
184
191
  else if (ch === " ") indent += 4;
185
192
  else break;
@@ -203,13 +210,17 @@ function parseSequenceDgmo(content) {
203
210
  }
204
211
  const lines = content.split("\n");
205
212
  let hasExplicitChart = false;
213
+ let contentStarted = false;
206
214
  let activeGroup = null;
215
+ const participantGroupMap = /* @__PURE__ */ new Map();
207
216
  const blockStack = [];
208
217
  const currentContainer = () => {
209
218
  if (blockStack.length === 0) return result.elements;
210
219
  const top = blockStack[blockStack.length - 1];
220
+ if (top.activeElseIfBranch) return top.activeElseIfBranch.children;
211
221
  return top.inElse ? top.block.elseChildren : top.block.children;
212
222
  };
223
+ let lastMsgFrom = null;
213
224
  for (let i = 0; i < lines.length; i++) {
214
225
  const raw = lines[i];
215
226
  const trimmed = raw.trim();
@@ -220,9 +231,15 @@ function parseSequenceDgmo(content) {
220
231
  }
221
232
  const groupMatch = trimmed.match(GROUP_HEADING_PATTERN);
222
233
  if (groupMatch) {
234
+ const groupColor = groupMatch[2]?.trim();
235
+ if (groupColor && groupColor.startsWith("#")) {
236
+ result.error = `Line ${lineNumber}: Use a named color instead of hex (e.g., blue, red, teal)`;
237
+ return result;
238
+ }
239
+ contentStarted = true;
223
240
  activeGroup = {
224
- name: groupMatch[1],
225
- color: groupMatch[2] || void 0,
241
+ name: groupMatch[1].trim(),
242
+ color: groupColor || void 0,
226
243
  participantIds: [],
227
244
  lineNumber
228
245
  };
@@ -232,7 +249,11 @@ function parseSequenceDgmo(content) {
232
249
  if (activeGroup && measureIndent(raw) === 0) {
233
250
  activeGroup = null;
234
251
  }
235
- if (trimmed.startsWith("#") || trimmed.startsWith("//")) continue;
252
+ if (trimmed.startsWith("//")) continue;
253
+ if (trimmed.startsWith("#") && !trimmed.startsWith("##")) {
254
+ result.error = `Line ${lineNumber}: Use // for comments. # is reserved for group headings (##)`;
255
+ return result;
256
+ }
236
257
  const sectionMatch = trimmed.match(SECTION_PATTERN);
237
258
  if (sectionMatch) {
238
259
  const sectionIndent = measureIndent(raw);
@@ -242,11 +263,16 @@ function parseSequenceDgmo(content) {
242
263
  blockStack.pop();
243
264
  }
244
265
  const labelRaw = sectionMatch[1].trim();
245
- const colorMatch = labelRaw.match(/^(.+?)\((\w+)\)$/);
266
+ const colorMatch = labelRaw.match(/^(.+?)\(([^)]+)\)$/);
267
+ if (colorMatch && colorMatch[2].trim().startsWith("#")) {
268
+ result.error = `Line ${lineNumber}: Use a named color instead of hex (e.g., blue, red, teal)`;
269
+ return result;
270
+ }
271
+ contentStarted = true;
246
272
  const section = {
247
273
  kind: "section",
248
274
  label: colorMatch ? colorMatch[1].trim() : labelRaw,
249
- color: colorMatch ? colorMatch[2] : void 0,
275
+ color: colorMatch ? colorMatch[2].trim() : void 0,
250
276
  lineNumber
251
277
  };
252
278
  result.sections.push(section);
@@ -256,24 +282,32 @@ function parseSequenceDgmo(content) {
256
282
  const colonIndex = trimmed.indexOf(":");
257
283
  if (colonIndex > 0 && !trimmed.includes("->") && !trimmed.includes("~>")) {
258
284
  const key = trimmed.substring(0, colonIndex).trim().toLowerCase();
259
- const value = trimmed.substring(colonIndex + 1).trim();
260
- if (key === "chart") {
261
- hasExplicitChart = true;
262
- if (value.toLowerCase() !== "sequence") {
263
- result.error = `Expected chart type "sequence", got "${value}"`;
285
+ if (key === "note" || key.startsWith("note ")) {
286
+ } else {
287
+ const value = trimmed.substring(colonIndex + 1).trim();
288
+ if (key === "chart") {
289
+ hasExplicitChart = true;
290
+ if (value.toLowerCase() !== "sequence") {
291
+ result.error = `Expected chart type "sequence", got "${value}"`;
292
+ return result;
293
+ }
294
+ continue;
295
+ }
296
+ if (contentStarted) {
297
+ result.error = `Line ${lineNumber}: Options like '${key}: ${value}' must appear before the first message or declaration`;
264
298
  return result;
265
299
  }
300
+ if (key === "title") {
301
+ result.title = value;
302
+ continue;
303
+ }
304
+ result.options[key] = value;
266
305
  continue;
267
306
  }
268
- if (key === "title") {
269
- result.title = value;
270
- continue;
271
- }
272
- result.options[key] = value;
273
- continue;
274
307
  }
275
308
  const isAMatch = trimmed.match(IS_A_PATTERN);
276
309
  if (isAMatch) {
310
+ contentStarted = true;
277
311
  const id = isAMatch[1];
278
312
  const typeStr = isAMatch[2].toLowerCase();
279
313
  const remainder = isAMatch[3]?.trim() || "";
@@ -296,12 +330,19 @@ function parseSequenceDgmo(content) {
296
330
  });
297
331
  }
298
332
  if (activeGroup && !activeGroup.participantIds.includes(id)) {
333
+ const existingGroup = participantGroupMap.get(id);
334
+ if (existingGroup) {
335
+ result.error = `Line ${lineNumber}: Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`;
336
+ return result;
337
+ }
299
338
  activeGroup.participantIds.push(id);
339
+ participantGroupMap.set(id, activeGroup.name);
300
340
  }
301
341
  continue;
302
342
  }
303
343
  const posOnlyMatch = trimmed.match(POSITION_ONLY_PATTERN);
304
344
  if (posOnlyMatch) {
345
+ contentStarted = true;
305
346
  const id = posOnlyMatch[1];
306
347
  const position = parseInt(posOnlyMatch[2], 10);
307
348
  if (!result.participants.some((p) => p.id === id)) {
@@ -314,11 +355,18 @@ function parseSequenceDgmo(content) {
314
355
  });
315
356
  }
316
357
  if (activeGroup && !activeGroup.participantIds.includes(id)) {
358
+ const existingGroup = participantGroupMap.get(id);
359
+ if (existingGroup) {
360
+ result.error = `Line ${lineNumber}: Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`;
361
+ return result;
362
+ }
317
363
  activeGroup.participantIds.push(id);
364
+ participantGroupMap.set(id, activeGroup.name);
318
365
  }
319
366
  continue;
320
367
  }
321
368
  if (activeGroup && measureIndent(raw) > 0 && /^\S+$/.test(trimmed)) {
369
+ contentStarted = true;
322
370
  const id = trimmed;
323
371
  if (!result.participants.some((p) => p.id === id)) {
324
372
  result.participants.push({
@@ -329,7 +377,13 @@ function parseSequenceDgmo(content) {
329
377
  });
330
378
  }
331
379
  if (!activeGroup.participantIds.includes(id)) {
380
+ const existingGroup = participantGroupMap.get(id);
381
+ if (existingGroup) {
382
+ result.error = `Line ${lineNumber}: Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`;
383
+ return result;
384
+ }
332
385
  activeGroup.participantIds.push(id);
386
+ participantGroupMap.set(id, activeGroup.name);
333
387
  }
334
388
  continue;
335
389
  }
@@ -337,28 +391,31 @@ function parseSequenceDgmo(content) {
337
391
  while (blockStack.length > 0) {
338
392
  const top = blockStack[blockStack.length - 1];
339
393
  if (indent > top.indent) break;
340
- if (indent === top.indent && trimmed.toLowerCase() === "else" && top.block.type === "if")
341
- break;
394
+ if (indent === top.indent && (top.block.type === "if" || top.block.type === "parallel")) {
395
+ const lower = trimmed.toLowerCase();
396
+ if (lower === "else" || lower.startsWith("else if ")) break;
397
+ }
342
398
  blockStack.pop();
343
399
  }
344
- let isAsync = false;
345
- let arrowLine = trimmed;
346
400
  const asyncPrefixMatch = trimmed.match(/^async\s+(.+)$/i);
347
- if (asyncPrefixMatch) {
348
- isAsync = true;
349
- arrowLine = asyncPrefixMatch[1];
401
+ if (asyncPrefixMatch && ARROW_PATTERN.test(asyncPrefixMatch[1])) {
402
+ result.error = `Line ${lineNumber}: Use ~> for async messages: A ~> B: message`;
403
+ return result;
350
404
  }
351
- const asyncArrowMatch = arrowLine.match(
405
+ let isAsync = false;
406
+ const asyncArrowMatch = trimmed.match(
352
407
  /^(\S+)\s*~>\s*([^\s:]+)\s*(?::\s*(.+))?$/
353
408
  );
354
- const syncArrowMatch = arrowLine.match(
409
+ const syncArrowMatch = trimmed.match(
355
410
  /^(\S+)\s*->\s*([^\s:]+)\s*(?::\s*(.+))?$/
356
411
  );
357
412
  const arrowMatch = asyncArrowMatch || syncArrowMatch;
358
413
  if (asyncArrowMatch) isAsync = true;
359
414
  if (arrowMatch) {
415
+ contentStarted = true;
360
416
  const from = arrowMatch[1];
361
417
  const to = arrowMatch[2];
418
+ lastMsgFrom = from;
362
419
  const rawLabel = arrowMatch[3]?.trim() || "";
363
420
  const { label, returnLabel } = isAsync ? { label: rawLabel, returnLabel: void 0 } : parseReturnLabel(rawLabel);
364
421
  const msg = {
@@ -391,6 +448,7 @@ function parseSequenceDgmo(content) {
391
448
  }
392
449
  const ifMatch = trimmed.match(/^if\s+(.+)$/i);
393
450
  if (ifMatch) {
451
+ contentStarted = true;
394
452
  const block = {
395
453
  kind: "block",
396
454
  type: "if",
@@ -405,6 +463,7 @@ function parseSequenceDgmo(content) {
405
463
  }
406
464
  const loopMatch = trimmed.match(/^loop\s+(.+)$/i);
407
465
  if (loopMatch) {
466
+ contentStarted = true;
408
467
  const block = {
409
468
  kind: "block",
410
469
  type: "loop",
@@ -419,6 +478,7 @@ function parseSequenceDgmo(content) {
419
478
  }
420
479
  const parallelMatch = trimmed.match(/^parallel(?:\s+(.+))?$/i);
421
480
  if (parallelMatch) {
481
+ contentStarted = true;
422
482
  const block = {
423
483
  kind: "block",
424
484
  type: "parallel",
@@ -431,15 +491,105 @@ function parseSequenceDgmo(content) {
431
491
  blockStack.push({ block, indent, inElse: false });
432
492
  continue;
433
493
  }
494
+ const elseIfMatch = trimmed.match(/^else\s+if\s+(.+)$/i);
495
+ if (elseIfMatch) {
496
+ if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent) {
497
+ const top = blockStack[blockStack.length - 1];
498
+ if (top.block.type === "parallel") {
499
+ result.error = `Line ${lineNumber}: parallel blocks don't support else if \u2014 list all concurrent messages directly inside the block`;
500
+ return result;
501
+ }
502
+ if (top.block.type === "if") {
503
+ const branch = { label: elseIfMatch[1].trim(), children: [] };
504
+ if (!top.block.elseIfBranches) top.block.elseIfBranches = [];
505
+ top.block.elseIfBranches.push(branch);
506
+ top.activeElseIfBranch = branch;
507
+ top.inElse = false;
508
+ }
509
+ }
510
+ continue;
511
+ }
434
512
  if (trimmed.toLowerCase() === "else") {
435
- if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent && blockStack[blockStack.length - 1].block.type === "if") {
436
- blockStack[blockStack.length - 1].inElse = true;
513
+ if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent) {
514
+ const top = blockStack[blockStack.length - 1];
515
+ if (top.block.type === "parallel") {
516
+ result.error = `Line ${lineNumber}: parallel blocks don't support else \u2014 list all concurrent messages directly inside the block`;
517
+ return result;
518
+ }
519
+ if (top.block.type === "if") {
520
+ top.inElse = true;
521
+ top.activeElseIfBranch = void 0;
522
+ }
437
523
  }
438
524
  continue;
439
525
  }
526
+ const noteSingleMatch = trimmed.match(NOTE_SINGLE);
527
+ if (noteSingleMatch) {
528
+ const notePosition = noteSingleMatch[1]?.toLowerCase() || "right";
529
+ let noteParticipant = noteSingleMatch[2] || null;
530
+ if (!noteParticipant) {
531
+ if (!lastMsgFrom) {
532
+ result.error = `Line ${lineNumber}: note requires a preceding message`;
533
+ return result;
534
+ }
535
+ noteParticipant = lastMsgFrom;
536
+ }
537
+ if (!result.participants.some((p) => p.id === noteParticipant)) {
538
+ result.error = `Line ${lineNumber}: note references unknown participant '${noteParticipant}'`;
539
+ return result;
540
+ }
541
+ const note = {
542
+ kind: "note",
543
+ text: noteSingleMatch[3].trim(),
544
+ position: notePosition,
545
+ participantId: noteParticipant,
546
+ lineNumber
547
+ };
548
+ currentContainer().push(note);
549
+ continue;
550
+ }
551
+ const noteMultiMatch = trimmed.match(NOTE_MULTI);
552
+ if (noteMultiMatch) {
553
+ const notePosition = noteMultiMatch[1]?.toLowerCase() || "right";
554
+ let noteParticipant = noteMultiMatch[2] || null;
555
+ if (!noteParticipant) {
556
+ if (!lastMsgFrom) {
557
+ result.error = `Line ${lineNumber}: note requires a preceding message`;
558
+ return result;
559
+ }
560
+ noteParticipant = lastMsgFrom;
561
+ }
562
+ if (!result.participants.some((p) => p.id === noteParticipant)) {
563
+ result.error = `Line ${lineNumber}: note references unknown participant '${noteParticipant}'`;
564
+ return result;
565
+ }
566
+ const noteLines = [];
567
+ while (i + 1 < lines.length) {
568
+ const nextRaw = lines[i + 1];
569
+ const nextTrimmed = nextRaw.trim();
570
+ if (!nextTrimmed) break;
571
+ const nextIndent = measureIndent(nextRaw);
572
+ if (nextIndent <= indent) break;
573
+ noteLines.push(nextTrimmed);
574
+ i++;
575
+ }
576
+ if (noteLines.length === 0) {
577
+ result.error = `Line ${lineNumber}: multi-line note has no content \u2014 add indented lines or use 'note: text'`;
578
+ return result;
579
+ }
580
+ const note = {
581
+ kind: "note",
582
+ text: noteLines.join("\n"),
583
+ position: notePosition,
584
+ participantId: noteParticipant,
585
+ lineNumber
586
+ };
587
+ currentContainer().push(note);
588
+ continue;
589
+ }
440
590
  }
441
591
  if (!hasExplicitChart && result.messages.length === 0) {
442
- const hasArrows = lines.some((line2) => ARROW_PATTERN.test(line2.trim()));
592
+ const hasArrows = lines.some((line3) => ARROW_PATTERN.test(line3.trim()));
443
593
  if (!hasArrows) {
444
594
  result.error = 'No "chart: sequence" header and no sequence content detected';
445
595
  return result;
@@ -450,13 +600,13 @@ function parseSequenceDgmo(content) {
450
600
  function looksLikeSequence(content) {
451
601
  if (!content) return false;
452
602
  const lines = content.split("\n");
453
- return lines.some((line2) => {
454
- const trimmed = line2.trim();
455
- if (trimmed.startsWith("#") || trimmed.startsWith("//")) return false;
603
+ return lines.some((line3) => {
604
+ const trimmed = line3.trim();
605
+ if (trimmed.startsWith("//")) return false;
456
606
  return ARROW_PATTERN.test(trimmed);
457
607
  });
458
608
  }
459
- var VALID_PARTICIPANT_TYPES, IS_A_PATTERN, POSITION_ONLY_PATTERN, GROUP_HEADING_PATTERN, SECTION_PATTERN, ARROW_PATTERN, ARROW_RETURN_PATTERN, UML_RETURN_PATTERN;
609
+ var VALID_PARTICIPANT_TYPES, IS_A_PATTERN, POSITION_ONLY_PATTERN, GROUP_HEADING_PATTERN, SECTION_PATTERN, ARROW_PATTERN, ARROW_RETURN_PATTERN, UML_RETURN_PATTERN, NOTE_SINGLE, NOTE_MULTI;
460
610
  var init_parser = __esm({
461
611
  "src/sequence/parser.ts"() {
462
612
  "use strict";
@@ -474,11 +624,13 @@ var init_parser = __esm({
474
624
  ]);
475
625
  IS_A_PATTERN = /^(\S+)\s+is\s+an?\s+(\w+)(?:\s+(.+))?$/i;
476
626
  POSITION_ONLY_PATTERN = /^(\S+)\s+position\s+(-?\d+)$/i;
477
- GROUP_HEADING_PATTERN = /^##\s+(\S+?)(?:\((\w+)\))?$/;
478
- SECTION_PATTERN = /^==\s+(.+?)\s*==$/;
627
+ GROUP_HEADING_PATTERN = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
628
+ SECTION_PATTERN = /^==\s+(.+?)(?:\s*==)?\s*$/;
479
629
  ARROW_PATTERN = /\S+\s*(?:->|~>)\s*\S+/;
480
630
  ARROW_RETURN_PATTERN = /^(.+?)\s*<-\s*(.+)$/;
481
631
  UML_RETURN_PATTERN = /^(\w+\([^)]*\))\s*:\s*(.+)$/;
632
+ NOTE_SINGLE = /^note(?:\s+(right|left)\s+of\s+(\S+))?\s*:\s*(.+)$/i;
633
+ NOTE_MULTI = /^note(?:\s+(right|left)\s+of\s+(\S+))?\s*$/i;
482
634
  }
483
635
  });
484
636
 
@@ -557,6 +709,369 @@ var init_colors = __esm({
557
709
  }
558
710
  });
559
711
 
712
+ // src/graph/flowchart-parser.ts
713
+ var flowchart_parser_exports = {};
714
+ __export(flowchart_parser_exports, {
715
+ looksLikeFlowchart: () => looksLikeFlowchart,
716
+ parseFlowchart: () => parseFlowchart
717
+ });
718
+ function measureIndent2(line3) {
719
+ let indent = 0;
720
+ for (const ch of line3) {
721
+ if (ch === " ") indent++;
722
+ else if (ch === " ") indent += 4;
723
+ else break;
724
+ }
725
+ return indent;
726
+ }
727
+ function nodeId(shape, label) {
728
+ return `${shape}:${label.toLowerCase().trim()}`;
729
+ }
730
+ function extractColor(label, palette) {
731
+ const m = label.match(COLOR_SUFFIX_RE);
732
+ if (!m) return { label };
733
+ const colorName = m[1].trim();
734
+ return {
735
+ label: label.substring(0, m.index).trim(),
736
+ color: resolveColor(colorName, palette)
737
+ };
738
+ }
739
+ function parseNodeRef(text, palette) {
740
+ const t = text.trim();
741
+ if (!t) return null;
742
+ let m = t.match(/^\[\[([^\]]+)\]\]$/);
743
+ if (m) {
744
+ const { label, color } = extractColor(m[1].trim(), palette);
745
+ return { id: nodeId("subroutine", label), label, shape: "subroutine", color };
746
+ }
747
+ m = t.match(/^\[([^\]]+)~\]$/);
748
+ if (m) {
749
+ const { label, color } = extractColor(m[1].trim(), palette);
750
+ return { id: nodeId("document", label), label, shape: "document", color };
751
+ }
752
+ m = t.match(/^\[([^\]]+)\]$/);
753
+ if (m) {
754
+ const { label, color } = extractColor(m[1].trim(), palette);
755
+ return { id: nodeId("process", label), label, shape: "process", color };
756
+ }
757
+ m = t.match(/^\((.+)\)$/);
758
+ if (m) {
759
+ const { label, color } = extractColor(m[1].trim(), palette);
760
+ return { id: nodeId("terminal", label), label, shape: "terminal", color };
761
+ }
762
+ m = t.match(/^<([^>]+)>$/);
763
+ if (m) {
764
+ const { label, color } = extractColor(m[1].trim(), palette);
765
+ return { id: nodeId("decision", label), label, shape: "decision", color };
766
+ }
767
+ m = t.match(/^\/([^/]+)\/$/);
768
+ if (m) {
769
+ const { label, color } = extractColor(m[1].trim(), palette);
770
+ return { id: nodeId("io", label), label, shape: "io", color };
771
+ }
772
+ return null;
773
+ }
774
+ function splitArrows(line3) {
775
+ const segments = [];
776
+ const arrowRe = /(?:^|\s)-([^>\s(][^(>]*?)?\s*(?:\(([^)]+)\))?\s*->|(?:^|\s)->/g;
777
+ let lastIndex = 0;
778
+ const arrowPositions = [];
779
+ let searchFrom = 0;
780
+ while (searchFrom < line3.length) {
781
+ const idx = line3.indexOf("->", searchFrom);
782
+ if (idx === -1) break;
783
+ let arrowStart = idx;
784
+ let label;
785
+ let color;
786
+ if (idx > 0 && line3[idx - 1] !== " " && line3[idx - 1] !== " ") {
787
+ let scanBack = idx - 1;
788
+ while (scanBack > 0 && line3[scanBack] !== "-") {
789
+ scanBack--;
790
+ }
791
+ if (line3[scanBack] === "-" && (scanBack === 0 || /\s/.test(line3[scanBack - 1]))) {
792
+ let arrowContent = line3.substring(scanBack + 1, idx);
793
+ if (arrowContent.endsWith("-")) arrowContent = arrowContent.slice(0, -1);
794
+ const colorMatch = arrowContent.match(/\(([^)]+)\)\s*$/);
795
+ if (colorMatch) {
796
+ color = colorMatch[1].trim();
797
+ const labelPart = arrowContent.substring(0, colorMatch.index).trim();
798
+ if (labelPart) label = labelPart;
799
+ } else {
800
+ const labelPart = arrowContent.trim();
801
+ if (labelPart) label = labelPart;
802
+ }
803
+ arrowStart = scanBack;
804
+ }
805
+ }
806
+ arrowPositions.push({ start: arrowStart, end: idx + 2, label, color });
807
+ searchFrom = idx + 2;
808
+ }
809
+ if (arrowPositions.length === 0) {
810
+ return [line3];
811
+ }
812
+ for (let i = 0; i < arrowPositions.length; i++) {
813
+ const arrow = arrowPositions[i];
814
+ const beforeText = line3.substring(lastIndex, arrow.start).trim();
815
+ if (beforeText || i === 0) {
816
+ segments.push(beforeText);
817
+ }
818
+ let arrowToken = "->";
819
+ if (arrow.label && arrow.color) arrowToken = `-${arrow.label}(${arrow.color})->`;
820
+ else if (arrow.label) arrowToken = `-${arrow.label}->`;
821
+ else if (arrow.color) arrowToken = `-(${arrow.color})->`;
822
+ segments.push(arrowToken);
823
+ lastIndex = arrow.end;
824
+ }
825
+ const remaining = line3.substring(lastIndex).trim();
826
+ if (remaining) {
827
+ segments.push(remaining);
828
+ }
829
+ return segments;
830
+ }
831
+ function parseArrowToken(token, palette) {
832
+ if (token === "->") return {};
833
+ const colorOnly = token.match(/^-\(([^)]+)\)->$/);
834
+ if (colorOnly) {
835
+ return { color: resolveColor(colorOnly[1].trim(), palette) };
836
+ }
837
+ const m = token.match(/^-(.+?)(?:\(([^)]+)\))?->$/);
838
+ if (m) {
839
+ const label = m[1]?.trim() || void 0;
840
+ const color = m[2] ? resolveColor(m[2].trim(), palette) : void 0;
841
+ return { label, color };
842
+ }
843
+ return {};
844
+ }
845
+ function parseFlowchart(content, palette) {
846
+ const lines = content.split("\n");
847
+ const result = {
848
+ type: "flowchart",
849
+ direction: "TB",
850
+ nodes: [],
851
+ edges: []
852
+ };
853
+ const nodeMap = /* @__PURE__ */ new Map();
854
+ const indentStack = [];
855
+ let currentGroup = null;
856
+ const groups = [];
857
+ let contentStarted = false;
858
+ function getOrCreateNode(ref, lineNumber) {
859
+ const existing = nodeMap.get(ref.id);
860
+ if (existing) return existing;
861
+ const node = {
862
+ id: ref.id,
863
+ label: ref.label,
864
+ shape: ref.shape,
865
+ lineNumber,
866
+ ...ref.color && { color: ref.color },
867
+ ...currentGroup && { group: currentGroup.id }
868
+ };
869
+ nodeMap.set(ref.id, node);
870
+ result.nodes.push(node);
871
+ if (currentGroup && !currentGroup.nodeIds.includes(ref.id)) {
872
+ currentGroup.nodeIds.push(ref.id);
873
+ }
874
+ return node;
875
+ }
876
+ function addEdge(sourceId, targetId, lineNumber, label, color) {
877
+ const edge = {
878
+ source: sourceId,
879
+ target: targetId,
880
+ lineNumber,
881
+ ...label && { label },
882
+ ...color && { color }
883
+ };
884
+ result.edges.push(edge);
885
+ }
886
+ function processContentLine(trimmed, lineNumber, indent) {
887
+ contentStarted = true;
888
+ while (indentStack.length > 0) {
889
+ const top = indentStack[indentStack.length - 1];
890
+ if (top.indent >= indent) {
891
+ indentStack.pop();
892
+ } else {
893
+ break;
894
+ }
895
+ }
896
+ const implicitSourceId = indentStack.length > 0 ? indentStack[indentStack.length - 1].nodeId : null;
897
+ const segments = splitArrows(trimmed);
898
+ if (segments.length === 1) {
899
+ const ref = parseNodeRef(segments[0], palette);
900
+ if (ref) {
901
+ const node = getOrCreateNode(ref, lineNumber);
902
+ indentStack.push({ nodeId: node.id, indent });
903
+ return node.id;
904
+ }
905
+ return null;
906
+ }
907
+ let lastNodeId = null;
908
+ let pendingArrow = null;
909
+ for (let i = 0; i < segments.length; i++) {
910
+ const seg = segments[i];
911
+ if (seg === "->" || /^-.+->$/.test(seg)) {
912
+ pendingArrow = parseArrowToken(seg, palette);
913
+ continue;
914
+ }
915
+ const ref = parseNodeRef(seg, palette);
916
+ if (!ref) continue;
917
+ const node = getOrCreateNode(ref, lineNumber);
918
+ if (pendingArrow !== null) {
919
+ const sourceId = lastNodeId ?? implicitSourceId;
920
+ if (sourceId) {
921
+ addEdge(
922
+ sourceId,
923
+ node.id,
924
+ lineNumber,
925
+ pendingArrow.label,
926
+ pendingArrow.color
927
+ );
928
+ }
929
+ pendingArrow = null;
930
+ } else if (lastNodeId === null && implicitSourceId === null) {
931
+ }
932
+ lastNodeId = node.id;
933
+ }
934
+ if (pendingArrow !== null && lastNodeId === null && implicitSourceId) {
935
+ }
936
+ if (segments.length >= 2 && segments[0] === "" && implicitSourceId && lastNodeId) {
937
+ }
938
+ if (lastNodeId) {
939
+ indentStack.push({ nodeId: lastNodeId, indent });
940
+ }
941
+ return lastNodeId;
942
+ }
943
+ for (let i = 0; i < lines.length; i++) {
944
+ const raw = lines[i];
945
+ const trimmed = raw.trim();
946
+ const lineNumber = i + 1;
947
+ const indent = measureIndent2(raw);
948
+ if (!trimmed) continue;
949
+ if (trimmed.startsWith("//")) continue;
950
+ const groupMatch = trimmed.match(GROUP_HEADING_RE);
951
+ if (groupMatch) {
952
+ const groupLabel = groupMatch[1].trim();
953
+ const groupColorName = groupMatch[2]?.trim();
954
+ const groupColor = groupColorName ? resolveColor(groupColorName, palette) : void 0;
955
+ currentGroup = {
956
+ id: `group:${groupLabel.toLowerCase()}`,
957
+ label: groupLabel,
958
+ nodeIds: [],
959
+ lineNumber,
960
+ ...groupColor && { color: groupColor }
961
+ };
962
+ groups.push(currentGroup);
963
+ continue;
964
+ }
965
+ if (!contentStarted && trimmed.includes(":") && !trimmed.includes("->")) {
966
+ const colonIdx = trimmed.indexOf(":");
967
+ const key = trimmed.substring(0, colonIdx).trim().toLowerCase();
968
+ const value = trimmed.substring(colonIdx + 1).trim();
969
+ if (key === "chart") {
970
+ if (value.toLowerCase() !== "flowchart") {
971
+ result.error = `Line ${lineNumber}: Expected chart type "flowchart", got "${value}"`;
972
+ return result;
973
+ }
974
+ continue;
975
+ }
976
+ if (key === "title") {
977
+ result.title = value;
978
+ continue;
979
+ }
980
+ if (key === "direction") {
981
+ const dir = value.toUpperCase();
982
+ if (dir === "TB" || dir === "LR") {
983
+ result.direction = dir;
984
+ }
985
+ continue;
986
+ }
987
+ continue;
988
+ }
989
+ processContentLine(trimmed, lineNumber, indent);
990
+ }
991
+ if (groups.length > 0) result.groups = groups;
992
+ if (result.nodes.length === 0 && !result.error) {
993
+ result.error = "No nodes found. Add flowchart content with shape syntax like [Process] or (Start).";
994
+ }
995
+ return result;
996
+ }
997
+ function looksLikeFlowchart(content) {
998
+ if (!content.includes("->")) return false;
999
+ const hasShapeDelimiter = /\[[^\]]+\]/.test(content) || /\([^)]+\)/.test(content) || /<[^>]+>/.test(content) || /\/[^/]+\//.test(content);
1000
+ if (!hasShapeDelimiter) return false;
1001
+ const shapeNearArrow = /[\])][ \t]*-.*->/.test(content) || // shape ] or ) followed by arrow
1002
+ /->[ \t]*[\[(<\/]/.test(content);
1003
+ return shapeNearArrow;
1004
+ }
1005
+ var COLOR_SUFFIX_RE, GROUP_HEADING_RE;
1006
+ var init_flowchart_parser = __esm({
1007
+ "src/graph/flowchart-parser.ts"() {
1008
+ "use strict";
1009
+ init_colors();
1010
+ COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
1011
+ GROUP_HEADING_RE = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
1012
+ }
1013
+ });
1014
+
1015
+ // src/dgmo-router.ts
1016
+ var dgmo_router_exports = {};
1017
+ __export(dgmo_router_exports, {
1018
+ DGMO_CHART_TYPE_MAP: () => DGMO_CHART_TYPE_MAP,
1019
+ getDgmoFramework: () => getDgmoFramework,
1020
+ parseDgmoChartType: () => parseDgmoChartType
1021
+ });
1022
+ function getDgmoFramework(chartType) {
1023
+ return DGMO_CHART_TYPE_MAP[chartType.toLowerCase()] ?? null;
1024
+ }
1025
+ function parseDgmoChartType(content) {
1026
+ const lines = content.split("\n");
1027
+ for (const line3 of lines) {
1028
+ const trimmed = line3.trim();
1029
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//"))
1030
+ continue;
1031
+ const match = trimmed.match(/^chart\s*:\s*(.+)/i);
1032
+ if (match) return match[1].trim().toLowerCase();
1033
+ }
1034
+ if (looksLikeSequence(content)) return "sequence";
1035
+ if (looksLikeFlowchart(content)) return "flowchart";
1036
+ return null;
1037
+ }
1038
+ var DGMO_CHART_TYPE_MAP;
1039
+ var init_dgmo_router = __esm({
1040
+ "src/dgmo-router.ts"() {
1041
+ "use strict";
1042
+ init_parser();
1043
+ init_flowchart_parser();
1044
+ DGMO_CHART_TYPE_MAP = {
1045
+ // Standard charts (via ECharts)
1046
+ bar: "echart",
1047
+ line: "echart",
1048
+ "multi-line": "echart",
1049
+ area: "echart",
1050
+ pie: "echart",
1051
+ doughnut: "echart",
1052
+ radar: "echart",
1053
+ "polar-area": "echart",
1054
+ "bar-stacked": "echart",
1055
+ // ECharts
1056
+ scatter: "echart",
1057
+ sankey: "echart",
1058
+ chord: "echart",
1059
+ function: "echart",
1060
+ heatmap: "echart",
1061
+ funnel: "echart",
1062
+ // D3
1063
+ slope: "d3",
1064
+ wordcloud: "d3",
1065
+ arc: "d3",
1066
+ timeline: "d3",
1067
+ venn: "d3",
1068
+ quadrant: "d3",
1069
+ sequence: "d3",
1070
+ flowchart: "d3"
1071
+ };
1072
+ }
1073
+ });
1074
+
560
1075
  // src/fonts.ts
561
1076
  var FONT_FAMILY;
562
1077
  var init_fonts = __esm({
@@ -1612,6 +2127,409 @@ var init_palettes = __esm({
1612
2127
  }
1613
2128
  });
1614
2129
 
2130
+ // src/graph/layout.ts
2131
+ var layout_exports = {};
2132
+ __export(layout_exports, {
2133
+ layoutGraph: () => layoutGraph
2134
+ });
2135
+ import dagre from "@dagrejs/dagre";
2136
+ function computeNodeWidth(label, shape) {
2137
+ const base = Math.max(120, label.length * 9 + 40);
2138
+ if (shape === "subroutine") return base + 10;
2139
+ return base;
2140
+ }
2141
+ function computeNodeHeight(shape) {
2142
+ return shape === "decision" ? 60 : 50;
2143
+ }
2144
+ function layoutGraph(graph) {
2145
+ if (graph.nodes.length === 0) {
2146
+ return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
2147
+ }
2148
+ const g = new dagre.graphlib.Graph({ compound: true });
2149
+ g.setGraph({
2150
+ rankdir: graph.direction,
2151
+ nodesep: 50,
2152
+ ranksep: 60,
2153
+ edgesep: 20
2154
+ });
2155
+ g.setDefaultEdgeLabel(() => ({}));
2156
+ const nodeDataMap = /* @__PURE__ */ new Map();
2157
+ for (const node of graph.nodes) {
2158
+ nodeDataMap.set(node.id, node);
2159
+ }
2160
+ if (graph.groups) {
2161
+ for (const group of graph.groups) {
2162
+ g.setNode(group.id, {
2163
+ label: group.label,
2164
+ clusterLabelPos: "top"
2165
+ });
2166
+ }
2167
+ }
2168
+ for (const node of graph.nodes) {
2169
+ const width = computeNodeWidth(node.label, node.shape);
2170
+ const height = computeNodeHeight(node.shape);
2171
+ g.setNode(node.id, { label: node.label, width, height });
2172
+ if (node.group && graph.groups?.some((gr) => gr.id === node.group)) {
2173
+ g.setParent(node.id, node.group);
2174
+ }
2175
+ }
2176
+ const edgeDataMap = /* @__PURE__ */ new Map();
2177
+ for (const edge of graph.edges) {
2178
+ const key = `${edge.source}->${edge.target}`;
2179
+ edgeDataMap.set(key, edge);
2180
+ g.setEdge(edge.source, edge.target, {
2181
+ label: edge.label ?? ""
2182
+ });
2183
+ }
2184
+ dagre.layout(g);
2185
+ const layoutNodes = graph.nodes.map((node) => {
2186
+ const pos = g.node(node.id);
2187
+ return {
2188
+ id: node.id,
2189
+ label: node.label,
2190
+ shape: node.shape,
2191
+ color: node.color,
2192
+ group: node.group,
2193
+ lineNumber: node.lineNumber,
2194
+ x: pos.x,
2195
+ y: pos.y,
2196
+ width: pos.width,
2197
+ height: pos.height
2198
+ };
2199
+ });
2200
+ const layoutEdges = graph.edges.map((edge) => {
2201
+ const edgeData = g.edge(edge.source, edge.target);
2202
+ return {
2203
+ source: edge.source,
2204
+ target: edge.target,
2205
+ points: edgeData?.points ?? [],
2206
+ label: edge.label,
2207
+ color: edge.color,
2208
+ lineNumber: edge.lineNumber
2209
+ };
2210
+ });
2211
+ const layoutGroups = [];
2212
+ if (graph.groups) {
2213
+ const nodeMap = new Map(layoutNodes.map((n) => [n.id, n]));
2214
+ for (const group of graph.groups) {
2215
+ const members = group.nodeIds.map((id) => nodeMap.get(id)).filter((n) => n !== void 0);
2216
+ if (members.length === 0) {
2217
+ layoutGroups.push({
2218
+ id: group.id,
2219
+ label: group.label,
2220
+ color: group.color,
2221
+ x: 0,
2222
+ y: 0,
2223
+ width: 0,
2224
+ height: 0
2225
+ });
2226
+ continue;
2227
+ }
2228
+ let minX = Infinity;
2229
+ let minY = Infinity;
2230
+ let maxX = -Infinity;
2231
+ let maxY = -Infinity;
2232
+ for (const member of members) {
2233
+ const left = member.x - member.width / 2;
2234
+ const right = member.x + member.width / 2;
2235
+ const top = member.y - member.height / 2;
2236
+ const bottom = member.y + member.height / 2;
2237
+ if (left < minX) minX = left;
2238
+ if (right > maxX) maxX = right;
2239
+ if (top < minY) minY = top;
2240
+ if (bottom > maxY) maxY = bottom;
2241
+ }
2242
+ layoutGroups.push({
2243
+ id: group.id,
2244
+ label: group.label,
2245
+ color: group.color,
2246
+ x: minX - GROUP_PADDING,
2247
+ y: minY - GROUP_PADDING,
2248
+ width: maxX - minX + GROUP_PADDING * 2,
2249
+ height: maxY - minY + GROUP_PADDING * 2
2250
+ });
2251
+ }
2252
+ }
2253
+ let totalWidth = 0;
2254
+ let totalHeight = 0;
2255
+ for (const node of layoutNodes) {
2256
+ const right = node.x + node.width / 2;
2257
+ const bottom = node.y + node.height / 2;
2258
+ if (right > totalWidth) totalWidth = right;
2259
+ if (bottom > totalHeight) totalHeight = bottom;
2260
+ }
2261
+ for (const group of layoutGroups) {
2262
+ const right = group.x + group.width;
2263
+ const bottom = group.y + group.height;
2264
+ if (right > totalWidth) totalWidth = right;
2265
+ if (bottom > totalHeight) totalHeight = bottom;
2266
+ }
2267
+ totalWidth += 40;
2268
+ totalHeight += 40;
2269
+ return {
2270
+ nodes: layoutNodes,
2271
+ edges: layoutEdges,
2272
+ groups: layoutGroups,
2273
+ width: totalWidth,
2274
+ height: totalHeight
2275
+ };
2276
+ }
2277
+ var GROUP_PADDING;
2278
+ var init_layout = __esm({
2279
+ "src/graph/layout.ts"() {
2280
+ "use strict";
2281
+ GROUP_PADDING = 20;
2282
+ }
2283
+ });
2284
+
2285
+ // src/graph/flowchart-renderer.ts
2286
+ var flowchart_renderer_exports = {};
2287
+ __export(flowchart_renderer_exports, {
2288
+ renderFlowchart: () => renderFlowchart,
2289
+ renderFlowchartForExport: () => renderFlowchartForExport
2290
+ });
2291
+ import * as d3Selection from "d3-selection";
2292
+ import * as d3Shape from "d3-shape";
2293
+ function mix(a, b, pct) {
2294
+ const parse = (h) => {
2295
+ const r = h.replace("#", "");
2296
+ const f = r.length === 3 ? r[0] + r[0] + r[1] + r[1] + r[2] + r[2] : r;
2297
+ return [parseInt(f.substring(0, 2), 16), parseInt(f.substring(2, 4), 16), parseInt(f.substring(4, 6), 16)];
2298
+ };
2299
+ const [ar, ag, ab] = parse(a), [br, bg, bb] = parse(b), t = pct / 100;
2300
+ const c = (x, y) => Math.round(x * t + y * (1 - t)).toString(16).padStart(2, "0");
2301
+ return `#${c(ar, br)}${c(ag, bg)}${c(ab, bb)}`;
2302
+ }
2303
+ function nodeFill(palette, isDark, nodeColor) {
2304
+ if (nodeColor) {
2305
+ return mix(nodeColor, isDark ? palette.surface : palette.bg, 25);
2306
+ }
2307
+ return mix(palette.primary, isDark ? palette.surface : palette.bg, 15);
2308
+ }
2309
+ function nodeStroke(palette, nodeColor) {
2310
+ return nodeColor ?? palette.textMuted;
2311
+ }
2312
+ function renderTerminal(g, node, palette, isDark) {
2313
+ const w = node.width;
2314
+ const h = node.height;
2315
+ const rx = h / 2;
2316
+ 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);
2317
+ }
2318
+ function renderProcess(g, node, palette, isDark) {
2319
+ const w = node.width;
2320
+ const h = node.height;
2321
+ 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);
2322
+ }
2323
+ function renderDecision(g, node, palette, isDark) {
2324
+ const w = node.width / 2;
2325
+ const h = node.height / 2;
2326
+ const points = [
2327
+ `${0},${-h}`,
2328
+ // top
2329
+ `${w},${0}`,
2330
+ // right
2331
+ `${0},${h}`,
2332
+ // bottom
2333
+ `${-w},${0}`
2334
+ // left
2335
+ ].join(" ");
2336
+ 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);
2337
+ }
2338
+ function renderIO(g, node, palette, isDark) {
2339
+ const w = node.width / 2;
2340
+ const h = node.height / 2;
2341
+ const sk = IO_SKEW;
2342
+ const points = [
2343
+ `${-w + sk},${-h}`,
2344
+ // top-left (shifted right)
2345
+ `${w + sk},${-h}`,
2346
+ // top-right (shifted right)
2347
+ `${w - sk},${h}`,
2348
+ // bottom-right (shifted left)
2349
+ `${-w - sk},${h}`
2350
+ // bottom-left (shifted left)
2351
+ ].join(" ");
2352
+ 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);
2353
+ }
2354
+ function renderSubroutine(g, node, palette, isDark) {
2355
+ const w = node.width;
2356
+ const h = node.height;
2357
+ const s = nodeStroke(palette, node.color);
2358
+ 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);
2359
+ 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);
2360
+ 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);
2361
+ }
2362
+ function renderDocument(g, node, palette, isDark) {
2363
+ const w = node.width;
2364
+ const h = node.height;
2365
+ const waveH = DOC_WAVE_HEIGHT;
2366
+ const left = -w / 2;
2367
+ const right = w / 2;
2368
+ const top = -h / 2;
2369
+ const bottom = h / 2 - waveH;
2370
+ const d = [
2371
+ `M ${left} ${top}`,
2372
+ `L ${right} ${top}`,
2373
+ `L ${right} ${bottom}`,
2374
+ `C ${right - w * 0.25} ${bottom + waveH * 2}, ${left + w * 0.25} ${bottom - waveH}, ${left} ${bottom}`,
2375
+ "Z"
2376
+ ].join(" ");
2377
+ 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);
2378
+ }
2379
+ function renderNodeShape(g, node, palette, isDark) {
2380
+ switch (node.shape) {
2381
+ case "terminal":
2382
+ renderTerminal(g, node, palette, isDark);
2383
+ break;
2384
+ case "process":
2385
+ renderProcess(g, node, palette, isDark);
2386
+ break;
2387
+ case "decision":
2388
+ renderDecision(g, node, palette, isDark);
2389
+ break;
2390
+ case "io":
2391
+ renderIO(g, node, palette, isDark);
2392
+ break;
2393
+ case "subroutine":
2394
+ renderSubroutine(g, node, palette, isDark);
2395
+ break;
2396
+ case "document":
2397
+ renderDocument(g, node, palette, isDark);
2398
+ break;
2399
+ }
2400
+ }
2401
+ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem, exportDims) {
2402
+ d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
2403
+ const width = exportDims?.width ?? container.clientWidth;
2404
+ const height = exportDims?.height ?? container.clientHeight;
2405
+ if (width <= 0 || height <= 0) return;
2406
+ const titleOffset = graph.title ? TITLE_HEIGHT : 0;
2407
+ const diagramW = layout.width;
2408
+ const diagramH = layout.height + titleOffset;
2409
+ const scaleX = (width - DIAGRAM_PADDING * 2) / diagramW;
2410
+ const scaleY = (height - DIAGRAM_PADDING * 2) / diagramH;
2411
+ const scale = Math.min(1, scaleX, scaleY);
2412
+ const scaledW = diagramW * scale;
2413
+ const scaledH = diagramH * scale;
2414
+ const offsetX = (width - scaledW) / 2;
2415
+ const offsetY = (height - scaledH) / 2;
2416
+ const svg = d3Selection.select(container).append("svg").attr("width", width).attr("height", height).style("background", palette.bg).style("font-family", FONT_FAMILY);
2417
+ const defs = svg.append("defs");
2418
+ 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);
2419
+ const edgeColors = /* @__PURE__ */ new Set();
2420
+ for (const edge of layout.edges) {
2421
+ if (edge.color) edgeColors.add(edge.color);
2422
+ }
2423
+ for (const color of edgeColors) {
2424
+ const id = `fc-arrow-${color.replace("#", "")}`;
2425
+ 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);
2426
+ }
2427
+ const mainG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY}) scale(${scale})`);
2428
+ if (graph.title) {
2429
+ 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);
2430
+ }
2431
+ const contentG = mainG.append("g").attr("transform", `translate(0, ${titleOffset})`);
2432
+ for (const group of layout.groups) {
2433
+ if (group.width === 0 && group.height === 0) continue;
2434
+ const gx = group.x - GROUP_EXTRA_PADDING;
2435
+ const gy = group.y - GROUP_EXTRA_PADDING - GROUP_LABEL_FONT_SIZE - 4;
2436
+ const gw = group.width + GROUP_EXTRA_PADDING * 2;
2437
+ const gh = group.height + GROUP_EXTRA_PADDING * 2 + GROUP_LABEL_FONT_SIZE + 4;
2438
+ const fillColor = group.color ? mix(group.color, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
2439
+ const strokeColor = group.color ?? palette.textMuted;
2440
+ 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");
2441
+ 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);
2442
+ }
2443
+ for (const edge of layout.edges) {
2444
+ if (edge.points.length < 2) continue;
2445
+ const edgeG = contentG.append("g").attr("class", "fc-edge-group").attr("data-line-number", String(edge.lineNumber));
2446
+ const edgeColor = edge.color ?? palette.textMuted;
2447
+ const markerId = edge.color ? `fc-arrow-${edge.color.replace("#", "")}` : "fc-arrow";
2448
+ const pathD = lineGenerator(edge.points);
2449
+ if (pathD) {
2450
+ 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");
2451
+ }
2452
+ if (edge.label) {
2453
+ const midIdx = Math.floor(edge.points.length / 2);
2454
+ const midPt = edge.points[midIdx];
2455
+ const labelLen = edge.label.length;
2456
+ const bgW = labelLen * 7 + 8;
2457
+ const bgH = 16;
2458
+ 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");
2459
+ 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);
2460
+ }
2461
+ }
2462
+ for (const node of layout.nodes) {
2463
+ const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "fc-node").attr("data-line-number", String(node.lineNumber));
2464
+ if (onClickItem) {
2465
+ nodeG.style("cursor", "pointer").on("click", () => {
2466
+ onClickItem(node.lineNumber);
2467
+ });
2468
+ }
2469
+ renderNodeShape(nodeG, node, palette, isDark);
2470
+ 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);
2471
+ }
2472
+ }
2473
+ function renderFlowchartForExport(content, theme, palette) {
2474
+ const parsed = parseFlowchart(content, palette);
2475
+ if (parsed.error || parsed.nodes.length === 0) return "";
2476
+ const layout = layoutGraph(parsed);
2477
+ const isDark = theme === "dark";
2478
+ const container = document.createElement("div");
2479
+ container.style.width = `${layout.width + DIAGRAM_PADDING * 2}px`;
2480
+ container.style.height = `${layout.height + DIAGRAM_PADDING * 2 + (parsed.title ? TITLE_HEIGHT : 0)}px`;
2481
+ container.style.position = "absolute";
2482
+ container.style.left = "-9999px";
2483
+ document.body.appendChild(container);
2484
+ const exportWidth = layout.width + DIAGRAM_PADDING * 2;
2485
+ const exportHeight = layout.height + DIAGRAM_PADDING * 2 + (parsed.title ? TITLE_HEIGHT : 0);
2486
+ try {
2487
+ renderFlowchart(
2488
+ container,
2489
+ parsed,
2490
+ layout,
2491
+ palette,
2492
+ isDark,
2493
+ void 0,
2494
+ { width: exportWidth, height: exportHeight }
2495
+ );
2496
+ const svgEl = container.querySelector("svg");
2497
+ if (!svgEl) return "";
2498
+ if (theme === "transparent") {
2499
+ svgEl.style.background = "none";
2500
+ }
2501
+ svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
2502
+ svgEl.style.fontFamily = FONT_FAMILY;
2503
+ return svgEl.outerHTML;
2504
+ } finally {
2505
+ document.body.removeChild(container);
2506
+ }
2507
+ }
2508
+ var DIAGRAM_PADDING, TITLE_HEIGHT, TITLE_FONT_SIZE, NODE_FONT_SIZE, EDGE_LABEL_FONT_SIZE, GROUP_LABEL_FONT_SIZE, EDGE_STROKE_WIDTH, NODE_STROKE_WIDTH, ARROWHEAD_W, ARROWHEAD_H, IO_SKEW, SUBROUTINE_INSET, DOC_WAVE_HEIGHT, GROUP_EXTRA_PADDING, lineGenerator;
2509
+ var init_flowchart_renderer = __esm({
2510
+ "src/graph/flowchart-renderer.ts"() {
2511
+ "use strict";
2512
+ init_fonts();
2513
+ init_flowchart_parser();
2514
+ init_layout();
2515
+ DIAGRAM_PADDING = 20;
2516
+ TITLE_HEIGHT = 30;
2517
+ TITLE_FONT_SIZE = 18;
2518
+ NODE_FONT_SIZE = 13;
2519
+ EDGE_LABEL_FONT_SIZE = 11;
2520
+ GROUP_LABEL_FONT_SIZE = 11;
2521
+ EDGE_STROKE_WIDTH = 1.5;
2522
+ NODE_STROKE_WIDTH = 1.5;
2523
+ ARROWHEAD_W = 10;
2524
+ ARROWHEAD_H = 7;
2525
+ IO_SKEW = 15;
2526
+ SUBROUTINE_INSET = 8;
2527
+ DOC_WAVE_HEIGHT = 10;
2528
+ GROUP_EXTRA_PADDING = 12;
2529
+ lineGenerator = d3Shape.line().x((d) => d.x).y((d) => d.y).curve(d3Shape.curveBasis);
2530
+ }
2531
+ });
2532
+
1615
2533
  // src/sequence/renderer.ts
1616
2534
  var renderer_exports = {};
1617
2535
  __export(renderer_exports, {
@@ -1622,8 +2540,43 @@ __export(renderer_exports, {
1622
2540
  groupMessagesBySection: () => groupMessagesBySection,
1623
2541
  renderSequenceDiagram: () => renderSequenceDiagram
1624
2542
  });
1625
- import * as d3Selection from "d3-selection";
1626
- function mix(a, b, pct) {
2543
+ import * as d3Selection2 from "d3-selection";
2544
+ function parseInlineMarkdown(text) {
2545
+ const spans = [];
2546
+ const regex = /\*\*(.+?)\*\*|\*(.+?)\*|`(.+?)`|\[(.+?)\]\((.+?)\)|([^*`[]+)/g;
2547
+ let match;
2548
+ while ((match = regex.exec(text)) !== null) {
2549
+ if (match[1]) spans.push({ text: match[1], bold: true });
2550
+ else if (match[2]) spans.push({ text: match[2], italic: true });
2551
+ else if (match[3]) spans.push({ text: match[3], code: true });
2552
+ else if (match[4]) spans.push({ text: match[4], href: match[5] });
2553
+ else if (match[6]) spans.push({ text: match[6] });
2554
+ }
2555
+ return spans;
2556
+ }
2557
+ function wrapTextLines(text, maxChars) {
2558
+ const rawLines = text.split("\n");
2559
+ const wrapped = [];
2560
+ for (const line3 of rawLines) {
2561
+ if (line3.length <= maxChars) {
2562
+ wrapped.push(line3);
2563
+ } else {
2564
+ const words = line3.split(" ");
2565
+ let current = "";
2566
+ for (const word of words) {
2567
+ if (current && (current + " " + word).length > maxChars) {
2568
+ wrapped.push(current);
2569
+ current = word;
2570
+ } else {
2571
+ current = current ? current + " " + word : word;
2572
+ }
2573
+ }
2574
+ if (current) wrapped.push(current);
2575
+ }
2576
+ }
2577
+ return wrapped;
2578
+ }
2579
+ function mix2(a, b, pct) {
1627
2580
  const parse = (h) => {
1628
2581
  const r = h.replace("#", "");
1629
2582
  const f = r.length === 3 ? r[0] + r[0] + r[1] + r[1] + r[2] + r[2] : r;
@@ -1729,7 +2682,12 @@ function groupMessagesBySection(elements, messages) {
1729
2682
  ...collectIndices(el.children),
1730
2683
  ...collectIndices(el.elseChildren)
1731
2684
  );
1732
- } else if (isSequenceSection(el)) {
2685
+ if (el.elseIfBranches) {
2686
+ for (const branch of el.elseIfBranches) {
2687
+ indices.push(...collectIndices(branch.children));
2688
+ }
2689
+ }
2690
+ } else if (isSequenceSection(el) || isSequenceNote(el)) {
1733
2691
  continue;
1734
2692
  } else {
1735
2693
  const idx = messages.indexOf(el);
@@ -1745,7 +2703,7 @@ function groupMessagesBySection(elements, messages) {
1745
2703
  } else if (currentGroup) {
1746
2704
  if (isSequenceBlock(el)) {
1747
2705
  currentGroup.messageIndices.push(...collectIndices([el]));
1748
- } else {
2706
+ } else if (!isSequenceNote(el)) {
1749
2707
  const idx = messages.indexOf(el);
1750
2708
  if (idx >= 0) currentGroup.messageIndices.push(idx);
1751
2709
  }
@@ -1902,7 +2860,7 @@ function applyGroupOrdering(participants, groups) {
1902
2860
  return result;
1903
2861
  }
1904
2862
  function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateToLine, options) {
1905
- d3Selection.select(container).selectAll("*").remove();
2863
+ d3Selection2.select(container).selectAll("*").remove();
1906
2864
  const { title, messages, elements, groups, options: parsedOptions } = parsed;
1907
2865
  const collapsedSections = options?.collapsedSections;
1908
2866
  const participants = applyPositionOverrides(
@@ -1943,7 +2901,15 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1943
2901
  if (isSequenceBlock(el)) {
1944
2902
  const idx = findFirstMsgIndex(el.children);
1945
2903
  if (idx >= 0) return idx;
1946
- } else if (!isSequenceSection(el)) {
2904
+ if (el.elseIfBranches) {
2905
+ for (const branch of el.elseIfBranches) {
2906
+ const branchIdx = findFirstMsgIndex(branch.children);
2907
+ if (branchIdx >= 0) return branchIdx;
2908
+ }
2909
+ }
2910
+ const elseIdx = findFirstMsgIndex(el.elseChildren);
2911
+ if (elseIdx >= 0) return elseIdx;
2912
+ } else if (!isSequenceSection(el) && !isSequenceNote(el)) {
1947
2913
  const idx = messages.indexOf(el);
1948
2914
  if (idx >= 0 && !hiddenMsgIndices.has(idx)) return idx;
1949
2915
  }
@@ -1963,6 +2929,13 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1963
2929
  if (!isSequenceBlock(el)) continue;
1964
2930
  const firstIdx = findFirstMsgIndex(el.children);
1965
2931
  if (firstIdx >= 0) addExtra(firstIdx, BLOCK_HEADER_SPACE);
2932
+ if (el.elseIfBranches) {
2933
+ for (const branch of el.elseIfBranches) {
2934
+ const firstBranchIdx = findFirstMsgIndex(branch.children);
2935
+ if (firstBranchIdx >= 0) addExtra(firstBranchIdx, BLOCK_HEADER_SPACE);
2936
+ markBlockSpacing(branch.children);
2937
+ }
2938
+ }
1966
2939
  const firstElseIdx = findFirstMsgIndex(el.elseChildren);
1967
2940
  if (firstElseIdx >= 0) addExtra(firstElseIdx, BLOCK_HEADER_SPACE);
1968
2941
  markBlockSpacing(el.children);
@@ -1984,15 +2957,27 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
1984
2957
  for (const child of block.children) {
1985
2958
  if (isSequenceBlock(child)) {
1986
2959
  indices.push(...collectMsgIndicesFromBlock(child));
1987
- } else if (!isSequenceSection(child)) {
2960
+ } else if (!isSequenceSection(child) && !isSequenceNote(child)) {
1988
2961
  const idx = messages.indexOf(child);
1989
2962
  if (idx >= 0) indices.push(idx);
1990
2963
  }
1991
2964
  }
2965
+ if (block.elseIfBranches) {
2966
+ for (const branch of block.elseIfBranches) {
2967
+ for (const child of branch.children) {
2968
+ if (isSequenceBlock(child)) {
2969
+ indices.push(...collectMsgIndicesFromBlock(child));
2970
+ } else if (!isSequenceSection(child) && !isSequenceNote(child)) {
2971
+ const idx = messages.indexOf(child);
2972
+ if (idx >= 0) indices.push(idx);
2973
+ }
2974
+ }
2975
+ }
2976
+ }
1992
2977
  for (const child of block.elseChildren) {
1993
2978
  if (isSequenceBlock(child)) {
1994
2979
  indices.push(...collectMsgIndicesFromBlock(child));
1995
- } else if (!isSequenceSection(child)) {
2980
+ } else if (!isSequenceSection(child) && !isSequenceNote(child)) {
1996
2981
  const idx = messages.indexOf(child);
1997
2982
  if (idx >= 0) indices.push(idx);
1998
2983
  }
@@ -2067,7 +3052,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2067
3052
  const GROUP_PADDING_TOP = 22;
2068
3053
  const GROUP_PADDING_BOTTOM = 8;
2069
3054
  const GROUP_LABEL_SIZE = 11;
2070
- const titleOffset = title ? TITLE_HEIGHT : 0;
3055
+ const titleOffset = title ? TITLE_HEIGHT2 : 0;
2071
3056
  const groupOffset = groups.length > 0 ? GROUP_PADDING_TOP + GROUP_LABEL_SIZE : 0;
2072
3057
  const participantStartY = TOP_MARGIN + titleOffset + PARTICIPANT_Y_OFFSET + groupOffset;
2073
3058
  const lifelineStartY0 = participantStartY + PARTICIPANT_BOX_HEIGHT;
@@ -2121,7 +3106,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2121
3106
  participants.forEach((p, i) => {
2122
3107
  participantX.set(p.id, offsetX + i * PARTICIPANT_GAP);
2123
3108
  });
2124
- const svg = d3Selection.select(container).append("svg").attr("width", "100%").attr("height", totalHeight).attr("viewBox", `0 0 ${svgWidth} ${totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("class", "sequence-diagram").style("font-family", FONT_FAMILY);
3109
+ const svg = d3Selection2.select(container).append("svg").attr("width", "100%").attr("height", totalHeight).attr("viewBox", `0 0 ${svgWidth} ${totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("class", "sequence-diagram").style("font-family", FONT_FAMILY);
2125
3110
  const defs = svg.append("defs");
2126
3111
  defs.append("marker").attr("id", "seq-arrowhead").attr("viewBox", `0 0 ${ARROWHEAD_SIZE} ${ARROWHEAD_SIZE}`).attr("refX", ARROWHEAD_SIZE).attr("refY", ARROWHEAD_SIZE / 2).attr("markerWidth", ARROWHEAD_SIZE).attr("markerHeight", ARROWHEAD_SIZE).attr("orient", "auto").append("polygon").attr(
2127
3112
  "points",
@@ -2147,7 +3132,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2147
3132
  const boxY = participantStartY - GROUP_PADDING_TOP;
2148
3133
  const boxH = PARTICIPANT_BOX_HEIGHT + GROUP_PADDING_TOP + GROUP_PADDING_BOTTOM;
2149
3134
  const resolvedGroupColor = group.color ? resolveColor(group.color, palette) : void 0;
2150
- const fillColor = resolvedGroupColor ? mix(resolvedGroupColor, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : palette.bg;
3135
+ const fillColor = resolvedGroupColor ? mix2(resolvedGroupColor, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : palette.bg;
2151
3136
  const strokeColor = resolvedGroupColor || palette.textMuted;
2152
3137
  svg.append("rect").attr("x", minX).attr("y", boxY).attr("width", maxX - minX).attr("height", boxH).attr("rx", 6).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("class", "group-box").attr("data-group-line", String(group.lineNumber));
2153
3138
  svg.append("text").attr("x", minX + 8).attr("y", boxY + GROUP_LABEL_SIZE + 4).attr("fill", strokeColor).attr("font-size", GROUP_LABEL_SIZE).attr("font-weight", "bold").attr("opacity", 0.7).attr("class", "group-label").attr("data-group-line", String(group.lineNumber)).text(group.name);
@@ -2172,7 +3157,12 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2172
3157
  ...collectMsgIndices(el.children),
2173
3158
  ...collectMsgIndices(el.elseChildren)
2174
3159
  );
2175
- } else if (!isSequenceSection(el)) {
3160
+ if (el.elseIfBranches) {
3161
+ for (const branch of el.elseIfBranches) {
3162
+ indices.push(...collectMsgIndices(branch.children));
3163
+ }
3164
+ }
3165
+ } else if (!isSequenceSection(el) && !isSequenceNote(el)) {
2176
3166
  const idx = messages.indexOf(el);
2177
3167
  if (idx >= 0) indices.push(idx);
2178
3168
  }
@@ -2185,8 +3175,21 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2185
3175
  for (const el of els) {
2186
3176
  if (!isSequenceBlock(el)) continue;
2187
3177
  const ifIndices = collectMsgIndices(el.children);
3178
+ const elseIfBranchData = [];
3179
+ if (el.elseIfBranches) {
3180
+ for (const branch of el.elseIfBranches) {
3181
+ elseIfBranchData.push({
3182
+ label: branch.label,
3183
+ indices: collectMsgIndices(branch.children)
3184
+ });
3185
+ }
3186
+ }
2188
3187
  const elseIndices = collectMsgIndices(el.elseChildren);
2189
- const allIndices = [...ifIndices, ...elseIndices];
3188
+ const allIndices = [
3189
+ ...ifIndices,
3190
+ ...elseIfBranchData.flatMap((b) => b.indices),
3191
+ ...elseIndices
3192
+ ];
2190
3193
  if (allIndices.length === 0) continue;
2191
3194
  let minStep = Infinity;
2192
3195
  let maxStep = -Infinity;
@@ -2224,6 +3227,32 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2224
3227
  italic: false,
2225
3228
  blockLine: el.lineNumber
2226
3229
  });
3230
+ for (const branchData of elseIfBranchData) {
3231
+ if (branchData.indices.length > 0) {
3232
+ let firstBranchStep = Infinity;
3233
+ for (const mi of branchData.indices) {
3234
+ const first = msgToFirstStep.get(mi);
3235
+ if (first !== void 0)
3236
+ firstBranchStep = Math.min(firstBranchStep, first);
3237
+ }
3238
+ if (firstBranchStep < Infinity) {
3239
+ const dividerY = stepY(firstBranchStep) - stepSpacing / 2;
3240
+ deferredLines.push({
3241
+ x1: frameX,
3242
+ y1: dividerY,
3243
+ x2: frameX + frameW,
3244
+ y2: dividerY
3245
+ });
3246
+ deferredLabels.push({
3247
+ x: frameX + 6,
3248
+ y: dividerY + 14,
3249
+ text: `else if ${branchData.label}`,
3250
+ bold: false,
3251
+ italic: true
3252
+ });
3253
+ }
3254
+ }
3255
+ }
2227
3256
  if (elseIndices.length > 0) {
2228
3257
  let firstElseStep = Infinity;
2229
3258
  for (const mi of elseIndices) {
@@ -2249,6 +3278,11 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2249
3278
  }
2250
3279
  }
2251
3280
  renderBlockFrames(el.children, depth + 1);
3281
+ if (el.elseIfBranches) {
3282
+ for (const branch of el.elseIfBranches) {
3283
+ renderBlockFrames(branch.children, depth + 1);
3284
+ }
3285
+ }
2252
3286
  renderBlockFrames(el.elseChildren, depth + 1);
2253
3287
  }
2254
3288
  };
@@ -2270,7 +3304,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2270
3304
  if (msg) coveredLines.push(msg.lineNumber);
2271
3305
  }
2272
3306
  svg.append("rect").attr("x", x).attr("y", y1).attr("width", ACTIVATION_WIDTH).attr("height", y2 - y1).attr("fill", isDark ? palette.surface : palette.bg);
2273
- const actFill = mix(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
3307
+ const actFill = mix2(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
2274
3308
  svg.append("rect").attr("x", x).attr("y", y1).attr("width", ACTIVATION_WIDTH).attr("height", y2 - y1).attr("fill", actFill).attr("stroke", palette.primary).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("data-participant-id", act.participantId).attr("data-msg-lines", coveredLines.join(",")).attr("class", "activation");
2275
3309
  });
2276
3310
  for (const ln of deferredLines) {
@@ -2396,6 +3430,91 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
2396
3430
  }
2397
3431
  }
2398
3432
  });
3433
+ const noteFill = isDark ? mix2(palette.surface, palette.bg, 50) : mix2(palette.bg, palette.surface, 15);
3434
+ const findAssociatedStep = (note) => {
3435
+ let bestMsgIndex = -1;
3436
+ let bestLine = -1;
3437
+ for (let mi = 0; mi < messages.length; mi++) {
3438
+ if (messages[mi].lineNumber < note.lineNumber && messages[mi].lineNumber > bestLine && !hiddenMsgIndices.has(mi)) {
3439
+ bestLine = messages[mi].lineNumber;
3440
+ bestMsgIndex = mi;
3441
+ }
3442
+ }
3443
+ if (bestMsgIndex < 0) return -1;
3444
+ return msgToFirstStep.get(bestMsgIndex) ?? -1;
3445
+ };
3446
+ const renderNoteElements = (els) => {
3447
+ for (const el of els) {
3448
+ if (isSequenceNote(el)) {
3449
+ const px = participantX.get(el.participantId);
3450
+ if (px === void 0) continue;
3451
+ const si = findAssociatedStep(el);
3452
+ if (si < 0) continue;
3453
+ const noteY = stepY(si);
3454
+ const wrappedLines = wrapTextLines(el.text, NOTE_CHARS_PER_LINE);
3455
+ const noteH = wrappedLines.length * NOTE_LINE_H + NOTE_PAD_V * 2;
3456
+ const maxLineLen = Math.max(...wrappedLines.map((l) => l.length));
3457
+ const noteW = Math.min(
3458
+ NOTE_MAX_W,
3459
+ Math.max(80, maxLineLen * NOTE_CHAR_W + NOTE_PAD_H * 2 + NOTE_FOLD)
3460
+ );
3461
+ const isRight = el.position === "right";
3462
+ const noteX = isRight ? px + ACTIVATION_WIDTH + NOTE_GAP : px - ACTIVATION_WIDTH - NOTE_GAP - noteW;
3463
+ const noteTopY = noteY - noteH / 2;
3464
+ const noteG = svg.append("g").attr("class", "note").attr("data-note-toggle", "").attr("data-line-number", String(el.lineNumber));
3465
+ noteG.append("path").attr(
3466
+ "d",
3467
+ [
3468
+ `M ${noteX} ${noteTopY}`,
3469
+ `L ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
3470
+ `L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`,
3471
+ `L ${noteX + noteW} ${noteTopY + noteH}`,
3472
+ `L ${noteX} ${noteTopY + noteH}`,
3473
+ "Z"
3474
+ ].join(" ")
3475
+ ).attr("fill", noteFill).attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-box");
3476
+ noteG.append("path").attr(
3477
+ "d",
3478
+ [
3479
+ `M ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
3480
+ `L ${noteX + noteW - NOTE_FOLD} ${noteTopY + NOTE_FOLD}`,
3481
+ `L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`
3482
+ ].join(" ")
3483
+ ).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-fold");
3484
+ const connectorNoteX = isRight ? noteX : noteX + noteW;
3485
+ const connectorLifeX = isRight ? px + ACTIVATION_WIDTH / 2 : px - ACTIVATION_WIDTH / 2;
3486
+ 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");
3487
+ wrappedLines.forEach((line3, li) => {
3488
+ const textY = noteTopY + NOTE_PAD_V + (li + 1) * NOTE_LINE_H - 3;
3489
+ 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");
3490
+ const spans = parseInlineMarkdown(line3);
3491
+ for (const span of spans) {
3492
+ if (span.href) {
3493
+ const a = textEl.append("a").attr("href", span.href);
3494
+ a.append("tspan").text(span.text).attr("fill", palette.primary).style("text-decoration", "underline");
3495
+ } else {
3496
+ const tspan = textEl.append("tspan").text(span.text);
3497
+ if (span.bold) tspan.attr("font-weight", "bold");
3498
+ if (span.italic) tspan.attr("font-style", "italic");
3499
+ if (span.code)
3500
+ tspan.attr("font-family", "monospace").attr("font-size", NOTE_FONT_SIZE - 1);
3501
+ }
3502
+ }
3503
+ });
3504
+ } else if (isSequenceBlock(el)) {
3505
+ renderNoteElements(el.children);
3506
+ if (el.elseIfBranches) {
3507
+ for (const branch of el.elseIfBranches) {
3508
+ renderNoteElements(branch.children);
3509
+ }
3510
+ }
3511
+ renderNoteElements(el.elseChildren);
3512
+ }
3513
+ }
3514
+ };
3515
+ if (elements && elements.length > 0) {
3516
+ renderNoteElements(elements);
3517
+ }
2399
3518
  }
2400
3519
  function renderParticipant(svg, participant, cx, cy, palette, isDark) {
2401
3520
  const g = svg.append("g").attr("transform", `translate(${cx}, ${cy})`).attr("class", "participant").attr("data-participant-id", participant.id);
@@ -2437,7 +3556,7 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark) {
2437
3556
  isActor ? PARTICIPANT_BOX_HEIGHT + 14 : PARTICIPANT_BOX_HEIGHT / 2 + 5
2438
3557
  ).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", 13).attr("font-weight", 500).text(participant.label);
2439
3558
  }
2440
- var PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, TOP_MARGIN, TITLE_HEIGHT, PARTICIPANT_Y_OFFSET, SERVICE_BORDER_RADIUS, MESSAGE_START_OFFSET, LIFELINE_TAIL, ARROWHEAD_SIZE, fill, stroke, SW, W, H;
3559
+ var PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, TOP_MARGIN, TITLE_HEIGHT2, PARTICIPANT_Y_OFFSET, SERVICE_BORDER_RADIUS, MESSAGE_START_OFFSET, LIFELINE_TAIL, ARROWHEAD_SIZE, NOTE_MAX_W, NOTE_FOLD, NOTE_PAD_H, NOTE_PAD_V, NOTE_FONT_SIZE, NOTE_LINE_H, NOTE_GAP, NOTE_CHAR_W, NOTE_CHARS_PER_LINE, fill, stroke, SW, W, H;
2441
3560
  var init_renderer = __esm({
2442
3561
  "src/sequence/renderer.ts"() {
2443
3562
  "use strict";
@@ -2448,13 +3567,22 @@ var init_renderer = __esm({
2448
3567
  PARTICIPANT_BOX_WIDTH = 120;
2449
3568
  PARTICIPANT_BOX_HEIGHT = 50;
2450
3569
  TOP_MARGIN = 20;
2451
- TITLE_HEIGHT = 30;
3570
+ TITLE_HEIGHT2 = 30;
2452
3571
  PARTICIPANT_Y_OFFSET = 10;
2453
3572
  SERVICE_BORDER_RADIUS = 10;
2454
3573
  MESSAGE_START_OFFSET = 30;
2455
3574
  LIFELINE_TAIL = 30;
2456
3575
  ARROWHEAD_SIZE = 8;
2457
- fill = (palette, isDark) => mix(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
3576
+ NOTE_MAX_W = 200;
3577
+ NOTE_FOLD = 10;
3578
+ NOTE_PAD_H = 8;
3579
+ NOTE_PAD_V = 6;
3580
+ NOTE_FONT_SIZE = 10;
3581
+ NOTE_LINE_H = 14;
3582
+ NOTE_GAP = 15;
3583
+ NOTE_CHAR_W = 6;
3584
+ NOTE_CHARS_PER_LINE = Math.floor((NOTE_MAX_W - NOTE_PAD_H * 2 - NOTE_FOLD) / NOTE_CHAR_W);
3585
+ fill = (palette, isDark) => mix2(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
2458
3586
  stroke = (palette) => palette.textMuted;
2459
3587
  SW = 1.5;
2460
3588
  W = PARTICIPANT_BOX_WIDTH;
@@ -2462,50 +3590,8 @@ var init_renderer = __esm({
2462
3590
  }
2463
3591
  });
2464
3592
 
2465
- // src/dgmo-router.ts
2466
- init_parser();
2467
- var DGMO_CHART_TYPE_MAP = {
2468
- // Standard charts (via ECharts)
2469
- bar: "echart",
2470
- line: "echart",
2471
- "multi-line": "echart",
2472
- area: "echart",
2473
- pie: "echart",
2474
- doughnut: "echart",
2475
- radar: "echart",
2476
- "polar-area": "echart",
2477
- "bar-stacked": "echart",
2478
- // ECharts
2479
- scatter: "echart",
2480
- sankey: "echart",
2481
- chord: "echart",
2482
- function: "echart",
2483
- heatmap: "echart",
2484
- funnel: "echart",
2485
- // D3
2486
- slope: "d3",
2487
- wordcloud: "d3",
2488
- arc: "d3",
2489
- timeline: "d3",
2490
- venn: "d3",
2491
- quadrant: "d3",
2492
- sequence: "d3"
2493
- };
2494
- function getDgmoFramework(chartType) {
2495
- return DGMO_CHART_TYPE_MAP[chartType.toLowerCase()] ?? null;
2496
- }
2497
- function parseDgmoChartType(content) {
2498
- const lines = content.split("\n");
2499
- for (const line2 of lines) {
2500
- const trimmed = line2.trim();
2501
- if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//"))
2502
- continue;
2503
- const match = trimmed.match(/^chart\s*:\s*(.+)/i);
2504
- if (match) return match[1].trim().toLowerCase();
2505
- }
2506
- if (looksLikeSequence(content)) return "sequence";
2507
- return null;
2508
- }
3593
+ // src/index.ts
3594
+ init_dgmo_router();
2509
3595
 
2510
3596
  // src/chart.ts
2511
3597
  init_colors();
@@ -2867,7 +3953,7 @@ function parseEChart(content, palette) {
2867
3953
  function buildEChartsOption(parsed, palette, isDark) {
2868
3954
  const textColor = palette.text;
2869
3955
  const axisLineColor = palette.border;
2870
- const gridOpacity = isDark ? 0.7 : 0.4;
3956
+ const gridOpacity = isDark ? 0.7 : 0.55;
2871
3957
  const colors = getSeriesColors(palette);
2872
3958
  if (parsed.error) {
2873
3959
  return {};
@@ -3573,7 +4659,7 @@ function buildEChartsOptionFromChart(parsed, palette, isDark) {
3573
4659
  const textColor = palette.text;
3574
4660
  const axisLineColor = palette.border;
3575
4661
  const splitLineColor = palette.border;
3576
- const gridOpacity = isDark ? 0.7 : 0.4;
4662
+ const gridOpacity = isDark ? 0.7 : 0.55;
3577
4663
  const colors = getSeriesColors(palette);
3578
4664
  const titleConfig = parsed.title ? {
3579
4665
  text: parsed.title,
@@ -3989,8 +5075,8 @@ init_fonts();
3989
5075
  init_colors();
3990
5076
  init_palettes();
3991
5077
  import * as d3Scale from "d3-scale";
3992
- import * as d3Selection2 from "d3-selection";
3993
- import * as d3Shape from "d3-shape";
5078
+ import * as d3Selection3 from "d3-selection";
5079
+ import * as d3Shape2 from "d3-shape";
3994
5080
  import * as d3Array from "d3-array";
3995
5081
  import cloud from "d3-cloud";
3996
5082
  var DEFAULT_CLOUD_OPTIONS = {
@@ -4094,10 +5180,10 @@ function parseD3(content, palette) {
4094
5180
  let currentArcGroup = null;
4095
5181
  let currentTimelineGroup = null;
4096
5182
  for (let i = 0; i < lines.length; i++) {
4097
- const line2 = lines[i].trim();
5183
+ const line3 = lines[i].trim();
4098
5184
  const lineNumber = i + 1;
4099
- if (!line2) continue;
4100
- const sectionMatch = line2.match(/^#{2,}\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/);
5185
+ if (!line3) continue;
5186
+ const sectionMatch = line3.match(/^#{2,}\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/);
4101
5187
  if (sectionMatch) {
4102
5188
  if (result.type === "arc") {
4103
5189
  const name = sectionMatch[1].trim();
@@ -4112,11 +5198,11 @@ function parseD3(content, palette) {
4112
5198
  }
4113
5199
  continue;
4114
5200
  }
4115
- if (line2.startsWith("#") || line2.startsWith("//")) {
5201
+ if (line3.startsWith("#") || line3.startsWith("//")) {
4116
5202
  continue;
4117
5203
  }
4118
5204
  if (result.type === "arc") {
4119
- const linkMatch = line2.match(
5205
+ const linkMatch = line3.match(
4120
5206
  /^(.+?)\s*->\s*(.+?)(?:\(([^)]+)\))?\s*(?::\s*(\d+(?:\.\d+)?))?$/
4121
5207
  );
4122
5208
  if (linkMatch) {
@@ -4146,7 +5232,7 @@ function parseD3(content, palette) {
4146
5232
  }
4147
5233
  }
4148
5234
  if (result.type === "timeline") {
4149
- const eraMatch = line2.match(
5235
+ const eraMatch = line3.match(
4150
5236
  /^era\s+(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
4151
5237
  );
4152
5238
  if (eraMatch) {
@@ -4159,7 +5245,7 @@ function parseD3(content, palette) {
4159
5245
  });
4160
5246
  continue;
4161
5247
  }
4162
- const markerMatch = line2.match(
5248
+ const markerMatch = line3.match(
4163
5249
  /^marker\s+(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
4164
5250
  );
4165
5251
  if (markerMatch) {
@@ -4174,7 +5260,7 @@ function parseD3(content, palette) {
4174
5260
  }
4175
5261
  }
4176
5262
  if (result.type === "timeline") {
4177
- const durationMatch = line2.match(
5263
+ const durationMatch = line3.match(
4178
5264
  /^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\?)?(\d+(?:\.\d{1,2})?)([dwmy])\s*:\s*(.+)$/
4179
5265
  );
4180
5266
  if (durationMatch) {
@@ -4193,7 +5279,7 @@ function parseD3(content, palette) {
4193
5279
  });
4194
5280
  continue;
4195
5281
  }
4196
- const rangeMatch = line2.match(
5282
+ const rangeMatch = line3.match(
4197
5283
  /^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\?)?(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
4198
5284
  );
4199
5285
  if (rangeMatch) {
@@ -4207,7 +5293,7 @@ function parseD3(content, palette) {
4207
5293
  });
4208
5294
  continue;
4209
5295
  }
4210
- const pointMatch = line2.match(
5296
+ const pointMatch = line3.match(
4211
5297
  /^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
4212
5298
  );
4213
5299
  if (pointMatch) {
@@ -4222,7 +5308,7 @@ function parseD3(content, palette) {
4222
5308
  }
4223
5309
  }
4224
5310
  if (result.type === "venn") {
4225
- const overlapMatch = line2.match(
5311
+ const overlapMatch = line3.match(
4226
5312
  /^(.+?&.+?)\s*:\s*(\d+(?:\.\d+)?)\s*(?:"([^"]*)")?\s*$/
4227
5313
  );
4228
5314
  if (overlapMatch) {
@@ -4232,7 +5318,7 @@ function parseD3(content, palette) {
4232
5318
  result.vennOverlaps.push({ sets, size, label, lineNumber });
4233
5319
  continue;
4234
5320
  }
4235
- const setMatch = line2.match(
5321
+ const setMatch = line3.match(
4236
5322
  /^(.+?)(?:\(([^)]+)\))?\s*:\s*(\d+(?:\.\d+)?)\s*(?:"([^"]*)")?\s*$/
4237
5323
  );
4238
5324
  if (setMatch) {
@@ -4245,7 +5331,7 @@ function parseD3(content, palette) {
4245
5331
  }
4246
5332
  }
4247
5333
  if (result.type === "quadrant") {
4248
- const xAxisMatch = line2.match(/^x-axis\s*:\s*(.+)/i);
5334
+ const xAxisMatch = line3.match(/^x-axis\s*:\s*(.+)/i);
4249
5335
  if (xAxisMatch) {
4250
5336
  const parts = xAxisMatch[1].split(",").map((s) => s.trim());
4251
5337
  if (parts.length >= 2) {
@@ -4254,7 +5340,7 @@ function parseD3(content, palette) {
4254
5340
  }
4255
5341
  continue;
4256
5342
  }
4257
- const yAxisMatch = line2.match(/^y-axis\s*:\s*(.+)/i);
5343
+ const yAxisMatch = line3.match(/^y-axis\s*:\s*(.+)/i);
4258
5344
  if (yAxisMatch) {
4259
5345
  const parts = yAxisMatch[1].split(",").map((s) => s.trim());
4260
5346
  if (parts.length >= 2) {
@@ -4264,7 +5350,7 @@ function parseD3(content, palette) {
4264
5350
  continue;
4265
5351
  }
4266
5352
  const quadrantLabelRe = /^(top-right|top-left|bottom-left|bottom-right)\s*:\s*(.+)/i;
4267
- const quadrantMatch = line2.match(quadrantLabelRe);
5353
+ const quadrantMatch = line3.match(quadrantLabelRe);
4268
5354
  if (quadrantMatch) {
4269
5355
  const position = quadrantMatch[1].toLowerCase();
4270
5356
  const labelPart = quadrantMatch[2].trim();
@@ -4280,7 +5366,7 @@ function parseD3(content, palette) {
4280
5366
  result.quadrantLabels.bottomRight = label;
4281
5367
  continue;
4282
5368
  }
4283
- const pointMatch = line2.match(
5369
+ const pointMatch = line3.match(
4284
5370
  /^(.+?):\s*([0-9]*\.?[0-9]+)\s*,\s*([0-9]*\.?[0-9]+)\s*$/
4285
5371
  );
4286
5372
  if (pointMatch) {
@@ -4297,13 +5383,13 @@ function parseD3(content, palette) {
4297
5383
  continue;
4298
5384
  }
4299
5385
  }
4300
- const colonIndex = line2.indexOf(":");
5386
+ const colonIndex = line3.indexOf(":");
4301
5387
  if (colonIndex !== -1) {
4302
- const rawKey = line2.substring(0, colonIndex).trim();
5388
+ const rawKey = line3.substring(0, colonIndex).trim();
4303
5389
  const key = rawKey.toLowerCase();
4304
5390
  const colorMatch = rawKey.match(/^(.+?)\(([^)]+)\)\s*$/);
4305
5391
  if (key === "chart") {
4306
- const value = line2.substring(colonIndex + 1).trim().toLowerCase();
5392
+ const value = line3.substring(colonIndex + 1).trim().toLowerCase();
4307
5393
  if (value === "slope" || value === "wordcloud" || value === "arc" || value === "timeline" || value === "venn" || value === "quadrant" || value === "sequence") {
4308
5394
  result.type = value;
4309
5395
  } else {
@@ -4313,35 +5399,35 @@ function parseD3(content, palette) {
4313
5399
  continue;
4314
5400
  }
4315
5401
  if (key === "title") {
4316
- result.title = line2.substring(colonIndex + 1).trim();
5402
+ result.title = line3.substring(colonIndex + 1).trim();
4317
5403
  if (result.type === "quadrant") {
4318
5404
  result.quadrantTitleLineNumber = lineNumber;
4319
5405
  }
4320
5406
  continue;
4321
5407
  }
4322
5408
  if (key === "orientation") {
4323
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5409
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4324
5410
  if (v === "horizontal" || v === "vertical") {
4325
5411
  result.orientation = v;
4326
5412
  }
4327
5413
  continue;
4328
5414
  }
4329
5415
  if (key === "order") {
4330
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5416
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4331
5417
  if (v === "name" || v === "group" || v === "degree") {
4332
5418
  result.arcOrder = v;
4333
5419
  }
4334
5420
  continue;
4335
5421
  }
4336
5422
  if (key === "sort") {
4337
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5423
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4338
5424
  if (v === "time" || v === "group") {
4339
5425
  result.timelineSort = v;
4340
5426
  }
4341
5427
  continue;
4342
5428
  }
4343
5429
  if (key === "swimlanes") {
4344
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5430
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4345
5431
  if (v === "on") {
4346
5432
  result.timelineSwimlanes = true;
4347
5433
  } else if (v === "off") {
@@ -4350,7 +5436,7 @@ function parseD3(content, palette) {
4350
5436
  continue;
4351
5437
  }
4352
5438
  if (key === "values") {
4353
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5439
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4354
5440
  if (v === "off") {
4355
5441
  result.vennShowValues = false;
4356
5442
  } else if (v === "on") {
@@ -4359,21 +5445,21 @@ function parseD3(content, palette) {
4359
5445
  continue;
4360
5446
  }
4361
5447
  if (key === "rotate") {
4362
- const v = line2.substring(colonIndex + 1).trim().toLowerCase();
5448
+ const v = line3.substring(colonIndex + 1).trim().toLowerCase();
4363
5449
  if (v === "none" || v === "mixed" || v === "angled") {
4364
5450
  result.cloudOptions.rotate = v;
4365
5451
  }
4366
5452
  continue;
4367
5453
  }
4368
5454
  if (key === "max") {
4369
- const v = parseInt(line2.substring(colonIndex + 1).trim(), 10);
5455
+ const v = parseInt(line3.substring(colonIndex + 1).trim(), 10);
4370
5456
  if (!isNaN(v) && v > 0) {
4371
5457
  result.cloudOptions.max = v;
4372
5458
  }
4373
5459
  continue;
4374
5460
  }
4375
5461
  if (key === "size") {
4376
- const v = line2.substring(colonIndex + 1).trim();
5462
+ const v = line3.substring(colonIndex + 1).trim();
4377
5463
  const parts = v.split(",").map((s) => parseInt(s.trim(), 10));
4378
5464
  if (parts.length === 2 && parts.every((n) => !isNaN(n) && n > 0) && parts[0] < parts[1]) {
4379
5465
  result.cloudOptions.minSize = parts[0];
@@ -4383,7 +5469,7 @@ function parseD3(content, palette) {
4383
5469
  }
4384
5470
  const labelPart = colorMatch ? colorMatch[1].trim() : rawKey;
4385
5471
  const colorPart = colorMatch ? resolveColor(colorMatch[2].trim(), palette) : null;
4386
- const valuePart = line2.substring(colonIndex + 1).trim();
5472
+ const valuePart = line3.substring(colonIndex + 1).trim();
4387
5473
  const values = valuePart.split(",").map((v) => v.trim());
4388
5474
  const numericValues = [];
4389
5475
  let allNumeric = true;
@@ -4414,15 +5500,15 @@ function parseD3(content, palette) {
4414
5500
  }
4415
5501
  }
4416
5502
  if (result.type === "wordcloud") {
4417
- if (colonIndex === -1 && !line2.includes(" ")) {
4418
- result.words.push({ text: line2, weight: 10, lineNumber });
5503
+ if (colonIndex === -1 && !line3.includes(" ")) {
5504
+ result.words.push({ text: line3, weight: 10, lineNumber });
4419
5505
  } else {
4420
- freeformLines.push(line2);
5506
+ freeformLines.push(line3);
4421
5507
  }
4422
5508
  continue;
4423
5509
  }
4424
- if (result.periods.length === 0 && line2.includes(",") && !line2.includes(":")) {
4425
- const periods = line2.split(",").map((p) => p.trim()).filter(Boolean);
5510
+ if (result.periods.length === 0 && line3.includes(",") && !line3.includes(":")) {
5511
+ const periods = line3.split(",").map((p) => p.trim()).filter(Boolean);
4426
5512
  if (periods.length >= 2) {
4427
5513
  result.periods = periods;
4428
5514
  continue;
@@ -4645,7 +5731,7 @@ var SLOPE_MARGIN = { top: 80, bottom: 40, left: 80 };
4645
5731
  var SLOPE_LABEL_FONT_SIZE = 14;
4646
5732
  var SLOPE_CHAR_WIDTH = 8;
4647
5733
  function renderSlopeChart(container, parsed, palette, isDark, onClickItem, exportDims) {
4648
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
5734
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
4649
5735
  const { periods, data, title } = parsed;
4650
5736
  if (data.length === 0 || periods.length < 2) return;
4651
5737
  const width = exportDims?.width ?? container.clientWidth;
@@ -4672,7 +5758,7 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
4672
5758
  const valuePadding = (maxVal - minVal) * 0.1 || 1;
4673
5759
  const yScale = d3Scale.scaleLinear().domain([minVal - valuePadding, maxVal + valuePadding]).range([innerHeight, 0]);
4674
5760
  const xScale = d3Scale.scalePoint().domain(periods).range([0, innerWidth]).padding(0);
4675
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5761
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
4676
5762
  const g = svg.append("g").attr("transform", `translate(${SLOPE_MARGIN.left},${SLOPE_MARGIN.top})`);
4677
5763
  const tooltip = createTooltip(container, palette, isDark);
4678
5764
  if (title) {
@@ -4683,7 +5769,7 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
4683
5769
  g.append("text").attr("x", x).attr("y", -15).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "18px").attr("font-weight", "600").text(period);
4684
5770
  g.append("line").attr("x1", x).attr("y1", 0).attr("x2", x).attr("y2", innerHeight).attr("stroke", mutedColor).attr("stroke-width", 1).attr("stroke-dasharray", "4,4");
4685
5771
  }
4686
- const lineGen = d3Shape.line().x((_d, i) => xScale(periods[i])).y((d) => yScale(d));
5772
+ const lineGen = d3Shape2.line().x((_d, i) => xScale(periods[i])).y((d) => yScale(d));
4687
5773
  data.forEach((item, idx) => {
4688
5774
  const color = item.color ?? colors[idx % colors.length];
4689
5775
  const firstVal = item.values[0];
@@ -4746,11 +5832,11 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
4746
5832
  const lineHeight = SLOPE_LABEL_FONT_SIZE * 1.2;
4747
5833
  const totalHeight = (lines.length - 1) * lineHeight;
4748
5834
  const startDy = -totalHeight / 2;
4749
- lines.forEach((line2, li) => {
5835
+ lines.forEach((line3, li) => {
4750
5836
  labelEl.append("tspan").attr("x", lastX + 10).attr(
4751
5837
  "dy",
4752
5838
  li === 0 ? `${startDy + SLOPE_LABEL_FONT_SIZE * 0.35}px` : `${lineHeight}px`
4753
- ).text(line2);
5839
+ ).text(line3);
4754
5840
  });
4755
5841
  }
4756
5842
  });
@@ -4845,7 +5931,7 @@ function orderArcNodes(links, order, groups) {
4845
5931
  }
4846
5932
  var ARC_MARGIN = { top: 60, right: 40, bottom: 60, left: 40 };
4847
5933
  function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, exportDims) {
4848
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
5934
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
4849
5935
  const { links, title, orientation, arcOrder, arcNodeGroups } = parsed;
4850
5936
  if (links.length === 0) return;
4851
5937
  const width = exportDims?.width ?? container.clientWidth;
@@ -4882,7 +5968,7 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
4882
5968
  const values = links.map((l) => l.value);
4883
5969
  const [minVal, maxVal] = d3Array.extent(values);
4884
5970
  const strokeScale = d3Scale.scaleLinear().domain([minVal, maxVal]).range([1.5, 6]);
4885
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5971
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
4886
5972
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
4887
5973
  if (title) {
4888
5974
  svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
@@ -4897,14 +5983,14 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
4897
5983
  function handleMouseEnter(hovered) {
4898
5984
  const connected = neighbors.get(hovered);
4899
5985
  g.selectAll(".arc-link").each(function() {
4900
- const el = d3Selection2.select(this);
5986
+ const el = d3Selection3.select(this);
4901
5987
  const src = el.attr("data-source");
4902
5988
  const tgt = el.attr("data-target");
4903
5989
  const isRelated = src === hovered || tgt === hovered;
4904
5990
  el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
4905
5991
  });
4906
5992
  g.selectAll(".arc-node").each(function() {
4907
- const el = d3Selection2.select(this);
5993
+ const el = d3Selection3.select(this);
4908
5994
  const name = el.attr("data-node");
4909
5995
  const isRelated = name === hovered || connected.has(name);
4910
5996
  el.attr("opacity", isRelated ? 1 : FADE_OPACITY);
@@ -4929,23 +6015,23 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
4929
6015
  const members = groupNodeSets.get(groupName);
4930
6016
  if (!members) return;
4931
6017
  g.selectAll(".arc-link").each(function() {
4932
- const el = d3Selection2.select(this);
6018
+ const el = d3Selection3.select(this);
4933
6019
  const isRelated = members.has(el.attr("data-source")) || members.has(el.attr("data-target"));
4934
6020
  el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
4935
6021
  });
4936
6022
  g.selectAll(".arc-node").each(function() {
4937
- const el = d3Selection2.select(this);
6023
+ const el = d3Selection3.select(this);
4938
6024
  el.attr("opacity", members.has(el.attr("data-node")) ? 1 : FADE_OPACITY);
4939
6025
  });
4940
6026
  g.selectAll(".arc-group-band").each(function() {
4941
- const el = d3Selection2.select(this);
6027
+ const el = d3Selection3.select(this);
4942
6028
  el.attr(
4943
6029
  "fill-opacity",
4944
6030
  el.attr("data-group") === groupName ? 0.18 : 0.03
4945
6031
  );
4946
6032
  });
4947
6033
  g.selectAll(".arc-group-label").each(function() {
4948
- const el = d3Selection2.select(this);
6034
+ const el = d3Selection3.select(this);
4949
6035
  el.attr("fill-opacity", el.attr("data-group") === groupName ? 1 : 0.2);
4950
6036
  });
4951
6037
  }
@@ -5148,7 +6234,14 @@ function computeTimeTicks(domainMin, domainMax, scale, boundaryStart, boundaryEn
5148
6234
  const firstYear = Math.ceil(domainMin);
5149
6235
  const lastYear = Math.floor(domainMax);
5150
6236
  if (lastYear >= firstYear + 1) {
5151
- for (let y = firstYear; y <= lastYear; y++) {
6237
+ const yearSpan = lastYear - firstYear;
6238
+ let step = 1;
6239
+ if (yearSpan > 80) step = 20;
6240
+ else if (yearSpan > 40) step = 10;
6241
+ else if (yearSpan > 20) step = 5;
6242
+ else if (yearSpan > 10) step = 2;
6243
+ const alignedFirst = Math.ceil(firstYear / step) * step;
6244
+ for (let y = alignedFirst; y <= lastYear; y += step) {
5152
6245
  ticks.push({ pos: scale(y), label: String(y) });
5153
6246
  }
5154
6247
  } else if (span > 0.25) {
@@ -5248,7 +6341,7 @@ function showEventDatesOnScale(g, scale, startDate, endDate, innerHeight, accent
5248
6341
  function hideEventDatesOnScale(g) {
5249
6342
  g.selectAll(".tl-event-date").remove();
5250
6343
  g.selectAll(".tl-scale-tick").each(function() {
5251
- const el = d3Selection2.select(this);
6344
+ const el = d3Selection3.select(this);
5252
6345
  const isDashed = el.attr("stroke-dasharray");
5253
6346
  el.attr("opacity", isDashed ? 0.15 : 0.4);
5254
6347
  });
@@ -5306,7 +6399,7 @@ function buildEraTooltipHtml(era) {
5306
6399
  return `<strong>${era.label}</strong><br>${formatDateLabel(era.startDate)} \u2192 ${formatDateLabel(era.endDate)}`;
5307
6400
  }
5308
6401
  function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims) {
5309
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
6402
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
5310
6403
  const {
5311
6404
  timelineEvents,
5312
6405
  timelineGroups,
@@ -5358,13 +6451,13 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5358
6451
  const FADE_OPACITY = 0.1;
5359
6452
  function fadeToGroup(g, groupName) {
5360
6453
  g.selectAll(".tl-event").each(function() {
5361
- const el = d3Selection2.select(this);
6454
+ const el = d3Selection3.select(this);
5362
6455
  const evGroup = el.attr("data-group");
5363
6456
  el.attr("opacity", evGroup === groupName ? 1 : FADE_OPACITY);
5364
6457
  });
5365
6458
  g.selectAll(".tl-legend-item, .tl-lane-header").each(
5366
6459
  function() {
5367
- const el = d3Selection2.select(this);
6460
+ const el = d3Selection3.select(this);
5368
6461
  const name = el.attr("data-group");
5369
6462
  el.attr("opacity", name === groupName ? 1 : FADE_OPACITY);
5370
6463
  }
@@ -5376,7 +6469,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5376
6469
  }
5377
6470
  function fadeToEra(g, eraStart, eraEnd) {
5378
6471
  g.selectAll(".tl-event").each(function() {
5379
- const el = d3Selection2.select(this);
6472
+ const el = d3Selection3.select(this);
5380
6473
  const date = parseFloat(el.attr("data-date"));
5381
6474
  const endDate = el.attr("data-end-date");
5382
6475
  const evEnd = endDate ? parseFloat(endDate) : date;
@@ -5388,14 +6481,14 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5388
6481
  FADE_OPACITY
5389
6482
  );
5390
6483
  g.selectAll(".tl-era").each(function() {
5391
- const el = d3Selection2.select(this);
6484
+ const el = d3Selection3.select(this);
5392
6485
  const s = parseFloat(el.attr("data-era-start"));
5393
6486
  const e = parseFloat(el.attr("data-era-end"));
5394
6487
  const isSelf = s === eraStart && e === eraEnd;
5395
6488
  el.attr("opacity", isSelf ? 1 : FADE_OPACITY);
5396
6489
  });
5397
6490
  g.selectAll(".tl-marker").each(function() {
5398
- const el = d3Selection2.select(this);
6491
+ const el = d3Selection3.select(this);
5399
6492
  const date = parseFloat(el.attr("data-marker-date"));
5400
6493
  const inside = date >= eraStart && date <= eraEnd;
5401
6494
  el.attr("opacity", inside ? 1 : FADE_OPACITY);
@@ -5427,7 +6520,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5427
6520
  const innerHeight = height - margin.top - margin.bottom;
5428
6521
  const laneWidth = innerWidth / laneCount;
5429
6522
  const yScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
5430
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6523
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5431
6524
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
5432
6525
  if (title) {
5433
6526
  svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
@@ -5521,7 +6614,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5521
6614
  const axisX = 20;
5522
6615
  const yScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
5523
6616
  const sorted = timelineEvents.slice().sort((a, b) => parseTimelineDate(a.date) - parseTimelineDate(b.date));
5524
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6617
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5525
6618
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
5526
6619
  if (title) {
5527
6620
  svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
@@ -5644,7 +6737,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5644
6737
  const totalGaps = (lanes.length - 1) * GROUP_GAP;
5645
6738
  const rowH = Math.min(28, (innerHeight - totalGaps) / totalEventRows);
5646
6739
  const xScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
5647
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6740
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5648
6741
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
5649
6742
  if (title) {
5650
6743
  svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
@@ -5748,7 +6841,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5748
6841
  if (ev.uncertain) {
5749
6842
  const gradientId = `uncertain-${ev.lineNumber}`;
5750
6843
  const defs = svg.select("defs").node() || svg.append("defs").node();
5751
- d3Selection2.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
6844
+ d3Selection3.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
5752
6845
  { offset: "0%", opacity: 1 },
5753
6846
  { offset: "80%", opacity: 1 },
5754
6847
  { offset: "100%", opacity: 0 }
@@ -5789,7 +6882,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5789
6882
  const innerHeight = height - margin.top - margin.bottom;
5790
6883
  const rowH = Math.min(28, innerHeight / sorted.length);
5791
6884
  const xScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
5792
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6885
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5793
6886
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
5794
6887
  if (title) {
5795
6888
  svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
@@ -5887,7 +6980,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
5887
6980
  if (ev.uncertain) {
5888
6981
  const gradientId = `uncertain-ts-${ev.lineNumber}`;
5889
6982
  const defs = svg.select("defs").node() || svg.append("defs").node();
5890
- d3Selection2.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
6983
+ d3Selection3.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
5891
6984
  { offset: "0%", opacity: 1 },
5892
6985
  { offset: "80%", opacity: 1 },
5893
6986
  { offset: "100%", opacity: 0 }
@@ -5920,7 +7013,7 @@ function getRotateFn(mode) {
5920
7013
  return () => 0;
5921
7014
  }
5922
7015
  function renderWordCloud(container, parsed, palette, _isDark, onClickItem, exportDims) {
5923
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
7016
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
5924
7017
  const { words, title, cloudOptions } = parsed;
5925
7018
  if (words.length === 0) return;
5926
7019
  const width = exportDims?.width ?? container.clientWidth;
@@ -5941,7 +7034,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
5941
7034
  return minSize + t * (maxSize - minSize);
5942
7035
  };
5943
7036
  const rotateFn = getRotateFn(cloudOptions.rotate);
5944
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7037
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5945
7038
  if (title) {
5946
7039
  svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
5947
7040
  }
@@ -5964,7 +7057,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
5964
7057
  }
5965
7058
  function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
5966
7059
  return new Promise((resolve) => {
5967
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
7060
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
5968
7061
  const { words, title, cloudOptions } = parsed;
5969
7062
  if (words.length === 0) {
5970
7063
  resolve();
@@ -5991,7 +7084,7 @@ function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
5991
7084
  return minSize + t * (maxSize - minSize);
5992
7085
  };
5993
7086
  const rotateFn = getRotateFn(cloudOptions.rotate);
5994
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7087
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
5995
7088
  if (title) {
5996
7089
  svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
5997
7090
  }
@@ -6135,7 +7228,7 @@ function circlePathD(cx, cy, r) {
6135
7228
  return `M${cx - r},${cy} A${r},${r} 0 1,0 ${cx + r},${cy} A${r},${r} 0 1,0 ${cx - r},${cy} Z`;
6136
7229
  }
6137
7230
  function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims) {
6138
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
7231
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
6139
7232
  const { vennSets, vennOverlaps, vennShowValues, title } = parsed;
6140
7233
  if (vennSets.length < 2) return;
6141
7234
  const width = exportDims?.width ?? container.clientWidth;
@@ -6200,7 +7293,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
6200
7293
  const setColors = vennSets.map(
6201
7294
  (s, i) => s.color ?? colors[i % colors.length]
6202
7295
  );
6203
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7296
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6204
7297
  const tooltip = createTooltip(container, palette, isDark);
6205
7298
  if (title) {
6206
7299
  svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
@@ -6346,7 +7439,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
6346
7439
  });
6347
7440
  }
6348
7441
  function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportDims) {
6349
- d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
7442
+ d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
6350
7443
  const {
6351
7444
  title,
6352
7445
  quadrantLabels,
@@ -6378,7 +7471,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6378
7471
  const chartHeight = height - margin.top - margin.bottom;
6379
7472
  const xScale = d3Scale.scaleLinear().domain([0, 1]).range([0, chartWidth]);
6380
7473
  const yScale = d3Scale.scaleLinear().domain([0, 1]).range([chartHeight, 0]);
6381
- const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
7474
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
6382
7475
  const tooltip = createTooltip(container, palette, isDark);
6383
7476
  if (title) {
6384
7477
  const titleText = svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").style(
@@ -6387,9 +7480,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6387
7480
  ).text(title);
6388
7481
  if (onClickItem && quadrantTitleLineNumber) {
6389
7482
  titleText.on("click", () => onClickItem(quadrantTitleLineNumber)).on("mouseenter", function() {
6390
- d3Selection2.select(this).attr("opacity", 0.7);
7483
+ d3Selection3.select(this).attr("opacity", 0.7);
6391
7484
  }).on("mouseleave", function() {
6392
- d3Selection2.select(this).attr("opacity", 1);
7485
+ d3Selection3.select(this).attr("opacity", 1);
6393
7486
  });
6394
7487
  }
6395
7488
  }
@@ -6472,9 +7565,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6472
7565
  quadrantLabelTexts.on("click", (_, d) => {
6473
7566
  if (d.label?.lineNumber) onClickItem(d.label.lineNumber);
6474
7567
  }).on("mouseenter", function() {
6475
- d3Selection2.select(this).attr("opacity", 0.7);
7568
+ d3Selection3.select(this).attr("opacity", 0.7);
6476
7569
  }).on("mouseleave", function() {
6477
- d3Selection2.select(this).attr("opacity", 1);
7570
+ d3Selection3.select(this).attr("opacity", 1);
6478
7571
  });
6479
7572
  }
6480
7573
  if (quadrantXAxis) {
@@ -6489,9 +7582,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6489
7582
  if (onClickItem && quadrantXAxisLineNumber) {
6490
7583
  [xLowLabel, xHighLabel].forEach((label) => {
6491
7584
  label.on("click", () => onClickItem(quadrantXAxisLineNumber)).on("mouseenter", function() {
6492
- d3Selection2.select(this).attr("opacity", 0.7);
7585
+ d3Selection3.select(this).attr("opacity", 0.7);
6493
7586
  }).on("mouseleave", function() {
6494
- d3Selection2.select(this).attr("opacity", 1);
7587
+ d3Selection3.select(this).attr("opacity", 1);
6495
7588
  });
6496
7589
  });
6497
7590
  }
@@ -6510,9 +7603,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6510
7603
  if (onClickItem && quadrantYAxisLineNumber) {
6511
7604
  [yLowLabel, yHighLabel].forEach((label) => {
6512
7605
  label.on("click", () => onClickItem(quadrantYAxisLineNumber)).on("mouseenter", function() {
6513
- d3Selection2.select(this).attr("opacity", 0.7);
7606
+ d3Selection3.select(this).attr("opacity", 0.7);
6514
7607
  }).on("mouseleave", function() {
6515
- d3Selection2.select(this).attr("opacity", 1);
7608
+ d3Selection3.select(this).attr("opacity", 1);
6516
7609
  });
6517
7610
  });
6518
7611
  }
@@ -6560,7 +7653,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6560
7653
  pointsG.selectAll("g.point-group").each(function(_2, i) {
6561
7654
  const pt = quadrantPoints[i];
6562
7655
  const ptQuad = getPointQuadrant(pt.x, pt.y);
6563
- d3Selection2.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
7656
+ d3Selection3.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
6564
7657
  });
6565
7658
  }).on("mouseleave", () => {
6566
7659
  quadrantRects.attr("opacity", 1);
@@ -6575,6 +7668,43 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
6575
7668
  var EXPORT_WIDTH = 1200;
6576
7669
  var EXPORT_HEIGHT = 800;
6577
7670
  async function renderD3ForExport(content, theme, palette) {
7671
+ const { parseDgmoChartType: parseDgmoChartType2 } = await Promise.resolve().then(() => (init_dgmo_router(), dgmo_router_exports));
7672
+ const detectedType = parseDgmoChartType2(content);
7673
+ if (detectedType === "flowchart") {
7674
+ const { parseFlowchart: parseFlowchart2 } = await Promise.resolve().then(() => (init_flowchart_parser(), flowchart_parser_exports));
7675
+ const { layoutGraph: layoutGraph2 } = await Promise.resolve().then(() => (init_layout(), layout_exports));
7676
+ const { renderFlowchart: renderFlowchart2 } = await Promise.resolve().then(() => (init_flowchart_renderer(), flowchart_renderer_exports));
7677
+ const isDark2 = theme === "dark";
7678
+ const { getPalette: getPalette3 } = await Promise.resolve().then(() => (init_palettes(), palettes_exports));
7679
+ const effectivePalette2 = palette ?? (isDark2 ? getPalette3("nord").dark : getPalette3("nord").light);
7680
+ const fcParsed = parseFlowchart2(content, effectivePalette2);
7681
+ if (fcParsed.error || fcParsed.nodes.length === 0) return "";
7682
+ const layout = layoutGraph2(fcParsed);
7683
+ const container2 = document.createElement("div");
7684
+ container2.style.width = `${EXPORT_WIDTH}px`;
7685
+ container2.style.height = `${EXPORT_HEIGHT}px`;
7686
+ container2.style.position = "absolute";
7687
+ container2.style.left = "-9999px";
7688
+ document.body.appendChild(container2);
7689
+ try {
7690
+ renderFlowchart2(container2, fcParsed, layout, effectivePalette2, isDark2, void 0, {
7691
+ width: EXPORT_WIDTH,
7692
+ height: EXPORT_HEIGHT
7693
+ });
7694
+ const svgEl = container2.querySelector("svg");
7695
+ if (!svgEl) return "";
7696
+ if (theme === "transparent") {
7697
+ svgEl.style.background = "none";
7698
+ } else if (!svgEl.style.background) {
7699
+ svgEl.style.background = effectivePalette2.bg;
7700
+ }
7701
+ svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
7702
+ svgEl.style.fontFamily = FONT_FAMILY;
7703
+ return svgEl.outerHTML;
7704
+ } finally {
7705
+ document.body.removeChild(container2);
7706
+ }
7707
+ }
6578
7708
  const parsed = parseD3(content, palette);
6579
7709
  if (parsed.error && parsed.type !== "sequence") {
6580
7710
  const looksLikeSequence2 = /->|~>|<-/.test(content);
@@ -6669,17 +7799,17 @@ function parseQuadrant(content) {
6669
7799
  };
6670
7800
  const lines = content.split("\n");
6671
7801
  for (let i = 0; i < lines.length; i++) {
6672
- const line2 = lines[i].trim();
7802
+ const line3 = lines[i].trim();
6673
7803
  const lineNumber = i + 1;
6674
- if (!line2 || line2.startsWith("#") || line2.startsWith("//")) continue;
6675
- if (/^chart\s*:/i.test(line2)) continue;
6676
- const titleMatch = line2.match(/^title\s*:\s*(.+)/i);
7804
+ if (!line3 || line3.startsWith("#") || line3.startsWith("//")) continue;
7805
+ if (/^chart\s*:/i.test(line3)) continue;
7806
+ const titleMatch = line3.match(/^title\s*:\s*(.+)/i);
6677
7807
  if (titleMatch) {
6678
7808
  result.title = titleMatch[1].trim();
6679
7809
  result.titleLineNumber = lineNumber;
6680
7810
  continue;
6681
7811
  }
6682
- const xMatch = line2.match(/^x-axis\s*:\s*(.+)/i);
7812
+ const xMatch = line3.match(/^x-axis\s*:\s*(.+)/i);
6683
7813
  if (xMatch) {
6684
7814
  const parts = xMatch[1].split(",").map((s) => s.trim());
6685
7815
  if (parts.length >= 2) {
@@ -6688,7 +7818,7 @@ function parseQuadrant(content) {
6688
7818
  }
6689
7819
  continue;
6690
7820
  }
6691
- const yMatch = line2.match(/^y-axis\s*:\s*(.+)/i);
7821
+ const yMatch = line3.match(/^y-axis\s*:\s*(.+)/i);
6692
7822
  if (yMatch) {
6693
7823
  const parts = yMatch[1].split(",").map((s) => s.trim());
6694
7824
  if (parts.length >= 2) {
@@ -6697,7 +7827,7 @@ function parseQuadrant(content) {
6697
7827
  }
6698
7828
  continue;
6699
7829
  }
6700
- const posMatch = line2.match(
7830
+ const posMatch = line3.match(
6701
7831
  /^(top-right|top-left|bottom-left|bottom-right)\s*:\s*(.+)/i
6702
7832
  );
6703
7833
  if (posMatch) {
@@ -6718,7 +7848,7 @@ function parseQuadrant(content) {
6718
7848
  }
6719
7849
  continue;
6720
7850
  }
6721
- const pointMatch = line2.match(DATA_POINT_RE);
7851
+ const pointMatch = line3.match(DATA_POINT_RE);
6722
7852
  if (pointMatch) {
6723
7853
  const key = pointMatch[1].trim().toLowerCase();
6724
7854
  if (!QUADRANT_POSITIONS.has(key)) {
@@ -6792,6 +7922,9 @@ function buildMermaidQuadrant(parsed, options = {}) {
6792
7922
  }
6793
7923
 
6794
7924
  // src/index.ts
7925
+ init_flowchart_parser();
7926
+ init_layout();
7927
+ init_flowchart_renderer();
6795
7928
  init_renderer();
6796
7929
  init_colors();
6797
7930
  init_palettes();
@@ -6825,7 +7958,10 @@ export {
6825
7958
  hslToHex,
6826
7959
  inferParticipantType,
6827
7960
  isSequenceBlock,
7961
+ isSequenceNote,
6828
7962
  isValidHex,
7963
+ layoutGraph,
7964
+ looksLikeFlowchart,
6829
7965
  looksLikeSequence,
6830
7966
  mute,
6831
7967
  nord,
@@ -6836,6 +7972,7 @@ export {
6836
7972
  parseD3,
6837
7973
  parseDgmoChartType,
6838
7974
  parseEChart,
7975
+ parseFlowchart,
6839
7976
  parseQuadrant,
6840
7977
  parseSequenceDgmo,
6841
7978
  parseTimelineDate,
@@ -6843,6 +7980,8 @@ export {
6843
7980
  renderArcDiagram,
6844
7981
  renderD3ForExport,
6845
7982
  renderEChartsForExport,
7983
+ renderFlowchart,
7984
+ renderFlowchartForExport,
6846
7985
  renderQuadrant,
6847
7986
  renderSequenceDiagram,
6848
7987
  renderSlopeChart,