Restore the two gating rules the redesign dropped, and put the dot back in its corner

A1 — hiding the assistant from the dot. The old build kept a chevron button
beside the dot wired to _hide_to_edge; the redesign moved that action into the
panel's ⋯ menu, so hiding went from one click to open-panel → ⋯ → Ẩn. The
drawing has no button there (26px, no text, no chevron), so the reach comes
back as a right-click on the dot, with the tooltip saying so.

A2 — per-part cost. Folding Input/Output/Cache into the total tile's sub-line
carried their token counts but not their prices, which live only on those three
cards, so the cost breakdown left the screen entirely. The wireframe asks for
the tiles anyway — "$0.31 Tổng chi phí · 57 lượt / 395.4K Tổng token / 292.8K
Input / 102.7K Output / 108.9K Cache" — so they are back as tiles, each with its
price, and the turn count rides on the cost tile's label as drawn.

The dot on Cowork. _update_dock_guard lifted it whenever a composer existed,
testing only the vertical axis. On a wide window the composer ends at the chat
column's right edge — x=1688 against a dot at x=1892 — so the dot rose 156px to
clear something that was never under it, and Cowork became the one screen where
it was not in the corner. It now lifts only when the two actually overlap:
18px from the bottom at 1936px wide, still lifted at 1200 and 900 where the
composer does reach under it.

Also: open_tooltip and show_tooltip still said "App Assistant" after the rename.

check_dock_corner covers the corner rule at three widths and fails if the lift
comes back unconditionally.

18/18 checkers pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nam Pham Dinh Thanh
2026-08-18 12:30:50 +09:00
co-authored by Claude Opus 5
parent 16406f863f
commit a0cef768c9
6 changed files with 249 additions and 22 deletions
+20 -2
View File
@@ -223,7 +223,8 @@ class HelpAgentWidget(QWidget):
self.launcher.setObjectName("helpLauncher")
self.launcher.setIcon(icon("sparkle", size=_DOT_ICON, color=_SPARK_GOLD))
self.launcher.setCursor(Qt.PointingHandCursor)
self.launcher.setToolTip(tr("help_agent.open_tooltip"))
self.launcher.setToolTip(
f'{tr("help_agent.open_tooltip")} · {tr("help_agent.dot_hint")}')
self.launcher.clicked.connect(self._expand)
def _build_panel(self) -> None:
@@ -267,6 +268,12 @@ class HelpAgentWidget(QWidget):
self.act_collapse.triggered.connect(self._collapse)
self.act_hide = menu.addAction(tr("help_agent.hide_tooltip"))
self.act_hide.triggered.connect(self._hide_to_edge)
# The old build put a chevron button beside the dot that hid the
# assistant in one click. The drawing has no such button — the dot is
# 26px, no text, no chevron — so the same reach comes back as a right-
# click on the dot rather than as pixels next to it.
self.launcher.setContextMenuPolicy(Qt.CustomContextMenu)
self.launcher.customContextMenuRequested.connect(self._dot_menu)
self.more_btn.setMenu(menu)
hb.addWidget(self.more_btn)
v.addWidget(header)
@@ -309,6 +316,16 @@ class HelpAgentWidget(QWidget):
self._state = _LAUNCHER_ST
self._apply_state()
def _dot_menu(self, pos) -> None:
"""Right-click on the dot: hide to the edge, the one action that means
anything while the panel is shut ("collapse to dot" already happened)."""
from PySide6.QtWidgets import QMenu
menu = QMenu(self.launcher)
act = menu.addAction(tr("help_agent.hide_tooltip"))
act.triggered.connect(self._hide_to_edge)
menu.exec(self.launcher.mapToGlobal(pos))
def _hide_to_edge(self) -> None:
self._state = _HIDDEN
self._apply_state()
@@ -460,7 +477,8 @@ class HelpAgentWidget(QWidget):
self._render()
self.title.setText(tr("help_agent.title"))
self.input.setPlaceholderText(tr("help_agent.placeholder"))
self.launcher.setToolTip(tr("help_agent.open_tooltip"))
self.launcher.setToolTip(
f'{tr("help_agent.open_tooltip")} · {tr("help_agent.dot_hint")}')
if self.launcher.open:
self.launcher.setText(f" {tr('help_agent.badge')}")
self._layout_launcher()
+14 -13
View File
@@ -459,13 +459,16 @@ class MonitoringTab(QWidget):
self.ov_usage_cache = StatCard()
self.ov_usage_cost = StatCard()
self.ov_usage_calls = StatCard()
for i, card in enumerate((self.ov_usage_total, self.ov_usage_cost,
self.ov_usage_calls)):
# The wireframe's usage block is five figures: cost (with the turn
# count on its label), then Tổng token / Input / Output / Cache. Folding
# the last three into a sub-line took their PER-PART COST off screen —
# the total tile has room for the token counts but not for three more
# prices — and the drawing asks for the tiles anyway.
for i, card in enumerate((self.ov_usage_cost, self.ov_usage_total,
self.ov_usage_in, self.ov_usage_out,
self.ov_usage_cache)):
usage_lay.addWidget(card, 0, i)
# Kept alive and updated, but off the KPI row: their figures ride on the
# token tile's sub-line instead of taking three tiles of their own.
for hidden in (self.ov_usage_in, self.ov_usage_out, self.ov_usage_cache):
hidden.setVisible(False)
self.ov_usage_calls.setVisible(False) # rides on the cost tile's label
# Budget: remaining/budget, direct entry, auto-warns red past 85% used —
# same box (and same usage.budget_* config) as the Dashboard's.
self.ov_budget_card = BudgetCard()
@@ -916,11 +919,7 @@ class MonitoringTab(QWidget):
events = ut.load_events()
s = ut.summarize(events)
costs = ut.cost_usd_events(events, pricing)
self.ov_usage_total.set(
tr("dashboard.card_total"), fmt_tokens(s["total"]),
f'{tr("dashboard.card_in")} {fmt_tokens(s["in"])} · '
f'{tr("dashboard.card_out")} {fmt_tokens(s["out"])} · '
f'{tr("dashboard.card_cache")} {fmt_tokens(s["cache"])}')
self.ov_usage_total.set(tr("dashboard.card_total"), fmt_tokens(s["total"]))
self.ov_usage_calls.set(tr("monitoring.overview_calls"), str(s["turns"]), "")
self.ov_usage_in.set(tr("dashboard.card_in"), fmt_tokens(s["in"]),
ut.format_cost(costs["in"], pricing))
@@ -928,8 +927,10 @@ class MonitoringTab(QWidget):
ut.format_cost(costs["out"], pricing))
self.ov_usage_cache.set(tr("dashboard.card_cache"), fmt_tokens(s["cache"]),
ut.format_cost(costs["cache"], pricing))
self.ov_usage_cost.set(tr("dashboard.card_cost"),
ut.format_cost(sum(costs.values()), pricing, digits=2))
# "Tổng chi phí · 57 lượt", exactly as the wireframe labels it.
self.ov_usage_cost.set(
f'{tr("dashboard.card_cost")} · {s["turns"]} {tr("monitoring.overview_calls")}',
ut.format_cost(sum(costs.values()), pricing, digits=2))
self._refresh_budget()
def _sync_sbx_more_label(self, *_a) -> None: