fix(ui): 4项界面美化——1.图标统一纯色字符(🎨→◐/🕸→◇/📁→▣/📥→↓/📄→◉ 全部移除彩色emoji); 2.Resizer加拖拽手柄+悬停高亮+accent强调色; EditorToolbar/Toolbar按钮去边框+圆角+悬停背景; TitleBar按钮宽度收紧+圆角; 3.自定义滚动条(6px宽+半透明+圆角+hover加深) webkit+firefox双引擎; 4.@media print隐藏滚动条+overflow visible防进度条打印

This commit is contained in:
hunter-dev
2026-08-26 19:10:49 +08:00
parent 8c47d27979
commit 34d3fec449
7 changed files with 32 additions and 14 deletions
@@ -11,10 +11,13 @@ function StarButton() {
return <button onClick={() => toggleStar()} title="星标" style={{ ...btn, color: meta.star ? 'var(--accent)' : 'var(--text-tertiary)', fontSize: 16 }}>{meta.star ? '★' : '☆'}</button>;
}
const btn: React.CSSProperties = { minWidth: 30, height: 28, border: '1px solid var(--control-border)', background: 'transparent', color: 'var(--text-primary)', cursor: 'pointer', borderRadius: 4, fontSize: 13 };
const btn: React.CSSProperties = { minWidth: 28, height: 26, border: 'none', background: 'transparent', color: 'var(--text-secondary)', cursor: 'pointer', borderRadius: 4, fontSize: 13 };
function T({ onClick, title, children, disabled }: { onClick: () => void; title: string; children: React.ReactNode; disabled?: boolean }) {
return <button style={{ ...btn, opacity: disabled ? 0.4 : 1 }} onClick={onClick} title={title} disabled={disabled}>{children}</button>;
return <button style={{ ...btn, opacity: disabled ? 0.35 : 1, padding: '0 6px' }}
onMouseEnter={e => { if (!disabled) e.currentTarget.style.background = 'var(--control-border)'; }}
onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; }}
onClick={onClick} title={title} disabled={disabled}>{children}</button>;
}
export function EditorToolbar() {
@@ -111,7 +111,7 @@ export function FileTree() {
onDragOver={e => { if (n.type === 'folder') { e.preventDefault(); setDragOver(n.path); } }}
onDragLeave={() => setDragOver(null)}
onDrop={e => n.type === 'folder' && onDrop(e, n.path)}>
<span style={{ width: 14 }}>{n.type === 'folder' ? (isCollapsed ? '▶' : '▼') : '📄'}</span>
<span style={{ width: 14 }}>{n.type === 'folder' ? (isCollapsed ? '▶' : '▼') : '◉'}</span>
{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); }}
@@ -72,7 +72,7 @@ export function LinkGraph({ onSwitchToEditor }: { onSwitchToEditor?: () => void
.attr('fill', 'var(--bg-secondary)').attr('stroke', (d) => d.color || 'var(--control-border)').attr('stroke-width', 1.5);
nodeSel.append('rect').attr('x', -CARD_W / 2).attr('y', -CARD_H / 2).attr('width', 4).attr('height', CARD_H).attr('fill', (d) => d.color || '#5db0ff');
// FR-G1:文档图标
nodeSel.append('text').attr('x', -CARD_W / 2 + 12).attr('y', 0).attr('font-size', 12).attr('fill', 'var(--text-tertiary)').text('📄');
nodeSel.append('text').attr('x', -CARD_W / 2 + 12).attr('y', 0).attr('font-size', 12).attr('fill', 'var(--text-tertiary)').text('◉');
// 标题
nodeSel.append('text').attr('x', -CARD_W / 2 + 28).attr('y', -2).attr('fill', 'var(--text-primary)').attr('font-size', 12).attr('font-family', 'sans-serif')
.text((d) => truncate(d.title, 14));
@@ -12,5 +12,11 @@ export function Resizer({ onDrag, side }: { onDrag: (delta: number) => void; sid
window.addEventListener('mousemove', move);
window.addEventListener('mouseup', up);
};
return <div onMouseDown={onDown} style={{ width: 5, cursor: 'col-resize', background: 'transparent', flexShrink: 0 }} onMouseEnter={e => (e.currentTarget.style.background = 'var(--accent)')} onMouseLeave={e => (e.currentTarget.style.background = 'transparent')} />;
return (
<div onMouseDown={onDown} style={{ width: 6, cursor: 'col-resize', background: 'transparent', flexShrink: 0, position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'center' }}
onMouseEnter={e => { e.currentTarget.style.background = 'var(--control-border)'; const h = e.currentTarget.querySelector('div'); if (h) (h as HTMLElement).style.background = 'var(--accent)'; }}
onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; const h = e.currentTarget.querySelector('div'); if (h) (h as HTMLElement).style.background = 'var(--text-tertiary)'; }}>
<div style={{ width: 2, height: 24, borderRadius: 2, background: 'var(--text-tertiary)', opacity: 0.4, transition: 'all 0.15s' }} />
</div>
);
}
@@ -15,10 +15,10 @@ export function TitleBar({ onToggleView, view, onSettings, onAppearance, onToggl
<div style={{ padding: '0 12px', color: 'var(--accent-text)', fontWeight: 600, fontSize: 13 }}>狩夜人笔记</div>
<div style={drag} />
<button title={"侧边栏开关"} onClick={onToggleSidebar} style={{ ...btn, ...noDrag, fontSize: 14 }}>▤</button>
<button title={"外观定制"} onClick={onAppearance} 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>
<button title={"切换 编辑器/图谱"} onClick={onToggleView} style={btn}>{view === 'editor' ? '◇' : '✎'}</button>
<button title={tabbarVisible ? '隐藏标签栏' : '显示标签栏'} onClick={onToggleTabbar} style={{ ...btn, fontSize: 14 }} data-testid="tabbar-toggle">{tabbarVisible ? '☰' : '–'}</button>
<button title={"极简模式"} onClick={onToggleMinimal} style={btn}>{minimal ? '⛶' : '⊡'}</button>
<button title={"设置"} onClick={onSettings} style={btn}>⚙</button>
@@ -31,4 +31,4 @@ export function TitleBar({ onToggleView, view, onSettings, onAppearance, onToggl
);
}
const btn: React.CSSProperties = { width: 36, height: 28, border: 'none', background: 'transparent', color: 'var(--text-secondary)', cursor: 'pointer', fontSize: 12 };
const btn: React.CSSProperties = { width: 34, height: 28, border: 'none', background: 'transparent', color: 'var(--text-secondary)', cursor: 'pointer', fontSize: 12, borderRadius: 4 };
@@ -50,8 +50,8 @@ export function Toolbar() {
return (
<div style={{ padding: '6px 12px', borderBottom: '1px solid var(--control-border)', display: 'flex', gap: 8, background: 'var(--bg-secondary)' }}>
<button onClick={newDoc} style={btn}>+ 新建文档</button>
<button onClick={newFolder} style={btn}>📁 新建文件夹</button>
<button onClick={importFiles} style={btn}>📥 导入</button>
<button onClick={newFolder} style={btn}>▣ 新建文件夹</button>
<button onClick={importFiles} style={btn}>↓ 导入</button>
<button onClick={switchVault} style={btn}>⇄ 切换库</button>
{/* FR-V4:最近库下拉快速切换 */}
{vault?.recents && vault.recents.length > 1 && (
@@ -74,4 +74,4 @@ export function Toolbar() {
</div>
);
}
const btn: React.CSSProperties = { padding: '4px 10px', border: '1px solid var(--control-border)', background: 'transparent', color: 'var(--text-primary)', borderRadius: 4, cursor: 'pointer', fontSize: 12 };
const btn: React.CSSProperties = { padding: '4px 12px', border: 'none', background: 'transparent', color: 'var(--text-secondary)', borderRadius: 4, cursor: 'pointer', fontSize: 12, transition: 'all 0.15s' };
+12 -3
View File
@@ -1,5 +1,12 @@
* { box-sizing: border-box; }
html, body, #root { margin: 0; height: 100%; }
/* 自定义滚动条——细+半透明+圆角 */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--control-border); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--text-tertiary); }
* { scrollbar-width: thin; scrollbar-color: var(--control-border) transparent; }
/* Tiptap 编辑器正文 */
.tiptap-wrap .ProseMirror {
font-size: var(--editor-font-size, 14px) !important;
@@ -87,10 +94,12 @@ html, body, #root { margin: 0; height: 100%; }
/* FR-X2:打印/另存为 PDF 干净排版 + 代码语法高亮 */
@media print {
body * { visibility: hidden; }
::-webkit-scrollbar { display: none !important; }
* { scrollbar-width: none !important; }
.tiptap-wrap, .tiptap-wrap * { visibility: visible; }
.tiptap-wrap { position: absolute; left: 0; top: 0; width: 100%; padding: 20px; }
.tiptap-wrap .ProseMirror { font-size: 14px !important; color: #000 !important; }
.tiptap-wrap .ProseMirror pre { background: #f4f4f4 !important; color: #333 !important; border: 1px solid #ddd; padding: 8px; border-radius: 4px; overflow: visible; }
.tiptap-wrap { position: absolute; left: 0; top: 0; width: 100%; padding: 20px; overflow: visible !important; }
.tiptap-wrap .ProseMirror { font-size: 14px !important; color: #000 !important; overflow: visible !important; }
.tiptap-wrap .ProseMirror pre { background: #f4f4f4 !important; color: #333 !important; border: 1px solid #ddd; padding: 8px; border-radius: 4px; overflow: visible !important; }
.tiptap-wrap .ProseMirror pre code { color: #333; }
.tiptap-wrap .ProseMirror h1, .tiptap-wrap .ProseMirror h2, .tiptap-wrap .ProseMirror h3 { color: #000; }
.tiptap-wrap .ProseMirror blockquote { border-color: #ccc; color: #555; }