From 36b18c0ffe3f87797109e072c14ec7bdbfeebe90 Mon Sep 17 00:00:00 2001 From: Victor_Jay Date: Thu, 28 May 2026 14:34:58 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BB=A3=E7=A0=81=E5=9D=97=E8=AF=AD?= =?UTF-8?q?=E6=B3=95=E9=AB=98=E4=BA=AE=20+=20=E8=AF=AD=E8=A8=80=E9=80=89?= =?UTF-8?q?=E6=8B=A9=20+=20=E6=A0=B7=E5=BC=8F=E7=BB=9F=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 引入 @tiptap/extension-code-block-lowlight + lowlight 实现代码语法高亮 - 工具栏新增语言下拉选择器,光标在代码块内时显示,支持 27 种常用语言 - 默认语言 text,高亮主题适配 MetaLab 深蓝背景 (#1a2332) - 代码块语言标签用 JS 动态注入 DOM 元素(避免 ::before 在 overflow 容器中失效) - 详情页同步代码块高亮和语言标签显示 - 编辑器与详情页样式统一:代码块暗色背景、引用蓝色边框、链接 accent 蓝 - sanitizePolicy 放行 data-language 属性和 hljs-* class - 修复 import map 避免 keyed plugin 重复实例 - 修复详情页代码块开头空白行(移除多余的 padding-top) - 修复语法错误(多余的闭合括号) --- internal/service/post_service.go | 9 +- templates/MetaLab-2026/html/posts/new.html | 122 +++++++++++++++++++- templates/MetaLab-2026/html/posts/show.html | 14 +++ templates/MetaLab-2026/static/css/posts.css | 55 +++++++-- templates/MetaLab-2026/static/js/editor.js | 73 ++++++++++-- 5 files changed, 248 insertions(+), 25 deletions(-) diff --git a/internal/service/post_service.go b/internal/service/post_service.go index f4a52f4..9edf841 100644 --- a/internal/service/post_service.go +++ b/internal/service/post_service.go @@ -2,6 +2,7 @@ package service import ( "fmt" + "regexp" "metazone.cc/metalab/internal/common" "metazone.cc/metalab/internal/config" @@ -12,11 +13,13 @@ import ( // sanitizePolicy Tiptap 输出 HTML 的消毒策略 // 前端 WYSIWYG 编辑器输出 HTML,由 bluemonday UGC 策略消毒 -// 显式放行 code/pre 的 class 属性以保留代码块样式 -// 放行 img 的 src/alt 属性以保留图片 +// 放行 code/pre 的 class/data-language 属性以保留代码高亮和语言标签 +// 放行 span 的 class 属性以保留 highlight.js 的 hljs-* 高亮 +// 放行 img 的 src/alt/title 属性以保留图片 var sanitizePolicy = func() *bluemonday.Policy { p := bluemonday.UGCPolicy() - p.AllowAttrs("class").OnElements("code", "pre") + p.AllowAttrs("class", "data-language").OnElements("code", "pre") + p.AllowAttrs("class").Matching(regexp.MustCompile("^hljs-")).OnElements("span") p.AllowAttrs("src", "alt", "title").OnElements("img") return p }() diff --git a/templates/MetaLab-2026/html/posts/new.html b/templates/MetaLab-2026/html/posts/new.html index 0bae38e..c32cba0 100644 --- a/templates/MetaLab-2026/html/posts/new.html +++ b/templates/MetaLab-2026/html/posts/new.html @@ -35,7 +35,7 @@ /* 引用 */ .tiptap blockquote { - border-left: 3px solid #e5e7eb; + border-left: 3px solid #3498db; padding-left: 1em; margin: 0.5em 0; color: #6b7280; @@ -43,16 +43,17 @@ /* 代码块 */ .tiptap pre { - background: #1f2937; - color: #f3f4f6; + background: #1a2332; + color: #e2e8f0; padding: 12px 16px; border-radius: 6px; font-size: 13px; line-height: 1.5; overflow-x: auto; margin: 0.5em 0; + position: relative; } -.tiptap pre code { background: transparent; padding: 0; color: inherit; font-size: inherit; } +.tiptap pre code { background: transparent; padding: 0; color: inherit; font-size: inherit; white-space: pre; display: block; } .tiptap code { background: #f3f4f6; padding: 2px 5px; @@ -60,6 +61,59 @@ font-size: 0.9em; font-family: 'SF Mono', 'Menlo', 'Monaco', monospace; } +/* 代码块语言标签 */ +.tiptap pre .code-lang-label { + position: absolute; + top: 0; + left: 0; + right: 0; + padding: 4px 16px; + font-size: 11px; + font-weight: 600; + color: #94a3b8; + background: #2c3e50; + border-radius: 6px 6px 0 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif; + pointer-events: none; + z-index: 1; +} + +/* highlight.js 语法高亮主题 — 基于 One Dark,适配 MetaLab 深蓝背景 */ +.tiptap pre code .hljs-keyword, +.tiptap pre code .hljs-selector-tag, +.tiptap pre code .hljs-literal, +.tiptap pre code .hljs-section, +.tiptap pre code .hljs-link { color: #c792ea; } +.tiptap pre code .hljs-string, +.tiptap pre code .hljs-title, +.tiptap pre code .hljs-name, +.tiptap pre code .hljs-type, +.tiptap pre code .hljs-attribute, +.tiptap pre code .hljs-symbol, +.tiptap pre code .hljs-bullet, +.tiptap pre code .hljs-addition, +.tiptap pre code .hljs-variable, +.tiptap pre code .hljs-template-tag, +.tiptap pre code .hljs-template-variable { color: #c3e88d; } +.tiptap pre code .hljs-comment, +.tiptap pre code .hljs-quote, +.tiptap pre code .hljs-deletion, +.tiptap pre code .hljs-meta { color: #676e95; } +.tiptap pre code .hljs-number, +.tiptap pre code .hljs-regexp, +.tiptap pre code .hljs-built_in { color: #f78c6c; } +.tiptap pre code .hljs-function .hljs-title, +.tiptap pre code .hljs-doctag, +.tiptap pre code .hljs-formula { color: #82aaff; } +.tiptap pre code .hljs-tag, +.tiptap pre code .hljs-subst { color: #f07178; } +.tiptap pre code .hljs-emphasis { font-style: italic; } +.tiptap pre code .hljs-strong { font-weight: bold; } +.tiptap pre code .hljs-attr, +.tiptap pre code .hljs-selector-attr, +.tiptap pre code .hljs-selector-class, +.tiptap pre code .hljs-selector-id, +.tiptap pre code .hljs-selector-pseudo { color: #ffcb6b; } /* 水平线 */ .tiptap hr { border: none; border-top: 1px solid #e5e7eb; margin: 1em 0; } @@ -68,7 +122,7 @@ .tiptap img { max-width: 100%; height: auto; border-radius: 6px; } /* 链接 */ -.tiptap a { color: #6366f1; text-decoration: underline; } +.tiptap a { color: #3498db; text-decoration: underline; } /* Placeholder */ .tiptap p.is-editor-empty:first-child::before { @@ -104,7 +158,21 @@ transition: background 0.15s; } .editor-toolbar button:hover { background: #e5e7eb; } -.editor-toolbar button.is-active { background: #1f2937; color: #fff; } +.editor-toolbar button.is-active { background: #1a2332; color: #fff; } +.code-lang-select { + height: 30px; + padding: 0 6px; + border: 1px solid transparent; + border-radius: 4px; + background: transparent; + color: #374151; + font-size: 12px; + cursor: pointer; + font-family: inherit; + outline: none; +} +.code-lang-select:hover { border-color: #e5e7eb; } +.code-lang-select:focus { border-color: #3498db; } .editor-toolbar .toolbar-divider { width: 1px; background: #e5e7eb; @@ -143,6 +211,35 @@ + @@ -170,6 +267,19 @@ {{template "layout/footer.html" .}} + diff --git a/templates/MetaLab-2026/html/posts/show.html b/templates/MetaLab-2026/html/posts/show.html index ed9c7d4..248a427 100644 --- a/templates/MetaLab-2026/html/posts/show.html +++ b/templates/MetaLab-2026/html/posts/show.html @@ -47,6 +47,20 @@