diff --git a/.workbuddy/memory/2026-10-19.md b/.workbuddy/memory/2026-10-19.md index 6b2ba6e..b3f2001 100644 --- a/.workbuddy/memory/2026-10-19.md +++ b/.workbuddy/memory/2026-10-19.md @@ -298,3 +298,90 @@ home-light / home-dark / explore-light 三页差异均 **0.024%**,bbox 只有 **教训(重要)**:像素测量脚本的扫描范围必须覆盖整幅图,循环上界不能与被测对象边界重合, 否则会把"边界截断"读成"图形特征"。之前 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"]`(暗)。 +- `` 在 `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 在 `` 输出 `data-theme-style` 并条件加载 `themes/.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 里的选择器,深色预览没生效; + 改为只看 `` 开标签。 +4. 脚本改 form/user.go 时把插入块写在结构体中间,拼出 `} Language ...`;靠 Read 复查发现并重写。 diff --git a/.workbuddy/memory/MEMORY.md b/.workbuddy/memory/MEMORY.md index 4a27c8e..c28f19b 100644 --- a/.workbuddy/memory/MEMORY.md +++ b/.workbuddy/memory/MEMORY.md @@ -88,6 +88,13 @@ - 静态文件是**显式 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` 这类复合选择器,否则静默失效(不报错)。 - 视觉自检:`TEST_PREVIEW=1 go test ./templates -run TestGenPreview` → `outputs/preview-*.html`; @@ -99,6 +106,21 @@ (置顶/私密/被举报/已读、投票未开始/进行中/已结束/已参与)。题型配色曾由 Go 的 `fieldTypeLabelClass` 生成(已删),故 grep `uk-label` 搜不到,改前先搜模板函数名。 +## 主题系统 + +- 一套主题 = `static/css/themes/.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` 控尺寸),图案叠在图片之上。 + ## 其它约定 - 校验靠 `valid:"required;maxlen:N"` 标签(`internal/form/*.go`),别在 handler 手写。 diff --git a/conf/app.sample.ini b/conf/app.sample.ini index e5863e9..ba8ad17 100644 --- a/conf/app.sample.ini +++ b/conf/app.sample.ini @@ -181,3 +181,13 @@ chat_message_len = 2000 chat_max_messages = 200 ; 单用户每日私信条数上限(用户可设范围 0..该值;0 表示不限制每日条数) daily_message_limit_max = 999 + +[theme] +; 主题系统:只改配色 / 图案 / 整页背景,不涉及布局;仅提问箱相关页面生效。 +; default :站点默认主题(箱主未选择时使用)。填 default 或留空 = 现有外观。 +; available :启用哪些主题,逗号分隔,同时是箱主设置页的白名单; +; 列了但 static/css/themes/.css 不存在的会被忽略并告警。 +; 新增主题:把 static/css/themes/_scaffold.css 复制成 .css, +; 改选择器与令牌值,再把 加进 available —— 不需要改模板。 +default = default +available = default,sakura diff --git a/internal/conf/conf.go b/internal/conf/conf.go index 5670ccc..35e17d9 100644 --- a/internal/conf/conf.go +++ b/internal/conf/conf.go @@ -89,6 +89,12 @@ func Init() error { // ini 键留空会被 int 解析为 0;<1 的值一律回退内置默认,防止上限被静默置零。 sanitizeLimits(&Limits) + // 主题:默认 slug 与启用清单。段缺失时 MapTo 得到空值,运行期回退内置 default, + // 因此老配置文件不写 [theme] 也能正常启动。 + if err := File.Section("theme").MapTo(&Theme); err != nil { + return errors.Wrap(err, "map 'theme'") + } + if err := File.Section("mail").MapTo(&Mail); err != nil { return errors.Wrap(err, "map 'mail'") } diff --git a/internal/conf/static.go b/internal/conf/static.go index 4845383..7e9ad88 100644 --- a/internal/conf/static.go +++ b/internal/conf/static.go @@ -8,6 +8,9 @@ package conf var ( BuildTime string BuildCommit = "dev" + + // StaticVersion 静态资源缓存版本号:base.css 与主题 css 共用,改样式只改这一处。 + StaticVersion = "20261019e" ) // 文本审核通道取值([security] text_censor_source)。定义在 conf 侧, @@ -35,6 +38,15 @@ var ( FontPath string `ini:"font_path"` } + // Theme 主题系统([theme] 段)。 + // default :站点默认主题 slug,留空/非法回退内置 default(= 现在的外观)。 + // available :启用哪些主题(逗号分隔),同时是箱主设置页的白名单; + // 列了但缺少 css 文件的会被忽略并告警(见 internal/theme)。 + Theme struct { + Default string `ini:"default"` + Available string `ini:"available"` + } + Security struct { EnableTextCensor bool `ini:"enable_text_censor"` IPBlacklist string `ini:"ip_blacklist"` diff --git a/internal/context/context.go b/internal/context/context.go index 8330987..4b65648 100644 --- a/internal/context/context.go +++ b/internal/context/context.go @@ -284,6 +284,8 @@ func Contexter() flamego.Handler { c.Data["CurrentURI"] = ctx.Request().Request.RequestURI c.Data["ExternalURL"] = conf.App.ExternalURL + // 静态资源版本号:base.css 与主题 css 共用同一个值,改样式只需改 conf.StaticVersion。 + c.Data["StaticVersion"] = conf.StaticVersion // 默认头像/背景图兜底:未设置自定义头像的箱主,其二维码 logo 应与页面头像一致回退到 default_avatar。 // 模板按原始 URL 直连(easy.qrcode 以 crossOrigin:'anonymous' 加载,配合对象存储桶 CORS diff --git a/internal/db/users.go b/internal/db/users.go index 3c6bb16..2edbc33 100644 --- a/internal/db/users.go +++ b/internal/db/users.go @@ -40,6 +40,7 @@ type UsersStore interface { // UpdateModerationStrictness 更新内容审核严格度(lax / standard / strict,空值表示跟随站点默认)。 // 只写 moderation_strictness 列,不影响防骚扰与屏蔽词。 UpdateModerationStrictness(ctx context.Context, id uint, strictness string) error + UpdateTheme(ctx context.Context, id uint, theme string) error Authenticate(ctx context.Context, email, password string) (*User, error) ChangePassword(ctx context.Context, id uint, oldPassword, newPassword string) error UpdatePassword(ctx context.Context, id uint, newPassword string) error @@ -85,30 +86,33 @@ type users struct { type User struct { gorm.Model `json:"-"` - UID string `json:"-"` - Name string `json:"name"` - Password string `json:"-"` - Email string `json:"-" gorm:"uniqueIndex:idx_users_email_unique"` - Avatar string `json:"avatar"` - Domain string `json:"domain" gorm:"uniqueIndex:idx_users_domain_unique"` - Background string `json:"background"` - Intro string `json:"intro"` - Introcolor string `json:"introcolor"` - Usernamecolor string `json:"usernamecolor"` - Qrcodebackcolor string `json:"qrcodebackcolor"` - Qrcodecolor string `json:"qrcodecolor"` - BackgroundImage string `json:"backgroundimage"` - Dotscale string `json:"dotscale"` - Acfunlink string `json:"acfunlink"` - Bililink string `json:"bililink"` - Weibolink string `json:"weibolink"` - Bloglink string `json:"bloglink"` - Xiaohongshulink string `json:"xiaohongshulink"` - Qrcodepdpcolor string `json:"qrcodepdpcolor"` - Backgroundimagealpha string `json:"Backgroundimagealpha"` - Notify NotifyType `json:"notify"` - HarassmentSetting HarassmentSettingType `json:"harassment_setting" gorm:"default:'none'"` - BlockWords string `json:"-"` + UID string `json:"-"` + Name string `json:"name"` + Password string `json:"-"` + Email string `json:"-" gorm:"uniqueIndex:idx_users_email_unique"` + Avatar string `json:"avatar"` + Domain string `json:"domain" gorm:"uniqueIndex:idx_users_domain_unique"` + Background string `json:"background"` + Intro string `json:"intro"` + Introcolor string `json:"introcolor"` + Usernamecolor string `json:"usernamecolor"` + Qrcodebackcolor string `json:"qrcodebackcolor"` + Qrcodecolor string `json:"qrcodecolor"` + BackgroundImage string `json:"backgroundimage"` + Dotscale string `json:"dotscale"` + Acfunlink string `json:"acfunlink"` + Bililink string `json:"bililink"` + Weibolink string `json:"weibolink"` + Bloglink string `json:"bloglink"` + Xiaohongshulink string `json:"xiaohongshulink"` + Qrcodepdpcolor string `json:"qrcodepdpcolor"` + Backgroundimagealpha string `json:"Backgroundimagealpha"` + // Theme 箱主选择的主题 slug(空串=跟随站点默认,default=基础外观)。 + // 取值合法性由 route/user 在写入前校验,读取侧由 internal/theme 回退。 + Theme string `json:"theme" gorm:"column:theme;type:varchar(32);default:''"` + Notify NotifyType `json:"notify"` + HarassmentSetting HarassmentSettingType `json:"harassment_setting" gorm:"default:'none'"` + BlockWords string `json:"-"` // 防骚扰设置取值 none / register_only(见下方常量)。历史账号此列为空串(建表时无默认值), // 与 none 语义等价但取值不同,会导致「按 == none 判断」的隐性漏判 → 已补 schema 默认值、 // Create 显式写入、启动期归一历史空值(见 db.Init 的 normalizeHarassmentSetting)。 @@ -445,6 +449,16 @@ func (db *users) UpdateModerationStrictness(ctx context.Context, id uint, strict return nil } +// UpdateTheme 仅更新主题 slug,不动其它个性化字段(背景/颜色各自独立保存)。 +func (db *users) UpdateTheme(ctx context.Context, id uint, theme string) error { + if err := db.WithContext(ctx).Model(&User{}).Where("id = ?", id).Updates(map[string]interface{}{ + "Theme": theme, + }).Error; err != nil { + return errors.Wrap(err, "update user") + } + return nil +} + func (db *users) Authenticate(ctx context.Context, email, password string) (*User, error) { u, err := db.GetByEmail(ctx, email) if err != nil { diff --git a/internal/form/user.go b/internal/form/user.go index 0b78924..0960566 100644 --- a/internal/form/user.go +++ b/internal/form/user.go @@ -26,6 +26,13 @@ type UpdateProfile struct { Language string `form:"language"` } +// UpdateProfileTheme 主题外观。空串表示跟随站点默认主题; +// 取值在白名单之外时保存被拒绝(见 route/user.UpdateTheme), +// 这样箱主不会「选了个不存在的主题,然后看起来什么都没变」。 +type UpdateProfileTheme struct { + Theme string `valid:"maxlen:32" label:"主题" msg:"profile.theme.invalid"` +} + // UpdateHarassment 仅「允许注册用户向我提问」开关(防骚扰类型)。 // 全局屏蔽词为独立入口 UpdateBlockWords:二者分处不同设置面板保存, // 若仍共用一个结构体会互相覆盖对方字段(见 route/user/harassment.go 注释)。 diff --git a/internal/i18n/dict_en.go b/internal/i18n/dict_en.go index 0d8d18b..12b5238 100644 --- a/internal/i18n/dict_en.go +++ b/internal/i18n/dict_en.go @@ -930,4 +930,14 @@ var enDict = map[string]string{ "form.register.recaptcha": "Unable to load the CAPTCHA. Please refresh the page or switch your network and try again", "form.register.agreeRules": "Please read and accept the community rules first", "form.register.repeatPassword": "The two passwords do not match", + + // 主题:设置页选择器与主题显示名(theme.name. 由 internal/theme 约定) + "profile.theme.title": "Theme", + "profile.theme.hint": "Applies to your question box pages only; the rest of the site keeps the default look.", + "profile.theme.followDefault": "Follow site default", + "profile.theme.save": "Save theme", + "profile.theme.saved": "Theme saved", + "profile.theme.invalid": "That theme is unavailable — pick one from the list", + "theme.name.default": "Default look", + "theme.name.sakura": "Sakura", } diff --git a/internal/i18n/dict_ja.go b/internal/i18n/dict_ja.go index 735895a..1cc7c02 100644 --- a/internal/i18n/dict_ja.go +++ b/internal/i18n/dict_ja.go @@ -930,4 +930,14 @@ var jaDict = map[string]string{ "form.register.recaptcha": "CAPTCHAを読み込めません。ページを再読み込みするか、ネットワークを変更してお試しください", "form.register.agreeRules": "コミュニティルールをお読みのうえ同意してください", "form.register.repeatPassword": "入力した2つのパスワードが一致しません", + + // 主题:设置页选择器与主题显示名(theme.name. 由 internal/theme 约定) + "profile.theme.title": "テーマ", + "profile.theme.hint": "質問箱のページにのみ適用されます。他のページは既定の外観のままです。", + "profile.theme.followDefault": "サイト既定に従う", + "profile.theme.save": "テーマを保存", + "profile.theme.saved": "テーマを保存しました", + "profile.theme.invalid": "このテーマは利用できません。一覧から選択してください", + "theme.name.default": "標準の外観", + "theme.name.sakura": "さくら", } diff --git a/internal/i18n/dict_zh_cn.go b/internal/i18n/dict_zh_cn.go index e7d0159..26b7fa6 100644 --- a/internal/i18n/dict_zh_cn.go +++ b/internal/i18n/dict_zh_cn.go @@ -872,6 +872,16 @@ var zhCNDict = map[string]string{ "profile.saveSocial": "保存社交链接", "profile.err.cropLibUnavailable": "图片裁剪组件加载失败,请刷新页面后重试", + // 主题:设置页选择器与主题显示名(theme.name. 由 internal/theme 约定) + "profile.theme.title": "主题外观", + "profile.theme.hint": "仅对提问箱页面生效,站内其它页面保持默认外观。", + "profile.theme.followDefault": "跟随站点默认", + "profile.theme.save": "保存主题", + "profile.theme.saved": "主题已保存", + "profile.theme.invalid": "该主题不可用,请从列表中选择", + "theme.name.default": "基础外观", + "theme.name.sakura": "樱花", + // 验证码 JSON 接口:前端消费(P2) "captcha.json.badRequest": "请求格式错误", "captcha.json.idRequired": "验证码 ID 不能为空", diff --git a/internal/i18n/dict_zh_tw.go b/internal/i18n/dict_zh_tw.go index 755e6d0..9f4a069 100644 --- a/internal/i18n/dict_zh_tw.go +++ b/internal/i18n/dict_zh_tw.go @@ -930,4 +930,14 @@ var zhTWDict = map[string]string{ "form.register.recaptcha": "無法人機驗證,請重新整理頁面或更換網路後重試", "form.register.agreeRules": "請先閱讀並同意社群規則", "form.register.repeatPassword": "兩次輸入的密碼不一致", + + // 主题:设置页选择器与主题显示名(theme.name. 由 internal/theme 约定) + "profile.theme.title": "主題外觀", + "profile.theme.hint": "僅對提問箱頁面生效,站內其他頁面維持預設外觀。", + "profile.theme.followDefault": "跟隨站點預設", + "profile.theme.save": "儲存主題", + "profile.theme.saved": "主題已儲存", + "profile.theme.invalid": "該主題無法使用,請從清單中選擇", + "theme.name.default": "基礎外觀", + "theme.name.sakura": "櫻花", } diff --git a/internal/route/route.go b/internal/route/route.go index 9a83d3f..d2d430f 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -346,6 +346,7 @@ func New() *flamego.Flame { f.Post("/update", form.Bind(form.UpdateProfile{}), rateLimitMiddleware(10, 1*time.Minute), user.UpdateProfile) f.Post("/export", user.ExportProfile) f.Post("/export-json", user.ExportProfileJSON) + f.Post("/theme", form.Bind(form.UpdateProfileTheme{}), rateLimitMiddleware(10, 1*time.Minute), user.UpdateTheme) f.Combo("/deactivate").Get(user.DeactivateProfile).Post(user.DeactivateProfileAction) f.Post("/update-page-password", form.Bind(form.UpdatePagePassword{}), rateLimitMiddleware(10, 1*time.Minute), user.UpdatePagePassword) diff --git a/internal/theme/theme.go b/internal/theme/theme.go new file mode 100644 index 0000000..2bee689 --- /dev/null +++ b/internal/theme/theme.go @@ -0,0 +1,176 @@ +// Package theme 维护箱主主题的注册表与取值回退。 +// +// 一个主题就是一个 CSS 文件(static/css/themes/.css),只覆盖设计令牌: +// 配色、页面背景图/图案,不涉及布局。新增主题不需要改任何模板或 Go 代码, +// 只要放文件并在 [theme] available 里登记。 +package theme + +import ( + "regexp" + "strings" + "sync" + + "github.com/sirupsen/logrus" + + "github.com/tamakyi/TamaBox/internal/conf" + "github.com/tamakyi/TamaBox/static" +) + +const ( + // DefaultSlug 内置默认主题:不加载任何 css,等价于站点原本的外观。 + DefaultSlug = "default" + + themeCSSDir = "css/themes/" +) + +var ( + slugRe = regexp.MustCompile(`^[a-z0-9][a-z0-9-]{0,31}$`) + // 仅接受纯色值,避免把主题文件里的任意内容拼进设置页的 style 属性。 + colorRe = regexp.MustCompile(`^(#[0-9a-fA-F]{3,8}|rgba?\([0-9.,\s%/]+\))$`) +) + +// Theme 是设置页需要的一项。名称由 i18n 键 theme.name. 提供, +// Swatch 是可直接放进 style 的预览背景(取不到颜色时为空)。 +type Theme struct { + Slug string + Swatch string +} + +var ( + once sync.Once + themes []Theme + bySlug map[string]Theme + siteDef string // 站点默认主题;空串表示「基础外观,不加载主题 css」 +) + +func load() { + once.Do(func() { + themes, siteDef, bySlug = build(conf.Theme.Default, conf.Theme.Available) + }) +} + +// build 是注册表的纯函数形态,便于单测直接喂配置。 +func build(defaultSlug, available string) ([]Theme, string, map[string]Theme) { + bySlug := make(map[string]Theme) + var list []Theme + + add := func(slug string) { + if _, ok := bySlug[slug]; ok { + return + } + css, err := static.FS.ReadFile(themeCSSDir + slug + ".css") + if err != nil { + logrus.WithField("theme", slug).Warn("主题文件不存在,已从可用列表忽略") + return + } + t := Theme{Slug: slug, Swatch: swatchOf(string(css), slug)} + bySlug[slug] = t + list = append(list, t) + } + + for _, raw := range strings.Split(available, ",") { + slug := normalize(raw) + if slug == "" { + continue + } + add(slug) + } + + def := normalize(defaultSlug) + if def == DefaultSlug || def == "" { + def = "" + } else if _, ok := bySlug[def]; !ok { + logrus.WithField("theme", def).Warn("站点默认主题不可用,回退基础外观") + def = "" + } + return list, def, bySlug +} + +func normalize(slug string) string { + slug = strings.ToLower(strings.TrimSpace(slug)) + if !slugRe.MatchString(slug) { + if slug != "" { + logrus.WithField("theme", slug).Warn("主题 slug 非法(只允许小写字母/数字/连字符),已忽略") + } + return "" + } + return slug +} + +// swatchOf 从主题 css 的浅色块里取底色与主色,拼成设置页的小预览。 +// 取不到(或颜色格式可疑)就返回空串,模板退化为纯色占位。 +func swatchOf(css, slug string) string { + block := ruleBody(css, `html[data-theme-style="`+slug+`"]`) + if block == "" { + return "" + } + bg := tokenValue(block, "--tb-bg") + primary := tokenValue(block, "--tb-primary") + if !colorRe.MatchString(bg) || !colorRe.MatchString(primary) { + return "" + } + return "linear-gradient(135deg, " + bg + " 0 45%, " + primary + " 45% 100%)" +} + +// ruleBody 返回 selector 对应的第一个声明块内容(主题令牌块内不含嵌套规则)。 +func ruleBody(css, selector string) string { + i := strings.Index(css, selector) + if i < 0 { + return "" + } + open := strings.IndexByte(css[i:], '{') + if open < 0 { + return "" + } + start := i + open + 1 + end := strings.IndexByte(css[start:], '}') + if end < 0 { + return "" + } + return css[start : start+end] +} + +func tokenValue(block, name string) string { + for _, line := range strings.Split(block, ";") { + kv := strings.SplitN(line, ":", 2) + if len(kv) != 2 || strings.TrimSpace(kv[0]) != name { + continue + } + return strings.TrimSpace(kv[1]) + } + return "" +} + +// List 返回设置页可选项(顺序与 [theme] available 一致)。 +func List() []Theme { + load() + return themes +} + +// IsAvailable 判断 slug 是否在白名单内且文件存在。 +func IsAvailable(slug string) bool { + load() + _, ok := bySlug[strings.ToLower(strings.TrimSpace(slug))] + return ok +} + +// Resolve 返回该页面要加载的主题 slug;空串表示不加载主题 css(基础外观)。 +// +// 回退顺序:箱主选择 > 站点默认 > 基础外观。 +// 注意 "" 与 "default" 语义不同:"" 是「没选,跟随站点默认」, +// "default" 是箱主明确选择基础外观(即使站点默认是别的主题也不覆盖)。 +func Resolve(userTheme string) string { + load() + return resolve(userTheme, siteDef, bySlug) +} + +func resolve(userTheme, siteDef string, available map[string]Theme) string { + slug := strings.ToLower(strings.TrimSpace(userTheme)) + if slug == DefaultSlug { + return "" + } + if _, ok := available[slug]; ok { + return slug + } + return siteDef +} diff --git a/internal/theme/theme_test.go b/internal/theme/theme_test.go new file mode 100644 index 0000000..e81aa9e --- /dev/null +++ b/internal/theme/theme_test.go @@ -0,0 +1,104 @@ +package theme + +import ( + "io/fs" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/tamakyi/TamaBox/static" +) + +// 主题必须自带的一套颜色令牌(形状/字体/阴影允许继承基础层)。 +var requiredTokens = []string{ + "--tb-bg", "--tb-surface", "--tb-surface-2", "--tb-surface-hover", "--tb-border", + "--tb-text", "--tb-text-secondary", "--tb-text-muted", + "--tb-primary", "--tb-primary-weak", +} + +func TestBuildAvailable(t *testing.T) { + list, def, _ := build("sakura", "default, sakura ,bad_Slug,missing") + + assert.Equal(t, "sakura", def, "站点默认应取配置值") + var slugs []string + for _, item := range list { + slugs = append(slugs, item.Slug) + } + // bad_Slug 非法、missing 没有 css 文件,都应被忽略 + assert.Equal(t, []string{"default", "sakura"}, slugs) +} + +func TestBuildInvalidDefault(t *testing.T) { + _, def, _ := build("does-not-exist", "sakura") + assert.Equal(t, "", def, "默认主题不可用时回退基础外观") + + _, def, _ = build("default", "default,sakura") + assert.Equal(t, "", def, "default 语义即基础外观,不应产生 css 请求") +} + +func TestResolve(t *testing.T) { + // 站点默认是 sakura + themes, siteDef, index := build("sakura", "default,sakura") + require.Equal(t, "sakura", siteDef) + require.Contains(t, index, "sakura") + require.NotEmpty(t, themes) + + assert.Equal(t, "sakura", resolve("", siteDef, index), "未选择时跟随站点默认") + assert.Equal(t, "sakura", resolve("SAKURA", siteDef, index), "大小写不敏感") + assert.Equal(t, "", resolve("default", siteDef, index), "显式选 default = 基础外观,不回退站点默认") + assert.Equal(t, "sakura", resolve("nope", siteDef, index), "非法值回退站点默认") + + // 站点默认是基础外观 + _, def2, index2 := build("default", "default,sakura") + assert.Equal(t, "", resolve("", def2, index2)) + assert.Equal(t, "sakura", resolve("sakura", def2, index2)) +} + +// TestThemeCSSComplete 防止「复制底稿只写了浅色块」这类问题: +// 半套主题不会报错,只会让深色模式变成浅色配色。 +func TestThemeCSSComplete(t *testing.T) { + entries, err := fs.ReadDir(static.FS, strings.TrimSuffix(themeCSSDir, "/")) + require.NoError(t, err) + require.NotEmpty(t, entries) + + for _, e := range entries { + name := e.Name() + if !strings.HasSuffix(name, ".css") { + continue + } + slug := strings.TrimSuffix(name, ".css") + // _scaffold.css 是给人复制的模板,default.css 永不加载(仅提供色块预览) + if strings.HasPrefix(slug, "_") || slug == DefaultSlug { + continue + } + + raw, err := static.FS.ReadFile(themeCSSDir + name) + require.NoError(t, err) + css := string(raw) + + light := ruleBody(css, `html[data-theme-style="`+slug+`"]`) + require.NotEmpty(t, light, "%s:缺少浅色块 html[data-theme-style=\"%s\"]", name, slug) + + dark := ruleBody(css, `html[data-theme="dark"][data-theme-style="`+slug+`"]`) + require.NotEmpty(t, dark, "%s:缺少深色块 html[data-theme=\"dark\"][data-theme-style=\"%s\"]", name, slug) + + for _, tok := range requiredTokens { + assert.NotEmpty(t, tokenValue(light, tok), "%s 浅色块缺少 %s", name, tok) + assert.NotEmpty(t, tokenValue(dark, tok), "%s 深色块缺少 %s", name, tok) + } + } +} + +func TestSwatch(t *testing.T) { + raw, err := static.FS.ReadFile(themeCSSDir + "sakura.css") + require.NoError(t, err) + sw := swatchOf(string(raw), "sakura") + assert.True(t, strings.HasPrefix(sw, "linear-gradient("), "swatch 应为渐变串,实际 %q", sw) + assert.Contains(t, sw, "#e5678f") + + assert.Equal(t, "", swatchOf("/* 空文件 */", "sakura"), "取不到颜色时返回空串") + assert.Equal(t, "", swatchOf(`html[data-theme-style="x"] { --tb-bg: url(evil); --tb-primary: #fff; }`, "x"), + "非色值内容不得进入 swatch") +} diff --git a/outputs/deploy-res5.png b/outputs/deploy-res5.png new file mode 100644 index 0000000..89c3cf2 Binary files /dev/null and b/outputs/deploy-res5.png differ diff --git a/outputs/deploy-vote.png b/outputs/deploy-vote.png new file mode 100644 index 0000000..ee24259 Binary files /dev/null and b/outputs/deploy-vote.png differ diff --git a/outputs/live-mobile.png b/outputs/live-mobile.png new file mode 100644 index 0000000..5e3c1b2 Binary files /dev/null and b/outputs/live-mobile.png differ diff --git a/outputs/m-bottom-strip.png b/outputs/m-bottom-strip.png new file mode 100644 index 0000000..b5f99df Binary files /dev/null and b/outputs/m-bottom-strip.png differ diff --git a/outputs/m-box.png b/outputs/m-box.png new file mode 100644 index 0000000..a3ca9ed Binary files /dev/null and b/outputs/m-box.png differ diff --git a/outputs/m-explore.png b/outputs/m-explore.png new file mode 100644 index 0000000..6c14706 Binary files /dev/null and b/outputs/m-explore.png differ diff --git a/outputs/m-profile.png b/outputs/m-profile.png new file mode 100644 index 0000000..7ddf061 Binary files /dev/null and b/outputs/m-profile.png differ diff --git a/outputs/m-rules.png b/outputs/m-rules.png new file mode 100644 index 0000000..3b65d58 Binary files /dev/null and b/outputs/m-rules.png differ diff --git a/outputs/preview-box-dark.html b/outputs/preview-box-dark.html index 69472fa..540ff67 100644 --- a/outputs/preview-box-dark.html +++ b/outputs/preview-box-dark.html @@ -3,7 +3,7 @@ - 预览 12:01 · + 预览 15:15 · @@ -20,675 +20,8 @@ - @@ -1303,7 +636,7 @@ document.getElementById("introcolor").style.color = "#ffffff"; href="/_/tester/1">查看回答
⭐ - 2026-10-19 12:01:52 + 2026-10-19 15:15:39

