schematex 0.6.10 → 0.7.0

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.
Files changed (35) hide show
  1. package/dist/ai/ai-sdk.cjs +8 -8
  2. package/dist/ai/ai-sdk.d.cts +1 -1
  3. package/dist/ai/ai-sdk.d.ts +1 -1
  4. package/dist/ai/ai-sdk.js +3 -3
  5. package/dist/ai/index.cjs +14 -14
  6. package/dist/ai/index.js +3 -3
  7. package/dist/browser.cjs +9 -9
  8. package/dist/browser.js +3 -3
  9. package/dist/{chunk-DZGA25O5.cjs → chunk-CTMJ3XP2.cjs} +156 -9
  10. package/dist/chunk-CTMJ3XP2.cjs.map +1 -0
  11. package/dist/{chunk-HL5PS6MG.js → chunk-EENA7KNU.js} +324 -27
  12. package/dist/chunk-EENA7KNU.js.map +1 -0
  13. package/dist/{chunk-3YRYBTLG.cjs → chunk-HFATQXFN.cjs} +1401 -148
  14. package/dist/chunk-HFATQXFN.cjs.map +1 -0
  15. package/dist/{chunk-6AWASOFO.js → chunk-IFNNV54X.js} +1400 -147
  16. package/dist/chunk-IFNNV54X.js.map +1 -0
  17. package/dist/{chunk-X4P4HKHP.js → chunk-IFXHIYZE.js} +154 -7
  18. package/dist/chunk-IFXHIYZE.js.map +1 -0
  19. package/dist/{chunk-BL57NQKN.cjs → chunk-JAYJ2G4R.cjs} +324 -27
  20. package/dist/chunk-JAYJ2G4R.cjs.map +1 -0
  21. package/dist/diagrams/phylo/index.cjs +6 -6
  22. package/dist/diagrams/phylo/index.d.cts +21 -0
  23. package/dist/diagrams/phylo/index.d.ts +21 -0
  24. package/dist/diagrams/phylo/index.js +1 -1
  25. package/dist/index.cjs +25 -25
  26. package/dist/index.js +3 -3
  27. package/dist/react.cjs +3 -3
  28. package/dist/react.js +2 -2
  29. package/package.json +1 -1
  30. package/dist/chunk-3YRYBTLG.cjs.map +0 -1
  31. package/dist/chunk-6AWASOFO.js.map +0 -1
  32. package/dist/chunk-BL57NQKN.cjs.map +0 -1
  33. package/dist/chunk-DZGA25O5.cjs.map +0 -1
  34. package/dist/chunk-HL5PS6MG.js.map +0 -1
  35. package/dist/chunk-X4P4HKHP.js.map +0 -1
