Feature/fsg gamma team ui fix #3

Merged
gitea-admin merged 41 commits from feature/FSG_GammaTeam_UI_Fix into main 2026-08-20 12:12:59 +00:00
5 changed files with 181 additions and 26 deletions
Showing only changes of commit e5bd6791e7 - Show all commits
@@ -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"
}
]
}
+2 -2
View File
@@ -2472,8 +2472,8 @@ STRINGS: Dict[str, Dict[str, str]] = {
"下から独自のSVGアイコンを追加でき、名前ですぐ使えます。", "下から独自の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 " "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."}, "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.search": {"en": "Search icons by name…", "ja": "名前でアイコンを検索…", "vi": "Tìm icon theo tên…"},
"icons_admin.builtin": {"en": "Built-in icons", "ja": "組込みアイコン", "vi": "Icon có sẵ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.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.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"}, "icons_admin.paste": {"en": "Paste SVG", "ja": "SVGを貼付", "vi": "Dán SVG"},
+17
View File
@@ -458,6 +458,23 @@ QLabel#navSectionHdr {
padding: 8px 8px 2px 8px; background: transparent; padding: 8px 8px 2px 8px; background: transparent;
} }
QTreeWidget#navRecents { border-top: 1px solid $nav_border; } 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. */ /* Rail header — the primary action, so it is the one filled button up there. */
QPushButton#navNewChatBtn { QPushButton#navNewChatBtn {
background: $accent_solid; color: #FFFFFF; border: none; font-weight: 600; background: $accent_solid; color: #FFFFFF; border: none; font-weight: 600;
+108
View File
@@ -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
View File
@@ -22,6 +22,7 @@ from .icons import icon
def _grid() -> QListWidget: def _grid() -> QListWidget:
g = QListWidget() g = QListWidget()
g.setObjectName("iconGrid") # accent border on hover/selection, see theme.py
g.setViewMode(QListWidget.IconMode) g.setViewMode(QListWidget.IconMode)
g.setResizeMode(QListWidget.Adjust) g.setResizeMode(QListWidget.Adjust)
g.setMovement(QListWidget.Static) g.setMovement(QListWidget.Static)
@@ -36,34 +37,44 @@ class IconsAdminTab(QWidget):
super().__init__() super().__init__()
self.ctx = ctx self.ctx = ctx
root = QVBoxLayout(self) root = QVBoxLayout(self)
self._hint = QLabel(); self._hint.setObjectName("hint"); self._hint.setWordWrap(True) # Header row: the three actions sit beside the title, where the drawing
root.addWidget(self._hint) # puts them, instead of in a strip below the two grids where they read
# as belonging to the custom grid alone.
# search over built-in names head = QHBoxLayout()
self.search = QLineEdit() self._title = QLabel()
self.search.textChanged.connect(self._reload_builtin) self._title.setObjectName("monTitle")
root.addWidget(self.search) head.addWidget(self._title)
head.addStretch(1)
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()
self.add_btn = QPushButton(); self.add_btn.setIcon(icon("plus")) self.add_btn = QPushButton(); self.add_btn.setIcon(icon("plus"))
self.add_btn.clicked.connect(self._add_icon) self.add_btn.clicked.connect(self._add_icon)
self.paste_btn = QPushButton() self.paste_btn = QPushButton()
self.paste_btn.clicked.connect(self._add_from_svg_text) self.paste_btn.clicked.connect(self._add_from_svg_text)
self.del_btn = QPushButton(); self.del_btn.setIcon(icon("trash")) self.del_btn = QPushButton(); self.del_btn.setIcon(icon("trash"))
self.del_btn.clicked.connect(self._delete_icon) self.del_btn.clicked.connect(self._delete_icon)
btns.addWidget(self.add_btn); btns.addWidget(self.paste_btn) for b in (self.add_btn, self.paste_btn, self.del_btn):
btns.addWidget(self.del_btn); btns.addStretch(1) head.addWidget(b)
root.addLayout(btns) 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) on_language_changed(self._retranslate)
self._retranslate() self._retranslate()
@@ -131,10 +142,13 @@ class IconsAdminTab(QWidget):
self._reload_custom() self._reload_custom()
def _retranslate(self) -> None: def _retranslate(self) -> None:
self._title.setText(tr("monitoring.tab_icons"))
self._hint.setText(tr("icons_admin.hint")) self._hint.setText(tr("icons_admin.hint"))
self.search.setPlaceholderText(tr("icons_admin.search")) self.search.setPlaceholderText(tr("icons_admin.search"))
self._builtin_lbl.setText(tr("icons_admin.builtin")) # ICON TÍCH HỢP / ICON TÙY CHỈNH — caps, like every other section
self._custom_lbl.setText(tr("icons_admin.custom")) # 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.add_btn.setText(tr("icons_admin.add"))
self.paste_btn.setText(tr("icons_admin.paste")) self.paste_btn.setText(tr("icons_admin.paste"))
self.del_btn.setText(tr("icons_admin.delete")) self.del_btn.setText(tr("icons_admin.delete"))