archaeopteryx 3.20.0 → 3.21.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.
package/README.md CHANGED
@@ -166,7 +166,7 @@ using `viewer.destroy()` to switch trees in place.
166
166
  ## Using the visualizations
167
167
 
168
168
  A tree opens **already coloured** by its most informative field — the viewer
169
- inspects what the tree carries (taxonomy, sequence fields, custom properties)
169
+ inspects what the tree carries (taxonomy, sequence fields, metadata)
170
170
  and decides by itself what is worth showing. There is nothing to configure.
171
171
 
172
172
  * The **Color** and **Shape** menus (top of the control panel) list every
@@ -1072,7 +1072,7 @@ a popup any more, and nothing fails silently.
1072
1072
  | **Newick** / New Hampshire (`.nwk`, `.nh`, `.tre`) | in / out | The base tree: topology, names, branch lengths, and bracketed confidence values. | [1] |
1073
1073
  | **NHX** — New Hampshire eXtended | in | `[&&NHX:...]` tags riding on Newick: taxonomy (`S=`, `T=`), sequence (`GN=`, `AC=`), support (`B=`), duplication/speciation events (`D=`). | [2] |
1074
1074
  | **Nexus** (`.nex`, `.nexus`) | in / out | One file for the tree(s) and, in a `CHARACTERS`/`DATA` block, an aligned protein/DNA/RNA matrix (sequential or interleaved) — the tree and its alignment together. | [3] |
1075
- | **phyloXML** (`.xml`) | in / out | The richest native format: taxonomy, sequences and alignments, dates, confidences, branch colours and arbitrary custom properties. | [4] |
1075
+ | **phyloXML** (`.xml`) | in / out | The richest native format: taxonomy, sequences and alignments, dates, confidences, branch colours and arbitrary metadata (custom properties). | [4] |
1076
1076
  | **Auspice / Nextstrain** `dataset.json` (v2) | in | Phylodynamic builds: sampling dates and their confidence, cumulative divergence, and discrete traits (country, clade, host, ...) with their posterior distributions. | [5] |
1077
1077
  | **BEAST** / BEAST 2 / TreeAnnotator annotations | in (embedded in Newick/Nexus) | `[&posterior=...,height_95%_HPD={lo,hi},rate=...]`-style blobs: posterior clade support, node-age confidence intervals, per-branch rates and other traits. FigTree's `!color` is read the same way. | [6, 7] |
1078
1078
  | **MrBayes** annotations | in (embedded in Newick/Nexus) | `prob=`/`prob.stddev=` blobs: posterior-probability clade support. | [8] |
@@ -1379,8 +1379,8 @@ copy-pastable JSON.
1379
1379
  | `layout` | `'rectangular'` | The starting layout: `'rectangular'`, `'circular'`, or `'unrooted'`. |
1380
1380
  | `ladderizeTree` | `true` | Ladderize the tree on load: at each node, the larger clade first (any number of children, so a polytomy sorts too). |
1381
1381
  | `showMsa` | tree-derived | Open with the alignment track shown. Default: on when the tree carries an aligned `mol_seq`, off otherwise — an explicit `true`/`false` overrides that. |
