fix(theme): 全主题文字对比度达标 WCAG AA——8套主题 textTertiary 从 #666/#999(对比2-3✗) 提升到 #8a8a9a/#6a5a4a 等(≥4.5✓); textSecondary 提升到 ≥5.3; 浅色主题 accentText 加深(latte #a06840→#8a5830, daylight #2070c0→#1860a8); 组件硬编码色 #2a2a3a/#3a4a6a/#4a6a8a/#fff/#888/#ddd 全替换为 CSS 变量(App/FileTree/GraphView/ImageView/TagFilter/SettingsPanel/InputDialog); styles.css 加 :root[data-theme-group=light] 浅色语法高亮配色; applyTheme 传 group 参数设置 data-theme-group 属性; 标签栏开关 ☰/– 图标修复可见性; TitleBar ▤🎨 按钮加 noDrag 修复不可点击

This commit is contained in:
hunter-dev
2026-08-26 16:44:38 +08:00
parent 01cec4e4c6
commit a6458c03e9
12 changed files with 43 additions and 28 deletions
+5 -5
View File
@@ -52,7 +52,7 @@ export function App() {
// 编辑器样式随设置生效(FR-编辑器设置真实生效)
useEffect(() => { document.documentElement.style.setProperty('--editor-font-size', settings.fontSize + 'px'); document.documentElement.style.setProperty('--editor-font-family', settings.fontFamily); document.documentElement.style.setProperty('--editor-white-space', settings.wordWrap ? 'pre-wrap' : 'pre'); }, [settings.fontSize, settings.fontFamily, settings.wordWrap]);
return (
<div style={{ position: 'relative', display: 'flex', flexDirection: 'column', height: '100vh', background: 'var(--bg-primary, #1a1a1a)', color: '#ddd' }}>
<div style={{ position: 'relative', display: 'flex', flexDirection: 'column', height: '100vh', background: 'var(--bg-primary, #1a1a1a)', color: 'var(--text-primary)' }}>
{appearance.bgImage && <div style={{ position: 'absolute', inset: 0, backgroundImage: 'url(' + appearance.bgImage + ')', backgroundSize: 'cover', backgroundPosition: 'center', filter: settings.perfMode ? 'none' : 'blur(var(--blur-px,0px))', opacity: 0.45, zIndex: 0, pointerEvents: 'none' }} />}
<div style={{ position: 'relative', zIndex: 1, display: 'flex', flexDirection: 'column', height: '100vh' }}>
<TitleBar view={view} onToggleView={() => setView(v => v === 'editor' ? 'graph' : 'editor')} onSettings={() => setShowSettings(true)} onAppearance={() => setShowAppearance(true)} onToggleSidebar={() => layout.toggleSidebar()} onToggleMinimal={() => layout.toggleMinimal()} onToggleTabbar={() => layout.toggleTabbar()} minimal={layout.minimal} tabbarVisible={layout.tabbarVisible} />
@@ -61,7 +61,7 @@ export function App() {
<div style={{ flex: 1, display: 'flex', overflow: 'hidden' }}>
{layout.minimal ? (<Editor />) : (<>
{layout.sidebarVisible && (<>
<div style={{ width: layout.sidebarWidth, borderRight: '1px solid #2a2a3a', display: 'flex', flexDirection: 'column', overflow: 'hidden', flexShrink: 0 }}>
<div style={{ width: layout.sidebarWidth, borderRight: '1px solid var(--control-border)', display: 'flex', flexDirection: 'column', overflow: 'hidden', flexShrink: 0 }}>
<SearchBox />
<div style={{ flex: 1, overflow: 'auto' }}><FileTree /></div>
<TagFilter />
@@ -71,8 +71,8 @@ export function App() {
{view === 'editor' ? <Editor /> : <GraphView onSwitchToEditor={() => setView('editor')} />}
{view === 'editor' && layout.outlineVisible && (<>
<Resizer side="right" onDrag={d => layout.setOutlineWidth(layout.outlineWidth + d)} />
<div style={{ width: layout.outlineWidth, borderLeft: '1px solid #2a2a3a', display: 'flex', flexDirection: 'column', overflow: 'hidden', flexShrink: 0 }}>
<div style={{ display: 'flex', borderBottom: '1px solid #2a2a3a' }}>
<div style={{ width: layout.outlineWidth, borderLeft: '1px solid var(--control-border)', display: 'flex', flexDirection: 'column', overflow: 'hidden', flexShrink: 0 }}>
<div style={{ display: 'flex', borderBottom: '1px solid var(--control-border)' }}>
<button style={tabBtn(rightTab==='outline')} onClick={() => setRightTab('outline')}>大纲</button>
<button style={tabBtn(rightTab==='info')} onClick={() => setRightTab('info')}>信息</button>
</div>
@@ -93,5 +93,5 @@ export function App() {
}
function tabBtn(active: boolean): React.CSSProperties {
return { flex: 1, padding: '6px', background: active ? '#2a2a3a' : 'transparent', border: 'none', color: active ? '#fff' : '#888', cursor: 'pointer', borderBottom: active ? '2px solid #4a9eff' : '2px solid transparent' };
return { flex: 1, padding: '6px', background: active ? 'var(--control-border)' : 'transparent', border: 'none', color: active ? 'var(--text-primary)' : 'var(--text-tertiary)', cursor: 'pointer', borderBottom: active ? '2px solid var(--accent)' : '2px solid transparent' };
}
@@ -92,7 +92,10 @@ export const useAppearance = create<AppearanceState>((set, get) => ({
get().preview();
},
preview: () => applyTheme(get().draft),
preview: () => {
const t = THEMES.find(x => x.id === get().themeId);
applyTheme(get().draft, t?.group);
},
load: async () => {
try {
@@ -8,18 +8,21 @@ export interface ThemeParams {
export interface ThemeDef { id: string; name: string; group: 'dark' | 'light'; desc: string; params: ThemeParams; }
export const THEMES: ThemeDef[] = [
{ id: 'transparent', name: '全透明', group: 'dark', desc: '窗口直接透出桌面', params: { accent:'#4a9eff', glow:'#6a8cff', accentText:'#7ab8ff', textPrimary:'#e8e8e8', textSecondary:'#aaa', textTertiary:'#666', controlBorder:'#333', bgPrimary:'transparent', bgSecondary:'rgba(20,20,28,0.5)', blurPx:20, surfaceOpacity:0.5, radius:8 } },
{ id: 'midnight', name: '午夜蓝', group: 'dark', desc: '深邃午夜蓝调', params: { accent:'#4a9eff', glow:'#6a8cff', accentText:'#7ab8ff', textPrimary:'#e8e8e8', textSecondary:'#aaa', textTertiary:'#666', controlBorder:'#2a3548', bgPrimary:'#0d1525', bgSecondary:'#141d30', blurPx:16, surfaceOpacity:0.6, radius:8 } },
{ id: 'purple-night', name: '深紫夜', group: 'dark', desc: '神秘紫夜', params: { accent:'#a06aff', glow:'#b88aff', accentText:'#c4a3ff', textPrimary:'#e8e8f0', textSecondary:'#aaa', textTertiary:'#666', controlBorder:'#2a2438', bgPrimary:'#1a1228', bgSecondary:'#241a35', blurPx:16, surfaceOpacity:0.6, radius:8 } },
{ id: 'forest', name: '森林绿', group: 'dark', desc: '静谧森林', params: { accent:'#5abf7a', glow:'#7ad99a', accentText:'#7ad99a', textPrimary:'#e8f0e8', textSecondary:'#aaa', textTertiary:'#666', controlBorder:'#243028', bgPrimary:'#102018', bgSecondary:'#1a2a22', blurPx:16, surfaceOpacity:0.6, radius:8 } },
{ id: 'amber', name: '暖琥珀', group: 'dark', desc: '温暖琥珀光', params: { accent:'#e0a040', glow:'#f0c060', accentText:'#f0c060', textPrimary:'#f0e8d8', textSecondary:'#aaa', textTertiary:'#666', controlBorder:'#3a3028', bgPrimary:'#1f1810', bgSecondary:'#2a2018', blurPx:16, surfaceOpacity:0.6, radius:8 } },
{ id: 'latte', name: '奶霜拿铁', group: 'light', desc: '柔润奶白', params: { accent:'#c08050', glow:'#e0a070', accentText:'#a06840', textPrimary:'#3a3028', textSecondary:'#666', textTertiary:'#999', controlBorder:'#d8c8b8', bgPrimary:'#f5efe6', bgSecondary:'#ede5d8', blurPx:10, surfaceOpacity:0.8, radius:8 } },
{ id: 'daylight', name: '晴空白昼', group: 'light', desc: '清爽天蓝', params: { accent:'#3080d0', glow:'#50a0f0', accentText:'#2070c0', textPrimary:'#2a3a4a', textSecondary:'#5a6a7a', textTertiary:'#9aa', controlBorder:'#c8d8e8', bgPrimary:'#f0f5fa', bgSecondary:'#e0eaf2', blurPx:10, surfaceOpacity:0.85, radius:8 } },
{ id: 'plain', name: '素纸简白', group: 'light', desc: '极简素纸', params: { accent:'#555', glow:'#888', accentText:'#333', textPrimary:'#222', textSecondary:'#666', textTertiary:'#999', controlBorder:'#ddd', bgPrimary:'#fff', bgSecondary:'#f5f5f5', blurPx:0, surfaceOpacity:0.9, radius:6 } },
{ id: 'transparent', name: '全透明', group: 'dark', desc: '窗口直接透出桌面', params: { accent:'#4a9eff', glow:'#6a8cff', accentText:'#7ab8ff', textPrimary:'#e8e8e8', textSecondary:'#b8b8c8', textTertiary:'#8a8a9a', controlBorder:'#333', bgPrimary:'transparent', bgSecondary:'rgba(20,20,28,0.5)', blurPx:20, surfaceOpacity:0.5, radius:8 } },
{ id: 'midnight', name: '午夜蓝', group: 'dark', desc: '深邃午夜蓝调', params: { accent:'#4a9eff', glow:'#6a8cff', accentText:'#7ab8ff', textPrimary:'#e8e8e8', textSecondary:'#b8b8c8', textTertiary:'#8a8a9a', controlBorder:'#2a3548', bgPrimary:'#0d1525', bgSecondary:'#141d30', blurPx:16, surfaceOpacity:0.6, radius:8 } },
{ id: 'purple-night', name: '深紫夜', group: 'dark', desc: '神秘紫夜', params: { accent:'#a06aff', glow:'#b88aff', accentText:'#c4a3ff', textPrimary:'#e8e8f0', textSecondary:'#b8b8c8', textTertiary:'#8a8a9a', controlBorder:'#2a2438', bgPrimary:'#1a1228', bgSecondary:'#241a35', blurPx:16, surfaceOpacity:0.6, radius:8 } },
{ id: 'forest', name: '森林绿', group: 'dark', desc: '静谧森林', params: { accent:'#5abf7a', glow:'#7ad99a', accentText:'#7ad99a', textPrimary:'#e8f0e8', textSecondary:'#b8c8b8', textTertiary:'#8a9a8a', controlBorder:'#243028', bgPrimary:'#102018', bgSecondary:'#1a2a22', blurPx:16, surfaceOpacity:0.6, radius:8 } },
{ id: 'amber', name: '暖琥珀', group: 'dark', desc: '温暖琥珀光', params: { accent:'#e0a040', glow:'#f0c060', accentText:'#f0c060', textPrimary:'#f0e8d8', textSecondary:'#c8c0b0', textTertiary:'#9a8a7a', controlBorder:'#3a3028', bgPrimary:'#1f1810', bgSecondary:'#2a2018', blurPx:16, surfaceOpacity:0.6, radius:8 } },
{ id: 'latte', name: '奶霜拿铁', group: 'light', desc: '柔润奶白', params: { accent:'#a06840', glow:'#c08860', accentText:'#8a5830', textPrimary:'#3a3028', textSecondary:'#6a5a4a', textTertiary:'#6a5a4a', controlBorder:'#d8c8b8', bgPrimary:'#f5efe6', bgSecondary:'#ede5d8', blurPx:10, surfaceOpacity:0.8, radius:8 } },
{ id: 'daylight', name: '晴空白昼', group: 'light', desc: '清爽天蓝', params: { accent:'#2070c0', glow:'#4090e0', accentText:'#1860a8', textPrimary:'#2a3a4a', textSecondary:'#4a5a6a', textTertiary:'#5a6a7a', controlBorder:'#c8d8e8', bgPrimary:'#f0f5fa', bgSecondary:'#e0eaf2', blurPx:10, surfaceOpacity:0.85, radius:8 } },
{ id: 'plain', name: '素纸简白', group: 'light', desc: '极简素纸', params: { accent:'#555', glow:'#888', accentText:'#333', textPrimary:'#222', textSecondary:'#555', textTertiary:'#666', controlBorder:'#ddd', bgPrimary:'#fff', bgSecondary:'#f5f5f5', blurPx:0, surfaceOpacity:0.9, radius:6 } },
];
export function applyTheme(params: ThemeParams) {
export function applyTheme(params: ThemeParams, group?: 'dark' | 'light') {
const root = document.documentElement;
// 设置主题组(dark/light)供 CSS 浅色语法高亮等区分
root.setAttribute('data-theme-group', group ?? 'dark');
root.style.setProperty('--accent', params.accent);
root.style.setProperty('--glow', params.glow);
root.style.setProperty('--accent-text', params.accentText);
@@ -28,7 +28,7 @@ export function InputDialog() {
)}
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 }}>
{mode !== 'alert' && <button onClick={() => close(null)} style={btn}>取消</button>}
<button onClick={submit} style={{ ...btn, background: mode === 'confirm' ? '#5a2a2a' : 'var(--accent)', color: mode === 'confirm' ? '#f88' : 'var(--accent-text)' }}>{mode === 'confirm' ? '确认删除' : '确定'}</button>
<button onClick={submit} style={{ ...btn, background: mode === 'confirm' ? '#c0392b' : 'var(--accent)', color: mode === 'confirm' ? '#fff' : 'var(--accent-text)' }}>{mode === 'confirm' ? '确认删除' : '确定'}</button>
</div>
</div>
</div>
@@ -12,7 +12,7 @@ export function ImageView({ node, selected }: NodeViewProps) {
const src = (node.attrs.src as string) || '';
const display = toDisplaySrc(src);
return (
<NodeViewWrapper as="div" className="img-nodeview" style={{ textAlign: 'center', margin: '0.6em 0', outline: selected ? '2px solid #4a6a8a' : 'none' }}>
<NodeViewWrapper as="div" className="img-nodeview" style={{ textAlign: 'center', margin: '0.6em 0', outline: selected ? '2px solid var(--accent)' : 'none' }}>
<img src={display} alt={(node.attrs.alt as string) || ''} title={(node.attrs.title as string) || ''} style={{ maxWidth: '100%', borderRadius: 4 }} draggable={false} />
</NodeViewWrapper>
);
@@ -101,7 +101,7 @@ export function FileTree() {
return (
<div key={n.path}>
<div style={{ ...pad, display: 'flex', alignItems: 'center', cursor: 'pointer',
background: currentPath === n.path ? '#3a4a6a' : (isDragOver ? '#2a4a3a' : 'transparent'),
background: currentPath === n.path ? 'var(--control-border)' : (isDragOver ? 'var(--control-border)' : 'transparent'),
padding: '3px 4px', borderRadius: 4, position: 'relative' }}
onMouseEnter={() => setHover(n.path)} onMouseLeave={() => setHover(null)}
onClick={() => isRenaming ? null : (n.type === 'folder' ? toggle(n.path) : openDoc(n.path))}
@@ -115,7 +115,7 @@ export function FileTree() {
{isRenaming ? (
<input autoFocus value={renameVal} onChange={e => setRenameVal(e.target.value)}
onKeyDown={e => { if (e.key === 'Enter') { setRenaming(null); invoke(IPC.fileRename, { from: n.path, toName: renameVal }).then(r => { useTabs.getState().renamePath(n.path, r.path); syncCurrentPath(n.path, r.path); loadTree(); }); } if (e.key === 'Escape') setRenaming(null); }}
onBlur={() => { if (renameVal.trim() && renameVal !== n.name) { setRenaming(null); invoke(IPC.fileRename, { from: n.path, toName: renameVal }).then(r2 => { useTabs.getState().renamePath(n.path, r2.path); syncCurrentPath(n.path, r2.path); loadTree(); }).catch(() => {}); } else setRenaming(null); }} style={{ flex: 1, background: 'var(--bg-secondary)', border: '1px solid #4a6a8a', color: 'var(--text-primary)', borderRadius: 3, padding: '1px 4px', fontSize: 13 }} />
onBlur={() => { if (renameVal.trim() && renameVal !== n.name) { setRenaming(null); invoke(IPC.fileRename, { from: n.path, toName: renameVal }).then(r2 => { useTabs.getState().renamePath(n.path, r2.path); syncCurrentPath(n.path, r2.path); loadTree(); }).catch(() => {}); } else setRenaming(null); }} style={{ flex: 1, background: 'var(--bg-secondary)', border: '1px solid var(--accent)', color: 'var(--text-primary)', borderRadius: 3, padding: '1px 4px', fontSize: 13 }} />
) : (
<span style={{ marginLeft: 4, flex: 1 }}>{n.name}</span>
)}
@@ -101,9 +101,9 @@ function MindmapTree() {
.mm-name { font-weight: 500; }
.mm-root .mm-name { font-weight: 700; color: var(--accent-text); }
.mm-folder .mm-name { font-weight: 600; color: var(--accent-text); }
.mm-doc .mm-name { color: #9aa0aa; font-weight: 400; }
.mm-doc .mm-name { color: var(--text-secondary); font-weight: 400; }
.mm-doc { cursor: pointer; }
.mm-doc:hover .mm-name { color: #fff; text-decoration: underline; }
.mm-doc:hover .mm-name { color: var(--text-primary); text-decoration: underline; }
.mm-folder, .mm-root { cursor: pointer; }
`}</style>
</div>
@@ -111,7 +111,7 @@ function MindmapTree() {
}
function tabBtn(active: boolean): React.CSSProperties {
return { padding: '3px 10px', fontSize: 12, background: active ? 'var(--control-border)' : 'transparent', color: active ? '#fff' : 'var(--text-tertiary)', border: '1px solid var(--control-border)', borderRadius: 4, cursor: 'pointer' };
return { padding: '3px 10px', fontSize: 12, background: active ? 'var(--control-border)' : 'transparent', color: active ? 'var(--text-primary)' : 'var(--text-tertiary)', border: '1px solid var(--control-border)', borderRadius: 4, cursor: 'pointer' };
}
export function GraphView({ onSwitchToEditor }: { onSwitchToEditor?: () => void }) {
@@ -23,7 +23,7 @@ export function TagFilter() {
{!collapsed && (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, maxHeight: 100, overflow: 'auto' }}>
{tags.map(t => (
<span key={t.tag} onClick={() => toggle(t.tag)} style={{ cursor: 'pointer', padding: '1px 7px', borderRadius: 10, fontSize: 11, background: selected.includes(t.tag) ? 'var(--accent)' : 'var(--control-border)', color: selected.includes(t.tag) ? '#fff' : 'var(--text-secondary)' }}>{t.tag} {t.count}</span>
<span key={t.tag} onClick={() => toggle(t.tag)} style={{ cursor: 'pointer', padding: '1px 7px', borderRadius: 10, fontSize: 11, background: selected.includes(t.tag) ? 'var(--accent)' : 'var(--control-border)', color: selected.includes(t.tag) ? 'var(--accent-text)' : 'var(--text-secondary)' }}>{t.tag} {t.count}</span>
))}
</div>
)}
@@ -54,7 +54,7 @@ export function SettingsPanel({ onClose }: { onClose: () => void }) {
</div>
<div style={{ display: 'flex', borderBottom: '1px solid var(--control-border)', marginBottom: 12 }}>
{(['editor','keymap','path','other'] as const).map(p => (
<button key={p} onClick={() => setPage(p)} style={{ ...pageBtn, color: page===p ? '#fff' : 'var(--text-tertiary)', borderBottom: page===p ? '2px solid var(--accent)' : '2px solid transparent' }}>{({editor:'编辑器',keymap:'快捷键',path:'文件路径',other:'其他'} as any)[p]}</button>
<button key={p} onClick={() => setPage(p)} style={{ ...pageBtn, color: page===p ? 'var(--text-primary)' : 'var(--text-tertiary)', borderBottom: page===p ? '2px solid var(--accent)' : '2px solid transparent' }}>{({editor:'编辑器',keymap:'快捷键',path:'文件路径',other:'其他'} as any)[p]}</button>
))}
</div>
{page === 'editor' && <>
@@ -99,7 +99,7 @@ export function SettingsPanel({ onClose }: { onClose: () => void }) {
}
const sel: React.CSSProperties = { background: 'var(--bg-secondary)', border: '1px solid var(--control-border)', color: 'var(--text-primary)', borderRadius: 4, padding: '4px 8px', fontSize: 13 };
function dot(on: boolean): React.CSSProperties { return { position: 'absolute', top: 2, left: on ? 18 : 2, width: 16, height: 16, borderRadius: '50%', background: '#fff', transition: 'left 0.2s' }; }
function dot(on: boolean): React.CSSProperties { return { position: 'absolute', top: 2, left: on ? 18 : 2, width: 16, height: 16, borderRadius: '50%', background: 'var(--text-primary)', transition: 'left 0.2s' }; }
// FR-V5: 默认库路径设置页——查看当前库、浏览选择、立即切换
function PathPage() {
@@ -1,5 +1,4 @@
import { create } from 'zustand';
import { applyTheme } from '../appearance/themes';
export interface Settings {
fontSize: number; // 12-20
@@ -14,8 +14,8 @@ export function TitleBar({ onToggleView, view, onSettings, onAppearance, onToggl
<div style={{ height: 36, display: 'flex', alignItems: 'center', background: 'var(--bg-secondary)', borderBottom: '1px solid var(--control-border)', WebkitAppRegion: 'drag', userSelect: 'none' } as React.CSSProperties}>
<div style={{ padding: '0 12px', color: 'var(--accent-text)', fontWeight: 600, fontSize: 13 }}>狩夜人笔记</div>
<div style={drag} />
<button title={"侧边栏开关"} onClick={onToggleSidebar} style={{ ...btn, fontSize: 14 }}>▤</button>
<button title={"外观定制"} onClick={onAppearance} style={{ ...btn, fontSize: 14 }}>🎨</button>
<button title={"侧边栏开关"} onClick={onToggleSidebar} style={{ ...btn, ...noDrag, fontSize: 14 }}>▤</button>
<button title={"外观定制"} onClick={onAppearance} style={{ ...btn, ...noDrag, fontSize: 14 }}>🎨</button>
<div style={{ display: 'flex', alignItems: 'center', gap: 2, padding: '0 6px', ...noDrag }}>
<button title={"刷新"} onClick={() => loadTree()} style={btn}>↻</button>
<button title={"切换 编辑器/图谱"} onClick={onToggleView} style={btn}>{view === 'editor' ? '🕸' : '✎'}</button>
+10
View File
@@ -38,6 +38,16 @@ html, body, #root { margin: 0; height: 100%; }
.tiptap-wrap .ProseMirror .hljs-deletion { color: #e06c75; }
.tiptap-wrap .ProseMirror .hljs-emphasis { font-style: italic; }
.tiptap-wrap .ProseMirror .hljs-strong { font-weight: 700; }
/* highlight.js 浅色主题——当 --bg-primary 为浅色时生效(latte/daylight/plain) */
:root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs { color: #24292e; }
:root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-comment, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-quote { color: #6a737d; }
:root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-doctag, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-keyword, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-formula, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-name, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-section, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-selector-tag, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-tag { color: #d73a49; }
:root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-addition, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-attribute, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-attr, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-string, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-template-tag, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-template-variable, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-value { color: #032f62; }
:root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-built_in, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-class .hljs-title, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-title, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-title.class_, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-type { color: #6f42c1; }
:root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-function .hljs-title, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-title.function_ { color: #005cc5; }
:root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-literal, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-number, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-operator, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-variable, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-params { color: #005cc5; }
:root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-bullet, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-link, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-meta, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-regexp, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-selector-attr, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-selector-class, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-selector-id, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-symbol, :root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-variable.constant_ { color: #005cc5; }
:root[data-theme-group="light"] .tiptap-wrap .ProseMirror .hljs-deletion { color: #d73a49; }
/* FR-E11 查找高亮 */
.tiptap-wrap .ProseMirror .find-match { background: rgba(255, 213, 79, 0.35); border-radius: 2px; }
.tiptap-wrap .ProseMirror .find-match-active { background: rgba(255, 145, 0, 0.65); }