Files
cowork-local/agent/knowledge/theme_tokens.md
T
anhtnm1andClaude Opus 5 7bd2b95a57 docs(agent): thư viện instruction cho việc sửa bug UI/UX
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>
2026-09-07 19:55:02 +09:00

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:

  1. Khai báo — gán objectName cho widget, style nó trong _TEMPLATE (theme/qss.py). Đây là cách mặc định.
  2. Vẽ tay — widget vẽ bằng QPainter (chart, canvas, syntax highlighter) thì gọi current_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ả DARK và LIGHT?
  • Nếu là chữ trên nền đặc: đã dùng accent_solid thay vì accent?
  • Contrast còn ≥ 4.5:1?
  • Widget dựng sau khi đổi theme có nhận đúng stylesheet? (xem qt_pitfalls.md P07)