d717f7dd by 柴进

docs: UI 重设计方案 — Apple 浅深双主题

- 设计令牌为单一真相源(theme.py 中 Python dict)
- Apple Blue 主色统一 macOS/Windows
- 监听 styleHints colorSchemeChanged 实现自动跟随系统
- 范围:外观 + 关键 layout 修复(按钮分级、卡片化、sidebar 头部)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5f2a43f7
...@@ -27,3 +27,10 @@ images/ ...@@ -27,3 +27,10 @@ images/
27 # OS 27 # OS
28 .DS_Store 28 .DS_Store
29 Thumbs.db 29 Thumbs.db
30
31 # Brainstorming / superpowers session artifacts
32 .superpowers/
33
34 # Local config / screenshots
35 config_no_db.json
36 ScreenShot_*.png
......
1 # UI 重设计:Apple 高级浅色主题 + 浅深双模式
2
3 **日期**:2026-05-09
4 **分支**`feat/ui-redesign-apple-theme`
5 **状态**:设计已定,实施进行中
6
7 ---
8
9 ## 1. 背景与目标
10
11 ### 1.1 问题陈述
12 当前 UI 是 PySide6 默认皮肤 + 65+ 处零散 inline `setStyleSheet`(image_generator.py:65 处、task_queue.py:4 处),没有统一设计语言。具体表现:
13
14 - 全灰白默认 Qt 风格,缺现代感
15 - "生成图片"和"下载图片"主次按钮视觉权重一样
16 - GroupBox 边框过重(Win95 观感)
17 - "任务队列"sidebar 标题在视觉上飘到主区 tab bar 旁边
18 - 字号/间距/圆角不统一
19 - macOS 系统切换深色主题时整个应用观感断裂
20
21 ### 1.2 目标
22 1. **统一设计语言**:Apple Human Interface Guidelines 浅色风格 + Apple Blue (#0071E3) 主色
23 2. **跨平台一致**:macOS 和 Windows 11 视觉一致(用户原话:"统一两端的设计语言")
24 3. **支持系统深浅主题自动切换**:跟随 `QGuiApplication.styleHints().colorScheme()`
25 4. **修复关键 layout 问题**:按钮主次分级、sidebar 头部、卡片化 GroupBox
26 5. **保持业务逻辑不变**:纯视觉层重构,不动生成 / 队列 / 历史功能
27
28 ### 1.3 非目标(YAGNI)
29 - 不引第三方 UI 库(qfluentwidgets、qt-material 等)
30 - 不重做信息架构(tabs、表单字段)
31 - 不增加新功能
32 - 不做主题色自定义(用户改色等高级功能)
33
34 ---
35
36 ## 2. 已确认的设计决策
37
38 | 决策点 | 结果 | 用户原话/理由 |
39 |---|---|---|
40 | 设计方向 | C · Apple 高级感浅色 | "可以统一 MacOS 与 Windows 的设计语言" |
41 | 主色调 | A · Apple Blue `#0071E3` | "跟我们的品牌元素的颜色契合" |
42 | 重写范围 | B · 外观 + 关键 layout 修复 | (选择推荐项)|
43 | 深色模式 | 完整双套主题 | "macos 会让软件跟随时间自由转换深浅主题,不做就巨丑巨难看" |
44 | 二级窗口 | 一并重做(登录 + 所有 dialog)| (选择推荐项)|
45 | 实现路径 | B · 设计令牌生成器(Python tokens → build_qss)| Python dict 比双 .qss 文件不容易飘,dark mode 90% 是规则反转 |
46
47 ---
48
49 ## 3. 设计令牌(Design Tokens)
50
51 所有数值在 `theme.py` 中以 Python dict 定义,单一真相源。
52
53 ### 3.1 颜色 — 浅色(Light)
54
55 ```
56 # 表面层级
57 bg_canvas #fbfbfd 主窗口背景(极浅冷灰)
58 bg_surface #ffffff 卡片 / dialog 背景
59 bg_elevated #ffffff 带投影的浮起容器
60 bg_subtle #f4f4f7 GroupBox / 输入框 hover 底色
61
62 # 文字层级
63 text_primary #1d1d1f 主文字
64 text_secondary #6e6e73 次要文字 / 标签
65 text_tertiary #86868b 占位符 / 禁用文字
66 text_on_accent #ffffff 主按钮上的白字
67
68 # 边框 / 分隔
69 border_default rgba(0,0,0,0.10) 默认边框(投影替代式)
70 border_strong rgba(0,0,0,0.15) 焦点 / 选中
71 divider rgba(0,0,0,0.06) 分隔线
72
73 # 主色
74 accent #0071e3 Apple Blue
75 accent_hover #0077ed 悬停(略亮)
76 accent_pressed #0062c4 按下
77 accent_subtle rgba(0,113,227,0.08) 主色淡背景(hover 卡片等)
78
79 # 状态色
80 success #34c759 成功 / 在线
81 warning #ff9500 警告
82 danger #ff3b30 危险 / 错误
83 ```
84
85 ### 3.2 颜色 — 深色(Dark)
86
87 ```
88 bg_canvas #1a1a1c
89 bg_surface #2c2c2e
90 bg_elevated #3a3a3c
91 bg_subtle #242426
92
93 text_primary #ffffff
94 text_secondary #98989d
95 text_tertiary #6e6e73
96 text_on_accent #ffffff
97
98 border_default rgba(255,255,255,0.10)
99 border_strong rgba(255,255,255,0.18)
100 divider rgba(255,255,255,0.06)
101
102 accent #0a84ff (Dark mode 下 Apple Blue 略亮)
103 accent_hover #1d8cff
104 accent_pressed #006edc
105 accent_subtle rgba(10,132,255,0.16)
106
107 success #30d158
108 warning #ff9f0a
109 danger #ff453a
110 ```
111
112 **设计原则**:浅深对照不是简单灰阶反转,而是参考 Apple SF Symbols / SwiftUI 的语义对照表。`bg_canvas``bg_surface` 低半级(浅色:surface 比 canvas 亮;深色:surface 比 canvas 亮)—— 卡片永远比底板"凸"。
113
114 ### 3.3 间距与尺寸
115
116 ```
117 # 圆角
118 radius_sm 4px 标签 / chip
119 radius_md 8px 输入框 / 普通按钮 / 卡片
120 radius_lg 12px 大卡片 / dialog
121 radius_pill 980px 胶囊主按钮(Apple HIG 标志性)
122
123 # 间距(4px grid)
124 space_1 4px
125 space_2 8px
126 space_3 12px
127 space_4 16px
128 space_5 20px
129 space_6 24px
130 space_8 32px
131
132 # 字号(pt - Qt 跨平台)
133 font_xs 10pt 提示 / 标签
134 font_sm 11pt 次要文字 / 按钮文字
135 font_base 12pt 正文(Windows)/ 13pt(macOS, 系统检测加 1)
136 font_lg 14pt 小标题
137 font_xl 17pt 主标题
138
139 # 控件高度
140 control_h_sm 28px 小按钮 / 紧凑输入框
141 control_h_md 32px 常规按钮 / 输入框(默认)
142 control_h_lg 40px 主操作按钮 / 大输入框
143 ```
144
145 ### 3.4 字体栈
146
147 ```
148 font_family = "-apple-system, 'SF Pro Text', 'PingFang SC',
149 'Microsoft YaHei UI', 'Segoe UI Variable',
150 'Segoe UI', sans-serif"
151 ```
152
153 策略:让操作系统选最合适的字体——macOS 走 SF Pro / 苹方,Windows 走 Segoe UI Variable / 微软雅黑。Qt 会自动 fallback。
154
155 ---
156
157 ## 4. 模块架构
158
159 ### 4.1 `theme.py`(新文件,约 300 行)
160
161 ```python
162 # 公共 API
163 def apply_theme(app: QApplication) -> ThemeManager
164 """应用主题。在 main() 创建 QApplication 后立即调用。返回 ThemeManager
165 实例(持有引用以保持信号连接)。"""
166
167 class ThemeManager(QObject):
168 theme_changed = Signal(str) # 'light' | 'dark'
169 def current_mode() -> str
170 def force_mode(mode: str | None) # None = 跟随系统
171 # 内部:监听 QGuiApplication.styleHints().colorSchemeChanged
172
173 # 令牌
174 TOKENS_LIGHT: dict[str, str]
175 TOKENS_DARK: dict[str, str]
176
177 def build_qss(mode: str) -> str
178 """根据 mode(light/dark)生成完整 QSS 字符串。"""
179 ```
180
181 ### 4.2 集成点
182
183 **main 入口** `image_generator.py:4940`
184 ```python
185 app = QApplication(sys.argv)
186 from theme import apply_theme
187 theme_manager = apply_theme(app) # 必须在创建任何窗口前
188 ```
189
190 **preflight 入口** `preflight.py:215, 218, 256, 258`:同上
191
192 **所有 widget**:通过 `setObjectName()``setProperty()` 来命中 QSS 选择器,**禁止再用 inline setStyleSheet**(除少数语义性状态色,见 §6.2)。
193
194 ### 4.3 数据流
195
196 ```
197 系统切换深色 → Qt 触发 colorSchemeChanged
198 → ThemeManager 接收信号
199 → build_qss(new_mode)
200 → app.setStyleSheet(new_qss)
201 → 所有 widget 自动重绘(Qt 内置)
202 → 发射 theme_changed 信号(业务代码可监听,目前不需要)
203 ```
204
205 ---
206
207 ## 5. 组件样式映射
208
209 ### 5.1 按钮分级
210
211 ```
212 QPushButton 默认次按钮(白底蓝边/字)
213 QPushButton[variant="primary"] 主按钮(实色 accent 背景,白字,pill 圆角)
214 QPushButton[variant="ghost"] 幽灵按钮(透明背景,hover 出 accent_subtle)
215 QPushButton[variant="danger"] 危险按钮(红字描边)
216 ```
217
218 **应用清单**
219 - `生成图片``variant="primary"`(pill 圆角 + accent 背景)
220 - `下载图片` / `添加图片` / `粘贴图片` → 默认次按钮
221 - `删除` / `清空历史``variant="danger"`
222 - `收藏` 之类的图标按钮 → `variant="ghost"`
223
224 ### 5.2 GroupBox → 卡片化
225
226 去掉边框 + Win95 风格标题,改为:
227 - 白色底(`bg_surface`
228 - 极淡的投影(`box-shadow: 0 1px 2px rgba(0,0,0,0.04)`,Qt QSS 不直接支持 box-shadow,用 `border: 1px solid border_default` 模拟)
229 - 标题不再镶嵌在边框上,改为顶部独立 label(uppercase 小字 + `text_secondary`
230
231 ### 5.3 Tab 栏
232
233 ```
234 QTabBar::tab 默认状态:透明背景 + text_secondary 文字
235 QTabBar::tab:selected 选中:accent 文字 + 底部 2px accent 横线
236 QTabBar::tab:hover 悬停:text_primary 文字
237 ```
238
239 去掉默认 Qt tab 的"凸起带边框"外观,改为下划线式(更现代,Notion/VSCode/Chrome 都这风格)。
240
241 ### 5.4 输入框 / 下拉
242
243 ```
244 QLineEdit, QTextEdit, QComboBox
245 默认:bg_surface + 1px border_default + radius_md
246 焦点:1px border_strong + 1px accent 外发光(QSS 不支持 outline,用双层 border 实现)
247 禁用:bg_subtle + text_tertiary
248 ```
249
250 ### 5.5 状态色(保留 inline,但走 property)
251
252 ```python
253 # 之前
254 self.status_label.setStyleSheet("QLabel { color: #34C759; }")
255 # 之后
256 self.status_label.setProperty("status", "success")
257 self.status_label.style().polish(self.status_label) # 重绘
258 ```
259
260 QSS 里:
261 ```css
262 QLabel[status="success"] { color: palette(success); }
263 QLabel[status="error"] { color: palette(danger); }
264 QLabel[status="muted"] { color: palette(text_secondary); }
265 ```
266
267 ### 5.6 任务队列 sidebar header 修复
268
269 当前 sidebar 顶部"任务队列"label 在视觉上飘到主区 tab bar 右侧。修复:sidebar 顶部加一个 32px 高的 header bar,使 sidebar 头部与主区 tab bar 在同一基线。
270
271 ---
272
273 ## 6. 迁移策略
274
275 ### 6.1 全量删除清单
276
277 ```
278 image_generator.py: 65 处 setStyleSheet
279 task_queue.py: 4 处 setStyleSheet
280 合计 69 处
281 ```
282
283 按性质分类:
284
285 | 类型 | 数量估算 | 处理方式 |
286 |---|---|---|
287 | 全局 QSS 块(line 1331/1602/1644/1659/1676/2427)| 6 | **删除**,迁移到 theme.py |
288 | 装饰色(按钮背景、容器边框、字体大小) | ~50 | **删除**,依赖全局主题 |
289 | 状态色(红/绿/灰)| ~10 | **改 setProperty + style().polish()** |
290 | 平台兼容 hack(Mac 输入可见性等) | ~3 | 保留并在 design doc 标注 |
291
292 ### 6.2 保留的 inline setStyleSheet
293
294 只有以下场景允许 inline:
295 1. 平台 hack(如 macOS 输入框可见性 fix,line 1285)
296 2. 临时高亮反馈(鼠标悬停后 1 秒消失这种)
297
298 其他一律走 QSS。
299
300 ### 6.3 OBJECT_NAME 命名约定
301
302 ```
303 loginDialog 登录对话框
304 mainWindow 主窗口
305 generationTab 生成 tab
306 styleDesignerTab 款式设计 tab
307 historyTab 历史记录 tab
308 taskQueueSidebar 任务队列侧边栏
309 sidebarHeader 侧边栏头部 32px
310 previewArea 预览区
311 referenceImageDrop 参考图拖拽区
312 ```
313
314 QSS 通过 `#objectName` 命中。
315
316 ---
317
318 ## 7. Layout 修复清单(Scope B 范围内)
319
320 | 问题 | 修复方案 |
321 |---|---|
322 | "任务队列"飘右上角 | sidebar 顶部加 32px header bar 与 tab bar 对齐 |
323 | 主次按钮不分 | "生成图片"加 `variant="primary"` |
324 | GroupBox 边框过重 | 卡片化(白底 + 极淡边框 + 圆角)|
325 | 控件间距不一 | 统一改用 `space_3 (12px)` 间距 |
326 | 状态文字颜色 inline | 改 property |
327 | 登录页和主窗口风格断裂 | 全部走全局主题 |
328
329 **不做的 layout 改动**(避免范围蔓延):
330 - 不重排 tab 顺序
331 - 不动 70/30 主区与 sidebar 比例
332 - 不改字段顺序
333 - 不改"款式设计"tab 的 9 个 GroupBox 信息架构
334
335 ---
336
337 ## 8. 测试策略
338
339 ### 8.1 自动化测试
340 项目目前没有 GUI 测试框架。**不为本次重构新增测试**(避免引入 pytest-qt 等依赖)。
341 保留现有 `test_config_loading.py` 跑通即可(QSS 重构不影响配置)。
342
343 ### 8.2 手工验收清单
344
345 启动后按顺序检查:
346
347 - [ ] 登录页能正常显示和登录
348 - [ ] 主窗口三个 tab 切换正常
349 - [ ] "生成图片"按钮视觉权重明显高于"下载图片"
350 - [ ] GroupBox 看起来是"卡片"而不是"边框"
351 - [ ] 任务队列侧边栏头部不再飘到主区 tab 上
352 - [ ] 输入框有焦点蓝边
353 - [ ] 系统切换深色(Mac: 系统设置 → 外观 / Win11: 设置 → 个性化 → 颜色)→ 应用自动跟随
354 - [ ] QMessageBox 弹窗也跟主题(Qt 默认会,确认一下)
355 - [ ] 所有"删除"按钮有 danger 视觉
356 - [ ] 业务功能完全没动(生成、队列、历史)
357
358 ### 8.3 打包验证
359 PyInstaller 打包跑一次,确保:
360 - exe 能启动到登录页
361 - 启动到主窗口
362 - 主题资源(如有)被打包进去
363
364 ---
365
366 ## 9. 风险与回退
367
368 ### 9.1 已知风险
369
370 | 风险 | 缓解 |
371 |---|---|
372 | Qt QSS 不支持 box-shadow,圆角阴影效果有限 | 用双层 border 模拟,效果接近但不等同 web |
373 | StyleHint colorSchemeChanged 在某些 Linux DE 不发射 | 项目实际部署只在 macOS + Windows,无影响 |
374 | 删 inline setStyleSheet 时漏改某个 widget → 视觉断层 | 每次 commit 单独跑一次启动验证 |
375 | 全局 palette 改变后 QMessageBox 系统 dialog 可能不跟随 | 全局 setStyleSheet 会覆盖;如不行再单独处理 |
376
377 ### 9.2 回退方案
378
379 每次 commit 颗粒度小,单点回退:
380 ```bash
381 git revert <commit-sha>
382 ```
383
384 最坏情况整分支扔掉:
385 ```bash
386 git checkout master
387 git branch -D feat/ui-redesign-apple-theme
388 ```
389
390 master 完全不动。
391
392 ---
393
394 ## 10. 实施步骤(参考 TaskList)
395
396 1. ✅ 创建分支
397 2. ✅ 写本设计文档
398 3. 实现 `theme.py`
399 4. 主入口接入
400 5. 迁移 ImageGeneratorWindow
401 6. 迁移 LoginDialog
402 7. 迁移 StyleDesignerTab + DraggableThumbnail
403 8. 迁移 TaskQueueWidget
404 9. 打包验证
405 10. 推送 + 写 OVERNIGHT_SUMMARY.md
406
407 每步 1-2 个 commit,每步完成后启动跑一次冒烟(`python image_generator.py` 能进登录页即可)。
408
409 ---
410
411 ## 11. 给明天的你
412
413 醒来先看仓库根目录的 `OVERNIGHT_SUMMARY.md`,里面有完整进度、commit 列表、怎么运行、怎么验收、已知问题。
414
415 如果某处方向不对,直接 `git checkout master` 当无事发生。