Implements the redesign from docs/ui-audit.html. Rearrangement only — no
feature was removed; every control that moved kept its handler, and the
gates that used to hide things now grey them out instead.
Navigation
* The rail is one flat list: the five Workspace sub-views sit at the top
level instead of behind an accordion, with Dashboard/Monitoring pinned
at the foot and Settings below them.
* Cowork and GraphRAG stay listed and greyed while no project is
selected, rather than vanishing and resizing the menu under the user.
* Monitoring keeps its eight sub-views in its own tab strip (unhidden)
instead of doubling the rail's length.
* _goto now moves the highlight itself, fixing a long-standing bug where
programmatic navigation left the rail pointing at the previous screen.
* Rail header gained the project picker and "New chat"; RECENTS lists the
active project's threads. Both are second views of existing state — the
Cowork toolbar button and the full History panel are untouched.
* Provider / language / theme moved from the top bar to an account row at
the foot of the rail (same widgets, same signals).
Screens
* Co4E: the flow tab strip is gone (per the design); Flow Status became a
toolbar toggle with its own way back, and the three icon-only tabs became
four labelled, foldable sections in one column. One flow open at a time
is the one capability this costs; background runs are unaffected.
* Dashboard: header split into two rows; cost promoted to a hero card.
* Monitoring Overview: one scrolling column of titled sections; the model
price table got its own full-width section instead of sharing a row with
the CPU meters.
* Settings and Task editor gained a section index down the left.
* Help dock: 84x64 launcher + chevron became one 26px dot that expands to
a labelled pill on hover; "hide to the edge" moved into the panel's menu.
Layout
* The window's minimum width dropped from 1453px to 768px. The main cause
was a QTabWidget taking its minimum from the widest page even when that
page is hidden, so Co4E was forcing Project and Cowork wide.
* Secondary panes fold themselves on a narrow window and restore when it
grows, never overriding a fold the user made.
* The long dialogs no longer scroll sideways at any font size.
Verification: tools/check_*.py build a real MainWindow offscreen against a
copy of ~/.cowork_local with the schedulers no-oped. check_design_parity.py
reads its checklist straight from the audit page's own proposals.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
93 lines
2.9 KiB
Python
93 lines
2.9 KiB
Python
"""Check the Dashboard header regroup, on the real widget, offscreen.
|
|
|
|
Nine controls were on one row. They are now on two, grouped by what they do —
|
|
so this asserts that all nine are still present, still wired, and that the
|
|
header really is two rows now (row 1 = title + Refresh, row 2 = the selectors).
|
|
|
|
Run: python tools/check_dashboard.py
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
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 _isolate_home, _load_fonts # noqa: E402
|
|
|
|
HEADER = ["_title", "chart_prev_btn", "_chart_period_lbl", "chart_next_btn",
|
|
"gran_combo", "metric_combo", "currency_lbl", "currency_combo",
|
|
"refresh_btn"]
|
|
|
|
|
|
def main() -> int:
|
|
sandbox = _isolate_home()
|
|
from PySide6.QtWidgets import QApplication
|
|
|
|
app = QApplication([])
|
|
_load_fonts()
|
|
|
|
from cowork_local.config import AppConfig, CONFIG_DIR
|
|
assert str(sandbox) in str(CONFIG_DIR), f"isolation failed: {CONFIG_DIR}"
|
|
|
|
from seed_demo_data import seed
|
|
seed()
|
|
|
|
from cowork_local.i18n import set_language
|
|
from cowork_local.state import AppContext
|
|
from cowork_local.ui.dashboard_tab import DashboardTab
|
|
|
|
set_language("vi")
|
|
tab = DashboardTab(AppContext(AppConfig.load()))
|
|
tab.resize(1100, 800)
|
|
tab.show()
|
|
app.processEvents()
|
|
tab.refresh()
|
|
app.processEvents()
|
|
|
|
fails: list[str] = []
|
|
missing = [n for n in HEADER if getattr(tab, n, None) is None]
|
|
print(f"control header con nguyen: {len(HEADER) - len(missing)}/{len(HEADER)}")
|
|
if missing:
|
|
fails.append(f"mat control: {missing}")
|
|
|
|
# Two rows: everything in the header must sit at one of exactly two y bands.
|
|
tops = {}
|
|
for n in HEADER:
|
|
w = getattr(tab, n)
|
|
tops.setdefault(round(w.mapTo(tab, w.rect().topLeft()).y() / 10), []).append(n)
|
|
print(f"so hang cua header : {len(tops)}")
|
|
for band, names in sorted(tops.items()):
|
|
print(f" y~{band * 10:>4}px : {names}")
|
|
if len(tops) != 2:
|
|
fails.append(f"header co {len(tops)} hang, cho 2")
|
|
|
|
# Still wired: changing the metric must not throw and must stick.
|
|
before = tab.metric_combo.currentData()
|
|
tab.metric_combo.setCurrentIndex(1 - tab.metric_combo.currentIndex())
|
|
app.processEvents()
|
|
after = tab.metric_combo.currentData()
|
|
print(f"doi chi so bieu do : {before} -> {after}")
|
|
if after == before:
|
|
fails.append("combo chi so khong doi duoc")
|
|
tab.refresh_btn.click()
|
|
app.processEvents()
|
|
print("bam Lam moi : khong loi")
|
|
|
|
print()
|
|
if fails:
|
|
print("*** LOI ***")
|
|
for f in fails:
|
|
print(" " + f)
|
|
return 1
|
|
print("KET QUA: header Dashboard chia 2 hang, du 9 control")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|