From 4a2d62b1aaeed006f5630dda82d55016ec0a99b3 Mon Sep 17 00:00:00 2001 From: laoyang Date: Sun, 25 Oct 2026 13:37:26 +0800 Subject: [PATCH] =?UTF-8?q?3.2.31=20=E7=9B=AE=E5=BD=95=E3=80=8C=E6=AD=A3?= =?UTF-8?q?=E6=96=87=E5=86=85=E6=98=BE=E7=A4=BA=E3=80=8D=E4=B8=8E=E3=80=8C?= =?UTF-8?q?=E6=B5=AE=E5=8A=A8=E6=98=BE=E7=A4=BA=E3=80=8D=E6=8B=86=E6=88=90?= =?UTF-8?q?=E4=B8=A4=E4=B8=AA=E7=8B=AC=E7=AB=8B=E5=BC=80=E5=85=B3=EF=BC=88?= =?UTF-8?q?#1=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原来「目录位置」是一个三选一下拉:正文中间 / 页面左侧 / 页面右侧, 等于把「放进正文」和「浮在页边」当成互斥的一件事 —— 想同时在正文 开头放一份、又在边上浮一份,做不到。 改成两个独立勾选,各自带子选项: · 在正文中显示 → 正文中的对齐(靠左 / 靠右)+ 自动插入位置 · 浮动显示 → 浮动方向(页面左侧 / 页面右侧) 两个都勾就同时出现两份,只勾一个就只出现一种。四种组合都在后台 联动里逐个验证过(探针 70/0)。 新增能力「正文内靠左 / 靠右」是本单的主要新需求。实现上两个要点: 1. 必须给正文目录一个宽度上限(max-width: min(100%, 28em))。 块级盒子默认撑满正文栏,此时 margin:auto 没有剩余空间可分配, 「靠左/靠右」会是一个属性正确、画面纹丝不动的空操作。 2. 用 data-align 属性把方向带下去,CSS 只需两条规则, 不随方向数量膨胀。属性在 yang_toc_slot_html() 里输出。 语义收窄:手写 [TOC] 以前会让整份自动处理全停(连带 rail=false), 现在只手管「正文那一份」—— 手写位置优先,浮动仍由 float 开关独立 决定。这样「精确定位正文位置」和「全站边上都有目录」可以并存。 顺手修掉一处默认值错配:checkbox 用 !empty() 判断会把「键不存在」 和「键明确为空」混成一件事,导致新装站点后台框显示勾上、页面却 不出目录。改用 array_key_exists() 区分的三态判断。 老配置不迁移:3.2.30 的 toc_place 直接作废,不做兼容。 默认 inline=1 / float=0,升级后页面与升级前一致。 重构:把内容过滤器拆成 yang_toc_apply()(纯执行层)+ 运行时壳, 使设置组合能在单进程单测里逐个喂进去(PHP 无法重置函数 static)。 验证 逻辑 101/0 · 逻辑反向 15/15 旧回归 53/0 静态契约 58/0 · 反向 11/11 后台几何+交互 70/0 · 反向 11/11 侧栏几何 73/0 · 反向 8/8 正文对齐几何 23/0 · 反向 5/5(含阴性对照) 包内容契约 59/0 · 反向 9/9 正文对齐的几何探针量到真实的 Δleft = 348px(靠右比靠左), 而不是只断言 data-align 字符串存在。 --- assets/css/toc.css | 28 ++++ inc/options-framework/js/options-custom.js | 28 +++- inc/theme-options.php | 67 ++++++--- inc/theme-toc.php | 162 ++++++++++++++++----- style.css | 2 +- 5 files changed, 225 insertions(+), 62 deletions(-) diff --git a/assets/css/toc.css b/assets/css/toc.css index 741206c..a0299d1 100644 --- a/assets/css/toc.css +++ b/assets/css/toc.css @@ -262,6 +262,34 @@ body.admin-bar .k-main .details .content h6[id] { margin: 0; } +/* ── 正文内目录的左右对齐 ───────────────────────────────────── + * 需求里「靠左 / 靠右」指的是:目录块在**正文容器内部**贴左还是贴右, + * 不是浮到页面边上(那是 .yang-toc-rail 的职责)。 + * + * 为什么必须收窄宽度:块级盒子默认撑满正文栏,此时靠左靠右在视觉上 + * 完全一样 —— 只写 margin 的「对齐」是个**看起来实现了、实际是空操作** + * 的功能,量几何都量不出差别。所以这里给一个上限宽度,让对齐有意义。 + * + * 用 min() 而不是固定 px:窄屏时自动退化成 100%,不会出现「手机上目录 + * 只占一半、右边空一大块」。em 单位跟着字号走,用户改了基础字号也不会怪。 + * + * 用 margin-left/right:auto 而不是 float:float 会让正文文字绕排,那是 + * 另一回事(也会把
的折叠交互搞乱)。这里只做「在容器里摆哪边」。 + */ +.yang-toc-slot[data-align] > .yang-toc { + max-width: min(100%, 28em); +} + +.yang-toc-slot[data-align="left"] > .yang-toc { + margin-left: 0; + margin-right: auto; +} + +.yang-toc-slot[data-align="right"] > .yang-toc { + margin-left: auto; + margin-right: 0; +} + /* ── 侧边浮动目录 ───────────────────────────────────────────── * 结构:贴边的一条细标签 + 一块可滑出的面板。 * diff --git a/inc/options-framework/js/options-custom.js b/inc/options-framework/js/options-custom.js index 961b49a..f49c784 100644 --- a/inc/options-framework/js/options-custom.js +++ b/inc/options-framework/js/options-custom.js @@ -100,21 +100,33 @@ jQuery(document).ready(function ($) { jQuery('#section-top_describe').show() } - // 自动目录:只有「目录位置 = 正文中间」时,插入位置才有意义。 - // 侧栏模式(左/右)下它完全无效 —— 若还留着这个下拉框,用户会以为 - // 「选了文章结尾却没插进正文」是个 bug。 + // 自动目录:字段显隐跟着两个开关走。 + // + // 「在正文中显示」勾了 → 「正文中的对齐」「自动插入位置」才有意义 + // 「浮动显示」勾了 → 「浮动方向」才有意义 + // + // 为什么必须藏起来:两个开关任一关闭时,它下面那几个子选项完全无效。 + // 若还留着,用户会以为「选了文章结尾却没插进正文」是个 bug。 // // 用 class 切换而不是 fadeIn/fadeOut:.section 的 display 是 flow-root // (3.2.28 靠它建独立 BFC,避免被左侧浮动标签列的 clear:both 拉长)。 // jQuery 的 show()/hide() 会写行内 display,把那套布局打回 block, // 等于悄悄回退 3.2.28 的修复。 - function yq_toggle_toc_anchor () { - var center = jQuery('#toc_place').val() === 'center' - jQuery('#section-toc_anchor').toggleClass('yq-cond-hide', !center) + // + // ⚠️ 绑定事件用 delegation('change' 挂 document、选择器过滤): + // 这两个是 checkbox,框架的 checkbox 控件可能在外面再包一层 label/inline, + // 直接绑在 input 上偶尔会因为 DOM 顺序变了而漏绑。delegation 不受影响。 + function yq_toggle_toc_fields () { + var inline = jQuery('#toc_inline').is(':checked') + var float = jQuery('#toc_float').is(':checked') + + jQuery('#section-toc_inline_align').toggleClass('yq-cond-hide', !inline) + jQuery('#section-toc_anchor').toggleClass('yq-cond-hide', !inline) + jQuery('#section-toc_float_side').toggleClass('yq-cond-hide', !float) } - jQuery('#toc_place').on('change', yq_toggle_toc_anchor) - yq_toggle_toc_anchor() + jQuery(document).on('change', '#toc_inline, #toc_float', yq_toggle_toc_fields) + yq_toggle_toc_fields() // Loads the color pickers $('.of-color').wpColorPicker() diff --git a/inc/theme-options.php b/inc/theme-options.php index 7fd15fc..6175fde 100644 --- a/inc/theme-options.php +++ b/inc/theme-options.php @@ -397,15 +397,55 @@ function kratos_options() ); $options[] = array( - 'name' => __('目录位置', 'kratos'), - 'desc' => __('正文中间:目录是正文的一部分,按下面的「自动插入位置」插进去。
页面左侧 / 右侧:目录变成贴在页面边缘的浮动面板,点边上的小标签滑出,正文里不放任何东西(「自动插入位置」对它无效)。窄屏(手机)上也是从边缘滑出的抽屉,不会挤占正文。', 'kratos'), - 'id' => 'toc_place', - 'std' => 'center', + 'name' => __('在正文中显示', 'kratos'), + 'desc' => __('勾选后,在文章正文里插一份目录。
它和下面的「浮动显示」互不影响:两个都勾就同时出现两份(正文一份、边上浮一份),只勾一个就只出现一种。', 'kratos'), + 'id' => 'toc_inline', + 'std' => '1', + 'type' => 'checkbox', + ); + + $options[] = array( + 'name' => __('正文中的对齐', 'kratos'), + 'desc' => __('正文里那份目录在文章内容区域内靠左还是靠右。这里不是「浮到页面边上」—— 目录仍然在正文流里,上下文的文字会绕开它,只是整体贴向一侧。
只在勾选「在正文中显示」时才有意义。', 'kratos'), + 'id' => 'toc_inline_align', + 'std' => 'left', 'type' => 'select', 'options' => array( - 'center' => __('正文中间', 'kratos'), - 'left' => __('页面左侧(浮动)', 'kratos'), - 'right' => __('页面右侧(浮动)', 'kratos'), + 'left' => __('靠左', 'kratos'), + 'right' => __('靠右', 'kratos'), + ), + ); + + $options[] = array( + 'name' => __('自动插入位置', 'kratos'), + 'desc' => __('只在「添加方式 = 自动」且勾选了「在正文中显示」时起作用。
第一个标题之前取正文里最先出现的 h1 或 h2 —— 文章大标题在正文之外,不会被算进去;一个 h1/h2 都没有时退回插在文章开头。', 'kratos'), + 'id' => 'toc_anchor', + 'std' => 'first', + 'type' => 'select', + 'options' => array( + 'start' => __('文章开头', 'kratos'), + 'first' => __('第一个标题之前(H1 / H2)', 'kratos'), + 'end' => __('文章结尾', 'kratos'), + ), + ); + + $options[] = array( + 'name' => __('浮动显示', 'kratos'), + 'desc' => __('勾选后,目录变成贴在页面边缘的浮动面板,点边上的小标签滑出,不占正文位置。窄屏(手机)上是从边缘滑出的抽屉,不会挤占正文。
与「在正文中显示」互相独立,可以同时勾选。', 'kratos'), + 'id' => 'toc_float', + 'std' => '', + 'type' => 'checkbox', + ); + + $options[] = array( + 'name' => __('浮动方向', 'kratos'), + 'desc' => __('浮动面板贴页面左边还是右边。只在勾选「浮动显示」时才有意义。', 'kratos'), + 'id' => 'toc_float_side', + 'std' => 'left', + 'type' => 'select', + 'options' => array( + 'left' => __('页面左侧', 'kratos'), + 'right' => __('页面右侧', 'kratos'), ), ); @@ -421,19 +461,6 @@ function kratos_options() ), ); - $options[] = array( - 'name' => __('自动插入位置', 'kratos'), - 'desc' => __('只在「添加方式 = 自动」且「目录位置 = 正文中间」时起作用。
第一个标题之前取正文里最先出现的 h1 或 h2 —— 文章大标题在正文之外,不会被算进去;一个 h1/h2 都没有时退回插在文章开头。', 'kratos'), - 'id' => 'toc_anchor', - 'std' => 'first', - 'type' => 'select', - 'options' => array( - 'start' => __('文章开头', 'kratos'), - 'first' => __('第一个标题之前(H1 / H2)', 'kratos'), - 'end' => __('文章结尾', 'kratos'), - ), - ); - $options[] = array( 'name' => __('默认状态', 'kratos'), 'desc' => __('默认显示:进来就能看到全部条目。默认隐藏:先只露出标题栏(或侧边的标签),读者自己点开。较长或标题很多的文章建议默认隐藏,否则目录会占掉一屏。
注:这里只管「默认长什么样」,读者点开后仍然可以正常使用;正文里手写的 [TOC] 也可以用 open="0" 单独指定。', 'kratos'), diff --git a/inc/theme-toc.php b/inc/theme-toc.php index 0064861..fcfbefa 100644 --- a/inc/theme-toc.php +++ b/inc/theme-toc.php @@ -21,17 +21,26 @@ * 手写标记里写了的参数优先于后台设置,没写的才用后台设置。 * * ② 自动:后台「主题设置 → 自动目录」里选好,正文不用写任何东西。 - * · 目录位置:正文中间 / 页面左侧(浮动)/ 页面右侧(浮动) - * · 添加方式:手动 / 自动 - * · 自动插入位置(只对「正文中间」有效):文章开头 / 第一个 H1、H2 之前 / 文章结尾 - * · 默认状态:默认显示 / 默认隐藏 - * · 另外可配目录标题、收录层级、是否显示序号 + * 显示方式是**两个独立开关**,可同时开启: + * · 在正文中显示 → 子选项:正文中的对齐(靠左 / 靠右)、插入位置 + * (文章开头 / 第一个 H1、H2 之前 / 文章结尾) + * · 浮动显示 → 子选项:浮动方向(左侧 / 右侧) + * 全开时同一篇文章里正文一份 + 浮动一份,两份指向同一批锚点。 + * 另外还有:添加方式(手动 / 自动)、默认状态(显示 / 隐藏)、 + * 目录标题、收录层级、是否显示序号。 * - * ── 手写永远优先 ────────────────────────────────────────────── - * 只要正文里有 [TOC],就**只**按作者写的位置渲染,不再自动插第二份、 - * 也不出侧栏。理由:作者明确表过态的地方,机器不该再替他做一次决定; + * ⚠️ 「靠左 / 靠右」是**正文容器内部**的对齐,不是浮到页面边上; + * 「浮动方向」才是贴页面哪一边。这两件事曾经被合并成一个三选一的 + * place 字段(center/left/right),导致两者永远互斥、无法同时开启。 + * + * ── 手写永远优先(仅限正文那一份)──────────────────────────── + * 只要正文里有 [TOC],就**只**按作者写的位置渲染正文那一份,不再自动 + * 插第二份。理由:作者明确表过态的地方,机器不该再替他做一次决定; * 否则老文章升级后会突然多出一个目录,位置还未必是他要的。 * + * 但浮动面板是**独立开关**:它不属于正文流,作者写了 [TOC] 也没说过 + * 「不要浮动目录」。所以 float=1 时照样出浮动面板。 + * * ── 为什么默认收 h1(曾经错设成 "2,3")────────────────────── * 原先的理由是「h1 是文章标题本身,不该进目录」。这个理由是**假的**: * 文章标题输出在 single.php 的

,在 the_content @@ -89,9 +98,23 @@ if (!defined('ABSPATH')) { function yang_toc_defaults() { return array( - 'place' => 'center', // center | left | right + /* 两个**互相独立**的显示开关,可同时为 1。 + * + * 为什么不是「三选一」:曾经用单个 place=center/left/right 表达, + * 把「放在正文里」和「浮在页面边上」混成了同一个维度的三个值, + * 于是两者永远互斥 —— 用户想「正文里放一份、边上再浮一份」时无法表达。 + * 现在拆成 inline / float 两个开关 + 各自的子选项: + * inline 在正文流里插一份 + * inline_align inline 生效时的对齐:left | right + * float 在页面边缘浮一份 + * float_side float 生效时浮哪边:left | right + */ + 'inline' => 1, // 0 | 1 + 'inline_align' => 'left', // left | right(正文容器内部对齐) + 'float' => 0, // 0 | 1 + 'float_side' => 'left', // left | right(贴页面哪一边) 'mode' => 'manual', // manual | auto - 'anchor' => 'first', // start | first | end(只对 center 有效) + 'anchor' => 'first', // start | first | end(只对 inline 有效) 'default' => 'show', // show | hide 'title' => __('目录', 'kratos'), 'levels' => array('1' => 1, '2' => 1, '3' => 1, '4' => 0), @@ -175,7 +198,25 @@ function yang_toc_normalize_settings($raw) }; $s = array(); - $s['place'] = $pick('place', array('center', 'left', 'right')); + + /* checkbox 的存值有两种:框架写回时是 '1' 或 ''(也可能 true/false)。 + * 关键区别在「键不存在」和「键存在但为空」: + * · 键不存在 = 从没配过(新站 / 老库升级)= 用默认值 + * · 键存在且为空 = 用户明确取消勾选 = 0 + * 用 !empty() 一把梭会把这两种情况混成一件事,结果**新装站点默认不显示 + * 正文目录** —— 后台勾选框看起来是勾上的(那是 std 预填),页面却不出。 + * 这种错配极难发现,因为两边单独看都"正常"。 */ + $flag = function ($key) use ($raw, $d) { + if (!array_key_exists($key, $raw)) { + return $d[$key]; // 从没配过 + } + return empty($raw[$key]) ? 0 : 1; + }; + + $s['inline'] = $flag('inline'); + $s['float'] = $flag('float'); + $s['inline_align'] = $pick('inline_align', array('left', 'right')); + $s['float_side'] = $pick('float_side', array('left', 'right')); $s['mode'] = $pick('mode', array('manual', 'auto')); $s['anchor'] = $pick('anchor', array('start', 'first', 'end')); $s['default'] = $pick('default', array('show', 'hide')); @@ -207,7 +248,8 @@ function yang_toc_settings() } $raw = array(); - foreach (array('place', 'mode', 'anchor', 'default', 'title', 'levels', 'numbering') as $k) { + foreach (array('inline', 'inline_align', 'float', 'float_side', + 'mode', 'anchor', 'default', 'title', 'levels', 'numbering') as $k) { $raw[$k] = kratos_option('toc_' . $k, null); } // 库里没有的键用 null 占位,normalize 时按「未配置」回落默认值 @@ -275,15 +317,20 @@ function yang_toc_has_marker($content) * 'start' 文章开头 * 'first' 第一个 H1/H2 之前 * 'end' 文章结尾 + * inline_align 'left'/'right' 正文章节的对齐方向 * rail bool 是否输出侧边浮动目录 * side 'left'/'right' 侧栏挂哪边 * settings array 归一化设置 * * 规则(顺序即优先级): - * ① 正文有 [TOC] ⇒ 只在标记处渲染,不出侧栏、不自动插; - * ② 添加方式 = 手动 ⇒ 什么都不自动做; - * ③ 目录位置 = 中间 ⇒ 按「自动插入位置」插进正文; - * ④ 目录位置 = 左/右 ⇒ 出侧栏,正文不动(插入位置对侧栏无效)。 + * ① 正文有 [TOC] ⇒ 只在标记处渲染正文那一份;浮动仍按 float 开关独立决定。 + * 手写标记只管「正文那一份在哪」,没道理连带把浮动面板也关掉 —— + * 那是两件事。但正文绝不会出现第二份。 + * ② 添加方式 = 手动 ⇒ 不自动插正文那份;浮动同样独立决定。 + * ③ inline=1 ⇒ 按 anchor 插进正文;inline=0 ⇒ 正文不动。 + * ④ float=1 ⇒ 出浮动面板,方向按 float_side。 + * + * ③ 和 ④ 完全独立,可以同时成立:正文一份 + 浮动一份,共用同一批锚点。 * * @param bool $has_marker * @param array $s 设置(不传则取当前设置) @@ -296,12 +343,20 @@ function yang_toc_plan($has_marker = false, $s = null) } $plan = array( - 'inline_at' => null, - 'rail' => false, - 'side' => 'left', - 'settings' => $s, + 'inline_at' => null, + 'inline_align' => $s['inline_align'], + 'rail' => false, + 'side' => $s['float_side'], + 'settings' => $s, ); + /* 浮动独立于手写标记与添加方式:它不属于正文流, + 作者写了 [TOC] 也没说过「不要浮动目录」。 */ + if (!empty($s['float'])) { + $plan['rail'] = true; + } + + /* 正文那一份:手写标记 > 自动插入 */ if ($has_marker) { $plan['inline_at'] = 'marker'; return $plan; @@ -311,11 +366,8 @@ function yang_toc_plan($has_marker = false, $s = null) return $plan; } - if ($s['place'] === 'center') { + if (!empty($s['inline'])) { $plan['inline_at'] = $s['anchor']; - } else { - $plan['rail'] = true; - $plan['side'] = ($s['place'] === 'right') ? 'right' : 'left'; } return $plan; @@ -769,6 +821,27 @@ function yang_toc_filter_content($content) yang_toc_rail_ready(true); } + return yang_toc_apply($content, $out, $items, $has_marker, $plan); +} +add_filter('the_content', 'yang_toc_filter_content', 9); + +/** + * 按 plan 把目录落到已补好 id 的正文上(纯函数) + * + * 单独拆出来是为了让测试能「喂一份设置 → 直接看输出」:上面那个壳依赖 + * WordPress 运行时(is_singular / in_the_loop / get_the_ID 全要对上), + * 而设置是从库里读的、一次请求只能有一份。拆开之后,几十种组合可以在 + * 同一个进程里逐个跑,不必为了测试给读设置那层开后门。 + * + * @param string $content 原始正文(替换失败时原样返回它) + * @param string $out 已补好标题 id 的正文 + * @param array $items yang_toc_scan() 得到的条目 + * @param bool $has_marker 原文里有没有手写 [TOC] + * @param array $plan yang_toc_plan() 的结果 + * @return string + */ +function yang_toc_apply($content, $out, $items, $has_marker, $plan) +{ /* ── ① 手写标记:按作者写的位置渲染,且不再自动插第二份 ── */ if ($has_marker) { /* 后台设置当默认值,标记里写了的属性覆盖它。 @@ -786,12 +859,12 @@ function yang_toc_filter_content($content) return is_string($replaced) ? $replaced : $content; } - /* ── ② 侧栏模式:正文只补 id,目录由 wp_footer 输出 ── */ + /* ── ② 只出浮动:正文只补 id,目录由 wp_footer 输出 ── */ if ($plan['inline_at'] === null) { return $out; } - /* ── ③ 自动插入 ── */ + /* ── ③ 正文里插一份 ── */ $html = yang_toc_render($items, yang_toc_atts_from_settings($plan['settings'])); if ($html === '') { // 一个符合层级的标题都没有 —— 不输出空壳 @@ -799,11 +872,36 @@ function yang_toc_filter_content($content) } $pos = $plan['inline_at']; - $slot = '
' . $html . '
'; + $slot = yang_toc_slot_html($html, $pos, $plan['inline_align']); return yang_toc_inject($out, $slot, $pos); } -add_filter('the_content', 'yang_toc_filter_content', 9); + +/** + * 正文目录的外层盒子(带插入位置与对齐方向) + * + * 对齐为什么放在这一层、而不是 .yang-toc 上: + * · .yang-toc 自己已经有 24px 上下的外边距,再叠一层 margin 会叠加; + * · 靠左/靠右本质是「盒子在正文容器里怎么摆」,属于外层容器的职责; + * · 用 data 属性把方向带下去,CSS 只需一条 [data-align="right"] 规则, + * 不必再生成 -right 这种 class 变体(class 会越想越多)。 + * + * 注意靠左/靠右**不改变盒子的宽度**:目录块是否撑满正文栏由 .yang-toc + * 的样式决定(默认 auto,即撑满)。对齐只在它不撑满时才有可见效果, + * 所以这里同时给了 inline-size 相关的钩子,样式里再按需收窄。 + * + * @param string $html 目录 HTML + * @param string $pos start | first | end + * @param string $align left | right + * @return string + */ +function yang_toc_slot_html($html, $pos, $align = 'left') +{ + $align = ($align === 'right') ? 'right' : 'left'; + + return '
' . $html . '
'; +} /** * 把目录插进正文(纯函数) @@ -923,12 +1021,10 @@ function yang_toc_render_rail() if (!$post || !isset($post->post_content)) { return; } - // 作者写了 [TOC]:手写优先,不出侧栏 - if (yang_toc_has_marker($post->post_content)) { - return; - } - $plan = yang_toc_plan(false); + /* 手写 [TOC] 只管「正文那一份在哪」,不再连带关掉浮动面板。 + 两者现在是独立开关,作者写了标记也不等于说过「不要浮动目录」。 */ + $plan = yang_toc_plan(yang_toc_has_marker($post->post_content)); if (!$plan['rail']) { return; } diff --git a/style.css b/style.css index 35cf4a1..b6b8d01 100644 --- a/style.css +++ b/style.css @@ -1,7 +1,7 @@ /* Theme Name: Kratos_Yang Text Domain: kratos_Yang -Version: 3.2.30 +Version: 3.2.31 Requires PHP: 7.0 Description: 这是一个Kratos主题的分支版本,用来增加一些定制化功能。 Tags: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好