代码高亮整套自研:10 浅 + 10 深配色、语言标签、评论区贴代码 (#11)

不再依赖第三方高亮插件,全部由主题自己实现。

主题配色(20 套)
- 浅色 10:github / vs / atom-one-light / idea / xcode /
  stackoverflow-light / intellij-light / googlecode / magula / color-brewer
- 深色 10:github-dark / atom-one-dark / monokai / github-dark-dimmed /
  a11y-dark / stackoverflow-dark / night-owl / srcery /
  panda-syntax-dark / tomorrow-night-blue
- 深色卡片带 data-kind="dark",顶栏跟着换深色底
- 只加载当前选中的一套(20 套全加载会让后出现的规则压前面的,
  切换配色容易"看起来没变")

代码语言标签(用户要求:防止复制错语言)
- 作者标了 language-xxx → 实线边框、正体
- 自动识别猜出来的 → 虚线边框、斜体
- 认不出来 → 显示 "—",仍保持虚线斜体(不冒充作者标注)

行号
- 默认关;行号槽在 <pre> 外面,复制天然不带行号
- 改掉原主题的 word-break:break-all(任意字符断行,代码像乱码)
- 归零 hljs 主题自带的 pre code.hljs{padding:1em},
  否则代码被压低 1em、与行号槽错位 15.5px

复制
- 默认开;安全上下文判断 + execCommand 退路(HTTP 下 clipboard 是 undefined)
- 取 textContent 而非 innerHTML
- 默认 opacity:0、hover 显示、触屏覆盖回 1
  (旧写法只在 @media(hover:none) 里写 1,等于空操作)

自动识别
- 做成选项,默认开
- 只对没标语言的块生效;站内实测长块准确率 22%,不能覆盖作者标注

评论区贴代码
- 评论工具条加"代码"按钮 + 29 种语言面板
- kses 放行 pre 并给 pre/code 放开 class
  (WP 默认 pre 整条剥掉、code 属性全清,此前根本贴不了代码)
- 放行 pre 之后 wpautop/wptexturize/make_clickable 自动让路,
  代码里的连续空行、引号、URL 都不会被改写
- comments.js 用占位符保护代码块,避免 walk() 的 /\n{3,}/ 压缩

其它
- 资源版本号改为读主题头,不再写死(写死会导致以后升级后
  浏览器一直用旧缓存,改了 CSS/JS 完全不生效)
- 删除 footer.php 里旧的 Prism 兜底(两套高亮器同页会互相打架)

验收
- 用线上 284 个真实代码块跑产品代码:284/284 全部变卡片,内容丢字 0、双转义 0
- 47 个 text 标记全部映射到 plaintext(hljs 无 text 语言,不映射会静默不上色)
- 80 个裸 <pre>(无 code 子元素)全部覆盖
- 核心纯函数 65/65;核心反向 10/10 变红
- 浏览器端到端 32/32;行号几何逐行对齐偏差 0.00px,几何反向 4/4 变红
- 端到端反向 7/7 变红;语言标签反向 2/2 变红
- 全量 PHP 语法 95 文件 0 错误

发布:走 theme-update 通道(zip + readme + update.json),
未覆盖服务器主题目录;线上主题目录仍是 3.2.41,待后台升级。
This commit is contained in:
2026-10-26 09:00:17 +08:00
parent f3583e07c5
commit da0d9ae4a1
32 changed files with 2784 additions and 23 deletions
+76 -1
View File
@@ -1549,4 +1549,79 @@
.yang-comment-content {
color: #000;
}
}
}
/* ── 3.2.42 代码块按钮与面板 ─────────────────────────────────
* 访客讨论正文里的代码时需要贴代码回来。此前这件事**做不到**:
* pre 不在 kses 白名单里(整条被剥)、code 的 class 被剥、
* 没有 pre 时 wpautop 会套 <p> 把缩进折叠掉。
* 现在 pre 已放行,后端由 theme-comments.php 的白名单挡着。
*/
.comment-tool-glyph-code {
font-family: var(--yq-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
font-size: 12px;
letter-spacing: -0.5px;
}
.comment-code-panel {
position: absolute;
top: calc(100% + 6px);
left: 0;
z-index: 21;
width: 260px;
padding: 10px;
border: 1px solid #e2e7ee;
border-radius: var(--yq-radius-lg, 6px);
background-color: #fff;
box-shadow: 0 6px 20px rgba(31, 35, 40, 0.12);
}
.comment-code-panel[hidden] {
display: none;
}
.comment-code-panel-title {
margin-bottom: 8px;
font-size: 12px;
font-weight: 600;
color: #656d76;
}
.comment-code-langs {
display: flex;
flex-wrap: wrap;
gap: 4px;
max-height: 190px;
overflow-y: auto;
}
.comment-code-lang {
padding: 3px 8px;
border: 1px solid #e2e7ee;
border-radius: 4px;
background: #fff;
color: #4b5563;
font-size: 11px;
font-family: var(--yq-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
cursor: pointer;
transition: color 0.12s, border-color 0.12s, background-color 0.12s;
}
.comment-code-lang:hover {
color: var(--yq-primary, #00a2ff);
border-color: var(--yq-primary, #00a2ff);
background: #f6fbff;
}
.comment-code-lang:focus-visible {
outline: 2px solid var(--yq-primary, #00a2ff);
outline-offset: 1px;
}
.comment-code-panel-foot {
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid #f0f3f7;
font-size: 11px;
line-height: 1.6;
color: #8a9199;
}
+208
View File
@@ -0,0 +1,208 @@
/**
* 代码块样式(highlight.js 自研接管后的卡片外观)
*
* 与 kratos.css 里原有那段的关系:
* 原有 `.k-main .details .article .content pre { … }` 里有
* `background-color:#f8f8f8`、`font-size:11px`、**`word-break:break-all`**。
* 最后这条会在任意字符处断行,代码看起来像乱码 —— 必须压掉。
* 本文件加载在 kratos.css 之后,用更高的特异性覆盖。
*/
/* ── 卡片外壳 ── */
.k-main .details .article .content .cb,
.comment-body .cb,
.cb {
position: relative;
margin: 14px 0;
border: 1px solid #d0d7de;
border-radius: 8px;
overflow: hidden;
background: #fff;
}
/* ── 顶部条:语言标签 + 复制按钮 ── */
.k-main .details .article .content .cb .cb-bar,
.cb .cb-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 6px 10px;
background: #f6f8fa;
border-bottom: 1px solid #d0d7de;
font-size: 12px;
line-height: 1.5;
}
/* 语言标签:让人一眼看到"这是 Java",防止把 Python 和 Java 的实现贴反。
站内实测有 19 篇文章含 ≥2 种语言、38 处相邻代码块语言不同 —— 确有实用价值。 */
.cb .cb-lang {
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 11px;
font-weight: 600;
letter-spacing: .02em;
color: #656d76;
padding: 1px 6px;
border-radius: 4px;
border: 1px solid #d0d7de;
background: #fff;
text-transform: none;
}
/* 🔴 自动识别出来的标签要**看得出来是猜的**:虚线边框 + 斜体。
站内 18 个 ≥4 行的未标注代码块实测自动识别只对了 4 个(22%)——
如果把它画得和作者标的一模一样,等于把 78% 的错误结果伪装成确定事实。 */
.cb .cb-lang[data-guess="1"] {
border-style: dashed;
font-style: italic;
opacity: .8;
}
.cb .cb-actions {
display: flex;
gap: 6px;
}
.cb .cb-copy {
font: inherit;
font-size: 11px;
padding: 3px 9px;
cursor: pointer;
color: #656d76;
background: #fff;
border: 1px solid #d0d7de;
border-radius: 5px;
transition: color .12s, border-color .12s, opacity .12s;
opacity: 0;
}
.cb:hover .cb-copy,
.cb .cb-copy:focus-visible { opacity: 1; }
.cb .cb-copy:hover { color: #0969da; border-color: #0969da; }
.cb .cb-copy.done { color: #1a7f37; border-color: #1a7f37; }
.cb .cb-copy.err { color: #cf222e; border-color: #cf222e; }
/* 🔴 触屏兜底:没有 hover 的设备上按钮必须常驻,否则永远点不到。
注意这里必须是「默认隐藏 + 触屏覆盖回 1」的写法。
如果默认是 1、只在 @media(hover:none) 里再写 1,那条规则等于空操作 ——
因为 1 本来就是默认值,删掉也不会变,故障测不出来。 */
@media (hover: none) {
.cb .cb-copy { opacity: 1; }
}
/* ── 代码区:左行号槽 + 右可滚动 pre ── */
.cb .cb-body {
display: flex;
align-items: stretch;
}
/* 行号槽。
🔴 它在 <pre> **外面**,所以行号文字永远不会进 code.textContent ⇒
"复制不带行号"是结构保证的,不靠 JS 删、也不靠正则过滤。
🔴 除了用户要求,这里还有一层现实原因:hljs 输出的是 token span,
没有行结构,想在 span 上挂 ::before 计数是挂不住的(没有 span.line 这种元素)。
原型阶段就是这么写的,结果 CSS 规则齐全、画面上零行号。 */
.cb .cb-ln {
display: none;
flex: 0 0 auto;
padding: 12px 0 12px 10px;
text-align: right;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 12.5px;
line-height: 1.62;
color: #9aa0a6;
background: #f6f8fa;
border-right: 1px solid #eaecef;
user-select: none;
-webkit-user-select: none;
overflow: hidden;
}
.cb .cb-ln > span { display: block; }
/* 行号开关(body 上带 opt-ln 类时显示)*/
.opt-ln .cb .cb-ln { display: block; }
/* 🔴 这里必须把 padding 显式写成 12px,和 .cb-ln 的 padding-top 对齐。
每套 hljs 官方主题都自带 `pre code.hljs{padding:1em}`,会把代码整体
往下压 1em。行号槽没有这个 padding,于是第一行代码的 top 比行号低 15.5px,
屏幕上就是"第 1 行代码旁边写着 2",往下每行错一位。
实测(原型页):修前逐行偏差 15.5px,修后 0.00px。 */
.k-main .details .article .content .cb pre,
.cb pre {
margin: 0;
padding: 12px 14px;
overflow-x: auto;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 12.5px;
line-height: 1.62;
/* 🔴 原有主题写的是 word-break:break-all,会在任意字符断行,代码像乱码。
长行必须横向滚动,不能折断。 */
word-break: normal;
overflow-wrap: normal;
white-space: pre;
tab-size: 4;
border: 0;
border-radius: 0;
background: transparent;
}
/* 主题自带的 code padding 必须归零,否则与行号槽错位(见上) */
.cb .cb-body > pre > code.hljs {
padding: 0;
overflow-x: visible;
}
.cb .cb-body > pre { flex: 1 1 auto; min-width: 0; }
.cb pre code {
display: block;
margin: 0;
padding: 0;
background: transparent;
font-size: inherit;
line-height: inherit;
color: inherit;
font-family: inherit;
}
/* 深色配色必须让卡片自带深色底与深色顶条。
站点正文是浅色,若只在浅底上印深色配色,
token 与背景对比不足(现有 Prism 出问题的地方正在这里)。 */
.cb[data-kind="dark"] {
border-color: #30363d;
background: #0d1117;
}
.cb[data-kind="dark"] .cb-bar {
background: #21262d;
border-bottom-color: #30363d;
}
.cb[data-kind="dark"] .cb-lang {
background: #21262d;
border-color: #30363d;
color: #8b949e;
}
.cb[data-kind="dark"] .cb-copy {
background: #21262d;
border-color: #30363d;
color: #8b949e;
}
.cb[data-kind="dark"] .cb-copy:hover {
color: #58a6ff;
border-color: #58a6ff;
}
.cb[data-kind="dark"] .cb-ln {
background: #161b22;
border-right-color: #30363d;
color: #6e7681;
}
/* 窄屏:行号槽收窄,避免挤占代码宽度 */
@media (max-width: 560px) {
.cb .cb-ln { padding: 12px 0 12px 6px; font-size: 11px; }
.cb .cb-ln > span { padding-right: 4px; }
.cb pre { padding: 12px 10px; font-size: 12px; }
}
/* 打印:行号与复制按钮都不需要 */
@media print {
.cb .cb-ln, .cb .cb-actions { display: none !important; }
.cb { break-inside: avoid; }
}
+1262
View File
File diff suppressed because one or more lines are too long
+7
View File
@@ -0,0 +1,7 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
Theme: a11y-dark
Author: @ericwbailey
Maintainer: @ericwbailey
Based on the Tomorrow Night Eighties theme: https://github.com/isagalaev/highlight.js/blob/master/src/styles/tomorrow-night-eighties.css
*/.hljs{background:#2b2b2b;color:#f8f8f2}.hljs-comment,.hljs-quote{color:#d4d0ab}.hljs-deletion,.hljs-name,.hljs-regexp,.hljs-selector-class,.hljs-selector-id,.hljs-tag,.hljs-template-variable,.hljs-variable{color:#ffa07a}.hljs-built_in,.hljs-link,.hljs-literal,.hljs-meta,.hljs-number,.hljs-params,.hljs-type{color:#f5ab35}.hljs-attribute{color:gold}.hljs-addition,.hljs-bullet,.hljs-string,.hljs-symbol{color:#abe338}.hljs-section,.hljs-title{color:#00e0e0}.hljs-keyword,.hljs-selector-tag{color:#dcc6e0}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}@media screen and (-ms-high-contrast:active){.hljs-addition,.hljs-attribute,.hljs-built_in,.hljs-bullet,.hljs-comment,.hljs-link,.hljs-literal,.hljs-meta,.hljs-number,.hljs-params,.hljs-quote,.hljs-string,.hljs-symbol,.hljs-type{color:highlight}.hljs-keyword,.hljs-selector-tag{font-weight:700}}
+1
View File
@@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}
+1
View File
@@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#383a42;background:#fafafa}.hljs-comment,.hljs-quote{color:#a0a1a7;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#a626a4}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e45649}.hljs-literal{color:#0184bb}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#50a14f}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#986801}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#4078f2}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#c18401}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}
+1
View File
@@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#000;background:#fff}.hljs-addition,.hljs-meta,.hljs-string,.hljs-symbol,.hljs-template-tag,.hljs-template-variable{color:#756bb1}.hljs-comment,.hljs-quote{color:#636363}.hljs-bullet,.hljs-link,.hljs-literal,.hljs-number,.hljs-regexp{color:#31a354}.hljs-deletion,.hljs-variable{color:#88f}.hljs-built_in,.hljs-doctag,.hljs-keyword,.hljs-name,.hljs-section,.hljs-selector-class,.hljs-selector-id,.hljs-selector-tag,.hljs-strong,.hljs-tag,.hljs-title,.hljs-type{color:#3182bd}.hljs-emphasis{font-style:italic}.hljs-attribute{color:#e6550d}
+9
View File
@@ -0,0 +1,9 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
Theme: GitHub Dark Dimmed
Description: Dark dimmed theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Colors taken from GitHub's CSS
*/.hljs{color:#adbac7;background:#22272e}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#f47067}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#dcbdfb}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#6cb6ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#96d0ff}.hljs-built_in,.hljs-symbol{color:#f69d50}.hljs-code,.hljs-comment,.hljs-formula{color:#768390}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#8ddb8c}.hljs-subst{color:#adbac7}.hljs-section{color:#316dca;font-weight:700}.hljs-bullet{color:#eac55f}.hljs-emphasis{color:#adbac7;font-style:italic}.hljs-strong{color:#adbac7;font-weight:700}.hljs-addition{color:#b4f1b4;background-color:#1b4721}.hljs-deletion{color:#ffd8d3;background-color:#78191b}
+10
View File
@@ -0,0 +1,10 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
Theme: GitHub Dark
Description: Dark theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Outdated base version: https://github.com/primer/github-syntax-dark
Current colors taken from GitHub's CSS
*/.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}
+10
View File
@@ -0,0 +1,10 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
Theme: GitHub
Description: Light theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Outdated base version: https://github.com/primer/github-syntax-light
Current colors taken from GitHub's CSS
*/.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}
+1
View File
@@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#fff;color:#000}.hljs-comment,.hljs-quote{color:#800}.hljs-keyword,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-title{color:#008}.hljs-template-variable,.hljs-variable{color:#660}.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-string{color:#080}.hljs-bullet,.hljs-link,.hljs-literal,.hljs-meta,.hljs-number,.hljs-symbol{color:#066}.hljs-attr,.hljs-built_in,.hljs-doctag,.hljs-params,.hljs-title,.hljs-type{color:#606}.hljs-attribute,.hljs-subst{color:#000}.hljs-formula{background-color:#eee;font-style:italic}.hljs-selector-class,.hljs-selector-id{color:#9b703f}.hljs-addition{background-color:#baeeba}.hljs-deletion{background-color:#ffc8bd}.hljs-doctag,.hljs-strong{font-weight:700}.hljs-emphasis{font-style:italic}
+1
View File
@@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#000;background:#fff}.hljs-subst,.hljs-title{font-weight:400;color:#000}.hljs-comment,.hljs-quote{color:grey;font-style:italic}.hljs-meta{color:olive}.hljs-tag{background:#efefef}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-section,.hljs-selector-class,.hljs-selector-id,.hljs-selector-tag,.hljs-type{font-weight:700;color:navy}.hljs-attribute,.hljs-link,.hljs-number,.hljs-regexp{font-weight:700;color:#00f}.hljs-link,.hljs-number,.hljs-regexp{font-weight:400}.hljs-string{color:green;font-weight:700}.hljs-bullet,.hljs-formula,.hljs-symbol{color:#000;background:#d0eded;font-style:italic}.hljs-doctag{text-decoration:underline}.hljs-template-variable,.hljs-variable{color:#660e7a}.hljs-addition{background:#baeeba}.hljs-deletion{background:#ffc8bd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
+1
View File
@@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#000;background:#fff}.hljs-subst,.hljs-title{font-weight:400;color:#000}.hljs-title.function_{color:#7a7a43}.hljs-code,.hljs-comment,.hljs-quote{color:#8c8c8c;font-style:italic}.hljs-meta{color:#9e880d}.hljs-section{color:#871094}.hljs-built_in,.hljs-keyword,.hljs-literal,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-selector-tag,.hljs-symbol,.hljs-template-tag,.hljs-type,.hljs-variable.language_{color:#0033b3}.hljs-attr,.hljs-property{color:#871094}.hljs-attribute{color:#174ad4}.hljs-number{color:#1750eb}.hljs-regexp{color:#264eff}.hljs-link{text-decoration:underline;color:#006dcc}.hljs-meta .hljs-string,.hljs-string{color:#067d17}.hljs-char.escape_{color:#0037a6}.hljs-doctag{text-decoration:underline}.hljs-template-variable{color:#248f8f}.hljs-addition{background:#bee6be}.hljs-deletion{background:#d6d6d6}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
+1
View File
@@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background-color:#f4f4f4;color:#000}.hljs-subst{color:#000}.hljs-addition,.hljs-attribute,.hljs-bullet,.hljs-string,.hljs-symbol,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-variable{color:#050}.hljs-comment,.hljs-quote{color:#777}.hljs-link,.hljs-literal,.hljs-number,.hljs-regexp,.hljs-type{color:#800}.hljs-deletion,.hljs-meta{color:#00e}.hljs-built_in,.hljs-doctag,.hljs-keyword,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-tag,.hljs-title{font-weight:700;color:navy}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
+1
View File
@@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#272822;color:#ddd}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-number,.hljs-selector-tag,.hljs-strong,.hljs-tag{color:#f92672}.hljs-code{color:#66d9ef}.hljs-attr,.hljs-attribute,.hljs-link,.hljs-regexp,.hljs-symbol{color:#bf79db}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-emphasis,.hljs-section,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-string,.hljs-subst,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type,.hljs-variable{color:#a6e22e}.hljs-class .hljs-title,.hljs-title.class_{color:#fff}.hljs-comment,.hljs-deletion,.hljs-meta,.hljs-quote{color:#75715e}.hljs-doctag,.hljs-keyword,.hljs-literal,.hljs-section,.hljs-selector-id,.hljs-selector-tag,.hljs-title,.hljs-type{font-weight:700}
+1
View File
@@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#011627;color:#d6deeb}.hljs-keyword{color:#c792ea;font-style:italic}.hljs-built_in{color:#addb67;font-style:italic}.hljs-type{color:#82aaff}.hljs-literal{color:#ff5874}.hljs-number{color:#f78c6c}.hljs-regexp{color:#5ca7e4}.hljs-string{color:#ecc48d}.hljs-subst{color:#d3423e}.hljs-symbol{color:#82aaff}.hljs-class{color:#ffcb8b}.hljs-function{color:#82aaff}.hljs-title{color:#dcdcaa;font-style:italic}.hljs-params{color:#7fdbca}.hljs-comment{color:#637777;font-style:italic}.hljs-doctag{color:#7fdbca}.hljs-meta,.hljs-meta .hljs-keyword{color:#82aaff}.hljs-meta .hljs-string{color:#ecc48d}.hljs-section{color:#82b1ff}.hljs-attr,.hljs-name,.hljs-tag{color:#7fdbca}.hljs-attribute{color:#80cbc4}.hljs-variable{color:#addb67}.hljs-bullet{color:#d9f5dd}.hljs-code{color:#80cbc4}.hljs-emphasis{color:#c792ea;font-style:italic}.hljs-strong{color:#addb67;font-weight:700}.hljs-formula{color:#c792ea}.hljs-link{color:#ff869a}.hljs-quote{color:#697098;font-style:italic}.hljs-selector-tag{color:#ff6363}.hljs-selector-id{color:#fad430}.hljs-selector-class{color:#addb67;font-style:italic}.hljs-selector-attr,.hljs-selector-pseudo{color:#c792ea;font-style:italic}.hljs-template-tag{color:#c792ea}.hljs-template-variable{color:#addb67}.hljs-addition{color:#addb67ff;font-style:italic}.hljs-deletion{color:#ef535090;font-style:italic}
+1
View File
@@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#e6e6e6;background:#2a2c2d}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}.hljs-comment,.hljs-quote{color:#bbb;font-style:italic}.hljs-params{color:#bbb}.hljs-attr,.hljs-punctuation{color:#e6e6e6}.hljs-meta,.hljs-name,.hljs-selector-tag{color:#ff4b82}.hljs-char.escape_,.hljs-operator{color:#b084eb}.hljs-deletion,.hljs-keyword{color:#ff75b5}.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-variable.language_{color:#ff9ac1}.hljs-code,.hljs-formula,.hljs-property,.hljs-section,.hljs-subst,.hljs-title.function_{color:#45a9f9}.hljs-addition,.hljs-bullet,.hljs-meta .hljs-string,.hljs-selector-class,.hljs-string,.hljs-symbol,.hljs-title.class_,.hljs-title.class_.inherited__{color:#19f9d8}.hljs-attribute,.hljs-built_in,.hljs-doctag,.hljs-link,.hljs-literal,.hljs-meta .hljs-keyword,.hljs-number,.hljs-punctuation,.hljs-selector-id,.hljs-tag,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type,.hljs-variable{color:#ffb86c}
+1
View File
@@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#1c1b19;color:#fce8c3}.hljs-literal,.hljs-quote,.hljs-subst{color:#fce8c3}.hljs-symbol,.hljs-type{color:#68a8e4}.hljs-deletion,.hljs-keyword{color:#ef2f27}.hljs-attribute,.hljs-function,.hljs-name,.hljs-section,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-title{color:#fbb829}.hljs-class,.hljs-code,.hljs-property,.hljs-template-variable,.hljs-variable{color:#0aaeb3}.hljs-addition,.hljs-bullet,.hljs-regexp,.hljs-string{color:#98bc37}.hljs-built_in,.hljs-params{color:#ff5c8f}.hljs-selector-tag,.hljs-template-tag{color:#2c78bf}.hljs-comment,.hljs-link,.hljs-meta,.hljs-number{color:#918175}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
+13
View File
@@ -0,0 +1,13 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
Theme: StackOverflow Dark
Description: Dark theme as used on stackoverflow.com
Author: stackoverflow.com
Maintainer: @Hirse
Website: https://github.com/StackExchange/Stacks
License: MIT
Updated: 2021-05-15
Updated for @stackoverflow/stacks v0.64.0
Code Blocks: /blob/v0.64.0/lib/css/components/_stacks-code-blocks.less
Colors: /blob/v0.64.0/lib/css/exports/_stacks-constants-colors.less
*/.hljs{color:#fff;background:#1c1b1b}.hljs-subst{color:#fff}.hljs-comment{color:#999}.hljs-attr,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-section,.hljs-selector-tag{color:#88aece}.hljs-attribute{color:#c59bc1}.hljs-name,.hljs-number,.hljs-quote,.hljs-selector-id,.hljs-template-tag,.hljs-type{color:#f08d49}.hljs-selector-class{color:#88aece}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-string,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#b5bd68}.hljs-meta,.hljs-selector-pseudo{color:#88aece}.hljs-built_in,.hljs-literal,.hljs-title{color:#f08d49}.hljs-bullet,.hljs-code{color:#ccc}.hljs-meta .hljs-string{color:#b5bd68}.hljs-deletion{color:#de7176}.hljs-addition{color:#76c490}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
+13
View File
@@ -0,0 +1,13 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
Theme: StackOverflow Light
Description: Light theme as used on stackoverflow.com
Author: stackoverflow.com
Maintainer: @Hirse
Website: https://github.com/StackExchange/Stacks
License: MIT
Updated: 2021-05-15
Updated for @stackoverflow/stacks v0.64.0
Code Blocks: /blob/v0.64.0/lib/css/components/_stacks-code-blocks.less
Colors: /blob/v0.64.0/lib/css/exports/_stacks-constants-colors.less
*/.hljs{color:#2f3337;background:#f6f6f6}.hljs-subst{color:#2f3337}.hljs-comment{color:#656e77}.hljs-attr,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-section,.hljs-selector-tag{color:#015692}.hljs-attribute{color:#803378}.hljs-name,.hljs-number,.hljs-quote,.hljs-selector-id,.hljs-template-tag,.hljs-type{color:#b75501}.hljs-selector-class{color:#015692}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-string,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#54790d}.hljs-meta,.hljs-selector-pseudo{color:#015692}.hljs-built_in,.hljs-literal,.hljs-title{color:#b75501}.hljs-bullet,.hljs-code{color:#535a60}.hljs-meta .hljs-string{color:#54790d}.hljs-deletion{color:#c02d2e}.hljs-addition{color:#2f6f44}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
+1
View File
@@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs-comment,.hljs-quote{color:#7285b7}.hljs-deletion,.hljs-name,.hljs-regexp,.hljs-selector-class,.hljs-selector-id,.hljs-tag,.hljs-template-variable,.hljs-variable{color:#ff9da4}.hljs-built_in,.hljs-link,.hljs-literal,.hljs-meta,.hljs-number,.hljs-params,.hljs-type{color:#ffc58f}.hljs-attribute{color:#ffeead}.hljs-addition,.hljs-bullet,.hljs-string,.hljs-symbol{color:#d1f1a9}.hljs-section,.hljs-title{color:#bbdaff}.hljs-keyword,.hljs-selector-tag{color:#ebbbff}.hljs{background:#002451;color:#fff}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
+1
View File
@@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#fff;color:#000}.hljs-comment,.hljs-quote,.hljs-variable{color:green}.hljs-built_in,.hljs-keyword,.hljs-name,.hljs-selector-tag,.hljs-tag{color:#00f}.hljs-addition,.hljs-attribute,.hljs-literal,.hljs-section,.hljs-string,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type{color:#a31515}.hljs-deletion,.hljs-meta,.hljs-selector-attr,.hljs-selector-pseudo{color:#2b91af}.hljs-doctag{color:grey}.hljs-attr{color:red}.hljs-bullet,.hljs-link,.hljs-symbol{color:#00b0e8}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
+1
View File
@@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#fff;color:#000}.xml .hljs-meta{color:silver}.hljs-comment,.hljs-quote{color:#007400}.hljs-attribute,.hljs-keyword,.hljs-literal,.hljs-name,.hljs-selector-tag,.hljs-tag{color:#aa0d91}.hljs-template-variable,.hljs-variable{color:#3f6e74}.hljs-code,.hljs-meta .hljs-string,.hljs-string{color:#c41a16}.hljs-link,.hljs-regexp{color:#0e0eff}.hljs-bullet,.hljs-number,.hljs-symbol,.hljs-title{color:#1c00cf}.hljs-meta,.hljs-section{color:#643820}.hljs-built_in,.hljs-class .hljs-title,.hljs-params,.hljs-title.class_,.hljs-type{color:#5c2699}.hljs-attr{color:#836c28}.hljs-subst{color:#000}.hljs-formula{background-color:#eee;font-style:italic}.hljs-addition{background-color:#baeeba}.hljs-deletion{background-color:#ffc8bd}.hljs-selector-class,.hljs-selector-id{color:#9b703f}.hljs-doctag,.hljs-strong{font-weight:700}.hljs-emphasis{font-style:italic}
+115 -2
View File
@@ -196,7 +196,7 @@
* 这样「选中一段字 → 点 B」的选区在 click 到来时还在。
* ⚠️ 面板内部的元素(色板、表情链接)也要挡:3.2.25 本地探针实测,
* 点色板时焦点被抢走,foreColor 落在塌陷光标上,颜色功能整个失效。 */
$$('.comment-tool-btn, .comment-color-swatch, .comment-color-panel, .comment-smile-panel, .comment-smile-panel a', form)
$$('.comment-tool-btn, .comment-color-swatch, .comment-color-panel, .comment-smile-panel, .comment-smile-panel a, .comment-code-panel, .comment-code-lang', form)
.forEach(function (b) {
b.addEventListener('mousedown', function (ev) { ev.preventDefault() })
})
@@ -284,6 +284,76 @@
})
}
/* ── 3.2.42 代码块面板 ──────────────────────────────────
* 与颜色/表情面板同款开合逻辑(点按钮开合、点外面关掉)。
* 选定语言后会在光标处插入:
* <pre><code class="language-xxx">这里换成你的代码</code></pre>
* 为什么先插占位文字而不是空块:空的 <pre> 在浏览器里高度接近 0,
* 光标定位困难、看起来像"没插进去",访客会反复点。
*
* 🔴 为什么语言必须让访客自己选:
* 自动识别在站内实测准确率只有 22%(18 个长块对 4 个),
* 让它替访客决定语言等于制造错误标签。
* 🔴 插入后必须把光标留在代码块里,否则访客紧接着打字会打到块外面。
*/
var codeBtn = $('#yang-code-btn')
var codePanel = $('#yang-code-panel')
if (codeBtn && codePanel) {
codeBtn.addEventListener('click', function (ev) {
ev.stopPropagation()
var open = !codePanel.hidden
codePanel.hidden = open
codeBtn.setAttribute('aria-expanded', open ? 'false' : 'true')
})
document.addEventListener('click', function (ev) {
if (codePanel.hidden) return
if (codePanel.contains(ev.target) || codeBtn.contains(ev.target)) return
codePanel.hidden = true
codeBtn.setAttribute('aria-expanded', 'false')
})
codePanel.addEventListener('click', function (ev) {
var b = ev.target.closest ? ev.target.closest('.comment-code-lang') : null
if (!b) return
ev.preventDefault()
var lang = b.getAttribute('data-lang') || 'text'
insertCodeBlock(lang)
codePanel.hidden = true
codeBtn.setAttribute('aria-expanded', 'false')
})
}
/* 在光标处插入一个代码块,并把占位文字选中,方便直接覆盖 */
function insertCodeBlock (lang) {
if (!editor) return
editor.focus()
var cls = lang && lang !== 'text' ? ' class="language-' + lang + '"' : ''
var html = '<pre><code' + cls + '></code></pre>'
// 先插空块,再把占位文字塞进 code 里并选中
try {
document.execCommand('insertHTML', false, html)
} catch (ex) {
// 老浏览器兜底:直接插纯文本标记
document.execCommand('insertText', false,
'\n<pre><code' + cls + '>\n\n</code></pre>\n')
return
}
// 找到刚插入的最后那个 code,放占位文字并全选
var codes = $$('pre:last-of-type code', editor)
var target = codes.length ? codes[codes.length - 1] : null
if (!target) {
var all = $$('pre code', editor)
target = all.length ? all[all.length - 1] : null
}
if (target) {
target.textContent = '在这里粘贴或输入代码'
var range = document.createRange()
range.selectNodeContents(target)
var sel = window.getSelection()
sel.removeAllRanges()
sel.addRange(range)
}
}
/* 编辑区被清空后常残留 <br>/<div><br></div>,压成真空,
* 否则 :empty 占位符不显示。 */
editor.addEventListener('input', function () {
@@ -316,6 +386,40 @@
document.createTextNode(' ' + (im.getAttribute('data-code') || '') + ' '),
im)
})
/* ── 3.2.42 代码块:必须在 walk() 之前单独序列化 ──────────
* 🔴 不能交给 walk() 递归处理,原因是 walk 会给所有文本做
* escHtml()(把 & 变 &amp;、< 变 &lt;)。代码块里的内容
* 出发时是**已转义过的显示文本**(浏览器 textContent 已把
* &lt; 还原成 <),需要重新转义一次才对;但 walk 还可能
* 把代码里的连续空行压掉(收尾的 /\n{3,}/ 替换),
* 而代码里连续空行是完全正常的。
* ⇒ 做法:先把每个 <pre> 换成唯一占位标记,
* walk 结束后再把标记替换回序列化好的代码块。
* 占位符用 \u0000 包裹,escHtml 不会改动它。
*/
var codeBlocks = []
var pres = $$('pre', clone)
for (var pi = 0; pi < pres.length; pi++) {
var pre = pres[pi]
var codeEl = pre.querySelector('code')
var srcEl = codeEl || pre
var cls = (codeEl ? codeEl.className : '') + ' ' + (pre.className || '')
var mm = /(?:language|lang)-([a-z0-9+#.-]+)/i.exec(cls)
var lang = mm ? mm[1].toLowerCase() : ''
// textContent 是浏览器还原后的纯文本,重新转义成安全形态再提交
var raw = srcEl.textContent || ''
var safe = raw.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
var block = '<pre><code'
+ (lang ? ' class="language-' + lang + '"' : '')
+ '>' + safe + '</code></pre>'
var ph = '\u0000CB' + codeBlocks.length + '\u0000'
codeBlocks.push(block)
pre.parentNode.replaceChild(document.createTextNode(ph), pre)
}
function walk (n) {
var out = ''
for (var i = 0; i < n.childNodes.length; i++) {
@@ -341,7 +445,16 @@
}
return out
}
return walk(clone).replace(/\n{3,}/g, '\n\n').trim()
var body = walk(clone)
// 空行压缩只作用于代码块之外 —— 先把已存的块换回来,避免误伤
body = body.replace(/\n{3,}/g, '\n\n')
// 🔴 占位符还原:必须在压缩空行**之后**做。
// 如果先还原,代码里的连续空行会被上面的 /\n{3,}/ 一起压掉。
for (var ci = 0; ci < codeBlocks.length; ci++) {
body = body.split('\u0000CB' + ci + '\u0000').join(codeBlocks[ci])
}
return body.trim()
}
/* ── 提交 ──────────────────────────────────────────────────
+203
View File
@@ -0,0 +1,203 @@
/**
* 代码高亮前端逻辑
*
* 服务端(inc/theme-highlight.php)已经把每个 <pre> 规范成了卡片结构:
* <figure class="cb hl-<key>" data-lang data-theme data-kind data-guess>
* <figcaption class="cb-bar"> 语言标签 + 复制按钮 </figcaption>
* <div class="cb-body">
* <div class="cb-ln">1 2 3 …</div> ← 行号,独立于 pre
* <pre class="hljs"><code class="hljs">…</code></pre>
* </div>
* </figure>
*
* 前端只做三件事:上色、复制、自动识别。
* 主题切换由后台设置决定,不在前台提供切换器。
*/
(function () {
'use strict';
var CFG = window.YANG_HL || {};
var L = CFG.labels || {};
/* ── 上色 ──────────────────────────────────────────────────
* 作者标了语言 → 按标记高亮。
* 没标 → 交给 runAuto(如果自动识别开着)。
*
* 🔴 必须先判断 hljs 在不在:脚本被 CDN 拦了 / 加载失败时,
* 直接调 hljs.highlightAll 会抛 TypeError,后面的复制绑定
* 全都执行不到 —— 表现成"整块代码没有任何按钮反应"。
* 这类故障排查起来极其费时,所以要显式挡一下。 */
function paint () {
if (typeof window.hljs === 'undefined') {
return false;
}
if (typeof hljs.highlightAll === 'function') {
hljs.highlightAll();
}
return true;
}
/* ── 自动识别 ────────────────────────────────────────────────
* 🔴 这个开关必须是**真的**:拨开就真跑一次 highlightAuto 并写回,
* 拨关就把高亮还原成纯文本、标签藏起来。
* 原型阶段这版曾经是个空壳(只改了一个不存在元素的 display),
* 点了没有任何变化 —— 那种假开关什么都验证不了。
*
* 🔴 只对**没标语言**的块生效。有标记的永远按标记来,
* 因为自动识别在站内实测准确率只有 22%,不能让它覆盖作者的标注。 */
function runAuto (on) {
if (typeof window.hljs === 'undefined') {
return;
}
var boxes = document.querySelectorAll('.cb');
for (var i = 0; i < boxes.length; i++) {
(function (box) {
// 作者标了语言的一律不动
if (box.getAttribute('data-guess') === '0') {
return;
}
var code = box.querySelector('pre > code');
var tag = box.querySelector('.cb-lang');
if (!code) { return; }
// 原始源码:用属性缓存,避免从高亮后的 DOM 反解
var src = code.getAttribute('data-raw');
if (src === null) {
src = code.textContent;
code.setAttribute('data-raw', src);
}
if (on) {
var r = hljs.highlightAuto(src);
if (r && r.language) {
code.innerHTML = r.value;
code.className = 'hljs language-' + r.language;
if (tag) {
tag.textContent = labelOf(r.language);
// 保持虚线边框:这是猜的,不是作者标的
tag.setAttribute('data-guess', '1');
tag.style.display = '';
}
box.setAttribute('data-lang', r.language);
} else {
// 认不出来:保持纯文本,标签显示 —
/* 🔴 这里**必须留着** tag 上的 data-guess="1"。
* 认不出 ≠ 已确认。删掉它,CSS 的虚线+斜体就失效,
* 标签退回成实线正体的「—」—— 看上去和作者亲手写的
* 「纯文本」一模一样,等于用"已确认"的样式表示"没认出来",
* 恰好相反。作者没标语言这件事本身也仍然是"未确认"状态。
* (box 上的 data-guess="1" 也不能动:runAuto 靠它
* 判断"这块可以反复重新识别"。) */
code.textContent = src;
code.className = 'hljs';
if (tag) {
tag.textContent = '—';
tag.setAttribute('data-guess', '1');
}
}
} else {
/* 自动识别被关掉:还原成纯文本。
* 🔴 同样要保留 tag 上的 data-guess="1":
* 关掉识别之后"这块是什么语言"依然**未知**,标签还是「—」,
* 就必须还是虚线斜体。删掉它会让所有未标注块在关闭识别时
* 一齐变成实线正体,看起来像"作者确认过是纯文本"。 */
code.textContent = src;
code.className = 'hljs';
if (tag) {
tag.textContent = '—';
tag.setAttribute('data-guess', '1');
}
box.setAttribute('data-lang', '');
}
})(boxes[i]);
}
}
/* 语言名 → 显示名 */
function labelOf (lang) {
var M = {
bash: 'Bash', c: 'C', cpp: 'C++', csharp: 'C#', css: 'CSS',
diff: 'Diff', dos: 'Batch', dockerfile: 'Dockerfile', go: 'Go',
graphql: 'GraphQL', ini: 'INI', java: 'Java',
javascript: 'JavaScript', json: 'JSON', kotlin: 'Kotlin',
less: 'Less', lua: 'Lua', makefile: 'Makefile',
markdown: 'Markdown', nginx: 'Nginx', objectivec: 'Objective-C',
perl: 'Perl', php: 'PHP', plaintext: '纯文本',
powershell: 'PowerShell', python: 'Python', r: 'R', ruby: 'Ruby',
rust: 'Rust', scss: 'SCSS', shell: 'Shell', sql: 'SQL',
swift: 'Swift', typescript: 'TypeScript', xml: 'HTML', yaml: 'YAML'
};
return M[lang] || (lang ? lang.toUpperCase() : '');
}
/* ── 复制 ──────────────────────────────────────────────────
* 🔴 两个硬约束:
* 1. navigator.clipboard 只在安全上下文(HTTPS/localhost)存在,
* HTTP 下是 undefined,直接调抛 TypeError,表现成"点了没反应"。
* 必须留 execCommand 退路。
* 2. 取 textContent 而不是 innerHTML —— 高亮后代码里全是 <span>。
* 行号在 <pre> 外面,天然不会进 textContent,所以复制永远不含行号。 */
function copyText (text) {
if (navigator.clipboard && window.isSecureContext) {
return navigator.clipboard.writeText(text);
}
return new Promise(function (resolve, reject) {
var ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.top = '-9999px';
ta.setAttribute('readonly', 'readonly');
document.body.appendChild(ta);
ta.select();
try { ta.setSelectionRange(0, text.length); } catch (e) { /* 老浏览器 */ }
var ok = false;
try { ok = document.execCommand('copy'); } catch (e2) { ok = false; }
document.body.removeChild(ta);
ok ? resolve() : reject(new Error('execCommand failed'));
});
}
function flash (btn, cls, label) {
var old = btn.textContent;
btn.textContent = label;
btn.className = btn.className + ' ' + cls;
btn.disabled = true;
setTimeout(function () {
btn.textContent = old;
btn.className = btn.className.replace(' ' + cls, '');
btn.disabled = false;
}, 2000);
}
/* 事件委托:评论区的代码块是 AJAX 插进来的,不能绑在元素上 */
document.addEventListener('click', function (e) {
var btn = e.target.closest ? e.target.closest('[data-copy]') : null;
if (!btn) { return; }
var box = btn.closest('.cb');
var code = box ? box.querySelector('pre > code') : null;
if (!code) { return; }
e.preventDefault();
copyText(code.textContent).then(function () {
flash(btn, 'done', L.copied || '✓ 已复制');
}).catch(function () {
flash(btn, 'err', L.failed || '✗ 复制失败');
});
});
/* ── 初始状态 ────────────────────────────────────────────── */
function init () {
if (!paint()) {
return;
}
runAuto(CFG.autoDetect !== false);
}
// 供评论区 AJAX 插入新内容后重新调用
window.yangHlRefresh = init;
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();