diff --git a/inc/theme-options.php b/inc/theme-options.php
index 9eb79a1..8a4cb3f 100644
--- a/inc/theme-options.php
+++ b/inc/theme-options.php
@@ -520,9 +520,23 @@ function kratos_options()
),
);
+ /* 3.2.59:从「共用」分组搬到这里。它只作用于正文那份,
+ 摆在「正文里的目录」分组下才不会让人以为它也管浮层。 */
+ $options[] = array(
+ 'name' => __('正文目录的默认状态', 'kratos'),
+ 'desc' => __('默认显示:进来就能看到全部条目。默认隐藏:先只露出标题栏,读者自己点开。较长或标题很多的文章建议默认隐藏,否则目录会占掉一屏。
这一项只管正文里那一份;浮动目录的默认状态在下面「浮动目录」分组里单独设,两者互不影响。
注:这里只管「默认长什么样」,读者点开后仍然可以正常使用;正文里手写的 [TOC] 还能用 open="0" 单独指定,且只影响正文这份。', 'kratos'),
+ 'id' => 'toc_inline_default',
+ 'std' => 'show',
+ 'type' => 'select',
+ 'options' => array(
+ 'show' => __('默认显示', 'kratos'),
+ 'hide' => __('默认隐藏', 'kratos'),
+ ),
+ );
+
$options[] = array(
'name' => __('浮动目录', 'kratos'),
- 'desc' => __('下面两项只影响浮在页面边上那一份 —— 它不占正文位置,和正文里的目录互不相干。', 'kratos'),
+ 'desc' => __('下面几项只影响浮在页面边上那一份 —— 它不占正文位置,和正文里的目录互不相干。', 'kratos'),
'id' => 'toc_group_float',
'type' => 'info',
);
@@ -548,24 +562,24 @@ function kratos_options()
);
$options[] = array(
- 'name' => __('两份目录共用的设置', 'kratos'),
- 'desc' => __('下面几项对正文里和浮动的那一份同时生效。', 'kratos'),
- 'id' => 'toc_group_common',
- 'type' => 'info',
- );
-
- $options[] = array(
- 'name' => __('默认状态', 'kratos'),
- 'desc' => __('默认显示:进来就能看到全部条目。默认隐藏:先只露出标题栏(或侧边的标签),读者自己点开。较长或标题很多的文章建议默认隐藏,否则目录会占掉一屏。
注:这里只管「默认长什么样」,读者点开后仍然可以正常使用;正文里手写的 [TOC] 也可以用 open="0" 单独指定。', 'kratos'),
- 'id' => 'toc_default',
+ 'name' => __('浮层目录的默认状态', 'kratos'),
+ 'desc' => __('默认显示:一进文章,面板就已经滑出展开。默认隐藏:只留边上那片竖向小标签,读者点一下才滑出 —— 长文建议用这个,不然面板会一直挡着正文。
这一项只管浮动那一份,和正文目录的默认状态各设各的:想让正文展开、浮层收起,就这里选「默认隐藏」、上面选「默认显示」。
注:展开态直接写在页面 HTML 的 class 上,浏览器禁用 JS 也能看到正确的默认状态。', 'kratos'),
+ 'id' => 'toc_float_default',
'std' => 'show',
'type' => 'select',
'options' => array(
- 'show' => __('默认显示', 'kratos'),
- 'hide' => __('默认隐藏', 'kratos'),
+ 'show' => __('默认显示(面板展开)', 'kratos'),
+ 'hide' => __('默认隐藏(只留侧边标签)', 'kratos'),
),
);
+ $options[] = array(
+ 'name' => __('两份目录共用的设置', 'kratos'),
+ 'desc' => __('下面几项对正文里和浮动的那一份同时生效 —— 它们的标题文字、收录哪些级别、要不要加序号,两份保持一致。
「默认状态」不在这里了:正文那份和浮层那份现在各自单独设(见上面两个分组),因为「正文展开、浮层收起」是很常见的搭配。', 'kratos'),
+ 'id' => 'toc_group_common',
+ 'type' => 'info',
+ );
+
$options[] = array(
'name' => __('目录标题', 'kratos'),
'desc' => __('显示在目录顶部的那行字。留空则用「目录」。', 'kratos'),
diff --git a/inc/theme-toc.php b/inc/theme-toc.php
index fcfbefa..dbef528 100644
--- a/inc/theme-toc.php
+++ b/inc/theme-toc.php
@@ -115,7 +115,25 @@ function yang_toc_defaults()
'float_side' => 'left', // left | right(贴页面哪一边)
'mode' => 'manual', // manual | auto
'anchor' => 'first', // start | first | end(只对 inline 有效)
- 'default' => 'show', // show | hide
+
+ /* ── 默认开合:两份**各自独立**(3.2.59 起)───────────────
+ *
+ * 原来只有一个 'default',正文那份和浮层那份共用同一个值,
+ * 改一个另一个必然跟着变。用户要的是「浮层默认隐藏、正文默认显示」
+ * 这种组合,一个值表达不了。
+ *
+ * ⚠️ 'default' 不再出现在返回数组里,但它**仍然是被接受的输入键**
+ * (见 yang_toc_normalize_settings() 里的迁移段)。老配置、
+ * 老版本的子主题调用传进来都还能生效。
+ *
+ * 为什么不在返回值里保留 'default' 当"两者的投影":
+ * 那会变成一个可能跟两个新键不一致的影子值,
+ * 将来谁读到它就成了一个说不清来源的 bug。宁可让老键
+ * 在输出里彻底消失 —— 输入兼容、输出单一事实。
+ */
+ 'inline_default' => 'show', // show | hide(正文那份)
+ 'float_default' => 'show', // show | hide(浮层那份)
+
'title' => __('目录', 'kratos'),
'levels' => array('1' => 1, '2' => 1, '3' => 1, '4' => 0),
'numbering' => 0,
@@ -219,7 +237,46 @@ function yang_toc_normalize_settings($raw)
$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'));
+
+ /* ── 默认开合:两份各自独立 + 老配置迁移 ───────────────────────
+ *
+ * 3.2.59 把原来的单个 'default' 拆成 inline_default / float_default。
+ * 这里要同时满足四件事,少一件就会有人升级后设置被偷偷改掉:
+ *
+ * ① 新键存在且合法 → 用它
+ * ② 新键存在但值非法 → 回落该键的默认(不用老键顶,老键更可能是脏的)
+ * ③ 新键**不存在**、老键存在 → 老键的值(这就是迁移:老站点只存了
+ * toc_default,升级后两份都该保持原样)
+ * ④ 新旧都不存在 → 各键默认
+ *
+ * 🔴 为什么不能简单写成 $pick('inline_default', ...):
+ * $pick 的语义是「键不存在 ⇒ 取 $d[$key]」,也就是 ④。
+ * 那样一来 ③ 就落进 ④,老站点设了「默认隐藏」的,升级后
+ * 两份**全部变回 show**,而界面上还看不出任何异常 ——
+ * 这正是「静默重置用户配置」,比崩掉更难发现。
+ *
+ * 为什么顺序是「先看新键,再退回老键」而不是反过来:
+ * 用户一旦在 3.2.59 之后动过新下拉框,库里就有新键了,
+ * 此时老键成了历史残留 —— 必须让新键完全压过它,否则
+ * 用户「改成默认显示」保存后会发现没生效(老键还在那儿赢)。
+ */
+ $legacy = null;
+ if (array_key_exists('default', $raw) && in_array($raw['default'], array('show', 'hide'), true)) {
+ $legacy = $raw['default'];
+ }
+
+ $new_pair = array('inline_default', 'float_default');
+ $s['inline_default'] = $d['inline_default'];
+ $s['float_default'] = $d['float_default'];
+ foreach ($new_pair as $k) {
+ if (array_key_exists($k, $raw)) {
+ $s[$k] = in_array($raw[$k], array('show', 'hide'), true)
+ ? $raw[$k]
+ : $d[$k];
+ } elseif ($legacy !== null) {
+ $s[$k] = $legacy; // ③ 迁移:只存过老键的站点
+ }
+ }
$title = array_key_exists('title', $raw) ? $raw['title'] : $d['title'];
$title = is_string($title) ? trim(wp_strip_all_tags($title)) : '';
@@ -249,7 +306,13 @@ function yang_toc_settings()
$raw = array();
foreach (array('inline', 'inline_align', 'float', 'float_side',
- 'mode', 'anchor', 'default', 'title', 'levels', 'numbering') as $k) {
+ 'mode', 'anchor', 'title', 'levels', 'numbering',
+ 'inline_default', 'float_default',
+ /* 'default' 是 3.2.58 及更早版本的旧键,**必须继续读**:
+ 不读就不知道老配置里存了什么,迁移段拿不到值,
+ 老站点升级后默认开合会被重置成 show。
+ 读进来之后由 normalize 的迁移段消化掉,不会出现在输出里。 */
+ 'default') as $k) {
$raw[$k] = kratos_option('toc_' . $k, null);
}
// 库里没有的键用 null 占位,normalize 时按「未配置」回落默认值
@@ -263,6 +326,80 @@ function yang_toc_settings()
return $cache;
}
+/**
+ * 后台表单读设置时,把老键 toc_default 的值"投影"到两个新键上
+ *
+ * ── 为什么需要这一层(3.2.59,工单 #27,实测出来的问题)───────────
+ *
+ * 迁移在 yang_toc_normalize_settings() 里已经做对了:老站点只存
+ * toc_default=hide 时,页面**实际**渲染成 hide。
+ *
+ * 但后台表单不是走 normalize 的 —— 框架(class-options-interface.php:87-96)
+ * 的规则是:
+ * $val = $value['std']; // 先取 std
+ * if (isset($settings[$id])) { $val = $settings[$id]; } // 库里有就覆盖
+ * 老站点库里**没有** toc_inline_default 这个键 ⇒ `$val` 停在 std=show。
+ *
+ * 结果就是(真机上实测到的):
+ * 页面实际渲染:hide
+ * 后台下拉框: show ← 错位
+ *
+ * 用户看到下拉框写着「默认显示」,会以为自己的设置丢了;而且他若不
+ * 主动去改,就永远不会触发保存,这个错位会一直存在。
+ * 更糟的是"看起来是 show、实际是 hide",两边单独看都正常。
+ *
+ * ── 怎么做:内存里投影,不写库 ─────────────────────────────────
+ * 用 `option_kratos` filter(WP 核心在 get_option 之后触发,**只影响读取**),
+ * 把老键的值补进缺的两个新键。库不动 —— 这样:
+ * · 用户没保存:每次读都投影一次,显示始终和页面一致
+ * · 用户保存了:框架写库时带上新键,之后投影自动失效(他动过了)
+ * · 想回滚到旧版:库里从未被加过新键,旧版读 toc_default 一切照旧
+ *
+ * 🔴 为什么不用 `pre_option_kratos`:那个 filter 在 get_option **之前**,
+ * 拿到的是 null,得自己去查库,等于绕开缓存重查一次。
+ * `option_kratos` 拿到的已经是解析好的数组,改完直接返回即可。
+ *
+ * 🔴 为什么不放在 normalize 里做:normalize 是纯函数,它的职责是
+ * 「输入 → 可渲染的设置」。让它去改后台表单看到的东西,是把
+ * 「渲染语义」和「配置界面语义」混在一起。这里用一层 filter 分开,
+ * 哪一层出问题都能单独定位。
+ *
+ * @param mixed $value get_option('kratos') 的返回值
+ * @return mixed
+ */
+function yang_toc_admin_legacy_values($value)
+{
+ if (!is_array($value)) {
+ return $value;
+ }
+
+ /* 老键必须存在且合法,否则没有可投影的值 */
+ if (!array_key_exists('toc_default', $value)) {
+ return $value;
+ }
+ $legacy = $value['toc_default'];
+ if (!in_array($legacy, array('show', 'hide'), true)) {
+ return $value;
+ }
+
+ /* 只补**缺失**的键。已经有的不动 —— 用户动过新下拉框之后,
+ 新键才是真相,老键是历史残留。 */
+ foreach (array('toc_inline_default', 'toc_float_default') as $k) {
+ if (!array_key_exists($k, $value)) {
+ $value[$k] = $legacy;
+ }
+ }
+
+ return $value;
+}
+
+/* 只在后台挂。前台也挂上的话,会让 kratos_option() 读到的值多出两个
+ 本来不存在的键 —— 虽然无害(normalize 有白名单),但那是没有理由的
+ 行为改变,且会让前台与"库里真实内容"不再一致。 */
+if (is_admin()) {
+ add_filter('option_kratos', 'yang_toc_admin_legacy_values');
+}
+
/**
* 把设置翻译成渲染参数(与短代码属性同名同格式)
*
@@ -293,7 +430,9 @@ function yang_toc_atts_from_settings($s = null)
'levels' => implode(',', $levels),
'numbering' => !empty($s['numbering']) ? '1' : '0',
'toggle' => '1',
- 'open' => $s['default'] === 'show' ? '1' : '0',
+ /* 正文那份默认开合。只影响正文内嵌的 ;
+ 浮层走 yang_toc_rail_html() 自己的 float_default,两条路互不引用。 */
+ 'open' => $s['inline_default'] === 'show' ? '1' : '0',
);
}
@@ -979,7 +1118,10 @@ function yang_toc_rail_html($items, $plan)
}
$side = ($plan['side'] === 'right') ? 'right' : 'left';
- $open = ($s['default'] === 'show');
+ /* 浮层那份默认开合,**独立于**正文那份(3.2.59 起)。
+ 展开态写在 class 上(is-open),所以没有 JS 也能看到默认状态;
+ JS 只负责之后的切换 —— 这个特性由 3.2.30 定下,拆分后必须保留。 */
+ $open = ($s['float_default'] === 'show');
$title = $s['title'];
$cls = 'yang-toc-rail yang-toc-rail-' . $side . ($open ? ' is-open' : '');
diff --git a/style.css b/style.css
index 15f5b4d..32779a9 100644
--- a/style.css
+++ b/style.css
@@ -1,7 +1,7 @@
/*
Theme Name: Kratos_Yang
Text Domain: kratos_Yang
-Version: 3.2.58
+Version: 3.2.59
Requires PHP: 7.0
Description: 这是一个Kratos主题的分支版本,用来增加一些定制化功能。
Tags: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好