diff --git a/.workbuddy/memory/2026-10-20.md b/.workbuddy/memory/2026-10-20.md index 0de8f0d..4569541 100644 --- a/.workbuddy/memory/2026-10-20.md +++ b/.workbuddy/memory/2026-10-20.md @@ -164,3 +164,36 @@ iframe 内部仍是 1512,媒体查询按 1512 判定 → 结论是错的。 - 拖拽标签写 `top:-17px` 会贴到画板边缘被压线/裁切,改成放在框内左上角; 整块半透明填充会把人物罩成一层米色(看着像"框住空气"),改为纯虚线 + 角标签。 - 面板一多左栏就滚不到底:按使用频率重排(图层 → 背景 → 窄屏 → 图片池 → 配色)。 + +## 15:20 生成器:图片处理(透明度 / 压白 / 去色 / 裁边 / 去白底) + +用户问「图片的透明度或者一些简单的处理能否安排上」。已实现并推送 `c3d34b5`。 + +**非破坏模型(关键设计)** +- `orig` 存上传时的**原始字节**(`FileReader.readAsDataURL`,不重编码),`fx` 只存参数, + 每次处理都从 orig 重算 → 「压白 16% → 20%」不会叠加两次 webp 压缩。 +- 打开面板看一眼就关:参数没变就**跳过重编码**,一个像素都不动(`fxSnapshot` 比对)。 + 这条不加,用户点开看看就会把图越磨越糊。 +- 预览按 420px 宽算(滑杆跟手),完成时全分辨率重算;用序号 `fxSeq` 丢弃过期结果, + 否则快速拖动时旧渲染覆盖新的。 + +**去白底两个坑(都实测过)** +- 手内侧 / 两腿之间是**闭合**白区,四边漫水进不去 → 按连通域单独掏,面积 ≥ 24 才掏 + (更小的多是眼睛高光,掏了变透明洞)。 +- **容差默认 16,不是 22**:背景 253、白袜子 237 只差 16。容差 30 实测把袜子吃穿(alpha 0)。 + +**验证(跑出来的)** +- `?dump=fx` 13 项断言全过:封闭孔开/关、容差 16 保袜子 / 30 吃穿、收边后轮廓 1px、 + alpha 50%→128、压白 20% 时 237→241、去色后 R=G=B、裁边 200×160→80×40。 +- `?dump=fxpipe` 链路:弹窗开关、预览 300×960、参数重开后仍在、改参后 dataURL 变、 + **无改动时 dataURL 一字节不变**(这条是防回归的关键)。 +- `?dump=fxkey` 端到端:把 shiroko 立绘合成到 253 白底再去白底,透明占比 46.3%, + 与手写 floodfill 抠出来的 46.6% 差 0.3%。 +- 生成的 CSS 仍过 `TestThemeCSSComplete` + `TestDecoSlotCount`。 + +**顺带** +- 工程文件升 v3:存 `orig` + `fx`,未处理时不重复存 dataURL(否则 json 平白翻倍); + 读 v2 旧工程把 dataURL 当 orig 用。 +- 新增 `?loadurl=` 把项目里的图直接当素材载入(不必先下载再上传),`?fxjson=` 给初始参数。 +- 截图看出两个 UI 问题:拖拽标签 `top:-17px` 贴到画板边缘被压线、半透明填充把人物罩成米色 + → 改框内左上角标签 + 纯虚线(上一轮已修,本轮沿用)。 diff --git a/.workbuddy/memory/MEMORY.md b/.workbuddy/memory/MEMORY.md index 03634d0..6607b86 100644 --- a/.workbuddy/memory/MEMORY.md +++ b/.workbuddy/memory/MEMORY.md @@ -10,7 +10,7 @@ - **无 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 当桩,**要检查 s3.go 本身得 tar 复制项目 + `go mod edit -replace`**(overlay 换不了 GOMODCACHE 文件)。 + overlay 换不了 GOMODCACHE 里的文件,**要检查 s3.go 本身得 tar 复制项目 + `go mod edit -replace`**。 - **「编译卡死」= `proxy.golang.org` 不可达**。每条 go 命令前: `export GOPROXY=https://goproxy.cn,direct GOSUMDB=off GOFLAGS=-mod=mod`(`go env -w` 在本沙箱失败)。 1C1G 是正式生产环境,**别用「沙箱慢」解释超时**。 @@ -28,11 +28,11 @@ `users.go`、`i18n/dict_*.go`、`route/question/*.go`、`question-list.html` 是 CRLF)。改前用 Python 数 `b'\r\n'`; `gofmt -l` 会把 CRLF 误报为未格式化。格式化走 `dev/gofmt_keep_crlf.py`。 - 往 gofmt 对齐组中间插键会污染 diff,新键插组末;注释别用 `''`(gofmt 会改成 `”`)。 -- ⚠️ **`templates/embed.go` 是显式清单**:新增**顶层**模板必须加进 `//go:embed` 那行,否则运行时 +- ⚠️ **`templates/embed.go` 是显式清单**:新增**顶层**模板必须加进 `//go:embed`,否则运行时 `html/template: "xxx" is undefined` → 500,编译期无提示(`checktpl` 也只查语法)。子目录已被目录项覆盖。 - `templates/user/moderation-strictness.html` 只用 shell/python 改(Edit/Write 会注入 `data-page-node-id`)。 -## 审核 +## 审核 / 提示 / 处置 - 线上通道 = 七牛(`ai.qiniuapi.com/v3/text/censor`,antispam);`[security] text_censor_source = qiniu|aliyun|auto`。 七牛只硬拦涉政/暴恐/色情,`review` 放行 → 裁决在 `censor/policy.go` + `moderation.Check`。 @@ -41,34 +41,26 @@ - 严格度三档 lax/standard/strict,存 `users.moderation_strictness`。**受处置用户强制 strict**: `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,下一请求才读 → + 提示静默丢失(「点了没反应」)。`Set*Flash + Redirect` 是正常模式。 扫描法:找 flash 调用 → 向下第一个 `Success(`/`Redirect(` → 前者即缺陷。 - -## 处置与举报 - -- L1 停止分发(禁分享卡 + 首页/探索/标签/随机位摘除)/L2 收窄公开(新回答强制私密、禁止 set-public)/ +- 处置四级:L1 停止分发(禁分享卡 + 首页/探索/标签/随机位摘除)/L2 收窄公开(新回答强制私密、禁 set-public)/ L3 冻结发布(仍可收信)/L4 整页停用(`/restricted`)。到期靠 `expires_at` 自动失效,**无 cron**; `restrict` 是**替换语义**;级别比较用 `RestrictionAtLeast`。 -- ⚠️ L2 强制私密必须在**审核通过之后**,且与写回答**同一条 UPDATE**(`AnswerByIDPrivate`): - 拆成「先 SetPrivate 再 Answer」会留下「公开+已作答」窗口,第二条失败还会变成「私密+无回答」。 -- ⚠️ **`ListAPI`(`/api/v1/user/{domain}/questions`)不经过 Pager 中间件**,L4 与页面密码必须自己校验 - (否则「整页停用」和访问密码都能被 API 绕过)。 +- ⚠️ L2 强制私密必须在**审核通过之后**,且与写回答**同一条 UPDATE**(`AnswerByIDPrivate`):拆开会留下 + 「公开+已作答」窗口,第二条失败还会变成「私密+无回答」。 +- ⚠️ **`ListAPI`(`/api/v1/user/{domain}/questions`)不经过 Pager 中间件**,L4 与页面密码必须自己校验。 - ⚠️ **整箱分享卡 `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`), - **只含级别名/到期/规则链接,不含 `--reason`**;`--no-notify` 可跳过(L1 静默止血用)。 - `--days > 90` 必须加 `--force`,交互模式还要再输一次级别名。`unrestrict` 也发解除通知,双方都有 `--no-notify`。 - ⚠️ **到期自动解除不发信**(读表比较时间,无调度器)。 +- `/rules` 是处置依据页(四语言);注册页必须勾 `agree_rules`(服务端同样校验),改 e2e 脚本要同步。 +- `restrict`/`unrestrict` 默认给箱主发通知(按 `users.language`,模板 `mail/restriction-notice.html`), + **只含级别名/到期/规则链接,不含 `--reason`**;`--no-notify` 跳过。`--days > 90` 必须加 `--force`。 + ⚠️ **到期自动解除不发信**(读表比较时间)。 ## GORM @@ -80,22 +72,23 @@ ## 前端样式 -- 基础样式 `static/css/base.css`(`/static/css/base.css?v=YYYYMMDDx`,**改完必须 +1**,静态缓存 1 天)。 - 设计令牌在文件开头 `:root`(亮)+ `html[data-theme="dark"]`(暗)。新样式一律用 `var(--tb-*)`,别写死色值,也别为深色另写分支。 -- 页面级样式仍内联在各自模板 `