feat(website): 添加项目官网

使用 Astro 框架构建的静态官网
This commit is contained in:
2025-12-12 22:53:02 +08:00
parent cb554c65b4
commit 2f9db8d7d8
14 changed files with 2098 additions and 0 deletions
@@ -0,0 +1 @@
export default new Map();
@@ -0,0 +1 @@
export default new Map();
+199
View File
@@ -0,0 +1,199 @@
declare module 'astro:content' {
export interface RenderResult {
Content: import('astro/runtime/server/index.js').AstroComponentFactory;
headings: import('astro').MarkdownHeading[];
remarkPluginFrontmatter: Record<string, any>;
}
interface Render {
'.md': Promise<RenderResult>;
}
export interface RenderedContent {
html: string;
metadata?: {
imagePaths: Array<string>;
[key: string]: unknown;
};
}
}
declare module 'astro:content' {
type Flatten<T> = T extends { [K: string]: infer U } ? U : never;
export type CollectionKey = keyof AnyEntryMap;
export type CollectionEntry<C extends CollectionKey> = Flatten<AnyEntryMap[C]>;
export type ContentCollectionKey = keyof ContentEntryMap;
export type DataCollectionKey = keyof DataEntryMap;
type AllValuesOf<T> = T extends any ? T[keyof T] : never;
type ValidContentEntrySlug<C extends keyof ContentEntryMap> = AllValuesOf<
ContentEntryMap[C]
>['slug'];
export type ReferenceDataEntry<
C extends CollectionKey,
E extends keyof DataEntryMap[C] = string,
> = {
collection: C;
id: E;
};
export type ReferenceContentEntry<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}) = string,
> = {
collection: C;
slug: E;
};
export type ReferenceLiveEntry<C extends keyof LiveContentConfig['collections']> = {
collection: C;
id: string;
};
/** @deprecated Use `getEntry` instead. */
export function getEntryBySlug<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}),
>(
collection: C,
// Note that this has to accept a regular string too, for SSR
entrySlug: E,
): E extends ValidContentEntrySlug<C>
? Promise<CollectionEntry<C>>
: Promise<CollectionEntry<C> | undefined>;
/** @deprecated Use `getEntry` instead. */
export function getDataEntryById<C extends keyof DataEntryMap, E extends keyof DataEntryMap[C]>(
collection: C,
entryId: E,
): Promise<CollectionEntry<C>>;
export function getCollection<C extends keyof AnyEntryMap, E extends CollectionEntry<C>>(
collection: C,
filter?: (entry: CollectionEntry<C>) => entry is E,
): Promise<E[]>;
export function getCollection<C extends keyof AnyEntryMap>(
collection: C,
filter?: (entry: CollectionEntry<C>) => unknown,
): Promise<CollectionEntry<C>[]>;
export function getLiveCollection<C extends keyof LiveContentConfig['collections']>(
collection: C,
filter?: LiveLoaderCollectionFilterType<C>,
): Promise<
import('astro').LiveDataCollectionResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>
>;
export function getEntry<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}),
>(
entry: ReferenceContentEntry<C, E>,
): E extends ValidContentEntrySlug<C>
? Promise<CollectionEntry<C>>
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof DataEntryMap,
E extends keyof DataEntryMap[C] | (string & {}),
>(
entry: ReferenceDataEntry<C, E>,
): E extends keyof DataEntryMap[C]
? Promise<DataEntryMap[C][E]>
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}),
>(
collection: C,
slug: E,
): E extends ValidContentEntrySlug<C>
? Promise<CollectionEntry<C>>
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof DataEntryMap,
E extends keyof DataEntryMap[C] | (string & {}),
>(
collection: C,
id: E,
): E extends keyof DataEntryMap[C]
? string extends keyof DataEntryMap[C]
? Promise<DataEntryMap[C][E]> | undefined
: Promise<DataEntryMap[C][E]>
: Promise<CollectionEntry<C> | undefined>;
export function getLiveEntry<C extends keyof LiveContentConfig['collections']>(
collection: C,
filter: string | LiveLoaderEntryFilterType<C>,
): Promise<import('astro').LiveDataEntryResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>>;
/** Resolve an array of entry references from the same collection */
export function getEntries<C extends keyof ContentEntryMap>(
entries: ReferenceContentEntry<C, ValidContentEntrySlug<C>>[],
): Promise<CollectionEntry<C>[]>;
export function getEntries<C extends keyof DataEntryMap>(
entries: ReferenceDataEntry<C, keyof DataEntryMap[C]>[],
): Promise<CollectionEntry<C>[]>;
export function render<C extends keyof AnyEntryMap>(
entry: AnyEntryMap[C][string],
): Promise<RenderResult>;
export function reference<C extends keyof AnyEntryMap>(
collection: C,
): import('astro/zod').ZodEffects<
import('astro/zod').ZodString,
C extends keyof ContentEntryMap
? ReferenceContentEntry<C, ValidContentEntrySlug<C>>
: ReferenceDataEntry<C, keyof DataEntryMap[C]>
>;
// Allow generic `string` to avoid excessive type errors in the config
// if `dev` is not running to update as you edit.
// Invalid collection names will be caught at build time.
export function reference<C extends string>(
collection: C,
): import('astro/zod').ZodEffects<import('astro/zod').ZodString, never>;
type ReturnTypeOrOriginal<T> = T extends (...args: any[]) => infer R ? R : T;
type InferEntrySchema<C extends keyof AnyEntryMap> = import('astro/zod').infer<
ReturnTypeOrOriginal<Required<ContentConfig['collections'][C]>['schema']>
>;
type ContentEntryMap = {
};
type DataEntryMap = {
};
type AnyEntryMap = ContentEntryMap & DataEntryMap;
type ExtractLoaderTypes<T> = T extends import('astro/loaders').LiveLoader<
infer TData,
infer TEntryFilter,
infer TCollectionFilter,
infer TError
>
? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError }
: { data: never; entryFilter: never; collectionFilter: never; error: never };
type ExtractDataType<T> = ExtractLoaderTypes<T>['data'];
type ExtractEntryFilterType<T> = ExtractLoaderTypes<T>['entryFilter'];
type ExtractCollectionFilterType<T> = ExtractLoaderTypes<T>['collectionFilter'];
type ExtractErrorType<T> = ExtractLoaderTypes<T>['error'];
type LiveLoaderDataType<C extends keyof LiveContentConfig['collections']> =
LiveContentConfig['collections'][C]['schema'] extends undefined
? ExtractDataType<LiveContentConfig['collections'][C]['loader']>
: import('astro/zod').infer<
Exclude<LiveContentConfig['collections'][C]['schema'], undefined>
>;
type LiveLoaderEntryFilterType<C extends keyof LiveContentConfig['collections']> =
ExtractEntryFilterType<LiveContentConfig['collections'][C]['loader']>;
type LiveLoaderCollectionFilterType<C extends keyof LiveContentConfig['collections']> =
ExtractCollectionFilterType<LiveContentConfig['collections'][C]['loader']>;
type LiveLoaderErrorType<C extends keyof LiveContentConfig['collections']> = ExtractErrorType<
LiveContentConfig['collections'][C]['loader']
>;
export type ContentConfig = typeof import("../src/content.config.mjs");
export type LiveContentConfig = never;
}
+1
View File
@@ -0,0 +1 @@
[["Map",1,2],"meta::meta",["Map",3,4,5,6],"astro-version","5.16.5","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4000,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[]},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false,\"staticImportMetaEnv\":false,\"chromeDevtoolsWorkspace\":false,\"failOnPrerenderConflict\":false,\"svgo\":false},\"legacy\":{\"collections\":false}}"]
+5
View File
@@ -0,0 +1,5 @@
{
"_variables": {
"lastUpdateCheck": 1765549520905
}
}
+2
View File
@@ -0,0 +1,2 @@
/// <reference types="astro/client" />
/// <reference path="content.d.ts" />
+9
View File
@@ -0,0 +1,9 @@
import { defineConfig } from 'astro/config';
import tailwind from '@astrojs/tailwind';
export default defineConfig({
integrations: [tailwind()],
server: {
port: 4000,
},
});
+16
View File
@@ -0,0 +1,16 @@
{
"name": "@ai-assistant/website",
"version": "0.0.1",
"type": "module",
"private": true,
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview"
},
"dependencies": {
"astro": "^5.0.0",
"@astrojs/tailwind": "^6.0.0",
"tailwindcss": "^3.4.0"
}
}
+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
<rect width="32" height="32" rx="6" fill="#0c4a6e"/>
<path d="M8 10h16M8 16h12M8 22h8" stroke="#38bdf8" stroke-width="2" stroke-linecap="round"/>
<circle cx="24" cy="22" r="3" fill="#38bdf8"/>
</svg>

