Skip to content

Commit ad87532

Browse files
committed
favicon: Add favicon to all pages for consistent branding and improved UX
1 parent 9168edb commit ad87532

File tree

23 files changed

+673
-40
lines changed

23 files changed

+673
-40
lines changed

docs/blog/blog-articles.json

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"readTime": "5 min read",
1212
"tags": ["Supabase", "Database", "Agents", "Commands", "MCP"],
1313
"difficulty": "basic",
14+
"featured": true,
1415
"order": 1
1516
},
1617
{
@@ -51,15 +52,82 @@
5152
"tags": ["Documentation", "Docusaurus", "Automation", "Agents"],
5253
"difficulty": "advanced",
5354
"order": 4
55+
},
56+
{
57+
"id": "claude-code-templates-guide",
58+
"title": "Complete Guide to Claude Code Templates",
59+
"description": "Comprehensive introduction to Claude Code Templates, covering installation, configuration, and best practices for AI-powered development.",
60+
"url": "https://medium.com/latinxinai/complete-guide-to-claude-code-templates-4e53d6688b34",
61+
"image": "https://miro.medium.com/v2/resize:fit:1400/format:webp/1*YwxsC2eeW5fW1K6E8VBZpA.png",
62+
"category": "Getting Started",
63+
"publishDate": "2025-08-27",
64+
"readTime": "5 min read",
65+
"tags": ["Getting Started", "Templates", "Installation", "Configuration"],
66+
"difficulty": "basic",
67+
"featured": true,
68+
"order": 5
69+
},
70+
{
71+
"id": "tunnel-vision-fix",
72+
"title": "Fixed Claude Code's 2024 Tunnel Vision with a Simple Hook",
73+
"description": "Learn how to solve Claude Code's tunnel vision problem using a simple automation hook for better context awareness.",
74+
"url": "https://medium.com/@dan.avila7/fixed-claude-codes-2024-tunnel-vision-with-a-simple-hook-cb32cfaf9b27",
75+
"image": "https://miro.medium.com/v2/resize:fit:720/format:webp/1*T5gG6RGS-4dpD0K6mhnkHA.png",
76+
"category": "Automation",
77+
"publishDate": "2025-09-13",
78+
"readTime": "3 min read",
79+
"tags": ["Hooks", "Automation", "Context", "Problem Solving"],
80+
"difficulty": "basic",
81+
"order": 6
82+
},
83+
{
84+
"id": "telegram-integration",
85+
"title": "Step-by-Step Guide: Connect Telegram with Claude Code Hooks",
86+
"description": "Complete tutorial for integrating Telegram notifications with Claude Code using automation hooks.",
87+
"url": "https://medium.com/@dan.avila7/step-by-step-guide-connect-telegram-with-claude-code-hooks-1686fadcee65",
88+
"image": "https://miro.medium.com/v2/resize:fit:720/format:webp/1*CMFx_4KLxgIbv8xZ6UCpQw.png",
89+
"category": "Integrations",
90+
"publishDate": "2025-08-18",
91+
"readTime": "4 min read",
92+
"tags": ["Telegram", "Hooks", "Notifications", "Integration"],
93+
"difficulty": "basic",
94+
"featured": true,
95+
"order": 7
96+
},
97+
{
98+
"id": "vercel-statusline",
99+
"title": "Vercel Statusline Tutorial for Claude Code",
100+
"description": "Learn how to create a custom Vercel statusline for Claude Code to monitor deployments and project status.",
101+
"url": "https://medium.com/@dan.avila7/vercel-statusline-tutorial-for-claude-code-14755f113d76",
102+
"image": "https://miro.medium.com/v2/resize:fit:720/format:webp/1*dGIs0XoRdrs9STzM-BWDSA.png",
103+
"category": "Customization",
104+
"publishDate": "2025-08-16",
105+
"readTime": "3 min read",
106+
"tags": ["Vercel", "Statusline", "Deployment", "Monitoring"],
107+
"difficulty": "basic",
108+
"order": 8
109+
},
110+
{
111+
"id": "prepare-codebase",
112+
"title": "Step-by-Step Guide: Prepare Your Codebase for Claude Code",
113+
"description": "Advanced guide to structuring and optimizing your codebase for maximum efficiency with Claude Code.",
114+
"url": "https://medium.com/@dan.avila7/step-by-step-guide-prepare-your-codebase-for-claude-code-3e14262566e9",
115+
"image": "https://miro.medium.com/v2/resize:fit:720/format:webp/1*lV1ujaM_2kDSJXNlmLIg2A.png",
116+
"category": "Best Practices",
117+
"publishDate": "2025-08-06",
118+
"readTime": "4 min read",
119+
"tags": ["Best Practices", "Code Organization", "Optimization", "Setup"],
120+
"difficulty": "advanced",
121+
"order": 9
54122
}
55123
],
56124
"metadata": {
57125
"lastUpdated": "2025-02-03",
58-
"totalArticles": 4,
126+
"totalArticles": 9,
59127
"difficultyLevels": {
60-
"basic": 1,
128+
"basic": 5,
61129
"intermediate": 1,
62-
"advanced": 2
130+
"advanced": 3
63131
}
64132
}
65133
}

