# projects.json 字段说明（schemaVersion 1.1）

站点根的 `projects.json` 是这份作品索引的**机器可读总清单**：纯静态 JSON，AI 可以直接抓取解析，人也能读。
它由 NAS 侧 `build-site.sh` 扫描作品根目录自动生成，**不要手工改**（下次构建会被覆盖）；要改内容就改各作品的 `project.meta` 与 `changes.md`。

- 顶层结构：`{ "site": {...}, "projects": [ {...}, ... ] }`
- 编码：UTF-8，无 BOM；不转义中文（保证人和 AI 都直接可读）
- 时间：日期一律 `YYYY-MM-DD`；`generatedAt` 为 `YYYY-MM-DD HH:MM:SS ±ZZZZ`
- 首页与项目页都是**构建时静态渲染**的（HTML 里直接有清单文字，不依赖 JavaScript）；`projects.json` 是同一份数据的机器可读形态。

## site 对象

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `title` | string | 站点名（来自配置 `SITE_TITLE`） |
| `description` | string | 站点一句话说明 |
| `url` | string | 站点根地址，来自配置 `SITE_DOMAIN`（唯一替换点），例：`https://你的域名`；未配置时为空串 |
| `owner` | string | 站点归属者备注，可为空 |
| `schemaVersion` | string | 本清单的结构版本号，字段增删时递增 |
| `schema` | string | 字段说明文件名（本文件） |
| `generatedAt` | string | 本次构建时间（设 `SOURCE_DATE_EPOCH` 可复现） |
| `generator` | string | 生成器标识，固定 `build-site.sh` |
| `counts` | object | `projects` 项目数、`timelineEntries` 迭代记录总条数 |
| `policy` | object | 站点三条既定口径：`assetsOnSite`（站内只放缩略图与直链）、`access`（先公开，需要时挂 Cloudflare Access）、`statusSource`（状态手工标注）、`lastModifiedRule`（最后更新时间的取值规则） |
| `notes` | string | 给阅读者/AI 的提示 |

## projects[] 元素

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `id` | string | 项目唯一标识，也是静态页文件名（`projects/<id>.html`）。只用小写字母、数字、`.`、`_`、`-` |
| `name` | string | 作品名称（显示用，可中文） |
| `summary` | string | 一句话用途说明 |
| `status` | string | 状态枚举：`active` 进行中 / `paused` 暂停 / `archived` 已归档 / `planned` 计划中 / `done` 已完成 |
| `statusLabel` | string | 上面状态的中文显示名（渲染用，AI 可直接用 `status` 判断） |
| `tags` | string[] | 标签数组，可为空数组 |
| `url` | string | 线上地址，无则空串 |
| `page` | string | 站内详情页相对路径，例：`projects/soft-toolbox.html` |
| `code` | string | 代码位置：仓库地址或本机路径，无则空串 |
| `files` | string | 文件位置：NAS / 坚果云上的目录，无则空串 |
| `run` | string | 怎么跑：一两句话，含关键命令或部署方式 |
| `deps` | string | 运行依赖（语言、服务、定时任务等） |
| `download` | string | 下载入口：直链或说明（大件放坚果云 / NAS，站上只给链接） |
| `thumb` | string | 缩略图相对路径；项目自带图会复制到 `assets/thumbs/`，没有则用 `assets/thumb-placeholder.svg` |
| `lastModified` | string | 最后更新时间 `YYYY-MM-DD` |
| `lastModifiedSource` | string | 这个日期来自哪里：`manual` / `git` / `files`（取值规则见下） |
| `timeline` | array | 迭代记录，按日期倒序 |

### lastModified 的取值规则（重要）

**手写值绝对优先**，顺序固定为：

1. `manual` —— `project.meta` 里手写了 `lastModified` 就用它，**不会被文件修改时间顶掉**（手标的日期就算数）；
2. `git` —— 没手写、且该目录是 git 仓库时，用末次提交日期；
3. `files` —— 两者都没有时，才用目录内（不含 `.git/`）文件的最新修改时间。

将来想把「完工」改成自动判定（例如 14 天无改动自动转 `archived`），改 `build-site.sh` 里这一小段判断即可，字段不用动。

### 缩略图约定（建议）

- 单张 **≤ 200 KB，长边 ≤ 640 px**，`.webp` / `.jpg` / `.png` / `.svg` 都行。
- 站上只放缩略图；原图、大图、视频放在坚果云 / NAS，站内用 `download` 给直链。
- 构建时按文件名复制进 `assets/thumbs/<id>.<扩展名>`；超过 200 KB 只打警告，不中止构建（Cloudflare Pages 免费档的硬上限是单文件 25 MiB，那个才中止）。

### timeline[] 元素

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `date` | string | 条目日期 `YYYY-MM-DD`；手写条目没写日期时为空串 |
| `text` | string | 一条迭代说明 |
| `source` | string | `手写`（来自 `changes.md`）或 `git`（来自提交记录，可关闭） |

## 给 AI 的读取建议

1. 先读 `projects.json` 定位项目（按 `id` / `name` / `tags` / `status` 过滤），再按需打开 `page` 指定的静态页看细节。
2. 要「最近在做什么」，把各项目 `timeline` 合并后按 `date` 倒序取前 N 条。
3. 「大件在哪」看 `download` 与 `files`：站内不放大文件，只有缩略图与直链。
4. 字段缺失即空串，不要当 `null` 处理；新字段只增不改，`schemaVersion` 变化时才需重新适配。
5. 首页 `index.html` 也是静态渲染的清单文本，抓不到 JSON 时直接读首页同样能看到项目名与一句话用途。

## 作品侧的输入文件（生成上面的字段）

`<作品根>/<作品目录>/project.meta`，每行 `键 = 值`，`#` 开头为注释，键不区分大小写：

`id` `name` `summary` `status` `url` `code` `files` `run` `deps` `download` `thumb` `tags`（逗号分隔） `order`（列表排序，小的在前，默认 100） `lastModified`（手写更新时间，写了就绝对优先）

`<作品根>/<作品目录>/changes.md`：迭代记录，每行以 `- ` 或 `* ` 开头，行首写 `YYYY-MM-DD` 会被自动识别为日期。
目录名以 `.` 或 `_` 开头会被跳过（例如飞牛同步的收件箱 `_inbox`）。
