Feature/fsg gamma team ui fix #3
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"kind": "cowork",
|
||||
"session_id": "20260818-130950-831",
|
||||
"title": "alo",
|
||||
"created": "2026-08-18T13:10:03",
|
||||
"pinned": false,
|
||||
"project_id": "project-mới",
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "alo"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2472,8 +2472,8 @@ STRINGS: Dict[str, Dict[str, str]] = {
|
||||
"下から独自のSVGアイコンを追加でき、名前ですぐ使えます。",
|
||||
"vi": "Các icon dùng cho agent và flow. Gõ tên vào ô Icon của step/agent để dùng. Thêm icon SVG "
|
||||
"của bạn ở dưới — dùng được ngay bằng tên."},
|
||||
"icons_admin.search": {"en": "Search built-in icons…", "ja": "組込みアイコンを検索…", "vi": "Tìm icon có sẵn…"},
|
||||
"icons_admin.builtin": {"en": "Built-in icons", "ja": "組込みアイコン", "vi": "Icon có sẵn"},
|
||||
"icons_admin.search": {"en": "Search icons by name…", "ja": "名前でアイコンを検索…", "vi": "Tìm icon theo tên…"},
|
||||
"icons_admin.builtin": {"en": "Built-in icons", "ja": "組込みアイコン", "vi": "Icon tích hợp"},
|
||||
"icons_admin.custom": {"en": "Custom icons", "ja": "カスタムアイコン", "vi": "Icon tùy chỉnh"},
|
||||
"icons_admin.add": {"en": "Add SVG file", "ja": "SVGファイルを追加", "vi": "Thêm tệp SVG"},
|
||||
"icons_admin.paste": {"en": "Paste SVG", "ja": "SVGを貼付", "vi": "Dán SVG"},
|
||||
|
||||
@@ -458,6 +458,23 @@ QLabel#navSectionHdr {
|
||||
padding: 8px 8px 2px 8px; background: transparent;
|
||||
}
|
||||
QTreeWidget#navRecents { border-top: 1px solid $nav_border; }
|
||||
|
||||
/* Icon library cells. The audit page's note on this screen is that the cells
|
||||
had no visible edge on hover or selection, so you could not tell what you
|
||||
were about to pick — "cùng ngôn ngữ thẻ với Agent/Công cụ". */
|
||||
QListWidget#iconGrid { background: transparent; border: none; }
|
||||
QListWidget#iconGrid::item {
|
||||
border: 1px solid transparent; border-radius: ${radius}px;
|
||||
color: $text_muted; padding: 4px;
|
||||
}
|
||||
QListWidget#iconGrid::item:hover {
|
||||
border: 1px solid $accent; background: $hover; color: $text;
|
||||
}
|
||||
QListWidget#iconGrid::item:selected {
|
||||
border: 1px solid $accent; background: $accent_wash; color: $text;
|
||||
}
|
||||
/* Screen title beside its actions, same weight the other admin screens use. */
|
||||
QLabel#monTitle { font-size: 15px; font-weight: 700; color: $text; }
|
||||
/* Rail header — the primary action, so it is the one filled button up there. */
|
||||
QPushButton#navNewChatBtn {
|
||||
background: $accent_solid; color: #FFFFFF; border: none; font-weight: 600;
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
"""The Icon library screen against its wireframe.
|
||||
|
||||
The drawing (section 16) puts the three actions on the title row, a magnifier
|
||||
in the search box, caps section headings, and — its stated complaint — a
|
||||
visible edge on an icon cell when you hover or select it, so you can tell what
|
||||
you are about to pick.
|
||||
"""
|
||||
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 ( # noqa: E402
|
||||
_apply_theme, _freeze_schedulers, _isolate_home, _load_fonts)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
sandbox = _isolate_home()
|
||||
from PySide6.QtCore import QPoint
|
||||
from PySide6.QtWidgets import QApplication, QLineEdit
|
||||
|
||||
app = QApplication([])
|
||||
_load_fonts()
|
||||
_freeze_schedulers()
|
||||
_apply_theme(app)
|
||||
|
||||
from cowork_local.config import CONFIG_DIR
|
||||
assert str(sandbox) in str(CONFIG_DIR), f"isolation failed: {CONFIG_DIR}"
|
||||
|
||||
from cowork_local.config import AppConfig
|
||||
from cowork_local.i18n import set_language
|
||||
from cowork_local.state import AppContext
|
||||
from cowork_local.ui.icons_admin_tab import IconsAdminTab
|
||||
|
||||
set_language("vi")
|
||||
tab = IconsAdminTab(AppContext(AppConfig.load()))
|
||||
tab.resize(1100, 700)
|
||||
tab.show()
|
||||
app.processEvents()
|
||||
|
||||
fails = []
|
||||
|
||||
# 1. actions on the title row, to its right — not in a strip below the
|
||||
# grids. Comparing y alone was not enough: a button left out of the
|
||||
# layout sits at (0,0), which is "the same row" by accident.
|
||||
title = tab._title
|
||||
t_pos = title.mapTo(tab, QPoint(0, 0))
|
||||
t_mid = t_pos.y() + title.height() // 2
|
||||
t_right = t_pos.x() + title.width()
|
||||
for name, btn in (("Thêm", tab.add_btn), ("Dán", tab.paste_btn),
|
||||
("Xóa", tab.del_btn)):
|
||||
pos = btn.mapTo(tab, QPoint(0, 0))
|
||||
mid = pos.y() + btn.height() // 2
|
||||
aligned = abs(mid - t_mid) <= 6
|
||||
after = pos.x() >= t_right
|
||||
print(f"nut {name:5}: tam y={mid} (tieu de {t_mid}) thang hang={aligned} "
|
||||
f"| x={pos.x()} (sau tieu de {t_right})={after}")
|
||||
if not (aligned and after):
|
||||
fails.append(f"nut {name} khong nam cung hang, ben phai tieu de")
|
||||
|
||||
# 2. magnifier in the search box
|
||||
lead = tab.search.actions()
|
||||
print(f"o tim co icon kinh lup: {bool(lead)}")
|
||||
if not lead:
|
||||
fails.append("o tim thieu icon kinh lup")
|
||||
|
||||
# 3. caps headings
|
||||
for name, lbl in (("tich hop", tab._builtin_lbl), ("tuy chinh", tab._custom_lbl)):
|
||||
text = lbl.text()
|
||||
print(f"tieu de {name}: {text!r}")
|
||||
if text != text.upper():
|
||||
fails.append(f"tieu de {name} chua viet hoa: {text!r}")
|
||||
|
||||
# 4. the cell has an edge to see — compare the painted cell hovered vs not
|
||||
grid = tab.builtin_grid
|
||||
if grid.count():
|
||||
rect = grid.visualItemRect(grid.item(0))
|
||||
plain = grid.grab(rect).toImage()
|
||||
grid.setCurrentRow(0)
|
||||
app.processEvents()
|
||||
picked = grid.grab(rect).toImage()
|
||||
diff = sum(1 for x in range(plain.width()) for y in range(plain.height())
|
||||
if plain.pixelColor(x, y) != picked.pixelColor(x, y))
|
||||
print(f"o icon doi {diff} diem anh khi duoc chon")
|
||||
if diff == 0:
|
||||
fails.append("o icon khong doi gi khi duoc chon — khong thay minh dang chon cai nao")
|
||||
else:
|
||||
fails.append("luoi icon rong")
|
||||
|
||||
print()
|
||||
for f in fails:
|
||||
print("FAIL " + f)
|
||||
print("PASS man Icon khop ban ve" if not fails else f"{len(fails)} problem(s)")
|
||||
sys.stdout.flush()
|
||||
os._exit(1 if fails else 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
+38
-24
@@ -22,6 +22,7 @@ from .icons import icon
|
||||
|
||||
def _grid() -> QListWidget:
|
||||
g = QListWidget()
|
||||
g.setObjectName("iconGrid") # accent border on hover/selection, see theme.py
|
||||
g.setViewMode(QListWidget.IconMode)
|
||||
g.setResizeMode(QListWidget.Adjust)
|
||||
g.setMovement(QListWidget.Static)
|
||||
@@ -36,34 +37,44 @@ class IconsAdminTab(QWidget):
|
||||
super().__init__()
|
||||
self.ctx = ctx
|
||||
root = QVBoxLayout(self)
|
||||
self._hint = QLabel(); self._hint.setObjectName("hint"); self._hint.setWordWrap(True)
|
||||
root.addWidget(self._hint)
|
||||
|
||||
# search over built-in names
|
||||
self.search = QLineEdit()
|
||||
self.search.textChanged.connect(self._reload_builtin)
|
||||
root.addWidget(self.search)
|
||||
|
||||
self._builtin_lbl = QLabel()
|
||||
root.addWidget(self._builtin_lbl)
|
||||
self.builtin_grid = _grid()
|
||||
root.addWidget(self.builtin_grid, 2)
|
||||
|
||||
self._custom_lbl = QLabel()
|
||||
root.addWidget(self._custom_lbl)
|
||||
self.custom_grid = _grid()
|
||||
root.addWidget(self.custom_grid, 1)
|
||||
|
||||
btns = QHBoxLayout()
|
||||
# Header row: the three actions sit beside the title, where the drawing
|
||||
# puts them, instead of in a strip below the two grids where they read
|
||||
# as belonging to the custom grid alone.
|
||||
head = QHBoxLayout()
|
||||
self._title = QLabel()
|
||||
self._title.setObjectName("monTitle")
|
||||
head.addWidget(self._title)
|
||||
head.addStretch(1)
|
||||
self.add_btn = QPushButton(); self.add_btn.setIcon(icon("plus"))
|
||||
self.add_btn.clicked.connect(self._add_icon)
|
||||
self.paste_btn = QPushButton()
|
||||
self.paste_btn.clicked.connect(self._add_from_svg_text)
|
||||
self.del_btn = QPushButton(); self.del_btn.setIcon(icon("trash"))
|
||||
self.del_btn.clicked.connect(self._delete_icon)
|
||||
btns.addWidget(self.add_btn); btns.addWidget(self.paste_btn)
|
||||
btns.addWidget(self.del_btn); btns.addStretch(1)
|
||||
root.addLayout(btns)
|
||||
for b in (self.add_btn, self.paste_btn, self.del_btn):
|
||||
head.addWidget(b)
|
||||
root.addLayout(head)
|
||||
|
||||
self._hint = QLabel(); self._hint.setObjectName("hint"); self._hint.setWordWrap(True)
|
||||
root.addWidget(self._hint)
|
||||
|
||||
# search over built-in names, with the magnifier the drawing asks for
|
||||
self.search = QLineEdit()
|
||||
self.search.addAction(icon("search"), QLineEdit.LeadingPosition)
|
||||
self.search.textChanged.connect(self._reload_builtin)
|
||||
root.addWidget(self.search)
|
||||
|
||||
self._builtin_lbl = QLabel()
|
||||
self._builtin_lbl.setObjectName("navSectionHdr") # quiet caps heading
|
||||
root.addWidget(self._builtin_lbl)
|
||||
self.builtin_grid = _grid()
|
||||
root.addWidget(self.builtin_grid, 2)
|
||||
|
||||
self._custom_lbl = QLabel()
|
||||
self._custom_lbl.setObjectName("navSectionHdr")
|
||||
root.addWidget(self._custom_lbl)
|
||||
self.custom_grid = _grid()
|
||||
root.addWidget(self.custom_grid, 1)
|
||||
|
||||
on_language_changed(self._retranslate)
|
||||
self._retranslate()
|
||||
@@ -131,10 +142,13 @@ class IconsAdminTab(QWidget):
|
||||
self._reload_custom()
|
||||
|
||||
def _retranslate(self) -> None:
|
||||
self._title.setText(tr("monitoring.tab_icons"))
|
||||
self._hint.setText(tr("icons_admin.hint"))
|
||||
self.search.setPlaceholderText(tr("icons_admin.search"))
|
||||
self._builtin_lbl.setText(tr("icons_admin.builtin"))
|
||||
self._custom_lbl.setText(tr("icons_admin.custom"))
|
||||
# ICON TÍCH HỢP / ICON TÙY CHỈNH — caps, like every other section
|
||||
# heading the audit page draws.
|
||||
self._builtin_lbl.setText(tr("icons_admin.builtin").upper())
|
||||
self._custom_lbl.setText(tr("icons_admin.custom").upper())
|
||||
self.add_btn.setText(tr("icons_admin.add"))
|
||||
self.paste_btn.setText(tr("icons_admin.paste"))
|
||||
self.del_btn.setText(tr("icons_admin.delete"))
|
||||
|
||||
Reference in New Issue
Block a user