docs/blog/e2b-claude-code-sandbox/index.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<title>E2B + Claude Code Sandbox: Secure Cloud Development Environment Guide</title>
7+
<!-- Favicon -->
8+
<link rel="icon" type="image/x-icon" href="../../static/favicon/favicon.ico">
9+
<link rel="icon" type="image/png" sizes="16x16" href="../../static/favicon/favicon-16x16.png">
10+
<link rel="icon" type="image/png" sizes="32x32" href="../../static/favicon/favicon-32x32.png">
11+
<link rel="apple-touch-icon" sizes="180x180" href="../../static/favicon/apple-touch-icon.png">
12+
<link rel="icon" type="image/png" sizes="192x192" href="../../static/favicon/android-chrome-192x192.png">
13+
<link rel="icon" type="image/png" sizes="512x512" href="../../static/favicon/android-chrome-512x512.png">
14+
715
<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.">
816

917
<!-- Open Graph / Facebook -->

docs/blog/index.html

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@
2121
<meta property="twitter:image" content="https://www.aitmpl.com/blog/assets/supabase-claude-code-templates-cover.png">
2222

2323
<link rel="canonical" href="https://aitmpl.com/blog/">
24+
25+
<!-- Favicon -->
26+
<link rel="icon" type="image/x-icon" href="../static/favicon/favicon.ico">
27+
<link rel="icon" type="image/png" sizes="16x16" href="../static/favicon/favicon-16x16.png">
28+
<link rel="icon" type="image/png" sizes="32x32" href="../static/favicon/favicon-32x32.png">
29+
<link rel="apple-touch-icon" sizes="180x180" href="../static/favicon/apple-touch-icon.png">
30+
<link rel="icon" type="image/png" sizes="192x192" href="../static/favicon/android-chrome-192x192.png">
31+
<link rel="icon" type="image/png" sizes="512x512" href="../static/favicon/android-chrome-512x512.png">
32+
2433
<link rel="stylesheet" href="../css/styles.css">
2534
<link rel="stylesheet" href="../css/blog.css">
2635
<link rel="stylesheet" href="css/blog-controls.css">
@@ -96,6 +105,33 @@ <h1 class="search-title">
96105

97106
<section class="blog-articles">
98107
<div class="container">
108+
<!-- Featured Posts Carousel -->
109+
<div class="featured-posts-section" id="featured-posts-section" style="display: none;">
110+
<div class="featured-header">
111+
<h2 class="featured-title">
112+
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
113+
<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"/>
114+
</svg>
115+
Featured Posts
116+
</h2>
117+
</div>
118+
<div class="featured-carousel-wrapper">
119+
<button class="carousel-nav carousel-prev" id="carousel-prev" aria-label="Previous">
120+
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
121+
<path d="M15.41,16.58L10.83,12L15.41,7.41L14,6L8,12L14,18L15.41,16.58Z"/>
122+
</svg>
123+
</button>
124+
<div class="featured-carousel" id="featured-carousel">
125+
<!-- Featured posts will be loaded here dynamically -->
126+
</div>
127+
<button class="carousel-nav carousel-next" id="carousel-next" aria-label="Next">
128+
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
129+
<path d="M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z"/>
130+
</svg>
131+
</button>
132+
</div>
133+
</div>
134+
99135
<!-- Search and Filter Controls -->
100136
<div class="blog-controls">
101137
<div class="search-container">

docs/blog/js/blog-loader.js

