chore: import upstream snapshot with attribution
@@ -0,0 +1,363 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This directory contains the static website (aitmpl.com) that serves as the public web interface for browsing and installing Claude Code components. The site is a vanilla JavaScript application that loads component data dynamically and provides an interactive browsing experience for 500+ components including agents, commands, settings, hooks, MCPs, and templates.
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Local Development
|
||||
```bash
|
||||
# Serve locally with any static file server
|
||||
python -m http.server 8000
|
||||
# or
|
||||
npx http-server
|
||||
|
||||
# Open browser to http://localhost:8000
|
||||
```
|
||||
|
||||
### Component Data Generation
|
||||
```bash
|
||||
# From project root - regenerate components.json
|
||||
cd ..
|
||||
python scripts/generate_components_json.py
|
||||
|
||||
# This creates/updates docs/components.json with all components
|
||||
```
|
||||
|
||||
### Deployment
|
||||
- Site is automatically deployed to GitHub Pages when changes are pushed to `docs/` directory
|
||||
- Deployed at: https://aitmpl.com (davila7.github.io/claude-code-templates)
|
||||
- Vercel configuration in `vercel.json` for routing
|
||||
|
||||
## Architecture
|
||||
|
||||
### Data Flow
|
||||
1. **Component Generation**: Python script (`scripts/generate_components_json.py`) scans `cli-tool/components/` and generates `docs/components.json` (~2MB file with embedded content)
|
||||
2. **Static Loading**: Website loads `components.json` on page load (with mobile optimization and progress indicators)
|
||||
3. **Dynamic Rendering**: JavaScript renders component cards based on user filters and search
|
||||
4. **Download Tracking**: Supabase integration tracks component installations via backend API
|
||||
|
||||
### Key Files
|
||||
|
||||
#### HTML Pages
|
||||
- `index.html` - Main page with component browser and search
|
||||
- `component.html` - Individual component detail pages
|
||||
- `plugin.html` - Plugin marketplace interface
|
||||
- `trending.html` - Trending components dashboard
|
||||
- `download-stats.html` - Analytics and download statistics
|
||||
|
||||
#### JavaScript Architecture (`js/`)
|
||||
- `data-loader.js` - Loads and caches components.json, handles data fetching
|
||||
- `search-functionality.js` - Real-time search across all components with fuzzy matching
|
||||
- `cart-manager.js` - Shopping cart for batch component installation
|
||||
- `stack-router.js` - Client-side routing for company stacks and plugins
|
||||
- `index-events.js` - Event handlers for filters, modals, and user interactions
|
||||
- `plugin-page.js` - Plugin marketplace functionality
|
||||
- `trending.js` - Trending components data visualization
|
||||
|
||||
#### CSS (`css/`)
|
||||
- Modular CSS with separate files for components, modals, responsive design
|
||||
- Terminal-themed design system with monospace fonts and CLI aesthetics
|
||||
|
||||
#### Data Files
|
||||
- `components.json` - Main component catalog (~2MB, generated file)
|
||||
- `trending-data.json` - Trending components rankings
|
||||
- `claude-jobs.json` - Job postings data
|
||||
|
||||
### Component System
|
||||
|
||||
#### Component Types
|
||||
Each component type has specific structure:
|
||||
|
||||
**Agents** (162 total)
|
||||
- AI specialists organized by category (Development, Data/AI, Security, Business, etc.)
|
||||
- Stored in `cli-tool/components/agents/{category}/{name}.md`
|
||||
- Installation: `--agent <name>`
|
||||
|
||||
**Commands** (210 total)
|
||||
- Custom slash commands organized by category
|
||||
- Stored in `cli-tool/components/commands/{category}/{name}.md`
|
||||
- Installation: `--command <name>`
|
||||
|
||||
**Settings** (60 total)
|
||||
- Claude Code configuration files
|
||||
- Includes statuslines with accompanying Python scripts
|
||||
- Stored in `cli-tool/components/settings/{category}/{name}.json`
|
||||
- Installation: `--setting <name>`
|
||||
|
||||
**Hooks** (39 total)
|
||||
- Event-driven automation triggers
|
||||
- Stored in `cli-tool/components/hooks/{category}/{name}.md`
|
||||
- Installation: `--hook <name>`
|
||||
|
||||
**MCPs** (55 total)
|
||||
- Model Context Protocol integrations
|
||||
- Stored in `cli-tool/components/mcps/{name}.json`
|
||||
- Installation: `--mcp <name>`
|
||||
|
||||
**Templates** (14 total)
|
||||
- Complete project configurations
|
||||
- Organized by language and framework in `cli-tool/templates/`
|
||||
- Installation: `--template <language>`
|
||||
|
||||
**Plugins** (10 total)
|
||||
- Component bundles with marketplace metadata
|
||||
- Defined in `.claude-plugin/marketplace.json`
|
||||
- Installation: `--plugin <name>`
|
||||
|
||||
### Data Loading Strategy
|
||||
|
||||
The site implements a multi-stage loading strategy for optimal mobile performance:
|
||||
|
||||
1. **Initial Load**: Show loading indicator with progress bar
|
||||
2. **Lazy Parsing**: Load components.json in chunks to avoid blocking
|
||||
3. **Progressive Rendering**: Render visible cards first, lazy-load others
|
||||
4. **Search Indexing**: Build search index in background
|
||||
5. **Caching**: LocalStorage caching for repeat visits
|
||||
|
||||
### Search Functionality
|
||||
|
||||
Real-time search across all components:
|
||||
- **Fuzzy Matching**: Tolerates typos and partial matches
|
||||
- **Multi-field Search**: Searches name, description, category, type
|
||||
- **Filter Integration**: Combines with type filters (agents/commands/etc)
|
||||
- **Category Filtering**: Dynamic category chips based on active filter
|
||||
- **Sorting**: Alphabetical or by download count
|
||||
|
||||
### Shopping Cart System
|
||||
|
||||
Batch installation builder:
|
||||
- Add multiple components across types (agents, commands, settings, hooks, MCPs)
|
||||
- Generates single NPX command for installation
|
||||
- Supports sharing cart via URL parameters
|
||||
- Social sharing to Twitter/Threads
|
||||
|
||||
### Plugin Marketplace
|
||||
|
||||
Plugin system for component bundles:
|
||||
- Each plugin contains agents, commands, and MCPs
|
||||
- Defined in marketplace.json with metadata
|
||||
- Installation installs all plugin components
|
||||
- Examples: `supabase-toolkit`, `nextjs-vercel-pro`, `ai-ml-toolkit`
|
||||
|
||||
### Download Analytics
|
||||
|
||||
Supabase-powered analytics:
|
||||
- Tracks component installations via API endpoints
|
||||
- Aggregates download counts by component and type
|
||||
- Displays trending components and download stats
|
||||
- Real-time updates with background refresh
|
||||
|
||||
## Important Implementation Patterns
|
||||
|
||||
### Component Data Structure
|
||||
|
||||
```javascript
|
||||
// components.json structure
|
||||
{
|
||||
"agents": [{
|
||||
"name": "agent-name",
|
||||
"category": "category-name",
|
||||
"content": "Full markdown content...",
|
||||
"path": "agents/category/agent-name.md",
|
||||
"url": "https://github.com/..."
|
||||
}],
|
||||
"commands": [...],
|
||||
"mcps": [...],
|
||||
"settings": [...],
|
||||
"hooks": [...],
|
||||
"templates": [...],
|
||||
"plugins": [...]
|
||||
}
|
||||
```
|
||||
|
||||
### Dynamic Component Loading
|
||||
|
||||
```javascript
|
||||
// Loading pattern from data-loader.js
|
||||
async function loadAllComponentsData() {
|
||||
const response = await fetch('components.json');
|
||||
const data = await response.json();
|
||||
componentsData = data;
|
||||
collectAvailableCategories();
|
||||
generateUnifiedComponentCards();
|
||||
}
|
||||
```
|
||||
|
||||
### Filter and Category System
|
||||
|
||||
```javascript
|
||||
// Current filter state
|
||||
let currentFilter = 'agents'; // or 'commands', 'mcps', etc.
|
||||
let currentCategoryFilter = 'all'; // or specific category
|
||||
|
||||
// Update filters
|
||||
function handleFilterClick(event, filter) {
|
||||
currentFilter = filter;
|
||||
updateCategorySubFilters();
|
||||
generateUnifiedComponentCards();
|
||||
}
|
||||
```
|
||||
|
||||
### Installation Command Generation
|
||||
|
||||
```javascript
|
||||
// Pattern from cart-manager.js
|
||||
function generateCartCommand(cart) {
|
||||
const flags = [];
|
||||
if (cart.agents.length) flags.push(...cart.agents.map(a => `--agent ${a}`));
|
||||
if (cart.commands.length) flags.push(...cart.commands.map(c => `--command ${c}`));
|
||||
// ... other types
|
||||
return `npx claude-code-templates@latest ${flags.join(' ')} --yes`;
|
||||
}
|
||||
```
|
||||
|
||||
## File Generation Workflow
|
||||
|
||||
### Updating Component Catalog
|
||||
|
||||
When components are added/modified in `cli-tool/components/`:
|
||||
|
||||
1. Run component generation script:
|
||||
```bash
|
||||
cd /path/to/project/root
|
||||
python scripts/generate_components_json.py
|
||||
```
|
||||
|
||||
2. This script:
|
||||
- Scans all directories in `cli-tool/components/`
|
||||
- Scans all templates in `cli-tool/templates/`
|
||||
- Fetches download stats from Supabase
|
||||
- Embeds file content inline
|
||||
- Excludes Python scripts from public listings (but includes them as dependencies)
|
||||
- Generates `docs/components.json`
|
||||
|
||||
3. Commit changes:
|
||||
```bash
|
||||
git add docs/components.json
|
||||
git commit -m "Update component catalog"
|
||||
```
|
||||
|
||||
### Testing Locally
|
||||
|
||||
Before committing:
|
||||
1. Serve site locally: `python -m http.server 8000`
|
||||
2. Test component loading and search
|
||||
3. Verify filter functionality
|
||||
4. Check responsive design on mobile
|
||||
5. Test cart and installation command generation
|
||||
|
||||
## SEO and Metadata
|
||||
|
||||
### Structured Data
|
||||
- Schema.org markup for SoftwareApplication
|
||||
- Open Graph tags for social sharing
|
||||
- Twitter Card metadata
|
||||
- Sitemap.xml for search engines
|
||||
|
||||
### Performance Optimization
|
||||
- Cache-busting query parameters for dynamic content
|
||||
- Progressive loading for large JSON files
|
||||
- LocalStorage caching for repeat visits
|
||||
- Lazy loading for images and components below fold
|
||||
|
||||
## External Integrations
|
||||
|
||||
### GitHub API
|
||||
- Fetches component files dynamically (legacy, now uses components.json)
|
||||
- Links to source files on GitHub
|
||||
|
||||
### Supabase
|
||||
- Tracks component downloads
|
||||
- Aggregates download statistics
|
||||
- Powers trending components
|
||||
|
||||
### Vercel
|
||||
- Handles routing for SPA-style URLs
|
||||
- Configured in `vercel.json`
|
||||
|
||||
### Analytics
|
||||
- Hotjar for user behavior tracking
|
||||
- Counter.dev for page view analytics
|
||||
|
||||
## Deployment Checklist
|
||||
|
||||
Before deploying to production:
|
||||
|
||||
1. **Component Data**: Run `python scripts/generate_components_json.py` to update components.json
|
||||
2. **Test Locally**: Verify all pages load correctly
|
||||
3. **Check Links**: Ensure GitHub links point to correct paths
|
||||
4. **Verify Search**: Test search functionality with various queries
|
||||
5. **Mobile Testing**: Check responsive design on multiple devices
|
||||
6. **Cart Testing**: Verify installation commands are generated correctly
|
||||
7. **Analytics**: Confirm tracking is working
|
||||
|
||||
## Common Development Tasks
|
||||
|
||||
### Adding New Component Type
|
||||
|
||||
1. Update `scripts/generate_components_json.py` to scan new component directory
|
||||
2. Add new filter button in `index.html`
|
||||
3. Add icon configuration in `js/script.js` typeConfig
|
||||
4. Update cart-manager.js to handle new type
|
||||
5. Regenerate components.json
|
||||
|
||||
### Modifying Search Behavior
|
||||
|
||||
Edit `js/search-functionality.js`:
|
||||
- `performSearch()` - Main search logic
|
||||
- `fuzzyMatch()` - Matching algorithm
|
||||
- `highlightMatches()` - Result highlighting
|
||||
|
||||
### Updating Styles
|
||||
|
||||
CSS is modular:
|
||||
- `css/styles.css` - Main styles and variables
|
||||
- `css/components.css` - Component cards and grid
|
||||
- `css/responsive.css` - Mobile breakpoints
|
||||
- `css/modals.css` - Modal dialogs
|
||||
- `css/cart.css` - Shopping cart sidebar
|
||||
|
||||
## Browser Compatibility
|
||||
|
||||
- Modern browsers (Chrome, Firefox, Safari, Edge)
|
||||
- ES6+ JavaScript features (Fetch API, async/await, template literals)
|
||||
- CSS Grid and Flexbox for layout
|
||||
- No build step required (vanilla JavaScript)
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Large File Handling
|
||||
- `components.json` is ~2MB - implement progressive loading
|
||||
- Use virtual scrolling for large component lists
|
||||
- Debounce search input to avoid excessive filtering
|
||||
- Cache parsed JSON in memory after initial load
|
||||
|
||||
### Mobile Optimization
|
||||
- Lazy load images and component content
|
||||
- Reduce initial bundle size
|
||||
- Minimize re-renders during search/filter
|
||||
- Use CSS animations sparingly
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Components Not Loading
|
||||
- Check browser console for fetch errors
|
||||
- Verify components.json is accessible
|
||||
- Ensure CORS is properly configured
|
||||
- Check GitHub API rate limits (if using legacy loading)
|
||||
|
||||
### Search Not Working
|
||||
- Verify search index is built correctly
|
||||
- Check fuzzy match threshold in search-functionality.js
|
||||
- Ensure component data includes searchable fields
|
||||
|
||||
### Cart Not Saving
|
||||
- Check LocalStorage is enabled
|
||||
- Verify cart data structure matches expected format
|
||||
- Clear cache and reload if data structure changed
|
||||
|
||||
This website serves as the primary user interface for the Claude Code Templates ecosystem, providing an intuitive way to discover and install 500+ components through a terminal-themed, responsive web interface.
|
||||
@@ -0,0 +1,49 @@
|
||||
# Claude Code Templates Website
|
||||
|
||||
This directory contains the static website for browsing and installing Claude Code configuration templates.
|
||||
|
||||
## Features
|
||||
|
||||
- **Dynamic Template Loading**: Templates are loaded directly from the GitHub repository's `templates.js` file
|
||||
- **Interactive Cards**: Click any template card to see the installation command
|
||||
- **Responsive Design**: Works on desktop and mobile devices
|
||||
- **Framework Logos**: Uses Devicon CDN for professional programming language and framework logos
|
||||
- **Copy to Clipboard**: Easy command copying with visual feedback
|
||||
|
||||
## Architecture
|
||||
|
||||
The website is built as a static site that:
|
||||
|
||||
1. **Fetches template data** from `cli-tool/src/templates.js` in the GitHub repository
|
||||
2. **Parses the configuration** to extract available languages and frameworks
|
||||
3. **Generates cards dynamically** with proper logos from Devicon CDN
|
||||
4. **Updates automatically** when new templates are added to the repository
|
||||
|
||||
## Files
|
||||
|
||||
- `index.html` - Main HTML structure
|
||||
- `styles.css` - Styling and responsive design
|
||||
- `script.js` - JavaScript for dynamic content loading and interactions
|
||||
- `_config.yml` - Jekyll configuration for GitHub Pages
|
||||
- `README.md` - This documentation
|
||||
|
||||
## GitHub Pages Deployment
|
||||
|
||||
This website is automatically deployed to GitHub Pages when changes are pushed to the `docs/` directory in the main branch.
|
||||
|
||||
Visit: [https://davila7.github.io/claude-code-templates](https://davila7.github.io/claude-code-templates)
|
||||
|
||||
## Development
|
||||
|
||||
To test locally:
|
||||
|
||||
1. Clone the repository
|
||||
2. Navigate to the `docs/` directory
|
||||
3. Serve with any static file server (e.g., `python -m http.server 8000`)
|
||||
4. Open `http://localhost:8000`
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **Devicon CDN**: For programming language and framework logos
|
||||
- **GitHub Raw Content**: For fetching template configurations
|
||||
- **Modern Browser**: Support for ES6+ features and Fetch API
|
||||
@@ -0,0 +1,43 @@
|
||||
# Jekyll configuration for GitHub Pages
|
||||
title: Claude Code Templates
|
||||
description: Browse and install Claude Code configuration templates for different languages and frameworks
|
||||
baseurl: ""
|
||||
url: "https://aitmpl.com"
|
||||
|
||||
# Build settings
|
||||
markdown: kramdown
|
||||
highlighter: rouge
|
||||
sass:
|
||||
sass_dir: _sass
|
||||
style: compressed
|
||||
|
||||
# Exclude files from processing
|
||||
exclude:
|
||||
- README.md
|
||||
- LICENSE
|
||||
- Gemfile
|
||||
- Gemfile.lock
|
||||
|
||||
# Include files in processing
|
||||
include:
|
||||
- _pages
|
||||
|
||||
# GitHub Pages specific settings
|
||||
plugins:
|
||||
- jekyll-feed
|
||||
- jekyll-sitemap
|
||||
|
||||
# Repository information
|
||||
repository: davila7/claude-code-templates
|
||||
|
||||
# Social media and SEO
|
||||
twitter:
|
||||
username:
|
||||
card: summary_large_image
|
||||
|
||||
# Google Analytics (optional - can be added later)
|
||||
# google_analytics:
|
||||
|
||||
# Additional settings
|
||||
timezone: UTC
|
||||
encoding: utf-8
|
||||
@@ -0,0 +1,91 @@
|
||||
# API Directory - Documentation
|
||||
|
||||
This directory contains **static files** for the Claude Code Templates project deployed on Vercel.
|
||||
|
||||
## 📁 Structure Overview
|
||||
|
||||
```
|
||||
docs/api/
|
||||
├── README.md # This file
|
||||
└── agents.json # Static JSON file for agent queries
|
||||
```
|
||||
|
||||
## 🔍 Why This Structure?
|
||||
|
||||
The project uses **TWO separate API directories**:
|
||||
|
||||
### 1. `/api/` (Root) - Serverless Functions ✅
|
||||
```
|
||||
/api/
|
||||
├── track-download-supabase.js # Serverless function
|
||||
└── package.json # Dependencies
|
||||
```
|
||||
|
||||
- **Purpose:** Vercel serverless functions (Edge Functions)
|
||||
- **Auto-detected by Vercel:** Functions in `/api/` are automatically deployed
|
||||
- **Runtime:** Node.js with ES Modules
|
||||
- **URL:** `https://www.aitmpl.com/api/track-download-supabase`
|
||||
|
||||
### 2. `/docs/api/` - Static Files ✅
|
||||
```
|
||||
/docs/api/
|
||||
├── README.md # This documentation
|
||||
└── agents.json # Static data file
|
||||
```
|
||||
|
||||
- **Purpose:** Static JSON files served with the frontend
|
||||
- **Served from:** `outputDirectory: "docs"` in `vercel.json`
|
||||
- **URL:** `https://www.aitmpl.com/api/agents.json`
|
||||
|
||||
## ⚠️ Important: Why Two Directories?
|
||||
|
||||
Vercel has a specific requirement:
|
||||
- **Serverless functions MUST be in `/api/`** at the project root
|
||||
- **Static files can be in `/docs/api/`** when using `outputDirectory: "docs"`
|
||||
|
||||
You **cannot** put serverless functions in `/docs/api/` - Vercel will serve them as static HTML files instead of executing them.
|
||||
|
||||
---
|
||||
|
||||
## 📄 Files in This Directory
|
||||
|
||||
### `agents.json` (Static File)
|
||||
- **Type:** Static JSON data
|
||||
- **Purpose:** Contains the list of available agents for the frontend
|
||||
- **Accessed by:** Frontend application via fetch
|
||||
- **URL:** `https://www.aitmpl.com/api/agents.json`
|
||||
- **Size:** ~25KB
|
||||
- **Updates:** Generated by `/scripts/generate_components_json.py` script
|
||||
|
||||
**Usage:**
|
||||
```javascript
|
||||
fetch('https://www.aitmpl.com/api/agents.json')
|
||||
.then(res => res.json())
|
||||
.then(data => console.log(data));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Related Files
|
||||
|
||||
### Serverless Functions (in `/api/`)
|
||||
|
||||
See `/api/README.md` for documentation on:
|
||||
- `track-download-supabase.js` - Download tracking endpoint
|
||||
- Environment variables required
|
||||
- Request/response formats
|
||||
- Database schema
|
||||
|
||||
---
|
||||
|
||||
## 📝 Best Practices
|
||||
|
||||
1. **Static Files:** Keep in `/docs/api/`
|
||||
2. **Serverless Functions:** Keep in `/api/` (root)
|
||||
3. **Never Mix:** Don't put serverless functions in `/docs/api/`
|
||||
4. **Version Control:** Commit both directories separately
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** October 22, 2025
|
||||
**Maintained by:** Claude Code Templates Team
|
||||
@@ -0,0 +1,942 @@
|
||||
{
|
||||
"agents": [
|
||||
{
|
||||
"name": "ai-ethics-advisor",
|
||||
"path": "ai-specialists/ai-ethics-advisor",
|
||||
"category": "ai-specialists",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "hackathon-ai-strategist",
|
||||
"path": "ai-specialists/hackathon-ai-strategist",
|
||||
"category": "ai-specialists",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "llms-maintainer",
|
||||
"path": "ai-specialists/llms-maintainer",
|
||||
"category": "ai-specialists",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "model-evaluator",
|
||||
"path": "ai-specialists/model-evaluator",
|
||||
"category": "ai-specialists",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "prompt-engineer",
|
||||
"path": "ai-specialists/prompt-engineer",
|
||||
"category": "ai-specialists",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "search-specialist",
|
||||
"path": "ai-specialists/search-specialist",
|
||||
"category": "ai-specialists",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "task-decomposition-expert",
|
||||
"path": "ai-specialists/task-decomposition-expert",
|
||||
"category": "ai-specialists",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "graphql-architect",
|
||||
"path": "api-graphql/graphql-architect",
|
||||
"category": "api-graphql",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "graphql-performance-optimizer",
|
||||
"path": "api-graphql/graphql-performance-optimizer",
|
||||
"category": "api-graphql",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "graphql-security-specialist",
|
||||
"path": "api-graphql/graphql-security-specialist",
|
||||
"category": "api-graphql",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "smart-contract-auditor",
|
||||
"path": "blockchain-web3/smart-contract-auditor",
|
||||
"category": "blockchain-web3",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "smart-contract-specialist",
|
||||
"path": "blockchain-web3/smart-contract-specialist",
|
||||
"category": "blockchain-web3",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "web3-integration-specialist",
|
||||
"path": "blockchain-web3/web3-integration-specialist",
|
||||
"category": "blockchain-web3",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "business-analyst",
|
||||
"path": "business-marketing/business-analyst",
|
||||
"category": "business-marketing",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "content-marketer",
|
||||
"path": "business-marketing/content-marketer",
|
||||
"category": "business-marketing",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "customer-support",
|
||||
"path": "business-marketing/customer-support",
|
||||
"category": "business-marketing",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "legal-advisor",
|
||||
"path": "business-marketing/legal-advisor",
|
||||
"category": "business-marketing",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "marketing-attribution-analyst",
|
||||
"path": "business-marketing/marketing-attribution-analyst",
|
||||
"category": "business-marketing",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "payment-integration",
|
||||
"path": "business-marketing/payment-integration",
|
||||
"category": "business-marketing",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "product-strategist",
|
||||
"path": "business-marketing/product-strategist",
|
||||
"category": "business-marketing",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "risk-manager",
|
||||
"path": "business-marketing/risk-manager",
|
||||
"category": "business-marketing",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "sales-automator",
|
||||
"path": "business-marketing/sales-automator",
|
||||
"category": "business-marketing",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "ai-engineer",
|
||||
"path": "data-ai/ai-engineer",
|
||||
"category": "data-ai",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "computer-vision-engineer",
|
||||
"path": "data-ai/computer-vision-engineer",
|
||||
"category": "data-ai",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "data-engineer",
|
||||
"path": "data-ai/data-engineer",
|
||||
"category": "data-ai",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "data-scientist",
|
||||
"path": "data-ai/data-scientist",
|
||||
"category": "data-ai",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "ml-engineer",
|
||||
"path": "data-ai/ml-engineer",
|
||||
"category": "data-ai",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "mlops-engineer",
|
||||
"path": "data-ai/mlops-engineer",
|
||||
"category": "data-ai",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "nlp-engineer",
|
||||
"path": "data-ai/nlp-engineer",
|
||||
"category": "data-ai",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "quant-analyst",
|
||||
"path": "data-ai/quant-analyst",
|
||||
"category": "data-ai",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "database-admin",
|
||||
"path": "database/database-admin",
|
||||
"category": "database",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "database-architect",
|
||||
"path": "database/database-architect",
|
||||
"category": "database",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "database-optimization",
|
||||
"path": "database/database-optimization",
|
||||
"category": "database",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "database-optimizer",
|
||||
"path": "database/database-optimizer",
|
||||
"category": "database",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "nosql-specialist",
|
||||
"path": "database/nosql-specialist",
|
||||
"category": "database",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "supabase-schema-architect",
|
||||
"path": "database/supabase-schema-architect",
|
||||
"category": "database",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "academic-researcher",
|
||||
"path": "deep-research-team/academic-researcher",
|
||||
"category": "deep-research-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "agent-overview",
|
||||
"path": "deep-research-team/agent-overview",
|
||||
"category": "deep-research-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "competitive-intelligence-analyst",
|
||||
"path": "deep-research-team/competitive-intelligence-analyst",
|
||||
"category": "deep-research-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "data-analyst",
|
||||
"path": "deep-research-team/data-analyst",
|
||||
"category": "deep-research-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "fact-checker",
|
||||
"path": "deep-research-team/fact-checker",
|
||||
"category": "deep-research-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "query-clarifier",
|
||||
"path": "deep-research-team/query-clarifier",
|
||||
"category": "deep-research-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "report-generator",
|
||||
"path": "deep-research-team/report-generator",
|
||||
"category": "deep-research-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "research-brief-generator",
|
||||
"path": "deep-research-team/research-brief-generator",
|
||||
"category": "deep-research-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "research-coordinator",
|
||||
"path": "deep-research-team/research-coordinator",
|
||||
"category": "deep-research-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "research-orchestrator",
|
||||
"path": "deep-research-team/research-orchestrator",
|
||||
"category": "deep-research-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "research-synthesizer",
|
||||
"path": "deep-research-team/research-synthesizer",
|
||||
"category": "deep-research-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "technical-researcher",
|
||||
"path": "deep-research-team/technical-researcher",
|
||||
"category": "deep-research-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "backend-architect",
|
||||
"path": "development-team/backend-architect",
|
||||
"category": "development-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cli-ui-designer",
|
||||
"path": "development-team/cli-ui-designer",
|
||||
"category": "development-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "devops-engineer",
|
||||
"path": "development-team/devops-engineer",
|
||||
"category": "development-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "frontend-developer",
|
||||
"path": "development-team/frontend-developer",
|
||||
"category": "development-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "fullstack-developer",
|
||||
"path": "development-team/fullstack-developer",
|
||||
"category": "development-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "ios-developer",
|
||||
"path": "development-team/ios-developer",
|
||||
"category": "development-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "mobile-developer",
|
||||
"path": "development-team/mobile-developer",
|
||||
"category": "development-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "ui-ux-designer",
|
||||
"path": "development-team/ui-ux-designer",
|
||||
"category": "development-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "code-reviewer",
|
||||
"path": "development-tools/code-reviewer",
|
||||
"category": "development-tools",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "command-expert",
|
||||
"path": "development-tools/command-expert",
|
||||
"category": "development-tools",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "context-manager",
|
||||
"path": "development-tools/context-manager",
|
||||
"category": "development-tools",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "debugger",
|
||||
"path": "development-tools/debugger",
|
||||
"category": "development-tools",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "dx-optimizer",
|
||||
"path": "development-tools/dx-optimizer",
|
||||
"category": "development-tools",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "error-detective",
|
||||
"path": "development-tools/error-detective",
|
||||
"category": "development-tools",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "mcp-expert",
|
||||
"path": "development-tools/mcp-expert",
|
||||
"category": "development-tools",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "performance-profiler",
|
||||
"path": "development-tools/performance-profiler",
|
||||
"category": "development-tools",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "test-engineer",
|
||||
"path": "development-tools/test-engineer",
|
||||
"category": "development-tools",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloud-architect",
|
||||
"path": "devops-infrastructure/cloud-architect",
|
||||
"category": "devops-infrastructure",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "deployment-engineer",
|
||||
"path": "devops-infrastructure/deployment-engineer",
|
||||
"category": "devops-infrastructure",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "devops-troubleshooter",
|
||||
"path": "devops-infrastructure/devops-troubleshooter",
|
||||
"category": "devops-infrastructure",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "monitoring-specialist",
|
||||
"path": "devops-infrastructure/monitoring-specialist",
|
||||
"category": "devops-infrastructure",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "network-engineer",
|
||||
"path": "devops-infrastructure/network-engineer",
|
||||
"category": "devops-infrastructure",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "security-engineer",
|
||||
"path": "devops-infrastructure/security-engineer",
|
||||
"category": "devops-infrastructure",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "terraform-specialist",
|
||||
"path": "devops-infrastructure/terraform-specialist",
|
||||
"category": "devops-infrastructure",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "vercel-deployment-specialist",
|
||||
"path": "devops-infrastructure/vercel-deployment-specialist",
|
||||
"category": "devops-infrastructure",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "api-documenter",
|
||||
"path": "documentation/api-documenter",
|
||||
"category": "documentation",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "changelog-generator",
|
||||
"path": "documentation/changelog-generator",
|
||||
"category": "documentation",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "docusaurus-expert",
|
||||
"path": "documentation/docusaurus-expert",
|
||||
"category": "documentation",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "technical-writer",
|
||||
"path": "documentation/technical-writer",
|
||||
"category": "documentation",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "agent-expert",
|
||||
"path": "expert-advisors/agent-expert",
|
||||
"category": "expert-advisors",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "architect-review",
|
||||
"path": "expert-advisors/architect-review",
|
||||
"category": "expert-advisors",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "dependency-manager",
|
||||
"path": "expert-advisors/dependency-manager",
|
||||
"category": "expert-advisors",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "documentation-expert",
|
||||
"path": "expert-advisors/documentation-expert",
|
||||
"category": "expert-advisors",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "audio-mixer",
|
||||
"path": "ffmpeg-clip-team/audio-mixer",
|
||||
"category": "ffmpeg-clip-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "audio-quality-controller",
|
||||
"path": "ffmpeg-clip-team/audio-quality-controller",
|
||||
"category": "ffmpeg-clip-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "podcast-content-analyzer",
|
||||
"path": "ffmpeg-clip-team/podcast-content-analyzer",
|
||||
"category": "ffmpeg-clip-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "podcast-metadata-specialist",
|
||||
"path": "ffmpeg-clip-team/podcast-metadata-specialist",
|
||||
"category": "ffmpeg-clip-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "podcast-transcriber",
|
||||
"path": "ffmpeg-clip-team/podcast-transcriber",
|
||||
"category": "ffmpeg-clip-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "social-media-clip-creator",
|
||||
"path": "ffmpeg-clip-team/social-media-clip-creator",
|
||||
"category": "ffmpeg-clip-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "timestamp-precision-specialist",
|
||||
"path": "ffmpeg-clip-team/timestamp-precision-specialist",
|
||||
"category": "ffmpeg-clip-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "video-editor",
|
||||
"path": "ffmpeg-clip-team/video-editor",
|
||||
"category": "ffmpeg-clip-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "3d-artist",
|
||||
"path": "game-development/3d-artist",
|
||||
"category": "game-development",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "game-designer",
|
||||
"path": "game-development/game-designer",
|
||||
"category": "game-development",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "unity-game-developer",
|
||||
"path": "game-development/unity-game-developer",
|
||||
"category": "game-development",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "unreal-engine-developer",
|
||||
"path": "game-development/unreal-engine-developer",
|
||||
"category": "game-development",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "mcp-deployment-orchestrator",
|
||||
"path": "mcp-dev-team/mcp-deployment-orchestrator",
|
||||
"category": "mcp-dev-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "mcp-integration-engineer",
|
||||
"path": "mcp-dev-team/mcp-integration-engineer",
|
||||
"category": "mcp-dev-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "mcp-protocol-specialist",
|
||||
"path": "mcp-dev-team/mcp-protocol-specialist",
|
||||
"category": "mcp-dev-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "mcp-registry-navigator",
|
||||
"path": "mcp-dev-team/mcp-registry-navigator",
|
||||
"category": "mcp-dev-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "mcp-security-auditor",
|
||||
"path": "mcp-dev-team/mcp-security-auditor",
|
||||
"category": "mcp-dev-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "mcp-server-architect",
|
||||
"path": "mcp-dev-team/mcp-server-architect",
|
||||
"category": "mcp-dev-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "mcp-testing-engineer",
|
||||
"path": "mcp-dev-team/mcp-testing-engineer",
|
||||
"category": "mcp-dev-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "architecture-modernizer",
|
||||
"path": "modernization/architecture-modernizer",
|
||||
"category": "modernization",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cloud-migration-specialist",
|
||||
"path": "modernization/cloud-migration-specialist",
|
||||
"category": "modernization",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "legacy-modernizer",
|
||||
"path": "modernization/legacy-modernizer",
|
||||
"category": "modernization",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "connection-agent",
|
||||
"path": "obsidian-ops-team/connection-agent",
|
||||
"category": "obsidian-ops-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "content-curator",
|
||||
"path": "obsidian-ops-team/content-curator",
|
||||
"category": "obsidian-ops-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "metadata-agent",
|
||||
"path": "obsidian-ops-team/metadata-agent",
|
||||
"category": "obsidian-ops-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "moc-agent",
|
||||
"path": "obsidian-ops-team/moc-agent",
|
||||
"category": "obsidian-ops-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "review-agent",
|
||||
"path": "obsidian-ops-team/review-agent",
|
||||
"category": "obsidian-ops-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "tag-agent",
|
||||
"path": "obsidian-ops-team/tag-agent",
|
||||
"category": "obsidian-ops-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "vault-optimizer",
|
||||
"path": "obsidian-ops-team/vault-optimizer",
|
||||
"category": "obsidian-ops-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "document-structure-analyzer",
|
||||
"path": "ocr-extraction-team/document-structure-analyzer",
|
||||
"category": "ocr-extraction-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "markdown-syntax-formatter",
|
||||
"path": "ocr-extraction-team/markdown-syntax-formatter",
|
||||
"category": "ocr-extraction-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "ocr-grammar-fixer",
|
||||
"path": "ocr-extraction-team/ocr-grammar-fixer",
|
||||
"category": "ocr-extraction-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "ocr-preprocessing-optimizer",
|
||||
"path": "ocr-extraction-team/ocr-preprocessing-optimizer",
|
||||
"category": "ocr-extraction-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "ocr-quality-assurance",
|
||||
"path": "ocr-extraction-team/ocr-quality-assurance",
|
||||
"category": "ocr-extraction-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "text-comparison-validator",
|
||||
"path": "ocr-extraction-team/text-comparison-validator",
|
||||
"category": "ocr-extraction-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "visual-analysis-ocr",
|
||||
"path": "ocr-extraction-team/visual-analysis-ocr",
|
||||
"category": "ocr-extraction-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "load-testing-specialist",
|
||||
"path": "performance-testing/load-testing-specialist",
|
||||
"category": "performance-testing",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "performance-engineer",
|
||||
"path": "performance-testing/performance-engineer",
|
||||
"category": "performance-testing",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "react-performance-optimization",
|
||||
"path": "performance-testing/react-performance-optimization",
|
||||
"category": "performance-testing",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "test-automator",
|
||||
"path": "performance-testing/test-automator",
|
||||
"category": "performance-testing",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "web-vitals-optimizer",
|
||||
"path": "performance-testing/web-vitals-optimizer",
|
||||
"category": "performance-testing",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "academic-research-synthesizer",
|
||||
"path": "podcast-creator-team/academic-research-synthesizer",
|
||||
"category": "podcast-creator-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "comprehensive-researcher",
|
||||
"path": "podcast-creator-team/comprehensive-researcher",
|
||||
"category": "podcast-creator-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "episode-orchestrator",
|
||||
"path": "podcast-creator-team/episode-orchestrator",
|
||||
"category": "podcast-creator-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "guest-outreach-coordinator",
|
||||
"path": "podcast-creator-team/guest-outreach-coordinator",
|
||||
"category": "podcast-creator-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "market-research-analyst",
|
||||
"path": "podcast-creator-team/market-research-analyst",
|
||||
"category": "podcast-creator-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "podcast-editor",
|
||||
"path": "podcast-creator-team/podcast-editor",
|
||||
"category": "podcast-creator-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "podcast-trend-scout",
|
||||
"path": "podcast-creator-team/podcast-trend-scout",
|
||||
"category": "podcast-creator-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "project-supervisor-orchestrator",
|
||||
"path": "podcast-creator-team/project-supervisor-orchestrator",
|
||||
"category": "podcast-creator-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "seo-podcast-optimizer",
|
||||
"path": "podcast-creator-team/seo-podcast-optimizer",
|
||||
"category": "podcast-creator-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "social-media-copywriter",
|
||||
"path": "podcast-creator-team/social-media-copywriter",
|
||||
"category": "podcast-creator-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "twitter-ai-influencer-manager",
|
||||
"path": "podcast-creator-team/twitter-ai-influencer-manager",
|
||||
"category": "podcast-creator-team",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "c-pro",
|
||||
"path": "programming-languages/c-pro",
|
||||
"category": "programming-languages",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "c-sharp-pro",
|
||||
"path": "programming-languages/c-sharp-pro",
|
||||
"category": "programming-languages",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "cpp-pro",
|
||||
"path": "programming-languages/cpp-pro",
|
||||
"category": "programming-languages",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "golang-pro",
|
||||
"path": "programming-languages/golang-pro",
|
||||
"category": "programming-languages",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "javascript-pro",
|
||||
"path": "programming-languages/javascript-pro",
|
||||
"category": "programming-languages",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "php-pro",
|
||||
"path": "programming-languages/php-pro",
|
||||
"category": "programming-languages",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "python-pro",
|
||||
"path": "programming-languages/python-pro",
|
||||
"category": "programming-languages",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "rust-pro",
|
||||
"path": "programming-languages/rust-pro",
|
||||
"category": "programming-languages",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "shell-scripting-pro",
|
||||
"path": "programming-languages/shell-scripting-pro",
|
||||
"category": "programming-languages",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "sql-pro",
|
||||
"path": "programming-languages/sql-pro",
|
||||
"category": "programming-languages",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "typescript-pro",
|
||||
"path": "programming-languages/typescript-pro",
|
||||
"category": "programming-languages",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "supabase-realtime-optimizer",
|
||||
"path": "realtime/supabase-realtime-optimizer",
|
||||
"category": "realtime",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "api-security-audit",
|
||||
"path": "security/api-security-audit",
|
||||
"category": "security",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "compliance-specialist",
|
||||
"path": "security/compliance-specialist",
|
||||
"category": "security",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "incident-responder",
|
||||
"path": "security/incident-responder",
|
||||
"category": "security",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "penetration-tester",
|
||||
"path": "security/penetration-tester",
|
||||
"category": "security",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "security-auditor",
|
||||
"path": "security/security-auditor",
|
||||
"category": "security",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "nextjs-architecture-expert",
|
||||
"path": "web-tools/nextjs-architecture-expert",
|
||||
"category": "web-tools",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "react-performance-optimizer",
|
||||
"path": "web-tools/react-performance-optimizer",
|
||||
"category": "web-tools",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "seo-analyzer",
|
||||
"path": "web-tools/seo-analyzer",
|
||||
"category": "web-tools",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "url-context-validator",
|
||||
"path": "web-tools/url-context-validator",
|
||||
"category": "web-tools",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "url-link-extractor",
|
||||
"path": "web-tools/url-link-extractor",
|
||||
"category": "web-tools",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "web-accessibility-checker",
|
||||
"path": "web-tools/web-accessibility-checker",
|
||||
"category": "web-tools",
|
||||
"description": ""
|
||||
}
|
||||
],
|
||||
"version": "1.0.0",
|
||||
"total": 156
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
# Blog Management System
|
||||
|
||||
Este blog utiliza un sistema dinámico de gestión de artículos basado en JSON.
|
||||
|
||||
## 📁 Estructura de Archivos
|
||||
|
||||
```
|
||||
docs/blog/
|
||||
├── index.html # Página principal del blog
|
||||
├── blog-articles.json # Base de datos de artículos
|
||||
├── js/
|
||||
│ └── blog-loader.js # Cargador dinámico de artículos
|
||||
└── assets/ # Imágenes y recursos
|
||||
```
|
||||
|
||||
## 🚀 Cómo Agregar Nuevos Artículos
|
||||
|
||||
Para agregar un nuevo artículo al blog, simplemente edita el archivo `blog-articles.json`:
|
||||
|
||||
### 1. Abre el archivo `blog-articles.json`
|
||||
|
||||
### 2. Agrega un nuevo objeto en el array `articles`:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "unique-article-id",
|
||||
"title": "Título del Artículo",
|
||||
"description": "Descripción breve del artículo (1-2 líneas)",
|
||||
"url": "https://medium.com/@tu-usuario/url-del-articulo",
|
||||
"image": "https://www.aitmpl.com/blog/assets/imagen-cover.png",
|
||||
"category": "Categoría",
|
||||
"publishDate": "2025-02-10",
|
||||
"readTime": "5 min read",
|
||||
"tags": ["Tag1", "Tag2", "Tag3"],
|
||||
"platform": "medium",
|
||||
"order": 5
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Actualiza los metadatos al final del archivo:
|
||||
|
||||
```json
|
||||
"metadata": {
|
||||
"lastUpdated": "2025-02-10",
|
||||
"totalArticles": 5,
|
||||
"platforms": {
|
||||
"medium": 5,
|
||||
"local": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📝 Campos Explicados
|
||||
|
||||
| Campo | Tipo | Descripción | Ejemplo |
|
||||
|-------|------|-------------|---------|
|
||||
| `id` | string | Identificador único del artículo | `"supabase-integration"` |
|
||||
| `title` | string | Título completo del artículo | `"Claude Code + Supabase Integration"` |
|
||||
| `description` | string | Descripción breve (1-2 líneas) | `"Learn how to integrate..."` |
|
||||
| `url` | string | URL completa del artículo | `"https://medium.com/@..."` |
|
||||
| `image` | string | URL de la imagen de portada | `"https://www.aitmpl.com/blog/assets/..."` |
|
||||
| `category` | string | Categoría del artículo | `"Database"`, `"Development"`, etc. |
|
||||
| `publishDate` | string | Fecha de publicación (YYYY-MM-DD) | `"2025-02-10"` |
|
||||
| `readTime` | string | Tiempo estimado de lectura | `"5 min read"` |
|
||||
| `tags` | array | Array de tags/etiquetas | `["Supabase", "Database", "MCP"]` |
|
||||
| `difficulty` | string | Nivel de dificultad del artículo | `"basic"`, `"intermediate"`, `"advanced"` |
|
||||
| `order` | number | Orden de aparición (menor = primero) | `1`, `2`, `3`, etc. |
|
||||
|
||||
## 🎨 Categorías Recomendadas
|
||||
|
||||
- **Database** - Artículos sobre bases de datos
|
||||
- **Development** - Desarrollo general
|
||||
- **Cloud & AI** - Cloud computing e inteligencia artificial
|
||||
- **Documentation** - Documentación y guías
|
||||
- **Frontend** - Desarrollo frontend
|
||||
- **Backend** - Desarrollo backend
|
||||
- **DevOps** - DevOps y CI/CD
|
||||
- **Security** - Seguridad
|
||||
|
||||
## 🎓 Niveles de Dificultad
|
||||
|
||||
Clasifica cada artículo según su complejidad:
|
||||
|
||||
- **`basic`** - Verde (#00D084): Artículos introductorios, tutoriales para principiantes
|
||||
- **`intermediate`** - Naranja (#FFA500): Requiere conocimientos previos, configuraciones más avanzadas
|
||||
- **`advanced`** - Rojo (#FF4444): Temas complejos, integraciones avanzadas, arquitecturas elaboradas
|
||||
|
||||
El badge de dificultad se muestra automáticamente en la metadata del artículo.
|
||||
|
||||
## 🏷️ Tags Recomendados
|
||||
|
||||
Usa tags específicos y relevantes:
|
||||
- Tecnologías: `Supabase`, `Next.js`, `React`, `Node.js`
|
||||
- Conceptos: `Agents`, `Commands`, `MCP`, `Automation`
|
||||
- Herramientas: `Git`, `Docker`, `Kubernetes`
|
||||
- Plataformas: `Vercel`, `Google Cloud`, `AWS`
|
||||
|
||||
## 📊 Orden de Artículos
|
||||
|
||||
Los artículos se muestran según el campo `order`:
|
||||
- **Menor número = Aparece primero**
|
||||
- Usa números consecutivos: 1, 2, 3, 4, 5...
|
||||
- Para reordenar, simplemente cambia los números
|
||||
|
||||
## 🔄 Proceso de Publicación
|
||||
|
||||
1. **Publica tu artículo** en Medium u otra plataforma
|
||||
2. **Copia la URL** del artículo publicado
|
||||
3. **Edita** `blog-articles.json`
|
||||
4. **Agrega** el nuevo artículo con todos los campos
|
||||
5. **Actualiza** los metadatos (totalArticles, lastUpdated)
|
||||
6. **Commit y push** los cambios
|
||||
|
||||
```bash
|
||||
git add docs/blog/blog-articles.json
|
||||
git commit -m "Add new blog article: [Título]"
|
||||
git push
|
||||
```
|
||||
|
||||
## 🎯 Badges de Dificultad
|
||||
|
||||
El sistema automáticamente muestra un badge de dificultad con colores específicos:
|
||||
- **Basic** (Verde): Para artículos introductorios y guías básicas
|
||||
- **Intermediate** (Naranja): Para configuraciones más avanzadas
|
||||
- **Advanced** (Rojo): Para temas complejos y arquitecturas avanzadas
|
||||
|
||||
## ✨ Características
|
||||
|
||||
- **Carga Dinámica**: Los artículos se cargan automáticamente desde JSON
|
||||
- **Ordenamiento**: Controla el orden con el campo `order`
|
||||
- **Seguridad**: HTML escapado automáticamente (prevención XSS)
|
||||
- **Performance**: Lazy loading de imágenes
|
||||
- **Responsive**: Diseño adaptable a móviles
|
||||
- **Loading States**: Indicadores de carga mientras se obtienen los datos
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Los artículos no se cargan
|
||||
1. Verifica que `blog-articles.json` esté en la raíz de `/docs/blog/`
|
||||
2. Revisa la consola del navegador para errores
|
||||
3. Asegúrate de que el JSON sea válido (usa un validador JSON online)
|
||||
|
||||
### Error de JSON inválido
|
||||
- Verifica que todas las comillas sean dobles (`"`)
|
||||
- Asegúrate de que no falten comas entre objetos
|
||||
- El último elemento del array no debe tener coma final
|
||||
|
||||
### Las imágenes no se muestran
|
||||
- Verifica que las URLs de las imágenes sean accesibles
|
||||
- Usa URLs completas (no relativas)
|
||||
- Asegúrate de que las imágenes estén en `/docs/blog/assets/`
|
||||
|
||||
## 📱 Testing Local
|
||||
|
||||
Para probar localmente:
|
||||
|
||||
```bash
|
||||
cd docs/blog
|
||||
python -m http.server 8000
|
||||
# o
|
||||
npx http-server
|
||||
```
|
||||
|
||||
Luego abre: `http://localhost:8000`
|
||||
|
||||
## 🎉 Ejemplo Completo
|
||||
|
||||
```json
|
||||
{
|
||||
"articles": [
|
||||
{
|
||||
"id": "my-new-article",
|
||||
"title": "Amazing New Feature in Claude Code",
|
||||
"description": "Discover how to use the latest feature that will revolutionize your workflow.",
|
||||
"url": "https://medium.com/@dan.avila7/amazing-new-feature-12345",
|
||||
"image": "https://www.aitmpl.com/blog/assets/new-feature-cover.png",
|
||||
"category": "Development",
|
||||
"publishDate": "2025-02-10",
|
||||
"readTime": "6 min read",
|
||||
"tags": ["Claude Code", "Productivity", "Automation"],
|
||||
"difficulty": "intermediate",
|
||||
"order": 1
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"lastUpdated": "2025-02-10",
|
||||
"totalArticles": 5,
|
||||
"difficultyLevels": {
|
||||
"basic": 2,
|
||||
"intermediate": 2,
|
||||
"advanced": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**¡Eso es todo!** Ahora puedes agregar artículos fácilmente sin tocar el HTML. 🚀
|
||||
@@ -0,0 +1,119 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630" fill="none">
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#0a0a0f"/>
|
||||
<stop offset="50%" style="stop-color:#111127"/>
|
||||
<stop offset="100%" style="stop-color:#0a0a0f"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="accent1" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" style="stop-color:#6366f1"/>
|
||||
<stop offset="100%" style="stop-color:#8b5cf6"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="accent2" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" style="stop-color:#22c55e"/>
|
||||
<stop offset="100%" style="stop-color:#4ade80"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="accent3" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" style="stop-color:#eab308"/>
|
||||
<stop offset="100%" style="stop-color:#facc15"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="line-grad" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" style="stop-color:#6366f1;stop-opacity:0"/>
|
||||
<stop offset="50%" style="stop-color:#6366f1;stop-opacity:0.6"/>
|
||||
<stop offset="100%" style="stop-color:#6366f1;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="1200" height="630" fill="url(#bg)"/>
|
||||
|
||||
<!-- Grid pattern -->
|
||||
<g opacity="0.06">
|
||||
<line x1="0" y1="0" x2="0" y2="630" stroke="#6366f1" stroke-width="1" transform="translate(100,0)"/>
|
||||
<line x1="0" y1="0" x2="0" y2="630" stroke="#6366f1" stroke-width="1" transform="translate(200,0)"/>
|
||||
<line x1="0" y1="0" x2="0" y2="630" stroke="#6366f1" stroke-width="1" transform="translate(300,0)"/>
|
||||
<line x1="0" y1="0" x2="0" y2="630" stroke="#6366f1" stroke-width="1" transform="translate(400,0)"/>
|
||||
<line x1="0" y1="0" x2="0" y2="630" stroke="#6366f1" stroke-width="1" transform="translate(500,0)"/>
|
||||
<line x1="0" y1="0" x2="0" y2="630" stroke="#6366f1" stroke-width="1" transform="translate(600,0)"/>
|
||||
<line x1="0" y1="0" x2="0" y2="630" stroke="#6366f1" stroke-width="1" transform="translate(700,0)"/>
|
||||
<line x1="0" y1="0" x2="0" y2="630" stroke="#6366f1" stroke-width="1" transform="translate(800,0)"/>
|
||||
<line x1="0" y1="0" x2="0" y2="630" stroke="#6366f1" stroke-width="1" transform="translate(900,0)"/>
|
||||
<line x1="0" y1="0" x2="0" y2="630" stroke="#6366f1" stroke-width="1" transform="translate(1000,0)"/>
|
||||
<line x1="0" y1="0" x2="0" y2="630" stroke="#6366f1" stroke-width="1" transform="translate(1100,0)"/>
|
||||
<line x1="0" y1="0" x2="1200" y2="0" stroke="#6366f1" stroke-width="1" transform="translate(0,100)"/>
|
||||
<line x1="0" y1="0" x2="1200" y2="0" stroke="#6366f1" stroke-width="1" transform="translate(0,200)"/>
|
||||
<line x1="0" y1="0" x2="1200" y2="0" stroke="#6366f1" stroke-width="1" transform="translate(0,300)"/>
|
||||
<line x1="0" y1="0" x2="1200" y2="0" stroke="#6366f1" stroke-width="1" transform="translate(0,400)"/>
|
||||
<line x1="0" y1="0" x2="1200" y2="0" stroke="#6366f1" stroke-width="1" transform="translate(0,500)"/>
|
||||
</g>
|
||||
|
||||
<!-- Team Lead node (center top) -->
|
||||
<g transform="translate(600, 180)">
|
||||
<circle cx="0" cy="0" r="45" fill="none" stroke="url(#accent1)" stroke-width="2.5" opacity="0.9"/>
|
||||
<circle cx="0" cy="0" r="35" fill="#6366f1" opacity="0.15"/>
|
||||
<text x="0" y="-2" font-family="monospace" font-size="18" font-weight="bold" fill="#a5b4fc" text-anchor="middle" dominant-baseline="middle">LEAD</text>
|
||||
<text x="0" y="18" font-family="monospace" font-size="10" fill="#6366f1" text-anchor="middle" opacity="0.7">opus</text>
|
||||
</g>
|
||||
|
||||
<!-- Connection lines from lead to teammates -->
|
||||
<line x1="555" y1="200" x2="280" y2="340" stroke="#6366f1" stroke-width="1.5" opacity="0.3" stroke-dasharray="6,4"/>
|
||||
<line x1="600" y1="225" x2="600" y2="340" stroke="#22c55e" stroke-width="1.5" opacity="0.3" stroke-dasharray="6,4"/>
|
||||
<line x1="645" y1="200" x2="920" y2="340" stroke="#eab308" stroke-width="1.5" opacity="0.3" stroke-dasharray="6,4"/>
|
||||
|
||||
<!-- Teammate 1: data-core (blue) -->
|
||||
<g transform="translate(280, 380)">
|
||||
<rect x="-90" y="-35" width="180" height="70" rx="8" fill="none" stroke="#6366f1" stroke-width="2" opacity="0.7"/>
|
||||
<rect x="-90" y="-35" width="180" height="70" rx="8" fill="#6366f1" opacity="0.08"/>
|
||||
<circle cx="-65" cy="0" r="8" fill="#6366f1" opacity="0.6"/>
|
||||
<text x="-40" y="-8" font-family="monospace" font-size="14" font-weight="bold" fill="#a5b4fc" dominant-baseline="middle">data-core</text>
|
||||
<text x="-40" y="12" font-family="monospace" font-size="10" fill="#6366f1" opacity="0.6" dominant-baseline="middle">4 files | 27 removed</text>
|
||||
</g>
|
||||
|
||||
<!-- Teammate 2: ui-events (green) -->
|
||||
<g transform="translate(600, 380)">
|
||||
<rect x="-90" y="-35" width="180" height="70" rx="8" fill="none" stroke="#22c55e" stroke-width="2" opacity="0.7"/>
|
||||
<rect x="-90" y="-35" width="180" height="70" rx="8" fill="#22c55e" opacity="0.08"/>
|
||||
<circle cx="-65" cy="0" r="8" fill="#22c55e" opacity="0.6"/>
|
||||
<text x="-40" y="-8" font-family="monospace" font-size="14" font-weight="bold" fill="#86efac" dominant-baseline="middle">ui-events</text>
|
||||
<text x="-40" y="12" font-family="monospace" font-size="10" fill="#22c55e" opacity="0.6" dominant-baseline="middle">3 files | 52 removed</text>
|
||||
</g>
|
||||
|
||||
<!-- Teammate 3: features (yellow) -->
|
||||
<g transform="translate(920, 380)">
|
||||
<rect x="-90" y="-35" width="180" height="70" rx="8" fill="none" stroke="#eab308" stroke-width="2" opacity="0.7"/>
|
||||
<rect x="-90" y="-35" width="180" height="70" rx="8" fill="#eab308" opacity="0.08"/>
|
||||
<circle cx="-65" cy="0" r="8" fill="#eab308" opacity="0.6"/>
|
||||
<text x="-40" y="-8" font-family="monospace" font-size="14" font-weight="bold" fill="#fde047" dominant-baseline="middle">features</text>
|
||||
<text x="-40" y="12" font-family="monospace" font-size="10" fill="#eab308" opacity="0.6" dominant-baseline="middle">5 files | 10 removed</text>
|
||||
</g>
|
||||
|
||||
<!-- Shared task list icon (center) -->
|
||||
<g transform="translate(600, 290)">
|
||||
<rect x="-50" y="-14" width="100" height="28" rx="14" fill="#1e1b4b" stroke="#6366f1" stroke-width="1" opacity="0.5"/>
|
||||
<text x="0" y="1" font-family="monospace" font-size="10" fill="#a5b4fc" text-anchor="middle" dominant-baseline="middle">shared tasks</text>
|
||||
</g>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="600" y="75" font-family="monospace" font-size="36" font-weight="bold" fill="#f8fafc" text-anchor="middle" letter-spacing="1">Agent Teams Guide</text>
|
||||
<text x="600" y="108" font-family="monospace" font-size="16" fill="#94a3b8" text-anchor="middle">Coordinate multiple Claude Code sessions working in parallel</text>
|
||||
|
||||
<!-- Bottom stats bar -->
|
||||
<rect x="0" y="470" width="1200" height="1" fill="url(#line-grad)"/>
|
||||
<g transform="translate(0, 510)">
|
||||
<text x="200" y="0" font-family="monospace" font-size="28" font-weight="bold" fill="#6366f1" text-anchor="middle">168</text>
|
||||
<text x="200" y="24" font-family="monospace" font-size="11" fill="#64748b" text-anchor="middle">console.log before</text>
|
||||
|
||||
<text x="440" y="0" font-family="monospace" font-size="28" font-weight="bold" fill="#22c55e" text-anchor="middle">78</text>
|
||||
<text x="440" y="24" font-family="monospace" font-size="11" fill="#64748b" text-anchor="middle">after cleanup</text>
|
||||
|
||||
<text x="680" y="0" font-family="monospace" font-size="28" font-weight="bold" fill="#eab308" text-anchor="middle">~4.5m</text>
|
||||
<text x="680" y="24" font-family="monospace" font-size="11" fill="#64748b" text-anchor="middle">total time</text>
|
||||
|
||||
<text x="920" y="0" font-family="monospace" font-size="28" font-weight="bold" fill="#f8fafc" text-anchor="middle">0</text>
|
||||
<text x="920" y="24" font-family="monospace" font-size="11" fill="#64748b" text-anchor="middle">file conflicts</text>
|
||||
</g>
|
||||
|
||||
<!-- Bottom accent -->
|
||||
<rect x="0" y="580" width="1200" height="1" fill="url(#line-grad)"/>
|
||||
<text x="600" y="605" font-family="monospace" font-size="12" fill="#475569" text-anchor="middle">aitmpl.com/blog/agent-teams-guide</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 465 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 148 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 149 KiB |
@@ -0,0 +1,93 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630" width="1200" height="630">
|
||||
<defs>
|
||||
<linearGradient id="termGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#1a1a2e;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0d0d1a;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="trophyGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#FFD700;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#E8A317;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="glowGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#FFD700;stop-opacity:0.15" />
|
||||
<stop offset="100%" style="stop-color:#E8A317;stop-opacity:0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="1200" height="630" fill="#000000"/>
|
||||
|
||||
<!-- Subtle glow behind trophy -->
|
||||
<circle cx="920" cy="250" r="200" fill="url(#glowGrad)"/>
|
||||
|
||||
<!-- Terminal Window -->
|
||||
<g transform="translate(60, 60)">
|
||||
<!-- Terminal chrome -->
|
||||
<rect x="0" y="0" width="520" height="380" rx="10" ry="10" fill="url(#termGrad)" stroke="#333" stroke-width="1"/>
|
||||
<!-- Title bar -->
|
||||
<rect x="0" y="0" width="520" height="36" rx="10" ry="10" fill="#1e1e2e"/>
|
||||
<rect x="0" y="20" width="520" height="16" fill="#1e1e2e"/>
|
||||
<!-- Traffic light dots -->
|
||||
<circle cx="20" cy="18" r="6" fill="#ff5f57"/>
|
||||
<circle cx="40" cy="18" r="6" fill="#febc2e"/>
|
||||
<circle cx="60" cy="18" r="6" fill="#28c840"/>
|
||||
<!-- Terminal title -->
|
||||
<text x="260" y="22" font-family="'Courier New', monospace" font-size="12" fill="#666" text-anchor="middle">claude-code</text>
|
||||
|
||||
<!-- Terminal content -->
|
||||
<text x="20" y="70" font-family="'Courier New', monospace" font-size="13" fill="#28c840">$</text>
|
||||
<text x="36" y="70" font-family="'Courier New', monospace" font-size="13" fill="#cccccc"> npx claude-code-templates \</text>
|
||||
<text x="36" y="90" font-family="'Courier New', monospace" font-size="13" fill="#cccccc"> --agent ai-specialists/</text>
|
||||
<text x="36" y="110" font-family="'Courier New', monospace" font-size="13" fill="#FFD700"> hackathon-ai-strategist</text>
|
||||
|
||||
<text x="20" y="145" font-family="'Courier New', monospace" font-size="12" fill="#28c840">// Installed successfully</text>
|
||||
|
||||
<text x="20" y="180" font-family="'Courier New', monospace" font-size="13" fill="#28c840">$</text>
|
||||
<text x="36" y="180" font-family="'Courier New', monospace" font-size="13" fill="#cccccc"> Strategist ready</text>
|
||||
|
||||
<text x="20" y="215" font-family="'Courier New', monospace" font-size="12" fill="#888888">[Judging Criteria]</text>
|
||||
<text x="20" y="237" font-family="'Courier New', monospace" font-size="12" fill="#FFD700"> Innovation ......... 30%</text>
|
||||
<text x="20" y="257" font-family="'Courier New', monospace" font-size="12" fill="#FFD700"> Technical depth .... 25%</text>
|
||||
<text x="20" y="277" font-family="'Courier New', monospace" font-size="12" fill="#FFD700"> Impact & scale ..... 25%</text>
|
||||
<text x="20" y="297" font-family="'Courier New', monospace" font-size="12" fill="#FFD700"> Demo quality ....... 15%</text>
|
||||
<text x="20" y="317" font-family="'Courier New', monospace" font-size="12" fill="#FFD700"> Completeness ....... 5%</text>
|
||||
|
||||
<text x="20" y="355" font-family="'Courier New', monospace" font-size="12" fill="#28c840"> Ready to win.</text>
|
||||
</g>
|
||||
|
||||
<!-- Trophy Icon -->
|
||||
<g transform="translate(920, 170)">
|
||||
<!-- Trophy cup -->
|
||||
<path d="M-60,-80 L60,-80 L50,20 Q0,70 -50,20 Z" fill="url(#trophyGrad)" opacity="0.9"/>
|
||||
<!-- Trophy handles -->
|
||||
<path d="M-60,-60 Q-110,-60 -110,-20 Q-110,20 -60,20" fill="none" stroke="#FFD700" stroke-width="8" stroke-linecap="round" opacity="0.8"/>
|
||||
<path d="M60,-60 Q110,-60 110,-20 Q110,20 60,20" fill="none" stroke="#FFD700" stroke-width="8" stroke-linecap="round" opacity="0.8"/>
|
||||
<!-- Trophy stem -->
|
||||
<rect x="-12" y="20" width="24" height="40" fill="#E8A317" rx="4"/>
|
||||
<!-- Trophy base -->
|
||||
<rect x="-45" y="60" width="90" height="16" rx="4" fill="#E8A317"/>
|
||||
<!-- Star on trophy -->
|
||||
<polygon points="0,-50 12,-25 40,-25 18,-8 26,18 0,4 -26,18 -18,-8 -40,-25 -12,-25" fill="#000000" opacity="0.2"/>
|
||||
<polygon points="0,-45 10,-24 34,-24 16,-10 22,12 0,0 -22,12 -16,-10 -34,-24 -10,-24" fill="#FFF8DC" opacity="0.6"/>
|
||||
</g>
|
||||
|
||||
<!-- Decorative sparkles around trophy -->
|
||||
<g fill="#FFD700" opacity="0.5">
|
||||
<circle cx="800" cy="140" r="3"/>
|
||||
<circle cx="1050" cy="180" r="2"/>
|
||||
<circle cx="830" cy="340" r="2.5"/>
|
||||
<circle cx="1020" cy="350" r="2"/>
|
||||
<circle cx="780" cy="240" r="2"/>
|
||||
<circle cx="1060" cy="270" r="3"/>
|
||||
</g>
|
||||
|
||||
<!-- Title text -->
|
||||
<text x="600" y="520" font-family="'Courier New', monospace" font-size="34" fill="#ffffff" text-anchor="middle" font-weight="bold">Win Your Next Hackathon with</text>
|
||||
<text x="600" y="558" font-family="'Courier New', monospace" font-size="34" fill="#ffffff" text-anchor="middle" font-weight="bold">the AI Strategist Agent</text>
|
||||
|
||||
<!-- Subtitle -->
|
||||
<text x="600" y="588" font-family="'Courier New', monospace" font-size="18" fill="#888888" text-anchor="middle">Ideation, evaluation & pitch strategy powered by Claude Code</text>
|
||||
|
||||
<!-- Footer -->
|
||||
<text x="600" y="618" font-family="'Courier New', monospace" font-size="13" fill="#444444" text-anchor="middle">Claude Code Templates | aitmpl.com</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
@@ -0,0 +1,72 @@
|
||||
<svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="bg-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#1a1a1a;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#2d2d2d;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="heygen-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#7c3aed;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#a855f7;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="1200" height="630" fill="url(#bg-gradient)"/>
|
||||
|
||||
<!-- HeyGen Avatar Icon -->
|
||||
<g transform="translate(150, 160)">
|
||||
<!-- Avatar silhouette -->
|
||||
<circle cx="100" cy="70" r="45" fill="url(#heygen-gradient)" opacity="0.3"/>
|
||||
<circle cx="100" cy="70" r="35" fill="url(#heygen-gradient)"/>
|
||||
<!-- Body -->
|
||||
<path d="M50,180 Q50,130 100,120 Q150,130 150,180" fill="url(#heygen-gradient)" opacity="0.8"/>
|
||||
<!-- AI waves -->
|
||||
<path d="M170,60 Q190,80 170,100" fill="none" stroke="#a855f7" stroke-width="4" opacity="0.6"/>
|
||||
<path d="M185,50 Q210,80 185,110" fill="none" stroke="#a855f7" stroke-width="4" opacity="0.4"/>
|
||||
<path d="M200,40 Q230,80 200,120" fill="none" stroke="#a855f7" stroke-width="4" opacity="0.2"/>
|
||||
</g>
|
||||
|
||||
<!-- Video Icon -->
|
||||
<g transform="translate(900, 160)">
|
||||
<!-- Play button circle -->
|
||||
<circle cx="100" cy="100" r="60" fill="none" stroke="#00ff41" stroke-width="6"/>
|
||||
<!-- Play triangle -->
|
||||
<path d="M80,70 L130,100 L80,130 Z" fill="#00ff41"/>
|
||||
<!-- Recording dot -->
|
||||
<circle cx="160" cy="50" r="12" fill="#ef4444">
|
||||
<animate attributeName="opacity" values="1;0.3;1" dur="1.5s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
</g>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="600" y="250" font-family="'Courier New', monospace" font-size="52" font-weight="bold" fill="#00ff41" text-anchor="middle">
|
||||
HeyGen Best Practices
|
||||
</text>
|
||||
|
||||
<!-- Subtitle -->
|
||||
<text x="600" y="310" font-family="'Courier New', monospace" font-size="32" fill="#b0b0b0" text-anchor="middle">
|
||||
AI Avatar Video Creation Skill
|
||||
</text>
|
||||
|
||||
<!-- Description -->
|
||||
<text x="600" y="380" font-family="'Courier New', monospace" font-size="24" fill="#808080" text-anchor="middle">
|
||||
18+ Rules for HeyGen API Integration
|
||||
</text>
|
||||
|
||||
<!-- Command Line -->
|
||||
<rect x="200" y="430" width="800" height="100" fill="#0a0a0a" stroke="#00ff41" stroke-width="2" rx="8"/>
|
||||
<text x="220" y="470" font-family="'Courier New', monospace" font-size="18" fill="#00ff41">
|
||||
<tspan>$ npx claude-code-templates@latest \</tspan>
|
||||
</text>
|
||||
<text x="240" y="500" font-family="'Courier New', monospace" font-size="18" fill="#b0b0b0">
|
||||
<tspan> --skill=development/heygen-best-practices --yes</tspan>
|
||||
</text>
|
||||
|
||||
<!-- Terminal cursor -->
|
||||
<rect x="710" y="485" width="12" height="20" fill="#00ff41" opacity="0.8"/>
|
||||
|
||||
<!-- Bottom tag -->
|
||||
<text x="600" y="580" font-family="'Courier New', monospace" font-size="20" fill="#606060" text-anchor="middle">
|
||||
Claude Code Templates • aitmpl.com
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
@@ -0,0 +1,116 @@
|
||||
<svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Background gradient -->
|
||||
<defs>
|
||||
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#0F0F0F;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#1A1A1A;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="neonGlow" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" style="stop-color:#00E599;stop-opacity:0.2" />
|
||||
<stop offset="50%" style="stop-color:#00E599;stop-opacity:0.8" />
|
||||
<stop offset="100%" style="stop-color:#00E599;stop-opacity:0.2" />
|
||||
</linearGradient>
|
||||
|
||||
<!-- Glow filter for Neon effect -->
|
||||
<filter id="glow">
|
||||
<feGaussianBlur stdDeviation="4" result="coloredBlur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="coloredBlur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<filter id="strongGlow">
|
||||
<feGaussianBlur stdDeviation="8" result="coloredBlur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="coloredBlur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="1200" height="630" fill="url(#bgGradient)"/>
|
||||
|
||||
<!-- Grid pattern -->
|
||||
<g opacity="0.05">
|
||||
<path d="M0 0 L1200 0 M0 50 L1200 50 M0 100 L1200 100 M0 150 L1200 150 M0 200 L1200 200 M0 250 L1200 250 M0 300 L1200 300 M0 350 L1200 350 M0 400 L1200 400 M0 450 L1200 450 M0 500 L1200 500 M0 550 L1200 550 M0 600 L1200 600" stroke="#00E599" stroke-width="1"/>
|
||||
<path d="M0 0 L0 630 M50 0 L50 630 M100 0 L100 630 M150 0 L150 630 M200 0 L200 630 M250 0 L250 630 M300 0 L300 630 M350 0 L350 630 M400 0 L400 630 M450 0 L450 630 M500 0 L500 630 M550 0 L550 630 M600 0 L600 630 M650 0 L650 630 M700 0 L700 630 M750 0 L750 630 M800 0 L800 630 M850 0 L850 630 M900 0 L900 630 M950 0 L950 630 M1000 0 L1000 630 M1050 0 L1050 630 M1100 0 L1100 630 M1150 0 L1150 630 M1200 0 L1200 630" stroke="#00E599" stroke-width="1"/>
|
||||
</g>
|
||||
|
||||
<!-- Terminal window mockup -->
|
||||
<g>
|
||||
<!-- Terminal header -->
|
||||
<rect x="100" y="80" width="1000" height="40" rx="8" fill="#1A1A1A"/>
|
||||
<circle cx="120" cy="100" r="6" fill="#FF5F56"/>
|
||||
<circle cx="140" cy="100" r="6" fill="#FFBD2E"/>
|
||||
<circle cx="160" cy="100" r="6" fill="#27C93F"/>
|
||||
|
||||
<!-- Terminal body -->
|
||||
<rect x="100" y="120" width="1000" height="430" fill="#0F0F0F" opacity="0.9"/>
|
||||
<rect x="100" y="120" width="1000" height="430" fill="none" stroke="#00E599" stroke-width="2" opacity="0.3"/>
|
||||
</g>
|
||||
|
||||
<!-- Terminal content -->
|
||||
<g font-family="'Courier New', monospace" fill="#00E599">
|
||||
<!-- Command prompt line -->
|
||||
<text x="130" y="165" font-size="18" fill="#888">$</text>
|
||||
<text x="155" y="165" font-size="18">npx claude-code-templates@latest --skill database/neon-instagres</text>
|
||||
|
||||
<!-- Success output -->
|
||||
<text x="130" y="200" font-size="16" fill="#00E599" filter="url(#glow)">✓ Provisioning Neon database...</text>
|
||||
<text x="130" y="230" font-size="16" fill="#00E599" filter="url(#glow)">✓ Database ready in 5 seconds</text>
|
||||
<text x="130" y="260" font-size="16" fill="#888">→ Connection string saved to .env</text>
|
||||
</g>
|
||||
|
||||
<!-- Neon logo area (simplified stylized text) -->
|
||||
<g>
|
||||
<text x="130" y="340" font-family="'Courier New', monospace" font-size="56" font-weight="bold" fill="#00E599" filter="url(#strongGlow)">NEON</text>
|
||||
<text x="340" y="340" font-family="'Courier New', monospace" font-size="32" fill="#888">Open Source</text>
|
||||
</g>
|
||||
|
||||
<!-- Subtitle -->
|
||||
<g>
|
||||
<text x="130" y="385" font-family="'Courier New', monospace" font-size="28" font-weight="600" fill="#FFFFFF">Claude Code Templates</text>
|
||||
</g>
|
||||
|
||||
<!-- Partnership badge -->
|
||||
<g>
|
||||
<rect x="130" y="410" width="280" height="45" rx="8" fill="#00E599" opacity="0.15"/>
|
||||
<rect x="130" y="410" width="280" height="45" rx="8" fill="none" stroke="#00E599" stroke-width="2"/>
|
||||
<text x="150" y="440" font-family="'Inter', sans-serif" font-size="20" font-weight="600" fill="#00E599">Partnership Announced</text>
|
||||
</g>
|
||||
|
||||
<!-- Feature highlights -->
|
||||
<g font-family="'Courier New', monospace" font-size="16" fill="#AAAAAA">
|
||||
<text x="130" y="495">→ $5,000 annual support</text>
|
||||
<text x="130" y="520">→ Instant Postgres provisioning</text>
|
||||
</g>
|
||||
|
||||
<!-- Decorative elements - corner brackets -->
|
||||
<g stroke="#00E599" stroke-width="3" fill="none" opacity="0.5">
|
||||
<!-- Top left -->
|
||||
<path d="M 80 80 L 80 50 L 110 50"/>
|
||||
<!-- Top right -->
|
||||
<path d="M 1120 50 L 1120 80"/>
|
||||
<path d="M 1090 50 L 1120 50"/>
|
||||
<!-- Bottom left -->
|
||||
<path d="M 80 570 L 80 600 L 110 600"/>
|
||||
<!-- Bottom right -->
|
||||
<path d="M 1090 600 L 1120 600 L 1120 570"/>
|
||||
</g>
|
||||
|
||||
<!-- Animated dots indicator -->
|
||||
<g>
|
||||
<circle cx="950" cy="495" r="4" fill="#00E599" opacity="0.8"/>
|
||||
<circle cx="970" cy="495" r="4" fill="#00E599" opacity="0.5"/>
|
||||
<circle cx="990" cy="495" r="4" fill="#00E599" opacity="0.3"/>
|
||||
</g>
|
||||
|
||||
<!-- Bottom glow line -->
|
||||
<line x1="100" y1="550" x2="1100" y2="550" stroke="url(#neonGlow)" stroke-width="2"/>
|
||||
|
||||
<!-- Branding footer -->
|
||||
<text x="600" y="595" font-family="'Courier New', monospace" font-size="14" fill="#666" text-anchor="middle">aitmpl.com/blog</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 55 KiB |
@@ -0,0 +1,60 @@
|
||||
<svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="bg-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#1a1a1a;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#2d2d2d;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="1200" height="630" fill="url(#bg-gradient)"/>
|
||||
|
||||
<!-- React Logo -->
|
||||
<g transform="translate(150, 180)">
|
||||
<circle cx="100" cy="100" r="25" fill="#61dafb" opacity="0.2"/>
|
||||
<ellipse cx="100" cy="100" rx="80" ry="30" fill="none" stroke="#61dafb" stroke-width="6"/>
|
||||
<ellipse cx="100" cy="100" rx="80" ry="30" fill="none" stroke="#61dafb" stroke-width="6" transform="rotate(60 100 100)"/>
|
||||
<ellipse cx="100" cy="100" rx="80" ry="30" fill="none" stroke="#61dafb" stroke-width="6" transform="rotate(120 100 100)"/>
|
||||
<circle cx="100" cy="100" r="15" fill="#61dafb"/>
|
||||
</g>
|
||||
|
||||
<!-- Performance Icon -->
|
||||
<g transform="translate(900, 180)">
|
||||
<path d="M100,50 L150,100 L100,150 L50,100 Z" fill="none" stroke="#00ff41" stroke-width="6"/>
|
||||
<path d="M70,100 L130,100" stroke="#00ff41" stroke-width="6" stroke-linecap="round"/>
|
||||
<path d="M100,70 L100,130" stroke="#00ff41" stroke-width="6" stroke-linecap="round"/>
|
||||
<circle cx="100" cy="100" r="10" fill="#00ff41"/>
|
||||
</g>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="600" y="250" font-family="'Courier New', monospace" font-size="52" font-weight="bold" fill="#00ff41" text-anchor="middle">
|
||||
React Best Practices
|
||||
</text>
|
||||
|
||||
<!-- Subtitle -->
|
||||
<text x="600" y="310" font-family="'Courier New', monospace" font-size="32" fill="#b0b0b0" text-anchor="middle">
|
||||
Performance Optimization Skill
|
||||
</text>
|
||||
|
||||
<!-- Description -->
|
||||
<text x="600" y="380" font-family="'Courier New', monospace" font-size="24" fill="#808080" text-anchor="middle">
|
||||
40+ Rules for React & Next.js Performance
|
||||
</text>
|
||||
|
||||
<!-- Command Line -->
|
||||
<rect x="200" y="430" width="800" height="100" fill="#0a0a0a" stroke="#00ff41" stroke-width="2" rx="8"/>
|
||||
<text x="220" y="470" font-family="'Courier New', monospace" font-size="18" fill="#00ff41">
|
||||
<tspan>$ npx claude-code-templates@latest \</tspan>
|
||||
</text>
|
||||
<text x="240" y="500" font-family="'Courier New', monospace" font-size="18" fill="#b0b0b0">
|
||||
<tspan> --skill=web-development/react-best-practices --yes</tspan>
|
||||
</text>
|
||||
|
||||
<!-- Terminal cursor -->
|
||||
<rect x="720" y="485" width="12" height="20" fill="#00ff41" opacity="0.8"/>
|
||||
|
||||
<!-- Bottom tag -->
|
||||
<text x="600" y="580" font-family="'Courier New', monospace" font-size="20" fill="#606060" text-anchor="middle">
|
||||
Claude Code Templates • aitmpl.com
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,151 @@
|
||||
<svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<clipPath id="terminal-clip">
|
||||
<rect x="60" y="100" width="480" height="430" rx="12"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="1200" height="630" fill="#000000"/>
|
||||
|
||||
<!-- Subtle grid pattern -->
|
||||
<g opacity="0.03">
|
||||
<line x1="0" y1="0" x2="0" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="60" y1="0" x2="60" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="120" y1="0" x2="120" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="180" y1="0" x2="180" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="240" y1="0" x2="240" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="300" y1="0" x2="300" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="360" y1="0" x2="360" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="420" y1="0" x2="420" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="480" y1="0" x2="480" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="540" y1="0" x2="540" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="600" y1="0" x2="600" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="660" y1="0" x2="660" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="720" y1="0" x2="720" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="780" y1="0" x2="780" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="840" y1="0" x2="840" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="900" y1="0" x2="900" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="960" y1="0" x2="960" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="1020" y1="0" x2="1020" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="1080" y1="0" x2="1080" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="1140" y1="0" x2="1140" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
<line x1="1200" y1="0" x2="1200" y2="630" stroke="#ffffff" stroke-width="1"/>
|
||||
</g>
|
||||
|
||||
<!-- LEFT SIDE: Claude Code Terminal -->
|
||||
<g transform="translate(60, 100)">
|
||||
<!-- Terminal window -->
|
||||
<rect width="480" height="430" rx="12" fill="#0a0a0a" stroke="#333333" stroke-width="1.5"/>
|
||||
|
||||
<!-- Terminal title bar -->
|
||||
<rect width="480" height="36" rx="12" fill="#1a1a1a"/>
|
||||
<rect y="24" width="480" height="12" fill="#1a1a1a"/>
|
||||
<!-- Traffic lights -->
|
||||
<circle cx="20" cy="18" r="6" fill="#ff5f57"/>
|
||||
<circle cx="40" cy="18" r="6" fill="#febc2e"/>
|
||||
<circle cx="60" cy="18" r="6" fill="#28c840"/>
|
||||
<!-- Title text -->
|
||||
<text x="240" y="22" font-family="'Courier New', monospace" font-size="12" fill="#888888" text-anchor="middle">claude-code ~ hooks</text>
|
||||
|
||||
<!-- Terminal content -->
|
||||
<g transform="translate(16, 52)">
|
||||
<!-- Prompt line 1 -->
|
||||
<text y="16" font-family="'Courier New', monospace" font-size="13" fill="#00ff41">$</text>
|
||||
<text x="16" y="16" font-family="'Courier New', monospace" font-size="13" fill="#cccccc"> cat .claude/settings.json</text>
|
||||
|
||||
<!-- JSON content -->
|
||||
<text y="44" font-family="'Courier New', monospace" font-size="12" fill="#888888">{</text>
|
||||
<text y="62" font-family="'Courier New', monospace" font-size="12" fill="#61afef"> "hooks"</text>
|
||||
<text x="72" y="62" font-family="'Courier New', monospace" font-size="12" fill="#888888">: {</text>
|
||||
<text y="80" font-family="'Courier New', monospace" font-size="12" fill="#e5c07b"> "PreToolUse"</text>
|
||||
<text x="120" y="80" font-family="'Courier New', monospace" font-size="12" fill="#888888">: [{</text>
|
||||
<text y="98" font-family="'Courier New', monospace" font-size="12" fill="#98c379"> "matcher": "Edit|Write"</text>
|
||||
<text y="116" font-family="'Courier New', monospace" font-size="12" fill="#888888"> }]</text>
|
||||
<text y="134" font-family="'Courier New', monospace" font-size="12" fill="#888888"> }</text>
|
||||
<text y="152" font-family="'Courier New', monospace" font-size="12" fill="#888888">}</text>
|
||||
|
||||
<!-- Prompt line 2 -->
|
||||
<text y="184" font-family="'Courier New', monospace" font-size="13" fill="#00ff41">$</text>
|
||||
<text x="16" y="184" font-family="'Courier New', monospace" font-size="13" fill="#cccccc"> detect-secrets.sh</text>
|
||||
|
||||
<!-- Output blocked -->
|
||||
<text y="212" font-family="'Courier New', monospace" font-size="13" fill="#ef4444" font-weight="bold">BLOCKED:</text>
|
||||
<text x="80" y="212" font-family="'Courier New', monospace" font-size="12" fill="#ef4444"> Secret detected!</text>
|
||||
|
||||
<text y="234" font-family="'Courier New', monospace" font-size="12" fill="#fbbf24">Pattern: AKIA[0-9A-Z]{16}</text>
|
||||
|
||||
<text y="260" font-family="'Courier New', monospace" font-size="12" fill="#888888">Use environment variables:</text>
|
||||
<text y="278" font-family="'Courier New', monospace" font-size="12" fill="#98c379"> process.env.AWS_KEY</text>
|
||||
|
||||
<!-- Prompt line 3 with cursor -->
|
||||
<text y="310" font-family="'Courier New', monospace" font-size="13" fill="#00ff41">$</text>
|
||||
<text x="16" y="310" font-family="'Courier New', monospace" font-size="13" fill="#cccccc"> _</text>
|
||||
<rect x="24" y="298" width="8" height="16" fill="#00ff41" opacity="0.8">
|
||||
<animate attributeName="opacity" values="0.8;0;0.8" dur="1.2s" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- RIGHT SIDE: Security Shield Icon -->
|
||||
<g transform="translate(750, 120)">
|
||||
<!-- Outer glow -->
|
||||
<g opacity="0.15">
|
||||
<path d="M200,30 L340,80 L340,200 C340,280 270,340 200,370 C130,340 60,280 60,200 L60,80 Z"
|
||||
fill="#ef4444" filter="url(#blur)"/>
|
||||
</g>
|
||||
|
||||
<!-- Shield outline -->
|
||||
<path d="M200,50 L320,95 L320,195 C320,265 260,315 200,340 C140,315 80,265 80,195 L80,95 Z"
|
||||
fill="none" stroke="#ef4444" stroke-width="3" opacity="0.6"/>
|
||||
|
||||
<!-- Shield filled -->
|
||||
<path d="M200,65 L305,105 L305,190 C305,250 250,295 200,318 C150,295 95,250 95,190 L95,105 Z"
|
||||
fill="#1a0000" stroke="#ef4444" stroke-width="2"/>
|
||||
|
||||
<!-- Lock icon inside shield -->
|
||||
<g transform="translate(200, 175)">
|
||||
<!-- Lock body -->
|
||||
<rect x="-30" y="-5" width="60" height="50" rx="6" fill="#ef4444" opacity="0.9"/>
|
||||
<!-- Lock shackle -->
|
||||
<path d="M-18,-5 L-18,-25 C-18,-45 18,-45 18,-25 L18,-5"
|
||||
fill="none" stroke="#ef4444" stroke-width="5" stroke-linecap="round"/>
|
||||
<!-- Keyhole -->
|
||||
<circle cx="0" cy="18" r="8" fill="#1a0000"/>
|
||||
<rect x="-3" y="18" width="6" height="14" rx="2" fill="#1a0000"/>
|
||||
</g>
|
||||
|
||||
<!-- Scanning lines animation -->
|
||||
<g opacity="0.3">
|
||||
<line x1="100" y1="130" x2="300" y2="130" stroke="#ef4444" stroke-width="1" stroke-dasharray="4,8">
|
||||
<animate attributeName="y1" values="130;280;130" dur="3s" repeatCount="indefinite"/>
|
||||
<animate attributeName="y2" values="130;280;130" dur="3s" repeatCount="indefinite"/>
|
||||
</line>
|
||||
</g>
|
||||
|
||||
<!-- Small key icons scattered -->
|
||||
<g opacity="0.2">
|
||||
<!-- Key 1 -->
|
||||
<text x="10" y="100" font-family="'Courier New', monospace" font-size="18" fill="#ef4444">API_KEY=***</text>
|
||||
<!-- Key 2 -->
|
||||
<text x="280" y="280" font-family="'Courier New', monospace" font-size="14" fill="#ef4444">ghp_***</text>
|
||||
<!-- Key 3 -->
|
||||
<text x="30" y="320" font-family="'Courier New', monospace" font-size="14" fill="#ef4444">sk_live_***</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Title - White text -->
|
||||
<text x="600" y="520" font-family="'Courier New', monospace" font-size="36" font-weight="bold" fill="#ffffff" text-anchor="middle">
|
||||
Block Secrets from Commits
|
||||
</text>
|
||||
|
||||
<!-- Subtitle -->
|
||||
<text x="600" y="558" font-family="'Courier New', monospace" font-size="20" fill="#888888" text-anchor="middle">
|
||||
Claude Code Security Hooks
|
||||
</text>
|
||||
|
||||
<!-- Bottom tag -->
|
||||
<text x="600" y="605" font-family="'Courier New', monospace" font-size="16" fill="#444444" text-anchor="middle">
|
||||
Claude Code Templates | aitmpl.com
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 155 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,81 @@
|
||||
<svg width="1200" height="630" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="termGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#1a1a2e;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0d0d1a;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="vercelGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#ffffff;stop-opacity:0.9" />
|
||||
<stop offset="100%" style="stop-color:#cccccc;stop-opacity:0.7" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="1200" height="630" fill="#000000"/>
|
||||
|
||||
<!-- Left side: Terminal window -->
|
||||
<g transform="translate(60, 60)">
|
||||
<!-- Terminal chrome -->
|
||||
<rect x="0" y="0" width="520" height="380" rx="12" ry="12" fill="#1a1a2e" stroke="#333" stroke-width="1"/>
|
||||
<!-- Title bar -->
|
||||
<rect x="0" y="0" width="520" height="36" rx="12" ry="12" fill="#252540"/>
|
||||
<rect x="0" y="24" width="520" height="12" fill="#252540"/>
|
||||
<!-- Traffic lights -->
|
||||
<circle cx="20" cy="18" r="6" fill="#ff5f56"/>
|
||||
<circle cx="40" cy="18" r="6" fill="#ffbd2e"/>
|
||||
<circle cx="60" cy="18" r="6" fill="#27c93f"/>
|
||||
<!-- Title text -->
|
||||
<text x="260" y="22" font-family="'Courier New', monospace" font-size="12" fill="#888" text-anchor="middle">claude-code</text>
|
||||
|
||||
<!-- Terminal content -->
|
||||
<text x="20" y="70" font-family="'Courier New', monospace" font-size="14" fill="#27c93f">$ claude</text>
|
||||
|
||||
<text x="20" y="100" font-family="'Courier New', monospace" font-size="13" fill="#888">Vercel Deployment Monitor</text>
|
||||
|
||||
<text x="20" y="135" font-family="'Courier New', monospace" font-size="13" fill="#ffffff">statusLine:</text>
|
||||
<text x="20" y="160" font-family="'Courier New', monospace" font-size="13" fill="#cccccc"> type: "command"</text>
|
||||
<text x="20" y="185" font-family="'Courier New', monospace" font-size="13" fill="#cccccc"> command: "bash -c '..."</text>
|
||||
|
||||
<text x="20" y="220" font-family="'Courier New', monospace" font-size="13" fill="#888">--- Status Bar ---</text>
|
||||
|
||||
<!-- Simulated statusline output -->
|
||||
<rect x="20" y="240" width="480" height="36" rx="4" ry="4" fill="#0a0a1a" stroke="#333" stroke-width="1"/>
|
||||
<text x="36" y="264" font-family="'Courier New', monospace" font-size="14" fill="#ffffff">▲ Vercel</text>
|
||||
<text x="130" y="264" font-family="'Courier New', monospace" font-size="14" fill="#666">|</text>
|
||||
<text x="145" y="264" font-family="'Courier New', monospace" font-size="14" fill="#27c93f">READY</text>
|
||||
<text x="210" y="264" font-family="'Courier New', monospace" font-size="14" fill="#666">|</text>
|
||||
<text x="225" y="264" font-family="'Courier New', monospace" font-size="14" fill="#ffbd2e">12m ago</text>
|
||||
<text x="325" y="264" font-family="'Courier New', monospace" font-size="14" fill="#666">|</text>
|
||||
<text x="340" y="264" font-family="'Courier New', monospace" font-size="14" fill="#5b9dff">Deploy</text>
|
||||
|
||||
<text x="20" y="320" font-family="'Courier New', monospace" font-size="13" fill="#27c93f">$ export VERCEL_TOKEN=***</text>
|
||||
<text x="20" y="345" font-family="'Courier New', monospace" font-size="13" fill="#27c93f">$ export VERCEL_PROJECT_ID=***</text>
|
||||
</g>
|
||||
|
||||
<!-- Right side: Vercel triangle icon -->
|
||||
<g transform="translate(780, 120)">
|
||||
<!-- Large Vercel triangle -->
|
||||
<polygon points="180,0 360,280 0,280" fill="none" stroke="#ffffff" stroke-width="3" opacity="0.15"/>
|
||||
<polygon points="180,40 320,240 40,240" fill="none" stroke="#ffffff" stroke-width="2" opacity="0.25"/>
|
||||
<polygon points="180,80 280,200 80,200" fill="#ffffff" opacity="0.08"/>
|
||||
<!-- Solid inner triangle -->
|
||||
<polygon points="180,100 260,190 100,190" fill="#ffffff" opacity="0.9"/>
|
||||
|
||||
<!-- Status indicators -->
|
||||
<circle cx="120" cy="260" r="8" fill="#27c93f"/>
|
||||
<text x="138" y="265" font-family="'Courier New', monospace" font-size="14" fill="#27c93f">READY</text>
|
||||
|
||||
<circle cx="240" cy="260" r="8" fill="#ffbd2e"/>
|
||||
<text x="258" y="265" font-family="'Courier New', monospace" font-size="14" fill="#ffbd2e">BUILD</text>
|
||||
</g>
|
||||
|
||||
<!-- Bottom: Title text -->
|
||||
<text x="600" y="530" font-family="'Courier New', monospace" font-size="36" fill="#ffffff" text-anchor="middle" font-weight="bold">Monitor Vercel Deployments</text>
|
||||
<text x="600" y="565" font-family="'Courier New', monospace" font-size="36" fill="#ffffff" text-anchor="middle" font-weight="bold">in Real-Time</text>
|
||||
|
||||
<!-- Subtitle -->
|
||||
<text x="600" y="595" font-family="'Courier New', monospace" font-size="20" fill="#888888" text-anchor="middle">Claude Code Statusline with Clickable Deploy Links</text>
|
||||
|
||||
<!-- Footer -->
|
||||
<text x="600" y="622" font-family="'Courier New', monospace" font-size="14" fill="#444444" text-anchor="middle">Claude Code Templates | aitmpl.com</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
@@ -0,0 +1,305 @@
|
||||
{
|
||||
"articles": [
|
||||
{
|
||||
"id": "neon-open-source-program",
|
||||
"title": "Claude Code Templates Joins the Neon Open Source Program",
|
||||
"description": "We're excited to announce that Claude Code Templates has been accepted into the Neon Open Source Program, bringing $5,000 annual sponsorship, co-marketing opportunities, and instant Postgres provisioning to all users.",
|
||||
"url": "neon-open-source-program/",
|
||||
"image": "assets/neon-open-source-program-cover.svg",
|
||||
"category": "Partnership",
|
||||
"readTime": "5 min read",
|
||||
"tags": ["Neon", "Postgres", "Open Source", "Partnership", "Sponsorship"],
|
||||
"difficulty": "basic",
|
||||
"featured": true,
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"id": "neon-complete-template-integration",
|
||||
"title": "Complete Neon Template for Claude Code: Instant Provisioning + Expert Agents",
|
||||
"description": "Complete Neon ecosystem for Claude Code: 1 auto-provisioning Skill + 5 expert agents + MCP + monitoring tools. From zero to production-optimized Postgres in minutes.",
|
||||
"url": "neon-complete-template-integration.html",
|
||||
"image": "assets/neon-template-cover.png",
|
||||
"category": "Partnership",
|
||||
"readTime": "12 min read",
|
||||
"tags": ["Neon", "Postgres", "Template", "Skills", "Agents", "MCP", "Partnership"],
|
||||
"difficulty": "basic",
|
||||
"featured": true,
|
||||
"order": 1
|
||||
},
|
||||
{
|
||||
"id": "frontend-developer-agent",
|
||||
"title": "Claude Code Frontend Developer Agent: Complete 2025 Tutorial",
|
||||
"description": "Complete guide to the #1 most downloaded Claude Code component. Build modern web applications with React, Vue, and Next.js using AI-powered frontend development. 7,553+ installations.",
|
||||
"url": "frontend-developer-agent/",
|
||||
"image": "assets/frontend-developer-agent-cover.png",
|
||||
"category": "Agents",
|
||||
"readTime": "12 min read",
|
||||
"tags": ["Agents", "Frontend", "React", "Vue", "Next.js", "AI Development"],
|
||||
"difficulty": "basic",
|
||||
"featured": true,
|
||||
"order": 1
|
||||
},
|
||||
{
|
||||
"id": "code-reviewer-agent",
|
||||
"title": "AI Code Review Automation with Claude Code: 2025 Complete Guide",
|
||||
"description": "Complete guide to the Code Reviewer Agent - automate code reviews, enforce best practices, and improve code quality with AI-powered analysis. 5,516+ installations.",
|
||||
"url": "code-reviewer-agent/",
|
||||
"image": "assets/code-reviewer-agent-cover.png",
|
||||
"category": "Agents",
|
||||
"readTime": "14 min read",
|
||||
"tags": ["Agents", "Code Review", "Security", "Best Practices", "Automation"],
|
||||
"difficulty": "intermediate",
|
||||
"featured": true,
|
||||
"order": 2
|
||||
},
|
||||
{
|
||||
"id": "context7-mcp",
|
||||
"title": "Context7 MCP for Claude Code: Real-Time Documentation Integration 2025",
|
||||
"description": "Complete guide to Context7 MCP - real-time library documentation integration for Claude Code. Access up-to-date docs for React, Next.js, Supabase, and 1000+ libraries. 3,288+ installations.",
|
||||
"url": "context7-mcp/",
|
||||
"image": "assets/context7-mcp-cover.png",
|
||||
"category": "MCP",
|
||||
"readTime": "12 min read",
|
||||
"tags": ["MCP", "Documentation", "Context7", "Real-time", "Libraries"],
|
||||
"difficulty": "basic",
|
||||
"featured": true,
|
||||
"order": 3
|
||||
},
|
||||
{
|
||||
"id": "skills-creator",
|
||||
"title": "Claude Code Skills Tutorial: Create Custom AI Workflows in 2025",
|
||||
"description": "Complete guide to Claude Code Skills system - create custom AI workflows with progressive context loading. Learn to build reusable automation for your development workflow. 2,298+ installations.",
|
||||
"url": "skills-creator/",
|
||||
"image": "assets/skills-creator-cover.png",
|
||||
"category": "Skills",
|
||||
"readTime": "15 min read",
|
||||
"tags": ["Skills", "Workflows", "Automation", "Progressive Loading", "Custom AI"],
|
||||
"difficulty": "intermediate",
|
||||
"featured": true,
|
||||
"order": 4
|
||||
},
|
||||
{
|
||||
"id": "e2b-claude-code-sandbox",
|
||||
"title": "E2B + Claude Code Sandbox: Secure Cloud Development Environment Guide",
|
||||
"description": "Complete guide to run Claude Code in isolated E2B cloud sandbox. Install components safely, execute prompts in secure environment, and develop without local system risks.",
|
||||
"url": "e2b-claude-code-sandbox/",
|
||||
"image": "assets/e2b-claude-code-sandbox-cover.png",
|
||||
"category": "Cloud Development",
|
||||
"readTime": "6 min read",
|
||||
"tags": ["E2B", "Cloud", "Sandbox", "Security", "Isolation"],
|
||||
"difficulty": "advanced",
|
||||
"featured": true,
|
||||
"order": 5
|
||||
},
|
||||
{
|
||||
"id": "supabase-integration",
|
||||
"title": "Claude Code + Supabase Integration: Complete Guide with Agents, Commands and MCP",
|
||||
"description": "Learn how to integrate Supabase with Claude Code using MCP, Agents, and Commands for faster database development.",
|
||||
"url": "https://medium.com/@dan.avila7/claude-code-supabase-integration-complete-guide-with-agents-commands-and-mcp-427613d9051e",
|
||||
"image": "https://miro.medium.com/v2/resize:fit:720/format:webp/1*UkD_4OChMB5_x50ZPledvw.png",
|
||||
"category": "Database",
|
||||
"readTime": "5 min read",
|
||||
"tags": ["Supabase", "Database", "Agents", "Commands", "MCP"],
|
||||
"difficulty": "basic",
|
||||
"featured": true,
|
||||
"order": 6
|
||||
},
|
||||
{
|
||||
"id": "git-flow-guide",
|
||||
"title": "Complete Guide to Setting Up Git Flow in Claude Code",
|
||||
"description": "Step-by-step guide to implementing Git Flow workflow in Claude Code for better version control and team collaboration.",
|
||||
"url": "https://medium.com/@dan.avila7/complete-guide-to-setting-up-git-flow-in-claude-code-616477941f78",
|
||||
"image": "https://miro.medium.com/v2/resize:fit:720/format:webp/1*hY3wY-RvsuF4aIFopX9cMg.png",
|
||||
"category": "Development",
|
||||
"readTime": "7 min read",
|
||||
"tags": ["Git", "Workflow", "Version Control", "Automation"],
|
||||
"difficulty": "intermediate",
|
||||
"order": 7
|
||||
},
|
||||
{
|
||||
"id": "google-cloud-vertex-ai",
|
||||
"title": "Step-by-Step Guide to Connect Claude Code with Google Cloud Vertex AI",
|
||||
"description": "Complete guide to integrate Claude Code with Google Cloud Vertex AI for enhanced AI capabilities and scalable cloud infrastructure.",
|
||||
"url": "https://medium.com/@dan.avila7/step-by-step-guide-to-connect-claude-code-with-google-cloud-vertex-ai-17e7916e711e",
|
||||
"image": "https://miro.medium.com/v2/resize:fit:720/format:webp/1*1mhKnj46P_xCxovKsQqBuQ.png",
|
||||
"category": "Cloud & AI",
|
||||
"readTime": "8 min read",
|
||||
"tags": ["Google Cloud", "Vertex AI", "Integration", "Cloud"],
|
||||
"difficulty": "advanced",
|
||||
"order": 8
|
||||
},
|
||||
{
|
||||
"id": "automated-documentation",
|
||||
"title": "Automated Documentation with Claude Code: Building Self-Updating Docs Using Docusaurus Agent",
|
||||
"description": "Learn how to create self-updating documentation systems using Claude Code and the Docusaurus agent for automatic content generation.",
|
||||
"url": "https://medium.com/@dan.avila7/automated-documentation-with-claude-code-building-self-updating-docs-using-docusaurus-agent-2c85d3ec0e19",
|
||||
"image": "https://miro.medium.com/v2/resize:fit:720/format:webp/1*Qxn21wqw6J4QACgoZkWWpg.png",
|
||||
"category": "Documentation",
|
||||
"readTime": "6 min read",
|
||||
"tags": ["Documentation", "Docusaurus", "Automation", "Agents"],
|
||||
"difficulty": "advanced",
|
||||
"order": 9
|
||||
},
|
||||
{
|
||||
"id": "claude-code-templates-guide",
|
||||
"title": "Complete Guide to Claude Code Templates",
|
||||
"description": "Comprehensive introduction to Claude Code Templates, covering installation, configuration, and best practices for AI-powered development.",
|
||||
"url": "https://medium.com/latinxinai/complete-guide-to-claude-code-templates-4e53d6688b34",
|
||||
"image": "https://miro.medium.com/v2/resize:fit:1400/format:webp/1*YwxsC2eeW5fW1K6E8VBZpA.png",
|
||||
"category": "Getting Started",
|
||||
"readTime": "5 min read",
|
||||
"tags": ["Getting Started", "Templates", "Installation", "Configuration"],
|
||||
"difficulty": "basic",
|
||||
"featured": true,
|
||||
"order": 10
|
||||
},
|
||||
{
|
||||
"id": "tunnel-vision-fix",
|
||||
"title": "Fixed Claude Code's 2024 Tunnel Vision with a Simple Hook",
|
||||
"description": "Learn how to solve Claude Code's tunnel vision problem using a simple automation hook for better context awareness.",
|
||||
"url": "https://medium.com/@dan.avila7/fixed-claude-codes-2024-tunnel-vision-with-a-simple-hook-cb32cfaf9b27",
|
||||
"image": "https://miro.medium.com/v2/resize:fit:720/format:webp/1*T5gG6RGS-4dpD0K6mhnkHA.png",
|
||||
"category": "Automation",
|
||||
"readTime": "3 min read",
|
||||
"tags": ["Hooks", "Automation", "Context", "Problem Solving"],
|
||||
"difficulty": "basic",
|
||||
"order": 11
|
||||
},
|
||||
{
|
||||
"id": "telegram-integration",
|
||||
"title": "Step-by-Step Guide: Connect Telegram with Claude Code Hooks",
|
||||
"description": "Complete tutorial for integrating Telegram notifications with Claude Code using automation hooks.",
|
||||
"url": "https://medium.com/@dan.avila7/step-by-step-guide-connect-telegram-with-claude-code-hooks-1686fadcee65",
|
||||
"image": "https://miro.medium.com/v2/resize:fit:720/format:webp/1*CMFx_4KLxgIbv8xZ6UCpQw.png",
|
||||
"category": "Integrations",
|
||||
"readTime": "4 min read",
|
||||
"tags": ["Telegram", "Hooks", "Notifications", "Integration"],
|
||||
"difficulty": "basic",
|
||||
"featured": true,
|
||||
"order": 12
|
||||
},
|
||||
{
|
||||
"id": "vercel-statusline",
|
||||
"title": "Vercel Statusline Tutorial for Claude Code",
|
||||
"description": "Learn how to create a custom Vercel statusline for Claude Code to monitor deployments and project status.",
|
||||
"url": "https://medium.com/@dan.avila7/vercel-statusline-tutorial-for-claude-code-14755f113d76",
|
||||
"image": "https://miro.medium.com/v2/resize:fit:720/format:webp/1*dGIs0XoRdrs9STzM-BWDSA.png",
|
||||
"category": "Customization",
|
||||
"readTime": "3 min read",
|
||||
"tags": ["Vercel", "Statusline", "Deployment", "Monitoring"],
|
||||
"difficulty": "basic",
|
||||
"order": 13
|
||||
},
|
||||
{
|
||||
"id": "cloudflare-sandbox",
|
||||
"title": "Claude Code Agents with Cloudflare Sandbox for Isolated Environments",
|
||||
"description": "Learn how to run Claude Code agents in isolated Cloudflare Workers sandbox environments for secure and scalable development.",
|
||||
"url": "https://medium.com/@dan.avila7/claude-code-agents-with-cloudflare-sandbox-for-isolated-environments-f89b2668a06d",
|
||||
"image": "https://miro.medium.com/v2/resize:fit:640/format:webp/1*g9xeEVW_qaX8vfrFY8lxqg.png",
|
||||
"category": "Cloud Development",
|
||||
"readTime": "7 min read",
|
||||
"tags": ["Cloudflare", "Sandbox", "Agents", "Security", "Workers"],
|
||||
"difficulty": "advanced",
|
||||
"order": 14
|
||||
},
|
||||
{
|
||||
"id": "prepare-codebase",
|
||||
"title": "Step-by-Step Guide: Prepare Your Codebase for Claude Code",
|
||||
"description": "Advanced guide to structuring and optimizing your codebase for maximum efficiency with Claude Code.",
|
||||
"url": "https://medium.com/@dan.avila7/step-by-step-guide-prepare-your-codebase-for-claude-code-3e14262566e9",
|
||||
"image": "https://miro.medium.com/v2/resize:fit:720/format:webp/1*lV1ujaM_2kDSJXNlmLIg2A.png",
|
||||
"category": "Best Practices",
|
||||
"readTime": "4 min read",
|
||||
"tags": ["Best Practices", "Code Organization", "Optimization", "Setup"],
|
||||
"difficulty": "advanced",
|
||||
"order": 15
|
||||
},
|
||||
{
|
||||
"id": "simple-notifications-hook",
|
||||
"title": "Simple Notifications Hook for Claude Code",
|
||||
"description": "Get instant desktop notifications when Claude Code operations complete. Cross-platform hook for macOS and Linux.",
|
||||
"url": "simple-notifications-hook/",
|
||||
"image": "https://www.aitmpl.com/blog/assets/simple-notifications-hook-cover.png",
|
||||
"category": "Automation",
|
||||
"readTime": "4 min read",
|
||||
"tags": ["Claude Code", "Hooks", "Notifications", "Automation", "Desktop Alerts"],
|
||||
"difficulty": "basic",
|
||||
"featured": true,
|
||||
"order": 16
|
||||
},
|
||||
{
|
||||
"id": "react-best-practices-skill",
|
||||
"title": "React Best Practices Skill: 40+ Performance Optimization Rules",
|
||||
"description": "Master React and Next.js performance optimization with 40+ proven rules. Learn to eliminate waterfalls, optimize bundles, and improve rendering performance in your applications.",
|
||||
"url": "react-best-practices-skill/",
|
||||
"image": "assets/react-best-practices-skill-cover.svg",
|
||||
"category": "Skills",
|
||||
"readTime": "8 min read",
|
||||
"tags": ["React", "Next.js", "Performance", "Optimization", "Best Practices", "Skills"],
|
||||
"difficulty": "intermediate",
|
||||
"featured": true,
|
||||
"order": 17
|
||||
},
|
||||
{
|
||||
"id": "heygen-best-practices-skill",
|
||||
"title": "HeyGen Best Practices Skill: AI Avatar Video Creation Guide",
|
||||
"description": "Master HeyGen API integration with 18+ best practice rules. Learn to create AI avatar videos, manage voices, handle video generation workflows, and implement streaming avatars.",
|
||||
"url": "heygen-best-practices-skill/",
|
||||
"image": "assets/heygen-best-practices-skill-cover.svg",
|
||||
"category": "Skills",
|
||||
"readTime": "10 min read",
|
||||
"tags": ["HeyGen", "AI Video", "Avatar", "Text-to-Video", "API Integration", "Skills"],
|
||||
"difficulty": "intermediate",
|
||||
"featured": true,
|
||||
"order": 18
|
||||
},
|
||||
{
|
||||
"id": "security-hooks-secrets",
|
||||
"title": "Block API Keys & Secrets from Your Commits with Claude Code Hooks",
|
||||
"description": "Learn how to set up a PreToolUse hook in Claude Code that automatically detects and blocks commits containing API keys, passwords, and secrets.",
|
||||
"url": "security-hooks-secrets/",
|
||||
"image": "assets/security-hooks-secrets-cover.svg",
|
||||
"category": "Security",
|
||||
"readTime": "4 min read",
|
||||
"tags": ["Security", "Hooks", "Git", "Secrets", "Best Practices"],
|
||||
"difficulty": "intermediate",
|
||||
"featured": true,
|
||||
"order": 19
|
||||
},
|
||||
{
|
||||
"id": "vercel-deployment-monitor-statusline",
|
||||
"title": "Monitor Vercel Deployments in Real-Time with Claude Code Statuslines",
|
||||
"description": "Add a real-time Vercel deployment monitor to your Claude Code terminal. See build status, time since last deploy, and click directly to your deployment URL.",
|
||||
"url": "vercel-deployment-monitor-statusline/",
|
||||
"image": "assets/vercel-deployment-monitor-statusline-cover.svg",
|
||||
"category": "Monitoring",
|
||||
"readTime": "5 min read",
|
||||
"tags": ["Vercel", "Statusline", "Deployment", "Monitoring", "DevOps"],
|
||||
"difficulty": "intermediate",
|
||||
"featured": true,
|
||||
"order": 20
|
||||
},
|
||||
{
|
||||
"id": "hackathon-ai-strategist-agent",
|
||||
"title": "Win Your Next Hackathon with the AI Strategist Agent for Claude Code",
|
||||
"description": "Install the Hackathon AI Strategist agent for Claude Code to get expert ideation, judging-criteria evaluation, pitch coaching, and time management for your next hackathon.",
|
||||
"url": "hackathon-ai-strategist-agent/",
|
||||
"image": "assets/hackathon-ai-strategist-agent-cover.svg",
|
||||
"category": "Agents",
|
||||
"readTime": "5 min read",
|
||||
"tags": ["Hackathon", "AI Strategy", "Agents", "Ideation", "Competition", "Claude Code"],
|
||||
"difficulty": "basic",
|
||||
"featured": true,
|
||||
"order": 21
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"lastUpdated": "2026-02-01",
|
||||
"totalArticles": 22,
|
||||
"difficultyLevels": {
|
||||
"basic": 9,
|
||||
"intermediate": 7,
|
||||
"advanced": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,730 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Code Reviewer Agent for Claude Code: Automated Code Quality & Security Expert</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="Install the Code Reviewer Agent for Claude Code to automate code reviews, detect security vulnerabilities, and enforce best practices. AI-powered code quality analysis with actionable feedback.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://aitmpl.com/blog/code-reviewer-agent/">
|
||||
<meta property="og:title" content="Code Reviewer Agent for Claude Code: Automated Code Quality & Security">
|
||||
<meta property="og:description" content="Install the Code Reviewer Agent for Claude Code to automate code reviews, detect security vulnerabilities, and enforce best practices. AI-powered code quality analysis with actionable feedback.">
|
||||
<meta property="og:image" content="https://aitmpl.com/blog/assets/code-reviewer-agent-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="Agents">
|
||||
<meta property="article:tag" content="Claude Code">
|
||||
<meta property="article:tag" content="Code Review">
|
||||
<meta property="article:tag" content="Security">
|
||||
<meta property="article:tag" content="Code Quality">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/blog/code-reviewer-agent/">
|
||||
<meta name="twitter:title" content="Code Reviewer Agent for Claude Code: Automated Code Quality & Security">
|
||||
<meta name="twitter:description" content="Install the Code Reviewer Agent for Claude Code to automate code reviews, detect security vulnerabilities, and enforce best practices. AI-powered code quality analysis with actionable feedback.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/blog/assets/code-reviewer-agent-cover.png">
|
||||
|
||||
<!-- Additional SEO -->
|
||||
<meta name="keywords" content="Claude Code agent, Code Reviewer Agent, Claude Code code review, automated code review, security scanning, code quality, AI code review, vulnerability detection, best practices, OWASP, code audit, static analysis, Claude Code security">
|
||||
<meta name="author" content="Claude Code Templates">
|
||||
<link rel="canonical" href="https://aitmpl.com/blog/code-reviewer-agent/">
|
||||
|
||||
<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": "Code Reviewer Agent for Claude Code: Automated Code Quality & Security Expert",
|
||||
"description": "Install the Code Reviewer Agent for Claude Code to automate code reviews, detect security vulnerabilities, and enforce best practices. AI-powered code quality analysis with actionable feedback.",
|
||||
"image": "https://aitmpl.com/blog/assets/code-reviewer-agent-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/code-reviewer-agent/"
|
||||
},
|
||||
"keywords": "Claude Code agent, Code Reviewer Agent, automated code review, security scanning, code quality, AI code review, Claude Code security",
|
||||
"wordCount": "600",
|
||||
"articleSection": "Claude Code Agents",
|
||||
"about": [
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Claude Code"
|
||||
},
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Code Review Agent"
|
||||
},
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Code Quality"
|
||||
},
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Security Analysis"
|
||||
},
|
||||
{
|
||||
"@type": "SoftwareApplication",
|
||||
"name": "Claude Code",
|
||||
"applicationCategory": "DeveloperApplication"
|
||||
}
|
||||
]
|
||||
}
|
||||
</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">Code Reviewer Agent for Claude Code: Automated Code Quality & Security</h1>
|
||||
<p class="article-subtitle">Learn how to install and use the Code Reviewer Agent for Claude Code to automate code reviews, detect security vulnerabilities, and enforce best practices with AI-powered analysis and actionable feedback.</p>
|
||||
<div class="article-meta-full">
|
||||
<span class="read-time">4 min read</span>
|
||||
<div class="article-tags">
|
||||
<span class="tag">Claude Code</span>
|
||||
<span class="tag">Agent</span>
|
||||
<span class="tag">Code Review</span>
|
||||
<span class="tag">Security</span>
|
||||
<span class="tag">Quality</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<article class="article-body">
|
||||
<img src="../assets/code-reviewer-agent-cover.png" alt="Code Reviewer Agent for Claude Code" class="article-cover" loading="lazy">
|
||||
|
||||
<div class="article-content-full">
|
||||
<h2>What is the Code Reviewer Agent?</h2>
|
||||
|
||||
<p>The Code Reviewer Agent is a specialized Claude Code agent focused on automated code quality and security analysis. It provides expert code reviews, detects security vulnerabilities, enforces best practices, and delivers actionable feedback to improve your codebase quality and maintainability.</p>
|
||||
|
||||
<!-- Mermaid Diagram -->
|
||||
<div class="mermaid-diagram" style="background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 2rem; margin: 2rem 0; text-align: center;">
|
||||
<pre class="mermaid">
|
||||
graph LR
|
||||
A[📝 Your Code] --> B[🔍 Code Reviewer Agent]
|
||||
B --> C[🛡️ Security & Quality Report]
|
||||
C --> D[✅ Improved Codebase]
|
||||
|
||||
style B fill:#F97316,stroke:#fff,color:#000
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<h2>Key Capabilities</h2>
|
||||
<ul>
|
||||
<li><strong>Automated Code Reviews</strong> (analyzes git diff for recent changes)</li>
|
||||
<li><strong>Security Vulnerability Detection</strong> (exposed secrets, API keys, input validation)</li>
|
||||
<li><strong>Code Quality Analysis</strong> (readability, naming conventions, duplication)</li>
|
||||
<li><strong>Best Practices Enforcement</strong> (error handling, test coverage, performance)</li>
|
||||
<li><strong>Prioritized Feedback</strong> (critical issues, warnings, suggestions)</li>
|
||||
<li><strong>Actionable Recommendations</strong> (specific examples of how to fix issues)</li>
|
||||
</ul>
|
||||
|
||||
<h2>Installation</h2>
|
||||
|
||||
<p>Install the Code Reviewer Agent using the Claude Code Templates CLI:</p>
|
||||
|
||||
<pre><code class="language-bash">npx claude-code-templates@latest --agent development-tools/code-reviewer</code></pre>
|
||||
|
||||
<p><strong>Where is the agent installed?</strong></p>
|
||||
<p>The agent is saved in <code>.claude/agents/code-reviewer.md</code> in your project directory:</p>
|
||||
|
||||
<pre><code class="language-bash">your-project/
|
||||
├── .claude/
|
||||
│ └── agents/
|
||||
│ └── code-reviewer.md # ← Agent installed here
|
||||
├── src/
|
||||
│ └── components/
|
||||
├── package.json
|
||||
└── README.md</code></pre>
|
||||
|
||||
<h2>How to Use the Agent</h2>
|
||||
|
||||
<p>Start Claude Code and explicitly request the agent in your prompt:</p>
|
||||
|
||||
<pre><code class="language-bash"># Start Claude Code
|
||||
claude
|
||||
|
||||
# Then write your prompt requesting the agent
|
||||
> Use the code-reviewer agent to review my recent changes for security issues and code quality</code></pre>
|
||||
|
||||
<p>The agent will automatically:</p>
|
||||
<ul>
|
||||
<li>Run <code>git diff</code> to see recent changes</li>
|
||||
<li>Focus review on modified files</li>
|
||||
<li>Provide feedback organized by priority (critical, warnings, suggestions)</li>
|
||||
<li>Include specific examples of how to fix identified issues</li>
|
||||
</ul>
|
||||
|
||||
<h2>Usage Examples</h2>
|
||||
|
||||
<h3>Example 1: Security Audit Before Commit</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Use the code-reviewer agent to check for security vulnerabilities in my code before I commit. Focus on exposed secrets, API keys, and input validation</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> Comprehensive security review identifying exposed credentials, missing input validation, and SQL injection risks with specific fixes for each issue.</p>
|
||||
|
||||
<h3>Example 2: Code Quality Review</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Use the code-reviewer agent to review this pull request for code quality. Check for duplicated code, naming conventions, and error handling</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> Detailed quality analysis highlighting code duplication, suggesting better variable names, and identifying missing error handling with refactoring examples.</p>
|
||||
|
||||
<h3>Example 3: Performance and Best Practices</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Use the code-reviewer agent to analyze performance issues and verify we're following best practices for this Node.js API</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> Performance analysis identifying N+1 queries, missing database indexes, and inefficient loops with optimization recommendations and code examples.</p>
|
||||
|
||||
<h2>Review Checklist</h2>
|
||||
|
||||
<p>The Code Reviewer Agent evaluates code against this comprehensive checklist:</p>
|
||||
|
||||
<ul>
|
||||
<li>✓ Code is simple and readable</li>
|
||||
<li>✓ Functions and variables are well-named</li>
|
||||
<li>✓ No duplicated code</li>
|
||||
<li>✓ Proper error handling implemented</li>
|
||||
<li>✓ No exposed secrets or API keys</li>
|
||||
<li>✓ Input validation present</li>
|
||||
<li>✓ Good test coverage</li>
|
||||
<li>✓ Performance considerations addressed</li>
|
||||
</ul>
|
||||
|
||||
<h2>Official Documentation</h2>
|
||||
<p>For more information about agents in Claude Code, see the <a href="https://code.claude.com/docs/en/sub-agents?utm_source=aitmpl&utm_medium=referral&utm_campaign=blog" target="_blank">official sub-agents documentation</a>.</p>
|
||||
|
||||
<!-- Explore Components Banner -->
|
||||
<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">© 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();
|
||||
}
|
||||
|
||||
// Explore components banner hover effect
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const banner = document.querySelector('.explore-components-banner a');
|
||||
if (banner) {
|
||||
banner.addEventListener('mouseenter', () => {
|
||||
banner.style.background = '#00cc33';
|
||||
banner.style.transform = 'translateY(-2px)';
|
||||
});
|
||||
banner.addEventListener('mouseleave', () => {
|
||||
banner.style.background = '#00ff41';
|
||||
banner.style.transform = 'translateY(0)';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 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')) {
|
||||
return '';
|
||||
}
|
||||
return element.textContent || '';
|
||||
case 'img':
|
||||
const alt = element.getAttribute('alt') || '';
|
||||
const src = element.getAttribute('src') || '';
|
||||
return ``;
|
||||
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>
|
||||
|
||||
<!-- Mermaid Diagram Support -->
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
theme: 'dark',
|
||||
themeVariables: {
|
||||
primaryColor: '#F97316',
|
||||
primaryTextColor: '#fff',
|
||||
primaryBorderColor: '#F97316',
|
||||
lineColor: '#00ff41',
|
||||
secondaryColor: '#1a1a1a',
|
||||
tertiaryColor: '#2d2d2d'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,694 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Context7 MCP for Claude Code: Real-Time Documentation & Library Integration Expert</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="Install the Context7 MCP for Claude Code to access real-time library documentation. AI-powered MCP for React, Next.js, Supabase, and 1000+ libraries with up-to-date API references.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://aitmpl.com/blog/context7-mcp/">
|
||||
<meta property="og:title" content="Context7 MCP for Claude Code: Real-Time Documentation & Library Integration">
|
||||
<meta property="og:description" content="Install the Context7 MCP for Claude Code to access real-time library documentation. AI-powered MCP for React, Next.js, Supabase, and 1000+ libraries.">
|
||||
<meta property="og:image" content="https://aitmpl.com/blog/assets/context7-mcp-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="MCP">
|
||||
<meta property="article:tag" content="MCP">
|
||||
<meta property="article:tag" content="Documentation">
|
||||
<meta property="article:tag" content="Context7">
|
||||
<meta property="article:tag" content="Real-time">
|
||||
<meta property="article:tag" content="Libraries">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/blog/context7-mcp/">
|
||||
<meta name="twitter:title" content="Context7 MCP for Claude Code: Real-Time Documentation & Library Integration">
|
||||
<meta name="twitter:description" content="Install the Context7 MCP for Claude Code to access real-time library documentation. AI-powered MCP for React, Next.js, Supabase, and 1000+ libraries.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/blog/assets/context7-mcp-cover.png">
|
||||
|
||||
<!-- Additional SEO -->
|
||||
<meta name="keywords" content="Claude Code MCP, Context7 MCP, Claude Code documentation, real-time library docs, React documentation Claude Code, Next.js docs, Supabase documentation, MCP integration, API documentation, up-to-date documentation, library integration">
|
||||
<meta name="author" content="Claude Code Templates">
|
||||
<link rel="canonical" href="https://aitmpl.com/blog/context7-mcp/">
|
||||
|
||||
<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": "Context7 MCP for Claude Code: Real-Time Documentation & Library Integration",
|
||||
"description": "Install the Context7 MCP for Claude Code to access real-time library documentation. AI-powered MCP for React, Next.js, Supabase, and 1000+ libraries with up-to-date API references.",
|
||||
"image": "https://aitmpl.com/blog/assets/context7-mcp-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/context7-mcp/"
|
||||
},
|
||||
"keywords": "Claude Code MCP, Context7 MCP, real-time documentation, library integration, Claude Code",
|
||||
"wordCount": "900",
|
||||
"articleSection": "Claude Code MCPs",
|
||||
"about": [
|
||||
{"@type": "Thing", "name": "Claude Code"},
|
||||
{"@type": "Thing", "name": "MCP"},
|
||||
{"@type": "Thing", "name": "Context7"},
|
||||
{"@type": "Thing", "name": "Documentation"},
|
||||
{"@type": "SoftwareApplication", "name": "Claude Code", "applicationCategory": "DeveloperApplication"}
|
||||
]
|
||||
}
|
||||
</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">Context7 MCP for Claude Code: Real-Time Documentation & Library Integration</h1>
|
||||
<p class="article-subtitle">Learn how to install and use the Context7 MCP for Claude Code to access real-time library documentation for React, Next.js, Supabase, and 1000+ popular libraries.</p>
|
||||
<div class="article-meta-full">
|
||||
<span class="read-time">4 min read</span>
|
||||
<div class="article-tags">
|
||||
<span class="tag">Claude Code</span>
|
||||
<span class="tag">MCP</span>
|
||||
<span class="tag">Context7</span>
|
||||
<span class="tag">Documentation</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<article class="article-body">
|
||||
<img src="../assets/context7-mcp-cover.png" alt="Context7 MCP for Claude Code" class="article-cover" loading="lazy">
|
||||
|
||||
<div class="article-content-full">
|
||||
<h2>What is the Context7 MCP?</h2>
|
||||
|
||||
<p>The Context7 MCP (Model Context Protocol) for Claude Code provides real-time access to library documentation, API references, and code examples from 1000+ popular programming libraries and frameworks. Never work with outdated documentation again.</p>
|
||||
|
||||
<!-- Mermaid Diagram -->
|
||||
<div class="mermaid-diagram" style="background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 2rem; margin: 2rem 0; text-align: center;">
|
||||
<pre class="mermaid">
|
||||
graph LR
|
||||
A[💻 Claude Code] --> B[🔌 Context7 MCP]
|
||||
B --> C[📚 Real-time Library Docs]
|
||||
C --> D[✨ Up-to-date Code]
|
||||
|
||||
style B fill:#F97316,stroke:#fff,color:#000
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<h2>Key Capabilities</h2>
|
||||
<ul>
|
||||
<li><strong>Real-Time Documentation Access</strong> - Get current API docs for React, Next.js, Supabase, and 1000+ libraries</li>
|
||||
<li><strong>Version-Specific References</strong> - Access documentation for specific library versions</li>
|
||||
<li><strong>Code Examples Integration</strong> - Working code snippets and usage patterns</li>
|
||||
<li><strong>Migration Guides</strong> - Upgrade paths and breaking changes documentation</li>
|
||||
<li><strong>Multi-Library Support</strong> - JavaScript, TypeScript, Python, databases, and UI frameworks</li>
|
||||
<li><strong>Automatic Fetching</strong> - Documentation loaded automatically when referenced in prompts</li>
|
||||
</ul>
|
||||
|
||||
<h2>Installation</h2>
|
||||
|
||||
<p>Install the Context7 MCP using the Claude Code Templates CLI:</p>
|
||||
|
||||
<pre><code class="language-bash">npx claude-code-templates@latest --mcp devtools/context7</code></pre>
|
||||
|
||||
<p><strong>Where is the MCP installed?</strong></p>
|
||||
|
||||
<p>The MCP configuration is saved in <code>.claude/mcp.json</code> in your project directory:</p>
|
||||
|
||||
<pre><code class="language-bash">your-project/
|
||||
├── .claude/
|
||||
│ └── mcp.json # ← MCP configuration installed here
|
||||
├── src/
|
||||
│ └── components/
|
||||
├── package.json
|
||||
└── README.md</code></pre>
|
||||
|
||||
<h2>How to Use the MCP</h2>
|
||||
|
||||
<p>Start Claude Code and simply reference libraries in your prompts - Context7 will automatically fetch current documentation:</p>
|
||||
|
||||
<pre><code class="language-bash"># Start Claude Code
|
||||
claude
|
||||
|
||||
# Then reference any library in your prompt
|
||||
> How do I use Server Actions in Next.js 14 App Router?</code></pre>
|
||||
|
||||
<p>Claude Code will automatically fetch the latest Next.js documentation via Context7 and provide accurate, up-to-date answers.</p>
|
||||
|
||||
<h2>Usage Examples</h2>
|
||||
|
||||
<h3>Example 1: React Server Components</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Create a React Server Component that fetches user data using the async/await pattern. Show me the latest best practices for data fetching in Server Components</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> Context7 fetches the current React Server Components API documentation, async component patterns, streaming and Suspense integration, and caching strategies. You get accurate, up-to-date code examples specific to your React version.</p>
|
||||
|
||||
<h3>Example 2: Next.js App Router Implementation</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> How do I create a dynamic route with multiple parameters in Next.js App Router? Show me the file structure and implementation</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> Context7 provides the latest Next.js App Router documentation including route structure conventions, dynamic segments syntax, generateStaticParams API, and examples of parallel and intercepting routes.</p>
|
||||
|
||||
<h3>Example 3: Supabase Authentication Setup</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Implement email authentication with Supabase in a Next.js app. Include signup, login, and session management</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> Context7 delivers current Supabase Auth API methods, session management best practices, server-side auth helpers, and email verification flows with working code examples.</p>
|
||||
|
||||
<h2>Official Documentation</h2>
|
||||
|
||||
<p>For more information about MCPs in Claude Code, see the <a href="https://code.claude.com/docs/en/mcps?utm_source=aitmpl&utm_medium=referral&utm_campaign=blog" target="_blank">official documentation</a>.</p>
|
||||
|
||||
<!-- Explore Components Banner -->
|
||||
<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">© 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();
|
||||
}
|
||||
|
||||
// Explore components banner hover effect
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const banner = document.querySelector('.explore-components-banner a');
|
||||
if (banner) {
|
||||
banner.addEventListener('mouseenter', () => {
|
||||
banner.style.background = '#00cc33';
|
||||
banner.style.transform = 'translateY(-2px)';
|
||||
});
|
||||
banner.addEventListener('mouseleave', () => {
|
||||
banner.style.background = '#00ff41';
|
||||
banner.style.transform = 'translateY(0)';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 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')) {
|
||||
return '';
|
||||
}
|
||||
return element.textContent || '';
|
||||
case 'img':
|
||||
const alt = element.getAttribute('alt') || '';
|
||||
const src = element.getAttribute('src') || '';
|
||||
return ``;
|
||||
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>
|
||||
|
||||
<!-- Mermaid Diagram Support -->
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
theme: 'dark',
|
||||
themeVariables: {
|
||||
primaryColor: '#F97316',
|
||||
primaryTextColor: '#fff',
|
||||
primaryBorderColor: '#F97316',
|
||||
lineColor: '#00ff41',
|
||||
secondaryColor: '#1a1a1a',
|
||||
tertiaryColor: '#2d2d2d'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,254 @@
|
||||
/**
|
||||
* Blog Search and Filter Controls Styles
|
||||
*/
|
||||
|
||||
/* Main controls container */
|
||||
.blog-controls {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Search container */
|
||||
.search-container {
|
||||
position: relative;
|
||||
flex: 0 1 400px;
|
||||
min-width: 280px;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
position: absolute;
|
||||
left: 1rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #d57455;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.blog-search-input {
|
||||
width: 100%;
|
||||
padding: 0.875rem 3rem 0.875rem 3rem;
|
||||
background: rgba(213, 116, 85, 0.05);
|
||||
border: 2px solid rgba(213, 116, 85, 0.2);
|
||||
border-radius: 8px;
|
||||
color: #E0E0E0;
|
||||
font-size: 0.95rem;
|
||||
font-family: 'Inter', monospace;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.blog-search-input:focus {
|
||||
outline: none;
|
||||
border-color: #d57455;
|
||||
background: rgba(213, 116, 85, 0.1);
|
||||
box-shadow: 0 0 0 3px rgba(213, 116, 85, 0.1);
|
||||
}
|
||||
|
||||
.blog-search-input::placeholder {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.clear-search-btn {
|
||||
position: absolute;
|
||||
right: 0.75rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: rgba(213, 116, 85, 0.1);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 0.375rem;
|
||||
cursor: pointer;
|
||||
color: #d57455;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.clear-search-btn:hover {
|
||||
background: rgba(213, 116, 85, 0.2);
|
||||
}
|
||||
|
||||
.clear-search-btn:active {
|
||||
transform: translateY(-50%) scale(0.95);
|
||||
}
|
||||
|
||||
/* Sort container */
|
||||
.sort-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.sort-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: #d57455;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.sort-label svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sort-select {
|
||||
padding: 0.75rem 2.5rem 0.75rem 1rem;
|
||||
background: rgba(213, 116, 85, 0.05);
|
||||
border: 2px solid rgba(213, 116, 85, 0.2);
|
||||
border-radius: 8px;
|
||||
color: #E0E0E0;
|
||||
font-size: 0.9rem;
|
||||
font-family: 'Inter', monospace;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2 4L6 8L10 4' stroke='%23d57455' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 0.875rem center;
|
||||
}
|
||||
|
||||
.sort-select:hover {
|
||||
border-color: #d57455;
|
||||
background-color: rgba(213, 116, 85, 0.08);
|
||||
}
|
||||
|
||||
.sort-select:focus {
|
||||
outline: none;
|
||||
border-color: #d57455;
|
||||
box-shadow: 0 0 0 3px rgba(213, 116, 85, 0.1);
|
||||
}
|
||||
|
||||
.sort-select option {
|
||||
background: #1a1a1a;
|
||||
color: #E0E0E0;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
/* Results info */
|
||||
.results-info {
|
||||
display: none;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 0.75rem 1rem;
|
||||
background: rgba(0, 208, 132, 0.05);
|
||||
border-left: 3px solid #00D084;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 0.9rem;
|
||||
color: #E0E0E0;
|
||||
}
|
||||
|
||||
#results-count {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.clear-filters-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
background: rgba(0, 208, 132, 0.1);
|
||||
border: 1px solid rgba(0, 208, 132, 0.3);
|
||||
border-radius: 6px;
|
||||
color: #00D084;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.clear-filters-btn:hover {
|
||||
background: rgba(0, 208, 132, 0.2);
|
||||
border-color: #00D084;
|
||||
}
|
||||
|
||||
.clear-filters-btn:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
/* Responsive design */
|
||||
@media (max-width: 768px) {
|
||||
.blog-controls {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
width: 100%;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.sort-container {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.sort-select {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.results-info {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.clear-filters-btn {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.blog-search-input {
|
||||
font-size: 0.875rem;
|
||||
padding: 0.75rem 2.75rem 0.75rem 2.75rem;
|
||||
}
|
||||
|
||||
.sort-select {
|
||||
font-size: 0.875rem;
|
||||
padding: 0.65rem 2.5rem 0.65rem 0.875rem;
|
||||
}
|
||||
|
||||
.sort-label {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animation for results */
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.results-info {
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
/* Dark mode optimization */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.blog-search-input,
|
||||
.sort-select {
|
||||
background: rgba(213, 116, 85, 0.03);
|
||||
}
|
||||
|
||||
.blog-search-input:focus,
|
||||
.sort-select:hover {
|
||||
background: rgba(213, 116, 85, 0.08);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,853 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>E2B + Claude Code Sandbox: Secure Cloud Development Environment Guide</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 run Claude Code in isolated E2B cloud sandbox. Install components safely, execute prompts in secure environment, and develop without local system risks.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://aitmpl.com/blog/e2b-claude-code-sandbox/">
|
||||
<meta property="og:title" content="E2B + Claude Code Sandbox: Secure Cloud Development Environment Guide">
|
||||
<meta property="og:description" content="Complete guide to run Claude Code in isolated E2B cloud sandbox. Install components safely, execute prompts in secure environment, and develop without local system risks.">
|
||||
<meta property="og:image" content="https://aitmpl.com/blog/assets/e2b-claude-code-sandbox-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="Cloud Development">
|
||||
<meta property="article:tag" content="E2B">
|
||||
<meta property="article:tag" content="Claude Code">
|
||||
<meta property="article:tag" content="Sandbox">
|
||||
<meta property="article:tag" content="Cloud Development">
|
||||
<meta property="article:tag" content="Security">
|
||||
<meta property="article:tag" content="Isolation">
|
||||
<meta property="article:tag" content="AI Development">
|
||||
<meta property="article:tag" content="Anthropic">
|
||||
<meta property="article:tag" content="Safe Execution">
|
||||
<meta property="article:tag" content="Python SDK">
|
||||
<meta property="article:tag" content="Development Environment">
|
||||
<meta property="article:tag" content="Remote Development">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/blog/e2b-claude-code-sandbox/">
|
||||
<meta name="twitter:title" content="E2B + Claude Code Sandbox: Secure Cloud Development Environment Guide">
|
||||
<meta name="twitter:description" content="Complete guide to run Claude Code in isolated E2B cloud sandbox. Install components safely, execute prompts in secure environment, and develop without local system risks.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/blog/assets/e2b-claude-code-sandbox-cover.png">
|
||||
|
||||
<!-- Additional SEO -->
|
||||
<meta name="keywords" content="E2B Claude Code sandbox, cloud development environment, secure code execution, isolated development, Claude Code safety, E2B Python SDK, remote development, AI sandbox, secure AI development, cloud sandbox, development isolation, safe code generation">
|
||||
<meta name="author" content="Claude Code Templates">
|
||||
<link rel="canonical" href="https://aitmpl.com/blog/e2b-claude-code-sandbox/">
|
||||
|
||||
<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": "E2B + Claude Code Sandbox: Secure Cloud Development Environment Guide",
|
||||
"description": "Complete guide to run Claude Code in isolated E2B cloud sandbox. Install components safely, execute prompts in secure environment, and develop without local system risks.",
|
||||
"image": "https://aitmpl.com/blog/assets/e2b-claude-code-sandbox-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/e2b-claude-code-sandbox/"
|
||||
},
|
||||
"keywords": "E2B Claude Code sandbox, cloud development environment, secure code execution, isolated development",
|
||||
"wordCount": "1800",
|
||||
"articleSection": "Cloud Development",
|
||||
"about": [
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "E2B"
|
||||
},
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Claude Code"
|
||||
},
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Cloud Development"
|
||||
},
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Sandbox Environment"
|
||||
}
|
||||
],
|
||||
"mentions": [
|
||||
{
|
||||
"@type": "SoftwareApplication",
|
||||
"name": "E2B",
|
||||
"url": "https://e2b.dev"
|
||||
},
|
||||
{
|
||||
"@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">E2B + Claude Code Sandbox: Secure Cloud Development Environment</h1>
|
||||
<p class="article-subtitle">Complete guide to execute Claude Code in isolated E2B cloud sandbox. Install any components safely, run prompts in secure environment, and develop without local system risks.</p>
|
||||
<div class="article-meta-full">
|
||||
<span class="read-time">6 min read</span>
|
||||
<div class="article-tags">
|
||||
<span class="tag">E2B</span>
|
||||
<span class="tag">Sandbox</span>
|
||||
<span class="tag">Security</span>
|
||||
<span class="tag">Cloud</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<article class="article-body">
|
||||
<img src="../assets/e2b-claude-code-sandbox-cover.png" alt="E2B + Claude Code Sandbox: Secure Cloud Development Environment" class="article-cover" loading="lazy">
|
||||
|
||||
<div class="article-content-full">
|
||||
<h2>Benefits of Secure Sandbox Execution</h2>
|
||||
|
||||
<p>Running Claude Code in an isolated E2B cloud sandbox provides essential security and development advantages:</p>
|
||||
|
||||
<table class="components-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Benefit</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>🔒 Complete Isolation</strong></td>
|
||||
<td>Code runs in a separate cloud environment with zero access to your local system, files, or network.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>🛡️ No Local Impact</strong></td>
|
||||
<td>Eliminates risk of system modifications, file corruption, or security vulnerabilities on your machine.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>🚀 Clean Environment</strong></td>
|
||||
<td>Fresh Ubuntu with Claude Code pre-installed, ensuring consistent execution without dependency conflicts.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>☁️ Remote Execution</strong></td>
|
||||
<td>Intensive operations run in the cloud without consuming your local CPU, memory, or storage.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>🧪 Safe Testing</strong></td>
|
||||
<td>Test new agents and commands risk-free, with automatic cleanup after execution.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>E2B Claude Code Sandbox Component</h2>
|
||||
|
||||
<p>Claude Code Templates provides a ready-to-use E2B sandbox integration:</p>
|
||||
|
||||
<h3>🔧 What's Included</h3>
|
||||
<table class="components-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Component</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>E2B Launcher Script</strong></td>
|
||||
<td>Python script that creates, configures, and manages E2B sandbox instances with Claude Code.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Automatic Setup</strong></td>
|
||||
<td>Downloads dependencies, checks Python environment, and installs E2B SDK automatically.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Component Integration</strong></td>
|
||||
<td>Installs any specified agents, commands, MCPs, settings, and hooks inside the sandbox.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2>Prerequisites</h2>
|
||||
|
||||
<p>You'll need two API keys to use the E2B sandbox:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>E2B API Key:</strong> Get it from <a href="https://e2b.dev/dashboard" target="_blank" rel="noopener">e2b.dev/dashboard</a> (free tier available)</li>
|
||||
<li><strong>Anthropic API Key:</strong> Get it from <a href="https://console.anthropic.com" target="_blank" rel="noopener">console.anthropic.com</a></li>
|
||||
<li><strong>Python 3.11+:</strong> Required for the E2B SDK (usually auto-installed)</li>
|
||||
</ul>
|
||||
|
||||
<h2>Installation and Usage</h2>
|
||||
|
||||
<h3>Quick Start - Simple Execution</h3>
|
||||
<pre><code class="language-bash"># Execute a prompt in secure E2B sandbox with API keys
|
||||
npx claude-code-templates@latest --sandbox e2b \
|
||||
--e2b-api-key your_e2b_key \
|
||||
--anthropic-api-key your_anthropic_key \
|
||||
--prompt "Create a React todo app with TypeScript"
|
||||
|
||||
# Or use environment variables (set E2B_API_KEY and ANTHROPIC_API_KEY)
|
||||
npx claude-code-templates@latest --sandbox e2b --prompt "Create a React todo app with TypeScript"
|
||||
|
||||
# Interactive mode - let the CLI guide you
|
||||
npx claude-code-templates@latest --sandbox e2b \
|
||||
--e2b-api-key your_e2b_key \
|
||||
--anthropic-api-key your_anthropic_key</code></pre>
|
||||
|
||||
<p><strong>This command will:</strong></p>
|
||||
<ul>
|
||||
<li>✓ Create secure E2B cloud sandbox</li>
|
||||
<li>✓ Execute your prompt using Claude Code</li>
|
||||
<li>✓ Download generated files to `sandbox-xxxxx` folder</li>
|
||||
<li>✓ Automatically cleanup after execution</li>
|
||||
</ul>
|
||||
|
||||
<h3>Advanced Usage - With Specific Agent</h3>
|
||||
<pre><code class="language-bash"># Execute with a specific agent in sandbox
|
||||
npx claude-code-templates@latest --sandbox e2b \
|
||||
--e2b-api-key your_e2b_key \
|
||||
--anthropic-api-key your_anthropic_key \
|
||||
--agent development-team/frontend-developer \
|
||||
--prompt "Create a modern todo app with TypeScript and Tailwind"</code></pre>
|
||||
|
||||
<h3>Full Stack Development</h3>
|
||||
<pre><code class="language-bash"># Complete full-stack app development
|
||||
npx claude-code-templates@latest --sandbox e2b \
|
||||
--e2b-api-key your_e2b_key \
|
||||
--anthropic-api-key your_anthropic_key \
|
||||
--agent development-team/fullstack-developer \
|
||||
--prompt "Create a Node.js API with PostgreSQL and authentication"</code></pre>
|
||||
|
||||
<h3>Data Science and Analysis</h3>
|
||||
<pre><code class="language-bash"># Data analysis with specialized agent
|
||||
npx claude-code-templates@latest --sandbox e2b \
|
||||
--e2b-api-key your_e2b_key \
|
||||
--anthropic-api-key your_anthropic_key \
|
||||
--agent ai-ml-specialists/data-scientist \
|
||||
--prompt "Analyze this dataset and create interactive visualizations"</code></pre>
|
||||
|
||||
<h2>API Key Configuration</h2>
|
||||
|
||||
<p>You can provide your E2B and Anthropic API keys in two convenient ways:</p>
|
||||
|
||||
<h3>🔑 Option 1: CLI Parameters (Recommended)</h3>
|
||||
<pre><code class="language-bash"># Pass API keys directly as command parameters
|
||||
npx claude-code-templates@latest --sandbox e2b \
|
||||
--e2b-api-key your_e2b_api_key_here \
|
||||
--anthropic-api-key your_anthropic_api_key_here \
|
||||
--prompt "Your prompt here"</code></pre>
|
||||
|
||||
<p><strong>Benefits:</strong></p>
|
||||
<ul>
|
||||
<li>✓ No need to set up environment variables</li>
|
||||
<li>✓ Works immediately without additional configuration</li>
|
||||
<li>✓ Perfect for CI/CD pipelines and automation</li>
|
||||
<li>✓ No risk of accidentally committing keys to repositories</li>
|
||||
</ul>
|
||||
|
||||
<h3>🌍 Option 2: Environment Variables</h3>
|
||||
<pre><code class="language-bash"># Set environment variables in your shell
|
||||
export E2B_API_KEY=your_e2b_api_key_here
|
||||
export ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
||||
|
||||
# Then run without API key parameters
|
||||
npx claude-code-templates@latest --sandbox e2b --prompt "Your prompt here"</code></pre>
|
||||
|
||||
<p><strong>Note:</strong> CLI parameters take precedence over environment variables, so you can override environment settings when needed.</p>
|
||||
|
||||
<h2>Environment Configuration</h2>
|
||||
|
||||
<p>After first execution, the sandbox component creates these files in your project:</p>
|
||||
|
||||
<pre><code class="language-bash">your-project/
|
||||
├── .claude/
|
||||
│ └── sandbox/
|
||||
│ ├── e2b-launcher.py # Python launcher script
|
||||
│ ├── requirements.txt # E2B SDK dependencies
|
||||
│ └── .env.example # Environment variables template
|
||||
└── sandbox-xxxxxxxx/ # Generated project files (after execution)
|
||||
├── index.html # Your generated files
|
||||
├── src/ # Project source code
|
||||
└── ...</code></pre>
|
||||
|
||||
<h2>Use Cases - When You Need Sandbox Isolation</h2>
|
||||
|
||||
<h3>🔥 System-Level Operations</h3>
|
||||
<pre><code class="language-bash"># Modify system configurations without risk
|
||||
npx claude-code-templates@latest --sandbox e2b \
|
||||
--e2b-api-key your_e2b_key \
|
||||
--anthropic-api-key your_anthropic_key \
|
||||
--agent devops-cloud/system-administrator \
|
||||
--prompt "Set up nginx with multiple virtual hosts, SSL certificates, and firewall rules"</code></pre>
|
||||
|
||||
<h3>🧪 Experimental Package Installation</h3>
|
||||
<pre><code class="language-bash"># Test unknown dependencies and packages safely
|
||||
npx claude-code-templates@latest --sandbox e2b \
|
||||
--e2b-api-key your_e2b_key \
|
||||
--anthropic-api-key your_anthropic_key \
|
||||
--agent development-team/fullstack-developer \
|
||||
--prompt "Install and configure a complex tech stack with Redis, RabbitMQ, Elasticsearch, and their dependencies"</code></pre>
|
||||
|
||||
<h3>🛡️ Security Vulnerability Testing</h3>
|
||||
<pre><code class="language-bash"># Run penetration testing tools without affecting your system
|
||||
npx claude-code-templates@latest --sandbox e2b \
|
||||
--e2b-api-key your_e2b_key \
|
||||
--anthropic-api-key your_anthropic_key \
|
||||
--agent security-testing/penetration-tester \
|
||||
--prompt "Set up and run OWASP ZAP, Metasploit, and Nmap to test this application"</code></pre>
|
||||
|
||||
<h3>💣 Database Operations</h3>
|
||||
<pre><code class="language-bash"># Perform destructive database migrations and schema changes
|
||||
npx claude-code-templates@latest --sandbox e2b \
|
||||
--e2b-api-key your_e2b_key \
|
||||
--anthropic-api-key your_anthropic_key \
|
||||
--agent data-analytics/database-administrator \
|
||||
--prompt "Create a PostgreSQL database, run complex migrations, drop tables, and test backup/restore procedures"</code></pre>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
|
||||
<h3>Common Issues and Solutions</h3>
|
||||
<table class="components-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Issue</th>
|
||||
<th>Solution</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>Python Not Found</strong></td>
|
||||
<td>Install Python 3.11+ from <a href="https://python.org/downloads" target="_blank">python.org/downloads</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>API Keys Not Set</strong></td>
|
||||
<td>Create <code>.env</code> file in <code>.claude/sandbox/</code> with your E2B and Anthropic API keys</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Dependencies Failed</strong></td>
|
||||
<td>Run <code>pip3 install -r .claude/sandbox/requirements.txt</code> manually</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Sandbox Timeout</strong></td>
|
||||
<td>Large prompts may take time; configured with 15-minute timeout for complex operations</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- Explore Components Banner -->
|
||||
<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">© 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();
|
||||
}
|
||||
|
||||
// Explore components banner hover effect
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const banner = document.querySelector('.explore-components-banner a');
|
||||
if (banner) {
|
||||
banner.addEventListener('mouseenter', () => {
|
||||
banner.style.background = '#00cc33';
|
||||
banner.style.transform = 'translateY(-2px)';
|
||||
});
|
||||
banner.addEventListener('mouseleave', () => {
|
||||
banner.style.background = '#00ff41';
|
||||
banner.style.transform = 'translateY(0)';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 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')) {
|
||||
return '';
|
||||
}
|
||||
return element.textContent || '';
|
||||
case 'img':
|
||||
const alt = element.getAttribute('alt') || '';
|
||||
const src = element.getAttribute('src') || '';
|
||||
return ``;
|
||||
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>
|
||||
@@ -0,0 +1,728 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Frontend Developer Agent for Claude Code: React, Vue & Vite Expert AI Assistant</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="Install the Frontend Developer Agent for Claude Code to build React, Vue, and Vite applications faster. AI-powered agent for UI components, state management, and performance optimization.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://aitmpl.com/blog/frontend-developer-agent/">
|
||||
<meta property="og:title" content="Frontend Developer Agent for Claude Code: React, Vue & Vite Expert">
|
||||
<meta property="og:description" content="Install the Frontend Developer Agent for Claude Code to build React, Vue, and Vite applications faster. AI-powered agent for UI components, state management, and performance optimization.">
|
||||
<meta property="og:image" content="https://aitmpl.com/blog/assets/frontend-developer-agent-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="Agents">
|
||||
<meta property="article:tag" content="Frontend">
|
||||
<meta property="article:tag" content="React">
|
||||
<meta property="article:tag" content="Vue">
|
||||
<meta property="article:tag" content="Next.js">
|
||||
<meta property="article:tag" content="AI Development">
|
||||
<meta property="article:tag" content="Web Development">
|
||||
<meta property="article:tag" content="Component Development">
|
||||
<meta property="article:tag" content="TypeScript">
|
||||
<meta property="article:tag" content="Responsive Design">
|
||||
<meta property="article:tag" content="Performance Optimization">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/blog/frontend-developer-agent/">
|
||||
<meta name="twitter:title" content="Frontend Developer Agent for Claude Code: React, Vue & Vite Expert">
|
||||
<meta name="twitter:description" content="Install the Frontend Developer Agent for Claude Code to build React, Vue, and Vite applications faster. AI-powered agent for UI components, state management, and performance optimization.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/blog/assets/frontend-developer-agent-cover.png">
|
||||
|
||||
<!-- Additional SEO -->
|
||||
<meta name="keywords" content="Claude Code agent, Frontend Developer Agent, Claude Code frontend, React agent, Vue agent, Vite development, Claude Code React, AI frontend development, React components, Vue components, state management, Tailwind CSS, TypeScript agent, frontend performance, Claude Code templates, AI coding assistant, React hooks, Vue Composition API, UI components, responsive design">
|
||||
<meta name="author" content="Claude Code Templates">
|
||||
<link rel="canonical" href="https://aitmpl.com/blog/frontend-developer-agent/">
|
||||
|
||||
<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": "Frontend Developer Agent for Claude Code: React, Vue & Vite Expert AI Assistant",
|
||||
"description": "Install the Frontend Developer Agent for Claude Code to build React, Vue, and Vite applications faster. AI-powered agent for UI components, state management, and performance optimization.",
|
||||
"image": "https://aitmpl.com/blog/assets/frontend-developer-agent-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/frontend-developer-agent/"
|
||||
},
|
||||
"keywords": "Claude Code agent, Frontend Developer Agent, React agent, Vue agent, Vite development, Claude Code React, AI frontend development, React components, state management, Claude Code templates",
|
||||
"wordCount": "600",
|
||||
"articleSection": "Claude Code Agents",
|
||||
"about": [
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Claude Code"
|
||||
},
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Frontend Development Agent"
|
||||
},
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "React Development"
|
||||
},
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Vue.js Development"
|
||||
},
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Vite"
|
||||
},
|
||||
{
|
||||
"@type": "SoftwareApplication",
|
||||
"name": "Claude Code",
|
||||
"applicationCategory": "DeveloperApplication"
|
||||
}
|
||||
]
|
||||
}
|
||||
</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">Frontend Developer Agent for Claude Code: React, Vue & Vite Expert</h1>
|
||||
<p class="article-subtitle">Learn how to install and use the Frontend Developer Agent for Claude Code to build React, Vue, and Vite applications faster with AI-powered assistance for UI components, state management, and performance optimization.</p>
|
||||
<div class="article-meta-full">
|
||||
<span class="read-time">4 min read</span>
|
||||
<div class="article-tags">
|
||||
<span class="tag">Claude Code</span>
|
||||
<span class="tag">Agent</span>
|
||||
<span class="tag">Frontend</span>
|
||||
<span class="tag">React</span>
|
||||
<span class="tag">Vue</span>
|
||||
<span class="tag">Vite</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<article class="article-body">
|
||||
<img src="../assets/frontend-developer-agent-cover.png" alt="Frontend Developer Agent for Claude Code" class="article-cover" loading="lazy">
|
||||
|
||||
<div class="article-content-full">
|
||||
<h2>What is the Frontend Developer Agent?</h2>
|
||||
|
||||
<p>The Frontend Developer Agent is a specialized Claude Code agent focused on modern frontend development with React, Vue, and Vite. It helps you build reusable UI components, implement responsive design, manage application state, and optimize frontend performance following industry best practices.</p>
|
||||
|
||||
<!-- Mermaid Diagram -->
|
||||
<div class="mermaid-diagram" style="background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 2rem; margin: 2rem 0; text-align: center;">
|
||||
<pre class="mermaid">
|
||||
graph LR
|
||||
A[👤 User Prompt] --> B[🤖 Frontend Developer Agent]
|
||||
B --> C[⚛️ React/Vue/Vite Code]
|
||||
C --> D[📦 Your Project]
|
||||
|
||||
style B fill:#F97316,stroke:#fff,color:#000
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<h2>Key Capabilities</h2>
|
||||
<ul>
|
||||
<li><strong>React Component Architecture</strong> (hooks, context, performance)</li>
|
||||
<li><strong>Vue.js Development</strong> (Composition API, Pinia, reactivity)</li>
|
||||
<li><strong>Vite Configuration</strong> (build optimization, dev server setup)</li>
|
||||
<li><strong>Responsive CSS</strong> (Tailwind CSS, CSS-in-JS, mobile-first design)</li>
|
||||
<li><strong>State Management</strong> (Redux, Zustand, Context API, Pinia)</li>
|
||||
<li><strong>Performance Optimization</strong> (lazy loading, code splitting, memoization)</li>
|
||||
<li><strong>Accessibility</strong> (WCAG compliance, ARIA labels, keyboard navigation)</li>
|
||||
</ul>
|
||||
|
||||
<h2>Installation</h2>
|
||||
|
||||
<p>Install the Frontend Developer Agent using the Claude Code Templates CLI:</p>
|
||||
|
||||
<pre><code class="language-bash">npx claude-code-templates@latest --agent development-team/frontend-developer</code></pre>
|
||||
|
||||
<p><strong>Where is the agent installed?</strong></p>
|
||||
<p>The agent is saved in <code>.claude/agents/frontend-developer.md</code> in your project directory:</p>
|
||||
|
||||
<pre><code class="language-bash">your-project/
|
||||
├── .claude/
|
||||
│ └── agents/
|
||||
│ └── frontend-developer.md # ← Agent installed here
|
||||
├── src/
|
||||
│ └── components/
|
||||
├── package.json
|
||||
└── README.md</code></pre>
|
||||
|
||||
<h2>How to Use the Agent</h2>
|
||||
|
||||
<p>Start Claude Code and explicitly request the agent in your prompt:</p>
|
||||
|
||||
<pre><code class="language-bash"># Start Claude Code
|
||||
claude
|
||||
|
||||
# Then write your prompt requesting the agent
|
||||
> Use the frontend-developer agent to create a Button component in React with TypeScript that supports variants (primary, secondary, danger) and sizes (sm, md, lg)</code></pre>
|
||||
|
||||
<p>The agent will generate code focused on:</p>
|
||||
<ul>
|
||||
<li>Complete React component with props interface</li>
|
||||
<li>Styling solution (Tailwind CSS or styled-components)</li>
|
||||
<li>State management implementation if needed</li>
|
||||
<li>Basic unit test structure</li>
|
||||
<li>Accessibility checklist</li>
|
||||
</ul>
|
||||
|
||||
<h2>Usage Examples</h2>
|
||||
|
||||
<h3>Example 1: Create a Product Card Component</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Use the frontend-developer agent to create a ProductCard component that displays image, title, price, and add to cart button. Make it responsive and accessible</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> React component with mobile-first design, proper ARIA attributes, and optimized image loading.</p>
|
||||
|
||||
<h3>Example 2: Implement State Management</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Use the frontend-developer agent to implement a shopping cart using Context API. Include actions for add, remove, and update quantities</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> Context provider with reducer, typed actions, and custom hooks to access cart state.</p>
|
||||
|
||||
<h3>Example 3: Build a Vue Component with Vite</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Use the frontend-developer agent to create a Vue 3 data table component with Vite. Include sorting, filtering, and pagination</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> Vue component using Composition API, reactive state management with Pinia, and optimized Vite build configuration.</p>
|
||||
|
||||
<h2>Official Documentation</h2>
|
||||
<p>For more information about agents in Claude Code, see the <a href="https://code.claude.com/docs/en/sub-agents?utm_source=aitmpl&utm_medium=referral&utm_campaign=blog" target="_blank">official sub-agents documentation</a>.</p>
|
||||
|
||||
<!-- Explore Components Banner -->
|
||||
<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">© 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();
|
||||
}
|
||||
|
||||
// Explore components banner hover effect
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const banner = document.querySelector('.explore-components-banner a');
|
||||
if (banner) {
|
||||
banner.addEventListener('mouseenter', () => {
|
||||
banner.style.background = '#00cc33';
|
||||
banner.style.transform = 'translateY(-2px)';
|
||||
});
|
||||
banner.addEventListener('mouseleave', () => {
|
||||
banner.style.background = '#00ff41';
|
||||
banner.style.transform = 'translateY(0)';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 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')) {
|
||||
return '';
|
||||
}
|
||||
return element.textContent || '';
|
||||
case 'img':
|
||||
const alt = element.getAttribute('alt') || '';
|
||||
const src = element.getAttribute('src') || '';
|
||||
return ``;
|
||||
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>
|
||||
|
||||
<!-- Mermaid Diagram Support -->
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
theme: 'dark',
|
||||
themeVariables: {
|
||||
primaryColor: '#F97316',
|
||||
primaryTextColor: '#fff',
|
||||
primaryBorderColor: '#F97316',
|
||||
lineColor: '#00ff41',
|
||||
secondaryColor: '#1a1a1a',
|
||||
tertiaryColor: '#2d2d2d'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,749 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Win Your Next Hackathon with the AI Strategist Agent for Claude Code</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="Install the Hackathon AI Strategist agent for Claude Code to get expert ideation, judging-criteria evaluation, pitch coaching, and time management for your next hackathon.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://aitmpl.com/blog/hackathon-ai-strategist-agent/">
|
||||
<meta property="og:title" content="Win Your Next Hackathon with the AI Strategist Agent for Claude Code">
|
||||
<meta property="og:description" content="Install the Hackathon AI Strategist agent for Claude Code to get expert ideation, judging-criteria evaluation, pitch coaching, and time management for your next hackathon.">
|
||||
<meta property="og:image" content="https://aitmpl.com/blog/assets/hackathon-ai-strategist-agent-cover.svg">
|
||||
<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="Agents">
|
||||
<meta property="article:tag" content="Hackathon">
|
||||
<meta property="article:tag" content="AI Strategy">
|
||||
<meta property="article:tag" content="Agents">
|
||||
<meta property="article:tag" content="Ideation">
|
||||
<meta property="article:tag" content="Competition">
|
||||
<meta property="article:tag" content="Claude Code">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/blog/hackathon-ai-strategist-agent/">
|
||||
<meta name="twitter:title" content="Win Your Next Hackathon with the AI Strategist Agent for Claude Code">
|
||||
<meta name="twitter:description" content="Install the Hackathon AI Strategist agent for Claude Code to get expert ideation, judging-criteria evaluation, pitch coaching, and time management for your next hackathon.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/blog/assets/hackathon-ai-strategist-agent-cover.svg">
|
||||
|
||||
<!-- Additional SEO -->
|
||||
<meta name="keywords" content="Hackathon AI Strategist, Claude Code Agent, Hackathon Strategy, AI Ideation, Hackathon Winning Tips, Claude Code Templates, Hackathon Judge Criteria, AI Competition, Pitch Strategy, MVP Scoping">
|
||||
<meta name="author" content="Claude Code Templates">
|
||||
<link rel="canonical" href="https://aitmpl.com/blog/hackathon-ai-strategist-agent/">
|
||||
|
||||
<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": "Win Your Next Hackathon with the AI Strategist Agent for Claude Code",
|
||||
"description": "Install the Hackathon AI Strategist agent for Claude Code to get expert ideation, judging-criteria evaluation, pitch coaching, and time management for your next hackathon.",
|
||||
"image": "https://aitmpl.com/blog/assets/hackathon-ai-strategist-agent-cover.svg",
|
||||
"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/hackathon-ai-strategist-agent/"
|
||||
},
|
||||
"keywords": "Hackathon AI Strategist, Claude Code Agent, Hackathon Strategy, AI Ideation, Hackathon Judge Criteria",
|
||||
"articleSection": "Agents"
|
||||
}
|
||||
</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">
|
||||
<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">Win Your Next Hackathon with the AI Strategist Agent for Claude Code</h1>
|
||||
<p class="article-subtitle">Get expert ideation, judge-level evaluation, pitch coaching, and time management from an AI mentor that has seen what wins.</p>
|
||||
<div class="article-meta-full">
|
||||
<span class="read-time">5 min read</span>
|
||||
<div class="article-tags">
|
||||
<span class="tag">Hackathon</span>
|
||||
<span class="tag">AI Strategy</span>
|
||||
<span class="tag">Agents</span>
|
||||
<span class="tag">Ideation</span>
|
||||
<span class="tag">Competition</span>
|
||||
<span class="tag">Claude Code</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<article class="article-body">
|
||||
<img src="../assets/hackathon-ai-strategist-agent-cover.svg" alt="Win Your Next Hackathon with the AI Strategist Agent for Claude Code" class="article-cover" loading="lazy">
|
||||
|
||||
<div class="article-content-full">
|
||||
<h2>Installation</h2>
|
||||
|
||||
<p>Install the Hackathon AI Strategist agent using the Claude Code Templates CLI:</p>
|
||||
|
||||
<pre><code class="language-bash">npx claude-code-templates@latest --agent ai-specialists/hackathon-ai-strategist</code></pre>
|
||||
|
||||
<p>This command automatically installs the agent configuration into your project's <code>.claude/agents/</code> directory, ready to use immediately in your next Claude Code session.</p>
|
||||
|
||||
<div class="info-box">
|
||||
<strong>Want to understand how it works?</strong> Keep reading to learn what this agent does under the hood and why it's essential for your workflow.
|
||||
</div>
|
||||
|
||||
<h2>The Problem: Most Hackathon Teams Lose Before They Start Building</h2>
|
||||
|
||||
<p>Hackathons are won and lost in the first two hours. The team that picks the right idea, scopes it correctly, and plans a compelling demo has an enormous advantage over the team that dives straight into code. Yet most developers skip strategy entirely and jump into building something that is either too ambitious to finish or too boring to impress the judges.</p>
|
||||
|
||||
<p>Common mistakes that kill hackathon projects:</p>
|
||||
|
||||
<ul>
|
||||
<li>Choosing an idea that cannot be demoed in 3 minutes</li>
|
||||
<li>Building features that judges do not care about</li>
|
||||
<li>Spending 80% of the time on backend plumbing nobody will see</li>
|
||||
<li>Ignoring the judging criteria until the pitch</li>
|
||||
<li>No fallback plan when the primary approach hits a wall at 3 AM</li>
|
||||
</ul>
|
||||
|
||||
<p>The Hackathon AI Strategist agent solves this by giving you an experienced mentor who thinks like both a winner and a judge.</p>
|
||||
|
||||
<h2>What the Agent Does</h2>
|
||||
|
||||
<p>The Hackathon AI Strategist is an expert agent with dual expertise: serial hackathon winner (20+ wins) and judge at major competitions like HackMIT, TreeHacks, and PennApps. It provides five core capabilities:</p>
|
||||
|
||||
<h3>1. Winning Concept Ideation</h3>
|
||||
|
||||
<p>The agent generates AI solution ideas that balance innovation, feasibility, and impact. It prioritizes clear problem-solution fit, technical impressiveness within a 24-48 hour timeframe, creative AI usage that goes beyond basic API calls, and solutions with a strong "wow factor" during demos.</p>
|
||||
|
||||
<h3>2. Judge-Level Evaluation</h3>
|
||||
|
||||
<p>Every idea gets scored against the same criteria real judges use:</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Criterion</th>
|
||||
<th>Weight</th>
|
||||
<th>What Judges Look For</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Innovation & Originality</td>
|
||||
<td>25-30%</td>
|
||||
<td>Novel approach, not another todo app</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Technical Complexity</td>
|
||||
<td>25-30%</td>
|
||||
<td>Clever engineering, not just API wrappers</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Impact & Scalability</td>
|
||||
<td>20-25%</td>
|
||||
<td>Real-world potential, clear user benefit</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Presentation & Demo</td>
|
||||
<td>15-20%</td>
|
||||
<td>Smooth demo, compelling narrative</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Completeness & Polish</td>
|
||||
<td>5-10%</td>
|
||||
<td>Feels finished, no rough edges visible</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>3. Strategic Time Management</h3>
|
||||
|
||||
<p>The agent recommends how to allocate your hours across ideation, building, and polishing. It identifies which features to prioritize for the demo and which to fake or skip entirely.</p>
|
||||
|
||||
<h3>4. AI Trend Awareness</h3>
|
||||
|
||||
<p>It stays current with cutting-edge AI capabilities and suggests incorporating the latest model features, novel applications of existing technology, clever multi-service combinations, and emerging techniques that judges have not seen repeatedly.</p>
|
||||
|
||||
<h3>5. Constraint Optimization</h3>
|
||||
|
||||
<p>The agent excels at scoping ambitious ideas into achievable MVPs, identifying pre-built components and APIs that accelerate development, suggesting impressive features that are secretly simple to implement, and planning fallback options when primary approaches fail.</p>
|
||||
|
||||
<h2>Agent Configuration</h2>
|
||||
|
||||
<p>Here is the core of the agent's system prompt that gets installed:</p>
|
||||
|
||||
<pre><code class="language-text">You are an elite hackathon strategist with dual expertise
|
||||
as both a serial hackathon winner and an experienced judge
|
||||
at major AI competitions.
|
||||
|
||||
Tools: Read, WebSearch, WebFetch
|
||||
Model: sonnet
|
||||
|
||||
Key behaviors:
|
||||
- Generate ideas balancing innovation + feasibility + impact
|
||||
- Evaluate through real judging criteria with weighted scores
|
||||
- Recommend team composition and skill distribution
|
||||
- Suggest time allocation across ideation, building, polishing
|
||||
- Identify technical pitfalls and shortcuts
|
||||
- Advise on features to prioritize vs. fake for demos
|
||||
- Coach on pitch narratives and demo flows</code></pre>
|
||||
|
||||
<p>The agent uses <code>WebSearch</code> and <code>WebFetch</code> tools to research current AI trends, hackathon themes, and sponsor technologies in real time, giving you advice grounded in what is happening right now rather than outdated patterns.</p>
|
||||
|
||||
<h2>Usage Examples</h2>
|
||||
|
||||
<p>Once installed, you can invoke the agent in Claude Code for different hackathon scenarios:</p>
|
||||
|
||||
<h3>Brainstorming Ideas for a Theme</h3>
|
||||
|
||||
<pre><code class="language-text">I'm at a healthcare AI hackathon with 36 hours.
|
||||
My team has 2 backend devs, 1 frontend dev, and 1 designer.
|
||||
The sponsor prizes include best use of LLMs and best social impact.
|
||||
Give me 5 winning project ideas ranked by likelihood of winning.</code></pre>
|
||||
|
||||
<h3>Evaluating Your Current Idea</h3>
|
||||
|
||||
<pre><code class="language-text">We want to build an AI-powered code review tool that uses
|
||||
vision models to analyze screenshots of code.
|
||||
Score this idea against typical judging criteria and tell me
|
||||
what to change to maximize our chances.</code></pre>
|
||||
|
||||
<h3>Planning Your Sprint</h3>
|
||||
|
||||
<pre><code class="language-text">We picked our idea: a multimodal AI assistant for elderly users.
|
||||
We have 24 hours left. Create a detailed hour-by-hour schedule
|
||||
including what to build, what to fake, and when to stop coding
|
||||
and start preparing the pitch.</code></pre>
|
||||
|
||||
<h3>Pitch Coaching</h3>
|
||||
|
||||
<pre><code class="language-text">Our demo is in 2 hours. Here's what we built: [description].
|
||||
Write a 3-minute pitch script that hits all the judging criteria
|
||||
and has a strong opening hook. Include what to show in the live demo
|
||||
and what to cover in slides.</code></pre>
|
||||
|
||||
<div class="info-box">
|
||||
<strong>Pro tip:</strong> Use the agent at three critical moments -- the start of the hackathon (ideation), the midpoint (scope check), and 3 hours before judging (pitch prep). These are the highest-leverage interventions.
|
||||
</div>
|
||||
|
||||
<h2>What Makes a Winning Hackathon Project</h2>
|
||||
|
||||
<p>Based on the strategist's evaluation framework, here are the patterns that consistently win:</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Winning Pattern</th>
|
||||
<th>Why It Works</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Solve a real problem you have experienced</td>
|
||||
<td>Authentic passion shows in the pitch</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Use AI in a non-obvious way</td>
|
||||
<td>Judges are tired of chatbot wrappers</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Build the demo first, infrastructure second</td>
|
||||
<td>Judges only see the demo</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Have one "wow moment" in the presentation</td>
|
||||
<td>Makes your project memorable</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Polish the visible 20%, skip the invisible 80%</td>
|
||||
<td>Perception of completeness matters more than actual completeness</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="warning-box">
|
||||
<strong>Common trap:</strong> Do not spend more than 2 hours on ideation. Analysis paralysis kills more hackathon projects than bad ideas do. Pick a direction, validate it with the strategist agent, and start building.
|
||||
</div>
|
||||
|
||||
<h2>Combining with Other Agents</h2>
|
||||
|
||||
<p>The Hackathon AI Strategist works best when paired with other Claude Code Templates agents for execution:</p>
|
||||
|
||||
<pre><code class="language-bash"># Strategy + execution stack
|
||||
npx claude-code-templates@latest --agent ai-specialists/hackathon-ai-strategist
|
||||
npx claude-code-templates@latest --agent frontend-developer
|
||||
npx claude-code-templates@latest --agent api-developer</code></pre>
|
||||
|
||||
<p>Use the strategist for planning and scoping, then hand off to specialized agents for building. Return to the strategist for scope checks and pitch preparation.</p>
|
||||
|
||||
<h2>Conclusion</h2>
|
||||
|
||||
<p>Hackathons reward teams that think strategically before they code. The Hackathon AI Strategist agent gives you an experienced mentor who evaluates your ideas through real judging criteria, helps you scope an achievable MVP, and coaches your pitch to land with maximum impact.</p>
|
||||
|
||||
<p>Install it before your next hackathon and use it at the three critical moments: ideation, midpoint scope check, and pitch prep. The difference between a good project and a winning project is almost always strategy, not code.</p>
|
||||
|
||||
<div class="success-box">
|
||||
<strong>Key takeaway:</strong> The best hackathon teams spend 20% of their time on strategy and 80% on focused execution. This agent makes that 20% dramatically more effective.
|
||||
</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">© 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() {
|
||||
const preElements = document.querySelectorAll('.article-content-full pre:not(.converted)');
|
||||
|
||||
preElements.forEach(pre => {
|
||||
const code = pre.querySelector('code');
|
||||
if (!code) return;
|
||||
|
||||
const language = this.detectLanguage(code);
|
||||
const isTerminal = language === 'bash' || language === 'terminal';
|
||||
|
||||
const codeBlock = document.createElement('div');
|
||||
codeBlock.className = isTerminal ? 'code-block terminal-block' : 'code-block';
|
||||
|
||||
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);
|
||||
|
||||
const newPre = pre.cloneNode(true);
|
||||
newPre.classList.add('converted');
|
||||
|
||||
codeBlock.appendChild(header);
|
||||
codeBlock.appendChild(newPre);
|
||||
|
||||
pre.parentNode.replaceChild(codeBlock, pre);
|
||||
});
|
||||
}
|
||||
|
||||
detectLanguage(codeElement) {
|
||||
const className = codeElement.className;
|
||||
if (className.includes('language-')) {
|
||||
return className.match(/language-(\w+)/)[1];
|
||||
}
|
||||
|
||||
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 {
|
||||
let textToCopy = code.textContent;
|
||||
|
||||
if (codeBlock.classList.contains('terminal-block')) {
|
||||
textToCopy = this.cleanTerminalOutput(textToCopy);
|
||||
}
|
||||
|
||||
await navigator.clipboard.writeText(textToCopy);
|
||||
|
||||
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');
|
||||
|
||||
setTimeout(() => {
|
||||
button.innerHTML = originalContent;
|
||||
button.classList.remove('copied');
|
||||
}, 2000);
|
||||
|
||||
} catch (err) {
|
||||
console.error('Failed to copy code:', err);
|
||||
|
||||
const selection = window.getSelection();
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(code);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cleanTerminalOutput(text) {
|
||||
return text
|
||||
.split('\n')
|
||||
.map(line => {
|
||||
line = line.replace(/^[\$❯]\s*/, '').replace(/^claude-code>\s*/, '');
|
||||
|
||||
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() !== '')
|
||||
.join('\n')
|
||||
.trim();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize when DOM is loaded
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => new CodeCopy());
|
||||
} else {
|
||||
new CodeCopy();
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
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>
|
||||
Copied!
|
||||
`;
|
||||
button.classList.add('copied');
|
||||
|
||||
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 content = document.querySelector('.article-content-full');
|
||||
|
||||
let markdown = `# ${title}\n\n`;
|
||||
|
||||
if (subtitle) {
|
||||
markdown += `${subtitle}\n\n`;
|
||||
}
|
||||
|
||||
if (!content) return markdown;
|
||||
|
||||
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'));
|
||||
}
|
||||
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 ``;
|
||||
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 = '';
|
||||
|
||||
const headerRow = rows[0];
|
||||
const headers = headerRow.querySelectorAll('th, td');
|
||||
const headerText = Array.from(headers).map(h => h.textContent.trim()).join(' | ');
|
||||
markdown += `| ${headerText} |\n`;
|
||||
|
||||
const separator = Array.from(headers).map(() => '---').join(' | ');
|
||||
markdown += `| ${separator} |\n`;
|
||||
|
||||
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;
|
||||
|
||||
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>
|
||||
@@ -0,0 +1,895 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>HeyGen Best Practices Skill for Claude Code: AI Avatar Video Creation Guide</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="Master HeyGen API integration with 18+ best practice rules. Learn to create AI avatar videos, manage voices, handle video generation workflows, and implement streaming avatars with Claude Code.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://aitmpl.com/blog/heygen-best-practices-skill/">
|
||||
<meta property="og:title" content="HeyGen Best Practices Skill: AI Avatar Video Creation Guide">
|
||||
<meta property="og:description" content="Master HeyGen API integration with 18+ best practice rules. Learn to create AI avatar videos, manage voices, and handle video generation workflows.">
|
||||
<meta property="og:image" content="https://aitmpl.com/blog/assets/heygen-best-practices-skill-cover.svg">
|
||||
<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="Skills">
|
||||
<meta property="article:tag" content="HeyGen">
|
||||
<meta property="article:tag" content="AI Video">
|
||||
<meta property="article:tag" content="Avatar">
|
||||
<meta property="article:tag" content="Text-to-Video">
|
||||
<meta property="article:tag" content="API Integration">
|
||||
<meta property="article:tag" content="Video Generation">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/blog/heygen-best-practices-skill/">
|
||||
<meta name="twitter:title" content="HeyGen Best Practices Skill: AI Avatar Video Creation Guide">
|
||||
<meta name="twitter:description" content="Master HeyGen API integration with 18+ best practice rules. Learn to create AI avatar videos, manage voices, and handle video generation workflows.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/blog/assets/heygen-best-practices-skill-cover.svg">
|
||||
|
||||
<!-- Additional SEO -->
|
||||
<meta name="keywords" content="HeyGen API, AI avatar, video generation, text-to-video, avatar video, Claude Code, HeyGen skill, video automation, AI presenter, talking head video, streaming avatar, video translation, webhook integration">
|
||||
<meta name="author" content="Claude Code Templates">
|
||||
<link rel="canonical" href="https://aitmpl.com/blog/heygen-best-practices-skill/">
|
||||
|
||||
<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": "HeyGen Best Practices Skill for Claude Code: AI Avatar Video Creation Guide",
|
||||
"description": "Master HeyGen API integration with 18+ best practice rules. Learn to create AI avatar videos, manage voices, and handle video generation workflows.",
|
||||
"image": "https://aitmpl.com/blog/assets/heygen-best-practices-skill-cover.svg",
|
||||
"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/heygen-best-practices-skill/"
|
||||
},
|
||||
"keywords": "HeyGen API, AI avatar, video generation, text-to-video, Claude Code, avatar video, streaming avatar",
|
||||
"wordCount": "1800",
|
||||
"articleSection": "Claude Code Skills",
|
||||
"about": [
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "HeyGen API Integration"
|
||||
},
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "AI Video Generation"
|
||||
},
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Avatar Technology"
|
||||
},
|
||||
{
|
||||
"@type": "SoftwareApplication",
|
||||
"name": "Claude Code",
|
||||
"applicationCategory": "DeveloperApplication"
|
||||
}
|
||||
]
|
||||
}
|
||||
</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">HeyGen Best Practices Skill: AI Avatar Video Creation Guide</h1>
|
||||
<p class="article-subtitle">Master HeyGen API integration with 18+ comprehensive rules for creating AI avatar videos, managing voices, handling video generation workflows, and implementing advanced features like streaming avatars and video translation.</p>
|
||||
<div class="article-meta-full">
|
||||
<span class="read-time">10 min read</span>
|
||||
<div class="article-tags">
|
||||
<span class="tag">Claude Code</span>
|
||||
<span class="tag">Skill</span>
|
||||
<span class="tag">HeyGen</span>
|
||||
<span class="tag">AI Video</span>
|
||||
<span class="tag">Avatar</span>
|
||||
<span class="tag">API</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<article class="article-body">
|
||||
<img src="../assets/heygen-best-practices-skill-cover.svg" alt="HeyGen Best Practices Skill for Claude Code" class="article-cover" loading="lazy">
|
||||
|
||||
<div class="article-content-full">
|
||||
<h2>What is the HeyGen Best Practices Skill?</h2>
|
||||
|
||||
<p>The HeyGen Best Practices skill is a comprehensive guide for integrating with HeyGen's AI avatar video creation API. It provides Claude Code with 18+ actionable rules organized by functionality, helping you create professional AI presenter videos, manage avatars and voices, handle video generation workflows, and implement advanced features.</p>
|
||||
|
||||
<p>This skill was created by the HeyGen community and includes real-world examples, TypeScript/Python code samples, and best practices for every aspect of the HeyGen API.</p>
|
||||
|
||||
<!-- Mermaid Diagram -->
|
||||
<div class="mermaid-diagram" style="background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 2rem; margin: 2rem 0; text-align: center;">
|
||||
<pre class="mermaid">
|
||||
graph TD
|
||||
A[Video Request] --> B[Claude + HeyGen Skill]
|
||||
B --> C[Select Avatar]
|
||||
B --> D[Choose Voice]
|
||||
B --> E[Write Script]
|
||||
C --> F[POST /v2/video/generate]
|
||||
D --> F
|
||||
E --> F
|
||||
F --> G[Poll Status]
|
||||
G --> H{Completed?}
|
||||
H -->|Yes| I[Download Video URL]
|
||||
H -->|No| G
|
||||
|
||||
style B fill:#F97316,stroke:#fff,color:#000
|
||||
style I fill:#00ff41,stroke:#fff,color:#000
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<h2>Why You Need This Skill</h2>
|
||||
|
||||
<p>Creating AI avatar videos programmatically requires understanding multiple API endpoints, matching avatars with voices, handling asynchronous video generation, and implementing proper error handling. This skill simplifies everything by:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>Organizing knowledge by function</strong> - Rules are grouped into Foundation, Core Video Creation, Customization, and Advanced Features</li>
|
||||
<li><strong>Providing production-ready code</strong> - Each rule includes TypeScript and Python examples you can use directly</li>
|
||||
<li><strong>Teaching best practices</strong> - Learn to use avatar's default voice, implement proper polling, and handle rate limits</li>
|
||||
<li><strong>Progressive context loading</strong> - The skill loads detailed guidelines on-demand to minimize token usage</li>
|
||||
</ul>
|
||||
|
||||
<h2>Key Feature Categories</h2>
|
||||
|
||||
<h3>1. Foundation (Required)</h3>
|
||||
<p>Essential knowledge for any HeyGen integration:</p>
|
||||
<ul>
|
||||
<li><strong>Authentication</strong> - API key setup, X-Api-Key header patterns</li>
|
||||
<li><strong>Quota Management</strong> - Credit system, usage limits, checking remaining quota</li>
|
||||
<li><strong>Video Status</strong> - Polling patterns, status types, retrieving download URLs</li>
|
||||
<li><strong>Assets</strong> - Uploading images, videos, and audio for use in videos</li>
|
||||
</ul>
|
||||
|
||||
<pre><code class="language-typescript">// Authenticated request example
|
||||
const response = await fetch("https://api.heygen.com/v2/avatars", {
|
||||
headers: {
|
||||
"X-Api-Key": process.env.HEYGEN_API_KEY!,
|
||||
},
|
||||
});
|
||||
const { data } = await response.json();</code></pre>
|
||||
|
||||
<h3>2. Core Video Creation</h3>
|
||||
<p>The main workflow for generating AI avatar videos:</p>
|
||||
<ul>
|
||||
<li><strong>Avatars</strong> - Listing avatars, styles (normal, closeUp, circle), avatar_id selection</li>
|
||||
<li><strong>Voices</strong> - Listing voices, locales, speed/pitch configuration</li>
|
||||
<li><strong>Scripts</strong> - Writing scripts, pauses/breaks, pacing, structure templates</li>
|
||||
<li><strong>Video Generation</strong> - POST /v2/video/generate workflow, multi-scene videos</li>
|
||||
<li><strong>Dimensions</strong> - Resolution options (720p/1080p) and aspect ratios</li>
|
||||
</ul>
|
||||
|
||||
<pre><code class="language-typescript">// Basic video generation
|
||||
const videoId = await generateVideo({
|
||||
video_inputs: [{
|
||||
character: {
|
||||
type: "avatar",
|
||||
avatar_id: "josh_lite3_20230714",
|
||||
avatar_style: "normal",
|
||||
},
|
||||
voice: {
|
||||
type: "text",
|
||||
input_text: "Hello! Welcome to our product demo.",
|
||||
voice_id: avatar.default_voice_id, // Use avatar's default voice
|
||||
speed: 1.0,
|
||||
},
|
||||
background: {
|
||||
type: "color",
|
||||
value: "#1a1a2e",
|
||||
},
|
||||
}],
|
||||
dimension: { width: 1920, height: 1080 },
|
||||
});</code></pre>
|
||||
|
||||
<h3>3. Video Customization</h3>
|
||||
<p>Make your videos visually appealing:</p>
|
||||
<ul>
|
||||
<li><strong>Backgrounds</strong> - Solid colors, images, and video backgrounds</li>
|
||||
<li><strong>Text Overlays</strong> - Adding text with fonts and positioning</li>
|
||||
<li><strong>Captions</strong> - Auto-generated captions and subtitle options</li>
|
||||
</ul>
|
||||
|
||||
<h3>4. Advanced Features</h3>
|
||||
<p>Take your integration to the next level:</p>
|
||||
<ul>
|
||||
<li><strong>Templates</strong> - Template listing and variable replacement</li>
|
||||
<li><strong>Video Translation</strong> - Translating videos, quality/fast modes, dubbing</li>
|
||||
<li><strong>Streaming Avatars</strong> - Real-time interactive avatar sessions</li>
|
||||
<li><strong>Photo Avatars</strong> - Creating avatars from photos (talking photos)</li>
|
||||
<li><strong>Webhooks</strong> - Registering webhook endpoints for async notifications</li>
|
||||
<li><strong>Remotion Integration</strong> - Using HeyGen videos in Remotion compositions</li>
|
||||
</ul>
|
||||
|
||||
<h2>Installation</h2>
|
||||
|
||||
<p>Install the HeyGen Best Practices skill using the Claude Code Templates CLI:</p>
|
||||
|
||||
<pre><code class="language-bash">npx claude-code-templates@latest --skill=development/heygen-best-practices --yes</code></pre>
|
||||
|
||||
<p><strong>Where is the skill installed?</strong></p>
|
||||
<p>The skill is saved in <code>.claude/skills/heygen-best-practices/</code> in your project directory:</p>
|
||||
|
||||
<pre><code class="language-bash">your-project/
|
||||
├── .claude/
|
||||
│ └── skills/
|
||||
│ └── heygen-best-practices/ # Skill installed here
|
||||
│ ├── SKILL.md
|
||||
│ └── rules/
|
||||
│ ├── authentication.md
|
||||
│ ├── avatars.md
|
||||
│ ├── voices.md
|
||||
│ ├── video-generation.md
|
||||
│ ├── video-status.md
|
||||
│ └── ... (18+ rule files)
|
||||
├── src/
|
||||
├── package.json
|
||||
└── README.md</code></pre>
|
||||
|
||||
<h2>How to Use the Skill</h2>
|
||||
|
||||
<p>The skill uses progressive context loading - it provides a quick reference first, then loads detailed guidelines only when needed. This approach minimizes token usage while giving you access to comprehensive information.</p>
|
||||
|
||||
<h3>Basic Usage</h3>
|
||||
<pre><code class="language-bash"># Start Claude Code
|
||||
claude
|
||||
|
||||
# Request HeyGen help
|
||||
> Use the heygen-best-practices skill to create a video with an AI avatar presenting our product</code></pre>
|
||||
|
||||
<p>Claude will:</p>
|
||||
<ol>
|
||||
<li>Load the skill's quick reference</li>
|
||||
<li>Identify the relevant rules (avatars, voices, video-generation)</li>
|
||||
<li>Load detailed guidelines for those specific areas</li>
|
||||
<li>Generate production-ready code with proper error handling</li>
|
||||
</ol>
|
||||
|
||||
<h2>Usage Examples</h2>
|
||||
|
||||
<h3>Example 1: Create a Simple Avatar Video</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Use the heygen-best-practices skill to create a 30-second product demo video with a professional male avatar</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> Claude loads the avatars, voices, and video-generation rules, then generates code that lists available avatars, selects a professional one, uses its default voice, and creates the video with proper status polling.</p>
|
||||
|
||||
<h3>Example 2: Multi-Scene Video with Different Backgrounds</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Use the heygen-best-practices skill to create a video with 3 scenes: intro, features, and conclusion. Each scene should have a different background color.</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> Claude generates multi-scene video configuration with proper scene transitions and background customization.</p>
|
||||
|
||||
<h3>Example 3: Implement Webhook Notifications</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Use the heygen-best-practices skill to set up webhooks so I get notified when my video is ready</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> Claude loads the webhooks rule and provides code for registering webhook endpoints and handling completion events.</p>
|
||||
|
||||
<h2>Best Practices Highlights</h2>
|
||||
|
||||
<h3>Always Use Avatar's Default Voice</h3>
|
||||
<p>Most avatars have a pre-matched <code>default_voice_id</code> that provides the best lip-sync quality:</p>
|
||||
|
||||
<pre><code class="language-typescript">// Get avatar details to find default voice
|
||||
const details = await getAvatarDetails(avatarId);
|
||||
|
||||
// Use the default voice for best results
|
||||
const voiceId = details.default_voice_id;</code></pre>
|
||||
|
||||
<h3>Implement Proper Status Polling</h3>
|
||||
<p>Video generation is asynchronous and can take 5-15 minutes:</p>
|
||||
|
||||
<pre><code class="language-typescript">async function waitForVideo(videoId: string): Promise<string> {
|
||||
const maxAttempts = 60;
|
||||
const pollInterval = 10000; // 10 seconds
|
||||
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
const response = await fetch(
|
||||
`https://api.heygen.com/v1/video_status.get?video_id=${videoId}`,
|
||||
{ headers: { "X-Api-Key": process.env.HEYGEN_API_KEY! } }
|
||||
);
|
||||
|
||||
const { data } = await response.json();
|
||||
|
||||
if (data.status === "completed") {
|
||||
return data.video_url;
|
||||
} else if (data.status === "failed") {
|
||||
throw new Error(data.error || "Video generation failed");
|
||||
}
|
||||
|
||||
await new Promise(r => setTimeout(r, pollInterval));
|
||||
}
|
||||
|
||||
throw new Error("Video generation timed out");
|
||||
}</code></pre>
|
||||
|
||||
<h3>Add Natural Pauses to Scripts</h3>
|
||||
<p>Use SSML-style break tags for more natural speech:</p>
|
||||
|
||||
<pre><code class="language-typescript">const script = `
|
||||
Welcome to our demo. <break time="1s"/>
|
||||
Today I'll show you three key features. <break time="0.5s"/>
|
||||
First, let's look at the dashboard.
|
||||
`;</code></pre>
|
||||
|
||||
<h2>Common Pitfalls Avoided</h2>
|
||||
|
||||
<p>The skill helps you avoid these common HeyGen integration mistakes:</p>
|
||||
|
||||
<ul>
|
||||
<li>Mismatching avatar and voice genders</li>
|
||||
<li>Not polling for video completion (expecting synchronous generation)</li>
|
||||
<li>Hardcoding API keys instead of using environment variables</li>
|
||||
<li>Not handling rate limits with exponential backoff</li>
|
||||
<li>Using the wrong endpoint for transparent background videos</li>
|
||||
<li>Setting insufficient timeouts for video generation</li>
|
||||
</ul>
|
||||
|
||||
<h2>Skill Structure</h2>
|
||||
|
||||
<p>The skill is organized into logical categories with 18+ rule files:</p>
|
||||
|
||||
<table class="components-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Category</th>
|
||||
<th>Rules</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>Foundation</strong></td>
|
||||
<td>authentication, quota, video-status, assets</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Core Video</strong></td>
|
||||
<td>avatars, voices, scripts, video-generation, video-agent, dimensions</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Customization</strong></td>
|
||||
<td>backgrounds, text-overlays, captions</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Advanced</strong></td>
|
||||
<td>templates, video-translation, streaming-avatars, photo-avatars, webhooks, remotion-integration</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Official Documentation</h2>
|
||||
<p>For more information about skills in Claude Code, see the <a href="https://code.claude.com/docs/en/skills?utm_source=aitmpl&utm_medium=referral&utm_campaign=blog" target="_blank">official skills documentation</a>.</p>
|
||||
|
||||
<p>For HeyGen API documentation, visit <a href="https://docs.heygen.com" target="_blank">docs.heygen.com</a>.</p>
|
||||
|
||||
<!-- Explore Components Banner -->
|
||||
<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">© 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.30.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>
|
||||
|
||||
<!-- Mermaid for diagrams -->
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
theme: 'dark',
|
||||
themeVariables: {
|
||||
primaryColor: '#F97316',
|
||||
primaryTextColor: '#fff',
|
||||
primaryBorderColor: '#F97316',
|
||||
lineColor: '#00ff41',
|
||||
secondaryColor: '#1a1a1a',
|
||||
tertiaryColor: '#2d2d2d'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- 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();
|
||||
}
|
||||
|
||||
// Explore components banner hover effect
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const banner = document.querySelector('.explore-components-banner a');
|
||||
if (banner) {
|
||||
banner.addEventListener('mouseenter', () => {
|
||||
banner.style.background = '#00cc33';
|
||||
banner.style.transform = 'translateY(-2px)';
|
||||
});
|
||||
banner.addEventListener('mouseleave', () => {
|
||||
banner.style.background = '#00ff41';
|
||||
banner.style.transform = 'translateY(0)';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 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>
|
||||
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 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 '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')) {
|
||||
return '';
|
||||
}
|
||||
return element.textContent || '';
|
||||
case 'img':
|
||||
const alt = element.getAttribute('alt') || '';
|
||||
const src = element.getAttribute('src') || '';
|
||||
return ``;
|
||||
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>
|
||||
@@ -0,0 +1,240 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Blog - Claude Code Templates</title>
|
||||
<meta name="description" content="Latest articles about Claude Code, AI development, and automation tools. Learn how to supercharge your development workflow with Anthropic's Claude Code.">
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://aitmpl.com/blog/">
|
||||
<meta property="og:title" content="Blog - Claude Code Templates">
|
||||
<meta property="og:description" content="Latest articles about Claude Code, AI development, and automation tools. Learn how to supercharge your development workflow with Anthropic's Claude Code.">
|
||||
<meta property="og:image" content="https://aitmpl.com/blog/assets/supabase-claude-code-templates-cover.png">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/blog/">
|
||||
<meta name="twitter:title" content="Blog - Claude Code Templates">
|
||||
<meta name="twitter:description" content="Latest articles about Claude Code, AI development, and automation tools.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/blog/assets/supabase-claude-code-templates-cover.png">
|
||||
|
||||
<link rel="canonical" href="https://aitmpl.com/blog/">
|
||||
|
||||
<!-- 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">
|
||||
|
||||
<link rel="stylesheet" href="../css/styles.css">
|
||||
<link rel="stylesheet" href="../css/blog.css">
|
||||
<link rel="stylesheet" href="css/blog-controls.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>
|
||||
</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 class="terminal-subtitle">
|
||||
<span class="status-dot"></span>
|
||||
Latest articles about <strong>Claude Code</strong> and AI development
|
||||
</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="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">
|
||||
<section class="blog-hero">
|
||||
<div class="container">
|
||||
<div class="terminal-command">
|
||||
<div class="header-content">
|
||||
<h1 class="search-title">
|
||||
<span class="terminal-dot"></span>
|
||||
<strong>Blog</strong>
|
||||
<span class="title-params">(articles/tutorials/guides)</span>
|
||||
</h1>
|
||||
<p class="search-subtitle">⎿ Learn how to maximize your AI-powered development with Claude Code</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="blog-articles">
|
||||
<div class="container">
|
||||
<!-- Featured Posts Carousel -->
|
||||
<div class="featured-posts-section" id="featured-posts-section" style="display: none;">
|
||||
<div class="featured-header">
|
||||
<h2 class="featured-title">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z"/>
|
||||
</svg>
|
||||
Featured Posts
|
||||
</h2>
|
||||
</div>
|
||||
<div class="featured-carousel-wrapper">
|
||||
<button class="carousel-nav carousel-prev" id="carousel-prev" aria-label="Previous">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M15.41,16.58L10.83,12L15.41,7.41L14,6L8,12L14,18L15.41,16.58Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="featured-carousel" id="featured-carousel">
|
||||
<!-- Featured posts will be loaded here dynamically -->
|
||||
</div>
|
||||
<button class="carousel-nav carousel-next" id="carousel-next" aria-label="Next">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search and Filter Controls -->
|
||||
<div class="blog-controls">
|
||||
<div class="search-container">
|
||||
<svg class="search-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<path d="m21 21-4.35-4.35"></path>
|
||||
</svg>
|
||||
<input
|
||||
type="text"
|
||||
id="blog-search"
|
||||
class="blog-search-input"
|
||||
placeholder="Search articles by title, description, tags..."
|
||||
autocomplete="off"
|
||||
>
|
||||
<button id="clear-search" class="clear-search-btn" style="display: none;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="sort-container">
|
||||
<label for="sort-by" class="sort-label">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M3,13H15V11H3M3,6V8H21V6M3,18H9V16H3V18Z"/>
|
||||
</svg>
|
||||
Sort by:
|
||||
</label>
|
||||
<select id="sort-by" class="sort-select">
|
||||
<option value="default">Default Order</option>
|
||||
<option value="difficulty-asc">Difficulty: Basic → Advanced</option>
|
||||
<option value="difficulty-desc">Difficulty: Advanced → Basic</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Results Info -->
|
||||
<div class="results-info" id="results-info" style="display: none;">
|
||||
<span id="results-count"></span>
|
||||
<button id="clear-filters" class="clear-filters-btn" style="display: none;">Clear filters</button>
|
||||
</div>
|
||||
|
||||
<!-- Articles will be loaded dynamically from blog-articles.json -->
|
||||
<div class="articles-grid" style="grid-template-columns: 1fr;"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</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">© 2025 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>
|
||||
|
||||
<!-- Blog Articles Dynamic Loader -->
|
||||
<script src="js/blog-loader.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,569 @@
|
||||
/**
|
||||
* Blog Articles Dynamic Loader
|
||||
* Loads and renders blog articles from blog-articles.json
|
||||
*/
|
||||
|
||||
class BlogLoader {
|
||||
constructor() {
|
||||
this.articles = [];
|
||||
this.filteredArticles = [];
|
||||
this.featuredArticles = [];
|
||||
this.articlesContainer = null;
|
||||
this.featuredCarousel = null;
|
||||
this.searchInput = null;
|
||||
this.sortSelect = null;
|
||||
this.currentSearchTerm = '';
|
||||
this.currentSortBy = 'date-desc';
|
||||
this.carouselScrollPosition = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the blog loader
|
||||
*/
|
||||
async init() {
|
||||
try {
|
||||
this.articlesContainer = document.querySelector('.articles-grid');
|
||||
this.featuredCarousel = document.getElementById('featured-carousel');
|
||||
this.searchInput = document.getElementById('blog-search');
|
||||
this.sortSelect = document.getElementById('sort-by');
|
||||
|
||||
if (!this.articlesContainer) {
|
||||
console.error('Articles container not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Show loading state
|
||||
this.showLoading();
|
||||
|
||||
// Load articles from JSON
|
||||
await this.loadArticles();
|
||||
|
||||
// Render featured posts
|
||||
this.renderFeaturedPosts();
|
||||
|
||||
// Setup event listeners
|
||||
this.setupEventListeners();
|
||||
|
||||
// Setup carousel navigation
|
||||
this.setupCarouselNavigation();
|
||||
|
||||
// Render articles
|
||||
this.renderArticles();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error initializing blog loader:', error);
|
||||
this.showError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup event listeners for search and sort
|
||||
*/
|
||||
setupEventListeners() {
|
||||
// Search input
|
||||
if (this.searchInput) {
|
||||
this.searchInput.addEventListener('input', (e) => {
|
||||
this.currentSearchTerm = e.target.value.trim().toLowerCase();
|
||||
this.applyFiltersAndSort();
|
||||
this.updateClearButton();
|
||||
});
|
||||
}
|
||||
|
||||
// Clear search button
|
||||
const clearSearchBtn = document.getElementById('clear-search');
|
||||
if (clearSearchBtn) {
|
||||
clearSearchBtn.addEventListener('click', () => {
|
||||
this.searchInput.value = '';
|
||||
this.currentSearchTerm = '';
|
||||
this.applyFiltersAndSort();
|
||||
this.updateClearButton();
|
||||
this.searchInput.focus();
|
||||
});
|
||||
}
|
||||
|
||||
// Sort select
|
||||
if (this.sortSelect) {
|
||||
this.sortSelect.addEventListener('change', (e) => {
|
||||
this.currentSortBy = e.target.value;
|
||||
this.applyFiltersAndSort();
|
||||
});
|
||||
}
|
||||
|
||||
// Clear filters button
|
||||
const clearFiltersBtn = document.getElementById('clear-filters');
|
||||
if (clearFiltersBtn) {
|
||||
clearFiltersBtn.addEventListener('click', () => {
|
||||
this.searchInput.value = '';
|
||||
this.currentSearchTerm = '';
|
||||
this.sortSelect.value = 'date-desc';
|
||||
this.currentSortBy = 'date-desc';
|
||||
this.applyFiltersAndSort();
|
||||
this.updateClearButton();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update clear button visibility
|
||||
*/
|
||||
updateClearButton() {
|
||||
const clearSearchBtn = document.getElementById('clear-search');
|
||||
if (clearSearchBtn) {
|
||||
clearSearchBtn.style.display = this.currentSearchTerm ? 'flex' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply filters and sorting
|
||||
*/
|
||||
applyFiltersAndSort() {
|
||||
// Start with all articles
|
||||
this.filteredArticles = [...this.articles];
|
||||
|
||||
// Apply search filter
|
||||
if (this.currentSearchTerm) {
|
||||
this.filteredArticles = this.filteredArticles.filter(article => {
|
||||
const searchableText = [
|
||||
article.title,
|
||||
article.description,
|
||||
article.category,
|
||||
...article.tags
|
||||
].join(' ').toLowerCase();
|
||||
|
||||
return searchableText.includes(this.currentSearchTerm);
|
||||
});
|
||||
}
|
||||
|
||||
// Apply sorting
|
||||
this.sortArticles(this.currentSortBy);
|
||||
|
||||
// Update results info
|
||||
this.updateResultsInfo();
|
||||
|
||||
// Re-render articles
|
||||
this.renderArticles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort articles based on selected option
|
||||
*/
|
||||
sortArticles(sortBy) {
|
||||
const difficultyOrder = { basic: 1, intermediate: 2, advanced: 3 };
|
||||
|
||||
switch (sortBy) {
|
||||
case 'difficulty-asc':
|
||||
this.filteredArticles.sort((a, b) =>
|
||||
difficultyOrder[a.difficulty] - difficultyOrder[b.difficulty]
|
||||
);
|
||||
break;
|
||||
case 'difficulty-desc':
|
||||
this.filteredArticles.sort((a, b) =>
|
||||
difficultyOrder[b.difficulty] - difficultyOrder[a.difficulty]
|
||||
);
|
||||
break;
|
||||
default:
|
||||
// Keep original order from JSON (by order field, newest first)
|
||||
this.filteredArticles.sort((a, b) => (b.order || 0) - (a.order || 0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update results info display
|
||||
*/
|
||||
updateResultsInfo() {
|
||||
const resultsInfo = document.getElementById('results-info');
|
||||
const resultsCount = document.getElementById('results-count');
|
||||
const clearFiltersBtn = document.getElementById('clear-filters');
|
||||
|
||||
if (!resultsInfo || !resultsCount) return;
|
||||
|
||||
const isFiltered = this.currentSearchTerm || this.currentSortBy !== 'date-desc';
|
||||
const total = this.articles.length;
|
||||
const showing = this.filteredArticles.length;
|
||||
|
||||
if (this.currentSearchTerm && showing === 0) {
|
||||
resultsInfo.style.display = 'flex';
|
||||
resultsCount.textContent = `No articles found for "${this.currentSearchTerm}"`;
|
||||
clearFiltersBtn.style.display = 'inline-flex';
|
||||
} else if (this.currentSearchTerm) {
|
||||
resultsInfo.style.display = 'flex';
|
||||
resultsCount.textContent = `Showing ${showing} of ${total} articles`;
|
||||
clearFiltersBtn.style.display = 'inline-flex';
|
||||
} else {
|
||||
resultsInfo.style.display = 'none';
|
||||
clearFiltersBtn.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load articles from JSON file
|
||||
*/
|
||||
async loadArticles() {
|
||||
try {
|
||||
const response = await fetch('blog-articles.json');
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Sort articles by order field descending (newest first)
|
||||
this.articles = data.articles.sort((a, b) => b.order - a.order);
|
||||
this.filteredArticles = [...this.articles];
|
||||
|
||||
// Extract featured articles
|
||||
this.featuredArticles = this.articles.filter(article => article.featured === true);
|
||||
|
||||
console.log(`Loaded ${this.articles.length} articles (${this.featuredArticles.length} featured)`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading articles:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render all articles to the DOM
|
||||
*/
|
||||
renderArticles() {
|
||||
if (!this.filteredArticles || this.filteredArticles.length === 0) {
|
||||
if (this.currentSearchTerm) {
|
||||
this.showNoResults();
|
||||
} else {
|
||||
this.showEmpty();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear container
|
||||
this.articlesContainer.innerHTML = '';
|
||||
|
||||
// Render each filtered article
|
||||
this.filteredArticles.forEach(article => {
|
||||
const articleCard = this.createArticleCard(article);
|
||||
this.articlesContainer.appendChild(articleCard);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show no results message
|
||||
*/
|
||||
showNoResults() {
|
||||
this.articlesContainer.innerHTML = `
|
||||
<div style="text-align: center; padding: 4rem; color: #666; grid-column: 1 / -1;">
|
||||
<svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="margin: 0 auto 1rem;">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<path d="m21 21-4.35-4.35"></path>
|
||||
<line x1="11" y1="8" x2="11" y2="14"></line>
|
||||
<line x1="11" y1="16" x2="11.01" y2="16"></line>
|
||||
</svg>
|
||||
<h3 style="margin-bottom: 0.5rem; color: #00D084;">No articles found</h3>
|
||||
<p>Try adjusting your search terms or filters</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create article card HTML element
|
||||
* @param {Object} article - Article data
|
||||
* @returns {HTMLElement} Article card element
|
||||
*/
|
||||
createArticleCard(article) {
|
||||
const articleElement = document.createElement('article');
|
||||
articleElement.className = 'article-card';
|
||||
|
||||
// Determine if it's external or local
|
||||
const isExternal = article.url.startsWith('http');
|
||||
const linkAttrs = isExternal
|
||||
? 'target="_blank" rel="noopener noreferrer"'
|
||||
: '';
|
||||
|
||||
// Create difficulty badge
|
||||
const difficultyBadge = this.getDifficultyBadge(article.difficulty);
|
||||
|
||||
// Generate tags HTML
|
||||
const tagsHTML = article.tags
|
||||
.map(tag => `<span class="tag">${this.escapeHtml(tag)}</span>`)
|
||||
.join('');
|
||||
|
||||
articleElement.innerHTML = `
|
||||
<a href="${this.escapeHtml(article.url)}" ${linkAttrs} class="article-link">
|
||||
<div class="article-image">
|
||||
<img src="${this.escapeHtml(article.image)}"
|
||||
alt="${this.escapeHtml(article.title)}"
|
||||
loading="lazy">
|
||||
<div class="article-category">${this.escapeHtml(article.category)}</div>
|
||||
</div>
|
||||
<div class="article-content">
|
||||
<div class="article-meta">
|
||||
<span class="read-time">${this.escapeHtml(article.readTime)}</span>
|
||||
${difficultyBadge}
|
||||
</div>
|
||||
<h2>${this.escapeHtml(article.title)}</h2>
|
||||
<p>${this.escapeHtml(article.description)}</p>
|
||||
<div class="article-tags">
|
||||
${tagsHTML}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
`;
|
||||
|
||||
return articleElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get difficulty badge HTML
|
||||
* @param {string} difficulty - Difficulty level (basic, intermediate, advanced)
|
||||
* @returns {string} Badge HTML
|
||||
*/
|
||||
getDifficultyBadge(difficulty) {
|
||||
const difficultyConfig = {
|
||||
basic: {
|
||||
label: 'Basic',
|
||||
color: '#00D084',
|
||||
textColor: '#000'
|
||||
},
|
||||
intermediate: {
|
||||
label: 'Intermediate',
|
||||
color: '#FFA500',
|
||||
textColor: '#000'
|
||||
},
|
||||
advanced: {
|
||||
label: 'Advanced',
|
||||
color: '#FF4444',
|
||||
textColor: '#FFF'
|
||||
}
|
||||
};
|
||||
|
||||
const config = difficultyConfig[difficulty] || difficultyConfig.basic;
|
||||
|
||||
return `<span class="tag difficulty-badge" style="margin-left: 8px; background: ${config.color}; color: ${config.textColor};">${config.label}</span>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show loading state
|
||||
*/
|
||||
showLoading() {
|
||||
this.articlesContainer.innerHTML = `
|
||||
<div style="text-align: center; padding: 4rem; color: #00D084;">
|
||||
<div class="loading-spinner"></div>
|
||||
<p style="margin-top: 1rem;">Loading articles...</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error state
|
||||
*/
|
||||
showError() {
|
||||
this.articlesContainer.innerHTML = `
|
||||
<div style="text-align: center; padding: 4rem; color: #ff4444;">
|
||||
<p>⚠️ Error loading articles. Please try again later.</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show empty state
|
||||
*/
|
||||
showEmpty() {
|
||||
this.articlesContainer.innerHTML = `
|
||||
<div style="text-align: center; padding: 4rem; color: #666;">
|
||||
<p>No articles available at the moment.</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape HTML to prevent XSS
|
||||
* @param {string} text - Text to escape
|
||||
* @returns {string} Escaped text
|
||||
*/
|
||||
escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render featured posts carousel
|
||||
*/
|
||||
renderFeaturedPosts() {
|
||||
if (!this.featuredCarousel || !this.featuredArticles || this.featuredArticles.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show featured section
|
||||
const featuredSection = document.getElementById('featured-posts-section');
|
||||
if (featuredSection) {
|
||||
featuredSection.style.display = 'block';
|
||||
}
|
||||
|
||||
// Clear carousel
|
||||
this.featuredCarousel.innerHTML = '';
|
||||
|
||||
// Render each featured article
|
||||
this.featuredArticles.forEach(article => {
|
||||
const featuredCard = this.createFeaturedCard(article);
|
||||
this.featuredCarousel.appendChild(featuredCard);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create featured article card
|
||||
* @param {Object} article - Article data
|
||||
* @returns {HTMLElement} Featured card element
|
||||
*/
|
||||
createFeaturedCard(article) {
|
||||
const cardElement = document.createElement('article');
|
||||
cardElement.className = 'featured-card';
|
||||
|
||||
const isExternal = article.url.startsWith('http');
|
||||
const linkAttrs = isExternal ? 'target="_blank" rel="noopener noreferrer"' : '';
|
||||
|
||||
const tagsHTML = article.tags
|
||||
.slice(0, 3)
|
||||
.map(tag => `<span class="tag">${this.escapeHtml(tag)}</span>`)
|
||||
.join('');
|
||||
|
||||
cardElement.innerHTML = `
|
||||
<div class="featured-star">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<a href="${this.escapeHtml(article.url)}" ${linkAttrs} class="featured-link">
|
||||
<div class="featured-image">
|
||||
<img src="${this.escapeHtml(article.image)}"
|
||||
alt="${this.escapeHtml(article.title)}"
|
||||
loading="lazy">
|
||||
</div>
|
||||
<div class="featured-content">
|
||||
<div class="featured-meta">
|
||||
<span class="read-time">${this.escapeHtml(article.readTime)}</span>
|
||||
</div>
|
||||
<h3>${this.escapeHtml(article.title)}</h3>
|
||||
<p>${this.escapeHtml(article.description)}</p>
|
||||
<div class="featured-tags">
|
||||
${tagsHTML}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
`;
|
||||
|
||||
return cardElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup carousel navigation
|
||||
*/
|
||||
setupCarouselNavigation() {
|
||||
const prevBtn = document.getElementById('carousel-prev');
|
||||
const nextBtn = document.getElementById('carousel-next');
|
||||
|
||||
if (!prevBtn || !nextBtn || !this.featuredCarousel) return;
|
||||
|
||||
// Check if navigation is needed
|
||||
this.checkCarouselNavigation(prevBtn, nextBtn);
|
||||
|
||||
// Scroll carousel on button click
|
||||
prevBtn.addEventListener('click', () => {
|
||||
this.featuredCarousel.scrollBy({
|
||||
left: -280,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
});
|
||||
|
||||
nextBtn.addEventListener('click', () => {
|
||||
this.featuredCarousel.scrollBy({
|
||||
left: 280,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
});
|
||||
|
||||
// Update button states on scroll
|
||||
this.featuredCarousel.addEventListener('scroll', () => {
|
||||
this.updateCarouselButtons(prevBtn, nextBtn);
|
||||
});
|
||||
|
||||
// Re-check on window resize
|
||||
window.addEventListener('resize', () => {
|
||||
this.checkCarouselNavigation(prevBtn, nextBtn);
|
||||
});
|
||||
|
||||
// Initial button state
|
||||
this.updateCarouselButtons(prevBtn, nextBtn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if carousel navigation buttons should be visible
|
||||
*/
|
||||
checkCarouselNavigation(prevBtn, nextBtn) {
|
||||
if (!this.featuredCarousel) return;
|
||||
|
||||
// Wait for images to load before checking
|
||||
setTimeout(() => {
|
||||
const isOverflowing = this.featuredCarousel.scrollWidth > this.featuredCarousel.clientWidth;
|
||||
|
||||
if (isOverflowing) {
|
||||
prevBtn.style.display = 'flex';
|
||||
nextBtn.style.display = 'flex';
|
||||
} else {
|
||||
prevBtn.style.display = 'none';
|
||||
nextBtn.style.display = 'none';
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update carousel button states based on scroll position
|
||||
*/
|
||||
updateCarouselButtons(prevBtn, nextBtn) {
|
||||
if (!this.featuredCarousel) return;
|
||||
|
||||
const scrollLeft = this.featuredCarousel.scrollLeft;
|
||||
const maxScroll = this.featuredCarousel.scrollWidth - this.featuredCarousel.clientWidth;
|
||||
|
||||
// Disable/enable previous button
|
||||
if (scrollLeft <= 0) {
|
||||
prevBtn.disabled = true;
|
||||
} else {
|
||||
prevBtn.disabled = false;
|
||||
}
|
||||
|
||||
// Disable/enable next button
|
||||
if (scrollLeft >= maxScroll - 1) {
|
||||
nextBtn.disabled = true;
|
||||
} else {
|
||||
nextBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize blog loader when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const blogLoader = new BlogLoader();
|
||||
blogLoader.init();
|
||||
});
|
||||
|
||||
// Add loading spinner CSS
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
.loading-spinner {
|
||||
border: 3px solid rgba(0, 208, 132, 0.1);
|
||||
border-top: 3px solid #00D084;
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
@@ -0,0 +1,518 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Complete Neon Template for Claude Code: Instant Provisioning + Expert Agents</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">
|
||||
|
||||
<!-- SEO Meta Tags -->
|
||||
<meta name="description" content="Complete Neon ecosystem for Claude Code: 1 auto-provisioning Skill + 5 expert agents + MCP + monitoring tools. From zero to production-optimized Postgres in minutes.">
|
||||
<meta name="keywords" content="Neon, Claude Code, Postgres, Database, Instagres, Serverless, AI Development, Drizzle ORM, Database Agents, MCP">
|
||||
<meta name="author" content="Claude Code Templates">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://aitmpl.com/blog/neon-complete-template-integration/">
|
||||
<meta property="og:title" content="Complete Neon Template for Claude Code: Instant Provisioning + Expert Agents">
|
||||
<meta property="og:description" content="Complete Neon ecosystem for Claude Code: From instant provisioning to production optimization with 9 components working together.">
|
||||
<meta property="og:image" content="https://aitmpl.com/blog/assets/neon-complete-template-integration-cover.png">
|
||||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="630">
|
||||
<meta property="og:image:alt" content="Complete Neon Template for Claude Code">
|
||||
<meta property="og:site_name" content="Claude Code Templates">
|
||||
<meta property="article:author" content="Claude Code Templates">
|
||||
<meta property="article:section" content="Partnership">
|
||||
<meta property="article:tag" content="Neon">
|
||||
<meta property="article:tag" content="Postgres">
|
||||
<meta property="article:tag" content="Claude Code">
|
||||
<meta property="article:tag" content="Database">
|
||||
<meta property="article:tag" content="Partnership">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/blog/neon-complete-template-integration/">
|
||||
<meta name="twitter:title" content="Complete Neon Template for Claude Code">
|
||||
<meta name="twitter:description" content="From zero to production-optimized Postgres in minutes with 9 integrated components">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/blog/assets/neon-complete-template-integration-cover.png">
|
||||
<meta name="twitter:image:alt" content="Complete Neon Template for Claude Code">
|
||||
<meta name="twitter:creator" content="@davila7">
|
||||
<meta name="twitter:site" content="@davila7">
|
||||
|
||||
<link rel="canonical" href="https://aitmpl.com/blog/neon-complete-template-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">
|
||||
|
||||
<!-- Structured Data -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BlogPosting",
|
||||
"headline": "Complete Neon Template for Claude Code: Instant Provisioning + Expert Agents",
|
||||
"description": "Complete Neon ecosystem for Claude Code: 1 auto-provisioning Skill + 5 expert agents + MCP + monitoring tools. From zero to production-optimized Postgres in minutes.",
|
||||
"image": "https://aitmpl.com/blog/assets/neon-complete-template-integration-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/neon-complete-template-integration/"
|
||||
},
|
||||
"keywords": "Neon, Claude Code, Postgres, Database, Instagres, Serverless, AI Development",
|
||||
"articleSection": "Partnership"
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Hotjar Tracking Code -->
|
||||
<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>
|
||||
</head>
|
||||
<body>
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<div class="header-content">
|
||||
<a href="../index.html" class="logo">
|
||||
<span class="logo-bracket">[</span>
|
||||
<span class="logo-text">Claude Code Templates</span>
|
||||
<span class="logo-bracket">]</span>
|
||||
</a>
|
||||
<nav class="nav">
|
||||
<a href="../index.html">Components</a>
|
||||
<a href="../blog/index.html" class="active">Blog</a>
|
||||
<a href="https://github.com/davila7/claude-code-templates" target="_blank">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="blog-article">
|
||||
<article class="container">
|
||||
<!-- Article Header -->
|
||||
<header class="article-header">
|
||||
<div class="article-meta">
|
||||
<span class="article-date">January 25, 2026</span>
|
||||
<span class="article-reading-time">• 12 min read</span>
|
||||
<span class="article-category">Partnership</span>
|
||||
</div>
|
||||
<h1 class="article-title">Complete Neon Template for Claude Code: Instant Provisioning + Expert Agents</h1>
|
||||
<p class="article-subtitle">How we integrated Neon's Instagres with 5 expert agents, MCP, and monitoring tools to create the most comprehensive Postgres template for AI development</p>
|
||||
<div class="article-author">
|
||||
<span class="author-name">Claude Code Templates</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Article Content -->
|
||||
<div class="article-content">
|
||||
<!-- Section 1: Introduction -->
|
||||
<section>
|
||||
<h2>Announcing the Neon OSS Program Partnership</h2>
|
||||
<p>We're excited to announce that Claude Code Templates has joined the <strong class="neon-highlight">Neon OSS Program</strong>, bringing together instant Postgres provisioning with the power of specialized AI agents.</p>
|
||||
|
||||
<p>This isn't just another database integration—it's a <strong>complete ecosystem</strong> that takes you from zero to production-optimized Postgres in minutes, not hours.</p>
|
||||
|
||||
<div class="stats-grid">
|
||||
<div class="stat-box">
|
||||
<div class="stat-number">⚡ 5s</div>
|
||||
<div class="stat-label">Database Provisioning</div>
|
||||
</div>
|
||||
<div class="stat-box">
|
||||
<div class="stat-number">9</div>
|
||||
<div class="stat-label">Integrated Components</div>
|
||||
</div>
|
||||
<div class="stat-box">
|
||||
<div class="stat-number">5</div>
|
||||
<div class="stat-label">Expert Agents</div>
|
||||
</div>
|
||||
<div class="stat-box">
|
||||
<div class="stat-number">180x</div>
|
||||
<div class="stat-label">Faster Setup</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Section 2: The Problem -->
|
||||
<section>
|
||||
<h2>The Database Setup Problem</h2>
|
||||
<p>Setting up a database traditionally takes <strong>15-30 minutes</strong> of manual work:</p>
|
||||
|
||||
<ol>
|
||||
<li><strong>Sign up</strong> for a database provider (5 min)</li>
|
||||
<li><strong>Wait</strong> for instance provisioning (2-5 min)</li>
|
||||
<li><strong>Configure</strong> connection strings (3 min)</li>
|
||||
<li><strong>Set up</strong> environment variables (2 min)</li>
|
||||
<li><strong>Initialize</strong> schema manually (5-10 min)</li>
|
||||
</ol>
|
||||
|
||||
<p>And that's just the basics. Need auth? Migrations? Performance optimization? Add hours more.</p>
|
||||
|
||||
<blockquote class="highlight-box">
|
||||
<p><strong>The Real Cost:</strong> Every minute spent on infrastructure is a minute not spent building your product.</p>
|
||||
</blockquote>
|
||||
</section>
|
||||
|
||||
<!-- Section 3: The Complete Solution -->
|
||||
<section>
|
||||
<h2>The Complete Solution: Neon Template</h2>
|
||||
<p>The Complete Neon Template brings together <strong>9 components</strong> that work seamlessly:</p>
|
||||
|
||||
<h3>3.1 Instant Provisioning <span class="component-badge">New Skill</span></h3>
|
||||
<p>The <code>neon-instagres</code> Skill auto-activates when Claude detects database needs.</p>
|
||||
|
||||
<pre class="code-block"><code># Install the Skill
|
||||
npx claude-code-templates@latest --skill database/neon-instagres
|
||||
|
||||
# Then just ask Claude:
|
||||
"I need a Postgres database for my Next.js app"
|
||||
|
||||
# Claude automatically runs:
|
||||
npx get-db --yes --ref 4eCjZDz</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> Production-ready Postgres in <span class="neon-highlight">5 seconds</span>.</p>
|
||||
|
||||
<h3>3.2 Expert Agents <span class="component-badge">5 Existing</span></h3>
|
||||
<p>After provisioning, the Skill delegates to specialist agents:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>neon-expert</strong> - Orchestrates complex Neon workflows</li>
|
||||
<li><strong>neon-database-architect</strong> - Schema design with Drizzle ORM</li>
|
||||
<li><strong>neon-auth-specialist</strong> - Stack Auth & Neon Auth integration</li>
|
||||
<li><strong>neon-migration-specialist</strong> - Safe migration patterns</li>
|
||||
<li><strong>neon-optimization-analyzer</strong> - Query performance tuning</li>
|
||||
</ul>
|
||||
|
||||
<h3>3.3 Management API <span class="component-badge">MCP</span></h3>
|
||||
<p>The <code>neon</code> MCP provides programmatic control:</p>
|
||||
<ul>
|
||||
<li>Project management</li>
|
||||
<li>Database branching</li>
|
||||
<li>Metrics and monitoring</li>
|
||||
</ul>
|
||||
|
||||
<h3>3.4 Development Tools <span class="component-badge">2 Settings</span></h3>
|
||||
<p>Real-time monitoring in your statusline:</p>
|
||||
<ul>
|
||||
<li><strong>neon-database-dev</strong> - Development metrics</li>
|
||||
<li><strong>neon-database-resources</strong> - Resource monitoring</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<!-- Section 4: Workflow Examples -->
|
||||
<section>
|
||||
<h2>Complete Workflow Examples</h2>
|
||||
|
||||
<h3>Example 1: Fullstack App from Zero</h3>
|
||||
<div class="workflow-diagram">
|
||||
<p><strong>User asks:</strong> "Build a todo app with auth using Neon"</p>
|
||||
<p style="margin: 1rem 0; color: #00E599;">↓</p>
|
||||
<p><strong>Step 1:</strong> neon-instagres Skill provisions database (5s)</p>
|
||||
<p style="margin: 1rem 0; color: #00E599;">↓</p>
|
||||
<p><strong>Step 2:</strong> neon-database-architect generates Drizzle schema</p>
|
||||
<p style="margin: 1rem 0; color: #00E599;">↓</p>
|
||||
<p><strong>Step 3:</strong> neon-auth-specialist sets up Stack Auth</p>
|
||||
<p style="margin: 1rem 0; color: #00E599;">↓</p>
|
||||
<p><strong>Result:</strong> Next.js app with API routes, database, and auth ready</p>
|
||||
</div>
|
||||
|
||||
<h3>Example 2: Production Migration</h3>
|
||||
<div class="workflow-diagram">
|
||||
<p><strong>User asks:</strong> "Migrate our users table to add email verification"</p>
|
||||
<p style="margin: 1rem 0; color: #00E599;">↓</p>
|
||||
<p><strong>Step 1:</strong> neon-migration-specialist creates safe migration</p>
|
||||
<p style="margin: 1rem 0; color: #00E599;">↓</p>
|
||||
<p><strong>Step 2:</strong> Uses Neon branching to test migration</p>
|
||||
<p style="margin: 1rem 0; color: #00E599;">↓</p>
|
||||
<p><strong>Step 3:</strong> neon-optimization-analyzer reviews performance impact</p>
|
||||
<p style="margin: 1rem 0; color: #00E599;">↓</p>
|
||||
<p><strong>Result:</strong> Migration executed with rollback plan</p>
|
||||
</div>
|
||||
|
||||
<h3>Example 3: Performance Optimization</h3>
|
||||
<div class="workflow-diagram">
|
||||
<p><strong>User asks:</strong> "My queries are slow, help optimize"</p>
|
||||
<p style="margin: 1rem 0; color: #00E599;">↓</p>
|
||||
<p><strong>Step 1:</strong> neon-optimization-analyzer analyzes query patterns</p>
|
||||
<p style="margin: 1rem 0; color: #00E599;">↓</p>
|
||||
<p><strong>Step 2:</strong> Recommends indexes and schema changes</p>
|
||||
<p style="margin: 1rem 0; color: #00E599;">↓</p>
|
||||
<p><strong>Step 3:</strong> neon MCP monitors resource usage</p>
|
||||
<p style="margin: 1rem 0; color: #00E599;">↓</p>
|
||||
<p><strong>Result:</strong> Optimizations implemented with benchmarks</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Section 5: Installation -->
|
||||
<section>
|
||||
<h2>Template Installation</h2>
|
||||
|
||||
<h3>Quick Start (Skill Only)</h3>
|
||||
<p>Get instant provisioning in one command:</p>
|
||||
<pre class="code-block"><code>npx claude-code-templates@latest --skill database/neon-instagres</code></pre>
|
||||
|
||||
<h3>Full Template (All 9 Components)</h3>
|
||||
<p>Install the complete ecosystem:</p>
|
||||
<pre class="code-block"><code>npx claude-code-templates@latest \
|
||||
--skill database/neon-instagres,database/using-neon \
|
||||
--agent database/neon-expert,database/neon-database-architect,database/neon-auth-specialist,data-ai/neon-migration-specialist,data-ai/neon-optimization-analyzer \
|
||||
--mcp database/neon \
|
||||
--setting statusline/neon-database-dev,statusline/neon-database-resources \
|
||||
--yes</code></pre>
|
||||
</section>
|
||||
|
||||
<!-- Section 6: Use Cases -->
|
||||
<section>
|
||||
<h2>Real-World Use Cases</h2>
|
||||
|
||||
<h3>🚀 Rapid Prototyping</h3>
|
||||
<p>Instant databases plus expert schema design means you can go from idea to working prototype in minutes. No more wrestling with database setup—just build.</p>
|
||||
|
||||
<h3>🧪 Testing & CI/CD</h3>
|
||||
<p>Ephemeral databases with automated migrations. Spin up fresh databases for each test run, then let them expire. Perfect for CI/CD pipelines.</p>
|
||||
|
||||
<h3>📚 Education & Learning</h3>
|
||||
<p>Zero-friction database access with best practices built-in. Students learn proper schema design, migrations, and optimization from expert agents.</p>
|
||||
|
||||
<h3>🏢 Enterprise Production</h3>
|
||||
<p>Database branching for safe migrations, performance monitoring, and optimization analysis. Everything production teams need in one template.</p>
|
||||
</section>
|
||||
|
||||
<!-- Section 7: Performance Benchmarks -->
|
||||
<section>
|
||||
<h2>Performance Benchmarks</h2>
|
||||
|
||||
<div class="comparison-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Task</th>
|
||||
<th>Traditional</th>
|
||||
<th>Neon Template</th>
|
||||
<th>Improvement</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Provisioning</td>
|
||||
<td>17-30 min</td>
|
||||
<td class="neon-highlight">5 seconds</td>
|
||||
<td>180x-360x faster</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Schema Design</td>
|
||||
<td>Manual trial & error</td>
|
||||
<td class="neon-highlight">Expert agent guidance</td>
|
||||
<td>Best practices built-in</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Migration Safety</td>
|
||||
<td>Risky production changes</td>
|
||||
<td class="neon-highlight">Branching + testing</td>
|
||||
<td>Zero-downtime migrations</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optimization</td>
|
||||
<td>Guesswork</td>
|
||||
<td class="neon-highlight">Data-driven analysis</td>
|
||||
<td>Measurable improvements</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Section 8: Partnership Details -->
|
||||
<section>
|
||||
<h2>Partnership Details</h2>
|
||||
<p>As part of the Neon OSS Program, we're proud to offer:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>$5,000 annual sponsorship</strong> supporting Claude Code Templates development</li>
|
||||
<li><strong>$20 referral commission</strong> for every customer from our community</li>
|
||||
<li><strong>Co-marketing initiatives</strong> with Neon's developer community</li>
|
||||
<li><strong>Maintained and updated template</strong> with new Neon features</li>
|
||||
</ul>
|
||||
|
||||
<p>This partnership positions <span class="neon-highlight">Neon as the recommended Postgres provider</span> across all Claude Code components.</p>
|
||||
</section>
|
||||
|
||||
<!-- Section 9: Component Deep Dives -->
|
||||
<section>
|
||||
<h2>Component Deep Dives</h2>
|
||||
|
||||
<h3>9.1 neon-instagres Skill</h3>
|
||||
<p><strong>Auto-Activation Triggers:</strong></p>
|
||||
<ul>
|
||||
<li>User mentions: "database", "postgres", "SQL"</li>
|
||||
<li>Framework contexts: Next.js, Vite, Express</li>
|
||||
<li>ORM mentions: Drizzle, Prisma, TypeORM</li>
|
||||
</ul>
|
||||
|
||||
<p><strong>Framework Integration:</strong></p>
|
||||
<ul>
|
||||
<li>Next.js: <code>--env .env.local</code></li>
|
||||
<li>Vite: Auto-provisioning with vite-plugin-db</li>
|
||||
<li>Express: Standard .env setup</li>
|
||||
</ul>
|
||||
|
||||
<h3>9.2 Agent Ecosystem</h3>
|
||||
<p><strong>Delegation Pattern:</strong></p>
|
||||
<p>The Skill acts as the entry point, then delegates to specialist agents based on the task:</p>
|
||||
<ul>
|
||||
<li><strong>Complex schema?</strong> → neon-database-architect</li>
|
||||
<li><strong>Need auth?</strong> → neon-auth-specialist</li>
|
||||
<li><strong>Production migration?</strong> → neon-migration-specialist</li>
|
||||
<li><strong>Performance issues?</strong> → neon-optimization-analyzer</li>
|
||||
</ul>
|
||||
|
||||
<h3>9.3 MCP Integration</h3>
|
||||
<p>Programmatic Neon control through the MCP server:</p>
|
||||
<ul>
|
||||
<li>Create and manage database branches</li>
|
||||
<li>Monitor resource usage and metrics</li>
|
||||
<li>Automate database operations</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<!-- Section 10: Getting Started Guide -->
|
||||
<section>
|
||||
<h2>Getting Started Guide</h2>
|
||||
|
||||
<h3>Step 1: Install the Skill</h3>
|
||||
<pre class="code-block"><code>npx claude-code-templates@latest --skill database/neon-instagres</code></pre>
|
||||
|
||||
<h3>Step 2: Ask Claude for a Database</h3>
|
||||
<p>Just describe what you need:</p>
|
||||
<ul>
|
||||
<li>"I need a database for my todo app"</li>
|
||||
<li>"Setup a Postgres database with auth"</li>
|
||||
<li>"Create a database for my Next.js project"</li>
|
||||
</ul>
|
||||
|
||||
<h3>Step 3: Add Specialist Agents (Optional)</h3>
|
||||
<p>For advanced workflows, install specific agents:</p>
|
||||
<ul>
|
||||
<li><strong>Schema design:</strong> <code>--agent database/neon-database-architect</code></li>
|
||||
<li><strong>Auth integration:</strong> <code>--agent database/neon-auth-specialist</code></li>
|
||||
<li><strong>Migrations:</strong> <code>--agent data-ai/neon-migration-specialist</code></li>
|
||||
<li><strong>Performance:</strong> <code>--agent data-ai/neon-optimization-analyzer</code></li>
|
||||
</ul>
|
||||
|
||||
<h3>Step 4: Enable Monitoring (Optional)</h3>
|
||||
<p>Add statusline settings for real-time metrics:</p>
|
||||
<pre class="code-block"><code>npx claude-code-templates@latest \
|
||||
--setting statusline/neon-database-dev \
|
||||
--setting statusline/neon-database-resources</code></pre>
|
||||
</section>
|
||||
|
||||
<!-- Section 11: Conclusion -->
|
||||
<section>
|
||||
<h2>Complete Neon Ecosystem in Claude Code</h2>
|
||||
<p>The Complete Neon Template represents a new approach to database integration in AI development tools:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>Instant provisioning</strong> eliminates setup friction</li>
|
||||
<li><strong>Expert agents</strong> provide guidance at every step</li>
|
||||
<li><strong>Programmatic control</strong> through MCP for advanced workflows</li>
|
||||
<li><strong>Real-time monitoring</strong> keeps you informed</li>
|
||||
</ul>
|
||||
|
||||
<p>From zero to production-optimized Postgres in <strong class="neon-highlight">minutes, not hours</strong>.</p>
|
||||
|
||||
<div class="cta-box">
|
||||
<h3>Ready to Get Started?</h3>
|
||||
<p>Try the Complete Neon Template today:</p>
|
||||
<div style="margin-top: 1.5rem;">
|
||||
<a href="https://get.neon.com/4eCjZDz" class="btn btn-primary" target="_blank">Try Neon Free →</a>
|
||||
<a href="../featured/neon-instagres/" class="btn btn-secondary">View Template →</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Resources -->
|
||||
<section>
|
||||
<h2>Resources</h2>
|
||||
<ul>
|
||||
<li><a href="https://neon.tech/docs/guides/instagres" target="_blank">Instagres Documentation</a></li>
|
||||
<li><a href="https://console.neon.tech" target="_blank">Neon Console</a></li>
|
||||
<li><a href="https://orm.drizzle.team/docs/get-started-postgresql#neon" target="_blank">Drizzle + Neon Guide</a></li>
|
||||
<li><a href="../featured/neon-instagres/" target="_blank">Complete Template Page</a></li>
|
||||
<li><a href="../index.html" target="_blank">Browse All Components</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Article Footer -->
|
||||
<footer class="article-footer">
|
||||
<div class="article-tags">
|
||||
<span class="tag">Neon</span>
|
||||
<span class="tag">Postgres</span>
|
||||
<span class="tag">Template</span>
|
||||
<span class="tag">Skills</span>
|
||||
<span class="tag">Agents</span>
|
||||
<span class="tag">MCP</span>
|
||||
<span class="tag">Partnership</span>
|
||||
</div>
|
||||
|
||||
<div class="article-share">
|
||||
<p>Share this article:</p>
|
||||
<div class="share-buttons">
|
||||
<a href="https://twitter.com/intent/tweet?text=Complete%20Neon%20Template%20for%20Claude%20Code&url=https://aitmpl.com/blog/neon-complete-template-integration.html" target="_blank" class="share-btn twitter">Twitter</a>
|
||||
<a href="https://www.linkedin.com/sharing/share-offsite/?url=https://aitmpl.com/blog/neon-complete-template-integration.html" target="_blank" class="share-btn linkedin">LinkedIn</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="back-to-blog">
|
||||
<a href="index.html">← Back to Blog</a>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</main>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p>© 2026 Claude Code Templates. Part of the Neon OSS Program.</p>
|
||||
<p>
|
||||
<a href="https://github.com/davila7/claude-code-templates" target="_blank">GitHub</a> •
|
||||
<a href="https://get.neon.com/4eCjZDz" target="_blank">Try Neon</a> •
|
||||
<a href="../index.html">Components</a>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="../js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,879 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Claude Code Templates Joins the Neon Open Source Program</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="Claude Code Templates has been accepted into the Neon Open Source Program, bringing $5,000 annual support and instant Postgres provisioning to the community. Learn about our partnership and how you can benefit from free serverless Postgres.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://aitmpl.com/blog/neon-open-source-program/">
|
||||
<meta property="og:title" content="Claude Code Templates Joins the Neon Open Source Program">
|
||||
<meta property="og:description" content="$5,000 annual support, instant Postgres provisioning, and co-marketing opportunities. Discover how our partnership with Neon benefits the Claude Code community.">
|
||||
<meta property="og:image" content="https://aitmpl.com/blog/assets/neon-open-source-program-cover.svg">
|
||||
<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="Partnership">
|
||||
<meta property="article:tag" content="Neon">
|
||||
<meta property="article:tag" content="Partnership">
|
||||
<meta property="article:tag" content="Open Source">
|
||||
<meta property="article:tag" content="Database">
|
||||
<meta property="article:tag" content="Postgres">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/blog/neon-open-source-program/">
|
||||
<meta name="twitter:title" content="Claude Code Templates Joins the Neon Open Source Program">
|
||||
<meta name="twitter:description" content="$5,000 annual support, instant Postgres provisioning, and co-marketing opportunities. Discover how our partnership with Neon benefits the Claude Code community.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/blog/assets/neon-open-source-program-cover.svg">
|
||||
|
||||
<!-- Additional SEO -->
|
||||
<meta name="keywords" content="Neon, Postgres, Open Source Program, Claude Code, Database, Serverless, Instagres, Developer Tools, OSS Sponsorship">
|
||||
<meta name="author" content="Claude Code Templates">
|
||||
<link rel="canonical" href="https://aitmpl.com/blog/neon-open-source-program/">
|
||||
|
||||
<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": "Claude Code Templates Joins the Neon Open Source Program",
|
||||
"description": "Claude Code Templates has been accepted into the Neon Open Source Program, bringing $5,000 annual support and instant Postgres provisioning to the community.",
|
||||
"image": "https://aitmpl.com/blog/assets/neon-open-source-program-cover.svg",
|
||||
"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/neon-open-source-program/"
|
||||
},
|
||||
"keywords": "Neon, Postgres, Open Source Program, Claude Code, Database, Serverless",
|
||||
"wordCount": "1200",
|
||||
"articleSection": "Partnership",
|
||||
"about": [
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Open Source Sponsorship"
|
||||
},
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Serverless Database"
|
||||
},
|
||||
{
|
||||
"@type": "SoftwareApplication",
|
||||
"name": "Neon",
|
||||
"applicationCategory": "DatabaseApplication"
|
||||
}
|
||||
]
|
||||
}
|
||||
</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 Templates Joins the Neon Open Source Program</h1>
|
||||
<p class="article-subtitle">We're proud to announce that Claude Code Templates has been accepted into the Neon Open Source Program, bringing financial support, instant Postgres provisioning, and co-marketing opportunities to our community of developers.</p>
|
||||
<div class="article-meta-full">
|
||||
<span class="read-time">5 min read</span>
|
||||
<div class="article-tags">
|
||||
<span class="tag">Partnership</span>
|
||||
<span class="tag">Neon</span>
|
||||
<span class="tag">Open Source</span>
|
||||
<span class="tag">Database</span>
|
||||
<span class="tag">Postgres</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<article class="article-body">
|
||||
<img src="../assets/neon-open-source-program-cover.svg" alt="Claude Code Templates joins Neon Open Source Program" class="article-cover" loading="lazy">
|
||||
|
||||
<div class="article-content-full">
|
||||
<h2>Announcing Our Partnership with Neon</h2>
|
||||
|
||||
<p>We're thrilled to share that Claude Code Templates has been officially accepted into the <strong>Neon Open Source Program</strong>. This partnership represents a significant milestone for our project and brings substantial benefits to the entire Claude Code community.</p>
|
||||
|
||||
<p>This collaboration combines the power of AI-assisted development with instant, serverless Postgres provisioning, making database setup as simple as asking Claude for help.</p>
|
||||
|
||||
<h2>What is the Neon Open Source Program?</h2>
|
||||
|
||||
<p>The <a href="https://get.neon.com/4eCjZDz" target="_blank">Neon Open Source Program</a> provides comprehensive support to open source projects that integrate with Neon's serverless Postgres platform. The program includes:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>$5,000 Annual Financial Support</strong> - Direct funding to support Claude Code Templates development and infrastructure costs</li>
|
||||
<li><strong>$20 Referral Payouts per Customer</strong> - Revenue sharing for every developer from our community who becomes a Neon customer</li>
|
||||
<li><strong>Co-Marketing Opportunities</strong> - Joint promotion through blog posts, social media, and community outreach</li>
|
||||
<li><strong>Technical Support</strong> - Direct access to Neon's engineering team for integration assistance</li>
|
||||
<li><strong>Early Access</strong> - Preview upcoming Neon features before public release</li>
|
||||
</ul>
|
||||
|
||||
<p>This partnership enables us to dedicate more resources to developing high-quality components and improving the Claude Code Templates ecosystem.</p>
|
||||
|
||||
<h2>What Neon Provides: Serverless Postgres Made Simple</h2>
|
||||
|
||||
<p><a href="https://get.neon.com/4eCjZDz" target="_blank">Neon</a> is a serverless Postgres platform that fundamentally changes how developers work with databases. Instead of spending 15-30 minutes setting up a database manually, Neon provisions production-ready Postgres instances in seconds.</p>
|
||||
|
||||
<h3>Key Neon Features</h3>
|
||||
|
||||
<ul>
|
||||
<li><strong>Instant Provisioning</strong> - Get a fully configured Postgres database in under 5 seconds</li>
|
||||
<li><strong>Database Branching</strong> - Create instant copies of your database for testing, perfect for development workflows</li>
|
||||
<li><strong>Autoscaling</strong> - Resources scale automatically based on demand, from zero to maximum capacity</li>
|
||||
<li><strong>Generous Free Tier</strong> - 10 GB storage, unlimited projects, and serverless compute at no cost</li>
|
||||
<li><strong>Modern Developer Experience</strong> - Connect via standard Postgres connection strings, works with all ORMs</li>
|
||||
<li><strong>Built for CI/CD</strong> - Ephemeral databases for testing, with automatic cleanup</li>
|
||||
</ul>
|
||||
|
||||
<p>Neon eliminates the friction of database setup, allowing developers to focus on building features instead of managing infrastructure.</p>
|
||||
|
||||
<h2>What Claude Code Templates Delivers</h2>
|
||||
|
||||
<p>As part of our commitment to the Neon Open Source Program, we're providing several key integrations and promotional efforts:</p>
|
||||
|
||||
<h3>1. Featured Documentation Integration</h3>
|
||||
|
||||
<p>Neon is now the recommended database provider throughout Claude Code Templates documentation. All database-related guides, tutorials, and examples feature Neon with our <a href="https://get.neon.com/4eCjZDz" target="_blank">referral link</a> prominently displayed.</p>
|
||||
|
||||
<h3>2. GitHub README Visibility</h3>
|
||||
|
||||
<p>The Neon logo appears in our <a href="https://github.com/davila7/claude-code-templates" target="_blank">GitHub README</a>, providing visibility to thousands of monthly visitors and establishing Neon as our preferred database partner.</p>
|
||||
|
||||
<h3>3. Instagres Integration</h3>
|
||||
|
||||
<p>We've integrated <strong>Instagres</strong>, Neon's instant database provisioning tool, making it trivially easy to get a Postgres database:</p>
|
||||
|
||||
<pre><code class="language-bash"># Instant Postgres with our referral
|
||||
npx get-db --yes --ref 4eCjZDz
|
||||
|
||||
# Result: Production-ready Postgres in 5 seconds
|
||||
# Connection string automatically saved to .env</code></pre>
|
||||
|
||||
<p>This single command replaces 15-30 minutes of manual setup work, including account creation, instance configuration, and environment variable management.</p>
|
||||
|
||||
<h3>4. Component Ecosystem Integration</h3>
|
||||
|
||||
<p>We've created a comprehensive ecosystem of Neon components:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>neon-instagres Skill</strong> - Auto-triggers when Claude detects database needs</li>
|
||||
<li><strong>neon-expert Agent</strong> - Specialized agent for Neon workflows and best practices</li>
|
||||
<li><strong>neon-database-architect Agent</strong> - Schema design with Drizzle ORM integration</li>
|
||||
<li><strong>neon-auth-specialist Agent</strong> - Authentication setup with Stack Auth and Neon Auth</li>
|
||||
<li><strong>neon-migration-specialist Agent</strong> - Safe database migrations using branching</li>
|
||||
<li><strong>neon-optimization-analyzer Agent</strong> - Query performance analysis and tuning</li>
|
||||
<li><strong>Neon MCP</strong> - Programmatic API access for advanced workflows</li>
|
||||
<li><strong>Statusline Settings</strong> - Real-time database metrics in your terminal</li>
|
||||
</ul>
|
||||
|
||||
<h2>The neon-instagres Skill: Database Setup in Seconds</h2>
|
||||
|
||||
<p>The centerpiece of our Neon integration is the <code>neon-instagres</code> Skill. This skill fundamentally changes how Claude Code users set up databases.</p>
|
||||
|
||||
<h3>How It Works</h3>
|
||||
|
||||
<p>When you mention needing a database while working with Claude, the skill automatically activates and provisions a Neon Postgres instance:</p>
|
||||
|
||||
<pre><code class="language-bash"># Install the skill
|
||||
npx claude-code-templates@latest --skill database/neon-instagres
|
||||
|
||||
# Then just ask Claude:
|
||||
"I need a Postgres database for my Next.js app"
|
||||
|
||||
# Claude automatically:
|
||||
# 1. Runs: npx get-db --yes --ref 4eCjZDz
|
||||
# 2. Provisions Neon database in 5 seconds
|
||||
# 3. Saves connection string to .env.local
|
||||
# 4. Installs and configures Drizzle ORM
|
||||
# 5. Creates initial schema files</code></pre>
|
||||
|
||||
<h3>Framework Integration</h3>
|
||||
|
||||
<p>The skill intelligently adapts to your framework:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>Next.js</strong> - Saves to <code>.env.local</code>, configures for Server Actions and Route Handlers</li>
|
||||
<li><strong>Vite/React</strong> - Uses <code>vite-plugin-db</code> for auto-provisioning</li>
|
||||
<li><strong>Express/Node.js</strong> - Standard <code>.env</code> setup with connection pooling</li>
|
||||
<li><strong>CLI/Scripts</strong> - Direct connection string for standalone tools</li>
|
||||
</ul>
|
||||
|
||||
<h3>Install the Skill</h3>
|
||||
|
||||
<pre><code class="language-bash">npx claude-code-templates@latest --skill database/neon-instagres --yes</code></pre>
|
||||
|
||||
<p>Once installed, Claude will automatically detect when you need a database and provision one instantly using Neon.</p>
|
||||
|
||||
<h2>What This Means for Users</h2>
|
||||
|
||||
<p>This partnership delivers concrete benefits to every Claude Code Templates user:</p>
|
||||
|
||||
<h3>1. Free Instant Postgres for Prototyping</h3>
|
||||
|
||||
<p>No credit card required. No manual setup. Just ask Claude for a database and get a production-ready Postgres instance in seconds. Perfect for:</p>
|
||||
|
||||
<ul>
|
||||
<li>Trying out new project ideas</li>
|
||||
<li>Building proof-of-concepts</li>
|
||||
<li>Learning database development</li>
|
||||
<li>Testing integration patterns</li>
|
||||
</ul>
|
||||
|
||||
<h3>2. Professional Development Workflows</h3>
|
||||
|
||||
<p>Database branching enables best practices like:</p>
|
||||
|
||||
<ul>
|
||||
<li>Testing migrations on branch databases before production</li>
|
||||
<li>Isolated development environments for each feature</li>
|
||||
<li>Automated testing with fresh database copies</li>
|
||||
<li>Safe experimentation without affecting production data</li>
|
||||
</ul>
|
||||
|
||||
<h3>3. Expert Guidance Built-In</h3>
|
||||
|
||||
<p>The Neon agent ecosystem provides specialized expertise:</p>
|
||||
|
||||
<ul>
|
||||
<li>Schema design following PostgreSQL best practices</li>
|
||||
<li>Authentication setup with proven patterns</li>
|
||||
<li>Migration strategies that minimize downtime</li>
|
||||
<li>Performance optimization based on query patterns</li>
|
||||
</ul>
|
||||
|
||||
<h3>4. Reduced Infrastructure Complexity</h3>
|
||||
|
||||
<p>Serverless Postgres eliminates common pain points:</p>
|
||||
|
||||
<ul>
|
||||
<li>No server management or provisioning</li>
|
||||
<li>Automatic scaling from zero to maximum capacity</li>
|
||||
<li>Built-in connection pooling</li>
|
||||
<li>No idle resource costs</li>
|
||||
</ul>
|
||||
|
||||
<h2>Getting Started with Neon and Claude Code</h2>
|
||||
|
||||
<h3>Step 1: Sign Up for Neon (Optional)</h3>
|
||||
|
||||
<p>While the <code>neon-instagres</code> skill can provision databases without an account, signing up for <a href="https://get.neon.com/4eCjZDz" target="_blank">Neon</a> gives you access to:</p>
|
||||
|
||||
<ul>
|
||||
<li>Dashboard for managing databases</li>
|
||||
<li>Database branching features</li>
|
||||
<li>Team collaboration tools</li>
|
||||
<li>Advanced monitoring and analytics</li>
|
||||
</ul>
|
||||
|
||||
<h3>Step 2: Install the neon-instagres Skill</h3>
|
||||
|
||||
<pre><code class="language-bash">npx claude-code-templates@latest --skill database/neon-instagres --yes</code></pre>
|
||||
|
||||
<h3>Step 3: Ask Claude for a Database</h3>
|
||||
|
||||
<p>Simply mention that you need a database while working on your project:</p>
|
||||
|
||||
<ul>
|
||||
<li>"I need a Postgres database for this todo app"</li>
|
||||
<li>"Set up a database with user authentication"</li>
|
||||
<li>"Create a database for my Next.js blog"</li>
|
||||
</ul>
|
||||
|
||||
<p>Claude will automatically provision a Neon database and configure your project.</p>
|
||||
|
||||
<h3>Step 4: Optional - Install Expert Agents</h3>
|
||||
|
||||
<p>For advanced database workflows, install specialist agents:</p>
|
||||
|
||||
<pre><code class="language-bash"># Database architecture
|
||||
npx claude-code-templates@latest --agent database/neon-database-architect
|
||||
|
||||
# Authentication integration
|
||||
npx claude-code-templates@latest --agent database/neon-auth-specialist
|
||||
|
||||
# Migration management
|
||||
npx claude-code-templates@latest --agent data-ai/neon-migration-specialist
|
||||
|
||||
# Performance optimization
|
||||
npx claude-code-templates@latest --agent data-ai/neon-optimization-analyzer</code></pre>
|
||||
|
||||
<h2>Real-World Examples</h2>
|
||||
|
||||
<h3>Example 1: Building a SaaS App</h3>
|
||||
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Build a multi-tenant SaaS app with user auth and billing
|
||||
|
||||
# Claude automatically:
|
||||
# 1. Provisions Neon database via neon-instagres skill
|
||||
# 2. Uses neon-auth-specialist to set up Stack Auth
|
||||
# 3. Uses neon-database-architect to design tenant schema
|
||||
# 4. Creates Next.js app with API routes and database
|
||||
# 5. Sets up row-level security for tenant isolation
|
||||
|
||||
# Result: Production-ready SaaS foundation in minutes</code></pre>
|
||||
|
||||
<h3>Example 2: Learning Database Development</h3>
|
||||
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> I want to learn about database indexes and query optimization
|
||||
|
||||
# Claude:
|
||||
# 1. Provisions practice database
|
||||
# 2. Loads neon-optimization-analyzer agent
|
||||
# 3. Creates sample schema with performance issues
|
||||
# 4. Demonstrates EXPLAIN ANALYZE
|
||||
# 5. Shows before/after metrics for index additions
|
||||
|
||||
# Result: Hands-on learning environment ready instantly</code></pre>
|
||||
|
||||
<h3>Example 3: Testing Database Migrations</h3>
|
||||
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> I need to add email verification to my users table, but I'm worried about breaking production
|
||||
|
||||
# Claude:
|
||||
# 1. Uses neon-migration-specialist agent
|
||||
# 2. Creates database branch via Neon MCP
|
||||
# 3. Tests migration on branch
|
||||
# 4. Validates with sample data
|
||||
# 5. Provides rollback plan
|
||||
# 6. Executes on production after approval
|
||||
|
||||
# Result: Safe migration with zero downtime</code></pre>
|
||||
|
||||
<h2>Why This Partnership Matters</h2>
|
||||
|
||||
<p>This partnership exemplifies our vision for Claude Code Templates: making professional development practices accessible to everyone through intelligent automation.</p>
|
||||
|
||||
<p>By combining Claude's AI capabilities with Neon's instant provisioning, we've eliminated one of the most frustrating parts of software development: database setup and management.</p>
|
||||
|
||||
<p>The $5,000 annual support enables us to:</p>
|
||||
|
||||
<ul>
|
||||
<li>Develop more database-focused components and agents</li>
|
||||
<li>Improve documentation and tutorials</li>
|
||||
<li>Maintain infrastructure for the component catalog</li>
|
||||
<li>Support the open source community</li>
|
||||
</ul>
|
||||
|
||||
<p>The referral program creates a sustainable model where community success directly supports project development.</p>
|
||||
|
||||
<h2>Looking Forward</h2>
|
||||
|
||||
<p>This is just the beginning of our Neon integration. We're planning:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>Enhanced Monitoring</strong> - Real-time query performance tracking in Claude Code</li>
|
||||
<li><strong>AI-Powered Schema Evolution</strong> - Agents that suggest schema improvements based on usage patterns</li>
|
||||
<li><strong>Template Expansions</strong> - Pre-configured project templates with Neon and popular frameworks</li>
|
||||
<li><strong>Educational Content</strong> - Tutorials and guides for database best practices using Neon</li>
|
||||
<li><strong>Community Features</strong> - Shared database templates and schemas</li>
|
||||
</ul>
|
||||
|
||||
<h2>Try It Today</h2>
|
||||
|
||||
<p>Experience instant Postgres provisioning with the neon-instagres skill:</p>
|
||||
|
||||
<pre><code class="language-bash">npx claude-code-templates@latest --skill database/neon-instagres --yes</code></pre>
|
||||
|
||||
<p>Or sign up for a free Neon account to access advanced features like database branching and team collaboration:</p>
|
||||
|
||||
<p><a href="https://get.neon.com/4eCjZDz" target="_blank" style="display: inline-block; padding: 0.875rem 2rem; background: #00E599; color: #000; text-decoration: none; border-radius: 4px; font-weight: 600; font-size: 1rem;">Get Started with Neon - Free</a></p>
|
||||
|
||||
<h2>Resources</h2>
|
||||
|
||||
<ul>
|
||||
<li><a href="https://get.neon.com/4eCjZDz" target="_blank">Neon Official Website</a></li>
|
||||
<li><a href="https://neon.tech/docs" target="_blank">Neon Documentation</a></li>
|
||||
<li><a href="https://neon.tech/docs/guides/instagres" target="_blank">Instagres Documentation</a></li>
|
||||
<li><a href="https://neon.tech/oss" target="_blank">Neon Open Source Program</a></li>
|
||||
<li><a href="https://github.com/davila7/claude-code-templates" target="_blank">Claude Code Templates on GitHub</a></li>
|
||||
<li><a href="../../index.html">Browse All Components at aitmpl.com</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- Explore Components Banner -->
|
||||
<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">© 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.30.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
|
||||
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();
|
||||
}
|
||||
|
||||
// Explore components banner hover effect
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const banner = document.querySelector('.explore-components-banner a');
|
||||
if (banner) {
|
||||
banner.addEventListener('mouseenter', () => {
|
||||
banner.style.background = '#00cc33';
|
||||
banner.style.transform = 'translateY(-2px)';
|
||||
});
|
||||
banner.addEventListener('mouseleave', () => {
|
||||
banner.style.background = '#00ff41';
|
||||
banner.style.transform = 'translateY(0)';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 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 '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')) {
|
||||
return '';
|
||||
}
|
||||
return element.textContent || '';
|
||||
case 'img':
|
||||
const alt = element.getAttribute('alt') || '';
|
||||
const src = element.getAttribute('src') || '';
|
||||
return ``;
|
||||
default:
|
||||
return element.textContent || '';
|
||||
}
|
||||
}
|
||||
|
||||
processList(listElement, marker) {
|
||||
const items = listElement.querySelectorAll('li');
|
||||
return Array.from(items)
|
||||
.map(item => `${marker} ${item.textContent}`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
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>
|
||||
@@ -0,0 +1,804 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>React Best Practices Skill for Claude Code: 40+ Performance Optimization Rules</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="Master React and Next.js performance optimization with 40+ proven rules. Learn to eliminate waterfalls, optimize bundles, and improve rendering performance with Claude Code's React Best Practices skill.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://aitmpl.com/blog/react-best-practices-skill/">
|
||||
<meta property="og:title" content="React Best Practices Skill: 40+ Performance Optimization Rules">
|
||||
<meta property="og:description" content="Master React and Next.js performance optimization with 40+ proven rules. Learn to eliminate waterfalls, optimize bundles, and improve rendering performance.">
|
||||
<meta property="og:image" content="https://aitmpl.com/blog/assets/react-best-practices-skill-cover.svg">
|
||||
<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="Skills">
|
||||
<meta property="article:tag" content="React">
|
||||
<meta property="article:tag" content="Next.js">
|
||||
<meta property="article:tag" content="Performance">
|
||||
<meta property="article:tag" content="Optimization">
|
||||
<meta property="article:tag" content="Best Practices">
|
||||
<meta property="article:tag" content="Bundle Size">
|
||||
<meta property="article:tag" content="Rendering">
|
||||
<meta property="article:tag" content="Server Components">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/blog/react-best-practices-skill/">
|
||||
<meta name="twitter:title" content="React Best Practices Skill: 40+ Performance Optimization Rules">
|
||||
<meta name="twitter:description" content="Master React and Next.js performance optimization with 40+ proven rules. Learn to eliminate waterfalls, optimize bundles, and improve rendering performance.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/blog/assets/react-best-practices-skill-cover.svg">
|
||||
|
||||
<!-- Additional SEO -->
|
||||
<meta name="keywords" content="React performance, Next.js optimization, bundle size, React best practices, waterfalls, rendering optimization, Server Components, Claude Code, React skills, performance optimization, web performance, JavaScript optimization, React hooks, memoization, code splitting">
|
||||
<meta name="author" content="Claude Code Templates">
|
||||
<link rel="canonical" href="https://aitmpl.com/blog/react-best-practices-skill/">
|
||||
|
||||
<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": "React Best Practices Skill for Claude Code: 40+ Performance Optimization Rules",
|
||||
"description": "Master React and Next.js performance optimization with 40+ proven rules. Learn to eliminate waterfalls, optimize bundles, and improve rendering performance.",
|
||||
"image": "https://aitmpl.com/blog/assets/react-best-practices-skill-cover.svg",
|
||||
"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/react-best-practices-skill/"
|
||||
},
|
||||
"keywords": "React performance, Next.js optimization, bundle size, React best practices, waterfalls, rendering optimization, Server Components, Claude Code",
|
||||
"wordCount": "1500",
|
||||
"articleSection": "Claude Code Skills",
|
||||
"about": [
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "React Performance Optimization"
|
||||
},
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Next.js Best Practices"
|
||||
},
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Web Performance"
|
||||
},
|
||||
{
|
||||
"@type": "SoftwareApplication",
|
||||
"name": "Claude Code",
|
||||
"applicationCategory": "DeveloperApplication"
|
||||
}
|
||||
]
|
||||
}
|
||||
</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">React Best Practices Skill: 40+ Performance Optimization Rules</h1>
|
||||
<p class="article-subtitle">Master React and Next.js performance optimization with a comprehensive skill that provides 40+ proven rules for eliminating waterfalls, optimizing bundles, and improving rendering performance in your applications.</p>
|
||||
<div class="article-meta-full">
|
||||
<span class="read-time">8 min read</span>
|
||||
<div class="article-tags">
|
||||
<span class="tag">Claude Code</span>
|
||||
<span class="tag">Skill</span>
|
||||
<span class="tag">React</span>
|
||||
<span class="tag">Next.js</span>
|
||||
<span class="tag">Performance</span>
|
||||
<span class="tag">Optimization</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<article class="article-body">
|
||||
<img src="../assets/react-best-practices-skill-cover.svg" alt="React Best Practices Skill for Claude Code" class="article-cover" loading="lazy">
|
||||
|
||||
<div class="article-content-full">
|
||||
<h2>What is the React Best Practices Skill?</h2>
|
||||
|
||||
<p>The React Best Practices skill is a comprehensive performance optimization guide for React and Next.js applications. It provides Claude Code with 40+ actionable rules organized by impact level, helping you eliminate performance bottlenecks, reduce bundle sizes, and improve user experience.</p>
|
||||
|
||||
<p>This skill was created by Vercel Engineering and includes real-world examples and code comparisons for each optimization rule.</p>
|
||||
|
||||
<!-- Mermaid Diagram -->
|
||||
<div class="mermaid-diagram" style="background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 2rem; margin: 2rem 0; text-align: center;">
|
||||
<pre class="mermaid">
|
||||
graph TD
|
||||
A[🎯 Performance Issue] --> B[🤖 Claude + React Best Practices]
|
||||
B --> C[📊 Analyze Bottleneck]
|
||||
C --> D{Issue Type?}
|
||||
D -->|Critical| E[⚡ Eliminate Waterfalls]
|
||||
D -->|Critical| F[📦 Optimize Bundle Size]
|
||||
D -->|High| G[🚀 Server-Side Performance]
|
||||
D -->|Medium| H[🔄 Re-render Optimization]
|
||||
E --> I[✅ Optimized App]
|
||||
F --> I
|
||||
G --> I
|
||||
H --> I
|
||||
|
||||
style B fill:#F97316,stroke:#fff,color:#000
|
||||
style I fill:#00ff41,stroke:#fff,color:#000
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<h2>Why You Need This Skill</h2>
|
||||
|
||||
<p>Performance optimization can be overwhelming. With so many possible improvements, it's hard to know where to start. This skill solves that problem by:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>Prioritizing optimizations</strong> - Rules are organized by impact (CRITICAL, HIGH, MEDIUM, LOW)</li>
|
||||
<li><strong>Providing proven patterns</strong> - Each rule includes correct/incorrect code examples</li>
|
||||
<li><strong>Measuring impact</strong> - Clear guidelines help you understand the value of each optimization</li>
|
||||
<li><strong>Progressive context loading</strong> - The skill loads detailed guidelines on-demand to minimize token usage</li>
|
||||
</ul>
|
||||
|
||||
<h2>Key Optimization Categories</h2>
|
||||
|
||||
<h3>1. Eliminating Waterfalls (CRITICAL)</h3>
|
||||
<p>Waterfalls are a major performance killer. Each sequential await adds full network latency. This category teaches you to:</p>
|
||||
<ul>
|
||||
<li>Defer await until needed</li>
|
||||
<li>Use Promise.all() for parallel operations</li>
|
||||
<li>Prevent waterfall chains in API routes</li>
|
||||
<li>Implement strategic Suspense boundaries</li>
|
||||
</ul>
|
||||
|
||||
<pre><code class="language-typescript">// ❌ Sequential waterfalls (slow)
|
||||
const user = await fetchUser()
|
||||
const posts = await fetchPosts()
|
||||
const comments = await fetchComments()
|
||||
|
||||
// ✅ Parallel fetching (fast)
|
||||
const [user, posts, comments] = await Promise.all([
|
||||
fetchUser(),
|
||||
fetchPosts(),
|
||||
fetchComments()
|
||||
])</code></pre>
|
||||
|
||||
<h3>2. Bundle Size Optimization (CRITICAL)</h3>
|
||||
<p>Reducing initial bundle size improves Time to Interactive (TTI) and Largest Contentful Paint (LCP):</p>
|
||||
<ul>
|
||||
<li>Avoid barrel file imports</li>
|
||||
<li>Use dynamic imports for heavy components</li>
|
||||
<li>Defer non-critical third-party libraries</li>
|
||||
<li>Preload based on user intent</li>
|
||||
</ul>
|
||||
|
||||
<pre><code class="language-typescript">// ❌ Loads entire library
|
||||
import { Check } from 'lucide-react'
|
||||
|
||||
// ✅ Loads only what you need
|
||||
import Check from 'lucide-react/dist/esm/icons/check'</code></pre>
|
||||
|
||||
<h3>3. Server-Side Performance (HIGH)</h3>
|
||||
<p>Optimize React Server Components and data fetching:</p>
|
||||
<ul>
|
||||
<li>Cross-request LRU caching</li>
|
||||
<li>Minimize serialization at RSC boundaries</li>
|
||||
<li>Parallel data fetching with component composition</li>
|
||||
<li>Per-request deduplication with React.cache()</li>
|
||||
</ul>
|
||||
|
||||
<h3>4. Re-render Optimization (MEDIUM)</h3>
|
||||
<p>Reduce unnecessary re-renders to minimize wasted computation:</p>
|
||||
<ul>
|
||||
<li>Defer state reads to usage point</li>
|
||||
<li>Extract to memoized components</li>
|
||||
<li>Narrow effect dependencies</li>
|
||||
<li>Use transitions for non-urgent updates</li>
|
||||
</ul>
|
||||
|
||||
<h2>Installation</h2>
|
||||
|
||||
<p>Install the React Best Practices skill using the Claude Code Templates CLI:</p>
|
||||
|
||||
<pre><code class="language-bash">npx claude-code-templates@latest --skill=web-development/react-best-practices --yes</code></pre>
|
||||
|
||||
<p><strong>Where is the skill installed?</strong></p>
|
||||
<p>The skill is saved in <code>.claude/skills/react-best-practices/</code> in your project directory:</p>
|
||||
|
||||
<pre><code class="language-bash">your-project/
|
||||
├── .claude/
|
||||
│ └── skills/
|
||||
│ └── react-best-practices/ # ← Skill installed here
|
||||
│ ├── SKILL.md
|
||||
│ └── references/
|
||||
│ └── react-performance-guidelines.md
|
||||
├── src/
|
||||
│ └── components/
|
||||
├── package.json
|
||||
└── README.md</code></pre>
|
||||
|
||||
<h2>How to Use the Skill</h2>
|
||||
|
||||
<p>The skill uses progressive context loading - it provides a quick reference first, then loads detailed guidelines only when needed. This approach minimizes token usage while giving you access to comprehensive information.</p>
|
||||
|
||||
<h3>Basic Usage</h3>
|
||||
<pre><code class="language-bash"># Start Claude Code
|
||||
claude
|
||||
|
||||
# Request optimization help
|
||||
> Use the react-best-practices skill to optimize my ProductList component that's loading slowly</code></pre>
|
||||
|
||||
<p>Claude will:</p>
|
||||
<ol>
|
||||
<li>Load the skill's quick reference</li>
|
||||
<li>Analyze your component</li>
|
||||
<li>Identify the optimization category (e.g., waterfalls, bundle size)</li>
|
||||
<li>Load detailed guidelines for that specific category</li>
|
||||
<li>Provide specific recommendations with code examples</li>
|
||||
</ol>
|
||||
|
||||
<h2>Usage Examples</h2>
|
||||
|
||||
<h3>Example 1: Eliminate Data Fetching Waterfalls</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Use the react-best-practices skill to review this component and eliminate any waterfalls:
|
||||
|
||||
async function Dashboard() {
|
||||
const user = await fetchUser()
|
||||
const posts = await fetchPosts(user.id)
|
||||
const stats = await fetchStats(user.id)
|
||||
return <div>...</div>
|
||||
}</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> Claude identifies the sequential awaits, loads the "Eliminating Waterfalls" guidelines, and refactors the code to use Promise.all() for parallel fetching.</p>
|
||||
|
||||
<h3>Example 2: Reduce Bundle Size</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Use the react-best-practices skill to optimize the bundle size of my app. I'm importing lucide-react and lodash</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> Claude loads the "Bundle Size Optimization" guidelines and recommends direct imports instead of barrel imports, potentially saving hundreds of KB.</p>
|
||||
|
||||
<h3>Example 3: Optimize Re-renders</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Use the react-best-practices skill to fix unnecessary re-renders in my form component</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> Claude analyzes the component, loads "Re-render Optimization" rules, and suggests extracting to memoized components, narrowing dependencies, or using transitions.</p>
|
||||
|
||||
<h2>Key Metrics to Track</h2>
|
||||
|
||||
<p>The skill helps you optimize for these critical web performance metrics:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>Time to Interactive (TTI)</strong> - When page becomes fully interactive</li>
|
||||
<li><strong>Largest Contentful Paint (LCP)</strong> - When main content is visible</li>
|
||||
<li><strong>First Input Delay (FID)</strong> - Responsiveness to user interactions</li>
|
||||
<li><strong>Cumulative Layout Shift (CLS)</strong> - Visual stability</li>
|
||||
<li><strong>Bundle size</strong> - Initial JavaScript payload</li>
|
||||
<li><strong>Server response time</strong> - TTFB for server-rendered content</li>
|
||||
</ul>
|
||||
|
||||
<h2>Common Pitfalls Avoided</h2>
|
||||
|
||||
<p>The skill helps you avoid these common React performance mistakes:</p>
|
||||
|
||||
<ul>
|
||||
<li>❌ Using barrel imports from large libraries</li>
|
||||
<li>❌ Blocking parallel operations with sequential awaits</li>
|
||||
<li>❌ Re-rendering entire trees when only part needs updating</li>
|
||||
<li>❌ Loading analytics/tracking in the critical path</li>
|
||||
<li>❌ Mutating arrays with .sort() instead of .toSorted()</li>
|
||||
<li>❌ Creating RegExp or heavy objects inside render</li>
|
||||
</ul>
|
||||
|
||||
<h2>Progressive Context Loading</h2>
|
||||
|
||||
<p>One of the unique features of this skill is its progressive context loading system:</p>
|
||||
|
||||
<ol>
|
||||
<li><strong>Quick Reference</strong> - Loads first with critical priorities and common patterns</li>
|
||||
<li><strong>Category Overview</strong> - High-level description of optimization categories</li>
|
||||
<li><strong>Detailed Guidelines</strong> - Full documentation loaded on-demand from references folder</li>
|
||||
</ol>
|
||||
|
||||
<p>This approach ensures you get the information you need without overwhelming Claude's context window.</p>
|
||||
|
||||
<h2>Official Documentation</h2>
|
||||
<p>For more information about skills in Claude Code, see the <a href="https://code.claude.com/docs/en/skills?utm_source=aitmpl&utm_medium=referral&utm_campaign=blog" target="_blank">official skills documentation</a>.</p>
|
||||
|
||||
<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">© 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.30.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>
|
||||
|
||||
<!-- Mermaid for diagrams -->
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
theme: 'dark',
|
||||
themeVariables: {
|
||||
primaryColor: '#F97316',
|
||||
primaryTextColor: '#fff',
|
||||
primaryBorderColor: '#F97316',
|
||||
lineColor: '#00ff41',
|
||||
secondaryColor: '#1a1a1a',
|
||||
tertiaryColor: '#2d2d2d'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- 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();
|
||||
}
|
||||
|
||||
// Explore components banner hover effect
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const banner = document.querySelector('.explore-components-banner a');
|
||||
if (banner) {
|
||||
banner.addEventListener('mouseenter', () => {
|
||||
banner.style.background = '#00cc33';
|
||||
banner.style.transform = 'translateY(-2px)';
|
||||
});
|
||||
banner.addEventListener('mouseleave', () => {
|
||||
banner.style.background = '#00ff41';
|
||||
banner.style.transform = 'translateY(0)';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 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 'pre':
|
||||
return this.processCodeBlock(element);
|
||||
case 'div':
|
||||
if (element.classList.contains('code-block')) {
|
||||
return this.processCodeBlock(element.querySelector('pre'));
|
||||
}
|
||||
// Skip banner and other divs
|
||||
if (element.classList.contains('explore-components-banner')) {
|
||||
return '';
|
||||
}
|
||||
return element.textContent || '';
|
||||
case 'img':
|
||||
const alt = element.getAttribute('alt') || '';
|
||||
const src = element.getAttribute('src') || '';
|
||||
return ``;
|
||||
default:
|
||||
return element.textContent || '';
|
||||
}
|
||||
}
|
||||
|
||||
processList(listElement, marker) {
|
||||
const items = listElement.querySelectorAll('li');
|
||||
return Array.from(items)
|
||||
.map(item => `${marker} ${item.textContent}`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
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>
|
||||
@@ -0,0 +1,819 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Block API Keys & Secrets from Your Commits with Claude Code Hooks</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="Learn how to set up a PreToolUse hook in Claude Code that automatically detects and blocks commits containing API keys, passwords, and secrets.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://aitmpl.com/blog/security-hooks-secrets/">
|
||||
<meta property="og:title" content="Block API Keys & Secrets from Your Commits with Claude Code Hooks">
|
||||
<meta property="og:description" content="Learn how to set up a PreToolUse hook in Claude Code that automatically detects and blocks commits containing API keys, passwords, and secrets.">
|
||||
<meta property="og:image" content="https://aitmpl.com/blog/assets/security-hooks-secrets-cover.svg">
|
||||
<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="Security">
|
||||
<meta property="article:tag" content="Security">
|
||||
<meta property="article:tag" content="Hooks">
|
||||
<meta property="article:tag" content="Git">
|
||||
<meta property="article:tag" content="Secrets">
|
||||
<meta property="article:tag" content="Claude Code">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/blog/security-hooks-secrets/">
|
||||
<meta name="twitter:title" content="Block API Keys & Secrets from Your Commits with Claude Code Hooks">
|
||||
<meta name="twitter:description" content="Learn how to set up a PreToolUse hook in Claude Code that automatically detects and blocks commits containing API keys, passwords, and secrets.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/blog/assets/security-hooks-secrets-cover.svg">
|
||||
|
||||
<!-- Additional SEO -->
|
||||
<meta name="keywords" content="Claude Code Hooks, Secret Detection, API Key Protection, Git Security, PreToolUse Hook, Claude Code Security, Credential Leak Prevention, Secrets Scanner, Claude Code Templates, DevSecOps">
|
||||
<meta name="author" content="Claude Code Templates">
|
||||
<link rel="canonical" href="https://aitmpl.com/blog/security-hooks-secrets/">
|
||||
|
||||
<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": "Block API Keys & Secrets from Your Commits with Claude Code Hooks",
|
||||
"description": "Learn how to set up a PreToolUse hook in Claude Code that automatically detects and blocks commits containing API keys, passwords, and secrets.",
|
||||
"image": "https://aitmpl.com/blog/assets/security-hooks-secrets-cover.svg",
|
||||
"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/security-hooks-secrets/"
|
||||
},
|
||||
"keywords": "Claude Code Hooks, Secret Detection, API Key Protection, Git Security, PreToolUse Hook, Security",
|
||||
"wordCount": "1200",
|
||||
"articleSection": "Claude Code Hooks",
|
||||
"about": [
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Secret Detection"
|
||||
},
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Git Security"
|
||||
},
|
||||
{
|
||||
"@type": "SoftwareApplication",
|
||||
"name": "Claude Code",
|
||||
"applicationCategory": "DeveloperApplication"
|
||||
}
|
||||
]
|
||||
}
|
||||
</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">Block API Keys & Secrets from Your Commits with Claude Code Hooks</h1>
|
||||
<p class="article-subtitle">Prevent accidental credential leaks with a PreToolUse hook that scans your code for sensitive data before every commit.</p>
|
||||
<div class="article-meta-full">
|
||||
<span class="read-time">4 min read</span>
|
||||
<div class="article-tags">
|
||||
<span class="tag">Security</span>
|
||||
<span class="tag">Hooks</span>
|
||||
<span class="tag">Git</span>
|
||||
<span class="tag">Secrets</span>
|
||||
<span class="tag">Claude Code</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<article class="article-body">
|
||||
<img src="../assets/security-hooks-secrets-cover.svg" alt="Block API Keys & Secrets from Your Commits with Claude Code Hooks" class="article-cover" loading="lazy">
|
||||
|
||||
<div class="article-content-full">
|
||||
<h2>Installation</h2>
|
||||
|
||||
<p>Install the Secret Scanner Hook using the Claude Code Templates CLI:</p>
|
||||
|
||||
<pre><code class="language-bash">npx claude-code-templates@latest --hook security/secret-scanner</code></pre>
|
||||
|
||||
<p>This command automatically installs the hook in your project's <code>.claude/settings.json</code> and creates the detection script at <code>.claude/hooks/secret-scanner.py</code>.</p>
|
||||
|
||||
<div class="info-box">
|
||||
<strong>Want to understand how it works?</strong> Keep reading to learn what this hook does under the hood and why it's essential for your workflow.
|
||||
</div>
|
||||
|
||||
<h2>The Problem: Accidental Secret Exposure</h2>
|
||||
|
||||
<p>We've all been there. You're working fast, testing something locally, and suddenly you realize you've committed an API key, password, or secret token to your repository. By the time you notice, it might already be in your git history or worse—pushed to a public repo.</p>
|
||||
|
||||
<p>Claude Code hooks provide a deterministic way to catch these mistakes <strong>before</strong> they happen. Unlike relying on CLAUDE.md suggestions (which Claude might overlook), hooks execute as code every single time.</p>
|
||||
|
||||
<h2>How Claude Code Hooks Work</h2>
|
||||
|
||||
<p>Claude Code provides several hook events that run at different points in the workflow:</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Hook Event</th>
|
||||
<th>When It Runs</th>
|
||||
<th>Can Block?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>PreToolUse</strong></td>
|
||||
<td>Before tool execution</td>
|
||||
<td>Yes (exit code 2)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>PostToolUse</strong></td>
|
||||
<td>After tool completion</td>
|
||||
<td>No</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Stop</strong></td>
|
||||
<td>When Claude finishes responding</td>
|
||||
<td>Yes (can force continuation)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p>For secrets detection, we use <code>PreToolUse</code> with a matcher for <code>Edit|Write</code> operations. When exit code 2 is returned, the operation is blocked and Claude receives feedback about what to fix.</p>
|
||||
|
||||
<h2>The Secrets Detection Hook</h2>
|
||||
|
||||
<p>Here's the complete hook configuration that detects and blocks commits containing potential secrets:</p>
|
||||
|
||||
<h3>1. Add the Hook Configuration</h3>
|
||||
|
||||
<p>Add this to your <code>.claude/settings.json</code> file:</p>
|
||||
|
||||
<pre><code class="language-json">{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Edit|Write",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash $CLAUDE_PROJECT_DIR/.claude/hooks/detect-secrets.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<h3>2. Create the Detection Script</h3>
|
||||
|
||||
<p>Create the file <code>.claude/hooks/detect-secrets.sh</code>:</p>
|
||||
|
||||
<pre><code class="language-bash">#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Read hook input from stdin
|
||||
HOOK_INPUT=$(cat)
|
||||
|
||||
# Extract file path and new content from the hook payload
|
||||
FILE=$(echo "$HOOK_INPUT" | jq -r '.tool_input.file_path // empty')
|
||||
NEW_CONTENT=$(echo "$HOOK_INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty')
|
||||
|
||||
# Skip if no content to check
|
||||
if [ -z "$NEW_CONTENT" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Define patterns for common secrets
|
||||
SECRET_PATTERNS=(
|
||||
# API Keys (generic)
|
||||
'api[_-]?key["\s:=]+["\x27]?[a-zA-Z0-9_-]{20,}["\x27]?'
|
||||
# AWS Keys
|
||||
'AKIA[0-9A-Z]{16}'
|
||||
'aws[_-]?secret[_-]?access[_-]?key'
|
||||
# GitHub Tokens
|
||||
'ghp_[a-zA-Z0-9]{36}'
|
||||
'github[_-]?token'
|
||||
# Stripe Keys
|
||||
'sk_live_[a-zA-Z0-9]{24,}'
|
||||
'sk_test_[a-zA-Z0-9]{24,}'
|
||||
# Generic secrets
|
||||
'password["\s:=]+["\x27][^\s"'\'']{8,}["\x27]'
|
||||
'secret["\s:=]+["\x27][^\s"'\'']{8,}["\x27]'
|
||||
'token["\s:=]+["\x27][a-zA-Z0-9_-]{20,}["\x27]'
|
||||
'private[_-]?key'
|
||||
'client[_-]?secret'
|
||||
# Database connection strings
|
||||
'mongodb(\+srv)?://[^\s]+'
|
||||
'postgres(ql)?://[^\s]+'
|
||||
'mysql://[^\s]+'
|
||||
'redis://[^\s]+'
|
||||
)
|
||||
|
||||
# Check for each pattern
|
||||
for pattern in "${SECRET_PATTERNS[@]}"; do
|
||||
if echo "$NEW_CONTENT" | grep -iE "$pattern" &>/dev/null; then
|
||||
echo "BLOCKED: Potential secret detected in $FILE" >&2
|
||||
echo "" >&2
|
||||
echo "Pattern matched: $pattern" >&2
|
||||
echo "" >&2
|
||||
echo "Please use environment variables instead:" >&2
|
||||
echo " 1. Store the secret in .env (add .env to .gitignore)" >&2
|
||||
echo " 2. Reference it via process.env.YOUR_SECRET" >&2
|
||||
echo "" >&2
|
||||
exit 2
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0</code></pre>
|
||||
|
||||
<h3>3. Make the Script Executable</h3>
|
||||
|
||||
<pre><code class="language-bash">chmod +x .claude/hooks/detect-secrets.sh</code></pre>
|
||||
|
||||
<h2>Project Structure</h2>
|
||||
|
||||
<p>After setup, your project should have this structure:</p>
|
||||
|
||||
<pre><code class="language-text">your-project/
|
||||
├── .claude/
|
||||
│ ├── hooks/
|
||||
│ │ └── detect-secrets.sh
|
||||
│ └── settings.json
|
||||
├── .gitignore
|
||||
└── src/</code></pre>
|
||||
|
||||
<h2>What Gets Detected</h2>
|
||||
|
||||
<p>The hook scans for common patterns including:</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Example Pattern</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>AWS Access Keys</td>
|
||||
<td><code>AKIA...</code> (20 chars)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>GitHub Tokens</td>
|
||||
<td><code>ghp_...</code> (40 chars)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Stripe Keys</td>
|
||||
<td><code>sk_live_...</code>, <code>sk_test_...</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Generic API Keys</td>
|
||||
<td><code>api_key = "..."</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Passwords</td>
|
||||
<td><code>password: "..."</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Database URLs</td>
|
||||
<td><code>mongodb://...</code>, <code>postgres://...</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>How It Works in Practice</h2>
|
||||
|
||||
<p>When you ask Claude Code to edit a file that would contain a secret:</p>
|
||||
|
||||
<div class="warning-box">
|
||||
<strong>BLOCKED:</strong> Potential secret detected in src/config.js<br><br>
|
||||
Pattern matched: api[_-]?key["\s:=]+...<br><br>
|
||||
Please use environment variables instead:<br>
|
||||
1. Store the secret in .env (add .env to .gitignore)<br>
|
||||
2. Reference it via process.env.YOUR_SECRET
|
||||
</div>
|
||||
|
||||
<p>Claude receives this feedback and automatically adjusts its approach, typically suggesting environment variables instead.</p>
|
||||
|
||||
<h2>Advanced: Adding More Patterns</h2>
|
||||
|
||||
<p>You can extend the <code>SECRET_PATTERNS</code> array to catch company-specific patterns:</p>
|
||||
|
||||
<pre><code class="language-bash"># Add your company's internal patterns
|
||||
SECRET_PATTERNS+=(
|
||||
'MYCOMPANY_[A-Z0-9]{32}'
|
||||
'internal[_-]?api[_-]?key'
|
||||
'prod[_-]?secret'
|
||||
)</code></pre>
|
||||
|
||||
<h2>Difference: CLAUDE.md vs Hooks</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Approach</th>
|
||||
<th>Reliability</th>
|
||||
<th>When to Use</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>CLAUDE.md</td>
|
||||
<td>Suggestions (may be ignored)</td>
|
||||
<td>Coding style, preferences</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hooks</td>
|
||||
<td>Enforcement (always runs)</td>
|
||||
<td>Security, compliance, automation</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="info-box">
|
||||
<strong>Rule of thumb:</strong> If it's critical and must never be violated, use a hook. If it's a preference or guideline, put it in CLAUDE.md.
|
||||
</div>
|
||||
|
||||
<h2>Conclusion</h2>
|
||||
|
||||
<p>With this hook in place, you've added a deterministic security layer to your Claude Code workflow. Every file edit and write operation gets scanned for secrets before it can be committed.</p>
|
||||
|
||||
<p>This is just one example of what PreToolUse hooks can do. You can apply the same pattern for:</p>
|
||||
|
||||
<ul>
|
||||
<li>Blocking edits to production files</li>
|
||||
<li>Enforcing code formatting</li>
|
||||
<li>Preventing commits with TODO comments</li>
|
||||
<li>Validating file naming conventions</li>
|
||||
</ul>
|
||||
|
||||
<div class="success-box">
|
||||
<strong>Remember:</strong> CLAUDE.md rules are suggestions. Hooks are enforcement. For security, always use hooks.
|
||||
</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">© 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() {
|
||||
const preElements = document.querySelectorAll('.article-content-full pre:not(.converted)');
|
||||
|
||||
preElements.forEach(pre => {
|
||||
const code = pre.querySelector('code');
|
||||
if (!code) return;
|
||||
|
||||
const language = this.detectLanguage(code);
|
||||
const isTerminal = language === 'bash' || language === 'terminal';
|
||||
|
||||
const codeBlock = document.createElement('div');
|
||||
codeBlock.className = isTerminal ? 'code-block terminal-block' : 'code-block';
|
||||
|
||||
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);
|
||||
|
||||
const newPre = pre.cloneNode(true);
|
||||
newPre.classList.add('converted');
|
||||
|
||||
codeBlock.appendChild(header);
|
||||
codeBlock.appendChild(newPre);
|
||||
|
||||
pre.parentNode.replaceChild(codeBlock, pre);
|
||||
});
|
||||
}
|
||||
|
||||
detectLanguage(codeElement) {
|
||||
const className = codeElement.className;
|
||||
if (className.includes('language-')) {
|
||||
return className.match(/language-(\w+)/)[1];
|
||||
}
|
||||
|
||||
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 {
|
||||
let textToCopy = code.textContent;
|
||||
|
||||
if (codeBlock.classList.contains('terminal-block')) {
|
||||
textToCopy = this.cleanTerminalOutput(textToCopy);
|
||||
}
|
||||
|
||||
await navigator.clipboard.writeText(textToCopy);
|
||||
|
||||
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');
|
||||
|
||||
setTimeout(() => {
|
||||
button.innerHTML = originalContent;
|
||||
button.classList.remove('copied');
|
||||
}, 2000);
|
||||
|
||||
} catch (err) {
|
||||
console.error('Failed to copy code:', err);
|
||||
|
||||
const selection = window.getSelection();
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(code);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cleanTerminalOutput(text) {
|
||||
return text
|
||||
.split('\n')
|
||||
.map(line => {
|
||||
line = line.replace(/^[\$❯]\s*/, '').replace(/^claude-code>\s*/, '');
|
||||
|
||||
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() !== '')
|
||||
.join('\n')
|
||||
.trim();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize when DOM is loaded
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => new CodeCopy());
|
||||
} else {
|
||||
new CodeCopy();
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
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>
|
||||
Copied!
|
||||
`;
|
||||
button.classList.add('copied');
|
||||
|
||||
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 content = document.querySelector('.article-content-full');
|
||||
|
||||
let markdown = `# ${title}\n\n`;
|
||||
|
||||
if (subtitle) {
|
||||
markdown += `${subtitle}\n\n`;
|
||||
}
|
||||
|
||||
if (!content) return markdown;
|
||||
|
||||
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'));
|
||||
}
|
||||
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 ``;
|
||||
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 = '';
|
||||
|
||||
const headerRow = rows[0];
|
||||
const headers = headerRow.querySelectorAll('th, td');
|
||||
const headerText = Array.from(headers).map(h => h.textContent.trim()).join(' | ');
|
||||
markdown += `| ${headerText} |\n`;
|
||||
|
||||
const separator = Array.from(headers).map(() => '---').join(' | ');
|
||||
markdown += `| ${separator} |\n`;
|
||||
|
||||
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;
|
||||
|
||||
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>
|
||||
@@ -0,0 +1,792 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Simple Notifications Hook for Claude Code: Desktop Alerts for macOS & Linux</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="Install the Simple Notifications Hook for Claude Code to get instant desktop notifications when operations complete. AI-powered automation hook for macOS and Linux systems.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://aitmpl.com/blog/simple-notifications-hook/">
|
||||
<meta property="og:title" content="Simple Notifications Hook for Claude Code: Desktop Alerts">
|
||||
<meta property="og:description" content="Get instant desktop notifications when Claude Code operations complete. Cross-platform hook for macOS and Linux.">
|
||||
<meta property="og:image" content="https://aitmpl.com/blog/assets/simple-notifications-hook-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="Automation">
|
||||
<meta property="article:tag" content="Claude Code">
|
||||
<meta property="article:tag" content="Hooks">
|
||||
<meta property="article:tag" content="Notifications">
|
||||
<meta property="article:tag" content="Automation">
|
||||
<meta property="article:tag" content="Desktop Alerts">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/blog/simple-notifications-hook/">
|
||||
<meta name="twitter:title" content="Simple Notifications Hook for Claude Code: Desktop Alerts">
|
||||
<meta name="twitter:description" content="Get instant desktop notifications when Claude Code operations complete.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/blog/assets/simple-notifications-hook-cover.png">
|
||||
|
||||
<!-- Additional SEO -->
|
||||
<meta name="keywords" content="Claude Code Hook, Simple Notifications, Claude Code Desktop Alerts, macOS Notifications, Linux Notifications, AI Automation, Claude Code Hooks, Desktop Notifications, PostToolUse Hook, Claude Code Alerts">
|
||||
<meta name="author" content="Claude Code Templates">
|
||||
<link rel="canonical" href="https://aitmpl.com/blog/simple-notifications-hook/">
|
||||
|
||||
<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": "Simple Notifications Hook for Claude Code: Desktop Alerts for macOS & Linux",
|
||||
"description": "Install the Simple Notifications Hook for Claude Code to get instant desktop notifications when operations complete. AI-powered automation hook for macOS and Linux systems.",
|
||||
"image": "https://aitmpl.com/blog/assets/simple-notifications-hook-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/simple-notifications-hook/"
|
||||
},
|
||||
"keywords": "Claude Code Hook, Simple Notifications, Desktop Alerts, macOS, Linux, Automation, Hooks",
|
||||
"wordCount": "850",
|
||||
"articleSection": "Claude Code Hooks",
|
||||
"about": [
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Claude Code"
|
||||
},
|
||||
{
|
||||
"@type": "Thing",
|
||||
"name": "Automation Hooks"
|
||||
},
|
||||
{
|
||||
"@type": "SoftwareApplication",
|
||||
"name": "Claude Code",
|
||||
"applicationCategory": "DeveloperApplication"
|
||||
}
|
||||
]
|
||||
}
|
||||
</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">Simple Notifications Hook for Claude Code: Desktop Alerts for macOS & Linux</h1>
|
||||
<p class="article-subtitle">Learn how to install and use the Simple Notifications Hook for Claude Code to get instant desktop notifications when operations complete.</p>
|
||||
<div class="article-meta-full">
|
||||
<span class="read-time">4 min read</span>
|
||||
<div class="article-tags">
|
||||
<span class="tag">Claude Code</span>
|
||||
<span class="tag">Hooks</span>
|
||||
<span class="tag">Notifications</span>
|
||||
<span class="tag">macOS</span>
|
||||
<span class="tag">Linux</span>
|
||||
<span class="tag">Automation</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<article class="article-body">
|
||||
<img src="../assets/simple-notifications-hook-cover.png" alt="Simple Notifications Hook for Claude Code" class="article-cover" loading="lazy">
|
||||
|
||||
<div class="article-content-full">
|
||||
<h2>What is the Simple Notifications Hook?</h2>
|
||||
|
||||
<p>The <strong>Simple Notifications Hook</strong> is a Claude Code automation hook that sends instant desktop notifications when Claude Code operations complete. It works seamlessly on both macOS and Linux systems, using native notification systems (osascript for macOS, notify-send for Linux) to alert you when tasks finish.</p>
|
||||
|
||||
<!-- Mermaid Diagram -->
|
||||
<div class="mermaid-diagram" style="background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 2rem; margin: 2rem 0; text-align: center;">
|
||||
<pre class="mermaid">
|
||||
graph LR
|
||||
A[📝 Tool Execution] --> B[🪝 Simple Notifications Hook]
|
||||
B --> C[🔔 Desktop Alert]
|
||||
C --> D[✅ You Get Notified]
|
||||
|
||||
style B fill:#F97316,stroke:#fff,color:#000
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<h2>Key Capabilities</h2>
|
||||
<ul>
|
||||
<li><strong>Instant Notifications</strong> - Get desktop alerts immediately when Claude Code operations complete</li>
|
||||
<li><strong>Cross-Platform</strong> - Works on both macOS (osascript) and Linux (notify-send) systems</li>
|
||||
<li><strong>PostToolUse Hook</strong> - Automatically triggers after any tool execution in Claude Code</li>
|
||||
<li><strong>No Configuration Needed</strong> - Works out of the box with native system notifications</li>
|
||||
<li><strong>Universal Matcher</strong> - Monitors all tools ("*" matcher) for completion</li>
|
||||
<li><strong>Lightweight</strong> - Zero performance overhead, runs only after tool completion</li>
|
||||
<li><strong>Non-Intrusive</strong> - Notifications appear as standard OS alerts</li>
|
||||
</ul>
|
||||
|
||||
<h2>Installation</h2>
|
||||
|
||||
<p>Install the Simple Notifications Hook using the Claude Code Templates CLI:</p>
|
||||
|
||||
<pre><code class="language-bash">npx claude-code-templates@latest --hook automation/simple-notifications</code></pre>
|
||||
|
||||
<div class="info-box">
|
||||
<strong>Want to understand how it works?</strong> Keep reading to learn what this hook does under the hood and why it's essential for your workflow.
|
||||
</div>
|
||||
|
||||
<p><strong>Where is the hook installed?</strong></p>
|
||||
<p>The hook is saved in <code>.claude/hooks/simple-notifications.json</code> in your project directory:</p>
|
||||
|
||||
<pre><code class="language-bash">your-project/
|
||||
├── .claude/
|
||||
│ └── hooks/
|
||||
│ └── simple-notifications.json # ← Hook installed here
|
||||
├── src/
|
||||
│ └── components/
|
||||
├── package.json
|
||||
└── README.md</code></pre>
|
||||
|
||||
<h2>How to Use the Hook</h2>
|
||||
|
||||
<p>The Simple Notifications Hook activates automatically whenever Claude Code completes a tool operation. You don't need to explicitly request it - it works in the background!</p>
|
||||
|
||||
<pre><code class="language-bash"># Start Claude Code
|
||||
claude
|
||||
|
||||
# Use any tool or command - notifications happen automatically
|
||||
> Read the package.json file
|
||||
> Edit src/index.js and add a console.log
|
||||
> Run npm test</code></pre>
|
||||
|
||||
<p>After each operation completes, you'll receive a desktop notification showing which tool was executed.</p>
|
||||
|
||||
<h2>Usage Examples</h2>
|
||||
|
||||
<h3>Example 1: File Operations</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Read all TypeScript files in the src directory and analyze for bugs</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> When Claude Code finishes reading files, you'll get a desktop notification: "Tool: Read completed"</p>
|
||||
|
||||
<h3>Example 2: Code Editing</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Update the authentication logic in src/auth.ts to use JWT tokens</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> After the Edit tool completes, you'll receive: "Tool: Edit completed"</p>
|
||||
|
||||
<h3>Example 3: Running Commands</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Run the test suite and fix any failing tests</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> When the Bash tool finishes running tests, notification shows: "Tool: Bash completed"</p>
|
||||
|
||||
<h2>System Requirements</h2>
|
||||
|
||||
<p>The Simple Notifications Hook works on:</p>
|
||||
<ul>
|
||||
<li><strong>macOS</strong> - Uses <code>osascript</code> (built-in, no installation needed)</li>
|
||||
<li><strong>Linux</strong> - Uses <code>notify-send</code> (usually pre-installed, or install via package manager)</li>
|
||||
</ul>
|
||||
|
||||
<p><strong>Installing notify-send on Linux:</strong></p>
|
||||
<pre><code class="language-bash"># Debian/Ubuntu
|
||||
sudo apt-get install libnotify-bin
|
||||
|
||||
# Fedora/RHEL
|
||||
sudo dnf install libnotify
|
||||
|
||||
# Arch Linux
|
||||
sudo pacman -S libnotify</code></pre>
|
||||
|
||||
<h2>How It Works</h2>
|
||||
|
||||
<p>The hook uses Claude Code's <strong>PostToolUse</strong> event system:</p>
|
||||
<ul>
|
||||
<li><strong>Matcher: "*"</strong> - Monitors ALL tool executions</li>
|
||||
<li><strong>Trigger</strong> - Activates after each tool completes</li>
|
||||
<li><strong>Command</strong> - Runs a shell command to display notification</li>
|
||||
<li><strong>Variable: $CLAUDE_TOOL_NAME</strong> - Dynamically shows which tool was used</li>
|
||||
</ul>
|
||||
|
||||
<p>The hook automatically detects your operating system and uses the appropriate notification command.</p>
|
||||
|
||||
<h2>Benefits of Desktop Notifications</h2>
|
||||
<ul>
|
||||
<li><strong>Stay Informed</strong> - Know exactly when long-running operations finish</li>
|
||||
<li><strong>Multitask Efficiently</strong> - Work on other tasks while Claude Code processes</li>
|
||||
<li><strong>Immediate Feedback</strong> - Get instant confirmation of completed actions</li>
|
||||
<li><strong>Better Workflow</strong> - Reduce context switching and checking back on terminal</li>
|
||||
<li><strong>Track Progress</strong> - Monitor multiple operations across different projects</li>
|
||||
</ul>
|
||||
|
||||
<h2>Customization Options</h2>
|
||||
|
||||
<p>You can customize the hook by editing <code>.claude/hooks/simple-notifications.json</code>:</p>
|
||||
|
||||
<pre><code class="language-json">{
|
||||
"description": "Send desktop notifications with custom messages",
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Read|Write|Edit", // Only specific tools
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "notify-send 'Custom Title' 'File operation: $CLAUDE_TOOL_NAME done!'"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
|
||||
<h3>Notifications not appearing on macOS</h3>
|
||||
<p>Check that terminal has notification permissions:</p>
|
||||
<ol>
|
||||
<li>Open System Preferences → Notifications & Focus</li>
|
||||
<li>Find your terminal app (Terminal.app, iTerm2, etc.)</li>
|
||||
<li>Enable "Allow Notifications"</li>
|
||||
</ol>
|
||||
|
||||
<h3>Notifications not appearing on Linux</h3>
|
||||
<p>Ensure notify-send is installed:</p>
|
||||
<pre><code class="language-bash"># Check if notify-send exists
|
||||
which notify-send
|
||||
|
||||
# Test notification manually
|
||||
notify-send "Test" "This is a test notification"</code></pre>
|
||||
|
||||
<h2>Official Documentation</h2>
|
||||
<p>For more information about hooks in Claude Code, see the <a href="https://code.claude.com/docs/en/hooks?utm_source=aitmpl&utm_medium=referral&utm_campaign=blog" target="_blank">official hooks documentation</a>.</p>
|
||||
|
||||
<div class="success-box">
|
||||
<strong>Key takeaway:</strong> Desktop notifications keep you informed without context-switching. Install this hook once and never miss a completed operation again.
|
||||
</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">© 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();
|
||||
}
|
||||
|
||||
// Explore components banner hover effect
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const banner = document.querySelector('.explore-components-banner a');
|
||||
if (banner) {
|
||||
banner.addEventListener('mouseenter', () => {
|
||||
banner.style.background = '#00cc33';
|
||||
banner.style.transform = 'translateY(-2px)';
|
||||
});
|
||||
banner.addEventListener('mouseleave', () => {
|
||||
banner.style.background = '#00ff41';
|
||||
banner.style.transform = 'translateY(0)';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 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 ``;
|
||||
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>
|
||||
|
||||
<!-- Mermaid Diagram Support -->
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
theme: 'dark',
|
||||
themeVariables: {
|
||||
primaryColor: '#F97316',
|
||||
primaryTextColor: '#fff',
|
||||
primaryBorderColor: '#F97316',
|
||||
lineColor: '#00ff41',
|
||||
secondaryColor: '#1a1a1a',
|
||||
tertiaryColor: '#2d2d2d'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,749 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Claude Code Skills for Custom Workflows: AI Automation Expert 2025</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="Install Claude Code Skills to create custom AI workflows with progressive context loading. Build reusable automation for your development workflow with slash commands.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://aitmpl.com/blog/skills-creator/">
|
||||
<meta property="og:title" content="Claude Code Skills for Custom Workflows: AI Automation">
|
||||
<meta property="og:description"
|
||||
content="Install Claude Code Skills to create custom AI workflows with progressive context loading. Build reusable automation for your development workflow.">
|
||||
<meta property="og:image" content="https://aitmpl.com/blog/assets/skills-creator-cover.png">
|
||||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="630">
|
||||
<meta property="article:published_time" content="2025-01-15T10:00:00Z">
|
||||
<meta property="article:author" content="Claude Code Templates">
|
||||
<meta property="article:section" content="Skills">
|
||||
<meta property="article:tag" content="Claude Code">
|
||||
<meta property="article:tag" content="Skills">
|
||||
<meta property="article:tag" content="Workflows">
|
||||
<meta property="article:tag" content="Automation">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/blog/skills-creator/">
|
||||
<meta name="twitter:title" content="Claude Code Skills for Custom Workflows: AI Automation">
|
||||
<meta name="twitter:description"
|
||||
content="Install Claude Code Skills to create custom AI workflows with progressive context loading. Build reusable automation for your development workflow.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/blog/assets/skills-creator-cover.png">
|
||||
|
||||
<!-- Additional SEO -->
|
||||
<meta name="keywords"
|
||||
content="Claude Code skills, custom AI workflows, Claude Code automation, progressive context loading, slash commands, AI development workflow, reusable skills, Claude Code productivity">
|
||||
<meta name="author" content="Claude Code Templates">
|
||||
<link rel="canonical" href="https://aitmpl.com/blog/skills-creator/">
|
||||
|
||||
<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": "Claude Code Skills for Custom Workflows: AI Automation Expert 2025",
|
||||
"description": "Install Claude Code Skills to create custom AI workflows with progressive context loading. Build reusable automation for your development workflow with slash commands.",
|
||||
"image": "https://aitmpl.com/blog/assets/skills-creator-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/skills-creator/"
|
||||
},
|
||||
"keywords": "Claude Code skills, custom AI workflows, automation, progressive context loading",
|
||||
"wordCount": "800",
|
||||
"articleSection": "Claude Code Skills",
|
||||
"about": [
|
||||
{"@type": "Thing", "name": "Claude Code"},
|
||||
{"@type": "Thing", "name": "Skills"},
|
||||
{"@type": "Thing", "name": "Workflow Automation"},
|
||||
{"@type": "Thing", "name": "Progressive Loading"},
|
||||
{"@type": "SoftwareApplication", "name": "Claude Code", "applicationCategory": "DeveloperApplication"}
|
||||
]
|
||||
}
|
||||
</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 Skills for Custom Workflows: AI Automation Expert</h1>
|
||||
<p class="article-subtitle">Learn how to install and use Claude Code Skills to create custom AI
|
||||
workflows with progressive context loading and slash command automation.</p>
|
||||
<div class="article-meta-full">
|
||||
<span class="read-time">4 min read</span>
|
||||
<div class="article-tags">
|
||||
<span class="tag">Claude Code</span>
|
||||
<span class="tag">Skills</span>
|
||||
<span class="tag">Workflows</span>
|
||||
<span class="tag">Automation</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<article class="article-body">
|
||||
<img src="../assets/skills-creator-cover.png" alt="Claude Code Skills System" class="article-cover"
|
||||
loading="lazy">
|
||||
|
||||
<div class="article-content-full">
|
||||
<h2>What are Claude Code Skills?</h2>
|
||||
|
||||
<p>Claude Code Skills are modular capabilities that extend Claude's functionality automatically. Skills
|
||||
package instructions, metadata, and optional resources (scripts, templates, documentation) that
|
||||
Claude uses on-demand when relevant to your request. Unlike commands that you invoke explicitly,
|
||||
Skills work behind the scenes - Claude automatically loads them when they match your task.</p>
|
||||
|
||||
<!-- Mermaid Diagram -->
|
||||
<div class="mermaid-diagram"
|
||||
style="background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 2rem; margin: 2rem 0; text-align: center;">
|
||||
<pre class="mermaid">
|
||||
graph TB
|
||||
A[👤 User Request] --> B[🔍 Skill Triggered]
|
||||
B --> C[📋 Level 1: Metadata Always Loaded]
|
||||
C --> D[📚 Level 2: SKILL.md Instructions]
|
||||
D --> E[📦 Level 3: Resources & Scripts]
|
||||
E --> F[✅ Task Complete]
|
||||
|
||||
style B fill:#F97316,stroke:#fff,color:#000
|
||||
style D fill:#F97316,stroke:#fff,color:#000
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<h2>Key Capabilities</h2>
|
||||
<ul>
|
||||
<li><strong>Automatic Activation</strong> - Claude triggers Skills automatically when they match
|
||||
your request</li>
|
||||
<li><strong>Progressive Context Loading</strong> - Three-level loading: metadata (always),
|
||||
instructions (when triggered), resources (as needed)</li>
|
||||
<li><strong>Domain Expertise Packaging</strong> - Bundle workflows, best practices, scripts, and
|
||||
reference materials</li>
|
||||
<li><strong>Zero Token Cost When Idle</strong> - Skills only consume context when actively used</li>
|
||||
<li><strong>Filesystem-Based Architecture</strong> - Skills exist as directories with SKILL.md and
|
||||
optional resources</li>
|
||||
<li><strong>Executable Scripts Support</strong> - Include Python/Node scripts that run without
|
||||
loading into context</li>
|
||||
</ul>
|
||||
|
||||
<h2>Installation</h2>
|
||||
|
||||
<p>Skills are typically created manually or installed from templates. Here's how to use the example
|
||||
skills-creator from Claude Code Templates:</p>
|
||||
|
||||
<pre><code class="language-bash">npx claude-code-templates@latest --skill=development/skill-creator</code></pre>
|
||||
|
||||
<p><strong>Where are skills installed?</strong></p>
|
||||
|
||||
<p>Skills are saved in <code>.claude/skills/</code> directory in your project:</p>
|
||||
|
||||
<pre><code class="language-bash">your-project/
|
||||
├── .claude/
|
||||
│ └── skills/
|
||||
│ └── my-skill/
|
||||
│ ├── SKILL.md # ← Required skill instructions
|
||||
│ └── scripts/ # Optional executable scripts
|
||||
│ └── helper.py # Example script
|
||||
├── src/
|
||||
│ └── components/
|
||||
├── package.json
|
||||
└── README.md</code></pre>
|
||||
|
||||
<h2>How Skills Work</h2>
|
||||
|
||||
<p>Skills activate automatically when Claude detects they're relevant to your request. You don't invoke
|
||||
them explicitly - just describe what you need, and Claude uses the appropriate Skill behind the
|
||||
scenes.</p>
|
||||
|
||||
<pre><code class="language-bash"># Start Claude Code
|
||||
claude
|
||||
|
||||
# Skills activate automatically based on your request
|
||||
> Review this code for security vulnerabilities
|
||||
|
||||
# Claude automatically uses the code-review Skill if installed</code></pre>
|
||||
|
||||
<p><strong>Progressive Loading:</strong> Skills load in three levels - metadata (always loaded),
|
||||
instructions (when triggered), and resources (as needed). This means you can install many Skills
|
||||
without performance penalty.</p>
|
||||
|
||||
<h2>Usage Examples</h2>
|
||||
|
||||
<h3>Example 1: PDF Processing Skill</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Extract text from this PDF and create a summary
|
||||
|
||||
# Claude automatically detects PDF task and loads pdf-processing Skill</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> The Skill provides Claude with PDF extraction expertise. Claude reads
|
||||
SKILL.md instructions, uses bundled pdfplumber scripts, and delivers extracted text with summary.
|
||||
Advanced features like form-filling documentation (FORMS.md) remain unloaded unless needed.</p>
|
||||
|
||||
<h3>Example 2: Database Schema Skill</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Write a query to get all users with pending orders
|
||||
|
||||
# Claude detects database query and loads database-schema Skill</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> Skill loads with your database schema as reference material. Claude accesses
|
||||
table definitions, relationships, and example queries from bundled files. The schema file only
|
||||
enters context if Claude needs to reference it - otherwise it stays on the filesystem.</p>
|
||||
|
||||
<h3>Example 3: API Integration Skill</h3>
|
||||
<pre><code class="language-bash">claude
|
||||
|
||||
> Show me how to authenticate with the Stripe API
|
||||
|
||||
# Claude recognizes API integration task and loads stripe-api Skill</code></pre>
|
||||
|
||||
<p><strong>Result:</strong> Skill provides Stripe API documentation, authentication patterns, and
|
||||
example code. Claude can execute bundled test scripts to validate API calls without loading script
|
||||
code into context - only the output appears.</p>
|
||||
|
||||
<h2>Official Documentation</h2>
|
||||
|
||||
<p>For more information about Skills in Claude Code, see the <a
|
||||
href="https://code.claude.com/docs/en/skills?utm_source=aitmpl&utm_medium=referral&utm_campaign=blog"
|
||||
target="_blank">official documentation</a>.</p>
|
||||
|
||||
<div class="success-box">
|
||||
<strong>Key takeaway:</strong> Skills give Claude Code domain expertise without wasting context tokens. Install them once, and Claude automatically uses them when relevant.
|
||||
</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">© 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();
|
||||
}
|
||||
|
||||
// Explore components banner hover effect
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const banner = document.querySelector('.explore-components-banner a');
|
||||
if (banner) {
|
||||
banner.addEventListener('mouseenter', () => {
|
||||
banner.style.background = '#00cc33';
|
||||
banner.style.transform = 'translateY(-2px)';
|
||||
});
|
||||
banner.addEventListener('mouseleave', () => {
|
||||
banner.style.background = '#00ff41';
|
||||
banner.style.transform = 'translateY(0)';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 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'));
|
||||
}
|
||||
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 ``;
|
||||
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>
|
||||
|
||||
<!-- Mermaid Diagram Support -->
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
theme: 'dark',
|
||||
themeVariables: {
|
||||
primaryColor: '#F97316',
|
||||
primaryTextColor: '#fff',
|
||||
primaryBorderColor: '#F97316',
|
||||
lineColor: '#00ff41',
|
||||
secondaryColor: '#1a1a1a',
|
||||
tertiaryColor: '#2d2d2d'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,849 @@
|
||||
<!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">© 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 ``;
|
||||
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>
|
||||
@@ -0,0 +1,771 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Monitor Vercel Deployments in Real-Time with Claude Code Statuslines</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="Add a real-time Vercel deployment monitor to your Claude Code terminal. See build status, time since last deploy, and click directly to your deployment URL.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://aitmpl.com/blog/vercel-deployment-monitor-statusline/">
|
||||
<meta property="og:title" content="Monitor Vercel Deployments in Real-Time with Claude Code Statuslines">
|
||||
<meta property="og:description" content="Add a real-time Vercel deployment monitor to your Claude Code terminal. See build status, time since last deploy, and click directly to your deployment URL.">
|
||||
<meta property="og:image" content="https://aitmpl.com/blog/assets/vercel-deployment-monitor-statusline-cover.svg">
|
||||
<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="Monitoring">
|
||||
<meta property="article:tag" content="Vercel">
|
||||
<meta property="article:tag" content="Statusline">
|
||||
<meta property="article:tag" content="Deployment">
|
||||
<meta property="article:tag" content="Monitoring">
|
||||
<meta property="article:tag" content="DevOps">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/blog/vercel-deployment-monitor-statusline/">
|
||||
<meta name="twitter:title" content="Monitor Vercel Deployments in Real-Time with Claude Code Statuslines">
|
||||
<meta name="twitter:description" content="Add a real-time Vercel deployment monitor to your Claude Code terminal. See build status, time since last deploy, and click directly to your deployment URL.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/blog/assets/vercel-deployment-monitor-statusline-cover.svg">
|
||||
|
||||
<!-- Additional SEO -->
|
||||
<meta name="keywords" content="Vercel Deployment Monitor, Claude Code Statusline, Vercel Status, Deployment Monitoring, Claude Code Settings, DevOps, Vercel API, Real-Time Status, Claude Code Templates">
|
||||
<meta name="author" content="Claude Code Templates">
|
||||
<link rel="canonical" href="https://aitmpl.com/blog/vercel-deployment-monitor-statusline/">
|
||||
|
||||
<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 -->
|
||||
<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": "Monitor Vercel Deployments in Real-Time with Claude Code Statuslines",
|
||||
"description": "Add a real-time Vercel deployment monitor to your Claude Code terminal. See build status, time since last deploy, and click directly to your deployment URL.",
|
||||
"image": "https://aitmpl.com/blog/assets/vercel-deployment-monitor-statusline-cover.svg",
|
||||
"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/vercel-deployment-monitor-statusline/"
|
||||
},
|
||||
"keywords": "Vercel, Statusline, Deployment, Monitoring, DevOps, Claude Code",
|
||||
"articleSection": "Monitoring"
|
||||
}
|
||||
</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">
|
||||
<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">Monitor Vercel Deployments in Real-Time with Claude Code Statuslines</h1>
|
||||
<p class="article-subtitle">See build status, deploy timing, and clickable deployment links directly in your Claude Code terminal.</p>
|
||||
<div class="article-meta-full">
|
||||
<span class="read-time">5 min read</span>
|
||||
<div class="article-tags">
|
||||
<span class="tag">Vercel</span>
|
||||
<span class="tag">Statusline</span>
|
||||
<span class="tag">Deployment</span>
|
||||
<span class="tag">Monitoring</span>
|
||||
<span class="tag">DevOps</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<article class="article-body">
|
||||
<img src="../assets/vercel-deployment-monitor-statusline-cover.svg" alt="Monitor Vercel Deployments in Real-Time with Claude Code Statuslines" class="article-cover" loading="lazy">
|
||||
|
||||
<div class="article-content-full">
|
||||
<h2>Installation</h2>
|
||||
|
||||
<p>Install the Vercel Deployment Monitor statusline using the Claude Code Templates CLI:</p>
|
||||
|
||||
<pre><code class="language-bash">npx claude-code-templates@latest --setting statusline/vercel-deployment-monitor</code></pre>
|
||||
|
||||
<p>This command automatically installs the statusline configuration in your project's <code>.claude/settings.json</code>. You will need to set two environment variables before it starts working.</p>
|
||||
|
||||
<div class="info-box">
|
||||
<strong>Want to understand how it works?</strong> Keep reading to learn what this statusline does under the hood and why it's essential for your workflow.
|
||||
</div>
|
||||
|
||||
<h2>The Problem: Blind Deployments</h2>
|
||||
|
||||
<p>If you deploy to Vercel, you know the routine: push code, switch to your browser, open the Vercel dashboard, wait for the build to finish, and then check if it succeeded. This context-switching breaks your flow every single time.</p>
|
||||
|
||||
<p>Worse, if you are working in Claude Code and asking it to make changes to a production project, you have no visibility into whether your last deployment succeeded or failed without leaving the terminal. You might be building on top of broken code without realizing it.</p>
|
||||
|
||||
<p>The Vercel Deployment Monitor statusline solves this by bringing real-time deployment status directly into your Claude Code terminal. No tab switching. No dashboard refreshing. Just a persistent status indicator that updates automatically.</p>
|
||||
|
||||
<h2>What You Get</h2>
|
||||
|
||||
<p>Once installed, a status bar appears at the bottom of your Claude Code session showing four pieces of information:</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Element</th>
|
||||
<th>Example</th>
|
||||
<th>What It Shows</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Platform indicator</td>
|
||||
<td><code>▲ Vercel</code></td>
|
||||
<td>Confirms the Vercel integration is active</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Build status</td>
|
||||
<td><code>READY</code> / <code>BUILDING</code> / <code>ERROR</code></td>
|
||||
<td>Current state of the latest deployment</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Time elapsed</td>
|
||||
<td><code>12m ago</code></td>
|
||||
<td>Minutes since the last deployment was created</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Deploy link</td>
|
||||
<td><code>Deploy</code> (clickable)</td>
|
||||
<td>OSC 8 hyperlink you can Cmd+click to open</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p>The status icons change based on deployment state:</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>State</th>
|
||||
<th>Icon</th>
|
||||
<th>Meaning</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>READY</td>
|
||||
<td>checkmark</td>
|
||||
<td>Deployment is live and serving traffic</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>BUILDING</td>
|
||||
<td>spinner</td>
|
||||
<td>Build is in progress</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>QUEUED</td>
|
||||
<td>hourglass</td>
|
||||
<td>Deployment is waiting to start</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ERROR</td>
|
||||
<td>cross</td>
|
||||
<td>Build failed, needs attention</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
|
||||
<p>Before the statusline can query Vercel, you need two pieces of information:</p>
|
||||
|
||||
<h3>1. Vercel API Token</h3>
|
||||
|
||||
<p>Generate a token at <a href="https://vercel.com/account/tokens" target="_blank">vercel.com/account/tokens</a>. Create a token with read-only scope for maximum security.</p>
|
||||
|
||||
<pre><code class="language-bash">export VERCEL_TOKEN=your_token_here</code></pre>
|
||||
|
||||
<h3>2. Vercel Project ID</h3>
|
||||
|
||||
<p>Find your project ID in the Vercel dashboard under Project Settings > General. It is the alphanumeric string shown under "Project ID".</p>
|
||||
|
||||
<pre><code class="language-bash">export VERCEL_PROJECT_ID=your_project_id_here</code></pre>
|
||||
|
||||
<div class="warning-box">
|
||||
<strong>Security note:</strong> Never hardcode these values in your settings file. The statusline command reads them from environment variables at runtime. Add the exports to your <code>~/.bashrc</code>, <code>~/.zshrc</code>, or a <code>.env</code> file loaded by your shell.
|
||||
</div>
|
||||
|
||||
<h2>How It Works</h2>
|
||||
|
||||
<p>The statusline is a <code>command</code>-type statusline that runs a bash one-liner. Here is the logic broken down step by step:</p>
|
||||
|
||||
<pre><code class="language-bash"># 1. Read workspace context from stdin
|
||||
input=$(cat)
|
||||
DIR=$(echo "$input" | jq -r ".workspace.current_dir")
|
||||
|
||||
# 2. Query the Vercel API for the latest deployment
|
||||
DEPLOY_DATA=$(curl -s \
|
||||
-H "Authorization: Bearer $VERCEL_TOKEN" \
|
||||
"https://api.vercel.com/v6/deployments?projectId=$VERCEL_PROJECT_ID&limit=1")
|
||||
|
||||
# 3. Extract deployment state, URL, and creation time
|
||||
STATE=$(echo "$DEPLOY_DATA" | jq -r ".deployments[0].state // empty")
|
||||
FULL_URL=$(echo "$DEPLOY_DATA" | jq -r ".deployments[0].url // empty")
|
||||
CREATED=$(echo "$DEPLOY_DATA" | jq -r ".deployments[0].created // empty")
|
||||
|
||||
# 4. Calculate time since deployment
|
||||
AGO=$(( ($(date +%s) - $CREATED/1000) / 60 ))
|
||||
TIME_AGO="${AGO}m ago"
|
||||
|
||||
# 5. Map state to status icon
|
||||
case "$STATE" in
|
||||
READY) STATUS_ICON="checkmark" ;;
|
||||
BUILDING) STATUS_ICON="spinner" ;;
|
||||
QUEUED) STATUS_ICON="hourglass" ;;
|
||||
ERROR) STATUS_ICON="cross" ;;
|
||||
esac
|
||||
|
||||
# 6. Output with OSC 8 clickable hyperlink
|
||||
echo "Vercel | $STATUS_ICON $STATE | $TIME_AGO | Deploy"</code></pre>
|
||||
|
||||
<p>The key feature is the <strong>OSC 8 hyperlink</strong> in the output. This is a terminal escape sequence that makes the "Deploy" text clickable in supported terminals (iTerm2, Hyper, Windows Terminal, and most modern terminal emulators). Cmd+click (or Ctrl+click) opens the deployment URL directly in your browser.</p>
|
||||
|
||||
<h2>The Full Configuration</h2>
|
||||
|
||||
<p>Here is the complete <code>.claude/settings.json</code> configuration that gets installed:</p>
|
||||
|
||||
<pre><code class="language-json">{
|
||||
"statusLine": {
|
||||
"type": "command",
|
||||
"command": "bash -c 'input=$(cat); DIR=$(echo \"$input\" | jq -r \".workspace.current_dir\"); DEPLOY_DATA=$(curl -s -H \"Authorization: Bearer $VERCEL_TOKEN\" \"https://api.vercel.com/v6/deployments?projectId=$VERCEL_PROJECT_ID&limit=1\" 2>/dev/null); if [ -n \"$DEPLOY_DATA\" ] && [ \"$DEPLOY_DATA\" != \"null\" ]; then STATE=$(echo \"$DEPLOY_DATA\" | jq -r \".deployments[0].state // empty\"); FULL_URL=$(echo \"$DEPLOY_DATA\" | jq -r \".deployments[0].url // empty\"); CREATED=$(echo \"$DEPLOY_DATA\" | jq -r \".deployments[0].created // empty\"); if [ -n \"$CREATED\" ] && [ \"$CREATED\" != \"null\" ]; then AGO=$(( ($(date +%s) - $CREATED/1000) / 60 )); TIME_AGO=\"${AGO}m ago\"; else TIME_AGO=\"unknown\"; fi; case \"$STATE\" in READY) STATUS_ICON=\"check\";; BUILDING) STATUS_ICON=\"spinner\";; QUEUED) STATUS_ICON=\"hourglass\";; ERROR) STATUS_ICON=\"cross\";; *) STATUS_ICON=\"unknown\";; esac; else STATE=\"unavailable\"; FULL_URL=\"\"; TIME_AGO=\"unknown\"; STATUS_ICON=\"unknown\"; fi; echo \"Vercel | $STATUS_ICON $STATE | $TIME_AGO | Deploy\"'"
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<div class="info-box">
|
||||
<strong>Tip:</strong> The statusline refreshes each time Claude Code updates the status bar. This means you get near-real-time updates without any manual polling.
|
||||
</div>
|
||||
|
||||
<h2>Dependencies</h2>
|
||||
|
||||
<p>The statusline relies on two command-line tools that are standard on most development machines:</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tool</th>
|
||||
<th>Purpose</th>
|
||||
<th>Install</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>curl</code></td>
|
||||
<td>Makes HTTP requests to the Vercel API</td>
|
||||
<td>Pre-installed on macOS and most Linux distributions</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>jq</code></td>
|
||||
<td>Parses JSON responses from the API</td>
|
||||
<td><code>brew install jq</code> or <code>apt install jq</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
|
||||
<p>If the statusline shows "unavailable", check these common issues:</p>
|
||||
|
||||
<h3>Environment variables not set</h3>
|
||||
|
||||
<pre><code class="language-bash"># Verify your variables are exported
|
||||
echo $VERCEL_TOKEN
|
||||
echo $VERCEL_PROJECT_ID</code></pre>
|
||||
|
||||
<p>Both should print non-empty values. If they are blank, add the exports to your shell profile and restart your terminal.</p>
|
||||
|
||||
<h3>Invalid token or project ID</h3>
|
||||
|
||||
<pre><code class="language-bash"># Test the API directly
|
||||
curl -s -H "Authorization: Bearer $VERCEL_TOKEN" \
|
||||
"https://api.vercel.com/v6/deployments?projectId=$VERCEL_PROJECT_ID&limit=1" | jq .</code></pre>
|
||||
|
||||
<p>You should see a JSON response with a <code>deployments</code> array. If you get an error, verify the token has not expired and the project ID is correct.</p>
|
||||
|
||||
<h3>jq not installed</h3>
|
||||
|
||||
<pre><code class="language-bash"># macOS
|
||||
brew install jq
|
||||
|
||||
# Ubuntu/Debian
|
||||
sudo apt install jq
|
||||
|
||||
# Verify installation
|
||||
jq --version</code></pre>
|
||||
|
||||
<h2>Combining with Other Statuslines</h2>
|
||||
|
||||
<p>Claude Code supports multiple statuslines. You can combine the Vercel monitor with other statuslines like Git branch indicators, test runners, or system monitors. Each statusline component adds its output to the status bar, giving you a comprehensive dashboard at a glance.</p>
|
||||
|
||||
<p>Browse the full collection of statuslines in the <a href="../../index.html">component catalog</a> to find others that complement your workflow.</p>
|
||||
|
||||
<h2>Conclusion</h2>
|
||||
|
||||
<p>The Vercel Deployment Monitor statusline eliminates the need to context-switch between your terminal and browser when deploying. With a single install command and two environment variables, you get persistent visibility into your deployment pipeline directly inside Claude Code.</p>
|
||||
|
||||
<p>The clickable deploy link is particularly useful: when a build finishes, you can Cmd+click to open the live deployment URL instantly. No more hunting through browser tabs or bookmarks to find your Vercel dashboard.</p>
|
||||
|
||||
<div class="success-box">
|
||||
<strong>Key takeaway:</strong> Statuslines turn Claude Code into a deployment-aware development environment. Install this one for Vercel, and check out the full statusline collection for GitHub, Docker, and more.
|
||||
</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">© 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() {
|
||||
const preElements = document.querySelectorAll('.article-content-full pre:not(.converted)');
|
||||
|
||||
preElements.forEach(pre => {
|
||||
const code = pre.querySelector('code');
|
||||
if (!code) return;
|
||||
|
||||
const language = this.detectLanguage(code);
|
||||
const isTerminal = language === 'bash' || language === 'terminal';
|
||||
|
||||
const codeBlock = document.createElement('div');
|
||||
codeBlock.className = isTerminal ? 'code-block terminal-block' : 'code-block';
|
||||
|
||||
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);
|
||||
|
||||
const newPre = pre.cloneNode(true);
|
||||
newPre.classList.add('converted');
|
||||
|
||||
codeBlock.appendChild(header);
|
||||
codeBlock.appendChild(newPre);
|
||||
|
||||
pre.parentNode.replaceChild(codeBlock, pre);
|
||||
});
|
||||
}
|
||||
|
||||
detectLanguage(codeElement) {
|
||||
const className = codeElement.className;
|
||||
if (className.includes('language-')) {
|
||||
return className.match(/language-(\w+)/)[1];
|
||||
}
|
||||
|
||||
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 {
|
||||
let textToCopy = code.textContent;
|
||||
|
||||
if (codeBlock.classList.contains('terminal-block')) {
|
||||
textToCopy = this.cleanTerminalOutput(textToCopy);
|
||||
}
|
||||
|
||||
await navigator.clipboard.writeText(textToCopy);
|
||||
|
||||
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');
|
||||
|
||||
setTimeout(() => {
|
||||
button.innerHTML = originalContent;
|
||||
button.classList.remove('copied');
|
||||
}, 2000);
|
||||
|
||||
} catch (err) {
|
||||
console.error('Failed to copy code:', err);
|
||||
|
||||
const selection = window.getSelection();
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(code);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cleanTerminalOutput(text) {
|
||||
return text
|
||||
.split('\n')
|
||||
.map(line => {
|
||||
line = line.replace(/^[\$\u276F]\s*/, '').replace(/^claude-code>\s*/, '');
|
||||
|
||||
if (line.trim().startsWith('# checkmark') ||
|
||||
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() !== '')
|
||||
.join('\n')
|
||||
.trim();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize when DOM is loaded
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => new CodeCopy());
|
||||
} else {
|
||||
new CodeCopy();
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
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>
|
||||
Copied!
|
||||
`;
|
||||
button.classList.add('copied');
|
||||
|
||||
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 content = document.querySelector('.article-content-full');
|
||||
|
||||
let markdown = `# ${title}\n\n`;
|
||||
|
||||
if (subtitle) {
|
||||
markdown += `${subtitle}\n\n`;
|
||||
}
|
||||
|
||||
if (!content) return markdown;
|
||||
|
||||
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'));
|
||||
}
|
||||
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 ``;
|
||||
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 = '';
|
||||
|
||||
const headerRow = rows[0];
|
||||
const headers = headerRow.querySelectorAll('th, td');
|
||||
const headerText = Array.from(headers).map(h => h.textContent.trim()).join(' | ');
|
||||
markdown += `| ${headerText} |\n`;
|
||||
|
||||
const separator = Array.from(headers).map(() => '---').join(' | ');
|
||||
markdown += `| ${separator} |\n`;
|
||||
|
||||
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;
|
||||
|
||||
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>
|
||||
@@ -0,0 +1,197 @@
|
||||
# Parallel Development with Git Worktrees: A Skill + Commands Pattern for Claude Code
|
||||
|
||||
Working on a single task at a time is fine -- until you have three bugs to fix, a feature to ship, and a PR review waiting. With git worktrees and Claude Code, you can work on all of them simultaneously in separate terminal panels, each with its own Claude instance.
|
||||
|
||||
## The Problem
|
||||
|
||||
Traditional git workflows force sequential development. You stash changes, switch branches, lose context, switch back. Every context switch costs time and mental energy.
|
||||
|
||||
Git worktrees solve this by letting you check out multiple branches simultaneously in separate directories. But managing worktrees manually -- creating branches, tracking tasks, cleaning up -- adds overhead.
|
||||
|
||||
## The Solution: 1 Skill + 4 Commands
|
||||
|
||||
We built a system that combines Claude Code's two extension models:
|
||||
|
||||
- **1 Skill** (`worktree-guide`) -- an interactive guide Claude can discover and suggest automatically
|
||||
- **4 Commands** -- manual workflows for each lifecycle step
|
||||
|
||||
```
|
||||
Architecture
|
||||
──────────────────────────────────────
|
||||
SKILL: /worktree-guide
|
||||
(discoverable, interactive guide)
|
||||
|
||||
Delegates to COMMANDS:
|
||||
├── /worktree-init Create worktrees
|
||||
├── /worktree-check Check status
|
||||
├── /worktree-deliver Commit + PR
|
||||
└── /worktree-cleanup Clean up
|
||||
──────────────────────────────────────
|
||||
```
|
||||
|
||||
### Why this architecture?
|
||||
|
||||
Commands and Skills serve different purposes in Claude Code:
|
||||
|
||||
| | Commands | Skills |
|
||||
|---|---------|--------|
|
||||
| **Who triggers** | Only the user | User and/or Claude |
|
||||
| **Best for** | Manual workflows with side effects | Discoverable knowledge and context |
|
||||
| **Git operations** | Perfect (push, branch, PR) | Overkill |
|
||||
| **Auto-discovery** | No | Yes |
|
||||
|
||||
The worktree operations create branches, push code, and delete things -- you want deterministic control over when that happens. But the guide that teaches you the workflow? That's perfect for a Skill that Claude can suggest when you mention "parallel development" or "multiple tasks."
|
||||
|
||||
## Installation
|
||||
|
||||
Copy the commands and the guide skill to your project:
|
||||
|
||||
```bash
|
||||
# Commands (manual workflows)
|
||||
cp .claude/commands/worktree-*.md your-project/.claude/commands/
|
||||
|
||||
# Skill (interactive guide)
|
||||
cp -r .claude/skills/worktree-guide your-project/.claude/skills/
|
||||
```
|
||||
|
||||
Or install globally for all projects:
|
||||
|
||||
```bash
|
||||
cp .claude/commands/worktree-*.md ~/.claude/commands/
|
||||
cp -r .claude/skills/worktree-guide ~/.claude/skills/
|
||||
```
|
||||
|
||||
## Workflow: From Tasks to PRs
|
||||
|
||||
### Start with the guide
|
||||
|
||||
If it's your first time, just ask Claude:
|
||||
|
||||
```
|
||||
How do I work on multiple tasks at once?
|
||||
```
|
||||
|
||||
Claude will discover the `worktree-guide` skill and walk you through the full workflow interactively, including Ghostty keybindings and Lazygit integration.
|
||||
|
||||
Or invoke it directly:
|
||||
|
||||
```
|
||||
/worktree-guide
|
||||
```
|
||||
|
||||
### 1. Initialize Worktrees
|
||||
|
||||
Start in your main repo and describe your tasks separated by `|`:
|
||||
|
||||
```
|
||||
/worktree-init fix login bug | add dark mode | update API docs
|
||||
```
|
||||
|
||||
Claude creates 3 worktrees, each on its own `wt/*` branch:
|
||||
|
||||
```
|
||||
| # | Task | Branch | Path |
|
||||
|---|-----------------|--------------------|------------------------------------|
|
||||
| 1 | fix login bug | wt/fix-login-bug | ../worktrees/repo/wt-fix-login-bug |
|
||||
| 2 | add dark mode | wt/add-dark-mode | ../worktrees/repo/wt-add-dark-mode |
|
||||
| 3 | update API docs | wt/update-api-docs | ../worktrees/repo/wt-update-api-docs|
|
||||
```
|
||||
|
||||
Each worktree gets a `.worktree-task.md` file with the task context.
|
||||
|
||||
### 2. Open Parallel Panels
|
||||
|
||||
In Ghostty, split your terminal:
|
||||
|
||||
- `Cmd+D` -- split right
|
||||
- `Cmd+Shift+D` -- split down
|
||||
|
||||
Navigate to each worktree and launch Claude:
|
||||
|
||||
```bash
|
||||
# Panel 1
|
||||
cd ../worktrees/repo/wt-fix-login-bug && claude
|
||||
|
||||
# Panel 2
|
||||
cd ../worktrees/repo/wt-add-dark-mode && claude
|
||||
|
||||
# Panel 3
|
||||
cd ../worktrees/repo/wt-update-api-docs && claude
|
||||
```
|
||||
|
||||
Now you have 3 independent Claude instances, each working on a separate task with full git isolation.
|
||||
|
||||
### 3. Check Status
|
||||
|
||||
Inside any worktree, run:
|
||||
|
||||
```
|
||||
/worktree-check
|
||||
```
|
||||
|
||||
You get a clean status report:
|
||||
|
||||
```
|
||||
Worktree Status
|
||||
──────────────────────────────────
|
||||
Branch: wt/fix-login-bug
|
||||
Task: fix login bug
|
||||
Commits: 3 ahead of main
|
||||
Modified: 2 files
|
||||
Staged: 0 files
|
||||
Untracked: 0 files
|
||||
──────────────────────────────────
|
||||
```
|
||||
|
||||
### 4. Deliver Your Work
|
||||
|
||||
When a task is done, run:
|
||||
|
||||
```
|
||||
/worktree-deliver
|
||||
```
|
||||
|
||||
Claude will:
|
||||
1. Show you all changes for review
|
||||
2. Generate a conventional commit message
|
||||
3. Push the branch
|
||||
4. Create a PR with the task description as context
|
||||
|
||||
### 5. Clean Up
|
||||
|
||||
After your PRs are merged, go back to the main repo and run:
|
||||
|
||||
```
|
||||
/worktree-cleanup --all
|
||||
```
|
||||
|
||||
This removes all merged worktrees, deletes local and remote `wt/*` branches, and prunes stale references.
|
||||
|
||||
Use `--dry-run` to preview what would be cleaned up first:
|
||||
|
||||
```
|
||||
/worktree-cleanup --dry-run
|
||||
```
|
||||
|
||||
## The Pattern: Skill as Orchestrator, Commands as Executors
|
||||
|
||||
This architecture demonstrates a pattern you can apply to any complex workflow:
|
||||
|
||||
1. **Create Commands** for each discrete step that has side effects (git operations, file modifications, deployments)
|
||||
2. **Create a Skill** that acts as an entry point, guide, and orchestrator -- referencing the commands
|
||||
3. The Skill is **discoverable** -- Claude can suggest it when relevant
|
||||
4. The Commands are **deterministic** -- only triggered by explicit user action
|
||||
|
||||
Other workflows that fit this pattern:
|
||||
- **Database migrations**: Skill guide + commands for create, apply, rollback
|
||||
- **Release management**: Skill guide + commands for bump, changelog, publish
|
||||
- **Environment setup**: Skill guide + commands for provision, configure, validate
|
||||
|
||||
## Tips
|
||||
|
||||
- **Name your tasks clearly**: The task description becomes the branch name and PR context
|
||||
- **One task per worktree**: Keep each worktree focused on a single deliverable
|
||||
- **Don't cross-edit**: Each worktree is isolated. Changes in one don't affect others until merged
|
||||
- **Install dependencies**: If your project uses npm/yarn/pnpm, run the install command in each new worktree
|
||||
- **Works with any terminal**: While optimized for Ghostty panels, this workflow works with any terminal that supports split panes (iTerm2, tmux, Warp, etc.)
|
||||
- **Use Lazygit**: Open it in a dedicated panel to monitor all worktrees visually
|
||||
@@ -0,0 +1,26 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="340" height="48" viewBox="0 0 340 48">
|
||||
<rect width="340" height="48" rx="6" fill="#1a1a1a"/>
|
||||
<rect x="1" y="1" width="338" height="46" rx="5" stroke="#D97757" stroke-width="1.5" fill="none"/>
|
||||
<!-- Claude starburst logo -->
|
||||
<g transform="translate(24, 24)">
|
||||
<!-- 8 lines radiating from center -->
|
||||
<line x1="0" y1="-10" x2="0" y2="10" stroke="#D97757" stroke-width="2" stroke-linecap="round"/>
|
||||
<line x1="-10" y1="0" x2="10" y2="0" stroke="#D97757" stroke-width="2" stroke-linecap="round"/>
|
||||
<line x1="-7.07" y1="-7.07" x2="7.07" y2="7.07" stroke="#D97757" stroke-width="2" stroke-linecap="round"/>
|
||||
<line x1="7.07" y1="-7.07" x2="-7.07" y2="7.07" stroke="#D97757" stroke-width="2" stroke-linecap="round"/>
|
||||
<!-- 8 additional thinner lines for density -->
|
||||
<line x1="0" y1="-8" x2="0" y2="-3" stroke="#D97757" stroke-width="1.2" stroke-linecap="round" transform="rotate(22.5)"/>
|
||||
<line x1="0" y1="3" x2="0" y2="8" stroke="#D97757" stroke-width="1.2" stroke-linecap="round" transform="rotate(22.5)"/>
|
||||
<line x1="0" y1="-8" x2="0" y2="-3" stroke="#D97757" stroke-width="1.2" stroke-linecap="round" transform="rotate(67.5)"/>
|
||||
<line x1="0" y1="3" x2="0" y2="8" stroke="#D97757" stroke-width="1.2" stroke-linecap="round" transform="rotate(67.5)"/>
|
||||
<line x1="0" y1="-8" x2="0" y2="-3" stroke="#D97757" stroke-width="1.2" stroke-linecap="round" transform="rotate(112.5)"/>
|
||||
<line x1="0" y1="3" x2="0" y2="8" stroke="#D97757" stroke-width="1.2" stroke-linecap="round" transform="rotate(112.5)"/>
|
||||
<line x1="0" y1="-8" x2="0" y2="-3" stroke="#D97757" stroke-width="1.2" stroke-linecap="round" transform="rotate(157.5)"/>
|
||||
<line x1="0" y1="3" x2="0" y2="8" stroke="#D97757" stroke-width="1.2" stroke-linecap="round" transform="rotate(157.5)"/>
|
||||
<!-- Center dot -->
|
||||
<circle cx="0" cy="0" r="2" fill="#D97757"/>
|
||||
</g>
|
||||
<!-- Text -->
|
||||
<text x="46" y="21" font-family="system-ui, -apple-system, 'Segoe UI', sans-serif" font-size="11" font-weight="600" letter-spacing="1.5" fill="#D97757">CLAUDE FOR</text>
|
||||
<text x="46" y="37" font-family="system-ui, -apple-system, 'Segoe UI', sans-serif" font-size="13" font-weight="700" letter-spacing="1" fill="white">OPEN SOURCE PROGRAM</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,511 @@
|
||||
{
|
||||
"generated_at": "2026-01-28T04:04:24.747050+00:00",
|
||||
"repo": "davila7/claude-code-templates",
|
||||
"total": 42,
|
||||
"prs": [
|
||||
{
|
||||
"number": 316,
|
||||
"title": "fix: X/Twitter preview images not showing for Neon pages",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/fix-x-preview-image-ZUbI7",
|
||||
"created_at": "2026-01-28T03:49:59Z",
|
||||
"merged_at": "2026-01-28T03:50:18Z",
|
||||
"closed_at": "2026-01-28T03:50:18Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/316",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 313,
|
||||
"title": "Fix domain URLs: remove www subdomain and update image paths",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/fix-seo-social-preview-UTZVa",
|
||||
"created_at": "2026-01-27T17:35:11Z",
|
||||
"merged_at": "2026-01-27T17:35:49Z",
|
||||
"closed_at": "2026-01-27T17:35:49Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/313",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 312,
|
||||
"title": "Add SVG cover image for security hooks blog article",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/create-blog-cover-Bg0IV",
|
||||
"created_at": "2026-01-27T15:31:48Z",
|
||||
"merged_at": "2026-01-27T15:53:43Z",
|
||||
"closed_at": "2026-01-27T15:53:43Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/312",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 311,
|
||||
"title": "feat(blog): Add security hooks secrets detection article",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/create-blog-security-hooks-gQITc",
|
||||
"created_at": "2026-01-27T03:12:39Z",
|
||||
"merged_at": "2026-01-27T03:34:42Z",
|
||||
"closed_at": "2026-01-27T03:34:42Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/311",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 309,
|
||||
"title": "feat: Migrate 244 skills from antigravity-awesome-skills repository",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/load-skills-migration-c1hDd",
|
||||
"created_at": "2026-01-25T15:00:51Z",
|
||||
"merged_at": "2026-01-25T15:01:14Z",
|
||||
"closed_at": "2026-01-25T15:01:14Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/309",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 307,
|
||||
"title": "feat: Add HeyGen Best Practices Skill blog article",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/create-hey-gen-skill-blog-3Kots",
|
||||
"created_at": "2026-01-25T04:06:37Z",
|
||||
"merged_at": "2026-01-25T04:10:46Z",
|
||||
"closed_at": "2026-01-25T04:10:46Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/307",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 306,
|
||||
"title": "feat: Migrate heygen-best-practices skill from heygen-com/skills",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/add-heygen-skills-TV306",
|
||||
"created_at": "2026-01-25T03:58:42Z",
|
||||
"merged_at": "2026-01-25T03:59:14Z",
|
||||
"closed_at": "2026-01-25T03:59:15Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/306",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 304,
|
||||
"title": "Add secret scanner hook to prevent hardcoded credentials in commits",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/review-deletion-hook-c7fNB",
|
||||
"created_at": "2026-01-24T23:02:37Z",
|
||||
"merged_at": "2026-01-24T23:07:22Z",
|
||||
"closed_at": "2026-01-24T23:07:22Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/304",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 303,
|
||||
"title": "Add component migrator agent and migrate initial components",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/migrate-to-components-HdgnR",
|
||||
"created_at": "2026-01-24T22:53:20Z",
|
||||
"merged_at": "2026-01-25T00:56:42Z",
|
||||
"closed_at": "2026-01-25T00:56:42Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/303",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 300,
|
||||
"title": "Add React Best Practices Skill blog article and cover image",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/create-react-blog-eP3zb",
|
||||
"created_at": "2026-01-23T04:20:37Z",
|
||||
"merged_at": "2026-01-23T04:43:46Z",
|
||||
"closed_at": "2026-01-23T04:43:47Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/300",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 299,
|
||||
"title": "feat: Improve motion-canvas skill with production-ready setup guide",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/improve-motion-canvas-skill-KDrxT",
|
||||
"created_at": "2026-01-22T03:54:48Z",
|
||||
"merged_at": "2026-01-22T04:48:12Z",
|
||||
"closed_at": "2026-01-22T04:48:12Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/299",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 298,
|
||||
"title": "Remove plugins filter chip from search interface",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/remove-plugins-section-et25T",
|
||||
"created_at": "2026-01-22T02:43:12Z",
|
||||
"merged_at": "2026-01-22T02:44:41Z",
|
||||
"closed_at": "2026-01-22T02:44:41Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/298",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 297,
|
||||
"title": "Add comprehensive skill guides for Manim and Motion Canvas video tools",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/add-remotion-similar-skills-V7ZqH",
|
||||
"created_at": "2026-01-22T02:33:51Z",
|
||||
"merged_at": "2026-01-22T02:35:46Z",
|
||||
"closed_at": "2026-01-22T02:35:46Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/297",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 296,
|
||||
"title": "Improve Security Audit Report GitHub Action",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/improve-security-audit-action-dCkBO",
|
||||
"created_at": "2026-01-22T02:20:16Z",
|
||||
"merged_at": "2026-01-22T02:23:31Z",
|
||||
"closed_at": "2026-01-22T02:23:31Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/296",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 295,
|
||||
"title": "feat: Add Remotion skill component for programmatic video creation",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/add-remotion-skills-component-Y3TRI",
|
||||
"created_at": "2026-01-22T01:45:26Z",
|
||||
"merged_at": "2026-01-22T02:07:24Z",
|
||||
"closed_at": "2026-01-22T02:07:24Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/295",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 294,
|
||||
"title": "Code review, standards compliance, and feature improvements",
|
||||
"state": "closed",
|
||||
"merged": false,
|
||||
"branch": "claude/code-review-standards-features-s8qI4",
|
||||
"created_at": "2026-01-21T15:48:48Z",
|
||||
"merged_at": null,
|
||||
"closed_at": "2026-01-21T16:13:28Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/294",
|
||||
"user": "djimit"
|
||||
},
|
||||
{
|
||||
"number": 292,
|
||||
"title": "feat: Add author field to component schema and display on website",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/add-author-field-WHIoM",
|
||||
"created_at": "2026-01-21T03:32:59Z",
|
||||
"merged_at": "2026-01-21T04:22:27Z",
|
||||
"closed_at": "2026-01-21T04:22:27Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/292",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 284,
|
||||
"title": "Neon Open Source Program",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/review-neon-oss-proposal-YpoGW",
|
||||
"created_at": "2026-01-18T22:01:40Z",
|
||||
"merged_at": "2026-01-27T22:48:40Z",
|
||||
"closed_at": "2026-01-27T22:48:40Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/284",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 282,
|
||||
"title": "Generate components.json file",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/generate-components-json-zLx77",
|
||||
"created_at": "2026-01-18T04:14:07Z",
|
||||
"merged_at": "2026-01-18T04:14:15Z",
|
||||
"closed_at": "2026-01-18T04:14:15Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/282",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 281,
|
||||
"title": "Fix missing security hook in documentation",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/fix-security-hook-docs-P3VaD",
|
||||
"created_at": "2026-01-18T04:10:28Z",
|
||||
"merged_at": "2026-01-18T04:10:35Z",
|
||||
"closed_at": "2026-01-18T04:10:36Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/281",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 280,
|
||||
"title": "Generate JSON for COM component",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/generate-com-json-YFT1I",
|
||||
"created_at": "2026-01-18T03:41:53Z",
|
||||
"merged_at": "2026-01-18T03:42:04Z",
|
||||
"closed_at": "2026-01-18T03:42:04Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/280",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 279,
|
||||
"title": "Create or update hook to block rm commands",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/add-rm-command-hook-PLddR",
|
||||
"created_at": "2026-01-18T03:06:52Z",
|
||||
"merged_at": "2026-01-18T03:37:15Z",
|
||||
"closed_at": "2026-01-18T03:37:15Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/279",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 278,
|
||||
"title": "feat: regenerate components.json catalog",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/generate-component-json-u3S90",
|
||||
"created_at": "2026-01-18T02:50:04Z",
|
||||
"merged_at": "2026-01-18T02:50:19Z",
|
||||
"closed_at": "2026-01-18T02:50:19Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/278",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 277,
|
||||
"title": "Add Agent Skills documentation to Claude Code",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/add-agent-skills-docs-ZkWmm",
|
||||
"created_at": "2026-01-18T02:40:37Z",
|
||||
"merged_at": "2026-01-18T02:43:14Z",
|
||||
"closed_at": "2026-01-18T02:43:14Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/277",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 276,
|
||||
"title": "Remove item from CLAUDE.md",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/remove-claude-md-item-eKyLu",
|
||||
"created_at": "2026-01-18T02:18:41Z",
|
||||
"merged_at": "2026-01-18T02:19:57Z",
|
||||
"closed_at": "2026-01-18T02:19:57Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/276",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 275,
|
||||
"title": "Create custom subagents documentation",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/custom-subagents-docs-Mtagk",
|
||||
"created_at": "2026-01-18T01:54:32Z",
|
||||
"merged_at": "2026-01-18T02:00:19Z",
|
||||
"closed_at": "2026-01-18T02:00:19Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/275",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 272,
|
||||
"title": "feat: Add React Best Practices skill from Vercel",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/add-react-skills-components-at7Ai",
|
||||
"created_at": "2026-01-14T21:37:28Z",
|
||||
"merged_at": "2026-01-15T01:58:56Z",
|
||||
"closed_at": "2026-01-15T01:58:57Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/272",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 268,
|
||||
"title": "feat: Update Webflow MCP configuration",
|
||||
"state": "open",
|
||||
"merged": false,
|
||||
"branch": "claude/add-mcp-webflow-LgWJe",
|
||||
"created_at": "2026-01-12T05:22:32Z",
|
||||
"merged_at": null,
|
||||
"closed_at": null,
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/268",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 267,
|
||||
"title": "feat: Add Featured Projects carousel with BrainGrid",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/add-featured-carousel-Ikw35",
|
||||
"created_at": "2026-01-12T01:33:55Z",
|
||||
"merged_at": "2026-01-13T03:58:00Z",
|
||||
"closed_at": "2026-01-13T03:58:00Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/267",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 266,
|
||||
"title": "feat: Configure Vercel preview deployments for PRs",
|
||||
"state": "closed",
|
||||
"merged": false,
|
||||
"branch": "claude/vercel-preview-links-DQ0uT",
|
||||
"created_at": "2026-01-12T00:33:25Z",
|
||||
"merged_at": null,
|
||||
"closed_at": "2026-01-12T01:19:14Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/266",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 264,
|
||||
"title": "feat(skills): Add Railway skills from railway-skills repository",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/extract-skills-components-9x6BK",
|
||||
"created_at": "2026-01-10T16:19:08Z",
|
||||
"merged_at": "2026-01-10T16:21:06Z",
|
||||
"closed_at": "2026-01-10T16:21:06Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/264",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 263,
|
||||
"title": "feat: Add 135 agents and 7 skills from awesome-copilot repository",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/add-agents-skills-JHu8U",
|
||||
"created_at": "2026-01-10T01:46:25Z",
|
||||
"merged_at": "2026-01-18T02:07:01Z",
|
||||
"closed_at": "2026-01-18T02:07:02Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/263",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 262,
|
||||
"title": "feat: Add components from happy-coding-agent repository",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/add-external-components-4xdZo",
|
||||
"created_at": "2026-01-09T11:55:45Z",
|
||||
"merged_at": "2026-01-09T11:56:20Z",
|
||||
"closed_at": "2026-01-09T11:56:20Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/262",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 261,
|
||||
"title": "feat: Add Obsidian skills from kepano/obsidian-skills repository",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/add-skill-component-Q7ZI7",
|
||||
"created_at": "2026-01-08T13:40:50Z",
|
||||
"merged_at": "2026-01-08T13:41:41Z",
|
||||
"closed_at": "2026-01-08T13:41:41Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/261",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 260,
|
||||
"title": "feat: Add planning-with-files skill from OthmanAdi repository",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/clone-planning-repo-SXBKv",
|
||||
"created_at": "2026-01-08T04:45:30Z",
|
||||
"merged_at": "2026-01-08T04:46:14Z",
|
||||
"closed_at": "2026-01-08T04:46:14Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/260",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 259,
|
||||
"title": "feat: Add 75 AI Research Engineering Skills from AI-research-SKILLs",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/add-skills-components-ZG9Md",
|
||||
"created_at": "2026-01-08T04:24:10Z",
|
||||
"merged_at": "2026-01-08T14:24:30Z",
|
||||
"closed_at": "2026-01-08T14:24:30Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/259",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 258,
|
||||
"title": "feat: Add 7 n8n workflow automation skills",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/add-n8n-skills-Ndwsh",
|
||||
"created_at": "2026-01-08T04:16:10Z",
|
||||
"merged_at": "2026-01-08T04:16:56Z",
|
||||
"closed_at": "2026-01-08T04:16:56Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/258",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 253,
|
||||
"title": "feat: Add 6 Sentry engineering skills",
|
||||
"state": "closed",
|
||||
"merged": true,
|
||||
"branch": "claude/add-sentry-skills-AEtGh",
|
||||
"created_at": "2026-01-06T13:31:11Z",
|
||||
"merged_at": "2026-01-06T13:31:33Z",
|
||||
"closed_at": "2026-01-06T13:31:33Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/253",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 138,
|
||||
"title": "Understand repository purpose and structure",
|
||||
"state": "closed",
|
||||
"merged": false,
|
||||
"branch": "claude/explore-repo-overview-01AQbpWjRc36eSFmQUEhhdEw",
|
||||
"created_at": "2025-11-29T20:56:15Z",
|
||||
"merged_at": null,
|
||||
"closed_at": "2025-12-20T18:00:29Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/138",
|
||||
"user": "davila7"
|
||||
},
|
||||
{
|
||||
"number": 137,
|
||||
"title": "Claude/multi agent web automation 01 vr hg wx8 wg2r g3s hzsdz37 f",
|
||||
"state": "closed",
|
||||
"merged": false,
|
||||
"branch": "claude/multi-agent-web-automation-01VrHGWx8WG2rG3sHzsdz37F",
|
||||
"created_at": "2025-11-28T07:20:52Z",
|
||||
"merged_at": null,
|
||||
"closed_at": "2025-11-28T07:21:14Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/137",
|
||||
"user": "agenticassets"
|
||||
},
|
||||
{
|
||||
"number": 134,
|
||||
"title": "feat: GitHub Copilot CLI Agent Arsenal - 20 Specialized Agents + Orchestration",
|
||||
"state": "closed",
|
||||
"merged": false,
|
||||
"branch": "claude/copilot-cli-agents-019NvHvDmydM6mypg2jHqG72",
|
||||
"created_at": "2025-11-23T01:16:33Z",
|
||||
"merged_at": null,
|
||||
"closed_at": "2026-01-01T02:00:24Z",
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/134",
|
||||
"user": "RLuf"
|
||||
},
|
||||
{
|
||||
"number": 116,
|
||||
"title": "feat: Install product-strategist agent for project brainstorming",
|
||||
"state": "open",
|
||||
"merged": false,
|
||||
"branch": "claude/explore-project-ideas-011CUMxKHdKF7Z3Dnx1d4tPA",
|
||||
"created_at": "2025-10-22T09:21:21Z",
|
||||
"merged_at": null,
|
||||
"closed_at": null,
|
||||
"url": "https://github.com/davila7/claude-code-templates/pull/116",
|
||||
"user": "TheHizzleGizzle"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,505 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Claude Code PRs - Claude Code Templates</title>
|
||||
<meta name="description" content="Track all Pull Requests created by Claude Code in the claude-code-templates repository.">
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://aitmpl.com/claude-prs/">
|
||||
<meta property="og:title" content="Claude Code PRs - Claude Code Templates">
|
||||
<meta property="og:description" content="Track all Pull Requests created by Claude Code in the claude-code-templates repository.">
|
||||
<meta property="og:image" content="https://aitmpl.com/images/social-preview.png">
|
||||
<meta property="og:site_name" content="Claude Code Templates">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Claude Code PRs - Claude Code Templates">
|
||||
<meta name="twitter:description" content="Track all Pull Requests created by Claude Code in the claude-code-templates repository.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/images/social-preview.png">
|
||||
|
||||
<!-- 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="stylesheet" href="../css/styles.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">
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
|
||||
<style>
|
||||
.dashboard-page {
|
||||
min-height: 100vh;
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
.dashboard-header {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.dashboard-title {
|
||||
font-size: 2.5rem;
|
||||
color: var(--text-accent);
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.dashboard-subtitle {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
display: inline-block;
|
||||
color: var(--text-accent);
|
||||
text-decoration: none;
|
||||
margin-bottom: 2rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.back-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Stats cards */
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-accent);
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.85rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
/* Chart section */
|
||||
.chart-section {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
color: var(--text-primary);
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 1rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
/* Table section */
|
||||
.table-section {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
padding: 1rem 1.5rem;
|
||||
border-bottom: 1px solid var(--border-primary);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.table-title {
|
||||
color: var(--text-primary);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.table-count {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.pr-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.pr-table thead th {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
padding: 0.75rem 1rem;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid var(--border-primary);
|
||||
}
|
||||
|
||||
.pr-table tbody tr {
|
||||
border-bottom: 1px solid var(--border-secondary);
|
||||
transition: background 0.15s ease;
|
||||
}
|
||||
|
||||
.pr-table tbody tr:hover {
|
||||
background: var(--bg-tertiary);
|
||||
}
|
||||
|
||||
.pr-table td {
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.pr-number {
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.pr-title-link {
|
||||
color: var(--text-info);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.pr-title-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.pr-state {
|
||||
display: inline-block;
|
||||
padding: 0.2rem 0.6rem;
|
||||
border-radius: 12px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.pr-state.merged {
|
||||
background: rgba(163, 113, 247, 0.15);
|
||||
color: #a371f7;
|
||||
}
|
||||
|
||||
.pr-state.open {
|
||||
background: rgba(63, 185, 80, 0.15);
|
||||
color: #3fb950;
|
||||
}
|
||||
|
||||
.pr-state.closed {
|
||||
background: rgba(248, 81, 73, 0.15);
|
||||
color: #f85149;
|
||||
}
|
||||
|
||||
.pr-date {
|
||||
color: var(--text-secondary);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pr-branch {
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Monaco', 'Menlo', monospace;
|
||||
font-size: 0.75rem;
|
||||
background: var(--bg-tertiary);
|
||||
padding: 0.2rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Loading */
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 3rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 2px solid var(--border-primary);
|
||||
border-top-color: var(--text-accent);
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.error-msg {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: var(--text-error);
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.dashboard-title {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.pr-table {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
height: 220px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="dashboard-page">
|
||||
<div class="container">
|
||||
<a href="../" class="back-link">← Back to Templates</a>
|
||||
|
||||
<div class="dashboard-header">
|
||||
<h1 class="dashboard-title">Claude Code PRs</h1>
|
||||
<p class="dashboard-subtitle">Pull Requests created by Claude Code in davila7/claude-code-templates</p>
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="stats-grid" id="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="total-prs">-</div>
|
||||
<div class="stat-label">Total PRs</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="merged-prs">-</div>
|
||||
<div class="stat-label">Merged</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="open-prs">-</div>
|
||||
<div class="stat-label">Open</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="closed-prs">-</div>
|
||||
<div class="stat-label">Closed</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Chart -->
|
||||
<div class="chart-section">
|
||||
<div class="chart-title">PRs per Week</div>
|
||||
<div class="chart-container">
|
||||
<canvas id="prs-chart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Table -->
|
||||
<div class="table-section">
|
||||
<div class="table-header">
|
||||
<span class="table-title">All Claude Code PRs</span>
|
||||
<span class="table-count" id="table-count"></span>
|
||||
</div>
|
||||
<div id="table-body">
|
||||
<div class="loading">
|
||||
<div class="loading-spinner"></div>
|
||||
<div>Loading PR data...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function getPRState(pr) {
|
||||
if (pr.merged) return 'merged';
|
||||
if (pr.state === 'open') return 'open';
|
||||
return 'closed';
|
||||
}
|
||||
|
||||
function formatDate(dateStr) {
|
||||
const d = new Date(dateStr);
|
||||
return d.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
|
||||
}
|
||||
|
||||
function updateStats(prs) {
|
||||
const merged = prs.filter(pr => pr.merged).length;
|
||||
const open = prs.filter(pr => pr.state === 'open' && !pr.merged).length;
|
||||
const closed = prs.filter(pr => pr.state === 'closed' && !pr.merged).length;
|
||||
|
||||
document.getElementById('total-prs').textContent = prs.length;
|
||||
document.getElementById('merged-prs').textContent = merged;
|
||||
document.getElementById('open-prs').textContent = open;
|
||||
document.getElementById('closed-prs').textContent = closed;
|
||||
}
|
||||
|
||||
function renderChart(prs) {
|
||||
const weekMap = {};
|
||||
prs.forEach(pr => {
|
||||
const d = new Date(pr.created_at);
|
||||
const start = new Date(d);
|
||||
start.setDate(d.getDate() - d.getDay());
|
||||
const key = start.toISOString().slice(0, 10);
|
||||
if (!weekMap[key]) weekMap[key] = { merged: 0, open: 0, closed: 0 };
|
||||
weekMap[key][getPRState(pr)]++;
|
||||
});
|
||||
|
||||
const sortedWeeks = Object.keys(weekMap).sort();
|
||||
const labels = sortedWeeks.map(w => {
|
||||
const d = new Date(w);
|
||||
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
||||
});
|
||||
|
||||
const ctx = document.getElementById('prs-chart').getContext('2d');
|
||||
new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels,
|
||||
datasets: [
|
||||
{
|
||||
label: 'Merged',
|
||||
data: sortedWeeks.map(w => weekMap[w].merged),
|
||||
backgroundColor: 'rgba(163, 113, 247, 0.7)',
|
||||
borderColor: '#a371f7',
|
||||
borderWidth: 1
|
||||
},
|
||||
{
|
||||
label: 'Open',
|
||||
data: sortedWeeks.map(w => weekMap[w].open),
|
||||
backgroundColor: 'rgba(63, 185, 80, 0.7)',
|
||||
borderColor: '#3fb950',
|
||||
borderWidth: 1
|
||||
},
|
||||
{
|
||||
label: 'Closed',
|
||||
data: sortedWeeks.map(w => weekMap[w].closed),
|
||||
backgroundColor: 'rgba(248, 81, 73, 0.7)',
|
||||
borderColor: '#f85149',
|
||||
borderWidth: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
labels: { color: '#7d8590', font: { family: "'Monaco', 'Menlo', monospace" } }
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
ticks: { color: '#7d8590', font: { size: 11 } },
|
||||
grid: { color: 'rgba(48, 54, 61, 0.5)' }
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
beginAtZero: true,
|
||||
ticks: { color: '#7d8590', stepSize: 1 },
|
||||
grid: { color: 'rgba(48, 54, 61, 0.5)' }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderTable(prs) {
|
||||
document.getElementById('table-count').textContent = `${prs.length} PRs`;
|
||||
|
||||
const table = document.createElement('table');
|
||||
table.className = 'pr-table';
|
||||
table.innerHTML = `
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Title</th>
|
||||
<th>State</th>
|
||||
<th>Branch</th>
|
||||
<th>Created</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${prs.map(pr => {
|
||||
const state = getPRState(pr);
|
||||
return `<tr>
|
||||
<td class="pr-number">#${pr.number}</td>
|
||||
<td><a href="${pr.url}" target="_blank" rel="noopener" class="pr-title-link">${escapeHtml(pr.title)}</a></td>
|
||||
<td><span class="pr-state ${state}">${state}</span></td>
|
||||
<td><span class="pr-branch">${escapeHtml(pr.branch)}</span></td>
|
||||
<td class="pr-date">${formatDate(pr.created_at)}</td>
|
||||
</tr>`;
|
||||
}).join('')}
|
||||
</tbody>
|
||||
`;
|
||||
|
||||
const container = document.getElementById('table-body');
|
||||
container.innerHTML = '';
|
||||
container.appendChild(table);
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function showError(msg) {
|
||||
document.getElementById('table-body').innerHTML = `<div class="error-msg">${msg}</div>`;
|
||||
}
|
||||
|
||||
function showLastUpdated(generatedAt) {
|
||||
const d = new Date(generatedAt);
|
||||
const formatted = d.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
|
||||
const sub = document.querySelector('.dashboard-subtitle');
|
||||
sub.innerHTML += `<br><small style="color:var(--text-tertiary)">Last updated: ${formatted}</small>`;
|
||||
}
|
||||
|
||||
async function init() {
|
||||
try {
|
||||
const res = await fetch('data.json');
|
||||
if (!res.ok) throw new Error(`Failed to load data.json (${res.status})`);
|
||||
const data = await res.json();
|
||||
const prs = data.prs || [];
|
||||
|
||||
if (prs.length === 0) {
|
||||
showError('No Claude Code PRs found yet. Run scripts/generate_claude_prs.py to generate data.');
|
||||
updateStats([]);
|
||||
return;
|
||||
}
|
||||
|
||||
showLastUpdated(data.generated_at);
|
||||
updateStats(prs);
|
||||
renderChart(prs);
|
||||
renderTable(prs);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
showError(`Failed to load PR data: ${err.message}. Run "python scripts/generate_claude_prs.py" to generate the data file.`);
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,969 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title id="page-title">Component - Claude Code Templates</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 tags -->
|
||||
<meta name="description" content="Explore Claude Code templates, agents, commands, and automation tools for enhanced development workflows.">
|
||||
<meta name="keywords" content="Claude Code, AI development, automation, templates, agents, commands">
|
||||
<link rel="canonical" href="" id="canonical-url">
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:title" content="Component - Claude Code Templates" id="og-title">
|
||||
<meta property="og:description" content="Explore Claude Code templates, agents, commands, and automation tools for enhanced development workflows." id="og-description">
|
||||
<meta property="og:image" content="https://aitmpl.com/images/social-preview.png">
|
||||
<meta property="og:url" content="" id="og-url">
|
||||
<meta property="og:type" content="website">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Component - Claude Code Templates" id="twitter-title">
|
||||
<meta name="twitter:description" content="Explore Claude Code templates, agents, commands, and automation tools for enhanced development workflows." id="twitter-description">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/images/social-preview.png">
|
||||
<meta name="twitter:url" content="" id="twitter-url">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
|
||||
<!-- Stylesheets -->
|
||||
<link rel="stylesheet" href="/css/component-page.css">
|
||||
<link rel="stylesheet" href="/css/styles.css">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono: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>
|
||||
</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 class="terminal-subtitle">
|
||||
<span class="status-dot"></span>
|
||||
Ready-to-use configurations for your <strong>Claude Code</strong> projects
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<a href="/index.html" class="header-btn anthropic-btn" title="Learn about Anthropic's Claude Code">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
|
||||
</svg>
|
||||
Home
|
||||
</a>
|
||||
<a href="/blog/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.23 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="component-page">
|
||||
<!-- Back Navigation -->
|
||||
<div class="back-navigation">
|
||||
<div class="container">
|
||||
<a href="/index.html" class="back-link">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.42-1.41L7.83 13H20v-2z"/>
|
||||
</svg>
|
||||
Back to Components
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading State -->
|
||||
<div class="loading-state" id="loadingState">
|
||||
<div class="loading-spinner"></div>
|
||||
<div class="loading-text">Loading component...</div>
|
||||
</div>
|
||||
|
||||
<!-- Error State -->
|
||||
<div class="error-state" id="errorState" style="display: none;">
|
||||
<div class="error-icon">❌</div>
|
||||
<h2>Component Not Found</h2>
|
||||
<p>The requested component could not be found. Please check the URL and try again.</p>
|
||||
<a href="/index.html" class="btn-primary">Back to Components</a>
|
||||
</div>
|
||||
|
||||
<!-- Component Content -->
|
||||
<div class="component-content" id="componentContent" style="display: none;">
|
||||
<div class="container">
|
||||
<!-- Component Header -->
|
||||
<div class="component-header">
|
||||
<div class="header-actions">
|
||||
<a id="githubLink" href="#" target="_blank" class="github-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.30.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>
|
||||
View on GitHub
|
||||
</a>
|
||||
<div class="share-dropdown" id="componentShareDropdown">
|
||||
<button class="share-btn" onclick="toggleComponentShareDropdown()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.6 20.92,19A2.84,2.84 0 0,0 18,16.08Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="share-options" id="componentShareOptions">
|
||||
<button class="share-option-btn" onclick="shareComponentOnTwitter()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
|
||||
</svg>
|
||||
Share on X
|
||||
</button>
|
||||
<button class="share-option-btn" onclick="shareComponentOnThreads()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12.186 24h-.007c-3.581-.024-6.334-1.205-8.184-3.509C2.35 18.44 1.5 15.586 1.472 12.01v-.017c.03-3.579.879-6.43 2.525-8.482C5.845 1.205 8.6.024 12.18 0h.014c2.746.02 5.043.725 6.826 2.098 1.677 1.29 2.858 3.13 3.509 5.467l-2.04.569c-1.104-3.96-3.898-5.984-8.304-6.015-2.91.022-5.11.936-6.54 2.717C4.307 6.504 3.616 8.914 3.589 12c.027 3.086.718 5.496 2.057 7.164 1.43 1.781 3.631 2.695 6.54 2.717 1.623-.02 3.094-.37 4.37-1.04.558-.29 1.029-.63 1.414-1.017.33-.33.602-.69.82-1.084.218-.395.381-.84.488-1.336.107-.495.16-1.044.16-1.644 0-.595-.053-1.144-.16-1.644-.107-.496-.27-.941-.488-1.336a3.79 3.79 0 00-.82-1.084 3.790 3.790 0 00-1.414-1.017c-1.276-.67-2.747-1.02-4.37-1.04h-.007c-1.623.02-3.094.37-4.37 1.04-.558.29-1.029.63-1.414 1.017-.33.33-.602.69-.82 1.084-.218.395-.381.84-.488 1.336-.107.5-.16 1.049-.16 1.644 0 .595.053 1.144.16 1.644.107.496.27.941.488 1.336.218.395.49.754.82 1.084.385.387.856.727 1.414 1.017 1.276.67 2.747 1.02 4.37 1.04z"/>
|
||||
</svg>
|
||||
Share on Threads
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="component-title-section">
|
||||
<div class="component-icon-large" id="componentIcon">🤖</div>
|
||||
<div class="component-title-info">
|
||||
<div class="component-title-with-downloads">
|
||||
<h1 id="componentTitle">Component Name</h1>
|
||||
<div class="component-download-badge" id="componentDownloadBadge" style="display: none;">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M5 20h14v-2H5v2zM19 9h-4V3H9v6H5l7 7 7-7z"/>
|
||||
</svg>
|
||||
<span id="downloadCount">0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="component-meta">
|
||||
<div class="component-type-badge-title" id="componentTypeBadge">AGENT</div>
|
||||
<span class="component-category-title" id="componentCategory">Development</span>
|
||||
<div class="component-validation-badge" id="componentValidationBadge" style="display: none;">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4z"/>
|
||||
</svg>
|
||||
<span id="validationScore">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add to Stack Button - Positioned in bottom-right of title section -->
|
||||
<button class="title-section-add-to-cart-btn" id="addToCartBtn">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M19,7H18V6A2,2 0 0,0 16,4H8A2,2 0 0,0 6,6V7H5A1,1 0 0,0 4,8A1,1 0 0,0 5,9H6V19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V9H19A1,1 0 0,0 20,8A1,1 0 0,0 19,7M8,6H16V7H8V6M16,19H8V9H16V19Z"/>
|
||||
</svg>
|
||||
<span>Add to Stack</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Tab Navigation (GitHub PR style) -->
|
||||
<div class="component-tabs" id="componentTabs">
|
||||
<button class="tab-btn active" data-tab="overview">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"/>
|
||||
</svg>
|
||||
Overview
|
||||
</button>
|
||||
<button class="tab-btn" data-tab="code">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M9.4,16.6L4.8,12L9.4,7.4L8,6L2,12L8,18L9.4,16.6M14.6,16.6L19.2,12L14.6,7.4L16,6L22,12L16,18L14.6,16.6Z"/>
|
||||
</svg>
|
||||
Code
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content -->
|
||||
<div class="tab-content-wrapper">
|
||||
|
||||
<!-- ===== OVERVIEW TAB (default - like PR Conversation) ===== -->
|
||||
<div class="tab-pane active" id="tab-overview">
|
||||
<div class="tab-content-grid">
|
||||
<!-- Main Content (left) -->
|
||||
<div class="tab-main">
|
||||
<!-- Description -->
|
||||
<section class="content-section">
|
||||
<h2>Overview</h2>
|
||||
<div class="component-description-content">
|
||||
<p id="componentDescription">Component description will be loaded here.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Quality Validation Section -->
|
||||
<section class="content-section validation-section" id="validationSection" style="display: none;">
|
||||
<h2>Quality Validation</h2>
|
||||
<div class="validation-compact-content">
|
||||
<div class="validation-summary">
|
||||
<div class="validation-score-display">
|
||||
<div class="score-circle" id="scoreCircle">
|
||||
<span id="scoreValue">--</span>
|
||||
</div>
|
||||
<div class="score-info">
|
||||
<span class="score-label">Quality Score</span>
|
||||
<span class="score-status" id="scoreStatus">Validated</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="validation-stats-inline">
|
||||
<div class="stat-inline">
|
||||
<span class="stat-icon">❌</span>
|
||||
<span class="stat-label">Errors:</span>
|
||||
<span class="stat-number" id="inlineErrors">0</span>
|
||||
</div>
|
||||
<div class="stat-inline">
|
||||
<span class="stat-icon">⚠️</span>
|
||||
<span class="stat-label">Warnings:</span>
|
||||
<span class="stat-number" id="inlineWarnings">0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="validation-checks" id="validationChecks"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Comments Section (Giscus - GitHub Discussions) -->
|
||||
<section class="content-section comments-section" id="commentsSection">
|
||||
<h2>Comments</h2>
|
||||
<p class="comments-description">Sign in with GitHub to leave a comment. Discussions are stored in the project's <a href="https://github.com/davila7/claude-code-templates/discussions" target="_blank">GitHub Discussions</a>.</p>
|
||||
<div id="giscusContainer"></div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar (right - like PR sidebar) -->
|
||||
<aside class="tab-sidebar">
|
||||
<!-- Quick Install -->
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-card-title">Quick Install</h3>
|
||||
<div class="quick-install-block">
|
||||
<code id="quickInstallCommand">npx claude-code-templates@latest --agent=example --yes</code>
|
||||
<button class="quick-copy-btn" onclick="copyToClipboard(document.getElementById('quickInstallCommand').textContent)">
|
||||
<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
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Metadata -->
|
||||
<div class="sidebar-card" id="metadataSection" style="display: none;">
|
||||
<h3 class="sidebar-card-title">Metadata</h3>
|
||||
<div class="sidebar-metadata-list">
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">Version</span>
|
||||
<span class="sidebar-meta-value" id="metadataVersion">--</span>
|
||||
</div>
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">Author</span>
|
||||
<span class="sidebar-meta-value" id="metadataAuthor">--</span>
|
||||
</div>
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">License</span>
|
||||
<span class="sidebar-meta-value" id="metadataLicense">--</span>
|
||||
</div>
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">Repository</span>
|
||||
<a class="metadata-link" id="metadataRepository" href="#" target="_blank" style="display: none;">View</a>
|
||||
<span class="sidebar-meta-value" id="metadataRepositoryNone" style="display: none;">--</span>
|
||||
</div>
|
||||
<div class="sidebar-meta-keywords-row">
|
||||
<span class="sidebar-meta-label">Keywords</span>
|
||||
<div class="metadata-keywords" id="metadataKeywords"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===== CODE TAB (like PR Files Changed) ===== -->
|
||||
<div class="tab-pane" id="tab-code">
|
||||
|
||||
<!-- ===== INSTALLATION TAB ===== -->
|
||||
<div class="tab-pane" id="tab-installation">
|
||||
<section class="content-section" id="installationSection">
|
||||
<h2>Installation</h2>
|
||||
|
||||
<!-- Basic Installation -->
|
||||
<div class="installation-method accordion-item">
|
||||
<button class="accordion-header active" onclick="toggleAccordion(this)">
|
||||
<span class="accordion-title">📦 Basic Installation</span>
|
||||
<svg class="accordion-icon" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M7,10L12,15L17,10H7Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="accordion-content" style="display: block;">
|
||||
<p class="method-description">Install this component locally in your project. Works with your existing Claude Code setup.</p>
|
||||
<div class="command-block">
|
||||
<div class="command-header">
|
||||
<span class="command-label">Terminal</span>
|
||||
</div>
|
||||
<div class="command-content">
|
||||
<code id="basicInstallCommand">npx claude-code-templates@latest --agent=example --yes</code>
|
||||
<button class="copy-btn" onclick="copyToClipboard(document.getElementById('basicInstallCommand').textContent)">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Global Agent Section (for agents only) -->
|
||||
<div class="installation-method accordion-item" id="globalAgentSection" style="display: none;">
|
||||
<button class="accordion-header" onclick="toggleAccordion(this)">
|
||||
<span class="accordion-title">🌍 Global Agent (Claude Code SDK)</span>
|
||||
<svg class="accordion-icon" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M7,10L12,15L17,10H7Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="accordion-content">
|
||||
<p class="method-description">Create a global AI agent accessible from anywhere with zero configuration. Perfect for automation and CI/CD workflows.</p>
|
||||
<div class="command-block">
|
||||
<div class="command-header">
|
||||
<span class="command-label">Terminal</span>
|
||||
</div>
|
||||
<div class="command-content">
|
||||
<code id="globalAgentCommand">npx claude-code-templates@latest --create-agent example</code>
|
||||
<button class="copy-btn" onclick="copyToClipboard(document.getElementById('globalAgentCommand').textContent)">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="usage-info">
|
||||
<h4>After installation, use from anywhere:</h4>
|
||||
<div class="command-block small">
|
||||
<div class="command-content">
|
||||
<code id="globalUsageCommand">example "your prompt here"</code>
|
||||
<button class="copy-btn" onclick="copyToClipboard(document.getElementById('globalUsageCommand').textContent)">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="features-list">
|
||||
<div class="feature">✅ Works in scripts, CI/CD, npm tasks</div>
|
||||
<div class="feature">✅ Auto-detects project context</div>
|
||||
<div class="feature">✅ Powered by Claude Code SDK</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cloud Sandbox Execution Section (for agents only) -->
|
||||
<div class="installation-method accordion-item" id="cloudSandboxSection" style="display: none;">
|
||||
<button class="accordion-header" onclick="toggleAccordion(this)">
|
||||
<span class="accordion-title">☁️ Run in Cloud Sandbox (Isolated Execution)</span>
|
||||
<svg class="accordion-icon" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M7,10L12,15L17,10H7Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="accordion-content">
|
||||
<p class="method-description">Execute Claude Code with this component in an isolated cloud environment. Perfect for testing complex projects without affecting your local system. Choose from multiple providers based on your needs.</p>
|
||||
|
||||
<!-- Provider Tabs -->
|
||||
<div class="sandbox-provider-tabs">
|
||||
<button class="sandbox-tab active" onclick="switchSandboxProvider('e2b')">
|
||||
<span class="tab-icon">🚀</span>
|
||||
<span class="tab-name">E2B</span>
|
||||
<span class="tab-badge">Full Stack</span>
|
||||
</button>
|
||||
<button class="sandbox-tab" onclick="switchSandboxProvider('cloudflare')">
|
||||
<span class="tab-icon">⚡</span>
|
||||
<span class="tab-name">Cloudflare</span>
|
||||
<span class="tab-badge">Serverless</span>
|
||||
</button>
|
||||
<button class="sandbox-tab" onclick="switchSandboxProvider('docker')">
|
||||
<span class="tab-icon">🐳</span>
|
||||
<span class="tab-name">Docker</span>
|
||||
<span class="tab-badge">Local</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- E2B Provider Content -->
|
||||
<div class="sandbox-provider-content" id="e2b-content" style="display: block;">
|
||||
<div class="provider-description">
|
||||
<h4>E2B Cloud Sandbox</h4>
|
||||
<p>Full-featured cloud development environment with complete isolation and all development tools pre-installed.</p>
|
||||
</div>
|
||||
|
||||
<div class="setup-requirements">
|
||||
<h4>🔑 Setup API Keys</h4>
|
||||
<p>Add to your .env file:</p>
|
||||
<div class="env-block">
|
||||
<code>ANTHROPIC_API_KEY=your_anthropic_key_here</code>
|
||||
<div class="env-comment"># Required for Claude Code access</div>
|
||||
<code>E2B_API_KEY=your_e2b_key_here</code>
|
||||
</div>
|
||||
<div class="api-key-links">
|
||||
<a href="https://console.anthropic.com" target="_blank" class="api-key-link">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
|
||||
</svg>
|
||||
Get Anthropic API Key
|
||||
</a>
|
||||
<a href="https://e2b.dev" target="_blank" class="api-key-link">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
|
||||
</svg>
|
||||
Get E2B API Key
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="command-block">
|
||||
<div class="command-header">
|
||||
<span class="command-label">Terminal</span>
|
||||
</div>
|
||||
<div class="command-content">
|
||||
<code id="e2bSandboxCommand">npx claude-code-templates@latest --sandbox e2b --agent=example --prompt "your development task"</code>
|
||||
<button class="copy-btn" onclick="copyToClipboard(document.getElementById('e2bSandboxCommand').textContent)">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sandbox-features-grid">
|
||||
<div class="feature-item">
|
||||
<span class="feature-icon">☁️</span>
|
||||
<span class="feature-text">Isolated cloud environment</span>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<span class="feature-icon">🔧</span>
|
||||
<span class="feature-text">Full development tools</span>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<span class="feature-icon">📊</span>
|
||||
<span class="feature-text">Real-time execution output</span>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<span class="feature-icon">🔒</span>
|
||||
<span class="feature-text">Secure & sandboxed</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cloudflare Provider Content -->
|
||||
<div class="sandbox-provider-content" id="cloudflare-content" style="display: none;">
|
||||
<div class="provider-description">
|
||||
<h4>Cloudflare Workers Sandbox</h4>
|
||||
<p>Serverless execution environment perfect for lightweight tasks, API development, and edge computing scenarios.</p>
|
||||
</div>
|
||||
|
||||
<div class="setup-requirements">
|
||||
<h4>🔑 Setup API Key</h4>
|
||||
<p>Add to your .env file:</p>
|
||||
<div class="env-block">
|
||||
<code>ANTHROPIC_API_KEY=your_anthropic_key_here</code>
|
||||
<div class="env-comment"># Required for Claude Code access</div>
|
||||
</div>
|
||||
<div class="api-key-links">
|
||||
<a href="https://console.anthropic.com" target="_blank" class="api-key-link">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
|
||||
</svg>
|
||||
Get Anthropic API Key
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="command-block">
|
||||
<div class="command-header">
|
||||
<span class="command-label">Terminal</span>
|
||||
</div>
|
||||
<div class="command-content">
|
||||
<code id="cloudflareSandboxCommand">npx claude-code-templates@latest --sandbox cloudflare --agent=example --prompt "your development task"</code>
|
||||
<button class="copy-btn" onclick="copyToClipboard(document.getElementById('cloudflareSandboxCommand').textContent)">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sandbox-features-grid">
|
||||
<div class="feature-item">
|
||||
<span class="feature-icon">⚡</span>
|
||||
<span class="feature-text">Ultra-fast serverless execution</span>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<span class="feature-icon">🌍</span>
|
||||
<span class="feature-text">Global edge network</span>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<span class="feature-icon">💰</span>
|
||||
<span class="feature-text">Cost-effective for small tasks</span>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<span class="feature-icon">🚀</span>
|
||||
<span class="feature-text">Instant cold starts</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Docker Provider Content -->
|
||||
<div class="sandbox-provider-content" id="docker-content" style="display: none;">
|
||||
<div class="provider-description">
|
||||
<h4>Docker Local Sandbox</h4>
|
||||
<p>Run Claude Code in a local Docker container. Perfect for offline development, custom environments, and maximum control over the execution environment.</p>
|
||||
</div>
|
||||
|
||||
<div class="setup-requirements">
|
||||
<h4>🔧 Prerequisites</h4>
|
||||
<div class="prerequisites-list">
|
||||
<div class="prerequisite-item">
|
||||
<span class="prerequisite-icon">🐳</span>
|
||||
<span>Docker Desktop installed and running</span>
|
||||
</div>
|
||||
<div class="prerequisite-item">
|
||||
<span class="prerequisite-icon">🔑</span>
|
||||
<span>Anthropic API key configured</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="api-key-links">
|
||||
<a href="https://www.docker.com/products/docker-desktop" target="_blank" class="api-key-link">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
|
||||
</svg>
|
||||
Download Docker Desktop
|
||||
</a>
|
||||
<a href="https://console.anthropic.com" target="_blank" class="api-key-link">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
|
||||
</svg>
|
||||
Get Anthropic API Key
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="command-block">
|
||||
<div class="command-header">
|
||||
<span class="command-label">Terminal</span>
|
||||
</div>
|
||||
<div class="command-content">
|
||||
<code id="dockerSandboxCommand">npx claude-code-templates@latest --sandbox docker --agent=example --prompt "your development task"</code>
|
||||
<button class="copy-btn" onclick="copyToClipboard(document.getElementById('dockerSandboxCommand').textContent)">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sandbox-features-grid">
|
||||
<div class="feature-item">
|
||||
<span class="feature-icon">🏠</span>
|
||||
<span class="feature-text">Fully local execution</span>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<span class="feature-icon">🔒</span>
|
||||
<span class="feature-text">Complete isolation</span>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<span class="feature-icon">⚙️</span>
|
||||
<span class="feature-text">Custom container images</span>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<span class="feature-icon">💾</span>
|
||||
<span class="feature-text">Local data & file access</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Provider Comparison -->
|
||||
<div class="provider-comparison">
|
||||
<h4>📊 Provider Comparison</h4>
|
||||
<table class="comparison-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Feature</th>
|
||||
<th>E2B</th>
|
||||
<th>Cloudflare</th>
|
||||
<th>Docker</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Execution Location</td>
|
||||
<td>☁️ Cloud</td>
|
||||
<td>🌍 Edge</td>
|
||||
<td>🏠 Local</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Setup Complexity</td>
|
||||
<td>Easy</td>
|
||||
<td>Easy</td>
|
||||
<td>Medium</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Best For</td>
|
||||
<td>Full stack projects</td>
|
||||
<td>Serverless & APIs</td>
|
||||
<td>Local dev & custom envs</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>API Keys Needed</td>
|
||||
<td>Anthropic + E2B</td>
|
||||
<td>Anthropic only</td>
|
||||
<td>Anthropic only</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div><!-- end tab-installation -->
|
||||
|
||||
<!-- ===== CODE TAB (like PR Files Changed) ===== -->
|
||||
<div class="tab-pane" id="tab-code">
|
||||
<section class="component-code-section">
|
||||
<h2>Component Code</h2>
|
||||
<div class="code-editor">
|
||||
<div class="code-header">
|
||||
<span class="code-language" id="codeLanguage">markdown</span>
|
||||
<button class="copy-code-btn" onclick="copyToClipboard(document.querySelector('#codeContent code').textContent)">
|
||||
<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 Code
|
||||
</button>
|
||||
</div>
|
||||
<div class="code-body">
|
||||
<div class="code-line-numbers" id="lineNumbers">
|
||||
<span>1</span>
|
||||
</div>
|
||||
<div class="code-content" id="codeContent">
|
||||
<pre><code class="language-markdown">Loading component code...</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div><!-- end tab-code -->
|
||||
|
||||
</div><!-- end tab-content-wrapper -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</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">© 2025 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>
|
||||
|
||||
<!-- Shopping Cart Sidebar -->
|
||||
<div id="shoppingCart" class="cart-sidebar">
|
||||
<div class="cart-content">
|
||||
<div class="cart-header">
|
||||
<div class="cart-header-main">
|
||||
<h3>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M7 18c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-0.16 0.27-0.25 0.58-0.25 0.96 0 1.1 0.9 2 2 2h12v-2H7.42c-0.14 0-0.25-0.11-0.25-0.25l0.03-0.12L8.1 13h7.45c0.75 0 1.41-0.41 1.75-1.03L21.7 4H5.21l-0.94-2H1zM17 18c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2z"/>
|
||||
</svg>
|
||||
Stack Builder
|
||||
</h3>
|
||||
<button class="cart-close" onclick="closeCart()">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Clear All Button - Subtle position -->
|
||||
<div class="cart-clear-section-prominent" id="cartClearProminent" style="display: none;">
|
||||
<button class="clear-all-btn-prominent" onclick="clearCart()">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M9,3V4H4V6H5V19A2,2 0 0,0 7,21H17A2,2 0 0,0 19,19V6H20V4H15V3H9M7,6H17V19H7V6M9,8V17H11V8H9M13,8V17H15V8H13Z"/>
|
||||
</svg>
|
||||
Clear All
|
||||
<span class="clear-count" id="clearCount">(0)</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cart-body">
|
||||
<div class="cart-empty" id="cartEmpty">
|
||||
<div class="empty-cart-icon">🛒</div>
|
||||
<p>Your stack is empty</p>
|
||||
<small>Add agents, commands, settings, hooks, or MCPs to build your development stack</small>
|
||||
</div>
|
||||
|
||||
<div class="cart-items" id="cartItems" style="display: none;">
|
||||
|
||||
<div class="cart-section">
|
||||
<h4>
|
||||
<span class="section-icon">🤖</span>
|
||||
Agents (<span id="agentsCount">0</span>)
|
||||
</h4>
|
||||
<div class="cart-section-items" id="agentsList"></div>
|
||||
</div>
|
||||
|
||||
<div class="cart-section">
|
||||
<h4>
|
||||
<span class="section-icon">⚡</span>
|
||||
Commands (<span id="commandsCount">0</span>)
|
||||
</h4>
|
||||
<div class="cart-section-items" id="commandsList"></div>
|
||||
</div>
|
||||
|
||||
<div class="cart-section">
|
||||
<h4>
|
||||
<span class="section-icon">⚙️</span>
|
||||
Settings (<span id="settingsCount">0</span>)
|
||||
</h4>
|
||||
<div class="cart-section-items" id="settingsList"></div>
|
||||
</div>
|
||||
|
||||
<div class="cart-section">
|
||||
<h4>
|
||||
<span class="section-icon">🪝</span>
|
||||
Hooks (<span id="hooksCount">0</span>)
|
||||
</h4>
|
||||
<div class="cart-section-items" id="hooksList"></div>
|
||||
</div>
|
||||
|
||||
<div class="cart-section">
|
||||
<h4>
|
||||
<span class="section-icon">🔌</span>
|
||||
MCPs (<span id="mcpsCount">0</span>)
|
||||
</h4>
|
||||
<div class="cart-section-items" id="mcpsList"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cart-footer" id="cartFooter" style="display: none;">
|
||||
<div class="command-preview">
|
||||
<div class="command-preview-header">
|
||||
<span>Generated Command:</span>
|
||||
</div>
|
||||
<div class="cart-command-container">
|
||||
<div class="command-code" id="generatedCommand">
|
||||
npx claude-code-templates@latest
|
||||
</div>
|
||||
<button class="cart-copy-overlay-btn" onclick="copyCartCommand()" title="Copy command">
|
||||
<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
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cart-instructions">
|
||||
<div class="instructions-header">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M13,9H11V7H13M13,17H11V11H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />
|
||||
</svg>
|
||||
<strong>Instructions</strong>
|
||||
</div>
|
||||
<p>Navigate to your project root and run the generated command. All selected components will be installed automatically.</p>
|
||||
</div>
|
||||
|
||||
<div class="cart-actions">
|
||||
<button class="copy-command-btn" onclick="copyCartCommand()">
|
||||
<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 Command
|
||||
</button>
|
||||
<div class="share-dropdown" id="shareDropdown">
|
||||
<button class="share-stack-btn" onclick="toggleShareDropdown()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.6 20.92,19A2.84,2.84 0 0,0 18,16.08Z"/>
|
||||
</svg>
|
||||
Share Stack
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor" class="dropdown-arrow">
|
||||
<path d="M7,10L12,15L17,10H7Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="share-options" id="shareOptions">
|
||||
<button class="share-option-btn" onclick="shareOnTwitter()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
|
||||
</svg>
|
||||
Share on X
|
||||
</button>
|
||||
<button class="share-option-btn" onclick="shareOnThreads()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12.186 24h-.007c-3.581-.024-6.334-1.205-8.184-3.509C2.35 18.44 1.5 15.586 1.472 12.01v-.017c.03-3.579.879-6.43 2.525-8.482C5.845 1.205 8.6.024 12.18 0h.014c2.746.02 5.043.725 6.826 2.098 1.677 1.29 2.858 3.13 3.509 5.467l-2.04.569c-1.104-3.96-3.898-5.984-8.304-6.015-2.91.022-5.11.936-6.54 2.717C4.307 6.504 3.616 8.914 3.589 12c.027 3.086.718 5.496 2.057 7.164 1.43 1.781 3.631 2.695 6.54 2.717 1.623-.02 3.094-.37 4.37-1.04.558-.29 1.029-.63 1.414-1.017.33-.33.602-.69.82-1.084.218-.395.381-.84.488-1.336.107-.495.16-1.044.16-1.644 0-.595-.053-1.144-.16-1.644-.107-.496-.27-.941-.488-1.336a3.79 3.79 0 00-.82-1.084 3.790 3.790 0 00-1.414-1.017c-1.276-.67-2.747-1.02-4.37-1.04h-.007c-1.623.02-3.094.37-4.37 1.04-.558.29-1.029.63-1.414 1.017-.33.33-.602.69-.82 1.084-.218.395-.381.84-.488 1.336-.107.5-.16 1.049-.16 1.644 0 .595.053 1.144.16 1.644.107.496.27.941.488 1.336.218.395.49.754.82 1.084.385.387.856.727 1.414 1.017 1.276.67 2.747 1.02 4.37 1.04z"/>
|
||||
</svg>
|
||||
Share on Threads
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cart Floating Button -->
|
||||
<div id="cartFloatingBtn" class="cart-floating-btn" onclick="openCart()" style="display: none;">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M7 18c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-0.16 0.27-0.25 0.58-0.25 0.96 0 1.1 0.9 2 2 2h12v-2H7.42c-0.14 0-0.25-0.11-0.25-0.25l0.03-0.12L8.1 13h7.45c0.75 0 1.41-0.41 1.75-1.03L21.7 4H5.21l-0.94-2H1zM17 18c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2z"/>
|
||||
</svg>
|
||||
<span class="cart-badge" id="cartBadge">0</span>
|
||||
</div>
|
||||
|
||||
<!-- Validation modal is now created dynamically in component-page.js -->
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="/js/data-loader.js"></script>
|
||||
<script src="/js/cart-manager.js"></script>
|
||||
<script src="/js/event-tracker.js"></script>
|
||||
<script src="/js/component-page.js"></script>
|
||||
<script src="/js/utils.js"></script>
|
||||
|
||||
<script>
|
||||
// Accordion toggle function
|
||||
function toggleAccordion(button) {
|
||||
const accordionItem = button.closest('.accordion-item');
|
||||
const content = accordionItem.querySelector('.accordion-content');
|
||||
const isActive = button.classList.contains('active');
|
||||
|
||||
// Get all accordion items in the same section
|
||||
const allAccordionItems = accordionItem.parentElement.querySelectorAll('.accordion-item');
|
||||
|
||||
// Close all other accordions
|
||||
allAccordionItems.forEach(item => {
|
||||
if (item !== accordionItem) {
|
||||
const otherButton = item.querySelector('.accordion-header');
|
||||
const otherContent = item.querySelector('.accordion-content');
|
||||
if (otherButton && otherContent) {
|
||||
otherButton.classList.remove('active');
|
||||
otherContent.style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle current accordion
|
||||
button.classList.toggle('active');
|
||||
|
||||
// Toggle content visibility with smooth animation
|
||||
if (isActive) {
|
||||
content.style.display = 'none';
|
||||
} else {
|
||||
content.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
// Validation modal functions are now in component-page.js
|
||||
// (Old inline modal code removed to use new modal system)
|
||||
|
||||
// Sandbox provider switcher function
|
||||
function switchSandboxProvider(provider) {
|
||||
console.log('Switching sandbox provider to:', provider);
|
||||
|
||||
// Update tab active states
|
||||
const tabs = document.querySelectorAll('.sandbox-tab');
|
||||
tabs.forEach(tab => {
|
||||
tab.classList.remove('active');
|
||||
});
|
||||
event.target.closest('.sandbox-tab').classList.add('active');
|
||||
|
||||
// Hide all provider contents
|
||||
const contents = document.querySelectorAll('.sandbox-provider-content');
|
||||
contents.forEach(content => {
|
||||
content.style.display = 'none';
|
||||
});
|
||||
|
||||
// Show selected provider content
|
||||
const selectedContent = document.getElementById(`${provider}-content`);
|
||||
if (selectedContent) {
|
||||
selectedContent.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
// Debug script loading and ensure proper initialization
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
console.log('=== Component Page Debug ===');
|
||||
console.log('Scripts loaded. Available objects:', {
|
||||
DataLoader: typeof DataLoader,
|
||||
ComponentPageManager: typeof ComponentPageManager,
|
||||
addToCart: typeof addToCart,
|
||||
CartManager: typeof CartManager,
|
||||
windowAddToCart: typeof window.addToCart
|
||||
});
|
||||
|
||||
// Ensure cart manager is initialized
|
||||
if (typeof CartManager !== 'undefined' && !window.cartManager) {
|
||||
console.log('Initializing CartManager...');
|
||||
window.cartManager = new CartManager();
|
||||
}
|
||||
|
||||
// Ensure component page manager is initialized
|
||||
if (typeof ComponentPageManager !== 'undefined' && !window.componentPageManager) {
|
||||
console.log('Initializing ComponentPageManager...');
|
||||
window.componentPageManager = new ComponentPageManager();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,303 @@
|
||||
{
|
||||
"metadata": {
|
||||
"unity-game-developer": {
|
||||
"tags": ["game-development", "unity", "c-sharp", "3d-graphics", "mobile-games", "physics", "optimization", "cross-platform"],
|
||||
"companies": ["unity-technologies", "epic-games", "ubisoft", "valve", "supercell"],
|
||||
"technologies": ["unity", "c-sharp", "blender", "photoshop", "visual-studio"]
|
||||
},
|
||||
"unreal-engine-developer": {
|
||||
"tags": ["game-development", "unreal-engine", "c-plus-plus", "blueprints", "aaa-games", "multiplayer", "rendering"],
|
||||
"companies": ["epic-games", "ubisoft", "valve", "activision", "cd-projekt"],
|
||||
"technologies": ["unreal-engine", "c-plus-plus", "blueprints", "visual-studio", "perforce"]
|
||||
},
|
||||
"c-pro": {
|
||||
"tags": ["c", "systems-programming", "embedded", "performance", "memory-management", "low-level"],
|
||||
"companies": ["valve", "epic-games", "unity-technologies", "nvidia", "intel"],
|
||||
"technologies": ["c", "gcc", "clang", "valgrind", "gdb", "make"]
|
||||
},
|
||||
"cpp-pro": {
|
||||
"tags": ["cpp", "systems-programming", "game-development", "performance", "modern-cpp", "templates"],
|
||||
"companies": ["epic-games", "valve", "unity-technologies", "ubisoft", "blizzard"],
|
||||
"technologies": ["cpp", "visual-studio", "clang", "cmake", "unreal-engine", "qt"]
|
||||
},
|
||||
"javascript-pro": {
|
||||
"tags": ["javascript", "web-development", "frontend", "backend", "nodejs", "react"],
|
||||
"companies": ["meta", "google", "netflix", "airbnb", "spotify"],
|
||||
"technologies": ["javascript", "nodejs", "react", "vue", "webpack", "npm"]
|
||||
},
|
||||
"python-pro": {
|
||||
"tags": ["python", "backend", "data-science", "ai", "machine-learning", "automation"],
|
||||
"companies": ["google", "netflix", "spotify", "uber", "dropbox"],
|
||||
"technologies": ["python", "django", "flask", "tensorflow", "pytorch", "pandas"]
|
||||
},
|
||||
"rust-pro": {
|
||||
"tags": ["rust", "systems-programming", "performance", "memory-safety", "web3", "blockchain"],
|
||||
"companies": ["valve", "meta", "microsoft", "mozilla", "dropbox"],
|
||||
"technologies": ["rust", "cargo", "tokio", "serde", "diesel", "webassembly"]
|
||||
},
|
||||
"mobile-developer": {
|
||||
"tags": ["mobile-development", "react-native", "flutter", "ios", "android", "cross-platform"],
|
||||
"companies": ["meta", "google", "uber", "airbnb", "spotify", "unity-technologies"],
|
||||
"technologies": ["react-native", "flutter", "swift", "kotlin", "xcode", "android-studio"]
|
||||
},
|
||||
"frontend-developer": {
|
||||
"tags": ["frontend", "web-development", "react", "ui-ux", "responsive-design", "performance"],
|
||||
"companies": ["meta", "google", "netflix", "airbnb", "spotify"],
|
||||
"technologies": ["react", "vue", "angular", "webpack", "sass", "typescript"]
|
||||
},
|
||||
"backend-architect": {
|
||||
"tags": ["backend", "architecture", "microservices", "scalability", "databases", "apis"],
|
||||
"companies": ["google", "amazon", "netflix", "uber", "spotify"],
|
||||
"technologies": ["nodejs", "python", "java", "postgresql", "redis", "docker"]
|
||||
}
|
||||
},
|
||||
"companies": {
|
||||
"openai": {
|
||||
"name": "OpenAI",
|
||||
"description": "AI development stack with GPT, DALL-E, and Whisper APIs",
|
||||
"logo": "🤖",
|
||||
"color": "#10a37f",
|
||||
"website": "https://openai.com"
|
||||
},
|
||||
"anthropic": {
|
||||
"name": "Anthropic",
|
||||
"description": "Claude AI integration and constitutional AI development",
|
||||
"logo": "🧠",
|
||||
"color": "#d97706",
|
||||
"website": "https://anthropic.com"
|
||||
},
|
||||
"stripe": {
|
||||
"name": "Stripe",
|
||||
"description": "Complete payment processing and financial infrastructure",
|
||||
"logo": "💳",
|
||||
"color": "#635bff",
|
||||
"website": "https://stripe.com"
|
||||
},
|
||||
"salesforce": {
|
||||
"name": "Salesforce",
|
||||
"description": "CRM development with Apex, Lightning, and Salesforce APIs",
|
||||
"logo": "☁️",
|
||||
"color": "#00a1e0",
|
||||
"website": "https://salesforce.com"
|
||||
},
|
||||
"shopify": {
|
||||
"name": "Shopify",
|
||||
"description": "E-commerce development with Shopify APIs and app ecosystem",
|
||||
"logo": "🛒",
|
||||
"color": "#95bf47",
|
||||
"website": "https://shopify.com"
|
||||
},
|
||||
"twilio": {
|
||||
"name": "Twilio",
|
||||
"description": "Communication APIs for SMS, voice, video, and messaging",
|
||||
"logo": "📞",
|
||||
"color": "#f22f46",
|
||||
"website": "https://twilio.com"
|
||||
},
|
||||
"aws": {
|
||||
"name": "Amazon Web Services",
|
||||
"description": "Cloud infrastructure and serverless development stack",
|
||||
"logo": "☁️",
|
||||
"color": "#ff9900",
|
||||
"website": "https://aws.amazon.com"
|
||||
},
|
||||
"github": {
|
||||
"name": "GitHub",
|
||||
"description": "Git workflow automation, Actions, and GitHub API integration",
|
||||
"logo": "🐙",
|
||||
"color": "#333",
|
||||
"website": "https://github.com"
|
||||
},
|
||||
"slack": {
|
||||
"name": "Slack",
|
||||
"description": "Slack bot development and workplace automation APIs",
|
||||
"logo": "💬",
|
||||
"color": "#4a154b",
|
||||
"website": "https://slack.com"
|
||||
},
|
||||
"discord": {
|
||||
"name": "Discord",
|
||||
"description": "Discord bot development and community management APIs",
|
||||
"logo": "🎮",
|
||||
"color": "#5865f2",
|
||||
"website": "https://discord.com"
|
||||
},
|
||||
"notion": {
|
||||
"name": "Notion",
|
||||
"description": "Notion API integration and workspace automation",
|
||||
"logo": "📝",
|
||||
"color": "#000000",
|
||||
"website": "https://notion.so"
|
||||
},
|
||||
"airtable": {
|
||||
"name": "Airtable",
|
||||
"description": "Database and workflow automation with Airtable APIs",
|
||||
"logo": "📊",
|
||||
"color": "#18bfff",
|
||||
"website": "https://airtable.com"
|
||||
},
|
||||
"sendgrid": {
|
||||
"name": "SendGrid",
|
||||
"description": "Email delivery and marketing automation APIs",
|
||||
"logo": "📧",
|
||||
"color": "#1a82e2",
|
||||
"website": "https://sendgrid.com"
|
||||
},
|
||||
"mongodb": {
|
||||
"name": "MongoDB",
|
||||
"description": "NoSQL database development with MongoDB Atlas APIs",
|
||||
"logo": "🍃",
|
||||
"color": "#47a248",
|
||||
"website": "https://mongodb.com"
|
||||
},
|
||||
"firebase": {
|
||||
"name": "Firebase",
|
||||
"description": "Google's mobile and web development platform",
|
||||
"logo": "🔥",
|
||||
"color": "#ffca28",
|
||||
"website": "https://firebase.google.com"
|
||||
},
|
||||
"supabase": {
|
||||
"name": "Supabase",
|
||||
"description": "Open source Firebase alternative with PostgreSQL",
|
||||
"logo": "⚡",
|
||||
"color": "#3ecf8e",
|
||||
"website": "https://supabase.com"
|
||||
},
|
||||
"vercel": {
|
||||
"name": "Vercel",
|
||||
"description": "Frontend deployment and serverless functions platform",
|
||||
"logo": "🔺",
|
||||
"color": "#000000",
|
||||
"website": "https://vercel.com"
|
||||
},
|
||||
"netlify": {
|
||||
"name": "Netlify",
|
||||
"description": "Web development platform with Git-based workflows",
|
||||
"logo": "🌐",
|
||||
"color": "#00ad9f",
|
||||
"website": "https://netlify.com"
|
||||
},
|
||||
"planetscale": {
|
||||
"name": "PlanetScale",
|
||||
"description": "Serverless MySQL database platform with branching",
|
||||
"logo": "🪐",
|
||||
"color": "#000000",
|
||||
"website": "https://planetscale.com"
|
||||
},
|
||||
"cloudflare": {
|
||||
"name": "Cloudflare",
|
||||
"description": "Edge computing and CDN development platform",
|
||||
"logo": "☁️",
|
||||
"color": "#f38020",
|
||||
"website": "https://cloudflare.com"
|
||||
},
|
||||
"unity-technologies": {
|
||||
"name": "Unity Technologies",
|
||||
"description": "Game development platform with Unity engine and APIs",
|
||||
"logo": "🎲",
|
||||
"color": "#000000",
|
||||
"website": "https://unity.com"
|
||||
},
|
||||
"epic-games": {
|
||||
"name": "Epic Games",
|
||||
"description": "Unreal Engine development and Epic Games Store APIs",
|
||||
"logo": "🎮",
|
||||
"color": "#313131",
|
||||
"website": "https://epicgames.com"
|
||||
},
|
||||
"linear": {
|
||||
"name": "Linear",
|
||||
"description": "Project management and issue tracking API integration",
|
||||
"logo": "📋",
|
||||
"color": "#5e6ad2",
|
||||
"website": "https://linear.app"
|
||||
},
|
||||
"figma": {
|
||||
"name": "Figma",
|
||||
"description": "Design tool integration and Figma API development",
|
||||
"logo": "🎨",
|
||||
"color": "#f24e1e",
|
||||
"website": "https://figma.com"
|
||||
},
|
||||
"adobe": {
|
||||
"name": "Adobe",
|
||||
"description": "Creative Cloud APIs and Adobe Experience Platform",
|
||||
"logo": "🎭",
|
||||
"color": "#ff0000",
|
||||
"website": "https://adobe.com"
|
||||
},
|
||||
"atlassian": {
|
||||
"name": "Atlassian",
|
||||
"description": "Jira, Confluence, and Bitbucket API development",
|
||||
"logo": "🔷",
|
||||
"color": "#0052cc",
|
||||
"website": "https://atlassian.com"
|
||||
},
|
||||
"hubspot": {
|
||||
"name": "HubSpot",
|
||||
"description": "CRM and marketing automation platform APIs",
|
||||
"logo": "🧲",
|
||||
"color": "#ff7a59",
|
||||
"website": "https://hubspot.com"
|
||||
},
|
||||
"mailchimp": {
|
||||
"name": "Mailchimp",
|
||||
"description": "Email marketing and audience management APIs",
|
||||
"logo": "🐵",
|
||||
"color": "#ffe01b",
|
||||
"website": "https://mailchimp.com"
|
||||
},
|
||||
"spotify": {
|
||||
"name": "Spotify",
|
||||
"description": "Music streaming and playlist management APIs",
|
||||
"logo": "🎵",
|
||||
"color": "#1db954",
|
||||
"website": "https://spotify.com"
|
||||
},
|
||||
"youtube": {
|
||||
"name": "YouTube",
|
||||
"description": "Video platform integration and YouTube Data API",
|
||||
"logo": "📺",
|
||||
"color": "#ff0000",
|
||||
"website": "https://youtube.com"
|
||||
},
|
||||
"twitter": {
|
||||
"name": "Twitter/X",
|
||||
"description": "Social media automation and Twitter API v2",
|
||||
"logo": "🐦",
|
||||
"color": "#1da1f2",
|
||||
"website": "https://twitter.com"
|
||||
}
|
||||
},
|
||||
"technologies": {
|
||||
"unity": {
|
||||
"name": "Unity Engine",
|
||||
"description": "Complete Unity development stack with agents and commands",
|
||||
"logo": "🎲",
|
||||
"color": "#000000",
|
||||
"website": "https://unity.com"
|
||||
},
|
||||
"unreal-engine": {
|
||||
"name": "Unreal Engine",
|
||||
"description": "Complete Unreal Engine development stack",
|
||||
"logo": "🎮",
|
||||
"color": "#313131",
|
||||
"website": "https://unrealengine.com"
|
||||
},
|
||||
"react": {
|
||||
"name": "React",
|
||||
"description": "Complete React development stack with modern tools",
|
||||
"logo": "⚛️",
|
||||
"color": "#61dafb",
|
||||
"website": "https://reactjs.org"
|
||||
},
|
||||
"nodejs": {
|
||||
"name": "Node.js",
|
||||
"description": "Complete Node.js backend development stack",
|
||||
"logo": "💚",
|
||||
"color": "#339933",
|
||||
"website": "https://nodejs.org"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
/* Featured Page Styles - Extends component-page.css */
|
||||
|
||||
/* Featured Logo in Header */
|
||||
.featured-logo {
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.featured-logo img {
|
||||
max-width: 280px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Partner Badge - Color variants for featured type */
|
||||
.partner-badge {
|
||||
background: linear-gradient(135deg, #6366f1 0%, #818cf8 100%);
|
||||
color: #ffffff;
|
||||
padding: 0.35rem 0.8rem;
|
||||
border-radius: 16px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
box-shadow:
|
||||
0 2px 8px rgba(99, 102, 241, 0.3),
|
||||
0 4px 16px rgba(99, 102, 241, 0.15),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.partner-badge:hover {
|
||||
transform: translateY(-1px) scale(1.02);
|
||||
filter: brightness(1.1);
|
||||
}
|
||||
|
||||
.partner-badge::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, transparent 50%);
|
||||
border-radius: 16px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Partner badge color variants */
|
||||
.partner-badge--partner {
|
||||
background: linear-gradient(135deg, #6366f1 0%, #818cf8 100%);
|
||||
box-shadow:
|
||||
0 2px 8px rgba(99, 102, 241, 0.3),
|
||||
0 4px 16px rgba(99, 102, 241, 0.15),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.partner-badge--database {
|
||||
background: linear-gradient(135deg, #059669 0%, #34d399 100%);
|
||||
box-shadow:
|
||||
0 2px 8px rgba(5, 150, 105, 0.3),
|
||||
0 4px 16px rgba(5, 150, 105, 0.15),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.partner-badge--toolkit {
|
||||
background: linear-gradient(135deg, #d97706 0%, #fbbf24 100%);
|
||||
box-shadow:
|
||||
0 2px 8px rgba(217, 119, 6, 0.3),
|
||||
0 4px 16px rgba(217, 119, 6, 0.15),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* External CTA Button in Sidebar */
|
||||
.sidebar-cta-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem;
|
||||
background: var(--accent-color);
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.sidebar-cta-btn:hover {
|
||||
background: var(--text-accent);
|
||||
color: #ffffff;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.sidebar-cta-btn svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Featured Article Content in Overview Tab */
|
||||
.featured-content h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-top: 2.5rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.4rem;
|
||||
border-bottom: 2px solid var(--accent-color);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.featured-content h2:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.featured-content h3 {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.featured-content p {
|
||||
font-size: 1rem;
|
||||
line-height: 1.7;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.featured-content ul,
|
||||
.featured-content ol {
|
||||
margin: 1rem 0;
|
||||
padding-left: 1.5rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.featured-content li {
|
||||
margin-bottom: 0.5rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.featured-content strong {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.featured-content a {
|
||||
color: var(--accent-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.featured-content a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.featured-content code {
|
||||
background: var(--bg-secondary);
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
color: var(--accent-color);
|
||||
font-family: 'Monaco', 'Menlo', monospace;
|
||||
}
|
||||
|
||||
.featured-content pre {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 8px;
|
||||
padding: 1.25rem;
|
||||
margin: 1.5rem 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.featured-content pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-primary);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.featured-content blockquote {
|
||||
background: var(--bg-tertiary);
|
||||
border-left: 3px solid var(--accent-color);
|
||||
padding: 1rem 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
.featured-content blockquote p {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.featured-content blockquote p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Feature List Cards */
|
||||
.featured-content .feature-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.featured-content .feature-item {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
padding: 1.25rem;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 8px;
|
||||
transition: transform 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.featured-content .feature-item:hover {
|
||||
transform: translateX(4px);
|
||||
border-color: var(--accent-color);
|
||||
}
|
||||
|
||||
.featured-content .feature-icon {
|
||||
font-size: 1.5rem;
|
||||
flex-shrink: 0;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: 8px;
|
||||
color: var(--accent-color);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.featured-content .feature-content h3 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
color: var(--text-primary);
|
||||
font-size: 1.05rem;
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.featured-content .feature-content p {
|
||||
margin: 0;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
/* Comparison Box */
|
||||
.featured-content .comparison-box {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 8px;
|
||||
padding: 1.25rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.featured-content .comparison-item {
|
||||
padding: 0.75rem 0;
|
||||
border-bottom: 1px solid var(--border-primary);
|
||||
}
|
||||
|
||||
.featured-content .comparison-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.featured-content .comparison-item strong {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
/* Info Note */
|
||||
.featured-content .info-note {
|
||||
background: var(--bg-secondary);
|
||||
border-left: 3px solid var(--accent-color);
|
||||
padding: 1rem 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
font-style: italic;
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
/* CTA Box */
|
||||
.featured-content .cta-box {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 8px;
|
||||
padding: 1.25rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
/* Website Tab iframe */
|
||||
.featured-website-frame {
|
||||
width: 100%;
|
||||
min-height: 600px;
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.website-tab-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.75rem 1rem;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.website-tab-url {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.85rem;
|
||||
font-family: 'Monaco', 'Menlo', monospace;
|
||||
}
|
||||
|
||||
.website-tab-open {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
color: var(--accent-color);
|
||||
text-decoration: none;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.website-tab-open:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Partner Info Card in Sidebar */
|
||||
.partner-info-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.partner-info-card img {
|
||||
max-width: 180px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.partner-info-description {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.featured-content .feature-item {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.featured-content .feature-icon {
|
||||
font-size: 1.2rem;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.featured-logo img {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.featured-website-frame {
|
||||
min-height: 400px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/* ===================================
|
||||
Partnership Banner - GLM Z.AI (Image Banner)
|
||||
=================================== */
|
||||
|
||||
.partnership-banner {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 1.5rem 3rem;
|
||||
width: 100%;
|
||||
background: #0d1117;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.partnership-sponsor-text {
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
}
|
||||
|
||||
.sponsor-label {
|
||||
color: #8b949e;
|
||||
font-size: 0.875rem;
|
||||
margin-right: 0.5rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.sponsor-name {
|
||||
color: #58a6ff;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.025em;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.sponsor-name:hover {
|
||||
color: #79c0ff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.partnership-banner a {
|
||||
display: block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.partnership-banner a:hover {
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
.partnership-banner img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-height: 200px;
|
||||
object-fit: contain;
|
||||
object-position: center;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 1024px) {
|
||||
.partnership-banner {
|
||||
padding: 1.25rem 2.5rem;
|
||||
}
|
||||
|
||||
.partnership-banner img {
|
||||
max-height: 180px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.partnership-banner {
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
|
||||
.partnership-sponsor-text {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.sponsor-label,
|
||||
.sponsor-name {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.partnership-banner img {
|
||||
max-height: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.partnership-banner {
|
||||
padding: 0.75rem 1.5rem;
|
||||
}
|
||||
|
||||
.partnership-sponsor-text {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.sponsor-label,
|
||||
.sponsor-name {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.partnership-banner img {
|
||||
max-height: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 360px) {
|
||||
.partnership-banner {
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.partnership-sponsor-text {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.sponsor-label,
|
||||
.sponsor-name {
|
||||
font-size: 0.7rem;
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sponsor-label {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.partnership-banner img {
|
||||
max-height: 100px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,648 @@
|
||||
/* Plugin Detail Page Styles */
|
||||
|
||||
.plugin-detail-page {
|
||||
min-height: calc(100vh - 200px);
|
||||
}
|
||||
|
||||
/* Back Navigation */
|
||||
.back-navigation {
|
||||
background: var(--bg-secondary);
|
||||
border-bottom: 1px solid var(--border-secondary);
|
||||
padding: 1rem 0;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: color 0.2s ease;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.back-link:hover {
|
||||
color: var(--text-accent);
|
||||
}
|
||||
|
||||
.back-link svg {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.back-link:hover svg {
|
||||
transform: translateX(-4px);
|
||||
}
|
||||
|
||||
.loading-state,
|
||||
.error-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 400px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 4px solid var(--border-secondary);
|
||||
border-top-color: var(--text-accent);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.error-state {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.error-state h2 {
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
margin-top: 1.5rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
background: var(--text-accent);
|
||||
color: var(--bg-primary);
|
||||
border-radius: 6px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.back-button:hover {
|
||||
background: var(--text-primary);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* Plugin Hero Section */
|
||||
.plugin-hero {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
padding: 3rem 0;
|
||||
border-bottom: 1px solid var(--border-secondary);
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.plugin-icon {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.plugin-icon .component-icon {
|
||||
font-size: 5rem;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-secondary);
|
||||
}
|
||||
|
||||
.plugin-header-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.plugin-meta {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.plugin-version {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-secondary);
|
||||
padding: 0.35rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.plugin-author {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.plugin-author::before {
|
||||
content: "👤";
|
||||
}
|
||||
|
||||
.plugin-hero h1 {
|
||||
font-size: 2.5rem;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 1rem;
|
||||
font-weight: 700;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.plugin-description {
|
||||
font-size: 1.1rem;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.plugin-keywords {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.plugin-hero .keyword-badge {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-accent);
|
||||
padding: 0.4rem 0.8rem;
|
||||
border-radius: 12px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
border: 1px solid var(--border-secondary);
|
||||
}
|
||||
|
||||
/* Installation Section */
|
||||
.installation-section {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.installation-section h2 {
|
||||
font-size: 1.8rem;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 1.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.installation-steps {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.step {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
background: var(--bg-secondary);
|
||||
padding: 1.5rem;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border-secondary);
|
||||
}
|
||||
|
||||
.step-number {
|
||||
flex-shrink: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: var(--text-accent);
|
||||
color: var(--bg-primary);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.step-content h3 {
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.command-box {
|
||||
position: relative;
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid var(--border-secondary);
|
||||
border-radius: 8px;
|
||||
padding: 1rem 1.25rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.command-box code {
|
||||
flex: 1;
|
||||
color: var(--text-accent);
|
||||
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
|
||||
font-size: 0.95rem;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.command-box .copy-btn {
|
||||
flex-shrink: 0;
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--text-accent);
|
||||
color: var(--bg-primary);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.command-box .copy-btn:hover {
|
||||
background: var(--text-primary);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Components Section */
|
||||
.components-section {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.components-section h2 {
|
||||
font-size: 1.8rem;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 1.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.components-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.component-category {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-secondary);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.category-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--border-secondary);
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.category-header h3 {
|
||||
color: var(--text-primary);
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.component-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.component-item {
|
||||
padding: 0.75rem 1rem;
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: 6px;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.component-item:hover {
|
||||
background: var(--bg-primary);
|
||||
}
|
||||
|
||||
.component-name {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.component-name::before {
|
||||
content: "▸";
|
||||
color: var(--text-accent);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.component-install-btn {
|
||||
flex-shrink: 0;
|
||||
padding: 0.4rem 0.75rem;
|
||||
background: var(--text-accent);
|
||||
color: var(--bg-primary);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.component-install-btn:hover {
|
||||
background: var(--text-primary);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.component-install-btn svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Related Plugins Section */
|
||||
.related-section {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.related-section h2 {
|
||||
font-size: 1.8rem;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 1.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.related-plugins {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.related-plugin-card {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-secondary);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
text-decoration: none;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.related-plugin-card:hover {
|
||||
border-color: var(--text-accent);
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.related-plugin-card h3 {
|
||||
color: var(--text-primary);
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.related-plugin-card p {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 1rem;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.related-plugin-stats {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--border-secondary);
|
||||
}
|
||||
|
||||
.related-plugin-stat {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* Install Command Modal */
|
||||
.modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
backdrop-filter: blur(4px);
|
||||
animation: fadeIn 0.2s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
position: relative;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-secondary);
|
||||
border-radius: 12px;
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
|
||||
animation: slideUp 0.3s ease;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 1.5rem;
|
||||
border-bottom: 1px solid var(--border-secondary);
|
||||
}
|
||||
|
||||
.modal-header h3 {
|
||||
color: var(--text-primary);
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
padding: 0.5rem;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-close:hover {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.modal-description {
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.modal-command-box {
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid var(--border-secondary);
|
||||
border-radius: 8px;
|
||||
padding: 1.25rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.modal-command-box code {
|
||||
color: var(--text-accent);
|
||||
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
|
||||
font-size: 0.95rem;
|
||||
word-break: break-all;
|
||||
line-height: 1.5;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.modal-copy-btn {
|
||||
padding: 0.75rem 1.25rem;
|
||||
background: var(--text-accent);
|
||||
color: var(--bg-primary);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.modal-copy-btn:hover {
|
||||
background: var(--text-primary);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.modal-copy-btn svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.plugin-hero {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.plugin-hero h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.plugin-meta {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.plugin-keywords {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.components-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.related-plugins {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.step {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.command-box {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.command-box .copy-btn {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.modal-header,
|
||||
.modal-body {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.modal-copy-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,776 @@
|
||||
/* Stack Page Styles */
|
||||
|
||||
/* Back Button */
|
||||
.back-button {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
color: #00ff88;
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
transition: all 0.2s ease;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.back-button:hover {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
transform: translateX(-2px);
|
||||
}
|
||||
|
||||
/* Stack Page Container */
|
||||
.stack-page {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* Stack Header */
|
||||
.stack-header {
|
||||
background: linear-gradient(135deg, rgba(0, 255, 136, 0.1), rgba(0, 255, 136, 0.05));
|
||||
border: 1px solid rgba(0, 255, 136, 0.3);
|
||||
border-radius: 12px;
|
||||
padding: 32px;
|
||||
margin-bottom: 32px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stack-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background:
|
||||
radial-gradient(circle at 20% 20%, rgba(0, 255, 136, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 80%, rgba(0, 255, 136, 0.05) 0%, transparent 50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.stack-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.stack-logo {
|
||||
font-size: 64px;
|
||||
line-height: 1;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stack-details h1 {
|
||||
font-size: 2.5rem;
|
||||
color: #00ff88;
|
||||
margin: 0 0 12px 0;
|
||||
font-weight: 700;
|
||||
text-shadow: 0 0 20px rgba(0, 255, 136, 0.3);
|
||||
}
|
||||
|
||||
.stack-description {
|
||||
font-size: 1.2rem;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
margin: 0 0 24px 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Stack Stats */
|
||||
.stack-stats {
|
||||
display: flex;
|
||||
gap: 32px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stack-stat {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
display: block;
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: #00ff88;
|
||||
line-height: 1;
|
||||
text-shadow: 0 0 10px rgba(0, 255, 136, 0.5);
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.stack-website {
|
||||
color: #00ff88;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border: 1px solid rgba(0, 255, 136, 0.3);
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.stack-website:hover {
|
||||
background: rgba(0, 255, 136, 0.1);
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
/* Stack Components */
|
||||
.stack-components {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.stack-section {
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.stack-section-header {
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.stack-section-header h3 {
|
||||
font-size: 1.8rem;
|
||||
color: #00ff88;
|
||||
margin: 0 0 8px 0;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.stack-section-header p {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.component-count {
|
||||
font-size: 0.875rem;
|
||||
color: rgba(0, 255, 136, 0.8);
|
||||
background: rgba(0, 255, 136, 0.1);
|
||||
padding: 4px 12px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(0, 255, 136, 0.2);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Stack Grid */
|
||||
.stack-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* Component Cards in Stack */
|
||||
.stack-component {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
transition: all 0.2s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.stack-component:hover {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-color: rgba(0, 255, 136, 0.3);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 32px rgba(0, 255, 136, 0.1);
|
||||
}
|
||||
|
||||
.stack-component .component-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.stack-component .component-header h4 {
|
||||
color: #00ff88;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
line-height: 1.3;
|
||||
flex: 1;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.stack-component .component-content {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.stack-component .component-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.stack-component .component-tag {
|
||||
background: rgba(0, 255, 136, 0.1);
|
||||
color: rgba(0, 255, 136, 0.8);
|
||||
font-size: 0.75rem;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(0, 255, 136, 0.2);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Add to Cart Button in Stack */
|
||||
.stack-component .add-to-cart-btn {
|
||||
background: rgba(0, 255, 136, 0.1);
|
||||
border: 1px solid rgba(0, 255, 136, 0.3);
|
||||
color: #00ff88;
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.875rem;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
transition: all 0.2s ease;
|
||||
font-weight: 500;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.stack-component .add-to-cart-btn:hover {
|
||||
background: rgba(0, 255, 136, 0.2);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(0, 255, 136, 0.2);
|
||||
}
|
||||
|
||||
.stack-component .add-to-cart-btn svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
/* Install All Section */
|
||||
.stack-install-all {
|
||||
background: linear-gradient(135deg, rgba(0, 255, 136, 0.1), rgba(0, 255, 136, 0.05));
|
||||
border: 1px solid rgba(0, 255, 136, 0.3);
|
||||
border-radius: 12px;
|
||||
padding: 32px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stack-install-all::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background:
|
||||
radial-gradient(circle at 50% 50%, rgba(0, 255, 136, 0.05) 0%, transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.stack-install-all h3 {
|
||||
color: #00ff88;
|
||||
font-size: 1.8rem;
|
||||
margin: 0 0 12px 0;
|
||||
font-weight: 600;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.stack-install-all p {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 1.1rem;
|
||||
margin: 0 0 24px 0;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.stack-install-all .command-line {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.stack-page {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.stack-header {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.stack-info {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.stack-logo {
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
.stack-details h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.stack-stats {
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.stack-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.stack-section-header h3 {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
font-size: 12px;
|
||||
padding: 6px 12px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loading States for Stack Pages */
|
||||
.stack-loading {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.stack-loading-spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 3px solid rgba(0, 255, 136, 0.2);
|
||||
border-top: 3px solid #00ff88;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto 20px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Company Stacks Carousel */
|
||||
.company-stacks-carousel {
|
||||
max-width: 1200px;
|
||||
margin: 20px auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.carousel-container {
|
||||
position: relative;
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.carousel-btn {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 165, 0, 0.3);
|
||||
color: #FFA500;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.carousel-btn:hover {
|
||||
background: rgba(255, 165, 0, 0.1);
|
||||
border-color: #FFA500;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.carousel-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.carousel-track {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
overflow: hidden;
|
||||
scroll-behavior: smooth;
|
||||
flex: 1;
|
||||
padding: 4px;
|
||||
cursor: grab;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.company-stack-card {
|
||||
background: rgba(20, 20, 20, 0.8);
|
||||
border: 1px solid rgba(255, 165, 0, 0.2);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-width: 260px;
|
||||
flex-shrink: 0;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.company-stack-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(135deg, rgba(255, 165, 0, 0.05), transparent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.company-stack-card:hover {
|
||||
background: rgba(30, 30, 30, 0.9);
|
||||
border-color: #FFA500;
|
||||
transform: translateY(-2px);
|
||||
box-shadow:
|
||||
0 4px 20px rgba(255, 165, 0, 0.2),
|
||||
inset 0 1px 0 rgba(255, 165, 0, 0.1);
|
||||
}
|
||||
|
||||
.company-stack-card:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
font-size: 1.8rem;
|
||||
line-height: 1;
|
||||
flex-shrink: 0;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
filter: grayscale(0.2);
|
||||
}
|
||||
|
||||
.company-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.company-info h3 {
|
||||
color: #FFA500;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
margin: 0 0 2px 0;
|
||||
font-family: 'Courier New', monospace;
|
||||
text-shadow: 0 0 10px rgba(255, 165, 0, 0.3);
|
||||
}
|
||||
|
||||
.company-info p {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 0.75rem;
|
||||
margin: 0;
|
||||
line-height: 1.3;
|
||||
font-family: 'Courier New', monospace;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.stack-arrow {
|
||||
font-size: 1.2rem;
|
||||
color: rgba(255, 165, 0, 0.6);
|
||||
transition: all 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.company-stack-card:hover .stack-arrow {
|
||||
color: #FFA500;
|
||||
transform: translateX(4px);
|
||||
text-shadow: 0 0 10px rgba(255, 165, 0, 0.5);
|
||||
}
|
||||
|
||||
/* View All Card Special Styling */
|
||||
.view-all-card {
|
||||
border: 1px solid rgba(255, 165, 0, 0.4) !important;
|
||||
background: linear-gradient(135deg, rgba(255, 165, 0, 0.1), rgba(255, 165, 0, 0.05)) !important;
|
||||
}
|
||||
|
||||
.view-all-card::before {
|
||||
background: linear-gradient(135deg, rgba(255, 165, 0, 0.15), transparent) !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.view-all-card:hover {
|
||||
border-color: #FFA500 !important;
|
||||
background: linear-gradient(135deg, rgba(255, 165, 0, 0.2), rgba(255, 165, 0, 0.1)) !important;
|
||||
box-shadow:
|
||||
0 4px 20px rgba(255, 165, 0, 0.3),
|
||||
inset 0 1px 0 rgba(255, 165, 0, 0.2) !important;
|
||||
}
|
||||
|
||||
.view-all-card .company-info h3 {
|
||||
color: #FFA500 !important;
|
||||
text-shadow: 0 0 15px rgba(255, 165, 0, 0.4) !important;
|
||||
}
|
||||
|
||||
/* Terminal-style scrollbar for carousel */
|
||||
.carousel-track::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.carousel-track {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
/* Responsive Design for Company Stacks */
|
||||
@media (max-width: 768px) {
|
||||
.company-stacks-carousel {
|
||||
padding: 16px 12px;
|
||||
}
|
||||
|
||||
.carousel-container {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.carousel-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.carousel-btn svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.company-stack-card {
|
||||
min-width: 220px;
|
||||
padding: 12px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
font-size: 1.5rem;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.company-info h3 {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.company-info p {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.stack-arrow {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* All Companies Page Styles */
|
||||
.all-companies-page .stack-logo {
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
.all-companies-content {
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.companies-category {
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.category-title {
|
||||
color: #FFA500;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
margin: 0 0 20px 0;
|
||||
font-family: 'Courier New', monospace;
|
||||
text-shadow: 0 0 10px rgba(255, 165, 0, 0.3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.category-title::before {
|
||||
content: '$';
|
||||
color: rgba(255, 165, 0, 0.6);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.companies-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.all-company-card {
|
||||
background: rgba(20, 20, 20, 0.8);
|
||||
border: 1px solid rgba(255, 165, 0, 0.2);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.all-company-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(135deg, rgba(255, 165, 0, 0.05), transparent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.all-company-card:hover {
|
||||
background: rgba(30, 30, 30, 0.9);
|
||||
border-color: #FFA500;
|
||||
transform: translateY(-2px);
|
||||
box-shadow:
|
||||
0 4px 20px rgba(255, 165, 0, 0.2),
|
||||
inset 0 1px 0 rgba(255, 165, 0, 0.1);
|
||||
}
|
||||
|
||||
.all-company-card:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.company-card-logo {
|
||||
font-size: 2rem;
|
||||
line-height: 1;
|
||||
flex-shrink: 0;
|
||||
width: 50px;
|
||||
text-align: center;
|
||||
filter: grayscale(0.1);
|
||||
}
|
||||
|
||||
.company-card-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.company-card-info h4 {
|
||||
color: #FFA500;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
margin: 0 0 4px 0;
|
||||
font-family: 'Courier New', monospace;
|
||||
text-shadow: 0 0 8px rgba(255, 165, 0, 0.3);
|
||||
}
|
||||
|
||||
.company-card-info p {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 0.85rem;
|
||||
margin: 0;
|
||||
line-height: 1.4;
|
||||
font-family: 'Courier New', monospace;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.company-card-arrow {
|
||||
font-size: 1.4rem;
|
||||
color: rgba(255, 165, 0, 0.6);
|
||||
transition: all 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.all-company-card:hover .company-card-arrow {
|
||||
color: #FFA500;
|
||||
transform: translateX(4px);
|
||||
text-shadow: 0 0 10px rgba(255, 165, 0, 0.5);
|
||||
}
|
||||
|
||||
/* Responsive Design for All Companies */
|
||||
@media (max-width: 768px) {
|
||||
.companies-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.all-company-card {
|
||||
padding: 14px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.company-card-logo {
|
||||
font-size: 1.6rem;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.company-card-info h4 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.company-card-info p {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.category-title {
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.companies-category {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Terminal loading dots animation */
|
||||
@keyframes terminalPulse {
|
||||
0%, 80%, 100% { opacity: 0.3; }
|
||||
40% { opacity: 1; }
|
||||
}
|
||||
@@ -0,0 +1,739 @@
|
||||
/* Dynamic Component Modal Styles */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
z-index: 1000;
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
.component-modal-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.component-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.component-type-badge {
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.component-details {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.component-description {
|
||||
padding: 20px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.installation-section {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.installation-section h4 {
|
||||
margin: 0 0 12px 0;
|
||||
color: var(--text-primary);
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.command-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
background: var(--bg-tertiary);
|
||||
padding: 12px 16px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.command-line code {
|
||||
flex: 1;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
||||
font-size: 14px;
|
||||
background: none;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background: var(--accent-color);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.copy-btn:hover {
|
||||
background: #d97706;
|
||||
}
|
||||
|
||||
.component-content {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.component-content h4 {
|
||||
margin: 0 0 12px 0;
|
||||
color: var(--text-primary);
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.component-preview {
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--border-color);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.component-preview pre {
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
overflow-x: auto;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.component-preview code {
|
||||
color: var(--text-primary);
|
||||
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.github-folder-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: #24292e;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
padding: 10px 16px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.github-folder-link:hover {
|
||||
background: #1b1f23;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
/* YAML Syntax Highlighting */
|
||||
.yaml-syntax {
|
||||
color: var(--text-primary);
|
||||
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/* YAML Comments */
|
||||
.yaml-comment {
|
||||
color: #6a9955;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* YAML Keys */
|
||||
.yaml-key {
|
||||
color: #4fc1ff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* YAML String Values */
|
||||
.yaml-string {
|
||||
color: #ce9178;
|
||||
}
|
||||
|
||||
/* YAML Numbers */
|
||||
.yaml-number {
|
||||
color: #b5cea8;
|
||||
}
|
||||
|
||||
/* YAML Booleans */
|
||||
.yaml-boolean {
|
||||
color: #569cd6;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* YAML Arrays/Lists */
|
||||
.yaml-array {
|
||||
color: #dcdcaa;
|
||||
}
|
||||
|
||||
/* YAML Special Characters */
|
||||
.yaml-punctuation {
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
/* YAML Indentation Guide */
|
||||
.yaml-indent {
|
||||
color: #404040;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* YAML Section Headers */
|
||||
.yaml-section {
|
||||
color: #c586c0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* YAML List Items */
|
||||
.yaml-list-item {
|
||||
color: #dcdcaa;
|
||||
}
|
||||
|
||||
/* YAML Path/File References */
|
||||
.yaml-path {
|
||||
color: #9cdcfe;
|
||||
}
|
||||
|
||||
/* Code editor with line numbers */
|
||||
.code-editor {
|
||||
display: flex;
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--border-primary);
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.code-line-numbers {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-secondary);
|
||||
padding: 16px 8px;
|
||||
text-align: right;
|
||||
user-select: none;
|
||||
border-right: 1px solid var(--border-primary);
|
||||
min-width: 40px;
|
||||
}
|
||||
|
||||
.code-line-numbers span {
|
||||
display: block;
|
||||
line-height: 1.5;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.code-content {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.code-content pre {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.code-content code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
/* Template files list styling */
|
||||
.template-files-list {
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.template-file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 0;
|
||||
border-bottom: 1px solid var(--border-secondary);
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.template-file-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
color: var(--text-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Template contribution modal styles */
|
||||
.template-types {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.template-type-section {
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.template-type-section h3 {
|
||||
color: var(--text-accent);
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.template-type-section > p {
|
||||
color: var(--text-secondary);
|
||||
margin: 0 0 20px 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Claude files info section */
|
||||
.claude-files-info {
|
||||
background: rgba(213, 116, 85, 0.1);
|
||||
border: 1px solid var(--text-accent);
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
.claude-files-info ul {
|
||||
margin: 0;
|
||||
padding-left: 20px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.claude-files-info li {
|
||||
margin: 8px 0;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.claude-files-info code {
|
||||
background: rgba(213, 116, 85, 0.2);
|
||||
color: var(--text-accent);
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Minimal Workflow Modal Styles */
|
||||
.workflow-modal .modal-content {
|
||||
max-width: 700px;
|
||||
width: 90vw;
|
||||
}
|
||||
|
||||
/* Method Tabs */
|
||||
.method-tabs {
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tab-link {
|
||||
padding: 12px 20px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
border-bottom: 2px solid transparent;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tab-link.active {
|
||||
color: var(--accent-color);
|
||||
border-bottom: 2px solid var(--accent-color);
|
||||
}
|
||||
|
||||
.tab-link:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* Method Content */
|
||||
.method-content {
|
||||
display: none;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.method-content.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.step-label {
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-top: 20px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.step-description {
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.section-label {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.section-description {
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
margin-bottom: 16px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Method Description */
|
||||
.method-description {
|
||||
margin-bottom: 20px;
|
||||
padding: 12px 16px;
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: 6px;
|
||||
border-left: 3px solid var(--accent-color);
|
||||
}
|
||||
|
||||
.method-description p {
|
||||
margin: 0;
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Prompt Indicator */
|
||||
.prompt-indicator {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
padding: 12px 16px;
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
border: 1px solid rgba(245, 158, 11, 0.3);
|
||||
border-radius: 6px;
|
||||
border-left: 3px solid var(--accent-color);
|
||||
}
|
||||
|
||||
.prompt-icon {
|
||||
font-size: 18px;
|
||||
flex-shrink: 0;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.prompt-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.prompt-content strong {
|
||||
display: block;
|
||||
color: var(--accent-color);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.prompt-content span {
|
||||
color: var(--text-primary);
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Command Containers */
|
||||
.command-container {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.command-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
margin-top: 12px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
/* Manual Steps */
|
||||
.manual-step {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.manual-step h5 {
|
||||
color: var(--text-primary);
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.step-description {
|
||||
margin-bottom: 12px;
|
||||
padding: 8px 12px;
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.step-description p {
|
||||
margin: 0;
|
||||
color: var(--text-secondary);
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.step-description code {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--accent-color);
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.yaml-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.command-scroll {
|
||||
flex: 1;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 12px 16px;
|
||||
overflow-x: auto;
|
||||
max-height: 120px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.workflow-command-text {
|
||||
color: var(--text-primary);
|
||||
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
||||
font-size: 13px;
|
||||
background: none;
|
||||
border: none;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.btn-copy-primary {
|
||||
background: var(--accent-color);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-width: auto;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn-copy-primary:hover {
|
||||
background: #b45309;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* YAML Section */
|
||||
.yaml-section {
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.yaml-accordion-header {
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: var(--bg-secondary);
|
||||
transition: background-color 0.2s ease;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.yaml-accordion-header:hover {
|
||||
background: var(--bg-tertiary);
|
||||
}
|
||||
|
||||
.yaml-accordion-arrow {
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.yaml-accordion-content {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease;
|
||||
}
|
||||
|
||||
.yaml-accordion-content.open {
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.yaml-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.yaml-preview {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
overflow-x: auto;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
color: var(--text-primary);
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.yaml-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.btn-copy-secondary {
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-width: auto;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn-copy-secondary:hover {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-primary);
|
||||
border-color: var(--text-secondary);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Button Styles */
|
||||
.btn-secondary {
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-primary);
|
||||
border-color: var(--text-secondary);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Modal Footer */
|
||||
.workflow-modal .modal-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
padding: 16px 20px;
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.workflow-modal .modal-content {
|
||||
width: 95vw;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.method-tabs {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.tab-link {
|
||||
padding: 10px 16px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,563 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Download Analytics - Claude Code Templates</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="Detailed download analytics and usage statistics for Claude Code Templates.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://aitmpl.com/download-stats.html">
|
||||
<meta property="og:title" content="Download Analytics - Claude Code Templates">
|
||||
<meta property="og:description" content="Detailed download analytics and usage statistics for Claude Code Templates.">
|
||||
<meta property="og:image" content="https://aitmpl.com/images/social-preview.png">
|
||||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="630">
|
||||
<meta property="og:site_name" content="Claude Code Templates">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Download Analytics - Claude Code Templates">
|
||||
<meta name="twitter:description" content="Detailed download analytics and usage statistics for Claude Code Templates.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/images/social-preview.png">
|
||||
|
||||
<link rel="stylesheet" href="css/styles.css">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<style>
|
||||
.analytics-page {
|
||||
min-height: 100vh;
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
.analytics-header {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.analytics-title {
|
||||
font-size: 2.5rem;
|
||||
color: var(--text-accent);
|
||||
margin-bottom: 1rem;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.analytics-subtitle {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: var(--text-info);
|
||||
text-decoration: none;
|
||||
margin-bottom: 2rem;
|
||||
padding: 0.5rem 0;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.back-link:hover {
|
||||
color: var(--text-accent);
|
||||
}
|
||||
|
||||
.metrics-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
font-size: 2.5rem;
|
||||
font-weight: bold;
|
||||
color: var(--text-accent);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.metric-change {
|
||||
font-size: 0.8rem;
|
||||
margin-top: 0.5rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.change-positive {
|
||||
color: var(--text-success);
|
||||
}
|
||||
|
||||
.change-neutral {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.charts-section {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
color: var(--text-accent);
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.chart-canvas {
|
||||
position: relative;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.detailed-tables {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
background: var(--bg-tertiary);
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--border-primary);
|
||||
}
|
||||
|
||||
.table-title {
|
||||
color: var(--text-accent);
|
||||
font-size: 1.1rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.table-content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.data-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.75rem 1rem;
|
||||
border-bottom: 1px solid var(--border-secondary);
|
||||
}
|
||||
|
||||
.data-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.data-label {
|
||||
color: var(--text-primary);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.data-value {
|
||||
color: var(--text-accent);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 60px;
|
||||
height: 4px;
|
||||
background: var(--border-primary);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: var(--text-accent);
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.charts-section,
|
||||
.detailed-tables {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.analytics-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.metrics-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="analytics-page">
|
||||
<div class="container">
|
||||
<a href="/" class="back-link">
|
||||
<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 Templates
|
||||
</a>
|
||||
|
||||
<div class="analytics-header">
|
||||
<h1 class="analytics-title">📊 Download Analytics</h1>
|
||||
<p class="analytics-subtitle">Comprehensive usage statistics and trends</p>
|
||||
</div>
|
||||
|
||||
<div class="metrics-grid" id="metricsGrid">
|
||||
<!-- Metrics will be populated by JavaScript -->
|
||||
</div>
|
||||
|
||||
<div class="charts-section">
|
||||
<div class="chart-container">
|
||||
<h3 class="chart-title">Downloads by Component Type</h3>
|
||||
<div class="chart-canvas">
|
||||
<canvas id="typeChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chart-container">
|
||||
<h3 class="chart-title">Downloads Over Time</h3>
|
||||
<div class="chart-canvas">
|
||||
<canvas id="timeChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detailed-tables">
|
||||
<div class="table-container">
|
||||
<div class="table-header">
|
||||
<h3 class="table-title">🏆 Popular Components</h3>
|
||||
</div>
|
||||
<div class="table-content" id="popularComponentsTable">
|
||||
<!-- Table will be populated by JavaScript -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<div class="table-header">
|
||||
<h3 class="table-title">📊 Category Breakdown</h3>
|
||||
</div>
|
||||
<div class="table-content" id="categoryBreakdownTable">
|
||||
<!-- Table will be populated by JavaScript -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stats-updated" style="margin-top: 2rem; text-align: center;">
|
||||
<span>Data last updated: <span id="lastUpdatedTime">Loading...</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let statsData = null;
|
||||
|
||||
// Load analytics data
|
||||
async function loadAnalyticsData() {
|
||||
try {
|
||||
const response = await fetch('/analytics/download-stats.json');
|
||||
statsData = await response.json();
|
||||
|
||||
populateMetrics();
|
||||
createCharts();
|
||||
populateTables();
|
||||
updateTimestamp();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to load analytics data:', error);
|
||||
showErrorState();
|
||||
}
|
||||
}
|
||||
|
||||
function populateMetrics() {
|
||||
const metricsGrid = document.getElementById('metricsGrid');
|
||||
const totalComponents = Object.keys(statsData.downloads_by_component).length;
|
||||
const todayDate = new Date().toISOString().split('T')[0];
|
||||
const todayDownloads = statsData.downloads_by_date[todayDate] || 0;
|
||||
|
||||
const metrics = [
|
||||
{
|
||||
value: statsData.total_downloads,
|
||||
label: 'Total Downloads',
|
||||
change: `+${statsData.data_points} tracked events`,
|
||||
changeType: 'positive'
|
||||
},
|
||||
{
|
||||
value: totalComponents,
|
||||
label: 'Unique Components',
|
||||
change: 'across all categories',
|
||||
changeType: 'neutral'
|
||||
},
|
||||
{
|
||||
value: todayDownloads,
|
||||
label: 'Downloads Today',
|
||||
change: new Date().toLocaleDateString(),
|
||||
changeType: 'positive'
|
||||
},
|
||||
{
|
||||
value: Object.keys(statsData.downloads_by_date).length,
|
||||
label: 'Active Days',
|
||||
change: 'with recorded activity',
|
||||
changeType: 'neutral'
|
||||
}
|
||||
];
|
||||
|
||||
metricsGrid.innerHTML = metrics.map(metric => `
|
||||
<div class="metric-card">
|
||||
<div class="metric-value">${metric.value}</div>
|
||||
<div class="metric-label">${metric.label}</div>
|
||||
<div class="metric-change change-${metric.changeType}">${metric.change}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function createCharts() {
|
||||
createTypeChart();
|
||||
createTimeChart();
|
||||
}
|
||||
|
||||
function createTypeChart() {
|
||||
const ctx = document.getElementById('typeChart').getContext('2d');
|
||||
const data = statsData.downloads_by_type;
|
||||
const labels = Object.keys(data).filter(key => data[key] > 0);
|
||||
const values = labels.map(key => data[key]);
|
||||
|
||||
new Chart(ctx, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: labels.map(label => label.charAt(0).toUpperCase() + label.slice(1)),
|
||||
datasets: [{
|
||||
data: values,
|
||||
backgroundColor: [
|
||||
'#d57455',
|
||||
'#f97316',
|
||||
'#a5d6ff',
|
||||
'#3fb950',
|
||||
'#f85149',
|
||||
'#7d8590'
|
||||
],
|
||||
borderWidth: 2,
|
||||
borderColor: '#0d1117'
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
labels: {
|
||||
color: '#c9d1d9',
|
||||
padding: 20
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createTimeChart() {
|
||||
const ctx = document.getElementById('timeChart').getContext('2d');
|
||||
const dates = Object.keys(statsData.downloads_by_date).sort();
|
||||
const values = dates.map(date => statsData.downloads_by_date[date]);
|
||||
|
||||
new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: dates.map(date => new Date(date).toLocaleDateString()),
|
||||
datasets: [{
|
||||
label: 'Downloads',
|
||||
data: values,
|
||||
borderColor: '#d57455',
|
||||
backgroundColor: 'rgba(213, 116, 85, 0.1)',
|
||||
borderWidth: 2,
|
||||
fill: true,
|
||||
tension: 0.4
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
color: '#c9d1d9'
|
||||
},
|
||||
grid: {
|
||||
color: '#30363d'
|
||||
}
|
||||
},
|
||||
x: {
|
||||
ticks: {
|
||||
color: '#c9d1d9'
|
||||
},
|
||||
grid: {
|
||||
color: '#30363d'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
labels: {
|
||||
color: '#c9d1d9'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function populateTables() {
|
||||
populatePopularComponents();
|
||||
populateCategoryBreakdown();
|
||||
}
|
||||
|
||||
function populatePopularComponents() {
|
||||
const table = document.getElementById('popularComponentsTable');
|
||||
const components = Object.entries(statsData.downloads_by_component)
|
||||
.sort(([,a], [,b]) => b - a)
|
||||
.slice(0, 10);
|
||||
|
||||
const maxDownloads = Math.max(...Object.values(statsData.downloads_by_component));
|
||||
|
||||
table.innerHTML = components.map(([name, downloads]) => {
|
||||
const percentage = (downloads / maxDownloads) * 100;
|
||||
return `
|
||||
<div class="data-row">
|
||||
<div class="data-label">${name}</div>
|
||||
<div style="display: flex; align-items: center;">
|
||||
<div class="data-value">${downloads}</div>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" style="width: ${percentage}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function populateCategoryBreakdown() {
|
||||
const table = document.getElementById('categoryBreakdownTable');
|
||||
const categories = Object.entries(statsData.downloads_by_type)
|
||||
.filter(([,count]) => count > 0)
|
||||
.sort(([,a], [,b]) => b - a);
|
||||
|
||||
const maxDownloads = Math.max(...Object.values(statsData.downloads_by_type));
|
||||
|
||||
table.innerHTML = categories.map(([type, downloads]) => {
|
||||
const percentage = (downloads / maxDownloads) * 100;
|
||||
const emoji = {
|
||||
'agent': '🤖',
|
||||
'command': '⚡',
|
||||
'mcp': '🔌',
|
||||
'template': '📦',
|
||||
'health-check': '🔍',
|
||||
'analytics': '📊'
|
||||
}[type] || '📄';
|
||||
|
||||
return `
|
||||
<div class="data-row">
|
||||
<div class="data-label">${emoji} ${type.charAt(0).toUpperCase() + type.slice(1)}</div>
|
||||
<div style="display: flex; align-items: center;">
|
||||
<div class="data-value">${downloads}</div>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" style="width: ${percentage}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function updateTimestamp() {
|
||||
const timestamp = new Date(statsData.last_updated);
|
||||
document.getElementById('lastUpdatedTime').textContent = timestamp.toLocaleString();
|
||||
}
|
||||
|
||||
function showErrorState() {
|
||||
document.getElementById('metricsGrid').innerHTML = `
|
||||
<div class="metric-card" style="grid-column: 1 / -1; text-align: center; padding: 2rem;">
|
||||
<div style="color: var(--text-error); font-size: 1.5rem; margin-bottom: 1rem;">⚠️</div>
|
||||
<div class="metric-label">Failed to load analytics data</div>
|
||||
<div class="metric-change change-neutral">Please try again later</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Load data when page loads
|
||||
loadAnalyticsData();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,59 @@
|
||||
<svg width="1200" height="630" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Background gradient -->
|
||||
<defs>
|
||||
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#0d1117;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#161b22;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="grad2" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" style="stop-color:#d97706;stop-opacity:0.3" />
|
||||
<stop offset="100%" style="stop-color:#d97706;stop-opacity:0.1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="1200" height="630" fill="url(#grad1)"/>
|
||||
|
||||
<!-- Grid pattern -->
|
||||
<g opacity="0.1">
|
||||
<line x1="0" y1="0" x2="1200" y2="630" stroke="#d97706" stroke-width="1"/>
|
||||
<line x1="0" y1="630" x2="1200" y2="0" stroke="#d97706" stroke-width="1"/>
|
||||
<line x1="600" y1="0" x2="600" y2="630" stroke="#d97706" stroke-width="2"/>
|
||||
<line x1="0" y1="315" x2="1200" y2="315" stroke="#d97706" stroke-width="2"/>
|
||||
</g>
|
||||
|
||||
<!-- Accent bar -->
|
||||
<rect x="0" y="0" width="1200" height="8" fill="url(#grad2)"/>
|
||||
|
||||
<!-- Main content -->
|
||||
<text x="600" y="200" font-family="monospace" font-size="72" font-weight="bold" fill="#d97706" text-anchor="middle">BrainGrid</text>
|
||||
|
||||
<text x="600" y="280" font-family="monospace" font-size="28" fill="#c9d1d9" text-anchor="middle">Product Management Agent</text>
|
||||
|
||||
<text x="600" y="340" font-family="monospace" font-size="24" fill="#7d8590" text-anchor="middle">for Claude Code</text>
|
||||
|
||||
<!-- Feature boxes -->
|
||||
<g transform="translate(200, 400)">
|
||||
<rect width="180" height="80" rx="8" fill="#21262d" stroke="#30363d" stroke-width="2"/>
|
||||
<text x="90" y="35" font-family="monospace" font-size="32" fill="#d97706" text-anchor="middle">💡</text>
|
||||
<text x="90" y="65" font-family="monospace" font-size="14" fill="#c9d1d9" text-anchor="middle">Planning</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(410, 400)">
|
||||
<rect width="180" height="80" rx="8" fill="#21262d" stroke="#30363d" stroke-width="2"/>
|
||||
<text x="90" y="35" font-family="monospace" font-size="32" fill="#d97706" text-anchor="middle">🗺️</text>
|
||||
<text x="90" y="65" font-family="monospace" font-size="14" fill="#c9d1d9" text-anchor="middle">UX Flows</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(620, 400)">
|
||||
<rect width="180" height="80" rx="8" fill="#21262d" stroke="#30363d" stroke-width="2"/>
|
||||
<text x="90" y="35" font-family="monospace" font-size="32" fill="#d97706" text-anchor="middle">⚡</text>
|
||||
<text x="90" y="65" font-family="monospace" font-size="14" fill="#c9d1d9" text-anchor="middle">AI Prompts</text>
|
||||
</g>
|
||||
|
||||
<!-- Footer -->
|
||||
<text x="600" y="570" font-family="monospace" font-size="18" fill="#7d8590" text-anchor="middle">Turn messy ideas into Claude Code-ready specifications</text>
|
||||
|
||||
<!-- Branding -->
|
||||
<text x="50" y="610" font-family="monospace" font-size="14" fill="#7d8590">aitmpl.com</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,484 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BrainGrid: Product Management Agent for Claude Code</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">
|
||||
|
||||
<meta name="description" content="BrainGrid is a Product Management Agent that works alongside Claude Code. Transform messy ideas into clear specifications, end-to-end UX flows, and Claude Code-ready prompts.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://aitmpl.com/featured/braingrid/">
|
||||
<meta property="og:title" content="BrainGrid: Product Management Agent for Claude Code">
|
||||
<meta property="og:description" content="Transform messy ideas into clear specifications and Claude Code-ready prompts with BrainGrid - the planning layer for your AI-powered development workflow.">
|
||||
<meta property="og:image" content="https://aitmpl.com/featured/braingrid/assets/braingrid-cover.svg">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/featured/braingrid/">
|
||||
<meta name="twitter:title" content="BrainGrid: Product Management Agent for Claude Code">
|
||||
<meta name="twitter:description" content="Transform messy ideas into clear specifications and Claude Code-ready prompts with BrainGrid.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/featured/braingrid/assets/braingrid-cover.svg">
|
||||
|
||||
<meta name="keywords" content="BrainGrid, Claude Code, Product Management Agent, AI Planning, Software Planning, Claude Code Integration, AI Development, Product Strategy">
|
||||
<link rel="canonical" href="https://aitmpl.com/featured/braingrid/">
|
||||
|
||||
<link rel="stylesheet" href="../../css/styles.css">
|
||||
<link rel="stylesheet" href="../../css/component-page.css">
|
||||
<link rel="stylesheet" href="../../css/featured-page.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 -->
|
||||
<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>
|
||||
</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 class="terminal-subtitle">
|
||||
<span class="status-dot"></span>
|
||||
Ready-to-use configurations for your <strong>Claude Code</strong> projects
|
||||
</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="../../blog/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="component-page">
|
||||
<!-- Back Navigation -->
|
||||
<div class="back-navigation">
|
||||
<div class="container">
|
||||
<a href="../../index.html" class="back-link">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.42-1.41L7.83 13H20v-2z"/>
|
||||
</svg>
|
||||
Back to Components
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Component Content -->
|
||||
<div class="component-content">
|
||||
<div class="container">
|
||||
<!-- Component Header -->
|
||||
<div class="component-header">
|
||||
<div class="header-actions">
|
||||
<div class="share-dropdown" id="componentShareDropdown">
|
||||
<button class="share-btn" onclick="toggleShareDropdown()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.6 20.92,19A2.84,2.84 0 0,0 18,16.08Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="share-options" id="shareOptions">
|
||||
<button class="share-option-btn" onclick="shareOnTwitter()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
|
||||
</svg>
|
||||
Share on X
|
||||
</button>
|
||||
<button class="share-option-btn" onclick="shareOnThreads()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12.186 24h-.007c-3.581-.024-6.334-1.205-8.184-3.509C2.35 18.44 1.5 15.586 1.472 12.01v-.017c.03-3.579.879-6.43 2.525-8.482C5.845 1.205 8.6.024 12.18 0h.014c2.746.02 5.043.725 6.826 2.098 1.677 1.29 2.858 3.13 3.509 5.467l-2.04.569c-1.104-3.96-3.898-5.984-8.304-6.015-2.91.022-5.11.936-6.54 2.717C4.307 6.504 3.616 8.914 3.589 12c.027 3.086.718 5.496 2.057 7.164 1.43 1.781 3.631 2.695 6.54 2.717 1.623-.02 3.094-.37 4.37-1.04.558-.29 1.029-.63 1.414-1.017.33-.33.602-.69.82-1.084.218-.395.381-.84.488-1.336.107-.495.16-1.044.16-1.644 0-.6-.053-1.149-.16-1.644a4.158 4.158 0 00-.488-1.336 3.996 3.996 0 00-.82-1.084 4.015 4.015 0 00-1.414-1.017c-1.276-.67-2.747-1.02-4.37-1.04h-.014c-1.623.02-3.094.37-4.37 1.04a4.015 4.015 0 00-1.414 1.017 3.996 3.996 0 00-.82 1.084 4.158 4.158 0 00-.488 1.336c-.107.495-.16 1.044-.16 1.644 0 .6.053 1.149.16 1.644.107.496.27.941.488 1.336.218.394.49.754.82 1.084.385.387.856.727 1.414 1.017 1.276.67 2.747 1.02 4.37 1.04z"/>
|
||||
</svg>
|
||||
Share on Threads
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="component-title-section">
|
||||
<div class="featured-logo">
|
||||
<img src="https://www.braingrid.ai/brand/full-logo-lime-on-transparent.png" alt="BrainGrid Logo">
|
||||
</div>
|
||||
<div class="component-title-info">
|
||||
<h1>BrainGrid</h1>
|
||||
<div class="component-meta">
|
||||
<div class="partner-badge partner-badge--partner">PARTNER</div>
|
||||
<span class="component-category-title">Product Management</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- External CTA Button -->
|
||||
<a href="https://braingrid.link/hsn8pFE" target="_blank" class="title-section-add-to-cart-btn" style="text-decoration: none;">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
|
||||
</svg>
|
||||
<span>Try BrainGrid</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Tab Navigation -->
|
||||
<div class="component-tabs">
|
||||
<button class="tab-btn active" data-tab="overview" onclick="switchTab('overview')">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"/>
|
||||
</svg>
|
||||
Overview
|
||||
</button>
|
||||
<button class="tab-btn" data-tab="website" onclick="switchTab('website')">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95a15.65 15.65 0 00-1.38-3.56A8.03 8.03 0 0118.92 8zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96zM4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2s.06 1.34.14 2H4.26zm.82 2h2.95c.32 1.25.78 2.45 1.38 3.56A7.987 7.987 0 015.08 16zm2.95-8H5.08a7.987 7.987 0 014.33-3.56A15.65 15.65 0 008.03 8zM12 19.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43 1.43-1.08 2.76-1.91 3.96zM14.34 14H9.66c-.09-.66-.16-1.32-.16-2s.07-1.35.16-2h4.68c.09.65.16 1.32.16 2s-.07 1.34-.16 2zm.25 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95a8.03 8.03 0 01-4.33 3.56zM16.36 14c.08-.66.14-1.32.14-2s-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2h-3.38z"/>
|
||||
</svg>
|
||||
Website
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content -->
|
||||
<div class="tab-content-wrapper">
|
||||
|
||||
<!-- Overview Tab -->
|
||||
<div class="tab-pane active" id="tab-overview">
|
||||
<div class="tab-content-grid">
|
||||
<!-- Main Content -->
|
||||
<div class="tab-main">
|
||||
<div class="featured-content">
|
||||
<h2>The Real Problem With AI Coding Is Not Code Generation</h2>
|
||||
<p>AI coding tools like Claude Code are extremely capable. With models like Opus 4.5, they can reason deeply, write high-quality code, and implement complex features.</p>
|
||||
<p>The problem shows up when builders try to turn a product idea into working software. As products grow, intent gets blurry, context fragments, and small changes ripple across the system in unexpected ways.</p>
|
||||
<p><strong>The limitation is not the model. It is the lack of product-level planning, structure, and strategy before any code is written.</strong></p>
|
||||
|
||||
<h2>BrainGrid Is Like Claude Code Plan Mode for Your Entire Product</h2>
|
||||
<p>Think of BrainGrid as Claude Code plan mode, but applied to your entire product, not just a single prompt.</p>
|
||||
|
||||
<div class="comparison-box">
|
||||
<div class="comparison-item">
|
||||
<strong>Claude Code</strong> helps you plan a feature.
|
||||
</div>
|
||||
<div class="comparison-item">
|
||||
<strong>BrainGrid</strong> helps you plan the product.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>BrainGrid acts as a Product Management Agent that sits upstream of Claude Code. It turns messy ideas into clear specifications, end-to-end UX flows, task breakdowns, and engineering-grade prompts that Claude Code can execute reliably.</p>
|
||||
|
||||
<h2>How BrainGrid Works With Claude Code</h2>
|
||||
<p>BrainGrid is not a replacement for Claude Code—it works alongside it. It is a planning and orchestration layer that makes Claude Code more effective.</p>
|
||||
|
||||
<div class="feature-list">
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">💡</div>
|
||||
<div class="feature-content">
|
||||
<h3>Clarifying Questions</h3>
|
||||
<p>Asks clarifying questions to surface edge cases and hidden complexity</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">🗺</div>
|
||||
<div class="feature-content">
|
||||
<h3>End-to-End UX Flows</h3>
|
||||
<p>Maps end-to-end UX flows so features are planned as complete experiences</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">📋</div>
|
||||
<div class="feature-content">
|
||||
<h3>Task Breakdown</h3>
|
||||
<p>Breaks large ideas into small, scoped tasks with goals and acceptance criteria</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">⚡</div>
|
||||
<div class="feature-content">
|
||||
<h3>Claude Code-Ready Prompts</h3>
|
||||
<p>Generates Claude Code-ready prompts with the right context attached</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">🛡</div>
|
||||
<div class="feature-content">
|
||||
<h3>Prevent Regressions</h3>
|
||||
<p>Helps prevent regressions by ensuring new work fits the existing system</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="info-note">Tasks can be sent to Claude Code via MCP, CLI or copied directly into the Claude Code workflow.</p>
|
||||
|
||||
<h2>Claude Code Integration</h2>
|
||||
<p>BrainGrid seamlessly integrates with your Claude Code workflow. Once BrainGrid generates your product specifications and task breakdowns, you can:</p>
|
||||
<ul>
|
||||
<li>Export tasks directly to Claude Code via MCP integration</li>
|
||||
<li>Copy generated prompts into your Claude Code sessions</li>
|
||||
<li>Use the CLI to sync tasks between BrainGrid and Claude Code</li>
|
||||
</ul>
|
||||
<div class="cta-box">
|
||||
<p><strong>Claude Code integration guide:</strong></p>
|
||||
<a href="https://docs.braingrid.ai/claude-code" target="_blank">https://docs.braingrid.ai/claude-code</a>
|
||||
</div>
|
||||
|
||||
<h2>Built for Production-Ready Apps, Not Just Prototypes</h2>
|
||||
<p>BrainGrid is designed for builders creating real AI-native SaaS products with:</p>
|
||||
<ul>
|
||||
<li>Authentication and multi-tenancy</li>
|
||||
<li>Billing and subscriptions</li>
|
||||
<li>AI features like chat, agents, and search</li>
|
||||
<li>Ongoing feature development and iteration</li>
|
||||
</ul>
|
||||
<p>It helps builders move beyond fragile demos and ship software that can actually scale.</p>
|
||||
|
||||
<h2>Who BrainGrid Is For</h2>
|
||||
<p>AI coding tools have created a new kind of builder. Domain experts, creators, and entrepreneurs who have never written code are now building software through natural language.</p>
|
||||
<p><strong>BrainGrid is for:</strong></p>
|
||||
<ul>
|
||||
<li>Non-technical founders building real products, not just prototypes</li>
|
||||
<li>Vibe coders hitting complexity walls as their apps grow</li>
|
||||
<li>AI builders who need help planning scope, sequencing work, and evolving apps without breaking what already works</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Comments Section -->
|
||||
<section class="content-section comments-section">
|
||||
<h2>Comments</h2>
|
||||
<p class="comments-description">Sign in with GitHub to leave a comment. Discussions are stored in the project's <a href="https://github.com/davila7/claude-code-templates/discussions" target="_blank">GitHub Discussions</a>.</p>
|
||||
<div id="giscusContainer"></div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<aside class="tab-sidebar">
|
||||
<!-- Partner Info -->
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-card-title">About BrainGrid</h3>
|
||||
<div class="partner-info-card">
|
||||
<img src="https://www.braingrid.ai/brand/full-logo-lime-on-transparent.png" alt="BrainGrid">
|
||||
<p class="partner-info-description">Product Management Agent for Claude Code. Transform messy ideas into clear specifications and Claude Code-ready prompts.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CTA -->
|
||||
<div class="sidebar-card">
|
||||
<a href="https://braingrid.link/hsn8pFE" target="_blank" class="sidebar-cta-btn">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
|
||||
</svg>
|
||||
Try BrainGrid Free
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Metadata -->
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-card-title">Details</h3>
|
||||
<div class="sidebar-metadata-list">
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">Type</span>
|
||||
<span class="sidebar-meta-value">Partner</span>
|
||||
</div>
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">Category</span>
|
||||
<span class="sidebar-meta-value">Planning</span>
|
||||
</div>
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">Website</span>
|
||||
<span class="sidebar-meta-value"><a href="https://braingrid.ai" target="_blank" style="color: var(--accent-color); text-decoration: none;">braingrid.ai</a></span>
|
||||
</div>
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">Integration</span>
|
||||
<span class="sidebar-meta-value">MCP, CLI</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Links -->
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-card-title">Links</h3>
|
||||
<div class="sidebar-links">
|
||||
<a href="https://docs.braingrid.ai/claude-code" target="_blank" class="sidebar-link-item">
|
||||
<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>
|
||||
Integration Docs
|
||||
</a>
|
||||
<a href="https://braingrid.ai" target="_blank" class="sidebar-link-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95a15.65 15.65 0 00-1.38-3.56A8.03 8.03 0 0118.92 8zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96z"/>
|
||||
</svg>
|
||||
braingrid.ai
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Website Tab -->
|
||||
<div class="tab-pane" id="tab-website">
|
||||
<div class="website-tab-header">
|
||||
<span class="website-tab-url">braingrid.ai</span>
|
||||
<a href="https://braingrid.ai" target="_blank" class="website-tab-open">
|
||||
Open in new tab
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<iframe src="https://braingrid.ai" class="featured-website-frame" title="BrainGrid Website" loading="lazy"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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">© 2026 Claude Code Templates. Open source project.</p>
|
||||
<div class="footer-links">
|
||||
<a href="../../blog/index.html" 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>
|
||||
Blog
|
||||
</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>
|
||||
|
||||
<script>
|
||||
// Tab switching
|
||||
function switchTab(tabName) {
|
||||
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
|
||||
document.querySelectorAll('.tab-pane').forEach(pane => pane.classList.remove('active'));
|
||||
document.querySelector(`[data-tab="${tabName}"]`).classList.add('active');
|
||||
document.getElementById(`tab-${tabName}`).classList.add('active');
|
||||
}
|
||||
|
||||
// Share dropdown
|
||||
function toggleShareDropdown() {
|
||||
document.getElementById('componentShareDropdown').classList.toggle('open');
|
||||
}
|
||||
|
||||
function shareOnTwitter() {
|
||||
const text = encodeURIComponent('BrainGrid: Product Management Agent for Claude Code - Transform messy ideas into clear specifications and Claude Code-ready prompts');
|
||||
const url = encodeURIComponent('https://aitmpl.com/featured/braingrid/');
|
||||
window.open(`https://twitter.com/intent/tweet?text=${text}&url=${url}`, '_blank');
|
||||
document.getElementById('componentShareDropdown').classList.remove('open');
|
||||
}
|
||||
|
||||
function shareOnThreads() {
|
||||
const text = encodeURIComponent('BrainGrid: Product Management Agent for Claude Code\n\nhttps://aitmpl.com/featured/braingrid/');
|
||||
window.open(`https://www.threads.net/intent/post?text=${text}`, '_blank');
|
||||
document.getElementById('componentShareDropdown').classList.remove('open');
|
||||
}
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!e.target.closest('.share-dropdown')) {
|
||||
document.getElementById('componentShareDropdown').classList.remove('open');
|
||||
}
|
||||
});
|
||||
|
||||
// Giscus comments
|
||||
function loadGiscus() {
|
||||
const container = document.getElementById('giscusContainer');
|
||||
if (!container) return;
|
||||
const script = document.createElement('script');
|
||||
script.src = 'https://giscus.app/client.js';
|
||||
script.setAttribute('data-repo', 'davila7/claude-code-templates');
|
||||
script.setAttribute('data-repo-id', 'R_kgDONN2YhQ');
|
||||
script.setAttribute('data-category', 'Component Comments');
|
||||
script.setAttribute('data-category-id', 'DIC_kwDONN2Yhc4CmFij');
|
||||
script.setAttribute('data-mapping', 'specific');
|
||||
script.setAttribute('data-term', 'featured/braingrid');
|
||||
script.setAttribute('data-strict', '0');
|
||||
script.setAttribute('data-reactions-enabled', '1');
|
||||
script.setAttribute('data-emit-metadata', '0');
|
||||
script.setAttribute('data-input-position', 'top');
|
||||
script.setAttribute('data-theme', 'dark_dimmed');
|
||||
script.setAttribute('data-lang', 'en');
|
||||
script.setAttribute('crossorigin', 'anonymous');
|
||||
script.async = true;
|
||||
container.appendChild(script);
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', loadGiscus);
|
||||
} else {
|
||||
loadGiscus();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,541 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ClaudeKit: AI Agents & Skills for Claude Code</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">
|
||||
|
||||
<meta name="description" content="ClaudeKit is the fastest path from idea to production. AI agents & skills that research, plan, code, test, and document so you can ship features in hours instead of weeks.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://aitmpl.com/featured/claudekit/">
|
||||
<meta property="og:title" content="ClaudeKit: AI Agents & Skills for Claude Code">
|
||||
<meta property="og:description" content="The fastest path from idea to production. AI agents & skills built on Claude Code that research, plan, code, test, and document so you can ship features in hours.">
|
||||
<meta property="og:image" content="https://docs.claudekit.cc/logo-horizontal.png">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/featured/claudekit/">
|
||||
<meta name="twitter:title" content="ClaudeKit: AI Agents & Skills for Claude Code">
|
||||
<meta name="twitter:description" content="The fastest path from idea to production. AI agents & skills built on Claude Code that research, plan, code, test, and document.">
|
||||
<meta name="twitter:image" content="https://docs.claudekit.cc/logo-horizontal.png">
|
||||
|
||||
<meta name="keywords" content="ClaudeKit, Claude Code, AI Agents, AI Skills, Software Development, Claude Code Workflow, AI Engineering, Marketing Automation, Code Review, Automated Testing">
|
||||
<link rel="canonical" href="https://aitmpl.com/featured/claudekit/">
|
||||
|
||||
<link rel="stylesheet" href="../../css/styles.css">
|
||||
<link rel="stylesheet" href="../../css/component-page.css">
|
||||
<link rel="stylesheet" href="../../css/featured-page.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 -->
|
||||
<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>
|
||||
</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 class="terminal-subtitle">
|
||||
<span class="status-dot"></span>
|
||||
Ready-to-use configurations for your <strong>Claude Code</strong> projects
|
||||
</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="../../blog/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="component-page">
|
||||
<!-- Back Navigation -->
|
||||
<div class="back-navigation">
|
||||
<div class="container">
|
||||
<a href="../../index.html" class="back-link">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.42-1.41L7.83 13H20v-2z"/>
|
||||
</svg>
|
||||
Back to Components
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Component Content -->
|
||||
<div class="component-content">
|
||||
<div class="container">
|
||||
<!-- Component Header -->
|
||||
<div class="component-header">
|
||||
<div class="header-actions">
|
||||
<div class="share-dropdown" id="componentShareDropdown">
|
||||
<button class="share-btn" onclick="toggleShareDropdown()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.6 20.92,19A2.84,2.84 0 0,0 18,16.08Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="share-options" id="shareOptions">
|
||||
<button class="share-option-btn" onclick="shareOnTwitter()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
|
||||
</svg>
|
||||
Share on X
|
||||
</button>
|
||||
<button class="share-option-btn" onclick="shareOnThreads()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12.186 24h-.007c-3.581-.024-6.334-1.205-8.184-3.509C2.35 18.44 1.5 15.586 1.472 12.01v-.017c.03-3.579.879-6.43 2.525-8.482C5.845 1.205 8.6.024 12.18 0h.014c2.746.02 5.043.725 6.826 2.098 1.677 1.29 2.858 3.13 3.509 5.467l-2.04.569c-1.104-3.96-3.898-5.984-8.304-6.015-2.91.022-5.11.936-6.54 2.717C4.307 6.504 3.616 8.914 3.589 12c.027 3.086.718 5.496 2.057 7.164 1.43 1.781 3.631 2.695 6.54 2.717 1.623-.02 3.094-.37 4.37-1.04.558-.29 1.029-.63 1.414-1.017.33-.33.602-.69.82-1.084.218-.395.381-.84.488-1.336.107-.495.16-1.044.16-1.644 0-.6-.053-1.149-.16-1.644a4.158 4.158 0 00-.488-1.336 3.996 3.996 0 00-.82-1.084 4.015 4.015 0 00-1.414-1.017c-1.276-.67-2.747-1.02-4.37-1.04h-.014c-1.623.02-3.094.37-4.37 1.04a4.015 4.015 0 00-1.414 1.017 3.996 3.996 0 00-.82 1.084 4.158 4.158 0 00-.488 1.336c-.107.495-.16 1.044-.16 1.644 0 .6.053 1.149.16 1.644.107.496.27.941.488 1.336.218.394.49.754.82 1.084.385.387.856.727 1.414 1.017 1.276.67 2.747 1.02 4.37 1.04z"/>
|
||||
</svg>
|
||||
Share on Threads
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="component-title-section">
|
||||
<div class="featured-logo">
|
||||
<img src="https://docs.claudekit.cc/logo-horizontal.png" alt="ClaudeKit Logo">
|
||||
</div>
|
||||
<div class="component-title-info">
|
||||
<h1>ClaudeKit</h1>
|
||||
<div class="component-meta">
|
||||
<div class="partner-badge partner-badge--toolkit">TOOLKIT</div>
|
||||
<span class="component-category-title">AI Engineering</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- External CTA Button -->
|
||||
<a href="https://claudekit.cc" target="_blank" class="title-section-add-to-cart-btn" style="text-decoration: none;">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
|
||||
</svg>
|
||||
<span>Get ClaudeKit</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Tab Navigation -->
|
||||
<div class="component-tabs">
|
||||
<button class="tab-btn active" data-tab="overview" onclick="switchTab('overview')">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"/>
|
||||
</svg>
|
||||
Overview
|
||||
</button>
|
||||
<button class="tab-btn" data-tab="website" onclick="switchTab('website')">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95a15.65 15.65 0 00-1.38-3.56A8.03 8.03 0 0118.92 8zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96zM4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2s.06 1.34.14 2H4.26zm.82 2h2.95c.32 1.25.78 2.45 1.38 3.56A7.987 7.987 0 015.08 16zm2.95-8H5.08a7.987 7.987 0 014.33-3.56A15.65 15.65 0 008.03 8zM12 19.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43 1.43-1.08 2.76-1.91 3.96zM14.34 14H9.66c-.09-.66-.16-1.32-.16-2s.07-1.35.16-2h4.68c.09.65.16 1.32.16 2s-.07 1.34-.16 2zm.25 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95a8.03 8.03 0 01-4.33 3.56zM16.36 14c.08-.66.14-1.32.14-2s-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2h-3.38z"/>
|
||||
</svg>
|
||||
Website
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content -->
|
||||
<div class="tab-content-wrapper">
|
||||
|
||||
<!-- Overview Tab -->
|
||||
<div class="tab-pane active" id="tab-overview">
|
||||
<div class="tab-content-grid">
|
||||
<!-- Main Content -->
|
||||
<div class="tab-main">
|
||||
<div class="featured-content">
|
||||
<h2>What Is ClaudeKit</h2>
|
||||
<p>ClaudeKit is a production-ready Claude Code setup with subagents, commands, hooks, and skills that have been battle-tested and used in real products. Built by someone who has been using Claude Code for 7+ months and documented everything publicly.</p>
|
||||
<p><strong>4,000+ happy users across 109 countries trust ClaudeKit to power their development workflow.</strong></p>
|
||||
<p>Beyond engineering, ClaudeKit also covers go-to-market with marketing automation to help you distribute your products to the world.</p>
|
||||
|
||||
<h2>Quick Installation</h2>
|
||||
<p>After purchasing, your GitHub username is granted access to the repos automatically. Install with these commands:</p>
|
||||
<pre><code># Login with your GitHub account
|
||||
gh auth login
|
||||
|
||||
# Install ClaudeKit CLI
|
||||
npm i -g claudekit-cli
|
||||
|
||||
# Install CK Engineer in your project
|
||||
ck init
|
||||
|
||||
# Or install globally for every project
|
||||
ck init -g</code></pre>
|
||||
|
||||
<h2>The Battle-Tested Workflow</h2>
|
||||
<p>ClaudeKit users rely on this proven workflow to ship features with confidence:</p>
|
||||
|
||||
<div class="feature-list">
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">1</div>
|
||||
<div class="feature-content">
|
||||
<h3>/docs:init</h3>
|
||||
<p>Scans and analyzes your codebase, then generates specs: codebase structure, system architecture, code standards, product overview, and design guidelines.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">2</div>
|
||||
<div class="feature-content">
|
||||
<h3>/brainstorm</h3>
|
||||
<p>Describe what you want to build. AI asks questions to fulfill requirements, challenges you with unresolved issues, and produces a report with requirement specs.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">3</div>
|
||||
<div class="feature-content">
|
||||
<h3>/plan</h3>
|
||||
<p>Researches approaches, spawns researcher agents in parallel, and creates a comprehensive implementation plan with separate phases and validation.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">4</div>
|
||||
<div class="feature-content">
|
||||
<h3>/clear</h3>
|
||||
<p>Fresh context window before implementation to ensure best code quality output and agents stick with the plan.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">5</div>
|
||||
<div class="feature-content">
|
||||
<h3>/code:auto</h3>
|
||||
<p>Reads the plan, implements phase by phase using progressive disclosure. Write code, write tests, run tests, code review (quality & security), verification, iterate until finished, then update specs & roadmap.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="info-note">This workflow is packed with 18+ years of professional development experience: Spec-driven Development, Test-driven Development, and the YAGNI - KISS - DRY principles.</p>
|
||||
|
||||
<h2>ClaudeKit Engineer</h2>
|
||||
<p>A complete engineering toolkit built on Claude Code:</p>
|
||||
|
||||
<div class="feature-list">
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">⚙</div>
|
||||
<div class="feature-content">
|
||||
<h3>Feature Development</h3>
|
||||
<p><code>/cook</code> - Standalone implementation with internal planning and development workflows</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">🔍</div>
|
||||
<div class="feature-content">
|
||||
<h3>Multi-Agent Code Review</h3>
|
||||
<p><code>/code:review</code> and <code>/codebase:review</code> - Specialized agents for security, performance, and standards enforcement</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">✅</div>
|
||||
<div class="feature-content">
|
||||
<h3>Automated Testing</h3>
|
||||
<p><code>/test</code> - Generates comprehensive test suites including E2E, integration, and unit tests</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">🐛</div>
|
||||
<div class="feature-content">
|
||||
<h3>Bug Diagnosis</h3>
|
||||
<p><code>/debug</code> - Analyzes logs, CI/CD failures, and provides root cause analysis</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">📄</div>
|
||||
<div class="feature-content">
|
||||
<h3>Documentation Sync</h3>
|
||||
<p><code>/docs</code> - Maintains synchronized technical docs: PRD, codebase summary, code standards, system architecture</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">📈</div>
|
||||
<div class="feature-content">
|
||||
<h3>Project Tracking</h3>
|
||||
<p><code>/watzup</code> - Project health metrics and milestone tracking</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>ClaudeKit Marketing</h2>
|
||||
<p>Go beyond code. ClaudeKit Marketing handles the go-to-market side of your products:</p>
|
||||
|
||||
<div class="feature-list">
|
||||
<div class="feature-item">
|
||||
<div class="feature-content">
|
||||
<h3>Content Strategy</h3>
|
||||
<p><code>/plan "Q1 content strategy"</code> - Campaign planning with TOFU/MOFU/BOFU funnel stages</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-content">
|
||||
<h3>Copywriting</h3>
|
||||
<p><code>/content/good</code> and <code>/content/cro</code> - Landing pages and conversion-optimized copy</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-content">
|
||||
<h3>SEO Optimization</h3>
|
||||
<p><code>/seo:audit</code> and <code>/seo:keywords</code> - Keyword research and competitor analysis</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-content">
|
||||
<h3>Campaign & Email</h3>
|
||||
<p><code>/campaign:email</code> - Email sequences, seasonal promotions, and drip campaigns via SendGrid and Resend MCPs</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-content">
|
||||
<h3>Analytics</h3>
|
||||
<p>GA4 + Google Ads MCPs for data-driven campaign optimization</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Comments Section -->
|
||||
<section class="content-section comments-section">
|
||||
<h2>Comments</h2>
|
||||
<p class="comments-description">Sign in with GitHub to leave a comment. Discussions are stored in the project's <a href="https://github.com/davila7/claude-code-templates/discussions" target="_blank">GitHub Discussions</a>.</p>
|
||||
<div id="giscusContainer"></div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<aside class="tab-sidebar">
|
||||
<!-- Partner Info -->
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-card-title">About ClaudeKit</h3>
|
||||
<div class="partner-info-card">
|
||||
<img src="https://docs.claudekit.cc/logo-horizontal.png" alt="ClaudeKit">
|
||||
<p class="partner-info-description">Battle-tested AI agents & skills for Claude Code. Ship features in hours instead of weeks with engineering and marketing automation.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CTA -->
|
||||
<div class="sidebar-card">
|
||||
<a href="https://claudekit.cc" target="_blank" class="sidebar-cta-btn">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
|
||||
</svg>
|
||||
Get ClaudeKit
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Metadata -->
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-card-title">Details</h3>
|
||||
<div class="sidebar-metadata-list">
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">Type</span>
|
||||
<span class="sidebar-meta-value">Toolkit</span>
|
||||
</div>
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">Category</span>
|
||||
<span class="sidebar-meta-value">Engineering</span>
|
||||
</div>
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">Users</span>
|
||||
<span class="sidebar-meta-value">4,000+</span>
|
||||
</div>
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">Countries</span>
|
||||
<span class="sidebar-meta-value">109</span>
|
||||
</div>
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">Website</span>
|
||||
<span class="sidebar-meta-value"><a href="https://claudekit.cc" target="_blank" style="color: var(--accent-color); text-decoration: none;">claudekit.cc</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Links -->
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-card-title">Links</h3>
|
||||
<div class="sidebar-links">
|
||||
<a href="https://docs.claudekit.cc" target="_blank" class="sidebar-link-item">
|
||||
<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://claudekit.cc" target="_blank" class="sidebar-link-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95a15.65 15.65 0 00-1.38-3.56A8.03 8.03 0 0118.92 8zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96z"/>
|
||||
</svg>
|
||||
claudekit.cc
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Website Tab -->
|
||||
<div class="tab-pane" id="tab-website">
|
||||
<div class="website-tab-header">
|
||||
<span class="website-tab-url">claudekit.cc</span>
|
||||
<a href="https://claudekit.cc" target="_blank" class="website-tab-open">
|
||||
Open in new tab
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<iframe src="https://claudekit.cc" class="featured-website-frame" title="ClaudeKit Website" loading="lazy"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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">© 2026 Claude Code Templates. Open source project.</p>
|
||||
<div class="footer-links">
|
||||
<a href="../../blog/index.html" 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>
|
||||
Blog
|
||||
</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>
|
||||
|
||||
<script>
|
||||
// Tab switching
|
||||
function switchTab(tabName) {
|
||||
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
|
||||
document.querySelectorAll('.tab-pane').forEach(pane => pane.classList.remove('active'));
|
||||
document.querySelector(`[data-tab="${tabName}"]`).classList.add('active');
|
||||
document.getElementById(`tab-${tabName}`).classList.add('active');
|
||||
}
|
||||
|
||||
// Share dropdown
|
||||
function toggleShareDropdown() {
|
||||
document.getElementById('componentShareDropdown').classList.toggle('open');
|
||||
}
|
||||
|
||||
function shareOnTwitter() {
|
||||
const text = encodeURIComponent('ClaudeKit: The fastest path from idea to production. Battle-tested AI agents & skills for Claude Code');
|
||||
const url = encodeURIComponent('https://aitmpl.com/featured/claudekit/');
|
||||
window.open(`https://twitter.com/intent/tweet?text=${text}&url=${url}`, '_blank');
|
||||
document.getElementById('componentShareDropdown').classList.remove('open');
|
||||
}
|
||||
|
||||
function shareOnThreads() {
|
||||
const text = encodeURIComponent('ClaudeKit: AI Agents & Skills for Claude Code\n\nhttps://aitmpl.com/featured/claudekit/');
|
||||
window.open(`https://www.threads.net/intent/post?text=${text}`, '_blank');
|
||||
document.getElementById('componentShareDropdown').classList.remove('open');
|
||||
}
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!e.target.closest('.share-dropdown')) {
|
||||
document.getElementById('componentShareDropdown').classList.remove('open');
|
||||
}
|
||||
});
|
||||
|
||||
// Giscus comments
|
||||
function loadGiscus() {
|
||||
const container = document.getElementById('giscusContainer');
|
||||
if (!container) return;
|
||||
const script = document.createElement('script');
|
||||
script.src = 'https://giscus.app/client.js';
|
||||
script.setAttribute('data-repo', 'davila7/claude-code-templates');
|
||||
script.setAttribute('data-repo-id', 'R_kgDONN2YhQ');
|
||||
script.setAttribute('data-category', 'Component Comments');
|
||||
script.setAttribute('data-category-id', 'DIC_kwDONN2Yhc4CmFij');
|
||||
script.setAttribute('data-mapping', 'specific');
|
||||
script.setAttribute('data-term', 'featured/claudekit');
|
||||
script.setAttribute('data-strict', '0');
|
||||
script.setAttribute('data-reactions-enabled', '1');
|
||||
script.setAttribute('data-emit-metadata', '0');
|
||||
script.setAttribute('data-input-position', 'top');
|
||||
script.setAttribute('data-theme', 'dark_dimmed');
|
||||
script.setAttribute('data-lang', 'en');
|
||||
script.setAttribute('crossorigin', 'anonymous');
|
||||
script.async = true;
|
||||
container.appendChild(script);
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', loadGiscus);
|
||||
} else {
|
||||
loadGiscus();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 70 KiB |
@@ -0,0 +1,71 @@
|
||||
<svg width="1200" height="630" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Background gradient -->
|
||||
<defs>
|
||||
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#0d1117;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#161b22;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="neonGrad" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" style="stop-color:#00e699;stop-opacity:0.3" />
|
||||
<stop offset="100%" style="stop-color:#00e699;stop-opacity:0.1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="1200" height="630" fill="url(#grad1)"/>
|
||||
|
||||
<!-- Grid pattern -->
|
||||
<g opacity="0.1">
|
||||
<line x1="0" y1="0" x2="1200" y2="630" stroke="#00e699" stroke-width="1"/>
|
||||
<line x1="0" y1="630" x2="1200" y2="0" stroke="#00e699" stroke-width="1"/>
|
||||
<line x1="600" y1="0" x2="600" y2="630" stroke="#00e699" stroke-width="2"/>
|
||||
<line x1="0" y1="315" x2="1200" y2="315" stroke="#00e699" stroke-width="2"/>
|
||||
</g>
|
||||
|
||||
<!-- Accent bar -->
|
||||
<rect x="0" y="0" width="1200" height="8" fill="url(#neonGrad)"/>
|
||||
|
||||
<!-- Main content -->
|
||||
<text x="600" y="180" font-family="monospace" font-size="72" font-weight="bold" fill="#00e699" text-anchor="middle">Neon</text>
|
||||
|
||||
<text x="600" y="250" font-family="monospace" font-size="28" fill="#c9d1d9" text-anchor="middle">Complete Postgres Template</text>
|
||||
|
||||
<text x="600" y="310" font-family="monospace" font-size="24" fill="#7d8590" text-anchor="middle">for Claude Code</text>
|
||||
|
||||
<!-- Feature boxes -->
|
||||
<g transform="translate(100, 380)">
|
||||
<rect width="180" height="80" rx="8" fill="#21262d" stroke="#30363d" stroke-width="2"/>
|
||||
<text x="90" y="35" font-family="monospace" font-size="28" fill="#00e699" text-anchor="middle">⚡</text>
|
||||
<text x="90" y="65" font-family="monospace" font-size="13" fill="#c9d1d9" text-anchor="middle">Instant Postgres</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(310, 380)">
|
||||
<rect width="180" height="80" rx="8" fill="#21262d" stroke="#30363d" stroke-width="2"/>
|
||||
<text x="90" y="35" font-family="monospace" font-size="28" fill="#00e699" text-anchor="middle">🤖</text>
|
||||
<text x="90" y="65" font-family="monospace" font-size="13" fill="#c9d1d9" text-anchor="middle">5 Expert Agents</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(520, 380)">
|
||||
<rect width="180" height="80" rx="8" fill="#21262d" stroke="#30363d" stroke-width="2"/>
|
||||
<text x="90" y="35" font-family="monospace" font-size="28" fill="#00e699" text-anchor="middle">🔌</text>
|
||||
<text x="90" y="65" font-family="monospace" font-size="13" fill="#c9d1d9" text-anchor="middle">MCP Integration</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(730, 380)">
|
||||
<rect width="180" height="80" rx="8" fill="#21262d" stroke="#30363d" stroke-width="2"/>
|
||||
<text x="90" y="35" font-family="monospace" font-size="28" fill="#00e699" text-anchor="middle">📊</text>
|
||||
<text x="90" y="65" font-family="monospace" font-size="13" fill="#c9d1d9" text-anchor="middle">Monitoring</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(940, 380)">
|
||||
<rect width="180" height="80" rx="8" fill="#21262d" stroke="#30363d" stroke-width="2"/>
|
||||
<text x="90" y="35" font-family="monospace" font-size="28" fill="#00e699" text-anchor="middle">🚀</text>
|
||||
<text x="90" y="65" font-family="monospace" font-size="13" fill="#c9d1d9" text-anchor="middle">Production Ready</text>
|
||||
</g>
|
||||
|
||||
<!-- Footer -->
|
||||
<text x="600" y="550" font-family="monospace" font-size="18" fill="#7d8590" text-anchor="middle">Production-ready Postgres infrastructure for AI development</text>
|
||||
|
||||
<!-- Branding -->
|
||||
<text x="50" y="610" font-family="monospace" font-size="14" fill="#7d8590">aitmpl.com</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,573 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Neon Complete Postgres Template for Claude Code</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">
|
||||
|
||||
<meta name="description" content="Complete Neon Template for Claude Code: Instant Postgres provisioning + 5 expert agents + MCP + monitoring tools. Production-ready database infrastructure.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://aitmpl.com/featured/neon-instagres/">
|
||||
<meta property="og:title" content="Neon Complete Postgres Template for Claude Code">
|
||||
<meta property="og:description" content="Production-ready Postgres infrastructure with instant provisioning, expert agents, and MCP integration.">
|
||||
<meta property="og:image" content="https://aitmpl.com/featured/neon-instagres/assets/neon-cover.png">
|
||||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="630">
|
||||
<meta property="og:image:alt" content="Neon Complete Postgres Template for Claude Code">
|
||||
<meta property="og:site_name" content="Claude Code Templates">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/featured/neon-instagres/">
|
||||
<meta name="twitter:title" content="Neon Complete Postgres Template for Claude Code">
|
||||
<meta name="twitter:description" content="Production-ready Postgres infrastructure with instant provisioning and expert agents.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/featured/neon-instagres/assets/neon-cover.png">
|
||||
<meta name="twitter:image:alt" content="Neon Complete Postgres Template for Claude Code">
|
||||
<meta name="twitter:creator" content="@davila7">
|
||||
<meta name="twitter:site" content="@davila7">
|
||||
|
||||
<meta name="keywords" content="Neon, Claude Code, Postgres, Database, Instagres, Serverless, AI Development, Drizzle ORM, Database Provisioning">
|
||||
<link rel="canonical" href="https://aitmpl.com/featured/neon-instagres/">
|
||||
|
||||
<link rel="stylesheet" href="../../css/styles.css">
|
||||
<link rel="stylesheet" href="../../css/component-page.css">
|
||||
<link rel="stylesheet" href="../../css/featured-page.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 -->
|
||||
<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>
|
||||
</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 class="terminal-subtitle">
|
||||
<span class="status-dot"></span>
|
||||
Ready-to-use configurations for your <strong>Claude Code</strong> projects
|
||||
</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="../../blog/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="component-page">
|
||||
<!-- Back Navigation -->
|
||||
<div class="back-navigation">
|
||||
<div class="container">
|
||||
<a href="../../index.html" class="back-link">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.42-1.41L7.83 13H20v-2z"/>
|
||||
</svg>
|
||||
Back to Components
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Component Content -->
|
||||
<div class="component-content">
|
||||
<div class="container">
|
||||
<!-- Component Header -->
|
||||
<div class="component-header">
|
||||
<div class="header-actions">
|
||||
<div class="share-dropdown" id="componentShareDropdown">
|
||||
<button class="share-btn" onclick="toggleShareDropdown()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.6 20.92,19A2.84,2.84 0 0,0 18,16.08Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="share-options" id="shareOptions">
|
||||
<button class="share-option-btn" onclick="shareOnTwitter()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
|
||||
</svg>
|
||||
Share on X
|
||||
</button>
|
||||
<button class="share-option-btn" onclick="shareOnThreads()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12.186 24h-.007c-3.581-.024-6.334-1.205-8.184-3.509C2.35 18.44 1.5 15.586 1.472 12.01v-.017c.03-3.579.879-6.43 2.525-8.482C5.845 1.205 8.6.024 12.18 0h.014c2.746.02 5.043.725 6.826 2.098 1.677 1.29 2.858 3.13 3.509 5.467l-2.04.569c-1.104-3.96-3.898-5.984-8.304-6.015-2.91.022-5.11.936-6.54 2.717C4.307 6.504 3.616 8.914 3.589 12c.027 3.086.718 5.496 2.057 7.164 1.43 1.781 3.631 2.695 6.54 2.717 1.623-.02 3.094-.37 4.37-1.04.558-.29 1.029-.63 1.414-1.017.33-.33.602-.69.82-1.084.218-.395.381-.84.488-1.336.107-.495.16-1.044.16-1.644 0-.6-.053-1.149-.16-1.644a4.158 4.158 0 00-.488-1.336 3.996 3.996 0 00-.82-1.084 4.015 4.015 0 00-1.414-1.017c-1.276-.67-2.747-1.02-4.37-1.04h-.014c-1.623.02-3.094.37-4.37 1.04a4.015 4.015 0 00-1.414 1.017 3.996 3.996 0 00-.82 1.084 4.158 4.158 0 00-.488 1.336c-.107.495-.16 1.044-.16 1.644 0 .6.053 1.149.16 1.644.107.496.27.941.488 1.336.218.394.49.754.82 1.084.385.387.856.727 1.414 1.017 1.276.67 2.747 1.02 4.37 1.04z"/>
|
||||
</svg>
|
||||
Share on Threads
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="component-title-section">
|
||||
<div class="featured-logo">
|
||||
<img src="https://neon.tech/brand/neon-logo-dark-color.svg" alt="Neon Logo">
|
||||
</div>
|
||||
<div class="component-title-info">
|
||||
<h1>Neon Complete Postgres Template</h1>
|
||||
<div class="component-meta">
|
||||
<div class="partner-badge partner-badge--database">DATABASE</div>
|
||||
<span class="component-category-title">Infrastructure</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- External CTA Button -->
|
||||
<a href="https://get.neon.com/4eCjZDz" target="_blank" class="title-section-add-to-cart-btn" style="text-decoration: none;">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
|
||||
</svg>
|
||||
<span>Try Neon Free</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Tab Navigation -->
|
||||
<div class="component-tabs">
|
||||
<button class="tab-btn active" data-tab="overview" onclick="switchTab('overview')">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"/>
|
||||
</svg>
|
||||
Overview
|
||||
</button>
|
||||
<button class="tab-btn" data-tab="website" onclick="switchTab('website')">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95a15.65 15.65 0 00-1.38-3.56A8.03 8.03 0 0118.92 8zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96zM4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2s.06 1.34.14 2H4.26zm.82 2h2.95c.32 1.25.78 2.45 1.38 3.56A7.987 7.987 0 015.08 16zm2.95-8H5.08a7.987 7.987 0 014.33-3.56A15.65 15.65 0 008.03 8zM12 19.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43 1.43-1.08 2.76-1.91 3.96zM14.34 14H9.66c-.09-.66-.16-1.32-.16-2s.07-1.35.16-2h4.68c.09.65.16 1.32.16 2s-.07 1.34-.16 2zm.25 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95a8.03 8.03 0 01-4.33 3.56zM16.36 14c.08-.66.14-1.32.14-2s-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2h-3.38z"/>
|
||||
</svg>
|
||||
Website
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content -->
|
||||
<div class="tab-content-wrapper">
|
||||
|
||||
<!-- Overview Tab -->
|
||||
<div class="tab-pane active" id="tab-overview">
|
||||
<div class="tab-content-grid">
|
||||
<!-- Main Content -->
|
||||
<div class="tab-main">
|
||||
<div class="featured-content">
|
||||
<h2>Overview</h2>
|
||||
<p>The Neon template provides a complete Postgres infrastructure for Claude Code projects. It combines Neon's Instagres provisioning service with specialized AI agents, MCP integration, and monitoring tools.</p>
|
||||
<p>This template is designed for developers who need production-ready database infrastructure without manual configuration.</p>
|
||||
|
||||
<h2>Template Architecture</h2>
|
||||
<p>The template consists of 10 components organized into four layers:</p>
|
||||
|
||||
<h3>1. Provisioning Layer</h3>
|
||||
<p><strong>neon-instagres Skill</strong> - Auto-activating skill that provisions Postgres databases via the <code>npx get-db</code> CLI. Triggers on keywords like "database", "postgres", or "PostgreSQL".</p>
|
||||
|
||||
<pre><code>npx get-db --yes --ref 4eCjZDz</code></pre>
|
||||
|
||||
<p>Creates three environment variables:</p>
|
||||
<ul>
|
||||
<li><code>DATABASE_URL</code> - Connection pooler endpoint (recommended for application queries)</li>
|
||||
<li><code>DATABASE_URL_DIRECT</code> - Direct connection (required for migrations and schema changes)</li>
|
||||
<li><code>PUBLIC_INSTAGRES_CLAIM_URL</code> - Claim URL with 72-hour validity window</li>
|
||||
</ul>
|
||||
|
||||
<p><strong>using-neon Skill</strong> - Comprehensive guides and best practices for working with Neon Serverless Postgres. Covers getting started, local development, connection methods, authentication (<code>@neondatabase/auth</code>), PostgREST-style data API (<code>@neondatabase/neon-js</code>), Neon CLI, and Platform API/SDKs. Includes 28 reference documents.</p>
|
||||
|
||||
<pre><code>npx claude-code-templates@latest --skill database/using-neon</code></pre>
|
||||
|
||||
<p>Reference documentation covers:</p>
|
||||
<ul>
|
||||
<li><strong>Core Guides</strong> - What is Neon, getting started, features, connection methods, devtools</li>
|
||||
<li><strong>Database Drivers</strong> - Neon Serverless Driver (<code>@neondatabase/serverless</code>), Drizzle ORM integration</li>
|
||||
<li><strong>Auth & Data API</strong> - Neon Auth (<code>@neondatabase/auth</code>), Neon JS (<code>@neondatabase/neon-js</code>)</li>
|
||||
<li><strong>Platform API & CLI</strong> - TypeScript/Python SDKs, REST API, Neon CLI reference</li>
|
||||
</ul>
|
||||
|
||||
<h3>2. Agent Layer</h3>
|
||||
<p>Five specialized agents handle different database operations:</p>
|
||||
<p><strong>neon-database-architect</strong> - Schema design with Drizzle ORM. Handles table relationships, indexes, constraints, and migration generation.</p>
|
||||
<p><strong>neon-auth-specialist</strong> - Authentication integration with Stack Auth. Implements multi-tenancy patterns, row-level security, and session management.</p>
|
||||
<p><strong>neon-migration-specialist</strong> - Production migration strategies including zero-downtime deployments, rollback procedures, and data migration patterns.</p>
|
||||
<p><strong>neon-optimization-analyzer</strong> - Query performance analysis using <code>EXPLAIN ANALYZE</code>, index recommendations, and connection pooling optimization.</p>
|
||||
<p><strong>neon-expert</strong> - General database consulting. Covers branching workflows, compute autoscaling, and Neon-specific features.</p>
|
||||
|
||||
<h3>3. Integration Layer</h3>
|
||||
<p><strong>neon MCP</strong> - Model Context Protocol integration providing programmatic access to Neon's management API for project and branch operations.</p>
|
||||
|
||||
<h3>4. Monitoring Layer</h3>
|
||||
<p><strong>neon-database-dev</strong> - Development metrics statusline showing active connections, transaction counts, and cache hit rates.</p>
|
||||
<p><strong>neon-database-resources</strong> - Resource monitoring statusline tracking CPU usage, memory consumption, and storage metrics.</p>
|
||||
|
||||
<h2>Installation</h2>
|
||||
|
||||
<h3>Option 1: Skills Only</h3>
|
||||
<p>Install both Neon skills (provisioning + best practices):</p>
|
||||
<pre><code>npx claude-code-templates@latest --skill database/neon-instagres,database/using-neon</code></pre>
|
||||
|
||||
<h3>Option 2: Complete Template</h3>
|
||||
<p>Install all 10 components:</p>
|
||||
<pre><code>npx claude-code-templates@latest \
|
||||
--skill database/neon-instagres,database/using-neon \
|
||||
--agent database/neon-expert,database/neon-database-architect,database/neon-auth-specialist,data-ai/neon-migration-specialist,data-ai/neon-optimization-analyzer \
|
||||
--mcp database/neon \
|
||||
--setting statusline/neon-database-dev,statusline/neon-database-resources \
|
||||
--yes</code></pre>
|
||||
|
||||
<h2>Usage Patterns</h2>
|
||||
|
||||
<h3>Basic Database Setup</h3>
|
||||
<p>The skill auto-activates when you mention database setup:</p>
|
||||
<blockquote>
|
||||
<p><strong>User:</strong> "Set up a Postgres database for this project"</p>
|
||||
<p><strong>Skill:</strong> Provisions database, writes credentials to <code>.env</code></p>
|
||||
</blockquote>
|
||||
|
||||
<h3>Schema Design</h3>
|
||||
<p>Complex schemas trigger delegation to the architect agent:</p>
|
||||
<blockquote>
|
||||
<p><strong>User:</strong> "Create a multi-tenant SaaS schema with users, organizations, and subscriptions"</p>
|
||||
<p><strong>Flow:</strong> Skill provisions → delegates to neon-database-architect → generates Drizzle schema</p>
|
||||
</blockquote>
|
||||
|
||||
<h3>Authentication Integration</h3>
|
||||
<blockquote>
|
||||
<p><strong>User:</strong> "Add Stack Auth with row-level security"</p>
|
||||
<p><strong>Flow:</strong> Delegates to neon-auth-specialist → implements RLS policies and auth tables</p>
|
||||
</blockquote>
|
||||
|
||||
<h3>Production Migration</h3>
|
||||
<blockquote>
|
||||
<p><strong>User:</strong> "Migrate to production with zero downtime"</p>
|
||||
<p><strong>Flow:</strong> Delegates to neon-migration-specialist → creates migration strategy with rollback plan</p>
|
||||
</blockquote>
|
||||
|
||||
<h2>Framework Integration</h2>
|
||||
<p>The template integrates with major JavaScript/TypeScript frameworks:</p>
|
||||
<ul>
|
||||
<li><strong>Next.js</strong> - Writes to <code>.env.local</code>, configures for server components and API routes</li>
|
||||
<li><strong>Vite</strong> - Creates <code>.env</code> with <code>VITE_</code> prefix for client-side access</li>
|
||||
<li><strong>Express</strong> - Standard <code>.env</code> configuration with connection pooling setup</li>
|
||||
<li><strong>SvelteKit</strong> - Writes to <code>.env</code> with <code>$env/static/private</code> patterns</li>
|
||||
<li><strong>Remix</strong> - Creates <code>.env</code> for server-side loaders and actions</li>
|
||||
</ul>
|
||||
<p>ORM support includes Drizzle, Prisma, TypeORM, Kysely, and raw SQL via node-postgres.</p>
|
||||
|
||||
<h2>Technical Implementation</h2>
|
||||
|
||||
<h3>Database Provisioning</h3>
|
||||
<p>Neon Instagres creates serverless Postgres instances with the following characteristics:</p>
|
||||
<ul>
|
||||
<li>Postgres 16 with standard extensions (pgvector, PostGIS, uuid-ossp, etc.)</li>
|
||||
<li>Connection pooling via PgBouncer in transaction mode</li>
|
||||
<li>Autoscaling compute from 0.25 to 8 vCPU based on load</li>
|
||||
<li>72-hour trial period before requiring account claim</li>
|
||||
</ul>
|
||||
|
||||
<h3>Agent Delegation Logic</h3>
|
||||
<p>The skill uses keyword matching to delegate to appropriate agents:</p>
|
||||
<ul>
|
||||
<li><code>schema</code>, <code>table</code>, <code>drizzle</code> → neon-database-architect</li>
|
||||
<li><code>auth</code>, <code>stack auth</code>, <code>RLS</code> → neon-auth-specialist</li>
|
||||
<li><code>migrate</code>, <code>migration</code>, <code>production</code> → neon-migration-specialist</li>
|
||||
<li><code>slow</code>, <code>optimize</code>, <code>performance</code> → neon-optimization-analyzer</li>
|
||||
<li><code>branch</code>, <code>scale</code> → neon-expert</li>
|
||||
</ul>
|
||||
|
||||
<h3>MCP Integration</h3>
|
||||
<p>The Neon MCP exposes management operations:</p>
|
||||
<ul>
|
||||
<li>List and create projects</li>
|
||||
<li>Manage database branches (create, delete, merge)</li>
|
||||
<li>Configure compute settings and autoscaling</li>
|
||||
<li>Query connection strings and endpoints</li>
|
||||
</ul>
|
||||
|
||||
<h2>Additional Resources</h2>
|
||||
<ul>
|
||||
<li><a href="https://neon.tech/docs/guides/instagres" target="_blank">Neon Instagres Documentation</a></li>
|
||||
<li><a href="https://neon.tech/docs/introduction" target="_blank">Neon Platform Overview</a></li>
|
||||
<li><a href="https://get.neon.com/4eCjZDz" target="_blank">Create Free Neon Account</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Comments Section -->
|
||||
<section class="content-section comments-section">
|
||||
<h2>Comments</h2>
|
||||
<p class="comments-description">Sign in with GitHub to leave a comment. Discussions are stored in the project's <a href="https://github.com/davila7/claude-code-templates/discussions" target="_blank">GitHub Discussions</a>.</p>
|
||||
<div id="giscusContainer"></div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<aside class="tab-sidebar">
|
||||
<!-- Partner Info -->
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-card-title">About Neon</h3>
|
||||
<div class="partner-info-card">
|
||||
<img src="https://neon.tech/brand/neon-logo-dark-color.svg" alt="Neon">
|
||||
<p class="partner-info-description">Serverless Postgres with instant provisioning, autoscaling compute, and branching for modern development workflows.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CTA -->
|
||||
<div class="sidebar-card">
|
||||
<a href="https://get.neon.com/4eCjZDz" target="_blank" class="sidebar-cta-btn">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
|
||||
</svg>
|
||||
Try Neon Free
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Quick Install -->
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-card-title">Quick Install</h3>
|
||||
<div class="quick-install-block">
|
||||
<code id="quickInstallCommand">npx claude-code-templates@latest --skill database/neon-instagres,database/using-neon --yes</code>
|
||||
<button class="quick-copy-btn" onclick="copyInstallCommand()">
|
||||
<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
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Metadata -->
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-card-title">Details</h3>
|
||||
<div class="sidebar-metadata-list">
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">Type</span>
|
||||
<span class="sidebar-meta-value">Template</span>
|
||||
</div>
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">Components</span>
|
||||
<span class="sidebar-meta-value">10</span>
|
||||
</div>
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">Category</span>
|
||||
<span class="sidebar-meta-value">Database</span>
|
||||
</div>
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">Website</span>
|
||||
<span class="sidebar-meta-value"><a href="https://neon.tech" target="_blank" style="color: var(--accent-color); text-decoration: none;">neon.tech</a></span>
|
||||
</div>
|
||||
<div class="sidebar-meta-row">
|
||||
<span class="sidebar-meta-label">Integration</span>
|
||||
<span class="sidebar-meta-value">MCP, CLI</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Links -->
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-card-title">Links</h3>
|
||||
<div class="sidebar-links">
|
||||
<a href="https://neon.tech/docs/guides/instagres" target="_blank" class="sidebar-link-item">
|
||||
<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>
|
||||
Instagres Docs
|
||||
</a>
|
||||
<a href="https://neon.tech/docs/introduction" target="_blank" class="sidebar-link-item">
|
||||
<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>
|
||||
Platform Overview
|
||||
</a>
|
||||
<a href="https://neon.tech" target="_blank" class="sidebar-link-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95a15.65 15.65 0 00-1.38-3.56A8.03 8.03 0 0118.92 8zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96z"/>
|
||||
</svg>
|
||||
neon.tech
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Website Tab -->
|
||||
<div class="tab-pane" id="tab-website">
|
||||
<div class="website-tab-header">
|
||||
<span class="website-tab-url">neon.tech</span>
|
||||
<a href="https://neon.tech" target="_blank" class="website-tab-open">
|
||||
Open in new tab
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<iframe src="https://neon.tech" class="featured-website-frame" title="Neon Website" loading="lazy"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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">© 2026 Claude Code Templates. Open source project.</p>
|
||||
<div class="footer-links">
|
||||
<a href="../../blog/index.html" 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>
|
||||
Blog
|
||||
</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>
|
||||
|
||||
<script>
|
||||
// Tab switching
|
||||
function switchTab(tabName) {
|
||||
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
|
||||
document.querySelectorAll('.tab-pane').forEach(pane => pane.classList.remove('active'));
|
||||
document.querySelector(`[data-tab="${tabName}"]`).classList.add('active');
|
||||
document.getElementById(`tab-${tabName}`).classList.add('active');
|
||||
}
|
||||
|
||||
// Share dropdown
|
||||
function toggleShareDropdown() {
|
||||
document.getElementById('componentShareDropdown').classList.toggle('open');
|
||||
}
|
||||
|
||||
function shareOnTwitter() {
|
||||
const text = encodeURIComponent('Neon Complete Postgres Template for Claude Code - Production-ready database infrastructure with instant provisioning and expert agents');
|
||||
const url = encodeURIComponent('https://aitmpl.com/featured/neon-instagres/');
|
||||
window.open(`https://twitter.com/intent/tweet?text=${text}&url=${url}`, '_blank');
|
||||
document.getElementById('componentShareDropdown').classList.remove('open');
|
||||
}
|
||||
|
||||
function shareOnThreads() {
|
||||
const text = encodeURIComponent('Neon Complete Postgres Template for Claude Code\n\nhttps://aitmpl.com/featured/neon-instagres/');
|
||||
window.open(`https://www.threads.net/intent/post?text=${text}`, '_blank');
|
||||
document.getElementById('componentShareDropdown').classList.remove('open');
|
||||
}
|
||||
|
||||
// Copy install command
|
||||
async function copyInstallCommand() {
|
||||
const command = document.getElementById('quickInstallCommand').textContent;
|
||||
try {
|
||||
await navigator.clipboard.writeText(command);
|
||||
const btn = document.querySelector('.quick-copy-btn');
|
||||
const original = btn.innerHTML;
|
||||
btn.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!';
|
||||
setTimeout(() => { btn.innerHTML = original; }, 2000);
|
||||
} catch (err) {
|
||||
// fallback
|
||||
}
|
||||
}
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!e.target.closest('.share-dropdown')) {
|
||||
document.getElementById('componentShareDropdown').classList.remove('open');
|
||||
}
|
||||
});
|
||||
|
||||
// Giscus comments
|
||||
function loadGiscus() {
|
||||
const container = document.getElementById('giscusContainer');
|
||||
if (!container) return;
|
||||
const script = document.createElement('script');
|
||||
script.src = 'https://giscus.app/client.js';
|
||||
script.setAttribute('data-repo', 'davila7/claude-code-templates');
|
||||
script.setAttribute('data-repo-id', 'R_kgDONN2YhQ');
|
||||
script.setAttribute('data-category', 'Component Comments');
|
||||
script.setAttribute('data-category-id', 'DIC_kwDONN2Yhc4CmFij');
|
||||
script.setAttribute('data-mapping', 'specific');
|
||||
script.setAttribute('data-term', 'featured/neon-instagres');
|
||||
script.setAttribute('data-strict', '0');
|
||||
script.setAttribute('data-reactions-enabled', '1');
|
||||
script.setAttribute('data-emit-metadata', '0');
|
||||
script.setAttribute('data-input-position', 'top');
|
||||
script.setAttribute('data-theme', 'dark_dimmed');
|
||||
script.setAttribute('data-lang', 'en');
|
||||
script.setAttribute('crossorigin', 'anonymous');
|
||||
script.async = true;
|
||||
container.appendChild(script);
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', loadGiscus);
|
||||
} else {
|
||||
loadGiscus();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,148 @@
|
||||
# Claude Jobs Scraper
|
||||
|
||||
Script para encontrar trabajos relacionados con Claude Code y Anthropic Claude utilizando múltiples fuentes y APIs profesionales.
|
||||
|
||||
## 🎯 Características
|
||||
|
||||
- **APIs Profesionales**: RapidAPI Jobs, Google Jobs (SerpAPI)
|
||||
- **Scraping Tradicional**: GitHub, YCombinator, WeWorkRemotely (fallback)
|
||||
- **Filtrado Estricto**: Solo trabajos que mencionen "Claude" explícitamente
|
||||
- **Datos Estructurados**: JSON compatible con la web existente
|
||||
- **Rate Limiting**: Manejo responsable de APIs
|
||||
|
||||
## 📋 Requisitos
|
||||
|
||||
### Opción 1: APIs Profesionales (Recomendado)
|
||||
|
||||
1. **RapidAPI Jobs API** - Acceso a 200M+ trabajos de LinkedIn, Indeed, Glassdoor
|
||||
- Regístrate en: https://rapidapi.com/letscrape-6bRBa3QguO5/api/jobs-search-realtime-data-api/
|
||||
- Plan gratuito: 100 requests/mes
|
||||
- Plan pagado: Desde $10/mes
|
||||
|
||||
2. **SerpAPI (Google Jobs)** - Búsqueda semántica avanzada
|
||||
- Regístrate en: https://serpapi.com/
|
||||
- Plan gratuito: 100 búsquedas/mes
|
||||
- Plan pagado: Desde $50/mes
|
||||
|
||||
### Opción 2: Solo Scraping Gratuito
|
||||
|
||||
- No requiere APIs pagadas
|
||||
- Resultados limitados debido a restricciones de sitios web
|
||||
|
||||
## ⚙️ Configuración
|
||||
|
||||
1. **Copia el archivo de configuración**:
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
2. **Agrega tus API keys en `.env`**:
|
||||
```bash
|
||||
# Para mejores resultados
|
||||
RAPIDAPI_KEY=tu_clave_rapidapi
|
||||
SERPAPI_KEY=tu_clave_serpapi
|
||||
|
||||
# Opcional
|
||||
GITHUB_TOKEN=tu_token_github
|
||||
```
|
||||
|
||||
3. **Instala dependencias**:
|
||||
```bash
|
||||
pip install requests python-dotenv
|
||||
```
|
||||
|
||||
## 🚀 Uso
|
||||
|
||||
```bash
|
||||
python generate_claude_jobs.py
|
||||
```
|
||||
|
||||
### Flujo de Funcionamiento:
|
||||
|
||||
1. **APIs Profesionales** (si están configuradas)
|
||||
- RapidAPI: Busca en LinkedIn, Indeed, Glassdoor, etc.
|
||||
- Google Jobs: Búsqueda semántica avanzada
|
||||
|
||||
2. **Scraping Tradicional** (fallback si no hay APIs)
|
||||
- GitHub Issues/Discussions
|
||||
- YCombinator Who's Hiring
|
||||
- WeWorkRemotely RSS
|
||||
|
||||
3. **Generación del JSON**:
|
||||
- Archivo: `docs/claude-jobs.json`
|
||||
- Estructura compatible con la web existente
|
||||
|
||||
## 📊 Datos Generados
|
||||
|
||||
Cada trabajo incluye:
|
||||
|
||||
```json
|
||||
{
|
||||
"company": "Anthropic",
|
||||
"company_icon": "https://anthropic.com/favicon.ico",
|
||||
"location": "Remote",
|
||||
"description": "Senior AI Developer to enhance Claude Code capabilities...",
|
||||
"job_link": "https://anthropic.com/careers/claude-developer",
|
||||
"source": "RapidAPI Jobs",
|
||||
"date_posted": "2025-09-10T10:00:00Z",
|
||||
"salary": 150000
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 Filtros Aplicados
|
||||
|
||||
El script usa filtrado **ultra-estricto**:
|
||||
|
||||
- **Debe mencionar "Claude"** explícitamente
|
||||
- Términos específicos: `claude code`, `anthropic claude`, `claude ai`, etc.
|
||||
- Validación de contexto laboral: `hiring`, `position`, `engineer`, etc.
|
||||
|
||||
## 📈 Resultados Esperados
|
||||
|
||||
Dado que Claude Code es muy nuevo (2025), los resultados serán limitados pero precisos:
|
||||
|
||||
- **Con APIs**: 5-20 trabajos relevantes potenciales
|
||||
- **Solo Scraping**: 0-5 trabajos (debido a restricciones)
|
||||
- **Calidad**: 100% relevantes (menciones específicas de Claude)
|
||||
|
||||
## 🔄 Automatización
|
||||
|
||||
Para ejecutar periódicamente:
|
||||
|
||||
```bash
|
||||
# Cron job diario a las 9 AM
|
||||
0 9 * * * cd /path/to/project && python generate_claude_jobs.py
|
||||
|
||||
# GitHub Actions (recomendado)
|
||||
# Ver ejemplo en .github/workflows/
|
||||
```
|
||||
|
||||
## ⚠️ Limitaciones
|
||||
|
||||
1. **Claude Code es nuevo**: Pocas ofertas laborales específicas aún
|
||||
2. **APIs pagadas**: Mejores resultados requieren suscripciones
|
||||
3. **Rate limits**: Respetar límites de APIs para evitar bloqueos
|
||||
4. **Falsos positivos**: Filtrado estricto puede omitir trabajos relevantes
|
||||
|
||||
## 🆘 Troubleshooting
|
||||
|
||||
### Sin resultados:
|
||||
- ✅ Verifica API keys en `.env`
|
||||
- ✅ Revisa límites de rate en las APIs
|
||||
- ✅ Claude Code es muy específico - resultados limitados son normales
|
||||
|
||||
### Errores de API:
|
||||
- ✅ Verifica saldo en RapidAPI/SerpAPI
|
||||
- ✅ Revisa formato de API keys
|
||||
- ✅ Usa VPN si hay restricciones geográficas
|
||||
|
||||
## 🔮 Futuro
|
||||
|
||||
A medida que Claude Code se popularice (2025-2026):
|
||||
- Más trabajos específicos aparecerán
|
||||
- Términos de búsqueda se pueden expandir
|
||||
- APIs especializadas en AI jobs pueden surgir
|
||||
|
||||
---
|
||||
|
||||
**Resultado**: JSON estructurado en `docs/claude-jobs.json` listo para consumo por la web.
|
||||
@@ -0,0 +1,60 @@
|
||||
# 🚀 Deployment Guide
|
||||
|
||||
This project is configured for automatic deployment to Vercel from the `main` branch.
|
||||
|
||||
## GitHub Actions Setup
|
||||
|
||||
### Required Secrets
|
||||
|
||||
Add these secrets to your GitHub repository settings:
|
||||
|
||||
1. **VERCEL_TOKEN**: Your Vercel account token
|
||||
- Go to [Vercel Account Settings](https://vercel.com/account/tokens)
|
||||
- Create a new token with appropriate permissions
|
||||
- Add as `VERCEL_TOKEN` in GitHub Secrets
|
||||
|
||||
2. **VERCEL_ORG_ID**: Your Vercel organization ID
|
||||
- Run `vercel link` in your project
|
||||
- Copy the `orgId` from `.vercel/project.json`
|
||||
- Add as `VERCEL_ORG_ID` in GitHub Secrets
|
||||
|
||||
3. **VERCEL_PROJECT_ID**: Your Vercel project ID
|
||||
- Run `vercel link` in your project
|
||||
- Copy the `projectId` from `.vercel/project.json`
|
||||
- Add as `VERCEL_PROJECT_ID` in GitHub Secrets
|
||||
|
||||
### Getting the IDs
|
||||
|
||||
Run these commands in your project root:
|
||||
|
||||
```bash
|
||||
# Link to Vercel project
|
||||
vercel link
|
||||
|
||||
# Get your IDs from the generated file
|
||||
cat .vercel/project.json
|
||||
```
|
||||
|
||||
## Deployment Flow
|
||||
|
||||
- ✅ **Push to main** → Automatic production deploy to aitmpl.com
|
||||
- ✅ **Other branches** → Manual deploy only (no auto-deploy)
|
||||
- ✅ **Pull Requests** → No deployment
|
||||
|
||||
## Manual Deployment
|
||||
|
||||
For testing other branches:
|
||||
|
||||
```bash
|
||||
# Deploy current branch to preview URL
|
||||
vercel
|
||||
|
||||
# Deploy current branch to production
|
||||
vercel --prod
|
||||
```
|
||||
|
||||
## Domain Configuration
|
||||
|
||||
The main branch deploys to the custom domain: **aitmpl.com**
|
||||
|
||||
Configured in Vercel dashboard under Project Settings → Domains.
|
||||
@@ -0,0 +1,313 @@
|
||||
# Discord Bot Setup - Vercel Serverless
|
||||
|
||||
Discord bot para claude-code-templates integrado con Vercel Functions usando webhooks. Este enfoque es más eficiente que mantener un bot tradicional con conexión persistente.
|
||||
|
||||
## 🚀 Ventajas de la Arquitectura Serverless
|
||||
|
||||
- ✅ **Sin servidor permanente**: Solo se ejecuta cuando hay interacciones
|
||||
- ✅ **Escalabilidad automática**: Vercel maneja el scaling
|
||||
- ✅ **Integrado con tu infraestructura**: Mismo proyecto, mismo deployment
|
||||
- ✅ **Costo reducido**: Solo pagas por ejecuciones reales
|
||||
- ✅ **Cache inteligente**: Reutiliza datos entre invocaciones
|
||||
|
||||
## 📋 Comandos Disponibles
|
||||
|
||||
### Comandos Básicos (Fase 1)
|
||||
|
||||
1. **`/search <query> [type]`** - Buscar componentes
|
||||
2. **`/info <name> [type]`** - Información detallada
|
||||
3. **`/install <name> [type]`** - Comando de instalación
|
||||
4. **`/popular <type> [limit]`** - Componentes más populares
|
||||
5. **`/random <type>`** - Descubre componentes aleatorios
|
||||
|
||||
## 🛠️ Setup Paso a Paso
|
||||
|
||||
### 1. Crear Discord Application
|
||||
|
||||
1. Ve a [Discord Developer Portal](https://discord.com/developers/applications)
|
||||
2. Click "New Application" y dale un nombre
|
||||
3. En la sección "General Information":
|
||||
- Copia el **Application ID** → Esto es tu `DISCORD_APP_ID`
|
||||
4. Ve a la sección "Bot":
|
||||
- Click "Add Bot"
|
||||
- Copia el **Bot Token** → Esto es tu `DISCORD_BOT_TOKEN`
|
||||
- Habilita "Message Content Intent" si lo necesitas
|
||||
5. En "General Information" nuevamente:
|
||||
- Copia el **Public Key** → Esto es tu `DISCORD_PUBLIC_KEY`
|
||||
|
||||
### 2. Configurar Variables de Entorno
|
||||
|
||||
#### En Local (para testing)
|
||||
|
||||
Crea/actualiza tu archivo `.env`:
|
||||
|
||||
```bash
|
||||
# Discord Bot Configuration
|
||||
DISCORD_APP_ID=123456789012345678
|
||||
DISCORD_BOT_TOKEN=MTIzNDU2Nzg5MDEyMzQ1Njc4.AbCdEf.GhIjKlMnOpQrStUvWxYz
|
||||
DISCORD_PUBLIC_KEY=abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890
|
||||
DISCORD_GUILD_ID=987654321098765432 # Opcional, para testing en un server específico
|
||||
|
||||
# API Configuration
|
||||
COMPONENTS_API_URL=https://aitmpl.com/components.json
|
||||
```
|
||||
|
||||
#### En Vercel (para producción)
|
||||
|
||||
1. Ve a tu proyecto en Vercel Dashboard
|
||||
2. Settings → Environment Variables
|
||||
3. Agrega estas variables:
|
||||
- `DISCORD_APP_ID`
|
||||
- `DISCORD_BOT_TOKEN`
|
||||
- `DISCORD_PUBLIC_KEY`
|
||||
- `COMPONENTS_API_URL` (opcional, por defecto usa aitmpl.com)
|
||||
|
||||
### 3. Instalar Dependencias
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
Esto instalará:
|
||||
- `discord-interactions` - Para verificar requests de Discord
|
||||
- `axios` - Para cargar components.json
|
||||
- `dotenv` - Para variables de entorno
|
||||
|
||||
### 4. Registrar Comandos con Discord
|
||||
|
||||
```bash
|
||||
npm run discord:register
|
||||
```
|
||||
|
||||
Output esperado:
|
||||
```
|
||||
📦 Registering Discord slash commands...
|
||||
|
||||
Registering to guild: 987654321098765432
|
||||
|
||||
✅ Successfully registered 5 commands!
|
||||
|
||||
📝 Registered commands:
|
||||
1. /search - Search for components by keyword
|
||||
2. /info - Get detailed information about a specific component
|
||||
3. /install - Get the installation command for a component
|
||||
4. /popular - View the most popular components by download count
|
||||
5. /random - Discover a random component
|
||||
```
|
||||
|
||||
### 5. Deploy a Vercel
|
||||
|
||||
```bash
|
||||
vercel --prod
|
||||
```
|
||||
|
||||
O usa el deployment automático de GitHub si lo tienes configurado.
|
||||
|
||||
Después del deploy, copia tu URL de producción:
|
||||
```
|
||||
https://your-project.vercel.app
|
||||
```
|
||||
|
||||
### 6. Configurar Interactions Endpoint en Discord
|
||||
|
||||
1. Ve a Discord Developer Portal → Tu aplicación
|
||||
2. Ve a "General Information"
|
||||
3. En **Interactions Endpoint URL**, pega:
|
||||
```
|
||||
https://your-project.vercel.app/api/discord/interactions
|
||||
```
|
||||
4. Click "Save Changes"
|
||||
|
||||
Discord intentará verificar la URL enviando un PING. Si todo está bien configurado, verás un ✅.
|
||||
|
||||
### 7. Invitar el Bot a tu Servidor
|
||||
|
||||
1. En Discord Developer Portal → OAuth2 → URL Generator
|
||||
2. Selecciona scopes:
|
||||
- ✅ `bot`
|
||||
- ✅ `applications.commands`
|
||||
3. Selecciona permisos del bot:
|
||||
- ✅ Send Messages
|
||||
- ✅ Embed Links
|
||||
- ✅ Read Message History
|
||||
4. Copia la URL generada y ábrela en tu navegador
|
||||
5. Selecciona tu servidor y autoriza
|
||||
|
||||
## 🧪 Testing Local
|
||||
|
||||
Para testear localmente antes de hacer deploy:
|
||||
|
||||
```bash
|
||||
# 1. Instala Vercel CLI si no lo tienes
|
||||
npm i -g vercel
|
||||
|
||||
# 2. Inicia el servidor de desarrollo
|
||||
vercel dev
|
||||
```
|
||||
|
||||
Esto correrá en `http://localhost:3000`
|
||||
|
||||
Para que Discord pueda enviar webhooks a tu localhost, necesitas un túnel:
|
||||
|
||||
```bash
|
||||
# Opción 1: ngrok
|
||||
ngrok http 3000
|
||||
|
||||
# Opción 2: Vercel dev con --listen
|
||||
vercel dev --listen 3000
|
||||
```
|
||||
|
||||
Usa la URL pública del túnel en Discord Interactions Endpoint.
|
||||
|
||||
## 📁 Estructura del Proyecto
|
||||
|
||||
```
|
||||
api/
|
||||
└── discord/
|
||||
├── interactions.js # Endpoint principal de webhooks
|
||||
├── register-commands.js # Script para registrar comandos
|
||||
├── handlers/ # Handlers de cada comando
|
||||
│ ├── search.js
|
||||
│ ├── info.js
|
||||
│ ├── install.js
|
||||
│ ├── popular.js
|
||||
│ └── random.js
|
||||
└── utils/ # Utilidades compartidas
|
||||
├── componentsLoader.js # Cache de components.json
|
||||
└── embedBuilder.js # Generador de embeds
|
||||
```
|
||||
|
||||
## 🔍 Cómo Funciona
|
||||
|
||||
### Flujo de una Interacción
|
||||
|
||||
1. Usuario ejecuta `/search security` en Discord
|
||||
2. Discord envía POST a `https://your-domain.vercel.app/api/discord/interactions`
|
||||
3. Vercel ejecuta `api/discord/interactions.js`
|
||||
4. El handler verifica la firma (seguridad)
|
||||
5. Identifica el comando y llama al handler correspondiente
|
||||
6. Handler carga components.json (desde cache si existe)
|
||||
7. Ejecuta la búsqueda y genera un embed
|
||||
8. Retorna respuesta a Discord
|
||||
9. Discord muestra el resultado al usuario
|
||||
|
||||
### Cache de Componentes
|
||||
|
||||
El sistema mantiene `components.json` en memoria por 5 minutos:
|
||||
- Primera request: Descarga desde aitmpl.com
|
||||
- Siguientes requests (< 5 min): Usa cache
|
||||
- Después de 5 min: Actualiza automáticamente
|
||||
|
||||
Esto reduce latencia y requests a la API.
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Error: "Invalid request signature"
|
||||
|
||||
**Causa**: La PUBLIC_KEY no está configurada o es incorrecta
|
||||
|
||||
**Solución**:
|
||||
1. Verifica que `DISCORD_PUBLIC_KEY` esté en las variables de entorno de Vercel
|
||||
2. Asegúrate de copiar la Public Key correcta del Developer Portal
|
||||
3. Redeploy después de cambiar variables de entorno
|
||||
|
||||
### Error: Discord no puede verificar la URL
|
||||
|
||||
**Causa**: El endpoint no responde correctamente al PING
|
||||
|
||||
**Solución**:
|
||||
1. Verifica que el deployment fue exitoso
|
||||
2. Prueba manualmente: `curl https://your-domain.vercel.app/api/discord/interactions`
|
||||
3. Revisa los logs en Vercel Dashboard → Functions
|
||||
|
||||
### Comandos no aparecen en Discord
|
||||
|
||||
**Causa**: No se registraron o falta scope
|
||||
|
||||
**Solución**:
|
||||
1. Ejecuta `npm run discord:register` nuevamente
|
||||
2. Si usaste `DISCORD_GUILD_ID`, los comandos solo aparecen en ese server
|
||||
3. Quita el bot del server y vuelve a invitarlo con el scope `applications.commands`
|
||||
4. Espera unos minutos (comandos globales pueden tardar hasta 1 hora)
|
||||
|
||||
### Bot no responde
|
||||
|
||||
**Causa**: Error en el handler o falta de permisos
|
||||
|
||||
**Solución**:
|
||||
1. Revisa logs en Vercel Dashboard → Functions
|
||||
2. Verifica que el bot tenga permisos de "Send Messages" y "Embed Links"
|
||||
3. Prueba con un comando simple como `/random`
|
||||
|
||||
### "Components data not loaded"
|
||||
|
||||
**Causa**: No puede acceder a components.json
|
||||
|
||||
**Solución**:
|
||||
1. Verifica que `https://aitmpl.com/components.json` sea accesible
|
||||
2. Revisa la variable `COMPONENTS_API_URL` si usas una URL custom
|
||||
3. Chequea los logs de Vercel para ver el error específico
|
||||
|
||||
## 📊 Monitoreo
|
||||
|
||||
### Vercel Dashboard
|
||||
|
||||
Ve a tu proyecto en Vercel → Functions para ver:
|
||||
- Invocaciones totales
|
||||
- Errores
|
||||
- Tiempo de respuesta
|
||||
- Logs en tiempo real
|
||||
|
||||
### Discord Logs
|
||||
|
||||
Cada comando registra en consola:
|
||||
```
|
||||
🔹 Command received: /search
|
||||
✅ Components data loaded
|
||||
```
|
||||
|
||||
Revisa estos logs en Vercel Functions.
|
||||
|
||||
## 🚀 Próximos Pasos
|
||||
|
||||
### Fase 2: Comandos Interactivos
|
||||
- `/stats` - Estadísticas de la plataforma
|
||||
- `/new` - Componentes recientes
|
||||
- `/daily` - Componente del día
|
||||
|
||||
### Fase 3: Botones y Menús
|
||||
- Agregar botones de acción a los embeds
|
||||
- Select menus para filtros avanzados
|
||||
- Modal forms para búsqueda compleja
|
||||
|
||||
### Fase 4: Integración Avanzada
|
||||
- Tracking de instalaciones vía Discord
|
||||
- Notificaciones automáticas de releases
|
||||
- Sistema de votación y reviews
|
||||
|
||||
## 💡 Tips y Mejores Prácticas
|
||||
|
||||
1. **Testing**: Usa `DISCORD_GUILD_ID` para testing rápido en tu server
|
||||
2. **Production**: Quita `DISCORD_GUILD_ID` para comandos globales
|
||||
3. **Cache**: Ajusta `CACHE_DURATION` en `componentsLoader.js` según tus necesidades
|
||||
4. **Errores**: Todos los errores retornan embeds ephemeral (solo visibles para el usuario)
|
||||
5. **Logs**: Los logs en Vercel te ayudan a debuggear problemas en producción
|
||||
|
||||
## 📚 Recursos
|
||||
|
||||
- [Discord Developer Portal](https://discord.com/developers/applications)
|
||||
- [Discord Interactions Documentation](https://discord.com/developers/docs/interactions/receiving-and-responding)
|
||||
- [Vercel Functions Documentation](https://vercel.com/docs/functions)
|
||||
- [discord-interactions npm](https://www.npmjs.com/package/discord-interactions)
|
||||
|
||||
## 🤝 Soporte
|
||||
|
||||
Si tienes problemas:
|
||||
1. Revisa los logs en Vercel Dashboard
|
||||
2. Verifica todas las variables de entorno
|
||||
3. Prueba los endpoints manualmente con curl
|
||||
4. Abre un issue en GitHub con los logs relevantes
|
||||
|
||||
---
|
||||
|
||||
**¡Listo!** Tu bot de Discord está corriendo en Vercel Functions 🎉
|
||||
@@ -0,0 +1,193 @@
|
||||
# Growth Kit - Content Marketing Automation Commands
|
||||
|
||||
## Overview
|
||||
|
||||
This PR adds **Growth Kit** - a comprehensive content marketing automation toolkit that helps developers distribute blog content across social media platforms with a single command.
|
||||
|
||||
Built by [Kanaeru Labs](https://www.kanaeru.ai) while building Kanaeru AI, these commands solve the time-consuming problem of manually converting blog posts into platform-specific content.
|
||||
|
||||
## What's Included
|
||||
|
||||
### Marketing Commands (5 new commands)
|
||||
|
||||
**New Category:** `commands/marketing/`
|
||||
|
||||
1. **`publisher-x.md`** - X/Twitter Thread Generator
|
||||
- Generates copy-pastable X threads from any content source
|
||||
- **3 format options**: Thread (5-8 posts), Single Long, Single Short
|
||||
- Supports multi-language (EN/JA)
|
||||
- Creates interactive HTML preview with copy buttons
|
||||
- Auto-opens X.com for posting
|
||||
|
||||
2. **`publisher-linkedin.md`** - LinkedIn Post Generator
|
||||
- Creates professional LinkedIn posts via API
|
||||
- Automatic media attachment (images/PDFs)
|
||||
- Handles OAuth flow automatically
|
||||
- Works with zero dependencies (bash + curl only)
|
||||
|
||||
3. **`publisher-medium.md`** - Medium Article Converter
|
||||
- Converts blog posts to Medium-ready HTML
|
||||
- Image upload markers with file paths
|
||||
- One-click copy from HTML preview
|
||||
- Opens Medium editor automatically
|
||||
|
||||
4. **`publisher-devto.md`** - Dev.to RSS Generator
|
||||
- Generates complete RSS feed from all blog posts
|
||||
- One-time setup for automatic syndication
|
||||
- All future posts auto-import to Dev.to
|
||||
|
||||
5. **`publisher-all.md`** - All-Platform Generator
|
||||
- Runs all 4 publishers in one command
|
||||
- Saves ~2 hours of manual work per post
|
||||
- Opens all previews in browser tabs
|
||||
|
||||
### Setup Commands (1 new command)
|
||||
|
||||
**Existing Category:** `commands/setup/`
|
||||
|
||||
6. **`vercel-analytics.md`** - Vercel Analytics Setup
|
||||
- Auto-installs @vercel/analytics and @vercel/speed-insights
|
||||
- Configures React/Vite apps
|
||||
- Creates vercel.json for SPA routing
|
||||
- Fixes 404 errors on Vercel deployment
|
||||
|
||||
## Key Features
|
||||
|
||||
✅ **Universal Input Support**
|
||||
- Blog post slugs (e.g., `2025-10-06-my-post`)
|
||||
- File paths (markdown, PDF, HTML, text)
|
||||
- URLs (fetches and converts)
|
||||
|
||||
✅ **Zero Dependencies**
|
||||
- Works in ANY repo type (Python, Rust, Go, JavaScript, etc.)
|
||||
- Uses only Claude's built-in tools (Read, Write, Bash, Glob, WebFetch)
|
||||
- No Node.js/npm required (except for Vercel Analytics)
|
||||
|
||||
✅ **Multi-Language Support**
|
||||
- English and Japanese (EN/JA)
|
||||
- Auto-detects language from content/path
|
||||
- Platform-specific tone and formatting
|
||||
|
||||
✅ **Production-Ready**
|
||||
- Tested at Kanaeru Labs in production
|
||||
- Handles LinkedIn API authentication
|
||||
- Proper character counting for X/Twitter
|
||||
- HTML preview files with copy buttons
|
||||
|
||||
## Use Cases
|
||||
|
||||
**For Blog Authors:**
|
||||
```bash
|
||||
# Distribute a blog post across all platforms
|
||||
/publisher-all my-latest-post
|
||||
|
||||
# Generate just an X thread
|
||||
/publisher-x my-latest-post
|
||||
|
||||
# Create LinkedIn post with custom image
|
||||
/publisher-linkedin my-latest-post en diagram.png
|
||||
```
|
||||
|
||||
**For Content Marketers:**
|
||||
```bash
|
||||
# Convert any URL to X thread
|
||||
/publisher-x https://competitor.com/article
|
||||
|
||||
# Generate Medium article from PDF
|
||||
/publisher-medium whitepaper.pdf
|
||||
|
||||
# Set up Dev.to auto-syndication
|
||||
/publisher-devto
|
||||
```
|
||||
|
||||
**For Developers:**
|
||||
```bash
|
||||
# Add analytics to React app
|
||||
/vercel-analytics
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
All commands have been:
|
||||
- ✅ Tested in production at Kanaeru Labs
|
||||
- ✅ Validated across multiple blog structures
|
||||
- ✅ Verified to work in Python, Rust, Go, and JavaScript repos
|
||||
- ✅ Tested with both English and Japanese content
|
||||
|
||||
## Benefits to the Community
|
||||
|
||||
**Time Savings:**
|
||||
- X thread generation: ~30 minutes → 2 minutes
|
||||
- All platforms: ~2 hours → 5 minutes
|
||||
- One-time Dev.to setup enables automatic future syndication
|
||||
|
||||
**Developer-Friendly:**
|
||||
- No configuration required (auto-detects blog structure)
|
||||
- Works universally (any repo type, any framework)
|
||||
- Clear error messages and helpful prompts
|
||||
|
||||
**Production Quality:**
|
||||
- Proper API authentication handling
|
||||
- Character limit validation
|
||||
- Multi-language support
|
||||
- HTML previews for easy copying
|
||||
|
||||
## Integration Notes
|
||||
|
||||
**File Locations:**
|
||||
- Marketing commands: `cli-tool/components/commands/marketing/`
|
||||
- Analytics command: `cli-tool/components/commands/setup/`
|
||||
|
||||
**Frontmatter Format:**
|
||||
All commands include proper frontmatter:
|
||||
```yaml
|
||||
---
|
||||
allowed-tools: Read, Write, Bash, Glob, WebFetch
|
||||
argument-hint: <input> [lang]
|
||||
description: [Clear description]
|
||||
---
|
||||
```
|
||||
|
||||
**Auto-Discovery:**
|
||||
Commands will be automatically discovered by the `scripts/generate_components_json.py` script when scanning the commands directory.
|
||||
|
||||
## Screenshots
|
||||
|
||||
Available at: https://github.com/kanaerulabs/growth-kit
|
||||
|
||||
See the Growth Kit README for visual examples of:
|
||||
- X thread preview with 3 format tabs
|
||||
- LinkedIn post with PDF attachment
|
||||
- Medium article preview
|
||||
- Dev.to RSS feed structure
|
||||
|
||||
## Attribution
|
||||
|
||||
**Author:** Kanaeru Labs (https://www.kanaeru.ai)
|
||||
**Source Repository:** https://github.com/kanaerulabs/growth-kit
|
||||
**License:** MIT
|
||||
**Contact:** support@kanaeru.ai
|
||||
|
||||
## Related Resources
|
||||
|
||||
- Growth Kit Documentation: https://github.com/kanaerulabs/growth-kit
|
||||
- Example Blog Posts: See Growth Kit repo for real examples
|
||||
- LinkedIn API Setup: https://www.linkedin.com/developers/apps
|
||||
|
||||
---
|
||||
|
||||
## Why This Matters
|
||||
|
||||
Content distribution is a major bottleneck for developer bloggers and technical content creators. These commands solve real pain points we experienced while building Kanaeru AI:
|
||||
|
||||
- **Manual X thread creation** - repetitive and time-consuming
|
||||
- **Platform-specific formatting** - each platform has different requirements
|
||||
- **Character counting** - easy to exceed limits
|
||||
- **Media attachment** - LinkedIn API complexity
|
||||
- **Syndication setup** - Dev.to RSS configuration
|
||||
|
||||
Growth Kit automates all of this with Claude Code's built-in capabilities.
|
||||
|
||||
---
|
||||
|
||||
**We believe these commands will be valuable to the Claude Code community and help developers share their technical content more effectively.**
|
||||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,727 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Claude Code Templates - Supercharge Your AI-Powered Development with Anthropic's Claude Code</title>
|
||||
<meta name="description" content="Professional templates and configurations for Anthropic's Claude Code. Get Claude Opus 4.1 working at terminal velocity with 100+ agents, 159+ commands, settings, hooks, and MCPs. Transform your development workflow with AI.">
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://aitmpl.com/">
|
||||
<meta property="og:title" content="Claude Code Templates - Supercharge Your AI Development with Anthropic Claude">
|
||||
<meta property="og:description" content="Professional templates for Anthropic's Claude Code. Deep coding at terminal velocity with Claude Opus 4.1. Install 100+ agents, commands, settings & hooks. Transform your AI-powered development workflow.">
|
||||
<meta property="og:image" content="https://aitmpl.com/images/social-preview.png">
|
||||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="630">
|
||||
<meta property="og:image:alt" content="Claude Code Templates - Supercharge Your AI Development with Anthropic Claude">
|
||||
<meta property="og:site_name" content="Claude Code Templates">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://aitmpl.com/">
|
||||
<meta name="twitter:title" content="Claude Code Templates - Supercharge Your AI Development">
|
||||
<meta name="twitter:description" content="Professional templates for Anthropic's Claude Code. Deep coding at terminal velocity with Claude Opus 4.1. Install 100+ agents, commands, settings & hooks.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/images/social-preview.png">
|
||||
<meta name="twitter:image:alt" content="Claude Code Templates - Supercharge Your AI Development with Anthropic Claude">
|
||||
<meta name="twitter:creator" content="@davila7">
|
||||
<meta name="twitter:site" content="@davila7">
|
||||
|
||||
<!-- Additional SEO -->
|
||||
<meta name="keywords" content="Claude Code, Anthropic Claude, AI development, Claude Opus 4.1, code templates, AI assistant, terminal velocity, deep coding, agents, commands, MCP, Model Context Protocol, development tools, AI-powered development, Claude Code configurations, Claude Code setup, agentic search, multi-file edits">
|
||||
<meta name="author" content="Claude Code Templates Community">
|
||||
<meta name="robots" content="index, follow">
|
||||
<link rel="canonical" href="https://aitmpl.com/">
|
||||
|
||||
<!-- Sitemap for SEO -->
|
||||
<link rel="sitemap" type="application/xml" href="/sitemap.xml">
|
||||
|
||||
<!-- Claude Code / Anthropic relation -->
|
||||
<meta name="product" content="Claude Code Templates">
|
||||
<meta name="platform" content="Anthropic Claude Code">
|
||||
<meta name="category" content="AI Development Tools">
|
||||
|
||||
<!-- 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">
|
||||
|
||||
<link rel="stylesheet" href="css/styles.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">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/devicon.min.css">
|
||||
<script src="https://cdn.counter.dev/script.js" data-id="1f599060-4af4-4cbc-a809-a158768ab768" data-utcoffset="-4"></script>
|
||||
|
||||
<!-- 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 for SEO - SoftwareApplication -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "SoftwareApplication",
|
||||
"name": "Claude Code Templates",
|
||||
"applicationCategory": "DeveloperApplication",
|
||||
"description": "Professional templates and configurations for Anthropic's Claude Code. Transform your AI-powered development workflow with Claude Opus 4.1 at terminal velocity.",
|
||||
"operatingSystem": ["Windows", "macOS", "Linux"],
|
||||
"softwareVersion": "1.18.0",
|
||||
"url": "https://aitmpl.com/",
|
||||
"downloadUrl": "https://www.npmjs.com/package/claude-code-templates",
|
||||
"author": {
|
||||
"@type": "Organization",
|
||||
"name": "Claude Code Templates Community",
|
||||
"url": "https://github.com/davila7/claude-code-templates"
|
||||
},
|
||||
"offers": {
|
||||
"@type": "Offer",
|
||||
"price": "0",
|
||||
"priceCurrency": "USD"
|
||||
},
|
||||
"relatedLink": "https://www.anthropic.com/claude-code",
|
||||
"keywords": "Claude Code, Anthropic, AI development, Claude Opus 4.1, terminal velocity, deep coding"
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Structured Data for SEO - WebSite with SearchAction -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebSite",
|
||||
"name": "Claude Code Templates",
|
||||
"url": "https://aitmpl.com/",
|
||||
"description": "Professional templates and configurations for Anthropic's Claude Code",
|
||||
"potentialAction": {
|
||||
"@type": "SearchAction",
|
||||
"target": "https://aitmpl.com/?search={search_term_string}",
|
||||
"query-input": "required name=search_term_string"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Screen reader only class for SEO-visible H1 -->
|
||||
<style>
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Beta dashboard banner -->
|
||||
<div style="background: linear-gradient(135deg, #059669, #0d9488, #2563eb); text-align: center; padding: 32px 24px; font-family: system-ui, -apple-system, sans-serif; color: white; border-bottom: 2px solid rgba(255,255,255,0.1);">
|
||||
<a href="https://app.aitmpl.com" target="_blank" rel="noopener noreferrer" style="color: white; text-decoration: none; display: flex; flex-direction: column; align-items: center; gap: 10px;">
|
||||
<div style="display: flex; align-items: center; gap: 14px;">
|
||||
<span style="font-size: 28px;">🚀</span>
|
||||
<span style="font-size: 24px; font-weight: 700;">Try our new Dashboard</span>
|
||||
<span style="background: rgba(255,255,255,0.2); padding: 4px 14px; border-radius: 6px; font-size: 13px; font-weight: 700; letter-spacing: 1.5px;">BETA</span>
|
||||
</div>
|
||||
<span style="font-size: 16px; opacity: 0.85;">Browse, search, and collect components in a modern UI →</span>
|
||||
</a>
|
||||
</div>
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<div class="header-content">
|
||||
<div class="terminal-header">
|
||||
<h1 class="sr-only">Claude Code Templates - AI-Powered Development Tools for Anthropic's Claude Code</h1>
|
||||
<div class="ascii-title" aria-hidden="true">
|
||||
<pre class="ascii-art"> ██████╗██╗ █████╗ ██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ███████╗ ████████╗███████╗███╗ ███╗██████╗ ██╗ █████╗ ████████╗███████╗███████╗
|
||||
██╔════╝██║ ██╔══██╗██║ ██║██╔══██╗██╔════╝ ██╔════╝██╔═══██╗██╔══██╗██╔════╝ ╚══██╔══╝██╔════╝████╗ ████║██╔══██╗██║ ██╔══██╗╚══██╔══╝██╔════╝██╔════╝
|
||||
██║ ██║ ███████║██║ ██║██║ ██║█████╗ ██║ ██║ ██║██║ ██║█████╗ ██║ █████╗ ██╔████╔██║██████╔╝██║ ███████║ ██║ █████╗ ███████╗
|
||||
██║ ██║ ██╔══██║██║ ██║██║ ██║██╔══╝ ██║ ██║ ██║██║ ██║██╔══╝ ██║ ██╔══╝ ██║╚██╔╝██║██╔═══╝ ██║ ██╔══██║ ██║ ██╔══╝ ╚════██║
|
||||
╚██████╗███████╗██║ ██║╚██████╔╝██████╔╝███████╗ ╚██████╗╚██████╔╝██████╔╝███████╗ ██║ ███████╗██║ ╚═╝ ██║██║ ███████╗██║ ██║ ██║ ███████╗███████║
|
||||
╚═════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝</pre>
|
||||
</div>
|
||||
<div class="terminal-subtitle">
|
||||
<span class="status-dot"></span>
|
||||
Ready-to-use configurations for your <strong>Claude Code</strong> projects
|
||||
</div>
|
||||
</div>
|
||||
<nav class="header-actions" aria-label="Main navigation">
|
||||
<a href="blog/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://discord.gg/dyTTwzBhwY" target="_blank" rel="noopener noreferrer" class="header-btn">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/>
|
||||
</svg>
|
||||
Discord
|
||||
</a>
|
||||
<!-- <a href="jobs.html" class="header-btn">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M10,2H14A2,2 0 0,1 16,4V6H20A2,2 0 0,1 22,8V19A2,2 0 0,1 20,21H4A2,2 0 0,1 2,19V8A2,2 0 0,1 4,6H8V4A2,2 0 0,1 10,2M14,6V4H10V6H14Z"/>
|
||||
</svg>
|
||||
Jobs
|
||||
</a> -->
|
||||
<a href="https://github.com/davila7/claude-code-templates" target="_blank" rel="noopener noreferrer" 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.23 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>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="terminal" role="main" aria-label="Claude Code Templates component browser">
|
||||
<section class="stats-section">
|
||||
<div class="stats-badges">
|
||||
<a href="https://z.ai?utm_source=aitmpl.com&utm_medium=badge&utm_campaign=partnership" target="_blank" class="stat-badge-link">
|
||||
<img src="https://img.shields.io/badge/Sponsored%20by-Z.AI-2563eb?style=flat&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cGF0aCBkPSJNMTIgMkw0IDhWMTRMMTIgMjBMMjAgMTRWOEwxMiAyWiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+" alt="Sponsored by Z.AI" class="stat-badge">
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/claude-code-templates" target="_blank" class="stat-badge-link">
|
||||
<img src="https://img.shields.io/npm/dt/claude-code-templates.svg" alt="npm downloads" class="stat-badge">
|
||||
</a>
|
||||
<a href="https://github.com/davila7/claude-code-templates" target="_blank" class="stat-badge-link">
|
||||
<img src="https://img.shields.io/github/stars/davila7/claude-code-templates.svg?style=social&label=Star" alt="GitHub stars" class="stat-badge">
|
||||
</a>
|
||||
<a href="https://github.com/davila7/claude-code-templates/blob/main/LICENSE" target="_blank" class="stat-badge-link">
|
||||
<img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat" alt="MIT License" class="stat-badge">
|
||||
</a>
|
||||
<a href="https://vercel.com/blog/vercel-open-source-program-fall-2025-cohort" target="_blank" class="stat-badge-link">
|
||||
<img src="https://vercel.com/oss/program-badge.svg" alt="Vercel OSS Program" class="stat-badge">
|
||||
</a>
|
||||
<a href="https://get.neon.com/4eCjZDz" target="_blank" class="stat-badge-link">
|
||||
<img src="https://img.shields.io/badge/Neon-Open%20Source%20Program-00E599?style=flat&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxMCIgZmlsbD0iIzAwRTU5OSIvPjwvc3ZnPg==" alt="Neon Open Source Program" class="stat-badge">
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<!-- Featured Projects - Compact -->
|
||||
<section class="featured-section featured-compact" id="featuredSection">
|
||||
<div class="featured-header">
|
||||
<span class="featured-label">Featured</span>
|
||||
<div class="carousel-nav">
|
||||
<button class="carousel-nav-btn" onclick="scrollFeaturedCarousel('left')">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M15.41,16.58L10.83,12L15.41,7.41L14,6L8,12L14,18L15.41,16.58Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="carousel-nav-btn" onclick="scrollFeaturedCarousel('right')">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-track-compact" id="featuredCarousel">
|
||||
<!-- Neon Featured Card -->
|
||||
<a href="featured/neon-instagres/index.html" class="featured-card-compact" target="_blank">
|
||||
<div class="featured-logo-compact">
|
||||
<img src="https://neon.tech/brand/neon-logo-dark-color.svg" alt="Neon Logo" onerror="this.style.display='none';">
|
||||
</div>
|
||||
<div class="featured-info-compact">
|
||||
<h3>Neon</h3>
|
||||
<p>Complete Postgres Template</p>
|
||||
</div>
|
||||
<span class="featured-arrow-compact">→</span>
|
||||
</a>
|
||||
<!-- ClaudeKit Featured Card -->
|
||||
<a href="featured/claudekit/index.html" class="featured-card-compact" target="_blank">
|
||||
<div class="featured-logo-compact">
|
||||
<img src="https://docs.claudekit.cc/logo-horizontal.png" alt="ClaudeKit Logo" onerror="this.style.display='none';">
|
||||
</div>
|
||||
<div class="featured-info-compact">
|
||||
<h3>ClaudeKit</h3>
|
||||
<p>AI Agents & Skills for Claude Code</p>
|
||||
</div>
|
||||
<span class="featured-arrow-compact">→</span>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="unified-filter-section">
|
||||
<div class="search-header">
|
||||
<div class="terminal-command">
|
||||
<div class="header-content">
|
||||
<h2 class="search-title"><span class="terminal-dot"></span><strong>Search </strong><span class="title-params">(components/settings/templates)</span></h2>
|
||||
<p class="search-subtitle">⎯ Build your personalized development stack</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="terminal-search-container">
|
||||
<div class="terminal-search-wrapper" onclick="focusSearchInput()">
|
||||
<span class="terminal-prompt">></span>
|
||||
<label for="searchInput" class="sr-only">Search Claude Code components</label>
|
||||
<input type="text" id="searchInput" class="terminal-search-input" placeholder="Search components..." oninput="handleSearchInput(event)" onkeydown="handleSearchKeydown(event)" aria-label="Search Claude Code components, agents, settings, hooks, and MCPs">
|
||||
<div class="terminal-cursor" id="terminalCursor"></div>
|
||||
<svg class="search-icon-end" width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M15.5,14H14.71L14.43,13.73C15.41,12.59 16,11.11 16,9.5C16,5.91 13.09,3 9.5,3C5.91,3 3,5.91 3,9.5C3,13.09 5.91,16 9.5,16C11.11,16 12.59,15.41 13.73,14.43L14,14.71V15.5L19,20.49L20.49,19L15.5,14M9.5,14C7.01,14 5,11.99 5,9.5C5,7.01 7.01,5 9.5,5C11.99,5 14,7.01 14,9.5C14,11.99 11.99,14 9.5,14Z"/>
|
||||
</svg>
|
||||
<button class="terminal-clear-btn" id="clearSearchBtn" onclick="clearSearch()" style="display: none;" title="Clear search">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="search-results-info" id="searchResultsInfo" style="display: none;">
|
||||
<div class="terminal-results">
|
||||
<span class="terminal-dot"></span>
|
||||
<div class="results-content">
|
||||
<div class="results-count" id="resultsCount">Found(0 results)</div>
|
||||
<div class="search-filters">⎯ <span class="search-filter-tags" id="searchFilterTags"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="component-type-filters">
|
||||
<div class="filter-group">
|
||||
<div class="filter-chips">
|
||||
<a href="/skills" class="filter-chip active" data-filter="skills" onclick="handleFilterClick(event, 'skills')">
|
||||
<span class="new-label">NEW</span>
|
||||
<span class="chip-icon">🎨</span>skills
|
||||
</a>
|
||||
<a href="/agents" class="filter-chip" data-filter="agents" onclick="handleFilterClick(event, 'agents')">
|
||||
<span class="chip-icon">🤖</span>agents
|
||||
</a>
|
||||
<a href="/commands" class="filter-chip" data-filter="commands" onclick="handleFilterClick(event, 'commands')">
|
||||
<span class="chip-icon">⚡</span>commands
|
||||
</a>
|
||||
<a href="/settings" class="filter-chip" data-filter="settings" onclick="handleFilterClick(event, 'settings')">
|
||||
<span class="chip-icon">⚙️</span>settings
|
||||
</a>
|
||||
<a href="/hooks" class="filter-chip" data-filter="hooks" onclick="handleFilterClick(event, 'hooks')">
|
||||
<span class="chip-icon">🪝</span>hooks
|
||||
</a>
|
||||
<a href="/mcps" class="filter-chip" data-filter="mcps" onclick="handleFilterClick(event, 'mcps')">
|
||||
<span class="chip-icon">🔌</span>mcps
|
||||
</a>
|
||||
<a href="/templates" class="filter-chip" data-filter="templates" onclick="handleFilterClick(event, 'templates')" style="display: none;">
|
||||
<span class="chip-icon">📦</span>templates
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="component-categories" id="componentCategories" style="display: none;">
|
||||
<div class="category-group">
|
||||
<span class="category-group-label">category:</span>
|
||||
<div class="category-chips" id="categoryChips"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="content-grid" id="contentGrid">
|
||||
<div class="sort-controls" id="sortControls">
|
||||
<div class="sort-wrapper">
|
||||
<span class="sort-label">Sort by:</span>
|
||||
<select class="sort-selector" id="sortSelector" onchange="handleSortChange(this.value)">
|
||||
<option value="downloads" selected>Most Downloaded</option>
|
||||
<option value="alphabetical">Alphabetical</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="loading-indicator" id="components-loading" style="display: none;">
|
||||
<div class="loading-spinner"></div>
|
||||
<div class="loading-text">
|
||||
<h3>Loading Components...</h3>
|
||||
<p>Optimizing data for better performance on mobile devices</p>
|
||||
<div class="loading-progress">
|
||||
<div class="progress-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="unified-grid" id="unifiedGrid">
|
||||
<!-- All content will be loaded here by JavaScript -->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Company Stacks Carousel -->
|
||||
<section class="company-stacks-carousel" style="display: none;">
|
||||
<div class="command-block">
|
||||
<div class="command-label">$ Popular Development Stacks</div>
|
||||
<div class="command-description">Complete component collections for major companies and technologies</div>
|
||||
</div>
|
||||
<div class="carousel-container">
|
||||
<button class="carousel-btn carousel-btn-left" onclick="scrollCarousel('left')">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M15.41,16.58L10.83,12L15.41,7.41L14,6L8,12L14,18L15.41,16.58Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="carousel-track" id="companyCarousel">
|
||||
<a href="#/company/openai" class="company-stack-card" onclick="window.stackRouter?.navigateTo('#/company/openai')">
|
||||
<div class="company-logo">🤖</div>
|
||||
<div class="company-info">
|
||||
<h3>OpenAI</h3>
|
||||
<p>GPT, DALL-E & Whisper APIs</p>
|
||||
</div>
|
||||
<div class="stack-arrow">→</div>
|
||||
</a>
|
||||
<a href="#/company/anthropic" class="company-stack-card" onclick="window.stackRouter?.navigateTo('#/company/anthropic')">
|
||||
<div class="company-logo">🧠</div>
|
||||
<div class="company-info">
|
||||
<h3>Anthropic</h3>
|
||||
<p>Claude AI integration</p>
|
||||
</div>
|
||||
<div class="stack-arrow">→</div>
|
||||
</a>
|
||||
<a href="#/company/stripe" class="company-stack-card" onclick="window.stackRouter?.navigateTo('#/company/stripe')">
|
||||
<div class="company-logo">💳</div>
|
||||
<div class="company-info">
|
||||
<h3>Stripe</h3>
|
||||
<p>Payment processing APIs</p>
|
||||
</div>
|
||||
<div class="stack-arrow">→</div>
|
||||
</a>
|
||||
<a href="#/company/salesforce" class="company-stack-card" onclick="window.stackRouter?.navigateTo('#/company/salesforce')">
|
||||
<div class="company-logo">☁️</div>
|
||||
<div class="company-info">
|
||||
<h3>Salesforce</h3>
|
||||
<p>CRM & Lightning platform</p>
|
||||
</div>
|
||||
<div class="stack-arrow">→</div>
|
||||
</a>
|
||||
<a href="#/company/shopify" class="company-stack-card" onclick="window.stackRouter?.navigateTo('#/company/shopify')">
|
||||
<div class="company-logo">🛒</div>
|
||||
<div class="company-info">
|
||||
<h3>Shopify</h3>
|
||||
<p>E-commerce APIs & apps</p>
|
||||
</div>
|
||||
<div class="stack-arrow">→</div>
|
||||
</a>
|
||||
<a href="#/company/twilio" class="company-stack-card" onclick="window.stackRouter?.navigateTo('#/company/twilio')">
|
||||
<div class="company-logo">📞</div>
|
||||
<div class="company-info">
|
||||
<h3>Twilio</h3>
|
||||
<p>Communication APIs</p>
|
||||
</div>
|
||||
<div class="stack-arrow">→</div>
|
||||
</a>
|
||||
<a href="#/company/aws" class="company-stack-card" onclick="window.stackRouter?.navigateTo('#/company/aws')">
|
||||
<div class="company-logo">☁️</div>
|
||||
<div class="company-info">
|
||||
<h3>AWS</h3>
|
||||
<p>Cloud & serverless APIs</p>
|
||||
</div>
|
||||
<div class="stack-arrow">→</div>
|
||||
</a>
|
||||
<a href="#/company/github" class="company-stack-card" onclick="window.stackRouter?.navigateTo('#/company/github')">
|
||||
<div class="company-logo">🐙</div>
|
||||
<div class="company-info">
|
||||
<h3>GitHub</h3>
|
||||
<p>Git automation & Actions</p>
|
||||
</div>
|
||||
<div class="stack-arrow">→</div>
|
||||
</a>
|
||||
<a href="#/companies" class="company-stack-card view-all-card" onclick="window.stackRouter?.navigateTo('#/companies')">
|
||||
<div class="company-logo">👀</div>
|
||||
<div class="company-info">
|
||||
<h3>View All</h3>
|
||||
<p>30+ companies & platforms</p>
|
||||
</div>
|
||||
<div class="stack-arrow">→</div>
|
||||
</a>
|
||||
</div>
|
||||
<button class="carousel-btn carousel-btn-right" onclick="scrollCarousel('right')">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="additional-tools">
|
||||
<div class="command-block">
|
||||
<div class="command-label">$ Additional Tools</div>
|
||||
<div class="command-description">Advanced tools to maximize your Claude Code AI development experience</div>
|
||||
</div>
|
||||
<div class="tools-grid">
|
||||
<div class="tool-card">
|
||||
<div class="tool-icon">📊</div>
|
||||
<h3>Claude Code Analytics</h3>
|
||||
<p>Monitor your AI-powered development sessions in real-time. Track Claude Opus 4.1 performance, tool usage, and productivity metrics</p>
|
||||
<div class="command-line">
|
||||
<span class="prompt">$</span>
|
||||
<code class="command">npx claude-code-templates@latest --analytics</code>
|
||||
<button class="copy-btn" onclick="copyToClipboard('npx claude-code-templates@latest --analytics')">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tool-card">
|
||||
<div class="tool-icon">🔍</div>
|
||||
<h3>Claude Code Health Check</h3>
|
||||
<p>Comprehensive diagnostics to ensure your Anthropic Claude Code installation is optimized for deep coding at terminal velocity</p>
|
||||
<div class="command-line">
|
||||
<span class="prompt">$</span>
|
||||
<code class="command">npx claude-code-templates@latest --health-check</code>
|
||||
<button class="copy-btn" onclick="copyToClipboard('npx claude-code-templates@latest --health-check')">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tool-card">
|
||||
<div class="tool-icon">💬</div>
|
||||
<h3>Claude Conversation Monitor</h3>
|
||||
<p>View Claude Opus 4.1 responses in real-time, analyze AI reasoning, and monitor agentic search patterns during your coding sessions</p>
|
||||
<div class="command-line">
|
||||
<span class="prompt">$</span>
|
||||
<code class="command">npx claude-code-templates@latest --chats</code>
|
||||
<button class="copy-btn" onclick="copyToClipboard('npx claude-code-templates@latest --chats')">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tool-card">
|
||||
<span class="new-label">NEW</span>
|
||||
<div class="tool-icon">🧩</div>
|
||||
<h3>Plugin Dashboard</h3>
|
||||
<p>Manage Claude Code plugins with a visual dashboard. View installed marketplaces, enable/disable plugins, and monitor component status in real-time</p>
|
||||
<div class="command-line">
|
||||
<span class="prompt">$</span>
|
||||
<code class="command">npx claude-code-templates@latest --plugins</code>
|
||||
<button class="copy-btn" onclick="copyToClipboard('npx claude-code-templates@latest --plugins')">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<div class="footer-content">
|
||||
<div class="footer-brand">
|
||||
<div class="footer-ascii">
|
||||
<pre class="footer-ascii-art"> █████╗ ██╗████████╗███╗ ███╗██████╗ ██╗
|
||||
██╔══██╗██║╚══██╔══╝████╗ ████║██╔══██╗██║
|
||||
███████║██║ ██║ ██╔████╔██║██████╔╝██║
|
||||
██╔══██║██║ ██║ ██║╚██╔╝██║██╔═══╝ ██║
|
||||
██║ ██║██║ ██║ ██║ ╚═╝ ██║██║ ███████╗
|
||||
╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚══════╝</pre>
|
||||
<p class="footer-tagline">Supercharge Anthropic's Claude Code</p>
|
||||
</div>
|
||||
<p class="footer-copyright">© 2026 Claude Code Templates. Open source project.</p>
|
||||
</div>
|
||||
<nav class="footer-columns" aria-label="Footer navigation">
|
||||
<div class="footer-column">
|
||||
<h4 class="footer-column-title">Product</h4>
|
||||
<a href="trending.html" class="footer-link">Trending</a>
|
||||
<a href="blog/" class="footer-link">Blog</a>
|
||||
<a href="https://www.anthropic.com/claude-code?ref=aitmpl.com" target="_blank" rel="noopener noreferrer" class="footer-link">Claude Code</a>
|
||||
</div>
|
||||
<div class="footer-column">
|
||||
<h4 class="footer-column-title">Community</h4>
|
||||
<a href="https://discord.gg/dyTTwzBhwY" target="_blank" rel="noopener noreferrer" class="footer-link">Discord</a>
|
||||
<a href="https://github.com/davila7/claude-code-templates" target="_blank" rel="noopener noreferrer" class="footer-link">GitHub</a>
|
||||
</div>
|
||||
<div class="footer-column">
|
||||
<h4 class="footer-column-title">Resources</h4>
|
||||
<a href="https://docs.aitmpl.com/" target="_blank" rel="noopener noreferrer" class="footer-link">Documentation</a>
|
||||
<a href="https://www.npmjs.com/package/claude-code-templates" target="_blank" rel="noopener noreferrer" class="footer-link">npm Package</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Shopping Cart Sidebar -->
|
||||
<div id="shoppingCart" class="cart-sidebar">
|
||||
<div class="cart-content">
|
||||
<div class="cart-header">
|
||||
<div class="cart-header-main">
|
||||
<h3>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M7 18c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-0.16 0.27-0.25 0.58-0.25 0.96 0 1.1 0.9 2 2 2h12v-2H7.42c-0.14 0-0.25-0.11-0.25-0.25l0.03-0.12L8.1 13h7.45c0.75 0 1.41-0.41 1.75-1.03L21.7 4H5.21l-0.94-2H1zM17 18c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2z"/>
|
||||
</svg>
|
||||
Stack Builder
|
||||
</h3>
|
||||
<button class="cart-close" onclick="closeCart()">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Clear All Button - Subtle position -->
|
||||
<div class="cart-clear-section-prominent" id="cartClearProminent" style="display: none;">
|
||||
<button class="clear-all-btn-prominent" onclick="clearCart()">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M9,3V4H4V6H5V19A2,2 0 0,0 7,21H17A2,2 0 0,0 19,19V6H20V4H15V3H9M7,6H17V19H7V6M9,8V17H11V8H9M13,8V17H15V8H13Z"/>
|
||||
</svg>
|
||||
Clear All
|
||||
<span class="clear-count" id="clearCount">(0)</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cart-body">
|
||||
<div class="cart-empty" id="cartEmpty">
|
||||
<div class="empty-cart-icon">🛒</div>
|
||||
<p>Your stack is empty</p>
|
||||
<small>Add agents, commands, settings, hooks, or MCPs to build your development stack</small>
|
||||
</div>
|
||||
|
||||
<div class="cart-items" id="cartItems" style="display: none;">
|
||||
|
||||
<div class="cart-section">
|
||||
<h4>
|
||||
<span class="section-icon">🤖</span>
|
||||
Agents (<span id="agentsCount">0</span>)
|
||||
</h4>
|
||||
<div class="cart-section-items" id="agentsList"></div>
|
||||
</div>
|
||||
|
||||
<div class="cart-section">
|
||||
<h4>
|
||||
<span class="section-icon">⚡</span>
|
||||
Commands (<span id="commandsCount">0</span>)
|
||||
</h4>
|
||||
<div class="cart-section-items" id="commandsList"></div>
|
||||
</div>
|
||||
|
||||
<div class="cart-section">
|
||||
<h4>
|
||||
<span class="section-icon">⚙️</span>
|
||||
Settings (<span id="settingsCount">0</span>)
|
||||
</h4>
|
||||
<div class="cart-section-items" id="settingsList"></div>
|
||||
</div>
|
||||
|
||||
<div class="cart-section">
|
||||
<h4>
|
||||
<span class="section-icon">🪝</span>
|
||||
Hooks (<span id="hooksCount">0</span>)
|
||||
</h4>
|
||||
<div class="cart-section-items" id="hooksList"></div>
|
||||
</div>
|
||||
|
||||
<div class="cart-section">
|
||||
<h4>
|
||||
<span class="section-icon">🔌</span>
|
||||
MCPs (<span id="mcpsCount">0</span>)
|
||||
</h4>
|
||||
<div class="cart-section-items" id="mcpsList"></div>
|
||||
</div>
|
||||
|
||||
<div class="cart-section">
|
||||
<h4>
|
||||
<span class="section-icon">🎨</span>
|
||||
Skills (<span id="skillsCount">0</span>)
|
||||
</h4>
|
||||
<div class="cart-section-items" id="skillsList"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cart-footer" id="cartFooter" style="display: none;">
|
||||
<div class="command-preview">
|
||||
<div class="command-preview-header">
|
||||
<span>Generated Command:</span>
|
||||
</div>
|
||||
<div class="cart-command-container">
|
||||
<div class="command-code" id="generatedCommand">
|
||||
npx claude-code-templates@latest
|
||||
</div>
|
||||
<button class="cart-copy-overlay-btn" onclick="copyCartCommand()" title="Copy command">
|
||||
<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
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cart-instructions">
|
||||
<div class="instructions-header">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M13,9H11V7H13M13,17H11V11H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />
|
||||
</svg>
|
||||
<strong>Instructions</strong>
|
||||
</div>
|
||||
<p>Navigate to your project root and run the generated command. All selected components will be installed automatically.</p>
|
||||
</div>
|
||||
|
||||
<div class="cart-actions">
|
||||
<button class="copy-command-btn" onclick="copyCartCommand()">
|
||||
<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 Command
|
||||
</button>
|
||||
<div class="share-dropdown" id="shareDropdown">
|
||||
<button class="share-stack-btn" onclick="toggleShareDropdown()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.6 20.92,19A2.84,2.84 0 0,0 18,16.08Z"/>
|
||||
</svg>
|
||||
Share Stack
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor" class="dropdown-arrow">
|
||||
<path d="M7,10L12,15L17,10H7Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="share-options" id="shareOptions">
|
||||
<button class="share-option-btn" onclick="shareOnTwitter()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
|
||||
</svg>
|
||||
Share on X
|
||||
</button>
|
||||
<button class="share-option-btn" onclick="shareOnThreads()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12.186 24h-.007c-3.581-.024-6.334-1.205-8.184-3.509C2.35 18.44 1.5 15.586 1.472 12.01v-.017c.03-3.579.879-6.43 2.525-8.482C5.845 1.205 8.6.024 12.18 0h.014c2.746.02 5.043.725 6.826 2.098 1.677 1.29 2.858 3.13 3.509 5.467l-2.04.569c-1.104-3.96-3.898-5.984-8.304-6.015-2.91.022-5.11.936-6.54 2.717C4.307 6.504 3.616 8.914 3.589 12c.027 3.086.718 5.496 2.057 7.164 1.43 1.781 3.631 2.695 6.54 2.717 1.623-.02 3.094-.37 4.37-1.04.558-.29 1.029-.63 1.414-1.017.33-.33.602-.69.82-1.084.218-.395.381-.84.488-1.336.107-.495.16-1.044.16-1.644 0-.595-.053-1.144-.16-1.644-.107-.496-.27-.941-.488-1.336a3.79 3.79 0 00-.82-1.084 3.790 3.790 0 00-1.414-1.017c-1.276-.67-2.747-1.02-4.37-1.04h-.007c-1.623.02-3.094.37-4.37 1.04-.558.29-1.029.63-1.414 1.017-.33.33-.602.69-.82 1.084-.218.395-.381.84-.488 1.336-.107.5-.16 1.049-.16 1.644 0 .595.053 1.144.16 1.644.107.496.27.941.488 1.336.218.395.49.754.82 1.084.385.387.856.727 1.414 1.017 1.276.67 2.747 1.02 4.37 1.04z"/>
|
||||
</svg>
|
||||
Share on Threads
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cart Floating Button -->
|
||||
<div id="cartFloatingBtn" class="cart-floating-btn" onclick="openCart()" style="display: none;">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M7 18c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-0.16 0.27-0.25 0.58-0.25 0.96 0 1.1 0.9 2 2 2h12v-2H7.42c-0.14 0-0.25-0.11-0.25-0.25l0.03-0.12L8.1 13h7.45c0.75 0 1.41-0.41 1.75-1.03L21.7 4H5.21l-0.94-2H1zM17 18c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2z"/>
|
||||
</svg>
|
||||
<span class="cart-badge" id="cartBadge">0</span>
|
||||
</div>
|
||||
|
||||
<link rel="stylesheet" href="css/workflows-modal.css">
|
||||
<link rel="stylesheet" href="css/stack-page.css">
|
||||
<script src="js/utils.js"></script>
|
||||
<script src="js/data-loader.js"></script>
|
||||
<script src="js/stack-router.js"></script>
|
||||
<script src="js/carousel.js"></script>
|
||||
<script src="js/cart-manager.js"></script>
|
||||
<script src="js/index-events.js"></script>
|
||||
<script src="js/modal-helpers.js"></script>
|
||||
<script src="js/generate-search-data.js"></script>
|
||||
<script src="js/search-functionality.js"></script>
|
||||
<script src="js/event-tracker.js"></script>
|
||||
<script>
|
||||
window.si = window.si || function () { (window.siq = window.siq || []).push(arguments); };
|
||||
</script>
|
||||
<script defer src="/_vercel/speed-insights/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,292 @@
|
||||
// Company Stacks Carousel functionality
|
||||
class CompanyCarousel {
|
||||
constructor() {
|
||||
this.carousel = document.getElementById('companyCarousel');
|
||||
this.leftBtn = document.querySelector('.carousel-btn-left');
|
||||
this.rightBtn = document.querySelector('.carousel-btn-right');
|
||||
this.scrollAmount = 280; // Width of one card plus gap
|
||||
this.autoScrollInterval = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
if (!this.carousel) return;
|
||||
|
||||
this.updateButtons();
|
||||
this.setupEventListeners();
|
||||
this.startAutoScroll();
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
// Scroll event to update button states
|
||||
this.carousel.addEventListener('scroll', () => {
|
||||
this.updateButtons();
|
||||
});
|
||||
|
||||
// Stop auto-scroll on hover
|
||||
this.carousel.addEventListener('mouseenter', () => {
|
||||
this.stopAutoScroll();
|
||||
});
|
||||
|
||||
// Resume auto-scroll when mouse leaves
|
||||
this.carousel.addEventListener('mouseleave', () => {
|
||||
this.startAutoScroll();
|
||||
});
|
||||
|
||||
// Touch/swipe support for mobile
|
||||
let startX = 0;
|
||||
let scrollLeft = 0;
|
||||
let isDown = false;
|
||||
|
||||
this.carousel.addEventListener('mousedown', (e) => {
|
||||
isDown = true;
|
||||
startX = e.pageX - this.carousel.offsetLeft;
|
||||
scrollLeft = this.carousel.scrollLeft;
|
||||
this.carousel.style.cursor = 'grabbing';
|
||||
this.stopAutoScroll();
|
||||
});
|
||||
|
||||
this.carousel.addEventListener('mouseleave', () => {
|
||||
isDown = false;
|
||||
this.carousel.style.cursor = 'grab';
|
||||
});
|
||||
|
||||
this.carousel.addEventListener('mouseup', () => {
|
||||
isDown = false;
|
||||
this.carousel.style.cursor = 'grab';
|
||||
this.startAutoScroll();
|
||||
});
|
||||
|
||||
this.carousel.addEventListener('mousemove', (e) => {
|
||||
if (!isDown) return;
|
||||
e.preventDefault();
|
||||
const x = e.pageX - this.carousel.offsetLeft;
|
||||
const walk = (x - startX) * 2;
|
||||
this.carousel.scrollLeft = scrollLeft - walk;
|
||||
});
|
||||
|
||||
// Touch events for mobile
|
||||
this.carousel.addEventListener('touchstart', (e) => {
|
||||
startX = e.touches[0].pageX - this.carousel.offsetLeft;
|
||||
scrollLeft = this.carousel.scrollLeft;
|
||||
this.stopAutoScroll();
|
||||
});
|
||||
|
||||
this.carousel.addEventListener('touchmove', (e) => {
|
||||
if (!startX) return;
|
||||
const x = e.touches[0].pageX - this.carousel.offsetLeft;
|
||||
const walk = (x - startX) * 2;
|
||||
this.carousel.scrollLeft = scrollLeft - walk;
|
||||
});
|
||||
|
||||
this.carousel.addEventListener('touchend', () => {
|
||||
startX = 0;
|
||||
this.startAutoScroll();
|
||||
});
|
||||
}
|
||||
|
||||
scrollLeft() {
|
||||
this.carousel.scrollBy({
|
||||
left: -this.scrollAmount,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
this.stopAutoScroll();
|
||||
setTimeout(() => this.startAutoScroll(), 3000);
|
||||
}
|
||||
|
||||
scrollRight() {
|
||||
this.carousel.scrollBy({
|
||||
left: this.scrollAmount,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
this.stopAutoScroll();
|
||||
setTimeout(() => this.startAutoScroll(), 3000);
|
||||
}
|
||||
|
||||
updateButtons() {
|
||||
const scrollLeft = this.carousel.scrollLeft;
|
||||
const maxScroll = this.carousel.scrollWidth - this.carousel.clientWidth;
|
||||
|
||||
// Update left button
|
||||
if (this.leftBtn) {
|
||||
this.leftBtn.disabled = scrollLeft <= 0;
|
||||
this.leftBtn.style.opacity = scrollLeft <= 0 ? '0.5' : '1';
|
||||
}
|
||||
|
||||
// Update right button
|
||||
if (this.rightBtn) {
|
||||
this.rightBtn.disabled = scrollLeft >= maxScroll - 1; // -1 for rounding errors
|
||||
this.rightBtn.style.opacity = scrollLeft >= maxScroll - 1 ? '0.5' : '1';
|
||||
}
|
||||
}
|
||||
|
||||
startAutoScroll() {
|
||||
this.stopAutoScroll();
|
||||
this.autoScrollInterval = setInterval(() => {
|
||||
const maxScroll = this.carousel.scrollWidth - this.carousel.clientWidth;
|
||||
const currentScroll = this.carousel.scrollLeft;
|
||||
|
||||
if (currentScroll >= maxScroll - 1) {
|
||||
// Reset to beginning
|
||||
this.carousel.scrollTo({
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
} else {
|
||||
// Scroll to next item
|
||||
this.scrollRight();
|
||||
}
|
||||
}, 4000); // Auto-scroll every 4 seconds
|
||||
}
|
||||
|
||||
stopAutoScroll() {
|
||||
if (this.autoScrollInterval) {
|
||||
clearInterval(this.autoScrollInterval);
|
||||
this.autoScrollInterval = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Global functions for button clicks
|
||||
function scrollCarousel(direction) {
|
||||
if (window.companyCarousel) {
|
||||
if (direction === 'left') {
|
||||
window.companyCarousel.scrollLeft();
|
||||
} else {
|
||||
window.companyCarousel.scrollRight();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Featured Projects Carousel functionality
|
||||
class FeaturedCarousel {
|
||||
constructor() {
|
||||
this.carousel = document.getElementById('featuredCarousel');
|
||||
this.leftBtn = document.querySelector('.featured-section .carousel-btn-left');
|
||||
this.rightBtn = document.querySelector('.featured-section .carousel-btn-right');
|
||||
this.scrollAmount = 304; // Width of one featured card (280px) plus gap (24px)
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
if (!this.carousel) return;
|
||||
|
||||
this.updateButtons();
|
||||
this.setupEventListeners();
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
// Scroll event to update button states
|
||||
this.carousel.addEventListener('scroll', () => {
|
||||
this.updateButtons();
|
||||
});
|
||||
|
||||
// Touch/swipe support for mobile
|
||||
let startX = 0;
|
||||
let scrollLeft = 0;
|
||||
let isDown = false;
|
||||
|
||||
this.carousel.addEventListener('mousedown', (e) => {
|
||||
isDown = true;
|
||||
startX = e.pageX - this.carousel.offsetLeft;
|
||||
scrollLeft = this.carousel.scrollLeft;
|
||||
this.carousel.style.cursor = 'grabbing';
|
||||
});
|
||||
|
||||
this.carousel.addEventListener('mouseleave', () => {
|
||||
isDown = false;
|
||||
this.carousel.style.cursor = 'grab';
|
||||
});
|
||||
|
||||
this.carousel.addEventListener('mouseup', () => {
|
||||
isDown = false;
|
||||
this.carousel.style.cursor = 'grab';
|
||||
});
|
||||
|
||||
this.carousel.addEventListener('mousemove', (e) => {
|
||||
if (!isDown) return;
|
||||
e.preventDefault();
|
||||
const x = e.pageX - this.carousel.offsetLeft;
|
||||
const walk = (x - startX) * 2;
|
||||
this.carousel.scrollLeft = scrollLeft - walk;
|
||||
});
|
||||
|
||||
// Touch events for mobile
|
||||
this.carousel.addEventListener('touchstart', (e) => {
|
||||
startX = e.touches[0].pageX - this.carousel.offsetLeft;
|
||||
scrollLeft = this.carousel.scrollLeft;
|
||||
});
|
||||
|
||||
this.carousel.addEventListener('touchmove', (e) => {
|
||||
if (!startX) return;
|
||||
const x = e.touches[0].pageX - this.carousel.offsetLeft;
|
||||
const walk = (x - startX) * 2;
|
||||
this.carousel.scrollLeft = scrollLeft - walk;
|
||||
});
|
||||
|
||||
this.carousel.addEventListener('touchend', () => {
|
||||
startX = 0;
|
||||
});
|
||||
}
|
||||
|
||||
scrollLeft() {
|
||||
this.carousel.scrollBy({
|
||||
left: -this.scrollAmount,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
|
||||
scrollRight() {
|
||||
this.carousel.scrollBy({
|
||||
left: this.scrollAmount,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
|
||||
updateButtons() {
|
||||
const scrollLeft = this.carousel.scrollLeft;
|
||||
const maxScroll = this.carousel.scrollWidth - this.carousel.clientWidth;
|
||||
|
||||
// Update left button
|
||||
if (this.leftBtn) {
|
||||
this.leftBtn.disabled = scrollLeft <= 0;
|
||||
this.leftBtn.style.opacity = scrollLeft <= 0 ? '0.5' : '1';
|
||||
}
|
||||
|
||||
// Update right button
|
||||
if (this.rightBtn) {
|
||||
this.rightBtn.disabled = scrollLeft >= maxScroll - 1;
|
||||
this.rightBtn.style.opacity = scrollLeft >= maxScroll - 1 ? '0.5' : '1';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Global functions for button clicks
|
||||
function scrollFeaturedCarousel(direction) {
|
||||
if (window.featuredCarousel) {
|
||||
if (direction === 'left') {
|
||||
window.featuredCarousel.scrollLeft();
|
||||
} else {
|
||||
window.featuredCarousel.scrollRight();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize carousel when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
window.companyCarousel = new CompanyCarousel();
|
||||
window.featuredCarousel = new FeaturedCarousel();
|
||||
});
|
||||
|
||||
// Re-initialize if page content changes
|
||||
function reinitializeCarousel() {
|
||||
if (window.companyCarousel) {
|
||||
window.companyCarousel.stopAutoScroll();
|
||||
}
|
||||
window.companyCarousel = new CompanyCarousel();
|
||||
}
|
||||
|
||||
// Export for module usage
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = { CompanyCarousel, scrollCarousel, reinitializeCarousel };
|
||||
}
|
||||
@@ -0,0 +1,692 @@
|
||||
// Shopping Cart Manager for Claude Code Templates
|
||||
class CartManager {
|
||||
constructor() {
|
||||
this.cart = {
|
||||
agents: [],
|
||||
commands: [],
|
||||
settings: [],
|
||||
hooks: [],
|
||||
mcps: [],
|
||||
skills: [],
|
||||
templates: [] // Add templates support
|
||||
};
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
// Load cart from localStorage if exists
|
||||
this.loadCartFromStorage();
|
||||
// Clean corrupted data
|
||||
this.cleanCorruptedData();
|
||||
this.updateCartUI();
|
||||
this.updateFloatingButton();
|
||||
|
||||
// Use requestAnimationFrame instead of setTimeout for better performance
|
||||
this.scheduleButtonUpdate();
|
||||
|
||||
// Listen for filter changes to update buttons
|
||||
this.setupFilterListeners();
|
||||
}
|
||||
|
||||
// Optimized button update scheduling
|
||||
scheduleButtonUpdate() {
|
||||
if (this.updatePending) return;
|
||||
|
||||
this.updatePending = true;
|
||||
requestAnimationFrame(() => {
|
||||
this.updateAddToCartButtons();
|
||||
this.updatePending = false;
|
||||
});
|
||||
}
|
||||
|
||||
// Add item to cart
|
||||
addToCart(item, type) {
|
||||
// Ensure the cart type exists
|
||||
if (!this.cart[type]) {
|
||||
console.warn(`Cart type '${type}' not found. Available types:`, Object.keys(this.cart));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if item already exists
|
||||
const existingItem = this.cart[type].find(cartItem => cartItem.path === item.path);
|
||||
if (existingItem) {
|
||||
this.showNotification(`${item.name} is already in your stack!`, 'warning');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add item to cart
|
||||
this.cart[type].push({
|
||||
name: item.name,
|
||||
path: item.path,
|
||||
category: item.category || '',
|
||||
description: item.description || '',
|
||||
icon: this.getTypeIcon(type)
|
||||
});
|
||||
|
||||
this.saveCartToStorage();
|
||||
this.updateCartUI();
|
||||
this.updateFloatingButton();
|
||||
this.showNotification(`${item.name} added to stack!`, 'success');
|
||||
|
||||
// Track cart add event
|
||||
window.eventTracker?.track('cart_add', {
|
||||
component_type: type,
|
||||
component_name: item.name,
|
||||
cart_size: this.getTotalItems()
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove item from cart
|
||||
removeFromCart(itemPath, type) {
|
||||
// Ensure the cart type exists
|
||||
if (!this.cart[type]) {
|
||||
console.warn(`Cart type '${type}' not found. Available types:`, Object.keys(this.cart));
|
||||
return;
|
||||
}
|
||||
|
||||
const removedItem = this.cart[type].find(item => item.path === itemPath);
|
||||
this.cart[type] = this.cart[type].filter(item => item.path !== itemPath);
|
||||
this.saveCartToStorage();
|
||||
this.updateCartUI();
|
||||
this.updateFloatingButton();
|
||||
this.showNotification('Item removed from stack', 'info');
|
||||
|
||||
// Track cart remove event
|
||||
window.eventTracker?.track('cart_remove', {
|
||||
component_type: type,
|
||||
component_name: removedItem?.name || itemPath,
|
||||
cart_size: this.getTotalItems()
|
||||
});
|
||||
}
|
||||
|
||||
// Clear entire cart
|
||||
clearCart() {
|
||||
// Initialize cart structure to prevent errors
|
||||
this.initializeCartStructure();
|
||||
|
||||
if (this.getTotalItems() === 0) return;
|
||||
|
||||
if (confirm('Are you sure you want to clear your entire stack?')) {
|
||||
this.cart = { agents: [], commands: [], settings: [], hooks: [], mcps: [], skills: [], templates: [] };
|
||||
this.saveCartToStorage();
|
||||
this.updateCartUI();
|
||||
this.updateFloatingButton();
|
||||
this.showNotification('Stack cleared', 'info');
|
||||
}
|
||||
}
|
||||
|
||||
// Get total items count
|
||||
getTotalItems() {
|
||||
this.initializeCartStructure();
|
||||
return (this.cart.agents?.length || 0) +
|
||||
(this.cart.commands?.length || 0) +
|
||||
(this.cart.settings?.length || 0) +
|
||||
(this.cart.hooks?.length || 0) +
|
||||
(this.cart.mcps?.length || 0) +
|
||||
(this.cart.skills?.length || 0) +
|
||||
(this.cart.templates?.length || 0);
|
||||
}
|
||||
|
||||
// Ensure cart has all required arrays
|
||||
initializeCartStructure() {
|
||||
if (!this.cart.agents) this.cart.agents = [];
|
||||
if (!this.cart.commands) this.cart.commands = [];
|
||||
if (!this.cart.settings) this.cart.settings = [];
|
||||
if (!this.cart.hooks) this.cart.hooks = [];
|
||||
if (!this.cart.mcps) this.cart.mcps = [];
|
||||
if (!this.cart.skills) this.cart.skills = [];
|
||||
if (!this.cart.templates) this.cart.templates = [];
|
||||
}
|
||||
|
||||
// Clean corrupted data from cart
|
||||
cleanCorruptedData() {
|
||||
let cleaned = false;
|
||||
Object.keys(this.cart).forEach(type => {
|
||||
if (Array.isArray(this.cart[type])) {
|
||||
const originalLength = this.cart[type].length;
|
||||
this.cart[type] = this.cart[type].filter(item =>
|
||||
item && typeof item === 'object' && item.name && item.path
|
||||
);
|
||||
if (this.cart[type].length !== originalLength) {
|
||||
cleaned = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (cleaned) {
|
||||
this.saveCartToStorage();
|
||||
}
|
||||
}
|
||||
|
||||
// Clean path by removing .md and .json extensions
|
||||
getCleanPath(path) {
|
||||
if (!path) return path;
|
||||
|
||||
// Remove .md extension if present
|
||||
if (path.endsWith('.md')) {
|
||||
return path.slice(0, -3);
|
||||
}
|
||||
|
||||
// Remove .json extension if present
|
||||
if (path.endsWith('.json')) {
|
||||
return path.slice(0, -5);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
// Clean component name by removing extensions and formatting
|
||||
getCleanComponentName(name) {
|
||||
if (!name) {
|
||||
return 'Unknown Component';
|
||||
}
|
||||
|
||||
let cleanName = name;
|
||||
|
||||
// Remove .md extension if present
|
||||
if (cleanName.endsWith('.md')) {
|
||||
cleanName = cleanName.slice(0, -3);
|
||||
}
|
||||
|
||||
// Remove .json extension if present
|
||||
if (cleanName.endsWith('.json')) {
|
||||
cleanName = cleanName.slice(0, -5);
|
||||
}
|
||||
|
||||
// Convert kebab-case or snake_case to Title Case
|
||||
cleanName = cleanName
|
||||
.replace(/[-_]/g, ' ')
|
||||
.split(' ')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
||||
.join(' ');
|
||||
|
||||
return cleanName;
|
||||
}
|
||||
|
||||
// Check if item is in cart
|
||||
isInCart(itemPath, type) {
|
||||
// Ensure the cart type exists
|
||||
if (!this.cart[type]) {
|
||||
console.warn(`Cart type '${type}' not found. Available types:`, Object.keys(this.cart));
|
||||
return false;
|
||||
}
|
||||
return this.cart[type].some(item => item.path === itemPath);
|
||||
}
|
||||
|
||||
// Update cart UI
|
||||
updateCartUI() {
|
||||
const cartEmpty = document.getElementById('cartEmpty');
|
||||
const cartItems = document.getElementById('cartItems');
|
||||
const cartFooter = document.getElementById('cartFooter');
|
||||
const cartClearProminent = document.getElementById('cartClearProminent');
|
||||
const clearCount = document.getElementById('clearCount');
|
||||
|
||||
// Early return if essential elements don't exist (e.g., on component page)
|
||||
if (!cartEmpty || !cartItems || !cartFooter) {
|
||||
return;
|
||||
}
|
||||
|
||||
const totalItems = this.getTotalItems();
|
||||
|
||||
if (totalItems === 0) {
|
||||
cartEmpty.style.display = 'block';
|
||||
cartItems.style.display = 'none';
|
||||
cartFooter.style.display = 'none';
|
||||
// Hide prominent clear button when cart is empty
|
||||
if (cartClearProminent) cartClearProminent.style.display = 'none';
|
||||
} else {
|
||||
cartEmpty.style.display = 'none';
|
||||
cartItems.style.display = 'block';
|
||||
cartFooter.style.display = 'block';
|
||||
|
||||
// Show and update prominent clear button
|
||||
if (cartClearProminent) {
|
||||
cartClearProminent.style.display = 'block';
|
||||
if (clearCount) {
|
||||
clearCount.textContent = `(${totalItems})`;
|
||||
}
|
||||
}
|
||||
|
||||
this.renderCartItems();
|
||||
this.updateCommand();
|
||||
}
|
||||
}
|
||||
|
||||
// Render cart items
|
||||
renderCartItems() {
|
||||
// Update counts - only if elements exist
|
||||
const agentsCount = document.getElementById('agentsCount');
|
||||
const commandsCount = document.getElementById('commandsCount');
|
||||
const settingsCount = document.getElementById('settingsCount');
|
||||
const hooksCount = document.getElementById('hooksCount');
|
||||
const mcpsCount = document.getElementById('mcpsCount');
|
||||
const skillsCount = document.getElementById('skillsCount');
|
||||
|
||||
if (agentsCount) agentsCount.textContent = this.cart.agents.length;
|
||||
if (commandsCount) commandsCount.textContent = this.cart.commands.length;
|
||||
if (settingsCount) settingsCount.textContent = this.cart.settings.length;
|
||||
if (hooksCount) hooksCount.textContent = this.cart.hooks.length;
|
||||
if (mcpsCount) mcpsCount.textContent = this.cart.mcps.length;
|
||||
if (skillsCount) skillsCount.textContent = this.cart.skills.length;
|
||||
|
||||
// Render sections
|
||||
this.renderSection('agents', 'agentsList');
|
||||
this.renderSection('commands', 'commandsList');
|
||||
this.renderSection('settings', 'settingsList');
|
||||
this.renderSection('hooks', 'hooksList');
|
||||
this.renderSection('mcps', 'mcpsList');
|
||||
this.renderSection('skills', 'skillsList');
|
||||
}
|
||||
|
||||
// Render a specific section
|
||||
renderSection(type, containerId) {
|
||||
const container = document.getElementById(containerId);
|
||||
|
||||
// Skip if container doesn't exist (e.g., on component page)
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
const items = this.cart[type];
|
||||
|
||||
if (items.length === 0) {
|
||||
container.innerHTML = '<div class="no-items">No items added</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = items.map(item => `
|
||||
<div class="cart-item">
|
||||
<div class="cart-item-icon">${item.icon}</div>
|
||||
<div class="cart-item-info">
|
||||
<div class="cart-item-name">${this.getCleanComponentName(item.name)}</div>
|
||||
<div class="cart-item-path">${this.getCleanPath(item.path)}</div>
|
||||
</div>
|
||||
<button class="cart-item-remove" onclick="cartManager.removeFromCart('${item.path}', '${type}')" title="Remove from stack">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// Update generated command
|
||||
updateCommand() {
|
||||
let command = 'npx claude-code-templates@latest';
|
||||
|
||||
if (this.cart.agents.length > 0) {
|
||||
const agentPaths = this.cart.agents.map(item => this.getCleanPath(item.path)).join(',');
|
||||
command += ` --agent ${agentPaths}`;
|
||||
}
|
||||
|
||||
if (this.cart.commands.length > 0) {
|
||||
const commandPaths = this.cart.commands.map(item => this.getCleanPath(item.path)).join(',');
|
||||
command += ` --command "${commandPaths}"`;
|
||||
}
|
||||
|
||||
if (this.cart.settings.length > 0) {
|
||||
const settingsPaths = this.cart.settings.map(item => this.getCleanPath(item.path)).join(',');
|
||||
command += ` --setting "${settingsPaths}"`;
|
||||
}
|
||||
|
||||
if (this.cart.hooks.length > 0) {
|
||||
const hooksPaths = this.cart.hooks.map(item => this.getCleanPath(item.path)).join(',');
|
||||
command += ` --hook "${hooksPaths}"`;
|
||||
}
|
||||
|
||||
if (this.cart.mcps.length > 0) {
|
||||
const mcpPaths = this.cart.mcps.map(item => this.getCleanPath(item.path)).join(',');
|
||||
command += ` --mcp "${mcpPaths}"`;
|
||||
}
|
||||
|
||||
if (this.cart.skills.length > 0) {
|
||||
const skillPaths = this.cart.skills.map(item => this.getCleanPath(item.path)).join(',');
|
||||
command += ` --skill "${skillPaths}"`;
|
||||
}
|
||||
|
||||
const generatedCommand = document.getElementById('generatedCommand');
|
||||
if (generatedCommand) {
|
||||
generatedCommand.textContent = command;
|
||||
}
|
||||
}
|
||||
|
||||
// Update floating button
|
||||
updateFloatingButton() {
|
||||
const floatingBtn = document.getElementById('cartFloatingBtn');
|
||||
const badge = document.getElementById('cartBadge');
|
||||
|
||||
// Skip if floating button doesn't exist (e.g., on component page)
|
||||
if (!floatingBtn || !badge) {
|
||||
return;
|
||||
}
|
||||
|
||||
const totalItems = this.getTotalItems();
|
||||
|
||||
if (totalItems > 0) {
|
||||
floatingBtn.style.display = 'flex';
|
||||
badge.textContent = totalItems;
|
||||
} else {
|
||||
floatingBtn.style.display = 'none';
|
||||
}
|
||||
|
||||
// Update all add-to-cart buttons in the page
|
||||
this.updateAddToCartButtons();
|
||||
}
|
||||
|
||||
// Update add-to-cart buttons state
|
||||
updateAddToCartButtons() {
|
||||
document.querySelectorAll('.add-to-cart-btn').forEach(btn => {
|
||||
const type = btn.dataset.type;
|
||||
const path = btn.dataset.path;
|
||||
|
||||
if (this.isInCart(path, type)) {
|
||||
btn.classList.add('added');
|
||||
btn.innerHTML = `
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M9,20.42L2.79,14.21L5.62,11.38L9,14.77L18.88,4.88L21.71,7.71L9,20.42Z"/>
|
||||
</svg>
|
||||
Added to Stack
|
||||
`;
|
||||
} else {
|
||||
btn.classList.remove('added');
|
||||
btn.innerHTML = `
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M19,7H18V6A2,2 0 0,0 16,4H8A2,2 0 0,0 6,6V7H5A1,1 0 0,0 4,8A1,1 0 0,0 5,9H6V19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V9H19A1,1 0 0,0 20,8A1,1 0 0,0 19,7M8,6H16V7H8V6M16,19H8V9H16V19Z"/>
|
||||
</svg>
|
||||
Add to Stack
|
||||
`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Get type icon
|
||||
getTypeIcon(type) {
|
||||
const icons = {
|
||||
agents: '🤖',
|
||||
commands: '⚡',
|
||||
settings: '⚙️',
|
||||
hooks: '🪝',
|
||||
mcps: '🔌',
|
||||
skills: '🎨',
|
||||
templates: '📦'
|
||||
};
|
||||
return icons[type] || '📦';
|
||||
}
|
||||
|
||||
// Save cart to localStorage
|
||||
saveCartToStorage() {
|
||||
localStorage.setItem('claudeCodeCart', JSON.stringify(this.cart));
|
||||
}
|
||||
|
||||
// Load cart from localStorage
|
||||
loadCartFromStorage() {
|
||||
const saved = localStorage.getItem('claudeCodeCart');
|
||||
if (saved) {
|
||||
try {
|
||||
this.cart = JSON.parse(saved);
|
||||
// Ensure all arrays exist using centralized method
|
||||
this.initializeCartStructure();
|
||||
if (!this.cart.templates) this.cart.templates = [];
|
||||
if (!this.cart.skills) this.cart.skills = [];
|
||||
} catch (e) {
|
||||
console.warn('Failed to load cart from storage:', e);
|
||||
this.cart = { agents: [], commands: [], settings: [], hooks: [], mcps: [], skills: [], templates: [] };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Optimized filter listeners with debouncing
|
||||
setupFilterListeners() {
|
||||
// Debounced update function
|
||||
let updateTimeout;
|
||||
const debouncedUpdate = () => {
|
||||
clearTimeout(updateTimeout);
|
||||
updateTimeout = setTimeout(() => this.scheduleButtonUpdate(), 150);
|
||||
};
|
||||
|
||||
// Listen for filter button clicks (more efficient than MutationObserver)
|
||||
document.addEventListener('click', (e) => {
|
||||
if (e.target.classList.contains('filter-btn') ||
|
||||
e.target.closest('.filter-btn')) {
|
||||
debouncedUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
// Lightweight MutationObserver for critical changes only
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
let shouldUpdate = false;
|
||||
|
||||
for (const mutation of mutations) {
|
||||
// Only update for significant DOM changes
|
||||
if (mutation.type === 'childList' &&
|
||||
mutation.addedNodes.length > 0 &&
|
||||
Array.from(mutation.addedNodes).some(node =>
|
||||
node.nodeType === 1 &&
|
||||
(node.classList?.contains('unified-card') ||
|
||||
node.querySelector?.('.unified-card'))
|
||||
)) {
|
||||
shouldUpdate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldUpdate) {
|
||||
debouncedUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
// Observe only the unified grid with specific options
|
||||
const unifiedGrid = document.getElementById('unifiedGrid');
|
||||
if (unifiedGrid) {
|
||||
observer.observe(unifiedGrid, {
|
||||
childList: true,
|
||||
subtree: false // Only direct children, not deep nesting
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Show notification
|
||||
showNotification(message, type = 'info') {
|
||||
// Create notification element
|
||||
const notification = document.createElement('div');
|
||||
notification.className = `cart-notification cart-notification-${type}`;
|
||||
notification.innerHTML = `
|
||||
<div class="notification-content">
|
||||
<span class="notification-icon">${this.getNotificationIcon(type)}</span>
|
||||
<span class="notification-message">${message}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Add to page
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// Show with animation using requestAnimationFrame
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => notification.classList.add('show'));
|
||||
});
|
||||
|
||||
// Remove after 3 seconds
|
||||
setTimeout(() => {
|
||||
notification.classList.remove('show');
|
||||
setTimeout(() => notification.remove(), 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Get notification icon
|
||||
getNotificationIcon(type) {
|
||||
const icons = {
|
||||
success: '✅',
|
||||
warning: '⚠️',
|
||||
error: '❌',
|
||||
info: 'ℹ️'
|
||||
};
|
||||
return icons[type] || 'ℹ️';
|
||||
}
|
||||
}
|
||||
|
||||
// Global cart manager instance
|
||||
const cartManager = new CartManager();
|
||||
|
||||
// Global functions for cart operations
|
||||
function openCart() {
|
||||
const sidebar = document.getElementById('shoppingCart');
|
||||
const floatingButton = document.getElementById('cartFloatingBtn');
|
||||
|
||||
sidebar.classList.add('active');
|
||||
|
||||
// Hide floating cart button when sidebar is open
|
||||
if (floatingButton) {
|
||||
floatingButton.style.transform = 'translateX(100px)';
|
||||
floatingButton.style.opacity = '0';
|
||||
floatingButton.style.pointerEvents = 'none';
|
||||
}
|
||||
|
||||
// Create subtle overlay that doesn't block content visibility
|
||||
if (window.innerWidth > 768) {
|
||||
const overlay = document.createElement('div');
|
||||
overlay.id = 'cartOverlay';
|
||||
overlay.style.cssText = `
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.15);
|
||||
z-index: 999;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
pointer-events: auto;
|
||||
`;
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
// Fade in overlay
|
||||
setTimeout(() => overlay.style.opacity = '1', 10);
|
||||
|
||||
// Close on overlay click
|
||||
overlay.addEventListener('click', closeCart);
|
||||
}
|
||||
}
|
||||
|
||||
function closeCart() {
|
||||
const sidebar = document.getElementById('shoppingCart');
|
||||
const floatingButton = document.getElementById('cartFloatingBtn');
|
||||
|
||||
sidebar.classList.remove('active');
|
||||
|
||||
// Show floating cart button again when sidebar is closed
|
||||
if (floatingButton) {
|
||||
floatingButton.style.transform = 'translateX(0)';
|
||||
floatingButton.style.opacity = '1';
|
||||
floatingButton.style.pointerEvents = 'auto';
|
||||
}
|
||||
|
||||
// Remove dynamic overlay
|
||||
const overlay = document.getElementById('cartOverlay');
|
||||
if (overlay) {
|
||||
overlay.style.opacity = '0';
|
||||
setTimeout(() => overlay.remove(), 300);
|
||||
}
|
||||
}
|
||||
|
||||
function clearCart() {
|
||||
cartManager.clearCart();
|
||||
}
|
||||
|
||||
function copyCartCommand() {
|
||||
const command = document.getElementById('generatedCommand').textContent;
|
||||
copyToClipboard(command, 'Command copied to clipboard!');
|
||||
|
||||
// Track cart checkout (copy command)
|
||||
const cart = cartManager.cart;
|
||||
const types = Object.keys(cart).filter(k => cart[k].length > 0);
|
||||
const items = types.reduce((sum, k) => sum + cart[k].length, 0);
|
||||
window.eventTracker?.track('cart_checkout', { items, types });
|
||||
}
|
||||
|
||||
function downloadStack() {
|
||||
const command = document.getElementById('generatedCommand').textContent;
|
||||
|
||||
// Ask user if they want to execute the command
|
||||
const shouldExecute = confirm(`Ready to download your stack?\n\nThis will run:\n${command}\n\nDo you want to proceed?`);
|
||||
|
||||
if (shouldExecute) {
|
||||
// Copy command to clipboard for user convenience
|
||||
copyToClipboard(command);
|
||||
|
||||
// Show instructions
|
||||
cartManager.showNotification('Command copied! Paste it in your terminal to download the stack.', 'success');
|
||||
|
||||
// Optionally clear the cart after download
|
||||
setTimeout(() => {
|
||||
if (confirm('Stack command is ready! Would you like to clear your cart?')) {
|
||||
cartManager.clearCart();
|
||||
closeCart();
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
function addToCart(item, type) {
|
||||
return cartManager.addToCart(item, type);
|
||||
}
|
||||
|
||||
// Close cart when clicking on overlay (handled by dynamic overlay now)
|
||||
/* document.addEventListener('click', (e) => {
|
||||
const sidebar = document.getElementById('shoppingCart');
|
||||
if (e.target === sidebar && sidebar.classList.contains('active')) {
|
||||
closeCart();
|
||||
}
|
||||
}); */
|
||||
|
||||
// Close cart with escape key
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
closeCart();
|
||||
}
|
||||
});
|
||||
|
||||
// Share functionality - Dropdown style
|
||||
function toggleShareDropdown() {
|
||||
const shareDropdown = document.getElementById('shareDropdown');
|
||||
shareDropdown.classList.toggle('open');
|
||||
}
|
||||
|
||||
function shareOnTwitter() {
|
||||
const command = document.getElementById('generatedCommand').textContent.trim();
|
||||
const message = `🚀 Just created this Claude Code Templates stack!
|
||||
|
||||
Just run this command:
|
||||
${command}
|
||||
|
||||
Create yours at https://aitmpl.com`;
|
||||
const twitterUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(message)}`;
|
||||
window.open(twitterUrl, '_blank');
|
||||
|
||||
// Close dropdown after sharing
|
||||
document.getElementById('shareDropdown').classList.remove('open');
|
||||
}
|
||||
|
||||
function shareOnThreads() {
|
||||
const command = document.getElementById('generatedCommand').textContent.trim();
|
||||
const message = `🚀 Just created this Claude Code Templates stack!
|
||||
|
||||
Just run this command:
|
||||
${command}
|
||||
|
||||
Create yours at https://aitmpl.com`;
|
||||
const threadsUrl = `https://threads.net/intent/post?text=${encodeURIComponent(message)}`;
|
||||
window.open(threadsUrl, '_blank');
|
||||
|
||||
// Close dropdown after sharing
|
||||
document.getElementById('shareDropdown').classList.remove('open');
|
||||
}
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
document.addEventListener('click', (e) => {
|
||||
const shareDropdown = document.getElementById('shareDropdown');
|
||||
if (shareDropdown && !shareDropdown.contains(e.target)) {
|
||||
shareDropdown.classList.remove('open');
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,633 @@
|
||||
// Data Loader - Handles loading component data from various sources
|
||||
class DataLoader {
|
||||
constructor() {
|
||||
this.componentsData = null;
|
||||
this.fullComponentsData = null; // Store full data for accurate counts
|
||||
this.templatesData = null; // Deprecated - templates now in components.json
|
||||
this.metadataData = null; // External metadata for tags, companies, technologies
|
||||
this.loadingStates = {
|
||||
components: false
|
||||
};
|
||||
this.cache = new Map();
|
||||
this.TIMEOUT_MS = 8000; // 8 seconds timeout
|
||||
this.ITEMS_PER_PAGE = 50; // Lazy loading batch size
|
||||
}
|
||||
|
||||
// Get data file paths (always absolute for production)
|
||||
getDataPath(filename) {
|
||||
return '/' + filename;
|
||||
}
|
||||
|
||||
// Load all components at once (simplified approach)
|
||||
async loadAllComponents() {
|
||||
try {
|
||||
this.loadingStates.components = true;
|
||||
this.showLoadingState('components', true);
|
||||
|
||||
const cacheKey = 'all_components';
|
||||
if (this.cache.has(cacheKey)) {
|
||||
this.showLoadingState('components', false);
|
||||
this.loadingStates.components = false;
|
||||
return this.cache.get(cacheKey);
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), this.TIMEOUT_MS);
|
||||
|
||||
const response = await fetch(this.getDataPath('components.json'), {
|
||||
signal: controller.signal,
|
||||
headers: {
|
||||
'Cache-Control': 'max-age=300' // 5 minutes cache
|
||||
}
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const allData = await response.json();
|
||||
|
||||
// Store both full and component data
|
||||
this.fullComponentsData = allData;
|
||||
this.componentsData = allData;
|
||||
|
||||
// Load metadata
|
||||
await this.loadMetadata();
|
||||
|
||||
this.cache.set(cacheKey, allData);
|
||||
|
||||
this.showLoadingState('components', false);
|
||||
this.loadingStates.components = false;
|
||||
|
||||
return this.componentsData;
|
||||
} catch (error) {
|
||||
this.showLoadingState('components', false);
|
||||
this.loadingStates.components = false;
|
||||
|
||||
if (error.name === 'AbortError') {
|
||||
console.error('Components loading timed out after', this.TIMEOUT_MS + 'ms');
|
||||
this.showError('Loading timed out. Using fallback data.');
|
||||
} else {
|
||||
console.error('Error loading components:', error);
|
||||
this.showError('Failed to load components. Using fallback data.');
|
||||
}
|
||||
|
||||
return this.getFallbackComponentData();
|
||||
}
|
||||
}
|
||||
|
||||
// Load components with lazy loading and timeout (kept for backward compatibility)
|
||||
async loadComponents(page = 1, itemsPerPage = this.ITEMS_PER_PAGE) {
|
||||
try {
|
||||
this.loadingStates.components = true;
|
||||
this.showLoadingState('components', true);
|
||||
|
||||
const cacheKey = `components_${page}_${itemsPerPage}`;
|
||||
if (this.cache.has(cacheKey)) {
|
||||
this.showLoadingState('components', false);
|
||||
this.loadingStates.components = false;
|
||||
return this.cache.get(cacheKey);
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), this.TIMEOUT_MS);
|
||||
|
||||
const response = await fetch(this.getDataPath('components.json'), {
|
||||
signal: controller.signal,
|
||||
headers: {
|
||||
'Cache-Control': 'max-age=300' // 5 minutes cache
|
||||
}
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const fullData = await response.json();
|
||||
|
||||
// Store full data for accurate counts (only on first load)
|
||||
if (page === 1) {
|
||||
this.fullComponentsData = fullData;
|
||||
}
|
||||
|
||||
// Apply pagination to reduce memory usage
|
||||
const paginatedData = this.paginateComponents(fullData, page, itemsPerPage);
|
||||
|
||||
// Store full data for templates if it exists
|
||||
if (fullData.templates && page === 1) {
|
||||
paginatedData.templates = fullData.templates; // Templates don't need pagination
|
||||
}
|
||||
|
||||
this.cache.set(cacheKey, paginatedData);
|
||||
this.componentsData = paginatedData;
|
||||
|
||||
this.showLoadingState('components', false);
|
||||
this.loadingStates.components = false;
|
||||
|
||||
return this.componentsData;
|
||||
} catch (error) {
|
||||
this.showLoadingState('components', false);
|
||||
this.loadingStates.components = false;
|
||||
|
||||
if (error.name === 'AbortError') {
|
||||
console.error('Components loading timed out after', this.TIMEOUT_MS + 'ms');
|
||||
this.showError('Loading timed out. Using fallback data.');
|
||||
} else {
|
||||
console.error('Error loading components:', error);
|
||||
this.showError('Failed to load components. Using fallback data.');
|
||||
}
|
||||
|
||||
return this.getFallbackComponentData();
|
||||
}
|
||||
}
|
||||
|
||||
// Get fallback component data when components.json is unavailable
|
||||
getFallbackComponentData() {
|
||||
return {
|
||||
agents: [
|
||||
{ name: 'code-reviewer', path: 'development/code-reviewer', category: 'development', type: 'agent', content: 'AI-powered code reviewer that analyzes your code for best practices, security issues, and optimization opportunities.' },
|
||||
{ name: 'documentation-writer', path: 'development/documentation-writer', category: 'development', type: 'agent', content: 'Generates comprehensive documentation for your codebase, including API docs, README files, and inline comments.' },
|
||||
{ name: 'bug-hunter', path: 'development/bug-hunter', category: 'development', type: 'agent', content: 'Specialized in finding and fixing bugs through systematic code analysis and testing.' },
|
||||
{ name: 'security-auditor', path: 'security/security-auditor', category: 'security', type: 'agent', content: 'Performs security audits on your code to identify vulnerabilities and security best practices.' },
|
||||
{ name: 'performance-optimizer', path: 'optimization/performance-optimizer', category: 'optimization', type: 'agent', content: 'Analyzes and optimizes code performance, identifying bottlenecks and suggesting improvements.' }
|
||||
],
|
||||
commands: [
|
||||
{ name: 'git-setup', path: 'development/git-setup', category: 'development', type: 'command', content: 'Sets up Git repository with best practices, including .gitignore, hooks, and workflow configurations.' },
|
||||
{ name: 'project-init', path: 'development/project-init', category: 'development', type: 'command', content: 'Initializes new projects with proper structure, dependencies, and configuration files.' },
|
||||
{ name: 'docker-setup', path: 'devops/docker-setup', category: 'devops', type: 'command', content: 'Creates Docker configurations including Dockerfile, docker-compose, and container optimization.' },
|
||||
{ name: 'test-runner', path: 'testing/test-runner', category: 'testing', type: 'command', content: 'Sets up comprehensive testing frameworks and runs automated test suites.' },
|
||||
{ name: 'build-pipeline', path: 'devops/build-pipeline', category: 'devops', type: 'command', content: 'Configures CI/CD pipelines for automated building, testing, and deployment.' }
|
||||
],
|
||||
mcps: [
|
||||
{ name: 'database-connector', path: 'database/database-connector', category: 'database', type: 'mcp', content: 'Connects to various databases and provides query and management capabilities.' },
|
||||
{ name: 'api-client', path: 'api/api-client', category: 'api', type: 'mcp', content: 'HTTP client for interacting with REST APIs and web services.' },
|
||||
{ name: 'file-manager', path: 'system/file-manager', category: 'system', type: 'mcp', content: 'File system operations including reading, writing, and organizing files.' },
|
||||
{ name: 'redis-cache', path: 'database/redis-cache', category: 'database', type: 'mcp', content: 'Redis caching implementation for improved application performance.' },
|
||||
{ name: 'email-service', path: 'communication/email-service', category: 'communication', type: 'mcp', content: 'Email sending and management service with template support.' }
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
// Load templates - now using components.json data (templates deprecated)
|
||||
async loadTemplates() {
|
||||
try {
|
||||
// Templates are now included in components.json as 'templates' section
|
||||
// This method is kept for backward compatibility
|
||||
if (this.componentsData && this.componentsData.templates) {
|
||||
this.templatesData = { templates: this.componentsData.templates };
|
||||
return this.templatesData;
|
||||
}
|
||||
|
||||
// If components aren't loaded yet, try to load them
|
||||
if (!this.componentsData) {
|
||||
await this.loadComponents();
|
||||
if (this.componentsData && this.componentsData.templates) {
|
||||
this.templatesData = { templates: this.componentsData.templates };
|
||||
return this.templatesData;
|
||||
}
|
||||
}
|
||||
|
||||
// Return empty if no templates in components.json
|
||||
console.warn('No templates found in components.json');
|
||||
this.templatesData = {};
|
||||
return this.templatesData;
|
||||
} catch (error) {
|
||||
console.error('Error loading templates from components:', error);
|
||||
this.templatesData = {};
|
||||
return this.templatesData;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the templates.js file content to extract TEMPLATES_CONFIG
|
||||
parseTemplatesConfig(fileContent) {
|
||||
try {
|
||||
const configMatch = fileContent.match(/const TEMPLATES_CONFIG = ({[\s\S]*?});/);
|
||||
if (!configMatch) {
|
||||
throw new Error('TEMPLATES_CONFIG not found in file');
|
||||
}
|
||||
|
||||
let configString = configMatch[1];
|
||||
configString = configString.replace(/'/g, '"');
|
||||
configString = configString.replace(/(\w+):/g, '"$1":');
|
||||
configString = configString.replace(/,(\s*[}\]])/g, '$1');
|
||||
|
||||
const config = JSON.parse(configString);
|
||||
return this.transformTemplatesData(config);
|
||||
} catch (error) {
|
||||
console.error('Error parsing templates config:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Transform templates data to a unified format
|
||||
transformTemplatesData(config) {
|
||||
const unified = {};
|
||||
|
||||
Object.entries(config).forEach(([category, items]) => {
|
||||
unified[category] = Object.entries(items).map(([key, template]) => ({
|
||||
id: key,
|
||||
name: template.name,
|
||||
description: template.description || '',
|
||||
type: 'template',
|
||||
category: category,
|
||||
folderPath: template.folderPath || '',
|
||||
content: template.description || '',
|
||||
files: template.files || [],
|
||||
...template
|
||||
}));
|
||||
});
|
||||
|
||||
return unified;
|
||||
}
|
||||
|
||||
// Get all components as a flat array
|
||||
getAllComponents() {
|
||||
if (!this.componentsData) return [];
|
||||
|
||||
const allComponents = [];
|
||||
['agents', 'commands', 'mcps', 'skills'].forEach(type => {
|
||||
if (this.componentsData[type]) {
|
||||
allComponents.push(...this.componentsData[type]);
|
||||
}
|
||||
});
|
||||
|
||||
return allComponents;
|
||||
}
|
||||
|
||||
// Find component by name and type
|
||||
findComponent(name, type) {
|
||||
if (!this.componentsData) return null;
|
||||
|
||||
const typeKey = type + 's';
|
||||
if (!this.componentsData[typeKey]) return null;
|
||||
|
||||
return this.componentsData[typeKey].find(component => component.name === name);
|
||||
}
|
||||
|
||||
// Get components by type
|
||||
getComponentsByType(type) {
|
||||
if (!this.componentsData) return [];
|
||||
|
||||
const typeKey = type + 's';
|
||||
return this.componentsData[typeKey] || [];
|
||||
}
|
||||
|
||||
// Paginate components data to reduce memory usage
|
||||
paginateComponents(fullData, page, itemsPerPage) {
|
||||
const paginatedData = {
|
||||
agents: [],
|
||||
commands: [],
|
||||
mcps: [],
|
||||
skills: [],
|
||||
templates: [] // Include templates in pagination structure
|
||||
};
|
||||
|
||||
['agents', 'commands', 'mcps', 'skills'].forEach(type => {
|
||||
if (fullData[type]) {
|
||||
const startIndex = (page - 1) * itemsPerPage;
|
||||
const endIndex = startIndex + itemsPerPage;
|
||||
paginatedData[type] = fullData[type].slice(startIndex, endIndex);
|
||||
}
|
||||
});
|
||||
|
||||
// Templates don't need pagination - include all if available
|
||||
if (fullData.templates) {
|
||||
paginatedData.templates = fullData.templates;
|
||||
}
|
||||
|
||||
return paginatedData;
|
||||
}
|
||||
|
||||
// Show loading states
|
||||
showLoadingState(type, isLoading) {
|
||||
const loadingElement = document.getElementById(`${type}-loading`);
|
||||
const contentElement = document.getElementById(`${type}-content`);
|
||||
|
||||
if (loadingElement && contentElement) {
|
||||
if (isLoading) {
|
||||
loadingElement.style.display = 'flex';
|
||||
contentElement.style.opacity = '0.5';
|
||||
} else {
|
||||
loadingElement.style.display = 'none';
|
||||
contentElement.style.opacity = '1';
|
||||
}
|
||||
}
|
||||
|
||||
// Also update any loading spinners in the UI
|
||||
const spinners = document.querySelectorAll('.loading-spinner');
|
||||
spinners.forEach(spinner => {
|
||||
spinner.style.display = isLoading ? 'block' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
// Show error messages
|
||||
showError(message) {
|
||||
// Try to use existing notification system
|
||||
if (window.showNotification) {
|
||||
window.showNotification(message, 'error', 5000);
|
||||
} else {
|
||||
console.warn(message);
|
||||
// Create simple toast notification
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'error-toast';
|
||||
toast.textContent = message;
|
||||
toast.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: #f85149;
|
||||
color: white;
|
||||
padding: 12px 16px;
|
||||
border-radius: 6px;
|
||||
z-index: 1000;
|
||||
font-size: 14px;
|
||||
max-width: 300px;
|
||||
`;
|
||||
document.body.appendChild(toast);
|
||||
setTimeout(() => toast.remove(), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
// Load more components (no longer needed but kept for compatibility)
|
||||
async loadMoreComponents(page) {
|
||||
// All components are now loaded at once, so this method returns null
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the total counts from full data for accurate filter counts
|
||||
getTotalCounts() {
|
||||
if (this.fullComponentsData) {
|
||||
return {
|
||||
agents: this.fullComponentsData.agents ? this.fullComponentsData.agents.length : 0,
|
||||
commands: this.fullComponentsData.commands ? this.fullComponentsData.commands.length : 0,
|
||||
mcps: this.fullComponentsData.mcps ? this.fullComponentsData.mcps.length : 0,
|
||||
settings: this.fullComponentsData.settings ? this.fullComponentsData.settings.length : 0,
|
||||
hooks: this.fullComponentsData.hooks ? this.fullComponentsData.hooks.length : 0,
|
||||
skills: this.fullComponentsData.skills ? this.fullComponentsData.skills.length : 0,
|
||||
templates: this.fullComponentsData.templates ? this.fullComponentsData.templates.length : 0
|
||||
};
|
||||
}
|
||||
|
||||
// Fallback to current loaded data
|
||||
return {
|
||||
agents: this.componentsData?.agents?.length || 0,
|
||||
commands: this.componentsData?.commands?.length || 0,
|
||||
mcps: this.componentsData?.mcps?.length || 0,
|
||||
settings: this.componentsData?.settings?.length || 0,
|
||||
hooks: this.componentsData?.hooks?.length || 0,
|
||||
skills: this.componentsData?.skills?.length || 0,
|
||||
templates: this.componentsData?.templates?.length || 0
|
||||
};
|
||||
}
|
||||
|
||||
// Load external metadata
|
||||
async loadMetadata() {
|
||||
try {
|
||||
const cacheKey = 'metadata';
|
||||
if (this.cache.has(cacheKey)) {
|
||||
this.metadataData = this.cache.get(cacheKey);
|
||||
return this.metadataData;
|
||||
}
|
||||
|
||||
const response = await fetch(this.getDataPath('components-metadata.json'), {
|
||||
headers: {
|
||||
'Cache-Control': 'max-age=300' // 5 minutes cache
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn('Could not load metadata file, using empty metadata');
|
||||
this.metadataData = { metadata: {}, companies: {}, technologies: {} };
|
||||
return this.metadataData;
|
||||
}
|
||||
|
||||
this.metadataData = await response.json();
|
||||
this.cache.set(cacheKey, this.metadataData);
|
||||
|
||||
return this.metadataData;
|
||||
} catch (error) {
|
||||
console.warn('Error loading metadata:', error);
|
||||
this.metadataData = { metadata: {}, companies: {}, technologies: {} };
|
||||
return this.metadataData;
|
||||
}
|
||||
}
|
||||
|
||||
// Get metadata for component (external metadata takes priority over frontmatter)
|
||||
getComponentMetadata(componentName) {
|
||||
if (!this.metadataData) {
|
||||
return { tags: [], companies: [], technologies: [] };
|
||||
}
|
||||
|
||||
const metadata = this.metadataData.metadata[componentName];
|
||||
if (metadata) {
|
||||
return {
|
||||
tags: metadata.tags || [],
|
||||
companies: metadata.companies || [],
|
||||
technologies: metadata.technologies || []
|
||||
};
|
||||
}
|
||||
|
||||
// Fallback to empty metadata
|
||||
return { tags: [], companies: [], technologies: [] };
|
||||
}
|
||||
|
||||
// Extract tags from component frontmatter (kept as fallback)
|
||||
extractTagsFromFrontmatter(content) {
|
||||
if (!content) return { tags: [], companies: [], technologies: [] };
|
||||
|
||||
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
||||
if (!frontmatterMatch) return { tags: [], companies: [], technologies: [] };
|
||||
|
||||
const frontmatter = frontmatterMatch[1];
|
||||
|
||||
// Extract tags array
|
||||
const tagsMatch = frontmatter.match(/tags:\s*\[(.*?)\]/s);
|
||||
const tags = tagsMatch
|
||||
? tagsMatch[1].split(',').map(tag => tag.trim().replace(/['"]/g, ''))
|
||||
: [];
|
||||
|
||||
// Extract companies array
|
||||
const companiesMatch = frontmatter.match(/companies:\s*\[(.*?)\]/s);
|
||||
const companies = companiesMatch
|
||||
? companiesMatch[1].split(',').map(company => company.trim().replace(/['"]/g, ''))
|
||||
: [];
|
||||
|
||||
// Extract technologies array
|
||||
const technologiesMatch = frontmatter.match(/technologies:\s*\[(.*?)\]/s);
|
||||
const technologies = technologiesMatch
|
||||
? technologiesMatch[1].split(',').map(tech => tech.trim().replace(/['"]/g, ''))
|
||||
: [];
|
||||
|
||||
return { tags, companies, technologies };
|
||||
}
|
||||
|
||||
// Get all unique tags from components
|
||||
getAllTags() {
|
||||
const allTags = new Set();
|
||||
this.getAllComponents().forEach(component => {
|
||||
const { tags } = this.getComponentMetadata(component.name);
|
||||
tags.forEach(tag => allTags.add(tag));
|
||||
});
|
||||
return Array.from(allTags).sort();
|
||||
}
|
||||
|
||||
// Get all unique companies from components
|
||||
getAllCompanies() {
|
||||
const allCompanies = new Set();
|
||||
this.getAllComponents().forEach(component => {
|
||||
const { companies } = this.getComponentMetadata(component.name);
|
||||
companies.forEach(company => allCompanies.add(company));
|
||||
});
|
||||
return Array.from(allCompanies).sort();
|
||||
}
|
||||
|
||||
// Get all unique technologies from components
|
||||
getAllTechnologies() {
|
||||
const allTechnologies = new Set();
|
||||
this.getAllComponents().forEach(component => {
|
||||
const { technologies } = this.getComponentMetadata(component.name);
|
||||
technologies.forEach(tech => allTechnologies.add(tech));
|
||||
});
|
||||
return Array.from(allTechnologies).sort();
|
||||
}
|
||||
|
||||
// Get company info from metadata
|
||||
getCompanyInfo(companySlug) {
|
||||
if (!this.metadataData || !this.metadataData.companies) {
|
||||
return null;
|
||||
}
|
||||
return this.metadataData.companies[companySlug] || null;
|
||||
}
|
||||
|
||||
// Get technology info from metadata
|
||||
getTechnologyInfo(techSlug) {
|
||||
if (!this.metadataData || !this.metadataData.technologies) {
|
||||
return null;
|
||||
}
|
||||
return this.metadataData.technologies[techSlug] || null;
|
||||
}
|
||||
|
||||
// Filter components by tags, companies, or technologies
|
||||
filterComponentsByTags(filterOptions = {}) {
|
||||
const { tags = [], companies = [], technologies = [], type = null } = filterOptions;
|
||||
|
||||
let filteredComponents = this.getAllComponents();
|
||||
|
||||
// Filter by type if specified
|
||||
if (type) {
|
||||
filteredComponents = filteredComponents.filter(component => component.type === type);
|
||||
}
|
||||
|
||||
// Filter by tags, companies, or technologies
|
||||
if (tags.length > 0 || companies.length > 0 || technologies.length > 0) {
|
||||
filteredComponents = filteredComponents.filter(component => {
|
||||
const componentMeta = this.getComponentMetadata(component.name);
|
||||
|
||||
const hasMatchingTag = tags.length === 0 || tags.some(tag =>
|
||||
componentMeta.tags.includes(tag)
|
||||
);
|
||||
|
||||
const hasMatchingCompany = companies.length === 0 || companies.some(company =>
|
||||
componentMeta.companies.includes(company)
|
||||
);
|
||||
|
||||
const hasMatchingTechnology = technologies.length === 0 || technologies.some(tech =>
|
||||
componentMeta.technologies.includes(tech)
|
||||
);
|
||||
|
||||
return hasMatchingTag || hasMatchingCompany || hasMatchingTechnology;
|
||||
});
|
||||
}
|
||||
|
||||
return filteredComponents;
|
||||
}
|
||||
|
||||
// Get components for a specific company stack
|
||||
getCompanyStack(companySlug) {
|
||||
const companyComponents = this.filterComponentsByTags({ companies: [companySlug] });
|
||||
|
||||
// Group by type
|
||||
const groupedComponents = {
|
||||
agents: companyComponents.filter(c => c.type === 'agent'),
|
||||
commands: companyComponents.filter(c => c.type === 'command'),
|
||||
mcps: companyComponents.filter(c => c.type === 'mcp'),
|
||||
settings: companyComponents.filter(c => c.type === 'setting'),
|
||||
hooks: companyComponents.filter(c => c.type === 'hook'),
|
||||
templates: companyComponents.filter(c => c.type === 'template')
|
||||
};
|
||||
|
||||
return groupedComponents;
|
||||
}
|
||||
|
||||
// Get components for a specific technology stack
|
||||
getTechnologyStack(techSlug) {
|
||||
const techComponents = this.filterComponentsByTags({ technologies: [techSlug] });
|
||||
|
||||
// Group by type
|
||||
const groupedComponents = {
|
||||
agents: techComponents.filter(c => c.type === 'agent'),
|
||||
commands: techComponents.filter(c => c.type === 'command'),
|
||||
mcps: techComponents.filter(c => c.type === 'mcp'),
|
||||
settings: techComponents.filter(c => c.type === 'setting'),
|
||||
hooks: techComponents.filter(c => c.type === 'hook'),
|
||||
templates: techComponents.filter(c => c.type === 'template')
|
||||
};
|
||||
|
||||
return groupedComponents;
|
||||
}
|
||||
|
||||
// Get all settings components
|
||||
getSettings() {
|
||||
if (!this.componentsData) return [];
|
||||
return this.componentsData.settings || [];
|
||||
}
|
||||
|
||||
// Get all hooks components
|
||||
getHooks() {
|
||||
if (!this.componentsData) return [];
|
||||
return this.componentsData.hooks || [];
|
||||
}
|
||||
|
||||
// Get settings by category
|
||||
getSettingsByCategory(category) {
|
||||
const settings = this.getSettings();
|
||||
return settings.filter(setting => setting.category === category);
|
||||
}
|
||||
|
||||
// Get hooks by category
|
||||
getHooksByCategory(category) {
|
||||
const hooks = this.getHooks();
|
||||
return hooks.filter(hook => hook.category === category);
|
||||
}
|
||||
|
||||
// Get all setting categories
|
||||
getSettingCategories() {
|
||||
const settings = this.getSettings();
|
||||
const categories = new Set();
|
||||
settings.forEach(setting => {
|
||||
if (setting.category) {
|
||||
categories.add(setting.category);
|
||||
}
|
||||
});
|
||||
return Array.from(categories).sort();
|
||||
}
|
||||
|
||||
// Get all hook categories
|
||||
getHookCategories() {
|
||||
const hooks = this.getHooks();
|
||||
const categories = new Set();
|
||||
hooks.forEach(hook => {
|
||||
if (hook.category) {
|
||||
categories.add(hook.category);
|
||||
}
|
||||
});
|
||||
return Array.from(categories).sort();
|
||||
}
|
||||
}
|
||||
|
||||
// Global instance
|
||||
window.dataLoader = new DataLoader();
|
||||
@@ -0,0 +1,108 @@
|
||||
// Event Tracker for Claude Code Templates Website
|
||||
// Batches and sends website analytics events (search, cart, component views)
|
||||
|
||||
class EventTracker {
|
||||
constructor() {
|
||||
this.queue = [];
|
||||
this.flushInterval = 30000; // 30 seconds
|
||||
this.maxQueueSize = 20;
|
||||
this.endpoint = '/api/track-website-events';
|
||||
this.sessionId = this.getOrCreateSessionId();
|
||||
this.visitorId = this.getOrCreateVisitorId();
|
||||
this.screenWidth = window.screen?.width || null;
|
||||
this.referrer = document.referrer || null;
|
||||
this.timer = null;
|
||||
|
||||
this.startAutoFlush();
|
||||
this.setupBeforeUnload();
|
||||
}
|
||||
|
||||
getOrCreateSessionId() {
|
||||
let id = sessionStorage.getItem('cct_session_id');
|
||||
if (!id) {
|
||||
id = this.generateId();
|
||||
sessionStorage.setItem('cct_session_id', id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
getOrCreateVisitorId() {
|
||||
let id = localStorage.getItem('cct_visitor_id');
|
||||
if (!id) {
|
||||
id = this.generateId();
|
||||
localStorage.setItem('cct_visitor_id', id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
generateId() {
|
||||
return Math.random().toString(36).substring(2, 15) +
|
||||
Math.random().toString(36).substring(2, 15);
|
||||
}
|
||||
|
||||
track(eventType, eventData) {
|
||||
this.queue.push({
|
||||
event_type: eventType,
|
||||
event_data: eventData || {},
|
||||
page_path: window.location.pathname,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
if (this.queue.length >= this.maxQueueSize) {
|
||||
this.flush();
|
||||
}
|
||||
}
|
||||
|
||||
flush() {
|
||||
if (this.queue.length === 0) return;
|
||||
|
||||
const events = this.queue.splice(0);
|
||||
const payload = JSON.stringify({
|
||||
events: events,
|
||||
session_id: this.sessionId,
|
||||
visitor_id: this.visitorId,
|
||||
screen_width: this.screenWidth,
|
||||
referrer: this.referrer
|
||||
});
|
||||
|
||||
// Use sendBeacon for reliability (survives page unloads)
|
||||
if (navigator.sendBeacon) {
|
||||
const blob = new Blob([payload], { type: 'application/json' });
|
||||
const sent = navigator.sendBeacon(this.endpoint, blob);
|
||||
if (!sent) {
|
||||
// Fallback to fetch if sendBeacon fails
|
||||
this.sendViaFetch(payload);
|
||||
}
|
||||
} else {
|
||||
this.sendViaFetch(payload);
|
||||
}
|
||||
}
|
||||
|
||||
sendViaFetch(payload) {
|
||||
fetch(this.endpoint, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: payload,
|
||||
keepalive: true
|
||||
}).catch(() => {
|
||||
// Silent failure - analytics should never break user experience
|
||||
});
|
||||
}
|
||||
|
||||
startAutoFlush() {
|
||||
this.timer = setInterval(() => this.flush(), this.flushInterval);
|
||||
}
|
||||
|
||||
setupBeforeUnload() {
|
||||
window.addEventListener('beforeunload', () => this.flush());
|
||||
// Also flush on visibility change (mobile tab switching)
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'hidden') {
|
||||
this.flush();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize global instance
|
||||
window.eventTracker = new EventTracker();
|
||||
@@ -0,0 +1,86 @@
|
||||
// Generate search data files from components.json
|
||||
// This script reads the main components.json and creates separate JSON files for each category
|
||||
|
||||
(async function generateSearchData() {
|
||||
try {
|
||||
// Read the main components.json file
|
||||
const response = await fetch('components.json');
|
||||
const data = await response.json();
|
||||
|
||||
console.log('Generating search data files...');
|
||||
|
||||
// Create separate files for each category
|
||||
const categories = ['agents', 'commands', 'settings', 'hooks', 'mcps', 'templates'];
|
||||
|
||||
for (const category of categories) {
|
||||
if (data[category] && Array.isArray(data[category])) {
|
||||
console.log(`Processing ${category}: ${data[category].length} items`);
|
||||
|
||||
// Process each component to ensure search-friendly structure
|
||||
const processedComponents = data[category].map(component => ({
|
||||
...component,
|
||||
// Add search-friendly fields
|
||||
searchableText: [
|
||||
component.name || component.title,
|
||||
component.description,
|
||||
component.category,
|
||||
...(component.tags || []),
|
||||
component.keywords || '',
|
||||
component.path || ''
|
||||
].filter(Boolean).join(' ').toLowerCase(),
|
||||
|
||||
// Normalize common fields
|
||||
title: component.name || component.title,
|
||||
displayName: (component.name || component.title || '').replace(/[-_]/g, ' '),
|
||||
category: category,
|
||||
|
||||
// Add tags if not present
|
||||
tags: component.tags || [component.category].filter(Boolean)
|
||||
}));
|
||||
|
||||
// Save to individual file (simulate file creation)
|
||||
// In a real scenario, this would write to the server filesystem
|
||||
console.log(`Would save ${category}.json with ${processedComponents.length} components`);
|
||||
|
||||
// Try to cache in localStorage (may fail if quota exceeded)
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
try {
|
||||
localStorage.setItem(`searchData_${category}`, JSON.stringify(processedComponents));
|
||||
} catch (e) {
|
||||
// Quota exceeded — in-memory cache is sufficient
|
||||
}
|
||||
}
|
||||
|
||||
// Also store in a global variable for immediate use
|
||||
window.searchDataCache = window.searchDataCache || {};
|
||||
window.searchDataCache[category] = processedComponents;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Search data generation complete!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error generating search data:', error);
|
||||
}
|
||||
})();
|
||||
|
||||
// Function to get search data for a category
|
||||
function getSearchData(category) {
|
||||
// Try localStorage first
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
const data = localStorage.getItem(`searchData_${category}`);
|
||||
if (data) {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
}
|
||||
|
||||
// Try global cache
|
||||
if (window.searchDataCache && window.searchDataCache[category]) {
|
||||
return window.searchDataCache[category];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
// Make function globally available
|
||||
window.getSearchData = getSearchData;
|
||||
@@ -0,0 +1,345 @@
|
||||
// modal-helpers.js
|
||||
|
||||
// Show component modal
|
||||
function showComponentModal(component) {
|
||||
const modalHTML = createComponentModalHTML(component);
|
||||
|
||||
// Remove existing modal if present
|
||||
const existingModal = document.querySelector('.modal-overlay');
|
||||
if (existingModal) {
|
||||
existingModal.remove();
|
||||
}
|
||||
|
||||
// Add modal to body
|
||||
document.body.insertAdjacentHTML('beforeend', modalHTML);
|
||||
|
||||
// Render code preview
|
||||
renderCodePreview(component);
|
||||
|
||||
// Add event listener for ESC key
|
||||
const handleEscape = (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
closeComponentModal();
|
||||
document.removeEventListener('keydown', handleEscape);
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', handleEscape);
|
||||
}
|
||||
|
||||
// Create component modal HTML
|
||||
function createComponentModalHTML(component) {
|
||||
const typeConfig = {
|
||||
agent: { icon: '🤖', color: '#ff6b6b', badge: 'AGENT' },
|
||||
command: { icon: '⚡', color: '#4ecdc4', badge: 'COMMAND' },
|
||||
mcp: { icon: '🔌', color: '#45b7d1', badge: 'MCP' },
|
||||
template: { icon: '📦', color: '#f9a825', badge: 'TEMPLATE' }
|
||||
};
|
||||
|
||||
const config = typeConfig[component.type] || typeConfig['template'];
|
||||
|
||||
// Generate install command - remove .md extension from path
|
||||
let componentPath = component.path || component.name;
|
||||
if (componentPath.endsWith('.md')) {
|
||||
componentPath = componentPath.replace(/\.md$/, '');
|
||||
}
|
||||
if (componentPath.endsWith('.json')) {
|
||||
componentPath = componentPath.replace(/\.json$/, '');
|
||||
}
|
||||
const installCommand = `npx claude-code-templates@latest --${component.type}=${componentPath} --yes`;
|
||||
|
||||
// Generate global agent command for agents only
|
||||
const globalAgentCommand = component.type === 'agent' ? `npx claude-code-templates@latest --create-agent ${componentPath}` : null;
|
||||
|
||||
const description = getComponentDescription(component); // Full description
|
||||
|
||||
// Construct GitHub URL
|
||||
let githubUrl = 'https://github.com/davila7/claude-code-templates/';
|
||||
if (component.type === 'template') {
|
||||
githubUrl += `tree/main/cli-tool/templates/${component.folderPath}`;
|
||||
} else {
|
||||
githubUrl += `blob/main/cli-tool/components/${component.type}s/${component.path}`;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="modal-overlay" onclick="closeComponentModal()">
|
||||
<div class="modal-content" onclick="event.stopPropagation()">
|
||||
<div class="modal-header">
|
||||
<div class="component-modal-title">
|
||||
<span class="component-icon">${config.icon}</span>
|
||||
<h3 style="color: var(--text-primary);">${formatComponentName(component.name)}</h3>
|
||||
<div class="component-type-badge" style="background-color: ${config.color};">${config.badge}</div>
|
||||
</div>
|
||||
<button class="modal-close" onclick="closeComponentModal()">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="component-details">
|
||||
<div class="component-description">${component.type === 'mcp' ? (component.description || description) : description}</div>
|
||||
|
||||
<div class="installation-section">
|
||||
<!-- Basic Installation -->
|
||||
<div class="basic-installation-section">
|
||||
<h4>📦 Basic Installation</h4>
|
||||
<p class="installation-description">Install this ${component.type} locally in your project. Works with your existing Claude Code setup.</p>
|
||||
<div class="command-line">
|
||||
<code>${installCommand}</code>
|
||||
<button class="copy-btn" data-command="${installCommand.replace(/"/g, '"')}" onclick="copyToClipboard(this.dataset.command)">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${globalAgentCommand ? `
|
||||
<!-- Global Agent (Claude Code SDK) -->
|
||||
<div class="global-agent-section">
|
||||
<h4>🌍 Global Agent (Claude Code SDK)</h4>
|
||||
<p class="global-agent-description">Create a global AI agent accessible from anywhere with zero configuration. Perfect for automation and CI/CD workflows.</p>
|
||||
|
||||
<div class="command-line">
|
||||
<code>${globalAgentCommand}</code>
|
||||
<button class="copy-btn" data-command="${globalAgentCommand.replace(/"/g, '"')}" onclick="copyToClipboard(this.dataset.command)">Copy</button>
|
||||
</div>
|
||||
|
||||
<div class="global-agent-usage">
|
||||
<div class="usage-example">
|
||||
<div class="usage-title">After installation, use from anywhere:</div>
|
||||
<div class="command-line usage-command">
|
||||
<code>${componentPath.split('/').pop()} "your prompt here"</code>
|
||||
<button class="copy-btn" data-command="${componentPath.split('/').pop()} "your prompt here"" onclick="copyToClipboard(this.dataset.command)">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="global-features">
|
||||
<div class="feature">✅ Works in scripts, CI/CD, npm tasks</div>
|
||||
<div class="feature">✅ Auto-detects project context</div>
|
||||
<div class="feature">✅ Powered by Claude Code SDK</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>` : ''}
|
||||
|
||||
${component.type === 'agent' ? `
|
||||
<!-- Run in E2B Sandbox (Cloud Execution) -->
|
||||
<div class="e2b-sandbox-section">
|
||||
<h4>☁️ Run in E2B Sandbox (Cloud Execution) <span class="new-badge">NEW</span></h4>
|
||||
<p class="e2b-description">Execute Claude Code with this ${component.type} in an isolated cloud environment using E2B. Perfect for testing complex projects without affecting your local system.</p>
|
||||
|
||||
<div class="e2b-api-setup">
|
||||
<h5>🔑 Setup API Keys</h5>
|
||||
<div class="env-setup-simple">
|
||||
<div class="env-comment">Add to your .env file:</div>
|
||||
<div class="env-example">
|
||||
<code>E2B_API_KEY=your_e2b_key_here</code>
|
||||
<code>ANTHROPIC_API_KEY=your_anthropic_key_here</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="command-line">
|
||||
<code>npx claude-code-templates@latest --sandbox e2b --agent=${componentPath} --prompt "your development task"</code>
|
||||
<button class="copy-btn" data-command="npx claude-code-templates@latest --sandbox e2b --agent=${componentPath} --prompt "your development task"" onclick="copyToClipboard(this.dataset.command)">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="e2b-features">
|
||||
<div class="feature">🔒 Isolated cloud environment</div>
|
||||
<div class="feature">⚡ Extended timeouts for complex operations</div>
|
||||
<div class="feature">📁 Files downloaded to project root</div>
|
||||
<div class="feature">🔍 Real-time execution monitoring</div>
|
||||
</div>
|
||||
|
||||
<div class="e2b-requirements">
|
||||
<div class="requirements-title">Get API Keys:</div>
|
||||
<div class="api-key-links">
|
||||
<a href="https://e2b.dev/dashboard" target="_blank" class="api-key-link">
|
||||
<span>🔑</span> E2B API Key
|
||||
</a>
|
||||
<a href="https://console.anthropic.com" target="_blank" class="api-key-link">
|
||||
<span>🔑</span> Anthropic API Key
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>` : ''}
|
||||
</div>
|
||||
|
||||
<div class="component-content">
|
||||
<h4>📋 Component Details</h4>
|
||||
<div class="code-editor" id="code-editor">
|
||||
<div class="code-line-numbers" id="line-numbers"></div>
|
||||
<div class="code-content">
|
||||
<pre id="code-viewer"><code>Loading content...</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button class="add-to-cart-btn"
|
||||
data-type="${component.type}s"
|
||||
data-path="${componentPath}"
|
||||
onclick="handleAddToCart('${component.name.replace(/'/g, "\\'")}', '${componentPath.replace(/'/g, "\\'")}', '${component.type}s', '${component.category ? component.category.replace(/'/g, "\\'") : 'Other'}', this); closeComponentModal();">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M19,7H18V6A2,2 0 0,0 16,4H8A2,2 0 0,0 6,6V7H5A1,1 0 0,0 4,8A1,1 0 0,0 5,9H6V19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V9H19A1,1 0 0,0 20,8A1,1 0 0,0 19,7M8,6H16V7H8V6M16,19H8V9H16V19Z"/>
|
||||
</svg>
|
||||
Add to Stack
|
||||
</button>
|
||||
<a href="${githubUrl}" target="_blank" class="github-folder-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.23 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>
|
||||
View on GitHub
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Render code preview in the modal
|
||||
function renderCodePreview(component) {
|
||||
const viewer = document.getElementById('code-viewer');
|
||||
const lineNumbers = document.getElementById('line-numbers');
|
||||
if (!viewer) return;
|
||||
|
||||
let content = component.content || "No content available.";
|
||||
let language = 'plaintext';
|
||||
|
||||
if (component.path) {
|
||||
const extension = component.path.split('.').pop();
|
||||
switch (extension) {
|
||||
case 'md':
|
||||
language = 'markdown';
|
||||
break;
|
||||
case 'json':
|
||||
language = 'json';
|
||||
// Pretty print JSON
|
||||
try {
|
||||
content = JSON.stringify(JSON.parse(content), null, 2);
|
||||
} catch (e) { /* Ignore parsing errors */ }
|
||||
break;
|
||||
case 'js':
|
||||
language = 'javascript';
|
||||
break;
|
||||
case 'yml':
|
||||
case 'yaml':
|
||||
language = 'yaml';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Show only a preview (first 15 lines) instead of full content
|
||||
const lines = content.split('\n');
|
||||
const previewLines = lines.slice(0, 15);
|
||||
const previewContent = previewLines.join('\n');
|
||||
|
||||
// Add truncation indicator if content is longer
|
||||
const truncatedContent = lines.length > 15 ? previewContent + '\n...' : previewContent;
|
||||
|
||||
const codeElement = viewer.querySelector('code');
|
||||
codeElement.innerHTML = highlightCode(truncatedContent, language);
|
||||
codeElement.className = `language-${language}`;
|
||||
|
||||
// Generate line numbers for preview
|
||||
if (lineNumbers) {
|
||||
const previewLineNumbers = previewLines.map((_, index) => `<span>${index + 1}</span>`).join('');
|
||||
lineNumbers.innerHTML = previewLineNumbers;
|
||||
}
|
||||
}
|
||||
|
||||
// Basic syntax highlighting
|
||||
function highlightCode(content, language) {
|
||||
// First escape HTML entities
|
||||
let highlighted = content
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
|
||||
if (language === 'markdown' || language === 'yaml') {
|
||||
// Highlight YAML/Markdown frontmatter keys (blue)
|
||||
highlighted = highlighted.replace(/^([a-zA-Z_-]+):/gm, '<span style="color: #569cd6;">$1</span>:');
|
||||
|
||||
// Highlight strings in quotes (orange)
|
||||
highlighted = highlighted.replace(/"([^&]+?)"/g, '<span style="color: #ce9178;">"$1"</span>');
|
||||
|
||||
// Highlight important keywords (light blue - like in the image)
|
||||
highlighted = highlighted.replace(/\b(hackathon|strategy|AI|solution|ideation|evaluation|projects|feedback|concepts|feasibility|guidance|agent|specialist|brainstorming|winning|judge|feedback|Context|User|Examples)\b/gi, '<span style="color: #4fc1ff;">$1</span>');
|
||||
|
||||
// Highlight markdown headers (blue)
|
||||
highlighted = highlighted.replace(/^(#+)\s*(.+)$/gm, '<span style="color: #569cd6;">$1</span> <span style="color: #dcdcaa;">$2</span>');
|
||||
|
||||
// Highlight code in backticks
|
||||
highlighted = highlighted.replace(/`([^`]+)`/g, '<span style="color: #ce9178;">$1</span>');
|
||||
|
||||
// Highlight YAML separators
|
||||
highlighted = highlighted.replace(/^---$/gm, '<span style="color: #808080;">---</span>');
|
||||
}
|
||||
|
||||
return highlighted;
|
||||
}
|
||||
|
||||
// Close component modal
|
||||
function closeComponentModal() {
|
||||
const modalOverlay = document.querySelector('.modal-overlay');
|
||||
if (modalOverlay) {
|
||||
modalOverlay.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// Utility to get component description
|
||||
function getComponentDescription(component, maxLength = 0) {
|
||||
let description = component.description || '';
|
||||
if (!description && component.content) {
|
||||
const descMatch = component.content.match(/description:\s*(.+?)(?:\n|$)/);
|
||||
if (descMatch) {
|
||||
description = descMatch[1].trim().replace(/^["']|["']$/g, '');
|
||||
} else {
|
||||
const lines = component.content.split('\n');
|
||||
const firstParagraph = lines.find(line => line.trim() && !line.startsWith('---') && !line.startsWith('#'));
|
||||
if (firstParagraph) {
|
||||
description = firstParagraph.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!description) {
|
||||
description = `A ${component.type} component.`;
|
||||
}
|
||||
if (maxLength && description.length > maxLength) {
|
||||
description = description.substring(0, maxLength - 3) + '...';
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
// Utility to format component name
|
||||
function formatComponentName(name) {
|
||||
return name.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
|
||||
}
|
||||
|
||||
// Global function for component details (called from onclick)
|
||||
function showComponentDetails(type, name, path, category) {
|
||||
// Instead of showing modal, redirect to component page
|
||||
const componentURL = createComponentURL(type, name, path);
|
||||
window.location.href = componentURL;
|
||||
}
|
||||
|
||||
// Function to create component URL
|
||||
function createComponentURL(type, name, path) {
|
||||
// Detect if we're in local development
|
||||
const isLocal = window.location.hostname === 'localhost' ||
|
||||
window.location.hostname === '127.0.0.1' ||
|
||||
window.location.hostname.includes('5500');
|
||||
|
||||
if (!isLocal && type && name) {
|
||||
// Use SEO-friendly URL structure for production: /component/type/name
|
||||
let cleanName = name;
|
||||
if (cleanName.endsWith('.md')) {
|
||||
cleanName = cleanName.slice(0, -3);
|
||||
}
|
||||
if (cleanName.endsWith('.json')) {
|
||||
cleanName = cleanName.slice(0, -5);
|
||||
}
|
||||
|
||||
return `component/${encodeURIComponent(type)}/${encodeURIComponent(cleanName)}`;
|
||||
}
|
||||
|
||||
// Use query parameters for local development or fallback
|
||||
const params = new URLSearchParams();
|
||||
params.set('type', type);
|
||||
if (name) params.set('name', name);
|
||||
if (path) params.set('path', path);
|
||||
|
||||
return `component.html?${params.toString()}`;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Partnership Banner - GLM Z.AI
|
||||
* Handles banner click tracking
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Initialize partnership banner
|
||||
*/
|
||||
function initPartnershipBanner() {
|
||||
// Track CTA clicks
|
||||
const primaryCTA = document.getElementById('partnershipPrimaryCTA');
|
||||
if (primaryCTA) {
|
||||
primaryCTA.addEventListener('click', function(e) {
|
||||
trackEvent('partnership_click', {
|
||||
partner: 'z.ai',
|
||||
action: 'subscribe_link',
|
||||
component: 'glm-coding-plan'
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Track analytics event
|
||||
*/
|
||||
function trackEvent(eventName, params) {
|
||||
// Google Analytics (gtag)
|
||||
if (typeof gtag === 'function') {
|
||||
gtag('event', eventName, params);
|
||||
}
|
||||
|
||||
// Console log for debugging
|
||||
if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
|
||||
console.log('Partnership Banner Event:', eventName, params);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize on DOM ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initPartnershipBanner);
|
||||
} else {
|
||||
initPartnershipBanner();
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,390 @@
|
||||
/**
|
||||
* Plugin Detail Page Manager
|
||||
* Handles loading and displaying plugin details
|
||||
*/
|
||||
|
||||
class PluginPageManager {
|
||||
constructor() {
|
||||
this.pluginData = null;
|
||||
this.allPlugins = [];
|
||||
this.pluginName = this.getPluginNameFromURL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plugin name from URL path
|
||||
*/
|
||||
getPluginNameFromURL() {
|
||||
const path = window.location.pathname;
|
||||
const segments = path.split('/').filter(segment => segment);
|
||||
|
||||
// URL format: /plugin/plugin-name
|
||||
if (segments[0] === 'plugin' && segments[1]) {
|
||||
return segments[1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the page
|
||||
*/
|
||||
async init() {
|
||||
if (!this.pluginName) {
|
||||
this.showError();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Load components data
|
||||
await this.loadComponentsData();
|
||||
|
||||
// Find the plugin
|
||||
this.pluginData = this.findPlugin(this.pluginName);
|
||||
|
||||
if (!this.pluginData) {
|
||||
this.showError();
|
||||
return;
|
||||
}
|
||||
|
||||
// Render the plugin details
|
||||
this.renderPluginDetails();
|
||||
this.renderRelatedPlugins();
|
||||
|
||||
// Update page title
|
||||
document.title = `${this.formatPluginName(this.pluginData.name)} - Claude Code Templates`;
|
||||
|
||||
// Setup keyboard listeners
|
||||
this.setupKeyboardListeners();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading plugin:', error);
|
||||
this.showError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup keyboard event listeners
|
||||
*/
|
||||
setupKeyboardListeners() {
|
||||
document.addEventListener('keydown', (e) => {
|
||||
// ESC key closes modal
|
||||
if (e.key === 'Escape') {
|
||||
const modal = document.getElementById('installModal');
|
||||
if (modal.style.display === 'flex') {
|
||||
this.closeInstallModal();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load components.json
|
||||
*/
|
||||
async loadComponentsData() {
|
||||
try {
|
||||
const response = await fetch('/components.json');
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to load components data');
|
||||
}
|
||||
const data = await response.json();
|
||||
this.allPlugins = data.plugins || [];
|
||||
} catch (error) {
|
||||
console.error('Error loading components:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find plugin by name
|
||||
*/
|
||||
findPlugin(name) {
|
||||
return this.allPlugins.find(plugin => plugin.name === name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format name (convert kebab-case to Title Case)
|
||||
*/
|
||||
formatPluginName(name) {
|
||||
return this.formatComponentName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format component name (convert kebab-case to Title Case)
|
||||
* Handles both "name" and "category/name" formats
|
||||
*/
|
||||
formatComponentName(name) {
|
||||
// Extract just the filename if it includes a category path
|
||||
const displayName = name.includes('/') ? name.split('/').pop() : name;
|
||||
|
||||
return displayName
|
||||
.split('-')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Render plugin details
|
||||
*/
|
||||
renderPluginDetails() {
|
||||
const plugin = this.pluginData;
|
||||
|
||||
// Update hero section
|
||||
document.getElementById('pluginName').textContent = this.formatPluginName(plugin.name);
|
||||
document.getElementById('pluginDescription').textContent = plugin.description;
|
||||
document.getElementById('pluginVersion').textContent = `v${plugin.version}`;
|
||||
document.getElementById('pluginAuthor').textContent = plugin.author || 'Claude Code Templates';
|
||||
|
||||
// Render keywords
|
||||
const keywordsContainer = document.getElementById('pluginKeywords');
|
||||
keywordsContainer.innerHTML = plugin.keywords
|
||||
.map(keyword => `<span class="keyword-badge">${keyword}</span>`)
|
||||
.join('');
|
||||
|
||||
// Update installation commands
|
||||
document.getElementById('installPluginCmd').textContent = `/plugin install ${plugin.name}@claude-code-templates`;
|
||||
|
||||
// Render components sections
|
||||
this.renderComponents(plugin);
|
||||
|
||||
// Show content, hide loading
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
document.getElementById('pluginContent').style.display = 'block';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render components (commands, agents, MCPs)
|
||||
*/
|
||||
renderComponents(plugin) {
|
||||
// Commands
|
||||
if (plugin.commands && plugin.commands > 0) {
|
||||
document.getElementById('commandsSection').style.display = 'block';
|
||||
document.getElementById('commandsCount').textContent = plugin.commands;
|
||||
|
||||
const commandsList = document.getElementById('commandsList');
|
||||
commandsList.innerHTML = this.generateComponentItems(
|
||||
plugin.commandsList || [],
|
||||
'command',
|
||||
'commands'
|
||||
);
|
||||
}
|
||||
|
||||
// Agents
|
||||
if (plugin.agents && plugin.agents > 0) {
|
||||
document.getElementById('agentsSection').style.display = 'block';
|
||||
document.getElementById('agentsCount').textContent = plugin.agents;
|
||||
|
||||
const agentsList = document.getElementById('agentsList');
|
||||
agentsList.innerHTML = this.generateComponentItems(
|
||||
plugin.agentsList || [],
|
||||
'agent',
|
||||
'agents'
|
||||
);
|
||||
}
|
||||
|
||||
// MCPs
|
||||
if (plugin.mcpServers && plugin.mcpServers > 0) {
|
||||
document.getElementById('mcpsSection').style.display = 'block';
|
||||
document.getElementById('mcpsCount').textContent = plugin.mcpServers;
|
||||
|
||||
const mcpsList = document.getElementById('mcpsList');
|
||||
mcpsList.innerHTML = this.generateComponentItems(
|
||||
plugin.mcpServersList || [],
|
||||
'mcp',
|
||||
'mcps'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate component items with install commands
|
||||
*/
|
||||
generateComponentItems(componentNames, componentType, pluralType) {
|
||||
if (!componentNames || componentNames.length === 0) {
|
||||
return '<li>No components available</li>';
|
||||
}
|
||||
|
||||
return componentNames.map((name, index) => {
|
||||
const formattedName = this.formatComponentName(name);
|
||||
const installCommand = `npx claude-code-templates@latest --${componentType} ${name}`;
|
||||
|
||||
return `
|
||||
<li class="component-item" data-index="${index}">
|
||||
<span class="component-name">${formattedName}</span>
|
||||
<button class="component-install-btn" onclick="window.pluginPageManager.showInstallCommand('${installCommand.replace(/'/g, "\\'")}', event)">
|
||||
<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>
|
||||
Install
|
||||
</button>
|
||||
</li>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show install command in a modal
|
||||
*/
|
||||
showInstallCommand(command, event) {
|
||||
event.stopPropagation();
|
||||
|
||||
// Store current command
|
||||
this.currentCommand = command;
|
||||
|
||||
// Update modal content
|
||||
document.getElementById('modalCommand').textContent = command;
|
||||
|
||||
// Show modal
|
||||
const modal = document.getElementById('installModal');
|
||||
modal.style.display = 'flex';
|
||||
|
||||
// Prevent body scroll
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
/**
|
||||
* Close install modal
|
||||
*/
|
||||
closeInstallModal() {
|
||||
const modal = document.getElementById('installModal');
|
||||
modal.style.display = 'none';
|
||||
|
||||
// Restore body scroll
|
||||
document.body.style.overflow = 'auto';
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy command from modal
|
||||
*/
|
||||
copyModalCommand() {
|
||||
const command = this.currentCommand;
|
||||
|
||||
navigator.clipboard.writeText(command).then(() => {
|
||||
const button = document.querySelector('.modal-copy-btn');
|
||||
const originalHTML = button.innerHTML;
|
||||
|
||||
button.innerHTML = `
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M9,20.42L2.79,14.21L5.62,11.38L9,14.77L18.88,4.88L21.71,7.71L9,20.42Z"/>
|
||||
</svg>
|
||||
Copied!
|
||||
`;
|
||||
|
||||
setTimeout(() => {
|
||||
button.innerHTML = originalHTML;
|
||||
}, 2000);
|
||||
}).catch(err => {
|
||||
console.error('Failed to copy:', err);
|
||||
alert('Failed to copy to clipboard');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Render related plugins (based on shared keywords)
|
||||
*/
|
||||
renderRelatedPlugins() {
|
||||
const currentPlugin = this.pluginData;
|
||||
const relatedPlugins = this.findRelatedPlugins(currentPlugin);
|
||||
|
||||
const container = document.getElementById('relatedPlugins');
|
||||
|
||||
if (relatedPlugins.length === 0) {
|
||||
container.innerHTML = '<p style="color: var(--text-secondary);">No related plugins found.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = relatedPlugins
|
||||
.map(plugin => this.createRelatedPluginCard(plugin))
|
||||
.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Find related plugins based on shared keywords
|
||||
*/
|
||||
findRelatedPlugins(currentPlugin) {
|
||||
const currentKeywords = new Set(currentPlugin.keywords);
|
||||
|
||||
return this.allPlugins
|
||||
.filter(plugin => plugin.name !== currentPlugin.name)
|
||||
.map(plugin => {
|
||||
// Count shared keywords
|
||||
const sharedKeywords = plugin.keywords.filter(k => currentKeywords.has(k)).length;
|
||||
return { plugin, sharedKeywords };
|
||||
})
|
||||
.filter(item => item.sharedKeywords > 0)
|
||||
.sort((a, b) => b.sharedKeywords - a.sharedKeywords)
|
||||
.slice(0, 3) // Show top 3 related plugins
|
||||
.map(item => item.plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create related plugin card HTML
|
||||
*/
|
||||
createRelatedPluginCard(plugin) {
|
||||
return `
|
||||
<a href="/plugin/${plugin.name}" class="related-plugin-card">
|
||||
<h3>${this.formatPluginName(plugin.name)}</h3>
|
||||
<p>${plugin.description}</p>
|
||||
<div class="related-plugin-stats">
|
||||
${plugin.commands > 0 ? `<span class="related-plugin-stat"><span class="stat-icon">⚡</span>${plugin.commands}</span>` : ''}
|
||||
${plugin.agents > 0 ? `<span class="related-plugin-stat"><span class="stat-icon">🤖</span>${plugin.agents}</span>` : ''}
|
||||
${plugin.mcpServers > 0 ? `<span class="related-plugin-stat"><span class="stat-icon">🔌</span>${plugin.mcpServers}</span>` : ''}
|
||||
</div>
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error state
|
||||
*/
|
||||
showError() {
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
document.getElementById('error').style.display = 'flex';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy command to clipboard
|
||||
*/
|
||||
function copyCommand(elementId, event) {
|
||||
const element = document.getElementById(elementId);
|
||||
const text = element.textContent;
|
||||
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
// Show feedback - find the button that was clicked
|
||||
let button = null;
|
||||
if (event && event.target) {
|
||||
button = event.target.closest('.copy-btn');
|
||||
}
|
||||
|
||||
// If we can't find the button through event, try to find it near the element
|
||||
if (!button) {
|
||||
const parentContainer = element.closest('.command-box');
|
||||
if (parentContainer) {
|
||||
button = parentContainer.querySelector('.copy-btn');
|
||||
}
|
||||
}
|
||||
|
||||
if (button) {
|
||||
const originalHTML = button.innerHTML;
|
||||
|
||||
button.innerHTML = `
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M9,20.42L2.79,14.21L5.62,11.38L9,14.77L18.88,4.88L21.71,7.71L9,20.42Z"/>
|
||||
</svg>
|
||||
Copied!
|
||||
`;
|
||||
|
||||
setTimeout(() => {
|
||||
button.innerHTML = originalHTML;
|
||||
}, 2000);
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('Failed to copy:', err);
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
window.pluginPageManager = new PluginPageManager();
|
||||
window.pluginPageManager.init();
|
||||
});
|
||||
@@ -0,0 +1,555 @@
|
||||
// Stack Router - Handles company and technology specific stack pages
|
||||
class StackRouter {
|
||||
constructor() {
|
||||
this.routes = new Map();
|
||||
this.currentRoute = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
// Listen for hash changes and initial load
|
||||
window.addEventListener('hashchange', () => this.handleRouteChange());
|
||||
window.addEventListener('load', () => this.handleRouteChange());
|
||||
|
||||
// Check for path-based routes on load
|
||||
this.handleRouteChange();
|
||||
}
|
||||
|
||||
// Get company info from data loader
|
||||
getCompanyInfo(slug) {
|
||||
return window.dataLoader.getCompanyInfo(slug);
|
||||
}
|
||||
|
||||
// Get technology info from data loader
|
||||
getTechnologyInfo(slug) {
|
||||
return window.dataLoader.getTechnologyInfo(slug);
|
||||
}
|
||||
|
||||
// Handle route changes
|
||||
handleRouteChange() {
|
||||
const path = window.location.pathname;
|
||||
const hash = window.location.hash;
|
||||
|
||||
// Check for company routes (/company/epic-games)
|
||||
const companyMatch = path.match(/\/company\/([^\/]+)/);
|
||||
if (companyMatch) {
|
||||
this.loadCompanyStack(companyMatch[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for technology routes (/technology/unity)
|
||||
const technologyMatch = path.match(/\/technology\/([^\/]+)/);
|
||||
if (technologyMatch) {
|
||||
this.loadTechnologyStack(technologyMatch[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for hash-based routes for development
|
||||
if (hash.startsWith('#/company/')) {
|
||||
const company = hash.replace('#/company/', '');
|
||||
this.loadCompanyStack(company);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hash.startsWith('#/technology/')) {
|
||||
const technology = hash.replace('#/technology/', '');
|
||||
this.loadTechnologyStack(technology);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hash === '#/companies') {
|
||||
this.loadAllCompaniesPage();
|
||||
return;
|
||||
}
|
||||
|
||||
// Return to main page
|
||||
this.loadMainPage();
|
||||
}
|
||||
|
||||
// Load company-specific stack page
|
||||
async loadCompanyStack(companySlug) {
|
||||
const companyInfo = this.getCompanyInfo(companySlug);
|
||||
if (!companyInfo) {
|
||||
console.error('Company not found:', companySlug);
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentRoute = { type: 'company', slug: companySlug, info: companyInfo };
|
||||
|
||||
// Wait for data to be loaded
|
||||
if (!window.dataLoader.componentsData) {
|
||||
await window.dataLoader.loadAllComponents();
|
||||
}
|
||||
|
||||
// Get company stack components
|
||||
const stackComponents = window.dataLoader.getCompanyStack(companySlug);
|
||||
|
||||
// Update page content
|
||||
this.renderStackPage(companyInfo, stackComponents, 'company');
|
||||
|
||||
// Update page title and meta
|
||||
document.title = `${companyInfo.name} Stack - Claude Code Templates`;
|
||||
this.updateMetaTags(companyInfo, 'company');
|
||||
}
|
||||
|
||||
// Load technology-specific stack page
|
||||
async loadTechnologyStack(techSlug) {
|
||||
const techInfo = this.getTechnologyInfo(techSlug);
|
||||
if (!techInfo) {
|
||||
console.error('Technology not found:', techSlug);
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentRoute = { type: 'technology', slug: techSlug, info: techInfo };
|
||||
|
||||
// Wait for data to be loaded
|
||||
if (!window.dataLoader.componentsData) {
|
||||
await window.dataLoader.loadAllComponents();
|
||||
}
|
||||
|
||||
// Get technology stack components
|
||||
const stackComponents = window.dataLoader.getTechnologyStack(techSlug);
|
||||
|
||||
// Update page content
|
||||
this.renderStackPage(techInfo, stackComponents, 'technology');
|
||||
|
||||
// Update page title and meta
|
||||
document.title = `${techInfo.name} Stack - Claude Code Templates`;
|
||||
this.updateMetaTags(techInfo, 'technology');
|
||||
}
|
||||
|
||||
// Render stack page content
|
||||
renderStackPage(stackInfo, components, type) {
|
||||
const main = document.querySelector('main.terminal');
|
||||
if (!main) return;
|
||||
|
||||
// Hide original content
|
||||
const originalSections = main.querySelectorAll('section:not(.stack-page)');
|
||||
originalSections.forEach(section => section.style.display = 'none');
|
||||
|
||||
// Remove existing stack page if any
|
||||
const existingStackPage = main.querySelector('.stack-page');
|
||||
if (existingStackPage) {
|
||||
existingStackPage.remove();
|
||||
}
|
||||
|
||||
// Create stack page
|
||||
const stackPageHTML = this.generateStackPageHTML(stackInfo, components, type);
|
||||
main.insertAdjacentHTML('afterbegin', stackPageHTML);
|
||||
|
||||
// Initialize cart functionality for the new page
|
||||
if (window.initializeCartForStackPage) {
|
||||
window.initializeCartForStackPage();
|
||||
}
|
||||
|
||||
// Update header to show back button
|
||||
this.updateHeaderForStack(stackInfo);
|
||||
}
|
||||
|
||||
// Generate stack page HTML
|
||||
generateStackPageHTML(stackInfo, components, type) {
|
||||
const totalComponents = Object.values(components).reduce((sum, arr) => sum + arr.length, 0);
|
||||
|
||||
return `
|
||||
<section class="stack-page">
|
||||
<!-- Stack Header -->
|
||||
<div class="stack-header">
|
||||
<div class="stack-info">
|
||||
<div class="stack-logo">${stackInfo.logo}</div>
|
||||
<div class="stack-details">
|
||||
<h1>${stackInfo.name} Development Stack</h1>
|
||||
<p class="stack-description">${stackInfo.description}</p>
|
||||
<div class="stack-stats">
|
||||
<div class="stack-stat">
|
||||
<span class="stat-number">${totalComponents}</span>
|
||||
<span class="stat-label">Components</span>
|
||||
</div>
|
||||
<div class="stack-stat">
|
||||
<span class="stat-number">${components.agents.length}</span>
|
||||
<span class="stat-label">Agents</span>
|
||||
</div>
|
||||
<div class="stack-stat">
|
||||
<span class="stat-number">${components.commands.length}</span>
|
||||
<span class="stat-label">Commands</span>
|
||||
</div>
|
||||
<div class="stack-stat">
|
||||
<span class="stat-number">${components.mcps.length}</span>
|
||||
<span class="stat-label">MCPs</span>
|
||||
</div>
|
||||
</div>
|
||||
${stackInfo.website ? `<a href="${stackInfo.website}" target="_blank" class="stack-website">Visit ${stackInfo.name} →</a>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stack Components -->
|
||||
<div class="stack-components">
|
||||
${this.generateStackComponentsHTML(components)}
|
||||
</div>
|
||||
|
||||
<!-- Install All Section -->
|
||||
<div class="stack-install-all">
|
||||
<h3>🚀 Install Complete ${stackInfo.name} Stack</h3>
|
||||
<p>Get all ${totalComponents} components with a single command:</p>
|
||||
<div class="command-line">
|
||||
<span class="prompt">$</span>
|
||||
<code class="command" id="stackInstallCommand">${this.generateStackInstallCommand(components)}</code>
|
||||
<button class="copy-btn" onclick="copyToClipboard(document.getElementById('stackInstallCommand').textContent)">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
`;
|
||||
}
|
||||
|
||||
// Generate components sections HTML
|
||||
generateStackComponentsHTML(components) {
|
||||
let html = '';
|
||||
|
||||
const sections = [
|
||||
{ type: 'agents', title: 'AI Agents', icon: '🤖', description: 'Specialized AI assistants for your workflow' },
|
||||
{ type: 'commands', title: 'Commands', icon: '⚡', description: 'Ready-to-use automation commands' },
|
||||
{ type: 'mcps', title: 'MCPs', icon: '🔌', description: 'Model Context Protocol integrations' }
|
||||
];
|
||||
|
||||
sections.forEach(section => {
|
||||
const items = components[section.type] || [];
|
||||
if (items.length > 0) {
|
||||
html += `
|
||||
<div class="stack-section">
|
||||
<div class="stack-section-header">
|
||||
<h3>${section.icon} ${section.title}</h3>
|
||||
<p>${section.description}</p>
|
||||
<span class="component-count">${items.length} ${items.length === 1 ? section.type.slice(0, -1) : section.type}</span>
|
||||
</div>
|
||||
<div class="stack-grid">
|
||||
${items.map(component => this.generateComponentCardHTML(component)).join('')}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
});
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
// Generate component card HTML
|
||||
generateComponentCardHTML(component) {
|
||||
const { tags, companies, technologies } = window.dataLoader.getComponentMetadata(component.name);
|
||||
|
||||
return `
|
||||
<div class="component-card stack-component" data-name="${component.name}" data-type="${component.type}">
|
||||
<div class="component-header">
|
||||
<h4>${component.name}</h4>
|
||||
<div class="component-actions">
|
||||
<button class="add-to-cart-btn" onclick="addToCart('${component.name}', '${component.type}')">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M19,7H18V6A6,6 0 0,0 6,6V7H5A1,1 0 0,0 4,8V19A3,3 0 0,0 7,22H17A3,3 0 0,0 20,19V8A1,1 0 0,0 19,7M12,2A4,4 0 0,1 16,6V7H8V6A4,4 0 0,1 12,2M7,20A1,1 0 0,1 6,19V9H8V11A1,1 0 0,0 10,11A1,1 0 0,0 10,9V9H14V11A1,1 0 0,0 16,11A1,1 0 0,0 16,9V9H18V19A1,1 0 0,1 17,20H7M12,13A1,1 0 0,0 11,14A1,1 0 0,0 12,15A1,1 0 0,0 13,14A1,1 0 0,0 12,13Z"/>
|
||||
</svg>
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="component-content">
|
||||
${this.extractDescription(component.content)}
|
||||
</div>
|
||||
${tags.length > 0 || technologies.length > 0 ? `
|
||||
<div class="component-tags">
|
||||
${[...tags.slice(0, 3), ...technologies.slice(0, 2)].map(tag =>
|
||||
`<span class="component-tag">${tag}</span>`
|
||||
).join('')}
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Extract description from component content
|
||||
extractDescription(content) {
|
||||
const descMatch = content.match(/description:\s*(.+?)(?:\n|$)/);
|
||||
if (descMatch) {
|
||||
return descMatch[1].replace(/^['"]|['"]$/g, '').substring(0, 150) + '...';
|
||||
}
|
||||
return 'No description available';
|
||||
}
|
||||
|
||||
// Generate install command for entire stack
|
||||
generateStackInstallCommand(components) {
|
||||
const allComponents = [...components.agents, ...components.commands, ...components.mcps];
|
||||
const componentArgs = allComponents.map(c => `--${c.type} ${c.name}`).join(' ');
|
||||
return `npx claude-code-templates@latest ${componentArgs}`;
|
||||
}
|
||||
|
||||
// Update header for stack page
|
||||
updateHeaderForStack(stackInfo) {
|
||||
const header = document.querySelector('.header');
|
||||
if (!header) return;
|
||||
|
||||
// Add back button
|
||||
let backButton = header.querySelector('.back-button');
|
||||
if (!backButton) {
|
||||
backButton = document.createElement('button');
|
||||
backButton.className = 'back-button';
|
||||
backButton.innerHTML = `
|
||||
<svg width="20" height="20" 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 Main
|
||||
`;
|
||||
backButton.onclick = () => this.goBack();
|
||||
|
||||
const headerContent = header.querySelector('.header-content');
|
||||
if (headerContent) {
|
||||
headerContent.insertBefore(backButton, headerContent.firstChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update meta tags for SEO
|
||||
updateMetaTags(stackInfo, type) {
|
||||
// Update Open Graph tags
|
||||
const ogTitle = document.querySelector('meta[property="og:title"]');
|
||||
const ogDescription = document.querySelector('meta[property="og:description"]');
|
||||
|
||||
if (ogTitle) {
|
||||
ogTitle.content = `${stackInfo.name} Development Stack - Claude Code Templates`;
|
||||
}
|
||||
|
||||
if (ogDescription) {
|
||||
ogDescription.content = stackInfo.description;
|
||||
}
|
||||
|
||||
// Update Twitter tags
|
||||
const twitterTitle = document.querySelector('meta[name="twitter:title"]');
|
||||
const twitterDescription = document.querySelector('meta[name="twitter:description"]');
|
||||
|
||||
if (twitterTitle) {
|
||||
twitterTitle.content = `${stackInfo.name} Development Stack - Claude Code Templates`;
|
||||
}
|
||||
|
||||
if (twitterDescription) {
|
||||
twitterDescription.content = stackInfo.description;
|
||||
}
|
||||
}
|
||||
|
||||
// Navigate back to main page
|
||||
goBack() {
|
||||
// Remove stack page
|
||||
const stackPage = document.querySelector('.stack-page');
|
||||
if (stackPage) {
|
||||
stackPage.remove();
|
||||
}
|
||||
|
||||
// Show original content
|
||||
const main = document.querySelector('main.terminal');
|
||||
if (main) {
|
||||
const originalSections = main.querySelectorAll('section:not(.stack-page)');
|
||||
originalSections.forEach(section => section.style.display = '');
|
||||
}
|
||||
|
||||
// Remove back button
|
||||
const backButton = document.querySelector('.back-button');
|
||||
if (backButton) {
|
||||
backButton.remove();
|
||||
}
|
||||
|
||||
// Reset route
|
||||
this.currentRoute = null;
|
||||
window.history.pushState({}, '', window.location.pathname);
|
||||
|
||||
// Reset page title and meta
|
||||
document.title = 'Claude Code Templates';
|
||||
this.resetMetaTags();
|
||||
}
|
||||
|
||||
// Load main page (reset everything)
|
||||
loadMainPage() {
|
||||
if (this.currentRoute) {
|
||||
this.goBack();
|
||||
}
|
||||
}
|
||||
|
||||
// Reset meta tags to original values
|
||||
resetMetaTags() {
|
||||
const ogTitle = document.querySelector('meta[property="og:title"]');
|
||||
const ogDescription = document.querySelector('meta[property="og:description"]');
|
||||
|
||||
if (ogTitle) {
|
||||
ogTitle.content = 'Claude Code Templates - Ready-to-use configurations';
|
||||
}
|
||||
|
||||
if (ogDescription) {
|
||||
ogDescription.content = 'Browse and install Claude Code configuration templates for different languages and frameworks. Includes 100+ agents, 159+ commands, 23+ MCPs, and 14+ templates.';
|
||||
}
|
||||
}
|
||||
|
||||
// Load all companies page
|
||||
async loadAllCompaniesPage() {
|
||||
this.currentRoute = { type: 'companies', slug: 'all' };
|
||||
|
||||
// Wait for data to be loaded
|
||||
if (!window.dataLoader.componentsData) {
|
||||
await window.dataLoader.loadAllComponents();
|
||||
}
|
||||
|
||||
// Update page content
|
||||
this.renderAllCompaniesPage();
|
||||
|
||||
// Update page title and meta
|
||||
document.title = 'All Development Stacks - Claude Code Templates';
|
||||
this.updateMetaTagsForAllCompanies();
|
||||
}
|
||||
|
||||
// Render all companies page
|
||||
renderAllCompaniesPage() {
|
||||
const main = document.querySelector('main.terminal');
|
||||
if (!main) return;
|
||||
|
||||
// Hide original content
|
||||
const originalSections = main.querySelectorAll('section:not(.stack-page)');
|
||||
originalSections.forEach(section => section.style.display = 'none');
|
||||
|
||||
// Remove existing stack page if any
|
||||
const existingStackPage = main.querySelector('.stack-page');
|
||||
if (existingStackPage) {
|
||||
existingStackPage.remove();
|
||||
}
|
||||
|
||||
// Get all companies from metadata
|
||||
const allCompanies = window.dataLoader.metadataData?.companies || {};
|
||||
|
||||
// Create all companies page
|
||||
const allCompaniesHTML = this.generateAllCompaniesPageHTML(allCompanies);
|
||||
main.insertAdjacentHTML('afterbegin', allCompaniesHTML);
|
||||
|
||||
// Update header to show back button
|
||||
this.updateHeaderForStack({ name: 'All Development Stacks' });
|
||||
}
|
||||
|
||||
// Generate all companies page HTML
|
||||
generateAllCompaniesPageHTML(companies) {
|
||||
const companiesArray = Object.entries(companies);
|
||||
|
||||
// Group companies by category
|
||||
const categories = {
|
||||
'AI & Machine Learning': ['openai', 'anthropic'],
|
||||
'Payments & E-commerce': ['stripe', 'shopify', 'shopify'],
|
||||
'CRM & Business': ['salesforce', 'hubspot', 'airtable', 'linear'],
|
||||
'Communication': ['twilio', 'slack', 'discord', 'sendgrid'],
|
||||
'Cloud & Infrastructure': ['aws', 'vercel', 'netlify', 'cloudflare', 'firebase', 'supabase'],
|
||||
'Databases': ['mongodb', 'planetscale'],
|
||||
'Development Tools': ['github', 'figma', 'adobe', 'atlassian', 'notion'],
|
||||
'Entertainment & Media': ['spotify', 'youtube', 'twitter'],
|
||||
'Game Development': ['unity-technologies', 'epic-games'],
|
||||
'Marketing': ['mailchimp', 'hubspot']
|
||||
};
|
||||
|
||||
let categorizedHTML = '';
|
||||
let uncategorizedCompanies = [...companiesArray];
|
||||
|
||||
// Generate categorized sections
|
||||
Object.entries(categories).forEach(([categoryName, companyIds]) => {
|
||||
const categoryCompanies = companyIds
|
||||
.map(id => companiesArray.find(([key]) => key === id))
|
||||
.filter(Boolean);
|
||||
|
||||
if (categoryCompanies.length > 0) {
|
||||
categorizedHTML += `
|
||||
<div class="companies-category">
|
||||
<h3 class="category-title">${categoryName}</h3>
|
||||
<div class="companies-grid">
|
||||
${categoryCompanies.map(([key, company]) => {
|
||||
// Remove from uncategorized list
|
||||
uncategorizedCompanies = uncategorizedCompanies.filter(([k]) => k !== key);
|
||||
return this.generateCompanyCardHTML(key, company);
|
||||
}).join('')}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
});
|
||||
|
||||
// Add uncategorized companies if any
|
||||
if (uncategorizedCompanies.length > 0) {
|
||||
categorizedHTML += `
|
||||
<div class="companies-category">
|
||||
<h3 class="category-title">Other Platforms</h3>
|
||||
<div class="companies-grid">
|
||||
${uncategorizedCompanies.map(([key, company]) =>
|
||||
this.generateCompanyCardHTML(key, company)
|
||||
).join('')}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
return `
|
||||
<section class="stack-page all-companies-page">
|
||||
<div class="stack-header">
|
||||
<div class="stack-info">
|
||||
<div class="stack-logo">🏢</div>
|
||||
<div class="stack-details">
|
||||
<h1>All Development Stacks</h1>
|
||||
<p class="stack-description">Complete list of companies and platforms with development APIs, SDKs, and integration opportunities</p>
|
||||
<div class="stack-stats">
|
||||
<div class="stack-stat">
|
||||
<span class="stat-number">${companiesArray.length}</span>
|
||||
<span class="stat-label">Companies</span>
|
||||
</div>
|
||||
<div class="stack-stat">
|
||||
<span class="stat-number">${Object.keys(categories).length}</span>
|
||||
<span class="stat-label">Categories</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="all-companies-content">
|
||||
${categorizedHTML}
|
||||
</div>
|
||||
</section>
|
||||
`;
|
||||
}
|
||||
|
||||
// Generate individual company card for all companies page
|
||||
generateCompanyCardHTML(companyKey, company) {
|
||||
return `
|
||||
<a href="#/company/${companyKey}" class="all-company-card" onclick="window.stackRouter?.navigateTo('#/company/${companyKey}')">
|
||||
<div class="company-card-logo">${company.logo}</div>
|
||||
<div class="company-card-info">
|
||||
<h4>${company.name}</h4>
|
||||
<p>${company.description}</p>
|
||||
</div>
|
||||
<div class="company-card-arrow">→</div>
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
|
||||
// Update meta tags for all companies page
|
||||
updateMetaTagsForAllCompanies() {
|
||||
const ogTitle = document.querySelector('meta[property="og:title"]');
|
||||
const ogDescription = document.querySelector('meta[property="og:description"]');
|
||||
|
||||
if (ogTitle) {
|
||||
ogTitle.content = 'All Development Stacks - Claude Code Templates';
|
||||
}
|
||||
|
||||
if (ogDescription) {
|
||||
ogDescription.content = 'Browse all available development stacks for major companies and platforms. Find agents, commands, and MCPs for APIs like OpenAI, Stripe, Shopify, AWS, and more.';
|
||||
}
|
||||
}
|
||||
|
||||
// Navigate programmatically
|
||||
navigateTo(path) {
|
||||
window.history.pushState({}, '', path);
|
||||
this.handleRouteChange();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize router when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
window.stackRouter = new StackRouter();
|
||||
});
|
||||
|
||||
// Make it available globally
|
||||
window.StackRouter = StackRouter;
|
||||
@@ -0,0 +1,794 @@
|
||||
/**
|
||||
* Trending Page JavaScript
|
||||
* GitHub-inspired trending components page for Claude Code Templates
|
||||
*/
|
||||
|
||||
class TrendingPage {
|
||||
constructor() {
|
||||
this.currentType = 'agents';
|
||||
this.currentRange = 'month'; // Changed from 'today' to 'month' to show real data
|
||||
this.data = null;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
async init() {
|
||||
try {
|
||||
await this.loadData();
|
||||
this.setupEventListeners();
|
||||
this.renderHeroStats();
|
||||
this.renderPopularItems();
|
||||
this.renderTopCountries();
|
||||
this.renderChart();
|
||||
this.renderTrendingItems();
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize trending page:', error);
|
||||
this.showError('Failed to load trending data');
|
||||
}
|
||||
}
|
||||
|
||||
async loadData() {
|
||||
try {
|
||||
const response = await fetch('trending-data.json');
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
this.data = await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error loading trending data:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
// Component tabs
|
||||
document.querySelectorAll('.component-tab').forEach(tab => {
|
||||
tab.addEventListener('click', (e) => {
|
||||
// Remove active class from all tabs
|
||||
document.querySelectorAll('.component-tab').forEach(t => t.classList.remove('active'));
|
||||
|
||||
// Add active class to clicked tab
|
||||
e.target.classList.add('active');
|
||||
|
||||
// Update current type
|
||||
this.currentType = e.target.dataset.type;
|
||||
this.renderTrendingItems();
|
||||
});
|
||||
});
|
||||
|
||||
// Date range filter
|
||||
const dateRange = document.getElementById('date-range');
|
||||
if (dateRange) {
|
||||
dateRange.addEventListener('change', (e) => {
|
||||
this.currentRange = e.target.value;
|
||||
this.renderTrendingItems();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderHeroStats() {
|
||||
const container = document.getElementById('hero-stats');
|
||||
if (!container || !this.data || !this.data.globalStats) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use real global statistics from the JSON
|
||||
const globalStats = this.data.globalStats;
|
||||
|
||||
const stats = [
|
||||
{
|
||||
number: globalStats.totalComponents.toLocaleString(),
|
||||
label: 'Unique Components',
|
||||
change: 'Across 7 categories',
|
||||
positive: true
|
||||
},
|
||||
{
|
||||
number: globalStats.totalDownloads.toLocaleString(),
|
||||
label: 'Total Downloads',
|
||||
change: 'All time downloads',
|
||||
positive: true
|
||||
},
|
||||
{
|
||||
number: globalStats.totalCountries.toLocaleString(),
|
||||
label: 'Countries',
|
||||
change: 'Global reach',
|
||||
positive: true
|
||||
}
|
||||
];
|
||||
|
||||
container.innerHTML = stats.map(stat => `
|
||||
<div class="hero-stat-item">
|
||||
<div class="hero-stat-number">${stat.number}</div>
|
||||
<div class="hero-stat-label">${stat.label}</div>
|
||||
<div class="hero-stat-change ${stat.positive ? '' : 'negative'}">
|
||||
<svg width="10" height="10" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="${stat.positive ? 'M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0zM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0zm7-3.25a.75.75 0 0 0-1.5 0v2.5h-2.5a.75.75 0 0 0 0 1.5h2.5v2.5a.75.75 0 0 0 1.5 0v-2.5h2.5a.75.75 0 0 0 0-1.5h-2.5v-2.5Z' : 'M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0zM3.25 8a.75.75 0 0 1 .75-.75h8a.75.75 0 0 1 0 1.5h-8A.75.75 0 0 1 3.25 8z'}"/>
|
||||
</svg>
|
||||
${stat.change}
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
renderPopularItems() {
|
||||
const container = document.getElementById('popular-categories');
|
||||
if (!container || !this.data || !this.data.trending) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Define categories to show and their display info (excluding templates)
|
||||
const categories = [
|
||||
{ key: 'agents', title: 'Agents', icon: 'M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm7-3.25a.75.75 0 0 0-1.5 0v2.5h-2.5a.75.75 0 0 0 0 1.5h2.5v2.5a.75.75 0 0 0 1.5 0v-2.5h2.5a.75.75 0 0 0 0-1.5h-2.5v-2.5Z' },
|
||||
{ key: 'commands', title: 'Commands', icon: 'M8 4a4 4 0 1 1 0 8 4 4 0 0 1 0-8zM2.5 8a5.5 5.5 0 1 0 11 0 5.5 5.5 0 0 0-11 0z' },
|
||||
{ key: 'mcps', title: 'MCPs', icon: 'M2.5 3A1.5 1.5 0 0 0 1 4.5v.793c0 .026.009.051.025.072L2.5 7a.5.5 0 0 1 0 .708L1.025 9.133a.149.149 0 0 0-.025.072V10.5A1.5 1.5 0 0 0 2.5 12h2.793a.149.149 0 0 0 .072-.025L7 10.5a.5.5 0 0 1 .708 0l1.625 1.475c.021.016.046.025.072.025H12.5A1.5 1.5 0 0 0 14 10.5v-.793a.149.149 0 0 0-.025-.072L12.5 8a.5.5 0 0 1 0-.708l1.475-1.625a.149.149 0 0 0 .025-.072V4.5A1.5 1.5 0 0 0 12.5 3H9.707a.149.149 0 0 0-.072.025L8 4.5a.5.5 0 0 1-.708 0L5.867 3.025A.149.149 0 0 0 5.793 3H2.5z' },
|
||||
{ key: 'settings', title: 'Settings', icon: 'M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492zM5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0z' },
|
||||
{ key: 'hooks', title: 'Hooks', icon: 'M1.5 1.5A.5.5 0 0 0 1 2v4.8a2.5 2.5 0 0 0 2.5 2.5h9.793l-3.347 3.346a.5.5 0 0 0 .708.708l4.2-4.2a.5.5 0 0 0 0-.708l-4.2-4.2a.5.5 0 0 0-.708.708L13.293 8.3H3.5A1.5 1.5 0 0 1 2 6.8V2a.5.5 0 0 0-.5-.5z' },
|
||||
{ key: 'skills', title: 'Skills', icon: 'M6.5 1A1.5 1.5 0 0 0 5 2.5V3H1.5A1.5 1.5 0 0 0 0 4.5v8A1.5 1.5 0 0 0 1.5 14h13a1.5 1.5 0 0 0 1.5-1.5v-8A1.5 1.5 0 0 0 14.5 3H11v-.5A1.5 1.5 0 0 0 9.5 1h-3zm0 1h3a.5.5 0 0 1 .5.5V3H6v-.5a.5.5 0 0 1 .5-.5zm1.886 6.914L15 7.151V12.5a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5V7.15l6.614 1.764a1.5 1.5 0 0 0 .772 0zM1.5 4h13a.5.5 0 0 1 .5.5v1.616L8.129 7.948a.5.5 0 0 1-.258 0L1 6.116V4.5a.5.5 0 0 1 .5-.5z' }
|
||||
];
|
||||
|
||||
container.innerHTML = '';
|
||||
|
||||
categories.forEach(category => {
|
||||
const categoryData = this.data.trending[category.key];
|
||||
if (!categoryData || !Array.isArray(categoryData) || categoryData.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort by monthly downloads and take only the top 1
|
||||
const topItem = categoryData
|
||||
.sort((a, b) => (b.downloadsMonth || 0) - (a.downloadsMonth || 0))[0];
|
||||
|
||||
const itemElement = this.createPopularItemCard(category, topItem);
|
||||
container.appendChild(itemElement);
|
||||
});
|
||||
}
|
||||
|
||||
renderTopCountries() {
|
||||
const container = document.getElementById('top-countries-list');
|
||||
if (!container || !this.data || !this.data.topCountries) {
|
||||
return;
|
||||
}
|
||||
|
||||
const topCountries = this.data.topCountries;
|
||||
|
||||
container.innerHTML = topCountries.map(country => `
|
||||
<div class="country-item">
|
||||
<div class="country-flag">${country.flag}</div>
|
||||
<div class="country-info">
|
||||
<div class="country-name">${country.name}</div>
|
||||
<div class="country-stats">
|
||||
<span class="country-downloads">${country.downloads.toLocaleString()}</span>
|
||||
<span class="country-percentage">(${country.percentage}%)</span>
|
||||
</div>
|
||||
<div class="country-bar-container">
|
||||
<div class="country-bar" style="width: ${country.percentage}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
createPopularItemCard(category, item) {
|
||||
const itemElement = document.createElement('div');
|
||||
itemElement.className = 'popular-item';
|
||||
|
||||
const totalDownloads = item.downloadsTotal || 0;
|
||||
|
||||
itemElement.innerHTML = `
|
||||
<div class="popular-item-header">
|
||||
<div class="popular-item-info">
|
||||
<div class="popular-item-type">${category.title}</div>
|
||||
<h4 class="popular-item-name">${item.name}</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="popular-total-downloads">
|
||||
<span class="total-number">${totalDownloads.toLocaleString()}</span>
|
||||
<span class="total-label">downloads</span>
|
||||
</div>
|
||||
|
||||
<div class="popular-stats">
|
||||
<button class="popular-install-btn" onclick="showInstallModal('${item.id || item.name}')">
|
||||
<svg class="popular-install-icon" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
|
||||
<path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
|
||||
</svg>
|
||||
Get
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return itemElement;
|
||||
}
|
||||
|
||||
renderChart() {
|
||||
if (!this.data || !this.data.chartData) {
|
||||
console.warn('No chart data available');
|
||||
return;
|
||||
}
|
||||
|
||||
const ctx = document.getElementById('downloadsChart');
|
||||
if (!ctx) {
|
||||
console.warn('Chart canvas not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Chart colors matching terminal theme (excluding templates)
|
||||
const colors = {
|
||||
commands: '#10b981', // emerald (green)
|
||||
agents: '#f59e0b', // amber (yellow)
|
||||
mcps: '#3b82f6', // blue
|
||||
settings: '#8b5cf6', // violet
|
||||
hooks: '#f97316', // orange
|
||||
skills: '#ec4899' // pink
|
||||
};
|
||||
|
||||
// Define the desired order for the legend (agents first - most popular, excluding templates)
|
||||
const categoryOrder = ['agents', 'commands', 'mcps', 'settings', 'hooks', 'skills'];
|
||||
|
||||
// Prepare datasets in the specified order
|
||||
const datasets = categoryOrder
|
||||
.filter(category => this.data.chartData.series[category]) // Only include categories that exist
|
||||
.map(category => ({
|
||||
label: category.charAt(0).toUpperCase() + category.slice(1),
|
||||
data: this.data.chartData.series[category],
|
||||
borderColor: colors[category] || '#6b7280',
|
||||
backgroundColor: (colors[category] || '#6b7280') + '20',
|
||||
borderWidth: 2,
|
||||
fill: false,
|
||||
tension: 0.1,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 4,
|
||||
pointBackgroundColor: colors[category] || '#6b7280',
|
||||
pointBorderColor: '#1f2937',
|
||||
pointBorderWidth: 2
|
||||
}));
|
||||
|
||||
// Create chart
|
||||
new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: this.data.chartData.dates.map(date => {
|
||||
return new Date(date).toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
});
|
||||
}),
|
||||
datasets: datasets
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
labels: {
|
||||
color: '#d1d5db',
|
||||
usePointStyle: true,
|
||||
pointStyle: 'circle',
|
||||
padding: 20,
|
||||
font: {
|
||||
family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace",
|
||||
size: 11
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
backgroundColor: '#1f2937',
|
||||
titleColor: '#f3f4f6',
|
||||
bodyColor: '#d1d5db',
|
||||
borderColor: '#374151',
|
||||
borderWidth: 1,
|
||||
titleFont: {
|
||||
family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
|
||||
},
|
||||
bodyFont: {
|
||||
family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
|
||||
},
|
||||
filter: function(tooltipItem, data) {
|
||||
return true; // Show all items
|
||||
},
|
||||
itemSort: function(a, b) {
|
||||
// Sort tooltip items by download count (highest to lowest)
|
||||
return b.parsed.y - a.parsed.y;
|
||||
},
|
||||
callbacks: {
|
||||
title: function(context) {
|
||||
return `Date: ${context[0].label}`;
|
||||
},
|
||||
label: function(context) {
|
||||
return `${context.dataset.label}: ${context.parsed.y.toLocaleString()} downloads`;
|
||||
},
|
||||
labelColor: function(context) {
|
||||
// Use the same colors as defined for the chart lines
|
||||
const category = context.dataset.label.toLowerCase();
|
||||
const color = colors[category] || '#6b7280';
|
||||
return {
|
||||
borderColor: color,
|
||||
backgroundColor: color
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
color: '#374151',
|
||||
borderColor: '#4b5563'
|
||||
},
|
||||
ticks: {
|
||||
color: '#9ca3af',
|
||||
font: {
|
||||
family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace",
|
||||
size: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
y: {
|
||||
grid: {
|
||||
color: '#374151',
|
||||
borderColor: '#4b5563'
|
||||
},
|
||||
ticks: {
|
||||
color: '#9ca3af',
|
||||
font: {
|
||||
family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace",
|
||||
size: 10
|
||||
},
|
||||
callback: function(value) {
|
||||
return value.toLocaleString();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
intersect: false,
|
||||
mode: 'index'
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
renderTrendingItems() {
|
||||
const container = document.getElementById('trending-list');
|
||||
const items = this.getFilteredItems();
|
||||
|
||||
if (items.length === 0) {
|
||||
container.innerHTML = this.getEmptyState();
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = '';
|
||||
items.forEach((item, index) => {
|
||||
const itemElement = this.createTrendingItem(item, index + 1);
|
||||
container.appendChild(itemElement);
|
||||
});
|
||||
}
|
||||
|
||||
getFilteredItems() {
|
||||
let allItems = [];
|
||||
|
||||
if (this.currentType === '') {
|
||||
// If no filter, get items from "all" category if it exists,
|
||||
// otherwise combine all categories
|
||||
if (this.data.trending.all) {
|
||||
allItems = this.data.trending.all;
|
||||
} else {
|
||||
Object.values(this.data.trending).forEach(categoryItems => {
|
||||
if (Array.isArray(categoryItems)) {
|
||||
allItems = allItems.concat(categoryItems);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Get items from selected category
|
||||
allItems = this.data.trending[this.currentType] || [];
|
||||
}
|
||||
|
||||
// Sort by the current date range downloads (highest to lowest)
|
||||
allItems.sort((a, b) => {
|
||||
const aDownloads = this.getDownloadsForRange(a);
|
||||
const bDownloads = this.getDownloadsForRange(b);
|
||||
return bDownloads - aDownloads;
|
||||
});
|
||||
|
||||
// For "all" categories, limit to top 10
|
||||
if (this.currentType === '') {
|
||||
allItems = allItems.slice(0, 10);
|
||||
}
|
||||
|
||||
return allItems;
|
||||
}
|
||||
|
||||
createTrendingItem(item, rank) {
|
||||
const itemElement = document.createElement('div');
|
||||
itemElement.className = 'trending-item';
|
||||
|
||||
const category = item.category || 'general';
|
||||
const itemType = this.getItemType(item);
|
||||
const componentType = this.getComponentTypeLabel(item);
|
||||
const downloads = this.getDownloadsForRange(item);
|
||||
|
||||
itemElement.innerHTML = `
|
||||
<div class="trending-rank">
|
||||
<span class="rank-number">#${rank}</span>
|
||||
</div>
|
||||
|
||||
<div class="trending-content">
|
||||
<div class="trending-header">
|
||||
<div class="trending-title">
|
||||
<svg class="trending-icon" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="${this.getIconPath(itemType)}"/>
|
||||
</svg>
|
||||
<h3 class="trending-name">${item.name}</h3>
|
||||
<span class="trending-category">${category}</span>
|
||||
</div>
|
||||
<button class="install-button" onclick="showInstallModal('${item.id || item.name}')">
|
||||
<svg class="install-icon" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
|
||||
<path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
|
||||
</svg>
|
||||
Install
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="trending-metadata">
|
||||
<div class="trending-downloads">
|
||||
<svg class="meta-icon" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
|
||||
<path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
|
||||
</svg>
|
||||
<span>${downloads.toLocaleString()} downloads ${this.getRangeLabel()}</span>
|
||||
</div>
|
||||
<div class="trending-type-badge">${componentType}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return itemElement;
|
||||
}
|
||||
|
||||
getDownloadsForRange(item) {
|
||||
switch(this.currentRange) {
|
||||
case 'today':
|
||||
return item.downloadsToday || 0;
|
||||
case 'week':
|
||||
return item.downloadsWeek || 0;
|
||||
case 'month':
|
||||
return item.downloadsMonth || 0;
|
||||
default:
|
||||
return item.downloadsToday || 0;
|
||||
}
|
||||
}
|
||||
|
||||
getRangeLabel() {
|
||||
switch(this.currentRange) {
|
||||
case 'today':
|
||||
return 'today';
|
||||
case 'week':
|
||||
return 'this week';
|
||||
case 'month':
|
||||
return 'this month';
|
||||
default:
|
||||
return 'today';
|
||||
}
|
||||
}
|
||||
|
||||
getComponentTypeLabel(item) {
|
||||
// Determine the component type from the item ID or context
|
||||
const id = item.id || '';
|
||||
|
||||
if (id.includes('agent-') || id.startsWith('agent') || this.currentType === 'agents') {
|
||||
return 'Agent';
|
||||
} else if (id.includes('command-') || id.startsWith('command') || this.currentType === 'commands') {
|
||||
return 'Command';
|
||||
} else if (id.includes('setting-') || id.startsWith('setting') || this.currentType === 'settings') {
|
||||
return 'Settings';
|
||||
} else if (id.includes('hook-') || id.startsWith('hook') || this.currentType === 'hooks') {
|
||||
return 'Hooks';
|
||||
} else if (id.includes('mcp-') || id.startsWith('mcp') || this.currentType === 'mcps') {
|
||||
return 'MCP';
|
||||
} else if (id.includes('skill-') || id.startsWith('skill') || this.currentType === 'skills') {
|
||||
return 'Skill';
|
||||
} else if (id.includes('template-') || id.startsWith('template') || this.currentType === 'templates') {
|
||||
return 'Template';
|
||||
}
|
||||
|
||||
// Fallback: try to determine from data structure
|
||||
if (item.expertise) return 'Agent';
|
||||
if (item.command) return 'Command';
|
||||
|
||||
return 'Component';
|
||||
}
|
||||
|
||||
renderContributors(contributors) {
|
||||
if (!contributors || contributors.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const contributorElements = contributors.slice(0, 3).map(contributor =>
|
||||
`<img class="contributor-avatar" src="${contributor.avatar}" alt="${contributor.name}" title="${contributor.name}">`
|
||||
).join('');
|
||||
|
||||
return `
|
||||
<div class="meta-item">
|
||||
<span>Built by</span>
|
||||
<div class="contributors">
|
||||
${contributorElements}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
renderTags(tags) {
|
||||
if (!tags || tags.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const tagElements = tags.map(tag =>
|
||||
`<span class="tag">${tag}</span>`
|
||||
).join('');
|
||||
|
||||
return `<div class="tags">${tagElements}</div>`;
|
||||
}
|
||||
|
||||
getLanguageClass(language) {
|
||||
const languageMap = {
|
||||
'JavaScript/TypeScript': 'typescript',
|
||||
'JavaScript': 'javascript',
|
||||
'TypeScript': 'typescript',
|
||||
'Node.js/Express': 'nodejs',
|
||||
'Python': 'python',
|
||||
'SQL/Node.js': 'sql',
|
||||
'Docker': 'docker',
|
||||
'C#/Unity': 'csharp',
|
||||
'Unity/Game Development': 'unity',
|
||||
'Git/Bash': 'git',
|
||||
'Multi-language': 'javascript',
|
||||
'Universal': 'javascript'
|
||||
};
|
||||
|
||||
return languageMap[language] || 'javascript';
|
||||
}
|
||||
|
||||
getItemType(item) {
|
||||
// Determine item type based on data structure or properties
|
||||
if (item.expertise) return 'agents';
|
||||
if (item.command) return 'commands';
|
||||
if (item.type === 'settings') return 'settings';
|
||||
if (item.type === 'hooks') return 'hooks';
|
||||
if (item.type === 'mcps') return 'mcps';
|
||||
if (item.type === 'skills') return 'skills';
|
||||
if (item.type === 'templates') return 'templates';
|
||||
return 'commands'; // default
|
||||
}
|
||||
|
||||
getIconPath(type) {
|
||||
const icons = {
|
||||
commands: 'M8 4a4 4 0 1 1 0 8 4 4 0 0 1 0-8zM2.5 8a5.5 5.5 0 1 0 11 0 5.5 5.5 0 0 0-11 0z',
|
||||
agents: 'M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm7-3.25a.75.75 0 0 0-1.5 0v2.5h-2.5a.75.75 0 0 0 0 1.5h2.5v2.5a.75.75 0 0 0 1.5 0v-2.5h2.5a.75.75 0 0 0 0-1.5h-2.5v-2.5Z',
|
||||
settings: 'M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492zM5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0z',
|
||||
hooks: 'M1.5 1.5A.5.5 0 0 0 1 2v4.8a2.5 2.5 0 0 0 2.5 2.5h9.793l-3.347 3.346a.5.5 0 0 0 .708.708l4.2-4.2a.5.5 0 0 0 0-.708l-4.2-4.2a.5.5 0 0 0-.708.708L13.293 8.3H3.5A1.5 1.5 0 0 1 2 6.8V2a.5.5 0 0 0-.5-.5z',
|
||||
mcps: 'M2.5 3A1.5 1.5 0 0 0 1 4.5v.793c0 .026.009.051.025.072L2.5 7a.5.5 0 0 1 0 .708L1.025 9.133a.149.149 0 0 0-.025.072V10.5A1.5 1.5 0 0 0 2.5 12h2.793a.149.149 0 0 0 .072-.025L7 10.5a.5.5 0 0 1 .708 0l1.625 1.475c.021.016.046.025.072.025H12.5A1.5 1.5 0 0 0 14 10.5v-.793a.149.149 0 0 0-.025-.072L12.5 8a.5.5 0 0 1 0-.708l1.475-1.625a.149.149 0 0 0 .025-.072V4.5A1.5 1.5 0 0 0 12.5 3H9.707a.149.149 0 0 0-.072.025L8 4.5a.5.5 0 0 1-.708 0L5.867 3.025A.149.149 0 0 0 5.793 3H2.5z',
|
||||
skills: 'M6.5 1A1.5 1.5 0 0 0 5 2.5V3H1.5A1.5 1.5 0 0 0 0 4.5v8A1.5 1.5 0 0 0 1.5 14h13a1.5 1.5 0 0 0 1.5-1.5v-8A1.5 1.5 0 0 0 14.5 3H11v-.5A1.5 1.5 0 0 0 9.5 1h-3zm0 1h3a.5.5 0 0 1 .5.5V3H6v-.5a.5.5 0 0 1 .5-.5zm1.886 6.914L15 7.151V12.5a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5V7.15l6.614 1.764a1.5 1.5 0 0 0 .772 0zM1.5 4h13a.5.5 0 0 1 .5.5v1.616L8.129 7.948a.5.5 0 0 1-.258 0L1 6.116V4.5a.5.5 0 0 1 .5-.5z',
|
||||
templates: 'M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25V1.75zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25H1.75zM7.25 4a.75.75 0 0 1 1.5 0v4.25H12a.75.75 0 0 1 0 1.5H8.75V12a.75.75 0 0 1-1.5 0V9.75H4a.75.75 0 0 1 0-1.5h3.25V4z'
|
||||
};
|
||||
|
||||
return icons[type] || icons.commands;
|
||||
}
|
||||
|
||||
getEmptyState() {
|
||||
return `
|
||||
<div class="empty-state">
|
||||
<svg class="empty-state-icon" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.879-2.773l4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"/>
|
||||
</svg>
|
||||
<h3>No trending items found</h3>
|
||||
<p>Try adjusting your filters or check back later for new trending content.</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
const container = document.getElementById('trending-list');
|
||||
container.innerHTML = `
|
||||
<div class="empty-state">
|
||||
<svg class="empty-state-icon" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
|
||||
<path d="M7.002 11a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 4.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995z"/>
|
||||
</svg>
|
||||
<h3>Error Loading Data</h3>
|
||||
<p>${message}</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
showLoading() {
|
||||
const container = document.getElementById('trending-list');
|
||||
container.innerHTML = `
|
||||
<div class="loading">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>Loading trending components...</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the trending page when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
window.trendingPageInstance = new TrendingPage();
|
||||
});
|
||||
|
||||
// Modal functionality
|
||||
function showInstallModal(componentName) {
|
||||
const modal = document.getElementById('installModal');
|
||||
const commandText = document.getElementById('commandText');
|
||||
|
||||
// Determine the component type from the current filter or component name
|
||||
let componentType = 'command'; // default
|
||||
let componentCategory = '';
|
||||
|
||||
// Get the current trending page instance to check the type
|
||||
if (window.trendingPageInstance) {
|
||||
componentType = window.trendingPageInstance.currentType || 'commands';
|
||||
|
||||
// Find the component in the data to get its category
|
||||
const items = window.trendingPageInstance.data.trending[componentType] || [];
|
||||
const item = items.find(i => (i.id || i.name) === componentName);
|
||||
if (item && item.category) {
|
||||
componentCategory = item.category;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert plural to singular for the command flag
|
||||
const typeMap = {
|
||||
'agents': 'agent',
|
||||
'commands': 'command',
|
||||
'settings': 'setting',
|
||||
'hooks': 'hook',
|
||||
'mcps': 'mcp',
|
||||
'skills': 'skill',
|
||||
'templates': 'template'
|
||||
};
|
||||
|
||||
const flagType = typeMap[componentType] || 'command';
|
||||
|
||||
// Clean the component name by removing prefixes
|
||||
let cleanName = componentName;
|
||||
const prefixesToRemove = ['agent-', 'command-', 'setting-', 'hook-', 'mcp-', 'skill-', 'template-'];
|
||||
|
||||
for (const prefix of prefixesToRemove) {
|
||||
if (cleanName.startsWith(prefix)) {
|
||||
cleanName = cleanName.substring(prefix.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Build the full component path: category/name
|
||||
const componentPath = componentCategory ? `${componentCategory}/${cleanName}` : cleanName;
|
||||
|
||||
// Update the command with the correct flag and full component path
|
||||
const command = `npx claude-code-templates@latest --${flagType} ${componentPath} --yes`;
|
||||
commandText.textContent = command;
|
||||
|
||||
// Show the modal
|
||||
modal.classList.add('show');
|
||||
|
||||
// Prevent body scrolling
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeInstallModal() {
|
||||
const modal = document.getElementById('installModal');
|
||||
modal.classList.remove('show');
|
||||
|
||||
// Restore body scrolling
|
||||
document.body.style.overflow = '';
|
||||
|
||||
// Hide copy feedback
|
||||
const feedback = document.getElementById('copyFeedback');
|
||||
feedback.classList.remove('show');
|
||||
}
|
||||
|
||||
function copyInstallCommand() {
|
||||
const commandText = document.getElementById('commandText');
|
||||
const copyFeedback = document.getElementById('copyFeedback');
|
||||
const copyButton = document.querySelector('.copy-button');
|
||||
|
||||
// Copy to clipboard
|
||||
navigator.clipboard.writeText(commandText.textContent).then(() => {
|
||||
// Show success feedback
|
||||
copyFeedback.classList.add('show');
|
||||
|
||||
// Temporarily change button text
|
||||
const originalText = copyButton.innerHTML;
|
||||
copyButton.innerHTML = `
|
||||
<svg class="copy-icon" width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/>
|
||||
</svg>
|
||||
Copied!
|
||||
`;
|
||||
|
||||
// Reset after 2 seconds
|
||||
setTimeout(() => {
|
||||
copyButton.innerHTML = originalText;
|
||||
copyFeedback.classList.remove('show');
|
||||
}, 2000);
|
||||
}).catch(err => {
|
||||
console.error('Failed to copy: ', err);
|
||||
// Fallback for older browsers
|
||||
fallbackCopyTextToClipboard(commandText.textContent);
|
||||
});
|
||||
}
|
||||
|
||||
function fallbackCopyTextToClipboard(text) {
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.value = text;
|
||||
textArea.style.position = "fixed";
|
||||
textArea.style.top = "0";
|
||||
textArea.style.left = "0";
|
||||
textArea.style.width = "2em";
|
||||
textArea.style.height = "2em";
|
||||
textArea.style.padding = "0";
|
||||
textArea.style.border = "none";
|
||||
textArea.style.outline = "none";
|
||||
textArea.style.boxShadow = "none";
|
||||
textArea.style.background = "transparent";
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
const copyFeedback = document.getElementById('copyFeedback');
|
||||
copyFeedback.classList.add('show');
|
||||
setTimeout(() => {
|
||||
copyFeedback.classList.remove('show');
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error('Fallback: Could not copy text: ', err);
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
|
||||
// Close modal on Escape key
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
closeInstallModal();
|
||||
}
|
||||
});
|
||||
|
||||
// Add some utility functions for future enhancements
|
||||
window.TrendingUtils = {
|
||||
formatDate(dateString) {
|
||||
return new Date(dateString).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
});
|
||||
},
|
||||
|
||||
formatNumber(num) {
|
||||
if (num >= 1000000) {
|
||||
return (num / 1000000).toFixed(1) + 'M';
|
||||
}
|
||||
if (num >= 1000) {
|
||||
return (num / 1000).toFixed(1) + 'k';
|
||||
}
|
||||
return num.toString();
|
||||
},
|
||||
|
||||
debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
// Shared Utility Functions
|
||||
|
||||
/**
|
||||
* Escape HTML special characters to prevent XSS when injecting into innerHTML.
|
||||
* Use this for any user-controlled or external data before inserting into HTML.
|
||||
*/
|
||||
function escapeHTML(str) {
|
||||
if (typeof str !== 'string') return '';
|
||||
return str
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function copyToClipboard(text, message = 'Command copied to clipboard!') {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
showNotification(message, 'success');
|
||||
}).catch(err => {
|
||||
console.error('Failed to copy: ', err);
|
||||
showNotification('Failed to copy command', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
function showNotification(message, type = 'info') {
|
||||
// Remove any existing notifications first
|
||||
const existingNotifications = document.querySelectorAll('.notification');
|
||||
existingNotifications.forEach(notif => {
|
||||
if (notif.parentNode) {
|
||||
notif.parentNode.removeChild(notif);
|
||||
}
|
||||
});
|
||||
|
||||
const notification = document.createElement('div');
|
||||
notification.textContent = message;
|
||||
notification.className = `notification notification-${type}`;
|
||||
notification.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: ${type === 'success' ? '#10b981' : type === 'error' ? '#ef4444' : '#3b82f6'};
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
border-radius: 6px;
|
||||
z-index: 10000;
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
animation: slideIn 0.3s ease;
|
||||
`;
|
||||
|
||||
document.body.appendChild(notification);
|
||||
setTimeout(() => {
|
||||
if (notification.parentNode) {
|
||||
notification.parentNode.removeChild(notification);
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
@@ -0,0 +1,620 @@
|
||||
// Workflow Builder JavaScript
|
||||
|
||||
// Global state
|
||||
let workflowState = {
|
||||
steps: [],
|
||||
properties: {
|
||||
name: '',
|
||||
description: '',
|
||||
tags: []
|
||||
},
|
||||
components: {
|
||||
agents: [],
|
||||
commands: [],
|
||||
mcps: []
|
||||
}
|
||||
};
|
||||
|
||||
// GitHub API configuration
|
||||
const GITHUB_CONFIG = {
|
||||
owner: 'davila7',
|
||||
repo: 'claude-code-templates',
|
||||
branch: 'main'
|
||||
};
|
||||
|
||||
// Initialize the workflow builder
|
||||
document.addEventListener('DOMContentLoaded', async function() {
|
||||
showLoadingSpinner();
|
||||
|
||||
try {
|
||||
await loadAllComponents();
|
||||
initializeDragAndDrop();
|
||||
initializeEventListeners();
|
||||
initializeModalEventListeners();
|
||||
initializeSortableSteps();
|
||||
} catch (error) {
|
||||
console.error('Error initializing Workflow Builder:', error);
|
||||
showError('Failed to load components. Please refresh the page.');
|
||||
} finally {
|
||||
hideLoadingSpinner();
|
||||
}
|
||||
});
|
||||
|
||||
// Load all components from GitHub using the Git Trees API
|
||||
async function loadAllComponents() {
|
||||
try {
|
||||
const response = await fetch('components.json');
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const allComponents = await response.json();
|
||||
|
||||
workflowState.components = {
|
||||
agents: allComponents.agents.sort((a, b) => a.path.localeCompare(b.path)),
|
||||
commands: allComponents.commands.sort((a, b) => a.path.localeCompare(b.path)),
|
||||
mcps: allComponents.mcps.sort((a, b) => a.path.localeCompare(b.path))
|
||||
};
|
||||
|
||||
renderComponentsList('agents', workflowState.components.agents);
|
||||
renderComponentsList('commands', workflowState.components.commands);
|
||||
renderComponentsList('mcps', workflowState.components.mcps);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading components:', error);
|
||||
loadComponentsWithFallback();
|
||||
}
|
||||
}
|
||||
|
||||
async function loadComponentsWithFallback() {
|
||||
const [agents, commands, mcps] = [
|
||||
getFallbackComponentData('agents'),
|
||||
getFallbackComponentData('commands'),
|
||||
getFallbackComponentData('mcps')
|
||||
];
|
||||
workflowState.components = { agents, commands, mcps };
|
||||
renderComponentsList('agents', agents);
|
||||
renderComponentsList('commands', commands);
|
||||
renderComponentsList('mcps', mcps);
|
||||
}
|
||||
|
||||
// Get fallback component data
|
||||
function getFallbackComponentData(type) {
|
||||
const fallbackData = {
|
||||
agents: [
|
||||
{ name: 'code-reviewer', path: 'development/code-reviewer', category: 'development', type: 'agent', icon: '🤖' },
|
||||
{ name: 'documentation-writer', path: 'development/documentation-writer', category: 'development', type: 'agent', icon: '🤖' },
|
||||
{ name: 'bug-hunter', path: 'development/bug-hunter', category: 'development', type: 'agent', icon: '🤖' },
|
||||
{ name: 'security-auditor', path: 'security/security-auditor', category: 'security', type: 'agent', icon: '🤖' },
|
||||
{ name: 'performance-optimizer', path: 'optimization/performance-optimizer', category: 'optimization', type: 'agent', icon: '🤖' }
|
||||
],
|
||||
commands: [
|
||||
{ name: 'git-setup', path: 'development/git-setup', category: 'development', type: 'command', icon: '⚡' },
|
||||
{ name: 'project-init', path: 'development/project-init', category: 'development', type: 'command', icon: '⚡' },
|
||||
{ name: 'docker-setup', path: 'devops/docker-setup', category: 'devops', type: 'command', icon: '⚡' },
|
||||
{ name: 'test-runner', path: 'testing/test-runner', category: 'testing', type: 'command', icon: '⚡' },
|
||||
{ name: 'build-pipeline', path: 'devops/build-pipeline', category: 'devops', type: 'command', icon: '⚡' }
|
||||
],
|
||||
mcps: [
|
||||
{ name: 'database-connector', path: 'database/database-connector', category: 'database', type: 'mcp', icon: '🔌' },
|
||||
{ name: 'api-client', path: 'api/api-client', category: 'api', type: 'mcp', icon: '🔌' },
|
||||
{ name: 'file-manager', path: 'system/file-manager', category: 'system', type: 'mcp', icon: '🔌' },
|
||||
{ name: 'redis-cache', path: 'database/redis-cache', category: 'database', type: 'mcp', icon: '🔌' },
|
||||
{ name: 'email-service', path: 'communication/email-service', category: 'communication', type: 'mcp', icon: '🔌' }
|
||||
]
|
||||
};
|
||||
|
||||
const typeKey = type;
|
||||
const data = fallbackData[typeKey] || [];
|
||||
return data;
|
||||
}
|
||||
|
||||
// Get icon for component type
|
||||
function getComponentIcon(type) {
|
||||
const icons = { 'agent': '🤖', 'command': '⚡', 'mcp': '🔌' };
|
||||
return icons[type] || '📦';
|
||||
}
|
||||
|
||||
// Render components list in the UI
|
||||
function renderComponentsList(type, components) {
|
||||
const container = document.getElementById(`${type}-tree`);
|
||||
const countElement = document.getElementById(`${type}-count`);
|
||||
|
||||
if (!container || !countElement) return;
|
||||
|
||||
countElement.textContent = components.length;
|
||||
container.innerHTML = '';
|
||||
|
||||
const groupedComponents = components.reduce((acc, component) => {
|
||||
const category = component.category === 'root' ? 'general' : component.category;
|
||||
if (!acc[category]) acc[category] = [];
|
||||
acc[category].push(component);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
Object.entries(groupedComponents).forEach(([category, categoryComponents]) => {
|
||||
const folderElement = createTreeFolder(category, categoryComponents, type);
|
||||
container.appendChild(folderElement);
|
||||
});
|
||||
}
|
||||
|
||||
// Create tree folder element
|
||||
function createTreeFolder(category, components, type) {
|
||||
const folderElement = document.createElement('div');
|
||||
folderElement.className = 'tree-node';
|
||||
const folderId = `${type}-${category.replace(/[^a-z0-9]/gi, '_').toLowerCase()}`;
|
||||
|
||||
folderElement.innerHTML = `
|
||||
<div class="tree-node-header" data-folder="${folderId}">
|
||||
<span class="tree-icon folder-icon">📁</span>
|
||||
<span class="tree-label">${category}</span>
|
||||
<span class="tree-count">${components.length}</span>
|
||||
<span class="tree-arrow">▶</span>
|
||||
</div>
|
||||
<div class="tree-children" id="${folderId}"></div>
|
||||
`;
|
||||
|
||||
const childrenContainer = folderElement.querySelector('.tree-children');
|
||||
components.forEach(component => {
|
||||
const fileElement = createTreeFile(component);
|
||||
childrenContainer.appendChild(fileElement);
|
||||
});
|
||||
|
||||
return folderElement;
|
||||
}
|
||||
|
||||
// Create tree file element
|
||||
function createTreeFile(component) {
|
||||
const element = document.createElement('div');
|
||||
element.className = 'tree-file';
|
||||
element.draggable = true;
|
||||
element.dataset.componentType = component.type;
|
||||
element.dataset.componentName = component.name;
|
||||
element.dataset.componentPath = component.path;
|
||||
element.dataset.componentCategory = component.category;
|
||||
|
||||
element.innerHTML = `
|
||||
<div class="tree-file-header">
|
||||
<span class="tree-icon file-icon">${getFileIcon(component.type)}</span>
|
||||
<span class="tree-file-name">${component.name}</span>
|
||||
<div class="tree-file-actions">
|
||||
<button class="tree-action-btn" title="View Details" onclick="showComponentDetails('${component.type}', '${component.name}', '${component.path}', '${component.category}')">ℹ️</button>
|
||||
<button class="tree-action-btn" title="Add to Workflow" onclick="addComponentFromButton(event)">➕</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
return element;
|
||||
}
|
||||
|
||||
// Get file icon based on type
|
||||
function getFileIcon(type) {
|
||||
const icons = { 'agent': '📄', 'command': '⚡', 'mcp': '⚙️' };
|
||||
return icons[type] || '📄';
|
||||
}
|
||||
|
||||
// Initialize drag and drop
|
||||
function initializeDragAndDrop() {
|
||||
const workflowSteps = document.getElementById('workflowSteps');
|
||||
document.addEventListener('dragstart', (event) => {
|
||||
if (event.target.closest('.tree-file')) {
|
||||
handleDragStart(event);
|
||||
}
|
||||
});
|
||||
workflowSteps.addEventListener('dragover', handleDragOver);
|
||||
workflowSteps.addEventListener('drop', handleDrop);
|
||||
workflowSteps.addEventListener('dragenter', (e) => e.target.closest('.drop-zone')?.classList.add('drag-over'));
|
||||
workflowSteps.addEventListener('dragleave', (e) => e.target.closest('.drop-zone')?.classList.remove('drag-over'));
|
||||
}
|
||||
|
||||
function handleDragStart(event) {
|
||||
const treeFile = event.target.closest('.tree-file');
|
||||
const componentData = { ...treeFile.dataset };
|
||||
event.dataTransfer.setData('application/json', JSON.stringify(componentData));
|
||||
event.dataTransfer.effectAllowed = 'copy';
|
||||
}
|
||||
|
||||
function handleDragOver(event) {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
|
||||
function handleDrop(event) {
|
||||
event.preventDefault();
|
||||
event.target.closest('.drop-zone')?.classList.remove('drag-over');
|
||||
try {
|
||||
const componentData = JSON.parse(event.dataTransfer.getData('application/json'));
|
||||
addWorkflowStep(componentData);
|
||||
} catch (error) {
|
||||
console.error('Error handling drop:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function addComponentFromButton(event) {
|
||||
const treeFile = event.target.closest('.tree-file');
|
||||
const componentData = { ...treeFile.dataset };
|
||||
addWorkflowStep(componentData);
|
||||
}
|
||||
|
||||
function addWorkflowStep(componentData) {
|
||||
const step = {
|
||||
id: `step_${Date.now()}`,
|
||||
type: componentData.componentType,
|
||||
name: componentData.componentName,
|
||||
path: componentData.componentPath,
|
||||
category: componentData.componentCategory,
|
||||
description: `Execute ${componentData.componentName}`,
|
||||
icon: getComponentIcon(componentData.componentType)
|
||||
};
|
||||
workflowState.steps.push(step);
|
||||
renderWorkflowSteps();
|
||||
updateWorkflowStats();
|
||||
}
|
||||
|
||||
function renderWorkflowSteps() {
|
||||
const container = document.getElementById('workflowSteps');
|
||||
const dropZone = container.querySelector('.drop-zone');
|
||||
const existingSteps = container.querySelectorAll('.workflow-step');
|
||||
existingSteps.forEach(step => step.remove());
|
||||
|
||||
if (workflowState.steps.length > 0) {
|
||||
dropZone.style.display = 'none';
|
||||
workflowState.steps.forEach((step, index) => {
|
||||
const stepElement = document.createElement('div');
|
||||
stepElement.className = 'workflow-step';
|
||||
stepElement.dataset.stepId = step.id;
|
||||
stepElement.innerHTML = `
|
||||
<div class="step-number">${index + 1}</div>
|
||||
<div class="step-icon">${step.icon}</div>
|
||||
<div class="step-content">
|
||||
<div class="step-name">${step.name}</div>
|
||||
<div class="step-type">${step.type}</div>
|
||||
</div>
|
||||
<div class="step-actions">
|
||||
<button class="step-action details" onclick="showComponentDetails('${step.type}', '${step.name}', '${step.path}', '${step.category}')">ℹ️</button>
|
||||
<button class="step-action remove" onclick="removeStep('${step.id}')">🗑️</button>
|
||||
</div>
|
||||
`;
|
||||
container.appendChild(stepElement);
|
||||
});
|
||||
} else {
|
||||
dropZone.style.display = 'flex';
|
||||
}
|
||||
initializeSortableSteps();
|
||||
}
|
||||
|
||||
function initializeSortableSteps() {
|
||||
const workflowSteps = document.getElementById('workflowSteps');
|
||||
if (workflowState.steps.length > 0) {
|
||||
new Sortable(workflowSteps, {
|
||||
animation: 150,
|
||||
onEnd: (evt) => {
|
||||
const movedStep = workflowState.steps.splice(evt.oldIndex, 1)[0];
|
||||
workflowState.steps.splice(evt.newIndex, 0, movedStep);
|
||||
renderWorkflowSteps();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function removeStep(stepId) {
|
||||
workflowState.steps = workflowState.steps.filter(step => step.id !== stepId);
|
||||
renderWorkflowSteps();
|
||||
updateWorkflowStats();
|
||||
}
|
||||
|
||||
function updateWorkflowStats() {
|
||||
const stats = { agents: 0, commands: 0, mcps: 0 };
|
||||
workflowState.steps.forEach(step => stats[step.type + 's']++);
|
||||
document.getElementById('agentCount').textContent = stats.agents;
|
||||
document.getElementById('commandCount').textContent = stats.commands;
|
||||
document.getElementById('mcpCount').textContent = stats.mcps;
|
||||
document.getElementById('totalSteps').textContent = workflowState.steps.length;
|
||||
}
|
||||
|
||||
function initializeEventListeners() {
|
||||
document.getElementById('componentSearch').addEventListener('input', handleComponentSearch);
|
||||
document.getElementById('clearCanvas').addEventListener('click', clearWorkflow);
|
||||
document.getElementById('generateWorkflow').addEventListener('click', openPropertiesModal);
|
||||
document.getElementById('closePropertiesModal').addEventListener('click', closePropertiesModal);
|
||||
document.getElementById('saveWorkflowProperties').addEventListener('click', saveAndGenerateWorkflow);
|
||||
|
||||
// Category accordion toggle
|
||||
document.querySelectorAll('.category-card-header').forEach(header => {
|
||||
header.addEventListener('click', () => {
|
||||
header.parentElement.classList.toggle('expanded');
|
||||
});
|
||||
});
|
||||
|
||||
// Add event listener for sub-folders
|
||||
document.addEventListener('click', function(event) {
|
||||
const header = event.target.closest('.tree-node-header');
|
||||
if (header) {
|
||||
const children = header.nextElementSibling;
|
||||
if (children && children.classList.contains('tree-children')) {
|
||||
header.classList.toggle('expanded');
|
||||
children.classList.toggle('expanded');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleComponentSearch(event) {
|
||||
const searchTerm = event.target.value.toLowerCase();
|
||||
document.querySelectorAll('.tree-file').forEach(file => {
|
||||
const match = file.dataset.componentName.toLowerCase().includes(searchTerm);
|
||||
file.style.display = match ? '' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
function clearWorkflow() {
|
||||
if (confirm('Are you sure you want to clear the workflow?')) {
|
||||
workflowState.steps = [];
|
||||
renderWorkflowSteps();
|
||||
updateWorkflowStats();
|
||||
}
|
||||
}
|
||||
|
||||
function openPropertiesModal() {
|
||||
if (workflowState.steps.length === 0) {
|
||||
showError('Add at least one component to the workflow.');
|
||||
return;
|
||||
}
|
||||
document.getElementById('propertiesModal').style.display = 'block';
|
||||
}
|
||||
|
||||
function closePropertiesModal() {
|
||||
document.getElementById('propertiesModal').style.display = 'none';
|
||||
}
|
||||
|
||||
function saveAndGenerateWorkflow() {
|
||||
workflowState.properties.name = document.getElementById('workflowName').value;
|
||||
workflowState.properties.description = document.getElementById('workflowDescription').value;
|
||||
workflowState.properties.tags = document.getElementById('workflowTags').value.split(',').map(t => t.trim());
|
||||
|
||||
if (!workflowState.properties.name) {
|
||||
showError('Workflow name is required.');
|
||||
return;
|
||||
}
|
||||
|
||||
closePropertiesModal();
|
||||
generateWorkflow();
|
||||
}
|
||||
|
||||
async function generateWorkflow() {
|
||||
showLoadingSpinner();
|
||||
try {
|
||||
const workflowHash = await generateWorkflowHash();
|
||||
const yamlContent = generateWorkflowYAML();
|
||||
showGenerateModal(workflowHash, yamlContent);
|
||||
} catch (error) {
|
||||
console.error('Error generating workflow:', error);
|
||||
} finally {
|
||||
hideLoadingSpinner();
|
||||
}
|
||||
}
|
||||
|
||||
async function generateWorkflowHash() {
|
||||
const dataString = JSON.stringify(workflowState);
|
||||
const hash = CryptoJS.SHA256(dataString).toString(CryptoJS.enc.Hex).substring(0, 12);
|
||||
localStorage.setItem(`workflow_${hash}`, dataString);
|
||||
return hash;
|
||||
}
|
||||
|
||||
function generateWorkflowYAML() {
|
||||
return `# Workflow: ${workflowState.properties.name}\n` +
|
||||
`steps:\n` +
|
||||
workflowState.steps.map((step, i) => ` - step: ${i+1}\n type: ${step.type}\n name: "${step.name}"`).join('\n');
|
||||
}
|
||||
|
||||
function showGenerateModal(hash, yaml) {
|
||||
document.getElementById('workflowCommand').textContent = `npx claude-code-templates@latest --workflow:#${hash}`;
|
||||
document.getElementById('yamlContent').textContent = yaml;
|
||||
document.getElementById('generateModal').style.display = 'block';
|
||||
}
|
||||
|
||||
function showLoadingSpinner() { document.getElementById('loadingSpinner').style.display = 'flex'; }
|
||||
function hideLoadingSpinner() { document.getElementById('loadingSpinner').style.display = 'none'; }
|
||||
function showError(msg) { alert(msg); }
|
||||
|
||||
async function showComponentDetails(type, name, path, category) {
|
||||
const component = workflowState.components[type + 's'].find(c => c.name === name);
|
||||
if (!component) {
|
||||
console.warn('Component not found:', type, name);
|
||||
return;
|
||||
}
|
||||
|
||||
showComponentModal(component);
|
||||
}
|
||||
|
||||
// Show detailed component modal (recreated from original version)
|
||||
function showComponentModal(component) {
|
||||
const typeConfig = {
|
||||
agent: { icon: '🤖', color: '#ff6b6b', label: 'AGENT' },
|
||||
command: { icon: '⚡', color: '#4ecdc4', label: 'COMMAND' },
|
||||
mcp: { icon: '🔌', color: '#45b7d1', label: 'MCP' }
|
||||
};
|
||||
|
||||
const config = typeConfig[component.type];
|
||||
const installCommand = generateInstallCommand(component);
|
||||
|
||||
const modalHTML = `
|
||||
<div class="modal-overlay" onclick="closeComponentModal()">
|
||||
<div class="modal-content component-modal" onclick="event.stopPropagation()">
|
||||
<div class="modal-header">
|
||||
<div class="component-modal-title">
|
||||
<span class="component-icon" style="color: ${config.color}">${config.icon}</span>
|
||||
<h3>${formatComponentName(component.name)}</h3>
|
||||
<span class="component-type-badge" style="background: ${config.color}">${config.label}</span>
|
||||
</div>
|
||||
<button class="modal-close" onclick="closeComponentModal()">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="component-details">
|
||||
<div class="component-description">
|
||||
${getComponentDescription(component)}
|
||||
</div>
|
||||
|
||||
<div class="installation-section">
|
||||
<h4>📦 Installation</h4>
|
||||
<div class="command-line">
|
||||
<code>${installCommand}</code>
|
||||
<button class="copy-btn" onclick="copyToClipboard('${installCommand}')">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="component-content">
|
||||
<h4>📋 Component Details</h4>
|
||||
<div class="component-preview">
|
||||
<pre><code>${component.content ? component.content.substring(0, 500) + (component.content.length > 500 ? '...' : '') : 'No content available.'}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-actions">
|
||||
<button class="github-folder-link" onclick="viewOnGitHub('${component.path}')">
|
||||
<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.23 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>
|
||||
View on GitHub
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Remove existing modal if present
|
||||
const existingModal = document.querySelector('.modal-overlay');
|
||||
if (existingModal) {
|
||||
existingModal.remove();
|
||||
}
|
||||
|
||||
// Add modal to body
|
||||
document.body.insertAdjacentHTML('beforeend', modalHTML);
|
||||
}
|
||||
|
||||
// Helper functions for the modal
|
||||
function formatComponentName(name) {
|
||||
return name.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
|
||||
}
|
||||
|
||||
function generateInstallCommand(component) {
|
||||
return `npx claude-code-templates@latest --${component.type} "${component.path}"`;
|
||||
}
|
||||
|
||||
function getComponentDescription(component) {
|
||||
if (!component.content) {
|
||||
return 'No description available.';
|
||||
}
|
||||
|
||||
// Try to extract description from frontmatter
|
||||
const descMatch = component.content.match(/description:\s*(.+?)(?:\n|$)/);
|
||||
if (descMatch) {
|
||||
return descMatch[1].trim().replace(/^["']|["']$/g, '');
|
||||
}
|
||||
|
||||
// Use first paragraph if no frontmatter description
|
||||
const lines = component.content.split('\n');
|
||||
const firstParagraph = lines.find(line => line.trim() && !line.startsWith('---') && !line.startsWith('#'));
|
||||
if (firstParagraph) {
|
||||
return firstParagraph.trim();
|
||||
}
|
||||
|
||||
return 'No description available.';
|
||||
}
|
||||
|
||||
function viewOnGitHub(path) {
|
||||
const url = `https://github.com/davila7/claude-code-templates/tree/main/${path}`;
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
|
||||
function copyToClipboard(text) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
showSuccess('Command copied to clipboard!');
|
||||
}).catch(err => {
|
||||
console.error('Failed to copy: ', err);
|
||||
});
|
||||
}
|
||||
|
||||
function closeComponentModal() {
|
||||
const modalOverlay = document.querySelector('.modal-overlay');
|
||||
if (modalOverlay) {
|
||||
modalOverlay.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function initializeModalEventListeners() {
|
||||
// Component modal event listeners
|
||||
document.getElementById('closeComponentModal').addEventListener('click', closeComponentModal);
|
||||
document.getElementById('closeComponentModalBtn').addEventListener('click', closeComponentModal);
|
||||
document.getElementById('addComponentToWorkflow').addEventListener('click', () => {
|
||||
const componentData = JSON.parse(document.getElementById('componentModal').dataset.currentComponent);
|
||||
addWorkflowStep({
|
||||
componentType: componentData.type,
|
||||
componentName: componentData.name,
|
||||
componentPath: componentData.path,
|
||||
componentCategory: componentData.category
|
||||
});
|
||||
closeComponentModal();
|
||||
});
|
||||
document.getElementById('copyUsageCommand').addEventListener('click', () => {
|
||||
const command = document.getElementById('componentModalUsage').textContent;
|
||||
navigator.clipboard.writeText(command).then(() => showSuccess('Command copied!'));
|
||||
});
|
||||
|
||||
// Generate modal event listeners
|
||||
const closeModal = document.getElementById('closeModal');
|
||||
if (closeModal) {
|
||||
closeModal.addEventListener('click', () => {
|
||||
document.getElementById('generateModal').style.display = 'none';
|
||||
});
|
||||
}
|
||||
|
||||
const copyCommand = document.getElementById('copyCommand');
|
||||
if (copyCommand) {
|
||||
copyCommand.addEventListener('click', () => {
|
||||
const command = document.getElementById('workflowCommand').textContent;
|
||||
navigator.clipboard.writeText(command).then(() => showSuccess('Command copied!'));
|
||||
});
|
||||
}
|
||||
|
||||
const copyYaml = document.getElementById('copyYaml');
|
||||
if (copyYaml) {
|
||||
copyYaml.addEventListener('click', () => {
|
||||
const yaml = document.getElementById('yamlContent').textContent;
|
||||
navigator.clipboard.writeText(yaml).then(() => showSuccess('YAML copied!'));
|
||||
});
|
||||
}
|
||||
|
||||
// Close modals when clicking outside
|
||||
window.addEventListener('click', (event) => {
|
||||
if (event.target.classList.contains('modal')) {
|
||||
event.target.style.display = 'none';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showSuccess(message) {
|
||||
// Simple success notification
|
||||
const notification = document.createElement('div');
|
||||
notification.textContent = message;
|
||||
notification.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: #10b981;
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
border-radius: 6px;
|
||||
z-index: 10000;
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
`;
|
||||
document.body.appendChild(notification);
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(notification);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// Add functions to window object for inline event handlers
|
||||
window.removeStep = removeStep;
|
||||
window.showComponentDetails = showComponentDetails;
|
||||
window.addComponentFromButton = addComponentFromButton;
|
||||
@@ -0,0 +1,289 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Plugin Details - Claude Code Templates</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="Plugin details for Claude Code Templates.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://aitmpl.com/plugin.html">
|
||||
<meta property="og:title" content="Plugin Details - Claude Code Templates">
|
||||
<meta property="og:description" content="Plugin details for Claude Code Templates.">
|
||||
<meta property="og:image" content="https://aitmpl.com/images/social-preview.png">
|
||||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="630">
|
||||
<meta property="og:site_name" content="Claude Code Templates">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Plugin Details - Claude Code Templates">
|
||||
<meta name="twitter:description" content="Plugin details for Claude Code Templates.">
|
||||
<meta name="twitter:image" content="https://aitmpl.com/images/social-preview.png">
|
||||
|
||||
<link rel="stylesheet" href="/css/styles.css">
|
||||
<link rel="stylesheet" href="/css/plugin-page.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">
|
||||
</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 class="terminal-subtitle">
|
||||
<span class="status-dot"></span>
|
||||
Ready-to-use configurations for your <strong>Claude Code</strong> projects
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<a href="https://www.anthropic.com/claude-code?ref=aitmpl.com" target="_blank" class="header-btn anthropic-btn" title="Learn about Anthropic's Claude Code">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
|
||||
</svg>
|
||||
Claude Code
|
||||
</a>
|
||||
<a href="/blog/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.23 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="plugin-detail-page">
|
||||
<!-- Back Navigation -->
|
||||
<div class="back-navigation">
|
||||
<div class="container">
|
||||
<a href="/plugins" class="back-link">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.42-1.41L7.83 13H20v-2z"/>
|
||||
</svg>
|
||||
Back to Plugins
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<!-- Loading State -->
|
||||
<div id="loading" class="loading-state">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>Loading plugin details...</p>
|
||||
</div>
|
||||
|
||||
<!-- Error State -->
|
||||
<div id="error" class="error-state" style="display: none;">
|
||||
<div class="error-icon">⚠️</div>
|
||||
<h2>Plugin Not Found</h2>
|
||||
<p>The plugin you're looking for doesn't exist or has been removed.</p>
|
||||
<a href="/plugins" class="back-button">Browse All Plugins</a>
|
||||
</div>
|
||||
|
||||
<!-- Plugin Content -->
|
||||
<div id="pluginContent" class="plugin-content" style="display: none;">
|
||||
<!-- Hero Section -->
|
||||
<section class="plugin-hero">
|
||||
<div class="plugin-icon">
|
||||
<span class="component-icon">🧩</span>
|
||||
</div>
|
||||
<div class="plugin-header-info">
|
||||
<div class="plugin-meta">
|
||||
<span class="plugin-version" id="pluginVersion">v1.0.0</span>
|
||||
<span class="plugin-author" id="pluginAuthor">Author</span>
|
||||
</div>
|
||||
<h1 id="pluginName">Plugin Name</h1>
|
||||
<p class="plugin-description" id="pluginDescription">Plugin description goes here</p>
|
||||
<div class="plugin-keywords" id="pluginKeywords"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Installation Section -->
|
||||
<section class="installation-section">
|
||||
<h2>Installation</h2>
|
||||
<div class="installation-steps">
|
||||
<div class="step">
|
||||
<div class="step-number">1</div>
|
||||
<div class="step-content">
|
||||
<h3>Add Marketplace</h3>
|
||||
<div class="command-box">
|
||||
<code id="addMarketplaceCmd">/plugin marketplace add https://github.com/davila7/claude-code-templates</code>
|
||||
<button class="copy-btn" onclick="copyCommand('addMarketplaceCmd', event)">
|
||||
<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
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">2</div>
|
||||
<div class="step-content">
|
||||
<h3>Install Plugin</h3>
|
||||
<div class="command-box">
|
||||
<code id="installPluginCmd">/plugin install plugin-name</code>
|
||||
<button class="copy-btn" onclick="copyCommand('installPluginCmd', event)">
|
||||
<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
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Components Section -->
|
||||
<section class="components-section">
|
||||
<h2>Included Components</h2>
|
||||
<div class="components-grid">
|
||||
<!-- Commands -->
|
||||
<div class="component-category" id="commandsSection" style="display: none;">
|
||||
<div class="category-header">
|
||||
<span class="category-icon">⚡</span>
|
||||
<h3>Commands (<span id="commandsCount">0</span>)</h3>
|
||||
</div>
|
||||
<ul class="component-list" id="commandsList"></ul>
|
||||
</div>
|
||||
|
||||
<!-- Agents -->
|
||||
<div class="component-category" id="agentsSection" style="display: none;">
|
||||
<div class="category-header">
|
||||
<span class="category-icon">🤖</span>
|
||||
<h3>Agents (<span id="agentsCount">0</span>)</h3>
|
||||
</div>
|
||||
<ul class="component-list" id="agentsList"></ul>
|
||||
</div>
|
||||
|
||||
<!-- MCPs -->
|
||||
<div class="component-category" id="mcpsSection" style="display: none;">
|
||||
<div class="category-header">
|
||||
<span class="category-icon">🔌</span>
|
||||
<h3>MCP Servers (<span id="mcpsCount">0</span>)</h3>
|
||||
</div>
|
||||
<ul class="component-list" id="mcpsList"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Related Plugins -->
|
||||
<section class="related-section">
|
||||
<h2>Related Plugins</h2>
|
||||
<div class="related-plugins" id="relatedPlugins"></div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</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">© 2025 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>
|
||||
|
||||
<!-- Install Command Modal -->
|
||||
<div id="installModal" class="modal" style="display: none;">
|
||||
<div class="modal-overlay" onclick="window.pluginPageManager.closeInstallModal()"></div>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3>Installation Command</h3>
|
||||
<button class="modal-close" onclick="window.pluginPageManager.closeInstallModal()">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="modal-description">Copy and run this command in your terminal to install this component:</p>
|
||||
<div class="modal-command-box">
|
||||
<code id="modalCommand"></code>
|
||||
<button class="modal-copy-btn" onclick="window.pluginPageManager.copyModalCommand()">
|
||||
<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 Command
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/utils.js"></script>
|
||||
<script src="/js/plugin-page.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,29 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
# Filter pages - high priority for crawling
|
||||
Allow: /agents
|
||||
Allow: /commands
|
||||
Allow: /settings
|
||||
Allow: /hooks
|
||||
Allow: /mcps
|
||||
Allow: /templates
|
||||
Allow: /skills
|
||||
|
||||
# Blog content
|
||||
Allow: /blog/
|
||||
|
||||
# Component pages
|
||||
Allow: /component/
|
||||
|
||||
# Important pages
|
||||
Allow: /trending.html
|
||||
|
||||
# Disallow temporary or admin files
|
||||
Disallow: /node_modules/
|
||||
Disallow: /.git/
|
||||
Disallow: /.vercel/
|
||||
Disallow: /dev-server.js
|
||||
|
||||
# Sitemap location (fixed: was pointing to GitHub Pages instead of aitmpl.com)
|
||||
Sitemap: https://aitmpl.com/sitemap.xml
|
||||
@@ -0,0 +1,180 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<!-- Main pages -->
|
||||
<url>
|
||||
<loc>https://aitmpl.com</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<!-- Filter pages -->
|
||||
<url>
|
||||
<loc>https://aitmpl.com/skills</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/agents</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/commands</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/settings</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/hooks</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/mcps</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/templates</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<!-- Blog pages -->
|
||||
<url>
|
||||
<loc>https://aitmpl.com/blog</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/blog/e2b-claude-code-sandbox</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/blog/supabase-claude-code-integration</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/blog/nextjs-vercel-claude-code-integration</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<!-- Other important pages -->
|
||||
<url>
|
||||
<loc>https://aitmpl.com/trending.html</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/component.html</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.6</priority>
|
||||
</url>
|
||||
<!-- Top Popular Agents (based on downloads) -->
|
||||
<url>
|
||||
<loc>https://aitmpl.com/component/agent/prompt-engineer</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/component/agent/llms-maintainer</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/component/agent/hackathon-ai-strategist</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/component/agent/model-evaluator</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/component/agent/security-auditor</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/component/agent/frontend-developer</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<!-- Popular Commands -->
|
||||
<url>
|
||||
<loc>https://aitmpl.com/component/command/setup-ci-cd-pipeline</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/component/command/migrate-to-typescript</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<!-- Popular MCPs -->
|
||||
<url>
|
||||
<loc>https://aitmpl.com/component/mcp/supabase</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/component/mcp/github-integration</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<!-- Popular Settings -->
|
||||
<url>
|
||||
<loc>https://aitmpl.com/component/setting/performance-optimization</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/component/setting/read-only-mode</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<!-- Popular Hooks -->
|
||||
<url>
|
||||
<loc>https://aitmpl.com/component/hook/auto-git-add</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://aitmpl.com/component/hook/smart-commit</loc>
|
||||
<lastmod>2026-02-25</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
@@ -0,0 +1,46 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="400" viewBox="0 0 800 400" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif">
|
||||
<style>
|
||||
.bg { fill: #ffffff; }
|
||||
.title { fill: #1f2328; font-size: 17px; font-weight: 600; }
|
||||
.subtitle { fill: #656d76; font-size: 11px; }
|
||||
.grid { stroke: #d0d7de; stroke-width: 1; stroke-dasharray: 3 3; }
|
||||
.axis { stroke: #656d76; stroke-width: 1.5; }
|
||||
.tick { fill: #656d76; font-size: 11px; }
|
||||
.area { fill: #ffd33d; opacity: 0.18; }
|
||||
.line { stroke: #f0b400; stroke-width: 2.5; fill: none; stroke-linejoin: round; stroke-linecap: round; }
|
||||
.dot { fill: #f0b400; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #0d1117; }
|
||||
.title { fill: #e6edf3; }
|
||||
.subtitle { fill: #8b949e; }
|
||||
.grid { stroke: #30363d; }
|
||||
.axis { stroke: #8b949e; }
|
||||
.tick { fill: #8b949e; }
|
||||
}
|
||||
</style>
|
||||
<rect class="bg" width="800" height="400" rx="6"/>
|
||||
<text x="70" y="26" class="title">Stargazers over time</text>
|
||||
<text x="70" y="42" class="subtitle">davila7/claude-code-templates · 29,032 stars · updated 2026-07-12</text>
|
||||
<line x1="70" y1="345.0" x2="745" y2="345.0" class="grid"/>
|
||||
<text x="60" y="349.0" text-anchor="end" class="tick">0</text>
|
||||
<line x1="70" y1="271.2" x2="745" y2="271.2" class="grid"/>
|
||||
<text x="60" y="275.2" text-anchor="end" class="tick">7,258</text>
|
||||
<line x1="70" y1="197.5" x2="745" y2="197.5" class="grid"/>
|
||||
<text x="60" y="201.5" text-anchor="end" class="tick">14,516</text>
|
||||
<line x1="70" y1="123.8" x2="745" y2="123.8" class="grid"/>
|
||||
<text x="60" y="127.8" text-anchor="end" class="tick">21,774</text>
|
||||
<line x1="70" y1="50.0" x2="745" y2="50.0" class="grid"/>
|
||||
<text x="60" y="54.0" text-anchor="end" class="tick">29,032</text>
|
||||
|
||||
<line x1="70" y1="50" x2="70" y2="345" class="axis"/>
|
||||
<line x1="70" y1="345" x2="745" y2="345" class="axis"/>
|
||||
<polygon class="area" points="70.0,345.0 70.0,345.0 78.0,342.0 89.9,339.1 101.7,336.1 109.2,333.2 116.0,330.2 119.6,327.3 120.5,324.3 121.9,321.4 122.9,318.4 126.0,315.5 128.0,312.5 132.2,309.6 137.3,306.6 144.0,303.7 149.5,300.7 159.3,297.8 166.7,294.8 179.5,291.9 187.9,288.9 196.1,286.0 209.8,283.0 231.8,280.1 243.5,277.1 246.1,274.2 248.0,271.2 249.7,268.3 252.0,265.3 254.9,262.4 258.9,259.4 262.9,256.5 269.3,253.5 279.0,250.6 284.6,247.6 290.1,244.7 297.6,241.7 301.6,238.8 304.5,235.8 314.5,232.9 329.1,229.9 337.3,227.0 345.3,224.0 361.8,221.1 374.2,218.1 377.9,215.2 379.7,212.2 382.0,209.3 388.8,206.3 393.5,203.4 400.7,200.4 407.5,197.5 410.8,194.5 413.6,191.6 415.2,188.6 420.5,185.7 422.1,182.7 424.2,179.8 425.9,176.8 427.3,173.9 428.8,170.9 431.4,168.0 435.2,165.0 440.2,162.1 444.7,159.1 449.3,156.2 454.4,153.2 460.6,150.3 465.8,147.3 471.3,144.4 478.0,141.4 481.6,138.5 487.0,135.5 495.2,132.6 501.7,129.6 505.8,126.7 510.6,123.7 516.4,120.8 522.0,117.8 530.3,114.9 538.3,111.9 547.0,109.0 556.7,106.0 563.4,103.1 574.3,100.1 586.6,97.2 597.5,94.2 603.3,91.3 604.9,88.3 607.7,85.4 609.1,82.4 610.4,79.5 617.9,76.5 624.6,73.6 633.8,70.6 650.5,67.7 671.2,64.7 690.0,61.8 709.0,58.8 736.0,55.9 742.9,52.9 745.0,50.0 745.0,345.0"/>
|
||||
<polyline class="line" points="70.0,345.0 78.0,342.0 89.9,339.1 101.7,336.1 109.2,333.2 116.0,330.2 119.6,327.3 120.5,324.3 121.9,321.4 122.9,318.4 126.0,315.5 128.0,312.5 132.2,309.6 137.3,306.6 144.0,303.7 149.5,300.7 159.3,297.8 166.7,294.8 179.5,291.9 187.9,288.9 196.1,286.0 209.8,283.0 231.8,280.1 243.5,277.1 246.1,274.2 248.0,271.2 249.7,268.3 252.0,265.3 254.9,262.4 258.9,259.4 262.9,256.5 269.3,253.5 279.0,250.6 284.6,247.6 290.1,244.7 297.6,241.7 301.6,238.8 304.5,235.8 314.5,232.9 329.1,229.9 337.3,227.0 345.3,224.0 361.8,221.1 374.2,218.1 377.9,215.2 379.7,212.2 382.0,209.3 388.8,206.3 393.5,203.4 400.7,200.4 407.5,197.5 410.8,194.5 413.6,191.6 415.2,188.6 420.5,185.7 422.1,182.7 424.2,179.8 425.9,176.8 427.3,173.9 428.8,170.9 431.4,168.0 435.2,165.0 440.2,162.1 444.7,159.1 449.3,156.2 454.4,153.2 460.6,150.3 465.8,147.3 471.3,144.4 478.0,141.4 481.6,138.5 487.0,135.5 495.2,132.6 501.7,129.6 505.8,126.7 510.6,123.7 516.4,120.8 522.0,117.8 530.3,114.9 538.3,111.9 547.0,109.0 556.7,106.0 563.4,103.1 574.3,100.1 586.6,97.2 597.5,94.2 603.3,91.3 604.9,88.3 607.7,85.4 609.1,82.4 610.4,79.5 617.9,76.5 624.6,73.6 633.8,70.6 650.5,67.7 671.2,64.7 690.0,61.8 709.0,58.8 736.0,55.9 742.9,52.9 745.0,50.0"/>
|
||||
<circle class="dot" cx="745.0" cy="50.0" r="4"/>
|
||||
<text x="70.0" y="367" text-anchor="middle" class="tick">Jul 2025</text>
|
||||
<text x="238.8" y="367" text-anchor="middle" class="tick">Oct 2025</text>
|
||||
<text x="407.5" y="367" text-anchor="middle" class="tick">Jan 2026</text>
|
||||
<text x="576.2" y="367" text-anchor="middle" class="tick">Apr 2026</text>
|
||||
<text x="745.0" y="367" text-anchor="middle" class="tick">Jul 2026</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
@@ -0,0 +1,6 @@
|
||||
This favicon was generated using the following font:
|
||||
|
||||
- Font Title: Leckerli One
|
||||
- Font Author: undefined
|
||||
- Font Source: https://fonts.gstatic.com/s/leckerlione/v21/V8mCoQH8VCsNttEnxnGQ-1itLZxcBtItFw.ttf
|
||||
- Font License: undefined)
|
||||
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 191 KiB |