Files
davila7__claude-code-templates/docs/blog/supabase-claude-code-integration/index.html
Daniel Avila c7397fe493 Blog standardization, worktree commands, and .gitignore fix (#341)
* fix: improve Chats Mobile security, CSS quality, and desktop layout

- Sanitize error.message with escapeHtml() to prevent XSS in innerHTML
- Replace !important overrides in .back-btn with proper specificity (.header-btn.back-btn)
- Add desktop side-by-side layout at 769px+ (360px sidebar + flexible chat view)
- Extract 100+ inline styles from analytics modal into reusable CSS classes

https://claude.ai/code/session_01Cfg7GGRKxgJoWiNj1EjWSb

* chore: standardize all 15 blog posts for consistency

Apply unified style guide across all local blog posts:
- Fix copyright year 2025 -> 2026 in all footers
- Remove inline styles from explore-components-banner (use CSS classes)
- Standardize CTA text to "800+" and relative links
- Fix OG tag prefix og:article: -> article: (code-reviewer)
- Fix www.aitmpl.com -> aitmpl.com in OG/Twitter/structured data
- Fix cover image absolute URLs to relative paths (e2b, nextjs)
- Standardize Mermaid theme to orange #F97316 (heygen)
- Fix author name to "Claude Code Templates" (neon-complete)
- Add missing explore-components-banner (security-hooks, vercel-monitor, hackathon)
- Add missing callout boxes (info-box, success-box) where absent
- Standardize CodeCopy JS and MarkdownCopier across posts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add worktree commands and blog post for parallel development

Add 4 worktree slash commands for git worktree lifecycle management:
- /worktree-init: create parallel worktrees from task descriptions
- /worktree-check: show current worktree status and task
- /worktree-deliver: commit, push, and create PR
- /worktree-cleanup: remove merged worktrees and branches

Add blog post explaining the Skill + Commands architecture pattern
where a discoverable Skill (worktree-guide) orchestrates manual
Commands for execution.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: ignore settings.local.json in all subdirectories

Change .claude/settings.local.json to **/settings.local.json
to cover nested paths like docs/.claude/settings.local.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add missing allowed-tools to worktree commands

- worktree-cleanup: add Bash(grep:*) for merge check commands
- worktree-init: add Bash(sed:*) for branch name detection

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-07 20:11:17 -05: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>