@helping-ai-workflow/md2doc 3.15.0 → 3.16.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 +148 -2
- package/package.json +1 -1
package/lib/md2doc.js
CHANGED
|
@@ -467,6 +467,7 @@ const READER_ICONS = {
|
|
|
467
467
|
x: ICON_SVG_OPEN + '<path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>',
|
|
468
468
|
copy: ICON_SVG_OPEN + '<rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>',
|
|
469
469
|
check: ICON_SVG_OPEN + '<path d="M20 6 9 17l-5-5"/></svg>',
|
|
470
|
+
camera: ICON_SVG_OPEN + '<path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"/><circle cx="12" cy="13" r="3"/></svg>',
|
|
470
471
|
};
|
|
471
472
|
|
|
472
473
|
async function renderMarkdown(mdText, srcPath, opts = {}) {
|
|
@@ -1492,7 +1493,8 @@ ${itemsHtml}
|
|
|
1492
1493
|
<div class="toc-header-actions">
|
|
1493
1494
|
<button id="toc-expand-all" type="button" aria-label="Expand all" title="Expand all">${READER_ICONS.unfold}</button>
|
|
1494
1495
|
<button id="toc-collapse-all" type="button" aria-label="Collapse all" title="Collapse all">${READER_ICONS.fold}</button>
|
|
1495
|
-
<button id="
|
|
1496
|
+
${opts.editMode ? '' : ` <button id="capture-mode-toggle" type="button" aria-label="Screenshot mode" title="Screenshot mode (Esc to exit)">${READER_ICONS.camera}</button>
|
|
1497
|
+
`} <button id="toc-collapse-toggle" type="button" aria-label="Collapse table of contents" title="Collapse / expand sidebar">${READER_ICONS.panel}</button>
|
|
1496
1498
|
</div>
|
|
1497
1499
|
<div class="toc-breadcrumb" data-toc-breadcrumb aria-label="Current location"></div>
|
|
1498
1500
|
</div>
|
|
@@ -2411,7 +2413,8 @@ const html = `<!DOCTYPE html>
|
|
|
2411
2413
|
TOC-list buttons (no list to act on) and shrink padding so the toggle
|
|
2412
2414
|
fits inside the rail instead of being clipped by overflow:hidden. */
|
|
2413
2415
|
body[data-toc-collapsed] #toc-expand-all,
|
|
2414
|
-
body[data-toc-collapsed] #toc-collapse-all
|
|
2416
|
+
body[data-toc-collapsed] #toc-collapse-all,
|
|
2417
|
+
body[data-toc-collapsed] #capture-mode-toggle {
|
|
2415
2418
|
display: none;
|
|
2416
2419
|
}
|
|
2417
2420
|
body[data-toc-collapsed] .toc {
|
|
@@ -2881,6 +2884,70 @@ const html = `<!DOCTYPE html>
|
|
|
2881
2884
|
min-width: 0;
|
|
2882
2885
|
flex: 1 1 auto;
|
|
2883
2886
|
}
|
|
2887
|
+
/* Screenshot mode (v3.16.0). A scroll-and-stitch full-page capture
|
|
2888
|
+
(FireShot and the like) glues one slice per screen, so whatever stays put
|
|
2889
|
+
while the page scrolls lands on every slice: hide all of it, and keep the
|
|
2890
|
+
column at the width it was read at (--md2doc-capture-w, measured on
|
|
2891
|
+
entry), centred, so its lines break as they did. */
|
|
2892
|
+
body[data-capture-mode] .reader-sidebar,
|
|
2893
|
+
body[data-capture-mode] .sidebar-splitter,
|
|
2894
|
+
body[data-capture-mode] .sidebar-scrim,
|
|
2895
|
+
body[data-capture-mode] .mobile-bar,
|
|
2896
|
+
body[data-capture-mode] .md2doc-theme-toggle {
|
|
2897
|
+
display: none;
|
|
2898
|
+
}
|
|
2899
|
+
body[data-capture-mode] .page-layout {
|
|
2900
|
+
display: block;
|
|
2901
|
+
}
|
|
2902
|
+
body[data-capture-mode] main.content {
|
|
2903
|
+
box-sizing: border-box;
|
|
2904
|
+
width: var(--md2doc-capture-w, auto);
|
|
2905
|
+
max-width: 100%;
|
|
2906
|
+
margin: 0 auto;
|
|
2907
|
+
}
|
|
2908
|
+
/* v3.16.1: a capture tool hides every sticky element after its first slice,
|
|
2909
|
+
so a sticky first column came out blank from the second screen down. The
|
|
2910
|
+
runtime also widens each box that scrolls sideways (fitWideBoxes); the
|
|
2911
|
+
rules below are its last resort, for a box still wider than the screen. */
|
|
2912
|
+
body[data-capture-mode] .content table tbody td:first-child,
|
|
2913
|
+
body[data-capture-mode] .content table thead th:first-child {
|
|
2914
|
+
position: static;
|
|
2915
|
+
}
|
|
2916
|
+
body[data-capture-mode] .content table.capture-wrap th:not(.cell-narrow),
|
|
2917
|
+
body[data-capture-mode] .content table.capture-wrap td:not(.cell-narrow),
|
|
2918
|
+
body[data-capture-mode] .content table.capture-wrap td:not(.cell-narrow) code {
|
|
2919
|
+
white-space: normal;
|
|
2920
|
+
overflow-wrap: break-word;
|
|
2921
|
+
min-width: 0;
|
|
2922
|
+
}
|
|
2923
|
+
body[data-capture-mode] .content table.capture-wrap th,
|
|
2924
|
+
body[data-capture-mode] .content table.capture-wrap td {
|
|
2925
|
+
padding: 0.4em 0.6em;
|
|
2926
|
+
}
|
|
2927
|
+
body[data-capture-mode] .content pre.capture-wrap,
|
|
2928
|
+
body[data-capture-mode] .content pre.capture-wrap code {
|
|
2929
|
+
white-space: pre-wrap;
|
|
2930
|
+
overflow-wrap: anywhere;
|
|
2931
|
+
}
|
|
2932
|
+
body[data-capture-mode] .content .diagram-scroll.capture-wrap > svg {
|
|
2933
|
+
max-width: 100% !important;
|
|
2934
|
+
height: auto;
|
|
2935
|
+
}
|
|
2936
|
+
.capture-toast {
|
|
2937
|
+
position: fixed;
|
|
2938
|
+
top: 16px;
|
|
2939
|
+
left: 50%;
|
|
2940
|
+
transform: translateX(-50%);
|
|
2941
|
+
z-index: 300;
|
|
2942
|
+
padding: 8px 16px;
|
|
2943
|
+
border-radius: 999px;
|
|
2944
|
+
background: #24292e;
|
|
2945
|
+
color: #ffffff;
|
|
2946
|
+
font-size: 14px;
|
|
2947
|
+
line-height: 1.4;
|
|
2948
|
+
box-shadow: 0 4px 14px rgba(0,0,0,0.18);
|
|
2949
|
+
pointer-events: none;
|
|
2950
|
+
}
|
|
2884
2951
|
.content p,
|
|
2885
2952
|
.content li,
|
|
2886
2953
|
.content blockquote,
|
|
@@ -5057,6 +5124,85 @@ ${mermaidInitTag}`)
|
|
|
5057
5124
|
setSidebarOpen(false);
|
|
5058
5125
|
}
|
|
5059
5126
|
});
|
|
5127
|
+
|
|
5128
|
+
// ── Screenshot mode (v3.16.0): only the document, for a full-page capture ──
|
|
5129
|
+
// The CSS beside '.capture-toast' hides everything that stays put while the
|
|
5130
|
+
// page scrolls. The column keeps the width measured here, so nothing reflows
|
|
5131
|
+
// and the reading position holds. Esc leaves; a reload never comes back in it.
|
|
5132
|
+
const captureBtn = document.getElementById('capture-mode-toggle');
|
|
5133
|
+
let captureToastTimer = 0;
|
|
5134
|
+
// A capture tool only sees the part of a sideways-scrolling box that is in
|
|
5135
|
+
// view, so each one (a wide table, a long code line, a wide diagram) is
|
|
5136
|
+
// widened to its full width, centred on the screen, at most the screen less
|
|
5137
|
+
// a 16px gutter each side; the prose keeps its reading width. One still too
|
|
5138
|
+
// wide wraps (.capture-wrap), and a table still too wide after that gets
|
|
5139
|
+
// smaller type. Each box's own style attribute is kept and put back on exit.
|
|
5140
|
+
let captureFitted = [];
|
|
5141
|
+
function fitWideBoxes() {
|
|
5142
|
+
const vw = document.documentElement.clientWidth;
|
|
5143
|
+
const boxes = document.querySelectorAll('main.content table, main.content pre, main.content .diagram-scroll');
|
|
5144
|
+
for (const el of boxes) {
|
|
5145
|
+
if (el.scrollWidth <= el.clientWidth + 1) continue;
|
|
5146
|
+
captureFitted.push({ el: el, style: el.getAttribute('style') });
|
|
5147
|
+
const w = Math.min(el.scrollWidth, vw - 32);
|
|
5148
|
+
el.style.boxSizing = 'border-box';
|
|
5149
|
+
el.style.width = w + 'px';
|
|
5150
|
+
el.style.maxWidth = 'none';
|
|
5151
|
+
const margin = parseFloat(getComputedStyle(el).marginLeft) || 0;
|
|
5152
|
+
el.style.marginLeft = (margin + (vw - w) / 2 - el.getBoundingClientRect().left) + 'px';
|
|
5153
|
+
if (el.scrollWidth > el.clientWidth + 1) el.classList.add('capture-wrap');
|
|
5154
|
+
// Many columns on a narrow screen can stay too wide even wrapped: shrink
|
|
5155
|
+
// the table's type (the wrap rule's cell padding is in em) until it fits.
|
|
5156
|
+
let size = parseFloat(getComputedStyle(el).fontSize);
|
|
5157
|
+
for (let i = 0; i < 8 && el.tagName === 'TABLE' && el.scrollWidth > el.clientWidth + 1; i++) {
|
|
5158
|
+
size = size * el.clientWidth / el.scrollWidth * 0.98;
|
|
5159
|
+
el.style.fontSize = size + 'px';
|
|
5160
|
+
}
|
|
5161
|
+
}
|
|
5162
|
+
}
|
|
5163
|
+
function unfitWideBoxes() {
|
|
5164
|
+
for (const f of captureFitted) {
|
|
5165
|
+
f.el.classList.remove('capture-wrap');
|
|
5166
|
+
if (f.style === null) f.el.removeAttribute('style');
|
|
5167
|
+
else f.el.setAttribute('style', f.style);
|
|
5168
|
+
}
|
|
5169
|
+
captureFitted = [];
|
|
5170
|
+
}
|
|
5171
|
+
function setCaptureMode(on) {
|
|
5172
|
+
clearTimeout(captureToastTimer);
|
|
5173
|
+
const oldToast = document.querySelector('.capture-toast');
|
|
5174
|
+
if (oldToast) oldToast.remove();
|
|
5175
|
+
if (!on) {
|
|
5176
|
+
unfitWideBoxes();
|
|
5177
|
+
document.body.removeAttribute('data-capture-mode');
|
|
5178
|
+
document.body.style.removeProperty('--md2doc-capture-w');
|
|
5179
|
+
return;
|
|
5180
|
+
}
|
|
5181
|
+
setSidebarOpen(false);
|
|
5182
|
+
const w = document.querySelector('main.content').getBoundingClientRect().width;
|
|
5183
|
+
document.body.style.setProperty('--md2doc-capture-w', w + 'px');
|
|
5184
|
+
document.body.setAttribute('data-capture-mode', '');
|
|
5185
|
+
unfitWideBoxes();
|
|
5186
|
+
fitWideBoxes();
|
|
5187
|
+
const toast = document.createElement('div');
|
|
5188
|
+
toast.className = 'capture-toast';
|
|
5189
|
+
toast.setAttribute('role', 'status');
|
|
5190
|
+
toast.textContent = 'Screenshot mode — press Esc or reload to exit';
|
|
5191
|
+
document.body.appendChild(toast);
|
|
5192
|
+
// Gone before the capture tool scrolls, or it would be on every slice too.
|
|
5193
|
+
captureToastTimer = setTimeout(() => toast.remove(), 2000);
|
|
5194
|
+
}
|
|
5195
|
+
if (captureBtn) {
|
|
5196
|
+
captureBtn.addEventListener('click', () => setCaptureMode(true));
|
|
5197
|
+
}
|
|
5198
|
+
// Registered ahead of the lightbox's own Esc handler, which closes the
|
|
5199
|
+
// diagram: while one is open, its Esc is the lightbox's and the mode stays.
|
|
5200
|
+
document.addEventListener('keydown', (event) => {
|
|
5201
|
+
if (lightboxIsOpen()) return;
|
|
5202
|
+
if (event.key === 'Escape' && document.body.hasAttribute('data-capture-mode')) {
|
|
5203
|
+
setCaptureMode(false);
|
|
5204
|
+
}
|
|
5205
|
+
});
|
|
5060
5206
|
document.addEventListener('click', (event) => {
|
|
5061
5207
|
if (!document.body.hasAttribute('data-sidebar-open')) return;
|
|
5062
5208
|
if (!event.target || !event.target.closest) return;
|