diff --git a/apps/desktop/src/renderer/App.tsx b/apps/desktop/src/renderer/App.tsx index 4456374..f2fc53a 100644 --- a/apps/desktop/src/renderer/App.tsx +++ b/apps/desktop/src/renderer/App.tsx @@ -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 ( -
+
{appearance.bgImage &&
}
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() {
{layout.minimal ? () : (<> {layout.sidebarVisible && (<> -
+
@@ -71,8 +71,8 @@ export function App() { {view === 'editor' ? : setView('editor')} />} {view === 'editor' && layout.outlineVisible && (<> layout.setOutlineWidth(layout.outlineWidth + d)} /> -
-
+
+
@@ -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' }; } \ No newline at end of file diff --git a/apps/desktop/src/renderer/features/appearance/appearance-store.ts b/apps/desktop/src/renderer/features/appearance/appearance-store.ts index 70f3de7..1f6b574 100644 --- a/apps/desktop/src/renderer/features/appearance/appearance-store.ts +++ b/apps/desktop/src/renderer/features/appearance/appearance-store.ts @@ -92,7 +92,10 @@ export const useAppearance = create((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 { diff --git a/apps/desktop/src/renderer/features/appearance/themes.ts b/apps/desktop/src/renderer/features/appearance/themes.ts index 32a2c60..0e2d8fa 100644 --- a/apps/desktop/src/renderer/features/appearance/themes.ts +++ b/apps/desktop/src/renderer/features/appearance/themes.ts @@ -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); diff --git a/apps/desktop/src/renderer/features/dialog/InputDialog.tsx b/apps/desktop/src/renderer/features/dialog/InputDialog.tsx index 915ab17..9113e47 100644 --- a/apps/desktop/src/renderer/features/dialog/InputDialog.tsx +++ b/apps/desktop/src/renderer/features/dialog/InputDialog.tsx @@ -28,7 +28,7 @@ export function InputDialog() { )}
{mode !== 'alert' && } - +
diff --git a/apps/desktop/src/renderer/features/editor/ImageView.tsx b/apps/desktop/src/renderer/features/editor/ImageView.tsx index a8634dd..7f5c884 100644 --- a/apps/desktop/src/renderer/features/editor/ImageView.tsx +++ b/apps/desktop/src/renderer/features/editor/ImageView.tsx @@ -12,7 +12,7 @@ export function ImageView({ node, selected }: NodeViewProps) { const src = (node.attrs.src as string) || ''; const display = toDisplaySrc(src); return ( - + {(node.attrs.alt ); diff --git a/apps/desktop/src/renderer/features/file-tree/FileTree.tsx b/apps/desktop/src/renderer/features/file-tree/FileTree.tsx index b811a17..0905540 100644 --- a/apps/desktop/src/renderer/features/file-tree/FileTree.tsx +++ b/apps/desktop/src/renderer/features/file-tree/FileTree.tsx @@ -101,7 +101,7 @@ export function FileTree() { return (
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 ? ( 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 }} /> ) : ( {n.name} )} diff --git a/apps/desktop/src/renderer/features/graph/GraphView.tsx b/apps/desktop/src/renderer/features/graph/GraphView.tsx index 90cd19d..9e78bd4 100644 --- a/apps/desktop/src/renderer/features/graph/GraphView.tsx +++ b/apps/desktop/src/renderer/features/graph/GraphView.tsx @@ -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; } `}
@@ -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 }) { diff --git a/apps/desktop/src/renderer/features/search/TagFilter.tsx b/apps/desktop/src/renderer/features/search/TagFilter.tsx index 610f1ca..cb6590d 100644 --- a/apps/desktop/src/renderer/features/search/TagFilter.tsx +++ b/apps/desktop/src/renderer/features/search/TagFilter.tsx @@ -23,7 +23,7 @@ export function TagFilter() { {!collapsed && (
{tags.map(t => ( - 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} + 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} ))}
)} diff --git a/apps/desktop/src/renderer/features/settings/SettingsPanel.tsx b/apps/desktop/src/renderer/features/settings/SettingsPanel.tsx index 72b3d8b..a2a6a78 100644 --- a/apps/desktop/src/renderer/features/settings/SettingsPanel.tsx +++ b/apps/desktop/src/renderer/features/settings/SettingsPanel.tsx @@ -54,7 +54,7 @@ export function SettingsPanel({ onClose }: { onClose: () => void }) {
{(['editor','keymap','path','other'] as const).map(p => ( - + ))}
{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() { diff --git a/apps/desktop/src/renderer/features/settings/settings-store.ts b/apps/desktop/src/renderer/features/settings/settings-store.ts index 7ae9b74..76f7c9e 100644 --- a/apps/desktop/src/renderer/features/settings/settings-store.ts +++ b/apps/desktop/src/renderer/features/settings/settings-store.ts @@ -1,5 +1,4 @@ import { create } from 'zustand'; -import { applyTheme } from '../appearance/themes'; export interface Settings { fontSize: number; // 12-20 diff --git a/apps/desktop/src/renderer/features/shell/TitleBar.tsx b/apps/desktop/src/renderer/features/shell/TitleBar.tsx index a37ee2e..c752bb7 100644 --- a/apps/desktop/src/renderer/features/shell/TitleBar.tsx +++ b/apps/desktop/src/renderer/features/shell/TitleBar.tsx @@ -14,8 +14,8 @@ export function TitleBar({ onToggleView, view, onSettings, onAppearance, onToggl
狩夜人笔记
- - + +
diff --git a/apps/desktop/src/renderer/styles.css b/apps/desktop/src/renderer/styles.css index 87b9a73..4fdbe76 100644 --- a/apps/desktop/src/renderer/styles.css +++ b/apps/desktop/src/renderer/styles.css @@ -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); }