这份手册讲的是:怎么往站点里加东西。 覆盖五种内容(项目 / 模块 / 蓝图 / 图集 / 文档)、图片处理、大文件规则、发布流程与排错。
站点是纯静态的,没有在线后台 —— 但有一个只在你本机跑的内容编写器 (
admin.bat,监听127.0.0.1:4322)。图片可以直接在编写器里多选上传, 文件落到public/media/下,它同时会替你量好宽高。 除此之外的内容仍然是磁盘上的文件:「上传」= 把文件放到content/里,然后构建。
目录
1. 先开服务
双击根目录的批处理文件即可,不用敲命令:
| 双击这个 | 干什么 | 地址 |
|---|---|---|
admin.bat |
写内容(本文的主角) | http://127.0.0.1:4322 |
dev.bat |
看成品(自动重建 + 起服务) | http://127.0.0.1:4321 |
preview.bat |
只构建一次再起服务(不自动重建) | http://127.0.0.1:4321 |
三点注意:
- 两个窗口都要开着,关掉窗口服务就停了。
dev/preview占同一个端口(4321)不能同时开;admin是 4322,可以和任一个并存。dev.bat会自动重建:你在编辑器里保存,它 1~2 秒后重新构建一次, 刷新浏览器就能看到新内容(窗口里会打一行✓ 重建完成)。 搜索索引也是每次重建都更新的,所以看搜索效果也用dev.bat就行。
写作时推荐组合:
dev.bat(4321 窗口)+admin.bat(4322 窗口)。⚠️ 别用
pnpm dev:hmr(那是 Astro 自带的开发服务器)。它对content/的改动 不可靠 —— 改完正文刷新,页面可能还是旧的,而且不报任何错。dev.bat走的是“改了就重新构建”,慢 1 秒但看到的一定是当前内容。
2. 通用规则(先读这几条)
2.1 slug 只用英文小写 + 连字符
slug 是网址的一部分,content/projects/tomato/ 就对应 /projects/tomato/。
✅ tomato marathon-volt dev-doc bp-farmer
❌ 番茄 Marathon_Volt 文档1 中文名
一旦发布不要改 slug —— 它就是这篇内容的身份,改了等于换了个人,外链和搜索记录全断。
例外:蓝图图表的文件名可以是 UE 那种大小写下划线风格
(BP_Farmer_Look、Blueprint_WallSconce_UserConstructionScript),因为它不进网址。
2.2 order 决定排序
数字小的排前面,同层级内比较。习惯上按 10、20、30 留间隔,方便以后往中间插。
2.3 draft: true = 存草稿
写一半的东西勾上草稿,构建时整篇不上线,不会在列表里出现,也不会被搜索索引。 写完了把勾去掉再构建。
2.4 图片必须写 w / h
<img> 不写宽高,图片加载完会把下面的内容顶下去(页面抖动,也就是 CLS 掉分)。
用第 10 节的脚本会自动量好给你。
2.5 单个文件不超过 25 MiB
这是托管平台的硬上限。超了必须走对象存储,见第 11 节。
2.6 编辑器里 Ctrl/Cmd + S 就是保存
有未保存改动时切走其它条目会拦你一下。覆盖已有文件前会自动备份到 tmp/admin-backup/。
3. 内容都放在哪
content/
projects/<项目slug>/
project.md 项目概述
modules/<模块slug>.md 功能模块
blueprints/<图表名>.t3d 蓝图原文(UE 里复制的)
blueprints/<图表名>.json 蓝图元数据(可选)
code/<模块slug>/<文件名> 该模块的源码(会显示在模块页「源码」区块)
mindmaps/<名称>.md 思维导图源(规划中,现在还不会显示)
gallery/<图集slug>/
album.md 图集元数据 + 图片清单
docs/<slug>.md 文档
models/<slug>.md 3D 模型元数据
public/
media/<图集slug>/*.webp 图片(加工后的)
media/models/*.glb 3D 模型本体
files/* 允许下载的文档副本
编辑器左侧就是按这六类分组的:项目 / 模块 / 蓝图 / 图集 / 文档 / 模型。 点右上角的「新建 XXX」建新的,点列表里的条目改已有的。
4. 项目
一个项目 = 一个目录,project.md 是它的门面。
用编辑器
- 左侧「项目」组右上角点 「+ 新建 项目」
- slug 填英文(如
tomato),确定 - 右侧表单填字段 →
Ctrl/Cmd + S保存
字段
| 字段 | 必填 | 说明 |
|---|---|---|
title |
✅ | 中文标题,如 钛心番茄 |
summary |
一句话摘要,出现在卡片和搜索结果里 | |
order |
排序 | |
cover |
封面图路径,如 /media/tomato/cover.webp(每件作品都要有封面) |
|
workspace |
UE 里的工作区名,如 U_Tomato |
|
engine |
默认 Unreal Engine 5 |
|
tech |
技术形态,一行一个:C++ / 蓝图 / 数据表 |
|
tags |
标签,复用为主,全站控制在 30 个以内 | |
status |
设计中 / 进行中 / 已完成 / 搁置 |
|
updated |
更新日期 | |
draft |
草稿 |
正文部分(--- 下面)就是普通的 Markdown:标题、表格、代码块、引用都能用。
kind: project 这个字段编辑器会自动填,不用管。
5. 功能模块
模块挂在某个项目下面,对应 /projects/<项目>/modules/<模块>/。
用编辑器
- 「模块」组点 「+ 新建 模块」
- 先下拉选一个项目,再填模块 slug(如
farming) - 填字段 → 保存
字段
和项目基本一样(title / summary / order / cover / workspace / engine / tech /
tags / status / updated / draft),区别是:
- 必须选所属项目(编辑器会写进
project:字段) - 不需要
kind,编辑器自动填成module
模块页会自动出现在所属项目的左侧导航里,不用手工维护目录。
附:把 C++ 源码放上去
模块页底部有一个「源码」区块,读的是这个目录:
content/projects/<项目>/code/<模块slug>/
例:
content/projects/tomato/code/farming/
FarmingComponent.h
FarmingComponent.cpp
PlantData.h
目录里有什么就显示什么,全部按 C++ 高亮,不用在正文里写代码块。
几条规矩:
- 目录名必须和模块 slug 一致 ——
modules/farming.md对应code/farming/,对不上就看不见。 - 跳过
_开头的文件 —— 不想让某个文件出现就给它加个下划线前缀。 - 目录不存在不会报错,只是模块页不显示「源码」区块。
把代码直接贴在模块正文里(用围栏代码块)也行,两种方式并存。
code/目录的好处是源码能在版本管理里单独跟踪,改代码不用动 md。
思维导图(暂未上线)
mindmaps/<名称>.md 这个位置在目录结构里留好了,计划用 markmap 直接渲染 Markdown 源文件。
但页面还没接(属于后续里程碑)—— 现在放进去不会显示,先别用它。
6. 蓝图(含 UE 里怎么复制)
蓝图是把 UE 里的节点图搬到网页上,能缩放平移、还能复制回 UE 里继续用。 这是这个站最特别的一环,步骤稍微多一点。
6.1 先在 UE 里复制
- 打开要存档的蓝图,进入目标图表(Event Graph / 构造脚本 / 函数 / 材质图……)
- 框选要存档的节点(想全要就
Ctrl + A) Ctrl + C—— 这时候剪贴板里已经是 T3D 文本了
复制出来的不是图片,是一段带节点坐标和连线信息的文本。 想确认的话,先粘到记事本里看一眼,应该能看到
Begin Object、NodePosX=、LinkedTo=(...)这类内容。
6.2 用编辑器粘贴
- 「蓝图」组点 「+ 新建 蓝图」
- 下拉选所属项目
- 填图表名(文件名,可以是
BP_Farmer_Look这种大小写下划线风格) - 在弹出的文本框里
Ctrl + V粘贴 T3D 原文 - 填字段 → 保存
编辑器会生成两个文件:<图表名>.t3d(原文)和 <图表名>.json(元数据)。
6.3 字段
| 字段 | 必填 | 说明 |
|---|---|---|
title |
✅ | 显示用的中文标题 |
graph |
图表全名,如 BP_Farmer:Look,显示在查看器标题栏 |
|
module |
归属到哪个功能模块(对应 modules/<文件名>,可留空) |
|
graphType |
BLUEPRINT / MATERIAL / MATERIAL FUNCTION / ANIM GRAPH —— 影响节点配色,材质图别选成 BLUEPRINT |
|
summary |
这张图做了什么(很有用,别空着) | |
order |
排序 | |
height |
画布高度 px,默认 640 | |
zoom |
初始缩放,负值 = 放大。留空交给库自己算 | |
tags / draft |
标签 / 草稿 |
source(T3D 原文)是构建时自动注入的,不要手写。
6.4 手写格式(不用编辑器时)
content/projects/tomato/blueprints/BP_Farmer_Look.t3d ← T3D 原文,直接整段贴进去
content/projects/tomato/blueprints/BP_Farmer_Look.json ← 元数据,可选
.json 长这样:
{
"title": "农民 · 视线检测",
"project": "tomato",
"graph": "BP_Farmer:Look",
"graphType": "BLUEPRINT",
"summary": "每帧从摄像机往前打一条射线,命中可交互物就更新准星。",
"order": 10,
"height": 720,
"tags": ["交互", "射线检测"]
}
没有 .json 也能跑 —— 标题会从文件名推断(下划线换成空格)。但建议写,摘要那一栏很有价值。
7. 图集
一个图集 = 一个目录 + 一份 album.md,页面上是「左边图片流、右边说明面板」。
7.1 图片怎么进去(编写器里多选上传)
打开 http://127.0.0.1:4322 → 左边选「图片」→ 选一个图集(或先新建)→ 在「图片清单」那一栏点 「⤒ 上传图片(可多选)」,一次把要用的图全选上。
上传时编写器自动做四件事:
| 自动做的 | 说明 |
|---|---|
| 落盘 | 写进 public/media/<图集slug>/,一次一张排队上传,哪张失败只报哪张 |
| 量宽高 | 直接读文件头(PNG / JPEG / WebP / GIF / AVIF),不用你右键看属性 |
| 改名 | 中文名 / 空格会被清成 ASCII 安全名;全中文名退回 img-<时间戳>;撞名自动加 -2 |
| 建条目 | 往 images: 里追加一条,默认「整栏、居中」 |
想用原始做法(先把图片批量加工成 1600px 的 webp 再入库)也行,见 7.4。
7.2 排版:顺序 + 占宽 + 对齐
编辑器里图片清单是三栏,从左到右各管一件事:
[① 清单(拖动排序)] [② 页面排布预览] [③ 这张图的表单(改文字/占宽)]
-
顺序 —— 拖动清单里的 ⠿ 手柄(或点 ▲ ▼)改行的先后。 清单里的行序就是页面上的顺序 —— 想让它出现在上面就往上拖。
-
占宽 —— 点任意一张,右栏选
layout:值 效果 full整栏,独占一行(默认) two-thirds占三分之二栏 half半栏 —— 连着 2 张半栏图并排 third三分之一 —— 连着 3 张并排 quarter四分之一 —— 连着 4 张并排 -
对齐 —— 比整栏窄的图,选
align:center/left/right。 只在“一行没排满”时有区别(铺满时靠左靠右长得一样)。
“哪几张凑一块”由“顺序 + 占宽”决定,不需要拖坐标:
- 连着几张同档位的图自动排成一行,档位一变就另起一行;
- 分组小标题会打断一行 —— 中间插了一条分组标题,两侧的图不会并排 (所以想让两张图并排,它们得在同一个分组里、并且挨着);
- 中栏那个「页面排布预览」实时按这套规则画出来,它长什么样,页面上就是什么样。
手机(窄于 860px)上一律退回整栏 —— 半宽的图在手机上没法看。
7.3 一次上传多张之后,还要做什么
不用给每张图补文字。alt 与 caption 都留空时,页面会退回用图集标题当替代文字,
figcaption 直接不渲染。只有你想给某张图加一句小注时才点开它填。
批量工具(都在「图片清单」那一栏):
| 按钮 | 干什么 |
|---|---|
| + 手动加一条 | 手工填一条(图已经在 public/ 里、只是没进清单时用) |
| 扫描 public/media/… | 把目录里已有的图列成条目(手建目录、或从别处拷图进来时用) |
| ⇅ 按文件名重排 | 按路径字母序重排(文件名是 01/02/… 时最有用) |
| ⟲ 自动补宽高 | 把清单里所有缺 w/h 的条目挨个从文件量一次 |
7.4 批量加工(把原图压成 1600px 的 webp)
要控制输出体积时用脚本(会生成 webp 并量好宽高、打印可直接粘贴的清单):
node scripts/ingest-gallery.mjs <源目录> <图集slug> [文件名前缀] [最大边]
例:
node scripts/ingest-gallery.mjs "C:/Users/KFC50/Desktop/Marathon" marathon-volt alex-tang-volt 1600
- 源目录只读,脚本绝不改动你的原图
- 输出到
public/media/<图集slug>/ - 跑完打印一段可直接粘贴的
images清单
7.5 PNG 会被自动减重(构建期)
PNG 可以直接传(上传、页面渲染、下载都没问题)。构建时会多跑一步
scripts/optimize-png.mjs:把 public/media/ 下超过 200 KB 的 PNG
转出一份同目录同名 .webp(带透明的走无损,不透明走有损 90),并打印一张对照表。
三点要知道:
- 它不删原 PNG、也不改你的
content/。转换是可逆的。 - 想用上小体积那份,就把该条目的
src从.png改成.webp(编辑器里改一下即可)。 构建日志会把“哪些图有更小的 webp 版本”列出来。 - 转换是幂等的:源图没变就不会重做,所以每次构建都跑也不会变慢。
7.6 字段
| 字段 | 必填 | 说明 |
|---|---|---|
title |
✅ | 图集标题 |
titleEn |
英文名,标题下方那行小字 | |
summary |
简介,显示在右侧面板顶部 | |
cover |
列表卡片的封面图路径 | |
order |
排序 | |
credit |
来源 / 版权说明 | |
tags |
标签 | |
images |
图片清单(见下) | |
updated / draft |
更新日期 / 草稿 |
panel(说明面板配色)已废弃:右侧面板现在直接吃站点的设计 token, 跟其它页面一套观感,页面上也不再提供配色调节。老的album.md里如果还留着panel:字段,构建不会报错、页面也不会读它,编辑器不再显示这个字段。
images(图片清单):
images:
- src: /media/marathon-volt/xxx.webp # 路径(相对 public/)
w: 1600 # 宽(上传/扫描时自动量好)
h: 900 # 高
alt: 图片内容的文字描述 # 可留空 —— 留空则用图集标题
caption: 图下小注 # 可留空 —— 留空则不渲染 figcaption
group: 携行箱 # 分组名,同组会插一个分组小标题
layout: half # full / two-thirds / half / third
align: center # center / left / right
数组顺序 = 页面上从上到下的顺序。w/h 仍然建议填全:不填的话图一加载完会把
下面的内容顶下去(首屏抖动),这也是上传时自动量高宽的原因。
8. 文档
一篇文档 = content/docs/<slug>.md,slug 即网址(foo.md → /docs/foo/)。
用编辑器
- 「文档」组点 「+ 新建 文档」
- 填 slug、填字段、写正文 → 保存
字段
| 字段 | 必填 | 说明 |
|---|---|---|
title |
✅ | 标题 |
summary |
摘要,列表卡片和页面描述都用它 | |
order |
排序 | |
format |
md / pdf / html / text —— 决定用哪个查看器(默认 pdf) |
|
docMode |
只读强度,见下 | |
file |
下载链接,相对 public/,如 /files/foo.md |
|
tags / updated / draft |
标签 / 更新日期 / 草稿 |
docMode 三档:
| 值 | 含义 |
|---|---|
public |
在线阅读 + 下载按钮 |
readonly |
能看不能拿(页面不提供任何下载入口)—— 默认 |
strict |
同上,另外禁右键、禁选中 |
下载按钮的出现条件是
docMode: public且file填了,两个条件缺一不可。 想给下载就把文件放到public/files/下(用英文文件名),再填file: /files/xxx.md。
侧栏目录是自动的
页面左边的目录从渲染结果算出来,不用手写。 手写一份目录也行(像自带那篇开发文档那样),但不必 —— 手写锚点容易和实际标题 id 对不上。
9. 3D 模型
一个模型 = 一个内容文件 + 一个 glb 文件:
content/models/<slug>.md ← 条目:标题、摘要、封面、指向 glb
public/media/models/<文件名>.glb ← 模型本体
两处的名字互相独立,靠 .md 里的 src: 连起来。详情页里模型能拖拽旋转、
滚轮缩放;右侧那些规格数字(面数、顶点数、包围盒)是构建时直接读 glb 算出来的,
不用你手填。
9.0 想改名字 / 加新模型,改哪几个地方
这是最容易问的一个问题,一次说清。<slug> 决定网址,所以改名 = 换网址。
| 你想改的 | 要动的地方 | 会不会换网址 |
|---|---|---|
| 显示标题(页面上那行大字) | .md 里的 title: |
不会 ✅ 最常改的就是这个 |
| 网址 / 文件名 | .md 的文件名(crate-01.md)→ 网址 /models/crate-01/ |
会 |
| glb 的文件名 | public/media/models/ 里的文件 + .md 里的 src: / cover: |
不会 |
| 封面图 | .md 里的 cover: |
不会 |
只改标题不用改名:
.md里写title: 我的新模型就够了, 文件名和网址可以一直叫sample-table之类的旧名字 —— 只是看起来有点怪。 改文件名(slug)才会换网址,旧网址会 404(站点没有重定向机制)。
要改 slug 时,四步一起做(漏一步就出现“模型打不开”或“封面破图”):
- 把
content/models/旧名.md复制成content/models/新名.md,再删掉旧的 (新建文件、别原地改名 —— 项目没有版本控制,出事了只有tmp/admin-backup/能救); - 把
public/media/models/旧文件.glb复制成新名字(glb 的名字跟 slug 一致最省心); - 打开新的
.md,把src:与cover:改成新路径; - 重新构建。首页第二屏的数据会自动跟着变(见下面 9.7), 不用再手改任何“详情页文案”。
加一个全新模型(crate-01 为例):
# 1) 把模型本体拷进去(英文名、连字符)
copy 我的模型.glb public/media/models/crate-01.glb
# 2) 编辑器里新建条目
# 「3D 模型」组 → 「+ 新建 3D 模型」→ slug 填 crate-01
# 然后「渲染用模型」那栏从下拉里选 /media/models/crate-01.glb
# 3) 填标题 / 摘要 / 封面 → 保存 → 构建
pnpm build
加完不用再改别的地方 —— 首页卡片、列表页、档案阵列都是从 content/models/
一份数据长出来的(9.7 讲的就是这条)。
9.1 先准备文件
模型本体放到:
public/media/models/<英文文件名>.glb
- 文件名用英文 + 连字符(
crate-01.glb),理由和图片一样(见 2.1)。 - 单个文件超过 25 MiB 就必须放 R2(见第 11 节),这时
src直接写完整 URL。 - 格式优先用
.glb(二进制 glTF,模型 + 贴图打包成一个文件)。.gltf+ 外挂贴图那种也能用,但要把贴图一起拷进去、路径还得对得上,容易漏 —— 导出时选「glb」最省事。
9.2 用编辑器
- 「模型」组点 「+ 新建 3D 模型」
- 填 slug(如
crate-01),确定 - 右侧表单里「渲染用模型」那栏,从下拉里选刚放进去的 glb
(编辑器会自动扫描
public/media/models/,所以先放文件再填这一栏) - 填字段 →
Ctrl/Cmd + S保存
顺序反过来也行(先建条目后放文件),但
src是必填 —— 空着保存不会立刻报错, 重新构建时才会指出来。
9.3 字段
| 字段 | 必填 | 说明 |
|---|---|---|
title |
✅ | 中文标题 |
titleEn |
英文名,卡片标题下面那行小字 | |
src |
✅ | 渲染用的模型文件:/media/models/xxx.glb,或 R2 的完整 URL |
srcNote |
这个文件做了什么处理,如「面数减半、贴图 2K WebP」 | |
original |
原始版文件(可选)。填了下载区会多出一行「原始版」 | |
originalNote |
原始版说明 | |
summary |
一句话摘要,卡片和搜索结果用它 | |
cover |
封面图。留空也能用 —— 列表卡片会用内置占位(深色渐变 + 线框立方体 + 面数) | |
viewer |
外观参数,见 9.4 | |
order / tags / updated / draft |
排序 / 标签 / 更新日期 / 草稿 |
为什么没有 stats(规格)字段:面数、顶点数、材质数、贴图数、包围盒、文件体积
全部是构建时读 glb 现算的,和文件永远一致,手填一份反而会写错、会过期。
(schema 里留了个 stats 覆盖位,只在模型放 R2、构建期读不到本地文件时才用得上,日常别碰。)
9.4 viewer(外观参数)
全部留空就用默认值,不影响渲染。 想微调时:
| 子字段 | 默认 | 说明 |
|---|---|---|
autoRotate |
false |
自动旋转。一页里几个模型同时转会互相抢注意力 |
orbit |
sphere |
sphere = 上下左右都能转;turntable = 只能绕圈,不会翻到脚底 |
zoom |
true |
允许滚轮缩放。关掉可以避免「鼠标停在模型上、页面滚不动」 |
theta |
0 |
初始水平角(度)。0 = 正对相机 |
phi |
初始俯仰角(度)。90 = 平视;小一点 = 从上往下看 |
|
radius |
初始距离。一般留空,由库按模型尺寸自动取 | |
exposure |
1 |
整体偏暗 / 偏亮时微调 |
shadow |
阴影强度。0 = 关掉地面阴影 |
|
environment |
环境贴图路径(HDR / EXR 的 public 路径)。留空用内置的中性棚拍光 | |
transparent |
true |
透明背景,模型直接坐在站点底色上 |
9.5 页面上是什么样
- 列表页
/models—— 封面卡网格。这一页不加载 3D 库,所以模型再多也不会拖慢列表。 - 详情页
/models/<slug>/—— 模型区滚进视口才开始加载 3D 库,加载时先显示一个转圈占位。 左下角工具条有三个按钮:重置视角 / 自动旋转 / 全屏。 - 规格表 —— 面数、顶点数、材质、贴图、包围盒、文件体积;空的行不显示。
- 下载区 —— 渲染版必有一行;
original填了才会多出「原始版」那一行。
手机上用手指转模型时,页面纵向滚动优先,不会出现「想滚页面结果在转模型」的尴尬。
3D 库是自托管的(放在
public/vendor/three-wire/),不走任何 CDN。 首次打开详情页会有约 0.9 MB 的下载,之后按 7 天缓存。这是它唯一“重”的地方。
9.5.1 视口画的是「形体 + 布线」
详情页的视口画两层:中性黏土的面 + glb 里真实的三角网特征边。 页面上没有「线框 / 实体」切换按钮 —— 两层同时画着,没有可切的。
为什么不做切换:DCC 里的“线框视图”是显示模式,不会导出进 glb,文件里只有三角面; 而作者要在这里展示的是“布线怎么撑起形体”,面与线缺一不可。 顺带三个好处:文件小、放大不糊、低模高模都能看清结构。
但线不能单独存在:只画线不画面,模型就只剩一团线条、完全看不出形状 (用户 2026-09-20 原话:“线框之间的面还是需要保留的,我只是想展示布线, 不是为了展示线框,这样完全无法看清模型的形状了”)。所以视口画的是两层:
| 层 | 是什么 | 怎么来的 |
|---|---|---|
| 面 | 中性黏土材质,只吃光照 | 直接复用 glb 的几何,不加载原始材质/贴图 |
| 线 | glb 里真实的三角网边 | EdgesGeometry,只取夹角超过 22° 的特征边 |
为什么不把三角剖分线全画出来:高模上那是几万条线,密到糊成一块剪影, 反而看不见形体。特征边阈值只保留“结构上的折线”,这才是布线该有的读法。 想看更密或更疏:改
public/vendor/three-wire/wireframe-runner.js里的edgeThreshold(默认 22 度,调小 = 线更多)。
9.6 手写格式(不用编辑器时)
content/models/crate-01.md
public/media/models/crate-01.glb
crate-01.md 长这样 —— 现成可读的例子是 content/models/chibi-model.md:
---
title: 货箱
titleEn: Supply Crate
summary: 一个低面数货箱,用来测贴图和金属度的表现。
order: 10
cover: /media/models/crate-01.webp
src: /media/models/crate-01.glb
srcNote: Draco 压缩后 1.2 MB,贴图 2K WebP
tags: [道具, 硬表面]
viewer:
autoRotate: true
orbit: turntable
phi: 75
updated: 2026-09-16
---
正文就是普通 Markdown —— 建模过程、参考图、遇到的坑,都能写。
9.7 首页那一屏会自动跟着变(不用手改)
首页第二屏(滚轴选页 + 三维档案阵列)里的卡片、右侧说明面板、点进去的详情,
全部来自 content/ —— 你改完内容重新构建,它就跟着变了。
具体是这样接起来的:
| 环节 | 谁产出 | 什么时候 |
|---|---|---|
content/ |
你写的(唯一真源) | 随时 |
| 档案数据 | spike/three-archive/gen-archives.mjs 从 content/ 收一遍 |
每次构建时自动跑 |
| 首页右栏 / 卡片 / 无 WebGL 回退 | 读那份档案数据 | 页面加载时 |
| 三维层(1.2 MB 的包,数据是烘进包里的) | 上一步数据变了才重出 | 同上,自动 |
所以:加了模型 / 删了图集 / 改了标题 → 跑一次 pnpm build 就够,
不需要去别的地方“同步写一遍”。构建日志里会看到这一行:
档案同步:首页数据已是最新(6 条),无需重出
如果它说的是「内容变了,重出首页三维包」,就说明这次真的重新生成了一遍 (数据一样时不会重出,所以日常构建不会因此变慢)。
以前这一步是手动的,而且漏了会很难查:内容删了首页还在讲旧的, 或者两份数据下标错位 —— 点卡片显示别的项目、详情跳错页。 现在它是构建流水线里的一步,并且会自检“两份数据是不是同一代”。
10. 图片处理流水线
图片是唯一需要“加工”一步的内容,单独说清楚。能自动的都自动了, 下面从“最省事”到“最可控”排三种做法。
最省事:在编写器里多选上传(推荐)
admin.bat → 「图片」→ 选中图集 → 「⤒ 上传图片(可多选)」。
落盘、量宽高、清洗文件名、追加条目全部自动,见 7.1。
- 超过 25 MiB 的图会被拒(Cloudflare Pages 单文件上限),报错里会写清是哪一张。
- 上传的是原图尺寸。想要小体积,见下面的减重与批量两条。
自动减重:构建期给大 PNG 补一份 webp
构建流水线里的一步,不用你手动跑(也可以单独跑 pnpm images:optimize):
| 命令 | 干什么 |
|---|---|
pnpm images:optimize |
超过 200 KB 的 PNG → 同目录同名 .webp(幂等,源图没变就跳过) |
pnpm images:optimize --force |
不管有没有,全部重转 |
pnpm images:optimize --min=120 |
阈值改成 120 KB |
pnpm images:optimize --dry |
只报告,不写文件 |
它不改 content/:转换结果只是多出一个同名 .webp 文件,想用就自己把
src 改成 .webp(PNG 原图可以留着供下载)。构建日志会列出所有“已经有更小
webp 版本”的图。
批量(图集用这个)
node scripts/ingest-gallery.mjs <源目录> <图集slug> [文件名前缀] [最大边]
| 参数 | 说明 |
|---|---|
| 源目录 | 原图所在目录,只读,不会被改动 |
| 图集slug | 输出到 public/media/<slug>/,和 content/gallery/<slug>/ 对应 |
| 文件名前缀 | 只挑文件名以它开头的图(留空 = 全部) |
| 最大边 | 长边缩到多少 px,默认 1600 |
跑完会打印 JSON 片段,直接贴进 album.md 的 images: 里。
单张(封面图之类)
手工放到 public/media/<图集>/ 也行,但必须自己确认宽高并写进引用它的地方。
编辑器里「封面图」那栏填的是路径(如 /media/tomato/cover.webp),
项目卡片的封面裁切由页面处理,不用你裁。
三条规矩
w/h一定要写 —— 不写就让页面抖(CLS)。 上传与扫描会自动量;清单里剩下的空值可以用「⟲ 自动补宽高」一键补。- 文件名用英文 + 连字符 —— 中文名进 URL 会变成一长串
%E4%B8%AD%E6%96%87,难看且容易出错。 (上传时会自动清成 ASCII 安全名,但自己命名时也照这条来。) - 原图留在源目录,别往仓库里塞 —— 加工后的 webp 才进
public/。
11. 大文件:什么时候必须走对象存储
单个文件超过 25 MiB 就必须放对象存储(R2),不能进前端仓库 —— 这是托管平台的硬上限, 也是整套架构的分界线。
现实情况(重要):
- 目前对象存储的流水线还没接(属于 M4)。所以在接上之前,大于 25 MiB 的文件暂时放不进来。
- 现在能放的:图片(加工后)、中小体积的 PDF、文本类文档、T3D 蓝图文本(通常几十 KB~几 MB)。
- 视频、大模型文件、大音频要等 M4 完成,或者你先把文件放别处、页面里写外链。
遇到大文件时别硬塞 —— 构建会失败,而且就算塞进去了,访客下载也会烧掉带宽。
12. 发布
内容写完,看一遍,再发布。
本地看
双击 preview.bat(它会先构建再起服务),打开 http://127.0.0.1:4321。
写内容的过程中用 dev.bat 更顺手。
用编辑器改完,预览里为什么没变?
这是踩得最多的一脚,先说清楚:
编辑器保存的只是磁盘上的源文件;预览服务读的是 dist/(构建产物)。
两者之间隔着一道「构建」。所以改完保存后:
- 预览页还是旧的 —— 正常,不是保存失败;
- 刷新也没用 —— 因为
dist/里那份文件从头到尾没被改过,刷新多少次读到的都是同一份旧产物。
正确的看法是这三步:
- 编辑器右上角会出现黄色提示 「有改动未构建」(看到它 = 保存成功了,但站点还没更新);
- 点它旁边的 「重新构建」,等 8~15 秒(按钮转圈时别重复点,服务端同一时刻只跑一个构建);
- 提示消失后,再去点「预览这一页」,这时才是你改完的版本。
想让保存即时生效(不用点构建),就用
dev.bat起开发服务器——它边写边热更新。 代价是 dev 模式下搜索是哑的(Pagefind 索引只在构建期生成), 而且要另开一个端口。日常改排版用 dev,验收成品用 preview。
上线的流程
pnpm build # 构建到 dist/(含 Pagefind 搜索索引)
pnpm run deploy # 推到 Cloudflare Pages(⚠️ 必须带 run,裸的 pnpm deploy 是 pnpm 内置命令)
或者双击 deploy.bat(两步连做)。
首次部署需要先做一次授权:
npx wrangler login(会弹浏览器)。 这个只做一次,之后deploy.bat就能直接用。
构建失败怎么办
构建是校验关卡,字段写错会直接失败并指出是哪个文件哪一行 —— 这是好事, 总比上线后页面空白强。根因几乎都是下面几条。
13. 排错对照表
| 现象 | 原因 | 怎么修 |
|---|---|---|
构建报 title 相关错误 |
必填字段没填 | 补上 title |
| 构建报枚举值不匹配 | status / graphType / docMode / format 写成了别的值 |
对照第 4、6、8 节的取值表改 |
| 新建条目被拒 | slug 不是英文小写+连字符 | 改名,或用编辑器建(它会校验) |
| 内容保存了但页面没变 | preview 是静态服务,读的是 dist/;源文件改了但没重新构建 |
编辑器点「重新构建」,或重新 pnpm build;想边写边看用 dev.bat |
| 改完保存、刷新也还是旧版 | 同上 —— 刷新读的还是那份没变的 dist/,不是缓存问题 |
别刷了,去点「重新构建」 |
| 搜索搜不到新内容 | 同上,索引是构建期产物 | 重新构建后才有 |
| 图片裂开 | 路径写错,或图不在 public/ 下 |
路径要以 /media/... 开头,且文件真实存在 |
| 页面加载时跳动 | 图片没写 w / h |
用第 10 节的脚本重新生成清单 |
| 蓝图页空白 / 提示没有蓝图 | .t3d 没放对位置 |
必须在 content/projects/<项目>/blueprints/ 下 |
| 蓝图节点位置乱 | 粘贴时 T3D 被截断 | 重新完整复制粘贴 |
| 模型页一直转圈 / 显示不出来 | src 指向的 glb 不存在,或路径没以 /media/... 开头 |
确认文件真在 public/media/models/ 下、文件名大小写一致 |
| 构建报「找不到模型文件」 | src 填了本地路径但文件不在 |
要么把文件放上去,要么把 src 改成 R2 完整 URL |
| 手机上看模型翻不了 / 页面滚不动 | 手指都用在转模型了 | 这是刻意设计(纵向滚动优先);想翻到脚底把 viewer.orbit 设为 sphere |
| 模型太暗 / 发白 | 环境光与模型材质不搭 | 调 viewer.exposure(默认 1,往大调更亮) |
| 详情页第一次打开有点慢 | 3D 库约 1 MB,只在这一页加载 | 正常,之后按 7 天缓存;列表页不受影响 |
构建报找不到模块(Cannot find module) |
依赖装了一半 | 跑 pnpm check:deps 看,再 pnpm install --force |
| 未知栏目 / 404 | slug 和目录名对不上 | 检查 content/ 下的目录名 |
一个万能自检
pnpm check:deps # 依赖是否完整
pnpm build # 内容是否合法(会逐个文件报错)
14. 上传前检查清单
每加一件东西,过一遍这个:
- slug 是英文小写 + 连字符的,且没有改过已发布内容的 slug
-
title填了 -
summary填了(它会进搜索结果,别浪费) -
order给了个合适的值 - 有封面(项目 / 模块 / 图集;模型篇可以没有,会自动用内置占位)
- 图片写了
w/h,文件名是英文 - 模型篇:
src指向的 glb 真的在public/media/models/下、文件名大小写也对 - 单文件没超过 25 MiB(模型同理)
- 标签是复用已有的,没在造第 31 个
- 写完了就把
draft取消掉 -
pnpm build过了 - 在
preview里实际点开看过(别只看构建成功)
附:速查
# 服务
admin.bat # 写内容 → 127.0.0.1:4322
dev.bat # 边写边看 → 127.0.0.1:4321(无搜索)
preview.bat # 看成品 → 127.0.0.1:4321(有搜索)
# 命令
node scripts/ingest-gallery.mjs <源目录> <图集slug> [前缀] [最大边] # 图片批处理
pnpm docs:sync # 开发文档 → content/docs/
pnpm check:deps # 依赖体检
pnpm build # 构建(含内容校验)
pnpm run deploy # 部署(⚠️ 必须带 run)