chore: reorganize project into monorepo

This commit is contained in:
2026-03-28 10:40:22 +08:00
parent 60367a5f51
commit 1455d93246
201 changed files with 30081 additions and 93 deletions

View File

@@ -0,0 +1,165 @@
---
import { createMarkdownProcessor } from '@astrojs/markdown-remark';
import BaseLayout from '../../layouts/BaseLayout.astro';
import TerminalWindow from '../../components/ui/TerminalWindow.astro';
import CommandPrompt from '../../components/ui/CommandPrompt.astro';
import TableOfContents from '../../components/TableOfContents.astro';
import RelatedPosts from '../../components/RelatedPosts.astro';
import ReadingProgress from '../../components/ReadingProgress.astro';
import BackToTop from '../../components/BackToTop.astro';
import Lightbox from '../../components/Lightbox.astro';
import CodeCopyButton from '../../components/CodeCopyButton.astro';
import Comments from '../../components/Comments.astro';
import { apiClient } from '../../lib/api/client';
import { resolveFileRef, getPostTypeColor } from '../../lib/utils';
export const prerender = false;
const { slug } = Astro.params;
let post = null;
try {
post = await apiClient.getPostBySlug(slug ?? '');
} catch (error) {
console.error('API Error:', error);
}
if (!post) {
return new Response(null, { status: 404 });
}
const typeColor = getPostTypeColor(post.type || 'article');
const contentText = post.content || post.description || '';
const wordCount = contentText.length;
const readTimeMinutes = Math.ceil(wordCount / 300);
const articleMarkdown = contentText.replace(/^#\s+.+\r?\n+/, '');
const markdownProcessor = await createMarkdownProcessor();
const renderedContent = await markdownProcessor.render(articleMarkdown);
---
<BaseLayout title={`${post.title} - Termi`} description={post.description}>
<ReadingProgress />
<BackToTop />
<Lightbox />
<CodeCopyButton />
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div class="flex flex-col gap-8 lg:flex-row">
<div class="min-w-0 flex-1">
<TerminalWindow title={`~/content/posts/${post.slug}.md`} class="w-full">
<div class="px-4 pb-2">
<div class="terminal-panel ml-4 mt-4 space-y-5">
<div class="flex flex-wrap items-start justify-between gap-4">
<div class="space-y-4">
<a href="/articles" class="terminal-link-arrow">
<i class="fas fa-arrow-left"></i>
<span>返回文章索引</span>
</a>
<div class="flex flex-wrap items-center gap-2">
<span class="terminal-kicker">
<i class="fas fa-file-code"></i>
document session
</span>
<span class="terminal-chip">
<span class="h-2.5 w-2.5 rounded-full" style={`background-color: ${typeColor}`}></span>
{post.type === 'article' ? 'article' : 'tweet'}
</span>
<span class="terminal-chip">
<i class="fas fa-folder-tree text-[var(--primary)]"></i>
{post.category}
</span>
</div>
</div>
<div class="flex flex-wrap gap-2">
<span class="terminal-stat-pill">
<i class="far fa-calendar text-[var(--primary)]"></i>
{post.date}
</span>
<span class="terminal-stat-pill">
<i class="far fa-clock text-[var(--primary)]"></i>
{readTimeMinutes} min
</span>
<span class="terminal-stat-pill">
<i class="fas fa-font text-[var(--primary)]"></i>
{wordCount} chars
</span>
</div>
</div>
<div class="space-y-3">
<h1 class="text-3xl font-bold tracking-tight text-[var(--title-color)] sm:text-4xl">{post.title}</h1>
<p class="max-w-3xl text-base leading-8 text-[var(--text-secondary)]">{post.description}</p>
</div>
{post.tags?.length > 0 && (
<div class="flex flex-wrap gap-2">
{post.tags.map(tag => (
<a href={`/tags?tag=${encodeURIComponent(tag)}`} class="terminal-filter">
<i class="fas fa-hashtag"></i>
<span>{tag}</span>
</a>
))}
</div>
)}
</div>
</div>
<div class="px-4 pb-2">
<CommandPrompt command={`bat --style=plain ${post.slug}.md`} />
<div class="ml-4 mt-4 space-y-6">
{post.image && (
<div class="terminal-panel-muted overflow-hidden">
<img
src={resolveFileRef(post.image)}
alt={post.title}
class="w-full h-auto rounded-xl border border-[var(--border-color)] cursor-zoom-in"
/>
</div>
)}
<div class="terminal-document article-content" set:html={renderedContent.code}></div>
</div>
</div>
<div class="px-4 py-6">
<div class="terminal-panel-muted ml-4 mt-4 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<span class="text-sm text-[var(--text-secondary)]">
file://content/posts/{post.slug}.md
</span>
<div class="flex flex-wrap gap-2">
<a href="/articles" class="terminal-action-button">
<i class="fas fa-list"></i>
<span>back to index</span>
</a>
<button
class="terminal-action-button terminal-action-button-primary"
onclick={`navigator.clipboard.writeText(window.location.href)`}
>
<i class="fas fa-link"></i>
<span>copy permalink</span>
</button>
</div>
</div>
</div>
</TerminalWindow>
<RelatedPosts
currentSlug={post.slug}
currentCategory={post.category}
currentTags={post.tags}
/>
<section class="mt-8">
<Comments postSlug={post.slug} class="terminal-panel" />
</section>
</div>
<TableOfContents />
</div>
</div>
</BaseLayout>