3.2.44: 后台配色实时预览 + 全站代码块语言标注统一 #13
需求 A —— 主题设置里选「代码配色」时能当场看到效果: - 新增 assets/css/highlight-admin.css:只负责预览区布局,不改代码块外观 - 新增 assets/js/highlight-admin.js:监听下拉框,删旧 link 再插新 link, 回调里重新上色 - inc/theme-highlight.php:新增 yang_hl_admin_assets()/yang_hl_admin_preview_html() - inc/theme-options.php:在配色下拉框后插入 type=info 的预览面板 几个关键取舍(都有实测依据): - 切换配色必须真正删掉旧 <link>。20 套配色的规则是裸选择器 (.hljs-keyword 之类),同场会互相压制,切换看起来像「没反应」。 - 预览复用前台同一份 CSS,另写一套必然与前台不一致。 - 不用 highlightElement(会在已上色元素上再套一层,颜色变脏), 改为从 textContent 取原文重新 highlight。 - 实测 hljs 的 HTML 转义无损(392 字符逐字符可还原),所以不需要额外的 data-raw 暂存;那是两份状态,不同步就是 bug 源头。已删该冗余。 需求 B —— 全站代码块语言标注统一(数据侧,不在本次 diff 里): - 284 块 / 71 篇全部统一为 <pre lang="xxx">,清掉 Enlighter 私有标记与 code 上的 language-* 类 - 关键发现:站内没有「完全无标注」的块(0 个),但有 87 块标注无效 (text 47 / generic 28 / raw 8 / jsp 2 / latex 2)—— 问题不是没标,是标是假的 - 87 块逐块判定:bash 174、plaintext 45、yaml 13、ini 9、xml 8、python 8、 javascript 7、php 6、json 5、java 5、ruby 2、sql 2 - 判定表优先级必须高于「沿用旧标注」:产品把 generic/raw/text 兜底成 plaintext,而 plaintext 在白名单里,旧逻辑会走 keep 把判定表整批丢弃 (第一版实测 87 条只有 4 条生效,且结果看起来是成功的) - 同一篇文章里有 11 对块原文完全相同,str_replace 会全局替换导致整片标错; 改为按文内序号 + 内容签名定位 验证: - 后台预览 jsdom 测试 22/0,反向注入 6 组全部真红 - 语言/属性单测 44/0 与 24/0,语言反向 10/0 - 独立审计脚本(另一套扫描逻辑)全绿;反向 3/3 真红 (去掉 lang / 改成 cobol / 塞回 enlighter 各自都能抓出来) - 真实页面 4 篇:data-guess 全为 0,正文里已无「猜出来的」代码块
This commit is contained in:
@@ -0,0 +1,113 @@
|
|||||||
|
/**
|
||||||
|
* 后台设置页:代码配色预览区布局
|
||||||
|
*
|
||||||
|
* 🔴 这个文件**只负责把预览框摆好**,绝不改代码块的外观。
|
||||||
|
* 代码块的一切外观(背景、边框、token 颜色)都来自两个前台文件:
|
||||||
|
* assets/hljs/styles/<配色>.min.css → token 颜色
|
||||||
|
* assets/css/highlight.css → 卡片骨架
|
||||||
|
* 在这里另写一套样式会让"预览"和"前台"不一致,比没有预览更误导人。
|
||||||
|
*
|
||||||
|
* 唯一必须在这里做的是**作用域隔离**:后台页面没有正文容器
|
||||||
|
* (.k-main .details .article .content),highlight.css 里大量规则带这个前缀,
|
||||||
|
* 单看 .cb 的规则在后台也能生效(那些规则都写了无前缀的兜底选择器),
|
||||||
|
* 但行号槽之类的尺寸依赖父级字号,所以这里要显式给一个正常的正文环境。
|
||||||
|
*/
|
||||||
|
|
||||||
|
.yang-hl-preview {
|
||||||
|
margin: 12px 0 4px;
|
||||||
|
padding: 14px 16px 10px;
|
||||||
|
border: 1px solid #dcdcde;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #fff;
|
||||||
|
max-width: 860px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yang-hl-preview-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 1px solid #f0f0f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yang-hl-preview-title {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1d2327;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yang-hl-preview-name {
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2271b1;
|
||||||
|
padding: 1px 7px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #f0f6fc;
|
||||||
|
border: 1px solid #c5d9ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yang-hl-preview-hint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #646970;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yang-hl-preview-note {
|
||||||
|
margin: 8px 0 0;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #646970;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 预览里的卡片:
|
||||||
|
* ① 给一个明确的字号基准(后台正文字号是 13px,代码用 12.5px 与前台一致)
|
||||||
|
* ② 限制高度,样例只有 13 行不需要滚动,但要防止窄屏下溢出 */
|
||||||
|
.yang-hl-preview .cb {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yang-hl-preview .cb pre,
|
||||||
|
.yang-hl-preview .cb code {
|
||||||
|
font-size: 12.5px;
|
||||||
|
line-height: 1.62;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 复制按钮在预览里没有实际功能(disabled),视觉上弱化,避免用户以为能点 */
|
||||||
|
.yang-hl-preview .cb-copy[disabled] {
|
||||||
|
opacity: .45;
|
||||||
|
cursor: default;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 后台配色切换时,卡片边框跟着深浅走,避免"深色代码 + 白边框"的割裂感。
|
||||||
|
* 这里只调边框和顶栏,token 颜色一律交给配色 CSS。 */
|
||||||
|
.yang-hl-preview .cb[data-kind="dark"] {
|
||||||
|
border-color: #3a3f46;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yang-hl-preview .cb[data-kind="dark"] .cb-bar {
|
||||||
|
background: #21262d;
|
||||||
|
border-bottom-color: #3a3f46;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yang-hl-preview .cb[data-kind="dark"] .cb-lang {
|
||||||
|
color: #adbac7;
|
||||||
|
background: #2d333b;
|
||||||
|
border-color: #444c56;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yang-hl-preview .cb[data-kind="dark"] .cb-body {
|
||||||
|
background: #22272e;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 782px) {
|
||||||
|
.yang-hl-preview {
|
||||||
|
padding: 10px 10px 8px;
|
||||||
|
}
|
||||||
|
.yang-hl-preview-hint {
|
||||||
|
flex-basis: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
/**
|
||||||
|
* 后台设置页:代码配色实时预览
|
||||||
|
*
|
||||||
|
* 做什么:监听「代码配色」下拉框,切换时立刻把预览卡片的配色换掉,
|
||||||
|
* 不用保存、不用刷新。
|
||||||
|
*
|
||||||
|
* 🔴 关键实现:**删掉旧的 <link>、插入新的 <link>**,而不是把所有配色
|
||||||
|
* CSS 都加载进来再靠 class 切换。
|
||||||
|
* 原因有两个:
|
||||||
|
* ① 20 套配色合计约 50KB,且它们的规则是"裸选择器"(.hljs / .hljs-keyword),
|
||||||
|
* 全部同时在场时会互相压制 —— 后加载的赢,切换看起来像"没变"。
|
||||||
|
* ② 前台就是只加载选中的一套(见 yang_hl_enqueue)。后台必须用同样的
|
||||||
|
* 加载方式,才可能得到同样的结果。多套共存时你看到的不一定是前台效果。
|
||||||
|
*
|
||||||
|
* 🔴 换完 CSS 必须重新给 token 上色。时序上有个坑:
|
||||||
|
* <link> 插入后**样式不是立刻可用**的,浏览器要异步加载。
|
||||||
|
* 直接同步去读 getComputedStyle 拿到的是旧配色。
|
||||||
|
* 正确做法是等 link.onload 之后再做高亮(或者干脆重新跑一次 hljs)。
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var cfg = window.YANG_HL_ADMIN || null;
|
||||||
|
if (!cfg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var LINK_ID = 'yang-hl-preview-style';
|
||||||
|
var PAGE_SIZE = 1; /* link 只用一条,避免多条同 id 并存 */
|
||||||
|
|
||||||
|
/** 当前预览容器 */
|
||||||
|
function previewBox() {
|
||||||
|
return document.querySelector('.yang-hl-preview');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 「代码配色」下拉框 —— 框架给 select 的 id 就是选项 id */
|
||||||
|
function themeSelect() {
|
||||||
|
return document.getElementById('a_highlight_theme');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把预览的配色换成 key 对应的那套
|
||||||
|
*
|
||||||
|
* @param {string} key 配色 key,如 'monokai'
|
||||||
|
* @param {boolean} noRecolor 只换 CSS 不重新上色(初始化时用,避免多余计算)
|
||||||
|
*/
|
||||||
|
function applyTheme(key, noRecolor) {
|
||||||
|
if (!key) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var box = previewBox();
|
||||||
|
var sel = themeSelect();
|
||||||
|
|
||||||
|
/* 先移除旧的 link。必须真正从 DOM 上拿掉,不能只改 href ——
|
||||||
|
* 改 href 也有效,但连续快速切换时浏览器可能复用已完成的旧请求,
|
||||||
|
* 出现"切了三次只变了一次"的观感。移除重建最稳。 */
|
||||||
|
var old = document.getElementById(LINK_ID);
|
||||||
|
if (old && old.parentNode) {
|
||||||
|
old.parentNode.removeChild(old);
|
||||||
|
}
|
||||||
|
|
||||||
|
var link = document.createElement('link');
|
||||||
|
link.id = LINK_ID;
|
||||||
|
link.rel = 'stylesheet';
|
||||||
|
link.type = 'text/css';
|
||||||
|
link.href = cfg.styleBase + key + '.min.css?v=' + encodeURIComponent(cfg.ver || '');
|
||||||
|
|
||||||
|
/* 配色深/浅决定卡片边框、顶栏的颜色(token 颜色由 CSS 自己管)。
|
||||||
|
* 这个必须在这里切,否则会看到"Monokai 的深色字 + GitHub 的浅色顶栏"。 */
|
||||||
|
var kind = (key.indexOf('dark') >= 0 || /^(monokai|night-owl|srcery|a11y-dark|tomorrow-night-blue|panda-syntax-dark)$/.test(key))
|
||||||
|
? 'dark' : 'light';
|
||||||
|
|
||||||
|
if (box) {
|
||||||
|
box.setAttribute('data-theme-key', key);
|
||||||
|
var card = box.querySelector('.cb');
|
||||||
|
if (card) {
|
||||||
|
card.className = 'cb hl-' + key;
|
||||||
|
card.setAttribute('data-theme', key);
|
||||||
|
card.setAttribute('data-kind', kind);
|
||||||
|
}
|
||||||
|
var nameEl = box.querySelector('[data-theme-name]');
|
||||||
|
if (nameEl && sel) {
|
||||||
|
var opt = sel.options[sel.selectedIndex];
|
||||||
|
if (opt) {
|
||||||
|
/* 选项文案形如「Monokai(深色)」,预览里只显示前半段 */
|
||||||
|
nameEl.textContent = String(opt.textContent || '').replace(/([^)]*)\s*$/, '').trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 等 CSS 真正生效后再重新上色。
|
||||||
|
* 用 onload 而不是 setTimeout:网络快时不用白等,慢时也不会提前上色。 */
|
||||||
|
link.onload = function () {
|
||||||
|
if (!noRecolor) {
|
||||||
|
recolor();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
link.onerror = function () {
|
||||||
|
/* 加载失败就让预览保持原样,不要清空 —— 清空会让人以为主题坏了 */
|
||||||
|
if (window.console && console.warn) {
|
||||||
|
console.warn('[yang-hl] 配色样式加载失败: ' + link.href);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.head.appendChild(link);
|
||||||
|
|
||||||
|
/* 兜底:onload 在某些缓存命中场景下不一定触发。
|
||||||
|
* 这里再排一次微延时重上色,保证最终一定是一致的。
|
||||||
|
* 两次 recolor 是幂等的(内部先清空再写),不会叠加出问题。 */
|
||||||
|
setTimeout(function () {
|
||||||
|
if (!noRecolor) {
|
||||||
|
recolor();
|
||||||
|
}
|
||||||
|
}, 120);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新给预览里的代码上色
|
||||||
|
*
|
||||||
|
* 为什么不直接用 highlightElement:它会在已经处理过的元素上再处理一次,
|
||||||
|
* 把已有的 token span 当成纯文本再套一层,颜色会变得很脏。
|
||||||
|
* 正确做法是**从 textContent 取原文重新 highlight**,把结果整体替换回去。
|
||||||
|
*
|
||||||
|
* 🔴 为什么可以直接用 textContent、不需要额外存一份 data-raw:
|
||||||
|
* hljs 输出的 HTML 转义是**无损**的(实测 392 字符原文,上色后
|
||||||
|
* textContent 回读 392 字符、逐字符相同),所以每次都能从当前
|
||||||
|
* DOM 拿到干净的原文,重算是幂等的。多存一份反而是冗余状态,
|
||||||
|
* 两份一旦不同步就是 bug 源头。
|
||||||
|
*/
|
||||||
|
function recolor() {
|
||||||
|
var code = document.querySelector('.yang-hl-preview code[data-sample]');
|
||||||
|
if (!code || !window.hljs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var raw = code.textContent;
|
||||||
|
var lang = code.getAttribute('data-sample') || 'php';
|
||||||
|
var res;
|
||||||
|
try {
|
||||||
|
res = window.hljs.highlight(raw, { language: lang, ignoreIllegals: true });
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
code.innerHTML = res.value;
|
||||||
|
code.className = 'hljs language-' + lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 初始化 */
|
||||||
|
function init() {
|
||||||
|
var sel = themeSelect();
|
||||||
|
if (!sel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 初始配色:页面已经由 PHP 输出了选中那套的 CSS(前台样式不在这里加载,
|
||||||
|
* 所以这里要主动插一次;noRecolor=true 让首次不重复计算) */
|
||||||
|
applyTheme(sel.value, true);
|
||||||
|
|
||||||
|
sel.addEventListener('change', function () {
|
||||||
|
applyTheme(sel.value, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
/* 有些场景下框架会程序化改 select.value(比如"恢复默认"),
|
||||||
|
* change 事件不会触发 ⇒ 监听 click 兜底 */
|
||||||
|
sel.addEventListener('click', function () {
|
||||||
|
/* 点击展开下拉不算切换,交给 change 处理 */
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
})();
|
||||||
+135
-1
@@ -685,7 +685,7 @@ function yang_hl_enqueue()
|
|||||||
* (本项目历史上被 OPcache/缓存这类"文件名对、内容旧"坑过多次)。
|
* (本项目历史上被 OPcache/缓存这类"文件名对、内容旧"坑过多次)。
|
||||||
* 读主题头用 wp_get_theme()->get('Version'),WordPress 自带缓存,无额外开销。 */
|
* 读主题头用 wp_get_theme()->get('Version'),WordPress 自带缓存,无额外开销。 */
|
||||||
$theme = wp_get_theme();
|
$theme = wp_get_theme();
|
||||||
$ver = $theme && $theme->get('Version') ? $theme->get('Version') : '3.2.43';
|
$ver = $theme && $theme->get('Version') ? $theme->get('Version') : '3.2.44';
|
||||||
$uri = get_template_directory_uri();
|
$uri = get_template_directory_uri();
|
||||||
$key = yang_hl_theme_key();
|
$key = yang_hl_theme_key();
|
||||||
|
|
||||||
@@ -738,3 +738,137 @@ function yang_hl_enqueue()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
add_action('wp_enqueue_scripts', 'yang_hl_enqueue', 20);
|
add_action('wp_enqueue_scripts', 'yang_hl_enqueue', 20);
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════════
|
||||||
|
* 后台设置页:代码配色实时预览
|
||||||
|
*
|
||||||
|
* 为什么要做:后台「代码配色」是个下拉框,20 个名字(GitHub / Monokai /
|
||||||
|
* Night Owl…)光看名字根本不知道长什么样。原来的流程是
|
||||||
|
* 选一个 → 保存 → 去前台刷新 → 不满意 → 回来再选
|
||||||
|
* 来回几次就放弃了,最后一直用默认那套。
|
||||||
|
*
|
||||||
|
* 做法:在选项页里放一个**真代码块卡片**,配色 CSS 用 JS 动态换。
|
||||||
|
*
|
||||||
|
* 🔴 关键约束:预览必须加载**前台同一份 CSS**(assets/hljs/styles/*.min.css
|
||||||
|
* 和 assets/css/highlight.css),绝不能在这里另写一套"预览专用样式"。
|
||||||
|
* 另写一套的后果是:预览好看、前台不一样(或反过来),比没有预览更糟。
|
||||||
|
*
|
||||||
|
* 🔴 20 套配色不能一次性全加载到后台,否则规则互相压制、看不出切换效果
|
||||||
|
* (前台也是只加载选中的一套,见 yang_hl_enqueue 的注释)。
|
||||||
|
* 这里用「删掉旧的 <link>、插入新的」的方式模拟真实加载顺序。
|
||||||
|
* ══════════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后台选项页加载预览所需资源
|
||||||
|
*
|
||||||
|
* @param string $hook 当前后台页面
|
||||||
|
*/
|
||||||
|
function yang_hl_admin_assets($hook)
|
||||||
|
{
|
||||||
|
if (strpos((string) $hook, 'theme') === false && strpos((string) $hook, 'option') === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$dir = get_template_directory();
|
||||||
|
$uri = get_template_directory_uri();
|
||||||
|
$theme = wp_get_theme();
|
||||||
|
$ver = $theme && $theme->get('Version') ? $theme->get('Version') : '3.2.44';
|
||||||
|
|
||||||
|
// 卡片骨架(与前台同一个文件)
|
||||||
|
wp_enqueue_style('yang-hl-admin-card',
|
||||||
|
$uri . '/assets/css/highlight.css', array(), $ver);
|
||||||
|
|
||||||
|
// 预览区自身的布局样式(只负责把卡片放在框里,不碰代码块外观)
|
||||||
|
wp_enqueue_style('yang-hl-admin-preview',
|
||||||
|
$uri . '/assets/css/highlight-admin.css', array('yang-hl-admin-card'), $ver);
|
||||||
|
|
||||||
|
wp_enqueue_script('yang-hl-admin-preview',
|
||||||
|
$uri . '/assets/js/highlight-admin.js', array(), $ver, true);
|
||||||
|
|
||||||
|
// 把 20 套配色的 key 和基准 URL 传给 JS
|
||||||
|
wp_localize_script('yang-hl-admin-preview', 'YANG_HL_ADMIN', array(
|
||||||
|
'styleBase' => $uri . '/assets/hljs/styles/',
|
||||||
|
'themes' => array_keys(yang_hl_themes()),
|
||||||
|
'ver' => $ver,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
add_action('admin_enqueue_scripts', 'yang_hl_admin_assets');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成后台预览用的 HTML(选项页的 info 块直接输出)
|
||||||
|
*
|
||||||
|
* 预览内容刻意覆盖多种 token:关键字、字符串、注释、数字、函数名、变量。
|
||||||
|
* 只给一行 `print("hi")` 看不出配色差异 —— 大部分主题的区别在注释色、
|
||||||
|
* 字符串色、数字色上。
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function yang_hl_admin_preview_html()
|
||||||
|
{
|
||||||
|
$key = yang_hl_theme_key();
|
||||||
|
$themes = yang_hl_themes();
|
||||||
|
$name = isset($themes[$key]) ? $themes[$key][0] : $key;
|
||||||
|
$kind = isset($themes[$key]) ? $themes[$key][1] : 'light';
|
||||||
|
|
||||||
|
// 样例代码:一段有代表性的 PHP,含注释/关键字/字符串/数字/函数/变量
|
||||||
|
$sample = "<?php\n"
|
||||||
|
. "// 从数据库取出最近 7 天的访问量\n"
|
||||||
|
. "\$days = 7;\n"
|
||||||
|
. "\$rows = \$wpdb->get_results(\n"
|
||||||
|
. " \"SELECT DATE(post_date) AS d, COUNT(*) AS n\n"
|
||||||
|
. " FROM {\$wpdb->posts}\n"
|
||||||
|
. " WHERE post_date > NOW() - INTERVAL 7 DAY\n"
|
||||||
|
. " GROUP BY d ORDER BY d\"\n"
|
||||||
|
. ");\n"
|
||||||
|
. "\n"
|
||||||
|
. "foreach (\$rows as \$r) {\n"
|
||||||
|
. " printf(\"%s 访问 %d 次\\n\", \$r->d, \$r->n);\n"
|
||||||
|
. "}\n";
|
||||||
|
|
||||||
|
/* 🔴 行数算法必须与 yang_hl_render_block 完全一致:
|
||||||
|
* substr_count("\n") 对 "a\nb\nc\n" 得到 3,但**实际只画 3 行**
|
||||||
|
* (末尾换行不产生新行盒)。所以不能无条件 +1。
|
||||||
|
*
|
||||||
|
* 本函数第一版写成 `substr_count($sample, "\n") + 1` ⇒ 13 行的样例
|
||||||
|
* 算出 14 个行号,比代码多一行 —— 由 _admin_preview_test.js 抓出。
|
||||||
|
* 这正是本项目红线的老坑:"行号条数 == 换行符数" 在末尾有换行时恒假。 */
|
||||||
|
$n = substr_count($sample, "\n");
|
||||||
|
if (substr($sample, -1) !== "\n") {
|
||||||
|
$n++;
|
||||||
|
}
|
||||||
|
$lines = max(1, $n);
|
||||||
|
$gutter = '';
|
||||||
|
for ($i = 1; $i <= $lines; $i++) {
|
||||||
|
$gutter .= '<span>' . $i . '</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 服务端就把 token 上好色,再交给 hljs 覆盖一次 ——
|
||||||
|
// 保证 JS 还没跑的时候也有颜色,不会先看到一坨黑字再变色
|
||||||
|
$escaped = esc_html($sample);
|
||||||
|
|
||||||
|
$out = '<div class="yang-hl-preview" data-theme-key="' . esc_attr($key) . '">';
|
||||||
|
$out .= '<div class="yang-hl-preview-head">';
|
||||||
|
$out .= '<span class="yang-hl-preview-title">实时预览</span>';
|
||||||
|
$out .= '<span class="yang-hl-preview-name" data-theme-name>' . esc_html($name) . '</span>';
|
||||||
|
$out .= '<span class="yang-hl-preview-hint">切换上面的配色,这里立刻跟着变(不用保存)</span>';
|
||||||
|
$out .= '</div>';
|
||||||
|
|
||||||
|
$out .= '<figure class="cb hl-' . esc_attr($key) . '"'
|
||||||
|
. ' data-lang="php" data-theme="' . esc_attr($key) . '"'
|
||||||
|
. ' data-kind="' . esc_attr($kind) . '" data-guess="0">';
|
||||||
|
$out .= '<figcaption class="cb-bar">';
|
||||||
|
$out .= '<span class="cb-lang">PHP</span>';
|
||||||
|
$out .= '<span class="cb-actions"><button type="button" class="cb-copy" data-copy disabled>复制</button></span>';
|
||||||
|
$out .= '</figcaption>';
|
||||||
|
$out .= '<div class="cb-body">';
|
||||||
|
$out .= '<div class="cb-ln" aria-hidden="true">' . $gutter . '</div>';
|
||||||
|
$out .= '<pre class="hljs"><code class="hljs language-php" data-sample="php">' . $escaped . '</code></pre>';
|
||||||
|
$out .= '</div></figure>';
|
||||||
|
|
||||||
|
$out .= '<p class="yang-hl-preview-note">'
|
||||||
|
. '预览用的是一段 PHP 样例,包含注释、关键字、字符串、数字、变量 —— '
|
||||||
|
. '不同配色的差别主要体现在这几种颜色上。'
|
||||||
|
. '</p>';
|
||||||
|
$out .= '</div>';
|
||||||
|
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1196,6 +1196,25 @@ function kratos_options()
|
|||||||
'requires' => 'a_highlight_enable',
|
'requires' => 'a_highlight_enable',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/* 配色预览:紧跟在配色下拉框后面
|
||||||
|
*
|
||||||
|
* 为什么需要:20 套配色只有名字(GitHub / Monokai / Night Owl…),
|
||||||
|
* 光看名字不知道长什么样。原来的流程是
|
||||||
|
* 选一个 → 保存 → 去前台刷新 → 不满意 → 回来再选,
|
||||||
|
* 来回几次就放弃、最后一直用默认那套。
|
||||||
|
*
|
||||||
|
* 这里放一张**真代码块卡片**,切换下拉框立刻换色,不用保存。
|
||||||
|
* 用的是前台同一份 CSS(配色 min.css + highlight.css),不另写一套 ——
|
||||||
|
* 另写一套会导致预览和前台不一致,比没有预览更误导人。 */
|
||||||
|
if (function_exists('yang_hl_admin_preview_html')) {
|
||||||
|
$options[] = array(
|
||||||
|
'name' => '',
|
||||||
|
'desc' => yang_hl_admin_preview_html(),
|
||||||
|
'type' => 'info',
|
||||||
|
'class' => 'yang-hl-preview-wrap',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$options[] = array(
|
$options[] = array(
|
||||||
'name' => __('显示行号', 'kratos'),
|
'name' => __('显示行号', 'kratos'),
|
||||||
'desc' => __('<b>默认关闭。</b>'
|
'desc' => __('<b>默认关闭。</b>'
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Theme Name: Kratos_Yang
|
Theme Name: Kratos_Yang
|
||||||
Text Domain: kratos_Yang
|
Text Domain: kratos_Yang
|
||||||
Version: 3.2.43
|
Version: 3.2.44
|
||||||
Requires PHP: 7.0
|
Requires PHP: 7.0
|
||||||
Description: 这是一个Kratos主题的分支版本,用来增加一些定制化功能。
|
Description: 这是一个Kratos主题的分支版本,用来增加一些定制化功能。
|
||||||
Tags: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好
|
Tags: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好
|
||||||
|
|||||||
Reference in New Issue
Block a user