置顶的问题

@@ -1315,7 +648,7 @@ document.getElementById("introcolor").style.color = "#ffffff"; href="/_/tester/2">查看回答
- 2026-10-19 11:01:52 + 2026-10-19 14:15:39

普通问题

diff --git a/outputs/preview-box-light.html b/outputs/preview-box-light.html index 1b37614..53803f8 100644 --- a/outputs/preview-box-light.html +++ b/outputs/preview-box-light.html @@ -3,7 +3,7 @@ - 预览 12:01 · + 预览 15:15 · @@ -20,675 +20,8 @@ - @@ -1303,7 +636,7 @@ document.getElementById("introcolor").style.color = "#ffffff"; href="/_/tester/1">查看回答
⭐ - 2026-10-19 12:01:52 + 2026-10-19 15:15:39

置顶的问题

@@ -1315,7 +648,7 @@ document.getElementById("introcolor").style.color = "#ffffff"; href="/_/tester/2">查看回答
- 2026-10-19 11:01:52 + 2026-10-19 14:15:39

普通问题

diff --git a/outputs/preview-explore-dark.html b/outputs/preview-explore-dark.html index 5382f8d..35634ce 100644 --- a/outputs/preview-explore-dark.html +++ b/outputs/preview-explore-dark.html @@ -3,7 +3,7 @@ - 预览 12:01 · 投票 + 预览 15:15 · 投票 @@ -20,675 +20,8 @@ - diff --git a/outputs/preview-explore-light.html b/outputs/preview-explore-light.html index 067092d..1fd289a 100644 --- a/outputs/preview-explore-light.html +++ b/outputs/preview-explore-light.html @@ -3,7 +3,7 @@ - 预览 12:01 · 投票 + 预览 15:15 · 投票 @@ -20,675 +20,8 @@ - diff --git a/outputs/preview-home-dark.html b/outputs/preview-home-dark.html index 8087683..869a8d3 100644 --- a/outputs/preview-home-dark.html +++ b/outputs/preview-home-dark.html @@ -3,7 +3,7 @@ - 预览 12:01 · TamaBox + 预览 15:15 · TamaBox @@ -20,675 +20,8 @@ - diff --git a/outputs/preview-home-light.html b/outputs/preview-home-light.html index c0ec5db..77bf57d 100644 --- a/outputs/preview-home-light.html +++ b/outputs/preview-home-light.html @@ -3,7 +3,7 @@ - 预览 12:01 · TamaBox + 预览 15:15 · TamaBox @@ -20,675 +20,8 @@ - diff --git a/outputs/preview-profile-account-light.html b/outputs/preview-profile-account-light.html new file mode 100644 index 0000000..40e6b01 --- /dev/null +++ b/outputs/preview-profile-account-light.html @@ -0,0 +1,2022 @@ + + + + + + 预览 15:15 · 设置 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+

裁剪图片

+
+
+
+ +
+

+
+ +
+
+ + +
+ + + + + +
+ + +
+
+ + + +
+

基础资料

+
+ + +
+
+ + +
+
+ +
+ +
+
+ + +
+
+ +
+ +
+
+ + + 选择访客访问你的提问箱时显示的语言(后台页面跟随此设置) +
+
+ + +
+
+ +
+
+ + +
+

头像与背景

+
+
+ + + + 1:1 +
+ + +
+ 裁剪框已锁定为 1:1 正方形 +
+
+ + + + 16:9 +
+ + +
+ 裁剪框已锁定为 16:9 宽屏 +
+
+ +
+ +
+
+ + +
+
    +
  • + 二维码样式 +
    +
    +
    +
    + +
    + +
    +
    + +
    + +
    +
    + +
    + +
    +
    + + 仅在启用时生效,否则颜色由系统自动生成 +
    +
    + +
    + + 0.35 + +
    +
    +
    + +
    + + 0.8 + +
    +
    +
    + +
    + + +
    + 选择图片后预览将即时更新 +
    +
    +
    +
    +

    实时预览

    +
    +
    +
    + 预设主题 +
    + + + + +
    +
    +
    + +
    +

    调整仅用于预览,最终效果点击「保存修改」后生效

    +
    +
    +
    +
    +
  • +
