Files
Shuery 3f11426964 feat: TS 构建管线、Spine 抓取器与 wallpapers/ 唯一真相来源
把项目从「手写 dist/」改成「wallpapers/ 是唯一真相来源,dist/ 由 pnpm build 生成」,
并补上配套的类型、门禁与抓取器。一次提交落地整条管线,因为拆开会留下不能构建的中间态。

- src/:运行时与模拟器源码(TS,strict),编译到 build/ 再拷进各分发
- tools/:build / dev / check-{syntax,paths,dist},以及抓取器与回归门禁 tools/checks/
  (.scratch/ 下那批一次性脚本移入 tools/checks/ 并入库为长期门禁)
- wallpapers/:七档壁纸的源数据 + README.md(id/音频/预设的完整规范)
- docs/adr/0005-0008:构建管线与分发拓扑、模拟器契约、自包含 sim、每骨架资源布局
- .gitignore:排除 .scratch/ 的参考资料副本(上游 spine 整仓克隆 ~1.2 GB、
  抓取侦查数据 ~680 MB)与调试转储;这些是本地调查材料,补偿会让仓库无法克隆
- 归一化 .gitignore/CONTEXT.md 行尾(工作区 CRLF、索引 LF 造成的整文件假 diff)

同时修掉三档卡住构建的未完工壁纸:
- kv45 的 meta.json 里 id 还是抓取期场景名 scene_main,经 downloader promote 正名为 kv45
- shajin / zhigengniao_juheye 的 meta.json 误用了骨架描述文件(name/spine/animations/pages)、
  且都缺 preset.template.json;现按规范重建:骨架沉到 spines/<名>/(spine-ts 按 atlas 所在
  目录解析贴图页)、补上元数据与单骨架预设,并清掉 zhigengniao 骨架里指向作者机的绝对路径
- 顺带 promote 已在 sources.yml 里的 kv46(月升之前,与兽共舞)

pnpm check 五道门全绿:10 个分发 / 7 档壁纸 / 141 处引用自包含。
2026-10-02 01:27:02 +08:00

106 lines
4.5 KiB
Markdown
Raw Permalink 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.
# 17 — 自动生成的 id 改成自增
Status: resolved
Type: task
## 需求
> 自动生成的 json 相关的 id 采取自增的形式,如 audios
两处(用户确认「两者都要」):
| | 旧 | 新 |
| --- | --- | --- |
| `project.json` 属性的 `index` / `order` | 手写在 `src/project.template.json` | build 按属性表书写顺序自动编号 |
| 音源 id(`meta.json` 的 `audio.choices[].id`) | 手写字符串(`zaiduheni` / `xilian` / `pv37`) | build 自增分配(`"1"` / `"2"` / `"3"`) |
## ① index / order
以前每个属性都要写 `"index": 4, "order": 104`。代价在**加一个属性**时才显出来:
后面全部要重编号——上一轮加 `use_custom_audio` 手改了 6 处,而且在产物 diff 里
刷出 8 行纯噪音,把真正的改动淹掉了。
现在 `numberProperties()` 按属性表的书写顺序编号:
```
index = 0,1,2… order = 100+index
```
**必须在删属性之后编号**:单档分发会删掉 `preset`/`preset_note`/`bgm`/`bgm_note` 四行,
以前删完就留下空号(0,1,4,5,6,7,8)。现在自动补上,单档是 0…6 连续。
`schemecolor` 是唯一例外:它是 WE 的**内置**属性,线上就是 `order 0` 且**没有** `index`。
用 `"$order": 0` 显式钉住,钉住的属性不参与 index 序列——这样 `collection-all` 的产物
与线上逐字一致,diff 里只剩真实改动。
## ② 音源 id
`meta.json` 里不再写 id:
```json
"audio": {
"choices": [
{ "name": "「再度和你」", "file": "audios/zaiduheni.flac" },
{ "name": "昔涟", "file": "audios/xilian-src.flac" }
]
}
```
### 一个必须解决的约束:id 要**全项目**唯一
第一版我按「每档壁纸各从 1 开始」写,**这是错的**。`generate.ts` 里:
```ts
bgmOptions.push({ label: choice.name, value: choice.id });
```
`bgm` 下拉会把一个分发里**所有**壁纸的音源平铺进同一个 combo。两档壁纸都叫 `"1"` 的话,
选中的到底是哪一个就无从分辨了。
所以用一个**全项目计数器**,分配顺序固定为:
```
游戏 → 该游戏的共享音频 → 各壁纸的音源(按 id 排序)
```
遍历的是**整棵资源库**而不是本次要构建的分发子集——否则同一个音源在不同分发里会拿到不同的
id,用户从合集切到单档时 bgm 选择就失效了。实测:xilian 的两个音源在 `collection-all` 与
`single-hsr-xilian` 里都是 `"2"` / `"3"`。
`default` 也跟着改:从「id 字符串」变成「**1 起的位置**」,省略 = 第一个。换算成 0 起的
`defaultIndex` 只做一处(`vault.ts` 的 `readAudioDecl`)。
### 代价(用户已确认接受)
音源 id 会进 `project.json` 的 `bgm` combo value。改成数字后,**老用户已保存的 bgm 选择会失效**
(和壁纸预设 id 同一个问题)。运行时对这种情况是安全的:`bgmSource()` 找不到匹配就回落到
本壁纸默认音源。另外在音源清单中间插入一条会让后面的 id 整体后移——加音源请往后加。
## 顺手修掉一个「一直红着」的测试
改完跑 `test-props.mjs`,2 项失败:
```
✗ bgm 选「昔涟」后填自定义 URL → 期望 custom-track.mp3,实得 xilian-src.flac
✗ 切壁纸不得清掉自定义 URL → 期望 custom-track.mp3,实得 pv37.mp3
```
**这不是这次改动造成的**:上一轮加 `use_custom_audio` 时,`audio_file` 被改成只在开关打开时
才生效,而当时我**没有重跑这个测试**——它从那时起就一直是红的。`test-acceptance.mjs` 同理。
两处都补上开关,并顺手把「开关关着时填 URL 不生效」加成一条断言——那正是这个开关的核心语义。
> 改了语义却没跑对应的测试,等于没测。这次是靠改另一个东西时才撞出来的。
## 验证
全量回归,全绿:
- `pnpm check`:四个分发、`check:dist` / `check:syntax` / `check:paths`("3 条音源声明无孤儿文件")
- `test-props.mjs`(bgm 选择/回落、fps 限流)、`test-resize.mjs`、`test-acceptance.mjs`、`verify-fitter.mjs`
- 调试服冒烟、浏览器冒烟、面板还原度(10 组)、热更新
- 四档自包含包 `file://`、静态托管预览、sim 门禁、内联守卫、模块改写
- `verify-visual-equivalence`:6 组与重构前逐像素相同
- `diff-project`:差异 13 → **14**,新增的那一条是 `bgm.options`(value 从名字变数字),
正是本次有意的改动;`index`/`order` 相关**没有**新增差异