用户报「最近两篇文章标签生成效果不好」。线上实测确认: #7161《开源密码生成工具SimplePassword》入库 11 个: https | laoyang | passwo | script | 代码 | 任何 | 复杂 | 密码 | 服务 | 记录 | 输入 只有「密码」沾主题,其余是 URL 碎片、代码切片、虚词、泛词。 #7157《痛苦根源》入库 11 个: 世界 | 孩子 | 幸福 | 微博 | 手机 | 无法 | 欲望 | 社会 | 老人 | 衣服 | 阶级 「衣服」「老人」「手机」全是从举例段落抓的,而标题词「痛苦」反而没入库。 四条根因,每条都有实测数据: ① 污染闭环(最关键)。词典 = 全量标签(get_terms),而上一轮引擎造的 passwo/任何/衣服(term_id 3423~3428,正是这两篇发布时新建的)下一轮就 变成"已有标签",走 exist 分支不占限额、无条件追加。垃圾进库→进词典→ 被当权威复用→喂大词典。修法:引擎新建标签打 term meta yang_auto, 词典按该标记识别并拒绝复用(除非它出现在本篇标题里,那是作者自己在用)。 判据用标记不用 count 猜 —— 实测 count=1 里既有垃圾(衣服/社会,只在正文), 也有好词(鸿蒙/日历,在标题),count 分不开来源。 ② 英文被 n-gram 切窗口。password(8字) 超 6 字上限进不了候选,它的 6 字窗口 passwo 频次与它相同,而切片消除要求"存在更长的候选包含它"才生效 —— 那个 更长词没进候选,切片没有对照物直接存活。库里非中文 count<=1 有 104 个 (gcc/ls/set/no/utf/cn/perl/ul/else/null/let/add/opt/usr…)。 修法:ASCII 按空格与中英边界取整词,不切窗口;整词+驼峰段+下划线分段 形态合并计频(SimplePassword 与 simple_password 是同一个词); 显示形态保留原文大小写,不产出全小写的机器味标签。 ③ IDF 方向反了。实测 N=969:工作 df=62→3.73,代码 df=18→4.93, 而 passwo/任何/衣服 df=1→7.18 全场最高。这份分布下"稀有"≈"垃圾", 公式在精准地把垃圾顶到前排。改成 df 越高罚越狠的惩罚项。 ④ 没有主题性约束。$full = $title.' '.$body 让标题只占一小段,痛苦(标题词) 与衣服(举例一次)在 TF 上拉不开。改成标题/正文分开计数、标题命中高权重; df<=1 的词要求标题佐证或本文高频。 另外:剥 <pre>/<code>/URL/邮箱/路径(laoyang 实测在 <pre> 内出现 5 次、 正文 0 次;40 篇代码块内高频词是 dolly/torch/conda/home/sudo/venv 这些 命令行与路径);停用词扩表但必须是并集 —— 泛化实词只在标签作用域生效, 摘要与分类看到的词表一字不变。 exist 上限 8 全收 → 6,且新词与 exist 重叠不再重复出。 验证(都是真实产品代码,不是模拟器副本): - 同进程双版本 A/B(新版改前缀 yw38_ 并存,同词典同数据)59 篇: 垃圾率 0.898→0.000、虚词率 0.831→0.000、标题词覆盖 0.707→0.809、 均标签 10.9→8.5;耗时均值 98.2→84.3ms(新版更快),峰值 2057→2036ms 且两版峰值出自同一篇 9610 字长文 ⇒ 不是回归。 #7161 → 密码|服务|代码|开源|技巧|SimplePassword(完整词形带大小写) - 越界检查 16/16:59 篇摘要输出逐字一致、分类逐项一致;stopwords() 无参 集合相同;max(1,count) 投影逐条相同(摘要 idf 权重零变化, 为此在摘要侧显式把 count 夹回 1)。 - 打标集成验证三段式(会写库,测后自证零残留):setup 全绿、 verify 对照成立(旧算法同输入复用 3/3、新算法 0)、 teardown 确认 709→709 无残留、草稿删净、站点配置未动。 必须分进程是因为 dict() 带 static 缓存 —— 同进程断言"新词没被复用" 是假绿(它还没进词典,不是过滤器起作用)。 - 判据三层:正向 55/0、反证 12/12、meta 19/0(12 组注入与断言的绑定 关系逐组证实 + 7 项结构检查)。 发布 4.2.1:包 md5 01141648eda7a58d8f4258cfda424b1a、239 文件、4.66MB; zip 与 readme.html 走 HTTP 200 且下载回来 md5 逐字一致;最后一步才改 update.json,并清掉派生缓存 puc_external_updates_theme-kratos_yang, 确认 PUC 检测到 4.2.1。 过程里被自己写的判据反噬三次,都记进 pitfalls: - 反证脚本 write(sub1(...)) 且锚点未命中返回 None,open('w') 先把产品文件 截成 0 字节才抛 TypeError —— 靠事前 cp 备份救回(#514 现场重演)。 现在 write() 先校验类型与长度再打开文件,且还原进 finally + atexit 双保险。 - 断言文案里插了变量("传 %s" % arg),meta 拿静态期望串找不到序号; 文案是机器对齐用的,不能随被测状态漂移。 - 改名用 str_replace 全文替换,把 '_yang_writing_done' 这个 meta **键名** 一起改了,导致"幂等锁生效"是假绿。改用 token_get_all 只动标识符。 写 4.2.1 顺带纠正 #37 发现的 readme.html 三处错误说法:头像不是"浏览器本地 生成"而是 PHP 服务器端生成;自动头像不在「全站配置」下而是独立分组; 不存在"只对评论生效/全站生效"这个开关(实测无此选项,已删该句)。
Kratos Yang
一个给中文技术博客用的 WordPress 主题。上游是 Kratos,fork 出来之后按自己的写作习惯一路改到现在,改成了一个偏「写作与阅读」的形状:写的时候少折腾,读的时候少干扰,后台能看见的东西尽量不靠插件。
- 主题预览:https://yangyq.net
- 源码主仓库:https://code.yangyq.net/laoyang/wordpress-kratos-yang
- 协议:MIT。上游 Kratos 的版权归属见
LICENSE。
当前版本号看 style.css 头部的 Version: 字段——那是版本号唯一的地方,JS 和控制台输出的版本都由它派生。
环境要求
| PHP | 7.0 以上(style.css 的 Requires PHP: 7.0) |
| WordPress | 开发机与线上实测均在 7.1.x;主题只用到 add_theme_support / register_sidebar / wp_get_theme 这些很成熟的 API,但低版本没有实测过 |
| 数据库 | 跟随 WordPress 本身的要求 |
style.css 没有声明 Tested up to,所以上面 WordPress 那一行请当作「已知可用的环境」,不是「验证过的最低版本」。
主题不依赖任何插件。代码高亮、图片灯箱、自动目录、评论表情、SMTP 发信、访问统计面板都是主题自带——装了同类插件反而会打架,原因见文末「已知冲突」。
后台设置:16 个分组
设置页左侧是分组标签,右侧只显示当前那一组。想找某项设置先看左边。
一、全站配置
| 项 | 键 | 默认 | 说明 |
|---|---|---|---|
| 图片 Logo | g_logo |
主题自带 | 留空用默认图 |
| Favicon 图片 | g_icon |
主题自带 | 浏览器标签页小图标 |
| 背景颜色 | g_background |
#f5f5f5 |
内容区背景,输出在 k-main 的 style 上 |
| Chrome 导航栏颜色 | g_chrome |
— | 只影响移动端 Chrome 的地址栏配色 |
| 404 页面图片 | — | 主题自带 | 走错地址时的插画 |
| 前台管理员导航 | g_adminbar |
开 | 关掉后管理员在前台看不到顶部黑色工具条 |
| CSS 动画库 | g_animate |
关 | 开:加载 animate.min.css,卡片进场有动效;关:少一个请求 |
| Gravatar 加速 | g_gravatar |
关 | 见下方说明 |
| 禁止生成缩略图 | g_removeimgsize |
关 | 开:停止生成 thumbnail / medium / large / medium_large 四档。只影响之后上传的图,已有图片不受影响 |
| Gutenberg 编辑器 | g_gutenberg |
关 | 默认就关——勾选才会启用区块编辑器。关闭时主题会连前端的 block-library CSS 一起卸掉 |
关于 Gravatar 加速,这里有个容易被选项名误导的细节:
主题默认就把所有 Gravatar 域名替换成 cn.gravatar.com(不管开关)。勾选「加速」后换的是 gravatar.loli.net。也就是说这个选项不是「开/关替换」,而是「换哪个镜像」。两种情况下顺带都把 http:// 升级成 https://。
为什么这很关键:
cn.gravatar.com在部分网络环境下同样不通。如果你发现头像仍然是破图,先确认这个开关的状态,再确认你的网络能不能通到所选镜像。彻底不依赖 Gravatar 的办法是开「二、自动头像」(见下)。
二、自动头像
没上传过头像的访客,不该是一个灰色神秘人。
| 项 | 键 | 默认 |
|---|---|---|
| 启用自动头像 | a_auto_enable |
关 |
| 注册用户保留原头像 | a_auto_keep_member |
开 |
| 头像风格 | a_auto_style |
18 套可选 |
开启后,主题按评论者邮箱地址生成一张 SVG 头像,以 data:image/svg+xml;base64,… 内联输出。服务端生成、不落盘、不请求任何外部服务(单次渲染实测 0.09~0.15 ms,比一次磁盘 stat 还便宜,所以没必要缓存到文件)。
- 算法与图形素材来自 DiceBear 9.x。主题把各风格的图形模板「编译」成了纯 PHP 数组(
inc/avatar-styles/*.php,共 18 个文件),运行时不执行任何 JS。 - 为了和 DiceBear 官方预览页逐字节一致,主题自己实现了 PRNG:DiceBear 9.x 用 xorshift + 自定义 hashSeed,全程在 JS 的 32 位有符号整数域里运算,而 PHP 整数是 64 位,必须逐步截断到 int32。不这么做,同一邮箱在两边会得到两张不同的脸。
- 18 套风格全部可自由商用且不要求署名(CC0,或作者自定义的「个人与商业可用」条款)。后台点风格名能看到实时预览。
- 尺寸按实际显示需求生成,上限 256px(再大模板会被撑得没必要地大)。
一个必须知道的取舍。 WordPress 的 get_avatar() 从不去查 Gravatar 上到底有没有这张图,它只负责拼 URL;返回真人图还是默认图,是 Gravatar 服务端在加载时才决定的。这意味着服务端无法在离线状态下区分「这个邮箱有 Gravatar」和「没有」——任何声称能精确判断的写法都要发一次外网请求,而 gravatar.com 在国内基本全超时。
所以主题的判据退了一步:src 指向 Gravatar 家族、且该邮箱不属于本站注册用户 ⇒ 替换成生成图;本站用户、媒体库图片、外链头像、已经是 data: 的(说明别的插件给过了)一律放行。
代价:如果你开了「Gravatar 加速」并且匿名访客确实有 Gravatar 头像,这里仍会盖掉他的图。相比「多数访客是一张破图」,「偶尔盖掉一个匿名访客的真实头像」伤害更小。
三、顶部导航
| 项 | 键 | 默认 |
|---|---|---|
| 顶部样式 | top_select |
图片导航 |
| 图片导航背景 | top_img |
主题自带 |
| 颜色导航背景 | top_color |
— |
| 副标题 | top_title |
— |
| 标题描述 | top_describe |
— |
图片导航背景、颜色导航背景分别是上面两种样式用的;选了另一种时该项不生效。
四、首页配置
| 项 | 键 | 默认 | 说明 |
|---|---|---|---|
| 特色图片 | g_thumbnail |
开 | 首页文章列表每篇标题上方显示一张配图 |
| 默认特色图 | g_postthumbnail |
主题自带 | 文章既没有正文图片、也没设特色图时顶上 |
| 无内容图片 | g_nothing |
主题自带 | 搜不到结果、或分类下还没有文章时的插画 |
五、文章配置
| 项 | 键 | 默认 | 说明 |
|---|---|---|---|
| 页面布局 | g_article_widgets |
双栏 | 文章详情页要不要侧边栏,单栏/双栏两张图直接点选。只影响文章页,首页不受它管 |
| 文章打赏 | g_donate |
关 | 开启后文章末尾出打赏按钮,点开是收款码 |
| 支付宝收款码 | g_donate_alipay |
主题自带 | |
| 微信收款码 | g_donate_wechat |
主题自带 |
侧栏的实际可见性还多一层:文章页的侧栏容器 class 是 col-lg-4 sidebar d-none d-lg-block,小屏(<992px)本来就隐藏;选「单栏」时主题干脆不输出这段 HTML,连 DOM 都没有。首页的侧栏是 col-lg-4 sidebar d-lg-block,所以移动端首页仍然显示侧栏。
六、自动目录
长文必备。正文里那份和浮层那份是两个独立开关,不是二选一,可以同时开——两份目录指向同一批锚点,点哪份都跳同一处。
正文里那份:
| 项 | 键 | 默认 |
|---|---|---|
| 在正文中显示 | toc_inline |
开 |
| 正文中的对齐 | toc_inline_align |
— |
| 添加方式 | toc_mode |
— |
| 自动插入位置 | toc_anchor |
— |
| 正文目录的默认状态 | toc_inline_default |
— |
浮层那份:
| 项 | 键 | 默认 |
|---|---|---|
| 浮动显示 | toc_float |
关 |
| 浮动方向 | toc_float_side |
— |
| 浮层目录的默认状态 | toc_float_default |
— |
两份共用:目录标题 toc_title、显示序号 toc_numbering(默认关)。
「添加方式」控制自动模式(正文一个字都不用写,按 toc_anchor 插)与手动模式(按你写的 [TOC] 位置放)。只要正文里有 [TOC],就只按你写的位置渲染正文那一份,不再重复插;但浮层那份独立判断——你写了 [TOC] 并不等于说「不要浮动目录」。
展开态写在 class(is-open)上而不是靠 JS 加,所以禁用 JS 也能看到目录的默认状态,JS 只负责之后的切换。
七、站长信息
个人头像 a_gravatar、个人昵称 a_nickname、个人简介 a_about。
这三项同时喂给两个地方:「个人简介」挂件,以及文章页底部的作者栏(pages/page-toolbar.php)。所以改这里,文章页的作者信息会跟着变。
八、页脚配置
| 项 | 键 | 说明 |
|---|---|---|
| 版权信息 | s_copyright |
留空则自动用「COPYRIGHT © 当前年份 站点名称 版权所有」 |
| 是否显示国旗 | g_flag |
默认关 |
| 国旗图片 | g_flag_url |
|
| 工信部备案信息 | s_icp |
|
| 公安网备案信息 | s_gov |
|
| 公安网备案链接 | s_gov_link |
公安备案没有可推导的统一格式,需单独填 |
九、哀悼模式
全站变灰,用于公祭日、地震纪念日等。
| 项 | 键 | 默认 |
|---|---|---|
| 手动常开 | g_rip |
关 |
| 按纪念日自动哀悼 | g_rip_auto |
关 |
| 附加日期 | g_rip_extra |
— |
| 说明页路径 | g_rip_page_slug |
memory |
自动模式内置一批日期,按当天判断。日期分两类可信度,代码里也分开标注:
- 法定纪念日(全国人大常委会决定设立、每年国家举行纪念活动):09-03 中国人民抗日战争胜利纪念日、09-30 烈士纪念日、12-13 南京大屠杀死难者国家公祭日。每个日期在源码里都写了设立依据的会议与文件出处。
- 民间广泛采用(非法定,但主流网站普遍变灰):05-12 汶川地震纪念、09-18 九一八事变纪念。
「说明页」是变灰时页面顶部那个入口点进去的页面,解释「今天为什么是灰的」。它由主题按 slug 直接 include 模板,绕开 WordPress 模板层级,因此不受全站灰度滤镜影响——这一页必须是彩色的。
十、收录与统计
SEO:关键词 seo_keywords、站点描述 seo_description、分享图片 seo_shareimg(搜索引擎或社交工具分享首页时抓的卡片图,留空自动用主题自带默认图)。
robots.txt:这一组底部直接显示主题生成的 robots.txt 内容(Disallow wp-admin / wp-includes / wp-content/plugins / wp-content/themes)。
统计埋点:
| 项 | 键 | 默认 |
|---|---|---|
| 统计代码 | seo_statistical |
— |
| 启用统计埋点 | stat_track_enable |
开 |
「统计代码」是你粘第三方埋点 <script> 段的地方(百度统计、GA 等)。
访问统计面板——把百度统计的数字直接画在后台设置页,不用跳去他家网站看:
| 项 | 键 | 存储位置 |
|---|---|---|
| API Key | stat_api_key |
凭据文件,不进 options 表 |
| Secret Key | stat_api_secret |
凭据文件 |
| refresh_token(高级) | stat_refresh |
凭据文件 |
| 站点 ID | stat_site_id |
options 表 |
| 授权码 | stat_auth_code |
不落库(一次性,换完 token 即弃) |
| 面板显示哪些指标 | stat_metrics |
options 表 |
凭据单独存(复用写作引擎那套 of_sanitize_password 拦截),options 表里只留空串——所以一次「导出主题配置」不会把密钥带出去。副作用是表单永远回显空框,因此**「空值 = 不修改」而不是「清空」**;否则你每次保存别的设置都会把凭据抹掉。
面板的三个设计约束都是踩出来的:
- 后台绝不能被 API 拖死。百度统计的接口是同步 HTTP,对方超时默认要等 30 秒以上——设置页会因为一个"附加面板"卡住,你会以为主题坏了。所以连接超时压到 5 秒 +
blocking => false,任何失败路径都返回数组(含 error 字段),渲染层只负责显示、不负责判断成败。整条链路没有一个wp_die()或异常外抛。 - 站点 ID 就是网站列表里点进站点后,地址栏
siteId=后面那串数字。整条地址粘进去也行,会自动抠出数字。 - 前台埋点仍走上面那个「统计代码」文本框,面板只负责取数与展示。
十一、邮件通知
| 项 | 键 | 默认 |
|---|---|---|
| SMTP 服务 | m_smtp |
关 |
| 邮件服务器 | m_host |
|
| 服务器端口 | m_port |
|
| 授权方式 | m_sec |
|
| 邮箱帐号 | m_username |
|
| 邮箱密码 | m_passwd |
填密码或授权码均可 |
| (测试发送按钮) | m_sendmail |
|
| 有人回复时发邮件通知 | m_comment_notify |
关 |
| 评论通过审核时通知评论者 | m_comment_approved_notify |
关 |
| 保留 WordPress 自带的作者通知 | m_keep_core_notify |
关 |
开启 SMTP 后 WordPress 的所有发信(找回密码、评论通知等)都走这里。填完点「邮箱密码」下面那个测试按钮直接发一封验证。
最后那个开关值得单独说。 装过 wpDiscuz 之类评论插件的人大概都遇过「一条评论收到好几封邮件」——插件的通知和主题/ WordPress 的通知完全重复。所以:
- 主题早期确实自己发过「回复通知」和「审核通过通知」,与 wpDiscuz 的通知完全重复,现已全部从
theme-smtp.php移除;发信通道的接管逻辑统一在inc/theme-mail.php。 - 上面三个开关原先只有代码在读取、后台根本没有界面,现在补齐了。想让别的插件发信,就把对应的关掉。
theme-mail.php接管 SMTP 的钩子优先级是 9999,并且会先判「配置不完整就不接管」「清掉别的插件残留配置」——避免两套 SMTP 配置互相覆盖。m_keep_core_notify关掉时,WordPress 自己那套「作者收到新评论」的邮件也停掉。
十二、CDN 加速
把页面里指向本站的静态资源地址,前缀换成你的 CDN 地址。思路参考 CDN Enabler 插件,但只保留三件事:CDN 地址、白名单、黑名单(黑名单优先)。
勾选「启用 CDN 加速」cdn_enable(默认关)并填 cdn_url 才生效。
改写发生在最终 HTML 的输出缓冲层,不是改 enqueue。 原因很实际:主题自己 enqueue 的资源只有十来个,可以精确改;但文章正文里的图片、插件输出的样式、Gutenberg 区块的图片都绕过 enqueue、直接写死在 HTML 里。只在最终 HTML 上做,才能一次覆盖全部,主题自身资源也顺带覆盖,不必维护两套逻辑。
硬编码的安全底线,不受白名单影响、不能放开——站点被 CDN 改坏最常见的原因是「把页面地址也改了」:导航链接变成 CDN 域名 → 用户点一下就离开本站 → 全站 404。所以下面这些一律不改写:
- 以
/结尾的地址(页面、栏目页) .php/.html/.htm等页面或动态后缀wp-admin/wp-login/wp-json/wp-cron等后台与接口路径- 非本站域名(外链、别人家的 CDN)
data:/mailto:/tel:/javascript:等非 http(s) 协议
后台这一组里把「白名单写法」「这些地址永远不走 CDN」「后台与接口里不改写」三段说明直接列了出来,不用翻文档。
十三、图片灯箱
正文图片点一下在当前页放大,不跳走;多张图可左右切换,键盘方向键也能翻。
| 项 | 键 | 默认 |
|---|---|---|
| 启用图片灯箱 | a_lightbox_enable |
开 |
| 遮罩配色 | a_lightbox_theme |
|
| 遮罩不透明度 | a_lightbox_opacity |
关掉后图片恢复成普通链接行为。
十四、代码高亮
不依赖任何高亮插件,主题自带一套(highlight.js 自托管)。Ender / Prism / Enlighter 那类可以卸掉——装多了反而打架,早期版本同时开两个,出现过复制按钮变两个、行号叠成重影。
| 项 | 键 | 默认 |
|---|---|---|
| 启用代码高亮 | a_highlight_enable |
开 |
| 代码配色 | a_highlight_theme |
|
| 显示行号 | a_highlight_line_numbers |
关 |
| 显示复制按钮 | a_highlight_copy_button |
开 |
| 自动识别代码语言 | a_highlight_autodetect |
开 |
- 20 套配色:10 浅 + 10 深(GitHub、VS、Atom One、IntelliJ、Monokai、Night Owl、Dracula 系、Srcery、Panda 等)。选完在后台当场就能看到效果。
- 只加载你选中的那一套 CSS。20 套全加在一起实测 19.3 KB(gzip 后 8.8 KB),不算大,但九成用不上的样式没必要拖在首屏。
- 语言标签显示在代码块左上角;代码围栏没写语言时去猜,猜不准标「可能」。
- 复制出去的代码永远不含行号——带行号粘出去没法直接跑。
- 深色配色会连着把代码卡片底色也变深。这条要单独做,否则深色代码贴在白卡片上,看着像没加载完。
十五、写作增强
发布文章时自动生成摘要 / 分类 / 标签 / 英文别名。四个引擎互不依赖,每个都能单独开关、单独失败:
| 项 | 键 | 默认 | 依赖 |
|---|---|---|---|
| 自动生成摘要 | w_on_excerpt |
关 | 纯本地 |
| 自动选择分类 | w_on_category |
关 | 纯本地 |
| 自动生成标签 | w_on_tag |
关 | 本地优先,可选外部 |
| 每篇最多新增几个标签 | w_tag_new_limit |
||
| 自动生成英文别名 | w_on_slug |
关 | 必须外部翻译 |
| 翻译服务商 | w_provider |
||
| 接口超时(秒) | w_timeout |
标签为什么能本地做:站里已有的几百个标签本身就是最好的领域词典,不需要外部服务。
别名为什么必须外部服务:线上实测过三条路,只有第三条可用。
intl拼音:信息焦虑综合症→xin-xi-jiao-lu-zong-he-zheng。能用,但不是翻译,且长到没法看。- 拿 966 组「中文标题 → 英文 slug」训练词对齐:5 个测试标题里 3 个译出 0 个词(数据摊到 8521 个中文片段上太稀疏)⇒ 不可用。
- 翻译 API:
信息焦虑综合症→Information Anxiety Syndrome⇒ 可用。
服务器没有 scws / jieba 任何中文分词扩展,「本地发现新词」这条路本身走不通——这是环境事实,不是偷懒。
翻译服务商可选:
| 服务商 | 要不要密钥 |
|---|---|
| MyMemory | 不要,直接可用(每日有免费字符限额,超限会返回错误,届时可换百度) |
| 百度翻译 | 要 APPID + 密钥(免费版每月 5 万字符) |
| 有道翻译 | 要应用 ID + 应用密钥 |
选完谁才显示谁的输入框,不会三家密钥全摆着(靠 yang-prov / yang-prov-<key> 两个 class 做分组,声明处就在选项定义里)。每家怎么拿账号,后台对应位置写了分步说明。
三条红线,改这块代码前先读:
- 绝不覆盖已有值。用户原话:「别名可能意味着 url,url 变化了会造成大量的 404 错误」。所以 slug 只在为空时写,其它三项同理。
- 绝不处理旧文章。只在「第一次变成 publish」时触发;已在 publish 状态下再次保存 ⇒ 直接返回,一个字都不动。
- 外部服务失败必须静默降级。发布是主流程,不能让一个翻译接口超时把「发布」这个动作卡住或报红。
十六、自定义代码
| 项 | 键 |
|---|---|
| 自定义 CSS | g_custom_css |
| 自定义 JS | g_custom_js |
两段内容存在数据库的选项里,升级、重装、改主题目录名都不受影响。它们分别在主题样式表和主题脚本之后加载,优先级最高。
- 不需要写
<style>/<script>标签,直接写 CSS / JS 内容。 - 内容不会被当作 HTML 处理,
>、<、~、反斜杠转义(如content: "\f101")都原样保留。 - 想清空,把框里内容删掉保存即可。
短代码
[TOC]
正文任意位置写一行 [TOC],那里就长出目录。可带参数(都可省略):
[TOC title="本文目录" levels="1,2,3" numbering="1" toggle="0" open="0"]
| 参数 | 含义 |
|---|---|
title |
这份目录的标题 |
levels |
收哪些层级,认 "1,2,3" 写法;只收 h2/h3 就写 "2,3" |
numbering |
1 = 条目前加 1 / 1.1 这样的序号,默认 0 |
toggle |
是否显示展开/收起按钮 |
open |
默认是否展开 |
手写参数优先于后台设置,没写的项才回落到后台。
[Archives]
年 / 月归档文章列表。在文章或页面正文任意位置写一行:
[Archives]
- 默认显示今年,按月份归类(只列有文章的月份)
- 每篇后面带浏览数 / 评论数 / 点赞数
- 列表四角各有「上一年 / 下一年」按钮
- 切年纯 JS 重建 DOM,不刷新页面
可调参数(都可省略):
[Archives year="2025" month="1" nav="1"]
| 参数 | 含义 |
|---|---|
year |
首次显示哪一年,默认今年;也可用 ?arc_year=2025 覆盖 |
month |
1 显示月份分组标题,0 只平铺列表。默认 1 |
nav |
1 显示四角年份导航,0 不显示。默认 1 |
同一页放两个 [Archives] 也只算一次数据。
为什么不用 WordPress 自带的 wp_get_archives():三个原因,每个都足以否掉它——① 它拿不到计数,要显示浏览数/评论数/点赞数只能每篇再查一次,71 篇就是 71 次查询外加 2×71 次 meta 查询;② 输出格式固定(<li><a>…</a></li> 或下拉框),塞不进三项计数;③ 它按「按月 / 按年」二选一输出整条时间线,没有「给我某一年」的能力。主题改成每次一条 SQL 拿全部计数。
短代码标签区分大小写,[archives] 和 [Archives] 都认。
侧栏挂件
主题自己实现了 7 个,在「外观 → 小工具」里拖用:
| 挂件 | 说明 |
|---|---|
| 个人简介 | 读「七、站长信息」的头像 / 昵称 / 简介,可设背景图 |
| 最近评论 | 主题自建,替代 Comments Widget Plus 一类插件 |
| 文章聚合 | 热点文章(按热度,不是按时间) |
| 标签聚合 | 标签云,可设列数 |
| 文章分类 | 接管并美化了 WP 自带的那個(原生的没被主题接管、也没美化) |
| 图片广告 | 一张图 + 一个链接 |
| 搜索框 | 站内搜索输入框 |
三条使用须知:
- 标题都能在后台改,不用动代码(早期「标签聚合」的标题是硬编码的,后台连输入框都没有)。
- 样式只依赖挂件自己的 class(如
.w-cats、.w-comments),不写成.k-main .sidebar .w-cats。因为「外观 → 小工具」的预览界面里没有.k-main .sidebar那层祖先,依赖祖先的规则在后台预览会全部失效——你会以为样式没加载,其实只在前台生效。 - 「文章聚合 · 热点」曾经长期只显示 1 条,原因是候选池被设成了「最近 30 天发布的文章」——那个时间窗才是根因,现在改成按热度取。
主题里的复用片段
pages/ 下的四个文件是被其它模板引入的片段(局部视图),不是可以在编辑器里选择的页面模板——它们不会出现在「模板」下拉里。
| 文件 | 被谁引入 | 渲染什么 |
|---|---|---|
page-content.php |
index.php 经 get_template_part() |
首页列表里每一篇的卡片 |
page-toolbar.php |
single.php |
文章页底部的作者栏 + 分享 + 打赏 |
page-smilies.php |
comments.php |
评论框上方的表情选择条 |
page-mourning.php |
inc/theme-mourning-page.php 直接 include |
哀悼说明页(见「九、哀悼模式」) |
要新增一个真正的独立页面,走 WordPress 正常的新建页面流程即可,不需要碰这里。
评论区增强
评论区不需要装插件就有这些:
- 表情条:
page-smilies.php输出的表情图标条,点一下往评论框插入:smile:这类代码,显示时换成图。主题把表情图片路径指向了自己的assets/img/smilies/,不依赖 WordPress 安装目录里的那套。 - 富文本工具条:加粗、斜体、文字颜色三个按钮。这里有个容易忽略的实现点——
<strong>/<em>本来就在 WordPress 评论的 kses 白名单里,但<span style="color:…">不在,访客提交时会被wp_filter_kses整个剥掉,颜色功能就白做了。所以主题把span(带style/class)追加进了白名单。安全性由三层兜着:kses 对style内容还会再过一遍safecss_filter_attr()(一份固定白名单,expression()/url()/behavior一律剥掉);工具条前端只生成color一个声明;有unfiltered_html权限的管理员本来就不走 kses。 - 评论区也能贴代码,和正文用同一套高亮。语言下拉里只列真正支持的,不会让你选了得到一个白板代码块。
- 回复的嵌套缩进保留,但每一层的引导线按层配色(第 1 层浅蓝、第 2 层深蓝、第 3 层用点赞色,再往下从一个色板按层循环)。配色是确定性的:同一层永远同一个颜色、相邻层必定不同色,刷新页面不变——用真随机会让同一层的线各页各色,层级信号反而乱了。颜色都取饱和偏深的中明度,因为引导线在白底上,淡黄之类根本看不见。嵌套深度沿用 WordPress 的「评论层级」设置(
thread_comments_depth,默认 5)。 - 评论关闭时整块彻底消失,不会留一个空框加一句「评论已关闭」。
- 装了 wpDiscuz 之类第三方评论系统时,主题这块会让位,不会两边各画一个框。
多语言
语言包在 languages/:kratos.pot 是模板,另有 en_US 与 zh_TW。仓库带 crowdin.yml,想补别的语言可以直接改 .po,也可以通过翻译平台提交。
主题自己的文本域是 kratos。
已知冲突与注意事项
1. 装了会改评论容器类名的插件,评论样式会失效。
有些评论插件会把评论容器的 class="comments" 换成 class="comments-area",主题的样式选择器就选不中了。主题没有内置这条兼容规则(那属于插件冲突,不改主题本身)。处理办法:在「主题设置 → 自定义代码 → 自定义 CSS」里补上:
.k-main .details .comments-area {
margin-top: 16px;
padding: 1.6px 16px 16px;
border-radius: 2px;
background-color: #fff;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
2. 装多个代码高亮插件会打架。 表现为复制按钮变两个、行号叠成重影。主题自带一整套,把插件卸掉。
3. 后台「可用挂件」列表被主题收窄过。
主题在 widgets_init 里用 unregister_widget() 注销了 9 个 WordPress 原生挂件:WP_Widget_Pages、WP_Widget_Meta、WP_Widget_Recent_Posts、WP_Widget_Recent_Comments、WP_Widget_RSS、WP_Widget_Search、WP_Widget_Tag_Cloud、WP_Nav_Menu_Widget、WP_Widget_Categories——前八个用主题自己的实现替代,WP_Widget_Categories 是被主题的 widget_categories 接管。
两个实现要点:
- 注销是按类名,不是按
id_base。WP_Widget_Factory::register()按类名存,所以只register自己的类不够——核心的WP_Widget_Categories还留在工厂里,后台列表会同时出现「分类」和「文章分类」两项,用户分不清该拖哪个。注销核心的之后列表里只剩一个。 - 注销不影响存量实例。
_register_widgets()是按id_base读 option 的,主题的接管类沿用了同样的id_base,所以旧实例照样被认领。 - 执行顺序也确认过:主题挂在
widgets_init优先级 10,工厂自己的_register_widgets挂在优先级 100,所以「先注销 + 再注册」一定在真正注册实例之前完成。
如果你另有插件注册了同名 id_base 的挂件,以最后一次注册为准。
4. 两个「文章聚合」会撞出重复 id。 侧栏里同时挂两个同类挂件时,曾经产生过同一页面里重复的 DOM id(HTML 非法)。现在按实例区分。
5. 小屏的标签页图标只有一份 DOM。 早期实现是 PHP 渲染两份(带图标的 + 不带图标的)用 class 切换显隐,导致 nav-tab 等四个 id 在同一页面各出现两次。现在 DOM 只有一份,显隐交给媒体查询。
改代码前必读:源文件与压缩产物
这两件事都会让人以为"改了没生效":
-
CSS:
assets/css/kratos.css是源文件,前台加载的是assets/css/kratos.min.css。历史上当过这样一次事故:把单行压缩版手工展开成可读格式时漏掉了一整段(.k-main .details .article .content下的pre/code/kbd/pre code/pre ol/pre li六条规则),而未压缩的kratos.css里它们是齐全的,于是源与产物对不上——表现是行内code与kbd完全没有样式(代码块因为当时装着 Prism 才没露馅)。别手工维护压缩产物,改完用压缩器重新生成:npx clean-css-cli@5.6.3 -o assets/css/kratos.min.css assets/css/kratos.css -
JS:同理,前台执行的是
assets/js/kratos.min.js,assets/js/kratos.js只是源文件。只改源文件等于没改。
仓库镜像
主仓库是 https://code.yangyq.net/laoyang/wordpress-kratos-yang(自建 Gitea,唯一权威来源)。下列平台是手动同步的镜像,不保证最新:
| 平台 | 地址 |
|---|---|
| GitHub | https://github.com/dryangyq/wordpress-kratos-yang |
| Gitee | https://gitee.com/dryangyq/wordpress-kratos-yang |
| GitLab | https://gitlab.com/dryangyq/wordpress-kratos-yang |
要拿最新版请直接克隆主仓库。GitHub 因网络原因同步经常失败;GitLab 上实测出现过落后一百多个小版本的情况。这不是承诺,是提醒。
这个主题不做什么
知道边界能省下试错的时间:
- 不做多套皮肤切换。 只有一个形状,可调的只有背景色和顶部样式两类颜色项。
- 不做页面切换动画。 跨文档 View Transition 要求站点每个页面都主动 opt-in,收益(一点点过渡顺滑)远低于代价——侧栏和内容区做整体动画极易出现元素跳动,并且首屏渲染会被拖慢。
- 不内置评论系统。 自带的是对原生评论的样式与增强;换 Disqus / wpDiscuz 时主题会让位。
- 不做拖拽建站 / 页面构建器。 正文就是一篇文章,排版靠主题的稳定规则。默认甚至关掉了 Gutenberg,要用区块编辑器得去「全站配置」里勾选。
- 不把用户数据写进主题目录。 自定义代码进数据库、图片进媒体库或对象存储,主题目录保持「可以被整体替换」的状态——这一点很重要,因为 WordPress 升级主题的做法是删掉整个目录再解压新包,任何直接改在主题目录里的东西都会丢。
遇到问题
- 先翻「这个主题不做什么」,确认你想要的不是它有意不做的。
- 功能没生效:先确认后台对应分组的开关。有几个开关的方向容易记反——
g_gutenberg默认关(勾上才启用区块编辑器)、toc_float默认关、a_highlight_line_numbers默认关;而a_lightbox_enable/a_highlight_enable/stat_track_enable/toc_inline是默认开的。 - 改了 CSS 或 JS 没变化:先回看上面那节「源文件与压缩产物」。
- 页面报错、白屏:把 PHP 错误日志的相关行贴出来,比描述现象有用得多。
- Issue 提到主仓库:https://code.yangyq.net/laoyang/wordpress-kratos-yang/issues