Bộ 7 role chuyên biệt (triage → specialist → implementer → reviewer) cùng lớp dùng chung: guardrail, tri thức về repo, checklist, và contract đầu ra. Vì sao có: bug UI/UX được báo bằng lời kể triệu chứng, và người sửa hay bỏ qua ba thứ mà repo này rất dễ vi phạm — luật "không file nào ngoài theme/ được đặt tên một màu", trần LOC theo bánh cóc, và việc ui/ với presentation/ cùng tồn tại nên sửa nhầm file là "đã fix mà vẫn thấy lỗi". knowledge/qt_pitfalls.md chép lại 20 nguyên nhân gốc hay gặp của bug PySide6; examples/bad_fix.md có hai ca CÓ THẬT, gồm ca chính bản vá trong nhánh này từng mắc (compare_digest trên str ngoài ASCII) và lọt qua vòng review đầu. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
5.0 KiB
Theme & Design Tokens — luật màu sắc của Cowork Local
Nguồn: docstring đầu theme/__init__.py, theme/palettes.py, theme/qss.py,
theme/qss_controls.py.
1. Luật gốc
Không file nào ngoài
theme/được đặt tên một màu.
Cơ chế duy nhất:
Palette (token ngữ nghĩa) → _TEMPLATE (một QSS duy nhất) → stylesheet(theme)
Hai cách hợp lệ để một widget có màu:
- Khai báo — gán
objectNamecho widget, style nó trong_TEMPLATE(theme/qss.py). Đây là cách mặc định. - Vẽ tay — widget vẽ bằng
QPainter(chart, canvas, syntax highlighter) thì gọicurrent_palette()rồi đọc token.
Cách không hợp lệ, bị reject review:
self.label.setStyleSheet("color: #dc2626;") # ❌ hex ngoài theme/
pen.setColor(QColor("red")) # ❌ tên màu literal
self.card.setStyleSheet("background: rgba(0,0,0,.1)") # ❌
2. API cần nhớ
| Hàm | Dùng khi |
|---|---|
theme.stylesheet(theme) |
Sinh QSS toàn app, truyền vào QApplication.setStyleSheet |
theme.set_active_theme(theme) |
Phải gọi ngay cạnh mỗi setStyleSheet(stylesheet(...)) |
theme.current_theme() |
'dark' / 'light' đang hiển thị |
theme.current_palette() |
Token của theme đang hiển thị — dùng trong paintEvent |
theme.palette(theme) |
Token của một theme cụ thể |
theme.resolve_theme('system') |
Suy ra dark/light từ color scheme của OS |
theme.role_colors(theme) |
Màu theo vai trò hội thoại: user/assistant/tool/result/error |
current_palette() tồn tại để code vẽ không phải đọc lại config.json mỗi lần
repaint — đó từng là bug hiệu năng thật. Không thay bằng đọc config.
3. Nhóm token
Palette là @dataclass(frozen=True). Các nhóm chính:
| Nhóm | Token | Ý nghĩa |
|---|---|---|
| Bề mặt (thang 4 bậc) | bg |
nền cửa sổ / canvas |
surface |
panel, card, group box (không phải nav rail) | |
surface_raised |
input, list, tree — thứ người dùng gõ/chọn | |
overlay |
menu, tooltip, popup | |
sunken |
log, code, terminal — thứ để đọc vào | |
hover / active |
trạng thái hover / đang bấm | |
| Chữ | text, text_muted, ... |
|
| Nhấn | accent, accent_solid |
Hai token khác nhau có chủ đích: màu đọc được dạng chữ trên nền tối thì quá nhạt để làm nền cho chữ trắng |
| Trạng thái | danger, ... |
|
| Vai trò hội thoại | role_user, role_assistant, role_tool, role_result, role_error |
|
| Code | code_string, ... |
syntax highlighting |
Token là ngữ nghĩa, không phải literal: danger / text_muted — không bao giờ
blue / grey2. Thêm một theme = thêm một Palette, không phải sửa stylesheet.
4. Ràng buộc thiết kế (đừng "sửa" nhầm thành bug)
- Không gradient, không glow. Bảng màu lấy từ VS Code "Dark Modern" / "Light Modern". Bề mặt phẳng, góc gần vuông, một màu accent chỉ dành cho thứ người dùng thao tác.
- Chiều sâu đến từ thang bề mặt và viền mảnh, không từ màu.
- Silhouette VS Code: nav rail tối hơn vùng nội dung, không sáng hơn.
Người dùng báo "menu trái tối quá" — đó là thiết kế, không phải bug. Xem
examples/bad_fix.md. - Contrast giữ ở WCAG AA (4.5:1) cho body text và cho chữ trên nút đặc.
- Bốn giá trị của VS Code không đạt AA đã được nhích lên vừa đủ (số dòng dark 3.59:1, chữ mờ trên sidebar sáng 4.28:1, xanh lá sáng 4.33:1, hổ phách sáng 3.12:1). Mỗi chỗ có comment ghi giá trị gốc — không trả chúng về giá trị VS Code.
5. Mũi tên combo box (_chevron_asset)
QSS image: chỉ nhận đường dẫn file/resource, không nhận QPixmap. Và một khi
::drop-down / ::up-button / ::down-button bị style, Qt ngừng vẽ mũi tên mặc định.
Vì vậy theme/palettes.py::_chevron_asset render sẵn PNG chevron ra thư mục tạm và cache
theo hash (direction, color).
Hệ quả khi debug:
- "Combo box mất mũi tên" → gần như luôn do một stylesheet cục bộ đè lên
::drop-down. - File cache nằm ở
%TEMP%/cowork_local_theme/chevron_*.png. Xoá nó để buộc render lại khi test màu mới.
6. Checklist sửa bug liên quan màu sắc
- Đã kiểm tra bug xuất hiện ở cả dark và light chưa? (
docs/screens/*-dark.png/*-light.png) - Bản sửa dùng token, không dùng hex?
- Nếu thêm token mới: đã thêm cho cả
DARKvàLIGHT? - Nếu là chữ trên nền đặc: đã dùng
accent_solidthay vìaccent? - Contrast còn ≥ 4.5:1?
- Widget dựng sau khi đổi theme có nhận đúng stylesheet? (xem
qt_pitfalls.mdP07)