1382
- | `showProperties` | tree-derived | Open with the nodes' property values in their labels. Default: off, unless the file's figure setting (`show.SHOW_PROPERTIES=true`) says on. See [Properties in the label](#properties-in-the-label). |
1383
- | `labelProperties` | tree-derived | Which properties the label shows, in order, as an array of refs (`['BVBRC:species', 'BVBRC:host_name']`). Default: the figure setting's `labelprops`, else `null` — every property but the heat map's columns. |
1382
+ | `showProperties` | tree-derived | Open with the nodes' metadata (their phyloXML properties) in their labels — the **Metadata** checkbox. Default: off, unless the file's figure setting (`show.SHOW_PROPERTIES=true`) says on. See [Metadata in the label](#metadata-in-the-label). |
1383
+ | `labelProperties` | tree-derived | Which metadata fields the label shows, in order, as an array of refs (`['BVBRC:species', 'BVBRC:host_name']`). Default: the figure setting's `labelprops`, else `null` — every property but the heat map's columns. |
1384
1384
  | `showMsaLogo` | `false` | Open with the alignment summarised as a sequence logo instead of a conservation bar: a stack of letters per column, as tall as its information content, over the tips currently on screen. Only drawn while the alignment track is shown. |
1385
1385
  | `showHeatmap` | tree-derived | Open with the heat map shown. Offered whenever the tree carries two or more numeric per-tip fields, but off unless asked for — almost any annotated tree has such fields, so turning it on by itself would be an opinion about the tree rather than a service — or unless the file's own figure setting (`aptx:figure`, see [Heat maps](#heat-maps)) draws them as a matrix. |
1386
1386
  | `heatmapColumnOrder` | tree-derived | How the heat map's columns are ordered: `'document'` (as the file lists them), `'clustered'` (Euclidean), `'clustered-presence'` (Bray–Curtis), `'alphabetical'`, `'frequency'`. The clustered modes also draw the dendrogram. Default: a **clustered** order, with the distance chosen from the values — Bray–Curtis where the matrix has zeros to ignore and nothing negative, Euclidean otherwise. An explicit value always wins and is never re-derived. |
@@ -1877,24 +1877,26 @@ rather than 300 identical "Influenz.." labels).
1877
1877
  Both menus live in the single control panel, above Display Data, which is where
1878
1878
  the desktop puts them.
1879
1879
 
1880
- ### Properties in the label
1880
+ ### Metadata in the label
1881
1881
 
1882
1882
  A tip's other fields — a species, a host, a country, anything a node carries
1883
- as a `<property>` — can go into its label, as on the desktop. **Properties**
1884
- under Display Data (offered when nodes carry properties, off by default) adds
1885
- their **values** after the rest of the label, joined by ` | ` as the rest of the
1883
+ as a `<property>` — are its **metadata**, and they can go into its label, as
1884
+ on the desktop. **Metadata** under Display Data (offered when nodes carry
1885
+ any, off by default) adds the **values** after the rest of the label, joined by ` | ` as the rest of the
1886
1886
  label is: `255457.3 | Brucella lupini | host`, or `Brucella lupini` alone with
1887
1887
  Node Name off. (The desktop joins them with `, `; a pipe was chosen here
1888
1888
  because values often contain commas — a host such as `Bottlenose dolphin,
1889
- Tursiops truncatus`.) A unit follows its value. With nothing chosen, every property shows in the
1890
- node's own order, except the heat map's columns while it is shown — a field
1891
- has one display role. **Label fields…** picks which properties, and in what
1892
- order; picking one turns Properties on. The checkbox and the fields travel in
1893
- a shared view (`show=…,properties`, `labelFields=`), and the config keys are
1889
+ Tursiops truncatus`.) A unit follows its value. With nothing chosen, every
1890
+ field shows in the node's own order, except the heat map's columns while it is
1891
+ shown — a field has one display role. **Metadata fields…** picks which fields
1892
+ (tick them; **All** and **None** tick and untick the lot) and in what order
1893
+ (drag a row, or use its arrows); Apply keeps the ticked ones in the order
1894
+ shown, and picking a field turns Metadata on. The checkbox and the fields
1895
+ travel in a shared view (`show=…,properties`, `labelFields=`), and the config keys are
1894
1896
  `showProperties` and `labelProperties`.
1895
1897
 
1896
1898
  A file can choose for itself: the desktop's figure setting (`aptx:figure`,
1897
- see [Heat maps](#heat-maps)) carries `labelprops=` (the properties, in order)
1899
+ see [Heat maps](#heat-maps)) carries `labelprops=` (the fields, in order)
1898
1900
  and the display switches `show.SHOW_PROPERTIES=true` and
1899
1901
  `show.SHOW_NODE_NAMES=false`, and a tree opens labelled that way in both
1900
1902
  programs. The config wins over the file.
@@ -2178,7 +2180,7 @@ child arrangement and `ladderizeTree` rewrites it — measured, one file once ga
2178
2180
  only mode that is **not** normalised first — being left alone is the whole point
2179
2181
  of it. It is set by the **Reorder columns…** dialog, which drags or arrow-keys a
2180
2182
  list of the columns as drawn; applying it writes `heatmapManualOrder` and sets
2181
- the mode, exactly as the desktop's Annotation Fields arrows switch a tab to
2183
+ the mode, exactly as the desktop's Metadata Fields arrows switch a tab to
2182
2184
  Manual. (Theirs also chooses which fields are columns and of what type; ours
2183
2185
  takes every numeric field automatically, so the dialog is about order alone.)
2184
2186
  Choosing Manual from the menu with nothing arranged yet freezes the order on
package/archaeopteryx.js CHANGED
@@ -20,7 +20,7 @@
20
20
  *
21
21
  */
22
22
 
23
- // v 3.20.0
23
+ // v 3.21.0
24
24
  // 2026-09-17
25
25
  //
26
26
  // Archaeopteryx.js is a software tool for the visualization and
@@ -103,7 +103,7 @@ function (root, d3, forester, phyloXml) {
103
103
  // IIFE's own function name -- a plain object says what it is.)
104
104
  let archaeopteryx = {};
105
105
 
106
- const VERSION = '3.20.0';
106
+ const VERSION = '3.21.0';
107
107
  const WEBSITE = 'https://cmzmasek.github.io/archaeopteryx-js/';
108
108
  const DESKTOP_WEBSITE = 'https://cmzmasek.github.io/archaeopteryx/';
109
109
  const SOURCE_WEBSITE = 'https://github.com/cmzmasek/archaeopteryx-js';
@@ -9227,6 +9227,63 @@ function (root, d3, forester, phyloXml) {
9227
9227
  // The same rule as the desktop's resolveEdited, minus its "did the order
9228
9228
  // actually change" test, which it needs only because its dialog does other
9229
9229
  // things too.
9230
+ // Drag a row of a reorder list -- the heat map's columns, the label's
9231
+ // metadata fields -- to a new place. The LIVE node is moved rather than
9232
+ // the list redrawn (a redraw would replace the element the drag is
9233
+ // following), its position read back from the DOM each time, and
9234
+ // `working` moved with it; `draw` runs once the pointer is released. A
9235
+ // press on a control in the row (an arrow, a checkbox) is not a drag.
9236
+ // Shared, so that both dialogs drag the same way: the rows' cursor
9237
+ // promised a drag the fields chooser did not have (Christian, 2026-10-01).
9238
+ function attachReorderDrag(list, row, working, draw) {
9239
+ function rowAt(y) {
9240
+ let rows = Array.prototype.slice.call(list.children);
9241
+ for (let i = 0; i < rows.length; ++i) {
9242
+ let b = rows[i].getBoundingClientRect();
9243
+ if (y < b.top + (b.height / 2)) {
9244
+ return i;
9245
+ }
9246
+ }
9247
+ return rows.length;
9248
+ }
9249
+ row.addEventListener('pointerdown', function (event) {
9250
+ if (event.target.closest('button, input')) {
9251
+ return;
9252
+ }
9253
+ event.preventDefault();
9254
+ row.classList.add('aptx-reorder-dragging');
9255
+ let onMove = function (ev) {
9256
+ let kids = Array.prototype.slice.call(list.children);
9257
+ let from = kids.indexOf(row);
9258
+ let to = rowAt(ev.clientY);
9259
+ if (to > from) {
9260
+ to -= 1; // taking the row out shifts everything after it up one
9261
+ }
9262
+ if (to === from || to < 0 || to >= kids.length) {
9263
+ return;
9264
+ }
9265
+ working.splice(to, 0, working.splice(from, 1)[0]);
9266
+ let others = kids.filter(function (k) {
9267
+ return k !== row;
9268
+ });
9269
+ list.insertBefore(row, others[to] || null);
9270
+ Array.prototype.slice.call(list.children).forEach(function (r, k) {
9271
+ let btns = r.querySelectorAll('.aptx-reorder-move');
9272
+ btns[0].disabled = (k === 0);
9273
+ btns[1].disabled = (k === list.children.length - 1);
9274
+ });
9275
+ };
9276
+ let onUp = function () {
9277
+ row.classList.remove('aptx-reorder-dragging');
9278
+ window.removeEventListener('pointermove', onMove);
9279
+ window.removeEventListener('pointerup', onUp);
9280
+ draw();
9281
+ };
9282
+ window.addEventListener('pointermove', onMove);
9283
+ window.addEventListener('pointerup', onUp);
9284
+ });
9285
+ }
9286
+
9230
9287
  function showHeatmapReorderDialog() {
9231
9288
  let ordered = heatmapOrdered();
9232
9289
  if (!ordered || ordered.columns.length < 2) {
@@ -9239,17 +9296,6 @@ function (root, d3, forester, phyloXml) {
9239
9296
  let list = repsElement(body, 'div', 'aptx-reorder');
9240
9297
  let working = ordered.columns.slice();
9241
9298
 
9242
- function rowAt(y) {
9243
- let rows = Array.prototype.slice.call(list.children);
9244
- for (let i = 0; i < rows.length; ++i) {
9245
- let b = rows[i].getBoundingClientRect();
9246
- if (y < b.top + (b.height / 2)) {
9247
- return i;
9248
- }
9249
- }
9250
- return rows.length;
9251
- }
9252
-
9253
9299
  function move(from, to) {
9254
9300
  if (to < 0 || to >= working.length || from === to) {
9255
9301
  return false;
@@ -9296,49 +9342,7 @@ function (root, d3, forester, phyloXml) {
9296
9342
  list.children[i + d].focus();
9297
9343
  }
9298
9344
  });
9299
- row.addEventListener('pointerdown', function (event) {
9300
- if (event.target.classList.contains('aptx-reorder-move')) {
9301
- return; // the arrows are not a drag handle
9302
- }
9303
- event.preventDefault();
9304
- row.classList.add('aptx-reorder-dragging');
9305
- // The LIVE node is moved rather than the list redrawn: a
9306
- // redraw would replace the element this drag is following.
9307
- // Its position is read back from the DOM each time instead
9308
- // of being tracked in a variable -- the first cut kept an
9309
- // index, updated it, and then used it to work out where to
9310
- // insert, by which point it no longer meant what the
9311
- // calculation assumed.
9312
- let onMove = function (ev) {
9313
- let kids = Array.prototype.slice.call(list.children);
9314
- let from = kids.indexOf(row);
9315
- let to = rowAt(ev.clientY);
9316
- if (to > from) {
9317
- to -= 1; // taking the row out shifts everything after it up one
9318
- }
9319
- if (to === from || to < 0 || to >= kids.length) {
9320
- return;
9321
- }
9322
- working.splice(to, 0, working.splice(from, 1)[0]);
9323
- let others = kids.filter(function (k) {
9324
- return k !== row;
9325
- });
9326
- list.insertBefore(row, others[to] || null);
9327
- Array.prototype.slice.call(list.children).forEach(function (r, k) {
9328
- let btns = r.querySelectorAll('.aptx-reorder-move');
9329
- btns[0].disabled = (k === 0);
9330
- btns[1].disabled = (k === list.children.length - 1);
9331
- });
9332
- };
9333
- let onUp = function () {
9334
- row.classList.remove('aptx-reorder-dragging');
9335
- window.removeEventListener('pointermove', onMove);
9336
- window.removeEventListener('pointerup', onUp);
9337
- draw();
9338
- };
9339
- window.addEventListener('pointermove', onMove);
9340
- window.addEventListener('pointerup', onUp);
9341
- });
9345
+ attachReorderDrag(list, row, working, draw);
9342
9346
  });
9343
9347
  }
9344
9348
  draw();
@@ -13777,7 +13781,7 @@ function (root, d3, forester, phyloXml) {
13777
13781
  }
13778
13782
 
13779
13783
  // Which properties the label shows, and in what order: each field ticked
13780
- // or not, and moved with the arrows (or the arrow keys). Apply keeps the ticked
13784
+ // or not, and moved by dragging, with the arrows, or with the arrow keys. Apply keeps the ticked
13781
13785
  // ones in the order shown; picking a field that was not shown turns the
13782
13786
  // Properties label on, as the desktop does, since otherwise choosing it
13783
13787
  // would visibly do nothing. "All" goes back to every field.
@@ -13810,10 +13814,10 @@ function (root, d3, forester, phyloXml) {
13810
13814
  }).map(function (w) {
13811
13815
  return w.ref;
13812
13816
  });
13813
- let shell = makeDialogShell(LABEL_FIELDS_DIALOG, 'Label fields', 340);
13817
+ let shell = makeDialogShell(LABEL_FIELDS_DIALOG, 'Metadata fields', 340);
13814
13818
  let body = shell.body;
13815
13819
  repsElement(body, 'div', 'aptx-reps-lead',
13816
- 'Tick the properties the labels show, and set their order with the arrows.');
13820
+ 'Tick the metadata fields the labels show; drag a field, or use the arrows, to set their order.');
13817
13821
  let list = repsElement(body, 'div', 'aptx-reorder');
13818
13822
 
13819
13823
  function move(from, to) {
@@ -13830,6 +13834,7 @@ function (root, d3, forester, phyloXml) {
13830
13834
  working.forEach(function (w, i) {
13831
13835
  let row = repsElement(list, 'div', 'aptx-reorder-row');
13832
13836
  row.tabIndex = 0;
13837
+ repsElement(row, 'span', 'aptx-reorder-grip', '≡');
13833
13838
  let box = repsElement(row, 'input', 'aptx-reorder-check');
13834
13839
  box.type = 'checkbox';
13835
13840
  box.checked = w.on;
@@ -13871,15 +13876,26 @@ function (root, d3, forester, phyloXml) {
13871
13876
  list.children[i + d].focus();
13872
13877
  }
13873
13878
  });
13879
+ attachReorderDrag(list, row, working, draw);
13874
13880
  });
13875
13881
  }
13876
13882
  draw();
13877
13883
 
13878
13884
  let actions = repsElement(body, 'div', 'aptx-reps-actions');
13885
+ // All and None tick the boxes and leave the dialog open for Apply --
13886
+ // All used to mean "back to the default" and close at once, which
13887
+ // read as the dialog vanishing (Christian, 2026-10-01)
13888
+ let tickAll = function (on) {
13889
+ working.forEach(function (w) {
13890
+ w.on = on;
13891
+ });
13892
+ draw();
13893
+ };
13879
13894
  repsButton(actions, 'All', false, function () {
13880
- _state.labelProperties = null;
13881
- shell.dialog.close();
13882
- scheduleUpdate();
13895
+ tickAll(true);
13896
+ });
13897
+ repsButton(actions, 'None', false, function () {
13898
+ tickAll(false);
13883
13899
  });
13884
13900
  repsButton(actions, 'Cancel', false, function () {
13885
13901
  shell.dialog.close();
@@ -14100,7 +14116,7 @@ function (root, d3, forester, phyloXml) {
14100
14116
  }
14101
14117
  let part = subtreeExtentNote();
14102
14118
  items.push({heading: part ? 'Tree: ' + part : 'Tree'});
14103
- items.push({label: 'phyloXML', detail: 'keeps everything the tree carries: taxonomy, sequences, dates, properties',
14119
+ items.push({label: 'phyloXML', detail: 'keeps everything the tree carries: taxonomy, sequences, dates, metadata',
14104
14120
  action: entry(PHYLOXML_EXPORT_FORMAT)});
14105
14121
  items.push({label: 'Newick', detail: 'names, branch lengths and support values only',
14106
14122
  action: entry(NH_EXPORT_FORMAT)});
@@ -14111,7 +14127,7 @@ function (root, d3, forester, phyloXml) {
14111
14127
  // Fasta entries) -- on any other tree the download would be empty
14112
14128
  items.push({heading: part ? 'Tip data: ' + part : 'Tip data'});
14113
14129
  items.push({label: 'TSV', detail: 'a table, one row per tip: names, taxonomy, sequence names, '
14114
- + 'branch lengths and every property', action: entry(TSV_EXPORT_FORMAT)});
14130
+ + 'branch lengths and every metadata field', action: entry(TSV_EXPORT_FORMAT)});
14115
14131
  if (_basicTreeProperties && _basicTreeProperties.maxMolSeqLength > 0) {
14116
14132
  items.push({label: 'FASTA', detail: 'every molecular sequence the tips carry',
14117
14133
  action: entry(FASTA_EXPORT_FORMAT)});
@@ -15874,7 +15890,7 @@ function (root, d3, forester, phyloXml) {
15874
15890
  }
15875
15891
  }
15876
15892
  if (rows) {
15877
- text += 'Properties<br>' + rows;
15893
+ text += 'Metadata<br>' + rows;
15878
15894
  }
15879
15895
  }
15880
15896
  return text;
@@ -16290,7 +16306,7 @@ function (root, d3, forester, phyloXml) {
16290
16306
  carries('Events', true, parts.join(', '));
16291
16307
  }
16292
16308
  st.propertyRefs.forEach(function (r) {
16293
- carries('Property ' + r.ref, true, statOf(r.nodes, st.nodes, 'nodes'));
16309
+ carries('Metadata ' + r.ref, true, statOf(r.nodes, st.nodes, 'nodes'));
16294
16310
  });
16295
16311
  if (carried === 0) {
16296
16312
  treePropRow(box, 'Annotations', 'none, names and topology only');
@@ -17152,8 +17168,10 @@ function (root, d3, forester, phyloXml) {
17152
17168
  labels.push(makeCheckboxItem('Sequence', SEQUENCE_CB, 'to show/hide node sequence information'));
17153
17169
  }
17154
17170
  if (labelFields().length > 0) {
17155
- labels.push(makeCheckboxItem('Properties', PROPERTIES_CB, 'to show/hide the nodes\' property values'
17156
- + ' (Label fields… chooses which, and in what order)'));
17171
+ // "Metadata", not phyloXML's "properties" (Christian, 2026-10-01):
17172
+ // the word the users' own tools use for these fields
17173
+ labels.push(makeCheckboxItem('Metadata', PROPERTIES_CB, 'to show/hide the nodes\' metadata (phyloXML'
17174
+ + ' properties: species, host, …) in their labels; Metadata fields… chooses which, and in what order'));
17157
17175
  }
17158
17176
  if (_nodeLabels) {
17159
17177
  for (const [key, value] of Object.entries(_nodeLabels)) {
@@ -17238,9 +17256,9 @@ function (root, d3, forester, phyloXml) {
17238
17256
  h = h.concat(makeCheckboxGroup('Labels', labels));
17239
17257
  if (labelFields().length > 0) {
17240
17258
  h = h.concat('<div class="aptx-domrow"><span class="aptx-domlabel"></span>'
17241
- + '<input type="button" class="aptx-widebtn" value="Label fields…" name="' + LABEL_FIELDS_BUTTON
17242
- + '" id="' + LABEL_FIELDS_BUTTON + '" title="choose which properties the labels show, and in what'
17243
- + ' order"></div>');
17259
+ + '<input type="button" class="aptx-widebtn" value="Metadata fields…" name="' + LABEL_FIELDS_BUTTON
17260
+ + '" id="' + LABEL_FIELDS_BUTTON + '" title="choose which metadata fields the labels show, and in'
17261
+ + ' what order"></div>');
17244
17262
  }
17245
17263
  h = h.concat(makeCheckboxGroup('Nodes', nodes));
17246
17264
  h = h.concat(makeCheckboxGroup('Options', opts));
@@ -17500,12 +17518,12 @@ function (root, d3, forester, phyloXml) {
17500
17518
  h = h.concat('<fieldset>');
17501
17519
  h = h.concat('<legend>Visualizations</legend>');
17502
17520
  if (hasColorVisualizations()) {
17503
- h = h.concat(makeSelectMenu('Color by:', '<br>', LABEL_COLOR_SELECT_MENU, 'colorize the node label and the node itself according to a property'));
17521
+ h = h.concat(makeSelectMenu('Color by:', '<br>', LABEL_COLOR_SELECT_MENU, 'colorize the node label and the node itself by a field the tree carries'));
17504
17522
  h = h.concat('<br>');
17505
17523
  h = h.concat('<br>');
17506
17524
  }
17507
17525
  if (hasShapeVisualizations()) {
17508
- h = h.concat(makeSelectMenu('Shape:', '<br>', NODE_SHAPE_SELECT_MENU, 'change the node shape according to a property'));
17526
+ h = h.concat(makeSelectMenu('Shape:', '<br>', NODE_SHAPE_SELECT_MENU, 'change the node shape by a field the tree carries'));
17509
17527
  h = h.concat('<br>');
17510
17528
  }
17511
17529
  h = h.concat('</fieldset>');
package/forester.js CHANGED
@@ -20,7 +20,7 @@
20
20
  *
21
21
  */
22
22
 
23
- // v 3.20.0
23
+ // v 3.21.0
24
24
  // 2026-09-17
25
25
  //
26
26
  // forester.js is a general suite for dealing with phylogenetic trees.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archaeopteryx",
3
- "version": "3.20.0",
3
+ "version": "3.21.0",
4
4
  "description": "Archaeopteryx.js is a software tool for the visualization and analysis of highly annotated phylogenetic trees.",
5
5
  "main": "archaeopteryx.js",
6
6
  "types": "archaeopteryx.d.ts",