From e028614dbad902fd10e7121efa4930050f510673 Mon Sep 17 00:00:00 2001 From: eezd Date: Wed, 21 Oct 2026 16:34:02 +0800 Subject: [PATCH] Adapt built-in themes to the supplied editor snippets --- internal/api/themes/black.css | 22 +++++++------- internal/api/themes/white.css | 20 ++++++------- web/src/highlight/theme.test.ts | 16 +++++++++++ web/src/highlight/theme.ts | 18 ++++++++++++ web/src/highlight/worker.ts | 6 ++-- web/src/share/share.css | 12 ++++---- web/src/themes/base.css | 51 ++++++++++++++++----------------- 7 files changed, 89 insertions(+), 56 deletions(-) create mode 100644 web/src/highlight/theme.test.ts create mode 100644 web/src/highlight/theme.ts diff --git a/internal/api/themes/black.css b/internal/api/themes/black.css index 4936f54..bf41263 100644 --- a/internal/api/themes/black.css +++ b/internal/api/themes/black.css @@ -1,30 +1,30 @@ :root { color-scheme: dark; - --rm-bg: #0b0b0c; - --rm-sidebar-bg: #111113; + --rm-bg: #1e2022; + --rm-sidebar-bg: #191b1d; --rm-text: #e6e8eb; --rm-text-muted: #9b9fa8; --rm-divider: #2b2d33; - --rm-accent: #8eafff; - --rm-accent-strong: #b3c9ff; - --rm-selection: rgba(142, 175, 255, 0.25); + --rm-accent: #42b983; + --rm-accent-strong: #72cda5; + --rm-selection: rgba(66, 185, 131, 0.28); --rm-hover: rgba(230, 232, 235, 0.065); - --rm-active: rgba(142, 175, 255, 0.14); + --rm-active: rgba(66, 185, 131, 0.16); --rm-danger: #ff8291; --rm-warn-bg: #2c2413; --rm-warn-text: #e8c477; --rm-shadow: 0 12px 36px rgba(0, 0, 0, 0.6); --rm-inline-code-bg: #202127; - --rm-inline-code-text: #c3d5f5; - --rm-code-bg: #18191e; - --rm-code-text: #e1e4e8; + --rm-inline-code-text: #f19a58; + --rm-code-bg: #263238; + --rm-code-text: #abb2bf; --rm-highlight-bg: rgba(232, 196, 119, 0.25); } -/* 浅蓝按钮使用深色文字,保持选中状态与操作按钮的对比度。 */ +/* 绿色按钮使用深色文字,保持按钮的文字对比度。 */ .rm-btn-primary, .rm-btn-primary:hover, .rm-segmented button.on, .rm-share-password button { - color: #0b0b0c; + color: #101b16; } diff --git a/internal/api/themes/white.css b/internal/api/themes/white.css index 764d712..77dc87c 100644 --- a/internal/api/themes/white.css +++ b/internal/api/themes/white.css @@ -1,22 +1,22 @@ :root { color-scheme: light; --rm-bg: #ffffff; - --rm-sidebar-bg: #f6f7f9; - --rm-text: #202124; + --rm-sidebar-bg: #f6f7f6; + --rm-text: #252a2e; --rm-text-muted: #656b75; --rm-divider: #e3e6eb; - --rm-accent: #2563eb; - --rm-accent-strong: #1d4ed8; - --rm-selection: rgba(37, 99, 235, 0.18); + --rm-accent: #42b983; + --rm-accent-strong: #27865e; + --rm-selection: rgba(66, 185, 131, 0.22); --rm-hover: rgba(32, 33, 36, 0.045); - --rm-active: rgba(37, 99, 235, 0.10); + --rm-active: rgba(66, 185, 131, 0.12); --rm-danger: #c42b3a; --rm-warn-bg: #fff8e6; --rm-warn-text: #855600; --rm-shadow: 0 12px 36px rgba(24, 31, 44, 0.12); - --rm-inline-code-bg: #f0f2f5; - --rm-inline-code-text: #40536d; - --rm-code-bg: #292d3e; - --rm-code-text: #e1e4e8; + --rm-inline-code-bg: #f8f8f8; + --rm-inline-code-text: #e96900; + --rm-code-bg: #263238; + --rm-code-text: #abb2bf; --rm-highlight-bg: #fff0a8; } diff --git a/web/src/highlight/theme.test.ts b/web/src/highlight/theme.test.ts new file mode 100644 index 0000000..0c5d6ce --- /dev/null +++ b/web/src/highlight/theme.test.ts @@ -0,0 +1,16 @@ +import { expect, it } from 'vitest' +import { createHighlighterCore } from 'shiki/core' +import { createJavaScriptRegexEngine } from 'shiki/engine/javascript' +import { bundledLanguages } from 'shiki/langs' +import { codeTheme } from './theme' + +it('实际语法高亮使用参考片段的关键字、字符串和注释颜色', async () => { + const language = await bundledLanguages.javascript() + const highlighter = await createHighlighterCore({ themes: [codeTheme], langs: language.default, engine: createJavaScriptRegexEngine() }) + try { + const tokens = highlighter.codeToTokensBase('const value = "hello" // comment', { lang: 'javascript', theme: codeTheme.name }).flat() + expect(tokens.find((token) => token.content === 'const')?.color).toBe('#C678DD') + expect(tokens.find((token) => token.content.includes('hello'))?.color).toBe('#98C379') + expect(tokens.find((token) => token.content.includes('comment'))?.color).toBe('#8F9094') + } finally { highlighter.dispose() } +}) diff --git a/web/src/highlight/theme.ts b/web/src/highlight/theme.ts new file mode 100644 index 0000000..4269590 --- /dev/null +++ b/web/src/highlight/theme.ts @@ -0,0 +1,18 @@ +// 适配用户提供的 Obsidian CodeBlock.css:编辑器与分享页共用深色代码配色。 +export const codeTheme = { + name: 'rawmark-code', + type: 'dark' as const, + colors: { 'editor.background': '#263238', 'editor.foreground': '#abb2bf' }, + tokenColors: [ + { scope: ['comment', 'punctuation.definition.comment'], settings: { foreground: '#8f9094', fontStyle: 'italic' } }, + { scope: ['keyword', 'storage'], settings: { foreground: '#c678dd' } }, + { scope: ['keyword.operator'], settings: { foreground: '#56b6c2' } }, + { scope: ['string', 'constant.other.symbol'], settings: { foreground: '#98c379' } }, + { scope: ['constant.numeric', 'constant.language', 'support.constant'], settings: { foreground: '#d19a66' } }, + { scope: ['entity.name.function', 'support.function'], settings: { foreground: '#d19a66' } }, + { scope: ['entity.name.tag', 'variable.other.property', 'support.type.property-name'], settings: { foreground: '#e06c75' } }, + { scope: ['entity.name.type', 'entity.name.class', 'support.type', 'support.class'], settings: { foreground: '#e5c07b' } }, + { scope: ['variable', 'punctuation'], settings: { foreground: '#abb2bf' } }, + { scope: ['variable.other.property'], settings: { foreground: '#e06c75' } }, + ], +} diff --git a/web/src/highlight/worker.ts b/web/src/highlight/worker.ts index fbadad2..a3e59d4 100644 --- a/web/src/highlight/worker.ts +++ b/web/src/highlight/worker.ts @@ -1,13 +1,13 @@ import { createHighlighterCore, type HighlighterCore } from 'shiki/core' import { createJavaScriptRegexEngine } from 'shiki/engine/javascript' import { bundledLanguages } from 'shiki/langs' -import githubDark from 'shiki/themes/github-dark.mjs' +import { codeTheme } from './theme' import type { HighlightRequest, HighlightResponse, TokenRun } from './protocol' -const THEME = 'github-dark' +const THEME = codeTheme.name const highlighterReady: Promise = createHighlighterCore({ - themes: [githubDark], + themes: [codeTheme], langs: [], engine: createJavaScriptRegexEngine({ forgiving: true }), }) diff --git a/web/src/share/share.css b/web/src/share/share.css index bbdc144..d4a1ba2 100644 --- a/web/src/share/share.css +++ b/web/src/share/share.css @@ -51,20 +51,20 @@ .rm-share-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .rm-share-time { flex: none; } -.rm-share-content h1 { font-size: 2em; line-height: 1.3; margin: 0.9em 0 0.4em; } -.rm-share-content h2 { font-size: 1.6em; line-height: 1.35; margin: 0.9em 0 0.4em; } -.rm-share-content h3 { font-size: 1.3em; line-height: 1.4; margin: 0.8em 0 0.4em; } -.rm-share-content h4 { font-size: 1.15em; margin: 0.8em 0 0.4em; } +.rm-share-content h1 { font-size: 2.2em; line-height: 1.3; margin: 0.9em 0 0.4em; } +.rm-share-content h2 { font-size: 1.75em; line-height: 1.225; padding-bottom: 0.5em; border-bottom: 1px solid var(--rm-divider); margin: 0.9em 0 0.4em; } +.rm-share-content h3 { font-size: 1.4em; line-height: 1.43; margin: 0.8em 0 0.4em; } +.rm-share-content h4 { font-size: 1.2em; margin: 0.8em 0 0.4em; } .rm-share-content h5, .rm-share-content h6 { font-size: 1em; margin: 0.8em 0 0.4em; } -.rm-share-content h6 { color: var(--rm-text-muted); } + .rm-share-content p { margin: 0.6em 0; } .rm-share-content a { color: var(--rm-accent-strong); text-decoration: none; } .rm-share-content a:hover { text-decoration: underline; } .rm-share-content img { max-width: 100%; border-radius: 4px; } .rm-share-content mark { background: var(--rm-highlight-bg); color: inherit; padding: 0 2px; border-radius: 2px; } .rm-share-content hr { border: 0; border-top: 1px solid var(--rm-divider); margin: 1.5em 0; } -.rm-share-content blockquote { margin: 0.8em 0; padding-left: 16px; border-left: 3px solid var(--rm-divider); color: var(--rm-text-muted); } +.rm-share-content blockquote { margin: 0.8em 0; padding: 5px 15px; border-left: 4px solid #42b983; background: rgba(66, 185, 131, 0.1); } .rm-share-content ul, .rm-share-content ol { padding-left: 1.6em; } .rm-share-content li > input[type='checkbox'] { margin: 0 0.4em 0 -1.4em; accent-color: var(--rm-accent); vertical-align: -0.1em; } diff --git a/web/src/themes/base.css b/web/src/themes/base.css index 4f895e7..63e2885 100644 --- a/web/src/themes/base.css +++ b/web/src/themes/base.css @@ -1,6 +1,6 @@ /* * 基础主题。--rm-* 变量即主题 API,第三方主题只需覆盖这些变量。 - * 代码块与行内代码配色取自 vuejs.org(@vue/theme + Shiki github-dark)。 + * 代码与编辑样式适配用户提供的 Obsidian CSS 片段。 */ :root { color-scheme: light; @@ -22,35 +22,35 @@ --rm-content-width: 760px; --rm-bg: #ffffff; - --rm-text: #213547; + --rm-text: #252a2e; --rm-text-muted: rgba(60, 60, 60, 0.7); --rm-divider: rgba(60, 60, 60, 0.12); --rm-accent: #42b883; --rm-accent-strong: #33a06f; --rm-selection: rgba(66, 184, 131, 0.22); - --rm-inline-code-bg: #f1f1f1; - --rm-inline-code-text: #476582; - --rm-code-bg: #292d3e; - --rm-code-text: #e1e4e8; + --rm-inline-code-bg: #f8f8f8; + --rm-inline-code-text: #e96900; + --rm-code-bg: #263238; + --rm-code-text: #abb2bf; --rm-code-font-size: 14px; --rm-highlight-bg: rgba(255, 213, 0, 0.35); } :root:where([data-rm-color-mode="dark"]) { color-scheme: dark; - --rm-bg: #1a1a1a; + --rm-bg: #1e2022; --rm-text: rgba(255, 255, 255, 0.87); --rm-text-muted: rgba(235, 235, 235, 0.6); --rm-divider: rgba(84, 84, 84, 0.48); --rm-accent: #42d392; --rm-accent-strong: #42b883; --rm-inline-code-bg: #2f2f2f; - --rm-inline-code-text: #aac8e4; - --rm-code-bg: #242424; + --rm-inline-code-text: #f19a58; + --rm-code-bg: #263238; --rm-highlight-bg: rgba(255, 213, 0, 0.28); - --rm-sidebar-bg: #161618; + --rm-sidebar-bg: #191b1d; --rm-hover: rgba(255, 255, 255, 0.06); --rm-active: rgba(66, 211, 146, 0.16); --rm-warn-bg: #2f2716; @@ -61,18 +61,18 @@ @media (prefers-color-scheme: dark) { :root:where(:not([data-rm-color-mode])) { color-scheme: dark; - --rm-bg: #1a1a1a; + --rm-bg: #1e2022; --rm-text: rgba(255, 255, 255, 0.87); --rm-text-muted: rgba(235, 235, 235, 0.6); --rm-divider: rgba(84, 84, 84, 0.48); --rm-accent: #42d392; --rm-accent-strong: #42b883; --rm-inline-code-bg: #2f2f2f; - --rm-inline-code-text: #aac8e4; - --rm-code-bg: #242424; + --rm-inline-code-text: #f19a58; + --rm-code-bg: #263238; --rm-highlight-bg: rgba(255, 213, 0, 0.28); - --rm-sidebar-bg: #161618; + --rm-sidebar-bg: #191b1d; --rm-hover: rgba(255, 255, 255, 0.06); --rm-active: rgba(66, 211, 146, 0.16); --rm-warn-bg: #2f2716; @@ -105,10 +105,10 @@ body { max-width: var(--rm-content-width); margin: 0 auto; padding: 32px 24px 40vh; - caret-color: var(--rm-accent-strong); + caret-color: #ff0000; } .cm-editor .cm-cursor { - border-left: 2px solid var(--rm-accent-strong); + border-left: 2px solid #ff0000; } .cm-editor .cm-selectionBackground, .cm-editor.cm-focused .cm-selectionBackground { @@ -116,13 +116,12 @@ body { } /* 标题 */ -.cm-line.cm-rm-h1 { font-size: 2em; line-height: 1.3; padding-top: 0.6em; } -.cm-line.cm-rm-h2 { font-size: 1.6em; line-height: 1.35; padding-top: 0.5em; } -.cm-line.cm-rm-h3 { font-size: 1.3em; line-height: 1.4; padding-top: 0.4em; } -.cm-line.cm-rm-h4 { font-size: 1.15em; padding-top: 0.3em; } -.cm-line.cm-rm-h5, -.cm-line.cm-rm-h6 { font-size: 1em; padding-top: 0.2em; } -.cm-line.cm-rm-h6 { color: var(--rm-text-muted); } +.cm-line:is(.cm-rm-h1, .cm-rm-h2, .cm-rm-h3, .cm-rm-h4, .cm-rm-h5, .cm-rm-h6) { line-height: 1.4; padding-top: 1rem; padding-bottom: 0.5rem; } +.cm-line.cm-rm-h1 { font-size: 2.2em; line-height: 1.3; } +.cm-line.cm-rm-h2 { font-size: 1.75em; line-height: 1.225; border-bottom: 1px solid var(--rm-divider); } +.cm-line.cm-rm-h3 { font-size: 1.4em; line-height: 1.43; } +.cm-line.cm-rm-h4 { font-size: 1.2em; } +.cm-line.cm-rm-h5, .cm-line.cm-rm-h6 { font-size: 1em; } /* Markdown 标记(激活行显示源码时) */ .cm-rm-markup, @@ -190,9 +189,9 @@ body { /* 引用 */ .cm-line.cm-rm-quote { - border-left: 3px solid var(--rm-divider); - padding-left: 16px; - color: var(--rm-text-muted); + border-left: 4px solid #42b983; + padding: 5px 15px; + background: rgba(66, 185, 131, 0.1); } /* 列表 */