@helping-ai-workflow/md2doc 3.16.1 → 3.17.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/lib/md2doc.js CHANGED
@@ -1993,6 +1993,48 @@ ${edUlMarkerCss}${edOlCounterCss}
1993
1993
  color: var(--md-strong); cursor: pointer; box-shadow: 0 1px 3px var(--md-ed-shadow);
1994
1994
  }
1995
1995
  .ed-wave-edit-btn:hover { background: var(--md-ed-surface); border-color: var(--md-border); }
1996
+ .ed-diagram-edit-btn {
1997
+ position: fixed; z-index: 12; padding: 3px 10px; font-size: 12px;
1998
+ display: inline-flex; align-items: center; gap: 4px;
1999
+ border: 1px solid var(--md-border); border-radius: 4px; background: var(--md-ed-surface);
2000
+ color: var(--md-strong); cursor: pointer; box-shadow: 0 1px 3px var(--md-ed-shadow);
2001
+ }
2002
+ .ed-diagram-edit-btn[hidden] { display: none; }
2003
+ .ed-diagram-edit-btn:hover { background: var(--md-ed-surface); border-color: var(--md-border); }
2004
+ .ed-diagram-edit-btn svg { flex: 0 0 auto; width: 14px; height: 14px; margin: 0; }
2005
+ /* v3.17.0 T5: the diagram panel (openDiagramPanel() in the client runtime).
2006
+ It replaces the block's content in place, inside the block, like the raw
2007
+ box: a 2px accent frame, the header (language, the muted hint, the done
2008
+ button), the source (monospace on the field colour, 50vh at most, then it
2009
+ scrolls), the preview (60vh at most, scrolls both ways) and one message
2010
+ line. A preview kept after an error, or after an answer with nothing to
2011
+ draw, is dimmed (.is-stale). No backticks in this comment: it lives
2012
+ inside a JS template literal. */
2013
+ .ed-diagram-panel {
2014
+ border: 2px solid var(--md-accent); border-radius: 6px; padding: 8px;
2015
+ background: var(--md-ed-surface); color: var(--md-strong);
2016
+ }
2017
+ .ed-diagram-head { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; font-size: 13px; }
2018
+ .ed-diagram-lang { font-weight: 600; }
2019
+ .ed-diagram-hint { flex: 1 1 auto; min-width: 0; color: var(--md-muted); font-size: 12px; }
2020
+ .ed-diagram-done {
2021
+ flex: 0 0 auto; font: inherit; font-size: 13px; padding: 2px 12px; cursor: pointer;
2022
+ border: 1px solid var(--md-border); border-radius: 6px;
2023
+ background: var(--md-ed-surface); color: var(--md-strong);
2024
+ }
2025
+ .ed-diagram-done:hover { background: var(--md-ed-hover); }
2026
+ .ed-diagram-src {
2027
+ display: block; width: 100%; box-sizing: border-box; min-height: 3em; max-height: 50vh; overflow-y: auto;
2028
+ padding: 6px; resize: vertical; tab-size: 2;
2029
+ font-family: "Cascadia Mono", Consolas, ui-monospace, monospace; font-size: 13px;
2030
+ border: 1px solid var(--md-border); border-radius: 6px;
2031
+ background: var(--md-ed-field); color: var(--md-strong);
2032
+ }
2033
+ .ed-diagram-preview { margin-top: 8px; max-height: 60vh; overflow: auto; outline: none; }
2034
+ .ed-diagram-preview svg { display: block; max-width: none; }
2035
+ .ed-diagram-preview.is-stale { opacity: 0.35; }
2036
+ .ed-diagram-error { margin-top: 6px; font-size: 12px; color: var(--md-ed-err); }
2037
+ .ed-diagram-error[data-kind="empty"] { color: var(--md-muted); }
1996
2038
  .ed-wave-overlay {
1997
2039
  position: fixed; inset: 0; z-index: 998; background: rgba(0,0,0,.45);
1998
2040
  display: flex; align-items: center; justify-content: center;
@@ -3520,6 +3562,17 @@ ${usesDrawio ? ` /* v3.4.0 §4: a document with no drawio reference must not pa
3520
3562
  outline: 2px solid var(--md-accent); outline-offset: 2px;
3521
3563
  caret-color: var(--md-accent);
3522
3564
  }
3565
+ /* v3.17.0 T2: a fenced code block edited in place. The pre itself is the
3566
+ editing host (editCodeInPlace() in the client runtime) and takes the
3567
+ same ring as an armed surface, drawn outside its own padding. Code never
3568
+ wraps, also where an engine's contenteditable default would wrap it, and
3569
+ an empty body keeps one line of height to click and type into. */
3570
+ pre[data-ed-code-edit] {
3571
+ white-space: pre; overflow-wrap: normal; word-wrap: normal; line-break: auto;
3572
+ cursor: text; caret-color: var(--md-accent);
3573
+ }
3574
+ pre[data-ed-code-edit]:focus { outline: 2px solid var(--md-accent); outline-offset: 2px; }
3575
+ pre[data-ed-code-edit] > code { display: block; min-height: 1lh; }
3523
3576
  /* v3.14.0 T4: the empty-paragraph hint. syncEmptyMark() in the client sets
3524
3577
  data-ed-empty on a paragraph surface with no text; a real paragraph shows
3525
3578
  the hint while focused, the transient (an empty document's, or one Enter
@@ -3687,6 +3740,53 @@ ${usesDrawio ? ` /* v3.4.0 §4: a document with no drawio reference must not pa
3687
3740
  flex: 0 0 auto; padding: 6px 10px; font-size: 11px; color: var(--md-muted);
3688
3741
  border-top: 1px solid var(--md-ed-ring);
3689
3742
  }
3743
+ /* v3.17.0 T3: the language chip and the copy chip of a fenced code block.
3744
+ Both live in one singleton on document.body (not inside .content: the
3745
+ client reads .content back as its own render), fixed at the top-right of
3746
+ the block the pointer is over or that is being edited. 24px tall, the
3747
+ editor surface with a 1px ring and a 6px radius; the copy chip shows the
3748
+ same check and Copied feedback as the reader button. The language menu
3749
+ is the slash menu look: 220px wide, 320px tall at most, its list scrolls
3750
+ on its own. No backticks in this comment: it lives inside a JS template
3751
+ literal. */
3752
+ .ed-code-chips {
3753
+ position: fixed; z-index: 12; display: flex; align-items: center; gap: 4px;
3754
+ }
3755
+ .ed-code-chips[hidden] { display: none; }
3756
+ .ed-code-chip {
3757
+ display: inline-flex; align-items: center; justify-content: center; gap: 4px;
3758
+ height: 24px; min-width: 24px; padding: 0 6px; margin: 0; box-sizing: border-box;
3759
+ border: 0; border-radius: 6px; background: var(--md-ed-surface); color: var(--md-muted);
3760
+ box-shadow: 0 0 0 1px var(--md-ed-ring);
3761
+ font: inherit; font-size: 12px; line-height: 1; cursor: pointer;
3762
+ }
3763
+ .ed-code-chip:hover, .ed-code-chip[aria-expanded="true"] { background: var(--md-ed-hover); color: var(--md-strong); }
3764
+ .ed-code-chip svg { flex: 0 0 auto; width: 14px; height: 14px; margin: 0; }
3765
+ .ed-code-copy.is-copied { color: var(--md-accent); }
3766
+ .ed-code-langmenu {
3767
+ position: fixed; z-index: 100; width: 220px; max-height: 320px;
3768
+ display: flex; flex-direction: column; box-sizing: border-box; overflow: hidden;
3769
+ border-radius: 8px; background: var(--md-ed-surface); color: var(--md-strong);
3770
+ box-shadow: 0 0 0 1px var(--md-ed-ring), 0 8px 24px var(--md-ed-shadow);
3771
+ font-size: 13px; line-height: 1.3;
3772
+ }
3773
+ .ed-code-langfilter {
3774
+ flex: 0 0 auto; margin: 4px 4px 0; padding: 0 8px; height: 28px; box-sizing: border-box;
3775
+ border: 0; border-radius: 6px; background: var(--md-ed-hover); color: inherit;
3776
+ font: inherit; outline: none;
3777
+ }
3778
+ .ed-code-langfilter::placeholder { color: var(--md-muted); }
3779
+ .ed-code-langlist { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 4px; }
3780
+ .ed-code-langitem {
3781
+ display: flex; align-items: center; gap: 8px; width: 100%; height: 32px;
3782
+ padding: 0 8px; box-sizing: border-box; margin: 0;
3783
+ border: 0; border-radius: 6px; background: transparent; color: inherit;
3784
+ font: inherit; text-align: left; cursor: pointer;
3785
+ }
3786
+ .ed-code-langitem:hover, .ed-code-langitem[data-ed-lang-cursor] { background: var(--md-ed-hover); }
3787
+ .ed-code-langitem span { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
3788
+ .ed-code-langitem svg { flex: 0 0 auto; width: 14px; height: 14px; margin: 0; }
3789
+ .ed-code-langempty { padding: 8px; color: var(--md-muted); }
3690
3790
  /* §10-gap fix: block-level INSERT — the + button sits NEXT TO the ⠿
3691
3791
  handle in the left gutter, same node/visibility-toggle shape as
3692
3792
  .ed-handle above (real per-block node, opacity-only reveal on hover).
@@ -3991,6 +4091,32 @@ ${usesDrawio ? ` /* v3.4.0 §4: a document with no drawio reference must not pa
3991
4091
  background: transparent; color: inherit; font: inherit; font-size: 13px; cursor: pointer;
3992
4092
  }
3993
4093
  .ed-linkcard-btn:hover { background: var(--md-ed-hover); }
4094
+ /* v3.17.0 T7: the image toolbar, 8px inside the top-left of the image the
4095
+ pointer is over (a fixed singleton on body, like the link card; the same
4096
+ surface, ring and shadow roles). 刪除 is the error colour. In its alt-text
4097
+ mode the buttons give way to one input. */
4098
+ .ed-imgtb {
4099
+ position: fixed; z-index: 15;
4100
+ display: flex; align-items: center; gap: 2px; box-sizing: border-box;
4101
+ max-width: calc(100vw - 8px); padding: 3px; border-radius: 8px;
4102
+ background: var(--md-ed-surface); color: var(--md-strong); font-size: 13px;
4103
+ box-shadow: 0 0 0 1px var(--md-ed-ring), 0 8px 24px var(--md-ed-shadow);
4104
+ }
4105
+ .ed-imgtb[hidden] { display: none; }
4106
+ .ed-imgtb-btns { display: flex; align-items: center; gap: 2px; }
4107
+ .ed-imgtb-btns[hidden], .ed-imgtb-input[hidden] { display: none; }
4108
+ .ed-imgtb-btn {
4109
+ flex: none; height: 26px; padding: 0 8px; border: 0; border-radius: 6px;
4110
+ background: transparent; color: inherit; font: inherit; font-size: 13px; cursor: pointer;
4111
+ }
4112
+ .ed-imgtb-btn:hover { background: var(--md-ed-hover); }
4113
+ .ed-imgtb-delete { color: var(--md-ed-err); }
4114
+ .ed-imgtb-input {
4115
+ flex: 1 1 auto; width: 260px; max-width: calc(100vw - 24px); min-width: 0; height: 28px;
4116
+ box-sizing: border-box; padding: 0 8px; border: 1px solid var(--md-ed-ring); border-radius: 6px;
4117
+ background: var(--md-ed-field); color: inherit; font: inherit; font-size: 13px;
4118
+ }
4119
+ .ed-imgtb-input:focus { outline: none; border-color: var(--md-muted); }
3994
4120
  /* v3.1.0 §4: the always-visible editing toolbar. Its numeric language is
3995
4121
  .lightbox-bar's (8px/12px padding, 6px gap, 13px type) over .ed-seltb's
3996
4122
  dark pill, so the two floating surfaces read as one family.
@@ -6652,7 +6778,7 @@ ${usesDrawioMultiPage ? ` // v3.4.0 batch2 Task 6: wired through a NAMED, reusa
6652
6778
  // server's on-demand GET /api/lib/<name> serves exactly what this file inlines
6653
6779
  // instead of resolving them a second time.
6654
6780
  const diagramEnginePaths = { mermaid: localMermaidJs, wavedrom: localWaveDromJs, wavedromSkin: localWaveDromSkin };
6655
- module.exports = { renderMarkdown, buildKatexStyleTag, diagramEnginePaths };
6781
+ module.exports = { renderMarkdown, buildKatexStyleTag, diagramEnginePaths, renderDotSvg };
6656
6782
 
6657
6783
  // Index of the first "{" in dot source that opens the graph body: not inside a
6658
6784
  // double-quoted string, a // or /* */ comment, a line starting with # (dot
@@ -6690,59 +6816,83 @@ function firstDotBrace(src) {
6690
6816
  return -1;
6691
6817
  }
6692
6818
 
6819
+ // One dot path for the bake and the editor preview. The loaded Graphviz
6820
+ // instance is cached at module scope (the library does not memoise load()).
6821
+ let graphvizLoading = null;
6822
+ function loadGraphviz() {
6823
+ if (!graphvizLoading) {
6824
+ const { Graphviz } = require('@hpcc-js/wasm-graphviz');
6825
+ graphvizLoading = Graphviz.load();
6826
+ // A failed load must not poison the cache: the next call tries again.
6827
+ graphvizLoading.catch(() => { graphvizLoading = null; });
6828
+ }
6829
+ return graphvizLoading;
6830
+ }
6831
+ async function renderDotSvg(dotSrc) {
6832
+ const gv = await loadGraphviz();
6833
+ // Graphviz measures label widths with the font it lays out in, and with
6834
+ // no fontname that is Times, whose metrics are narrower than the sans
6835
+ // the SVG is drawn in -- so labels overflowed their boxes. Declare
6836
+ // Helvetica BEFORE layout, right after the first brace; a fontname the
6837
+ // author writes later in the source still wins. Exactly "Helvetica":
6838
+ // a "Helvetica,sans-Serif" family list falls back to Times widths.
6839
+ const at = firstDotBrace(dotSrc);
6840
+ const laidOut = at < 0 ? dotSrc : dotSrc.slice(0, at + 1) +
6841
+ ' graph [fontname="Helvetica"]; node [fontname="Helvetica"]; edge [fontname="Helvetica"]; ' + dotSrc.slice(at + 1);
6842
+ let laid;
6843
+ try {
6844
+ laid = gv.dot(laidOut);
6845
+ } catch (injectErr) {
6846
+ // Injection must never turn a valid graph into an error: lay the
6847
+ // original source out as the author wrote it.
6848
+ if (laidOut === dotSrc) throw injectErr;
6849
+ laid = gv.dot(dotSrc);
6850
+ }
6851
+ return laid
6852
+ .replace(/<\?xml[^>]*\?>/g, '')
6853
+ .replace(/<!DOCTYPE[\s\S]*?>/g, '')
6854
+ // drop graphviz's "Generated by graphviz" banner comment so the div and <svg> are adjacent
6855
+ .replace(/<!--[\s\S]*?-->/g, '')
6856
+ // v3.8.0: graphviz falls back to Times when no fontname is set. The
6857
+ // injection above makes Helvetica the layout font, so this rewrite
6858
+ // now only touches an author's explicit Times-Roman, or a figure
6859
+ // whose injection fell back to the original source; other explicit
6860
+ // fontnames are untouched.
6861
+ .replace(/font-family="Times,serif"/g, 'font-family="Helvetica,sans-Serif"')
6862
+ .trim();
6863
+ }
6864
+
6693
6865
  // Render every deferred dot/graphviz placeholder to inline SVG using the
6694
6866
  // in-process WASM engine. Loads the WASM module only when at least one dot
6695
6867
  // block exists, so text-only docs pay nothing.
6696
6868
  async function bakeGraphviz(htmlStr) {
6697
6869
  if (!GRAPHVIZ_PLACEHOLDER.probe.test(htmlStr)) return htmlStr;
6698
- let Graphviz;
6699
6870
  try {
6700
- ({ Graphviz } = require('@hpcc-js/wasm-graphviz'));
6871
+ require('@hpcc-js/wasm-graphviz');
6701
6872
  } catch (e) {
6702
6873
  console.error('[ERROR] @hpcc-js/wasm-graphviz not installed — run `npm install`:', e.message);
6703
6874
  return htmlStr;
6704
6875
  }
6705
- const gv = await Graphviz.load();
6706
- return htmlStr.replace(GRAPHVIZ_PLACEHOLDER.re, (m, b64) => {
6707
- const dotSrc = Buffer.from(b64, 'base64').toString('utf8');
6876
+ // A WASM load failure throws, as it always did; only a per-block render
6877
+ // failure degrades that block to pre/code.
6878
+ await loadGraphviz();
6879
+ const jobs = [];
6880
+ htmlStr.replace(GRAPHVIZ_PLACEHOLDER.re, (m, b64) => {
6881
+ jobs.push(Buffer.from(b64, 'base64').toString('utf8'));
6882
+ return m;
6883
+ });
6884
+ const out = [];
6885
+ for (const dotSrc of jobs) {
6708
6886
  try {
6709
- // Graphviz measures label widths with the font it lays out in, and with
6710
- // no fontname that is Times, whose metrics are narrower than the sans
6711
- // the SVG is drawn in -- so labels overflowed their boxes. Declare
6712
- // Helvetica BEFORE layout, right after the first brace; a fontname the
6713
- // author writes later in the source still wins. Exactly "Helvetica":
6714
- // a "Helvetica,sans-Serif" family list falls back to Times widths.
6715
- const at = firstDotBrace(dotSrc);
6716
- const laidOut = at < 0 ? dotSrc : dotSrc.slice(0, at + 1) +
6717
- ' graph [fontname="Helvetica"]; node [fontname="Helvetica"]; edge [fontname="Helvetica"]; ' + dotSrc.slice(at + 1);
6718
- let laid;
6719
- try {
6720
- laid = gv.dot(laidOut);
6721
- } catch (injectErr) {
6722
- // Injection must never turn a valid graph into an error: lay the
6723
- // original source out as the author wrote it.
6724
- if (laidOut === dotSrc) throw injectErr;
6725
- laid = gv.dot(dotSrc);
6726
- }
6727
- const svg = laid
6728
- .replace(/<\?xml[^>]*\?>/g, '')
6729
- .replace(/<!DOCTYPE[\s\S]*?>/g, '')
6730
- // drop graphviz's "Generated by graphviz" banner comment so the div and <svg> are adjacent
6731
- .replace(/<!--[\s\S]*?-->/g, '')
6732
- // v3.8.0: graphviz falls back to Times when no fontname is set. The
6733
- // injection above makes Helvetica the layout font, so this rewrite
6734
- // now only touches an author's explicit Times-Roman, or a figure
6735
- // whose injection fell back to the original source; other explicit
6736
- // fontnames are untouched.
6737
- .replace(/font-family="Times,serif"/g, 'font-family="Helvetica,sans-Serif"')
6738
- .trim();
6739
- return `<div class="graphviz">${svg}</div>`;
6887
+ out.push(`<div class="graphviz">${await renderDotSvg(dotSrc)}</div>`);
6740
6888
  } catch (e) {
6741
6889
  console.error('[WARN] graphviz render failed:', e.message);
6742
6890
  const escaped = dotSrc.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
6743
- return `<pre><code class="language-dot">${escaped}</code></pre>\n`;
6891
+ out.push(`<pre><code class="language-dot">${escaped}</code></pre>\n`);
6744
6892
  }
6745
- });
6893
+ }
6894
+ let i = 0;
6895
+ return htmlStr.replace(GRAPHVIZ_PLACEHOLDER.re, () => out[i++]);
6746
6896
  }
6747
6897
 
6748
6898
  function launchBrowser(puppeteer) {
@@ -22,7 +22,9 @@
22
22
  if (at !== -1) listeners.splice(at, 1);
23
23
  };
24
24
  }
25
- function notify(dark) { for (var i = 0; i < listeners.length; i++) listeners[i](dark); }
25
+ // Over a copy: a listener may unsubscribe itself (or another) while it
26
+ // runs, and a splice under the loop would skip the next one.
27
+ function notify(dark) { var ls = listeners.slice(); for (var i = 0; i < ls.length; i++) ls[i](dark); }
26
28
  function isDark() { return root.getAttribute('data-md2doc-theme') === 'dark'; }
27
29
  function save(dark) {
28
30
  try {
@@ -228,6 +230,35 @@
228
230
  return true;
229
231
  };
230
232
 
233
+ // v3.17.0 (D13): one off-page render for the editor's diagram panel preview.
234
+ // It waits its turn on the same chain, because mermaid has ONE global config
235
+ // and a render beside a light / dark draw could be drawn with the other's
236
+ // theme. Its turn initialises the theme on screen at that moment, renders
237
+ // under a fresh id, and puts the light config back after (the invariant
238
+ // every draw here keeps). Resolves the svg text; rejects with mermaid's
239
+ // error, its stray #d<id> element removed. Counted in inFlight like the
240
+ // draws, so a toggle to a cached theme waits behind it too.
241
+ var previewSeq = 0;
242
+ function renderMermaid(src) {
243
+ if (typeof window.mermaid === 'undefined') return Promise.reject(new Error('mermaid is not loaded'));
244
+ inFlight++;
245
+ var run = chain.then(function () {
246
+ var id = 'md2doc-mermaid-preview-' + (++previewSeq);
247
+ window.mermaid.initialize(mermaidConfig(isDark() ? 'dark' : 'light'));
248
+ return Promise.resolve().then(function () { return window.mermaid.render(id, String(src)); }).then(function (res) {
249
+ window.mermaid.initialize(mermaidConfig('light'));
250
+ return res.svg;
251
+ }, function (e) {
252
+ var stray = document.getElementById('d' + id);
253
+ if (stray && stray.parentNode) stray.parentNode.removeChild(stray);
254
+ window.mermaid.initialize(mermaidConfig('light'));
255
+ throw e;
256
+ });
257
+ });
258
+ chain = run.catch(function () { /* the caller shows the error */ }).then(function () { inFlight--; });
259
+ return run;
260
+ }
261
+
231
262
  // ── graphviz / WaveDrom / baked SVG: recolour by computed colour ──
232
263
  // Rules: spec "Diagram recolour rules (D10)". Reading COMPUTED fill/stroke
233
264
  // covers both graphviz presentation attributes and WaveDrom's own <style>.
@@ -617,6 +648,6 @@
617
648
  // Public surface for pages that draw their own SVG outside .content (the wave editor canvas).
618
649
  // `__listenerCount` is a test hook (test/editor-click.test.js): the wave
619
650
  // editor subscribes once per open and must unsubscribe on close.
620
- window.md2docTheme = { isDark: isDark, onTheme: onTheme, recolourSvg: recolourSvg,
651
+ window.md2docTheme = { isDark: isDark, onTheme: onTheme, recolourSvg: recolourSvg, renderMermaid: renderMermaid,
621
652
  __listenerCount: function () { return listeners.length; } };
622
653
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helping-ai-workflow/md2doc",
3
- "version": "3.16.1",
3
+ "version": "3.17.0",
4
4
  "description": "Markdown → HTML / PDF renderer with WaveDrom, Mermaid, Graphviz, and draw.io support",
5
5
  "keywords": [
6
6
  "markdown",
@@ -46,7 +46,7 @@
46
46
  },
47
47
  "scripts": {
48
48
  "preinstall": "node scripts/preinstall.js",
49
- "test": "node test/md2doc.test.js && node test/highlight.test.js && node test/blockmap.test.js && node test/drawio.test.js && node test/images.test.js && node test/scroll-anchor.test.js && node test/lightbox.test.js && node test/lightbox-anno.test.js && node test/lightbox-anno-style.test.js && node test/reader-panels.test.js && node test/cli.test.js && node test/code-operator.test.js && node test/convert-md.test.js && node test/block-catalog.test.js && node test/render-api.test.js && node test/lineops.test.js && node test/indent-clamp.test.js && node test/selection.test.js && node test/cell-range.test.js && node test/editmode-render.test.js && node test/editmode-wavedrom-reinit.test.js && node test/editor-server.test.js && node test/cli-edit.test.js && node test/open-viewer.test.js && node test/editor-client.test.js && node test/editor-client-runtime.test.js && node test/roundtrip.test.js && node test/byte-stability.test.js && node test/editor-server-throw.test.js && node test/editor-reader-rebind.test.js && node test/inline-md.test.js && node test/table-md.test.js && node test/table-anchor-recovery.test.js && node test/gate-compat.test.js && node test/history.test.js && node test/list-md.test.js && node test/table-width.test.js && node test/toolbar-model.test.js && node test/asset.test.js && node test/paste-md.test.js && node test/patchmap.test.js && node test/wave-codec.test.js && node test/wave-draw.test.js && node test/wave-geometry.test.js && node test/wave-store.test.js && node test/editor-journey.test.js && node test/detach-census.test.js && node test/editor-arrow-nav.test.js && node test/editor-cell-range.test.js && node test/reader-design.test.js && node test/theme.test.js && node test/release-check.test.js && node test/wave-overflow.test.js",
49
+ "test": "node test/md2doc.test.js && node test/highlight.test.js && node test/blockmap.test.js && node test/drawio.test.js && node test/images.test.js && node test/scroll-anchor.test.js && node test/lightbox.test.js && node test/lightbox-anno.test.js && node test/lightbox-anno-style.test.js && node test/reader-panels.test.js && node test/cli.test.js && node test/code-operator.test.js && node test/convert-md.test.js && node test/block-catalog.test.js && node test/code-edit.test.js && node test/render-api.test.js && node test/lineops.test.js && node test/indent-clamp.test.js && node test/selection.test.js && node test/cell-range.test.js && node test/editmode-render.test.js && node test/editmode-wavedrom-reinit.test.js && node test/editor-server.test.js && node test/cli-edit.test.js && node test/open-viewer.test.js && node test/editor-client.test.js && node test/editor-client-runtime.test.js && node test/roundtrip.test.js && node test/byte-stability.test.js && node test/editor-server-throw.test.js && node test/editor-reader-rebind.test.js && node test/inline-md.test.js && node test/table-md.test.js && node test/table-anchor-recovery.test.js && node test/gate-compat.test.js && node test/history.test.js && node test/list-md.test.js && node test/table-width.test.js && node test/toolbar-model.test.js && node test/asset.test.js && node test/paste-md.test.js && node test/patchmap.test.js && node test/wave-codec.test.js && node test/wave-draw.test.js && node test/wave-geometry.test.js && node test/wave-store.test.js && node test/editor-journey.test.js && node test/detach-census.test.js && node test/editor-arrow-nav.test.js && node test/editor-cell-range.test.js && node test/reader-design.test.js && node test/theme.test.js && node test/theme-runtime.test.js && node test/release-check.test.js && node test/wave-overflow.test.js",
50
50
  "test:browser": "node test/reader-click.test.js && node test/editor-click.test.js && node test/wave-canvas.test.js"
51
51
  },
52
52
  "repository": {