CI / test (push) Canceled after 0s
## Summary What changed and why? ## Change Type - [ ] Cowork feature - [ ] Bug fix - [x] Core AI contribution - [ ] Test / hardening - [ ] Performance - [ ] Documentation ## Related Work Cowork Task: Core Repo: http://34.143.229.138/gitea-admin/fsg-ai-core-assets Core AI Issue: Core Task: Related PR: ## Scope What is intentionally included? What is intentionally NOT included? ## Validation - [ ] Unit tests - [ ] Integration tests - [ ] Manual verification - [ ] Regression check Commands / evidence: ## Security Impact Permission / credential / network / customer data impact: ## Compatibility - [ ] No breaking change - [ ] Breaking change documented ## Reviewer Notes Anything Cowork reviewers should pay attention to. --------- Co-authored-by: Hiep Ha Van <hiephv3@fpt.com> Co-authored-by: Nam Pham Dinh Thanh <nampdt@fpt.com> Co-authored-by: Lam Hoang Van <lamhv7@fpt.com> Co-authored-by: NamPDT <minhanhpkpro@gmail.com> Reviewed-on: #3
178 lines
6.8 KiB
Python
178 lines
6.8 KiB
Python
"""Workspace ▸ Cowork against its wireframe (section 5).
|
||
|
||
The drawing heads the screen with the THREAD's title — "Gom số liệu doanh thu",
|
||
not the word "Cowork" — with the model beside it, Skills and Cuộc trò chuyện mới
|
||
on the right, and a caps TỆP ĐẦU RA (n) panel down the side.
|
||
"""
|
||
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.QtWidgets import QApplication
|
||
|
||
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 seed_demo_data import seed
|
||
seed()
|
||
|
||
from cowork_local.app import MainWindow
|
||
from cowork_local.config import AppConfig
|
||
from cowork_local.core.history import list_conversations, load_conversation
|
||
from cowork_local.i18n import set_language, tr
|
||
from cowork_local.state import AppContext
|
||
|
||
set_language("vi")
|
||
win = MainWindow(AppContext(AppConfig.load()), user_name="local")
|
||
win.resize(1920, 1000)
|
||
win.show()
|
||
app.processEvents()
|
||
win._goto(win._ROW_WORKSPACE, win.workspace._cowork_tab_idx)
|
||
app.processEvents()
|
||
c = win.cowork
|
||
fails = []
|
||
|
||
# a new thread has no title yet, so the screen name stands in
|
||
print(f"chua mo thread: tieu de={c._title_lbl.text()!r}")
|
||
if not c._title_lbl.text().strip():
|
||
fails.append("tieu de trong khi chua mo thread")
|
||
|
||
convs = list_conversations()
|
||
if not convs:
|
||
fails.append("khong co hoi thoai de kiem")
|
||
else:
|
||
conv = load_conversation(Path(convs[0]["path"]))
|
||
c.load_conversation(conv)
|
||
app.processEvents()
|
||
want = conv.get("title", "")
|
||
print(f"sau khi mo thread: tieu de={c._title_lbl.text()!r} (thread={want!r})")
|
||
if want and c._title_lbl.text() != want:
|
||
fails.append(f"tieu de khong theo thread: {c._title_lbl.text()!r} != {want!r}")
|
||
if c._title_lbl.text() == tr("cowork.title") and want:
|
||
fails.append("tieu de van la ten man hinh")
|
||
|
||
# the files panel is a caps section carrying its own count
|
||
head = c.output_section.header.text()
|
||
print(f"pane tep dau ra: {head!r}")
|
||
body = head.lstrip("▾▸ ").split(" (")[0]
|
||
if body != body.upper():
|
||
fails.append(f"tieu de pane chua viet hoa: {head!r}")
|
||
if "(" not in head:
|
||
fails.append("tieu de pane khong kem so luong")
|
||
|
||
# toolbar keeps both actions the drawing shows
|
||
for name, btn in (("Skills", c.skills_btn), ("chat moi", c._new_btn)):
|
||
if not btn.isVisible():
|
||
fails.append(f"thieu nut {name} tren thanh cong cu")
|
||
print(f"nut tren thanh cong cu: {c.skills_btn.text()!r}, {c._new_btn.text()!r}")
|
||
|
||
# the status strip, read left to right, is
|
||
# Agent: … · Định tuyến: … · ↓in ↑out · $cost · 📁 folder
|
||
from PySide6.QtCore import QPoint as _P
|
||
|
||
bar = c._usage_total_lbl.parentWidget()
|
||
def _x(widget):
|
||
return widget.mapTo(bar, _P(0, 0)).x()
|
||
|
||
usage_text = c._usage_total_lbl.text()
|
||
print(f"usage tren dai: {usage_text!r}")
|
||
if not usage_text.strip():
|
||
fails.append("dai duoi khong hien token/chi phi cua thread da mo")
|
||
order = [("Agent", _x(c._agent_lbl)), ("Dinh tuyen", _x(c.routing_toggle)),
|
||
("usage", _x(c._usage_total_lbl))]
|
||
folder = getattr(c, "folder_lbl", None)
|
||
if folder is not None:
|
||
order.append(("thu muc", _x(folder)))
|
||
print("thu tu: " + " < ".join(f"{n}({x})" for n, x in order))
|
||
for (n1, x1), (n2, x2) in zip(order, order[1:]):
|
||
if x1 >= x2:
|
||
fails.append(f"dai duoi sai thu tu: {n1} khong dung truoc {n2}")
|
||
|
||
# the drawing gives Cowork two columns; History lives in the rail's RECENTS
|
||
# and its pane arrives folded to the strip the drawing keeps
|
||
w = win.workspace
|
||
sb = w._sidebar
|
||
print(f"vao Cowork: History hien={sb.isVisible()}")
|
||
if sb.isVisible():
|
||
fails.append("pane Lich su van o tren man Cowork — ban ve chi co 2 cot")
|
||
w.show_history_pane()
|
||
app.processEvents()
|
||
print(f"sau 'Tat ca project…': History hien={sb.isVisible()} rong={sb.width()}px")
|
||
if not sb.isVisible():
|
||
fails.append("'Tat ca project…' khong mo duoc pane Lich su")
|
||
w._on_history_fold(True)
|
||
app.processEvents()
|
||
|
||
# one heading on the files panel, with the chevron at its right — the
|
||
# drawing has "TỆP ĐẦU RA (3) ›" and nothing above it
|
||
from PySide6.QtCore import QPoint as _QP
|
||
|
||
hdr_w = c.output_section.header
|
||
chev = c._io_collapse_btn
|
||
same_row = abs(hdr_w.mapTo(c, _QP(0, 0)).y() - chev.mapTo(c, _QP(0, 0)).y()) <= 8
|
||
chev_right = chev.mapTo(c, _QP(0, 0)).x() > hdr_w.mapTo(c, _QP(0, 0)).x()
|
||
# Count what the panel actually shows as headings. Asking whether the old
|
||
# label is visible proved nothing: unparented, it reports invisible whether
|
||
# or not the code hides it.
|
||
from PySide6.QtWidgets import QLabel
|
||
|
||
heads = [l.text() for l in c._io_widget.findChildren(QLabel)
|
||
if l.isVisible() and l.text().strip()]
|
||
print(f"tieu de hien trong pane: {heads} | "
|
||
f"chevron cung hang={same_row} ben phai={chev_right}")
|
||
if heads:
|
||
fails.append(f"pane co tieu de thua ngoai '{hdr_w.text()}': {heads}")
|
||
if not (same_row and chev_right):
|
||
fails.append("chevron thu gon khong nam cuoi hang tieu de pane")
|
||
|
||
# the composer spans the screen, under BOTH columns — inside the chat
|
||
# column it stopped at the files panel's edge and shrank when files arrived
|
||
from PySide6.QtCore import QPoint
|
||
|
||
c._set_io_collapsed(False)
|
||
app.processEvents()
|
||
split = c.center_split
|
||
files_pane = split.widget(1)
|
||
comp_right = c.composer.mapTo(c, QPoint(c.composer.width(), 0)).x()
|
||
files_left = files_pane.mapTo(c, QPoint(0, 0)).x()
|
||
below = c.composer.mapTo(c, QPoint(0, 0)).y() > split.mapTo(c, QPoint(0, 0)).y()
|
||
spans = comp_right > files_left
|
||
print(f"o nhap: rong {c.composer.width()} / man {c.width()} | "
|
||
f"duoi splitter={below} | trai qua duoi pane tep={spans}")
|
||
if not below:
|
||
fails.append("o nhap khong nam duoi hang than")
|
||
if not spans:
|
||
fails.append("o nhap dung lai o mep pane tep, khong trai het man")
|
||
|
||
print()
|
||
for f in fails:
|
||
print("FAIL " + f)
|
||
print("PASS man Cowork 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())
|