Files
lab/ts/web/nextjs/polaris/docs/design/Polaris 项目说明文档.md
T
labmem 2c674b7f2d
Polaris CI / verify (push) Successful in 1m47s
feat(polaris)!: 改为 standalone 容器运行
将 Next.js 构建切换为 standalone,新增多阶段 Docker 镜像和非 root 运行。
CI 改为启动服务检查页面与静态资源,并移除静态导出校验。

BREAKING CHANGE: 不再生成 out/ 静态产物,部署需运行 Node 服务容器。
2026-10-12 17:42:26 +08:00

436 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Polaris 项目说明文档
> 关联文档:
> [Polaris 首页设计文档](./Polaris%20首页设计文档.md) —— 首页布局、搜索交互、视觉规范
> [Polaris 分类页设计文档](./Polaris%20分类页设计文档.md) —— 分类页布局、卡片、状态设计
>
> 配套资产:[占位 Logo](../../public/brand/logo.svg)(后续替换)、[Polaris 页面设计图](../assets/Polaris页面设计图.png)
## 1. 项目名称
**Polaris**
Polaris 原意为“北极星”,具有方向、导航、定位、指引的含义。该名称与个人导航站的产品定位非常契合。
本项目的核心目标是打造一个轻量、安静、高效、可长期维护的个人导航站,用于集中管理和访问常用网站、分类资源与个人入口。
---
## 2. 项目定位
Polaris 不是后台管理系统,也不是信息密集型 Dashboard。
> 一个具有个人审美、可自定义背景、可快速搜索和访问常用链接的浏览器起始页。
产品形态是**动态可配置的个人导航站**:第一版可以静态部署,数据写死在本地配置中,但页面具有动态交互(实时时钟、搜索、侧边栏折叠、快捷键等),后续将本地数据源替换为 Go 后端 API,不需要推翻前端整体结构。
---
## 3. 项目目标
第一阶段目标:
1. 提供一个美观的个人导航首页;
2. 支持快速访问常用链接;
3. 支持左侧分类导航与分类页;
4. 支持搜索框(Google / Bing / GitHub / 本地);
5. 支持当前时间与日期展示;
6. 支持左侧边栏折叠(状态持久化);
7. 支持背景自定义的设计预留;
8. 数据第一版写死在本地 JSON 数据配置中;
9. 代码结构预留未来接入 Go 后端接口的能力。
后续阶段目标:
1. 从 Go 后端 API 获取链接、分类、设置等数据;
2. 使用数据库持久化配置;
3. 支持后台或隐藏入口管理链接;
4. 支持用户自定义背景;
5. 支持主题色、深色模式、导入导出、移动端适配等个性化能力。
---
## 4. 已确认的关键决策
以下决策已于 2026-08-21 确认,作为实施基线:
| # | 决策项 | 结论 |
| --- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| 1 | 技术栈 | 以本文档为准:Next.js + TypeScript + Tailwind CSS + shadcn/ui + lucide-react(早期 README 中 Astro 设想作废) |
| 2 | 渲染策略 | 静态导出(`output: 'export'`),时钟/搜索/侧边栏折叠为 client 岛屿 |
| 3 | 包管理器 | pnpm |
| 4 | 项目位置 | `ts/web/nextjs/polaris/` |
| 5 | Logo | 现有 `logo.svg` 仅作占位,后续替换,不投入设计精力 |
| 6 | 数据载体 | 本地 JSON 数据文件(`links.json` / `categories.json` / `settings.json`),结构以 Vitest 校验测试兜底 |
| 7 | 第一版数据范围 | 「开发工具」15 条 + 首页常用链接 8 条(跨分类条目归入真实分类,见首页设计文档 8.1);设计资源、学习资料、生活服务三个分类展示空状态 |
| 8 | 默认背景 | CSS 低饱和青蓝渐变,不依赖图片资产;`settings.background.type` 预留 `image` |
| 9 | 深色模式 | 第一版不做;CSS 变量按双主题友好组织,后续以 `[data-theme="dark"]` 覆盖 |
| 10 | 移动端 | 第一版不适配,仅桌面端 |
| 11 | 快捷键 | Ctrl+K(macOS ⌘K)聚焦搜索框,仅首页生效;提示按平台检测显示 |
| 12 | 图标方案 | 四级 fallback:自定义图标 → Simple Icons / 本地图标库 → favicon 自动抓取 → 文字占位(见 6.4) |
| 13 | 后端数据库 | PostgreSQL(后续阶段);表结构不锁死 PG 特有语法(JSON 存通用 TEXT) |
| 14 | 搜索行为 | 按模式严格分离:选中哪个引擎就只用哪个引擎搜索(回车直跳);仅 Local 模式显示本地结果下拉 |
2026-10-12 部署阶段将第 2 项由静态导出调整为 standalone 服务端输出;当前实现见第 5.2 节。初期内容仍在构建时从本地 JSON 生成,在线数据管理需后续接入后端。
---
## 5. 技术选型
### 5.1 前端技术栈
```text
Next.js(App Router)
TypeScript
Tailwind CSS
shadcn/ui
lucide-react
simple-icons
```
说明:
1. **Next.js**:适合从静态页面起步,后续接入动态接口;
2. **TypeScript**:适合维护链接、分类、配置等结构化数据;
3. **Tailwind CSS**:适合快速实现玻璃感、响应式、主题变量;
4. **shadcn/ui**:用于基础组件,例如 Button、Input、Dialog、Command;
5. **lucide-react**:用于侧边栏和操作图标;
6. **simple-icons**:品牌图标库(npm 包,按 slug 引用)。
测试:Vitest + Testing Library,重点覆盖纯函数(问候语分段、本地搜索匹配排序、icon 解析、搜索 URL 模板)与核心交互组件。
### 5.2 渲染策略
采用 standalone 输出:
```ts
// next.config.ts
const nextConfig = { output: "standalone" }
```
- 首页与全部分类页在构建时从本地数据生成(`generateStaticParams`);
- 时钟、搜索、侧边栏折叠等交互以 client 组件实现;
- 构建产物为 `.next/standalone` 下的最小 Node 服务器(`server.js`),`public` 与 `.next/static` 需按 Next 文档在部署时手动复制;
- 后续接 Go 后端时,改造 service 层为 fetch,数据页面改为客户端获取。
### 5.3 数据访问层
**页面组件不直接依赖数据文件,一律通过 service 层获取数据。**
第一版:
```ts
import links from "@/data/links.json"
export function getLinks(): NavLink[] {
return links
}
```
后续接 Go 后端时,只需要修改 service 层:
```ts
export async function getLinks() {
const res = await fetch("/api/links")
if (!res.ok) {
throw new Error("Failed to fetch links")
}
return res.json()
}
```
这样页面组件不用大规模重构。
JSON 缺少编译期类型检查,用 **Vitest 数据校验测试**兜底:校验 `data/*.json` 满足 `Category[]` / `NavLink[]` / `AppSettings` 结构(必填字段齐全、类型正确、categoryId 存在于分类中),字段拼错或漏填在测试阶段即报错。
### 5.4 后端技术栈(后续阶段)
后端由 Go 实现,数据库 PostgreSQL。
候选组合:
```text
Go + Chi + sqlc —— 结构清晰、SQL 可控(推荐)
Go + Gin + GORM —— 开发更快、上手成本低
```
---
## 6. 数据结构
### 6.1 分类数据
```ts
{
id: "dev",
title: "开发工具",
description: "高效的开发工具和资源集合,提升你的开发体验和效率。",
icon: "code",
sort: 20
}
```
字段说明:
```text
id: 分类唯一标识
title: 分类名称
description: 分类描述(分类页标题下方展示)
icon: 图标名称(侧边栏,lucide 图标名)
sort: 排序值
```
### 6.2 链接数据
```ts
{
id: "github",
title: "GitHub",
description: "全球最大的代码托管平台",
url: "https://github.com",
icon: "si:github",
categoryId: "dev",
pinned: true,
pinnedSort: 10,
keywords: ["代码", "仓库", "开源", "git"],
sort: 10
}
```
字段说明:
```text
id: 链接唯一标识
title: 链接名称
description: 简短描述(分类页卡片展示,8-16 个中文字)
url: 链接地址
icon: 图标引用,格式见 6.4
categoryId: 所属分类
pinned: 是否展示在首页常用链接区域
pinnedSort: 首页常用链接顺序(仅 pinned 为 true 时必填)
keywords: 本地搜索关键词
sort: 分类内排序值
```
### 6.3 设置数据
```ts
{
siteName: "Polaris",
theme: "light-glass",
search: {
defaultProvider: "google",
enabledProviders: ["google", "bing", "github", "local"]
},
background: {
type: "gradient", // "gradient" | "image"
url: null, // type 为 image 时生效
blur: 8,
overlay: 0.32
},
sidebar: {
defaultCollapsed: false
}
}
```
### 6.4 统一图标方案
链接卡片与搜索结果的图标按以下顺序解析,逐级 fallback:
```text
1. 自定义图标 icon: "local:xxx" → public/icons/xxx.svg
2. Simple Icons icon: "si:github" → simple-icons npm 包按 slug 渲染品牌图标
3. favicon 抓取 icon: "url:https://..." → 远程图标;icon 缺省时按链接 url 域名自动抓取
4. 文字占位 以上全部失败 → 标题首字符 + 圆角玻璃底兜底
```
侧边栏分类导航图标使用 lucide-react(如 `home` / `code` / `palette`),不参与此 fallback 链。
---
## 7. 未来 Go 后端 API 设计
后续接入 Go 后端时,推荐 API:
```text
GET /api/categories
GET /api/links
GET /api/links?categoryId=dev
GET /api/settings
```
后续管理能力:
```text
POST /api/links
PUT /api/links/:id
DELETE /api/links/:id
POST /api/categories
PUT /api/categories/:id
DELETE /api/categories/:id
PUT /api/settings
```
背景相关:
```text
POST /api/backgrounds
GET /api/backgrounds
PUT /api/settings/background
```
---
## 8. 数据库表设计草案
> 草案使用跨数据库通用语法;JSON 以 TEXT 存储。PostgreSQL 下可将 `TEXT`(存 JSON)字段替换为 `JSONB` 以获得索引与查询能力。
### 8.1 categories
```sql
CREATE TABLE categories (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
icon TEXT NOT NULL,
sort INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
```
### 8.2 links
```sql
CREATE TABLE links (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
url TEXT NOT NULL,
icon TEXT,
category_id TEXT NOT NULL,
pinned BOOLEAN NOT NULL DEFAULT FALSE,
pinned_sort INTEGER,
keywords TEXT NOT NULL DEFAULT '[]', -- JSON 数组字符串
sort INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_links_category
FOREIGN KEY (category_id)
REFERENCES categories(id)
);
```
### 8.3 settings
```sql
CREATE TABLE settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL, -- JSON 字符串
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
```
---
## 9. 开发阶段
### 第一阶段:前端静态版(当前阶段)
目标:完成首页与分类页基础体验。
```text
页面背景(CSS 渐变)与背景遮罩
左侧边栏 + 折叠(localStorage 持久化)+ 导航选中态
时间 / 日期 / 问候语
搜索框(Google / Bing / GitHub / 本地搜索)+ Ctrl+K 快捷键
常用链接(pinned,8 个)
分类页路由 + 卡片网格 + 空状态
桌面端响应式
```
数据范围:开发工具 15 个链接 + 首页 8 个常用链接(跨分类条目归入真实分类:ChatGPT → AI 工具,Gmail / Notion → 效率工具,YouTube / Bilibili → 娱乐媒体);设计资源、学习资料、生活服务三个分类展示空状态。
### 第二阶段:数据补全与体验完善
```text
补全其余 6 个分类的链接数据
深色玻璃主题
移动端 / 平板适配
```
### 第三阶段:数据访问层抽象
```text
封装 links / categories / settings service
统一 TypeScript 类型
统一错误处理与 loading 状态
```
此阶段完成后,页面组件不直接关心数据来自本地配置还是 API。
### 第四阶段:Go 后端 API
```text
Go API 服务
数据库连接(PostgreSQL)
links / categories / settings 查询接口
基础 CORS 配置
Docker 部署预留
```
### 第五阶段:管理能力
```text
新增 / 编辑 / 删除链接
调整排序、修改分类
修改背景、主题
导入导出配置
```
管理能力不进入首页主视觉,放到独立管理页或隐藏入口。
---
## 10. 项目目录结构
```text
polaris/
public/
icons/ # 自定义 SVG 图标(icon: "local:xxx" 引用)
logo.svg
src/
app/
layout.tsx # 背景层 + 字体
page.tsx # 首页
categories/
[categoryId]/
page.tsx # 分类页(generateStaticParams 生成)
globals.css # Tailwind + CSS 变量(浅色值,深色预留覆盖位)
components/
layout/ # AppShell / Sidebar / SidebarNavItem
home/ # ClockGreeting / SearchBox / SearchResults / QuickLinks
category/ # CategoryHeader / ToolGrid / ToolCard
common/ # SiteIcon(四级 fallback)/ GlassPanel
data/ # links.json / categories.json / settings.json
services/ # links.ts / categories.ts / settings.ts / search.ts
types/ # navigation.ts / settings.ts / search.ts
lib/ # cn.ts / greeting.ts / local-search.ts / search-providers.ts / icon.ts(纯函数)
next.config.ts # output: 'standalone'
vitest.config.ts
```
---
## 11. 最终目标
> 打开 Polaris 后,用户可以立刻搜索、立刻访问常用网站,也可以通过侧边栏进入分类页面。页面具有个人氛围,但保持干净、高效、克制。第一版数据本地配置,后续平滑切换到 Go 后端。