RhineLabUI 拆解
RHINELAB UI TEARDOWN
状态:🟢 现行 · 最后核对 2026-09-18 上游:RhineLabUI(MIT License,Copyright (c) 2026 LBEILC)· 本地副本
G:\refs\RhineLabUI我们的复刻:blog/terminal/,已上线 https://blog.manana.icu/terminal/ · 授权逐项说明见blog/terminal/NOTICE.md环境:Blender 4.5.9 便携版在G:\refs\tools\blender-4.5.9-windows-x64\(重建模型用,不必装进系统)
0. 结论(三句话)
- 资产:只有三类能直接用 —— MiSans 字体包、三轨原创配乐、自建模型 + 建模脚本。其余要么是商业授权(Novecento)、要么明确"未声明 MIT"(GLB 成品、参考图、verification 产物)、要么带原作采样(PV 打字音)。
- 思想:最值钱的两样是 「内容与代码分离 + 硬校验器」 和 「50 个
check-*.mjs客观断言」 —— 这两样恰好是我们最缺的(我们的内容是.char/剧情/记忆,校验是事后审计;前端几乎没有断言)。 - 技术:挑自包含模块搬(滚动数字、HUD 投影、惯性拖拽、字形烘焙、PWA 更新流);聊天主界面不加 3D;主前端不引入构建(
frontend/的无构建发布是优点,别为一个动效破掉)。
一句话:抄资产是省钱,抄思想是补课,抄交互要看场景 —— 我们不缺三维能力,缺的是把内容变成可用数据的管线和能自动验的工程习惯。
1. 他们用了什么(客观清单)
1.1 规模
| 项 | 数字 |
|---|---|
| TS 源码 | 74 个文件 / 12,690 行(最大:scene.ts 1840、main.ts 1273、audio.ts 664、model-viewer.ts 622) |
| CSS | 14 个(style / theme / startup / responsive / decryption / workbench / wallpaper* …) |
| 自验脚本 | 50 个 check-*.mjs,scripts/ 合计 4,866 行 |
| 内容 | content/archives.json 36.5 KB(页面与 TXT 下载共用) |
| 资源 | GLB 3.4 MB + 3.2 MB、音频 3.0 MB、MiSans 字体 26 MB |
| 构建 | npm run build ≈ 2.1–2.4 秒,产物 818 文件 / 33.8 MiB |
1.2 架构骨架:一个 canvas + DOM HUD
三维场景全部画在单个 WebGL canvas 里,文字/按钮/面板是真正的 DOM(所以排版质量和可访问性都在)。两者用坐标投影对齐:
hud-projection.ts(126 行)—— 把 DOM 元素投到三维坐标上;左上品牌区、右下POWERED BY、底部键位提示都靠它定位。viewport-layout.ts/viewer-camera.ts—— 参考坐标系在影片与 1920×1080 之间保持精确。shared-depth.ts/glass-reveal.ts/inspection-overlay.ts—— 磨砂玻璃揭示、检视覆盖层、共用深度。
这是"3D 界面还能读"的关键分工,也是我们将来做任何三维视图时唯一正确的起点。
1.3 内容管线:内容与代码彻底分离
content/archives.json 一个文件决定全部档案;src/data.ts 只有 35 行类型与阵列位置映射:
export function fileLocation(index) { // 三维墙上的物理位置
const lane = archiveColumns.indexOf(records[index].category);
const row = 12 + columnFiles(lane).indexOf(index);
return { lane, row, slot: lane * 32 + row }; // ⇒ 每列上限 20 格
}
scripts/archive-content.mjs 是硬校验器:分类必须 5 个且两处同名、id 必须按序、source 必须是合法 http/https、每列条数达标;校验失败就不写任何下载文件。
content/README.md 把"改内容"写成了一套流程:改 JSON → npm run export:archives 校验并导出 TXT → npm run check:content 检查一致性 → npm run build。prebuild 钩子会自动跑校验与导出。
1.4 自验套件(50 个 check-*.mjs)
每个特性一个脚本,playwright 驱动,做客观断言而非人眼比图。覆盖到很细:check-archive-drag(拖拽)、check-archive-momentum(惯性)、check-decryption(解密时序)、check-boot-hud、check-font-loading、check-pwa-recovery、check-responsive、check-model-precision、check-bottom-insets、check-hud-performance……
还有 summarize-performance.mjs / summarize-model-precision.mjs 把结果汇总成报告。
1.5 授权与合规(三层,写得很规范)
| 层 | 内容 | 结论 |
|---|---|---|
| 代码 / 建模脚本 / 技术文档 | MIT © 2026 LBEILC | ✅ 可商用可闭源,须保留声明 |
| 资源(GLB、图片、动图) | "未另行声明为 MIT" | ❌ 成品不可搬;但建模脚本是 MIT,可自行生成 |
| 第三方 | MiSans(小米,带许可 PDF)✅ / Rolling Number(MIT)✅ / Novecento Sans Wide(商业) ❌ / PV 打字采样 ❌ | 逐项区分 |
README 甚至写明「《明日方舟》相关名称、标志、设定、原 PV 和原作视觉设计,不因本项目公开而获得额外授权」——这套"逐项声明 + 明确划出不可授权部分"的写法,值得我们以后开源/公开任何东西时照抄。
1.6 我原本没预料到的部分
| 模块 | 是什么 |
|---|---|
archive-play-motion.ts + archive-playground.ts |
互动关卡:RelayRound / SpectrumEnvelope / score / status: preparing→…,一套节奏判定玩法 |
workbench.ts + workbench-state.ts + workbench-rolling.ts |
工作台模式:番茄钟(focus / break 状态机)+ 滚动文字面板 |
wallpaper*.ts(8 个模块) |
Wallpaper Engine 集成:属性同步、宿主检测、图像 URL 处理、画质分档、开场衔接 |
typing-rhythm.ts |
打字节奏与开场动画同步(决定何时发按键音) |
quality-renderer.ts / render-quality.ts / render-state.ts |
画质分档与"精确状态比较,绝不跳变/吸附动画" |
2. 逐项对照:抄不抄 / 用在哪 / 成本
A. 可直接搬(自包含,不依赖上游资源)
| 模块 | 它做什么 | 搬到哪 | 成本 |
|---|---|---|---|
rolling-clock.ts + workbench-rolling.ts + 打过补丁的 @kitlangton/rolling-number |
滚动数字/文字(补丁修了投影 HUD 的字形测量) | 好感度、天数、统计、终端标题 | 低 |
pwa.ts 的更新流(配合 build-pwa.mjs) |
"新版本已就绪 / 更新并重启" | frontend/sw.js —— 我们被缓存坑过多次 |
低 ★ |
ui-transitions.ts |
统一缓动常量(cubic-bezier(0.22,1,0.36,1)) |
frontend/css/ 过渡统一 |
低 |
screen-finish.ts |
SVG 滤镜做屏幕质感(把 WebGL + DOM 一起处理) | 全局质感层 | 低 |
brand.ts 的光学间距做法(analysisPositions 手工调字距,逐字母绝对定位) |
让固定字标排版精准 | 品牌标题 / 干员名 | 低 |
html.ts |
纯文本转义 + HTML 属性安全 | 前端渲染用户输入 | 低 |
hud-projection.ts |
DOM 元素投到三维坐标 | 将来做三维视图时的 HUD 对齐 | 中 |
archive-drag.ts |
拖拽的投影反解 + 惯性动量(ArchivePlaneMomentum) |
消息列表 / 联系人的横向滑动 | 中 |
boot-lettering.ts + scripts/make-boot-lettering.py + boot-lettering-art.json |
字体烘焙成 SVG path → 逐字符描边动画 | frontend/intro.html 开场 —— 成本低、质感提升最大 |
中 ★ |
50 个 check-*.mjs 的方法论 |
每个特性配客观断言 | frontend/ 关键路径 |
中,但收益最大 ★★ |
B. 要改造才能用
| 模块 | 它做什么 | 改造点 | 成本 |
|---|---|---|---|
三轨原创配乐(atmosphere / motif / pulse ogg,MIT) |
环境音 / 音效 / 氛围层 | 接进 frontend/assets/audio/,做音量与场景切换 |
中 |
content 管线(JSON + 校验器 + export + prebuild) |
内容与代码分离、校验失败不产出 | 给 .char / 剧情 / 记忆做写入口前置校验(替代事后 char-audit) |
中 ★★ |
workbench.ts |
番茄钟 + 状态机 | 可做成独立小工具页 | 中 |
quality-settings.ts / render-quality.ts(SUPER PERFORMANCE) |
画质分档、降低分辨率保留动效 | 我们已有同类开关,可互相对照 | 低 |
archive-play-motion.ts / archive-playground.ts |
节奏互动关卡 | 若做养成互动可参考判定与计分结构 | 高 |
wallpaper*.ts(8 个) |
Wallpaper Engine 集成 | 若要做桌面壁纸(依赖 WE 的 web 宿主) | 高 |
theme-material.ts |
运行时向材质注入主题色 | 仅当有 3D 且需要主题联动 | 中 |
档案盒模型 + art/*.py |
Blender 程序化建模 → GLB | 最适合 UE 项目(ue-home-screen / ue-intro-animation 正缺克制的中性道具) |
中 |
C. 明确不抄
| 项 | 原因 |
|---|---|
三维档案墙本体(scene.ts 1840 行等) |
展示型交互模型(状态少:浏览/选中/读取),不适合聊天主界面。它已经在 blog/terminal 用掉了 |
| Novecento Sans Wide | 商业授权,上游脚本本身就在非官方构建时跳过它 |
PV 打字采样(typing-preview.wav) |
上游明确"不纳入 MIT";我们已删除并换成程序合成 |
上游 reference/(15M)、verification/(58M)、art/*.blend |
"非代码资产未声明 MIT" |
把 Vite 构建引进 frontend/ |
会破坏"无构建、零工具链发布"的优点。blog/terminal 作为独立子项目用构建是正确的隔离,保持这样 |
3. 思想层:比代码更值钱的 5 条
-
内容与代码分离 + 硬校验器 ★★★★★ 内容换一个 JSON 就换掉整站;schema 由校验器强制,不合法就不产出任何东西。我们的
.char、剧情、记忆都是"内容",但校验停留在事后审计(char-audit)。把校验器搬到写入口,比再写十个审计 skill 有用。 -
50 个自验脚本,而不是一次手测 ★★★★★ 2026-09-18 这几天反复验证了这条路的价值:能用脚本自己看画面,改动才敢做。我们的前端基本没有断言,后端 pytest 长期挂着 90 个失败 —— 这是最大的工程缺口。
-
一个 WebGL canvas + DOM HUD 的分工 ★★★★ 三维归 canvas、文字按钮归 DOM。任何时候要做 3D,先照这个分工起手。
-
PWA 的更新流 ★★★★ 我们被缓存坑过(博客 CSS 陈旧、本地白屏),上游有完整的"新版本已就绪 → 更新并重启"。
-
逐项声明授权,并明确划出不可授权部分 ★★★
NOTICE.md那种写法:代码 MIT、资源未声明、商业字体排除、原作视觉不获授权。以后我们公开任何东西都可以照抄这套结构。
4. 落地路线
P0(低成本、立刻可感)
- 自托管 MiSans —— 用
blog/terminal/public/fonts/那套(按 unicode-range 分片,浏览器只下载命中片;带许可 PDF,须保留署名)。 PWA 更新流→ 不搬 ✓ 2026-09-18 更正:frontend/sw.js里已明确写着"刻意没有 fetch 处理器,也永远不要加",且项目已有?v=+ no-cache 回源 + nginx 分路径的缓存体系(scripts/bump_frontend_cache.py)—— 上游那套更新提示依赖 SW 的 waiting 状态,搬过来等于在既有缓存体系外面又套一层,会重现"旧 main.js + 新 settings.js"的白屏。我们的方案更好,这条从待办里删除。- 修掉两处已知不一致(见第 6 节)。
P1(收益最大)
- 扩
frontend_smoke.mjs的路径覆盖 —— 2026-09-18 更正:上游那 50 个check-*.mjs是playwright 截图/像素断言,与我们的路线不同,不要照搬 ✗。我们已有的scripts/frontend_smoke.mjs用 esbuild + jsdom,而且它自己写着"只回答一个问题:打开页面会不会炸"、"不断言 DOM 长得对不对" —— 这是对的 ✗✗像素测试。该做的是在同一骨架上加路径:navigateTo(...)每个一级页、设置开关持久化、深聊自动续上、改名、表情上传。 .char/ 剧情内容的前置校验器 —— 抄archive-content.mjs的结构:字段必填 + 枚举 + 编号顺序 + 引用完整性,失败即拒绝写入。
P2(体验升级)
intro.html用字形描边开场(上游现成的烘焙管线)。- 应用内嵌档案入口:复用
blog那套数据(make_docs.py已跑通 docs → 结构化),先纯 DOM。 - 三维档案墙进 app —— 最后做,必须懒加载、只在非输入场景。
与产品目标的直接结合点
产品目标是「干员被问起剧情记忆时能准确回复,并明白自己与博士的关系、处境与时间线」。档案终端恰好可以是这件事的可视化出口:
用户问「你还记得那件事吗」→ 干员回答的同时,档案墙亮起对应几格(数据来自同一份 docs + 剧情数据)→ 用户看见她记得什么、依据是什么。
这解决的是信任问题:干员记忆目前对用户是黑盒,无法判断是真记得还是在编。把检索结果做成可浏览的档案,记忆就从隐性变成可验证的 —— 而这条管线现在已经在博客上跑着了。
延伸成本很低:blog/make_docs.py 已经把「docs/ → 结构化档案」跑通,同一条路可以直接吃 .char 干员卡、剧情事件、关系抽取结果 —— 一个生成器,三种内容。
5. 已经落地的(本仓库现状)
| 位置 | 内容 |
|---|---|
blog/terminal/ |
RhineLabUI 的本地化复刻,品牌为罗德岛;部署在 /terminal/(子目录,vite base=/terminal/ 避开博客的 /assets/) |
blog/terminal/art/*.py + Blender 4.5.9 |
用上游 MIT 建模脚本自行生成两个 GLB(盒面/模压文字已改为自有品牌)→ 100% 属于我们 |
blog/terminal/src/typing-samples.ts |
打字音效改为程序合成;PV 采样、来源记录、提取脚本已删除 |
blog/terminal/NOTICE.md |
逐项来源与授权说明(抄的就是第 3 节第 5 条) |
blog/make_docs.py |
docs/ → 终端档案数据 + 博客文档索引(一个生成器,两处消费) |
blog/build.py |
55 篇 /docs/<slug>.html + /docs.html 索引;生成时脱敏(凭据 + 公网 IP) |
blog/scan_sensitive.py |
公开前排敏扫描器(16 类规则,默认扫产物,--source 自检) |
scripts/blog_deploy.sh |
发布时先构建终端、合并进 dist/terminal/,/terminal.html 作跳转 |
6. 与本仓库既有决策的对照更正(2026-09-18)
写这份拆解时有两处建议与本仓库既有决策冲突,此处更正,避免后续会话被误导:
| 我原来的建议 | 实际情况 | 结论 |
|---|---|---|
| 搬上游 PWA 更新流 | frontend/sw.js 明确"永远不要加 fetch 处理器",且已有 ?v= + no-cache + nginx 分路径体系 |
不搬,我们的更好 ✓ |
抄上游 50 个 check-*.mjs |
我们用 esbuild + jsdom 做"会不会炸"的冒烟,明确拒绝像素级/长相断言 |
改路线:在同一骨架上加路径覆盖 ✓ |
教训:借鉴前先读目标仓库里"为什么不做某件事"的注释 —— 上游的好东西不一定适合我们,而我们自己拒绝过的方案往往已有更硬的理由。
6. 待办与已知不一致
- [ ]
blog/terminal/content/README.md仍写着「四十份档案 / 每列恰好八份」✗ —— 我们已扩到 55 篇、每列 13/11/9/11/11,校验器也放宽为 5–100 条 / 每列 ≤20 格(依据slot编址上限)。这份 README 要同步改写。 - [ ] 扩容量后
npm run check:content未重跑,可能仍在按旧的 40 条规则断言。 - [ ] 排敏扫描报告
blog/data/sensitive-scan*.json含命中上下文,已加入.gitignore,不要提交。 - [ ] 标志形状待定:现在的圆+轴看起来像
+○-,可收成纯⊕或换塔形(改完重跑建模 + 构建 + 发布约 3 分钟)。 - [ ] 上游
scripts/里 50 个check-*.mjs直接可用(同一套 playwright + 本机 Edge),但断言里含上游品牌字符串(如check-boot-hud.mjs断言'RHINE LAB')——要跑就得先改这些期望值。