react-msaview-cli 8.4.1 → 9.0.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 (2) hide show
  1. package/dist/index.js +280 -180
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -46123,7 +46123,10 @@ var init_legendRows = __esm({
46123
46123
  });
46124
46124
 
46125
46125
  // ../lib/dist/constants.js
46126
- var defaultRowHeight, defaultColWidth, defaultHeight, defaultScrollX, defaultScrollY, defaultCurrentAlignment, defaultShowDomains, defaultShowDomainLegend, defaultHideGaps, defaultAllowedGappyness, defaultSubFeatureRows, subFeatureRowHeight, domainUnderlineHeight, segmentFeatureTypes, segmentShades, defaultDrawMsaLetters, defaultScrollZoom, scrollZoomAxes, defaultScrollZoomAxis, minLetterRowHeight, minLetterColWidth, minFeatureLabelHeight, labelReferenceFontSize, minColWidth, minRowHeight, maxCellSize, defaultBgColor, defaultColorSchemeName, defaultShowColumnStats, defaultDrawLabels, defaultLabelsAlignRight, defaultTreeAreaWidth, defaultTreeWidth, defaultShowBranchLen, treeOrders, defaultTreeOrder, defaultDrawTree, defaultDrawNodeBubbles, defaultDrawNodeLabels, defaultShowTreeOverview, defaultOverviewHeight, maxNeighborJoiningRows, rowTintAlpha, cladeHighlightColor, cladeHighlightAlpha, maxInlineSnapshotBytes, rowPanelHeaderHeight, treeScaleBarHeight, defaultFeaturePanelWidth, msaOverlayZIndex, legendZIndex;
46126
+ function fontSizeFor(rowHeight) {
46127
+ return Math.min(Math.max(6, rowHeight - 3), 18);
46128
+ }
46129
+ var defaultRowHeight, defaultColWidth, defaultHeight, defaultScrollX, defaultScrollY, defaultCurrentAlignment, defaultShowDomains, defaultShowDomainLegend, defaultHideGaps, defaultAllowedGappyness, defaultSubFeatureRows, subFeatureRowHeight, domainUnderlineHeight, segmentFeatureTypes, segmentShades, defaultDrawMsaLetters, defaultScrollZoom, scrollZoomAxes, defaultScrollZoomAxis, minLetterRowHeight, minLetterColWidth, minFeatureLabelHeight, labelFitFontSize, minColWidth, minRowHeight, maxCellSize, defaultBgColor, defaultColorSchemeName, defaultShowColumnStats, defaultDrawLabels, defaultLabelsAlignRight, minTreeWidth, maxFitTreeWidth, pxPerTreeDepth, maxTreeAreaShare, defaultShowBranchLen, treeOrders, defaultTreeOrder, defaultDrawTree, defaultDrawNodeBubbles, defaultDrawNodeLabels, defaultShowTreeOverview, defaultOverviewHeight, maxNeighborJoiningRows, rowTintAlpha, cladeHighlightColor, cladeHighlightAlpha, maxInlineSnapshotBytes, rowPanelHeaderHeight, treeScaleBarHeight, defaultFeaturePanelWidth, msaOverlayZIndex, legendZIndex;
46127
46130
  var init_constants = __esm({
46128
46131
  "../lib/dist/constants.js"() {
46129
46132
  "use strict";
@@ -46156,7 +46159,7 @@ var init_constants = __esm({
46156
46159
  minLetterRowHeight = 8;
46157
46160
  minLetterColWidth = 5;
46158
46161
  minFeatureLabelHeight = 9;
46159
- labelReferenceFontSize = 16;
46162
+ labelFitFontSize = fontSizeFor(defaultRowHeight);
46160
46163
  minColWidth = 0.2;
46161
46164
  minRowHeight = 1;
46162
46165
  maxCellSize = 80;
@@ -46165,8 +46168,10 @@ var init_constants = __esm({
46165
46168
  defaultShowColumnStats = true;
46166
46169
  defaultDrawLabels = true;
46167
46170
  defaultLabelsAlignRight = false;
46168
- defaultTreeAreaWidth = 400;
46169
- defaultTreeWidth = 300;
46171
+ minTreeWidth = 70;
46172
+ maxFitTreeWidth = 160;
46173
+ pxPerTreeDepth = 10;
46174
+ maxTreeAreaShare = 0.3;
46170
46175
  defaultShowBranchLen = true;
46171
46176
  treeOrders = [
46172
46177
  "branchLength",
@@ -47629,13 +47634,13 @@ function cladeLabelHorizontal(clade, rowHeight, fontSize2) {
47629
47634
  function cladeLabelOffset(clade) {
47630
47635
  return clade.mark === "bracket" ? bracketGap + bracketBarWidth + bracketGap : bracketGap;
47631
47636
  }
47632
- function cladeGutterWidth({ clades, rowHeight, fontSize: fontSize2 }) {
47637
+ function cladeGutterWidth(clades) {
47633
47638
  let widest = 0;
47634
47639
  for (const clade of clades) {
47635
47640
  if (!cladeInGutter(clade)) {
47636
47641
  continue;
47637
47642
  }
47638
- const text2 = clade.label ? cladeLabelHorizontal(clade, rowHeight, fontSize2) ? measureTextCanvas(clade.label, fontSize2) : fontSize2 : 0;
47643
+ const text2 = clade.label ? measureTextCanvas(clade.label, labelFitFontSize) : 0;
47639
47644
  widest = Math.max(widest, cladeLabelOffset(clade) + text2 + bracketGap);
47640
47645
  }
47641
47646
  return Math.min(maxCladeGutterWidth, Math.ceil(widest));
@@ -47644,6 +47649,7 @@ var bracketBarWidth, bracketGap, maxCladeGutterWidth;
47644
47649
  var init_cladeBrackets = __esm({
47645
47650
  "../lib/dist/components/tree/cladeBrackets.js"() {
47646
47651
  "use strict";
47652
+ init_constants();
47647
47653
  init_measureTextCanvas();
47648
47654
  bracketBarWidth = 3;
47649
47655
  bracketGap = 4;
@@ -47928,6 +47934,29 @@ var init_hierarchy2 = __esm({
47928
47934
  }
47929
47935
  });
47930
47936
 
47937
+ // ../lib/dist/components/tree/ellipsize.js
47938
+ function ellipsize(measure, text2, room) {
47939
+ const width3 = measure(text2);
47940
+ if (width3 <= room) {
47941
+ return { text: text2, width: width3 };
47942
+ }
47943
+ for (let n2 = Math.floor(text2.length * room / width3); n2 > 0; n2--) {
47944
+ const truncated = text2.slice(0, n2).trimEnd() + ellipsis;
47945
+ const truncatedWidth = measure(truncated);
47946
+ if (truncatedWidth <= room) {
47947
+ return { text: truncated, width: truncatedWidth };
47948
+ }
47949
+ }
47950
+ return { text: "", width: 0 };
47951
+ }
47952
+ var ellipsis;
47953
+ var init_ellipsize = __esm({
47954
+ "../lib/dist/components/tree/ellipsize.js"() {
47955
+ "use strict";
47956
+ ellipsis = "\u2026";
47957
+ }
47958
+ });
47959
+
47931
47960
  // ../lib/dist/components/tree/renderTreeCanvas.js
47932
47961
  function blockPad(model2) {
47933
47962
  return Math.max(model2.rowHeight, highlightLabelHeight);
@@ -48100,7 +48129,8 @@ function renderNodeLabels({ ctx, theme, offsetY, model: model2, tipX, maxDepthTo
48100
48129
  setFontSize(ctx, fontSize2);
48101
48130
  }
48102
48131
  function renderTreeLabels({ theme, model: model2, offsetY, ctx, clickMap, tipX, maxDepthToLeaf, blockSizeYOverride, collapsedSet }) {
48103
- const { fontSize: fontSize2, showBranchLenEffective: showBranchLen, tipLabelColors, blockSize, labelsAlignRight, drawTree, treeAreaWidthMinusMargin, marginLeft, noTree, labelWidthMap, labelWidthScale } = model2;
48132
+ const { fontSize: fontSize2, showBranchLenEffective: showBranchLen, tipLabelColors, blockSize, labelsAlignRight, drawTree, treeAreaWidthMinusMargin, marginLeft, noTree } = model2;
48133
+ const treeDrawn = drawTree && !noTree;
48104
48134
  const by = blockSizeYOverride ?? blockSize;
48105
48135
  const visibleLeaves = getVisibleLeaves({ model: model2, offsetY, blockSizeY: by });
48106
48136
  const pad2 = blockPad(model2);
@@ -48119,41 +48149,42 @@ function renderTreeLabels({ theme, model: model2, offsetY, ctx, clickMap, tipX,
48119
48149
  const isAnonymousCollapsed = collapsedSet.has(id) && name === id;
48120
48150
  if (!isAnonymousCollapsed && inYBlock(y2, offsetY, by, pad2)) {
48121
48151
  const yp = y2 + fontSize2 / 4;
48122
- let xp = 0;
48123
- if (!noTree) {
48124
- xp = getNodeX(node2, showBranchLen, tipX, maxDepthToLeaf) ?? 0;
48125
- }
48126
- const measured = labelWidthMap.get(name);
48127
- const width3 = measured === void 0 ? ctx.measureText(displayName).width : measured * labelWidthScale;
48152
+ const xp = treeDrawn ? getNodeX(node2, showBranchLen, tipX, maxDepthToLeaf) ?? 0 : 0;
48153
+ const fit = (room) => ellipsize((text2) => ctx.measureText(text2).width, displayName, room);
48154
+ const boxWidth = (width3) => Math.max(width3, fontSize2);
48128
48155
  ctx.fillStyle = tipLabelColors?.get(name) ?? theme.palette.text.primary;
48129
48156
  if (labelsAlignRight) {
48130
48157
  const smallPadding = 2;
48131
48158
  const offset4 = treeAreaWidthMinusMargin - smallPadding;
48132
- if (drawTree && !noTree) {
48159
+ const { text: text2, width: width3 } = fit(offset4 - (treeDrawn ? xp + radius + 2 + smallPadding : 0));
48160
+ if (treeDrawn) {
48133
48161
  ctx.beginPath();
48134
48162
  ctx.moveTo(xp + radius + 2, y2);
48135
48163
  ctx.lineTo(offset4 - smallPadding - width3, y2);
48136
48164
  ctx.stroke();
48137
48165
  }
48138
- ctx.fillText(displayName, offset4, yp);
48166
+ ctx.fillText(text2, offset4, yp);
48139
48167
  const labelRight = offset4 + marginLeft;
48140
48168
  clickMap?.insert({
48141
- minX: labelRight - width3,
48169
+ minX: labelRight - boxWidth(width3),
48142
48170
  maxX: labelRight,
48143
48171
  minY: yp - emHeight,
48144
48172
  maxY: yp,
48145
48173
  name,
48174
+ label: displayName,
48146
48175
  id
48147
48176
  });
48148
48177
  } else {
48149
- const labelX = noTree ? 2 : xp + d2;
48150
- ctx.fillText(displayName, labelX, yp);
48178
+ const labelX = treeDrawn ? xp + d2 : 2;
48179
+ const { text: text2, width: width3 } = fit(treeAreaWidthMinusMargin - labelX);
48180
+ ctx.fillText(text2, labelX, yp);
48151
48181
  clickMap?.insert({
48152
48182
  minX: labelX + marginLeft,
48153
- maxX: labelX + width3 + marginLeft,
48183
+ maxX: labelX + boxWidth(width3) + marginLeft,
48154
48184
  minY: yp - emHeight,
48155
48185
  maxY: yp,
48156
48186
  name,
48187
+ label: displayName,
48157
48188
  id
48158
48189
  });
48159
48190
  }
@@ -48171,9 +48202,9 @@ function firstRow(rowIndices) {
48171
48202
  return min3;
48172
48203
  }
48173
48204
  function renderCladeHighlights({ ctx, model: model2, offsetY, tipX, maxDepthToLeaf, blockSizeYOverride }) {
48174
- const { resolvedClades, hierarchy: hierarchy3, showBranchLenEffective: showBranchLen, rowHeight, treeAreaWidth, marginLeft, blockSize } = model2;
48205
+ const { resolvedClades, hierarchy: hierarchy3, showBranchLenEffective: showBranchLen, rowHeight, effectiveTreeAreaWidth, marginLeft, blockSize } = model2;
48175
48206
  const by = blockSizeYOverride ?? blockSize;
48176
- const right2 = treeAreaWidth - marginLeft;
48207
+ const right2 = effectiveTreeAreaWidth - marginLeft;
48177
48208
  for (const { rows, color: color2, mark } of resolvedClades) {
48178
48209
  if (mark !== "highlight") {
48179
48210
  continue;
@@ -48190,12 +48221,12 @@ function renderCladeHighlights({ ctx, model: model2, offsetY, tipX, maxDepthToLe
48190
48221
  }
48191
48222
  }
48192
48223
  function renderCladeBrackets({ ctx, model: model2, theme, offsetY, blockSizeYOverride }) {
48193
- const { resolvedClades, cladeGutterWidth: cladeGutterWidth2, rowHeight, treeAreaWidth, marginLeft, blockSize } = model2;
48224
+ const { resolvedClades, cladeGutterWidth: cladeGutterWidth2, rowHeight, effectiveTreeAreaWidth, marginLeft, blockSize } = model2;
48194
48225
  if (cladeGutterWidth2 === 0) {
48195
48226
  return;
48196
48227
  }
48197
48228
  const by = blockSizeYOverride ?? blockSize;
48198
- const x2 = treeAreaWidth - marginLeft - cladeGutterWidth2 + bracketGap + bracketBarWidth / 2;
48229
+ const x2 = effectiveTreeAreaWidth - marginLeft - cladeGutterWidth2 + bracketGap + bracketBarWidth / 2;
48199
48230
  ctx.lineWidth = bracketBarWidth;
48200
48231
  for (const { rows, mark, markColor } of resolvedClades) {
48201
48232
  const top2 = rows[0] * rowHeight + 1;
@@ -48213,12 +48244,12 @@ function renderCladeBrackets({ ctx, model: model2, theme, offsetY, blockSizeYOve
48213
48244
  ctx.strokeStyle = theme.palette.text.primary;
48214
48245
  }
48215
48246
  function renderRowHighlights({ ctx, model: model2, theme, offsetY, blockSizeYOverride }) {
48216
- const { resolvedHighlights, rowTints, referenceRowIndex, rowHeight, treeAreaWidth, marginLeft, blockSize } = model2;
48247
+ const { resolvedHighlights, rowTints, referenceRowIndex, rowHeight, effectiveTreeAreaWidth, marginLeft, blockSize } = model2;
48217
48248
  const by = blockSizeYOverride ?? blockSize;
48218
48249
  const pad2 = blockPad(model2);
48219
48250
  const placed = [];
48220
48251
  const fillRow = (index3) => {
48221
- ctx.fillRect(-marginLeft, index3 * rowHeight, treeAreaWidth, rowHeight);
48252
+ ctx.fillRect(-marginLeft, index3 * rowHeight, effectiveTreeAreaWidth, rowHeight);
48222
48253
  };
48223
48254
  if (rowTints) {
48224
48255
  const top2 = Math.max(0, Math.floor((offsetY - pad2) / rowHeight));
@@ -48250,8 +48281,8 @@ function renderRowHighlights({ ctx, model: model2, theme, offsetY, blockSizeYOve
48250
48281
  label: label2,
48251
48282
  x: -marginLeft + 2,
48252
48283
  y: first * rowHeight,
48253
- spanWidth: treeAreaWidth,
48254
- bounds: { min: -marginLeft, max: treeAreaWidth - marginLeft },
48284
+ spanWidth: effectiveTreeAreaWidth,
48285
+ bounds: { min: -marginLeft, max: effectiveTreeAreaWidth - marginLeft },
48255
48286
  placed
48256
48287
  });
48257
48288
  }
@@ -48277,8 +48308,8 @@ function renderTreeCanvas({ model: model2, clickMap, ctx, offsetY, theme, highRe
48277
48308
  ctx.scale(k2, k2);
48278
48309
  ctx.translate(marginLeft, -offsetY);
48279
48310
  setFontSize(ctx, fontSize2);
48280
- const { maxBranchLength, maxDepthToLeaf, showBranchLenEffective, treeWidth } = model2;
48281
- const tipX = showBranchLenEffective ? maxBranchLength : treeWidth;
48311
+ const { maxBranchLength, maxDepthToLeaf, showBranchLenEffective, effectiveTreeWidth } = model2;
48312
+ const tipX = showBranchLenEffective ? maxBranchLength : effectiveTreeWidth;
48282
48313
  const collapsedSet = new Set(model2.collapsed);
48283
48314
  renderCladeHighlights({
48284
48315
  ctx,
@@ -48361,6 +48392,7 @@ var init_renderTreeCanvas = __esm({
48361
48392
  init_renderHighlights();
48362
48393
  init_overlayColors();
48363
48394
  init_cladeBrackets();
48395
+ init_ellipsize();
48364
48396
  radius = 2.5;
48365
48397
  d2 = radius * 2;
48366
48398
  }
@@ -62833,13 +62865,13 @@ function legendHeight(rows) {
62833
62865
  return LEGEND_PAD * 2 + rows.length * LEGEND_ROW_H;
62834
62866
  }
62835
62867
  function getLayout(model2, opts) {
62836
- const { scrollX, scrollY, totalWidth, totalHeight, treeAreaWidth, rowPanelsWidth, rowPanelsHeaderHeight, totalTrackAreaHeight, minimapHeight, msaAreaHeight, msaCanvasWidth, showHorizontalScrollbar, treeOverviewHeight, treeScaleBar } = model2;
62868
+ const { scrollX, scrollY, totalWidth, totalHeight, effectiveTreeAreaWidth, rowPanelsWidth, rowPanelsHeaderHeight, totalTrackAreaHeight, minimapHeight, msaAreaHeight, msaCanvasWidth, showHorizontalScrollbar, treeOverviewHeight, treeScaleBar } = model2;
62837
62869
  const trackHeight = opts.includeTracks ? totalTrackAreaHeight : 0;
62838
62870
  const includeMinimap = opts.exportType === "viewport" && !!opts.includeMinimap && showHorizontalScrollbar;
62839
62871
  const rows = legendRows(model2.legends);
62840
62872
  const legendWidth = rows.length > 0 ? getLegendWidth(rows) : 0;
62841
62873
  const topHeight = Math.max(includeMinimap ? minimapHeight : 0, rowPanelsHeaderHeight, treeOverviewHeight + (treeScaleBar ? treeScaleBarHeight : 0));
62842
- const gutterWidth = treeAreaWidth + rowPanelsWidth;
62874
+ const gutterWidth = effectiveTreeAreaWidth + rowPanelsWidth;
62843
62875
  return opts.exportType === "entire" ? {
62844
62876
  width: totalWidth + gutterWidth,
62845
62877
  msaAreaWidth: totalWidth,
@@ -62879,7 +62911,7 @@ async function renderToSvg(model2, opts) {
62879
62911
  }
62880
62912
  function MsaSvg({ model: model2, theme, Context: Context2, layout, layers }) {
62881
62913
  const { width: width3, trackHeight, includeMinimap, legendWidth, topHeight } = layout;
62882
- const { treeAreaWidth, rowPanelsWidth } = model2;
62914
+ const { effectiveTreeAreaWidth, rowPanelsWidth } = model2;
62883
62915
  const totalWidth = width3 + legendWidth;
62884
62916
  const legendTop = topHeight;
62885
62917
  const height2 = legendWidth > 0 ? Math.max(layout.height, legendTop + legendHeight(legendRows(model2.legends))) : layout.height;
@@ -62903,7 +62935,7 @@ function MsaSvg({ model: model2, theme, Context: Context2, layout, layers }) {
62903
62935
  null,
62904
62936
  includeMinimap ? import_react10.default.createElement(
62905
62937
  "g",
62906
- { id: "minimap-panel", transform: `translate(${treeAreaWidth + rowPanelsWidth} 0)` },
62938
+ { id: "minimap-panel", transform: `translate(${effectiveTreeAreaWidth + rowPanelsWidth} 0)` },
62907
62939
  import_react10.default.createElement(MinimapSVG, { model: model2, theme })
62908
62940
  ) : null,
62909
62941
  import_react10.default.createElement(RowPanelHeadersSVG, { model: model2, theme, bandHeight: topHeight }),
@@ -62919,16 +62951,16 @@ function MsaSvg({ model: model2, theme, Context: Context2, layout, layers }) {
62919
62951
  );
62920
62952
  }
62921
62953
  function TreeOverviewSVG({ model: model2, theme, Context: Context2, layers }) {
62922
- const { treeAreaWidth, overviewHeight, id } = model2;
62923
- const ctx = new Context2(treeAreaWidth, overviewHeight);
62954
+ const { effectiveTreeAreaWidth, overviewHeight, id } = model2;
62955
+ const ctx = new Context2(effectiveTreeAreaWidth, overviewHeight);
62924
62956
  renderTreeOverview({
62925
62957
  model: model2,
62926
62958
  ctx,
62927
62959
  theme,
62928
- width: treeAreaWidth,
62960
+ width: effectiveTreeAreaWidth,
62929
62961
  height: overviewHeight
62930
62962
  });
62931
- return import_react10.default.createElement(ClipGroup, { panelId: "tree-overview", clipId: `tree-overview-${id}`, width: treeAreaWidth, height: overviewHeight, ctx, layers });
62963
+ return import_react10.default.createElement(ClipGroup, { panelId: "tree-overview", clipId: `tree-overview-${id}`, width: effectiveTreeAreaWidth, height: overviewHeight, ctx, layers });
62932
62964
  }
62933
62965
  function TreeScaleBarSVG({ model: model2, theme }) {
62934
62966
  const { marginLeft, treeScaleBar: bar, treeOverviewHeight } = model2;
@@ -62942,12 +62974,12 @@ function TreeScaleBarSVG({ model: model2, theme }) {
62942
62974
  );
62943
62975
  }
62944
62976
  function RowPanelHeadersSVG({ model: model2, theme, bandHeight }) {
62945
- const { resolvedRowPanels, treeAreaWidth, fontSize: fontSize2 } = model2;
62977
+ const { resolvedRowPanels, effectiveTreeAreaWidth, fontSize: fontSize2 } = model2;
62946
62978
  const y2 = bandHeight - 4;
62947
62979
  return resolvedRowPanels.length === 0 ? null : import_react10.default.createElement("g", { id: "rowpanel-headers" }, resolvedRowPanels.map((panel) => {
62948
62980
  const across = headerRunsAcross(panel.width);
62949
62981
  const size5 = across ? fontSize2 : Math.min(fontSize2, panel.width);
62950
- const center = treeAreaWidth + panel.offsetX + panel.width / 2;
62982
+ const center = effectiveTreeAreaWidth + panel.offsetX + panel.width / 2;
62951
62983
  const x2 = across ? center : center + size5 / 3;
62952
62984
  const room = across ? panel.width : y2 - LEGEND_PAD;
62953
62985
  const maxChars = Math.floor(room / (size5 * CHAR_WIDTH));
@@ -62963,11 +62995,11 @@ function clipCladeLabel(label2, room, fontSize2) {
62963
62995
  return `${label2.slice(0, Math.max(1, fits - 1))}\u2026`;
62964
62996
  }
62965
62997
  function CladeLabelsSVG({ model: model2, theme, offsetY }) {
62966
- const { resolvedClades, cladeGutterWidth: cladeGutterWidth2, rowHeight, treeAreaWidth, fontSize: fontSize2 } = model2;
62998
+ const { resolvedClades, cladeGutterWidth: cladeGutterWidth2, rowHeight, effectiveTreeAreaWidth, fontSize: fontSize2 } = model2;
62967
62999
  if (cladeGutterWidth2 === 0) {
62968
63000
  return null;
62969
63001
  }
62970
- const gutterLeft = treeAreaWidth - cladeGutterWidth2;
63002
+ const gutterLeft = effectiveTreeAreaWidth - cladeGutterWidth2;
62971
63003
  return import_react10.default.createElement("g", { id: "clade-labels" }, resolvedClades.map((clade, index3) => {
62972
63004
  const { label: label2, markColor, rows } = clade;
62973
63005
  if (!label2 || !cladeInGutter(clade)) {
@@ -62977,7 +63009,7 @@ function CladeLabelsSVG({ model: model2, theme, offsetY }) {
62977
63009
  const top2 = rows[0] * rowHeight - offsetY;
62978
63010
  const height2 = cladeHeight(clade, rowHeight);
62979
63011
  const horizontal = cladeLabelHorizontal(clade, rowHeight, fontSize2);
62980
- const room = horizontal ? treeAreaWidth - left2 - bracketGap : height2;
63012
+ const room = horizontal ? effectiveTreeAreaWidth - left2 - bracketGap : height2;
62981
63013
  const x2 = horizontal ? left2 : left2 + fontSize2;
62982
63014
  const y2 = horizontal ? top2 + height2 / 2 + fontSize2 / 3 : top2 + height2;
62983
63015
  return import_react10.default.createElement("text", { key: `${rows[0]}-${rows[1]}-${index3}`, x: x2, y: y2, transform: horizontal ? void 0 : `rotate(-90 ${x2} ${y2})`, fontSize: fontSize2, fill: markColor ?? theme.palette.text.primary }, clipCladeLabel(label2, room, fontSize2));
@@ -63004,8 +63036,8 @@ function LegendSVG({ model: model2, theme, width: width3 }) {
63004
63036
  }
63005
63037
  function CoreRendering({ model: model2, theme, layout, Context: Context2, layers }) {
63006
63038
  const { contentHeight, offsetX, offsetY, msaAreaWidth } = layout;
63007
- const { treeAreaWidth, rowPanelsWidth, id } = model2;
63008
- const treeCtx = new Context2(treeAreaWidth, contentHeight);
63039
+ const { effectiveTreeAreaWidth, rowPanelsWidth, id } = model2;
63040
+ const treeCtx = new Context2(effectiveTreeAreaWidth, contentHeight);
63009
63041
  renderTreeCanvas({
63010
63042
  model: model2,
63011
63043
  theme,
@@ -63063,10 +63095,10 @@ function CoreRendering({ model: model2, theme, layout, Context: Context2, layers
63063
63095
  return import_react10.default.createElement(
63064
63096
  import_react10.default.Fragment,
63065
63097
  null,
63066
- import_react10.default.createElement(ClipGroup, { panelId: "tree-panel", clipId: `tree-${id}`, width: treeAreaWidth, height: contentHeight, ctx: treeCtx, layers }),
63098
+ import_react10.default.createElement(ClipGroup, { panelId: "tree-panel", clipId: `tree-${id}`, width: effectiveTreeAreaWidth, height: contentHeight, ctx: treeCtx, layers }),
63067
63099
  import_react10.default.createElement(CladeLabelsSVG, { model: model2, theme, offsetY }),
63068
- rowPanelsCtx ? import_react10.default.createElement(ClipGroup, { panelId: "rowpanels-panel", clipId: `rowpanels-${id}`, width: rowPanelsWidth, height: contentHeight, transform: `translate(${treeAreaWidth} 0)`, ctx: rowPanelsCtx, layers }) : null,
63069
- import_react10.default.createElement(ClipGroup, { panelId: "msa-panel", clipId: `msa-${id}`, width: msaAreaWidth, height: contentHeight, transform: `translate(${treeAreaWidth + rowPanelsWidth} 0)`, ctx: msaCtx, layers, underlay: raster })
63100
+ rowPanelsCtx ? import_react10.default.createElement(ClipGroup, { panelId: "rowpanels-panel", clipId: `rowpanels-${id}`, width: rowPanelsWidth, height: contentHeight, transform: `translate(${effectiveTreeAreaWidth} 0)`, ctx: rowPanelsCtx, layers }) : null,
63101
+ import_react10.default.createElement(ClipGroup, { panelId: "msa-panel", clipId: `msa-${id}`, width: msaAreaWidth, height: contentHeight, transform: `translate(${effectiveTreeAreaWidth + rowPanelsWidth} 0)`, ctx: msaCtx, layers, underlay: raster })
63070
63102
  );
63071
63103
  }
63072
63104
  function rasterBackground({ model: model2, theme, layout }) {
@@ -63099,7 +63131,7 @@ function rasterBackground({ model: model2, theme, layout }) {
63099
63131
  }
63100
63132
  function TrackRendering({ model: model2, theme, layout, Context: Context2, layers }) {
63101
63133
  const { trackHeight, offsetX, msaAreaWidth } = layout;
63102
- const { treeAreaWidth, rowPanelsWidth, id } = model2;
63134
+ const { effectiveTreeAreaWidth, rowPanelsWidth, id } = model2;
63103
63135
  const ctx = new Context2(msaAreaWidth, trackHeight);
63104
63136
  renderAllTracks({
63105
63137
  model: model2,
@@ -63115,13 +63147,13 @@ function TrackRendering({ model: model2, theme, layout, Context: Context2, layer
63115
63147
  import_react10.default.createElement(TrackLabelsSVG, { model: model2, theme }),
63116
63148
  import_react10.default.createElement(
63117
63149
  "g",
63118
- { transform: `translate(${treeAreaWidth + rowPanelsWidth} 0)` },
63150
+ { transform: `translate(${effectiveTreeAreaWidth + rowPanelsWidth} 0)` },
63119
63151
  import_react10.default.createElement(ClipGroup, { panelId: "tracks-panel", clipId: `tracks-${id}`, width: msaAreaWidth, height: trackHeight, ctx, layers })
63120
63152
  )
63121
63153
  );
63122
63154
  }
63123
63155
  function TrackLabelsSVG({ model: model2, theme }) {
63124
- const { turnedOnTracks, treeAreaWidth, fontSize: fontSize2, drawLabels } = model2;
63156
+ const { turnedOnTracks, effectiveTreeAreaWidth, fontSize: fontSize2, drawLabels } = model2;
63125
63157
  if (!drawLabels) {
63126
63158
  return null;
63127
63159
  }
@@ -63132,7 +63164,7 @@ function TrackLabelsSVG({ model: model2, theme }) {
63132
63164
  return { id, name, size: size5, baseline: top2 + height2 / 2 + size5 / 3 };
63133
63165
  });
63134
63166
  return import_react10.default.createElement(import_react10.default.Fragment, null, labels.map(({ id, name, size: size5, baseline }) => {
63135
- return size5 < 5 ? null : import_react10.default.createElement("text", { key: id, x: treeAreaWidth - 4, y: baseline, fontSize: size5, textAnchor: "end", fill: theme.palette.text.primary }, name);
63167
+ return size5 < 5 ? null : import_react10.default.createElement("text", { key: id, x: effectiveTreeAreaWidth - 4, y: baseline, fontSize: size5, textAnchor: "end", fill: theme.palette.text.primary }, name);
63136
63168
  }));
63137
63169
  }
63138
63170
  function ClipGroup({ panelId, clipId, width: width3, height: height2, transform: transform2, ctx, layers, underlay }) {
@@ -79971,7 +80003,7 @@ var init_ResizeHandles = __esm({
79971
80003
  const onDrag = (0, import_react33.useCallback)((delta, startWidth) => {
79972
80004
  model2.setTreeAreaWidth(startWidth + delta);
79973
80005
  }, [model2]);
79974
- return import_react33.default.createElement(DragHandle, { axis: "x", variant: "resizer", getStart: () => model2.treeAreaWidth, onDrag, style: { width: model2.resizeHandleWidth } });
80006
+ return import_react33.default.createElement(DragHandle, { axis: "x", variant: "resizer", getStart: () => model2.effectiveTreeAreaWidth, onDrag, style: { width: model2.resizeHandleWidth } });
79975
80007
  });
79976
80008
  HorizontalResizeHandle = (0, import_mobx_react6.observer)(function({ model: model2 }) {
79977
80009
  const onDrag = (0, import_react33.useCallback)((delta, startHeight) => {
@@ -82783,7 +82815,7 @@ var init_Track = __esm({
82783
82815
  const closeMenu = () => {
82784
82816
  setAnchorEl(void 0);
82785
82817
  };
82786
- const { drawLabels, fontSize: fontSize2, treeAreaWidth: width3 } = model2;
82818
+ const { drawLabels, fontSize: fontSize2, effectiveTreeAreaWidth: width3 } = model2;
82787
82819
  const { model: { name, height: height2 } } = track;
82788
82820
  const { classes } = useStyles10();
82789
82821
  const trackLabelHeight = Math.min(height2, fontSize2);
@@ -84039,9 +84071,9 @@ var init_ExportSVGDialog = __esm({
84039
84071
  const [error, setError] = (0, import_react54.useState)();
84040
84072
  const [exporting, setExporting] = (0, import_react54.useState)(false);
84041
84073
  const theme = useTheme4();
84042
- const { totalWidth, totalHeight, treeAreaWidth, rowPanelsWidth, turnedOnTracks, totalTrackAreaHeight, numColumns, leaves: leaves2, showMsaLetters, treeOverviewHeight } = model2;
84074
+ const { totalWidth, totalHeight, effectiveTreeAreaWidth, rowPanelsWidth, turnedOnTracks, totalTrackAreaHeight, numColumns, leaves: leaves2, showMsaLetters, treeOverviewHeight } = model2;
84043
84075
  const hasTracks = turnedOnTracks.length > 0;
84044
- const entireWidth = totalWidth + treeAreaWidth + rowPanelsWidth;
84076
+ const entireWidth = totalWidth + effectiveTreeAreaWidth + rowPanelsWidth;
84045
84077
  const entireHeight = totalHeight + (includeTracks ? totalTrackAreaHeight : 0) + treeOverviewHeight;
84046
84078
  const glyphs = showMsaLetters && exportType === "entire" ? numColumns * leaves2.length : 0;
84047
84079
  const isLargeExport = glyphs > 1e5;
@@ -84496,7 +84528,7 @@ function msaSettingsMenuItems(model2) {
84496
84528
  ];
84497
84529
  }
84498
84530
  function treeSettingsMenuItems(model2) {
84499
- const { drawTree, showBranchLen, labelsAlignRight, drawNodeBubbles, drawNodeLabels, drawLabels, showTreeOverview, treeOrder, treeRoot, rotated } = model2;
84531
+ const { drawTree, showBranchLen, labelsAlignRight, drawNodeBubbles, drawNodeLabels, drawLabels, treeAreaWidth, showTreeOverview, treeOrder, treeRoot, rotated } = model2;
84500
84532
  return [
84501
84533
  {
84502
84534
  label: "Order",
@@ -84557,6 +84589,9 @@ function treeSettingsMenuItems(model2) {
84557
84589
  }),
84558
84590
  toggle("Draw labels", drawLabels, (arg2) => {
84559
84591
  model2.setDrawLabels(arg2);
84592
+ }),
84593
+ toggle("Fit tree area to labels and tree", treeAreaWidth === void 0, (arg2) => {
84594
+ model2.setTreeAreaWidth(arg2 ? void 0 : model2.effectiveTreeAreaWidth);
84560
84595
  })
84561
84596
  ];
84562
84597
  }
@@ -85049,7 +85084,7 @@ var version2;
85049
85084
  var init_version3 = __esm({
85050
85085
  "../lib/dist/version.js"() {
85051
85086
  "use strict";
85052
- version2 = "8.4.1";
85087
+ version2 = "9.0.0";
85053
85088
  }
85054
85089
  });
85055
85090
 
@@ -85949,10 +85984,10 @@ var init_TreeOverview = __esm({
85949
85984
  init_useCanvasAutorun();
85950
85985
  init_renderTreeOverview();
85951
85986
  TreeOverview = (0, import_mobx_react39.observer)(function({ model: model2 }) {
85952
- const { treeAreaWidth, overviewHeight, highResScaleFactor } = model2;
85987
+ const { effectiveTreeAreaWidth, overviewHeight, highResScaleFactor } = model2;
85953
85988
  const theme = useTheme4();
85954
85989
  const [hover, setHover] = (0, import_react82.useState)();
85955
- const width3 = Math.round(treeAreaWidth * highResScaleFactor);
85990
+ const width3 = Math.round(effectiveTreeAreaWidth * highResScaleFactor);
85956
85991
  const height2 = Math.round(overviewHeight * highResScaleFactor);
85957
85992
  const ref = useCanvasAutorun({
85958
85993
  draw: (ctx) => {
@@ -85961,12 +85996,17 @@ var init_TreeOverview = __esm({
85961
85996
  const image = treeOverviewImage({
85962
85997
  model: model2,
85963
85998
  theme,
85964
- width: treeAreaWidth,
85999
+ width: effectiveTreeAreaWidth,
85965
86000
  height: overviewHeight,
85966
86001
  scale: highResScaleFactor
85967
86002
  });
85968
86003
  ctx.scale(highResScaleFactor, highResScaleFactor);
85969
- const box = { model: model2, ctx, width: treeAreaWidth, height: overviewHeight };
86004
+ const box = {
86005
+ model: model2,
86006
+ ctx,
86007
+ width: effectiveTreeAreaWidth,
86008
+ height: overviewHeight
86009
+ };
85970
86010
  if (image) {
85971
86011
  ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, box.width, box.height);
85972
86012
  } else {
@@ -85996,7 +86036,7 @@ var init_TreeOverview = __esm({
85996
86036
  display: "block",
85997
86037
  flexShrink: 0,
85998
86038
  cursor: "pointer",
85999
- width: treeAreaWidth,
86039
+ width: effectiveTreeAreaWidth,
86000
86040
  height: overviewHeight
86001
86041
  }, onMouseMove: (event) => {
86002
86042
  const hit = model2.treeOverviewHit(event.nativeEvent.offsetY);
@@ -86021,12 +86061,12 @@ var init_CladeLabels = __esm({
86021
86061
  import_mobx_react40 = __toESM(require_dist3(), 1);
86022
86062
  init_cladeBrackets();
86023
86063
  CladeLabels = (0, import_mobx_react40.observer)(function({ model: model2 }) {
86024
- const { resolvedClades, cladeGutterWidth: cladeGutterWidth2, rowHeight, treeAreaWidth, fontSize: fontSize2 } = model2;
86064
+ const { resolvedClades, cladeGutterWidth: cladeGutterWidth2, rowHeight, effectiveTreeAreaWidth, fontSize: fontSize2 } = model2;
86025
86065
  const theme = useTheme4();
86026
86066
  if (cladeGutterWidth2 === 0) {
86027
86067
  return null;
86028
86068
  }
86029
- const gutterLeft = treeAreaWidth - cladeGutterWidth2;
86069
+ const gutterLeft = effectiveTreeAreaWidth - cladeGutterWidth2;
86030
86070
  return import_react83.default.createElement(import_react83.default.Fragment, null, resolvedClades.map((clade, index3) => {
86031
86071
  const { label: label2, markColor, rows } = clade;
86032
86072
  if (!label2 || !cladeInGutter(clade)) {
@@ -86038,7 +86078,7 @@ var init_CladeLabels = __esm({
86038
86078
  position: "absolute",
86039
86079
  left: left2,
86040
86080
  top: rows[0] * rowHeight,
86041
- width: treeAreaWidth - left2 - bracketGap,
86081
+ width: effectiveTreeAreaWidth - left2 - bracketGap,
86042
86082
  height: cladeHeight(clade, rowHeight),
86043
86083
  display: "flex",
86044
86084
  alignItems: "center",
@@ -86773,8 +86813,8 @@ var init_TreeCanvasBlock = __esm({
86773
86813
  const [menu, setMenu] = (0, import_react87.useState)();
86774
86814
  const { clickMap, anchor, hitTest, onMouseMove, onMouseLeave } = useTreeHover({ model: model2, offsetY });
86775
86815
  const hovered = anchor?.value;
86776
- const { treeAreaWidth, blockSize, highResScaleFactor } = model2;
86777
- const width3 = treeAreaWidth;
86816
+ const { effectiveTreeAreaWidth, blockSize, highResScaleFactor } = model2;
86817
+ const width3 = effectiveTreeAreaWidth;
86778
86818
  const height2 = blockSize;
86779
86819
  const canvasWidth = width3 * highResScaleFactor;
86780
86820
  const canvasHeight = height2 * highResScaleFactor;
@@ -86829,7 +86869,7 @@ var init_TreeCanvasBlock = __esm({
86829
86869
  width: hovered.maxX - hovered.minX,
86830
86870
  height: hovered.maxY - hovered.minY
86831
86871
  } }) : null,
86832
- anchor ? import_react87.default.createElement(BaseTooltip, { clientPoint: anchor.clientPoint }, anchor.value.name) : null
86872
+ anchor ? import_react87.default.createElement(BaseTooltip, { clientPoint: anchor.clientPoint }, anchor.value.label ?? anchor.value.name) : null
86833
86873
  );
86834
86874
  });
86835
86875
  TreeCanvasBlock_default = TreeCanvasBlock;
@@ -86838,7 +86878,7 @@ var init_TreeCanvasBlock = __esm({
86838
86878
 
86839
86879
  // ../lib/dist/components/tree/renderTreeMouseover.js
86840
86880
  function renderTreeMouseover({ ctx, model: model2 }) {
86841
- const { rowHeight, treeAreaWidth: width3, height: height2, scrollY, mouseRow, referenceRowIndex, hoveredRowIndices, highResScaleFactor } = model2;
86881
+ const { rowHeight, effectiveTreeAreaWidth: width3, height: height2, scrollY, mouseRow, referenceRowIndex, hoveredRowIndices, highResScaleFactor } = model2;
86842
86882
  ctx.resetTransform();
86843
86883
  ctx.clearRect(0, 0, width3 * highResScaleFactor, height2 * highResScaleFactor);
86844
86884
  ctx.scale(highResScaleFactor, highResScaleFactor);
@@ -86875,12 +86915,12 @@ var init_TreeCanvas = __esm({
86875
86915
  init_renderTreeMouseover();
86876
86916
  TreeCanvas = (0, import_mobx_react44.observer)(function({ model: model2 }) {
86877
86917
  const ref = (0, import_react88.useRef)(null);
86878
- const { height: height2, blocksY, treeAreaWidth, scrollY, highResScaleFactor } = model2;
86918
+ const { height: height2, blocksY, effectiveTreeAreaWidth, scrollY, highResScaleFactor } = model2;
86879
86919
  const onScrollY = (0, import_react88.useCallback)((d3) => {
86880
86920
  model2.doScrollY(d3);
86881
86921
  }, [model2]);
86882
86922
  const { onMouseDown, onMouseUp } = useWheelScroll({ ref, onScrollY });
86883
- const canvasWidth = treeAreaWidth * highResScaleFactor;
86923
+ const canvasWidth = effectiveTreeAreaWidth * highResScaleFactor;
86884
86924
  const canvasHeight = height2 * highResScaleFactor;
86885
86925
  const mouseoverRef = useCanvasAutorun({
86886
86926
  draw: (ctx) => {
@@ -86897,7 +86937,7 @@ var init_TreeCanvas = __esm({
86897
86937
  { ref, onMouseDown, onMouseUp, style: {
86898
86938
  height: height2,
86899
86939
  position: "relative",
86900
- width: treeAreaWidth
86940
+ width: effectiveTreeAreaWidth
86901
86941
  } },
86902
86942
  import_react88.default.createElement(
86903
86943
  "div",
@@ -86915,7 +86955,7 @@ var init_TreeCanvas = __esm({
86915
86955
  position: "absolute",
86916
86956
  top: 0,
86917
86957
  left: 0,
86918
- width: treeAreaWidth,
86958
+ width: effectiveTreeAreaWidth,
86919
86959
  height: height2,
86920
86960
  zIndex: 1e3,
86921
86961
  pointerEvents: "none"
@@ -86935,10 +86975,14 @@ var init_TreePanel = __esm({
86935
86975
  import_mobx_react45 = __toESM(require_dist3(), 1);
86936
86976
  init_TreeCanvas();
86937
86977
  TreePanel = (0, import_mobx_react45.observer)(function({ model: model2 }) {
86938
- const { treeAreaWidth } = model2;
86978
+ const { effectiveTreeAreaWidth } = model2;
86939
86979
  return import_react89.default.createElement(
86940
86980
  "div",
86941
- { style: { overflow: "hidden", flexShrink: 0, width: treeAreaWidth } },
86981
+ { style: {
86982
+ overflow: "hidden",
86983
+ flexShrink: 0,
86984
+ width: effectiveTreeAreaWidth
86985
+ } },
86942
86986
  import_react89.default.createElement(TreeCanvas_default, { model: model2 })
86943
86987
  );
86944
86988
  });
@@ -86957,11 +87001,11 @@ var init_TreeRuler = __esm({
86957
87001
  init_constants();
86958
87002
  init_ScaleBarSVG();
86959
87003
  TreeRuler = (0, import_mobx_react46.observer)(({ model: model2 }) => {
86960
- const { treeAreaWidth, marginLeft, treeScaleBar: bar } = model2;
87004
+ const { effectiveTreeAreaWidth, marginLeft, treeScaleBar: bar } = model2;
86961
87005
  const theme = useTheme4();
86962
- return import_react90.default.createElement("div", { style: { flexShrink: 0, width: treeAreaWidth } }, bar ? import_react90.default.createElement(
87006
+ return import_react90.default.createElement("div", { style: { flexShrink: 0, width: effectiveTreeAreaWidth } }, bar ? import_react90.default.createElement(
86963
87007
  "svg",
86964
- { width: treeAreaWidth, height: treeScaleBarHeight, style: { display: "block" } },
87008
+ { width: effectiveTreeAreaWidth, height: treeScaleBarHeight, style: { display: "block" } },
86965
87009
  import_react90.default.createElement(ScaleBarSVG, { bar, marginLeft, y: treeScaleBarHeight, color: theme.palette.text.primary })
86966
87010
  ) : null);
86967
87011
  });
@@ -87005,7 +87049,7 @@ var init_MSAView = __esm({
87005
87049
  );
87006
87050
  });
87007
87051
  TrackColumnIndicator = (0, import_mobx_react47.observer)(function({ model: model2 }) {
87008
- const { mouseCol, mouseClickCol, colWidth, scrollX, treeAreaWidth, rowPanelsWidth, resizeHandleWidth, totalTrackAreaHeight, msaCanvasWidth, resolvedSelection } = model2;
87052
+ const { mouseCol, mouseClickCol, colWidth, scrollX, effectiveTreeAreaWidth, rowPanelsWidth, resizeHandleWidth, totalTrackAreaHeight, msaCanvasWidth, resolvedSelection } = model2;
87009
87053
  const bands = [
87010
87054
  {
87011
87055
  start: resolvedSelection?.startCol,
@@ -87017,7 +87061,7 @@ var init_MSAView = __esm({
87017
87061
  ];
87018
87062
  return import_react91.default.createElement("div", { style: {
87019
87063
  position: "absolute",
87020
- left: treeAreaWidth + rowPanelsWidth + resizeHandleWidth,
87064
+ left: effectiveTreeAreaWidth + rowPanelsWidth + resizeHandleWidth,
87021
87065
  top: 0,
87022
87066
  width: msaCanvasWidth,
87023
87067
  height: totalTrackAreaHeight,
@@ -88027,15 +88071,18 @@ function TreeModelF() {
88027
88071
  labelsAlignRight: stripDefault2(types.boolean, defaultLabelsAlignRight),
88028
88072
  /**
88029
88073
  * #property
88030
- * width of the area the tree is drawn in, px
88074
+ * width of the area the tree and its labels draw in, px. Undefined fits
88075
+ * the area to the labels and the tree; `effectiveTreeAreaWidth` reads
88076
+ * the width either way
88031
88077
  */
88032
- treeAreaWidth: stripDefault2(types.number, defaultTreeAreaWidth),
88078
+ treeAreaWidth: types.maybe(types.number),
88033
88079
  /**
88034
88080
  * #property
88035
- * width of the tree within the treeArea, px. automatically synced to
88036
- * fit within treeAreaWidth
88081
+ * width of the tree within the tree area, px. Undefined gives the tree
88082
+ * the area less the labels; `effectiveTreeWidth` reads the width either
88083
+ * way
88037
88084
  */
88038
- treeWidth: stripDefault2(types.number, defaultTreeWidth),
88085
+ treeWidth: types.maybe(types.number),
88039
88086
  /**
88040
88087
  * #property
88041
88088
  * use "branch length" e.g. evolutionary distance to draw tree branch
@@ -88076,28 +88123,24 @@ function TreeModelF() {
88076
88123
  * #property
88077
88124
  * height of the tree overview band, px
88078
88125
  */
88079
- overviewHeight: stripDefault2(types.number, defaultOverviewHeight),
88080
- /**
88081
- * #property
88082
- * auto-size treeAreaWidth to fit the row labels (plus the tree, if drawn)
88083
- * instead of using a fixed width. useful when there is no tree, so the
88084
- * label gutter isn't padded out to the default 400px
88085
- */
88086
- autoTreeAreaWidth: stripDefault2(types.boolean, false)
88126
+ overviewHeight: stripDefault2(types.number, defaultOverviewHeight)
88087
88127
  }).actions((self) => ({
88088
88128
  /**
88089
88129
  * #action
88090
- * set tree area width (px)
88130
+ * set the tree area width, px, or undefined to fit it to the labels and
88131
+ * the tree. The tree then fills the area less the labels
88091
88132
  */
88092
88133
  setTreeAreaWidth(n2) {
88093
- self.treeAreaWidth = Math.round(n2);
88134
+ self.treeAreaWidth = n2 === void 0 ? void 0 : Math.round(n2);
88135
+ self.treeWidth = void 0;
88094
88136
  },
88095
88137
  /**
88096
88138
  * #action
88097
- * set tree width (px)
88139
+ * set the tree width, px, or undefined to fill the tree area less the
88140
+ * labels
88098
88141
  */
88099
88142
  setTreeWidth(n2) {
88100
- self.treeWidth = Math.round(n2);
88143
+ self.treeWidth = n2 === void 0 ? void 0 : Math.round(n2);
88101
88144
  },
88102
88145
  /**
88103
88146
  * #action
@@ -88111,12 +88154,6 @@ function TreeModelF() {
88111
88154
  setDrawTree(arg2) {
88112
88155
  self.drawTree = arg2;
88113
88156
  },
88114
- /**
88115
- * #action
88116
- */
88117
- setAutoTreeAreaWidth(arg2) {
88118
- self.autoTreeAreaWidth = arg2;
88119
- },
88120
88157
  /**
88121
88158
  * #action
88122
88159
  */
@@ -88841,6 +88878,53 @@ var init_seqPosToGlobalCol = __esm({
88841
88878
  }
88842
88879
  });
88843
88880
 
88881
+ // ../lib/dist/tipLabels.js
88882
+ function treeTips(tree) {
88883
+ const names = [];
88884
+ let depth = 0;
88885
+ const stack = [[tree, 0]];
88886
+ for (let entry = stack.pop(); entry; entry = stack.pop()) {
88887
+ const [node2, level] = entry;
88888
+ const { children } = node2;
88889
+ if (children.length > 0) {
88890
+ for (const child of children) {
88891
+ stack.push([child, level + 1]);
88892
+ }
88893
+ } else {
88894
+ names.push(node2.name);
88895
+ depth = Math.max(depth, level);
88896
+ }
88897
+ }
88898
+ return { names, depth };
88899
+ }
88900
+ function widestLabel(labels, fontSize2) {
88901
+ const counts = [];
88902
+ for (const label2 of labels) {
88903
+ counts[label2.length] = (counts[label2.length] ?? 0) + 1;
88904
+ }
88905
+ let shortest = counts.length;
88906
+ for (let seen = 0; shortest > 0 && seen < measuredLabels; ) {
88907
+ shortest--;
88908
+ seen += counts[shortest] ?? 0;
88909
+ }
88910
+ let widest = 0;
88911
+ let ties = 0;
88912
+ for (const label2 of labels) {
88913
+ if (label2.length > shortest || label2.length === shortest && ties++ < measuredLabels) {
88914
+ widest = Math.max(widest, measureTextCanvas(label2, fontSize2));
88915
+ }
88916
+ }
88917
+ return widest;
88918
+ }
88919
+ var measuredLabels;
88920
+ var init_tipLabels = __esm({
88921
+ "../lib/dist/tipLabels.js"() {
88922
+ "use strict";
88923
+ init_measureTextCanvas();
88924
+ measuredLabels = 100;
88925
+ }
88926
+ });
88927
+
88844
88928
  // ../lib/dist/writeNewick.js
88845
88929
  function quote(name) {
88846
88930
  return needsQuotes.test(name) ? `'${name.replaceAll("'", "''")}'` : name;
@@ -90056,10 +90140,10 @@ function stateModelFactory({ maxInlineSnapshotBytes: maxInlineSnapshotBytes2 = m
90056
90140
  * the row table: extra fields per row, keyed by row name, which the
90057
90141
  * `encodings` channels read. It is stored as the JSON string
90058
90142
  * `data.treeMetadata`, the name that travels in existing links, so the
90059
- * inline size limit and `treeMetadataFilehandle` cover it. labelWidthMap
90060
- * reads it on every layout, so a malformed user-supplied file returns {}
90061
- * instead of throwing out of rendering, and every field is a string by
90062
- * the time a scale sorts it.
90143
+ * inline size limit and `treeMetadataFilehandle` cover it. The tree area
90144
+ * fit reads it on every layout, so a malformed user-supplied file
90145
+ * returns {} instead of throwing out of rendering, and every field is a
90146
+ * string by the time a scale sorts it.
90063
90147
  */
90064
90148
  get rowData() {
90065
90149
  const text2 = self.data.treeMetadata;
@@ -90169,11 +90253,80 @@ function stateModelFactory({ maxInlineSnapshotBytes: maxInlineSnapshotBytes2 = m
90169
90253
  * where no clade draws a bar or a label.
90170
90254
  */
90171
90255
  get cladeGutterWidth() {
90172
- return cladeGutterWidth({
90173
- clades: this.resolvedClades,
90174
- rowHeight: self.rowHeight,
90175
- fontSize: this.fontSize
90176
- });
90256
+ return cladeGutterWidth(self.clades);
90257
+ },
90258
+ /**
90259
+ * #getter
90260
+ * whether a tree draws beside the labels: false for a file with no
90261
+ * tree, and with drawTree off
90262
+ */
90263
+ get treeDrawn() {
90264
+ return self.drawTree && !this.noTree;
90265
+ },
90266
+ /**
90267
+ * #getter
90268
+ * the leaf names of the whole tree and its depth in levels. The fit
90269
+ * reads these, so a collapse or a focus leaves the tree area as it is
90270
+ */
90271
+ get treeTips() {
90272
+ return treeTips(this.tree);
90273
+ },
90274
+ /**
90275
+ * #getter
90276
+ * the widest tip label at labelFitFontSize, measured whether or not a
90277
+ * zoom hides the labels
90278
+ */
90279
+ get maxTipLabelWidth() {
90280
+ return widestLabel(this.treeTips.names.map((name) => this.rowDataOf(name)?.genome || name), labelFitFontSize);
90281
+ },
90282
+ /**
90283
+ * #getter
90284
+ * the px the tip labels take across the tree area: the widest label at
90285
+ * labelFitFontSize and the gaps either side of it, or 0 with drawLabels
90286
+ * off
90287
+ */
90288
+ get labelColumnWidth() {
90289
+ return self.drawLabels ? this.maxTipLabelWidth + (this.treeDrawn ? 10 : 6) : 0;
90290
+ },
90291
+ /**
90292
+ * #getter
90293
+ * the tree area width while treeAreaWidth is undefined: the label column
90294
+ * beside a tree that widens with its depth, up to maxTreeAreaShare of the
90295
+ * view, or the room the row panels leave where there is no alignment.
90296
+ * Where that binds, the tree keeps minTreeWidth and the renderer
90297
+ * ellipsizes the labels
90298
+ */
90299
+ get fittedTreeAreaWidth() {
90300
+ const chrome = self.marginLeft + this.cladeGutterWidth;
90301
+ const tree = this.treeDrawn ? self.treeWidth ?? clamp3(this.treeTips.depth * pxPerTreeDepth, minTreeWidth, maxFitTreeWidth) : 0;
90302
+ const fit = chrome + tree + this.labelColumnWidth;
90303
+ const room = self.viewInitialized ? this.numColumns > 0 ? self.width * maxTreeAreaShare : self.width - this.rowPanelsWidth - self.resizeHandleWidth : Infinity;
90304
+ const cap = Math.max(room, chrome + Math.min(tree, minTreeWidth));
90305
+ return Math.ceil(Math.min(fit, cap));
90306
+ },
90307
+ /**
90308
+ * #getter
90309
+ * the width of the area the tree and its labels draw in, px:
90310
+ * treeAreaWidth where set, the fit otherwise
90311
+ */
90312
+ get effectiveTreeAreaWidth() {
90313
+ return self.treeAreaWidth ?? this.fittedTreeAreaWidth;
90314
+ },
90315
+ /**
90316
+ * #getter
90317
+ * the width of the tree, px: treeWidth where set, otherwise the tree
90318
+ * area less the label column, which a zoom hiding the labels gives to
90319
+ * the tree
90320
+ */
90321
+ get effectiveTreeWidth() {
90322
+ const labels = this.showTreeText ? this.labelColumnWidth : 0;
90323
+ return self.treeWidth ?? Math.max(minTreeWidth, Math.floor(this.effectiveTreeAreaWidth - self.marginLeft - this.cladeGutterWidth - labels));
90324
+ },
90325
+ /**
90326
+ * #getter
90327
+ */
90328
+ get showTreeText() {
90329
+ return self.drawLabels && self.rowHeight >= minLetterRowHeight;
90177
90330
  },
90178
90331
  /**
90179
90332
  * #getter
@@ -90330,7 +90483,7 @@ function stateModelFactory({ maxInlineSnapshotBytes: maxInlineSnapshotBytes2 = m
90330
90483
  * for the MSA
90331
90484
  */
90332
90485
  get msaAreaWidth() {
90333
- return self.width - self.treeAreaWidth - this.rowPanelsWidth - self.resizeHandleWidth;
90486
+ return self.width - this.effectiveTreeAreaWidth - this.rowPanelsWidth - self.resizeHandleWidth;
90334
90487
  },
90335
90488
  /**
90336
90489
  * #getter
@@ -90338,7 +90491,7 @@ function stateModelFactory({ maxInlineSnapshotBytes: maxInlineSnapshotBytes2 = m
90338
90491
  * margin and the bracket gutter
90339
90492
  */
90340
90493
  get treeAreaWidthMinusMargin() {
90341
- return self.treeAreaWidth - self.marginLeft - this.cladeGutterWidth;
90494
+ return this.effectiveTreeAreaWidth - self.marginLeft - this.cladeGutterWidth;
90342
90495
  },
90343
90496
  /**
90344
90497
  * #getter
@@ -90461,7 +90614,7 @@ function stateModelFactory({ maxInlineSnapshotBytes: maxInlineSnapshotBytes2 = m
90461
90614
  * #getter
90462
90615
  */
90463
90616
  get fontSize() {
90464
- return Math.min(Math.max(6, self.rowHeight - 3), 18);
90617
+ return fontSizeFor(self.rowHeight);
90465
90618
  },
90466
90619
  /**
90467
90620
  * #getter
@@ -90551,17 +90704,18 @@ function stateModelFactory({ maxInlineSnapshotBytes: maxInlineSnapshotBytes2 = m
90551
90704
  /**
90552
90705
  * #getter
90553
90706
  * `root` laid out on new nodes in row and branch-length units, read in
90554
- * pixels through `rowHeight` and `treeWidth`. A zoom leaves it and every
90555
- * node in it as they are, and an observer reading a node's `x` or `len`
90556
- * observes the size it scales by.
90707
+ * pixels through `rowHeight` and `effectiveTreeWidth`. A zoom leaves it
90708
+ * and every node in it as they are, and an observer reading a node's `x`
90709
+ * or `len` observes the size it scales by.
90557
90710
  */
90558
90711
  get treeLayout() {
90712
+ const treeWidth = () => this.effectiveTreeWidth;
90559
90713
  return layoutTree(this.root, {
90560
90714
  get rowHeight() {
90561
90715
  return self.rowHeight;
90562
90716
  },
90563
90717
  get treeWidth() {
90564
- return self.treeWidth;
90718
+ return treeWidth();
90565
90719
  }
90566
90720
  });
90567
90721
  },
@@ -90594,11 +90748,11 @@ function stateModelFactory({ maxInlineSnapshotBytes: maxInlineSnapshotBytes2 = m
90594
90748
  },
90595
90749
  /**
90596
90750
  * #getter
90597
- * x-position of the farthest tip in a phylogram, px: treeWidth, or 0 for
90598
- * a tree with no branch lengths (drawn as a cladogram)
90751
+ * x-position of the farthest tip in a phylogram, px: effectiveTreeWidth,
90752
+ * or 0 for a tree with no branch lengths (drawn as a cladogram)
90599
90753
  */
90600
90754
  get maxBranchLength() {
90601
- return this.rootToTipLength ? self.treeWidth : 0;
90755
+ return this.rootToTipLength ? this.effectiveTreeWidth : 0;
90602
90756
  },
90603
90757
  /**
90604
90758
  * #getter
@@ -90607,7 +90761,7 @@ function stateModelFactory({ maxInlineSnapshotBytes: maxInlineSnapshotBytes2 = m
90607
90761
  */
90608
90762
  get pxPerBranchLength() {
90609
90763
  const max3 = this.rootToTipLength;
90610
- return this.showBranchLenEffective && max3 ? self.treeWidth / max3 : 0;
90764
+ return this.showBranchLenEffective && max3 ? this.effectiveTreeWidth / max3 : 0;
90611
90765
  },
90612
90766
  /**
90613
90767
  * #getter
@@ -90642,12 +90796,6 @@ function stateModelFactory({ maxInlineSnapshotBytes: maxInlineSnapshotBytes2 = m
90642
90796
  */
90643
90797
  get showMsaLetters() {
90644
90798
  return self.drawMsaLetters && self.rowHeight >= minLetterRowHeight && self.colWidth >= minLetterColWidth && self.colWidth > self.rowHeight / 2;
90645
- },
90646
- /**
90647
- * #getter
90648
- */
90649
- get showTreeText() {
90650
- return self.drawLabels && self.rowHeight >= minLetterRowHeight;
90651
90799
  }
90652
90800
  })).views((self) => {
90653
90801
  const columnTrackModels = /* @__PURE__ */ new WeakMap();
@@ -90750,37 +90898,6 @@ function stateModelFactory({ maxInlineSnapshotBytes: maxInlineSnapshotBytes2 = m
90750
90898
  }
90751
90899
  };
90752
90900
  }).views((self) => ({
90753
- /**
90754
- * #getter
90755
- */
90756
- get labelWidthMap() {
90757
- const { showTreeText, leaves: leaves2 } = self;
90758
- return showTreeText ? new Map(leaves2.map((node2) => {
90759
- const { name } = node2.data;
90760
- const displayName = self.rowDataOf(name)?.genome || name;
90761
- return [
90762
- name,
90763
- measureTextCanvas(displayName, labelReferenceFontSize)
90764
- ];
90765
- })) : /* @__PURE__ */ new Map();
90766
- },
90767
- /**
90768
- * #getter
90769
- * factor turning a labelWidthMap entry into its width at the current
90770
- * font size
90771
- */
90772
- get labelWidthScale() {
90773
- return self.fontSize / labelReferenceFontSize;
90774
- },
90775
- get labelsWidth() {
90776
- let max3 = 0;
90777
- for (const width3 of this.labelWidthMap.values()) {
90778
- if (width3 > max3) {
90779
- max3 = width3;
90780
- }
90781
- }
90782
- return max3 * this.labelWidthScale;
90783
- },
90784
90901
  /**
90785
90902
  * #getter
90786
90903
  */
@@ -91194,7 +91311,7 @@ function stateModelFactory({ maxInlineSnapshotBytes: maxInlineSnapshotBytes2 = m
91194
91311
  * or when the tree area is too narrow for one
91195
91312
  */
91196
91313
  get treeScaleBar() {
91197
- return scaleBarLength(self.pxPerBranchLength, self.treeAreaWidth - self.marginLeft * 2);
91314
+ return scaleBarLength(self.pxPerBranchLength, self.effectiveTreeAreaWidth - self.marginLeft * 2);
91198
91315
  },
91199
91316
  /**
91200
91317
  * #getter
@@ -92522,21 +92639,6 @@ function stateModelFactory({ maxInlineSnapshotBytes: maxInlineSnapshotBytes2 = m
92522
92639
  }
92523
92640
  self.columns;
92524
92641
  }));
92525
- addDisposer(self, (0, import_mobx5.autorun)(() => {
92526
- if (self.autoTreeAreaWidth && (self.noTree || !self.drawTree) && self.labelsWidth) {
92527
- self.setTreeAreaWidth(self.labelsWidth + self.marginLeft + 12 + self.cladeGutterWidth);
92528
- }
92529
- }));
92530
- let pinnedAreaWidth = self.treeWidth === defaultTreeWidth ? void 0 : self.treeAreaWidth;
92531
- addDisposer(self, (0, import_mobx5.autorun)(() => {
92532
- const areaWidth = self.treeAreaWidth;
92533
- const labelsWidth = self.labelsWidth;
92534
- if (pinnedAreaWidth === areaWidth) {
92535
- return;
92536
- }
92537
- pinnedAreaWidth = void 0;
92538
- self.setTreeWidth(Math.max(50, areaWidth - labelsWidth - 10 - self.marginLeft - self.cladeGutterWidth));
92539
- }));
92540
92642
  }
92541
92643
  })).postProcessSnapshot(({ data, columnTracks, ...rest }) => ({
92542
92644
  // stripDefault handles per-property defaults; this drops inline documents
@@ -92580,7 +92682,6 @@ var init_model = __esm({
92580
92682
  init_fetchUtils();
92581
92683
  init_flatToTree();
92582
92684
  init_hierarchy2();
92583
- init_measureTextCanvas();
92584
92685
  init_DataModel();
92585
92686
  init_DialogQueue();
92586
92687
  init_msaModel();
@@ -92594,6 +92695,7 @@ var init_model = __esm({
92594
92695
  init_seqPosToGlobalCol();
92595
92696
  init_sequenceLogo();
92596
92697
  init_stripDefault();
92698
+ init_tipLabels();
92597
92699
  init_util3();
92598
92700
  init_fileSaver();
92599
92701
  init_writeNewick();
@@ -92637,7 +92739,6 @@ var init_model = __esm({
92637
92739
  "drawNodeLabels",
92638
92740
  "showTreeOverview",
92639
92741
  "overviewHeight",
92640
- "autoTreeAreaWidth",
92641
92742
  "turnedOffTracks",
92642
92743
  "trackHeights",
92643
92744
  "hideGaps",
@@ -92692,7 +92793,7 @@ function useModelReaction(model2, key, handler) {
92692
92793
  latest.current?.(value);
92693
92794
  }, { equals: import_mobx6.compareStructural }) : void 0, [model2, key, listening]);
92694
92795
  }
92695
- function Viewer({ msa, tree, gff, msaFilehandle, treeFilehandle, gffFilehandle, colorScheme, height: height2, colWidth, rowHeight, allowedGappyness, highlightColumns, highlights, features, clades, relativeTo, columnTracks, residueMappings, rowData, encodings, rowPanels, drawTree, residueEncoding, treeAreaWidth, autoTreeAreaWidth, showBranchLen, treeOrder, treeRoot, region, selection, hideHeader, theme, onCellHover, onCellClick, onViewportChange, onSelectionChange, onModel }) {
92796
+ function Viewer({ msa, tree, gff, msaFilehandle, treeFilehandle, gffFilehandle, colorScheme, height: height2, colWidth, rowHeight, allowedGappyness, highlightColumns, highlights, features, clades, relativeTo, columnTracks, residueMappings, rowData, encodings, rowPanels, drawTree, residueEncoding, treeAreaWidth, showBranchLen, treeOrder, treeRoot, region, selection, hideHeader, theme, onCellHover, onCellClick, onViewportChange, onSelectionChange, onModel }) {
92696
92797
  const [model2] = (0, import_react96.useState)(() => model_default().create({
92697
92798
  type: "MsaView",
92698
92799
  ...msa || tree || gff || rowData ? {
@@ -92725,7 +92826,6 @@ function Viewer({ msa, tree, gff, msaFilehandle, treeFilehandle, gffFilehandle,
92725
92826
  ...rowPanels ? { rowPanels } : {},
92726
92827
  ...drawTree !== void 0 ? { drawTree } : {},
92727
92828
  ...treeAreaWidth ? { treeAreaWidth } : {},
92728
- ...autoTreeAreaWidth ? { autoTreeAreaWidth } : {},
92729
92829
  ...showBranchLen !== void 0 ? { showBranchLen } : {},
92730
92830
  ...treeOrder ? { treeOrder } : {},
92731
92831
  ...treeRoot ? { treeRoot } : {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-msaview-cli",
3
- "version": "8.4.1",
3
+ "version": "9.0.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {
@@ -20,8 +20,8 @@
20
20
  "@types/node": "^26.6.2",
21
21
  "esbuild": "^0.28.2",
22
22
  "mobx-react": "^10.0.2",
23
- "msa-parsers": "8.4.1",
24
- "react-msaview": "8.4.1"
23
+ "msa-parsers": "9.0.0",
24
+ "react-msaview": "9.0.0"
25
25
  },
26
26
  "dependencies": {
27
27
  "jsdom": "^30.1.1"