615 lines
49 KiB
Markdown
615 lines
49 KiB
Markdown
# 2026-10-19
|
||
|
||
## 部署后复测(强制严格档那批)
|
||
|
||
- 规则页返回首页已居中:线上 `<div class="uk-margin-large-top uk-text-center">` ✔
|
||
- 验证码弹窗按钮已缩短:线上 `>送信<` ✔(旧的「認証して送信」已消失)
|
||
- 箱主面板 `/user/questions`:限制期条幅 + 「查看社区规则」链接都在 ✔
|
||
- 严格度锁定(admin6 当前 L2):
|
||
- `/user/profile` 出现「处置期间审核严格度已锁定为最高档,解除后自动恢复你的原设置」✔
|
||
- `select[name=strictness]` 与「保存审核严格度」按钮均带 `disabled` ✔
|
||
- 直接 POST `/user/moderation-strictness/update?tab=security` + `strictness=lax` → 302 回 profile,
|
||
flash 仍是「已锁定」,且选中档仍为 `strict`(说明未写入)✔
|
||
- 踩坑:表单 action 是 `/user/moderation-strictness/update`(**没有** `/profile` 段);用错路径返回 302 到 `/`,
|
||
容易误判成「被中间件拦」。另外每次 POST 都要重新取 `_csrf`,用上一页(已跳首页)的 token 会 400。
|
||
|
||
## 记忆维护
|
||
|
||
MEMORY.md 一度 19KB 被注入截断,已重写压缩到约 4.9k 字符(8.1KB),保留构建/行尾/embed/审核/处置/CLI 等硬约束,
|
||
删去已在日志里的过程细节。
|
||
|
||
## CLI 文档 + 全量代码审阅(同日)
|
||
|
||
新增 `docs/cli.md`:命令总览、reports 三种用法与口径、restrict 级别语义/副作用、unrestrict、
|
||
hide-public、ban/restore、典型处置流程、排错表、明确不做的事。
|
||
|
||
审阅中发现并修复(全部本地临时库实测):
|
||
|
||
1. **`hide-public` 举报闭环静默失效**(真 bug):`build()` 带 `is_private=false`,在 Update 之后拿它当子查询恒为空集
|
||
→ 隐藏成功但 `content_reports` 仍 pending。改为 Update 前 `Pluck("id")` 快照,再按 ID 更新/标记。
|
||
实测:3 条 pending → accepted,未波及其他箱主。
|
||
2. **整箱分享卡未拦截**:`ShareCard` 直接走缓存,L1–L3 仍能生成整箱图。已在缓存查询前加拦截(fail-closed)。
|
||
3. **L2 通知邮件误导**:强制私密后仍给提问者发「你的提问有回答」,链接点开 302。改为 `!forcedPrivate` 才发。
|
||
4. **L2 写入顺序**:`SetPrivate` + `AnswerByID` 两条语句有「公开+已作答」窗口,第二条失败还会留下「私密+无回答」。
|
||
新增 `Questions.AnswerByIDPrivate`(单条 UPDATE 同时写 answer/is_read/is_private),并补测试 `TestQuestions/AnswerByIDPrivate`。
|
||
5. **`ListAPI` 绕过 Pager**:L4 停用与页面访问密码只挡 HTML,`/api/v1/user/{domain}/questions` 仍能整页拉。
|
||
已补两道校验(均返回「用户不存在」,不暴露原因)。**页面密码可被 API 绕过是升级前就存在的问题。**
|
||
6. **L1 曝光残留**:`GetShowInHome` 的 Count 未过滤(分页总数虚高、末页空白)、热门标签查询未过滤。已补同一子查询。
|
||
7. **性能**:`ActiveLevel` 由两次查询改一次(取最高 rank);新增 `moderation.EffectiveStrictnessFor(level, raw)`,
|
||
发布路径上不再重复查库(原先一次发布最多 4 次处置查询)。
|
||
8. **证据包口径**:分母是「累计公开回答」不是窗口产出 → 文案加「累计」;新增 `待处理 N` / `已处理` 标记,
|
||
避免把已闭合的举报误读成还挂着;补 `q.deleted_at IS NULL`;`--stats` 说明最长回溯 30 天;CLI 对 `active` 做 nil 守卫。
|
||
|
||
验证:`go build ./...` EXIT=0;db/i18n/templates 测试全绿;CLI 全流程(restrict L3 → 证据包 → unrestrict)实测通过;
|
||
临时库已 DROP。
|
||
|
||
遗留(未改,已向用户说明):L4 不覆盖 `/vote/{token}`;`restrict` 没有 `--days` 上限;处置不发邮件给箱主(只有登录后条幅);
|
||
`abuseAlertCooldowns` 进程内 map 无清理(重启即失效)。
|
||
|
||
## 第二轮:处置通知邮件 + 超长确认 + 冷却表清理
|
||
|
||
- 新增 `templates/mail/restriction-notice.html` + `mail.SendRestrictionNoticeMail(RestrictionNotice)`:
|
||
文案由调用方按**箱主自己的语言**(`users.language`,空则 `conf.App.DefaultLang`)预翻译后传入,
|
||
模板本身单语言。**不含 `--reason` 原文**,只有级别名/到期/规则链接/申诉渠道。
|
||
i18n 新增 10 个键 ×4 语言:`mail.restriction.*`(6)+ `restriction.level.L1..L4`。
|
||
- `restrict`:新增 `--force`(`--days > 90` 必填,否则直接报错)与 `--no-notify`;交互模式下超长处置
|
||
还要再输一次级别名(`--force --yes` 视为两次确认)。发信失败只 warn + 终端提示,**不回滚处置**;
|
||
发信加了 20s 超时(gomail 无 Timeout 字段,裸 Dial 会等系统 TCP 超时)。
|
||
- `abuseAlertCooldowns` 加惰性清理(>1024 条时删除已过 24h 的项)。
|
||
- 顺带:`/user/questions` 限制期条幅改用 `restriction.level.*` 的名称(「收窄公开」)而不是裸 `L2`。
|
||
|
||
验证:真实 SMTP sink 实收两封(ja 与 zh-CN),标题/级别名/到期/规则链接均正确、`--reason` 未出现、
|
||
链接未被 `ZgotmplZ` 转义;`--days 120` 无 `--force` 被拒;黑洞 SMTP 下 23s 内返回(未阻塞两分钟)。
|
||
`go build ./...`、`go vet`、db/i18n/templates 测试、checktpl 全绿。
|
||
|
||
遗留:`unrestrict` 不发通知(只解禁);`restrict` 对 `--days` 无上限(设计如此)。
|
||
|
||
## 第三轮:解除处置也发通知
|
||
|
||
- 复用同一封模板 `mail/restriction-notice.html`:`appeal` 段改为 `{{if .appeal}}` 可选,
|
||
解除通知不传该字段就整段省略。
|
||
- 新增 i18n 键 ×4:`mail.unrestrict.heading/body/note`(body 带域与级别名;
|
||
note 明确写「处置记录保留用于审计、私密内容不会自动恢复」)。
|
||
- `unrestrict` 新增 `--no-notify`;撤销前先 `ActiveLevel` 取级别(撤销后读不到),
|
||
撤销后按箱主语言发解除通知。`affected==0` 直接返回、不发信。
|
||
- 抽出公共件:`sendNotice`(20s 超时)、`reportNoticeResult`(统一成功/失败输出)。
|
||
- 文档补充:`unrestrict` 的 `--no-notify`、解除通知内容、以及**到期自动解除不会发信**(无调度器)。
|
||
|
||
实测(本地 SMTP sink,2 封):处置信含级别名/到期/申诉段;解除信含「已解除」+ 私密内容提示、
|
||
**无申诉段**;两封都不含 `--reason`。`--no-notify` 两处都不发信;无生效处置时不发信。
|
||
`go build ./...`、db/i18n/templates 测试、checktpl 全绿。
|
||
|
||
## 上线前最终验收(部署后实测)
|
||
|
||
- 公开页:`/` `/rules` `/restricted` `/deactivated` 全部 200;`/rules` 四语言标题正确。
|
||
- 箱主面板(admin6,L2):条幅显示「收窄公开」+ 规则链接;严格度面板锁定提示 1 处、下拉 disabled。
|
||
- **L2 写入路径实测**(关键,因为改成了 `AnswerByIDPrivate` 单条 UPDATE):
|
||
对未回答题 q95 作答 → 302 回题目页 → flash「已保存为私密:当前处于平台处置期,回答不会公开」→ 匿名访问 302。
|
||
- 公开列表 API `/api/v1/user/{domain}/questions` 正常返回 `{code,data:{data,next_cursor,has_more}}`(新增的 L4/密码校验没有打断正常路径)。
|
||
- 整箱分享卡:受处置箱主 302(拦截生效);未处置箱主 500,原因是**测试站未配置字体**(`分享图生成失败:字体文件未配置或无法加载`),与本次改动无关。
|
||
这一点反而证明拦截位置在生成之前。
|
||
- 未能自测:CLI 发信(restrict/unrestrict 通知)需要在服务器上跑命令,只能由用户执行。
|
||
|
||
注意:admin6 现有测试题 73/88/89/90/94/95;其中 95 已被我回答并存为私密(内容带 `restrict self-check`)。
|
||
admin6 的 L2 于 2026-10-19 21:29 自动到期。
|
||
|
||
## 邮件模板统一(用户反馈:页脚与其它邮件不一致)
|
||
|
||
- 原因:`restriction-notice.html` 是我新写的,页脚用了自造的 `© 2026 TamaBox`,与项目其它 11 封邮件的
|
||
「此邮件由系统自动发送,请勿直接回复。<br/>{{ .year }} 狼的提问箱」不一致。
|
||
- 处理:整封模板按 `question-reported.html` 的结构重写(24px 标题 + `{{.time}}` 副行、同样的卡片/页脚/外间距),
|
||
`time` 由 `mail.SendRestrictionNoticeMail` 统一注入(调用方不用各自拼时间)。
|
||
- 实测(本地 SMTP sink):页脚文本与 question-reported 完全一致,`© ... TamaBox` 已消失,头部时间行存在。
|
||
- 注意:站点名在所有邮件模板里都是硬编码「狼的提问箱」,不是配置项;改站名要一起改 12 个模板。
|
||
|
||
## 页脚精简(移动端一行放不下)
|
||
|
||
- 去掉「盘」链接(`https://share.shiroko.one`,zh 文案就叫「盘」),并删除已无引用的
|
||
`site.footer.drive` 键(四语言同步删,保证 `TestDictionaryComplete` 词条数一致)。
|
||
- `site.footer.rules`:zh-CN「社区规则」→「规则」,zh-TW「社群規則」→「規則」(en/ja 本就短)。
|
||
- 现在页脚一行:规则 | 找站长 | 公告 | 聊天 | 状态 | 主页。
|
||
- 注意:`templates/base/footer.html` 用 `|` 分隔,行首行尾各带一个,删整行时不会留下悬空分隔符(已核对)。
|
||
|
||
## 页脚改两行(用户改主意:盘恢复)
|
||
|
||
- 恢复 `https://share.shiroko.one`「盘」链接与 `site.footer.drive` 键(zh-CN 盘 / zh-TW 盤 / en Drive / ja ドライブ)。
|
||
- `site.footer.rules` 文案恢复:社区规则 / 社群規則(en/ja 不变)。
|
||
- 布局两行(靠 `<br>` 断行,不用额外 CSS):
|
||
第一行 `社区规则 | 找站长`,第二行 `盘 | 公告板 | 聊天室 | 监控板 | 导航页`。
|
||
- 键插在 `site.footer.rules` 之后(组末),空格数按同长度键复制,gofmt 对齐不变;dict 仍 CRLF。
|
||
- 验证:build / checktpl / i18n / templates 测试全绿。
|
||
|
||
## 页脚改为「从少到多」四行
|
||
|
||
版权行与存活计时移到最后两行(原来在最上面),整块按文字量递增:
|
||
`社区规则 | 找站长` → `盘 | 公告板 | 聊天室 | 监控板 | 导航页` → `Powered by NekoBox | ICP` → `提问箱已成功存活:…`。
|
||
实现只调整了 `<br>` 的位置,仍共 4 个 `<br>`,所以块高与顶部空行跟原来一致(未新增占位)。
|
||
注意:`box_runtime` 的脚本按 id 取元素,移动位置不影响;但 `<br>` 数量别随意增删,否则页脚高度会变。
|
||
|
||
## 前端样式体检(只出结论,未改代码)
|
||
|
||
量化事实(脚本统计 templates/**/*.html):
|
||
|
||
- 全站 **0 个 .css 文件**,样式全部内联:`base/header.html` 内含 **17.5KB CSS + 10.6KB JS**(模板总 42.5KB),
|
||
footer 3.0KB JS;全模板共 **24 个 `<style>` 块**,`user/profile.html` 自身还有 11.2KB CSS + 22.8KB JS(总 62KB)。
|
||
- `--tb-*` 设计变量 **只在 `html[data-theme="dark"]` 里定义**,light 主题没有 `:root` 一套;
|
||
调用处全靠 fallback(`#e9e9e9` / `#e5e5e5` / `#fafafa` / `#333` / `#666` 混用),explore 等页面还写死色值。
|
||
- **深色模式 FOUC**:`applyTheme(getTheme())` 在 body 内、`<nav>` 之后执行(header.html:812),
|
||
深色用户刷新时顶栏会先白闪一下。修法:把 theme 初始化内联脚本提到 `<head>`。
|
||
- 结构:`uk-container-xsmall`(750px)由 header 统一输出,仅 explore/vote/chat 页面自套 small;
|
||
`question/list.html` 仍用 `<center>`、内联 `style="min-height:260px"`,用户名/简介颜色由用户自选压在背景图上,无 scrim → 可读性无保障。
|
||
- 首页 `home.html` 是纯 UIkit 卡片 + `<hr>` + `style="color: red"` + `<pre>` 统计 + 首页挂 Waline(250 行 locale)。
|
||
|
||
建议分档(未实施):P0 = light/dark 双套 token + 圆角/阴影/中文字体栈 + 首页重排 + 背景图 scrim;
|
||
P1 = header CSS 抽成 `static/css/*.css`(注意 `embed.go`/`static/fs.go` 都是显式清单)+ FOUC 修复;
|
||
P2 = 图标统一(UIkit icons 取代 emoji/位图 png)、标签减量、viewpoint/meta 去重。
|
||
|
||
## P0 前端美化(已实施)
|
||
|
||
用户拍板:卡片**结构不变,只加圆角和柔阴影**;先做 P0,不抽 CSS 文件。
|
||
|
||
改动文件:
|
||
- `templates/base/header.html`
|
||
- head 顶部新增**主题初始化内联脚本**(深色用户不再白闪;原 `applyTheme` 在 body 里、nav 之后执行)。
|
||
顺手删掉 body 脚本里的 `applyTheme(getTheme())`,只保留切换逻辑。
|
||
- 新增**设计令牌块**:`:root` 亮色 + `html[data-theme=dark]` 暗色,同名 14 个(bg/surface/surface-2/surface-hover/
|
||
border/text/text-secondary/text-muted/primary/primary-weak/radius/radius-sm/radius-xs/shadow/shadow-hover/font)。
|
||
原暗色块里的 7 个变量定义已删除(现在统一在令牌块)。
|
||
- 新增**基础质感层**:html+body 底色与字体栈(system-ui + 苹方/冬青黑/雅黑)、卡片 12px 圆角 + 双层柔投影、
|
||
按钮/输入 8px、标签 6px、alert 10px、modal 14px、导航栏白底、`:focus-visible` 描边、`tabular-nums`、
|
||
`prefers-reduced-motion`。
|
||
- `.tb-cover`:封面遮罩;默认浅色(配 `.uk-light` 的黑字兜底),`.tb-cover-dark` 为深色。
|
||
- `.tb-img-uploader` 上收到 header(原来 item / new-question 各写一份**内联 style**,写死 #f8f8f8 → 深色下露白)。
|
||
- `templates/home.html`:整页重排为 hero(标题+一句话+探索/规则按钮)+ 3 张功能卡 + 域名迁移公告
|
||
+ 提醒列表 + `<details>` 折叠更新日志 + 3 个数字统计卡(`.SiteStats` 数值字段,30 分钟 JS 刷新改为按字段更新)。
|
||
全部原有信息保留;页内 `style=` 归零。
|
||
- `templates/question/list.html`:封面加 `.tb-cover`(仅当有背景图),新增 12 行 JS 按用户名颜色亮度
|
||
决定遮罩方向;`min-height:260px` 内联改为 `.tb-profile-cover`。
|
||
- 6 个页面样式块里的硬编码色值收敛到令牌(explore/question-list/profile/list/item/vote-page,共 ~84 行),
|
||
CRLF 文件用字节级替换(未改动行尾)。
|
||
|
||
验证:`go test ./templates/... ./internal/i18n/...` 全绿;`go build`(overlay)通过。
|
||
新增 `templates/zz_preview_test.go`:`TEST_PREVIEW=1 go test ./templates -run TestGenPreview` 产出
|
||
`outputs/preview-{home,box,explore}-{light,dark}.html`,用无头 Chrome 截图核对过亮暗两套。
|
||
⚠️ 本机 **Chrome 新版 headless 会挂住**,必须 `--headless=old`,且页面外部资源要本地化
|
||
(`outputs/preview-assets/`,从 cdnjs.snrat.com 取 UIkit 3.3.3;cloudflare/jsdelivr 在本机不通)。
|
||
|
||
## 封面遮罩 P0 缺陷修复(重要)
|
||
|
||
**症状**:`.tb-cover::after` 遮罩不渲染,且**完全不报错**——截图对比像素只差 ±1(只看着像"没效果")。
|
||
|
||
**根因**:UIkit 3.3.3 自带
|
||
`.uk-flex::after,.uk-flex::before{display:none}`(优先级 0,2,0),
|
||
而封面元素是 `uk-flex`,我写的 `.tb-cover::after`(0,1,1)优先级不够 → 被压成 display:none。
|
||
定位方法:`getComputedStyle(el,'::after').display` 得到 `none`;再逐个 `classList.remove` 试 →
|
||
去掉 `uk-flex` 立刻变 `block`。最后在 uikit.min.css 里 grep 到那条规则。
|
||
**修法**:选择器写成 `.tb-cover::after, .tb-cover.uk-flex::after` 并显式 `display:block`。
|
||
|
||
**实测效果**(无头 Chrome 截图 + 像素测量,非目测):
|
||
- 深色渐变背景 + 白字:封面底部亮度 0.229→0.102,白字对比度 **3.77→6.89**(过 WCAG AA 4.5)
|
||
- 线上默认背景(`box_background.png` 2048×1152,接近全白)+ 黑字:0.912→0.971,对比度 19.25→20.42,
|
||
即**对默认背景几乎没有可见影响**
|
||
- 亮度判定 JS 已验证:`#fff` 会加 `tb-cover-dark`,`#000` 不加。
|
||
|
||
**教训**:CSS 改动不能只看"规则写上了/类加上了",必须量像素。伪元素 + UIkit 工具类组合要在计算样式层验证。
|
||
|
||
## 账号背景图的真实链路(回答用户提问时查证)
|
||
|
||
- 注册时把 `[upload] default_background` **一次性复制**进 `users.background`(`route/auth/register.go:51`),
|
||
不是每次渲染兜底 → 之后改配置**不影响存量用户**。
|
||
- 页面只用 `.PageUser.Background` 渲染 `data-src`;为空则无背景图、无遮罩。
|
||
- 分享卡是另一条路:`route/question/share.go:345` 用 `user.Background`,为空时回退 `conf.App.ShareBackground`。
|
||
- 线上 `box.shiroko.one/_/tamakyi` 实际值 = `https://r2.337845818.best/tamabox/box_background.png`。
|
||
- 预览里那张"渐变图"是 `zz_preview_test.go` 造的测试数据(data: URI 渐变),**不是产品行为**;
|
||
顺带发现 Go 模板会把 `data:` URI 在属性上下文里替换成 `#ZgotmplZ`(真实 http URL 不受影响)。
|
||
|
||
## 设置页「防护设置」缺白底(用户报)
|
||
|
||
根因:其它面板都包在 `<section class="tb-pcard">`,只有内容防护面板(profile.html:842)是裸 `div.profile-panel`,
|
||
直接露出页面灰底 `--tb-bg`。修法:包一层 `section.tb-pcard`,并把 `.tb-pcard` 的 `background:#fff` 改成
|
||
`var(--tb-surface)`(删掉随之冗余的 dark 覆盖)。
|
||
实测(计算样式):account 与 security 面板 bg 亮色均 `rgb(255,255,255)`、暗色均 `rgb(36,40,48)`,圆角 10px,完全一致。
|
||
顺手把 `security` 加入 `render_test.go` 的面板渲染测试列表(原来只测 account/question/chat/vote)。
|
||
|
||
⚠️ 教训:用脚本批量缩进/hash 插入时先 dry-run。这次第一版脚本把「空行」也当成需要写 `<section>` 的行,
|
||
一次性插了 5 个多余标签;靠立刻 `sed` 复查发现并回滚。
|
||
|
||
## P1 第一步:CSS 抽离(已完成)
|
||
|
||
- 新增 `static/css/base.css`(23KB,含注释;gzip 后约 6KB),内容 = 原 header 里的
|
||
设计令牌 / UIkit 补丁 / 深色覆盖 / 质感层 / 顶栏响应式 / 标签 / Markdown / 进度条 / 移动端底栏。
|
||
- `header.html`:内联 `<style>` 从 17.5KB 降到只剩 1 个(算数验证码,跟着 `{{ if CaptchaType }}` 条件渲染);
|
||
改为 `<link href="/static/css/base.css?v=20261019a">`,位置在 UIkit 样式之后(保证覆盖顺序)。
|
||
单页 HTML 42.5KB → 30.9KB。
|
||
- `static/fs.go` 的 `//go:embed` 显式清单加 `css/base.css`(漏了就是运行期 404 + 全站无样式,编译期不报错)。
|
||
- 静态缓存沿用既有 `CacheControl: public, max-age=86400`;靠 `?v=` 版本号破缓存。
|
||
|
||
**验证方式**:改动前后各截一次同样尺寸的截图做像素比对。
|
||
home-light / home-dark / explore-light 三页差异均 **0.024%**,bbox 只有 (11,56)-(29,74) 一个 18×18 方块
|
||
= 导航栏随机动物 emoji(每次加载都变),不是样式回归。go build + 模板/i18n 测试全绿。
|
||
|
||
## 测试环境复测(P1 CSS 抽离 + 防护面板白底,已上线)
|
||
|
||
- `GET /static/css/base.css?v=20261019a` → 200 / `text/css` / **23088 字节,与本地 `static/css/base.css` 逐字节一致** /
|
||
`Cache-Control: public, max-age=86400` / gzip 后 7109 字节。
|
||
- 首页 32747 字节(改动前 52747),gzip 10534(改动前 13642);页面内联 `<style>` 只剩验证码 + 首页自己的样式块。
|
||
- 设置页(登录 admin6):`data-profile-panel="security"` 下已有 `<section class="tb-pcard">`;
|
||
用线上 HTML + 本地 CSS 复算样式 → account / security 两块 `bg=rgb(255,255,255)`、`border-radius:10px`,页面底 `rgb(247,248,250)`。
|
||
- `/_/admin6` 公开页:`class="... uk-light tb-cover"`,`:after` 计算值 **display=block, width=689px**
|
||
→ 遮罩在真实部署上确实渲染了(UIkit `.uk-flex::after{display:none}` 的坑已解决)。
|
||
该用户用户名为 `rgb(0,0,0)`,走浅色遮罩(符合预期)。
|
||
- 各页 200:`/`、`/explore`、`/rules`、`/user/questions`;`/user/vote-form` 无参 302 属正常。
|
||
- 首页首次访问总字节略增(10.5+7.1 vs 13.6KB gzip),但 CSS 跨页缓存后每页少 3.1KB,且服务端少渲染 17.5KB 模板。
|
||
|
||
## 专属测试账号(用户指定)
|
||
|
||
- `https://box-dev-api.337845818.best/_/admin6`,邮箱 `[email protected]`,密码 `adminadmin`。
|
||
- 登录需算数验证码,curl 流程:GET /login 取 `_csrf`(同时存 cookie)→ `GET /api/captcha/generate` 拿 `{id, question}`
|
||
→ 算答案 → `POST /api/captcha/verify`(头 `X-CSRF-Token`,体 `{id, answer}`)→
|
||
`POST /login`(email/password/_csrf/`g-recaptcha-response=id:answer`)→ 302 到 `/`,cookie 已带登录态。
|
||
|
||
## P2 一致性收尾(已实施,未部署)
|
||
|
||
1. **导航图标**:🌐🌙☀️ emoji → 内联单色 SVG(`stroke: currentColor`,跟随主题/悬停色)。
|
||
⚠️ 原计划用 UIkit icons,实测 **UIkit 3.3.3 的图标集里没有 moon / sun**(只有 world),
|
||
照写会渲染成空白按钮——所以三个图标全部改成内联 SVG,也不依赖 JS。
|
||
2. **标签**:投票类型 20 处 `uk-label uk-label-primary`(实心蓝)→ `.tb-chip` 轻量胶囊;
|
||
探索页热门标签原来的 5 色轮换(success/warning/danger/primary/secondary)也统一成 `.tb-chip`。
|
||
箱主面板 `question-list.html` 另 6 处字节级替换(CRLF 保留)。
|
||
保留:置顶/私密/被举报/已读 这些**语义状态**仍是彩色 uk-label(有含义,不该降级)。
|
||
3. **遗留写法**:`<center>` ×2 → `div.uk-text-center`;占比条从
|
||
`div.uk-progress > div.uk-progressbar(style:width)` 改成 UIkit 3 原生
|
||
`<progress class="uk-progress" value=".." max="100">`,**删掉 25 行 UIkit2 兼容补丁**,
|
||
只保留 10px 高/6px 圆角的外观收窄。测试 `percent_check_test.go` 的断言同步改成 `value="66" max="100"`。
|
||
4. **meta**:删掉重复的 `msvalidate.01`(保留 head 顶部一份)、viewport 补 `initial-scale=1`。
|
||
|
||
**踩坑**:暗色主题图标原来写 `display:inline`,span 里放块级 `<svg>` 会让导航栏高度变化 → 整页下移 36px。
|
||
用行亮度曲线做互相关定位到 dy=36,改成 `inline-flex` 后复测差异回到 0.059%(只剩随机 emoji)。
|
||
教训:**改图标尺寸/显示模式一定要量整页位移**,不能只看局部截图。
|
||
|
||
## P2 线上复测(2026-10-19 12:2x)
|
||
|
||
- `GET /static/css/base.css?v=20261019c` → 200 / text/css / **23918 字节,与本地逐字节一致**,页面已引用该版本。
|
||
- meta:viewport 已带 `initial-scale=1`;`msvalidate.01` 只剩 1 份。
|
||
- 图标:首页 emoji 🌐🌙☀️ 归零,`<svg ... stroke="currentColor">` ×5(语言 1 + 桌面主题 2 + 移动主题 2);
|
||
计算样式暗色下 `#theme-toggle .tb-icon-dark` = `display:flex`、亮色图标 `none`;
|
||
**导航高度亮/暗均 80px** → 上次 36px 位移问题确认修好。
|
||
- 探索页:`.tb-chip` ×25(全部有底色 `rgb(244,246,248)`、文字 `rgb(92,99,112)`、圆角 6px、字号 11.52px);
|
||
残留彩色 `uk-label` 4 个,均为置顶/私密等语义状态。
|
||
- 结果页:`/user/votes/{id}/results` 原生 `<progress class="uk-progress" value max>`,计算高度 **10px**、圆角 6px;
|
||
遍历 10 个结果页 `uk-progressbar` 全部为 0。
|
||
- 巡检 `/ /explore /rules /login /register /_/admin6 /_/admin3` 均 200。
|
||
|
||
## P2 补漏(已改,待部署)
|
||
|
||
复测时发现 **结果页与参与页的题型标签仍是实心彩色** —— 那两处的 class 由 Go 模板函数
|
||
`fieldTypeLabelClass` 生成,我上一轮只按字面 `class="uk-label uk-label-primary"` 搜索,漏了它们。
|
||
|
||
- `templates/user/vote-results.html`(2 处)、`templates/vote/page.html`(2 处):序号 + 题型 → `.tb-chip`;
|
||
状态标签(未开始/已结束/进行中/已参与)保留彩色,属语义。
|
||
- 删除 `internal/template/template.go` 的 `fieldTypeLabelClass`(19 行,已无引用)。
|
||
顺带查明:该函数 `text/textarea` 走 `return ""`,注释说"默认灰",**实际渲染成 UIkit 默认蓝**,
|
||
所以文本框标签一直是蓝的——注释与实现不符。
|
||
- `.uk-progress::-webkit-progress-value` 圆角改成两端都圆(`-moz-progress-bar` 同步)。
|
||
⚠️ 起因是误判:我先前测"右端方角"时像素扫描循环上界写成 `range(110,830)`,恰好等于条宽,
|
||
于是右端永远"恒定不变"→ 假象。放宽到整幅宽度后两版端点曲线完全一致(830/832/833/834/833/832/830),
|
||
即**没有视觉 bug**。保留改动只为 Firefox 的 `-moz-progress-bar` 保险。
|
||
- `base.css?v=` → `20261019d`。`go build` + `./templates ./internal/i18n ./internal/template` 全绿。
|
||
|
||
**教训(重要)**:像素测量脚本的扫描范围必须覆盖整幅图,循环上界不能与被测对象边界重合,
|
||
否则会把"边界截断"读成"图形特征"。之前 3.77→6.89 那组对比不知是否也受此影响,需复查时再确认。
|
||
|
||
## P2 补漏部署后复测(12:48)
|
||
|
||
- `base.css?v=20261019d` 上线,24023 字节与本地逐字节一致。
|
||
- 删掉 `fieldTypeLabelClass` 后**无运行期报错**:公开投票页 3 个(200,tb-chip 8/16/4,uk-label 各 1 = 状态)、
|
||
结果页 4 个(200,tb-chip 8/2/0/16,彩色 label 全 0,progress 7/0/10/19)、
|
||
草稿预览页 2 个(200,tb-chip 2/6)。
|
||
- 巡检 `/ /explore /rules /user/questions /user/profile?tab=security /_/admin6 /_/admin3` 全 200 无模板错误。
|
||
- `/user/votes/{id}/preview` 对**已发布**投票 302 到 `/vote/{token}` 是设计如此(vote.go:1093),不是回归。
|
||
- 截图核对:结果页「1 単選 / 2 多選」已为浅色胶囊,进度条 10px 圆角 6px;参与页字段头同样统一。
|
||
|
||
## "移动端语言/主题按钮不见了"排查(结论:非缺陷)
|
||
|
||
实测(线上测试环境,`--force-device-scale-factor=1`):
|
||
- 宽 360/390/414/640 → 顶栏 `.tb-nav-utils` = `display:none`,底栏 `#tb-mobile-utils` = `flex`,
|
||
rect `0,639,W,46`,语言 `block`、主题按钮 `block`(48×30,SVG 16×16),命中测试落在 `mobile-lang-select`。
|
||
- 宽 700/768/980 → 底栏 `none`,顶栏 `flex`,右上角地球 + 月亮都可见(截图 `outputs/t-nav-right.png`)。
|
||
- 页面级差异:`/_/admin6` 底栏**只有月亮没有语言下拉** —— `IsUserPage=true` 让语言块整体不渲染(桌面同样没有)。
|
||
- 生产 `box.shiroko.one` 仍是旧构建(页面无 `base.css` 引用,底栏是 emoji 月亮),移动端同样正常。
|
||
|
||
用户确认是忘了「移动端移到固定底栏」这个设计,不是 bug。
|
||
留下的真实缺口:**箱主页(最常见的外部入口)完全没有语言切换**,见 MEMORY.md 前端样式条目。
|
||
|
||
## 修正:箱主页无语言切换是设计,不是缺口
|
||
|
||
上一节把它记成"真实缺口"是错的。用户明确:**箱主页属箱主个性化,以箱主配置为主**,
|
||
语言入口不出现是有意为之(`IsUserPage` 门控)。
|
||
MEMORY.md 对应条目已改为"有意设计、别当 bug 修、要动先问"。
|
||
|
||
## 备份(用户要求)
|
||
|
||
`../TamaBox-v4-mainline-20261019-1445.tar.gz`(940K,302 条目,排除 .git/.workbuddy/outputs)。
|
||
P1 剩余两项(首屏跟随系统深色、首页 Waline 懒加载)用户明确**先不做**。
|
||
|
||
## 主题系统方案(只出设计,未实现)
|
||
|
||
用户诉求:箱主在后台选主题、仅对箱主相关页面生效;站点级默认主题在配置里;要一份可复用的主题底稿。
|
||
|
||
已确认的现状事实(设计依据):
|
||
- 设计令牌只有 16 个,集中在 `static/css/base.css` 的 `:root`(亮) + `html[data-theme="dark"]`(暗)。
|
||
- `<html lang>` 在 `templates/base/header.html:2`,服务端渲染;主题属性可放在这里(无 FOUC)。
|
||
- `question.Pager`(`route/question/page.go:45`)是 `/_/{domain}` 与 `/_/{domain}/{id}` 的共同中间件
|
||
→ 打主题标记的唯一合适位置。
|
||
- `/user/*` 与 `/_/*` 共用 `IsUserPage`(context.go:241 按 URL 前缀判定),所以「箱主相关页面」
|
||
≠ IsUserPage,必须单独区分,否则设置页会被主题化。
|
||
- `static/fs.go` 与 `templates/embed.go` 都是显式 embed 清单(新增主题 css 必须手加)。
|
||
- 用户模型已有大量个性化字段(usernamecolor/introcolor/background…),主题作为 `users.theme` 同族最自然。
|
||
- 分享卡是服务端绘制图片(`route/question/share.go`),不经过 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)。
|
||
只写浅色块会让深色模式被浅色令牌污染(同特异性 + 主题文件后加载)。
|
||
|
||
## 主题系统初版(已实现,未部署)
|
||
|
||
用户拍板:①作用范围跟随箱主配置(即 /_/ 箱主相关页面)②主题只做配色/图案/整页背景,**不涉及布局**
|
||
③主题名可本地化 ④分享卡不参与。
|
||
|
||
实现:
|
||
- `internal/theme`:注册表 + 三级回退。`[theme] default/available` → 白名单(slug 正则 + css 是否存在),
|
||
`Resolve(用户值)`:`default`→空串(不加载)/命中→该 slug/其余→站点默认。"" 与 "default" 语义不同:
|
||
""=跟随站点默认,"default"=箱主明确选基础外观。
|
||
设置页色块从主题 css 浅色块里抓 `--tb-bg`/`--tb-primary` 拼渐变(值必须过色值正则,防注入)。
|
||
- `users.theme varchar(32) default ''`(AutoMigrate 加列)+ `UpdateTheme` 单列更新。
|
||
- `question.Pager` 注入 `Data["ThemeSlug"]`(`/_/{domain}`、单题、密码页、分享卡路由共用这一个中间件);
|
||
header.html 在 `<html>` 输出 `data-theme-style` 并条件加载 `themes/<slug>.css`。
|
||
- 设置页账号面板末尾新增「主题外观」卡片(独立表单 `POST /user/profile/theme`,不塞进 UpdateProfile,
|
||
避免与该面板其它字段互相覆盖);`{{ if .Themes }}` 包裹,未配置主题时不显示。
|
||
- 静态版本号统一 `conf.StaticVersion = "20261019e"`(base.css 与主题 css 共用,改样式只改一处)。
|
||
- `static/fs.go` 改嵌目录 `css/themes`(以后加主题文件不用再改 embed 清单)。
|
||
- 主题文件:`_scaffold.css`(底稿,含 19 令牌说明 + 素材规格 + 特异性规范)、`default.css`(恒等,仅色块预览)、
|
||
`sakura.css`(示例,纯 CSS 网点图案 + 粉调)。
|
||
- base.css:body 接 `--tb-page-image` / `--tb-page-pattern` / `--tb-page-pattern-size` 两层背景;
|
||
**UIkit 主色是编译期常量 #1e87f0**,新增映射块把 a/uk-link、uk-button-primary、checkbox/radio、input:focus、
|
||
uk-tab active 接到 `--tb-primary`(基础主题值相同,默认外观零变化)。
|
||
|
||
实测(无头 Chrome,box 页 data-theme-style=sakura):
|
||
- 浅色 primary `#e5678f`/bg `#fff6f8`/pattern 16px 网点命中;深色 `#ff8ab0`/`#241a1f`,卡片表面各自跟随。
|
||
- 未选主题的 box 页:无 data 属性、无 themes link、主按钮仍 `rgb(30,135,240)` → 零回归。
|
||
|
||
踩坑:
|
||
1. 第一版 sakura 页「发送提问」还是蓝色 —— UIkit 组件色写死,令牌覆盖不到,必须按组件映射。
|
||
2. 预览里 `{{ .StaticVersion }}` 为空(预览夹具没这个键),导致按固定串替换主题 css 失败;改用正则。
|
||
3. `writePreview` 用全文搜 `data-theme="dark"` 判断是否已加属性 → 命中内联主题 css 里的选择器,深色预览没生效;
|
||
改为只看 `<html ...>` 开标签。
|
||
4. 脚本改 form/user.go 时把插入块写在结构体中间,拼出 `} Language ...`;靠 Read 复查发现并重写。
|
||
|
||
## 主题 follow-up(部署后反馈两处)
|
||
|
||
1. **/user/* 不生效主题**:根因是 `ctx.Data["ThemeSlug"]` 只在 `question.Pager`(`/_/{domain}`)里注入,
|
||
`/user/*` 完全没走这条链路。修法:`internal/context/context.go` 的 Contexter 里按
|
||
`isThemeScope(path) && c.IsLogged` 注入 `theme.Resolve(c.User.Theme)`;
|
||
`isThemeScope` 只认 `/user` 与 `/user/`(不能用裸 `HasPrefix("/user")`,会误吞 `/username`)。
|
||
新增 `internal/context/context_test.go` 守住路径白名单;page.go 的注释同步改。
|
||
公开页(`/`、`/explore`、`/vote/{token}`)**仍不跟随**登录用户主题,保持站点默认。
|
||
|
||
2. **sakura 显示奇怪**(用户选择:顶栏白与粉底割裂 / 卡片纯白太突 / 底色分布不均):
|
||
- base.css 新增令牌 `--tb-navbar`(`:root` 里 `var(--tb-surface)`,亮暗两条 navbar 规则都改成
|
||
`var(--tb-navbar, var(--tb-surface))`),主题可单独给顶栏上色。
|
||
- sakura v2:底色改**纯色** `#fceef3`(不用渐变——渐变会被读成"不均匀"),
|
||
顶栏 `#fff6f9`、卡片 `#fffafc`(暖白,不再是纯白硬切),网点从 16px/0.14 放宽到 24px/0.05。
|
||
- 静态版本 `20261019f`;`_scaffold.css` 令牌说明 19→20。
|
||
- `profile.theme.hint` 四语言文案改成「提问箱页面 + 本人后台生效,公开页保持默认」。
|
||
- `zz_preview_test.go` 抽出 `inlineThemeCSS()`,并新增 `preview-profile-sakura-*`(后台页面也要在主题下检查)。
|
||
|
||
验证:`go build ./...`(须带 webp overlay)、i18n/theme/context/templates 四个包测试全绿。
|
||
渲染法:抓线上 `/_/admin6` HTML → 把 base.css 指到本地新文件、主题 css 内联 → 无头 Chrome `--headless=old` 截图。
|
||
|
||
## 投票页 + 标签主色(部署复核后第二轮)
|
||
|
||
- `/vote/{token}` 主题:`route/vote/vote.go` 的 `Page` 里本来就会 `db.Users.GetByID(vote.UserID)` 取 owner,
|
||
直接在同一分支补 `ctx.Data["ThemeSlug"] = theme.Resolve(owner.Theme)`;box 页 / /user 后台 / 投票页三处
|
||
全部由发布者配置决定。GetByID 失败(箱主已删)保持站点默认外观。
|
||
- **`uk-label` 默认色就是 UIkit 主色 #1e87f0**:主题下提问管理的「未回答 / 未读」两枚胶囊还是蓝的。
|
||
base.css 追加
|
||
`.uk-label:not(.uk-label-danger):not(.uk-label-warning):not(.uk-label-success):not(.uk-label-secondary)`
|
||
→ `--tb-primary`。**必须用 :not() 排除语义色**:同特异性且 base.css 在 UIkit 之后,不排除会把
|
||
danger/warning/success 一起刷成主色。默认主题实测仍是 (30,135,240),零回归。
|
||
- StaticVersion → `20261019g`。
|
||
- 线上复核(admin6 专属账号,curl 走完整登录:GET /login 取 _csrf → /api/captcha/generate →
|
||
/api/captcha/verify(**answer 必须是数字,传字符串会 400 请求格式错误**)→ POST /login):
|
||
`/user/profile`、`/user/questions` 均已输出 `data-theme-style="sakura"` 与 themes link(v=20261019f)。
|
||
投票页在 f 版本仍无主题 → 需再部署一次。
|
||
|
||
## 投票页语言跟随发布者(第三轮)
|
||
|
||
- 抽出 `applyOwnerLocale(data map[string]interface{}, owner *db.User)`(route/vote/vote.go 顶部):
|
||
同时写 `Owner` / `Lang` / `LangCode` / `ThemeSlug`。
|
||
签名故意收 Data 而不是 `context.Context`:`Context` 内部字段不可在包外构造,收 map 才能单测
|
||
(`route/vote/locale_test.go`,纯内存,不碰 DB)。
|
||
- `Page`:owner 查询提到 `FeatureStateOf` **之前**——否则「投票功能已关闭」这类提示先按访客语言生成。
|
||
- `Submit`:同样先取 owner 再取 `lang`,保证提交后回跳页的 flash 与页面语言一致;
|
||
顺带删掉尾部重复的 `GetByID(owner)`(表单类型通知邮件复用同一个 owner),少一次查询。
|
||
- 四语言 `profile.theme.hint` 与 changelog 范围说明补上「你发布的投票页」。
|
||
- 验证:vote/i18n/context/theme/templates 全绿;线上 `lang="ja"` + 中文内容的不一致待部署后复测。
|
||
|
||
### 部署后复测(v=20261019g)
|
||
|
||
- `/vote/7kcP5kR8YnJC`(admin6):`lang="zh-CN" data-theme-style="sakura"`,base/theme css 均 v=g,
|
||
日文串(投票する / 匿名での参加 / ログイン)全部消失 → 语言与主题同步跟随发布者。
|
||
- `/ _/admin6`、`/user/profile`:仍为 `zh-CN` + sakura,未回归。
|
||
- 隔离性:另取三个 token 抽查 —— 4G3i79OCYa2E / 7l0MwF7cLz4T 为 `lang="ja"`(无语言偏好、无主题),
|
||
9J1zgXwQ05Ai 为 `lang="zh-CN"` 且**无** data-theme-style → 主题不会跨箱主泄漏。
|
||
- 遗留观感问题(未改,等用户决定):投票页的「允许匿名参与」提示条是 `uk-alert-primary`,
|
||
仍是 UIkit 浅蓝,在粉底上偏冷。改它需要「浅底 + 深主色文字」的对比度,现有 `--tb-primary-weak`
|
||
叠白后对比度只有 ~2.6:1,得先加一个 primary-strong 令牌,故未动。
|
||
|
||
### 顶栏配色再修(v=20261019h)
|
||
|
||
用户反馈「探索/注册链接底色粉色,但整条顶栏是灰色,很突兀」。实测部署版:顶栏 `#fff6f9`、
|
||
页面 `#fceef3` —— 近白横带压在饱和粉底上,肉眼就读成「白色/灰色的一条」。无头 Chrome 截图 +
|
||
像素采样确认不是 UIkit 默认 #f8f8f8 回退(那说明变量没生效,现象会连带整页失效)。
|
||
- sakura 浅色 `--tb-navbar` 直接取页面底色 `#fceef3`(深色同样 `#241a1f`),横带消失;
|
||
- 顶栏链接 `#999` 在粉底上偏淡 → 主题内微调为 `--tb-text-secondary`,hover 用 `--tb-primary`
|
||
(深色被 base.css 的 `!important` 规则接管,不受影响);
|
||
- 顺带 `uk-alert-primary`(匿名参与提示/空列表)的 UIkit 浅蓝是页面上唯一的冷色块,
|
||
改为 `--tb-primary-weak` 底 + `#a63a5e` 文字(≈5.3:1)。
|
||
- 以上三条都写在 sakura.css 的「组件微调」段,只对 sakura 生效,默认主题零影响。
|
||
|
||
## 主题「砂狼白子 / shiroko」(v=20261019i)
|
||
|
||
- `static/css/themes/shiroko.css`:灰蓝银白令牌(浅 #edf2f8 / 主色 #4a7fc1),
|
||
组件微调只有两条 —— `.uk-card-default` 右下角水印立绘(`background-size: auto 176px`,纯背景层不改布局)、
|
||
顶栏链接色提到 `--tb-text-secondary`;深色不铺立绘(浅色构图在深色下会糊)。
|
||
- 素材 `static/images/themes/shiroko/`(68KB):`bg-desktop.webp`(2400×1350) / `bg-mobile.webp`(900×1600) /
|
||
`hero-fade.webp`(275×620,16% alpha) / `pattern.svg` / `pattern-dark.svg`。
|
||
- **`static/fs.go` 的 embed 清单加了 `images/themes`**(目录项,子目录自动覆盖)——
|
||
这是「新增顶层静态目录必须手工登记」的又一次踩点。
|
||
- 背景是 PIL 合成的:1280 行的竖向渐变 + 白色径向光晕 + 立绘 30% 贴右下 + 狼 10% / 光环 20% / 纸飞机 18%。
|
||
实测 mean≈244、std≈9;右侧留白区 std 13(立绘可见),中间 std 1.9(正文区干净)。
|
||
- ⚠️ **`cover` 会按视口裁剪**:竖屏/超宽屏会把贴在图片最右的立绘裁掉,所以立绘不能太靠边 ——
|
||
位置要留在图片横向 24%~76% 之外就得配 `@media` 换图(本次桌面 16:9 + 移动 9:16 两张)。
|
||
- 生成素材(ImageGen)8 张里只有 3 张可用:线稿图案(狼/光环/纸飞机)抠白底干净;
|
||
2 张人像抠底失败(背景不是纯白,`background: transparent` 实际没生效),2 张场景背景几乎没有内容(std<5)。
|
||
教训:**没有视觉能力时,生成图要用 `mean/std/edge` 先验内容量**,别直接拿去合成。
|
||
- 素材来源与版权写在 `docs/theme-shiroko.md`(角色版权 NEXON / Yostar,非商用同人)。
|
||
- 启用需要在 `conf/app.ini` 的 `[theme] available` 里加 `shiroko`(示例配置已更新)。
|
||
|
||
## shiroko 插画重做:动物线稿 → Q 版人物(v=20261019k)
|
||
|
||
用户反馈「简笔画不是画动物,是人物形象」,插画层整体重做。
|
||
|
||
- 新增 `dev/gen_shiroko_chibi.py`:手写 SVG 生成 6 个 Q 版白子(wave/cheer/think/run/doze/peek),
|
||
3.8KB/张,造型逐项对齐用户立绘(灰发蘑菇头 + 圆钝狼耳 + 倾斜光环 + 青围巾 + 深蓝外套 + 灰裙 + 青黑球鞋)。
|
||
**生成模型这条路走不通**:`background: transparent` 不生效、无视觉反馈时无法判断好坏;
|
||
代码画路径反而可控、体积小、任意尺寸不糊。
|
||
- ⚠️ **cover 必然裁掉贴边插画**:1280×1400 视口下,2400×1350 的背景图只露出中部 1234px,
|
||
右下立绘被裁成 38px 一条。→ base.css 新增装饰层令牌 `--tb-deco / --tb-deco-size / --tb-deco-position`
|
||
(固定像素 + 四角定位 + fixed,不随视口比例变化)。
|
||
- ⚠️ **多背景层的值个数必须恒定**:deco 先写成 4 个值,background-image 变 6 层而 size/position 只有 3 个,
|
||
CSS 循环取值 → 整页重复小人。改成固定 6 个槽位(1 图案 + 6 装饰 + 1 背景 = 8 层),
|
||
主题必须写满 6 个(缺的填 none),这条写进了 `_scaffold.css`。
|
||
- 删掉 `bg-desktop.webp` / `bg-mobile.webp` / `hero.webp` / `dev/gen_shiroko_bg.py`;
|
||
整页底色改成纯 CSS 渐变(不用图片,也就没有 cover 缩放差异)。
|
||
- 深色关掉 deco(浅色线稿在深色底上糊成灰块);pattern 里的爪印换成 Q 版头像,
|
||
opacity 0.42→0.2、tile 240→320(原来铺得太密,像壁纸)。
|
||
- 验证:默认主题背景仍 `#f7f8fa`、顶栏纯白、卡片纯白(零回归);箱主页与 `/user` 后台四角装饰都不压内容;
|
||
移动端只留立绘 + peek。
|
||
|
||
## shiroko 插画二次返工:手绘 SVG → 参考立绘图生图(v=20261019l)
|
||
|
||
用户反馈上次的简笔画"太抽象"。根因判断:用户的立绘是**平涂无描边色块风**,
|
||
我用手绘线稿去配它,风格本身就冲突,而且没有真人手感,形准也做不到。
|
||
|
||
- 新方案:ImageGen **图生图**(`image1` = `static/images/shiroko-cutout.png`,`input_fidelity: high`),
|
||
提示词统一为「same character, same art style... flat vector illustration, no outlines」+ 换动作句,
|
||
生成 4 个姿态(wave / cheer / think / doze),1024×1024、纯白背景。
|
||
**四张角色特征与画风一致**,这一步是手绘做不到的。
|
||
- 抠图:四角 + 四边中点 8 点 floodfill(容差 16)+ alpha 1px MinFilter 收边,裁 bbox 后统一高 620、
|
||
WebP q90。实测无白边,光环中间的孔也正确透出。
|
||
- 删除 `dev/gen_shiroko_chibi.py` 与 6 个手绘 SVG;`chibi/*.webp` 4 张共 106KB。
|
||
图案里的手绘小头像也删了(和位图人物并排显得最抽象),改成雪花 + 光环 + 圆环 + 点簇。
|
||
- ⚠️ **deco 的 `center` 定位在矮视口会撞车**:右下立绘 520px 高,1366×768 下从 y=248 一直到底,
|
||
托腮放 `right center` 会直接站在立绘头顶。百分比是「(视口-图高)×pct」,改用 `top 46%` 并挪到左侧中部。
|
||
验证视口:1366×768 / 1440×900 / 1280×1400 三档,人物互不重叠。
|
||
- docs 补了完整提示词骨架、动作句与抠图参数,说明"必须平涂无描边"这条经验。
|
||
|
||
## shiroko 布局重排 + 新坐姿(v=20261019m)
|
||
|
||
用户反馈:左右不协调、最上边的头被顶栏挡住。
|
||
- 布局改为**左右各两个**(左上 wave / 左下 sit,右上 cheer / 右下立绘),
|
||
`≥920px` 真实视口才补中间两个(think / doze),矮屏不放(否则压到立绘)。
|
||
- 顶部定位从百分比改成**固定 `top 96px`**:顶栏实际高 60px(不是 80),
|
||
百分比是「(视口-图高)×pct」,小视口下算出来的值会落进顶栏里。
|
||
`bottom 0` 也改成 `bottom 6px`,避免脚贴页底。
|
||
- 新增坐姿(盘腿),ImageGen 图生图第 2 次才可用:第 1 张脚被画到画布底边(底部 78 像素贴边),
|
||
提示词补 `centered composition, whole body fully visible with generous empty margin` 后正常。
|
||
- ⚠️ **Chrome headless 的 `--window-size` ≠ CSS 视口**:实测 1080 → innerHeight 985、768 → 673
|
||
(差约 95px 的窗口边框)。所以 `@media (min-height: 1050px)` 在 1080p 下永远不触发。
|
||
判断媒体查询门槛要按「真实视口」写,截图验证必须加 `--force-device-scale-factor=1`,
|
||
否则系统 DPI 缩放会让截图尺寸和 window-size 也对不上(曾出现 1920 请求出 1088 宽)。
|
||
- git:仓库可用(origin = https://git.shiroko.one/tamakyi/tamabox,main),
|
||
已本地提交 `0c39aff`,但 `credential.helper=store` 里的凭据过期 → `Failed to authenticate user`。
|
||
打包 `../TamaBox-v4-mainline-20261019-2037.tar.gz`(1.5M,含图片素材)供直接部署。
|
||
|
||
### git push 补推
|
||
|
||
上次的 `Failed to authenticate user` 是临时态 —— 用户本机 Git for Windows 认证过之后 store 凭据恢复,
|
||
本轮 `git push origin main` 直接成功(`38cd41a..0c39aff`),`git rev-list --left-right --count origin/main...HEAD` = 0 0。
|
||
远端 8 个 shiroko 素材文件齐全。教训:**不要用一次 push 失败就断言无凭据**,先看一眼提交是否已存在、再试一次。
|
||
|
||
### 部署后「设置页找不到 shiroko」
|
||
|
||
线上实测:`/static/css/themes/shiroko.css?v=20261019m` 200 / 5038B 且与本地逐字节一致,
|
||
说明二进制确实带了新主题;但 `/user/profile` 渲染出来的主题单选只有 `""`、`default`、`sakura`
|
||
→ **根因是 `[theme] available` 白名单没登记 shiroko**(列表在 `theme.load()` 里按配置构建,启动时读一次)。
|
||
- `internal/theme/theme.go` 新增 `reportRegistry()` / `discover()`:启动打 info「主题注册表已加载 enabled=...」,
|
||
并把「css 在、但没登记」的主题以 warning 打出建议的 available 值。
|
||
没有这段时,这个故障在日志里完全没有痕迹,只能靠猜。
|
||
`discover()` 用 `fs.Glob(static.FS, "css/themes/*.css")`,用 slugRe 过滤掉 `_scaffold.css`(下划线开头)。
|
||
- `conf/app.sample.ini`:`available = default,sakura,shiroko` + 补充「没登记就不显示」的注释。
|
||
- `docs/theme-shiroko.md` 补「部署后看不到主题」两段排查(先 curl css 看 200/404,再看 available)。
|
||
- 排查口径:**先分「文件没发布」与「没登记」两类**,别一上来怀疑缓存;`StaticVersion` 只影响缓存,不影响列表。
|
||
|
||
### shiroko 装饰返工:散射 → 左下角一组(v=20261019n)
|
||
|
||
用户反馈「主题正常了,但展示方式怪怪的」。实测线上 1512×982 截图确认三处:
|
||
1. 四角各一个人 = 贴纸墙纸(人物之间、人物与内容都没有关系);
|
||
2. 半透明立绘(16%)叠在实心人物旁边 → 虚实混搭,像重影;
|
||
3. 右下角人物和右对齐的页脚文案(社区规则 / 备案号 / 存活时长)挤在一起。
|
||
改法:只留左下角一组(sit 330 + wave 190,`left 0/bottom 0`),删掉 hero-fade,
|
||
纹样减到雪花 + 光环 + 两点(opacity .16、tile 340),卡片补 1px 冷调描边(box-shadow 不占布局)。
|
||
990px 以下缩小左移,900px 以下(卡片占满宽)不出人物。
|
||
|
||
⚠️ **`background-position` 的四值语法必须以边关键字开头**:写成 `max(8px, calc(50% - 555px)) bottom 0`
|
||
整条声明作废并回落到左上角(表现为人物跑到顶上、背景出现错位色带)。
|
||
要贴卡片边只能 `left max(8px, calc(50% - 555px)) bottom 0`。
|
||
|
||
### 纹样横向条纹(v=20261019o)
|
||
|
||
图案元素原先全在 tile 上半部(雪花 y=52 / 光环 62 / 点 150、176),铺开后在整页形成
|
||
**横向条纹带**(左栏 60px 带宽均值 245→237→249,跳变 12),这是"看着怪"的第二个来源。
|
||
改成 3 行均布(52 / 132 / 152-212)后收敛为单调 245→236。
|
||
⚠️ 图片不走 `StaticVersion` 缓存键(`Cache-Control: max-age=86400`),换图案要自己给 url 加 `?v=`。
|
||
|
||
### 定稿:半隐在卡片后的单人(v=20261019p)
|
||
|
||
用户从 A/B/C/D 四版里选了 D(半隐在卡片后),并追加两条:人物离开底边一点、纹样再淡一点。
|
||
- `sit` 单人,`left max(6px, calc(50% - 515px)) bottom 26px`;宽度 177px,右缘压进卡片 40px。
|
||
- **离底边后必须配接触阴影**:一层 `radial-gradient(ellipse, rgba(63,96,138,.22), transparent 72%)`
|
||
128×15px,垫在脚下一格(deco 第 2 个槽位,绘制顺序在人物之下)。没有它,抬高 26px 会读成悬空。
|
||
- 纹样 opacity 0.13→0.085(深色 0.09→0.055);`?v=20261019p` 同步改。
|
||
- 剩余素材(wave/cheer/think/doze)保留但不上页面,换姿态只改 `--tb-deco` 的 url。
|
||
|
||
### shiroko 按二次元官网重做:整页场景 + 悬浮磨砂 UI(v=20261019q)
|
||
|
||
用户反馈「还是很怪,去网上学一下布局」。实际去看站:
|
||
- 蔚蓝档案 JP 首页(bluearchive.jp,截图存 outputs/ref/bluearchive_jp_.png):**整屏一张场景插画,
|
||
logo/按钮/横幅浮在画面上**,没有"白卡片"这层东西;
|
||
- marshmallow-qa / peing 这类真正的提问箱服务被 Cloudflare 拦,拿不到参照。
|
||
结论:前几版都卡在"白底 + 留白摆人物"这个结构上,人物没有环境,怎么调位置都像贴纸。
|
||
|
||
改法:生成教室场景(ImageGen,1536×1024)→ 裁掉底部 160px 水印 → 混白 38% → 模糊 1.4px →
|
||
WebP 1920×1080(22KB)/ 720×1003 竖版(6KB);卡片与顶栏改 `rgba(255,255,255,.86)` +
|
||
`backdrop-filter: blur(16px)`;人物 340px 站在左侧地面、离底 36px + 椭圆接触阴影;浅色不再叠纹样。
|
||
深色仍用渐变 + pattern-dark(白天教室压在深底上会脏)。
|
||
|
||
⚠️ **`ruleBody` 必须剥 CSS 注释**:令牌前写一行注释时,`tokenValue` 按 ";" 切完再按 ":" 取键,
|
||
会拿到 `/* ... */ --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` 已推送。教训:**人像与场景的"雾度"要对齐**——场景压白多少,人像也压相当比例。
|
||
|