@helping-ai-workflow/md2doc 3.11.0 → 3.12.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 +88 -3
- package/package.json +1 -1
package/lib/md2doc.js
CHANGED
|
@@ -443,6 +443,8 @@ const READER_ICONS = {
|
|
|
443
443
|
up: ICON_SVG_OPEN + '<path d="m18 15-6-6-6 6"/></svg>',
|
|
444
444
|
down: ICON_SVG_OPEN + '<path d="m6 9 6 6 6-6"/></svg>',
|
|
445
445
|
x: ICON_SVG_OPEN + '<path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>',
|
|
446
|
+
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>',
|
|
447
|
+
check: ICON_SVG_OPEN + '<path d="M20 6 9 17l-5-5"/></svg>',
|
|
446
448
|
};
|
|
447
449
|
|
|
448
450
|
async function renderMarkdown(mdText, srcPath, opts = {}) {
|
|
@@ -1045,9 +1047,16 @@ ${itemsHtml}
|
|
|
1045
1047
|
// The info string goes into an attribute: escape it (an info string such
|
|
1046
1048
|
// as x" onmouseover=… must stay text). Ordinary language names are unchanged.
|
|
1047
1049
|
const langAttr = escapeHtml(lang);
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1050
|
+
const pre = coloured !== null
|
|
1051
|
+
? `<pre><code class="language-${langAttr} hljs">${coloured}</code></pre>`
|
|
1052
|
+
: `<pre><code class="language-${langAttr}">${code.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')}</code></pre>`;
|
|
1053
|
+
// v3.12.0: the reader's copy button. It sits on a wrapper beside the
|
|
1054
|
+
// <pre>, not inside it — <pre> scrolls sideways and would carry the
|
|
1055
|
+
// button off with the code. Icon only: a text label would be picked up
|
|
1056
|
+
// by search and by select-all. Never in edit mode, where it would land
|
|
1057
|
+
// inside the editable surface.
|
|
1058
|
+
if (opts.editMode) return pre + '\n';
|
|
1059
|
+
return `<div class="code-block">${pre}<button class="code-copy" type="button" aria-label="Copy code" title="Copy code">${READER_ICONS.copy}</button></div>\n`;
|
|
1051
1060
|
};
|
|
1052
1061
|
|
|
1053
1062
|
renderer.heading = function(token) {
|
|
@@ -2890,6 +2899,32 @@ const html = `<!DOCTYPE html>
|
|
|
2890
2899
|
line-height: 1.45;
|
|
2891
2900
|
}
|
|
2892
2901
|
pre code { background: none; padding: 0; font-size: 0.875em; }
|
|
2902
|
+
/* v3.12.0: the copy button renderer.code puts beside each fenced block.
|
|
2903
|
+
The wrapper takes over the margin the pre used to have, so the spacing
|
|
2904
|
+
around a block does not move. The button hides at rest and shows on
|
|
2905
|
+
hover, on keyboard focus and after a press; a touch screen has no hover,
|
|
2906
|
+
so there it always shows. Colours are existing reader literals, so the
|
|
2907
|
+
theme post-pass carries them into dark mode. */
|
|
2908
|
+
.code-block { position: relative; margin: 1em 0; }
|
|
2909
|
+
.code-block > pre { margin: 0; }
|
|
2910
|
+
.code-copy {
|
|
2911
|
+
position: absolute; top: 8px; right: 8px;
|
|
2912
|
+
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
|
|
2913
|
+
height: 30px; min-width: 30px; padding: 0 7px;
|
|
2914
|
+
border: 1px solid #d0d7de; border-radius: 6px;
|
|
2915
|
+
background: #ffffff; color: #57606a;
|
|
2916
|
+
font: inherit; font-size: 12px; line-height: 1; cursor: pointer;
|
|
2917
|
+
opacity: 0; transition: opacity 0.12s;
|
|
2918
|
+
}
|
|
2919
|
+
.code-block:hover .code-copy,
|
|
2920
|
+
.code-copy:focus-visible,
|
|
2921
|
+
.code-block.is-copied .code-copy,
|
|
2922
|
+
.code-block.is-manual .code-copy { opacity: 1; }
|
|
2923
|
+
.code-copy:hover { background: #eef2f6; color: #24292e; }
|
|
2924
|
+
.code-copy:focus-visible { outline: 2px solid #0969da; outline-offset: 2px; }
|
|
2925
|
+
.code-block.is-copied .code-copy { color: #0969da; }
|
|
2926
|
+
@media (hover: none) { .code-copy { opacity: 1; } }
|
|
2927
|
+
@media (prefers-reduced-motion: reduce) { .code-copy { transition: none; } }
|
|
2893
2928
|
/* v3.10.0: fenced-code syntax colour, written at render time by
|
|
2894
2929
|
lib/highlight.js (no script on the page). Colours are the syn-* roles. */
|
|
2895
2930
|
.hljs-keyword, .hljs-selector-tag, .hljs-literal { color: var(--md-syn-keyword); }
|
|
@@ -3276,6 +3311,7 @@ ${usesDrawio ? ` /* v3.4.0 §4: a document with no drawio reference must not pa
|
|
|
3276
3311
|
.content table tbody td:first-child,
|
|
3277
3312
|
.content table thead th:first-child { position: static; }
|
|
3278
3313
|
.heading-anchor { display: none; }
|
|
3314
|
+
.code-copy { display: none; }
|
|
3279
3315
|
pre { font-size: 9pt; }
|
|
3280
3316
|
a[href]:after { content: none; }
|
|
3281
3317
|
}
|
|
@@ -6010,6 +6046,55 @@ ${usesDrawioMultiPage ? ` // v3.4.0 batch2 Task 6: wired through a NAMED, reusa
|
|
|
6010
6046
|
var shown = lightboxCanvas.firstElementChild;
|
|
6011
6047
|
lightboxFitZoom = computeLightboxFit(!!(shown && shown.tagName.toLowerCase() === 'svg'));
|
|
6012
6048
|
});
|
|
6049
|
+
|
|
6050
|
+
// v3.12.0: copy button on fenced code (renderer.code emits it; edit mode
|
|
6051
|
+
// does not). Delegated from document, so the edit page's wholesale
|
|
6052
|
+
// re-render of .content needs no rebind. The clipboard API first; then
|
|
6053
|
+
// execCommand over a selection of the code; and when both are refused the
|
|
6054
|
+
// code stays selected and the button says which keys to press.
|
|
6055
|
+
var CODE_COPY_RESET_MS = 2000;
|
|
6056
|
+
var CODE_COPY_DONE = '${READER_ICONS.check}<span>Copied</span>';
|
|
6057
|
+
var CODE_COPY_KEYS = /Mac|iPhone|iPad/.test(navigator.platform || '') ? 'Press Cmd+C' : 'Press Ctrl+C';
|
|
6058
|
+
function showCodeCopyResult(btn, wrap, ok) {
|
|
6059
|
+
if (btn.__md2docIcon === undefined) btn.__md2docIcon = btn.innerHTML;
|
|
6060
|
+
clearTimeout(btn.__md2docReset);
|
|
6061
|
+
wrap.classList.toggle('is-copied', ok);
|
|
6062
|
+
wrap.classList.toggle('is-manual', !ok);
|
|
6063
|
+
btn.innerHTML = ok ? CODE_COPY_DONE : '<span>' + CODE_COPY_KEYS + '</span>';
|
|
6064
|
+
btn.setAttribute('aria-label', ok ? 'Copied' : CODE_COPY_KEYS);
|
|
6065
|
+
btn.__md2docReset = setTimeout(function () {
|
|
6066
|
+
wrap.classList.remove('is-copied', 'is-manual');
|
|
6067
|
+
btn.innerHTML = btn.__md2docIcon;
|
|
6068
|
+
btn.setAttribute('aria-label', 'Copy code');
|
|
6069
|
+
}, CODE_COPY_RESET_MS);
|
|
6070
|
+
}
|
|
6071
|
+
function copyCodeBlock(btn) {
|
|
6072
|
+
var wrap = btn.closest('.code-block');
|
|
6073
|
+
var code = wrap && wrap.querySelector('pre > code');
|
|
6074
|
+
if (!code) return;
|
|
6075
|
+
function bySelection() {
|
|
6076
|
+
var range = document.createRange();
|
|
6077
|
+
range.selectNodeContents(code);
|
|
6078
|
+
var sel = window.getSelection();
|
|
6079
|
+
sel.removeAllRanges();
|
|
6080
|
+
sel.addRange(range);
|
|
6081
|
+
var ok = false;
|
|
6082
|
+
try { ok = document.execCommand('copy'); } catch (e) { ok = false; }
|
|
6083
|
+
if (ok) sel.removeAllRanges();
|
|
6084
|
+
showCodeCopyResult(btn, wrap, ok);
|
|
6085
|
+
}
|
|
6086
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
6087
|
+
navigator.clipboard.writeText(code.textContent).then(function () {
|
|
6088
|
+
showCodeCopyResult(btn, wrap, true);
|
|
6089
|
+
}, bySelection);
|
|
6090
|
+
} else {
|
|
6091
|
+
bySelection();
|
|
6092
|
+
}
|
|
6093
|
+
}
|
|
6094
|
+
document.addEventListener('click', function (event) {
|
|
6095
|
+
var btn = event.target.closest && event.target.closest('.code-block > .code-copy');
|
|
6096
|
+
if (btn) copyCodeBlock(btn);
|
|
6097
|
+
});
|
|
6013
6098
|
})();
|
|
6014
6099
|
</script>
|
|
6015
6100
|
</body>
|