diff --git a/scripts/annotate.mjs b/scripts/annotate.mjs index 777ff5c..2d66294 100644 --- a/scripts/annotate.mjs +++ b/scripts/annotate.mjs @@ -386,9 +386,18 @@ body:not(.ann-mode) .article-help { display: none; } .article a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; } .article code { font-family: var(--mono); font-size: .9em; background: var(--bg-soft); padding: .12em .4em; border-radius: 4px; } -.article pre { background: #1e1e24; color: #e6e6eb; padding: 16px 18px; border-radius: 8px; +.article pre { position: relative; background: #1e1e24; color: #e6e6eb; padding: 16px 18px; border-radius: 8px; overflow-x: auto; font-size: .88rem; line-height: 1.55; margin: 1.2em 0; } .article pre code { background: none; padding: 0; color: inherit; font-size: inherit; } +.code-copy-btn { position: absolute; top: 8px; right: 8px; background: rgba(255,255,255,0.08); + color: #e6e6eb; border: 1px solid rgba(255,255,255,0.15); border-radius: 5px; + padding: 4px 10px; font: 600 11px var(--sans); cursor: pointer; opacity: 0; + transition: opacity .15s ease, background .15s ease; letter-spacing: .02em; z-index: 2; } +.article pre:hover .code-copy-btn, +.article pre:focus-within .code-copy-btn { opacity: 1; } +.code-copy-btn:hover { background: rgba(255,255,255,0.16); } +.code-copy-btn:focus { opacity: 1; outline: 2px solid var(--accent); outline-offset: 2px; } +.code-copy-btn.copied { background: var(--green); color: #fff; border-color: var(--green); } .article blockquote { margin: 1.2em 0; padding: .5em 1.2em; border-left: 4px solid var(--accent); background: var(--accent-soft); color: var(--text-dim); border-radius: 0 6px 6px 0; } .article ul, .article ol { padding-left: 1.8em; margin: .9em 0; } @@ -871,6 +880,32 @@ refreshArticleAnnotations(); renderPanel(); updateCounts(); setMode(true); + +// ── Code-block copy buttons ── +document.querySelectorAll('.article pre').forEach(function(pre) { + var btn = document.createElement('button'); + btn.type = 'button'; + btn.className = 'code-copy-btn'; + btn.textContent = 'Copy'; + btn.setAttribute('aria-label', 'Copy code block to clipboard'); + btn.addEventListener('click', function(e) { + e.stopPropagation(); + var code = pre.querySelector('code'); + var text = code ? code.textContent : pre.textContent; + navigator.clipboard.writeText(text).then(function() { + btn.textContent = 'Copied'; + btn.classList.add('copied'); + setTimeout(function() { + btn.textContent = 'Copy'; + btn.classList.remove('copied'); + }, 1500); + }).catch(function() { + btn.textContent = 'Failed'; + setTimeout(function() { btn.textContent = 'Copy'; }, 1500); + }); + }); + pre.appendChild(btn); +}); `.trim(); // ---------------------------------------------------------------------------