Files
wehub-resource-sync bb5c75ce05
Component Security Validation / Security Audit (push) Has been cancelled
Deploy to Cloudflare Pages / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:58 +08:00

849 lines
39 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Supabase Claude Code Integration Guide: 8 Commands + AI Agents + MCP Server</title>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-YWW6FV2SGN"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-YWW6FV2SGN');
</script>
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="../../static/favicon/favicon.ico">
<link rel="icon" type="image/png" sizes="16x16" href="../../static/favicon/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="../../static/favicon/favicon-32x32.png">
<link rel="apple-touch-icon" sizes="180x180" href="../../static/favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="192x192" href="../../static/favicon/android-chrome-192x192.png">
<link rel="icon" type="image/png" sizes="512x512" href="../../static/favicon/android-chrome-512x512.png">
<meta name="description" content="Complete guide to integrate Supabase with Claude Code. Install 8 database commands, 2 AI agents, and MCP server for automated schema design, migrations, and TypeScript generation.">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="article">
<meta property="og:url" content="https://aitmpl.com/blog/supabase-claude-code-integration/">
<meta property="og:title" content="Supabase Claude Code Integration Guide: 8 Commands + AI Agents + MCP Server">
<meta property="og:description" content="Complete guide to integrate Supabase with Claude Code. Install 8 database commands, 2 AI agents, and MCP server for automated schema design, migrations, and TypeScript generation.">
<meta property="og:image" content="https://aitmpl.com/blog/assets/supabase-claude-code-templates-cover.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="article:author" content="Claude Code Templates">
<meta property="article:section" content="Database">
<meta property="article:tag" content="Supabase">
<meta property="article:tag" content="Claude Code">
<meta property="article:tag" content="MCP">
<meta property="article:tag" content="Database">
<meta property="article:tag" content="PostgreSQL">
<meta property="article:tag" content="TypeScript">
<meta property="article:tag" content="AI Development">
<meta property="article:tag" content="Anthropic">
<meta property="article:tag" content="Schema Design">
<meta property="article:tag" content="Database Migration">
<meta property="article:tag" content="Automation">
<meta property="article:tag" content="Real-time Database">
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:url" content="https://aitmpl.com/blog/supabase-claude-code-integration/">
<meta name="twitter:title" content="Supabase Claude Code Integration Guide: 8 Commands + AI Agents + MCP Server">
<meta name="twitter:description" content="Complete guide to integrate Supabase with Claude Code. Install 8 database commands, 2 AI agents, and MCP server for automated schema design, migrations, and TypeScript generation.">
<meta name="twitter:image" content="https://aitmpl.com/blog/assets/supabase-claude-code-templates-cover.png">
<!-- Additional SEO -->
<meta name="keywords" content="Supabase Claude Code integration, MCP server Supabase, Claude Code agents, database development AI, Supabase schema architect, PostgreSQL Claude Code, AI database tools, Supabase automation, Claude Code templates, database migration assistant, Supabase TypeScript generator, real-time database monitoring, Supabase backup automation, database security audit, Anthropic Claude Code, AI-powered database development">
<meta name="author" content="Claude Code Templates">
<link rel="canonical" href="https://aitmpl.com/blog/supabase-claude-code-integration/">
<link rel="stylesheet" href="../../css/styles.css">
<link rel="stylesheet" href="../../css/blog.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Hotjar Tracking Code for https://aitmpl.com -->
<script>
(function(h,o,t,j,a,r){
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
h._hjSettings={hjid:6519181,hjsv:6};
a=o.getElementsByTagName('head')[0];
r=o.createElement('script');r.async=1;
r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
a.appendChild(r);
})(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');
</script>
<!-- Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "Supabase Claude Code Integration Guide: 8 Commands + AI Agents + MCP Server",
"description": "Complete guide to integrate Supabase with Claude Code. Install 8 database commands, 2 AI agents, and MCP server for automated schema design, migrations, and TypeScript generation.",
"image": "https://aitmpl.com/blog/assets/supabase-claude-code-templates-cover.png",
"author": {
"@type": "Organization",
"name": "Claude Code Templates"
},
"publisher": {
"@type": "Organization",
"name": "Claude Code Templates",
"logo": {
"@type": "ImageObject",
"url": "https://aitmpl.com/static/img/logo.svg"
}
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://aitmpl.com/blog/supabase-claude-code-integration/"
},
"keywords": "Supabase Claude Code integration, MCP server, database development AI, schema architect, TypeScript generator, database automation",
"wordCount": "1200",
"articleSection": "Database Development",
"about": [
{
"@type": "Thing",
"name": "Supabase"
},
{
"@type": "Thing",
"name": "Claude Code"
},
{
"@type": "Thing",
"name": "Model Context Protocol"
},
{
"@type": "Thing",
"name": "Database Development"
}
],
"mentions": [
{
"@type": "SoftwareApplication",
"name": "Supabase",
"url": "https://supabase.com"
},
{
"@type": "SoftwareApplication",
"name": "Claude Code",
"url": "https://claude.ai/code"
}
]
}
</script>
</head>
<body>
<header class="header">
<div class="container">
<div class="header-content">
<div class="terminal-header">
<div class="ascii-title">
<pre class="ascii-art">
██████╗ ██╗ ██████╗ ██████╗
██╔══██╗██║ ██╔═══██╗██╔════╝
██████╔╝██║ ██║ ██║██║ ███╗
██╔══██╗██║ ██║ ██║██║ ██║
██████╔╝███████╗╚██████╔╝╚██████╔╝
╚═════╝ ╚══════╝ ╚═════╝ ╚═════╝</pre>
</div>
</div>
<div class="header-actions">
<a href="../../index.html" class="header-btn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z"/>
</svg>
Home
</a>
<a href="../index.html" class="header-btn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z"/>
</svg>
Blog
</a>
<a href="https://github.com/davila7/claude-code-templates" target="_blank" class="header-btn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.30 3.297-1.30.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
GitHub
</a>
</div>
</div>
</div>
</header>
<main class="terminal">
<header class="article-header">
<div class="container">
<!-- Copy Markdown Button -->
<button id="copy-markdown-btn" class="copy-markdown-button" title="Copy post as Markdown">
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<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"/>
</svg>
Copy as Markdown
</button>
<h1 class="article-title">Claude Code + Supabase Integration: Complete Guide with Agents, Commands and MCP</h1>
<p class="article-subtitle">Step-by-step tutorial to install and use 2 Agents, 8 database commands, and MCP server for automated Supabase development with Claude Code.</p>
<div class="article-meta-full">
<span class="read-time">5 min read</span>
<div class="article-tags">
<span class="tag">Supabase</span>
<span class="tag">Database</span>
<span class="tag">MCP</span>
<span class="tag">Agents</span>
</div>
</div>
</div>
</header>
<article class="article-body">
<img src="../assets/supabase-claude-code-templates-cover.png" alt="Supabase and Claude Code Integration" class="article-cover" loading="lazy">
<div class="article-content-full">
<h2>Claude Code stack for Supabase</h2>
<p>Claude Code Templates offers three pre-built components for Supabase integration:</p>
<h3>🤖 Agents</h3>
<table class="components-table">
<thead>
<tr>
<th>Component</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Supabase Schema Architect</strong></td>
<td>Expert in database design, migrations, and RLS policies. Automatically analyzes requirements and generates production-ready schemas with optimal performance.</td>
</tr>
<tr>
<td><strong>Supabase Realtime Optimizer</strong></td>
<td>Specialist in WebSocket optimization and real-time performance. Monitors connections, optimizes subscriptions, and ensures scalable real-time applications.</td>
</tr>
</tbody>
</table>
<h3>⚡ Commands</h3>
<table class="components-table">
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>supabase-schema-sync</strong></td>
<td>Synchronize local and remote schemas, manage version control, and detect schema drift automatically.</td>
</tr>
<tr>
<td><strong>supabase-migration-assistant</strong></td>
<td>Generate, manage, and execute database migrations with rollback capabilities and conflict resolution.</td>
</tr>
<tr>
<td><strong>supabase-performance-optimizer</strong></td>
<td>Analyze query performance, suggest indexes, and optimize database operations for maximum speed.</td>
</tr>
<tr>
<td><strong>supabase-security-audit</strong></td>
<td>Comprehensive security analysis, RLS policy validation, and vulnerability scanning with automated fixes.</td>
</tr>
<tr>
<td><strong>supabase-backup-manager</strong></td>
<td>Automated backup scheduling, restoration procedures, and disaster recovery planning with testing.</td>
</tr>
<tr>
<td><strong>supabase-type-generator</strong></td>
<td>Generate TypeScript types from database schema, maintain type safety, and auto-update on schema changes.</td>
</tr>
<tr>
<td><strong>supabase-data-explorer</strong></td>
<td>Interactive data exploration, visual query builder, and data export capabilities with filtering.</td>
</tr>
<tr>
<td><strong>supabase-realtime-monitor</strong></td>
<td>Real-time connection monitoring, performance tracking, and WebSocket health diagnostics.</td>
</tr>
</tbody>
</table>
<h3>🔌 MCP</h3>
<table class="components-table">
<thead>
<tr>
<th>Component</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>🔌 <strong>Supabase MCP Server</strong></td>
<td>Direct integration with Supabase API through Model Context Protocol for seamless Claude Code interaction.</td>
</tr>
</tbody>
</table>
<h2>Browse all components on AITMPL.com</h2>
<p>Before installing, you can explore all available Supabase components on the official Claude Code Templates website:</p>
<p>Visit <strong><a href="https://aitmpl.com" target="_blank" rel="noopener">aitmpl.com</a></strong> and search for "supabase" to see:</p>
<img src="https://aitmpl.com/blog/assets/aitmpl-supabase-search.png" alt="Searching for Supabase components on AITMPL.com" loading="lazy">
<!-- Newsletter CTA - Middle -->
<div class="newsletter-cta middle-newsletter">
<div class="newsletter-cta-content">
<h3>Stay Updated</h3>
<p>Get new articles, components and templates for Claude Code delivered to your inbox.</p>
<div id="middle-newsletter-success" class="newsletter-message success-message" style="display: none;">
✓ Subscribed successfully
</div>
<div id="middle-newsletter-error" class="newsletter-message error-message" style="display: none;">
✗ Error. Try again.
</div>
<form id="middle-newsletter-form" method="POST" action="https://817715f5.sibforms.com/serve/MUIFAAfji55QSo2kjmh_3Y4i1XrFpOiTqwu6ZKbWQtps1SHh-4OOCCzhwRCvknpSybxI0_-V0KWocb0_b12iXBT-OahbBE4YiCQc9nVU0mD2R18vQfpP9wN11ZW8XqgTo5I9EGLrt-qdfR_1rxbmJX4uiWGXbJSYYrTVno0hUd0Pp42BOLsWJ3hssO3NMq2fdWhVZa9OIIWW3L85" data-type="subscription" class="newsletter-form" target="middle_hidden_iframe">
<div class="input-group">
<input type="email" name="EMAIL" placeholder="your@email.com" required class="newsletter-input">
<button type="submit" class="newsletter-submit">
<span class="submit-text"></span>
</button>
</div>
<input type="text" name="email_address_check" value="" style="display: none;">
<input type="hidden" name="locale" value="en">
</form>
<iframe name="middle_hidden_iframe" style="display: none;"></iframe>
</div>
</div>
<h2>Installation Options</h2>
<p>There are multiple ways to install the Supabase stack for Claude Code. Choose the approach that best fits your workflow:</p>
<div class="info-box">
<strong>Want to understand how it works?</strong> Keep reading to learn about each component and how they work together in your Supabase development workflow.
</div>
<h3>Install Individual Components</h3>
<pre><code class="language-bash">
# Install specific agent
npx claude-code-templates@latest --agent supabase-schema-architect
# Install specific Supabase command
npx claude-code-templates@latest --command supabase-schema-sync
# Install MCP server
npx claude-code-templates@latest --mcp supabase</code></pre>
<p><strong>Components will be installed to:</strong></p>
<ul>
<li>📁 <code>.claude/commands/</code></li>
<li>📁 <code>.claude/agents/</code></li>
<li>📁 <code>.mcp.json</code></li>
</ul>
<h3>Create Global Agents (Available Anywhere)</h3>
<pre><code class="language-bash"># Create global agents accessible from any project
npx claude-code-templates@latest --create-agent supabase-schema-architect
npx claude-code-templates@latest --create-agent supabase-realtime-optimizer
# List all global agents
npx claude-code-templates@latest --list-agents
# Update global agents
npx claude-code-templates@latest --update-agent supabase-schema-architect
# Remove global agents
npx claude-code-templates@latest --remove-agent supabase-realtime-optimizer</code></pre>
<h3>Install Multiple Components at Once</h3>
<pre><code class="language-bash">
# Install specific Supabase commands (comma-separated for multiple)
npx claude-code-templates@latest --command supabase-schema-sync,supabase-type-generator,supabase-data-explorer
</code></pre>
<pre><code class="language-bash">
# Install all Supabase components in one command
npx claude-code-templates@latest \
--command supabase-schema-sync,supabase-migration-assistant,supabase-performance-optimizer,supabase-security-audit,supabase-backup-manager,supabase-type-generator,supabase-data-explorer,supabase-realtime-monitor \
--agent supabase-schema-architect,supabase-realtime-optimizer \
--mcp supabase</code></pre>
<p><strong>This will install:</strong></p>
<ul>
<li>✓ 8 database commands</li>
<li>✓ 2 specialized AI agents</li>
<li>✓ 1 MCP server integration</li>
</ul>
<h2>Where Components Are Installed</h2>
<p>The installation creates a standard Claude Code structure with components organized as follows:</p>
<pre><code class="language-bash">your-project/
├── .claude/
│ ├── commands/
│ │ ├── supabase-schema-sync.md
│ │ ├── supabase-migration-assistant.md
│ │ ├── supabase-performance-optimizer.md
│ │ ├── supabase-security-audit.md
│ │ ├── supabase-backup-manager.md
│ │ ├── supabase-type-generator.md
│ │ ├── supabase-data-explorer.md
│ │ └── supabase-realtime-monitor.md
│ └── agents/
│ └── supabase-schema-architect.md
│ └── supabase-realtime-optimizer.md
└── .mcp.json
└── src/ # Your application code</code></pre>
<p>That's it! Claude Code will automatically detect all components and you can start using them immediately.</p>
<div class="success-box">
<strong>Key takeaway:</strong> With agents, commands, and MCP working together, Claude Code becomes a full-featured Supabase development environment.
</div>
<div class="explore-components-banner">
<h3>Explore 800+ Claude Code Components</h3>
<p>Discover agents, commands, MCPs, settings, hooks, skills and templates to supercharge your Claude Code workflow</p>
<a href="../../index.html">Browse All Components</a>
</div>
</div>
<div class="article-nav">
<a href="../index.html" class="back-to-blog">
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z"/>
</svg>
Back to Blog
</a>
</div>
</article>
</main>
<footer class="footer">
<div class="container">
<div class="footer-content">
<div class="footer-left">
<div class="footer-ascii">
<pre class="footer-ascii-art"> █████╗ ██╗████████╗███╗ ███╗██████╗ ██╗
██╔══██╗██║╚══██╔══╝████╗ ████║██╔══██╗██║
███████║██║ ██║ ██╔████╔██║██████╔╝██║
██╔══██║██║ ██║ ██║╚██╔╝██║██╔═══╝ ██║
██║ ██║██║ ██║ ██║ ╚═╝ ██║██║ ███████╗
╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚══════╝</pre>
<p class="footer-tagline">Supercharge Anthropic's Claude Code</p>
</div>
</div>
<div class="footer-right">
<p class="footer-copyright">&copy; 2026 Claude Code Templates. Open source project.</p>
<div class="footer-links">
<a href="../../trending.html" class="footer-link">
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M16,6L18.29,8.29L13.41,13.17L9.41,9.17L2,16.59L3.41,18L9.41,12L13.41,16L19.71,9.71L22,12V6H16Z"/>
</svg>
Trending
</a>
<a href="https://docs.aitmpl.com/" target="_blank" class="footer-link">
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z"/>
</svg>
Documentation
</a>
<a href="https://github.com/davila7/claude-code-templates" target="_blank" class="footer-link">
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.30 3.297-1.30.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
GitHub
</a>
</div>
</div>
</div>
</div>
</footer>
<!-- Code Copy Functionality -->
<script>
// Code Copy Functionality for Blog Articles
class CodeCopy {
constructor() {
this.initCodeBlocks();
this.setupCopyFunctionality();
}
initCodeBlocks() {
// Convert existing pre elements to new code-block structure
const preElements = document.querySelectorAll('.article-content-full pre:not(.converted)');
preElements.forEach(pre => {
const code = pre.querySelector('code');
if (!code) return;
// Detect language from class or content
const language = this.detectLanguage(code);
const isTerminal = language === 'bash' || language === 'terminal';
// Create new code block structure
const codeBlock = document.createElement('div');
codeBlock.className = isTerminal ? 'code-block terminal-block' : 'code-block';
// Create header
const header = document.createElement('div');
header.className = 'code-header';
const languageSpan = document.createElement('span');
languageSpan.className = 'code-language';
languageSpan.textContent = language;
const copyButton = document.createElement('button');
copyButton.className = 'copy-button';
copyButton.innerHTML = `
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
<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"/>
</svg>
Copy
`;
header.appendChild(languageSpan);
header.appendChild(copyButton);
// Clone and prepare the pre element
const newPre = pre.cloneNode(true);
newPre.classList.add('converted');
// Assemble new structure
codeBlock.appendChild(header);
codeBlock.appendChild(newPre);
// Replace original pre
pre.parentNode.replaceChild(codeBlock, pre);
});
}
detectLanguage(codeElement) {
// Check for class-based language detection
const className = codeElement.className;
if (className.includes('language-')) {
return className.match(/language-(\w+)/)[1];
}
// Check content patterns
const content = codeElement.textContent;
if (content.includes('npm ') || content.includes('$ ') || content.includes('claude-code ')) {
return 'bash';
}
if (content.includes('import ') && content.includes('from ')) {
return 'javascript';
}
if (content.includes('CREATE TABLE') || content.includes('SELECT ')) {
return 'sql';
}
if (content.includes('{') && content.includes('"')) {
return 'json';
}
if (content.includes('def ') || content.includes('import ')) {
return 'python';
}
return 'text';
}
setupCopyFunctionality() {
document.addEventListener('click', async (e) => {
if (!e.target.closest('.copy-button')) return;
const button = e.target.closest('.copy-button');
const codeBlock = button.closest('.code-block');
const pre = codeBlock.querySelector('pre');
const code = pre.querySelector('code');
if (!code) return;
try {
// Get clean text content
let textToCopy = code.textContent;
// Clean up terminal prompts if it's a terminal block
if (codeBlock.classList.contains('terminal-block')) {
textToCopy = this.cleanTerminalOutput(textToCopy);
}
await navigator.clipboard.writeText(textToCopy);
// Update button state
const originalContent = button.innerHTML;
button.innerHTML = `
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
</svg>
Copied!
`;
button.classList.add('copied');
// Reset after 2 seconds
setTimeout(() => {
button.innerHTML = originalContent;
button.classList.remove('copied');
}, 2000);
} catch (err) {
console.error('Failed to copy code:', err);
// Fallback: select text
const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(code);
selection.removeAllRanges();
selection.addRange(range);
}
});
}
cleanTerminalOutput(text) {
// Remove common terminal prompts and clean output
return text
.split('\n')
.map(line => {
// Remove prompts like "$ ", " ", "claude-code> "
line = line.replace(/^[\$]\s*/, '').replace(/^claude-code>\s*/, '');
// Remove output/result comments (lines starting with # ✓)
if (line.trim().startsWith('# ✓') ||
line.trim().startsWith('# Start using') ||
line.trim().startsWith('# Your .claude') ||
line.trim().startsWith('# This will') ||
line.includes('Components will be installed') ||
line.includes('directory now contains')) {
return '';
}
return line;
})
.filter(line => line.trim() !== '') // Remove empty lines
.join('\n')
.trim();
}
}
// Initialize when DOM is loaded
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => new CodeCopy());
} else {
new CodeCopy();
}
// Newsletter forms handler
function initNewsletterForm(formId, successId, errorId) {
const form = document.getElementById(formId);
const successMessage = document.getElementById(successId);
const errorMessage = document.getElementById(errorId);
if (!form) return;
const submitButton = form.querySelector('.newsletter-submit');
const submitText = submitButton.querySelector('.submit-text');
form.addEventListener('submit', (e) => {
// Set loading state
submitButton.disabled = true;
submitText.textContent = '...';
// Hide messages initially
successMessage.style.display = 'none';
errorMessage.style.display = 'none';
// Show success message after form submits to iframe
setTimeout(() => {
successMessage.style.display = 'block';
submitButton.disabled = false;
submitText.textContent = '→';
form.reset();
}, 1500);
});
}
// Initialize both newsletter forms
document.addEventListener('DOMContentLoaded', () => {
initNewsletterForm('middle-newsletter-form', 'middle-newsletter-success', 'middle-newsletter-error');
initNewsletterForm('end-newsletter-form', 'end-newsletter-success', 'end-newsletter-error');
});
// Copy Markdown functionality
class MarkdownCopier {
constructor() {
this.setupButton();
}
setupButton() {
const button = document.getElementById('copy-markdown-btn');
if (!button) return;
button.addEventListener('click', async () => {
const markdown = this.extractMarkdown();
try {
await navigator.clipboard.writeText(markdown);
// Update button state
const originalContent = button.innerHTML;
button.innerHTML = `
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
</svg>
`;
button.classList.add('copied');
// Reset after 2 seconds
setTimeout(() => {
button.innerHTML = originalContent;
button.classList.remove('copied');
}, 2000);
} catch (err) {
console.error('Failed to copy markdown:', err);
}
});
}
extractMarkdown() {
const title = document.querySelector('.article-title')?.textContent || '';
const subtitle = document.querySelector('.article-subtitle')?.textContent || '';
const date = document.querySelector('time')?.textContent || '';
const content = document.querySelector('.article-content-full');
let markdown = `# ${title}\n\n`;
if (subtitle) {
markdown += `${subtitle}\n\n`;
}
if (date) {
markdown += `*${date}*\n\n`;
}
if (!content) return markdown;
// Process content elements
const elements = content.children;
for (const element of elements) {
markdown += this.processElement(element) + '\n\n';
}
return markdown.trim();
}
processElement(element) {
const tagName = element.tagName.toLowerCase();
switch (tagName) {
case 'h2':
return `## ${element.textContent}`;
case 'h3':
return `### ${element.textContent}`;
case 'h4':
return `#### ${element.textContent}`;
case 'p':
return element.textContent;
case 'ul':
return this.processList(element, '-');
case 'ol':
return this.processList(element, '1.');
case 'table':
return this.processTable(element);
case 'pre':
return this.processCodeBlock(element);
case 'div':
if (element.classList.contains('code-block')) {
return this.processCodeBlock(element.querySelector('pre'));
}
// Skip newsletter CTAs and other divs
if (element.classList.contains('newsletter-cta') ||
element.classList.contains('explore-components-banner')) {
return '';
}
return element.textContent || '';
case 'img':
const alt = element.getAttribute('alt') || '';
const src = element.getAttribute('src') || '';
return `![${alt}](${src})`;
default:
return element.textContent || '';
}
}
processList(listElement, marker) {
const items = listElement.querySelectorAll('li');
return Array.from(items)
.map(item => `${marker} ${item.textContent}`)
.join('\n');
}
processTable(table) {
const rows = table.querySelectorAll('tr');
if (rows.length === 0) return '';
let markdown = '';
// Process header
const headerRow = rows[0];
const headers = headerRow.querySelectorAll('th, td');
const headerText = Array.from(headers).map(h => h.textContent.trim()).join(' | ');
markdown += `| ${headerText} |\n`;
// Add separator
const separator = Array.from(headers).map(() => '---').join(' | ');
markdown += `| ${separator} |\n`;
// Process body rows
for (let i = 1; i < rows.length; i++) {
const row = rows[i];
const cells = row.querySelectorAll('td, th');
const cellText = Array.from(cells).map(c => c.textContent.trim()).join(' | ');
markdown += `| ${cellText} |\n`;
}
return markdown;
}
processCodeBlock(preElement) {
if (!preElement) return '';
const code = preElement.querySelector('code');
const codeText = code ? code.textContent : preElement.textContent;
// Detect language from parent code-block or code element classes
const codeBlock = preElement.closest('.code-block');
let language = '';
if (codeBlock) {
const languageSpan = codeBlock.querySelector('.code-language');
if (languageSpan) {
language = languageSpan.textContent.toLowerCase();
}
} else if (code && code.className.includes('language-')) {
language = code.className.match(/language-(\w+)/)?.[1] || '';
}
return `\`\`\`${language}\n${codeText}\n\`\`\``;
}
}
// Initialize markdown copier
document.addEventListener('DOMContentLoaded', () => {
new MarkdownCopier();
});
</script>
</body>
</html>