@helping-ai-workflow/md2doc 2.5.0 → 2.6.1
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 +138 -21
- package/package.json +2 -2
package/lib/md2doc.js
CHANGED
|
@@ -706,7 +706,7 @@ ${itemsHtml}
|
|
|
706
706
|
const tocTree = buildTocTree(tocItems);
|
|
707
707
|
tocHtml = `<aside class="reader-sidebar" data-reader-sidebar>
|
|
708
708
|
<section class="reader-tools">
|
|
709
|
-
<label class="reader-search-label" for="doc-search-input">Search
|
|
709
|
+
<label class="reader-search-label" for="doc-search-input">Search</label>
|
|
710
710
|
<div class="reader-search-row">
|
|
711
711
|
<input type="search" id="doc-search-input" placeholder="Enter keyword and press Enter">
|
|
712
712
|
<button id="doc-search-submit" type="button">Search</button>
|
|
@@ -733,7 +733,8 @@ ${itemsHtml}
|
|
|
733
733
|
</div>
|
|
734
734
|
${renderTocNodes(tocTree)}
|
|
735
735
|
</nav>
|
|
736
|
-
</aside
|
|
736
|
+
</aside>
|
|
737
|
+
<div class="sidebar-splitter" id="sidebar-splitter" role="separator" aria-orientation="vertical" aria-label="Resize sidebar"></div>`;
|
|
737
738
|
}
|
|
738
739
|
} catch (e) {
|
|
739
740
|
console.error('[ERROR] marked not found — install with: npm install marked');
|
|
@@ -765,7 +766,9 @@ const html = `<!DOCTYPE html>
|
|
|
765
766
|
.page-layout {
|
|
766
767
|
display: flex;
|
|
767
768
|
align-items: flex-start;
|
|
768
|
-
|
|
769
|
+
/* Sidebar/content spacing is carried by .sidebar-splitter (32px), not gap,
|
|
770
|
+
so the drag handle can live inside the visual gutter. */
|
|
771
|
+
gap: 0;
|
|
769
772
|
margin: 0;
|
|
770
773
|
padding: 24px 24px 48px;
|
|
771
774
|
max-width: 100%;
|
|
@@ -774,9 +777,9 @@ const html = `<!DOCTYPE html>
|
|
|
774
777
|
.reader-sidebar {
|
|
775
778
|
position: sticky;
|
|
776
779
|
top: 24px;
|
|
777
|
-
flex: 0
|
|
778
|
-
width: clamp(220px, 22vw, 300px);
|
|
779
|
-
min-width:
|
|
780
|
+
flex: 0 0 auto;
|
|
781
|
+
width: var(--md2doc-sidebar-w, clamp(220px, 22vw, 300px));
|
|
782
|
+
min-width: 0;
|
|
780
783
|
height: calc(100vh - 48px);
|
|
781
784
|
overflow: hidden;
|
|
782
785
|
padding-right: 8px;
|
|
@@ -817,6 +820,32 @@ const html = `<!DOCTYPE html>
|
|
|
817
820
|
body[data-toc-collapsed] .toc-header-actions {
|
|
818
821
|
justify-content: center;
|
|
819
822
|
}
|
|
823
|
+
.sidebar-splitter {
|
|
824
|
+
flex: 0 0 32px;
|
|
825
|
+
align-self: stretch;
|
|
826
|
+
cursor: col-resize;
|
|
827
|
+
display: flex;
|
|
828
|
+
justify-content: center;
|
|
829
|
+
touch-action: none;
|
|
830
|
+
user-select: none;
|
|
831
|
+
}
|
|
832
|
+
.sidebar-splitter::before {
|
|
833
|
+
content: '';
|
|
834
|
+
width: 3px;
|
|
835
|
+
border-radius: 2px;
|
|
836
|
+
background: transparent;
|
|
837
|
+
transition: background 0.15s ease;
|
|
838
|
+
}
|
|
839
|
+
.sidebar-splitter:hover::before,
|
|
840
|
+
.sidebar-splitter.is-dragging::before {
|
|
841
|
+
background: #93c5fd;
|
|
842
|
+
}
|
|
843
|
+
body[data-toc-collapsed] .sidebar-splitter {
|
|
844
|
+
cursor: default;
|
|
845
|
+
}
|
|
846
|
+
body[data-toc-collapsed] .sidebar-splitter::before {
|
|
847
|
+
display: none;
|
|
848
|
+
}
|
|
820
849
|
#toc-collapse-toggle {
|
|
821
850
|
margin-left: 0;
|
|
822
851
|
padding: 2px 8px;
|
|
@@ -1032,6 +1061,16 @@ const html = `<!DOCTYPE html>
|
|
|
1032
1061
|
font-size: 0.82em;
|
|
1033
1062
|
color: #57606a;
|
|
1034
1063
|
line-height: 1.35;
|
|
1064
|
+
display: -webkit-box;
|
|
1065
|
+
-webkit-line-clamp: 2;
|
|
1066
|
+
-webkit-box-orient: vertical;
|
|
1067
|
+
overflow: hidden;
|
|
1068
|
+
}
|
|
1069
|
+
.search-result-snippet mark {
|
|
1070
|
+
background: #fde68a;
|
|
1071
|
+
color: inherit;
|
|
1072
|
+
padding: 0 1px;
|
|
1073
|
+
border-radius: 2px;
|
|
1035
1074
|
}
|
|
1036
1075
|
.search-empty {
|
|
1037
1076
|
margin: 0;
|
|
@@ -1056,6 +1095,10 @@ const html = `<!DOCTYPE html>
|
|
|
1056
1095
|
flex: 1 1 auto;
|
|
1057
1096
|
min-height: 0;
|
|
1058
1097
|
overflow-y: auto;
|
|
1098
|
+
/* Horizontal peek: thin native scrollbar (drag/track-click/touchpad) plus
|
|
1099
|
+
the shift+wheel handler; appears only when a title actually overflows. */
|
|
1100
|
+
overflow-x: auto;
|
|
1101
|
+
scrollbar-width: thin;
|
|
1059
1102
|
}
|
|
1060
1103
|
.toc ul {
|
|
1061
1104
|
list-style: none;
|
|
@@ -1074,21 +1117,19 @@ const html = `<!DOCTYPE html>
|
|
|
1074
1117
|
}
|
|
1075
1118
|
.toc a {
|
|
1076
1119
|
display: block;
|
|
1077
|
-
|
|
1120
|
+
/* Full natural width (no ellipsis) so shift+wheel can peek clipped tails;
|
|
1121
|
+
min-width keeps hover/match backgrounds covering the whole row. */
|
|
1122
|
+
width: max-content;
|
|
1123
|
+
min-width: 100%;
|
|
1078
1124
|
color: #57606a;
|
|
1079
1125
|
text-decoration: none;
|
|
1080
1126
|
padding: 2px 0;
|
|
1081
1127
|
line-height: 1.4;
|
|
1082
1128
|
white-space: nowrap;
|
|
1083
|
-
overflow: hidden;
|
|
1084
|
-
text-overflow: ellipsis;
|
|
1085
1129
|
box-sizing: border-box;
|
|
1086
1130
|
}
|
|
1087
|
-
/* Parent rows put the anchor inside a flex <summary>; default flex min-width
|
|
1088
|
-
is auto, which would block ellipsis. Allow the anchor to shrink + clip. */
|
|
1089
1131
|
.toc summary > a {
|
|
1090
|
-
|
|
1091
|
-
flex: 0 1 auto;
|
|
1132
|
+
flex: 0 0 auto;
|
|
1092
1133
|
}
|
|
1093
1134
|
.toc summary {
|
|
1094
1135
|
min-width: 0;
|
|
@@ -1387,6 +1428,7 @@ const html = `<!DOCTYPE html>
|
|
|
1387
1428
|
flex: initial;
|
|
1388
1429
|
}
|
|
1389
1430
|
body[data-sidebar-open] .reader-sidebar { transform: translateX(0); }
|
|
1431
|
+
.sidebar-splitter { display: none; }
|
|
1390
1432
|
.heading-anchor { opacity: 1; }
|
|
1391
1433
|
}
|
|
1392
1434
|
@media print {
|
|
@@ -1399,6 +1441,7 @@ const html = `<!DOCTYPE html>
|
|
|
1399
1441
|
}
|
|
1400
1442
|
.reader-sidebar,
|
|
1401
1443
|
.sidebar-toggle,
|
|
1444
|
+
.sidebar-splitter,
|
|
1402
1445
|
.sidebar-scrim { display: none !important; }
|
|
1403
1446
|
.content { max-width: 100%; }
|
|
1404
1447
|
.heading-anchor { display: none; }
|
|
@@ -1615,14 +1658,24 @@ ${mermaidInitTag}` : ''}
|
|
|
1615
1658
|
const lower = haystack.toLowerCase();
|
|
1616
1659
|
const index = lower.indexOf(query);
|
|
1617
1660
|
if (index === -1) {
|
|
1618
|
-
return haystack.slice(0,
|
|
1661
|
+
return { text: haystack.slice(0, 80), matchStart: -1, matchLength: 0 };
|
|
1619
1662
|
}
|
|
1620
|
-
const start = Math.max(0, index -
|
|
1621
|
-
const end = Math.min(haystack.length, index + query.length +
|
|
1622
|
-
let
|
|
1623
|
-
|
|
1624
|
-
if (
|
|
1625
|
-
|
|
1663
|
+
const start = Math.max(0, index - 25);
|
|
1664
|
+
const end = Math.min(haystack.length, index + query.length + 45);
|
|
1665
|
+
let text = haystack.slice(start, end);
|
|
1666
|
+
let matchStart = index - start;
|
|
1667
|
+
if (start > 0) { text = '…' + text; matchStart += 1; }
|
|
1668
|
+
if (end < haystack.length) text += '…';
|
|
1669
|
+
return { text, matchStart, matchLength: query.length };
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
function renderSnippetHtml(snippet) {
|
|
1673
|
+
if (!snippet) return '';
|
|
1674
|
+
if (snippet.matchStart < 0) return escapeHtml(snippet.text);
|
|
1675
|
+
const before = snippet.text.slice(0, snippet.matchStart);
|
|
1676
|
+
const hit = snippet.text.slice(snippet.matchStart, snippet.matchStart + snippet.matchLength);
|
|
1677
|
+
const after = snippet.text.slice(snippet.matchStart + snippet.matchLength);
|
|
1678
|
+
return escapeHtml(before) + '<mark>' + escapeHtml(hit) + '</mark>' + escapeHtml(after);
|
|
1626
1679
|
}
|
|
1627
1680
|
|
|
1628
1681
|
function clearMatchedTocState() {
|
|
@@ -1684,7 +1737,7 @@ ${mermaidInitTag}` : ''}
|
|
|
1684
1737
|
list.innerHTML = readerState.results.map((result, index) => (
|
|
1685
1738
|
'<button class="search-result-item' + (index === readerState.selectedResultIndex ? ' is-active' : '') + '" data-result-index="' + index + '" type="button">'
|
|
1686
1739
|
+ '<span class="search-result-title">' + escapeHtml(result.title) + '</span>'
|
|
1687
|
-
+ '<span class="search-result-snippet">' +
|
|
1740
|
+
+ '<span class="search-result-snippet">' + renderSnippetHtml(result.snippet) + '</span>'
|
|
1688
1741
|
+ '</button>'
|
|
1689
1742
|
)).join('');
|
|
1690
1743
|
}
|
|
@@ -1946,6 +1999,70 @@ ${mermaidInitTag}` : ''}
|
|
|
1946
1999
|
}
|
|
1947
2000
|
});
|
|
1948
2001
|
|
|
2002
|
+
// ── Sidebar splitter: drag to resize, persisted; double-click resets ─────
|
|
2003
|
+
const sidebarEl = document.querySelector('.reader-sidebar');
|
|
2004
|
+
const splitterEl = document.getElementById('sidebar-splitter');
|
|
2005
|
+
const SIDEBAR_WIDTH_KEY = 'md2doc.sidebar.width';
|
|
2006
|
+
function clampSidebarWidth(px) {
|
|
2007
|
+
return Math.min(Math.max(px, 180), Math.round(window.innerWidth * 0.5));
|
|
2008
|
+
}
|
|
2009
|
+
function applySidebarWidth(px) {
|
|
2010
|
+
document.documentElement.style.setProperty('--md2doc-sidebar-w', px + 'px');
|
|
2011
|
+
}
|
|
2012
|
+
try {
|
|
2013
|
+
const storedWidth = parseInt(localStorage.getItem(SIDEBAR_WIDTH_KEY), 10);
|
|
2014
|
+
if (Number.isFinite(storedWidth)) applySidebarWidth(clampSidebarWidth(storedWidth));
|
|
2015
|
+
} catch (e) { /* storage unavailable */ }
|
|
2016
|
+
if (splitterEl && sidebarEl) {
|
|
2017
|
+
let dragging = false;
|
|
2018
|
+
let dragMoved = false;
|
|
2019
|
+
let dragStartX = 0;
|
|
2020
|
+
let dragStartWidth = 0;
|
|
2021
|
+
splitterEl.addEventListener('pointerdown', (event) => {
|
|
2022
|
+
if (document.body.hasAttribute('data-toc-collapsed')) return;
|
|
2023
|
+
dragging = true;
|
|
2024
|
+
dragMoved = false;
|
|
2025
|
+
dragStartX = event.clientX;
|
|
2026
|
+
dragStartWidth = sidebarEl.offsetWidth;
|
|
2027
|
+
splitterEl.classList.add('is-dragging');
|
|
2028
|
+
if (splitterEl.setPointerCapture) splitterEl.setPointerCapture(event.pointerId);
|
|
2029
|
+
event.preventDefault();
|
|
2030
|
+
});
|
|
2031
|
+
splitterEl.addEventListener('pointermove', (event) => {
|
|
2032
|
+
if (!dragging) return;
|
|
2033
|
+
const delta = event.clientX - dragStartX;
|
|
2034
|
+
if (Math.abs(delta) >= 3) dragMoved = true;
|
|
2035
|
+
if (dragMoved) applySidebarWidth(clampSidebarWidth(dragStartWidth + delta));
|
|
2036
|
+
});
|
|
2037
|
+
function endSidebarDrag() {
|
|
2038
|
+
if (!dragging) return;
|
|
2039
|
+
dragging = false;
|
|
2040
|
+
splitterEl.classList.remove('is-dragging');
|
|
2041
|
+
if (!dragMoved) return;
|
|
2042
|
+
try { localStorage.setItem(SIDEBAR_WIDTH_KEY, String(sidebarEl.offsetWidth)); }
|
|
2043
|
+
catch (e) { /* storage unavailable */ }
|
|
2044
|
+
}
|
|
2045
|
+
splitterEl.addEventListener('pointerup', endSidebarDrag);
|
|
2046
|
+
splitterEl.addEventListener('pointercancel', endSidebarDrag);
|
|
2047
|
+
splitterEl.addEventListener('dblclick', () => {
|
|
2048
|
+
document.documentElement.style.removeProperty('--md2doc-sidebar-w');
|
|
2049
|
+
try { localStorage.removeItem(SIDEBAR_WIDTH_KEY); }
|
|
2050
|
+
catch (e) { /* storage unavailable */ }
|
|
2051
|
+
});
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
// ── TOC horizontal peek: shift+wheel scrolls a hidden-overflow x-axis ─────
|
|
2055
|
+
const tocScrollEl = document.querySelector('.toc > .toc-list');
|
|
2056
|
+
if (tocScrollEl) {
|
|
2057
|
+
tocScrollEl.addEventListener('wheel', (event) => {
|
|
2058
|
+
if (!event.shiftKey) return;
|
|
2059
|
+
const delta = event.deltaX !== 0 ? event.deltaX : event.deltaY;
|
|
2060
|
+
if (!delta) return;
|
|
2061
|
+
event.preventDefault();
|
|
2062
|
+
tocScrollEl.scrollLeft += delta;
|
|
2063
|
+
}, { passive: false });
|
|
2064
|
+
}
|
|
2065
|
+
|
|
1949
2066
|
// ── Zoom / resize scroll anchoring ────────────────────────────────────────
|
|
1950
2067
|
// Browser zoom (and any window resize) reflows the column but leaves the
|
|
1951
2068
|
// pixel scroll offset untouched, so the passage being read slides out of
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helping-ai-workflow/md2doc",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.1",
|
|
4
4
|
"description": "Markdown → HTML / PDF renderer with WaveDrom, Mermaid, and Graphviz support",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"preinstall": "node scripts/preinstall.js",
|
|
39
|
-
"test": "node test/md2doc.test.js && node test/images.test.js && node test/scroll-anchor.test.js && node test/lightbox.test.js && node test/cli.test.js && node test/code-operator.test.js"
|
|
39
|
+
"test": "node test/md2doc.test.js && node test/images.test.js && node test/scroll-anchor.test.js && node test/lightbox.test.js && node test/reader-panels.test.js && node test/cli.test.js && node test/code-operator.test.js"
|
|
40
40
|
},
|
|
41
41
|
"repository": {
|
|
42
42
|
"type": "git",
|