After

Width:  |  Height:  |  Size: 279 B

+34
View File
@@ -0,0 +1,34 @@
---
interface Props {
title: string;
}
const { title } = Astro.props;
---
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="AI Terminal Assistant - 终端 AI 编程助手" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>{title}</title>
</head>
<body class="bg-gray-950 text-gray-100 antialiased">
<slot />
</body>
</html>
<style is:global>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
html {
font-family: 'Inter', system-ui, sans-serif;
scroll-behavior: smooth;
}
::selection {
background: rgba(14, 165, 233, 0.3);
}
</style>
+202
View File
@@ -0,0 +1,202 @@
---
import Layout from '../layouts/Layout.astro';
const features = [
{
icon: '⚡',
title: '流式对话',
description: '基于 Claude API 的实时流式响应,让 AI 交互更加自然流畅。',
},
{
icon: '🛠️',
title: '丰富工具',
description: '内置文件操作、Bash 执行、代码搜索等开发工具,直接在终端中完成编程任务。',
},
{
icon: '📝',
title: '智能编辑',
description: '支持多种编辑模式:整体替换、Diff 补丁、搜索替换,灵活应对各种场景。',
},
{
icon: '🔍',
title: 'LSP 集成',
description: '集成语言服务器协议,提供代码诊断、定义跳转等智能编程支持。',
},
{
icon: '💾',
title: 'Checkpoint',
description: 'Shadow Git 检查点系统,随时保存和恢复工作状态,操作更安全。',
},
{
icon: '🔌',
title: 'MCP 支持',
description: '支持 Model Context Protocol,轻松扩展 AI 能力边界。',
},
];
---
<Layout title="AI Terminal Assistant - 终端 AI 编程助手">
<!-- Navigation -->
<nav class="fixed top-0 left-0 right-0 z-50 border-b border-gray-800 bg-gray-950/80 backdrop-blur-sm">
<div class="mx-auto flex max-w-6xl items-center justify-between px-6 py-4">
<a href="/" class="text-xl font-bold text-white">
<span class="text-primary-400">AI</span> Terminal Assistant
</a>
<div class="flex items-center gap-6">
<a href="#features" class="text-sm text-gray-400 transition hover:text-white">功能</a>
<a href="#quickstart" class="text-sm text-gray-400 transition hover:text-white">快速开始</a>
<a
href="https://github.com"
target="_blank"
class="rounded-lg bg-gray-800 px-4 py-2 text-sm font-medium text-white transition hover:bg-gray-700"
>
GitHub
</a>
</div>
</div>
</nav>
<!-- Hero Section -->
<section class="relative flex min-h-screen items-center justify-center overflow-hidden px-6 pt-20">
<!-- Background gradient -->
<div class="absolute inset-0 bg-gradient-to-b from-primary-900/20 via-transparent to-transparent"></div>
<div class="absolute top-1/4 left-1/2 h-96 w-96 -translate-x-1/2 rounded-full bg-primary-500/10 blur-3xl"></div>
<div class="relative z-10 mx-auto max-w-4xl text-center">
<h1 class="mb-6 text-5xl font-bold leading-tight text-white md:text-6xl">
终端中的
<span class="bg-gradient-to-r from-primary-400 to-cyan-400 bg-clip-text text-transparent">
AI 编程助手
</span>
</h1>
<p class="mx-auto mb-10 max-w-2xl text-lg text-gray-400">
基于 Claude API 构建的终端 AI 助手。支持文件操作、代码搜索、智能编辑,
让 AI 成为你的编程伙伴。
</p>
<!-- Terminal Demo -->
<div class="mx-auto mb-10 max-w-2xl overflow-hidden rounded-xl border border-gray-800 bg-gray-900 shadow-2xl">
<div class="flex items-center gap-2 border-b border-gray-800 bg-gray-900 px-4 py-3">
<div class="h-3 w-3 rounded-full bg-red-500"></div>
<div class="h-3 w-3 rounded-full bg-yellow-500"></div>
<div class="h-3 w-3 rounded-full bg-green-500"></div>
<span class="ml-2 text-sm text-gray-500">terminal</span>
</div>
<div class="p-6 text-left font-mono text-sm">
<p class="text-gray-500">$ ai-assistant serve --port 3000</p>
<p class="mt-2 text-green-400">✓ Server started on http://localhost:3000</p>
<p class="mt-4 text-gray-500">$ ai-assistant attach</p>
<p class="mt-2 text-primary-400">AI Assistant ready. How can I help?</p>
<p class="mt-4 text-white">
<span class="text-gray-500">&gt;</span> 帮我找出这个项目中所有的 TODO 注释
</p>
<p class="mt-2 text-gray-300">正在搜索代码库中的 TODO 注释...</p>
<span class="inline-block h-4 w-2 animate-pulse bg-primary-400"></span>
</div>
</div>
<!-- CTA Buttons -->
<div class="flex flex-wrap items-center justify-center gap-4">
<a
href="#quickstart"
class="rounded-xl bg-primary-500 px-8 py-3 font-medium text-white shadow-lg shadow-primary-500/25 transition hover:bg-primary-600"
>
快速开始
</a>
<a
href="https://github.com"
target="_blank"
class="rounded-xl border border-gray-700 bg-gray-900 px-8 py-3 font-medium text-white transition hover:border-gray-600 hover:bg-gray-800"
>
查看源码
</a>
</div>
</div>
</section>
<!-- Features Section -->
<section id="features" class="px-6 py-24">
<div class="mx-auto max-w-6xl">
<h2 class="mb-4 text-center text-3xl font-bold text-white">核心功能</h2>
<p class="mx-auto mb-16 max-w-2xl text-center text-gray-400">
专为开发者设计,让 AI 真正融入你的开发工作流
</p>
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{
features.map((feature) => (
<div class="group rounded-2xl border border-gray-800 bg-gray-900/50 p-6 transition hover:border-gray-700 hover:bg-gray-900">
<div class="mb-4 text-4xl">{feature.icon}</div>
<h3 class="mb-2 text-lg font-semibold text-white">{feature.title}</h3>
<p class="text-sm leading-relaxed text-gray-400">{feature.description}</p>
</div>
))
}
</div>
</div>
</section>
<!-- Quick Start Section -->
<section id="quickstart" class="px-6 py-24">
<div class="mx-auto max-w-3xl">
<h2 class="mb-4 text-center text-3xl font-bold text-white">快速开始</h2>
<p class="mx-auto mb-12 max-w-xl text-center text-gray-400">只需几步即可开始使用</p>
<div class="space-y-6">
<!-- Step 1 -->
<div class="rounded-xl border border-gray-800 bg-gray-900 p-6">
<div class="mb-3 flex items-center gap-3">
<span class="flex h-8 w-8 items-center justify-center rounded-full bg-primary-500/20 text-sm font-bold text-primary-400">
1
</span>
<h3 class="font-semibold text-white">克隆项目</h3>
</div>
<pre class="overflow-x-auto rounded-lg bg-gray-950 p-4 font-mono text-sm text-gray-300"><code>git clone https://github.com/your-repo/ai-terminal-assistant.git
cd ai-terminal-assistant</code></pre>
</div>
<!-- Step 2 -->
<div class="rounded-xl border border-gray-800 bg-gray-900 p-6">
<div class="mb-3 flex items-center gap-3">
<span class="flex h-8 w-8 items-center justify-center rounded-full bg-primary-500/20 text-sm font-bold text-primary-400">
2
</span>
<h3 class="font-semibold text-white">安装依赖</h3>
</div>
<pre class="overflow-x-auto rounded-lg bg-gray-950 p-4 font-mono text-sm text-gray-300"><code>pnpm install</code></pre>
</div>
<!-- Step 3 -->
<div class="rounded-xl border border-gray-800 bg-gray-900 p-6">
<div class="mb-3 flex items-center gap-3">
<span class="flex h-8 w-8 items-center justify-center rounded-full bg-primary-500/20 text-sm font-bold text-primary-400">
3
</span>
<h3 class="font-semibold text-white">配置 API Key</h3>
</div>
<pre class="overflow-x-auto rounded-lg bg-gray-950 p-4 font-mono text-sm text-gray-300"><code>echo "ANTHROPIC_API_KEY=your-api-key" > .env</code></pre>
</div>
<!-- Step 4 -->
<div class="rounded-xl border border-gray-800 bg-gray-900 p-6">
<div class="mb-3 flex items-center gap-3">
<span class="flex h-8 w-8 items-center justify-center rounded-full bg-primary-500/20 text-sm font-bold text-primary-400">
4
</span>
<h3 class="font-semibold text-white">启动服务</h3>
</div>
<pre class="overflow-x-auto rounded-lg bg-gray-950 p-4 font-mono text-sm text-gray-300"><code>pnpm server:dev</code></pre>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="border-t border-gray-800 px-6 py-12">
<div class="mx-auto max-w-6xl text-center">
<p class="text-sm text-gray-500">
AI Terminal Assistant · Built with Claude API
</p>
</div>
</footer>
</Layout>
+23
View File
@@ -0,0 +1,23 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {
colors: {
primary: {
50: '#f0f9ff',
100: '#e0f2fe',
200: '#bae6fd',
300: '#7dd3fc',
400: '#38bdf8',
500: '#0ea5e9',
600: '#0284c7',
700: '#0369a1',
800: '#075985',
900: '#0c4a6e',
},
},
},
},
plugins: [],
};
+9
View File
@@ -0,0 +1,9 @@
{
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}