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