--- import BaseLayout from '../../layouts/BaseLayout.astro'; import TerminalWindow from '../../components/ui/TerminalWindow.astro'; import CommandPrompt from '../../components/ui/CommandPrompt.astro'; import FilterPill from '../../components/ui/FilterPill.astro'; import PostCard from '../../components/PostCard.astro'; import { api } from '../../lib/api/client'; import { getI18n } from '../../lib/i18n'; import type { Category, Post, Tag } from '../../lib/types'; import { getAccentVars, getCategoryTheme, getPostTypeTheme, getTagTheme } from '../../lib/utils'; export const prerender = false; let allPosts: Post[] = []; let allTags: Tag[] = []; let allCategories: Category[] = []; const url = new URL(Astro.request.url); const selectedSearch = url.searchParams.get('search') || ''; const { t } = getI18n(Astro); try { const [posts, categories, rawTags] = await Promise.all([ selectedSearch ? api.searchPosts(selectedSearch) : api.getPosts(), api.getCategories(), api.getTags(), ]); allPosts = posts; allCategories = categories; const seenTagIds = new Set(); allTags = rawTags.filter(tag => { const key = `${tag.slug}:${tag.name}`.toLowerCase(); if (seenTagIds.has(key)) return false; seenTagIds.add(key); return true; }); } catch (error) { console.error('API Error:', error); } const selectedType = url.searchParams.get('type') || 'all'; const selectedTag = url.searchParams.get('tag') || ''; const selectedCategory = url.searchParams.get('category') || ''; const currentPage = parseInt(url.searchParams.get('page') || '1'); const postsPerPage = 10; const normalizedSelectedTag = selectedTag.trim().toLowerCase(); const isMatchingTag = (value: string) => value.trim().toLowerCase() === normalizedSelectedTag; const isSelectedTag = (tag: Tag) => tag.name.trim().toLowerCase() === normalizedSelectedTag || tag.slug.trim().toLowerCase() === normalizedSelectedTag; const filteredPosts = allPosts.filter(post => { if (selectedType !== 'all' && post.type !== selectedType) return false; if (selectedTag && !post.tags?.some(isMatchingTag)) return false; if (selectedCategory && post.category?.toLowerCase() !== selectedCategory.toLowerCase()) return false; return true; }); const totalPosts = filteredPosts.length; const totalPages = Math.ceil(totalPosts / postsPerPage); const startIndex = (currentPage - 1) * postsPerPage; const paginatedPosts = filteredPosts.slice(startIndex, startIndex + postsPerPage); const postTypeFilters = [ { id: 'all', name: t('common.all'), icon: 'fa-stream' }, { id: 'article', name: t('common.article'), icon: 'fa-file-alt' }, { id: 'tweet', name: t('common.tweet'), icon: 'fa-comment-dots' } ]; const typePromptCommand = selectedType === 'all' ? `grep -E "^type: (article|tweet)$" ./posts/*.md` : `grep -E "^type: ${selectedType}$" ./posts/*.md`; const categoryPromptCommand = selectedCategory ? `grep -El "^category: ${selectedCategory}$" ./posts/*.md` : `cut -d: -f2 ./categories.index | sort -u`; const tagPromptCommand = selectedTag ? `grep -Ril "#${selectedTag}" ./posts` : `cut -d: -f2 ./tags.index | sort -u`; const categoryAccentMap = Object.fromEntries( allCategories.map((category) => [category.name.toLowerCase(), getAccentVars(getCategoryTheme(category.name))]) ); const tagAccentMap = Object.fromEntries( allTags.map((tag) => [String(tag.slug || tag.name).toLowerCase(), getAccentVars(getTagTheme(tag.name))]) ); const buildArticlesUrl = ({ type = selectedType, search = selectedSearch, tag = selectedTag, category = selectedCategory, page, }: { type?: string; search?: string; tag?: string; category?: string; page?: number; }) => { const params = new URLSearchParams(); if (type && type !== 'all') params.set('type', type); if (search) params.set('search', search); if (tag) params.set('tag', tag); if (category) params.set('category', category); if (page && page > 1) params.set('page', String(page)); const queryString = params.toString(); return queryString ? `/articles?${queryString}` : '/articles'; }; ---

{t('articlesPage.title')}

{t('articlesPage.description')}

{t('articlesPage.totalPosts', { count: filteredPosts.length })} {selectedSearch && ( grep: {selectedSearch} )} {selectedCategory} {selectedTag}
{postTypeFilters.map(filter => ( {filter.name} ))}
{allCategories.length > 0 && (
{t('articlesPage.allCategories')} {allCategories.map(category => ( {category.name} {category.count} ))}
)} {allTags.length > 0 && (
{t('articlesPage.allTags')} {allTags.map(tag => ( {tag.name} ))}
)}
{allPosts.length > 0 ? (
{allPosts.map((post, index) => { const matchesCurrentFilter = (selectedType === 'all' || post.type === selectedType) && (!selectedTag || post.tags?.some(isMatchingTag)) && (!selectedCategory || post.category?.toLowerCase() === selectedCategory.toLowerCase()); const filteredIndex = matchesCurrentFilter ? filteredPosts.findIndex((item) => item.slug === post.slug) : -1; const isVisible = matchesCurrentFilter && filteredIndex >= startIndex && filteredIndex < startIndex + postsPerPage; return (
tag.trim().toLowerCase()).join('|')} data-article-index={index} class:list={[!isVisible && 'hidden']} >
); })}
) : null}
0 && 'hidden']}>

{t('articlesPage.emptyTitle')}

{t('articlesPage.emptyDescription')}

{t('common.resetFilters')}
{t('articlesPage.pageSummary', { current: currentPage, total: totalPages, count: totalPosts })}