1/* Copy-to-clipboard helper. Auto-attaches copy buttons to any .cmd-inline that 2 contains a shell command (prefixed with $ or #) but doesn't already have one. 3 Skips instructional snippets that use arrow prompts (â ⺠etc). */ 4 5const COPY_ICON_SVG = `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`; 6 7function extractCommandText(el) { 8 const clone = el.cloneNode(true); 9 clone.querySelectorAll('.copy-btn').forEach(b => b.remove()); 10 const first = clone.querySelector('span'); 11 if (first) first.remove(); 12 return clone.textContent.replace(/\s+/g, ' ').trim(); 13} 14 15function ensureCommandTextElement(el, promptEl, text) { 16 if (el.querySelector('.cmd-text')) return; 17 const textEl = document.createElement('span'); 18 textEl.className = 'cmd-text'; 19 textEl.textContent = text; 20 Array.from(el.childNodes).forEach((node) => { 21 if (node === promptEl) return; 22 node.remove(); 23 }); 24 promptEl.after(textEl); 25} 26 27document.querySelectorAll('.cmd-inline').forEach((el) => { 28 if (el.querySelector('.copy-btn')) return; 29 const promptEl = el.querySelector('span'); 30 const prompt = promptEl ? promptEl.textContent.trim() : ''; 31 if (prompt !== '$' && prompt !== '#') return; 32 const text = extractCommandText(el); 33 if (!text) return; 34 ensureCommandTextElement(el, promptEl, text); 35 const btn = document.createElement('button'); 36 btn.className = 'copy-btn'; 37 btn.setAttribute('data-copy', text); 38 btn.setAttribute('aria-label', 'Copy command'); 39 btn.innerHTML = `${COPY_ICON_SVG}<span class="copy-label">Copy</span>`; 40 el.appendChild(btn); 41 if (!el.style.display || el.style.display === 'inline-block') { 42 el.style.display = 'inline-flex'; 43 el.style.alignItems = 'center'; 44 el.style.gap = '14px'; 45 } else if (el.style.display === 'block') { 46 el.style.display = 'flex'; 47 el.style.alignItems = 'center'; 48 el.style.justifyContent = 'flex-start'; 49 el.style.gap = '16px'; 50 } 51}); 52 53/* Attaches a copy button to every block of code in long-form content: the SEO 54 article shell and the .prose blog/landing body. Each block is wrapped in a 55 .code-block so the button can sit inside it, top-right, revealed on hover. 56 The button carries a selector reference rather than a fixed string, so the 57 text copied is always the block's current text. Runs after the .cmd-inline 58 pass above, and skips anything that already carries its own button. */ 59document.querySelectorAll( 60 'pre.ns-code, .prose pre, .seo-shell main.content pre, .seo-shell .sec pre, .spoke-drip pre' 61).forEach((pre, i) => { 62 if (pre.closest('.code-block')) return; 63 if (pre.querySelector('.copy-btn')) return; 64 const text = pre.textContent.replace(/\n+$/, ''); 65 if (!text.trim()) return; 66 67 if (!pre.id) pre.id = `code-block-${i + 1}`; 68 const wrap = document.createElement('div'); 69 wrap.className = 'code-block'; 70 pre.parentNode.insertBefore(wrap, pre); 71 wrap.appendChild(pre); 72 73 const btn = document.createElement('button'); 74 btn.type = 'button'; 75 btn.className = 'copy-btn copy-btn-light'; 76 btn.setAttribute('data-copy', `selector:#${pre.id}`); 77 btn.setAttribute('aria-label', 'Copy code to clipboard'); 78 btn.innerHTML = `${COPY_ICON_SVG}<span class="copy-label">Copy</span>`; 79 wrap.appendChild(btn); 80}); 81 82/* The Clipboard API needs a secure context and a permission the browser is 83 free to refuse, so fall back to a hidden textarea and, failing that, tell 84 the reader to press the shortcut rather than silently doing nothing. */ 85function writeToClipboard(text) { 86 const fallback = () => 87 legacyCopy(text) ? Promise.resolve() : Promise.reject(new Error('copy failed')); 88 if (navigator.clipboard && navigator.clipboard.writeText) { 89 return navigator.clipboard.writeText(text).catch(fallback); 90 } 91 return fallback(); 92} 93 94/* Returns a plain boolean, never a promise: writeToClipboard is the only 95 caller and it is what decides how a failure is reported. */ 96function legacyCopy(text) { 97 try { 98 const ta = document.createElement('textarea'); 99 ta.value = text; 100 ta.setAttribute('readonly', ''); 101 ta.style.cssText = 'position:absolute;
101left:-9999px;top:0;'; 102 document.body.appendChild(ta); 103 ta.select(); 104 const ok = document.execCommand('copy'); 105 document.body.removeChild(ta); 106 return ok; 107 } catch (err) { 108 return false; 109 } 110} 111 112document.addEventListener('click', (e) => { 113 const btn = e.target.closest('[data-copy]'); 114 if (!btn) return; 115 const raw = btn.getAttribute('data-copy'); 116 let text = raw; 117 if (raw && raw.startsWith('selector:')) { 118 const sel = raw.slice('selector:'.length); 119 const src = document.querySelector(sel); 120 text = src ? src.textContent.trim() : ''; 121 } 122 if (!text) return; 123 124 const label = btn.querySelector('.copy-label'); 125 const original = label ? label.textContent : btn.textContent; 126 const report = (word) => { 127 btn.classList.add('copied'); 128 if (label) label.textContent = word; 129 else btn.textContent = word; 130 setTimeout(() => { 131 btn.classList.remove('copied'); 132 if (label) label.textContent = original; 133 else btn.textContent = original; 134 }, 1600); 135 }; 136 137 Promise.resolve(writeToClipboard(text)).then( 138 () => report('Copied'), 139 () => report('Press Ctrl+C') 140 ); 141});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.