@@ -252,6 +252,9 @@ function parseHeaderProps(propsStr) {
252
252
  case "mode":
253
253
  if (["phylogram", "cladogram", "chronogram"].includes(val)) {
254
254
  result.mode = val;
255
+ } else if (val === "dendrogram") {
256
+ result.mode = "cladogram";
257
+ result.dendrogram = true;
255
258
  }
256
259
  break;
257
260
  case "branch-width":
@@ -315,6 +318,7 @@ function parsePhylo(text2) {
315
318
  let root = null;
316
319
  let scaleLabel;
317
320
  let outgroup;
321
+ let cut;
318
322
  const clades = [];
319
323
  const indentLines = [];
320
324
  let inIndentTree = false;
@@ -342,6 +346,12 @@ function parsePhylo(text2) {
342
346
  outgroup = trimmed.slice(9).trim();
343
347
  continue;
344
348
  }
349
+ if (/^cut\b/i.test(trimmed)) {
350
+ const cutStr = trimmed.replace(/^cut\s*:?\s*/i, "").trim();
351
+ const cutVal = Number(cutStr);
352
+ if (!Number.isNaN(cutVal)) cut = cutVal;
353
+ continue;
354
+ }
345
355
  if (trimmed.startsWith("clade ")) {
346
356
  const clade = parseCladeLine(trimmed);
347
357
  if (clade) clades.push(clade);
@@ -370,6 +380,9 @@ function parsePhylo(text2) {
370
380
  if (!root) {
371
381
  throw new PhyloParseError("No tree definition found (newick: or indent tree)");
372
382
  }
383
+ const metadata = {};
384
+ if (headerProps.dendrogram) metadata.dendrogram = "true";
385
+ if (cut !== void 0) metadata.cut = String(cut);
373
386
  return {
374
387
  type: "phylo",
375
388
  title: title2,
@@ -381,11 +394,25 @@ function parsePhylo(text2) {
381
394
  scaleLabel,
382
395
  mrsd: headerProps.mrsd,
383
396
  outgroup,
384
- metadata: {}
397
+ metadata
385
398
  };
386
399
  }
387
400
 
388
- // src/diagrams/phylo/layout.ts
401
+ // src/diagrams/phylo/dendrogram.ts
402
+ var TIP_SPACING = 24;
403
+ var PADDING_LEFT = 20;
404
+ var PADDING_RIGHT = 20;
405
+ var PADDING_TOP = 24;
406
+ var PADDING_BOTTOM = 52;
407
+ function isDendrogram(ast) {
408
+ return ast.metadata?.dendrogram === "true";
409
+ }
410
+ function getCut(ast) {
411
+ const raw = ast.metadata?.cut;
412
+ if (raw === void 0) return void 0;
413
+ const val = Number(raw);
414
+ return Number.isNaN(val) ? void 0 : val;
415
+ }
389
416
  function collectLeaves(node) {
390
417
  if (node.isLeaf) return [node];
391
418
  const leaves = [];
@@ -394,6 +421,166 @@ function collectLeaves(node) {
394
421
  }
395
422
  return leaves;
396
423
  }
424
+ function estimateLabelWidth(node) {
425
+ const label = node.label ?? node.id;
426
+ return label.length * 7.2 + 6;
427
+ }
428
+ function computeHeights(node, out) {
429
+ if (node.isLeaf) {
430
+ out.set(node.id, 0);
431
+ return 0;
432
+ }
433
+ let max = 0;
434
+ for (const child of node.children) {
435
+ const childHeight = computeHeights(child, out);
436
+ const branch = child.branchLength ?? 0;
437
+ const reach = childHeight + branch;
438
+ if (reach > max) max = reach;
439
+ }
440
+ out.set(node.id, max);
441
+ return max;
442
+ }
443
+ function computeClusters(ast, heights, cutValue) {
444
+ const clusters = [];
445
+ function visit(node, parentHeight) {
446
+ const h = heights.get(node.id) ?? 0;
447
+ if (h <= cutValue && parentHeight > cutValue) {
448
+ clusters.push({ rootId: node.id, leafIds: collectLeaves(node).map((l) => l.id) });
449
+ return;
450
+ }
451
+ if (h > cutValue) {
452
+ for (const child of node.children) visit(child, h);
453
+ } else {
454
+ clusters.push({ rootId: node.id, leafIds: collectLeaves(node).map((l) => l.id) });
455
+ }
456
+ }
457
+ visit(ast.root, Number.POSITIVE_INFINITY);
458
+ return clusters;
459
+ }
460
+ function layoutDendrogram(ast) {
461
+ const leaves = collectLeaves(ast.root);
462
+ const numLeaves = leaves.length;
463
+ const heights = /* @__PURE__ */ new Map();
464
+ const maxHeight = computeHeights(ast.root, heights);
465
+ const maxLabelWidth = Math.max(...leaves.map(estimateLabelWidth), 60);
466
+ const availableWidth = Math.max(320, numLeaves * 36 + maxLabelWidth + 120);
467
+ const plotWidth = availableWidth - PADDING_LEFT - PADDING_RIGHT - maxLabelWidth;
468
+ const scale = maxHeight > 0 ? plotWidth / maxHeight : plotWidth;
469
+ const baselineX = PADDING_LEFT + plotWidth;
470
+ const heightToX = (h) => baselineX - h * scale;
471
+ const nodeMap = /* @__PURE__ */ new Map();
472
+ let leafIdx = 0;
473
+ function assignLeaf(node) {
474
+ if (node.isLeaf) {
475
+ const y = PADDING_TOP + leafIdx * TIP_SPACING;
476
+ nodeMap.set(node.id, { node, x: baselineX, y });
477
+ leafIdx++;
478
+ return;
479
+ }
480
+ for (const child of node.children) assignLeaf(child);
481
+ }
482
+ assignLeaf(ast.root);
483
+ function assignInternal(node) {
484
+ const existing = nodeMap.get(node.id);
485
+ if (node.isLeaf && existing) return existing.y;
486
+ const childYs = node.children.map(assignInternal);
487
+ const y = (Math.min(...childYs) + Math.max(...childYs)) / 2;
488
+ const x = heightToX(heights.get(node.id) ?? 0);
489
+ if (existing) {
490
+ existing.y = y;
491
+ existing.x = x;
492
+ } else {
493
+ nodeMap.set(node.id, { node, x, y });
494
+ }
495
+ return y;
496
+ }
497
+ assignInternal(ast.root);
498
+ const cut = getCut(ast);
499
+ const leafCluster = /* @__PURE__ */ new Map();
500
+ let clusterCount = 0;
501
+ if (cut !== void 0) {
502
+ const clusters = computeClusters(ast, heights, cut);
503
+ clusterCount = clusters.length;
504
+ clusters.forEach((cluster, idx) => {
505
+ for (const leafId of cluster.leafIds) leafCluster.set(leafId, idx);
506
+ });
507
+ }
508
+ function subtreeCluster(node) {
509
+ const ids = collectLeaves(node).map((l) => l.id);
510
+ const first = leafCluster.get(ids[0]);
511
+ if (first === void 0) return void 0;
512
+ for (const id of ids) {
513
+ if (leafCluster.get(id) !== first) return void 0;
514
+ }
515
+ return first;
516
+ }
517
+ const branches = [];
518
+ function generate(node) {
519
+ if (node.children.length === 0) return;
520
+ const parent = nodeMap.get(node.id);
521
+ if (!parent) return;
522
+ const childLayouts = node.children.map((c) => nodeMap.get(c.id)).filter((l) => l !== void 0);
523
+ if (childLayouts.length === 0) return;
524
+ const minY = Math.min(...childLayouts.map((c) => c.y));
525
+ const maxY = Math.max(...childLayouts.map((c) => c.y));
526
+ branches.push({
527
+ path: `M ${parent.x},${minY} V ${maxY}`,
528
+ fromId: node.id,
529
+ toId: node.id,
530
+ isConnector: true
531
+ });
532
+ for (const child of node.children) {
533
+ const childLayout = nodeMap.get(child.id);
534
+ if (!childLayout) continue;
535
+ const cluster = subtreeCluster(child);
536
+ branches.push({
537
+ path: `M ${parent.x},${childLayout.y} H ${childLayout.x}`,
538
+ fromId: node.id,
539
+ toId: child.id,
540
+ cladeId: cluster !== void 0 ? `cut${cluster}` : void 0,
541
+ isConnector: false
542
+ });
543
+ }
544
+ for (const child of node.children) generate(child);
545
+ }
546
+ generate(ast.root);
547
+ const allNodes = Array.from(nodeMap.values());
548
+ const maxX = Math.max(
549
+ ...allNodes.map((n) => n.x + (n.node.isLeaf ? estimateLabelWidth(n.node) : 0))
550
+ );
551
+ const maxNodeY = Math.max(...allNodes.map((n) => n.y));
552
+ const width = Math.max(maxX + PADDING_RIGHT, availableWidth);
553
+ const height = maxNodeY + PADDING_TOP + PADDING_BOTTOM;
554
+ return {
555
+ width,
556
+ height,
557
+ nodes: allNodes,
558
+ branches,
559
+ ast,
560
+ scale,
561
+ // Dendrogram-specific extras stashed for the renderer (see types below).
562
+ dendrogram: {
563
+ maxHeight,
564
+ baselineX,
565
+ plotLeftX: PADDING_LEFT,
566
+ scale,
567
+ cut,
568
+ clusterCount,
569
+ leafCluster,
570
+ heights
571
+ }
572
+ };
573
+ }
574
+
575
+ // src/diagrams/phylo/layout.ts
576
+ function collectLeaves2(node) {
577
+ if (node.isLeaf) return [node];
578
+ const leaves = [];
579
+ for (const child of node.children) {
580
+ leaves.push(...collectLeaves2(child));
581
+ }
582
+ return leaves;
583
+ }
397
584
  function maxRootToTip(node, distSoFar) {
398
585
  if (node.isLeaf) return distSoFar;
399
586
  let maxDist = distSoFar;
@@ -412,7 +599,7 @@ function maxDepth(node) {
412
599
  }
413
600
  return max;
414
601
  }
415
- function estimateLabelWidth(node) {
602
+ function estimateLabelWidth2(node) {
416
603
  const label = node.label ?? node.id;
417
604
  return label.length * 7.2 + 6;
418
605
  }
@@ -447,20 +634,23 @@ function markCladeBranches(node, memberSet, cladeId, result) {
447
634
  }
448
635
  return false;
449
636
  }
450
- var TIP_SPACING = 20;
451
- var PADDING_LEFT = 20;
452
- var PADDING_RIGHT = 20;
453
- var PADDING_TOP = 20;
454
- var PADDING_BOTTOM = 40;
637
+ var TIP_SPACING2 = 20;
638
+ var PADDING_LEFT2 = 20;
639
+ var PADDING_RIGHT2 = 20;
640
+ var PADDING_TOP2 = 20;
641
+ var PADDING_BOTTOM2 = 40;
455
642
  function layoutPhylo(ast) {
456
- const leaves = collectLeaves(ast.root);
643
+ if (isDendrogram(ast)) {
644
+ return layoutDendrogram(ast);
645
+ }
646
+ const leaves = collectLeaves2(ast.root);
457
647
  const numLeaves = leaves.length;
458
- const tipSpacing = TIP_SPACING;
459
- const maxLabelWidth = Math.max(...leaves.map(estimateLabelWidth), 60);
648
+ const tipSpacing = TIP_SPACING2;
649
+ const maxLabelWidth = Math.max(...leaves.map(estimateLabelWidth2), 60);
460
650
  const maxDist = maxRootToTip(ast.root, 0);
461
651
  const isCladogram = ast.mode === "cladogram";
462
652
  const availableWidth = Math.max(300, numLeaves * 30 + maxLabelWidth + 100);
463
- const plotWidth = availableWidth - PADDING_LEFT - PADDING_RIGHT - maxLabelWidth;
653
+ const plotWidth = availableWidth - PADDING_LEFT2 - PADDING_RIGHT2 - maxLabelWidth;
464
654
  let scale;
465
655
  if (isCladogram || maxDist === 0) {
466
656
  const depth = maxDepth(ast.root);
@@ -472,7 +662,7 @@ function layoutPhylo(ast) {
472
662
  let leafIdx = 0;
473
663
  function assignLeafY(node) {
474
664
  if (node.isLeaf) {
475
- const y = PADDING_TOP + leafIdx * tipSpacing;
665
+ const y = PADDING_TOP2 + leafIdx * tipSpacing;
476
666
  nodeMap.set(node.id, { node, x: 0, y });
477
667
  leafIdx++;
478
668
  return;
@@ -498,12 +688,12 @@ function layoutPhylo(ast) {
498
688
  function assignX(node, parentX, depth) {
499
689
  let x;
500
690
  if (node === ast.root) {
501
- x = PADDING_LEFT;
691
+ x = PADDING_LEFT2;
502
692
  } else if (isCladogram) {
503
693
  if (node.isLeaf) {
504
- x = PADDING_LEFT + plotWidth;
694
+ x = PADDING_LEFT2 + plotWidth;
505
695
  } else {
506
- x = PADDING_LEFT + depth * (plotWidth / maxDepth(ast.root));
696
+ x = PADDING_LEFT2 + depth * (plotWidth / maxDepth(ast.root));
507
697
  }
508
698
  } else {
509
699
  x = parentX + (node.branchLength ?? 0) * scale;
@@ -514,7 +704,7 @@ function layoutPhylo(ast) {
514
704
  assignX(child, x, depth + 1);
515
705
  }
516
706
  }
517
- assignX(ast.root, PADDING_LEFT, 0);
707
+ assignX(ast.root, PADDING_LEFT2, 0);
518
708
  if (isCladogram) {
519
709
  assignCladogramInternalX(ast.root, nodeMap);
520
710
  }
@@ -567,7 +757,7 @@ function layoutPhylo(ast) {
567
757
  }
568
758
  generateBranches(ast.root);
569
759
  const allNodes = Array.from(nodeMap.values());
570
- let maxX = Math.max(...allNodes.map((n) => n.x + (n.node.isLeaf ? estimateLabelWidth(n.node) : 0)));
760
+ let maxX = Math.max(...allNodes.map((n) => n.x + (n.node.isLeaf ? estimateLabelWidth2(n.node) : 0)));
571
761
  let maxCladeLabelWidth = 0;
572
762
  for (const clade of ast.clades) {
573
763
  if (clade.label && clade.highlight && clade.highlight !== "branch") {
@@ -577,8 +767,8 @@ function layoutPhylo(ast) {
577
767
  }
578
768
  maxX += maxCladeLabelWidth;
579
769
  const maxNodeY = Math.max(...allNodes.map((n) => n.y));
580
- const width = Math.max(maxX + PADDING_RIGHT, availableWidth);
581
- const height = maxNodeY + PADDING_TOP + PADDING_BOTTOM;
770
+ const width = Math.max(maxX + PADDING_RIGHT2, availableWidth);
771
+ const height = maxNodeY + PADDING_TOP2 + PADDING_BOTTOM2;
582
772
  return {
583
773
  width,
584
774
  height,
@@ -600,9 +790,9 @@ function assignCladogramInternalX(node, nodeMap) {
600
790
  const layout = nodeMap.get(node.id);
601
791
  if (layout) {
602
792
  layout.x = minChildX - 40;
603
- if (layout.x < PADDING_LEFT) layout.x = PADDING_LEFT;
793
+ if (layout.x < PADDING_LEFT2) layout.x = PADDING_LEFT2;
604
794
  }
605
- return layout?.x ?? PADDING_LEFT;
795
+ return layout?.x ?? PADDING_LEFT2;
606
796
  }
607
797
 
608
798
  // src/diagrams/phylo/renderer.ts
@@ -626,12 +816,25 @@ function buildCSS(ast, t) {
626
816
  .schematex-phylo-clade-bg-${c.id} { fill: ${color}; fill-opacity: 0.12; }
627
817
  .schematex-phylo-clade-label-${c.id} { fill: ${color}; }`;
628
818
  });
819
+ const cutColors = [];
820
+ if (ast.metadata?.dendrogram === "true" && ast.metadata?.cut !== void 0) {
821
+ for (let i = 0; i < t.cladeColors.length; i++) {
822
+ cutColors.push(
823
+ `.schematex-phylo-clade-cut${i} { stroke: ${t.cladeColors[i % t.cladeColors.length]}; }`
824
+ );
825
+ }
826
+ }
629
827
  return `
630
828
  .schematex-phylo {${chunkNZT5P2XZ_cjs.cssCustomProperties(t)}
631
829
  font-family: system-ui, -apple-system, sans-serif;
632
830
  }
633
831
  .schematex-phylo-branch { fill: none; stroke: ${t.text}; stroke-width: ${chunkNZT5P2XZ_cjs.STROKE_WIDTH.normal}; stroke-linecap: round; }
634
832
  .schematex-phylo-branch-connector { fill: none; stroke: ${t.text}; stroke-width: ${chunkNZT5P2XZ_cjs.STROKE_WIDTH.normal}; }
833
+ .schematex-phylo-dendro-axis line { stroke: ${t.text}; stroke-width: ${chunkNZT5P2XZ_cjs.STROKE_WIDTH.thin}; }
834
+ .schematex-phylo-dendro-axis text { font-size: 10px; fill: ${t.textMuted}; text-anchor: middle; }
835
+ .schematex-phylo-dendro-cut { stroke: ${t.supportBad}; stroke-width: ${chunkNZT5P2XZ_cjs.STROKE_WIDTH.normal}; stroke-dasharray: 5 4; fill: none; }
836
+ .schematex-phylo-dendro-cut-label { font-size: 10px; fill: ${t.supportBad}; text-anchor: start; }
837
+ ${cutColors.join("\n")}
635
838
  .schematex-phylo-tip-label { font-size: ${chunkNZT5P2XZ_cjs.FONT_SIZE.label}px; fill: ${t.text}; dominant-baseline: central; }
636
839
  .schematex-phylo-tip-label-italic { font-style: italic; }
637
840
  .schematex-phylo-support-label { font-size: ${chunkNZT5P2XZ_cjs.FONT_SIZE.small}px; fill: ${t.textMuted}; text-anchor: middle; dominant-baseline: auto; }
@@ -736,6 +939,75 @@ function renderCladeBackgrounds(layout, t) {
736
939
  }
737
940
  return elements;
738
941
  }
942
+ function niceStep(maxHeight, targetTicks) {
943
+ if (maxHeight <= 0) return 1;
944
+ const rough = maxHeight / targetTicks;
945
+ const mag = Math.pow(10, Math.floor(Math.log10(rough)));
946
+ const norm = rough / mag;
947
+ let step;
948
+ if (norm < 1.5) step = 1;
949
+ else if (norm < 3) step = 2;
950
+ else if (norm < 7) step = 5;
951
+ else step = 10;
952
+ return step * mag;
953
+ }
954
+ function formatTick(value) {
955
+ if (value === 0) return "0";
956
+ if (Math.abs(value) < 0.01) return value.toExponential(0);
957
+ return String(Math.round(value * 1e3) / 1e3);
958
+ }
959
+ function renderDendrogramAxis(layout, t) {
960
+ const d = layout.dendrogram;
961
+ if (!d) return "";
962
+ const axisY = layout.height - 28;
963
+ const elements = [];
964
+ elements.push(
965
+ chunk3WNW5Y7P_cjs.line({ x1: d.plotLeftX, y1: axisY, x2: d.baselineX, y2: axisY })
966
+ );
967
+ const step = niceStep(d.maxHeight, 5);
968
+ for (let h = 0; h <= d.maxHeight + step * 1e-3; h += step) {
969
+ const x = d.baselineX - h * d.scale;
970
+ elements.push(chunk3WNW5Y7P_cjs.line({ x1: x, y1: axisY, x2: x, y2: axisY + 4 }));
971
+ elements.push(
972
+ chunk3WNW5Y7P_cjs.text({ x, y: axisY + 16, "text-anchor": "middle" }, formatTick(h))
973
+ );
974
+ }
975
+ if (layout.ast.scaleLabel) {
976
+ elements.push(
977
+ chunk3WNW5Y7P_cjs.text(
978
+ {
979
+ x: (d.plotLeftX + d.baselineX) / 2,
980
+ y: axisY + 28,
981
+ "text-anchor": "middle",
982
+ "font-size": "9",
983
+ fill: t.textMuted
984
+ },
985
+ layout.ast.scaleLabel
986
+ )
987
+ );
988
+ }
989
+ return chunk3WNW5Y7P_cjs.group({ class: "schematex-phylo-dendro-axis" }, elements);
990
+ }
991
+ function renderCutLine(layout) {
992
+ const d = layout.dendrogram;
993
+ if (!d || d.cut === void 0) return "";
994
+ const x = d.baselineX - d.cut * d.scale;
995
+ const topY = 8;
996
+ const bottomY = layout.height - 32;
997
+ return chunk3WNW5Y7P_cjs.group({}, [
998
+ chunk3WNW5Y7P_cjs.line({
999
+ x1: x,
1000
+ y1: topY,
1001
+ x2: x,
1002
+ y2: bottomY,
1003
+ class: "schematex-phylo-dendro-cut"
1004
+ }),
1005
+ chunk3WNW5Y7P_cjs.text(
1006
+ { x: x + 4, y: topY + 4, class: "schematex-phylo-dendro-cut-label" },
1007
+ `cut = ${formatTick(d.cut)}`
1008
+ )
1009
+ ]);
1010
+ }
739
1011
  function renderPhylo(layout) {
740
1012
  const { ast, nodes, branches } = layout;
741
1013
  const t = chunkNZT5P2XZ_cjs.resolveBiologyTheme(ast.metadata?.theme ?? "default");
@@ -812,15 +1084,24 @@ function renderPhylo(layout) {
812
1084
  }
813
1085
  }
814
1086
  const cladeBgElements = renderCladeBackgrounds(layout, t);
815
- const scaleBarEl = renderScaleBar(layout, t, ast.scaleLabel);
1087
+ const isDendro = layout.dendrogram !== void 0;
1088
+ const scaleBarEl = isDendro ? "" : renderScaleBar(layout, t, ast.scaleLabel);
1089
+ const dendroAxisEl = isDendro ? renderDendrogramAxis(layout, t) : "";
1090
+ const cutLineEl = isDendro ? renderCutLine(layout) : "";
816
1091
  const titleEl = ast.title ? chunk3WNW5Y7P_cjs.text(
817
1092
  { x: totalWidth / 2, y: 20, class: "schematex-phylo-title" },
818
1093
  ast.title
819
1094
  ) : "";
820
1095
  const leafCount = nodes.filter((n) => n.node.isLeaf).length;
1096
+ const isDendrogramMode = layout.dendrogram !== void 0;
1097
+ const descMode = isDendrogramMode ? "dendrogram" : ast.mode;
1098
+ const descTitle = isDendrogramMode ? "Dendrogram" : "Phylogenetic Tree";
1099
+ const cutSuffix = isDendrogramMode && layout.dendrogram?.cut !== void 0 ? `, cut at ${layout.dendrogram.cut} into ${layout.dendrogram.clusterCount} clusters` : "";
821
1100
  const svgContent = [
822
- chunk3WNW5Y7P_cjs.title(`Phylogenetic Tree${ast.title ? `: ${ast.title}` : ""}`),
823
- chunk3WNW5Y7P_cjs.desc(`Phylogenetic tree with ${leafCount} taxa, ${ast.mode} mode, ${ast.layout} layout`),
1101
+ chunk3WNW5Y7P_cjs.title(`${descTitle}${ast.title ? `: ${ast.title}` : ""}`),
1102
+ chunk3WNW5Y7P_cjs.desc(
1103
+ `${isDendrogramMode ? "Dendrogram" : "Phylogenetic tree"} with ${leafCount} taxa, ${descMode} mode, ${ast.layout} layout${cutSuffix}`
1104
+ ),
824
1105
  chunk3WNW5Y7P_cjs.el("style", {}, css)
825
1106
  ];
826
1107
  if (titleEl) svgContent.push(titleEl);
@@ -859,6 +1140,22 @@ function renderPhylo(layout) {
859
1140
  )
860
1141
  );
861
1142
  }
1143
+ if (dendroAxisEl) {
1144
+ svgContent.push(
1145
+ chunk3WNW5Y7P_cjs.group(
1146
+ { transform: transformY ? `translate(0,${transformY})` : void 0 },
1147
+ [dendroAxisEl]
1148
+ )
1149
+ );
1150
+ }
1151
+ if (cutLineEl) {
1152
+ svgContent.push(
1153
+ chunk3WNW5Y7P_cjs.group(
1154
+ { transform: transformY ? `translate(0,${transformY})` : void 0 },
1155
+ [cutLineEl]
1156
+ )
1157
+ );
1158
+ }
862
1159
  return chunk3WNW5Y7P_cjs.svgRoot(
863
1160
  {
864
1161
  class: "schematex-diagram schematex-phylo",
@@ -890,5 +1187,5 @@ exports.layoutPhylo = layoutPhylo;
890
1187
  exports.parsePhylo = parsePhylo;
891
1188
  exports.phylo = phylo;
892
1189
  exports.renderPhylo = renderPhylo;
893
- //# sourceMappingURL=chunk-BL57NQKN.cjs.map
894
- //# sourceMappingURL=chunk-BL57NQKN.cjs.map
1190
+ //# sourceMappingURL=chunk-JAYJ2G4R.cjs.map
1191
+ //# sourceMappingURL=chunk-JAYJ2G4R.cjs.map