+
+ +
+
+ + +
+
    +
  • + 社交信息设置 +
    + 勾选后填写主页地址(无需 https://),未勾选的项将不会在前台显示。 + + + + + +
    +
  • +
+
+ +
+
+ + +
+

修改密码

+
+ + +
+
+ + +
+

保存后当前会话立即失效,需使用新密码重新登录

+
+
+ +
+
+
+ + +
+

标签设置

+
+ +
+ + +
+
+ +
+
+ +
+
+
+ + +
+

账号设置

+ +
+ + +
+

主题外观

+
+ +

仅对提问箱页面生效,站内其它页面保持默认外观。

+
+ + + + + + +
+
+ +
+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + +

+
+ 社区规则 | + + 找站长 +
+ 盘 + | 公告板 + | 聊天室 + | 监控板 + | 导航页 +
Powered by NekoBox | +
提问箱已成功存活: +

+ + +
+
+ + + + + diff --git a/outputs/preview-profile-dark.html b/outputs/preview-profile-dark.html index 7801ed3..c97656c 100644 --- a/outputs/preview-profile-dark.html +++ b/outputs/preview-profile-dark.html @@ -3,7 +3,7 @@ - 预览 12:01 · 设置 + 预览 15:15 · 设置 @@ -20,675 +20,8 @@ - @@ -1728,6 +1061,40 @@ document.addEventListener("DOMContentLoaded", function() { + + +
+

主题外观

+
+ +

仅对提问箱页面生效,站内其它页面保持默认外观。

+
+ + + + + + +
+
+ +
+
+
+ + diff --git a/outputs/preview-profile-light.html b/outputs/preview-profile-light.html index e11c7b7..3fd0992 100644 --- a/outputs/preview-profile-light.html +++ b/outputs/preview-profile-light.html @@ -3,7 +3,7 @@ - 预览 12:01 · 设置 + 预览 15:15 · 设置 @@ -20,675 +20,8 @@ - @@ -1728,6 +1061,40 @@ document.addEventListener("DOMContentLoaded", function() { + + +
+

主题外观

+
+ +

仅对提问箱页面生效,站内其它页面保持默认外观。

+
+ + + + + + +
+
+ +
+
+
+ + diff --git a/outputs/preview-theme-sakura-dark.html b/outputs/preview-theme-sakura-dark.html new file mode 100644 index 0000000..1a0f586 --- /dev/null +++ b/outputs/preview-theme-sakura-dark.html @@ -0,0 +1,911 @@ + + + + + + 预览 15:15 · + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + +
+ + + + + + + + +
+
+
+ +

测试箱主

+

欢迎提问

+
+ + + + + +
+
+
+
+ +
+
+ + + + +
+ + +

扫码向@测试箱主提问
谁都可以以匿名的形式提问

+ + + + + + + + + + + + + + + + + + +
+ + + + + +
+ +
+ +
+ + + + + + + +
+ +
+
+ + + +
+ +
+
+ +
+ + + +
+ +
+
+
+ + + +
+ +
+ +
+ +
+
+
+ + + + + + +

@测试箱主 以前回答过的问题 (2)

+ +
+ +
+
+ 查看回答 +
+ ⭐ + 2026-10-19 15:15:39 +
+

置顶的问题 +

+
+ +
+
+ 查看回答 +
+ + 2026-10-19 14:15:39 +
+

普通问题 +

+
+ +
+ + +
+ +
+
+ 无更多留言 +

+
+
+ + +
+ + +
+
+ +

+
+ 社区规则 | + + 找站长 +
+ 盘 + | 公告板 + | 聊天室 + | 监控板 + | 导航页 +
Powered by NekoBox | +
提问箱已成功存活: +

+ + +
+
+ + + + diff --git a/outputs/preview-theme-sakura-light.html b/outputs/preview-theme-sakura-light.html new file mode 100644 index 0000000..7f12ba3 --- /dev/null +++ b/outputs/preview-theme-sakura-light.html @@ -0,0 +1,911 @@ + + + + + + 预览 15:15 · + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + +
+ + + + + + + + +
+
+
+ +

测试箱主

+

欢迎提问

+
+ + + + + +
+
+
+
+ +
+
+ + + + +
+ + +

扫码向@测试箱主提问
谁都可以以匿名的形式提问

+ + + + + + + + + + + + + + + + + + +
+ + + + + +
+ +
+ +
+ + + + + + + +
+ +
+
+ + + +
+ +
+
+ +
+ + + +
+ +
+
+
+ + + +
+ +
+ +
+ +
+
+
+ + + + + + +

@测试箱主 以前回答过的问题 (2)

+ +
+ +
+
+ 查看回答 +
+ ⭐ + 2026-10-19 15:15:39 +
+

置顶的问题 +

+
+ +
+
+ 查看回答 +
+ + 2026-10-19 14:15:39 +
+

普通问题 +

+
+ +
+ + +
+ +
+
+ 无更多留言 +

+
+
+ + +
+ + +
+
+ +

+
+ 社区规则 | + + 找站长 +
+ 盘 + | 公告板 + | 聊天室 + | 监控板 + | 导航页 +
Powered by NekoBox | +
提问箱已成功存活: +

+ + +
+
+ + + + diff --git a/outputs/prod-bottom.png b/outputs/prod-bottom.png new file mode 100644 index 0000000..b0fb426 Binary files /dev/null and b/outputs/prod-bottom.png differ diff --git a/outputs/prod-mobile.png b/outputs/prod-mobile.png new file mode 100644 index 0000000..64264da Binary files /dev/null and b/outputs/prod-mobile.png differ diff --git a/outputs/profile-account.png b/outputs/profile-account.png new file mode 100644 index 0000000..8d79c18 Binary files /dev/null and b/outputs/profile-account.png differ diff --git a/outputs/profile-full.png b/outputs/profile-full.png new file mode 100644 index 0000000..acd5ccb Binary files /dev/null and b/outputs/profile-full.png differ diff --git a/outputs/profile-theme-card.png b/outputs/profile-theme-card.png new file mode 100644 index 0000000..5a86e9a Binary files /dev/null and b/outputs/profile-theme-card.png differ diff --git a/outputs/shot-default-check.html b/outputs/shot-default-check.html new file mode 100644 index 0000000..6d7039b --- /dev/null +++ b/outputs/shot-default-check.html @@ -0,0 +1,1640 @@ + + + + + + 预览 15:09 · + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + +
+ + + + + + + + +
+
+
+ +

测试箱主

+

欢迎提问

+
+ + + + + +
+
+
+
+ +
+
+ + + + +
+ + +

扫码向@测试箱主提问
谁都可以以匿名的形式提问

+ + + + + + + + + + + + + + + + + + +
+ + + + + +
+ +
+ +
+ + + + + + + +
+ +
+
+ + + +
+ +
+
+ +
+ + + +
+ +
+
+
+ + + +
+ +
+ +
+ +
+
+
+ + + + + + +

@测试箱主 以前回答过的问题 (2)

+ +
+ +
+
+ 查看回答 +
+ ⭐ + 2026-10-19 15:09:46 +
+

置顶的问题 +

+
+ +
+
+ 查看回答 +
+ + 2026-10-19 14:09:46 +
+

普通问题 +

+
+ +
+ + +
+ +
+
+ 无更多留言 +

+
+
+ + +
+ + +
+
+ +

+
+ 社区规则 | + + 找站长 +
+ 盘 + | 公告板 + | 聊天室 + | 监控板 + | 导航页 +
Powered by NekoBox | +
提问箱已成功存活: +

+ + +
+
+ + +
+ diff --git a/outputs/shot-preview-theme-sakura-dark.html b/outputs/shot-preview-theme-sakura-dark.html new file mode 100644 index 0000000..dca600c --- /dev/null +++ b/outputs/shot-preview-theme-sakura-dark.html @@ -0,0 +1,1685 @@ + + + + + + 预览 15:09 · + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + +
+ + + + + + + + +
+
+
+ +

测试箱主

+

欢迎提问

+
+ + + + + +
+
+
+
+ +
+
+ + + + +
+ + +

扫码向@测试箱主提问
谁都可以以匿名的形式提问

+ + + + + + + + + + + + + + + + + + +
+ + + + + +
+ +
+ +
+ + + + + + + +
+ +
+
+ + + +
+ +
+
+ +
+ + + +
+ +
+
+
+ + + +
+ +
+ +
+ +
+
+
+ + + + + + +

@测试箱主 以前回答过的问题 (2)

+ +
+ +
+
+ 查看回答 +
+ ⭐ + 2026-10-19 15:09:46 +
+

置顶的问题 +

+
+ +
+
+ 查看回答 +
+ + 2026-10-19 14:09:46 +
+

普通问题 +

+
+ +
+ + +
+ +
+
+ 无更多留言 +

+
+
+ + +
+ + +
+
+ +

+
+ 社区规则 | + + 找站长 +
+ 盘 + | 公告板 + | 聊天室 + | 监控板 + | 导航页 +
Powered by NekoBox | +
提问箱已成功存活: +

+ + +
+
+ + +
+ diff --git a/outputs/shot-preview-theme-sakura-light.html b/outputs/shot-preview-theme-sakura-light.html new file mode 100644 index 0000000..ba83397 --- /dev/null +++ b/outputs/shot-preview-theme-sakura-light.html @@ -0,0 +1,1685 @@ + + + + + + 预览 15:09 · + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + +
+ + + + + + + + +
+
+
+ +

测试箱主

+

欢迎提问

+
+ + + + + +
+
+
+
+ +
+
+ + + + +
+ + +

扫码向@测试箱主提问
谁都可以以匿名的形式提问

+ + + + + + + + + + + + + + + + + + +
+ + + + + +
+ +
+ +
+ + + + + + + +
+ +
+
+ + + +
+ +
+
+ +
+ + + +
+ +
+
+
+ + + +
+ +
+ +
+ +
+
+
+ + + + + + +

@测试箱主 以前回答过的问题 (2)

+ +
+ +
+
+ 查看回答 +
+ ⭐ + 2026-10-19 15:09:46 +
+

置顶的问题 +

+
+ +
+
+ 查看回答 +
+ + 2026-10-19 14:09:46 +
+

普通问题 +

+
+ +
+ + +
+ +
+
+ 无更多留言 +

+
+
+ + +
+ + +
+
+ +

+
+ 社区规则 | + + 找站长 +
+ 盘 + | 公告板 + | 聊天室 + | 监控板 + | 导航页 +
Powered by NekoBox | +
提问箱已成功存活: +

+ + +
+
+ + +
+ diff --git a/outputs/shot-profile-account-probe.html b/outputs/shot-profile-account-probe.html new file mode 100644 index 0000000..f0a572b --- /dev/null +++ b/outputs/shot-profile-account-probe.html @@ -0,0 +1,2797 @@ + + + + + + 预览 15:13 · 设置 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+

裁剪图片

+
+
+
+ +
+

+
+ +
+
+ + +
+ + + + + +
+ + +
+
+ + + +
+

基础资料

+
+ + +
+
+ + +
+
+ +
+ +
+
+ + +
+
+ +
+ +
+
+ + + 选择访客访问你的提问箱时显示的语言(后台页面跟随此设置) +
+
+ + +
+
+ +
+
+ + +
+

头像与背景

+
+
+ + + + 1:1 +
+ + +
+ 裁剪框已锁定为 1:1 正方形 +
+
+ + + + 16:9 +
+ + +
+ 裁剪框已锁定为 16:9 宽屏 +
+
+ +
+ +
+
+ + +
+
    +
  • + 二维码样式 +
    +
    +
    +
    + +
    + +
    +
    + +
    + +
    +
    + +
    + +
    +
    + + 仅在启用时生效,否则颜色由系统自动生成 +
    +
    + +
    + + 0.35 + +
    +
    +
    + +
    + + 0.8 + +
    +
    +
    + +
    + + +
    + 选择图片后预览将即时更新 +
    +
    +
    +
    +

    实时预览

    +
    +
    +
    + 预设主题 +
    + + + + +
    +
    +
    + +
    +

    调整仅用于预览,最终效果点击「保存修改」后生效

    +
    +
    +
    +
    +
  • +
+
+ +
+
+ + +
+
    +
  • + 社交信息设置 +
    + 勾选后填写主页地址(无需 https://),未勾选的项将不会在前台显示。 + + + + + +
    +
  • +
+
+ +
+
+ + +
+

修改密码

+
+ + +
+
+ + +
+

保存后当前会话立即失效,需使用新密码重新登录

+
+
+ +
+
+
+ + +
+

标签设置

+
+ +
+ + +
+
+ +
+
+ +
+
+
+ + +
+

账号设置

+ +
+ + +
+

主题外观

+
+ +

仅对提问箱页面生效,站内其它页面保持默认外观。

+
+ + + + + + +
+
+ +
+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + +

+
+ 社区规则 | + + 找站长 +
+ 盘 + | 公告板 + | 聊天室 + | 监控板 + | 导航页 +
Powered by NekoBox | +
提问箱已成功存活: +

+ + +
+
+ + +
+ + diff --git a/outputs/shot-profile-account.html b/outputs/shot-profile-account.html new file mode 100644 index 0000000..afa164f --- /dev/null +++ b/outputs/shot-profile-account.html @@ -0,0 +1,2789 @@ + + + + + + 预览 15:15 · 设置 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+

裁剪图片

+
+
+
+ +
+

+
+ +
+
+ + +
+ + + + + +
+ + +
+
+ + + +
+

基础资料

+
+ + +
+
+ + +
+
+ +
+ +
+
+ + +
+
+ +
+ +
+
+ + + 选择访客访问你的提问箱时显示的语言(后台页面跟随此设置) +
+
+ + +
+
+ +
+
+ + +
+

头像与背景

+
+
+ + + + 1:1 +
+ + +
+ 裁剪框已锁定为 1:1 正方形 +
+
+ + + + 16:9 +
+ + +
+ 裁剪框已锁定为 16:9 宽屏 +
+
+ +
+ +
+
+ + +
+
    +
  • + 二维码样式 +
    +
    +
    +
    + +
    + +
    +
    + +
    + +
    +
    + +
    + +
    +
    + + 仅在启用时生效,否则颜色由系统自动生成 +
    +
    + +
    + + 0.35 + +
    +
    +
    + +
    + + 0.8 + +
    +
    +
    + +
    + + +
    + 选择图片后预览将即时更新 +
    +
    +
    +
    +

    实时预览

    +
    +
    +
    + 预设主题 +
    + + + + +
    +
    +
    + +
    +

    调整仅用于预览,最终效果点击「保存修改」后生效

    +
    +
    +
    +
    +
  • +
+
+ +
+
+ + +
+
    +
  • + 社交信息设置 +
    + 勾选后填写主页地址(无需 https://),未勾选的项将不会在前台显示。 + + + + + +
    +
  • +
+
+ +
+
+ + +
+

修改密码

+
+ + +
+
+ + +
+

保存后当前会话立即失效,需使用新密码重新登录

+
+
+ +
+
+
+ + +
+

标签设置

+
+ +
+ + +
+
+ +
+
+ +
+
+
+ + +
+

账号设置

+ +
+ + +
+

主题外观

+
+ +

仅对提问箱页面生效,站内其它页面保持默认外观。

+
+ + + + + + +
+
+ +
+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + +

+
+ 社区规则 | + + 找站长 +
+ 盘 + | 公告板 + | 聊天室 + | 监控板 + | 导航页 +
Powered by NekoBox | +
提问箱已成功存活: +

+ + +
+
+ + + + + diff --git a/outputs/t-700.png b/outputs/t-700.png new file mode 100644 index 0000000..126ce99 Binary files /dev/null and b/outputs/t-700.png differ diff --git a/outputs/t-768.png b/outputs/t-768.png new file mode 100644 index 0000000..295b5f4 Binary files /dev/null and b/outputs/t-768.png differ diff --git a/outputs/t-980.png b/outputs/t-980.png new file mode 100644 index 0000000..4a2bcd3 Binary files /dev/null and b/outputs/t-980.png differ diff --git a/outputs/t-nav-right.png b/outputs/t-nav-right.png new file mode 100644 index 0000000..d258a6f Binary files /dev/null and b/outputs/t-nav-right.png differ diff --git a/outputs/theme-dark.png b/outputs/theme-dark.png new file mode 100644 index 0000000..1f024f7 Binary files /dev/null and b/outputs/theme-dark.png differ diff --git a/outputs/theme-light.png b/outputs/theme-light.png new file mode 100644 index 0000000..03e2a14 Binary files /dev/null and b/outputs/theme-light.png differ diff --git a/outputs/verify/c3.json b/outputs/verify/c3.json new file mode 100644 index 0000000..9fc30f4 --- /dev/null +++ b/outputs/verify/c3.json @@ -0,0 +1 @@ +{"code":0,"data":{"id":"af87f847522ec487","question":"10 × 5 ="}} diff --git a/outputs/verify/cj3.txt b/outputs/verify/cj3.txt new file mode 100644 index 0000000..9fb38cf --- /dev/null +++ b/outputs/verify/cj3.txt @@ -0,0 +1,5 @@ +# Netscape HTTP Cookie File +# https://curl.se/docs/http-cookies.html +# This file was generated by libcurl! Edit at your own risk. + +#HttpOnly_box-dev-api.337845818.best FALSE / TRUE 0 flamego_session xzuo76g1wkt93yde diff --git a/outputs/verify/d-base.css b/outputs/verify/d-base.css new file mode 100644 index 0000000..c5d2a97 --- /dev/null +++ b/outputs/verify/d-base.css @@ -0,0 +1,670 @@ +/* TamaBox 全站基础样式:设计令牌 / 主题覆盖 / 组件质感 / 响应式。 + 由 base/header.html 内联块抽出,集中一份便于缓存与维护。 + ⚠️ 改完务必把 header 里的 ?v= 版本号 +1,否则老访客仍用缓存。 */ + +/* 设计令牌:亮色为默认(:root),深色在 html[data-theme="dark"] 覆盖。 + 页面与组件样式一律引用 var(--tb-*),不再写死色值/圆角/阴影; + var() 的第二个参数仅作兜底(旧缓存页、邮件模板等不引入本文件的场景)。 + 改配色只动这一块,亮暗两套自动保持一致。 */ + :root { + --tb-bg: #f7f8fa; + --tb-surface: #ffffff; + --tb-surface-2: #f4f6f8; + --tb-surface-hover: #f3f7ff; + --tb-border: #e8eaee; + --tb-text: #1f2329; + --tb-text-secondary: #5c6370; + --tb-text-muted: #8a919c; + --tb-primary: #1e87f0; + --tb-primary-weak: rgba(30, 135, 240, 0.10); + --tb-radius: 12px; + --tb-radius-sm: 8px; + --tb-radius-xs: 6px; + --tb-shadow: 0 1px 2px rgba(16, 24, 40, 0.04), 0 12px 28px -18px rgba(16, 24, 40, 0.20); + --tb-shadow-hover: 0 1px 2px rgba(16, 24, 40, 0.05), 0 18px 40px -20px rgba(16, 24, 40, 0.28); + --tb-font: system-ui, -apple-system, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Noto Sans CJK SC", "Microsoft YaHei", sans-serif; + } + html[data-theme="dark"] { + --tb-bg: #1a1d23; + --tb-surface: #242830; + --tb-surface-2: #2d323c; + --tb-surface-hover: rgba(92, 141, 255, 0.12); + --tb-border: #3a404c; + --tb-text: #d8dce4; + --tb-text-secondary: #a8b0bd; + --tb-text-muted: #8b93a1; + --tb-primary: #5c8dff; + --tb-primary-weak: rgba(92, 141, 255, 0.14); + --tb-shadow: 0 1px 2px rgba(0, 0, 0, 0.30), 0 12px 28px -18px rgba(0, 0, 0, 0.55); + --tb-shadow-hover: 0 1px 2px rgba(0, 0, 0, 0.35), 0 18px 40px -20px rgba(0, 0, 0, 0.65); + } + +/* UIkit 补丁:复刻原自建版 "modify-black-font"(官方版与自建版仅 3 处颜色差异——把有色容器内默认白字/白标题改为黑字)。 + 模板当前只用到 .uk-light 容器(用户背景封面,question/list.html、question-not-found.html; + 其中 h3/p 已由 #usernamecolor/#introcolor 内联 style 覆盖),故只需补该容器的 3 处投影即可保持渲染一致。 + 若日后新增 .uk-card-primary/.uk-card-secondary/.uk-section-primary/.uk-tile-*/.uk-overlay-primary 等容器且需黑字,须同步扩展。 */ + .uk-light { color: rgba(0,0,0,.7); } + .uk-light .uk-h1, .uk-light .uk-h2, .uk-light .uk-h3, .uk-light .uk-h4, .uk-light .uk-h5, .uk-light .uk-h6, + .uk-light .uk-heading-2xlarge, .uk-light .uk-heading-large, .uk-light .uk-heading-medium, .uk-light .uk-heading-small, .uk-light .uk-heading-xlarge, + .uk-light h1, .uk-light h2, .uk-light h3, .uk-light h4, .uk-light h5, .uk-light h6 { color: #000; } + .uk-light .uk-table th { color: rgba(0,0,0,.7); } + + .grecaptcha-badge {visibility: hidden;} + .icon-container { + display: flex; + justify-content: center; + width: 100%; + } + .icon { + margin-left: 10px; + width: 20px; /* 设置图标的宽度 */ + height: auto; /* 保持图标的高度自动调整 */ + } + .emoji-placeholder { + display: inline-block; + animation: jump 0.5s ease-in-out; + } + + @keyframes jump { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-5px); } + } + /* 防止 UIkit Dropbar 初始闪烁 */ + .uk-dropbar { + opacity: 0; + visibility: hidden; + transition: opacity 0.2s ease, visibility 0.2s ease; + } + + .uk-dropbar.uk-open { + opacity: 1; + visibility: visible; + } + + a.uk-button.uk-button-link { + text-transform: none; + } + + /* 移动端优化:缩小字体和间距,避免右侧空白 */ + @media (max-width: 640px) { + .uk-logo { + font-size: 1rem; + } + .uk-navbar-nav a { + font-size: 0.78rem; + padding: 0 4px; + } + .emoji-placeholder { + font-size: 1rem; + } + /* 移动端用户名限宽省略,避免长英文用户名撑爆顶栏 */ + .tb-nav-username { + max-width: 64px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: inline-block; + vertical-align: middle; + } + } + +/* 深色模式:html[data-theme="dark"] 时覆盖 UIkit 3.3.3 编译主题的关键颜色 */ + /* 变量(--tb-*)已上移到 head 顶部的令牌块,亮暗两套同名常量在那里定义。 */ + /* 根元素与 body 一起覆盖,确保整个视口(含内容不足一屏时的空白区)都是深色背景 */ + html[data-theme="dark"], + html[data-theme="dark"] body { + background-color: var(--tb-bg) !important; + color: var(--tb-text) !important; + } + /* 通用表面容器 */ + html[data-theme="dark"] .uk-card, + html[data-theme="dark"] .uk-card-default, + html[data-theme="dark"] .uk-card-secondary, + html[data-theme="dark"] .uk-dropbar, + html[data-theme="dark"] .uk-navbar-dropdown, + html[data-theme="dark"] .uk-modal-dialog, + html[data-theme="dark"] .uk-dropdown, + html[data-theme="dark"] .uk-offcanvas-bar, + html[data-theme="dark"] .uk-section, + html[data-theme="dark"] .uk-tile, + html[data-theme="dark"] .uk-panel, + html[data-theme="dark"] .uk-background-default, + html[data-theme="dark"] .uk-search-default .uk-search-input { + background-color: var(--tb-surface) !important; + color: var(--tb-text) !important; + } + html[data-theme="dark"] .uk-navbar-container { + background-color: var(--tb-surface) !important; + color: var(--tb-text) !important; + } + html[data-theme="dark"] .uk-navbar-nav > li > a, + html[data-theme="dark"] .uk-card a, + html[data-theme="dark"] .uk-link, + html[data-theme="dark"] a.uk-button-link, + html[data-theme="dark"] .uk-navbar-dropdown-nav > li > a, + html[data-theme="dark"] .uk-dropdown-nav > li > a, + html[data-theme="dark"] .uk-list a { + color: var(--tb-text) !important; + } + html[data-theme="dark"] .uk-navbar-nav > li > a:hover, + html[data-theme="dark"] .uk-card a:hover, + html[data-theme="dark"] .uk-navbar-dropdown-nav > li > a:hover, + html[data-theme="dark"] .uk-dropdown-nav > li > a:hover { + color: var(--tb-primary) !important; + } + html[data-theme="dark"] .uk-input, + html[data-theme="dark"] .uk-textarea, + html[data-theme="dark"] .uk-select { + background-color: var(--tb-surface-2) !important; + color: var(--tb-text) !important; + border-color: var(--tb-border) !important; + } + html[data-theme="dark"] .uk-input:focus, + html[data-theme="dark"] .uk-textarea:focus, + html[data-theme="dark"] .uk-select:focus { + border-color: var(--tb-primary) !important; + } + html[data-theme="dark"] .uk-text-muted, + html[data-theme="dark"] .uk-text-meta, + html[data-theme="dark"] .uk-text-secondary, + html[data-theme="dark"] .uk-text-muted a, + html[data-theme="dark"] .uk-text-small.uk-text-muted { + color: var(--tb-text-muted) !important; + } + html[data-theme="dark"] .uk-divider-icon::after, + html[data-theme="dark"] hr, + html[data-theme="dark"] .uk-table th, + html[data-theme="dark"] .uk-table td, + html[data-theme="dark"] .uk-list-divider > li:nth-child(n+2) { + border-color: var(--tb-border) !important; + } + html[data-theme="dark"] .uk-button-default { + background-color: var(--tb-surface-2) !important; + color: var(--tb-text) !important; + border-color: var(--tb-border) !important; + } + html[data-theme="dark"] .uk-modal-title, + html[data-theme="dark"] h1, html[data-theme="dark"] h2, html[data-theme="dark"] h3, + html[data-theme="dark"] h4, html[data-theme="dark"] h5, html[data-theme="dark"] h6, + html[data-theme="dark"] .uk-card-title { + color: var(--tb-text) !important; + } + html[data-theme="dark"] .uk-alert-warning, + html[data-theme="dark"] .uk-alert-danger, + html[data-theme="dark"] .uk-alert-success, + html[data-theme="dark"] .uk-alert-primary { + background-color: var(--tb-surface-2) !important; + color: var(--tb-text) !important; + } + html[data-theme="dark"] #math-captcha-modal .uk-modal-dialog { + background-color: var(--tb-surface) !important; + color: var(--tb-text) !important; + } + html[data-theme="dark"] #math-captcha-modal .captcha-equation-box { + background-color: var(--tb-surface-2) !important; + border-color: var(--tb-border) !important; + } + html[data-theme="dark"] #math-captcha-modal .math-captcha-modal-question { + color: var(--tb-text) !important; + } + html[data-theme="dark"] #math-captcha-modal #captcha-modal-answer { + background-color: var(--tb-surface) !important; + color: var(--tb-text) !important; + border-color: var(--tb-border) !important; + } + html[data-theme="dark"] #math-captcha-modal #captcha-modal-answer::placeholder { + color: var(--tb-text-muted) !important; + } + html[data-theme="dark"] #math-captcha-modal .uk-text-meta, + html[data-theme="dark"] #math-captcha-modal #captcha-modal-refresh { + color: var(--tb-text-muted) !important; + } + html[data-theme="dark"] #math-captcha-modal #captcha-modal-refresh:hover { + color: var(--tb-primary) !important; + } + html[data-theme="dark"] .uk-label { + color: #fff !important; + } + /* 自定义组件(探索页推荐卡片等)深色覆盖 */ + html[data-theme="dark"] .recommend-card, + html[data-theme="dark"] .recommend-card:hover { + background-color: var(--tb-surface-2) !important; + } + html[data-theme="dark"] .explore-name a { + color: var(--tb-text) !important; + } + html[data-theme="dark"] .explore-name a:hover { + color: var(--tb-primary) !important; + } + html[data-theme="dark"] .explore-intro { + color: var(--tb-text-muted) !important; + } + /* 切换按钮图标切换(内联 SVG,不依赖 UIkit 图标库) */ + #theme-toggle .tb-icon-light, #theme-toggle-mobile .tb-icon-light, + #theme-toggle .tb-icon-dark, #theme-toggle-mobile .tb-icon-dark { + display: inline-flex; + align-items: center; + } + #theme-toggle .tb-icon-light svg, #theme-toggle-mobile .tb-icon-light svg, + #theme-toggle .tb-icon-dark svg, #theme-toggle-mobile .tb-icon-dark svg { display: block; } + #theme-toggle .tb-icon-dark, #theme-toggle-mobile .tb-icon-dark { display: none; } + html[data-theme="dark"] #theme-toggle .tb-icon-light, + html[data-theme="dark"] #theme-toggle-mobile .tb-icon-light { display: none; } + html[data-theme="dark"] #theme-toggle .tb-icon-dark, + html[data-theme="dark"] #theme-toggle-mobile .tb-icon-dark { display: inline-flex; } + +/* 基础质感层:只调整外观(圆角/投影/字体/数字对齐),不改任何结构与布局。 + 颜色一律取自头部令牌,亮暗两套自动生效,因此这里不需要再写 html[data-theme="dark"] 分支。 */ + /* 根元素与 body 同时上色:内容不足一屏时,body 之外的视口区域也要是同一底色 */ + html, + body { + font-family: var(--tb-font); + background-color: var(--tb-bg); + color: var(--tb-text); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + /* 卡片:12px 圆角 + 一层极淡接触影 + 一层大扩散影(UIkit 默认的 0 5px 15px 偏硬) */ + .uk-card { + border-radius: var(--tb-radius); + } + .uk-card-default { + background-color: var(--tb-surface); + box-shadow: var(--tb-shadow); + } + .uk-card-default.uk-card-hover:hover { + box-shadow: var(--tb-shadow-hover); + } + .uk-card-header { + border-radius: var(--tb-radius) var(--tb-radius) 0 0; + } + .uk-card-footer { + border-radius: 0 0 var(--tb-radius) var(--tb-radius); + } + .uk-card, + .uk-button, + .uk-alert, + .uk-label { + transition: box-shadow 0.18s ease, background-color 0.18s ease, border-color 0.18s ease, color 0.18s ease; + } + /* 交互元素:统一 8px 圆角,标签 6px */ + .uk-button { + border-radius: var(--tb-radius-sm); + } + .uk-input, + .uk-select, + .uk-textarea, + .uk-form-small, + .uk-search-input { + border-radius: var(--tb-radius-sm); + } + .uk-label { + border-radius: var(--tb-radius-xs); + } + .uk-alert { + border-radius: 10px; + } + .uk-modal-dialog { + border-radius: 14px; + } + .uk-dropdown, + .uk-navbar-dropdown { + border-radius: 10px; + box-shadow: var(--tb-shadow); + } + .uk-navbar-container:not(.uk-navbar-transparent) { + background-color: var(--tb-surface); + } + /* 键盘可达性:仅在键盘聚焦时出现描边,鼠标点击不出现 */ + .uk-button:focus-visible, + .uk-navbar-nav > li > a:focus-visible { + outline: 2px solid var(--tb-primary); + outline-offset: 2px; + } + /* 次要文字统一走令牌,避免 #999 / #8a919c 混用 */ + .uk-text-muted, + .uk-text-meta { + color: var(--tb-text-muted); + } + +/* 轻量胶囊:用于投票类型、热门标签等「只是分类信息」的位置。 + 实心 uk-label 一屏多个会变成一排色块,这里统一降级为浅底 + 次要色文字。 */ +.tb-chip { + display: inline-block; + padding: 1px 8px; + border-radius: var(--tb-radius-xs); + background-color: var(--tb-surface-2); + color: var(--tb-text-secondary); + font-size: 0.72rem; + line-height: 1.7; + white-space: nowrap; +} +a.tb-chip:hover { + background-color: var(--tb-primary-weak); + color: var(--tb-primary); + text-decoration: none; +} + +/* 统计/日期等数字列等宽对齐,避免刷新时左右跳动 */ + .uk-table, + .tb-num { + font-variant-numeric: tabular-nums; + } + /* 图片上传条:原为 item/new-question 两处各写一份内联 style(写死 #f8f8f8,深色下露白), + 收敛到一处并走令牌,亮暗自动适配 */ + .tb-img-uploader { + width: 100%; + background-color: var(--tb-surface-2); + display: flex; + padding-top: 5px; + padding-bottom: 5px; + align-items: center; + justify-content: flex-start; + gap: 5px; + } + /* 背景图封面(提问箱主页):渐变遮罩,保证用户自选文字色叠加后仍可读。 + 默认浅色遮罩(页面 .uk-light 补丁把文字兜底成深色);若箱主选了浅色文字, + question/list.html 会按亮度加上 .tb-cover-dark 换成深色遮罩。 */ + .tb-cover { + position: relative; + border-radius: var(--tb-radius) var(--tb-radius) 0 0; + overflow: hidden; + } + /* ⚠️ 必须带 .uk-flex 复合选择器:UIkit 3.3.3 里有 + .uk-flex::after,.uk-flex::before{display:none}(优先级 0,2,0), + 会把这个遮罩伪元素整个干掉——不报错、只是什么都不显示(踩过一次)。 + 单写 .tb-cover::after 优先级不够,会被它压回 display:none。 */ + .tb-cover::after, + .tb-cover.uk-flex::after { + content: ""; + display: block; + position: absolute; + inset: 0; + z-index: 0; + background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.58) 55%, rgba(255, 255, 255, 0.84) 100%); + pointer-events: none; + } + .tb-cover.tb-cover-dark::after { + background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.16) 45%, rgba(0, 0, 0, 0.46) 100%); + } + .tb-cover > * { + position: relative; + z-index: 1; + } + .tb-cover h3, + .tb-cover p { + text-shadow: 0 1px 10px rgba(255, 255, 255, 0.65); + } + .tb-cover.tb-cover-dark h3, + .tb-cover.tb-cover-dark p { + text-shadow: 0 1px 12px rgba(0, 0, 0, 0.45); + } + /* 尊重系统的减少动效设置 */ + @media (prefers-reduced-motion: reduce) { + .uk-card, + .uk-button, + .emoji-placeholder { + transition: none; + animation: none; + } + } + +/* 顶栏自适应:导航全部直接显示,窄屏时分级缩小字号/间距,保证英文/日文等长文字不溢出 */ + .uk-navbar-nav a { + font-size: 0.95rem; + padding: 0 10px; + } + /* 未登录移动端占位:桌面端隐藏,仅 ≤640px 显示(见下方 media 查询)。 + 桌面端布局与改动前完全一致,不受影响。 */ + .tb-mobile-balancer { display: none; } + /* 桌面端保持 UIkit 原生居中三列布局(无需额外 CSS) */ + .tb-navbar .uk-logo { + white-space: nowrap; + } + @media (max-width: 1200px) { + .uk-navbar-nav a { + font-size: 0.88rem; + padding: 0 8px; + } + } + @media (max-width: 1000px) { + .uk-navbar-nav a { + font-size: 0.82rem; + padding: 0 6px; + } + .uk-logo { + font-size: 1.1rem; + } + /* 登录用户名限宽,过长省略号截断 */ + .tb-nav-username { + max-width: 110px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: inline-block; + vertical-align: middle; + } + } + @media (max-width: 800px) { + .uk-navbar-nav a { + font-size: 0.78rem; + padding: 0 4px; + } + .uk-logo { + font-size: 1rem; + } + .tb-nav-username { + max-width: 80px; + } + } + /* 手机端(≤640px):沿用 UIkit 桌面端 navbar 的原生三列定位。 + 不再重写 .uk-navbar-center / center-left / center-right 的定位, + 避免 logo 被挤到下一行;底部语言与深色模式按钮仍由下方独立栏承载。 */ + @media (max-width: 640px) { + .tb-navbar { + position: relative; + min-height: 56px; + overflow: visible; + } + .tb-navbar > .uk-navbar-center { + position: relative !important; + top: auto !important; + left: auto !important; + right: auto !important; + bottom: auto !important; + transform: none !important; + display: flex !important; + align-items: center; + justify-content: center; + width: 100%; + min-height: 56px; + white-space: nowrap; + flex-wrap: nowrap; + } + .tb-navbar .uk-navbar-center-left, + .tb-navbar .uk-navbar-center-right { + position: static !important; + top: auto !important; + left: auto !important; + right: auto !important; + bottom: auto !important; + transform: none !important; + display: flex !important; + align-items: center; + flex: 0 0 auto; + white-space: nowrap; + } + .tb-navbar .uk-navbar-center-left > div, + .tb-navbar .uk-navbar-center-right > div { + display: flex; + align-items: center; + white-space: nowrap; + } + .tb-navbar .uk-navbar-nav { + display: flex; + flex-wrap: nowrap; + flex: 0 0 auto; + } + .tb-navbar .uk-navbar-nav > li > a, + .tb-navbar .uk-logo { + white-space: nowrap; + } + /* 未登录:移动端显示与「我的」等宽的隐藏占位,使左栏「探索+占位」与 + 右栏「注册/登录」两项等宽,logo 真正居中,左右视觉平衡 */ + .tb-mobile-balancer { display: block; } + } + +/* 标签样式的按钮(管理页置顶等): + 尺寸完全继承 .uk-label(font-size/padding/line-height/text-transform 等), + 仅覆盖 button 元素特有的默认样式(边框/字体族/光标等),保证与相邻 span 标签完全一致。 */ + .tb-label-btn { + border: none !important; + cursor: pointer; + font-family: inherit; + margin: 0; + vertical-align: middle; + outline: none; + background-image: none; + box-sizing: border-box; + -webkit-appearance: none; + appearance: none; + } + .tb-label-btn:hover { + opacity: 0.85; + } + /* 公开页置顶小图标:仅一个图钉,不占行高、不影响原布局 */ + .tb-pin-flag { + font-size: 0.9em; + margin-right: 4px; + vertical-align: -1px; + cursor: default; + } + +/* Markdown 渲染排版(问题/回答/追问正文,左对齐以保留排版格式) */ + /* 纯文本列表:保留换行与连续空格(pre-wrap),不渲染 Markdown 大字号/彩色 */ + .tb-preline { + white-space: pre-wrap; + } + /* 纯文本列表:超过 5 行截断并显示省略号(不足 5 行则完整显示) */ + .tb-clamp-5 { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 5; + overflow: hidden; + } + .md-body { + line-height: 1.7; + text-align: left; + word-break: break-word; + } + .md-body p { margin: 0.5em 0; } + .md-body h1, .md-body h2, .md-body h3, + .md-body h4, .md-body h5, .md-body h6 { + margin: 0.8em 0 0.4em; + font-weight: 600; + line-height: 1.4; + } + .md-body ul, .md-body ol { padding-left: 1.6em; margin: 0.5em 0; } + .md-body li { margin: 0.2em 0; } + .md-body blockquote { + border-left: 3px solid #d3d1c7; + margin: 0.6em 0; + padding: 0.2em 1em; + color: #5f5e5a; + } + .md-body pre { + background: #f5f5f5; + padding: 0.8em 1em; + border-radius: 6px; + overflow-x: auto; + margin: 0.6em 0; + } + .md-body code { + background: #f0f0f0; + padding: 0.1em 0.35em; + border-radius: 4px; + font-size: 0.9em; + } + .md-body pre code { background: none; padding: 0; } + .md-body img { max-width: 100%; height: auto; } + .md-body table { border-collapse: collapse; margin: 0.6em 0; } + .md-body th, .md-body td { + border: 1px solid #ddd; + padding: 0.4em 0.8em; + } + .md-body hr { margin: 1em 0; } + html[data-theme="dark"] .md-body blockquote { + border-color: #3a404c; + color: #8b93a1; + } + html[data-theme="dark"] .md-body pre, + html[data-theme="dark"] .md-body code { background: #2d323c; } + html[data-theme="dark"] .md-body th, + html[data-theme="dark"] .md-body td { border-color: #3a404c; } + +/* 投票结果占比条:模板已改用 UIkit 3 原生 , + 这里只把默认的 15px 高 / 500px 圆角 / 20px 外边距收紧到与页面一致的规格。 + (原先为 UIkit 2 的 .uk-progressbar 结构打的补丁已删除) */ +.uk-progress { + height: 10px; + margin: 0; + border-radius: var(--tb-radius-xs); + background-color: var(--tb-surface-2); +} +.uk-progress::-webkit-progress-bar { + background-color: var(--tb-surface-2); + border-radius: var(--tb-radius-xs); +} +.uk-progress::-webkit-progress-value { + /* 两端都圆:值 100% 时若只圆左边,右端方角会顶出轨道圆角 */ + background-color: var(--tb-primary); + border-radius: var(--tb-radius-xs); +} +.uk-progress::-moz-progress-bar { + background-color: var(--tb-primary); + border-radius: var(--tb-radius-xs); +} + +/* 移动端底部操作栏:固定底栏承载语言切换与深色模式,桌面端隐藏 */ + #tb-mobile-utils { + display: none; + } + @media (max-width: 640px) { + /* 顶栏不再容纳语言/深色按钮,恢复原有导航空间 */ + .tb-nav-utils { + display: none !important; + } + /* 手机端隐藏登录用户名(左边仅保留"探索"),保证 logo 居中、 + 两侧各不超过 2 个短按钮,避免用户名宽度挤压布局 */ + .tb-nav-username { + display: none !important; + } + /* 固定底部操作栏 */ + #tb-mobile-utils { + display: flex; + position: fixed; + left: 0; + right: 0; + bottom: 0; + z-index: 1000; + justify-content: center; + align-items: center; + gap: 10px; + padding: 8px 12px calc(8px + env(safe-area-inset-bottom)); + background: rgba(255, 255, 255, 0.92); + -webkit-backdrop-filter: blur(8px); + backdrop-filter: blur(8px); + box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.08); + } + #tb-mobile-utils .uk-select { + width: 110px; + } + html[data-theme="dark"] #tb-mobile-utils { + background: rgba(36, 40, 48, 0.92); + box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.4); + } + /* 给页面底部预留空间,避免页脚内容被固定底栏遮挡 */ + body { + padding-bottom: 56px; + } + } diff --git a/outputs/verify/d-home.html b/outputs/verify/d-home.html new file mode 100644 index 0000000..dd52593 --- /dev/null +++ b/outputs/verify/d-home.html @@ -0,0 +1,937 @@ + + + + + + 狼の質問箱 | 匿名質問箱 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + +
+ + + +
+
+

人機認証

+

以下の計算問題を解いて、ロボットでないことを証明してください

+
+ 読み込み中... + +
+
+ +
+ +
+ + +
+
+
+ + + + + + + +
+

狼の質問箱 | 匿名質問箱

+

+ 一个无需注册即可提问的匿名提问箱。不用下载 App,不用绑定社交账号,连邮箱都不必是真的。 +

+ +
+ +
+
+

匿名也能问

+

不需要账号、不需要真实邮箱,打开就能写。想收到回复再留邮箱即可。

+
+
+

不止一问一答

+

问题二维码、私聊、投票、追加提问、Markdown 排版,原版没有的这里都有。

+
+
+

箱子归你

+

头像、背景、二维码配色都能自己调,还能设置审核严格度和收集时段。

+
+
+ +
+

域名迁移

+

+ 本站已切换到 box.shiroko.one,原域名将在 2026 年 12 月 24 日到期, + 期间通过 301 转发指向新域名,到期后旧域名将彻底不再使用。 +

+

+ 审核服务已切换到阿里云,审核速度提升且误伤更少;同时允许用户调整审核严格度,在不碰底线的前提下 + 可以设置不接受辱骂、广告等信息(即「不听坏话模式」)。 +

+
+ +
+

一点提醒

+
    +
  • 本站唯一指定认证站长:小狼,注意不要被骗。
  • +
  • 本站提倡友好、友善的发言,请遵守社区规则。无论匿名与否,都要为自己的发言负责。
  • +
  • 不允许垃圾信息、广告、辱骂、涉政、暴恐、色情、违禁、无意义、灌水提问,涉政或不良言论将直接删除。
  • +
+

+ 想先试试功能,可以到站长的提问箱 + 小狼的提问箱 提问或提意见。 +

+
+ +
+ 近期更新 +
    +
  • 2026.9 新增审核严格度选项,增加提问、投票、私聊的总开关,可设置定时收集、定量收集或关闭功能,前后端显示也更易用了。
  • +
  • 2026.8 新增投票功能、追加提问、多语言、Markdown 显示、置顶显示等,详情见这里。
  • +
  • 2026.8 放弃使用 recaptcha 等对国内用户不友好的验证方式,更新密码加密方式,修复图片上传等一系列问题。
  • +
  • 2026.5 增加私聊功能,可连续对话,详见介绍;另有提问箱卡片分享。
  • +
  • 2026.2 新增用户展示和标签功能,可给自己贴 5 个标签并选择是否在探索页展示,详情见狼的公告板。
  • +
+
+ +
+

質問箱の統計

+
+
+ 28 + 注册用户 +
+
+ 94 + 累计提问 +
+
+ 21 + 已获回答 +
+
+

+ 最后提问:2026-10-19 00:58:03
+ 最后回答:2026-10-19 02:44:47
+ 数据更新于:2026-10-19 12:48:26 +

+
+ +
+ +

+
+ ルール | + + 管理者へ +
+ ドライブ + | お知らせ + | チャット + | ステータス + | リンク +
Powered by NekoBox | 狼ICP备 20230101 +
質問箱は稼働中: +

+ + +
+
+ + + + diff --git a/outputs/verify/d-prev-10.html b/outputs/verify/d-prev-10.html new file mode 100644 index 0000000..c50437a --- /dev/null +++ b/outputs/verify/d-prev-10.html @@ -0,0 +1,854 @@ + + + + + + 预览:测试 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + +
+ + + +
+
+

人机验证

+

请计算下方的算术题以验证你不是机器人

+
+ 加载中... + +
+
+ +
+ +
+ + +
+
+
+ + + + + + + + + + + + + + + + + + +
+
+ + +
+ admin6 +
发起的投票
+
+ +
+ + + 进行中 + +
+
+ +

测试

+ +

测试

+ +
+ + +
预览模式:这是发布前的页面样例,提交已禁用
+ + + + + + + + +
该投票允许匿名参与,你的身份不会被记录(同一设备最多可参与 1 次)。
+ + +
+ + + +
+ + +
+
+ 1 + 多行文本 +
测是
+
+ + + +
+ + + + +
+ +
+ + + 预览模式,发布后即可提交 + +
+
+ + + + + + + + + + + +

+
+ 社区规则 | + + 找站长 +
+ 盘 + | 公告板 + | 聊天室 + | 监控板 + | 导航页 +
Powered by NekoBox | 狼ICP备 20230101 +
提问箱已成功存活: +

+ + +
+
+ + + + + diff --git a/outputs/verify/d-prev-11.html b/outputs/verify/d-prev-11.html new file mode 100644 index 0000000..f6f4a9d --- /dev/null +++ b/outputs/verify/d-prev-11.html @@ -0,0 +1,902 @@ + + + + + + 预览:今天吃什么 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + +
+ + + +
+
+

人机验证

+

请计算下方的算术题以验证你不是机器人

+
+ 加载中... + +
+
+ +
+ +
+ + +
+
+
+ + + + + + + + + + + + + + + + + + +
+
+ + +
+ admin6 +
发起的投票
+
+ +
+ + + 进行中 + +
+
+ +

今天吃什么

+ +

是啊,吃什么?

+ +
+ + +
预览模式:这是发布前的页面样例,提交已禁用
+ + + + + + + + +
该投票允许匿名参与,你的身份不会被记录(同一设备最多可参与 1 次)。
+ + +
+ + + +
+ + +
+
+ 1 + 单选 +
水果
+
+ + + + + + + + + + +
+ +
+
+ 2 + 多选 +
主食 *
+
+ + + + + + + + + + +
+ +
+
+ 3 + 数字 +
吃多少 *
+
+ + + +
+ + + + +
+ +
+ + + 预览模式,发布后即可提交 + +
+
+ + + + + + + + + + + +

+
+ 社区规则 | + + 找站长 +
+ 盘 + | 公告板 + | 聊天室 + | 监控板 + | 导航页 +
Powered by NekoBox | 狼ICP备 20230101 +
提问箱已成功存活: +

+ + +
+
+ + + + + diff --git a/outputs/verify/d-res-12.html b/outputs/verify/d-res-12.html new file mode 100644 index 0000000..f1d67c2 --- /dev/null +++ b/outputs/verify/d-res-12.html @@ -0,0 +1,758 @@ + + + + + + 投票结果 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + +
+ + + +
+
+

人机验证

+

请计算下方的算术题以验证你不是机器人

+
+ 加载中... + +
+
+ +
+ +
+ + +
+
+
+ + + + + + +
+

投票结果:测试功能

+ +
+ +
+
3
+
总参与人数
+
+ + + + +

投票结果

+
+ +
+
+ 1 + 单选 + 必填 + 回答数: 3 +
+ + + + + +
+
+ A + 3 票(100%) +
+ +
+ +
+
+ B + 0 票(0%) +
+ +
+ +
+
+ C + 0 票(0%) +
+ +
+ + + + +
+ +
+
+ 2 + 多选 + 非必填 + 回答数: 1 +
+ + + + + +
+
+ A + 1 票(100%) +
+ +
+ +
+
+ B + 0 票(0%) +
+ +
+ +
+
+ C + 0 票(0%) +
+ +
+ +
+
+ D + 0 票(0%) +
+ +
+ + + + +
+ +
+
+ 3 + 单行文本 + 文本1 + 回答数: 0 +
+ + +
已填写: 0
+ + + +
+ +
+
+ 4 + 多行文本 + 文本2 + 回答数: 3 +
+ + +
已填写: 3
+ + +
+
+ ▾ + 查看文本提交(3) +
+ +
+ + +
+ +
+ + + + + + + +

+
+ 社区规则 | + + 找站长 +
+ 盘 + | 公告板 + | 聊天室 + | 监控板 + | 导航页 +
Powered by NekoBox | 狼ICP备 20230101 +
提问箱已成功存活: +

+ + +
+
+ + + + + diff --git a/outputs/verify/d-res-13.html b/outputs/verify/d-res-13.html new file mode 100644 index 0000000..fde00cd --- /dev/null +++ b/outputs/verify/d-res-13.html @@ -0,0 +1,626 @@ + + + + + + 投票结果 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + +
+ + + +
+
+

人机验证

+

请计算下方的算术题以验证你不是机器人

+
+ 加载中... + +
+
+ +
+ +
+ + +
+
+
+ + + + + + +
+

投票结果:吃什么

+ +
+ +
+
0
+
总参与人数
+
+ + + + +

投票结果

+
+ +
+
+ 1 + 单行文本 + 是啊,吃什么? + 回答数: 0 +
+ + +
已填写: 0
+ + + +
+ +
+ + + + + + + +

+
+ 社区规则 | + + 找站长 +
+ 盘 + | 公告板 + | 聊天室 + | 监控板 + | 导航页 +
Powered by NekoBox | 狼ICP备 20230101 +
提问箱已成功存活: +

+ + +
+
+ + + + + diff --git a/outputs/verify/d-res-3.html b/outputs/verify/d-res-3.html new file mode 100644 index 0000000..47d1e65 --- /dev/null +++ b/outputs/verify/d-res-3.html @@ -0,0 +1,693 @@ + + + + + + 投票结果 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + +
+ + + +
+
+

人机验证

+

请计算下方的算术题以验证你不是机器人

+
+ 加载中... + +
+
+ +
+ +
+ + +
+
+
+ + + + + + +
+

投票结果:明天吃什么

+ +
+ +
+
1
+
总参与人数
+
+ + + +

投票结果

+
+ +
平均分: 8.0
+ +
+
+ 1 分 + 0 票 +
+ +
+ +
+
+ 2 分 + 0 票 +
+ +
+ +
+
+ 3 分 + 0 票 +
+ +
+ +
+
+ 4 分 + 0 票 +
+ +
+ +
+
+ 5 分 + 0 票 +
+ +
+ +
+
+ 6 分 + 0 票 +
+ +
+ +
+
+ 7 分 + 0 票 +
+ +
+ +
+
+ 8 分 + 1 票 +
+ +
+ +
+
+ 9 分 + 0 票 +
+ +
+ +
+
+ 10 分 + 0 票 +
+ +
+ + + + + +
+ + + + +

+
+ 社区规则 | + + 找站长 +
+ 盘 + | 公告板 + | 聊天室 + | 监控板 + | 导航页 +
Powered by NekoBox | 狼ICP备 20230101 +
提问箱已成功存活: +

+ + +
+
+ + + + + diff --git a/outputs/verify/d-res-5.html b/outputs/verify/d-res-5.html new file mode 100644 index 0000000..9ee0e82 --- /dev/null +++ b/outputs/verify/d-res-5.html @@ -0,0 +1,929 @@ + + + + + + 投票结果 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + +
+ + + +
+
+

人机验证

+

请计算下方的算术题以验证你不是机器人

+
+ 加载中... + +
+
+ +
+ +
+ + +
+
+
+ + + + + + +
+

投票结果:11

+ +
+ +
+
2
+
总参与人数
+
+ + + + +

投票结果

+
+ +
+
+ 1 + 单选 + 11 + 回答数: 2 +
+ + + + + +
+
+ 11 + 2 票(100%) +
+ +
+ +
+
+ 22 + 0 票(0%) +
+ +
+ +
+
+ 33 + 0 票(0%) +
+ +
+ + + + +
+ +
+
+ 2 + 多选 + 22 + 回答数: 2 +
+ + + + + +
+
+ 11 + 2 票(66%) +
+ +
+ +
+
+ 22 + 1 票(33%) +
+ +
+ +
+
+ 33 + 0 票(0%) +
+ +
+ + + + +
+ +
+
+ 3 + 单行文本 + 33 + 回答数: 2 +
+ + +
已填写: 2
+ + +
+
+ ▾ + 查看文本提交(2) +
+ +
+ + +
+ +
+
+ 4 + 多行文本 + 44 + 回答数: 2 +
+ + +
已填写: 2
+ + +
+
+ ▾ + 查看文本提交(2) +
+ +
+ + +
+ +
+
+ 5 + 下拉选择 + 55 + 回答数: 2 +
+ + + + + +
+
+ 11 + 2 票(100%) +
+ +
+ +
+
+ 22 + 0 票(0%) +
+ +
+ +
+
+ 33 + 0 票(0%) +
+ +
+ + + + +
+ +
+
+ 6 + 数字 + 66 + 回答数: 2 +
+ + +
平均: 4.0(2 票)
+ + +
+ +
+
+ 7 + 评分 + 77 + 回答数: 2 +
+ + +
平均分: 9.0(2 票)
+ +
+ 1 分 + 0 票(0%) +
+ + +
+ 2 分 + 0 票(0%) +
+ + +
+ 3 分 + 0 票(0%) +
+ + +
+ 4 分 + 0 票(0%) +
+ + +
+ 5 分 + 0 票(0%) +
+ + +
+ 6 分 + 0 票(0%) +
+ + +
+ 7 分 + 0 票(0%) +
+ + +
+ 8 分 + 1 票(50%) +
+ + +
+ 9 分 + 0 票(0%) +
+ + +
+ 10 分 + 1 票(50%) +
+ + + + +
+ +
+
+ 8 + 排序 + 88 + 回答数: 2 +
+ + +
平均排名: 1.48(2 票)
+ +
+ 11 + 平均排名: 1.10 · 被排次数: 2 +
+ +
+ 22 + 平均排名: 1.25 · 被排次数: 2 +
+ +
+ 33 + 平均排名: 2.10 · 被排次数: 2 +
+ + + +
+ +
+ + + + + + + +

+
+ 社区规则 | + + 找站长 +
+ 盘 + | 公告板 + | 聊天室 + | 监控板 + | 导航页 +
Powered by NekoBox | 狼ICP备 20230101 +
提问箱已成功存活: +

+ + +
+
+ + + + + diff --git a/outputs/verify/d-shot-res5.html b/outputs/verify/d-shot-res5.html new file mode 100644 index 0000000..57bd940 --- /dev/null +++ b/outputs/verify/d-shot-res5.html @@ -0,0 +1,1603 @@ + + + + + + 投票结果 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + +
+ + + +
+
+

人机验证

+

请计算下方的算术题以验证你不是机器人

+
+ 加载中... + +
+
+ +
+ +
+ + +
+
+
+ + + + + + +
+

投票结果:11

+ +
+ +
+
2
+
总参与人数
+
+ + + + +

投票结果

+
+ +
+
+ 1 + 单选 + 11 + 回答数: 2 +
+ + + + + +
+
+ 11 + 2 票(100%) +
+ +
+ +
+
+ 22 + 0 票(0%) +
+ +
+ +
+
+ 33 + 0 票(0%) +
+ +
+ + + + +
+ +
+
+ 2 + 多选 + 22 + 回答数: 2 +
+ + + + + +
+
+ 11 + 2 票(66%) +
+ +
+ +
+
+ 22 + 1 票(33%) +
+ +
+ +
+
+ 33 + 0 票(0%) +
+ +
+ + + + +
+ +
+
+ 3 + 单行文本 + 33 + 回答数: 2 +
+ + +
已填写: 2
+ + +
+
+ ▾ + 查看文本提交(2) +
+ +
+ + +
+ +
+
+ 4 + 多行文本 + 44 + 回答数: 2 +
+ + +
已填写: 2
+ + +
+
+ ▾ + 查看文本提交(2) +
+ +
+ + +
+ +
+
+ 5 + 下拉选择 + 55 + 回答数: 2 +
+ + + + + +
+
+ 11 + 2 票(100%) +
+ +
+ +
+
+ 22 + 0 票(0%) +
+ +
+ +
+
+ 33 + 0 票(0%) +
+ +
+ + + + +
+ +
+
+ 6 + 数字 + 66 + 回答数: 2 +
+ + +
平均: 4.0(2 票)
+ + +
+ +
+
+ 7 + 评分 + 77 + 回答数: 2 +
+ + +
平均分: 9.0(2 票)
+ +
+ 1 分 + 0 票(0%) +
+ + +
+ 2 分 + 0 票(0%) +
+ + +
+ 3 分 + 0 票(0%) +
+ + +
+ 4 分 + 0 票(0%) +
+ + +
+ 5 分 + 0 票(0%) +
+ + +
+ 6 分 + 0 票(0%) +
+ + +
+ 7 分 + 0 票(0%) +
+ + +
+ 8 分 + 1 票(50%) +
+ + +
+ 9 分 + 0 票(0%) +
+ + +
+ 10 分 + 1 票(50%) +
+ + + + +
+ +
+
+ 8 + 排序 + 88 + 回答数: 2 +
+ + +
平均排名: 1.48(2 票)
+ +
+ 11 + 平均排名: 1.10 · 被排次数: 2 +
+ +
+ 22 + 平均排名: 1.25 · 被排次数: 2 +
+ +
+ 33 + 平均排名: 2.10 · 被排次数: 2 +
+ + + +
+ +
+ + + + + + + +

+
+ 社区规则 | + + 找站长 +
+ 盘 + | 公告板 + | 聊天室 + | 监控板 + | 导航页 +
Powered by NekoBox | 狼ICP备 20230101 +
提问箱已成功存活: +

+ + +
+
+ + + + + diff --git a/outputs/verify/d-shot-vote.html b/outputs/verify/d-shot-vote.html new file mode 100644 index 0000000..5c6350a --- /dev/null +++ b/outputs/verify/d-shot-vote.html @@ -0,0 +1,1620 @@ + + + + + + 测试功能 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + +
+ + + +
+
+

人機認証

+

以下の計算問題を解いて、ロボットでないことを証明してください

+
+ 読み込み中... + +
+
+ +
+ +
+ + +
+
+
+ + + + + + + + + + + + + + + + + + +
+
+ + +
+ admin6 +
の投票
+
+ +
+ + + 進行中 + +
+
+ +

测试功能

+ +
+ + + + + + + + + +
匿名での参加が可能で、あなたの身元は記録されません(同一端末で最大1回)。
+ + +
+ + + +
+ + +
+
+ 1 + 単一選択 +
必填 *
+
+ + + + + + + + + + +
+ +
+
+ 2 + 複数選択 +
非必填
+
+ + + + + + + + + + + + +
+ +
+
+ 3 + 一行テキスト +
文本1
+
+ + + +
+ +
+
+ 4 + 複数行テキスト +
文本2 *
+
+ + + +
+ + + + +
+ +
+ + + +
+
+ + + + + + + + + +

+
+ ルール | + + 管理者へ +
+ ドライブ + | お知らせ + | チャット + | ステータス + | リンク +
Powered by NekoBox | 狼ICP备 20230101 +
質問箱は稼働中: +

+ + +
+
+ + + + + diff --git a/outputs/verify/d-v-9wfpVjXi73mc.html b/outputs/verify/d-v-9wfpVjXi73mc.html new file mode 100644 index 0000000..bbe548e --- /dev/null +++ b/outputs/verify/d-v-9wfpVjXi73mc.html @@ -0,0 +1,946 @@ + + + + + + 测试功能 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + +
+ + + +
+
+

人機認証

+

以下の計算問題を解いて、ロボットでないことを証明してください

+
+ 読み込み中... + +
+
+ +
+ +
+ + +
+
+
+ + + + + + + + + + + + + + + + + + +
+
+ + +
+ admin6 +
の投票
+
+ +
+ + + 進行中 + +
+
+ +

测试功能

+ +
+ + + + + + + + + +
匿名での参加が可能で、あなたの身元は記録されません(同一端末で最大1回)。
+ + +
+ + + +
+ + +
+
+ 1 + 単一選択 +
必填 *
+
+ + + + + + + + + + +
+ +
+
+ 2 + 複数選択 +
非必填
+
+ + + + + + + + + + + + +
+ +
+
+ 3 + 一行テキスト +
文本1
+
+ + + +
+ +
+
+ 4 + 複数行テキスト +
文本2 *
+
+ + + +
+ + + + +
+ +
+ + + +
+
+ + + + + + + + + +

+
+ ルール | + + 管理者へ +
+ ドライブ + | お知らせ + | チャット + | ステータス + | リンク +
Powered by NekoBox | 狼ICP备 20230101 +
質問箱は稼働中: +

+ + +
+
+ + + + + diff --git a/outputs/verify/d-v-urSsfenvRomb.html b/outputs/verify/d-v-urSsfenvRomb.html new file mode 100644 index 0000000..376dcbe --- /dev/null +++ b/outputs/verify/d-v-urSsfenvRomb.html @@ -0,0 +1,1039 @@ + + + + + + 11 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + +
+ + + +
+
+

人機認証

+

以下の計算問題を解いて、ロボットでないことを証明してください

+
+ 読み込み中... + +
+
+ +
+ +
+ + +
+
+
+ + + + + + + + + + + + + + + + + + +
+
+ + +
+ admin6 +
の投票
+
+ +
+ + + 進行中 + +
+
+ +

11

+ +
+ + + + + + + + + +
匿名での参加が可能で、あなたの身元は記録されません(同一端末で最大1回)。
+ + +
+ + + +
+ + +
+
+ 1 + 単一選択 +
11
+
+ + + + + + + + + + +
+ +
+
+ 2 + 複数選択 +
22 *
+
+ + + + + + + + + + +
+ +
+
+ 3 + 一行テキスト +
33 *
+
+ + + +
+ +
+
+ 4 + 複数行テキスト +
44 *
+
+ + + +
+ +
+
+ 5 + ドロップダウン選択 +
55 *
+
+ + + + +
+ +
+
+ 6 + 数値 +
66 *
+
+ + + +
+ +
+
+ 7 + 評価 +
77 *
+
+ + + +
+ +
+
+ 8 + 順位 +
88 *
+
+ +
+ 順位 1-N(必須) +
+ + +
+ 11 + +
+ +
+ 22 + +
+ +
+ 33 + +
+ + +
+ + + + +
+ +
+ + + +
+
+ + + + + + + + + +

+
+ ルール | + + 管理者へ +
+ ドライブ + | お知らせ + | チャット + | ステータス + | リンク +
Powered by NekoBox | 狼ICP备 20230101 +
質問箱は稼働中: +

+ + +
+
+ + + + + diff --git a/outputs/verify/d-v-z9F8mIqPtDOA.html b/outputs/verify/d-v-z9F8mIqPtDOA.html new file mode 100644 index 0000000..e2135ec --- /dev/null +++ b/outputs/verify/d-v-z9F8mIqPtDOA.html @@ -0,0 +1,924 @@ + + + + + + 测试4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + +
+ + + +
+
+

人機認証

+

以下の計算問題を解いて、ロボットでないことを証明してください

+
+ 読み込み中... + +
+
+ +
+ +
+ + +
+
+
+ + + + + + + + + + + + + + + + + + +
+
+ + +
+ admin6 +
の投票
+
+ +
+ + + 進行中 + +
+
+ +

测试4

+ +
+ + + + + + + + + +
匿名での参加が可能で、あなたの身元は記録されません(同一端末で最大1回)。
+ + +
+ + + +
+ + +
+
+ 1 + 単一選択 +
这里显示第一题
+
+ + + + + + + + + + + + + + +
+ +
+
+ 2 + 単一選択 +
11
+
+ + + + + + + + +
+ + + + +
+ +
+ + + +
+
+ + + + + + + + + +

+
+ ルール | + + 管理者へ +
+ ドライブ + | お知らせ + | チャット + | ステータス + | リンク +
Powered by NekoBox | 狼ICP备 20230101 +
質問箱は稼働中: +

+ + +
+
+ + + + + diff --git a/outputs/verify/h-prev-10.txt b/outputs/verify/h-prev-10.txt new file mode 100644 index 0000000..aabbdcb --- /dev/null +++ b/outputs/verify/h-prev-10.txt @@ -0,0 +1,22 @@ +HTTP/1.1 200 Connection Established + +HTTP/1.1 200 OK +Date: Mon, 19 Oct 2026 04:50:43 GMT +Content-Type: text/html; charset=utf-8 +Transfer-Encoding: chunked +Connection: keep-alive +Speculation-Rules: "/cdn-cgi/speculation" +referrer-policy: strict-origin-when-cross-origin +strict-transport-security: max-age=31536000; includeSubDomains +trace-id: 00000000000000000000000000000000 +vary: Accept-Encoding +x-content-type-options: nosniff +x-frame-options: DENY +x-xss-protection: 1; mode=block +cf-cache-status: DYNAMIC +Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} +Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=LNNyS3fb%2BRQ4lDSreatYe3z7vYXJ5DBvFEY39WJc%2B8HZ1NjXYKxVWdDWaiWMQ%2B%2FvlEA0T2DYuliVVbPWsERm5o1d9QfSG0wMX8hF5b0XBHoxociB8pPyH1LZDRlllEINsKtHcLfseGtFGQr58g%3D%3D"}]} +Server: cloudflare +CF-RAY: a4cd29d93d591ca5-AMS +alt-svc: h3=":443"; ma=86400 + diff --git a/outputs/verify/h-prev-11.txt b/outputs/verify/h-prev-11.txt new file mode 100644 index 0000000..ba16ee7 --- /dev/null +++ b/outputs/verify/h-prev-11.txt @@ -0,0 +1,22 @@ +HTTP/1.1 200 Connection Established + +HTTP/1.1 200 OK +Date: Mon, 19 Oct 2026 04:50:47 GMT +Content-Type: text/html; charset=utf-8 +Transfer-Encoding: chunked +Connection: keep-alive +Speculation-Rules: "/cdn-cgi/speculation" +referrer-policy: strict-origin-when-cross-origin +strict-transport-security: max-age=31536000; includeSubDomains +trace-id: 00000000000000000000000000000000 +vary: Accept-Encoding +x-content-type-options: nosniff +x-frame-options: DENY +x-xss-protection: 1; mode=block +cf-cache-status: DYNAMIC +Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} +Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=s9ceWO0ESedGa5u0nddaXW07CHQrtIc9OIdjAPsRo7ngWVNAPIIdFbgiTZR5%2Bl3u2H9LTwdWSKOYpb1U90XF09z4x88W0xbDzjFZio9EwdShj1rTkK3E6WboqRk75JfBct3otKwp8vRlSed8vg%3D%3D"}]} +Server: cloudflare +CF-RAY: a4cd29f74dae1af9-AMS +alt-svc: h3=":443"; ma=86400 + diff --git a/outputs/verify/h3.txt b/outputs/verify/h3.txt new file mode 100644 index 0000000..7a93462 --- /dev/null +++ b/outputs/verify/h3.txt @@ -0,0 +1,21 @@ +HTTP/1.1 200 Connection Established + +HTTP/1.1 302 Found +Date: Mon, 19 Oct 2026 04:48:53 GMT +Content-Length: 0 +Connection: keep-alive +location: / +referrer-policy: strict-origin-when-cross-origin +Set-Cookie: flamego_session=xzuo76g1wkt93yde; Path=/; HttpOnly; Secure; SameSite=Lax +strict-transport-security: max-age=31536000; includeSubDomains +trace-id: 00000000000000000000000000000000 +x-content-type-options: nosniff +x-frame-options: DENY +x-xss-protection: 1; mode=block +cf-cache-status: DYNAMIC +Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} +Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=4lN%2FjpiiXhqAFEa04LgpyUx1T6gq6As3ND39p0U%2B%2B%2BnV18I8OPf%2B3C4P1hF2ZbvemgZD3yKBN0KGjMXH3MKidzjjBRg0UN0glf5LWn9QPaATLorzaAcF3cY3eMFsZ%2BFA3GJVVI%2FAv4v2dquZoA%3D%3D"}]} +Server: cloudflare +CF-RAY: a4cd27291b28a007-AMS +alt-svc: h3=":443"; ma=86400 + diff --git a/outputs/verify/l3.html b/outputs/verify/l3.html new file mode 100644 index 0000000..912ffa8 --- /dev/null +++ b/outputs/verify/l3.html @@ -0,0 +1,638 @@ + + + + + + 狼の質問箱 | 匿名質問箱 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + +
+ + + +
+
+

人機認証

+

以下の計算問題を解いて、ロボットでないことを証明してください

+
+ 読み込み中... + +
+
+ +
+ +
+ + +
+
+
+ + + + + +
+
+ + ログイン + + + + + + + + + +
+ + +
+
+ + +
+
+ + + + + パスワードを忘れた + +
+
+
+

+
+ ルール | + + 管理者へ +
+ ドライブ + | お知らせ + | チャット + | ステータス + | リンク +
Powered by NekoBox | 狼ICP备 20230101 +
質問箱は稼働中: +

+ + +
+
+ + + + + diff --git a/outputs/verify/live-mobile.html b/outputs/verify/live-mobile.html new file mode 100644 index 0000000..938573b --- /dev/null +++ b/outputs/verify/live-mobile.html @@ -0,0 +1,936 @@ + + + + + 狼の質問箱 | 匿名質問箱 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + +
+ + + +
+
+

人機認証

+

以下の計算問題を解いて、ロボットでないことを証明してください

+
+ 読み込み中... + +
+
+ +
+ +
+ + +
+
+
+ + + + + + + +
+

狼の質問箱 | 匿名質問箱

+

+ 一个无需注册即可提问的匿名提问箱。不用下载 App,不用绑定社交账号,连邮箱都不必是真的。 +

+ +
+ +
+
+

匿名也能问

+

不需要账号、不需要真实邮箱,打开就能写。想收到回复再留邮箱即可。

+
+
+

不止一问一答

+

问题二维码、私聊、投票、追加提问、Markdown 排版,原版没有的这里都有。

+
+
+

箱子归你

+

头像、背景、二维码配色都能自己调,还能设置审核严格度和收集时段。

+
+
+ +
+

域名迁移

+

+ 本站已切换到 box.shiroko.one,原域名将在 2026 年 12 月 24 日到期, + 期间通过 301 转发指向新域名,到期后旧域名将彻底不再使用。 +

+

+ 审核服务已切换到阿里云,审核速度提升且误伤更少;同时允许用户调整审核严格度,在不碰底线的前提下 + 可以设置不接受辱骂、广告等信息(即「不听坏话模式」)。 +

+
+ +
+

一点提醒

+
    +
  • 本站唯一指定认证站长:小狼,注意不要被骗。
  • +
  • 本站提倡友好、友善的发言,请遵守社区规则。无论匿名与否,都要为自己的发言负责。
  • +
  • 不允许垃圾信息、广告、辱骂、涉政、暴恐、色情、违禁、无意义、灌水提问,涉政或不良言论将直接删除。
  • +
+

+ 想先试试功能,可以到站长的提问箱 + 小狼的提问箱 提问或提意见。 +

+
+ +
+ 近期更新 +
    +
  • 2026.9 新增审核严格度选项,增加提问、投票、私聊的总开关,可设置定时收集、定量收集或关闭功能,前后端显示也更易用了。
  • +
  • 2026.8 新增投票功能、追加提问、多语言、Markdown 显示、置顶显示等,详情见这里。
  • +
  • 2026.8 放弃使用 recaptcha 等对国内用户不友好的验证方式,更新密码加密方式,修复图片上传等一系列问题。
  • +
  • 2026.5 增加私聊功能,可连续对话,详见介绍;另有提问箱卡片分享。
  • +
  • 2026.2 新增用户展示和标签功能,可给自己贴 5 个标签并选择是否在探索页展示,详情见狼的公告板。
  • +
+
+ +
+

質問箱の統計

+
+
+ 28 + 注册用户 +
+
+ 94 + 累计提问 +
+
+ 21 + 已获回答 +
+
+

+ 最后提问:2026-10-19 00:58:03
+ 最后回答:2026-10-19 02:44:47
+ 数据更新于:2026-10-19 12:48:26 +

+
+ +
  • 哇哦
    0
    哇哦
  • prpr
    0
    prpr
  • 流汗
    0
    流汗
  • 生气
    0
    生气
  • 流鼻血
    0
    流鼻血
  • あべ
    0
    あべ
评论
  • 按正序
  • 按倒序
  • 按热度
来发评论吧~
+ +

+
+ ルール | + + 管理者へ +
+ ドライブ + | お知らせ + | チャット + | ステータス + | リンク +
Powered by NekoBox | 狼ICP备 20230101 +
質問箱は稼働中:3年 110天 18时 44分 45秒 +

+ + +
+
+ + + + + diff --git a/outputs/verify/pm-home.html b/outputs/verify/pm-home.html new file mode 100644 index 0000000..9a8c3d7 --- /dev/null +++ b/outputs/verify/pm-home.html @@ -0,0 +1,1629 @@ + + + + + + 狼の質問箱 | 匿名質問箱 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + +
+ + + +
+
+

人機認証

+

以下の計算問題を解いて、ロボットでないことを証明してください

+
+ 読み込み中... + +
+
+ +
+ +
+ + +
+
+
+ + + + + + + +
+

狼の質問箱 | 匿名質問箱

+

+ 一个无需注册即可提问的匿名提问箱。不用下载 App,不用绑定社交账号,连邮箱都不必是真的。 +

+ +
+ +
+
+

匿名也能问

+

不需要账号、不需要真实邮箱,打开就能写。想收到回复再留邮箱即可。

+
+
+

不止一问一答

+

问题二维码、私聊、投票、追加提问、Markdown 排版,原版没有的这里都有。

+
+
+

箱子归你

+

头像、背景、二维码配色都能自己调,还能设置审核严格度和收集时段。

+
+
+ +
+

域名迁移

+

+ 本站已切换到 box.shiroko.one,原域名将在 2026 年 12 月 24 日到期, + 期间通过 301 转发指向新域名,到期后旧域名将彻底不再使用。 +

+

+ 审核服务已切换到阿里云,审核速度提升且误伤更少;同时允许用户调整审核严格度,在不碰底线的前提下 + 可以设置不接受辱骂、广告等信息(即「不听坏话模式」)。 +

+
+ +
+

一点提醒

+
    +
  • 本站唯一指定认证站长:小狼,注意不要被骗。
  • +
  • 本站提倡友好、友善的发言,请遵守社区规则。无论匿名与否,都要为自己的发言负责。
  • +
  • 不允许垃圾信息、广告、辱骂、涉政、暴恐、色情、违禁、无意义、灌水提问,涉政或不良言论将直接删除。
  • +
+

+ 想先试试功能,可以到站长的提问箱 + 小狼的提问箱 提问或提意见。 +

+
+ +
+ 近期更新 +
    +
  • 2026.9 新增审核严格度选项,增加提问、投票、私聊的总开关,可设置定时收集、定量收集或关闭功能,前后端显示也更易用了。
  • +
  • 2026.8 新增投票功能、追加提问、多语言、Markdown 显示、置顶显示等,详情见这里。
  • +
  • 2026.8 放弃使用 recaptcha 等对国内用户不友好的验证方式,更新密码加密方式,修复图片上传等一系列问题。
  • +
  • 2026.5 增加私聊功能,可连续对话,详见介绍;另有提问箱卡片分享。
  • +
  • 2026.2 新增用户展示和标签功能,可给自己贴 5 个标签并选择是否在探索页展示,详情见狼的公告板。
  • +
+
+ +
+

質問箱の統計

+
+
+ 28 + 注册用户 +
+
+ 94 + 累计提问 +
+
+ 21 + 已获回答 +
+
+

+ 最后提问:2026-10-19 00:58:03
+ 最后回答:2026-10-19 02:44:47
+ 数据更新于:2026-10-19 12:48:26 +

+
+ +
+ +

+
+ ルール | + + 管理者へ +
+ ドライブ + | お知らせ + | チャット + | ステータス + | リンク +
Powered by NekoBox | 狼ICP备 20230101 +
質問箱は稼働中: +

+ + +
+
+ + +
+ + + diff --git a/outputs/verify/pm-results.html b/outputs/verify/pm-results.html new file mode 100644 index 0000000..e5c7755 --- /dev/null +++ b/outputs/verify/pm-results.html @@ -0,0 +1,1621 @@ + + + + + + 投票结果 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + +
+ + + +
+
+

人机验证

+

请计算下方的算术题以验证你不是机器人

+
+ 加载中... + +
+
+ +
+ +
+ + +
+
+
+ + + + + + +
+

投票结果:11

+ +
+ +
+
2
+
总参与人数
+
+ + + + +

投票结果

+
+ +
+
+ 1 + 单选 + 11 + 回答数: 2 +
+ + + + + +
+
+ 11 + 2 票(100%) +
+ +
+ +
+
+ 22 + 0 票(0%) +
+ +
+ +
+
+ 33 + 0 票(0%) +
+ +
+ + + + +
+ +
+
+ 2 + 多选 + 22 + 回答数: 2 +
+ + + + + +
+
+ 11 + 2 票(66%) +
+ +
+ +
+
+ 22 + 1 票(33%) +
+ +
+ +
+
+ 33 + 0 票(0%) +
+ +
+ + + + +
+ +
+
+ 3 + 单行文本 + 33 + 回答数: 2 +
+ + +
已填写: 2
+ + +
+
+ ▾ + 查看文本提交(2) +
+ +
+ + +
+ +
+
+ 4 + 多行文本 + 44 + 回答数: 2 +
+ + +
已填写: 2
+ + +
+
+ ▾ + 查看文本提交(2) +
+ +
+ + +
+ +
+
+ 5 + 下拉选择 + 55 + 回答数: 2 +
+ + + + + +
+
+ 11 + 2 票(100%) +
+ +
+ +
+
+ 22 + 0 票(0%) +
+ +
+ +
+
+ 33 + 0 票(0%) +
+ +
+ + + + +
+ +
+
+ 6 + 数字 + 66 + 回答数: 2 +
+ + +
平均: 4.0(2 票)
+ + +
+ +
+
+ 7 + 评分 + 77 + 回答数: 2 +
+ + +
平均分: 9.0(2 票)
+ +
+ 1 分 + 0 票(0%) +
+ + +
+ 2 分 + 0 票(0%) +
+ + +
+ 3 分 + 0 票(0%) +
+ + +
+ 4 分 + 0 票(0%) +
+ + +
+ 5 分 + 0 票(0%) +
+ + +
+ 6 分 + 0 票(0%) +
+ + +
+ 7 分 + 0 票(0%) +
+ + +
+ 8 分 + 1 票(50%) +
+ + +
+ 9 分 + 0 票(0%) +
+ + +
+ 10 分 + 1 票(50%) +
+ + + + +
+ +
+
+ 8 + 排序 + 88 + 回答数: 2 +
+ + +
平均排名: 1.48(2 票)
+ +
+ 11 + 平均排名: 1.10 · 被排次数: 2 +
+ +
+ 22 + 平均排名: 1.25 · 被排次数: 2 +
+ +
+ 33 + 平均排名: 2.10 · 被排次数: 2 +
+ + + +
+ +
+ + + + + + + +

+
+ 社区规则 | + + 找站长 +
+ 盘 + | 公告板 + | 聊天室 + | 监控板 + | 导航页 +
Powered by NekoBox | 狼ICP备 20230101 +
提问箱已成功存活: +

+ + +
+
+ + +
+ + + + diff --git a/outputs/verify/probe-mobile.html b/outputs/verify/probe-mobile.html new file mode 100644 index 0000000..3ff28a8 --- /dev/null +++ b/outputs/verify/probe-mobile.html @@ -0,0 +1,1635 @@ + + + + + + 狼の質問箱 | 匿名質問箱 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + +
+ + + +
+
+

人機認証

+

以下の計算問題を解いて、ロボットでないことを証明してください

+
+ 読み込み中... + +
+
+ +
+ +
+ + +
+
+
+ + + + + + + +
+

狼の質問箱 | 匿名質問箱

+

+ 一个无需注册即可提问的匿名提问箱。不用下载 App,不用绑定社交账号,连邮箱都不必是真的。 +

+ +
+ +
+
+

匿名也能问

+

不需要账号、不需要真实邮箱,打开就能写。想收到回复再留邮箱即可。

+
+
+

不止一问一答

+

问题二维码、私聊、投票、追加提问、Markdown 排版,原版没有的这里都有。

+
+
+

箱子归你

+

头像、背景、二维码配色都能自己调,还能设置审核严格度和收集时段。

+
+
+ +
+

域名迁移

+

+ 本站已切换到 box.shiroko.one,原域名将在 2026 年 12 月 24 日到期, + 期间通过 301 转发指向新域名,到期后旧域名将彻底不再使用。 +

+

+ 审核服务已切换到阿里云,审核速度提升且误伤更少;同时允许用户调整审核严格度,在不碰底线的前提下 + 可以设置不接受辱骂、广告等信息(即「不听坏话模式」)。 +

+
+ +
+

一点提醒

+
    +
  • 本站唯一指定认证站长:小狼,注意不要被骗。
  • +
  • 本站提倡友好、友善的发言,请遵守社区规则。无论匿名与否,都要为自己的发言负责。
  • +
  • 不允许垃圾信息、广告、辱骂、涉政、暴恐、色情、违禁、无意义、灌水提问,涉政或不良言论将直接删除。
  • +
+

+ 想先试试功能,可以到站长的提问箱 + 小狼的提问箱 提问或提意见。 +

+
+ +
+ 近期更新 +
    +
  • 2026.9 新增审核严格度选项,增加提问、投票、私聊的总开关,可设置定时收集、定量收集或关闭功能,前后端显示也更易用了。
  • +
  • 2026.8 新增投票功能、追加提问、多语言、Markdown 显示、置顶显示等,详情见这里。
  • +
  • 2026.8 放弃使用 recaptcha 等对国内用户不友好的验证方式,更新密码加密方式,修复图片上传等一系列问题。
  • +
  • 2026.5 增加私聊功能,可连续对话,详见介绍;另有提问箱卡片分享。
  • +
  • 2026.2 新增用户展示和标签功能,可给自己贴 5 个标签并选择是否在探索页展示,详情见狼的公告板。
  • +
+
+ +
+

質問箱の統計

+
+
+ 28 + 注册用户 +
+
+ 94 + 累计提问 +
+
+ 21 + 已获回答 +
+
+

+ 最后提问:2026-10-19 00:58:03
+ 最后回答:2026-10-19 02:44:47
+ 数据更新于:2026-10-19 12:48:26 +

+
+ +
+ +

+
+ ルール | + + 管理者へ +
+ ドライブ + | お知らせ + | チャット + | ステータス + | リンク +
Powered by NekoBox | 狼ICP备 20230101 +
質問箱は稼働中: +

+ + +
+
+ + +
+ + + diff --git a/route/question/page.go b/route/question/page.go index 9e6948a..8a85273 100644 --- a/route/question/page.go +++ b/route/question/page.go @@ -40,6 +40,7 @@ import ( "github.com/tamakyi/TamaBox/internal/shared" "github.com/tamakyi/TamaBox/internal/storage" templatepkg "github.com/tamakyi/TamaBox/internal/template" + "github.com/tamakyi/TamaBox/internal/theme" ) func Pager(ctx context.Context) { @@ -76,6 +77,10 @@ func Pager(ctx context.Context) { ctx.Data["LangCode"] = lang } + // 主题只作用于箱主相关页面(提问箱主页 / 单题页 / 密码页 / 分享卡路由), + // 与背景图、颜色同属箱主个性化配置;/user 后台保持中性外观。 + // 空串 = 不加载主题 css(站点默认就是基础外观时省掉一次请求)。 + ctx.Data["ThemeSlug"] = theme.Resolve(pageUser.Theme) // 页面密码检查 if pageUser.PagePassword != "" && !(ctx.IsLogged && ctx.User.ID == pageUser.ID) { passKey := fmt.Sprintf("page_pass_%d", pageUser.ID) diff --git a/route/user/profile.go b/route/user/profile.go index 03cd6cb..eeba127 100644 --- a/route/user/profile.go +++ b/route/user/profile.go @@ -9,6 +9,7 @@ import ( "fmt" "net/url" "strconv" + "strings" "time" "github.com/pkg/errors" @@ -22,6 +23,7 @@ import ( "github.com/tamakyi/TamaBox/internal/i18n" "github.com/tamakyi/TamaBox/internal/imagecheck" "github.com/tamakyi/TamaBox/internal/storage" + "github.com/tamakyi/TamaBox/internal/theme" ) // Profile 账号设置页。按面板分区: @@ -39,6 +41,14 @@ func Profile(ctx context.Context) { ctx.Data["StrictnessLocked"] = true } ctx.Data["TagLimits"] = tagLimits() + // 主题:可选清单(顺序同 [theme] available)+ 当前选择。 + // 当前值取原始存储值,非法/已下线主题归零为「跟随站点默认」,避免单选一个都不选中。 + ctx.Data["Themes"] = theme.List() + if theme.IsAvailable(ctx.User.Theme) { + ctx.Data["CurrentTheme"] = strings.ToLower(strings.TrimSpace(ctx.User.Theme)) + } else { + ctx.Data["CurrentTheme"] = "" + } ctx.Data["ChatLimits"] = chatLimits() InjectFeatureViews(ctx) ctx.Success("user/profile") @@ -64,6 +74,34 @@ func chatLimits() map[string]int { } } +// UpdateTheme 保存箱主选择的主题。只写 users.theme 一列, +// 与背景图/颜色等个性化设置互不影响(各自独立的表单入口)。 +func UpdateTheme(ctx context.Context, f form.UpdateProfileTheme) { + if ctx.HasError() { + ctx.Success("user/profile") + return + } + + slug := strings.ToLower(strings.TrimSpace(f.Theme)) + // 白名单校验:既挡住拼写错误,也挡住把任意字符串拼进 的路径穿越尝试。 + // 用 flash + 重定向而不是原地渲染:本页数据由 Profile() 装配(可选主题清单等), + // 直接 Success 会渲染出缺数据的页面。 + if slug != "" && !theme.IsAvailable(slug) { + ctx.SetErrorFlash(i18n.T(ctx.Lang(), "profile.theme.invalid")) + ctx.Redirect("/user/profile") + return + } + + if err := db.Users.UpdateTheme(ctx.Request().Context(), ctx.User.ID, slug); err != nil { + logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to update theme") + ctx.SetInternalErrorFlash() + ctx.Redirect("/user/profile") + return + } + ctx.SetSuccessFlash(i18n.T(ctx.Lang(), "profile.theme.saved")) + ctx.Redirect("/user/profile") +} + func ProfileAPI(ctx context.Context) error { return ctx.JSON(ctx.User) } diff --git a/static/css/base.css b/static/css/base.css index c5d2a97..2e801ab 100644 --- a/static/css/base.css +++ b/static/css/base.css @@ -23,6 +23,10 @@ --tb-shadow: 0 1px 2px rgba(16, 24, 40, 0.04), 0 12px 28px -18px rgba(16, 24, 40, 0.20); --tb-shadow-hover: 0 1px 2px rgba(16, 24, 40, 0.05), 0 18px 40px -20px rgba(16, 24, 40, 0.28); --tb-font: system-ui, -apple-system, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Noto Sans CJK SC", "Microsoft YaHei", sans-serif; + /* 整页背景:主题可覆盖。图案层叠在背景图之上,两者都不用时保持纯色底。 */ + --tb-page-image: none; + --tb-page-pattern: none; + --tb-page-pattern-size: auto; } html[data-theme="dark"] { --tb-bg: #1a1d23; @@ -260,6 +264,12 @@ body { font-family: var(--tb-font); background-color: var(--tb-bg); + /* 主题的整页背景:图案(平铺)在上,图片(cover)在下,都不设置时等价于纯色 */ + background-image: var(--tb-page-pattern, none), var(--tb-page-image, none); + background-size: var(--tb-page-pattern-size, auto), cover; + background-repeat: repeat, no-repeat; + background-position: left top, center center; + background-attachment: scroll, fixed; color: var(--tb-text); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; @@ -327,6 +337,89 @@ color: var(--tb-text-muted); } +/* 主题选择器(设置页):色块 + 名称。纯展示,不参与主题令牌,所以放在基础层。 */ +.tb-theme-hint { + margin: 0 0 6px; + font-size: 0.8rem; + color: var(--tb-text-muted); +} +.tb-theme-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); + gap: 8px; + margin: 8px 0 2px; +} +.tb-theme-opt { + display: flex; + align-items: center; + gap: 8px; + padding: 7px 9px; + border: 1px solid var(--tb-border); + border-radius: var(--tb-radius-sm); + cursor: pointer; +} +.tb-theme-opt:hover { + background-color: var(--tb-surface-hover); +} +.tb-theme-opt input { + margin: 0; +} +.tb-theme-swatch { + flex: 0 0 auto; + width: 26px; + height: 26px; + border-radius: var(--tb-radius-xs); + border: 1px solid var(--tb-border); + background-color: var(--tb-surface-2); +} +/* 选中态用描边而不是 :has(),兼容老 WebView */ +.tb-theme-opt input:checked + .tb-theme-swatch { + outline: 2px solid var(--tb-primary); + outline-offset: 1px; +} +.tb-theme-name { + font-size: 0.84rem; + line-height: 1.3; +} +.tb-theme-opt input:checked ~ .tb-theme-name { + color: var(--tb-primary); +} + +/* UIkit 组件跟随主题色:UIkit 3.3.3 的主色是编译期常量(#1e87f0), + CSS 变量注入不进去,只能按组件覆盖。基础主题的 --tb-primary 恰好就是 #1e87f0, + 所以这段对默认外观零改动,只是让箱主主题能贯穿到按钮/链接/勾选。 */ +a, .uk-link { + color: var(--tb-primary); +} +a:hover, .uk-link:hover { + color: var(--tb-primary); +} +.uk-button-primary { + background-color: var(--tb-primary); +} +.uk-button-primary:hover, +.uk-button-primary:focus { + background-color: var(--tb-primary); + filter: brightness(1.07); +} +.uk-checkbox:checked, +.uk-checkbox:indeterminate, +.uk-radio:checked { + background-color: var(--tb-primary); + border-color: transparent; +} +.uk-input:focus, +.uk-select:focus, +.uk-textarea:focus { + border-color: var(--tb-primary); +} +.uk-tab > .uk-active > a { + border-color: var(--tb-primary); +} +.uk-text-primary { + color: var(--tb-primary) !important; +} + /* 轻量胶囊:用于投票类型、热门标签等「只是分类信息」的位置。 实心 uk-label 一屏多个会变成一排色块,这里统一降级为浅底 + 次要色文字。 */ .tb-chip { diff --git a/static/css/themes/_scaffold.css b/static/css/themes/_scaffold.css new file mode 100644 index 0000000..25d1635 --- /dev/null +++ b/static/css/themes/_scaffold.css @@ -0,0 +1,110 @@ +/* ============================================================================ + 主题底稿(scaffold)—— 复制本文件改名为 .css 即可新增一套主题。 + 文件名即 slug:只允许小写字母、数字、连字符(如 sakura / ocean-mist)。 + 新增后需在 conf/app.ini 登记: + + [theme] + default = default + available = default,sakura,ocean-mist + + ⚠️ 硬规则(违反会导致深色模式错乱或整页错位): + 1. 两个块都必须写:浅色「html[data-theme-style="SLUG"]」 + 与深色「html[data-theme="dark"][data-theme-style="SLUG"]」。 + 只写浅色块会连深色模式一起覆盖(同特异性 + 本文件在 base.css 之后加载)。 + 2. 只改令牌(下方变量)与「组件微调」段。不要改布局:宽高、margin、padding、 + display / flex / grid、position 一律不动 —— 主题不是换皮模板。 + 3. 本文件不参与构建,仅作参考;真正生效的是同目录下的 .css。 + + ── 可用的 19 个令牌 ────────────────────────────────────────────── + 底色层 --tb-bg 页面底色(html/body) + --tb-surface 卡片、输入框等前景面板 + --tb-surface-2 次级面(标签底、进度条轨道、代码块) + --tb-surface-hover 悬停底(列表行、下拉项) + 线条 --tb-border 边框、分隔线 + 文字 --tb-text 正文 + --tb-text-secondary 次要文字(说明、meta) + --tb-text-muted 弱化文字(占位、时间戳) + 强调 --tb-primary 主色:链接、主按钮、进度条 + --tb-primary-weak 主色 10% 左右的浅底(选中态、标签) + 形状 --tb-radius 卡片 12px / --tb-radius-sm 8px / --tb-radius-xs 6px + 阴影 --tb-shadow 卡片默认 / --tb-shadow-hover 悬停 + 字体 --tb-font 字体栈(一般不改) + 页面背景 --tb-page-image 整页背景图(cover,fixed) + --tb-page-pattern 整页图案(平铺,可叠加在背景图之上) + --tb-page-pattern-size 图案平铺尺寸(默认 auto) + + ── 素材规格(放 R2,不需要重新编译) ───────────────────────────── + 页面背景图 2400×1600(3:2)或 2560×1440,WebP q80,≤300KB;明暗各一张: + https://r2.337845818.best/tamabox/themes//bg-light.webp + https://r2.337845818.best/tamabox/themes//bg-dark.webp + 注意:背景图上还要叠卡片和文字,主题要保证「--tb-surface 不透明」 + 或足够对比;照片类背景建议配 10%~20% 的同色半透明遮罩。 + 平铺图案 200×200 或 400×400 可无缝平铺,PNG/WebP,≤40KB; + 建议透明度 ≤15%,否则会和正文抢注意力。 + 设置页色块 由本文件浅色块的 --tb-bg 与 --tb-primary 自动生成,无需额外素材。 + ============================================================================ */ + +/* 1) 浅色(必须)—— 选择器里的 SLUG 换成文件名 */ +html[data-theme-style="SLUG"] { + --tb-bg: #f7f8fa; + --tb-surface: #ffffff; + --tb-surface-2: #f4f6f8; + --tb-surface-hover: #f3f7ff; + --tb-border: #e8eaee; + + --tb-text: #1f2329; + --tb-text-secondary: #5c6370; + --tb-text-muted: #8a919c; + + --tb-primary: #1e87f0; + --tb-primary-weak: rgba(30, 135, 240, 0.10); + + --tb-radius: 12px; + --tb-radius-sm: 8px; + --tb-radius-xs: 6px; + + --tb-shadow: 0 1px 2px rgba(16, 24, 40, 0.04), 0 12px 28px -18px rgba(16, 24, 40, 0.20); + --tb-shadow-hover: 0 1px 2px rgba(16, 24, 40, 0.05), 0 18px 40px -20px rgba(16, 24, 40, 0.28); + + --tb-font: system-ui, -apple-system, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Noto Sans CJK SC", "Microsoft YaHei", sans-serif; + + /* 整页背景:两者可同时用,图案叠在图片之上;不需要就保持 none */ + --tb-page-image: none; + --tb-page-pattern: none; + --tb-page-pattern-size: auto; +} + +/* 2) 深色(必须)—— 深浅两套必须成对,缺一段会让深色用户看到「浅色的深色主题」 */ +html[data-theme="dark"][data-theme-style="SLUG"] { + --tb-bg: #1a1d23; + --tb-surface: #242830; + --tb-surface-2: #2d323c; + --tb-surface-hover: rgba(92, 141, 255, 0.12); + --tb-border: #3a404c; + + --tb-text: #d8dce4; + --tb-text-secondary: #a8b0bd; + --tb-text-muted: #8b93a1; + + --tb-primary: #5c8dff; + --tb-primary-weak: rgba(92, 141, 255, 0.14); + + --tb-shadow: 0 1px 2px rgba(0, 0, 0, 0.30), 0 12px 28px -18px rgba(0, 0, 0, 0.55); + --tb-shadow-hover: 0 1px 2px rgba(0, 0, 0, 0.35), 0 18px 40px -20px rgba(0, 0, 0, 0.65); + + --tb-page-image: none; + --tb-page-pattern: none; + --tb-page-pattern-size: auto; +} + +/* 3) 组件微调(可选,默认整段注释掉) + 只允许「外观」层面的补充:边框、阴影、封面遮罩浓度等。 + 禁止改尺寸/间距/定位 —— 那属于布局,主题不管。 */ +/* +html[data-theme-style="SLUG"] .uk-card-default { + border-color: transparent; +} +html[data-theme-style="SLUG"] .tb-pcard { + box-shadow: var(--tb-shadow); +} +*/ diff --git a/static/css/themes/default.css b/static/css/themes/default.css new file mode 100644 index 0000000..de06166 --- /dev/null +++ b/static/css/themes/default.css @@ -0,0 +1,13 @@ +/* 基础外观(default):与 base.css 完全一致,不覆盖任何令牌。 + 它的存在只为让 [theme] 里可以显式写 default(= 不使用主题)。 + 实际渲染时不会加载本文件 —— internal/theme.Resolve 对 default 返回空串, + 省掉一次静态请求;下方令牌仅用于设置页色块预览。 */ +html[data-theme-style="default"] { + --tb-bg: #f7f8fa; + --tb-primary: #1e87f0; +} + +html[data-theme="dark"][data-theme-style="default"] { + --tb-bg: #1a1d23; + --tb-primary: #5c8dff; +} diff --git a/static/css/themes/sakura.css b/static/css/themes/sakura.css new file mode 100644 index 0000000..0350ba0 --- /dev/null +++ b/static/css/themes/sakura.css @@ -0,0 +1,45 @@ +/* 樱花(sakura):粉调 + 细网点图案,纯 CSS 实现,不依赖图片素材。 + 复制 _scaffold.css 写的示例:只覆盖令牌,未改动任何布局。 */ +html[data-theme-style="sakura"] { + --tb-bg: #fff6f8; + --tb-surface: #ffffff; + --tb-surface-2: #fdeef2; + --tb-surface-hover: #fdf1f5; + --tb-border: #f2dde4; + + --tb-text: #3a2b30; + --tb-text-secondary: #6b545c; + --tb-text-muted: #a08c94; + + --tb-primary: #e5678f; + --tb-primary-weak: rgba(229, 103, 143, 0.12); + + --tb-shadow: 0 1px 2px rgba(120, 60, 80, 0.05), 0 12px 28px -18px rgba(120, 60, 80, 0.22); + --tb-shadow-hover: 0 1px 2px rgba(120, 60, 80, 0.07), 0 18px 40px -20px rgba(120, 60, 80, 0.30); + + --tb-page-image: none; + --tb-page-pattern: radial-gradient(circle at 1px 1px, rgba(229, 103, 143, 0.14) 1px, transparent 0); + --tb-page-pattern-size: 16px 16px; +} + +html[data-theme="dark"][data-theme-style="sakura"] { + --tb-bg: #241a1f; + --tb-surface: #2e2129; + --tb-surface-2: #3a2a33; + --tb-surface-hover: rgba(255, 138, 176, 0.10); + --tb-border: #49333d; + + --tb-text: #f2e4e9; + --tb-text-secondary: #c6aab4; + --tb-text-muted: #9d818b; + + --tb-primary: #ff8ab0; + --tb-primary-weak: rgba(255, 138, 176, 0.16); + + --tb-shadow: 0 1px 2px rgba(0, 0, 0, 0.34), 0 12px 28px -18px rgba(0, 0, 0, 0.58); + --tb-shadow-hover: 0 1px 2px rgba(0, 0, 0, 0.40), 0 18px 40px -20px rgba(0, 0, 0, 0.68); + + --tb-page-image: none; + --tb-page-pattern: radial-gradient(circle at 1px 1px, rgba(255, 138, 176, 0.10) 1px, transparent 0); + --tb-page-pattern-size: 16px 16px; +} diff --git a/static/fs.go b/static/fs.go index bc62480..08b1fff 100644 --- a/static/fs.go +++ b/static/fs.go @@ -8,5 +8,5 @@ import ( "embed" ) -//go:embed favicon.ico sitemap.xml llms.txt llms-full.txt robots.txt tamabox.changelogs.json index-now/66e5e0911b0941f2ba7dbfbbd961e9be.txt css/base.css +//go:embed favicon.ico sitemap.xml llms.txt llms-full.txt robots.txt tamabox.changelogs.json index-now/66e5e0911b0941f2ba7dbfbbd961e9be.txt css/base.css css/themes var FS embed.FS diff --git a/static/tamabox.changelogs.json b/static/tamabox.changelogs.json index 2a38318..a843901 100644 --- a/static/tamabox.changelogs.json +++ b/static/tamabox.changelogs.json @@ -1,9 +1,18 @@ { "page": 1, "perPage": 3, - "totalItems": 4, + "totalItems": 5, "totalPages": 2, "items": [ + { + "collectionId": "tamabox", + "collectionName": "tamabox-changelogs", + "content": "新增:箱主主题功能。设置页可为主题箱主页与单题页选择配色,只影响配色、图案与整页背景,不影响布局;未选择时跟随站点默认主题。
\n新增:主题底稿 static/css/themes/_scaffold.css,复制改名为 <slug>.css 并在 [theme] available 登记即可新增一套主题,无需改模板;示例主题「樱花」。", + "created": "2026-10-19 15:10:00.000Z", + "id": "none", + "published_at": "2026-10-19 15:10:00.000Z", + "updated": "2026-10-19 15:10:00.000Z" + }, { "collectionId": "tamabox", "collectionName": "tamabox-changelogs", diff --git a/templates/base/header.html b/templates/base/header.html index 4898b31..8060873 100644 --- a/templates/base/header.html +++ b/templates/base/header.html @@ -1,5 +1,5 @@ - + @@ -20,8 +20,10 @@ - - + + + {{ if .ThemeSlug }} + {{ end }} diff --git a/templates/user/profile.html b/templates/user/profile.html index 386e18c..99e59f4 100644 --- a/templates/user/profile.html +++ b/templates/user/profile.html @@ -788,6 +788,34 @@ + + {{ if .Themes }} +
+

{{t .LangCode "profile.theme.title"}}

+
+ {{ .CSRFTokenHTML }} +

{{t .LangCode "profile.theme.hint"}}

+
+ + {{ range .Themes }} + + {{ end }} +
+
+ +
+
+
+ {{ end }} + diff --git a/templates/zz_preview_test.go b/templates/zz_preview_test.go index 94d7632..6faa354 100644 --- a/templates/zz_preview_test.go +++ b/templates/zz_preview_test.go @@ -3,6 +3,7 @@ package templates import ( "os" "path/filepath" + "regexp" "strings" "testing" "time" @@ -72,6 +73,29 @@ func TestGenPreview(t *testing.T) { writePreview(t, outDir, "preview-box-"+theme+".html", box, theme, "../static") } + // 主题预览:与箱主页同一份数据,只是打开 sakura 主题(验证令牌覆盖与深色块) + themeBox := publicBaseData() + themeBox["VConsole"] = false + themeBox["ThemeSlug"] = "sakura" + themeBox["PageUser"].(map[string]interface{})["Background"] = bgURI + themeBox["PageUser"].(map[string]interface{})["Usernamecolor"] = "#3a2b30" + themeBox["PageUser"].(map[string]interface{})["Introcolor"] = "#6b545c" + themeCSS, err := os.ReadFile(filepath.Join("..", "static", "css", "themes", "sakura.css")) + if err != nil { + t.Fatalf("read theme css: %v", err) + } + for _, theme := range []string{"light", "dark"} { + page, err := renderToString(t, "question/list", themeBox) + if err != nil { + t.Fatalf("render themed list: %v", err) + } + // 预览页走 file://,把主题 css 内联进去(真实页面是一次带缓存的静态请求)。 + // 用正则匹配:预览数据里没有 StaticVersion,v 参数为空串,不能按固定串替换。 + link := regexp.MustCompile(``) + page = link.ReplaceAllString(page, "") + writePreview(t, outDir, "preview-theme-sakura-"+theme+".html", page, theme, "../static") + } + // 探索页:核对令牌替换后的卡片/列表配色 explore := voteBaseData() explore["LangCode"] = "zh-CN" @@ -102,6 +126,12 @@ func TestGenPreview(t *testing.T) { // 设置页「内容防护」面板:核对是否与其它面板同样是白底卡片 profile := profileBaseData() + // 主题选择器(account 面板):用固定假数据,避免测试依赖 conf 的 [theme] 段 + profile["Themes"] = []map[string]interface{}{ + {"Slug": "default", "Swatch": ""}, + {"Slug": "sakura", "Swatch": "linear-gradient(135deg, #fff6f8 0 55%, #e5678f 55% 100%)"}, + } + profile["CurrentTheme"] = "sakura" profile["ActiveProfileTab"] = "security" profile["FeatureQuestion"] = nil profile["FeatureChat"] = nil @@ -113,6 +143,22 @@ func TestGenPreview(t *testing.T) { } writePreview(t, outDir, "preview-profile-"+theme+".html", page, theme, "../static") } + + // 设置页「账号」面板:主题选择器的实际排版(卡片在面板末尾) + profileAccount := profileBaseData() + profileAccount["Themes"] = profile["Themes"] + profileAccount["CurrentTheme"] = "sakura" + profileAccount["ActiveProfileTab"] = "account" + profileAccount["FeatureQuestion"] = nil + profileAccount["FeatureChat"] = nil + profileAccount["FeatureVote"] = nil + for _, theme := range []string{"light"} { + page, err := renderToString(t, "user/profile", profileAccount) + if err != nil { + t.Fatalf("render account profile: %v", err) + } + writePreview(t, outDir, "preview-profile-account-"+theme+".html", page, theme, "../static") + } } // writePreview 把 CDN 换成可直连的镜像、钉死主题,仅用于本地预览。 @@ -120,7 +166,16 @@ func writePreview(t *testing.T, dir, name, html, theme, staticPrefix string) { t.Helper() html = strings.ReplaceAll(html, "https://cdnjs.snrat.com/", "https://cdnjs.cloudflare.com/") if theme == "dark" { - html = strings.Replace(html, ``, ``, 1) + // 上可能还有 data-theme-style,只在这个开标签内判断/插入; + // 不能全文搜索 —— 内联的主题 css 里有 html[data-theme="dark"] 选择器 + if i := strings.Index(html, "= 0 { + if j := strings.Index(html[i:], ">"); j > 0 { + tag := html[i : i+j] + if !strings.Contains(tag, "data-theme=") { + html = html[:i+j] + ` data-theme="dark"` + html[i+j:] + } + } + } } html = strings.ReplaceAll(html, `href="/favicon.ico"`, `href="`+staticPrefix+`/favicon.ico"`) html = strings.Replace(html, ``, `<title>预览 `+time.Now().Format("15:04")+` · `, 1)