## 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
This commit was merged in pull request #3.
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
"""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())
|
||||
Reference in New Issue
Block a user