Skip to content

Commit a491e25

Browse files
committed
code-copy script blog
1 parent af7d311 commit a491e25

File tree

2 files changed

+170
-169
lines changed

2 files changed

+170
-169
lines changed

docs/blog/code-copy.js

Lines changed: 0 additions & 168 deletions
This file was deleted.

docs/blog/supabase-claude-code-integration/index.html

Lines changed: 170 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,175 @@ <h2>Where Components Are Installed</h2>
355355
</footer>
356356

357357
<!-- Code Copy Functionality -->
358-
<script src="../code-copy.js?v=1.0"></script>
358+
<script>
359+
// Code Copy Functionality for Blog Articles
360+
class CodeCopy {
361+
constructor() {
362+
this.initCodeBlocks();
363+
this.setupCopyFunctionality();
364+
}
365+
366+
initCodeBlocks() {
367+
// Convert existing pre elements to new code-block structure
368+
const preElements = document.querySelectorAll('.article-content-full pre:not(.converted)');
369+
370+
preElements.forEach(pre => {
371+
const code = pre.querySelector('code');
372+
if (!code) return;
373+
374+
// Detect language from class or content
375+
const language = this.detectLanguage(code);
376+
const isTerminal = language === 'bash' || language === 'terminal';
377+
378+
// Create new code block structure
379+
const codeBlock = document.createElement('div');
380+
codeBlock.className = isTerminal ? 'code-block terminal-block' : 'code-block';
381+
382+
// Create header
383+
const header = document.createElement('div');
384+
header.className = 'code-header';
385+
386+
const languageSpan = document.createElement('span');
387+
languageSpan.className = 'code-language';
388+
languageSpan.textContent = language;
389+
390+
const copyButton = document.createElement('button');
391+
copyButton.className = 'copy-button';
392+
copyButton.innerHTML = `
393+
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
394+
<path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/>
395+
</svg>
396+
Copy
397+
`;
398+
399+
header.appendChild(languageSpan);
400+
header.appendChild(copyButton);
401+
402+
// Clone and prepare the pre element
403+
const newPre = pre.cloneNode(true);
404+
newPre.classList.add('converted');
405+
406+
// Assemble new structure
407+
codeBlock.appendChild(header);
408+
codeBlock.appendChild(newPre);
409+
410+
// Replace original pre
411+
pre.parentNode.replaceChild(codeBlock, pre);
412+
});
413+
}
414+
415+
detectLanguage(codeElement) {
416+
// Check for class-based language detection
417+
const className = codeElement.className;
418+
if (className.includes('language-')) {
419+
return className.match(/language-(\w+)/)[1];
420+
}
421+
422+
// Check content patterns
423+
const content = codeElement.textContent;
424+
425+
if (content.includes('npm ') || content.includes('$ ') || content.includes('claude-code ')) {
426+
return 'bash';
427+
}
428+
if (content.includes('import ') && content.includes('from ')) {
429+
return 'javascript';
430+
}
431+
if (content.includes('CREATE TABLE') || content.includes('SELECT ')) {
432+
return 'sql';
433+
}
434+
if (content.includes('{') && content.includes('"')) {
435+
return 'json';
436+
}
437+
if (content.includes('def ') || content.includes('import ')) {
438+
return 'python';
439+
}
440+
441+
return 'text';
442+
}
443+
444+
setupCopyFunctionality() {
445+
document.addEventListener('click', async (e) => {
446+
if (!e.target.closest('.copy-button')) return;
447+
448+
const button = e.target.closest('.copy-button');
449+
const codeBlock = button.closest('.code-block');
450+
const pre = codeBlock.querySelector('pre');
451+
const code = pre.querySelector('code');
452+
453+
if (!code) return;
454+
455+
try {
456+
// Get clean text content
457+
let textToCopy = code.textContent;
458+
459+
// Clean up terminal prompts if it's a terminal block
460+
if (codeBlock.classList.contains('terminal-block')) {
461+
textToCopy = this.cleanTerminalOutput(textToCopy);
462+
}
463+
464+
await navigator.clipboard.writeText(textToCopy);
465+
466+
// Update button state
467+
const originalContent = button.innerHTML;
468+
button.innerHTML = `
469+
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
470+
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
471+
</svg>
472+
Copied!
473+
`;
474+
button.classList.add('copied');
475+
476+
// Reset after 2 seconds
477+
setTimeout(() => {
478+
button.innerHTML = originalContent;
479+
button.classList.remove('copied');
480+
}, 2000);
481+
482+
} catch (err) {
483+
console.error('Failed to copy code:', err);
484+
485+
// Fallback: select text
486+
const selection = window.getSelection();
487+
const range = document.createRange();
488+
range.selectNodeContents(code);
489+
selection.removeAllRanges();
490+
selection.addRange(range);
491+
}
492+
});
493+
}
494+
495+
cleanTerminalOutput(text) {
496+
// Remove common terminal prompts and clean output
497+
return text
498+
.split('\n')
499+
.map(line => {
500+
// Remove prompts like "$ ", "❯ ", "claude-code> "
501+
line = line.replace(/^[\$]\s*/, '').replace(/^claude-code>\s*/, '');
502+
503+
// Remove output/result comments (lines starting with # ✓)
504+
if (line.trim().startsWith('# ✓') ||
505+
line.trim().startsWith('# Start using') ||
506+
line.trim().startsWith('# Your .claude') ||
507+
line.trim().startsWith('# This will') ||
508+
line.includes('Components will be installed') ||
509+
line.includes('directory now contains')) {
510+
return '';
511+
}
512+
513+
return line;
514+
})
515+
.filter(line => line.trim() !== '') // Remove empty lines
516+
.join('\n')
517+
.trim();
518+
}
519+
}
520+
521+
// Initialize when DOM is loaded
522+
if (document.readyState === 'loading') {
523+
document.addEventListener('DOMContentLoaded', () => new CodeCopy());
524+
} else {
525+
new CodeCopy();
526+
}
527+
</script>
359528
</body>
360529
</html>

0 commit comments

Comments
 (0)