docs(memory): 生成器 P0 四项的实现要点与踩到的坑

This commit is contained in:
tamakyi
2026-10-20 13:33:17 +08:00
parent 8ffa7b0ba7
commit bf1c994c8f
2 changed files with 42 additions and 2 deletions
+40
View File
@@ -124,3 +124,43 @@
`git check-ignore -v outputs/x` 确认命中。
- 另两条同样无效的规则没动:`./workbuddy`(目录根本不存在,笔误)、`./scripts`(有 1 个文件被跟踪)。
用户只要求 outputs;要清理得单独确认。
## 13:10 生成器能力盘点(含实测)
用户问「有什么改进建议」,顺手实测了一个之前没验证过的点:**窄屏(390 宽)**。
- 没有 `@media` 规则的主题,在 390×844 下:16:9 桌面背景被 `cover` 裁成一条无意义纹理,
人物被占满屏的卡片完全盖住 —— 等于白配。
- shiroko 因为手写了 `@media (max-width:900px)`(换 `scene-mobile.webp` + deco 全关),
同一视口下背景是协调的竖版、装饰退场。
- 结论:**生成器导出的主题没有窄屏段**,这是真实缺口,不是理论问题。
其它已确认的缺口(grep 过,不是猜):无 localStorage / 无 json 导入导出(关掉全丢)、
无图层上移下移(遮挡关系由顺序决定,只能删了重加)、无导出前校验、无既有主题导入。
⚠️ 测窄屏时必须让 **iframe 宽度**一起变(`verify.html?w=390`)。第一次只改外层 window,
iframe 内部仍是 1512,媒体查询按 1512 判定 → 结论是错的。
## 14:20 生成器补 P0 四项
用户问建议 → 我给了 P0/P1 清单 → 他选「P0 四条一起做」。已实现并推送。
1. **配置留得住**:`localStorage` 自动草稿(renderAll 后 700ms debounce)+ `theme-<slug>.json`
保存/打开(图片 base64 内嵌,单文件带走);zip 里也塞一份 json;`?fresh=1` 跳过恢复。
2. **图层 ↑↓**:顺序即层级(`background-image` 第一层画在最上)。
3. **窄屏段**:竖版背景图 + 隐藏装饰 + 断点(默认 900),导出 `@media` 块。
⚠️ **只写浅色规则**:深色块特异性 0,2,1 > 浅色 0,1,1,本来就压住它;多写一份反而容易出错。
4. **导出前校验**:slug / 图片层缺图 / 引用已删素材 / 超 10 层 /
生成 CSS 每处装饰是否真 10 个值(与 Go `TestDecoSlotCount` 同规则,给工具自身兜底)。
**实测**
- 390 视口首层装饰 `url(char.webp)` → `none`,1512 下不变;两种视口都 12 层对齐。
- 草稿往返:带 `?demo=1` 保存 → 不带 demo 打开,`validate` 仍为空(说明恢复成功)。
- 示例 CSS 过 `TestThemeCSSComplete` + `TestDecoSlotCount`(27 处声明)。
**踩到的小坑**
- `makeDemo()` 里 `S.assets.push` 不检查重复 → 重复点「载入示例」会撞 id(`assetById` 取第一个),
导出时静默用错图。改成开头 `S.assets = []`。
- 拖拽标签写 `top:-17px` 会贴到画板边缘被压线/裁切,改成放在框内左上角;
整块半透明填充会把人物罩成一层米色(看着像"框住空气"),改为纯虚线 + 角标签。
- 面板一多左栏就滚不到底:按使用频率重排(图层 → 背景 → 窄屏 → 图片池 → 配色)。
+2 -2
View File
@@ -1384,13 +1384,13 @@ function makeDemo(){
gc.bg = 'bg';
gc.layers = [
Object.assign(newLayer('image'), { asset:'char', h:420, ah:'start', av:'end', dx:40, dy:-36 }),
Object.assign(newLayer('radial'), { color:'#fff6d8', alpha:0.5, r:300, cx:20, cy:88 }),
Object.assign(newLayer('radial'), { color:'#ffd9a0', alpha:0.42, r:320, cx:20, cy:86 }),
];
const ac = S.scopes.admin;
ac.inherit = false;
ac.bg = null;
ac.layers = [Object.assign(newLayer('radial'), { color:'#fff3cf', alpha:0.4, r:340, cx:88, cy:12 })];
ac.layers = [Object.assign(newLayer('radial'), { color:'#ffcf9a', alpha:0.38, r:380, cx:86, cy:10 })];
S.curLayer = null;
S.scopes.box = blankScope('box');