/* ============================================ Todo List - 样式表 Linear 暗色风格 · 紧凑排版 · 精致细节 ============================================ */ /* ============================================ 设计 Token ============================================ */ :root { /* 间距(4 的倍数) */ --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 20px; --space-6: 24px; --space-7: 32px; --space-8: 40px; /* 圆角 */ --radius-xs: 4px; --radius-sm: 6px; --radius-md: 8px; --radius-lg: 10px; --radius-xl: 14px; --radius-pill: 999px; /* 行高 */ --lh-tight: 1.35; --lh-base: 1.55; --lh-relaxed: 1.7; /* 字号层级 — 默认(中)。 在原版基础上整体 +0.5px —— 用户反馈「文字看着累」需要稍稍宽松,但不应过头: 底部栏 / hint / 描述等次要文本仍走 xs (12px),保持紧凑而非臃肿。 */ --font-2xs: 10.5px; --font-xs: 12px; --font-sm: 13px; --font-md-sm: 14px; --font-base: 14.5px; --font-md: 15.5px; --font-lg: 17.5px; --font-xl: 20.5px; --font-2xl: 24.5px; /* 高度(控件) */ --h-control: 32px; --h-control-sm: 28px; --h-toolbar: 52px; --h-statusbar: 30px; /* 缓动:out 用了更柔的曲线,spring 略快一点避免拖泥带水 */ --ease-out: cubic-bezier(0.16, 1, 0.3, 1); --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); --ease-soft: cubic-bezier(0.4, 0, 0.2, 1); /* 焦点环:offset 1px 的环 + 3px 的光晕,比单层更现代 */ --focus-ring: 0 0 0 1px var(--color-accent), 0 0 0 4px var(--color-accent-bg); --focus-ring-light: 0 0 0 1px var(--color-bg), 0 0 0 4px var(--color-accent); /* 层级 */ --z-modal: 3000; --z-toast: 2000; --z-menu: 1000; } /* ============================================ 浅色主题 - Linear / Things 风格清爽层次 - 纯白卡面 + 微微灰背景,强调层级而非装饰 - 强调色:蓝紫调 ============================================ */ :root, [data-theme="light"] { --color-bg: #f7f7f9; --color-bg-secondary: #ffffff; --color-bg-tertiary: #eeeef2; --color-bg-elevated: #ffffff; --color-bg-overlay: rgba(247, 247, 249, 0.78); --color-chip-bg: #eeeef2; /* chip 控件背景 */ --color-border: #e2e2e8; --color-border-muted: #ececf0; --color-border-strong: rgba(15, 23, 42, 0.12); --color-text: #18181c; --color-text-secondary: #54545d; --color-text-muted: #71717a; /* 略加深,保证白底上的「次要文字」仍清晰 */ --color-text-tertiary: #65656d; /* 介于 secondary 与 muted 之间 */ --color-text-faint: #94949a; --color-accent: #4f46e5; /* Indigo-600 */ --color-accent-hover: #4338ca; --color-accent-bg: rgba(79, 70, 229, 0.08); --color-accent-bg-strong: rgba(79, 70, 229, 0.12); --color-accent-gradient: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%); --color-accent-glow: rgba(79, 70, 229, 0.35); --color-success: #059669; --color-success-bg: rgba(5, 150, 105, 0.10); --color-danger: #e11d48; --color-danger-bg: rgba(225, 29, 72, 0.10); --color-warning: #d97706; --color-warning-bg: rgba(217, 119, 6, 0.10); --color-star: #d97706; /* 重要任务 - 琥珀色(与当前任务的暖黄区分) */ --color-shadow: rgba(15, 23, 42, 0.05); --color-shadow-strong: rgba(15, 23, 42, 0.12); --color-ambient-1: rgba(124, 58, 237, 0.05); --color-ambient-2: rgba(79, 70, 229, 0.04); --color-current: #3b82f6; /* 「当前任务」语义强调色;per-style 可覆盖 */ --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.05), 0 1px 1px rgba(15, 23, 42, 0.03); --shadow-md: 0 1px 2px rgba(15, 23, 42, 0.04), 0 4px 16px rgba(15, 23, 42, 0.06); --shadow-lg: 0 4px 16px rgba(15, 23, 42, 0.06), 0 24px 48px -8px rgba(15, 23, 42, 0.18); --shadow-accent: 0 4px 14px rgba(79, 70, 229, 0.22), 0 1px 2px rgba(79, 70, 229, 0.12); --font-stack: -apple-system, BlinkMacSystemFont, "Segoe UI Variable Display", "Segoe UI", "Inter", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", sans-serif; --font-mono: "Cascadia Code", "JetBrains Mono", "SF Mono", ui-monospace, Menlo, Consolas, "Liberation Mono", monospace; --scrollbar-thumb: rgba(15, 23, 42, 0.16); --scrollbar-thumb-hover: rgba(15, 23, 42, 0.28); } /* ============================================ 深色主题 - Linear 风格中性暗色 - 真正的中性灰(无紫调),强调色沿用 indigo - 加了微妙的蓝色 ambient 让背景「活」起来 ============================================ */ [data-theme="dark"] { --color-bg: #0a0a0c; /* 主背景 - 近纯黑,无色相 */ --color-bg-secondary: #131316; /* 工具栏 / 状态栏 */ --color-bg-tertiary: #1d1d21; /* 任务项 hover / chip 背景 */ --color-bg-elevated: #17171a; /* 侧边栏 */ --color-bg-overlay: rgba(10, 10, 12, 0.7); --color-chip-bg: #1d1d21; /* chip 控件(filter/sort)背景 */ --color-border: rgba(255, 255, 255, 0.07); --color-border-muted: rgba(255, 255, 255, 0.04); --color-border-strong: rgba(255, 255, 255, 0.13); --color-text: #ededee; --color-text-secondary: #c1c3c8; /* 略提亮 —— 配合 completed 任务等场景用 */ --color-text-muted: #96999f; /* 略提亮,保证「次要文字」依然清晰可读 */ --color-text-tertiary: #82858c; /* 介于 secondary 与 muted 之间 */ --color-text-faint: #7d7f84; --color-accent: #8189ff; /* Indigo-400(Linear 风) */ --color-accent-hover: #9aa3ff; --color-accent-bg: rgba(129, 137, 255, 0.14); --color-accent-bg-strong: rgba(129, 137, 255, 0.22); --color-accent-gradient: linear-gradient(135deg, #8189ff 0%, #a594ff 100%); --color-accent-glow: rgba(129, 137, 255, 0.4); --color-success: #4ec9b0; --color-success-bg: rgba(78, 201, 176, 0.12); --color-danger: #f87171; --color-danger-bg: rgba(248, 113, 113, 0.12); --color-warning: #eab308; --color-warning-bg: rgba(234, 179, 8, 0.12); --color-star: #f59e0b; /* 重要任务 - 琥珀色(与当前任务的金黄区分) */ --color-shadow: rgba(0, 0, 0, 0.4); --color-shadow-strong: rgba(0, 0, 0, 0.6); --color-ambient-1: rgba(99, 102, 241, 0.08); --color-ambient-2: rgba(168, 85, 247, 0.05); --color-current: #60a5fa; /* 「当前任务」语义强调色;per-style 可覆盖 */ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4); --shadow-md: 0 1px 2px rgba(0, 0, 0, 0.3), 0 4px 16px rgba(0, 0, 0, 0.4); --shadow-lg: 0 1px 3px rgba(0, 0, 0, 0.4), 0 24px 48px -8px rgba(0, 0, 0, 0.6); --shadow-accent: 0 6px 20px rgba(129, 137, 255, 0.32), 0 1px 2px rgba(129, 137, 255, 0.18); --font-stack: -apple-system, BlinkMacSystemFont, "Segoe UI Variable Display", "Segoe UI", "Inter", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", sans-serif; --font-mono: "Cascadia Code", "JetBrains Mono", "SF Mono", ui-monospace, Menlo, Consolas, "Liberation Mono", monospace; --scrollbar-thumb: rgba(255, 255, 255, 0.10); --scrollbar-thumb-hover: rgba(255, 255, 255, 0.20); } /* ============================================ 配色风格 (colorStyle) — orthogonal to light/dark theme 复合选择器特异性 (0,0,2,0) > 单 [data-theme] 的 (0,0,1,0), 因此每个风格块可以稳定覆盖基块的 accent / ambient / shadow-accent / current 共 10 个 token。 bg / border / text / star / warning / success / danger / scrollbar 全部由 mode 控制,不重复。 indigo 是基块的默认配色,不需要再列一份(删除它会让"indigo"等于"未指定",反而失去显式语义)。 ============================================ */ /* ---------- ocean (sky / cyan) — 冷静专注 ---------- */ [data-theme="light"][data-color-style="ocean"] { --color-accent: #0284c7; --color-accent-hover: #0369a1; --color-accent-bg: rgba(2, 132, 199, 0.08); --color-accent-bg-strong: rgba(2, 132, 199, 0.12); --color-accent-gradient: linear-gradient(135deg, #0284c7 0%, #0ea5e9 100%); --color-accent-glow: rgba(2, 132, 199, 0.35); --color-ambient-1: rgba(8, 145, 178, 0.05); --color-ambient-2: rgba(2, 132, 199, 0.04); --shadow-accent: 0 4px 14px rgba(2, 132, 199, 0.22), 0 1px 2px rgba(2, 132, 199, 0.12); --color-current: #06b6d4; } [data-theme="dark"][data-color-style="ocean"] { --color-accent: #38bdf8; --color-accent-hover: #7dd3fc; --color-accent-bg: rgba(56, 189, 248, 0.14); --color-accent-bg-strong: rgba(56, 189, 248, 0.22); --color-accent-gradient: linear-gradient(135deg, #38bdf8 0%, #22d3ee 100%); --color-accent-glow: rgba(56, 189, 248, 0.4); --color-ambient-1: rgba(56, 189, 248, 0.08); --color-ambient-2: rgba(34, 211, 238, 0.05); --shadow-accent: 0 6px 20px rgba(56, 189, 248, 0.32), 0 1px 2px rgba(56, 189, 248, 0.18); --color-current: #22d3ee; } /* ---------- forest (emerald / teal) — 自然放松 ---------- */ [data-theme="light"][data-color-style="forest"] { --color-accent: #059669; --color-accent-hover: #047857; --color-accent-bg: rgba(5, 150, 105, 0.08); --color-accent-bg-strong: rgba(5, 150, 105, 0.12); --color-accent-gradient: linear-gradient(135deg, #059669 0%, #10b981 100%); --color-accent-glow: rgba(5, 150, 105, 0.35); --color-ambient-1: rgba(16, 185, 129, 0.05); --color-ambient-2: rgba(5, 150, 105, 0.04); --shadow-accent: 0 4px 14px rgba(5, 150, 105, 0.22), 0 1px 2px rgba(5, 150, 105, 0.12); --color-current: #0d9488; /* teal 拉开与 emerald --color-success 的色距 */ } [data-theme="dark"][data-color-style="forest"] { --color-accent: #34d399; --color-accent-hover: #6ee7b7; --color-accent-bg: rgba(52, 211, 153, 0.14); --color-accent-bg-strong: rgba(52, 211, 153, 0.22); --color-accent-gradient: linear-gradient(135deg, #34d399 0%, #10b981 100%); --color-accent-glow: rgba(52, 211, 153, 0.4); --color-ambient-1: rgba(52, 211, 153, 0.08); --color-ambient-2: rgba(16, 185, 129, 0.05); --shadow-accent: 0 6px 20px rgba(52, 211, 153, 0.32), 0 1px 2px rgba(52, 211, 153, 0.18); --color-current: #2dd4bf; } /* ---------- sunset (orange / amber + yellow current) — 暖能 ---------- */ [data-theme="light"][data-color-style="sunset"] { --color-accent: #ea580c; --color-accent-hover: #c2410c; --color-accent-bg: rgba(234, 88, 12, 0.08); --color-accent-bg-strong: rgba(234, 88, 12, 0.12); --color-accent-gradient: linear-gradient(135deg, #ea580c 0%, #f59e0b 100%); --color-accent-glow: rgba(234, 88, 12, 0.35); --color-ambient-1: rgba(245, 158, 11, 0.05); --color-ambient-2: rgba(234, 88, 12, 0.04); --shadow-accent: 0 4px 14px rgba(234, 88, 12, 0.22), 0 1px 2px rgba(234, 88, 12, 0.12); --color-current: #facc15; /* yellow 拉开与 amber --color-star 的色距 */ } [data-theme="dark"][data-color-style="sunset"] { --color-accent: #fb923c; --color-accent-hover: #fdba74; --color-accent-bg: rgba(251, 146, 60, 0.14); --color-accent-bg-strong: rgba(251, 146, 60, 0.22); --color-accent-gradient: linear-gradient(135deg, #fb923c 0%, #fbbf24 100%); --color-accent-glow: rgba(251, 146, 60, 0.4); --color-ambient-1: rgba(251, 146, 60, 0.08); --color-ambient-2: rgba(251, 191, 36, 0.05); --shadow-accent: 0 6px 20px rgba(251, 146, 60, 0.32), 0 1px 2px rgba(251, 146, 60, 0.18); --color-current: #fde047; } /* ---------- rose (玫红 / pink) — 柔和舒适 ---------- */ [data-theme="light"][data-color-style="rose"] { --color-accent: #e11d48; --color-accent-hover: #be123c; --color-accent-bg: rgba(225, 29, 72, 0.08); --color-accent-bg-strong: rgba(225, 29, 72, 0.12); --color-accent-gradient: linear-gradient(135deg, #e11d48 0%, #f472b6 100%); --color-accent-glow: rgba(225, 29, 72, 0.35); --color-ambient-1: rgba(244, 114, 182, 0.05); --color-ambient-2: rgba(225, 29, 72, 0.04); --shadow-accent: 0 4px 14px rgba(225, 29, 72, 0.22), 0 1px 2px rgba(225, 29, 72, 0.12); --color-current: #f472b6; } [data-theme="dark"][data-color-style="rose"] { --color-accent: #fb7185; --color-accent-hover: #fda4af; --color-accent-bg: rgba(251, 113, 133, 0.14); --color-accent-bg-strong: rgba(251, 113, 133, 0.22); --color-accent-gradient: linear-gradient(135deg, #fb7185 0%, #f9a8d4 100%); --color-accent-glow: rgba(251, 113, 133, 0.4); --color-ambient-1: rgba(251, 113, 133, 0.08); --color-ambient-2: rgba(244, 114, 182, 0.05); --shadow-accent: 0 6px 20px rgba(251, 113, 133, 0.32), 0 1px 2px rgba(251, 113, 133, 0.18); --color-current: #f9a8d4; } /* ---------- mono (slate 灰,无色相) — 极简克制 ---------- */ [data-theme="light"][data-color-style="mono"] { --color-accent: #475569; --color-accent-hover: #334155; --color-accent-bg: rgba(71, 85, 105, 0.08); --color-accent-bg-strong: rgba(71, 85, 105, 0.14); --color-accent-gradient: linear-gradient(135deg, #475569 0%, #64748b 100%); --color-accent-glow: rgba(71, 85, 105, 0.35); --color-ambient-1: rgba(100, 116, 139, 0.05); --color-ambient-2: rgba(71, 85, 105, 0.04); --shadow-accent: 0 4px 14px rgba(71, 85, 105, 0.22), 0 1px 2px rgba(71, 85, 105, 0.12); --color-current: #3b82f6; /* 「当前」仍用蓝,确保在 mono 中可识别 */ } [data-theme="dark"][data-color-style="mono"] { --color-accent: #94a3b8; --color-accent-hover: #cbd5e1; --color-accent-bg: rgba(148, 163, 184, 0.14); --color-accent-bg-strong: rgba(148, 163, 184, 0.22); --color-accent-gradient: linear-gradient(135deg, #94a3b8 0%, #cbd5e1 100%); --color-accent-glow: rgba(148, 163, 184, 0.4); --color-ambient-1: rgba(148, 163, 184, 0.08); --color-ambient-2: rgba(203, 213, 225, 0.05); --shadow-accent: 0 6px 20px rgba(148, 163, 184, 0.32), 0 1px 2px rgba(148, 163, 184, 0.18); --color-current: #60a5fa; } /* ============================================ 基础重置 ============================================ */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } html, body { height: 100%; overflow: hidden; } body { font-family: var(--font-stack); font-size: var(--font-base); line-height: 1.55; color: var(--color-text); background: var(--color-bg); font-feature-settings: "cv11", "ss01", "ss03"; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; user-select: none; scroll-behavior: smooth; /* 环境光:两层柔和的 radial-gradient 给静态背景一点「呼吸感」, 弱到不抢视觉但能让窗口不再像一块灰玻璃 */ background-image: radial-gradient(ellipse 90% 60% at 0% 0%, var(--color-ambient-1), transparent 60%), radial-gradient(ellipse 70% 50% at 100% 100%, var(--color-ambient-2), transparent 65%); background-attachment: fixed; } button { font-family: inherit; font-size: inherit; cursor: pointer; border: none; background: none; color: inherit; letter-spacing: inherit; } input, textarea { font-family: inherit; font-size: inherit; color: inherit; background: none; border: none; outline: none; letter-spacing: inherit; } input::placeholder, textarea::placeholder { color: var(--color-text-muted); } /* 选区颜色 原来复用 --color-accent-bg-strong(light 12% / dark 22% 透明度),太淡: 编辑任务时看不出「光标停在末尾」和「整行已被全选」的区别,一键入就覆盖原文。 这里单独给选区一档更高的不透明度,文字仍用 --color-text 保证两套主题都可读。 */ ::selection { background: color-mix(in srgb, var(--color-accent) 32%, transparent); color: var(--color-text); } /* 深色主题的强调色是浅色系(如 #8189ff),同样透明度看起来更弱,再加一档 */ [data-theme="dark"] ::selection { background: color-mix(in srgb, var(--color-accent) 40%, transparent); } /* 可编辑区域的选区再强一档:误覆盖的代价最高的就是这里 (任务编辑框、新建任务框、重命名分类的对话框——后者一聚焦就是全选状态) */ input::selection, textarea::selection, [contenteditable]::selection { background: color-mix(in srgb, var(--color-accent) 45%, transparent); color: var(--color-text); } [data-theme="dark"] input::selection, [data-theme="dark"] textarea::selection, [data-theme="dark"] [contenteditable]::selection { background: color-mix(in srgb, var(--color-accent) 55%, transparent); } /* 通用焦点环(键盘) */ button:focus-visible, input:focus-visible, textarea:focus-visible, [tabindex]:focus-visible { outline: none; box-shadow: var(--focus-ring-light); border-radius: var(--radius-sm); transition: box-shadow 0.15s var(--ease-soft); } /* ============================================ 应用容器 ============================================ */ #app { display: grid; grid-template-rows: var(--h-toolbar) 1fr var(--h-statusbar); height: 100vh; width: 100vw; background: transparent; /* 让 body 的环境光透出来 */ position: relative; } /* 主区顶部的 1px 高光线 —— 现代 app 的小细节,让「上沿」更精神 */ #app::before { content: ""; position: absolute; top: var(--h-toolbar); left: 0; right: 0; height: 1px; background: linear-gradient( to right, transparent 0%, var(--color-border-strong) 20%, var(--color-border-strong) 80%, transparent 100% ); pointer-events: none; z-index: 50; } /* ============================================ 工具栏 ============================================ */ .toolbar { display: flex; align-items: center; gap: var(--space-3); padding: 0 16px; height: var(--h-toolbar); background: var(--color-bg-overlay); /* 玻璃效果:让 body 的 ambient radial 微微透过工具栏 */ backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%); border-bottom: 1px solid var(--color-border); -webkit-app-region: drag; min-width: 0; position: relative; z-index: 10; } /* 工具栏底部的高光线:模拟「上方光源」造成的细高光 */ .toolbar::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px; background: linear-gradient( to right, transparent, rgba(255, 255, 255, 0.06) 50%, transparent ); pointer-events: none; } [data-theme="light"] .toolbar::after { background: linear-gradient( to right, transparent, rgba(255, 255, 255, 0.6) 50%, transparent ); } .toolbar-left { display: flex; align-items: center; gap: var(--space-3); min-width: 0; flex-shrink: 1; } /* 中间搜索栏:撑满剩余空间 */ .toolbar-center { flex: 1; min-width: 0; display: flex; align-items: center; -webkit-app-region: no-drag; /* 搜索框外侧左右各让 16px:toolbar gap=12 + 这里 16 = 28px, 把搜索区与「Todo List」标题和右侧按钮都推开。 只在 .toolbar-center 上加 margin 而不是改 toolbar 的 gap, 是为了不破坏标题与窗口控件本身的紧凑节奏 */ margin: 0 16px; } /* 右侧按钮组:固定宽度,紧贴右边缘 */ .toolbar-right { display: flex; align-items: center; gap: var(--space-1); flex-shrink: 0; -webkit-app-region: no-drag; } /* 自定义标题栏接管 OS 标题栏后,标题是窗口唯一的"标题"识别,需要更醒目: 字号用 --font-lg(17.5px,default 下),与 24px logo 形成约 1:1.4 的视觉比例 —— 不至于让 logo 抢走整块权重;font-weight 700 + 微负字距减少「Todo List」粘连感。 line-height 锁紧 1 —— toolbar 52px 高,标题行高 1.55 会让 baseline 偏低、 视觉重心往下沉;锁 1 让 title-text 与 logo 顶部/底部都贴齐。 */ .app-title { display: flex; align-items: center; gap: 10px; font-size: var(--font-lg); font-weight: 700; line-height: 1; color: var(--color-text); letter-spacing: -0.018em; flex-shrink: 0; /* 整块作为 drag 区域内的「元素」,不要再继承按钮的 cursor: pointer, 否则用户把鼠标移到标题上会看到手指光标,误以为可点 */ cursor: default; } .title-text { color: var(--color-text); font-feature-settings: "cv11", "ss01"; /* 文字与 logo 视觉中心对齐:logo 是块级 img + border-radius, 文字 baseline 比 logo 视觉中心略低,translateY 1px 把视觉中心拉齐 */ transform: translateY(1px); } .logo { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 7px; flex-shrink: 0; transition: transform 0.2s var(--ease-spring), box-shadow 0.15s; } .logo img { display: block; width: 100%; height: 100%; object-fit: contain; border-radius: inherit; } /* ============================================ 自定义窗口控制按钮(frame: false 后接管系统标题栏) ============================================ 整组按钮放在工具栏最右侧 —— 工具栏本身是 drag 区, 子节点 window-controls 设 no-drag 让按钮可点。 尺寸统一 32px(=--h-control),与左侧设置/主题按钮同高,整体视觉节奏一致; 但按下/悬浮反馈是窗口控件语义(关闭按钮走危险色),与普通 icon 按钮区分。 ============================================ */ .window-controls { display: inline-flex; align-items: center; gap: 0; margin-left: var(--space-2); padding-left: var(--space-2); /* 与左侧设置按钮做一道极细分隔 —— 颜色取 --color-border 但 0.6 透明度, 比硬 1px 更柔和,不抢走窗口控件组的视觉重心 */ border-left: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent); -webkit-app-region: no-drag; flex-shrink: 0; } .btn-window-control { width: 34px; height: 34px; padding: 0; display: inline-flex; align-items: center; justify-content: center; background: transparent; border: 1px solid transparent; color: var(--color-text-secondary); border-radius: var(--radius-sm); /* 过渡稍放慢一点(150ms)—— 悬浮色块和颜色切换更平滑,避免硬切感 */ transition: background 0.15s var(--ease-soft), color 0.15s var(--ease-soft); -webkit-app-region: no-drag; cursor: pointer; } /* 让 SVG 图标默认色随 currentColor 走,避免 hover 时颜色切换有延迟 */ .btn-window-control svg { display: block; } /* 关闭按钮:悬浮用危险色 —— 提示用户这是不可逆操作(隐藏到托盘) */ .btn-window-control:hover { background: var(--color-bg-tertiary); color: var(--color-text); } .btn-window-control.btn-close:hover { /* 用更深一档的 danger-bg 作为 hover 背景,色块更显眼 */ background: var(--color-danger); color: #fff; } /* 关闭按钮按下:用更深的色调(color-mix 调暗 12%)—— 让点击瞬间有"按下了"的反馈 */ .btn-window-control.btn-close:active { background: color-mix(in srgb, var(--color-danger) 88%, #000); } /* 其余两个按钮按下:背景色比 hover 再深一档(color-mix 朝黑偏移 25%) —— 比 hover 色块更深,但比纯黑亮,确保暗色主题下也看得见"按下"的反馈 */ .btn-window-control:not(.btn-close):active { background: color-mix(in srgb, var(--color-bg-tertiary) 75%, #000); } .btn-window-control:active { /* 取消 .btn 全局 0.96 缩放 —— 窗口按钮点击应当即时生效, 否则快速连点 X 会出现明显延迟感 */ transform: none; } /* 最大化/还原图标:data-state 控制显示哪一个。两种图标都画在 DOM 里, 通过 display 切换比 innerHTML 重写省一次回流。 */ .btn-window-control[data-state="maximize"] .icon-restore { display: none; } .btn-window-control[data-state="restore"] .icon-maximize { display: none; } /* light theme 下按钮默认色比暗色主题更弱,hover 用稍深的色块 */ [data-theme="light"] .btn-window-control { color: var(--color-text-secondary); } [data-theme="light"] .btn-window-control:hover { background: var(--color-bg-tertiary); color: var(--color-text); } [data-theme="light"] .btn-window-control:not(.btn-close):active { background: color-mix(in srgb, var(--color-bg-tertiary) 75%, #000); } .app-title:hover .logo { transform: scale(1.05); } /* 文件信息条:path chip + 跳转文件管理器按钮(现位于底部状态栏) */ .file-path-chip { display: inline-flex; align-items: center; /* gap:0:让圆点、hint、路径文字三者紧贴,节奏紧凑;间距由 ::before margin 控制 */ gap: 0; /* 不设 max-width:状态栏剩下的空间([chip][spacer flex:1][right])让 chip 自然吃满, 路径短就小、路径长就拉到状态栏宽度减右侧文字宽再减 padding/gap。 真正放不下时由 .file-path-text 内部 ellipsis 处理 —— JS 不再 shorten,title 始终是全路径。 min-width:0 是 ellipsis 起作用的必要前提(flex item 默认 min-content,会顶死父容器)。 */ min-width: 0; flex: 0 1 auto; height: 24px; padding: 0 5px 0 7px; background: transparent; border: 1px solid transparent; border-radius: var(--radius-sm); /* 与状态栏整体走 font-xs —— 文件路径是辅助信息,配合 30px 高的状态栏 */ font-size: var(--font-xs); color: var(--color-text-secondary); font-family: var(--font-mono); transition: background 0.15s var(--ease-soft), border-color 0.15s var(--ease-soft); -webkit-app-region: no-drag; letter-spacing: -0.003em; position: relative; } /* 路径前的标签「数据路径:」,跟 chip 一起组成完整语义。 放在 chip 内、紧贴路径文字,让「标签 + 路径」读起来是一个整体, 避免留白过多。文案在 HTML 里写死(≤620px 极窄窗口隐藏整个 chip)。 */ .file-path-hint { flex-shrink: 0; /* 与 file-path-chip 保持一致 —— 「数据路径:」标签也是辅助信息 */ font-size: var(--font-xs); color: var(--color-text-secondary); white-space: nowrap; user-select: none; pointer-events: none; } .file-path-chip::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--color-success); /* 圆点和 hint 标签之间留 5px,让状态点和标签读起来不黏在一起 */ margin-right: 5px; flex-shrink: 0; transition: all 0.2s; } .file-path-chip:hover { background: var(--color-bg-tertiary); border-color: var(--color-border); } .file-path-chip.dirty { border-color: var(--color-warning); background: var(--color-warning-bg); } .file-path-chip.dirty::before { background: var(--color-warning); box-shadow: 0 0 6px var(--color-warning); animation: pulse-dot 1.5s ease-in-out infinite; } .file-path-chip.dirty:hover { background: var(--color-warning-bg); border-color: var(--color-warning); filter: brightness(1.05); } @keyframes pulse-dot { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.6; transform: scale(0.85); } } .file-path-chip .file-path-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; user-select: text; min-width: 0; flex: 1; font-weight: 500; /* 不挂 mask:与侧栏分类名不同,这里 .file-path-text 通过 flex:1 撑满 chip 剩余空间, 文字宽度 = 元素宽度 = 延伸到右边缘。如果照搬侧栏的右侧 12px 渐隐 mask,会把 "未截断时的最后 12px 真实文字" 也淡掉,用户会感觉路径"差一点点没显示全"。 截断时由 text-overflow:ellipsis 自己提供 … 提示,硬切一点但语义清晰。 */ } .file-path-chip.unsaved { background: transparent; border-color: var(--color-border); border-style: dashed; color: var(--color-text-muted); } .file-path-chip.unsaved::before { background: transparent; border: 1px solid var(--color-text-faint); width: 5px; height: 5px; margin-right: 4px; } .file-path-chip.unsaved:hover { background: var(--color-bg-tertiary); border-style: solid; } .file-path-chip.unsaved .file-path-text { color: var(--color-text-muted); font-style: italic; } /* 搜索框:与工具栏其它 icon 按钮同为 32px 高,整体视觉节奏一致 */ .search-wrapper { display: flex; align-items: center; gap: var(--space-2); background: var(--color-bg-tertiary); border: 1px solid transparent; border-radius: var(--radius-md); /* 左 13px / 右 20px:左侧放大镜是搜索的视觉锚点,离边缘越近 越像「就地放大」的提示,太多留白反而让它显得「丢失」; 右侧快捷键标签 / ✕ 按钮需要明确的呼吸位,保留 20px */ padding: 0 20px 0 13px; width: 100%; height: var(--h-control); transition: background 0.18s var(--ease-soft), border-color 0.18s var(--ease-soft), box-shadow 0.2s var(--ease-soft); -webkit-app-region: no-drag; position: relative; } .search-wrapper:hover { background: var(--color-bg-elevated); border-color: var(--color-border); /* 悬浮时给一点微上浮感 */ transform: translateY(-0.5px); } .search-wrapper:focus-within { background: var(--color-bg-elevated); border-color: var(--color-accent); box-shadow: 0 0 0 4px var(--color-accent-bg); } .search-wrapper:focus-within .search-shortcut { opacity: 0; width: 0; padding: 0; margin: 0; border-color: transparent; } .search-icon { color: var(--color-text-secondary); display: inline-flex; align-items: center; flex-shrink: 0; transition: color 0.15s; } .search-wrapper:focus-within .search-icon { color: var(--color-accent); } .search-icon svg { width: 14px; height: 14px; } .search-input { flex: 1; height: 100%; font-size: var(--font-base); min-width: 0; font-weight: 400; letter-spacing: -0.005em; } .search-shortcut { font-family: var(--font-mono); font-size: var(--font-xs); color: var(--color-text-secondary); padding: 2px 6px; background: var(--color-bg); border: 1px solid var(--color-border); border-radius: 4px; flex-shrink: 0; transition: opacity 0.15s, width 0.15s, padding 0.15s, margin 0.15s; overflow: hidden; line-height: 1.2; white-space: nowrap; font-weight: 500; } /* 按钮内的快捷键标签已迁移到 .btn-kbd —— 旧 .btn-shortcut 不再使用(cleaned: 26-08) */ .search-clear { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-sm); background: transparent; color: var(--color-text-muted); line-height: 1; transition: background 0.15s, color 0.15s; flex-shrink: 0; } /* 修正:display: flex 会覆盖原生 [hidden] 的 display: none —— 这里显式回归 */ .search-clear[hidden] { display: none; } /* 提交按钮的 hidden 处理(task-list.js 在输入为空时设为 hidden) */ .task-submit-btn[hidden] { display: none; } .search-clear:hover { background: var(--color-border-strong); color: var(--color-text); } /* 按钮(通用:紧凑、低对比、悬浮态柔和) */ .btn { display: inline-flex; align-items: center; gap: 7px; height: var(--h-control); padding: 0 11px; border-radius: var(--radius-md); font-size: var(--font-sm); font-weight: 500; color: var(--color-text-secondary); background: transparent; border: 1px solid transparent; transition: background 0.15s var(--ease-soft), color 0.15s var(--ease-soft), border-color 0.15s var(--ease-soft), box-shadow 0.2s var(--ease-soft), transform 0.08s var(--ease-soft); -webkit-app-region: no-drag; letter-spacing: -0.005em; white-space: nowrap; position: relative; } .btn:active { transform: scale(0.96); } .btn:hover { background: var(--color-bg-tertiary); color: var(--color-text); } /* 主操作按钮:沉稳、扁平 */ .btn-primary { background: var(--color-accent); color: #ffffff; border-color: transparent; font-weight: 500; font-size: var(--font-sm); box-shadow: var(--shadow-accent); transition: background 0.15s var(--ease-soft), box-shadow 0.2s var(--ease-soft), transform 0.08s var(--ease-soft); letter-spacing: -0.003em; } .btn-primary:hover { background: var(--color-accent-hover); color: #ffffff; box-shadow: 0 0 0 4px var(--color-accent-bg), var(--shadow-accent); transform: translateY(-1px); } /* 主操作按钮的 danger 变体(用于 confirmDialog 的删除确认按钮) */ .btn-primary.btn-danger { background: var(--color-danger); border-color: var(--color-danger); box-shadow: 0 1px 2px rgba(225, 29, 72, 0.3); } .btn-primary.btn-danger:hover { background: color-mix(in srgb, var(--color-danger) 88%, #ffffff); filter: brightness(1.05); box-shadow: 0 0 0 3px var(--color-danger-bg), 0 1px 2px rgba(225, 29, 72, 0.4); } /* 链接样的危险按钮(settings-dialog 的"恢复全部默认") */ .btn-link-danger { background: transparent; border: none; color: var(--color-danger); font-size: var(--font-sm); font-weight: 500; cursor: pointer; padding: 5px 12px; border-radius: var(--radius-md); margin-right: auto; transition: background 0.15s, color 0.15s; } .btn-link-danger:hover { background: var(--color-danger-bg); color: var(--color-danger); } .btn-icon { padding: 0; /* 30×30:toolbar 整体加高到 52px 后,原 28px 显得偏小、与窗口控件 34px 差距过大; 30px 与图标 12px 同步上调,比例协调,悬浮反馈也更易感知 */ width: 30px; height: 30px; justify-content: center; color: var(--color-text-secondary); background: var(--color-chip-bg); border-color: var(--color-border); /* 给所有 icon 按钮一个微阴影,悬浮态时升级——「可点击」的微提示 */ box-shadow: var(--shadow-sm); transition: background 0.15s var(--ease-soft), color 0.15s var(--ease-soft), border-color 0.15s var(--ease-soft), box-shadow 0.2s var(--ease-soft), transform 0.1s var(--ease-soft); } .btn-icon:hover { background: var(--color-bg-tertiary); color: var(--color-text); border-color: var(--color-border-strong); transform: translateY(-0.5px); box-shadow: var(--shadow-md); } .btn-icon.active { background: var(--color-accent-bg); color: var(--color-accent-hover); border-color: transparent; box-shadow: 0 0 0 3px var(--color-accent-bg); } /* .btn-block 已停用 —— 现在按钮内自带 icon + label,宽度由 flex 自然撑开, 不需要「强制 100% 宽度」。删除以避免被后人误用。cleaned: 26-08 */ .btn-icon-sm { width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center; border-radius: var(--radius-sm); font-size: var(--font-sm); color: var(--color-text-muted); transition: background 0.15s, color 0.15s; } .btn-icon-sm:hover { background: var(--color-bg-tertiary); color: var(--color-text); } .btn-icon-sm.btn-danger:hover { background: var(--color-danger-bg); color: var(--color-danger); } .btn svg { /* 14px:保留模态对话框按钮里的图标尺寸,不与顶部 toolbar 一起缩小 */ width: 15px; height: 15px; flex-shrink: 0; } .btn-icon svg { /* 12px:toolbar 顶部按钮专用 —— 与升到 30px 的按钮框同步, 内边距 9px 比之前 8.5px 略松,更显轻盈 */ width: 12px; height: 12px; flex-shrink: 0; } .btn-icon-sm svg { width: 13px; height: 13px; flex-shrink: 0; } /* ============================================ 下拉菜单基础样式(被 sort 菜单复用) ============================================ */ .more-menu { position: fixed; z-index: var(--z-menu); min-width: 240px; background: var(--color-bg-elevated); border: 1px solid var(--color-border-strong); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); padding: 5px; font-size: var(--font-sm); animation: menu-in 0.16s var(--ease-out); -webkit-app-region: no-drag; } .more-menu-item { display: flex; align-items: center; gap: 11px; width: 100%; padding: 8px 11px; border-radius: 5px; cursor: pointer; color: var(--color-text); text-align: left; background: none; border: none; font-family: inherit; font-size: var(--font-sm); font-weight: 500; letter-spacing: -0.003em; transition: background 0.1s, color 0.1s; } .more-menu-item:hover { background: var(--color-accent-bg); color: var(--color-accent-hover); } .more-menu-item > svg { width: 14px; height: 14px; flex-shrink: 0; color: var(--color-text-muted); transition: color 0.1s; } .more-menu-item:hover > svg { color: var(--color-accent-hover); } .more-menu-item > span { flex: 1; min-width: 0; letter-spacing: -0.003em; } /* 排序菜单(由 sort-btn 触发,固定到 btn-sort 旁边) */ .sort-menu { min-width: 180px; } /* 过滤菜单(由 filter-btn 触发,风格与 sort-menu 保持一致) */ .filter-menu { min-width: 168px; } .sort-menu-header { padding: 6px var(--space-3) 4px; font-size: var(--font-xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-text-muted); } .sort-check { color: var(--color-accent); font-weight: 600; margin-left: auto; } /* 显式盖过 `.more-menu-item > span { flex: 1 }`,让勾选标记不会被撑开 (否则在 3-span 的过滤菜单里,label 和 ✓ 会平分空间而非贴右对齐) */ .more-menu-item > .sort-check { flex: 0 0 auto; } /* ============================================ 主体布局 - 两栏 (Sidebar | Task List) ============================================ */ .layout { display: grid; /* 侧栏宽度按窗口宽度在 245–280px 之间平滑过渡(中宽度临界点不再「抖」) */ grid-template-columns: clamp(245px, 25%, 280px) 1fr; overflow: hidden; min-height: 0; } /* ============================================ 侧边栏 ============================================ */ .sidebar { display: flex; flex-direction: column; background: var(--color-bg-secondary); border-right: 1px solid var(--color-border); overflow: hidden; min-width: 0; position: relative; /* 侧栏上沿的高光线,配合主区那条 — 让顶部边缘更像被光照亮 */ } /* 侧栏顶部的 1px 高光,让窗格衔接处更精致 */ .sidebar::before { content: ""; position: absolute; top: 0; right: -1px; bottom: 0; width: 1px; background: linear-gradient( to bottom, rgba(255, 255, 255, 0.08) 0%, transparent 30%, transparent 70%, rgba(255, 255, 255, 0.04) 100% ); pointer-events: none; z-index: 1; } [data-theme="light"] .sidebar::before { background: linear-gradient( to bottom, rgba(255, 255, 255, 0.8) 0%, transparent 30%, transparent 70%, rgba(0, 0, 0, 0.02) 100% ); } .category-list { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 10px 8px 0; min-height: 0; } .category-list::after { content: ""; display: block; height: var(--space-2); } /* 智能列表与子分类之间的柔和分隔线 */ .sidebar-divider { height: 1px; background: var(--color-border-muted); margin: 10px 12px 8px; border-radius: 1px; } /* 「+ 新建子分类」按钮:渲染在子分类列表末尾(紧跟最后一个子分类) 风格上融入列表:与子分类项同字号、虚框表「待添加」位,hover 变为实心强调色。*/ .btn-add-subcategory { display: flex; align-items: center; gap: 10px; width: 100%; height: 32px; padding: 0 11px 0 14px; margin-top: 5px; font-size: var(--font-sm); font-weight: 500; color: var(--color-text-secondary); background: transparent; border: 1px dashed var(--color-border-muted); border-radius: 7px; cursor: pointer; text-align: left; white-space: nowrap; /* border-style 不可平滑过渡,省略以免给人「卡顿」错觉 */ transition: background 0.15s var(--ease-soft), color 0.15s var(--ease-soft), border-color 0.15s var(--ease-soft), transform 0.12s var(--ease-soft); } .btn-add-subcategory:hover { background: var(--color-accent-bg); color: var(--color-accent-hover); border-color: var(--color-accent); border-style: solid; } .btn-add-subcategory:active { transform: scale(0.98); } .btn-add-subcategory:focus-visible { outline: none; box-shadow: var(--focus-ring-light); } .btn-add-subcategory svg { flex-shrink: 0; } .btn-add-subcategory .btn-text { font-weight: 500; } .category-item { display: flex; align-items: center; gap: 10px; padding: 7px 11px 7px 13px; border-radius: 7px; cursor: pointer; margin-bottom: 1px; font-size: var(--font-sm); font-weight: 500; color: var(--color-text-secondary); transition: background 0.15s var(--ease-soft), color 0.15s var(--ease-soft), transform 0.15s var(--ease-soft); user-select: none; position: relative; min-height: 32px; } .category-item:hover { background: var(--color-bg-tertiary); color: var(--color-text); /* 微平移给出「回应」的感觉 */ transform: translateX(1px); } .category-item:focus-visible { outline: none; box-shadow: var(--focus-ring-light); } /* 已激活项 hover 时不要平移(会和指示条一起抖),且颜色保持 accent 不退回 */ .category-item.active:hover { transform: none; } .category-item.active { /* 激活态:柔和的渐变背景 + 细微 inset 高光,立体感而不是平面色块 */ background: linear-gradient( to right, var(--color-accent-bg-strong), var(--color-accent-bg) 60%, transparent ); color: var(--color-accent-hover); font-weight: 600; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06); } .category-item.active::before { content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%); width: 2.5px; height: 16px; background: var(--color-accent); border-radius: 0 3px 3px 0; /* 激活态的指示条带一点 accent glow,更精神 */ box-shadow: 0 0 8px var(--color-accent-glow); } .category-icon { width: 17px; height: 17px; display: inline-flex; align-items: center; justify-content: center; color: var(--color-text-muted); flex-shrink: 0; transition: color 0.15s; } .category-item.active .category-icon { color: var(--color-accent-hover); } .category-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; letter-spacing: -0.005em; /* 长名称被截断时,右侧加 12px 渐隐到透明,让硬切的边界不那么扎眼。 未截断时这块透明区没东西可盖,视觉无差异。 */ -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 12px), transparent 100%); mask-image: linear-gradient(to right, #000 calc(100% - 12px), transparent 100%); } /* ============================================ 侧边栏条目右侧数量徽章 —— 让每个分类一眼看出工作量 ============================================ */ .category-count { /* 紧凑的胶囊:左侧贴名字、右侧留 2px 余量给 hover/选中态边距; flex-shrink: 0 防被 .category-name 的 ellipsis + mask 抢到 0 宽度 */ flex-shrink: 0; margin-left: auto; margin-right: 2px; min-width: 22px; height: 18px; padding: 0 6px; display: inline-flex; align-items: center; justify-content: center; font-size: 10.5px; font-weight: 600; font-variant-numeric: tabular-nums; /* 数字等宽,避免 1→9→10 时徽章宽度抖动 */ line-height: 1; color: var(--color-text-muted); background: var(--color-bg-tertiary); border-radius: var(--radius-pill); transition: background 0.15s var(--ease-soft), color 0.15s var(--ease-soft); /* 透出柔和的「非关键信息」感;激活态由 .active 规则覆盖为强调色 */ letter-spacing: 0; } /* hover:徽章轻微提亮(与背景层一起切到更亮的底色,给用户「这一行被点到」的反馈) */ .category-item:hover .category-count { background: var(--color-bg-secondary); color: var(--color-text-secondary); } /* 激活态:徽章用与文字一致的强调色 + 透明底(避免和渐变背景重叠看起来糊成一团) */ .category-item.active .category-count { background: rgba(79, 70, 229, 0.14); color: var(--color-accent-hover); } /* 智能列表的徽章 — 默认稍重一点(让「当前/重要」一眼看出待办量) 注:--color-chip-bg 与 .category-count 默认的 --color-bg-tertiary 数值完全一致, 所以这里不再重复声明背景 —— 等颜色变量需要分流时再启用此规则。 */ /* 回收站的徽章:用略带警告感的灰底,与回收站整体的低调配色一致 */ .category-item.category-trash .category-count { background: var(--color-bg-secondary); color: var(--color-text-tertiary); } /* 子分类 + hover 操作按钮共存时,操作按钮要盖在徽章位置上(保证可点); 这里不强制定位,只让徽章在 hover/active 时降低不透明度,让操作按钮更醒目。 仅在操作按钮存在时被选择器匹配(subcategory-actions 是兄弟节点) */ .category-item.subcategory:hover .category-count, .category-item.subcategory.active .category-count, .category-item.subcategory:focus-within .category-count { /* 操作按钮出现时徽章退到更淡的视觉权重,避免与按钮抢戏 */ opacity: 0.55; transition: opacity 0.15s var(--ease-soft); } /* ============================================ 智能列表(侧边栏顶部) ============================================ */ .smart-list { position: relative; margin-bottom: 1px; } .smart-list .category-icon { width: 17px; height: 17px; display: inline-flex; align-items: center; justify-content: center; transition: transform 0.2s var(--ease-spring); } .smart-list:hover .category-icon { transform: scale(1.08); } /* 重要 — 琥珀色(与「当前任务」的暖黄区分开,避免视觉混淆) */ /* sidebar.js 把 smart-${key} 类放在 .smart-icon span 上而不是 .category-item 上, 旧选择器 .smart-list.smart-important .category-icon 永远不匹配 —— 改成 .smart-icon。 这里同时保留旧的(万一以后又迁回)和新的实际命中的选择器 */ .smart-list .smart-icon.smart-important, .smart-list.smart-important .category-icon { color: var(--color-star); } /* 激活态:使用与普通激活态一致的渐变(不要用纯色块,破坏统一感) */ .smart-list.active { background: linear-gradient( to right, var(--color-accent-bg-strong), var(--color-accent-bg) 60%, transparent ); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06); } .smart-list.active .category-name { color: var(--color-accent-hover); font-weight: 600; } /* 激活时图标变为统一强调色(保持原色调以传达语义) */ .smart-list.smart-important.active .category-icon { color: var(--color-accent); } /* 智能列表与子分类之间的分隔线由 sidebar.js 用 class="sidebar-divider" 控制 */ /* 「当前任务」智能列表 —— v3 起从真实分类下沉为标识驱动的视图。 视觉权重略高于「重要任务」,符合「现在要聚焦的」语义 */ .category-item.smart-list[data-smart-key="current"] { font-weight: 600; } /* 「当前任务」图标:与「重要任务」区分色(蓝色聚焦感) */ .category-item.smart-list[data-smart-key="current"] .smart-icon { color: var(--color-current); } .category-item.smart-list[data-smart-key="current"].active .smart-icon { color: var(--color-accent); } /* 已完成分类:使用默认文本色,与其他分类保持一致 */ .category-item.category-completed { color: inherit; } .category-item.category-completed .category-icon { color: inherit; } /* 回收站:略淡的颜色,与「已完成」呼应但更低调 */ .category-item.category-trash { color: var(--color-text-tertiary); margin-top: 4px; } .category-item.category-trash .category-icon { color: var(--color-text-tertiary); } .category-item.category-trash.active { color: var(--color-text); } .category-item.category-trash.active .category-icon { color: var(--color-text); } /* 回收站中的任务项:降低视觉权重 */ .task-item.task-trash { opacity: 0.7; } /* 回收站里只有「恢复」+「编辑」两个按钮(没有星标、当前、删除), 默认就应该清楚地能看到 —— 不像普通行那样 hover/selected 才显形, 否则用户会以为按钮点不动。*/ .task-item.task-trash .task-actions { opacity: 0.9; } .task-item.task-trash .task-text { text-decoration: line-through; text-decoration-color: var(--color-text-tertiary); } .category-item.dragging { opacity: 0.4; } .category-item.dragging::before { content: ""; position: absolute; inset: 0; background: var(--color-bg-tertiary); border: 1px dashed var(--color-accent); border-radius: var(--radius-md); } /* 拖拽落点指示:drag-over = 插到该项之前,drag-over-below = 插到该项之后 */ .category-item.drag-over { border-top: 2px solid var(--color-accent); } .category-item.drag-over-below { border-bottom: 2px solid var(--color-accent); } /* ============================================ 子分类(缩进显示) ============================================ */ .subcategory-list { /* 子分类容器,仅做缩进布局 */ margin-left: 14px; padding-left: 6px; } .category-item.subcategory { padding-left: 14px; } .category-item.subcategory .category-icon { width: 12px; height: 12px; opacity: 0.7; } .category-item.subcategory:hover .category-icon { opacity: 1; } .category-item.subcategory .category-name { font-size: var(--font-sm); font-weight: 500; color: var(--color-text-secondary); } /* 子分类右侧操作按钮(默认隐藏,hover/active 时显示) */ .subcategory-actions { display: flex; align-items: center; gap: 1px; flex-shrink: 0; opacity: 0; transition: opacity 0.12s; } .category-item.subcategory:hover .subcategory-actions, .category-item.subcategory.active .subcategory-actions, .category-item.subcategory:focus-within .subcategory-actions { opacity: 1; } .subcategory-action-btn { width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center; border-radius: 4px; color: var(--color-text-muted); background: transparent; border: none; cursor: pointer; transition: background 0.12s var(--ease-soft), color 0.12s var(--ease-soft), transform 0.08s var(--ease-soft); } .subcategory-action-btn:hover { background: var(--color-bg-elevated); color: var(--color-text); } .subcategory-action-btn:active { transform: scale(0.88); } .subcategory-action-btn.subcategory-action-delete:hover { background: var(--color-danger-bg); color: var(--color-danger); } .category-item.subcategory.dragging { opacity: 0.5; } .category-item.subcategory.drag-over { background: var(--color-accent-bg); } /* 跨分类转移落点:拖任务 → 侧边栏子分类。 与「拖子分类 → 子分类」用的 .drag-over 区别开,避免语义混淆: .drag-over 是「插到这条之前/之后」,task-drop-target 是「把任务搬进这个分类」。 */ .category-item.subcategory.task-drop-target { background: var(--color-accent-bg); outline: 1px solid var(--color-accent); outline-offset: -1px; border-radius: var(--radius-sm); } .category-item.subcategory.task-drop-target .category-name { color: var(--color-accent-hover); font-weight: 600; } /* 「全部任务」智能列表项(容器入口) */ .category-item.category-other-tasks { font-weight: 600; } .category-item.category-other-tasks .category-icon { color: var(--color-text-secondary); } .category-item.category-other-tasks.active .category-icon { color: var(--color-accent-hover); } /* 空状态:没有子分类时给一个柔和的提示 */ .subcategory-empty { font-size: var(--font-xs); color: var(--color-text-muted); padding: 7px 14px 7px 28px; font-style: italic; user-select: none; } /* ============================================ 任务面板 ============================================ */ .task-panel { display: flex; flex-direction: column; overflow: hidden; background: var(--color-bg); min-width: 0; } .task-header { display: flex; align-items: center; padding: 16px var(--space-7) 14px; border-bottom: 1px solid var(--color-border); flex-shrink: 0; gap: var(--space-3); min-width: 0; } .task-header-main { display: flex; align-items: baseline; gap: 11px; min-width: 0; flex: 1; overflow: hidden; /* 防止长标题撑破容器导致按钮溢出 */ } .task-category-title { font-size: var(--font-xl); font-weight: 700; color: var(--color-text); letter-spacing: -0.028em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; line-height: 1.25; flex-shrink: 1; display: inline-flex; align-items: center; gap: 9px; } /* 标题前缀图标:与侧边栏一致使用 SVG */ .task-category-title .title-icon { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; color: var(--color-text-secondary); width: 22px; height: 22px; transition: color 0.15s; } .task-category-title .title-icon svg { width: 100%; height: 100%; display: block; } /* 「当前任务」图标:与侧边栏一致使用蓝色(与重要任务的琥珀色区分) */ .task-category-title .title-icon-current { color: var(--color-current); } .task-category-title .title-icon-important { color: var(--color-star); } .task-category-title .title-icon-completed { color: var(--color-success); } .task-category-title .title-icon-folder, .task-category-title .title-icon-sub { color: var(--color-text-muted); } .task-category-title .title-icon-trash { color: var(--color-text-tertiary); } /* 标题旁的进度文字:紧凑、不抢视觉 */ .task-progress-text { /* 与 page title (font-xl) 同处一行 —— 用 font-xs 让「3/5 项」 这种元数据明显小于标题、不抢视觉重心 */ font-size: var(--font-xs); font-weight: 500; color: var(--color-text-secondary); font-variant-numeric: tabular-nums; white-space: nowrap; letter-spacing: -0.005em; flex-shrink: 0; } .task-progress-text.completed { color: var(--color-success); } /* 任务头部右侧的操作区:过滤器 / 排序 */ .task-header-actions { display: flex; align-items: center; gap: var(--space-2); flex-shrink: 0; } /* 过滤器 / 排序 chip */ .filter-chip-btn { background: transparent; border: 1px solid transparent; padding: 0; border-radius: var(--radius-pill); cursor: pointer; transition: background 0.15s var(--ease-soft), border-color 0.15s var(--ease-soft), transform 0.08s var(--ease-soft); } .filter-chip-btn:hover { background: var(--color-bg-tertiary); } .filter-chip-btn:active { transform: scale(0.96); } .filter-chip { display: inline-flex; align-items: center; gap: 6px; font-size: var(--font-sm); font-weight: 500; color: var(--color-text-secondary); padding: 4px 12px; background: var(--color-chip-bg); border: 1px solid var(--color-border); border-radius: var(--radius-pill); white-space: nowrap; box-shadow: var(--shadow-sm); transition: background 0.15s var(--ease-soft), color 0.15s var(--ease-soft), border-color 0.15s var(--ease-soft), box-shadow 0.2s var(--ease-soft), transform 0.1s var(--ease-soft); letter-spacing: -0.003em; } .filter-chip-btn:hover .filter-chip { background: var(--color-bg-elevated); border-color: var(--color-border-strong); color: var(--color-text); transform: translateY(-0.5px); box-shadow: var(--shadow-md); } .filter-chip[data-filter="current"] { background: var(--color-warning-bg); color: var(--color-warning); } .filter-chip[data-filter="completed"] { background: var(--color-success-bg); color: var(--color-success); } /* 排序 chip — 中性配色,不带 filter 的语义色彩 */ .filter-chip.sort-chip { background: var(--color-chip-bg); color: var(--color-text-secondary); border-color: var(--color-border); } .filter-chip-btn:hover .filter-chip.sort-chip { background: var(--color-bg-elevated); border-color: var(--color-border-strong); color: var(--color-text); } /* 排序 chip 在「手动」模式(默认):中性配色 */ .filter-chip.sort-chip[data-sort="manual"] { background: var(--color-chip-bg); color: var(--color-text-secondary); border-color: var(--color-border); } /* hover 时提升层级 */ .filter-chip-btn:hover .filter-chip.sort-chip[data-sort="manual"] { background: var(--color-bg-elevated); border-color: var(--color-border-strong); color: var(--color-text); } .filter-chip[data-filter="important"] { background: var(--color-warning-bg); color: var(--color-warning); font-weight: 600; } /* 排序 chip 在「字母」模式(非默认):强调色,给用户「这是另一种排序」的视觉提示 */ .sort-chip[data-sort="alphabet"] { background: var(--color-accent-bg); color: var(--color-accent-hover); font-weight: 500; } /* 导入 chip —— 与 sort chip 同款中性配色: 不带过滤/排序的语义色彩,避免用户在「过滤/排序/导入」三连按钮里被暗示成「这也是过滤」 */ .import-chip { background: var(--color-chip-bg); color: var(--color-text-secondary); border-color: var(--color-border); } .filter-chip-btn:hover .import-chip { background: var(--color-bg-elevated); border-color: var(--color-border-strong); color: var(--color-text); } .filter-chip .chip-icon { font-size: 10px; line-height: 1; } .filter-chip svg { flex-shrink: 0; } /* 任务输入(在任务面板底部) */ .task-input-wrapper { display: flex; align-items: center; gap: 11px; padding: 9px var(--space-7); border-top: 1px solid var(--color-border); background: var(--color-bg-overlay); backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%); flex-shrink: 0; transition: background 0.18s var(--ease-soft); position: relative; z-index: 5; } /* 输入区顶部的细高光,与工具栏呼应 */ .task-input-wrapper::before { content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 1px; background: linear-gradient( to right, transparent, rgba(255, 255, 255, 0.06) 50%, transparent ); pointer-events: none; } [data-theme="light"] .task-input-wrapper::before { background: linear-gradient( to right, transparent, rgba(255, 255, 255, 0.6) 50%, transparent ); } .task-input-wrapper:focus-within { background: var(--color-bg-secondary); } .task-input { flex: 1; font-size: var(--font-sm); padding: 5px 0; min-width: 0; letter-spacing: -0.003em; } /* 输入框右侧的「提交」按钮(常驻可见:无文本或非法分类时禁用) */ .task-submit-btn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: var(--radius-md); background: var(--color-accent); color: #ffffff; flex-shrink: 0; box-shadow: var(--shadow-accent); transition: background 0.15s var(--ease-soft), transform 0.1s var(--ease-soft), box-shadow 0.2s var(--ease-soft), opacity 0.15s var(--ease-soft); } .task-submit-btn:hover { background: var(--color-accent-hover); transform: translateY(-0.5px) scale(1.04); box-shadow: 0 6px 18px var(--color-accent-glow), 0 1px 2px var(--color-accent-glow); } .task-submit-btn:active { transform: scale(0.94); } .task-submit-btn:disabled { opacity: 0.4; cursor: not-allowed; box-shadow: none; } .task-list { flex: 1; list-style: none; overflow-y: auto; overflow-x: hidden; padding: 4px 0; min-height: 0; scroll-padding-block: var(--space-2); } /* 任务列表为空时折叠,把整块中间空间让给 .empty-state, 让它的 justify-content: center 落在 header 和输入框之间的正中。 task-list.js 在空态会执行 listEl.innerHTML = '' —— 这会清掉注释, 所以 :empty 能稳定命中。 */ .task-list:empty { display: none; } /* ============================================ 任务项 - 使用 absolute 伪元素绘制 hover 背景以实现"贴边" - 任务项本身只占文本/控件所需宽度 ============================================ */ .task-item { position: relative; display: flex; align-items: center; gap: 12px; padding: 8px 13px; cursor: grab; /* 触屏上禁止浏览器默认的滚动/缩放/双击放大 —— 让整行的 pointer events 全部交给我们自己的拖拽/点击判定,否则手指一滑就被认作滚动而错过拖拽。 列表滚动靠空白处 / 滚动条触发。 */ touch-action: none; transition: opacity 0.25s var(--ease-out), transform 0.18s var(--ease-soft); /* 无条件 animation 已移除:每次 render() 重建 innerHTML 都会重放动画, 引起整个列表闪烁;改为只在 .task-item.is-new 上挂 fade-in —— 见 task-list.js */ border-radius: 8px; margin: 0 8px; } /* 仅新增的任务(构造函数里 _knownTaskIds 没见过的 id)才挂 fade-in; 已有任务不会闪,只走 transition 平滑过渡 */ .task-item.is-new { animation: task-fade-in 0.2s var(--ease-out); } .task-item::before { content: ""; position: absolute; inset: 0; background: transparent; border-radius: 8px; border: 1px solid transparent; transition: background 0.18s var(--ease-soft), border-color 0.18s var(--ease-soft), box-shadow 0.2s var(--ease-soft); pointer-events: none; z-index: 0; } .task-item:hover::before { background: var(--color-bg-tertiary); } .task-item.selected::before { background: var(--color-accent-bg-strong); border-color: var(--color-accent); box-shadow: 0 0 0 4px var(--color-accent-bg); } /* 已完成任务:用「文本降级 + line-through」传达完成语义即可, 不再叠整行 opacity —— 否则配合 .task-text 的 muted 色,文字会糊到看不清。 跟用户定下的「灰度处理也要清晰可读」原则对齐。 */ .task-item.completed { /* 整行留一点点 alpha,仅用来压一压图标/按钮等周边元素 */ opacity: 0.92; } /* 子元素提升到伪元素之上 */ .task-item > * { position: relative; z-index: 1; } /* 复选框:增大点击区 */ .task-checkbox-wrapper { display: flex; align-items: center; justify-content: center; cursor: pointer; flex-shrink: 0; width: 24px; height: 24px; border-radius: 7px; /* 点击时给整个点击区一个轻微的弹性反馈 —— 比单靠 checkbox 自带的 check-pop 更完整地传达「打勾了」。 */ transition: background 0.15s var(--ease-soft), transform 0.12s var(--ease-spring); } .task-checkbox-wrapper:active { transform: scale(0.88); } .task-checkbox-wrapper:hover { background: var(--color-bg-tertiary); } .task-checkbox { position: absolute; opacity: 0; pointer-events: none; } .task-checkbox-custom { display: inline-flex; align-items: center; justify-content: center; width: 19px; height: 19px; border: 1.6px solid var(--color-text-faint); border-radius: 6px; background: transparent; transition: background 0.2s var(--ease-soft), border-color 0.2s var(--ease-soft), transform 0.18s var(--ease-spring), box-shadow 0.2s var(--ease-soft); position: relative; } .task-checkbox:checked + .task-checkbox-custom { background: var(--color-accent); border-color: var(--color-accent); box-shadow: 0 2px 6px var(--color-accent-glow); } .task-checkbox:checked + .task-checkbox-custom::after { content: ""; position: absolute; left: 5.5px; top: 1px; width: 5.5px; height: 11px; border-right: 2px solid white; border-bottom: 2px solid white; transform: rotate(45deg); animation: check-pop 0.22s var(--ease-spring); } .task-item:hover .task-checkbox-custom { border-color: var(--color-text-muted); } /* 任务文本 */ .task-text { flex: 1; font-size: var(--font-sm); font-weight: 400; color: var(--color-text); user-select: none; word-break: break-word; line-height: 1.55; min-width: 0; padding: 0; letter-spacing: -0.005em; /* 完成 ↔ 未完成的文字色和删除线颜色平滑过渡,避免硬切。 text-decoration-color 自 2022 起在主流浏览器均可过渡。 */ transition: color 0.2s var(--ease-soft), text-decoration-color 0.2s var(--ease-soft); } /* 中西文之间的间隙元素(由 escapeHtmlAutospace 注入的空 span)。 Electron 28 = Chromium 120,还没有 CSS text-autospace,只能在渲染时插标签。 用 padding 而不是 width/margin:已完成任务的 .task-text 带 line-through, 祖先的 text-decoration 不会画过 inline-block 这类原子行内盒,也不画在 margin 区域上 —— 那样删除线会在每个间隙处断开。padding 区域会被画到,删除线保持连续。 0.16em ≈ 思源/苹方在正文字号下的四分之一空格,比真空格窄,只做「不挤」的程度。 */ .cjk-gap { padding-right: 0.16em; } .task-text mark { /* 搜索命中:accent 浅底 + 默认字色 —— 仅起到"这里被命中"的视觉提示, 不再使用 warning 配色,避免和「重要任务」「未保存」等真实警告混在一起 */ background: var(--color-accent-bg); color: inherit; padding: 0 3px; border-radius: 2px; } /* 任务内容容器(文本 + 元数据行) */ .task-content { flex: 1; display: flex; flex-direction: column; gap: 3px; min-width: 0; } /* 任务「原分类」徽章:仅在已完成任务 / 回收站视图中显示。 对应 markdown 文件里的 [原分类:xxx] 标记,让用户在界面上 一眼看到这条任务来自哪里。胶囊样式,弱化视觉权重, 关键 —— 不继承 .task-text 的 line-through(语义上是元数据而非任务内容)。 */ .task-original-category { display: inline-flex; align-items: center; align-self: flex-start; max-width: 100%; font-size: var(--font-xs); font-weight: 500; color: var(--color-text-tertiary); background: var(--color-bg-tertiary); border: 1px solid var(--color-border-muted); border-radius: var(--radius-sm); padding: 1px 7px; line-height: 1.55; letter-spacing: 0.01em; text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; user-select: none; } /* hover 时略微提亮,便于发现这是可识别的元数据 */ .task-item:hover .task-original-category { color: var(--color-text-secondary); border-color: var(--color-border); } .task-item.completed .task-text { /* 从 --color-text-muted 升级到 --color-text-secondary —— 配合整行 opacity 0.92 后整体仍是「偏淡但可扫视」的层级,不再像之前那样糊在一起。 line-through + 略弱字色已经足够区分「已完成」语义。 */ color: var(--color-text-secondary); text-decoration: line-through; text-decoration-color: var(--color-text-tertiary); text-decoration-thickness: 1px; } /* 编辑模式 */ .task-edit-input { flex: 1; font-size: var(--font-sm); padding: 6px var(--space-2); background: var(--color-bg-elevated); border: 1px solid var(--color-accent); border-radius: var(--radius-sm); box-shadow: 0 0 0 3px var(--color-accent-bg); margin: -2px 0; min-width: 0; color: var(--color-text); z-index: 2; letter-spacing: -0.003em; } /* 重要标记 ⭐ 按钮 */ .task-star-btn { width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center; border-radius: var(--radius-sm); background: transparent; color: var(--color-text-faint); flex-shrink: 0; transition: background 0.15s, color 0.15s, transform 0.1s var(--ease-spring); border: none; padding: 0; cursor: pointer; } .task-star-btn:hover { background: var(--color-bg-tertiary); color: var(--color-warning); } .task-item.important .task-star-btn { color: var(--color-warning); } .task-star-btn:active { transform: scale(0.85); } .task-star-btn svg { width: 15px; height: 15px; } .task-item.completed.important .task-star-btn { opacity: 0.55; } /* 「当前」标记按钮:与 ⭐ 同尺寸,激活时蓝色,与星标的琥珀色区分 */ .task-current-btn { width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center; border-radius: var(--radius-sm); background: transparent; color: var(--color-text-faint); flex-shrink: 0; transition: background 0.15s, color 0.15s, transform 0.1s var(--ease-spring); border: none; padding: 0; cursor: pointer; } .task-current-btn:hover { background: var(--color-bg-tertiary); color: var(--color-current); } .task-item.current .task-current-btn { color: var(--color-current); } .task-current-btn:active { transform: scale(0.85); } .task-current-btn svg { width: 15px; height: 15px; } .task-item.completed.current .task-current-btn { opacity: 0.55; } /* 任务操作按钮:默认弱化可见,hover/选中时强调 */ .task-actions { display: flex; align-items: center; gap: 2px; opacity: 0.45; transition: opacity 0.15s; flex-shrink: 0; } .task-item:hover .task-actions, .task-item.selected .task-actions, .task-item:focus-within .task-actions { opacity: 1; } /* 键盘聚焦:让 :focus-visible 在行级别也有一圈清晰轮廓 —— 只在键盘 tab 时出现, 不打扰鼠标用户。复用了 hover 的 ::before 渐变底,再叠 2px accent ring。 */ .task-item:focus-visible { outline: none; } .task-item:focus-visible::before { background: var(--color-bg-tertiary); border-color: var(--color-accent); box-shadow: 0 0 0 2px var(--color-accent-bg-strong); } .task-action-btn { width: 26px; height: 26px; display: flex; align-items: center; justify-content: center; border-radius: 5px; font-size: var(--font-sm); color: var(--color-text-muted); cursor: pointer; transition: background 0.15s var(--ease-soft), color 0.15s var(--ease-soft), transform 0.08s var(--ease-soft); } .task-action-btn:hover { background: var(--color-bg-tertiary); color: var(--color-text); } .task-action-btn:active { transform: scale(0.88); } .task-action-delete:hover { background: var(--color-danger-bg); color: var(--color-danger); } .task-action-restore:hover { background: var(--color-success-bg); color: var(--color-success); } .task-action-btn svg { width: 12px; height: 12px; fill: currentColor; } /* 拖拽:旧的 .task-drag-handle 元素已移除 —— 整行本身就是拖拽区, cursor: grab 写在 .task-item 上,激活拖拽时切到 grabbing */ /* 拖拽进行中:源项降透明 + grabbing 光标,提示「这里正在被拖」 */ .task-item.drag-source { opacity: 0.35; cursor: grabbing; } /* ghost:跟随光标的克隆项 */ .drag-ghost { position: fixed; top: 0; left: 0; pointer-events: none; z-index: 9999; opacity: 0.92; border-radius: var(--radius-md); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.32), 0 2px 6px rgba(0, 0, 0, 0.18); /* 旋转一点,更直观地表达「抓起来」 */ transform-origin: top left; will-change: transform; } /* 落点指示线:2px 高亮横线,定位 fixed */ .drag-indicator { position: fixed; height: 2px; background: var(--color-accent); border-radius: 2px; pointer-events: none; z-index: 9998; box-shadow: 0 0 6px var(--color-accent); transition: opacity 0.1s; } /* 空状态 */ .empty-state { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--space-7) var(--space-5); color: var(--color-text-muted); text-align: center; gap: 11px; min-height: 0; animation: fade-in 0.3s var(--ease-out); } /* 关键:作者 display:flex 胜过 UA 的 [hidden]{display:none} —— 显式覆盖。 之前 task-list.js 设置 emptyEl.hidden = true 是个 no-op,导致「暂无任务」一直占着 任务面板、并且残留文本浮在非空列表上方。 */ .empty-state[hidden] { display: none; } .empty-icon { margin-bottom: var(--space-2); color: var(--color-text-faint); display: flex; align-items: center; justify-content: center; opacity: 0.65; width: 68px; height: 68px; /* 取消边框 + 卡片背景,让空白更轻盈 */ border-radius: 18px; background: linear-gradient( 135deg, var(--color-bg-tertiary), var(--color-bg-secondary) ); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04); } .empty-title { /* 空状态标题 —— font-md (15.5px) 已足够让它在空场景里突出, 不必上到 font-lg (17.5px) 抢主区域视觉 */ font-size: var(--font-md); color: var(--color-text); font-weight: 600; letter-spacing: -0.015em; } .empty-hint { font-size: var(--font-sm); margin-top: 2px; color: var(--color-text-secondary); max-width: 320px; line-height: 1.65; } .empty-hint kbd { display: inline-block; padding: 1px 6px; background: var(--color-bg-tertiary); border: 1px solid var(--color-border); border-bottom-width: 2px; border-radius: var(--radius-xs); font-family: var(--font-mono); font-size: var(--font-xs); color: var(--color-text); margin: 0 2px; } /* 空状态里的行动按钮(如「清空搜索」)—— 文字按钮风格, 不要和普通 hint 文字混在一起,不然用户找不到交互入口 */ .empty-hint b { color: var(--color-text-secondary); font-weight: 500; } .empty-action-btn { display: inline-block; margin-top: 6px; padding: 5px 13px; background: var(--color-bg-tertiary); border: 1px solid var(--color-border); border-radius: var(--radius-md); font-size: var(--font-sm); color: var(--color-text); font-weight: 500; transition: background 0.15s, color 0.15s, border-color 0.15s; } .empty-action-btn:hover { background: var(--color-bg-elevated); border-color: var(--color-border-strong); color: var(--color-text); } /* ============================================ 源码面板 / 详情面板 已整体移除 - 每个任务只有三个字段:文本 / 勾选状态 / 重要 - 详见 README 极简哲学 ============================================ */ /* 智能列表项 - 微妙的图标着色 */ .category-item.smart-list .category-icon { color: var(--color-accent-hover); } /* ============================================ 命令面板 (Cmd+K) ============================================ */ .command-palette-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.4); backdrop-filter: blur(12px) saturate(160%); -webkit-backdrop-filter: blur(12px) saturate(160%); display: flex; align-items: flex-start; justify-content: center; z-index: var(--z-modal); padding-top: 12vh; animation: fade-in 0.2s var(--ease-soft); } .command-palette { width: 600px; max-width: 92vw; background: var(--color-bg-elevated); border: 1px solid var(--color-border-strong); border-radius: var(--radius-xl); box-shadow: var(--shadow-lg); overflow: hidden; animation: palette-in 0.26s var(--ease-out); display: flex; flex-direction: column; max-height: 60vh; } @keyframes palette-in { from { opacity: 0; transform: translateY(-10px) scale(0.97); } to { opacity: 1; transform: translateY(0) scale(1); } } .command-palette-input-row { display: flex; align-items: center; gap: 13px; padding: 16px 19px; border-bottom: 1px solid var(--color-border); } .command-palette-input { flex: 1; font-size: var(--font-md-sm); color: var(--color-text); outline: none; background: transparent; border: none; font-family: inherit; } .command-palette-list { flex: 1; overflow-y: auto; padding: 6px; list-style: none; } .command-palette-item { display: flex; align-items: center; gap: 13px; padding: 10px 13px; border-radius: var(--radius-sm); cursor: pointer; font-size: var(--font-sm); color: var(--color-text); transition: background 0.1s, color 0.1s; letter-spacing: -0.005em; font-weight: 500; } .command-palette-item:hover, .command-palette-item.active { background: var(--color-accent-bg); color: var(--color-accent-hover); } .command-palette-item-icon { width: 18px; height: 18px; display: flex; align-items: center; justify-content: center; color: var(--color-text-muted); flex-shrink: 0; } .command-palette-item-label { flex: 1; font-weight: 500; } .command-palette-item-hint { color: var(--color-text-muted); font-size: var(--font-xs); font-family: var(--font-mono); } .command-palette-empty { padding: var(--space-6) var(--space-4); text-align: center; color: var(--color-text-secondary); font-size: var(--font-sm); } /* ============================================ 状态栏 ============================================ */ .statusbar { display: flex; align-items: center; padding: 0 16px; background: var(--color-bg-overlay); /* 与工具栏对称的玻璃效果 — 状态栏也淡淡透出 ambient */ backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%); border-top: 1px solid var(--color-border); /* 状态栏整体走 font-xs —— 底部状态栏是「辅助信息」层级, 与系统托盘类似不需要大字;放大会挤压 30px 高度的视觉空间 */ font-size: var(--font-xs); color: var(--color-text-secondary); gap: var(--space-3); min-width: 0; -webkit-app-region: no-drag; height: var(--h-statusbar); position: relative; } /* 状态栏顶部细高光,呼应工具栏底部的设计 */ .statusbar::before { content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 1px; background: linear-gradient( to right, transparent, rgba(255, 255, 255, 0.06) 50%, transparent ); pointer-events: none; } [data-theme="light"] .statusbar::before { background: linear-gradient( to right, transparent, rgba(255, 255, 255, 0.6) 50%, transparent ); } .status-text { /* 状态栏主文本 —— 略大于 meta 但仍走紧凑档(font-xs), 与整体 30px 高度协调,不会撑高视觉重量 */ font-size: var(--font-xs); font-weight: 500; color: var(--color-text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex-shrink: 1; letter-spacing: -0.008em; /* 与 file chip / meta 的层级区分 */ font-variant-numeric: tabular-nums; } .status-spacer { flex: 1; min-width: var(--space-2); } /* 状态栏右侧区块:保存时间 + 进度 */ .statusbar-right { display: inline-flex; align-items: center; gap: var(--space-3); flex-shrink: 0; } .status-meta { color: var(--color-text-secondary); font-variant-numeric: tabular-nums; white-space: nowrap; flex-shrink: 0; display: inline-flex; align-items: center; gap: 6px; letter-spacing: -0.005em; font-weight: 500; /* 与状态栏整体保持紧凑 —— 「已保存 5 秒前」是辅助元数据 */ font-size: var(--font-xs); } .status-meta.dirty { color: var(--color-warning); } .status-meta.saved { color: var(--color-success); } /* 状态指示灯(圆点) */ .status-meta::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex-shrink: 0; /* 微微的扩散光,让指示灯看起来在「发光」 */ box-shadow: 0 0 7px currentColor; } .status-meta.saved::before { background: var(--color-success); color: var(--color-success); } /* 无保存状态文本时(启动后尚未发生过保存)不显示孤立的圆点 */ .status-meta:empty::before { display: none; } /* 文件监视状态指示(永远显示,证明同步机制在工作,缓解用户的"软件到底有没有响应"的疑虑) */ .watch-indicator { display: inline-block; width: 7px; height: 7px; border-radius: 50%; /* 用主题 success 色 + 微微的光晕 —— 与 .status-meta.saved::before 同款 视觉重量,但放在最左侧,被 file chip 的视觉重量自然推开 */ background: var(--color-success); color: var(--color-success); box-shadow: 0 0 7px currentColor; flex-shrink: 0; cursor: help; /* 缓慢呼吸动画 —— 1.6s 一个周期,颜色变浅一下再回来,提示"它在持续盯着文件" */ animation: watch-pulse 1.6s ease-in-out infinite; user-select: none; } @keyframes watch-pulse { 0%, 100% { opacity: 1; box-shadow: 0 0 7px currentColor; } 50% { opacity: 0.55; box-shadow: 0 0 3px currentColor; } } /* ============================================ 右键菜单 ============================================ */ .context-menu { position: fixed; z-index: var(--z-menu); min-width: 188px; max-width: min(340px, calc(100vw - 16px)); /* 用户可能建了十几个分类,「移到分类…」一展开就能塞满半个屏幕。 这里限制到视口 60%,剩下的滚动可见 —— 比 slice(0, 6) 静默截掉更好, 后者让用户根本看不到后半段的分类,以为功能不存在。 */ max-height: 60vh; overflow-y: auto; overscroll-behavior: contain; background: var(--color-bg-elevated); border: 1px solid var(--color-border-strong); border-radius: 10px; box-shadow: var(--shadow-lg); padding: 5px; font-size: var(--font-sm); animation: menu-in 0.18s var(--ease-out); backdrop-filter: blur(16px) saturate(160%); -webkit-backdrop-filter: blur(16px) saturate(160%); /* 始终可见的细滚动条 —— Windows/Chromium 默认 overlay 样式只在悬停时显, 用户看到菜单被截就以为没内容,不知道能滚。这里强制 always-visible 提示。 */ scrollbar-width: thin; scrollbar-color: rgba(128, 128, 128, 0.35) transparent; } /* 暗色主题:菜单背景偏深 (--color-bg-elevated=#17171a) 与 task-panel 主区 (#0a0a0c) 仅相差 13 阶亮度,backdrop-filter 还会进一步"吃掉"对比; 强制叠一层 12% 半透明黑覆盖,让菜单与背景拉开层级 —— 与 toast 浅色处理同款思路 */ [data-theme="dark"] .context-menu { background: color-mix(in srgb, var(--color-bg-elevated) 92%, #000); border-color: color-mix(in srgb, var(--color-border-strong) 100%, transparent); } .context-menu::-webkit-scrollbar { width: 8px; } .context-menu::-webkit-scrollbar-track { background: transparent; } .context-menu::-webkit-scrollbar-thumb { background: rgba(128, 128, 128, 0.3); border-radius: 4px; } .context-menu::-webkit-scrollbar-thumb:hover { background: rgba(128, 128, 128, 0.5); } @keyframes menu-in { from { opacity: 0; transform: translateY(-4px) scale(0.97); } to { opacity: 1; transform: translateY(0) scale(1); } } .context-menu-item { padding: 8px 11px; border-radius: var(--radius-sm); cursor: pointer; color: var(--color-text); display: flex; align-items: center; gap: 9px; transition: background 0.12s var(--ease-soft), color 0.12s var(--ease-soft); letter-spacing: -0.005em; font-weight: 500; } .context-menu-item:hover { background: var(--color-accent-bg); color: var(--color-accent-hover); } .context-menu-item.context-menu-danger:hover { background: var(--color-danger-bg); color: var(--color-danger); } .context-menu-separator { height: 1px; background: var(--color-border); margin: 5px 0; } .context-menu-item.context-menu-indent { padding-left: 28px; color: var(--color-text-secondary); } /* ============================================ Toast 通知 ============================================ */ .toast-container { position: fixed; /* statusbar (--h-statusbar) + input wrapper (~52px) + 10px gap = 92px. 之前是 52px —— 与右下角的「+ 提交」按钮(位于输入框 wrapper 内)垂直重叠, toast 一出现就盖住 + 按钮,用户既看不到按钮也没法避开误点。 */ bottom: calc(var(--h-statusbar) + 62px); right: 22px; display: flex; flex-direction: column; gap: 8px; z-index: var(--z-toast); pointer-events: none; /* 视口太低时(如重排到 200px)允许 toast 顶部贴近屏幕顶部, 但仍尽量维持在主操作区上方(.. over input wrapper) */ max-height: calc(100vh - var(--h-statusbar) - 72px); overflow-y: auto; } .toast { display: flex; align-items: center; gap: 11px; padding: 12px 17px; background: var(--color-bg-elevated); border: 1px solid var(--color-border-strong); border-radius: 10px; box-shadow: var(--shadow-lg); font-size: var(--font-sm); color: var(--color-text); animation: toast-in 0.32s var(--ease-out); pointer-events: auto; min-width: 240px; max-width: 400px; letter-spacing: -0.005em; font-weight: 500; /* Toast 整体上提一点不透明度阈值,避免轻主题下「白底白文字」模糊感。 内部 backdrop-filter 仍保留玻璃质感(柔和、不过分实色),但叠一层极薄的同色覆盖, 让"文字色 vs 容器背景色"对比度稳定够用 —— 用户反馈"白底几乎看不到字"是这里没做。 */ backdrop-filter: blur(12px) saturate(160%); -webkit-backdrop-filter: blur(12px) saturate(160%); } [data-theme="light"] .toast { /* 浅色 toast 背景用接近纯白(--color-bg-secondary=#ffffff)+ 强一点阴影与边框, 让「已加载:…」在 #f7f7f9 主背景上能撑出清晰的边界, 不至于和工具栏 / 状态栏「白成一片」。 */ background: var(--color-bg-secondary); border-color: var(--color-border-strong); box-shadow: 0 4px 16px rgba(15, 23, 42, 0.10), 0 1px 3px rgba(15, 23, 42, 0.06), 0 24px 48px -8px rgba(15, 23, 42, 0.16); } .toast.toast-success { border-left: 3px solid var(--color-success); } .toast.toast-error { border-left: 3px solid var(--color-danger); } .toast.toast-warning { border-left: 3px solid var(--color-warning); } /* 可操作的 toast(带按钮) */ .toast.toast-actionable { padding: 8px 8px 8px 16px; } .toast .toast-text { flex: 1; } .toast .toast-action { background: transparent; border: 1px solid var(--color-accent); color: var(--color-accent); padding: 4px 10px; border-radius: var(--radius-sm); font-size: var(--font-sm); font-weight: 500; cursor: pointer; transition: background 0.15s var(--ease-soft); } .toast .toast-action:hover { background: var(--color-accent-bg); } .toast.toast-info { border-left: 3px solid var(--color-accent); } @keyframes toast-in { from { transform: translateY(8px) scale(0.97); opacity: 0; } to { transform: translateY(0) scale(1); opacity: 1; } } @keyframes toast-out { to { transform: translateY(4px) scale(0.97); opacity: 0; } } /* ============================================ 滚动条 ============================================ */ ::-webkit-scrollbar { width: 10px; height: 10px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb, rgba(255, 255, 255, 0.08)); border-radius: 5px; border: 2.5px solid transparent; background-clip: padding-box; transition: background 0.18s; } ::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover, rgba(255, 255, 255, 0.18)); background-clip: padding-box; border: 2.5px solid transparent; } ::-webkit-scrollbar-corner { background: transparent; } /* 暗色主题下滚动条更醒目一点 —— 默认 0.10 太弱,看不到自己在哪 */ [data-theme="dark"] ::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb, rgba(255, 255, 255, 0.13)); } [data-theme="dark"] ::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover, rgba(255, 255, 255, 0.25)); } /* Firefox 滚动条 */ * { scrollbar-width: thin; scrollbar-color: var(--scrollbar-thumb, rgba(255, 255, 255, 0.08)) transparent; } /* ============================================ 模态对话框 ============================================ */ .modal-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.55); backdrop-filter: blur(8px) saturate(140%); -webkit-backdrop-filter: blur(8px) saturate(140%); display: flex; align-items: center; justify-content: center; z-index: var(--z-modal); animation: fade-in 0.2s var(--ease-soft); } .modal { background: var(--color-bg-elevated); border: 1px solid var(--color-border-strong); border-radius: var(--radius-xl); box-shadow: var(--shadow-lg); /* 输入类弹窗(inputDialog)只需容纳一两个汉字的输入框, confirmDialog 的多行 message 也不会因宽度变窄而拥挤(自动换行即可) */ min-width: 340px; max-width: 500px; overflow: hidden; animation: modal-in 0.28s var(--ease-out); } @keyframes modal-in { from { opacity: 0; transform: translateY(8px) scale(0.96); } to { opacity: 1; transform: translateY(0) scale(1); } } /* 退出动画 —— 模态/命令面板关掉时先缩放淡出再 remove, 比同步 remove 多 ~180ms 缓冲,视觉上"主动关掉"而非"硬切"。 通过 .is-closing 类触发;JS 在 animationend 后再真正销毁节点 */ @keyframes modal-out { from { opacity: 1; transform: translateY(0) scale(1); } to { opacity: 0; transform: translateY(4px) scale(0.97); } } @keyframes palette-out { from { opacity: 1; transform: translateY(0) scale(1); } to { opacity: 0; transform: translateY(-6px) scale(0.97); } } .modal.is-closing { animation: modal-out 0.18s var(--ease-soft) forwards; } .command-palette.is-closing { animation: palette-out 0.18s var(--ease-soft) forwards; } @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } } .modal-header { /* 上下 16px:保留「标题独立成块」的呼吸感,又比 18px 更紧凑 */ padding: 16px 22px; border-bottom: 1px solid var(--color-border); font-size: var(--font-md); font-weight: 600; letter-spacing: -0.015em; } .modal-body { /* 上下 16px:仅一个输入框时上下空白 = 16+10(input padding) = 26px, 比之前 14+8 = 22px 略松但更舒适;多行 message 也仍然呼吸良好 */ padding: 16px 22px; font-size: var(--font-base); line-height: 1.6; color: var(--color-text-secondary); white-space: pre-wrap; } .modal-footer { display: flex; justify-content: flex-end; gap: 8px; /* 上下 12px:按钮本来就自带视觉重量,再叠 14px 显得「压脚」 */ padding: 12px 22px; border-top: 1px solid var(--color-border); background: var(--color-bg-secondary); } /* 输入对话框专属样式(替代 inline style) */ /* 紧凑版 modal:用于 inputDialog(新建/重命名子分类、新建列表)。 相比通用 .modal: - 去掉了 header/body 之间的 border-bottom,让标题与输入框视觉融合成一个色块 - 整体 padding 收紧,输入框上下空白从 ~22px 减到 ~12px - 弹窗高度从 ~178px 减到 ~134px,视觉重心更贴近输入区 */ .input-dialog-modal { min-width: 300px; max-width: 420px; } .input-dialog-title { padding: 12px 19px 5px; font-size: var(--font-md); font-weight: 600; letter-spacing: -0.015em; color: var(--color-text); } .input-dialog-body { padding: 5px 19px 12px; } .input-dialog-footer { display: flex; justify-content: flex-end; gap: 8px; padding: 10px 16px; border-top: 1px solid var(--color-border); background: var(--color-bg-secondary); } .input-dialog-message { /* 7px:标题与 input 视觉融合后,message 作为「补充说明」只需要与 input 清晰分隔 */ margin-bottom: 7px; color: var(--color-text-secondary); font-size: var(--font-base); line-height: 1.5; } .input-dialog-input { width: 100%; padding: 9px 13px; border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-bg); color: var(--color-text); font-family: inherit; font-size: var(--font-base); letter-spacing: -0.005em; transition: border-color 0.15s, box-shadow 0.15s; } .input-dialog-input:focus { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-bg); } .input-dialog-input::placeholder { color: var(--color-text-muted); } /* 校验失败:输入框变红 + 内联错误提示 */ .input-dialog-input.input-invalid { border-color: var(--color-danger); } .input-dialog-input.input-invalid:focus { border-color: var(--color-danger); box-shadow: 0 0 0 3px var(--color-danger-bg); } .input-dialog-error { /* 6px:紧贴输入框,校验失败时一眼可见,又不把弹窗撑高很多 */ margin-top: 6px; color: var(--color-danger); font-size: var(--font-sm); line-height: 1.4; } /* ============================================ 内容导入对话框(import-dialog.js) 复用 .modal-overlay / .modal 容器,自定义 textarea + 选项区 ============================================ */ .import-dialog-modal { /* 比 input-dialog 更宽:textarea 内容需要更多水平空间 */ min-width: 460px; max-width: 600px; } .import-dialog-title { padding: 14px 18px 4px; /* 14.5px:与其他 inline-task 字保持视觉量级,避免一开弹窗「字变粗变大」的违和感 */ font-size: var(--font-base); font-weight: 600; letter-spacing: -0.01em; color: var(--color-text); } .import-dialog-body { padding: 4px 18px 12px; display: flex; flex-direction: column; gap: 12px; } .import-dialog-hint { margin: 0; color: var(--color-text-secondary); font-size: var(--font-sm); line-height: 1.55; } .import-dialog-hint code { background: var(--color-bg-tertiary); padding: 1px 5px; border-radius: var(--radius-xs); font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.92em; color: var(--color-text); } .import-dialog-textarea { width: 100%; min-height: 180px; max-height: 360px; padding: 11px 13px; border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-bg); color: var(--color-text); font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: var(--font-sm); line-height: 1.55; letter-spacing: -0.005em; resize: vertical; transition: border-color 0.15s, box-shadow 0.15s; } .import-dialog-textarea:focus { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-bg); } .import-dialog-textarea::placeholder { color: var(--color-text-muted); white-space: pre; /* placeholder 内容用换行展示示例 —— 浏览器原生 white-space: pre 让 \n 生效 */ } .import-dialog-options { display: flex; flex-direction: column; gap: 8px; } .import-dialog-option { display: flex; align-items: center; gap: 8px; font-size: var(--font-sm); color: var(--color-text-secondary); cursor: pointer; user-select: none; } .import-dialog-option input[type="checkbox"] { margin: 0; cursor: pointer; } .import-dialog-option code { background: var(--color-bg-tertiary); padding: 1px 5px; border-radius: var(--radius-xs); font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.92em; color: var(--color-text); } .import-dialog-target { /* 让 select 不会在长选项名时被压缩 */ gap: 6px; } .import-dialog-select { flex: 1; padding: 6px 10px; border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-bg); color: var(--color-text); font-family: inherit; font-size: var(--font-sm); cursor: pointer; } .import-dialog-select:focus { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-bg); } /* 排序方式 segmented —— 与主设置面板的 settings-segmented / seg-btn 同款视觉, 但用 import-dialog-* 命名空间隔离开,避免后续主设置样式变更牵连弹窗。 */ .import-dialog-order { flex-wrap: wrap; gap: 6px; } .import-dialog-order-label { flex-shrink: 0; } .import-dialog-segmented { display: inline-flex; background: var(--color-bg); border: 1px solid var(--color-border); border-radius: 8px; padding: 2px; gap: 0; box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.04); flex-wrap: wrap; /* 选项可能因翻译 / 字号在窄窗口换行,flex-wrap 让 segmented 视觉风格不变 */ } .import-dialog-seg-btn { display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px; background: transparent; border: none; border-radius: 6px; font-size: var(--font-sm); color: var(--color-text-secondary); cursor: pointer; font-weight: 500; font-family: inherit; transition: background 0.15s var(--ease-soft), color 0.15s var(--ease-soft), box-shadow 0.2s var(--ease-soft), transform 0.08s var(--ease-soft); } .import-dialog-seg-btn:hover { color: var(--color-text); } .import-dialog-seg-btn:active { transform: scale(0.96); } .import-dialog-seg-btn:focus-visible { outline: none; box-shadow: 0 0 0 1px var(--color-accent), 0 0 0 3px var(--color-accent-bg); } .import-dialog-seg-btn.active { background: var(--color-accent-bg); color: var(--color-accent-hover); font-weight: 600; box-shadow: var(--shadow-sm), 0 0 0 1px var(--color-accent-bg-strong); } .import-dialog-count { font-size: var(--font-sm); color: var(--color-text-secondary); padding: 6px 10px; background: var(--color-bg-tertiary); border-radius: var(--radius-sm); border: 1px solid var(--color-border); /* 与 input-dialog-error 等高时的呼吸感 */ } .import-dialog-count-empty { color: var(--color-text-muted); } .import-dialog-count-error { color: var(--color-danger); border-color: var(--color-danger); } .import-dialog-footer { display: flex; justify-content: flex-end; gap: 8px; padding: 8px 14px; border-top: 1px solid var(--color-border); background: var(--color-bg-secondary); } /* 通用按钮禁用态(inputDialog 在空值/校验失败时使用)。 hover 时不要套用主按钮色:普通 .btn 的 base 是 transparent / text-secondary */ .btn:disabled, .btn-primary:disabled { cursor: not-allowed; opacity: 0.45; box-shadow: none; } .btn:disabled:hover { background: transparent; color: var(--color-text-secondary); box-shadow: none; } .btn-primary:disabled:hover { background: var(--color-accent); color: #ffffff; box-shadow: none; } /* ============================================ 设置对话框 ============================================ */ .settings-overlay .modal.settings-modal { width: 560px; max-width: 92vw; background: var(--color-bg-elevated); } .settings-header { display: flex; align-items: center; justify-content: space-between; padding: 16px 22px; border-bottom: 1px solid var(--color-border); font-size: var(--font-md); } .settings-header-left { display: flex; align-items: center; gap: 11px; color: var(--color-text); font-weight: 600; letter-spacing: -0.015em; } .settings-close { color: var(--color-text-muted); } .settings-close:hover { background: var(--color-bg-tertiary); color: var(--color-text); } .settings-body { max-height: 65vh; overflow-y: auto; padding: 0; white-space: normal; /* 覆盖 .modal-body 的 pre-wrap,避免缩进空白撑高布局 */ } .settings-section { padding: 24px 24px; border-bottom: 1px solid var(--color-border); } .settings-section:last-child { border-bottom: none; } .settings-section-header { margin-bottom: var(--space-4); } .settings-section-header:not(:has(.settings-section-desc)) { margin-bottom: var(--space-2); } .settings-section-title { /* 设置项的分组标题 —— 走 font-md-sm (14px),与正文 (font-sm=13px) 形成清晰但克制 的层级;font-md (15.5px) 留给真正的主标题(弹窗标题、页面标题) */ font-size: var(--font-md-sm); font-weight: 600; color: var(--color-text); margin-bottom: 4px; letter-spacing: -0.012em; } .settings-section-desc { /* 描述文字是辅助信息,走 font-xs —— 比 hint 略轻,但比 hint code 稍重 */ font-size: var(--font-xs); color: var(--color-text-secondary); line-height: 1.55; } .settings-row { margin-bottom: var(--space-3); } .settings-row:last-child { margin-bottom: 0; } .settings-label { display: flex; align-items: center; justify-content: space-between; font-size: var(--font-sm); color: var(--color-text-secondary); margin-bottom: var(--space-2); font-weight: 500; letter-spacing: -0.005em; } .settings-label-toggle { cursor: pointer; margin-bottom: var(--space-2); } .settings-input-group { display: flex; gap: var(--space-2); align-items: center; } .settings-input { flex: 1; height: var(--h-control-sm); padding: 0 var(--space-3); background: var(--color-bg); border: 1px solid var(--color-border); border-radius: var(--radius-md); font-size: var(--font-sm); font-family: var(--font-mono); color: var(--color-text); user-select: text; min-width: 0; letter-spacing: -0.003em; transition: border-color 0.15s, box-shadow 0.15s; } .settings-input:focus { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-bg); } .settings-hint { margin-top: 10px; /* helper text 是辅助信息,走 font-xs —— 12px 在 65vh 的对话框里仍清晰, 但不会与主设置项抢视觉重量 */ font-size: var(--font-xs); color: var(--color-text-secondary); line-height: 1.6; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; } .settings-hint code { font-family: var(--font-mono); font-size: var(--font-xs); padding: 2px 6px; background: var(--color-bg-tertiary); border-radius: var(--radius-xs); color: var(--color-text-secondary); user-select: text; margin: 0 4px; vertical-align: 1px; } .settings-link { background: none; border: none; color: var(--color-accent); font-size: var(--font-xs); cursor: pointer; padding: 0; text-decoration: none; font-family: inherit; margin-left: 6px; } .settings-link:hover { color: var(--color-accent-hover); text-decoration: underline; } /* 「恢复默认」点击后的瞬态反馈 —— 把按钮文本换成「✓ 已恢复」时同步换色, 给用户「点了生效了」的视觉确认;不靠底部的 toast(用户视线在按钮上时容易错过)。 用 success 色调区分默认态:accent 蓝是「可点击」,success 绿是「刚点过」。 文本+样式都在 JS 里 setTimeout 1.6s 后还原,这里只管样式。 */ .settings-link-applied { color: var(--color-success); cursor: default; text-decoration: none; } .settings-link-applied:hover { color: var(--color-success); text-decoration: none; } /* 分段控件(主题) */ .settings-segmented { display: inline-flex; background: var(--color-bg); border: 1px solid var(--color-border); border-radius: 8px; padding: 2px; gap: 0; box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.04); } /* 配色风格 6 个按钮可能换行 —— 单独给一个变体允许换行,并保持 segmented 视觉风格 */ .settings-segmented-color { flex-wrap: wrap; max-width: 100%; } /* 配色风格 swatch:每个按钮前的小色点 —— 用 600 色阶(偏深), 在浅色和深色 chip 背景上都能清晰可辨 */ .color-swatch { width: 10px; height: 10px; border-radius: 50%; display: inline-block; flex-shrink: 0; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12); } .seg-btn { display: inline-flex; align-items: center; gap: 6px; padding: 6px var(--space-3); background: transparent; border: none; border-radius: 6px; font-size: var(--font-sm); color: var(--color-text-secondary); cursor: pointer; font-weight: 500; transition: background 0.15s var(--ease-soft), color 0.15s var(--ease-soft), box-shadow 0.2s var(--ease-soft), transform 0.08s var(--ease-soft); font-family: inherit; } .seg-btn:hover { color: var(--color-text); } /* 按下微缩放 —— 与其它按钮 0.88~0.96 一致,键盘 Enter/Space 也走同一视觉 */ .seg-btn:active { transform: scale(0.96); } /* 键盘焦点环 —— 默认 UA 轮廓在 segmented 上会和选中态阴影打架, 显式换成 1px accent + 3px accent-bg 的复合 ring,颜色与选中态一致 */ .seg-btn:focus-visible { outline: none; box-shadow: 0 0 0 1px var(--color-accent), 0 0 0 3px var(--color-accent-bg); } /* 选中 + 键盘聚焦:用更深的 bg-strong 作为外圈,避免和选中态自带的 1px 描边重影 */ .seg-btn.active:focus-visible { box-shadow: 0 0 0 1px var(--color-accent), 0 0 0 3px var(--color-accent-bg-strong); } /* 选中态 —— 与应用里其它 .active 模式(.btn-icon.active / .command-palette-item.active / .category-item.active 等)保持一致:淡 accent 底 + accent 文字 + 加粗到 600。 原方案用 --color-bg-elevated 中性白底,浅色主题下与未选中态相差仅 ~5%, 看不出「选了哪个」;换成 accent 色系后形成 「容器(--color-bg) → 未选(透明) → 选中(accent 淡底)」三层视觉层级。 浅色主题下再叠一层 accent-bg-strong 的弱描边,提升辨识度。 */ .seg-btn.active { background: var(--color-accent-bg); color: var(--color-accent-hover); font-weight: 600; box-shadow: var(--shadow-sm), 0 0 0 1px var(--color-accent-bg-strong); } /* 关于区 */ .settings-about { display: flex; flex-direction: column; gap: 1px; background: var(--color-bg); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: var(--space-2) var(--space-3); } .settings-about-row { display: flex; justify-content: space-between; align-items: center; font-size: var(--font-sm); padding: 5px 0; } .settings-about-key { color: var(--color-text-muted); } .settings-about-val { color: var(--color-text); font-family: var(--font-mono); font-size: var(--font-xs); padding: 2px 8px; background: var(--color-bg-tertiary); border-radius: var(--radius-xs); } /* 关于区里的可点击链接 —— 视觉上是值(带背景),行为是可点; 鼠标悬停时换色 + 下划线提示。颜色变量与其它设置链接保持一致。 */ .settings-about-link { cursor: pointer; text-decoration: none; transition: color 0.12s ease; } .settings-about-link:hover { color: var(--color-accent); text-decoration: underline; } .settings-about-link:active { opacity: 0.7; } /* 紧凑模式已移除 */ /* ============================================ 动画 ============================================ */ @keyframes task-fade-in { from { opacity: 0; transform: translateY(-2px); } to { opacity: 1; transform: translateY(0); } } @keyframes check-pop { 0% { transform: rotate(45deg) scale(0.4); opacity: 0; } 100% { transform: rotate(45deg) scale(1); opacity: 1; } } /* 任务完成过渡已合并到主 .task-item 定义 */ /* 右键菜单禁用项 */ .context-menu-disabled { color: var(--color-text-faint) !important; cursor: not-allowed !important; pointer-events: none; opacity: 0.5; } .context-menu-disabled:hover { background: transparent !important; color: var(--color-text-faint) !important; } /* ============================================ 字号层级 — 小 - 在 medium 基础上再缩 ~3-5%,给喜欢紧凑字号的留一个档位。 - 缩幅收窄(原来 6-8%):避免 2xs 跌到 10px 以下看不清。 ============================================ */ [data-font-size="small"] { --font-2xs: 10px; --font-xs: 11.5px; --font-sm: 12.5px; --font-md-sm: 13.5px; --font-base: 14px; --font-md: 15px; --font-lg: 17px; --font-xl: 20px; --font-2xl: 24px; } /* ============================================ 字号层级 — 大 - 在 medium 基础上放大 ~6-8%,给老花 / 大屏 / 高分辨率留档 - 比原来 ~12% 更克制:medium 已经 +0.5,再叠加太多大档就过头了 ============================================ */ [data-font-size="large"] { --font-2xs: 11.5px; --font-xs: 13px; --font-sm: 14px; --font-md-sm: 15px; --font-base: 15.5px; --font-md: 16.5px; --font-lg: 19px; --font-xl: 22.5px; --font-2xl: 26.5px; } /* 硬编码字号 → 使用变量以响应层级切换。 语义分组(从大到小): - 标题档(font-xl/md/md-sm):弹窗标题、页标题、分组标题 - 正文档(font-sm):主交互文本(任务、按钮、菜单、表单) - 辅助档(font-xs):hint、描述、状态栏、空提示的副信息 - 极小档(font-2xs):svg、kbd 旁的微标签 注:菜单 / 命令面板 / toast 这几项的基础定义已经是 var(--font-sm), 它们只需「响应层级切换」即可,不需要额外再叠一层 !important 强行放大; 之前用 var(--font-md) 强行盖掉 font-sm 会让右键菜单显得过大。 */ .modal-header, .settings-header, .command-palette-input { font-size: var(--font-md) !important; } /* 设置项分组标题 —— 比弹窗主标题小一档 */ .settings-section-title { font-size: var(--font-md-sm) !important; } .modal-body, .input-dialog-message, .input-dialog-input { font-size: var(--font-base) !important; } .category-name, .category-item, .task-text, .task-input, .search-input, .btn, .seg-btn, .settings-label, .settings-about-row, .filter-chip, .btn-primary, .btn-add-subcategory, .category-item.subcategory .category-name, .task-edit-input, .settings-input, .empty-hint, .command-palette-empty { font-size: var(--font-sm) !important; } .task-category-title { font-size: var(--font-xl) !important; } /* 辅助/次要文本 —— 状态栏、hint、进度数字、搜索快捷键标签 */ .task-progress-text, .search-shortcut, .settings-section-desc, .command-palette-item-hint, .status-meta, .status-text, .file-path-chip, .file-path-hint, .settings-hint, .settings-hint code, .settings-link, .settings-about-val { font-size: var(--font-xs) !important; } .subcategory-action-btn, .more-menu-item > svg, .task-action-btn svg { font-size: var(--font-2xs) !important; } /* 空状态标题 —— 弹窗主标题小一档、但比 hint 大一档 */ .empty-title { font-size: var(--font-md) !important; } .app-title { font-size: var(--font-md) !important; } .empty-hint kbd { font-size: var(--font-xs) !important; } .filter-chip .chip-icon { font-size: 10px !important; } .empty-icon svg { width: 26px !important; height: 26px !important; } /* 字号层级行高微调 */ [data-font-size="small"] body, body { line-height: 1.55; } [data-font-size="large"] body { line-height: 1.6; } /* ============================================ 响应式 - 思路:侧栏宽度已用 clamp() 做成连续过渡,下面只剩 3 个真正影响布局的断点 - ≥1400 大屏加留白;≤900 中宽度开始紧凑;≤620 进图标模式 ============================================ */ /* 大窗口:留更多空白 */ @media (min-width: 1400px) { .task-header, .task-input-wrapper { padding-left: var(--space-8); padding-right: var(--space-8); } .toolbar { padding-left: var(--space-4); } } /* 中等窗口(约 ≤900px):紧凑模式 - 工具栏 padding 收紧、gap 减小,给中宽窗口留更多有效空间 - 标题保持显示(frame:false 后是唯一的窗口标识,不再为搜索让位) - 子分类缩进保持默认,不在中宽度打扰视觉 - file-path-chip 不再设 max-width:状态栏剩下的空间自然决定 chip 宽度, 路径真正放不下时由 .file-path-text 内部 ellipsis 处理。 */ @media (max-width: 900px) { .toolbar { padding: 0 var(--space-3); gap: var(--space-2); } /* 中宽度窗口不再隐藏标题文字:frame:false 接管 OS 标题栏后, 标题是窗口唯一的"标题"标识,必须始终可见。 极窄宽度(≤620px)下再统一交给 flex 自然收缩。 */ .task-header, .task-input-wrapper { padding-left: var(--space-4); padding-right: var(--space-4); } } /* 较窄窗口(≤780px):再给任务面板腾点空间 */ @media (max-width: 780px) { .search-shortcut { display: none; } .task-progress-text { display: none; } } /* 极窄窗口:仅图标(≤620px 侧边栏收起为图标栏,内容区留给任务列表) */ @media (max-width: 620px) { /* 极窄窗口没空间再放提示文字,让 chip 自己负责(hover 看 title) */ .file-path-hint { display: none; } /* .app-title .logo 在 ≤620px 不再缩到更小:全局 .logo 已是 24px,再缩会破坏与 标题字号的视觉对齐(16px 文字配 20px logo 在窄窗口里更难受)。 */ .layout { grid-template-columns: 60px 1fr; } .sidebar { align-items: center; } .sidebar-header { padding: var(--space-3) 0 var(--space-1); justify-content: center; } .sidebar-title { display: none; } .category-list { padding: 0 var(--space-1); } .category-item { padding: 10px 0; justify-content: center; min-height: 38px; } .category-name { display: none; } .category-icon { margin: 0; width: 18px; height: 18px; display: inline-flex; align-items: center; justify-content: center; } .category-icon svg { width: 100%; height: 100%; } /* 图标模式下:取消子分类缩进,让图标垂直对齐父分类那一列 */ .subcategory-list { margin-left: 0; padding-left: 0; } .category-item.subcategory { padding-left: 0; } /* 图标模式下:「+ 新建子分类」只保留图标,居中 */ .btn-add-subcategory { justify-content: center; padding: 0; height: 34px; } .btn-add-subcategory .btn-text { display: none; } /* 图标模式下隐藏子分类的编辑按钮(重命名/删除),空间不够也没有意义 */ .subcategory-actions { display: none; } /* 隐藏空分类提示文字 */ .subcategory-empty { display: none; } /* 隐藏搜索快捷键提示,腾出空间给输入框 */ .search-shortcut { display: none; } /* 隐藏标题旁的进度文字,省空间 */ .task-progress-text { display: none; } /* 收紧任务面板水平 padding */ .task-header, .task-input-wrapper { padding-left: var(--space-3); padding-right: var(--space-3); } .task-item { padding: 8px var(--space-3); } .task-item::before { inset: 0 var(--space-2); } /* 状态栏收紧间距 */ .statusbar { gap: var(--space-2); padding: 0 var(--space-3); } .status-text { flex-shrink: 1; } } /* ============================================ 选择模式专用圆形选择器(与完成 checkbox 视觉完全独立) ============================================ 普通模式下复选框 = 完成状态(方框 + 完成勾)。 选择模式下需要明确告诉用户「这是选中,不是完成」, 所以用圆形 + accent 实心填充,形态和颜色都和 checkbox 拉开距离。 已完成状态仍由 .task-item.completed 的 line-through + 透明度承载。 */ .task-selection-wrapper { display: flex; align-items: center; justify-content: center; cursor: pointer; flex-shrink: 0; width: 24px; height: 24px; border-radius: 7px; transition: background 0.15s var(--ease-soft), transform 0.12s var(--ease-spring); } .task-selection-wrapper:hover { background: var(--color-bg-tertiary); } .task-selection-wrapper:active { transform: scale(0.88); } .task-selection-circle { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; border: 1.6px solid var(--color-text-faint); border-radius: 50%; /* 圆形 —— 与 checkbox 的圆角矩形区分 */ background: transparent; position: relative; transition: background 0.2s var(--ease-soft), border-color 0.2s var(--ease-soft), transform 0.18s var(--ease-spring), box-shadow 0.2s var(--ease-soft); } .task-selection-wrapper:hover .task-selection-circle { border-color: var(--color-text-muted); } .task-selection-circle.selected { background: var(--color-accent); border-color: var(--color-accent); box-shadow: 0 2px 6px var(--color-accent-glow); } .task-selection-circle.selected svg { animation: check-pop 0.22s var(--ease-spring); } /* 选择模式下:整行变成「点这里切换选中」的语义。 隐藏 actions(编辑/删除),但 ⭐/▶ 改成只读状态指示器显示出来 — 这样用户在批量选择时还能看到「哪些任务已经是当前/重要」,方便判断要不要继续操作。 只读样式走 .task-readonly-indicator 修饰类;按钮仍用原 class 是为了让 CSS 上 important/current 的填色规则可以共用。 */ .task-item.select-mode .task-actions { display: none; } /* 只读指示器:彻底屏蔽交互 + 视觉上弱化,让用户一眼区分「这是状态显示,不是按钮」 */ .task-item.select-mode .task-readonly-indicator { cursor: default; pointer-events: none; /* 不响应 hover / click,避免和行点击选中打架 */ } /* hover 反馈在只读态下应静默 —— 否则会和「指针在这里不能点」相矛盾 */ .task-item.select-mode .task-readonly-indicator:hover { background: transparent; color: inherit; transform: none; } /* 已标记的任务:颜色清晰,让「这批里有几个标了 ⭐/▶」一眼可见 */ .task-item.select-mode.important .task-readonly-indicator.task-star-btn, .task-item.select-mode.current .task-readonly-indicator.task-current-btn { opacity: 1; } /* 未标记的任务:留个非常淡的轮廓,让用户感知到「这里本来有标记,但这条没标」, 同时不抢已标记任务的视觉权重。 */ .task-item.select-mode:not(.important) .task-readonly-indicator.task-star-btn, .task-item.select-mode:not(.current) .task-readonly-indicator.task-current-btn { opacity: 0.32; } /* 选择模式全局开关:body[data-selection-mode="true"] */ body[data-selection-mode="true"] .task-item { cursor: pointer; } /* 选择模式下隐藏输入框 —— 浮栏会浮在底部,输入框会和「移到分类」等按钮撞位置。 同时语义上选择模式下不需要新增任务(用户焦点在「整理」不在「添加」)。 */ body[data-selection-mode="true"] .task-input-wrapper { display: none; } /* 选择模式下:右下角浮栏会盖住最底下的几行任务,给任务列表底部预留 padding 让最后一行能滚到浮栏之上。浮栏最大高度约 280px,再加 24px 底部偏移 = ~310px。 注意:task-list 是 task-panel 内部的滚动容器,所以 padding-bottom 要加在 task-list 上而不是 task-panel 上,避免整个面板出现空隙。 */ body[data-selection-mode="true"] .task-list { padding-bottom: 320px; } /* 选择模式下的「已选」高亮:复用 .task-item.selected::before 的视觉 (accent 浅底 + 描边 + 光晕),不需要单独写。 */ /* 已选任务的行高亮 + 内容粗细微调,让选中态更易扫视 */ .task-item.select-mode.selected { font-weight: 500; } /* ============================================ 批量操作浮栏(.batch-action-bar) ============================================ 视觉:固定在底部居中,横向排布、允许换行(通常落成两行), 与 .more-menu 同款设计语言(elevated bg + 描边 + 阴影 + 圆角)。 结构(宽度够时两行): ┌───────────────────────────────────────────────┐ │ 已选 3 项 ✕ │ 移到分类 置顶 置底 │ 标重要 取消重要 │ │ 标当前 取消当前 │ 完成 移到回收站 │ └───────────────────────────────────────────────┘ 之所以放底部横向:早先的右下角纵向浮栏会盖住任务列表右侧一整列, 遮挡明显;底部两行只占屏幕最下方一小条,扫视和点击都更顺。 */ .batch-action-bar { position: fixed; /* 底部 statusbar 高 30px + 8px gap = 浮栏下沿不能进入此 38px 区。 */ bottom: calc(var(--h-statusbar) + 8px); /* 落在主区(侧栏右侧),不要悬浮在左侧栏上。 侧栏宽度两档(与 .layout 同款): - 默认:clamp(245px, 25%, 280px) - ≤620px:折叠成 60px(见下方 @media) 取两档较大值再 +8px = 浮栏左缘。 */ left: calc(max(60px, clamp(245px, 25%, 280px)) + 8px); right: 8px; transform: none; z-index: calc(var(--z-menu) - 1); /* 比右键菜单稍低,避免遮挡菜单 */ background: var(--color-bg-elevated); border: 1px solid var(--color-border-strong); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); font-size: var(--font-sm); -webkit-app-region: no-drag; display: flex; } /* inner:真正承载排版 + 入场动画 —— 浮栏水平居中由外层的 left/right 决定, 这里只做 translateY,不会跟 translateX(-50%) 冲突 */ .batch-action-bar-inner { display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: center; /* 1100px viewport(默认测试尺寸)下 12 个按钮约 ~700px, 留 720px 让大多数常见视口(≥900px)单行容纳; <900px 自动 wrap 成 2 行(设计已校验 wrap 不破坏语义)。 */ gap: 1px 3px; padding: 5px 6px; width: min(65vw, 720px); max-width: calc(100vw - max(60px, clamp(245px, 25%, 280px)) - 16px); box-sizing: border-box; margin: 0 auto; animation: batch-bar-in 0.18s var(--ease-out); } @keyframes batch-bar-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } /* header:计数 + 关闭按钮 —— 横排时与各组用 1px 竖线分开 */ .batch-bar-header { display: inline-flex; align-items: center; gap: 6px; padding: 0 8px 0 4px; margin-right: 2px; border-right: 1px solid var(--color-border-muted); } /* 「全选/取消全选」toggle 按钮 —— 紧贴「已选 N 项」旁边,复用 .batch-action 风格 但去掉自身的左 padding,让按钮贴 count 文字更紧凑,与紧邻的 ✕ 按钮视觉对称。 同时给它一个与「执行类」按钮可区分的微调:不参与 danger 配色(不删任务)。 */ .batch-bar-header .batch-action-select-all { padding-left: 4px; } .batch-count { display: inline-flex; align-items: center; color: var(--color-text-muted); font-size: var(--font-xs); font-weight: 600; letter-spacing: 0.02em; white-space: nowrap; flex: 1; min-width: 0; } /* ✕ 关闭按钮:紧贴 header 右上角,方形小图标按钮 */ .batch-action-close { width: 22px; height: 22px; padding: 0; display: inline-flex; align-items: center; justify-content: center; border-radius: 5px; background: none; border: none; color: var(--color-text-muted); cursor: pointer; flex-shrink: 0; transition: background 0.1s var(--ease-soft), color 0.1s var(--ease-soft), transform 0.08s var(--ease-soft); } .batch-action-close:hover { background: var(--color-bg-tertiary); color: var(--color-text); } .batch-action-close:active { transform: scale(0.88); } /* 每个组:横向并排 + 隐式分隔(容器自身不画线,靠相邻组的 padding 形成视觉断点) */ .batch-bar-group { display: inline-flex; align-items: center; gap: 1px; padding: 0; } .batch-action { display: inline-flex; align-items: center; gap: 4px; padding: 3px 7px; border-radius: 5px; background: none; border: none; color: var(--color-text); font-family: inherit; font-size: 12px; font-weight: 500; cursor: pointer; text-align: left; white-space: nowrap; transition: background 0.1s, color 0.1s; } .batch-action > svg { width: 14px; height: 14px; flex-shrink: 0; color: var(--color-text-muted); transition: color 0.1s; } .batch-action > span { flex: 1; min-width: 0; } .batch-action:hover:not(:disabled) { background: var(--color-accent-bg); color: var(--color-accent-hover); } .batch-action:hover:not(:disabled) > svg { color: var(--color-accent-hover); } .batch-action:active:not(:disabled) { transform: scale(0.98); } .batch-action:focus-visible { outline: none; box-shadow: 0 0 0 1px var(--color-accent), 0 0 0 3px var(--color-accent-bg); } /* 禁用态:灰显 + 不可点 —— 必须用 :disabled 而非 [disabled] 才能在某些浏览器里拿到正确的「不可点」语义 */ .batch-action:disabled { opacity: 0.35; cursor: not-allowed; /* hover 时不变色 —— 防止 disabled 按钮看起来像可点 */ } .batch-action:disabled > svg { color: var(--color-text-muted); } /* 危险按钮:移到回收站 —— 借用 --color-danger-bg */ .batch-action.batch-action-danger:hover:not(:disabled) { background: var(--color-danger-bg); color: var(--color-danger); } .batch-action.batch-action-danger:hover:not(:disabled) > svg { color: var(--color-danger); } /* 组间分隔:除了 header 的右边线,给除第一个外的组加一道左边线 —— 横排时清晰 */ .batch-bar-group + .batch-bar-group { border-left: 1px solid var(--color-border-muted); padding-left: 6px; margin-left: 2px; } /* 小屏:仅收紧 padding / 字号 —— 浮栏仍让位侧栏(无论侧栏是 245–280px 还是收成 60px)。 之前用 ≤720px 改 left:8px / right:8px 是错的:620–720px 区间侧栏还是 245–280px, 浮栏会盖过去。改用 max() 在 base 规则里统一处理浮栏左缘 —— 这里只剩视觉压缩。 */ @media (max-width: 720px) { .batch-action-bar { /* 紧贴 statusbar 上方 —— 大屏是 +8px gap,小屏再压 4px 让出空间 */ bottom: calc(var(--h-statusbar) + 4px); } .batch-action-bar-inner { padding: 5px 6px; gap: 2px 4px; max-width: none; } .batch-action { padding: 5px 7px; gap: 5px; font-size: 12px; } .batch-count { font-size: 11px; } } /* 极窄屏(≤620px 侧栏已折成图标栏,主区内容更宽了,但浮栏按钮总数不变): 进一步压缩 —— 隐藏文字标签,只留图标,tooltip 保留全名。 否则 4 个组会塞成 3 行,盖住底部状态栏。 */ @media (max-width: 620px) { .batch-action-bar-inner { gap: 1px 2px; padding: 4px 5px; } .batch-action { padding: 6px; gap: 0; } /* span 里是按钮标签, 始终保留 */ .batch-action > span { display: none; } /* 但 header 里的关闭按钮文字其实没有,只有 SVG;count 文字要保留 */ .batch-bar-header .batch-count { display: inline; } .batch-action-close { width: 22px; height: 22px; padding: 0; } } /* ============================================ 选择模式入口按钮(btn-batch)按下态 ============================================ */ /* 当 selectionMode 为 true 时给按钮一个更明显的「激活」外观 */ #btn-batch[aria-pressed="true"] .filter-chip { background: var(--color-accent-bg-strong); color: var(--color-accent-hover); } /* ============================================ 「重要 / 当前」图标按钮的 SVG 颜色与 select-mode 兼容 ============================================ 选择模式下 ⭐/▶ 已改为 .task-readonly-indicator 显示(见上方 select-mode 块), SVG 颜色仍由 .task-item.important / .task-item.current 触发的 fill 与 color 控制,这里无需重复声明 —— 只是兜底:如果未来调整读法,留个指针。 */ /* 减少动画(无障碍) */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } } /* ============================================ 冲突解决对话框(v3.4 任务粒度合并) ============================================ 布局:上中下三段 - .conflict-dialog-summary: 顶部统计(X 处差异 / 文件新加 N / 本地新加 N / 两边都改 N) - .conflict-dialog-grid-header + .conflict-dialog-body: 三列内容(文件版本 / 选择器 / 本地版本) - .conflict-dialog-footer: 底部四个按钮(取消 / 全部用本地 / 全部用文件 / 应用合并) 配色全部走现有 token(--color-warning / --color-success / --color-accent / --color-danger), AA 对比度由 token 保证。 */ .conflict-dialog-overlay .conflict-dialog-modal { min-width: 720px; max-width: 980px; max-height: 80vh; display: flex; flex-direction: column; } .conflict-dialog-header { font-weight: 600; /* 标题 + 右侧行级 diff 按钮要两列布局 —— 默认 .modal-header 是块级,靠左堆叠 */ display: flex; align-items: center; justify-content: space-between; gap: 12px; } .conflict-dialog-title-text { /* 占满剩余空间,把按钮推到右侧 */ flex: 1; min-width: 0; } .conflict-diff-preview-btn { /* 轻量文字按钮 —— 覆盖 .btn 默认的 --h-control 高度,让它紧凑贴在标题旁 */ height: auto; font-size: 12px; font-weight: 500; padding: 4px 10px; border-radius: 4px; background: transparent; border: 1px solid var(--color-border); color: var(--color-accent-hover); cursor: pointer; flex-shrink: 0; transition: background 0.15s, border-color 0.15s; } .conflict-diff-preview-btn:hover { background: var(--color-accent-bg); border-color: var(--color-accent); } .conflict-diff-preview-btn:focus-visible { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-bg); } .conflict-dialog-summary { padding: 12px 24px 16px; border-bottom: 1px solid var(--color-border); display: flex; flex-wrap: wrap; gap: 12px; } .conflict-stat { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 12px; background: var(--color-bg-elevated); color: var(--color-text-secondary); font-size: 12px; font-weight: 500; } .conflict-stat-total { background: var(--color-accent-bg); color: var(--color-accent-hover); } .conflict-stat-conflict { background: var(--color-warning-bg); color: var(--color-warning); } .conflict-dialog-grid-header { display: grid; grid-template-columns: 1fr 200px 1fr; gap: 12px; padding: 12px 24px; background: var(--color-bg-elevated); border-bottom: 1px solid var(--color-border); font-size: 12px; font-weight: 600; color: var(--color-text-secondary); } .conflict-dialog-body { flex: 1; overflow-y: auto; padding: 12px 24px; } .conflict-empty { padding: 32px; text-align: center; color: var(--color-text-secondary); } .conflict-row { display: grid; grid-template-columns: 1fr 200px 1fr; gap: 12px; padding: 8px 0; align-items: center; border-bottom: 1px solid var(--color-border); } .conflict-row:last-child { border-bottom: none; } .conflict-cell { min-width: 0; padding: 6px 10px; border-radius: 6px; font-size: 13px; line-height: 1.5; word-break: break-word; } .conflict-cell-file { background: var(--color-warning-bg); color: var(--color-text); } .conflict-cell-mine { background: var(--color-accent-bg); color: var(--color-text); } .conflict-cell-chooser { display: flex; align-items: center; justify-content: center; } .conflict-task-side-file, .conflict-task-side-mine { /* 行级标签:仅占位用,文本颜色由父元素 .conflict-cell-* 控制 */ } .conflict-task-empty { color: var(--color-text-tertiary); font-style: italic; font-size: 12px; } /* 任务原文里的 [原分类:xxx] 标记 —— 灰色小字,视觉权重低于 checkbox 和 ▶/⭐ 标记。 在 COMPLETED / TRASH 分类里才有意义(普通子分类 task.originalCategory 通常为 null)。 见 markdown-writer.js:formatTaskLine 的 showOriginalCategory 判断 —— 这里是纯展示。 */ .conflict-task-orig { display: inline-block; margin-left: 2px; padding: 1px 6px; border-radius: 3px; background: var(--color-bg); color: var(--color-text-tertiary); font-size: 11px; font-weight: 500; letter-spacing: 0; /* 与 [▶] [⭐] 标记视觉区分 —— 这条是"任务来源追溯"元数据,不是任务本身状态 */ border: 1px dashed var(--color-border); } .conflict-choice { width: 100%; padding: 6px 8px; font-size: 12px; border: 1px solid var(--color-border); border-radius: 4px; background: var(--color-bg); color: var(--color-text); } .conflict-choice:focus-visible { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-bg); } .conflict-dialog-footer { /* 复用 .modal-footer 的 flex 布局 —— 这里四个按钮用 gap 隔开 */ gap: 8px; } /* ============================================ 行级 diff 预览(v3.4+ 辅助视图) ============================================ */ .diff-preview-overlay .diff-preview-modal { min-width: 720px; max-width: 1100px; max-height: 80vh; display: flex; flex-direction: column; } .diff-preview-legend { padding: 8px 24px; display: flex; gap: 16px; font-size: 12px; color: var(--color-text-secondary); border-bottom: 1px solid var(--color-border); } .diff-line-add-legend::before { content: '+'; margin-right: 4px; color: var(--color-success); font-weight: 600; } .diff-line-remove-legend::before { content: '-'; margin-right: 4px; color: var(--color-danger); font-weight: 600; } .diff-preview-body { flex: 1; overflow: auto; padding: 12px 24px; font-family: var(--font-mono); font-size: 12px; line-height: 1.5; background: var(--color-bg-elevated); } .diff-line { display: flex; padding: 1px 0; } .diff-line-prefix { flex: 0 0 24px; color: var(--color-text-tertiary); } .diff-line-text { flex: 1; white-space: pre-wrap; word-break: break-word; } .diff-line-add { background: var(--color-success-bg); } .diff-line-add .diff-line-prefix { color: var(--color-success); } .diff-line-remove { background: var(--color-danger-bg); } .diff-line-remove .diff-line-prefix { color: var(--color-danger); } .diff-line-equal { color: var(--color-text-secondary); } .diff-empty { padding: 24px; text-align: center; color: var(--color-text-secondary); } /* 打印(隐藏不必要元素) */ @media print { .toolbar, .statusbar, .btn-add-subcategory, .toast-container, .context-menu { display: none !important; } }