Files
laoyang 8f8cffd69e 3.2.43: 修代码块重影与语言识别,含 4 个静默 bug (#12)
排查结论:不是插件冲突,对手是主题自己的 kratos.css。

现象一「重影/阴影」的真实成因是 CSS 优先级 + 行高错位:
kratos.css 的 .content code(0,4,1) / .content pre code(0,4,2) 抢走了
border-radius:4px、color:#333、font-size:13px。字号被顶到 13px 后
行高算成 21.06px,而行号槽是 12.5px/20.25px,每行错开 0.81px,
30 行累计 24px —— 视觉上就是「两层东西叠在一起」。

现象二「多一个 copy 按钮」未能复现:2 个页面实测每张卡恰好 1 个
.cb-copy;Prism.plugins 只有 autoloader,copy-to-clipboard 未注册;
Enlighter 插件目录不存在。已请用户指认页面。

修复:
- highlight.css 的选择器提到与原主题同级 (0,4,2),靠出现更晚取胜,
  不用 !important。
- 顺带修掉 4 个静默 bug(都不报错、页面看着正常):
  1. header.php 从未调 body_class(),裸 <body> ⇒ 所有挂 body_class
     过滤器的类全部静默失效,包括行号开关 opt-ln(选项已开却不显示)。
  2. 79 个代码块的语言写在 <pre lang=> 或 data-enlighter-language 上,
     只读 <code> class 必然读不到 ⇒ 标签显示「—」。
  3. 白名单 4 项指向未打包语言(powershell/dos/dockerfile/nginx)。
     hljs 遇到不认识的语言不抛错,只 warn 一句就退回不高亮 ⇒
     整块白板。站内这 4 种用量均为 0,属潜伏 bug。
  4. hljs 把 plaintext 块改写成 language-undefined;highlightAuto
     对纯文本返回字符串 'undefined',if (r.language) 会误当真值。

顺带收敛:yang_hl_languages() 原本手写第二份清单,与 yang_hl_norm_lang()
里的表互相漂移,改为从 yang_hl_known_map() 单一来源派生。

验证:
- 单测 44 + 24 断言全通过
- 反向验证 10 组注入全部变红(含专门注入「恢复手写清单」验证新判据)
- 全站 284 块直连数据库校验:568 通过 / 0 失败,
  语言覆盖率 71.8% → 98.6%,残留与污染全 0
- 剩余 4 块显示「—」是正确降级(jsp×2、latex×2 未打包进 hljs)
- 夹具对照:新版 0 失败 vs 全旧版 10 失败
2026-10-26 11:00:47 +08:00

109 lines
5.7 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 主题页眉
* @author Yongquan Yang <yangyq.net>
* @license MIT License
* @version 2020.06.21
*/
?>
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title><?php wp_title( '-', true, 'right' ); ?></title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="format-detection" content="telphone=no, email=no">
<meta name="keywords" content="<?php echo keywords(); ?>">
<meta name="description" itemprop="description" content="<?php echo description(); ?>">
<meta name="theme-color" content="<?php echo kratos_option('g_chrome', '#282a2c'); ?>">
<?php
/* favicon:空值时落到主题包内的默认图标,**绝不输出空 href**。
标签由 yang_favicon_tag() 生成,测试直接测那个函数。 */
echo yang_favicon_tag();
?>
<?php wp_head(); wp_print_scripts('jquery'); ?>
</head>
<?php flush(); ?>
<body class="<?php echo esc_attr(implode(' ', get_body_class())); ?>">
<?php
/**
* wp_body_open 是 WordPress 5.2 引入的标准钩子,正文最顶部(且必须紧跟 <body>)。
* 主题之前没有调用它,导致所有挂在这个钩子上的东西(插件、主题自己的提示条)静默失效。
* 补上是必须的:不调用它不会报错,只会"什么都没发生",极难发现。
*
* 🔴 body_class() 曾经犯过一模一样的错:这里原来就是裸的 `<body>`,
* 根本没有调用 body_class()。后果不是报错,而是**所有**挂在
* `body_class` 过滤器上的类都不输出 —— 包括代码高亮的行号开关
* (.opt-ln)和评论头像的开关。表现是"后台勾了行号,前台一行都没有",
* 而 CSS、JS、选项值全是对的,只有 <body> 是空 class。
* 实测:修前 body.className === '',修后才有 opt-ln 等类。
* 判据不能只看"页面有没有报错",必须直接读 body 的 className。
*/
wp_body_open();
?>
<div class="k-header">
<nav class="k-nav navbar navbar-expand-lg navbar-light fixed-top" <?php if(kratos_option('top_select', 'banner') !== 'banner'){ echo 'style="background:' . kratos_option('top_color', '#24292e') .'"';} ?>>
<div class="container">
<a class="navbar-brand" href="<?php echo get_option('home'); ?>">
<?php
/* Logo 空着时不塞通用图,回落到站点名称文字 —— 一张人人
共用的占位 logo 会让每个站看起来一模一样,比文字更难看。
但不能用 kratos_option('g_logo') 直接判真假:用户在后台
把那一格清空后值是 ''、键却还在,isset 仍成立。
用 yang_option_image() 统一按"空串也算空"判断。 */
$yang_logo = yang_option_image('g_logo');
if ($yang_logo !== '') {
echo '<img src="' . esc_url($yang_logo) . '" alt="' . esc_attr(get_bloginfo('name')) . '">';
} else {
bloginfo('name');
}
?>
</a>
<?php if ( has_nav_menu('header_menu') ) { ?>
<button class="navbar-toggler navbar-toggler-right" id="navbutton" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
<span class="line first-line"></span>
<span class="line second-line"></span>
<span class="line third-line"></span>
</button>
<?php }
if ( has_nav_menu('header_menu') ) {
wp_nav_menu( array(
'theme_location' => 'header_menu',
/* 3.2.27:原来是 2 —— WP 只输出两层,二级子菜单
在 HTML 里根本不存在,"只显示一级菜单"就是它。
0 = 不限层数,能出几层由菜单管理器决定。 */
'depth' => 0,
'container' => 'div',
'container_class' => 'collapse navbar-collapse',
'container_id' => 'navbarResponsive',
'menu_class' => 'navbar-nav ml-auto',
'walker' => new WP_Bootstrap_Navwalker(),
) );
}
?>
</div>
</nav>
<?php if(kratos_option('top_select', 'banner') == 'banner'){ ?>
<div class="banner">
<div class="overlay"></div>
<div class="content text-center" style="background-image: url(<?php
/* 顶部图片导航的背景图。空值(含用户清空那一格)落到包内默认图。
CSS 是 height:300px + background-size:cover,默认图已按
1920x640 压好(见 _tooling 里的生成脚本)。 */
echo esc_url(yang_option_image('top_img')); ?>);">
<div class="introduce animated fadeInUp">
<?php
if (is_category() || is_tag()) {
echo '<div class="title">' . single_cat_title('', false) . '</div>';
echo '<div class="mate">' . strip_tags(category_description()) . '</div>';
} else {
echo '<div class="title">' . kratos_option('top_title', 'Kratos') . '</div>';
echo '<div class="mate">' . kratos_option('top_describe', __('一款专注于用户阅读体验的响应式博客主题', 'kratos')) . '</div>';
}
?>
</div>
</div>
</div>
<?php } ?>
</div>