Settings sat 7px further in than the Dashboard/Giám sát rows above it — its
QSS gave it a 6px side margin where those rows start at the rail edge. At the
collapsed 54px width that put its icon near the middle of the rail, which is
what "thu gọn menu lại ra giữa" was describing. Icon and label now start on
the same x as the rows, open and collapsed, in both themes.
The help panel's transcript is rendered HTML, so switching language re-labelled
the chrome but left the greeting — and the "AI Assistant" speaker label — in
whatever language the panel was built in. retranslate() now rewrites the
greeting (matched by identity, so a real reply is never touched) and re-renders.
The sparkle is #FDBE59, sampled from the audit page's own render. Its CSS says
.spark{color:#0F9B8A}, but the glyph is the ✨ emoji and a colour emoji ignores
CSS colour, so the page has always drawn a gold star.
Behind all three: MainWindow does not style itself — run() calls
app.setStyleSheet — so 12 of 13 checkers were measuring a window with no
padding, margins or borders. Every QSS-driven layout bug was invisible to them,
and an unstyled window reported an icon drift that does not exist. Added
_apply_theme() and wired it through.
Two checker repairs that followed:
· check_no_hscroll flagged the 9pt dialogs on sizeHintForColumn(0), which
returns 182px at 9pt, 11pt and 14pt alike. Nothing was clipped. It now
compares the painted text against the width actually on screen, and fails
on a squeezed list (24 combos) where the old test passed.
· the checkers print Vietnamese and died mid-report on a cp932 console.
New: check_rail_align (icons hold one line, both themes, both states) and
check_help_i18n (transcript follows the language). Both verified to fail
without their fix.
15/15 checkers pass. check_nav and check_design_parity segfault in Qt teardown
roughly one run in three — pre-existing, after the verdict prints, and it
happens with or without the theme change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
135 lines
5.1 KiB
Python
135 lines
5.1 KiB
Python
"""Check the redesigned help dock on the real widget, offscreen.
|
||
|
||
The claim being made is a size claim ("84×64 → 26×26"), so this measures the
|
||
widget instead of trusting the constants, and confirms that nothing the old
|
||
three-button layout could do has gone missing — hiding to the edge just moved
|
||
into the panel's ⋯ menu.
|
||
|
||
Run: python tools/check_help_dock.py
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import os
|
||
import sys
|
||
|
||
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
||
from pathlib import Path
|
||
|
||
REPO = Path(__file__).resolve().parent.parent
|
||
sys.path.insert(0, str(REPO.parent))
|
||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||
|
||
from capture_screens import _apply_theme, _isolate_home, _load_fonts # noqa: E402
|
||
|
||
|
||
def main() -> int:
|
||
sandbox = _isolate_home()
|
||
from PySide6.QtWidgets import QApplication, QWidget
|
||
|
||
app = QApplication([])
|
||
_load_fonts()
|
||
|
||
_apply_theme(app) # measure the styled widget, not a bare one
|
||
from cowork_local.config import AppConfig, CONFIG_DIR
|
||
assert str(sandbox) in str(CONFIG_DIR), f"isolation failed: {CONFIG_DIR}"
|
||
|
||
from cowork_local.i18n import set_language, tr
|
||
from cowork_local.state import AppContext
|
||
from cowork_local.ui.help_agent_widget import HelpAgentWidget
|
||
|
||
set_language("vi")
|
||
host = QWidget()
|
||
host.resize(1200, 800)
|
||
dock = HelpAgentWidget(AppContext(AppConfig.load()), host, user_name="local")
|
||
app.processEvents()
|
||
|
||
fails: list[str] = []
|
||
OLD_W, OLD_H = 84, 64 # 64px badge + 2px gap + 18px chevron
|
||
|
||
closed = dock.size()
|
||
print(f"dong : {closed.width()}x{closed.height()}px "
|
||
f"(cu {OLD_W}x{OLD_H})")
|
||
area_new, area_old = closed.width() * closed.height(), OLD_W * OLD_H
|
||
print(f"dien tich : {area_new} vs {area_old}px2 "
|
||
f"({100 - round(area_new / area_old * 100)}% nho hon)")
|
||
if closed.width() > 30 or closed.height() > 30:
|
||
fails.append(f"nut dong van {closed.width()}x{closed.height()}, cho <=30")
|
||
# 26px clears the ~24px comfortable-tap floor the old 18px chevron missed.
|
||
if min(closed.width(), closed.height()) < 24:
|
||
fails.append("vung bam nho hon 24px")
|
||
|
||
# Hover: the name appears, and only then.
|
||
print(f"chu luc dong : {dock.launcher.text()!r} (phai rong)")
|
||
if dock.launcher.text().strip():
|
||
fails.append("nut dong ma van hien chu")
|
||
dock.launcher._set_open(True)
|
||
app.processEvents()
|
||
hovered = dock.size()
|
||
print(f"re chuot : {hovered.width()}x{hovered.height()}px · "
|
||
f"chu = {dock.launcher.text().strip()!r}")
|
||
if tr("help_agent.badge") not in dock.launcher.text():
|
||
fails.append("re chuot khong hien 'AI Assistant'")
|
||
if hovered.width() <= closed.width():
|
||
fails.append("re chuot ma nut khong no ra")
|
||
dock.launcher._set_open(False)
|
||
app.processEvents()
|
||
if dock.size().width() != closed.width():
|
||
fails.append("roi chuot ma nut khong thu lai")
|
||
|
||
# Every state still reachable, and the corner anchor still holds.
|
||
for state, call in (("panel", dock._expand), ("launcher", dock._collapse),
|
||
("hidden", dock._hide_to_edge), ("launcher", dock._show_launcher)):
|
||
call()
|
||
app.processEvents()
|
||
got = dock._state
|
||
inside = (dock.x() + dock.width() <= host.width()
|
||
and dock.y() + dock.height() <= host.height())
|
||
print(f"trang thai {state:9}: {got:9} {dock.width():3}x{dock.height():3} "
|
||
f"goc phai duoi = {inside}")
|
||
if got != state:
|
||
fails.append(f"khong vao duoc trang thai {state}")
|
||
if not inside:
|
||
fails.append(f"trang thai {state} tran ra ngoai cua so")
|
||
|
||
# The edge tab was 16px — below anything comfortable to hit.
|
||
dock._hide_to_edge()
|
||
app.processEvents()
|
||
print(f"tab mep : {dock.width()}px (cu 16px)")
|
||
if dock.width() < 24:
|
||
fails.append(f"tab mep {dock.width()}px, van duoi 24px")
|
||
dock._show_launcher()
|
||
|
||
# Nothing removed: "hide to the edge" is in the ⋯ menu now.
|
||
items = [a.text() for a in dock.more_btn.menu().actions()]
|
||
print(f"menu ⋯ : {items}")
|
||
for key in ("help_agent.collapse_tooltip", "help_agent.hide_tooltip"):
|
||
if tr(key) not in items:
|
||
fails.append(f"menu thieu muc {tr(key)}")
|
||
print(f"nut thu nho : {dock.min_btn.toolTip()!r}")
|
||
print(f"nut gui / o nhap: {dock.send_btn is not None} / {dock.input is not None}")
|
||
|
||
# All three languages must have the new strings.
|
||
for lang in ("vi", "en", "ja"):
|
||
set_language(lang)
|
||
dock.retranslate()
|
||
vals = [dock.launcher.toolTip(), tr("help_agent.badge"),
|
||
tr("help_agent.more_tooltip")]
|
||
print(f" {lang}: badge={vals[1]!r} more={vals[2]!r}")
|
||
if any(not v or v.startswith("help_agent.") for v in vals):
|
||
fails.append(f"thieu ban dich cho {lang}")
|
||
set_language("vi")
|
||
|
||
print()
|
||
if fails:
|
||
print("*** LOI ***")
|
||
for f in fails:
|
||
print(" " + f)
|
||
return 1
|
||
print("KET QUA: nut tro ly gon lai, khong mat chuc nang nao")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|