Lines changed: 171 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ class BlogLoader {
77
constructor() {
88
this.articles = [];
99
this.filteredArticles = [];
10+
this.featuredArticles = [];
1011
this.articlesContainer = null;
12+
this.featuredCarousel = null;
1113
this.searchInput = null;
1214
this.sortSelect = null;
1315
this.currentSearchTerm = '';
1416
this.currentSortBy = 'date-desc';
17+
this.carouselScrollPosition = 0;
1518
}
1619

1720
/**
@@ -20,6 +23,7 @@ class BlogLoader {
2023
async init() {
2124
try {
2225
this.articlesContainer = document.querySelector('.articles-grid');
26+
this.featuredCarousel = document.getElementById('featured-carousel');
2327
this.searchInput = document.getElementById('blog-search');
2428
this.sortSelect = document.getElementById('sort-by');
2529

@@ -34,9 +38,15 @@ class BlogLoader {
3438
// Load articles from JSON
3539
await this.loadArticles();
3640

41+
// Render featured posts
42+
this.renderFeaturedPosts();
43+
3744
// Setup event listeners
3845
this.setupEventListeners();
3946

47+
// Setup carousel navigation
48+
this.setupCarouselNavigation();
49+
4050
// Render articles
4151
this.renderArticles();
4252

@@ -217,7 +227,10 @@ class BlogLoader {
217227
this.articles = data.articles.sort((a, b) => a.order - b.order);
218228
this.filteredArticles = [...this.articles];
219229

220-
console.log(`Loaded ${this.articles.length} articles`);
230+
// Extract featured articles
231+
this.featuredArticles = this.articles.filter(article => article.featured === true);
232+
233+
console.log(`Loaded ${this.articles.length} articles (${this.featuredArticles.length} featured)`);
221234

222235
} catch (error) {
223236
console.error('Error loading articles:', error);
@@ -403,6 +416,163 @@ class BlogLoader {
403416
div.textContent = text;
404417
return div.innerHTML;
405418
}
419+
420+
/**
421+
* Render featured posts carousel
422+
*/
423+
renderFeaturedPosts() {
424+
if (!this.featuredCarousel || !this.featuredArticles || this.featuredArticles.length === 0) {
425+
return;
426+
}
427+
428+
// Show featured section
429+
const featuredSection = document.getElementById('featured-posts-section');
430+
if (featuredSection) {
431+
featuredSection.style.display = 'block';
432+
}
433+
434+
// Clear carousel
435+
this.featuredCarousel.innerHTML = '';
436+
437+
// Render each featured article
438+
this.featuredArticles.forEach(article => {
439+
const featuredCard = this.createFeaturedCard(article);
440+
this.featuredCarousel.appendChild(featuredCard);
441+
});
442+
}
443+
444+
/**
445+
* Create featured article card
446+
* @param {Object} article - Article data
447+
* @returns {HTMLElement} Featured card element
448+
*/
449+
createFeaturedCard(article) {
450+
const cardElement = document.createElement('article');
451+
cardElement.className = 'featured-card';
452+
453+
const isExternal = article.url.startsWith('http');
454+
const linkAttrs = isExternal ? 'target="_blank" rel="noopener noreferrer"' : '';
455+
456+
const tagsHTML = article.tags
457+
.slice(0, 3)
458+
.map(tag => `<span class="tag">${this.escapeHtml(tag)}</span>`)
459+
.join('');
460+
461+
cardElement.innerHTML = `
462+
<div class="featured-star">
463+
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
464+
<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"/>
465+
</svg>
466+
</div>
467+
<a href="${this.escapeHtml(article.url)}" ${linkAttrs} class="featured-link">
468+
<div class="featured-image">
469+
<img src="${this.escapeHtml(article.image)}"
470+
alt="${this.escapeHtml(article.title)}"
471+
loading="lazy">
472+
</div>
473+
<div class="featured-content">
474+
<div class="featured-meta">
475+
<time datetime="${this.escapeHtml(article.publishDate)}">
476+
${this.formatDate(article.publishDate)}
477+
</time>
478+
<span class="read-time">${this.escapeHtml(article.readTime)}</span>
479+
</div>
480+
<h3>${this.escapeHtml(article.title)}</h3>
481+
<p>${this.escapeHtml(article.description)}</p>
482+
<div class="featured-tags">
483+
${tagsHTML}
484+
</div>
485+
</div>
486+
</a>
487+
`;
488+
489+
return cardElement;
490+
}
491+
492+
/**
493+
* Setup carousel navigation
494+
*/
495+
setupCarouselNavigation() {
496+
const prevBtn = document.getElementById('carousel-prev');
497+
const nextBtn = document.getElementById('carousel-next');
498+
499+
if (!prevBtn || !nextBtn || !this.featuredCarousel) return;
500+
501+
// Check if navigation is needed
502+
this.checkCarouselNavigation(prevBtn, nextBtn);
503+
504+
// Scroll carousel on button click
505+
prevBtn.addEventListener('click', () => {
506+
this.featuredCarousel.scrollBy({
507+
left: -280,
508+
behavior: 'smooth'
509+
});
510+
});
511+
512+
nextBtn.addEventListener('click', () => {
513+
this.featuredCarousel.scrollBy({
514+
left: 280,
515+
behavior: 'smooth'
516+
});
517+
});
518+
519+
// Update button states on scroll
520+
this.featuredCarousel.addEventListener('scroll', () => {
521+
this.updateCarouselButtons(prevBtn, nextBtn);
522+
});
523+
524+
// Re-check on window resize
525+
window.addEventListener('resize', () => {
526+
this.checkCarouselNavigation(prevBtn, nextBtn);
527+
});
528+
529+
// Initial button state
530+
this.updateCarouselButtons(prevBtn, nextBtn);
531+
}
532+
533+
/**
534+
* Check if carousel navigation buttons should be visible
535+
*/
536+
checkCarouselNavigation(prevBtn, nextBtn) {
537+
if (!this.featuredCarousel) return;
538+
539+
// Wait for images to load before checking
540+
setTimeout(() => {
541+
const isOverflowing = this.featuredCarousel.scrollWidth > this.featuredCarousel.clientWidth;
542+
543+
if (isOverflowing) {
544+
prevBtn.style.display = 'flex';
545+
nextBtn.style.display = 'flex';
546+
} else {
547+
prevBtn.style.display = 'none';
548+
nextBtn.style.display = 'none';
549+
}
550+
}, 100);
551+
}
552+
553+
/**
554+
* Update carousel button states based on scroll position
555+
*/
556+
updateCarouselButtons(prevBtn, nextBtn) {
557+
if (!this.featuredCarousel) return;
558+
559+
const scrollLeft = this.featuredCarousel.scrollLeft;
560+
const maxScroll = this.featuredCarousel.scrollWidth - this.featuredCarousel.clientWidth;
561+
562+
// Disable/enable previous button
563+
if (scrollLeft <= 0) {
564+
prevBtn.disabled = true;
565+
} else {
566+
prevBtn.disabled = false;
567+
}
568+
569+
// Disable/enable next button
570+
if (scrollLeft >= maxScroll - 1) {
571+
nextBtn.disabled = true;
572+
} else {
573+
nextBtn.disabled = false;
574+
}
575+
}
406576
}
407577

408578
// Initialize blog loader when DOM is ready

docs/blog/nextjs-vercel-claude-code-integration/index.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<title>Next.js + Vercel + Claude Code Integration: Complete Guide with Agents, Commands and Hooks</title>
7+
<!-- Favicon -->
8+
<link rel="icon" type="image/x-icon" href="../../static/favicon/favicon.ico">
9+
<link rel="icon" type="image/png" sizes="16x16" href="../../static/favicon/favicon-16x16.png">
10+
<link rel="icon" type="image/png" sizes="32x32" href="../../static/favicon/favicon-32x32.png">
11+
<link rel="apple-touch-icon" sizes="180x180" href="../../static/favicon/apple-touch-icon.png">
12+
<link rel="icon" type="image/png" sizes="192x192" href="../../static/favicon/android-chrome-192x192.png">
13+
<link rel="icon" type="image/png" sizes="512x512" href="../../static/favicon/android-chrome-512x512.png">
14+
715
<meta name="description" content="Complete guide to integrate Next.js and Vercel with Claude Code. Install 10 development commands, 3 specialized AI agents, and 5 automated hooks for Next.js deployment, optimization, and monitoring.">
816

917
<!-- Open Graph / Facebook -->

0 commit comments

Comments
 (0)