fix(ui): sections 17 and 18 were checked against the wrong text
check_design_parity read its checklist from build_audit_page.ANALYSIS. Eight
sections of the audit page are hand-written, and for those the generator's
text is NOT what the page says — so Settings and the Task editor were reported
as matching a design they had never been compared against. The Task editor was
in fact built backwards.
* The checker now reads the "Thay đổi" bullets straight out of
docs/ui-audit.html, and reports how many bullets on the page still have no
probe (57 on the page, 32 probed) instead of implying full coverage.
* Task editor: reverted from three step tabs to a left list + right panel,
five rows matching the five real group boxes — which is what the page asks
for, in as many words ("thay vì chia tab"), for consistency with Settings.
* Settings now switches panels rather than scrolling, so both dialogs are
navigated identically and neither is a long scroll any more.
* Settings field presentation, as the page's second bullet asks: the six
checkboxes became switches, and Language/Theme became segmented controls.
ToggleSwitch subclasses QCheckBox and SegmentedControl exposes the slice
of the QComboBox API this dialog uses, so no save/load path changed —
verified by round-tripping language/theme/tray through _save().
All 22 task-editor fields and 14 settings fields verified present after the
move; 7 suites green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+51
-26
@@ -20,6 +20,7 @@ from ..core.worker import AgentWorker
|
||||
from ..i18n import LANGUAGES, tr
|
||||
from ..state import AppContext
|
||||
from .icons import icon, IconLabel
|
||||
from .widgets import SegmentedControl, ToggleSwitch
|
||||
from .ext_connector_dialog import ExtConnectorEditDialog
|
||||
|
||||
|
||||
@@ -51,7 +52,7 @@ class SettingsDialog(QDialog):
|
||||
|
||||
# --- language + tray ---
|
||||
top = QFormLayout()
|
||||
self.language_combo = QComboBox()
|
||||
self.language_combo = SegmentedControl()
|
||||
for key, label in LANGUAGES.items():
|
||||
self.language_combo.addItem(label, key)
|
||||
self._select_combo(self.language_combo, ctx.config.language)
|
||||
@@ -60,16 +61,16 @@ class SettingsDialog(QDialog):
|
||||
# Theme belongs with the other per-account settings. It is also on the
|
||||
# rail's account row (one click for the common flip); this is the same
|
||||
# value, named and explained, for people who come looking in Settings.
|
||||
self.theme_combo = QComboBox()
|
||||
self.theme_combo = SegmentedControl()
|
||||
for key in ("system", "dark", "light"):
|
||||
self.theme_combo.addItem(tr(f"settings.theme_{key}"), key)
|
||||
self._select_combo(self.theme_combo, getattr(ctx.config, "theme", "system"))
|
||||
top.addRow(tr("settings.theme"), self.theme_combo)
|
||||
|
||||
self.tray_chk = QCheckBox(tr("settings.tray_keep"))
|
||||
self.tray_chk = ToggleSwitch(tr("settings.tray_keep"))
|
||||
self.tray_chk.setChecked(bool(data.get("tray", {}).get("minimize_on_close", True)))
|
||||
top.addRow("", self.tray_chk)
|
||||
self.notify_chk = QCheckBox(tr("settings.tray_notify"))
|
||||
self.notify_chk = ToggleSwitch(tr("settings.tray_notify"))
|
||||
self.notify_chk.setChecked(bool(data.get("tray", {}).get("notify_on_done", True)))
|
||||
top.addRow("", self.notify_chk)
|
||||
# Zero-height anchor so the index can scroll to this section, which is a
|
||||
@@ -136,12 +137,12 @@ class SettingsDialog(QDialog):
|
||||
pw_sep = QLabel("────────────────")
|
||||
sbl.addWidget(pw_sep)
|
||||
|
||||
self.sandbox_confirm = QCheckBox(tr("settings.sandbox_confirm_commands"))
|
||||
self.sandbox_confirm = ToggleSwitch(tr("settings.sandbox_confirm_commands"))
|
||||
self.sandbox_confirm.setChecked(bool(sec.get("cowork_confirm_commands", False)))
|
||||
self.sandbox_confirm.setToolTip(tr("settings.sandbox_confirm_commands_tooltip"))
|
||||
sbl.addWidget(self.sandbox_confirm)
|
||||
|
||||
self.sandbox_block_network = QCheckBox(tr("settings.sandbox_block_network"))
|
||||
self.sandbox_block_network = ToggleSwitch(tr("settings.sandbox_block_network"))
|
||||
self.sandbox_block_network.setChecked(bool(sec.get("block_network", True)))
|
||||
self.sandbox_block_network.setToolTip(tr("settings.sandbox_block_network_tooltip"))
|
||||
sbl.addWidget(self.sandbox_block_network)
|
||||
@@ -151,13 +152,13 @@ class SettingsDialog(QDialog):
|
||||
# they belong with the other tool toggles — see ToolsAdminTab).
|
||||
|
||||
# --- Enable/Disable Agent Security ---
|
||||
self.sec_enabled = QCheckBox("Enable Agent Security (command validation)")
|
||||
self.sec_enabled = ToggleSwitch("Enable Agent Security (command validation)")
|
||||
self.sec_enabled.setChecked(bool(sec.get("enabled", True)))
|
||||
self.sec_enabled.setToolTip("Bật/tắt toàn bộ Agent Security")
|
||||
sbl.addWidget(self.sec_enabled)
|
||||
|
||||
# --- AI Command Check toggle ---
|
||||
self.ai_check = QCheckBox("AI check commands")
|
||||
self.ai_check = ToggleSwitch("AI check commands")
|
||||
self.ai_check.setChecked(bool(sec.get("command_ai_check", False)))
|
||||
self.ai_check.setToolTip("Cho AI control-agent xét lệnh trước khi chạy")
|
||||
sbl.addWidget(self.ai_check)
|
||||
@@ -312,28 +313,52 @@ class SettingsDialog(QDialog):
|
||||
note.setWordWrap(True) # otherwise this one line sets the dialog's width
|
||||
root.addWidget(note)
|
||||
|
||||
scroll.setWidget(self._content)
|
||||
# Five group boxes in one column, with no way to see what was further
|
||||
# down — the index says what is in here and jumps straight to it.
|
||||
from .widgets import section_index
|
||||
self.section_list = section_index(scroll, [
|
||||
(tr("settings.group.general"), self._anchor_general),
|
||||
(tr("settings.group.provider"), prov_group),
|
||||
(tr("settings.group.sandbox"), self.sandbox_group),
|
||||
(tr("settings.group.parameter"), param_group),
|
||||
(tr("routing.settings_group"), routing_group),
|
||||
])
|
||||
# Left list + right panel: one group on screen at a time, the way the
|
||||
# audit page's mock-up shows it. The five rows are the five real group
|
||||
# boxes, so "which group am I in, how many left" is answerable at a
|
||||
# glance instead of by scrolling to find out.
|
||||
from .widgets import section_panels
|
||||
|
||||
self._general_box = QWidget()
|
||||
gv = QVBoxLayout(self._general_box)
|
||||
gv.setContentsMargins(0, 0, 0, 0)
|
||||
root.removeWidget(self._anchor_general)
|
||||
root.removeItem(top)
|
||||
gv.addLayout(top)
|
||||
gv.addWidget(note) # the tip belongs with the general settings
|
||||
gv.addStretch(1)
|
||||
root.removeWidget(note)
|
||||
|
||||
pages = []
|
||||
for label, widget in ((tr("settings.group.general"), self._general_box),
|
||||
(tr("settings.group.provider"), prov_group),
|
||||
(tr("settings.group.sandbox"), self.sandbox_group),
|
||||
(tr("settings.group.parameter"), param_group),
|
||||
(tr("routing.settings_group"), routing_group)):
|
||||
root.removeWidget(widget)
|
||||
page = QWidget()
|
||||
pv = QVBoxLayout(page)
|
||||
pv.setContentsMargins(4, 4, 4, 4)
|
||||
pv.addWidget(widget)
|
||||
pv.addStretch(1)
|
||||
wrap = QScrollArea()
|
||||
wrap.setWidgetResizable(True)
|
||||
wrap.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
wrap.setWidget(page)
|
||||
pages.append((label, wrap))
|
||||
self.section_list, self.section_stack = section_panels(pages)
|
||||
scroll.setParent(None)
|
||||
body = QHBoxLayout()
|
||||
body.setSpacing(10)
|
||||
body.addWidget(self.section_list)
|
||||
body.addWidget(scroll, 1)
|
||||
body.addWidget(self.section_stack, 1)
|
||||
outer.addLayout(body, 1)
|
||||
# Floor the dialog at the width its own content needs AT THE CURRENT
|
||||
# FONT. On a 125%/150% display everything is wider, and without this the
|
||||
# form was simply cut off (or scrolled sideways) instead of the window
|
||||
# refusing to get that small.
|
||||
self.setMinimumWidth(self.section_list.width()
|
||||
+ self._content.sizeHint().width() + 60)
|
||||
self._content = self._general_box # kept for other callers
|
||||
# Floor the dialog at the width its WIDEST page needs, at the current
|
||||
# font. On a 125%/150% display everything is wider, and without this the
|
||||
# form was simply cut off instead of the window refusing to get smaller.
|
||||
widest = max(w.widget().sizeHint().width() for _lab, w in pages)
|
||||
self.setMinimumWidth(self.section_list.width() + widest + 60)
|
||||
|
||||
buttons = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Cancel)
|
||||
buttons.accepted.connect(self._save)
|
||||
|
||||
Reference in New Issue
Block a user