docs(memory): 耳朵/白孔/场景站位与 background-position 百分比陷阱
This commit is contained in:
@@ -600,3 +600,15 @@ WebP 1920×1080(22KB)/ 720×1003 竖版(6KB);卡片与顶栏改 `rgba(
|
||||
会拿到 `/* ... */ --tb-surface`,色块预览静默变空、`TestThemeCSSComplete` 报"缺少令牌"。
|
||||
已加 `commentRe` + `TestRuleBodyIgnoresComments`。
|
||||
⚠️ ImageGen 出的图右下角有 `AI生成 WORKBUDDY` 水印,用作整页背景必须裁掉。
|
||||
|
||||
## 23:40 人像换成「窗边背影」
|
||||
|
||||
- 用户否掉了盘腿坐的正面人像("和背景没那么突兀的")。根因:正面直视 + 无环境 + 饱和度高,
|
||||
在压白 38% 的空教室里就是一块贴纸。
|
||||
- 新素材 `chibi/window.webp`(135×460, 13KB):文生图出侧后方 3/4 视角(双手背在身后)→ 再
|
||||
image-to-image 转"纯色块无描边 + 去窗框"→ 抠白底 → **RGB 压白 16%** → 高 460 / q86。
|
||||
第一步文生图会把窗框画进背景,抠不干净,必须靠第二步重绘。
|
||||
- CSS:`--tb-deco` 首槽换 window.webp,高 380→400px,接触阴影 96×13 复位到脚底;`StaticVersion` → `20261019r`
|
||||
(图片另带 `?v=20261019r`)。删除 `sit.webp`;`docs/theme-shiroko.md` 补两步做法与摆位说明。
|
||||
- 提交 `e95671f` 已推送。教训:**人像与场景的"雾度"要对齐**——场景压白多少,人像也压相当比例。
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# 2026-10-20
|
||||
|
||||
## 01:10 shiroko 主题补「空气层」
|
||||
|
||||
- 用户问「还有没有其他元素,贴合主题又不突兀」。结论:场景压白 38% 后整页亮度只有 235~246,
|
||||
再摆物件(书包/粉笔/纸飞机)一定会像贴纸,所以走**纯渐变造光**路线。
|
||||
- 做了 4 个候选截图(A 纯窗光 / B 白散景 / C 光环符号 / D 组合),用户选 D。
|
||||
⚠️ **B 是反面教材**:白色散景叠在已经压白的场景上,实测差异 0.14 级,肉眼完全不可见。
|
||||
要"提亮"必须先有暗部,真正起作用的是「底部压深 + 暖色光斑」的组合。
|
||||
- 最终 `--tb-deco` 后 4 槽:左窗光柱(112deg)、右侧 120px 暖散景(88% 30%)、
|
||||
脚下 560×170 地面光斑(28% 94%)、底部 24% 压深。亮度差 3~9 级。
|
||||
- 验证手段(本轮**模型读不了图**,全靠数值):留白区差分(与候选 D 差 0.0)、
|
||||
分区亮度表、横向扫描相邻像素差 max 4(判色阶断层)、PNG 体积比对。
|
||||
- ⚠️ 造预览页两个坑:① 覆盖块必须包在 `<style>` 里,裸写会被当正文渲染,整页布局炸掉;
|
||||
② `--tb-deco` 各层之间**漏逗号**会让整条声明失效 → 人物和所有背景层全没,且各变体看起来"完全一样"。
|
||||
以后先 diff 两个变体的 CSS 文本再截图。
|
||||
- `StaticVersion` → `20261020a`;提交 `fb94b8c` 已推送。文档 `docs/theme-shiroko.md` 加「空气层」一节。
|
||||
|
||||
## 08:30 重画光环
|
||||
|
||||
- 用户反馈「光环变形太奇怪,而且中间没有扣干净」。根因两条:
|
||||
① AI 画的环本身歪(bbox 291,93–584,232,比头还宽、偏移到左边);
|
||||
② 环内是**封闭白孔**(面积 5190px 的包围区域),四角 floodfill 进不去 → 抠完环里糊着一团白。
|
||||
- 做法:非白像素从环上一点漫水拿到独立连通域(与头发不连通,头部顶端在 y≈265,环只到 232),
|
||||
整块抹白;**先把 alpha 建好,再在 RGBA 上画新环**——环心天然就是透明的,不用给封闭孔补洞。
|
||||
- 新环参数:中心 (515,160)、100×34、线宽 10、-7° 倾斜、4 倍超采样后 LANCZOS 缩小、
|
||||
外加 45% 模糊柔光;颜色 (127,211,220),压白 16% 后实测环心 (164,220,224)。
|
||||
- 坑:擦除矩形右边界要绕开**灰色狼耳**(x>630,y 190-260,颜色 (139,140,146) 不是青色),
|
||||
差点一起抹掉。判据是连通域 bbox 而不是"上方全是空白"。
|
||||
- 验证:alpha 字符图确认环闭合、环心 alpha=0;渲染后页面里青色像素 bbox (201,467)-(271,649),
|
||||
环在 y 468-486,头顶约 505,间距正常。
|
||||
- `StaticVersion` → `20261020b`,图片 `?v=20261020b`;提交 `2e9bd6b` 已推送。
|
||||
|
||||
|
||||
## 09:40 部署反馈三修 + 自行车/步枪
|
||||
|
||||
- **狼耳顶端被削平**:上一版用矩形 `(281,83)-(594,242)` 抹旧光环,右下角正好切到左耳尖
|
||||
(耳尖在 y≈185、x 420-480)。改成只抹光环的**非白连通域**(bbox 291,93-584,232,与头发不连通)。
|
||||
- **手内侧 / 大腿内侧白没抠干净**:那是**闭合**的白色区域,四角 floodfill 进不去。
|
||||
加封闭白孔检测(>=246 且不与边缘连通、面积>=40)整块掏成透明。
|
||||
⚠️ 阈值必须 >=251 才能当"背景":背景恒为 (253,253,253),而**白袜子是 237**,
|
||||
用 246 当背景阈值会把袜子一起吃掉(第一版就吃穿了一只袜子)。
|
||||
- **人物像站在课桌上**:原场景前景全是课桌,根本没有地面。重出场景:
|
||||
前景留空地、课桌退到右后方,人物脚下才是地板。
|
||||
- **自行车 + 步枪**:独立素材 `props/bike.webp`(镜像成车头朝右、与人同向、压白 26%、
|
||||
自带接触阴影),放在**人物身后偏左**。deco 层顺序 = 画在上面的在前,所以第一层是人、
|
||||
第三层是车,车天然被她挡住。
|
||||
- ⚠️ **`background-position` 的百分比是相对 `(定位区宽 - 图片宽)`**,不是相对视口宽。
|
||||
人(117px)和车(397px)用同一个 `calc(50% - 901px)` 会差出 280px,车直接跑出屏幕只剩一片轮圈。
|
||||
按各自宽度反解后:人 `calc(50% - 524px)`、车 `calc(50% - 612px)`。
|
||||
- ⚠️ 道具**不能烘焙进背景**:背景走 `cover`,视口一变裁切量就变,和固定 px 的人物必然分家。
|
||||
- 场景重出后色调用「逐通道均值/方差对齐旧场景」保持同一雾度;移动端从同图竖裁,避免两个房间。
|
||||
- `StaticVersion` → `20261020d`;提交 `15aebd6` 已推送。
|
||||
+72
-116
@@ -8,11 +8,11 @@
|
||||
|
||||
## 构建与测试
|
||||
|
||||
- **无 libwebp/GCC**,cgo 的 `internal/storage/s3.go` 挡编译 → 用 overlay 换 `dev/s3stub/s3_stub.go`:
|
||||
- **无 libwebp/GCC**,cgo 的 `internal/storage/s3.go` 挡编译 → overlay 换桩 `dev/s3stub/s3_stub.go`:
|
||||
`-overlay="C:/Users/Tamakyi Gekka/AppData/Local/Temp/tb-webpcheck/overlay.json"`。
|
||||
但 overlay 会把它当桩,**检查 s3.go 本身要 tar 复制项目 + `go mod edit -replace`**(overlay 不能替换 GOMODCACHE 文件)。
|
||||
但 overlay 把 s3.go 当桩,**要检查 s3.go 本身得 tar 复制项目 + `go mod edit -replace`**(overlay 换不了 GOMODCACHE 文件)。
|
||||
- **「编译卡死」= `proxy.golang.org` 不可达**。每条 go 命令前:
|
||||
`export GOPROXY=https://goproxy.cn,direct GOSUMDB=off GOFLAGS=-mod=mod`。`go env -w` 在本沙箱失败。
|
||||
`export GOPROXY=https://goproxy.cn,direct GOSUMDB=off GOFLAGS=-mod=mod`(`go env -w` 在本沙箱失败)。
|
||||
1C1G 是正式生产环境,**别用「沙箱慢」解释超时**。
|
||||
- DB 测试:`DB_TYPE=postgres DB_USER=postgres DB_PASSWORD=15207786347hXPm DB_HOST=127.0.0.1 DB_PORT=5432`
|
||||
(DSN 不带库名,`NewTestDB` 自建临时库后 DROP)。带 DB 的 `go test ./...` 目前全绿。
|
||||
@@ -39,36 +39,35 @@
|
||||
- **新增审核调用点必须走 `moderation.Check`**(直调 `censor.Text` 会绕过档位)。不可用时 **fail-closed**。
|
||||
- ⚠️ **圈内黑话(代称+群体标签+避雷叙述)任何通用审核都拦不住**,别指望词库;靠处置 + 举报 + 人工判断。
|
||||
- 严格度三档 lax/standard/strict,存 `users.moderation_strictness`。**受处置用户强制 strict**:
|
||||
`moderation.EffectiveStrictness(ctx, userID, raw)`(7 个入口已全部改用它);**查询失败时回退用户设置**
|
||||
`moderation.EffectiveStrictness(ctx, userID, raw)`(7 个入口已全部改用);**查询失败时回退用户设置**
|
||||
(否则 DB 抖动会让全站突然加严)。处置期内 `UpdateModerationStrictness` 拒绝保存 + 设置页 disabled。
|
||||
|
||||
## 提示(Flash)
|
||||
|
||||
- ⚠️ **同请求渲染只能用 `ctx.SetError` / `SetInternalError`,不能用 `*Flash`**:flash 写 session,要下一次请求才读 →
|
||||
提示静默丢失(「点了没反应」)。2026-10-18 已清理 9 处;`Set*Flash + Redirect` 是正常模式。
|
||||
- ⚠️ **同请求渲染只能用 `ctx.SetError` / `SetInternalError`,不能用 `*Flash`**:flash 写 session,下一次请求才读 →
|
||||
提示静默丢失(「点了没反应」)。2026-10-18 清理 9 处;`Set*Flash + Redirect` 是正常模式。
|
||||
扫描法:找 flash 调用 → 向下第一个 `Success(`/`Redirect(` → 前者即缺陷。
|
||||
|
||||
## 处置与举报
|
||||
|
||||
- L1 停止分发(禁分享卡 + 首页/探索/标签/随机位摘除)/L2 收窄公开(新回答强制私密、禁止 set-public)/
|
||||
L3 冻结发布(仍可收信)/L4 整页停用(`/restricted`)。到期靠 `expires_at` 自动失效,**无 cron**;
|
||||
`restrict` 是**替换语义**;级别比较用 `RestrictionAtLeast`(显式权重)。
|
||||
`restrict` 是**替换语义**;级别比较用 `RestrictionAtLeast`。
|
||||
- ⚠️ L2 强制私密必须在**审核通过之后**,且与写回答**同一条 UPDATE**(`AnswerByIDPrivate`):
|
||||
拆成「先 SetPrivate 再 Answer」会留下「公开+已作答」窗口,第二条失败还会变成「私密+无回答」。
|
||||
- ⚠️ **`ListAPI`(`/api/v1/user/{domain}/questions`)不经过 Pager 中间件**,L4 与页面密码必须自己校验
|
||||
(已在 2026-10-19 补;否则「整页停用」和访问密码都能被 API 绕过)。
|
||||
- ⚠️ **整箱分享卡 `ShareCard` 与单题分享卡都要拦**(拦截必须在缓存查询之前,否则 30 分钟内还能拿到旧图)。
|
||||
- 举报 `POST /_/{domain}/{qid}/report`:公开免登录 + 验证码 + 限流;理由固定枚举;同 IP 同题唯一索引 →
|
||||
(否则「整页停用」和访问密码都能被 API 绕过)。
|
||||
- ⚠️ **整箱分享卡 `ShareCard` 与单题分享卡都要拦**,且拦截必须在缓存查询之前(否则 30 分钟内仍给旧图)。
|
||||
- 举报 `POST /_/{domain}/{qid}/report`:公开免登录 + 验证码 + 限流;同 IP 同题唯一索引 →
|
||||
**幂等且提示与首次一致**(否则成了内容存在性探测器)。箱主只看「被举报 N 次」,**不给 IP**。
|
||||
- 闭环:箱主删除/设私密或 `hide-public` → `MarkAcceptedByQuestion`;待处理超 30 天视为过期。
|
||||
⚠️ `hide-public` 里必须**先快照目标 ID 再更新**:更新后再用带 `is_private=false` 的子查询恒为空集(曾静默失效)。
|
||||
⚠️ `hide-public` 里必须**先快照目标 ID 再更新**(更新后用带 `is_private=false` 的子查询恒为空集,曾静默失效)。
|
||||
- 告警:24h≥8 或 7d≥20 → 邮件到 `[security] security_alert_email`(24h 冷却,进程内);单条恰好 3 人举报 → 提醒箱主。
|
||||
⚠️ 该邮箱为空时静默跳过,无日志;改配置必须重启。
|
||||
⚠️ 该邮箱为空时静默跳过、无日志;改配置必须重启。
|
||||
- `/rules` 是处置依据页(四语言);注册页必须勾选 `agree_rules`(服务端同样校验),改 e2e 脚本要同步。
|
||||
- `restrict` 默认给箱主发处置通知邮件(按 `users.language`,模板 `mail/restriction-notice.html`),
|
||||
- `restrict` 默认给箱主发处置通知(按 `users.language`,模板 `mail/restriction-notice.html`),
|
||||
**只含级别名/到期/规则链接,不含 `--reason`**;`--no-notify` 可跳过(L1 静默止血用)。
|
||||
`--days > 90` 必须加 `--force`(防手误),交互模式还要再输一次级别名。
|
||||
`unrestrict` 也会发解除通知(同模板、无申诉段),双方都有 `--no-notify`。
|
||||
`--days > 90` 必须加 `--force`,交互模式还要再输一次级别名。`unrestrict` 也发解除通知,双方都有 `--no-notify`。
|
||||
⚠️ **到期自动解除不发信**(读表比较时间,无调度器)。
|
||||
|
||||
## GORM
|
||||
@@ -81,72 +80,79 @@
|
||||
|
||||
## 前端样式
|
||||
|
||||
- 基础样式在 `static/css/base.css`(2026-10-19 从 header 内联块抽出,`/static/css/base.css?v=YYYYMMDDx`,
|
||||
**改完必须把 header 里的 v 号 +1**,静态缓存 1 天)。设计令牌是文件开头的 `:root`(亮)+ `html[data-theme="dark"]`(暗)。
|
||||
新样式一律用 `var(--tb-*)`,别再写死色值/圆角/阴影,也别为深色另写一套分支。
|
||||
- 页面级样式仍内联在各自模板的 `<style>` 里(variance 大、复用少),但同样只能用令牌。
|
||||
- 静态文件是**显式 embed 清单**(`static/fs.go`),新增 css/js 必须手动加进 `//go:embed` 那行,否则运行期 404。
|
||||
- 基础样式 `static/css/base.css`(`/static/css/base.css?v=YYYYMMDDx`,**改完必须 +1**,静态缓存 1 天)。
|
||||
设计令牌在文件开头 `:root`(亮)+ `html[data-theme="dark"]`(暗)。新样式一律用 `var(--tb-*)`,别写死色值,也别为深色另写分支。
|
||||
- 页面级样式仍内联在各自模板 `<style>` 里,同样只用令牌;尽量用类不用 `style=`(深色下内联写死色值会露白)。
|
||||
- 静态文件是**显式 embed 清单**(`static/fs.go`),新增 css/js 必须手动加进 `//go:embed`,否则运行期 404。
|
||||
- 主题属性在 head 内联脚本里打(cookie `theme`),**不能挪回 body**,否则深色用户白闪。
|
||||
- 页面级样式尽量用类,不用 `style=`(深色下内联写死色值会露白,`.tb-img-uploader` 踩过)。
|
||||
- **移动端(≤640px)语言切换与主题按钮不在顶栏**:顶栏 `.tb-nav-utils` 被 `display:none !important` 隐藏,
|
||||
改由 `#tb-mobile-utils` 固定底栏承载(`position:fixed;bottom:0;z-index:1000`,body 预留 `padding-bottom:56px`)。
|
||||
顶栏图标只在 >640px 出现。用户曾误以为"按钮不见了",先解释位置再排查。
|
||||
⚠️ `/_/{domain}` 箱主页把 `IsUserPage` 设为 true(`route/question/page.go:133`),语言切换整块包在
|
||||
`{{ if not .IsUserPage }}` 里(header.html:272 桌面 / :314 移动),所以**箱主页没有语言切换入口**——
|
||||
**这是有意设计,不是缺陷**(用户 2026-10-19 明确:箱主页属箱主个性化,以箱主配置为准)。
|
||||
别把它当 bug 去"修",要动先问。
|
||||
- ⚠️ **UIkit 3.3.3 有 `.uk-flex::before/::after{display:none}`**:给带 `uk-flex` 的元素加伪元素遮罩必须写
|
||||
`.tb-cover.uk-flex::after` 这类复合选择器,否则静默失效(不报错)。
|
||||
- **移动端(≤640px)语言切换与主题按钮不在顶栏**:`.tb-nav-utils` 被隐藏,改由 `#tb-mobile-utils` 固定底栏承载
|
||||
(`bottom:0;z-index:1000`,body 留 `padding-bottom:56px`)。用户曾误以为"按钮不见了",先解释位置再排查。
|
||||
⚠️ `/_/{domain}` 箱主页 `IsUserPage = true`(`route/question/page.go:133`),语言切换包在 `{{ if not .IsUserPage }}` 里,
|
||||
所以**箱主页没有语言切换入口——有意设计**(用户 2026-10-19 明确:箱主页以箱主配置为准),别当 bug 修。
|
||||
- ⚠️ **UIkit 3.3.3 有 `.uk-flex::before/::after{display:none}`**:给带 `uk-flex` 的元素加伪元素必须写
|
||||
`.tb-cover.uk-flex::after` 这类复合选择器,否则静默失效。
|
||||
- 分类/题型标签一律 `.tb-chip`;**彩色 `uk-label` 只留给语义状态**(置顶/私密/被举报/已读、投票四态)。
|
||||
- 视觉自检:`TEST_PREVIEW=1 go test ./templates -run TestGenPreview` → `outputs/preview-*.html`;
|
||||
截图必须 `chrome --headless=old`(新版 headless 在本机挂住),外部资源先落到 `outputs/preview-assets/`。
|
||||
改 CSS 的正确验证法是**像素比对**(PIL 在 `~/.workbuddy/binaries/python/envs/default`),不是目测。
|
||||
⚠️ 像素扫描的循环上界**不能与物体边界重合**(曾把 `range(110,830)` 当成条宽 →
|
||||
右端恒为 829 被读成"方角",其实是边界截断)。
|
||||
- 分类/题型标签一律 `.tb-chip`(浅底 + 次要色);**彩色 `uk-label` 只留给语义状态**
|
||||
(置顶/私密/被举报/已读、投票未开始/进行中/已结束/已参与)。题型配色曾由 Go 的
|
||||
`fieldTypeLabelClass` 生成(已删),故 grep `uk-label` 搜不到,改前先搜模板函数名。
|
||||
截图必须 `chrome --headless=old --force-device-scale-factor=1`(新版 headless 本机挂住)。
|
||||
⚠️ `--window-size` ≠ CSS 视口(1080→985)。验证 CSS 靠**像素比对**(PIL 在 `~/.workbuddy/binaries/python/envs/default`)
|
||||
而非目测;扫描循环上界不能与物体边界重合(曾把边界截断读成"方角")。
|
||||
|
||||
## 主题系统
|
||||
## 主题(箱主个性化)
|
||||
|
||||
- 一套主题 = `static/css/themes/<slug>.css` 一个文件,**只覆盖令牌**(配色/图案/整页背景),不碰布局。
|
||||
新增主题:复制 `_scaffold.css` → 改名 → 登记到 `[theme] available`;`static/fs.go` 已嵌整个目录,无需改清单。
|
||||
- 作用域:仅 `/_/*` 箱主相关页面(`question.Pager` 注入 `ThemeSlug`);`/user` 后台与全站其它页面不主题化。
|
||||
优先级 `users.theme > [theme] default > 基础外观`;`""`=跟随站点默认,`"default"`=箱主明确选基础外观(不加载 css)。
|
||||
- ⚠️ 主题 css **必须成对写**:`html[data-theme-style="x"]`(0,1,1,压过 `:root` 的 0,1,0)+
|
||||
`html[data-theme="dark"][data-theme-style="x"]`(0,2,1,压过 base 的 dark)。只写浅色块会让深色模式被浅色令牌污染。
|
||||
兜底测试 `TestThemeCSSComplete`(检查两个选择器 + 10 个颜色令牌齐全)。
|
||||
- ⚠️ **UIkit 3.3.3 的主题色是编译期常量 `#1e87f0`**,令牌注入不进去。base.css 里有一段「UIkit 组件跟随主题色」
|
||||
按组件映射(a/uk-link、uk-button-primary、checkbox/radio、input:focus、uk-tab active)。新增会显示主色的组件时,
|
||||
先确认它是否已映射,否则换主题后会出现「页面变粉、按钮还蓝」。
|
||||
- 静态资源版本号统一 `conf.StaticVersion`(base.css 与主题 css 共用),改样式只改这一处;页面通过 `.StaticVersion` 取。
|
||||
- 整页背景用 `--tb-page-image`(cover)与 `--tb-page-pattern`(平铺,`--tb-page-pattern-size` 控尺寸),图案叠在图片之上。
|
||||
- 三个注入点,别再漏:`/_/{domain}/*` → `question.Pager` 按 `pageUser.Theme`;`/user/*` → `internal/context`
|
||||
的 Contexter 按 `c.User.Theme`(`isThemeScope()` 只认 `/user` 与 `/user/`);`/vote/{token}` → `route/vote.Page`
|
||||
按发布者 `owner.Theme`。都看箱主/发布者,不看访客。全站页(`/`、`/explore`、`/login`)不主题化。
|
||||
- 投票页**语言**也跟发布者:`applyOwnerLocale(data map[string]interface{}, owner)`(与主题同一个函数),
|
||||
必须在任何 i18n 取词之前调用;箱主未配语言才回退访客/站点默认。签名收 map 是为了能纯内存单测。
|
||||
- 一套主题 = `static/css/themes/<slug>.css`,**只覆盖令牌**,不碰布局。`static/fs.go` 已嵌整个 `themes/` 目录,
|
||||
新增主题只需放文件 + 登记 `[theme] available`(底稿 `_scaffold.css`)。
|
||||
- ⚠️ **`[theme] available` 是硬白名单,启动时读一次**:css 发布了但没登记,设置页就是看不到,现象和「部署没生效」一样。
|
||||
排查分两类:`curl /static/css/themes/<slug>.css` 200 = 文件在(那就是没登记);404 = 旧二进制。
|
||||
启动日志有 `主题注册表已加载 enabled=...` 与 `[theme] available 未登记以下主题`。
|
||||
- ⚠️ 主题 css **必须成对写**:`html[data-theme-style="x"]`(0,1,1)+ `html[data-theme="dark"][data-theme-style="x"]`(0,2,1)。
|
||||
只写浅色块会让深色模式被浅色令牌污染。`TestThemeCSSComplete` 查两个选择器 + 10 个颜色令牌。
|
||||
- ⚠️ **UIkit 3.3.3 的主色是编译期常量 `#1e87f0`**,令牌注入不进去。base.css 末尾按组件映射(a/uk-link、
|
||||
uk-button-primary、checkbox/radio、input:focus、uk-tab active、`.uk-progress`、`.uk-label:not(danger|warning|success|secondary)`)。
|
||||
新增会显示主色的组件先确认已映射,否则换主题后「页面变粉、按钮还蓝」。
|
||||
- 令牌 `--tb-navbar`:顶栏底色,默认 `var(--tb-surface)`。页面底色偏彩时主题必须覆盖,否则顶栏是纯白带压在彩底上割裂。
|
||||
- 静态资源版本号统一 `conf.StaticVersion`(base.css 与主题 css 共用);**图片不走这个缓存键**(`max-age=86400`),换图要给 URL 带 `?v=`。
|
||||
- 整页背景 `--tb-page-image`(cover)/ `--tb-page-pattern`(平铺,`--tb-page-pattern-size` 控尺寸),图案叠在图片之上。
|
||||
底色用**纯色**别用整页渐变;图案 ≥24px、透明度 ≤0.06。
|
||||
- ⚠️ **多背景层不能塞变量列表**:变量自身是逗号列表会让层数与 size/position 错位(曾整页重复同一张插画)。
|
||||
多张装饰用固定槽位:`--tb-deco` 恒 6 个,缺的填 `none`,size/position 同步补齐。
|
||||
- ⚠️ `background-position` 四值语法必须**先是边关键字**:`left calc(...) bottom 0` 合法,`max(...) bottom 0` 非法 →
|
||||
整条声明作废、人物跑到左上。
|
||||
- 装饰摆位:**贴边、成组、避开页脚**;四角各摆一个会读成贴纸墙纸;页脚文案右对齐 → 右下角不能放人物。
|
||||
`--tb-deco` 是 fixed 视口层,贴边插画别放进 `cover` 背景图(必裁边)。
|
||||
- 主题视觉的**结构**比摆位重要:二次元站点通行做法是「整屏场景插画 + UI 悬浮其上」(蔚蓝档案 JP)。
|
||||
白底留白里摆人物,无论多大都会读成贴纸;卡片/顶栏配半透明白 + `backdrop-filter` 才有"浮在画面上"的关系。
|
||||
- 素材放 `static/images/themes/<slug>/`(embed 已含 `images/themes`,子目录自动覆盖;**新增其它顶层目录才改清单**)。
|
||||
整页图用 16:9 + 竖版两张,靠 `@media` 切换。
|
||||
- 人物素材:**图生图 + 抠白底**(`image1` 给立绘、`input_fidelity: high`、提示词强调 `flat vector illustration, no outlines`),
|
||||
角色一致性远好于手绘 SVG;抠图用四角+四边中点 floodfill(容差 16)+ alpha MinFilter(3) 收边。
|
||||
⚠️ 正面立绘直接贴进空场景像贴纸:改用**侧后方视角**(不直视访客),并把 RGB **压白 12~16%** 与压白 38% 的场景对齐雾度。
|
||||
- ⚠️ `ruleBody` 取令牌前要剥 `/* */`:注释会和后面的令牌粘成一个键,色块预览静默失效。
|
||||
|
||||
## 其它约定
|
||||
|
||||
- 校验靠 `valid:"required;maxlen:N"` 标签(`internal/form/*.go`),别在 handler 手写。
|
||||
- 终止性操作(停用)落独立公开页 `/deactivated`(注册在 `reqUserSignIn` 外);改密码后重登才用 flash + `/login`。
|
||||
- 只有箱主 `/user` 面板,**无管理员角色、无站方后台,且用户要求不要加**:站方动作只走 CLI / 邮件 / 改配置重启。
|
||||
要开管理页先问:能不能用 CLI 表达?
|
||||
- git 仓库可用(origin = `https://git.shiroko.one/tamakyi/tamabox`,分支 `main`)。**`git push` 能直接推**,
|
||||
用户本机的 Git for Windows 已认证,`~/.gitconfig` 的 `credential.helper=store` 与系统 `manager` 并存,
|
||||
store 里的记录会被刷新 —— 所以「Failed to authenticate user」是**临时态**(用户在别处提交后即恢复),
|
||||
不要据此断言「无凭据、只能打包」。推之前先 `git log --oneline -3` 看本地是否已提交,直接试推一次即可。
|
||||
改代码时仍只做只读 `git status/log`,别动历史。
|
||||
- git 仓库 `https://git.shiroko.one/tamakyi/tamabox`(`main`)。**`git push` 能直接推**(本机 Git for Windows 已认证);
|
||||
「Failed to authenticate user」是**临时态**,别据此断言「无凭据、只能打包」。推前 `git log --oneline -3` 确认已提交。
|
||||
改代码时只做只读 `git status/log`,别动历史。`outputs/` 未纳入版本控制(untracked),别整目录 add。
|
||||
- 打包交付用 tar.gz(排除 `.git`/`.workbuddy`/`outputs`),放项目同级。
|
||||
|
||||
## 测试环境 box-dev-api.337845818.best
|
||||
|
||||
- 专属测试账号:`[email protected]` / `adminadmin`,公开页 `/_/admin6`(用户 2026-10-19 指定)。
|
||||
登录要过算数验证码:`GET /login` 取 `_csrf`+cookie → `GET /api/captcha/generate` → `POST /api/captcha/verify`
|
||||
(头 `X-CSRF-Token`)→ `POST /login` 带 `g-recaptcha-response="<id>:<答案>"`。
|
||||
- 跑本工作区构建,但**部署有延迟**;模板是编译期 embed,**只重启不重编不会变**。用户说「已部署但没变化」→
|
||||
- 账号 `[email protected]` / `adminadmin`,公开页 `/_/admin6`(用户 2026-10-19 指定)。
|
||||
- 登录要过算数验证码:`GET /login` 取 `_csrf`+cookie → `GET /api/captcha/generate` → 算 `a op b` →
|
||||
`POST /login` 带 `g-recaptcha-response="<id>:<答案>"`。默认语言 **ja**,四语言文案要一起加(`TestDictionaryComplete`)。
|
||||
- 跑本工作区构建但**部署有延迟**;模板是编译期 embed,**只重启不重编不会变**。用户说「已部署但没变化」→
|
||||
先抓线上 HTML 比对标记,别先怀疑缓存。
|
||||
- 默认语言 **ja**;四语言文案要一起加(`TestDictionaryComplete` 查齐全)。验证码是数学题:
|
||||
`GET /api/captcha/generate` → 算 `a op b` → `g-recaptcha-response="<id>:<答案>"`,并带页面 `_csrf`。
|
||||
- 表单字段:提问 `content`,回答 `answer`。`GET /user/votes` 故意 302;匿名无邮箱提问成功直接 200 渲染。
|
||||
- 自检:`dev/devcheck-e2e.py`(28 项)、`dev/devcheck-restrict.sh`(**基线非 200 就别往下走**;`RUN=1` 才真跑;
|
||||
`trap` 保证撤销)。
|
||||
- 图片:编码三档(头像 512/q78、背景 1920/q80、通用 1600/q80),**EXIF 方向必须先归一**(转 WebP 后会丢),
|
||||
- 自检:`dev/devcheck-e2e.py`(28 项)、`dev/devcheck-restrict.sh`(**基线非 200 就别往下走**;`RUN=1` 才真跑;`trap` 保证撤销)。
|
||||
- 图片:编码三档(头像 512/q78、背景 1920/q80、通用 1600/q80),**EXIF 方向必须先归一**(转 WebP 会丢),
|
||||
比例校验用同一套方向(头像 1:1 测不出,只有背景 16:9 触发)。
|
||||
|
||||
## CLI(`TamaBox <cmd>`)
|
||||
@@ -155,55 +161,5 @@
|
||||
否则 `No help topic for '<cmd>'`。
|
||||
- 现有:`web` `censor` `uid` `ban` `restore` `reports` `restrict` `unrestrict` `hide-public`。
|
||||
- `restrict --domain|--email --level L1..L4 --days N --reason` / `unrestrict`;
|
||||
`reports --domain|--email [--days]`(证据包)、`--pending`、`--stats --days N`(Top 被举报箱主 + Top 举报 IP);
|
||||
`hide-public [--only-reported] [--since N]`。
|
||||
`reports --domain|--email [--days]`(证据包)、`--pending`、`--stats --days N`;`hide-public [--only-reported] [--since N]`。
|
||||
- 本地冒烟:`NEKOBOX_CONFIG_PATH=<ini>`;`conf.Init` 强制 `app.external_url`、`server.salt`、`server.xsrf_key` 非空。
|
||||
|
||||
## 主题(箱主个性化)
|
||||
|
||||
- 三个注入点,别再漏:`/_/{domain}/*` → `question.Pager` 按 `pageUser.Theme`;
|
||||
`/user/*` → `internal/context` 的 Contexter 按 `c.User.Theme`(`isThemeScope()` 只认 `/user` 与 `/user/`);
|
||||
`/vote/{token}` → `route/vote.Page` 按发布者 `owner.Theme`。都看「箱主/发布者」的配置,不看访客。
|
||||
全站页(`/`、`/explore`、`/login`)不跟随任何人的主题。
|
||||
- 投票页**语言**也跟发布者(`applyOwnerLocale(ctx.Data, owner)`,与主题同一个函数):
|
||||
必须在任何 i18n 取词之前调用,否则先渲染出的提示会按访客语言。箱主未配语言时才回退访客/站点默认。
|
||||
- 主题 = 一个 css 文件只覆盖令牌;新增主题不用改 Go/模板,放 `static/css/themes/<slug>.css` +
|
||||
`[theme] available` 登记即可(`static/fs.go` 已嵌整个目录)。底稿 `_scaffold.css`。
|
||||
- ⚠️ **`[theme] available` 是硬白名单,启动时读一次**:css 随二进制发布了但没登记,设置页就是看不到,
|
||||
现象和「部署没生效」一模一样。排查先分两类 —— `curl /static/css/themes/<slug>.css` 返回 200 说明文件在
|
||||
(那就是没登记),404 才是旧二进制;`StaticVersion` 只管缓存、不影响列表。
|
||||
启动日志会打 `主题注册表已加载 enabled=...` 与 `[theme] available 未登记以下主题`(2026-10-19 加)。
|
||||
- 带图片素材的主题把图放 `static/images/themes/<slug>/`(`static/fs.go` 的 embed 已含 `images/themes`,
|
||||
子目录自动覆盖;**新增别的顶层目录才要改清单**)。整页图用 16:9 + 竖版两张,靠 `@media` 切换 ——
|
||||
`--tb-page-image` 是 `cover`,贴着图片边缘放的插画会被按视口裁掉。
|
||||
- **必须同时写浅色块与深色块**(`html[data-theme-style=x]` / `html[data-theme="dark"][data-theme-style=x]`),
|
||||
`TestThemeCSSComplete` 会查;色块预览由浅色块的 `--tb-bg` + `--tb-primary` 自动生成(色值过正则防注入)。
|
||||
- 令牌 `--tb-navbar`:顶栏底色,默认 `var(--tb-surface)`。页面底色偏彩时主题应覆盖它,
|
||||
否则顶栏是纯白带、压在彩底上割裂(用户实测反馈过)。
|
||||
- UIkit 主色是编译期常量,主题要走通必须逐个组件映射(见 base.css 末尾那段):a/uk-link、
|
||||
uk-button-primary、checkbox/radio、input:focus、uk-tab active、`.uk-progress`,
|
||||
以及 **`.uk-label:not(danger|warning|success|secondary)`**——`uk-label` 默认色就是主色,
|
||||
但必须用 :not() 排除语义色(同特异性 + base.css 后加载,不排除会连带刷掉 danger/success)。
|
||||
- 主题底色用**纯色**,别用整页渐变——会被读成「底色分布不均匀」;图案建议 ≥24px、透明度 ≤0.06。
|
||||
- 改了 base.css / 主题 css 必须 +1 `conf.StaticVersion`(两者共用同一个 v)。
|
||||
- ⚠️ **多背景层不能塞变量列表**:`background-image: var(--a), var(--b)` 里若某个变量自身是逗号列表,
|
||||
层数会变而 size/position 的个数不变 → CSS 循环取值、整层错位(曾出现整页重复同一张插画)。
|
||||
需要多张装饰时用固定槽位:`--tb-deco` 恒 6 个,缺的填 `none`,size/position 同步补齐。
|
||||
- ⚠️ 整页 `cover` 背景图必然裁边(1280×1400 视口只露出 2400 宽的中间 1234px,竖屏更狠)
|
||||
→ 贴边的插画不要放进背景图,放 `--tb-deco`(固定像素 + 四角定位 + fixed)。
|
||||
- 主题插画可以不用图片:`dev/gen_shiroko_chibi.py` 用 SVG 路径手绘 Q 版人物,3.8KB/张、任意尺寸不糊;
|
||||
生成模型给的"透明背景"线稿抠不干净、无法目检,不如自己画。
|
||||
- 主题插画优先"图生图 + 抠白底":`image1` 给既有立绘、`input_fidelity: high`、
|
||||
提示词强调 `flat vector illustration, no outlines`,四张姿态的角色一致性远好于手绘 SVG。
|
||||
抠图用四角+四边中点 8 点 floodfill(容差 16)+ alpha MinFilter(3) 收边。
|
||||
- ⚠️ deco 的定位百分比是「(视口高-图高)×pct」,`center` 在矮视口会和大尺寸立绘重叠;
|
||||
右侧被立绘(bottom 0,500+px)占住时,第二个人物要放左侧、用显式 `top N%`。
|
||||
- 主题装饰摆位:**贴边、成组、避开页脚**。四角各摆一个会读成贴纸墙纸;
|
||||
页脚文案右对齐 → 右下角不能放人物。`--tb-deco` 是 `fixed` 的视口层,
|
||||
多个人物时要成组(左右各一个是"图钉",同一角一组才像场景)。
|
||||
- ⚠️ `background-position` 四值语法必须**先是边关键字**:`left calc(...) bottom 0` 合法,
|
||||
`calc(...) bottom 0` 非法 → 整条声明作废、回落到 0% 0%(人物跑到左上、背景层错位出现色带)。
|
||||
- 主题视觉的**结构**比摆位重要:二次元站点的通行做法是「整屏场景插画 + UI 悬浮其上」
|
||||
(蔚蓝档案 JP)。在白底上往留白里摆人物,无论多大、几个,都会读成贴纸。
|
||||
卡片/顶栏配半透明白 + `backdrop-filter` 才有"浮在画面上"的关系。
|
||||
- ⚠️ `ruleBody` 取令牌前要剥 `/* */`:注释会和后面的令牌粘成一个键,色块预览静默失效。
|
||||
|
||||
Reference in New Issue
Block a user