From 800129e2458c8595d30479df606252497f34d4b9 Mon Sep 17 00:00:00 2001 From: xfy Date: Thu, 23 Jul 2026 15:12:48 +0800 Subject: [PATCH] =?UTF-8?q?refactor(shared):=20=E4=B8=8B=E6=B2=89=20mermai?= =?UTF-8?q?d=20Catppuccin=20=E4=B8=BB=E9=A2=98=E5=8F=98=E9=87=8F=E5=88=B0?= =?UTF-8?q?=20shared=20=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 前台 yggdrasil-core 的 LATTE_VARS/MOCHA_VARS 是 mermaid 配色唯一真相源。 后台 tiptap 编辑器要实现 mermaid 预览且与前台视觉一致,需复用同一套变量; 若复制一份会立即产生漂移风险,而「主题一致性」正是编辑器预览的核心目标。 按 shared 包「单一真相源」的设计意图,把两套主题变量(~100 行)+ MermaidThemeVariables 类型 + mermaidThemeVarsFor(theme) 工具函数搬到 shared。yggdrasil-core 改 import 自 shared,删除本地定义——行为零变化(同一份变量,只换引用源)。 前台 37 个测试全绿回归,含 10 个 mermaid 测试验证亮/暗主题传入正确调色板。 --- libs/shared/src/index.ts | 128 +++++++++++++++++++++++++++++ libs/yggdrasil-core/src/mermaid.ts | 124 ++-------------------------- 2 files changed, 133 insertions(+), 119 deletions(-) diff --git a/libs/shared/src/index.ts b/libs/shared/src/index.ts index 3831d5f..209103d 100644 --- a/libs/shared/src/index.ts +++ b/libs/shared/src/index.ts @@ -25,3 +25,131 @@ export const THEME_CHANGE_EVENT = 'yggdrasil:theme-change'; export function prefersReducedMotion(): boolean { return !!window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; } + +// --------------------------------------------------------------------------- +// Mermaid Catppuccin 主题变量 +// --------------------------------------------------------------------------- +// +// 这是 mermaid 渲染配色的单一真相源。前台 yggdrasil-core 与后台 tiptap-editor +// 都从这里取,保证「编辑器预览 = 线上文章页」视觉一致(各 IIFE 不能 import 彼此, +// 但都能 inline 本包)。 +// +// mermaid 把颜色烤进 SVG 内联 style,无法靠 CSS 原地改主题,故须在 initialize 时注入。 +// 用 `theme: 'base'`(非 'default'/'dark'):base 主题不硬编码颜色,themeVariables 能完全 +// 控制调色板;'default' 主题硬编码 mainBkg=#ECECFF 等会阻断覆盖。 +// +// 设计哲学:极简卡片化——节点用 surface 色阶(卡片感)、边框/连线用 subtext 色阶(克制)、 +// 文字用 primary text。不滥用强调色,绿/紫等 accent 留给作者用 classDef 手动强调。 +// hex 值取自 themes/Catppuccin Latte.tmTheme 与 Catppuccin Mocha.tmTheme。 +// +// 覆盖的字段涵盖 flowchart / sequence / class 三类图(测试文章用到的全部类型)。 + +/** mermaid themeVariables 的字段集合(mermaid 实际接受更多字段,这里只列项目用到的)。 */ +export type MermaidThemeVariables = Record; + +/** Latte(亮)主题:节点 surface 色阶、文字 #4c4f69、连线 subtext1 #5c5f77。 */ +export const MERMAID_LATTE_VARS: MermaidThemeVariables = { + background: '#dce0e8', // = --color-paper-code-block,图背景与 pre 无缝衔接 + // 节点填充:surface 色阶递进(主/次/三级),卡片质感 + primaryColor: '#e6e9ef', + secondaryColor: '#ccd0da', + tertiaryColor: '#bcc0cc', + mainBkg: '#e6e9ef', + nodeBkg: '#e6e9ef', + secondBkg: '#ccd0da', + // 节点边框:surface1 偏冷灰 + primaryBorderColor: '#bcc0cc', + secondaryBorderColor: '#acb0be', + tertiaryBorderColor: '#9ca0b0', + nodeBorder: '#bcc0cc', + clusterBorder: '#bcc0cc', + labelBoxBorderColor: '#bcc0cc', + // 文字:primary text #4c4f69 + primaryTextColor: '#4c4f69', + secondaryTextColor: '#5c5f77', + tertiaryTextColor: '#6c6f85', + textColor: '#4c4f69', + nodeTextColor: '#4c4f69', + titleColor: '#4c4f69', + classText: '#4c4f69', + labelTextColor: '#4c4f69', + // 连线/箭头:subtext1 #5c5f77 + lineColor: '#5c5f77', + defaultLinkColor: '#5c5f77', + arrowheadColor: '#5c5f77', + // 时序图 + actorBkg: '#e6e9ef', + actorBorder: '#bcc0cc', + actorTextColor: '#4c4f69', + actorLineColor: '#5c5f77', + signalColor: '#5c5f77', + signalTextColor: '#4c4f69', + loopTextColor: '#4c4f69', + sequenceNumberColor: '#eff1f5', + activationBkgColor: '#ccd0da', + activationBorderColor: '#bcc0cc', + // 边标签 / 子图背景 + edgeLabelBackground: '#eff1f5', + labelBoxBkgColor: '#eff1f5', + clusterBkg: 'rgba(239, 241, 245, 0.5)', + // 注释:低饱和黄(Latte yellow #df8e1d) + noteBkgColor: 'rgba(223, 142, 29, 0.15)', + noteBorderColor: '#df8e1d', + noteTextColor: '#4c4f69', + // 字体:与正文 sans 对齐,中文友好 + fontFamily: + "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif", + fontSize: '16px', +}; + +/** Mocha(暗)主题:节点 surface 色阶、文字 #cdd6f4、连线 subtext0 #a6adc8。 */ +export const MERMAID_MOCHA_VARS: MermaidThemeVariables = { + background: '#313244', // = --color-paper-code-block(暗) + primaryColor: '#45475a', + secondaryColor: '#585b70', + tertiaryColor: '#1e1e2e', + mainBkg: '#45475a', + nodeBkg: '#45475a', + secondBkg: '#585b70', + primaryBorderColor: '#585b70', + secondaryBorderColor: '#45475a', + tertiaryBorderColor: '#313244', + nodeBorder: '#585b70', + clusterBorder: '#585b70', + labelBoxBorderColor: '#585b70', + primaryTextColor: '#cdd6f4', + secondaryTextColor: '#bac2de', + tertiaryTextColor: '#a6adc8', + textColor: '#cdd6f4', + nodeTextColor: '#cdd6f4', + titleColor: '#cdd6f4', + classText: '#cdd6f4', + labelTextColor: '#cdd6f4', + lineColor: '#a6adc8', + defaultLinkColor: '#a6adc8', + arrowheadColor: '#a6adc8', + actorBkg: '#45475a', + actorBorder: '#585b70', + actorTextColor: '#cdd6f4', + actorLineColor: '#a6adc8', + signalColor: '#a6adc8', + signalTextColor: '#cdd6f4', + loopTextColor: '#cdd6f4', + sequenceNumberColor: '#1e1e2e', + activationBkgColor: '#585b70', + activationBorderColor: '#6c7086', + edgeLabelBackground: '#1e1e2e', + labelBoxBkgColor: '#1e1e2e', + clusterBkg: 'rgba(30, 30, 46, 0.5)', + noteBkgColor: 'rgba(249, 226, 175, 0.12)', + noteBorderColor: '#f9e2af', + noteTextColor: '#cdd6f4', + fontFamily: + "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif", + fontSize: '16px', +}; + +/** 按主题返回对应 Catppuccin themeVariables 的副本(调用方可安全 mutate)。 */ +export function mermaidThemeVarsFor(theme: ThemeName): MermaidThemeVariables { + return theme === 'dark' ? { ...MERMAID_MOCHA_VARS } : { ...MERMAID_LATTE_VARS }; +} diff --git a/libs/yggdrasil-core/src/mermaid.ts b/libs/yggdrasil-core/src/mermaid.ts index 97dc59c..c05b5d5 100644 --- a/libs/yggdrasil-core/src/mermaid.ts +++ b/libs/yggdrasil-core/src/mermaid.ts @@ -20,6 +20,7 @@ */ import type { ThemeName } from '@yggdrasil/shared'; +import { mermaidThemeVarsFor } from '@yggdrasil/shared'; import { onThemeChange } from './theme-transition'; /** 文章正文容器选择器(与 post_content.rs 的 __initMermaid 调用一致)。 */ @@ -31,126 +32,11 @@ type MermaidApi = { }; /** - * Catppuccin themeVariables(与站点 --color-paper-* / .tmTheme 调色板对齐)。 - * - * mermaid 把颜色烤进 SVG 内联 style,无法靠 CSS 原地改主题,故须在 initialize 时注入。 - * 用 `theme: 'base'`(非 'default'/'dark'):base 主题不硬编码颜色,themeVariables 能完全 - * 控制调色板;'default' 主题硬编码 mainBkg=#ECECFF 等会阻断覆盖。 - * - * 设计哲学:极简卡片化——节点用 surface 色阶(卡片感)、边框/连线用 subtext 色阶(克制)、 - * 文字用 primary text。不滥用强调色,绿/紫等 accent 留给作者用 classDef 手动强调。 - * hex 值取自 themes/Catppuccin Latte.tmTheme 与 Catppuccin Mocha.tmTheme。 - * - * 覆盖的字段涵盖 flowchart / sequence / class 三类图(测试文章用到的全部类型)。 + * Catppuccin themeVariables 已下沉到 @yggdrasil/shared(前台与后台 tiptap 编辑器 + * 共用单一真相源,保证渲染一致)。用 `theme: 'base'` 让 themeVariables 完全控制 + * 调色板;详见 shared/src/index.ts 的 MERMAID_LATTE_VARS / MERMAID_MOCHA_VARS。 */ -/** Latte(亮)主题:节点 surface 色阶、文字 #4c4f69、连线 subtext1 #5c5f77。 */ -const LATTE_VARS = { - background: '#dce0e8', // = --color-paper-code-block,图背景与 pre 无缝衔接 - // 节点填充:surface 色阶递进(主/次/三级),卡片质感 - primaryColor: '#e6e9ef', - secondaryColor: '#ccd0da', - tertiaryColor: '#bcc0cc', - mainBkg: '#e6e9ef', - nodeBkg: '#e6e9ef', - secondBkg: '#ccd0da', - // 节点边框:surface1 偏冷灰 - primaryBorderColor: '#bcc0cc', - secondaryBorderColor: '#acb0be', - tertiaryBorderColor: '#9ca0b0', - nodeBorder: '#bcc0cc', - clusterBorder: '#bcc0cc', - labelBoxBorderColor: '#bcc0cc', - // 文字:primary text #4c4f69 - primaryTextColor: '#4c4f69', - secondaryTextColor: '#5c5f77', - tertiaryTextColor: '#6c6f85', - textColor: '#4c4f69', - nodeTextColor: '#4c4f69', - titleColor: '#4c4f69', - classText: '#4c4f69', - labelTextColor: '#4c4f69', - // 连线/箭头:subtext1 #5c5f77 - lineColor: '#5c5f77', - defaultLinkColor: '#5c5f77', - arrowheadColor: '#5c5f77', - // 时序图 - actorBkg: '#e6e9ef', - actorBorder: '#bcc0cc', - actorTextColor: '#4c4f69', - actorLineColor: '#5c5f77', - signalColor: '#5c5f77', - signalTextColor: '#4c4f69', - loopTextColor: '#4c4f69', - sequenceNumberColor: '#eff1f5', - activationBkgColor: '#ccd0da', - activationBorderColor: '#bcc0cc', - // 边标签 / 子图背景 - edgeLabelBackground: '#eff1f5', - labelBoxBkgColor: '#eff1f5', - clusterBkg: 'rgba(239, 241, 245, 0.5)', - // 注释:低饱和黄(Latte yellow #df8e1d) - noteBkgColor: 'rgba(223, 142, 29, 0.15)', - noteBorderColor: '#df8e1d', - noteTextColor: '#4c4f69', - // 字体:与正文 sans 对齐,中文友好 - fontFamily: - "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif", - fontSize: '16px', -} as const; - -/** Mocha(暗)主题:节点 surface 色阶、文字 #cdd6f4、连线 subtext0 #a6adc8。 */ -const MOCHA_VARS = { - background: '#313244', // = --color-paper-code-block(暗) - primaryColor: '#45475a', - secondaryColor: '#585b70', - tertiaryColor: '#1e1e2e', - mainBkg: '#45475a', - nodeBkg: '#45475a', - secondBkg: '#585b70', - primaryBorderColor: '#585b70', - secondaryBorderColor: '#45475a', - tertiaryBorderColor: '#313244', - nodeBorder: '#585b70', - clusterBorder: '#585b70', - labelBoxBorderColor: '#585b70', - primaryTextColor: '#cdd6f4', - secondaryTextColor: '#bac2de', - tertiaryTextColor: '#a6adc8', - textColor: '#cdd6f4', - nodeTextColor: '#cdd6f4', - titleColor: '#cdd6f4', - classText: '#cdd6f4', - labelTextColor: '#cdd6f4', - lineColor: '#a6adc8', - defaultLinkColor: '#a6adc8', - arrowheadColor: '#a6adc8', - actorBkg: '#45475a', - actorBorder: '#585b70', - actorTextColor: '#cdd6f4', - actorLineColor: '#a6adc8', - signalColor: '#a6adc8', - signalTextColor: '#cdd6f4', - loopTextColor: '#cdd6f4', - sequenceNumberColor: '#1e1e2e', - activationBkgColor: '#585b70', - activationBorderColor: '#6c7086', - edgeLabelBackground: '#1e1e2e', - labelBoxBkgColor: '#1e1e2e', - clusterBkg: 'rgba(30, 30, 46, 0.5)', - noteBkgColor: 'rgba(249, 226, 175, 0.12)', - noteBorderColor: '#f9e2af', - noteTextColor: '#cdd6f4', - fontFamily: - "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif", - fontSize: '16px', -} as const; - -/** 按主题返回对应 Catppuccin themeVariables。 */ -function themeVarsFor(theme: ThemeName): Record { - return theme === 'dark' ? { ...MOCHA_VARS } : { ...LATTE_VARS }; -} - declare global { interface Window { MermaidRenderer?: MermaidApi; @@ -234,7 +120,7 @@ async function renderBlock(pre: HTMLPreElement, source: string, theme: ThemeName useMaxWidth: true, htmlLabels: true, }, - themeVariables: themeVarsFor(theme), + themeVariables: mermaidThemeVarsFor(theme), }); const id = `mermaid-svg-${++renderCounter}`; const { svg } = await mermaid.render(id, source);