Rework Workspace ▸ Project to its wireframe
Section 4 draws a "Quản lý project" title with + Project mới on its right, a caps PROJECT heading over the list, every row carrying "N đoạn chat · M task", and the form reading Tên / Mô tả / Instructions / Thư mục làm việc. The screen had none of that: the header still said "Workspace — Projects", the create button sat at the foot of the list among the list's own controls, rows were bare names, the folder field had no label, and Instructions carried a parenthetical the drawing does not. The row counts are the substantive part — they are the only thing on the screen that says a project contains anything. Read once per refresh from list_conversations() and list_tasks() grouped by project_id, not per row. Delete stays under the list it acts on. The drawing does not show it, but it does not show it moved either, and dropping a control is not something a layout pass gets to do. Also shortened by the drawing: "Đổi thư mục…"/"Mở thư mục" to "Đổi"/"Mở", which the new "Thư mục làm việc" label above them now disambiguates. check_project_screen covers the four points and fails when the count line is removed. 22/22 checkers pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
416d88d72f
commit
9393fc1748
+65
-8
@@ -32,6 +32,25 @@ from .osutil import open_folder
|
||||
from .widgets import CollapseStrip
|
||||
|
||||
|
||||
class _ProjectRow(QWidget):
|
||||
"""A project in the list: its name, and under it how much is in it.
|
||||
|
||||
The drawing gives every row a second line — "2 đoạn chat · 3 task" — which
|
||||
is the only thing on this screen that says a project holds anything at all.
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, counts: str):
|
||||
super().__init__()
|
||||
lay = QVBoxLayout(self)
|
||||
lay.setContentsMargins(6, 4, 6, 4)
|
||||
lay.setSpacing(0)
|
||||
title = QLabel(name)
|
||||
sub = QLabel(counts)
|
||||
sub.setObjectName("hint")
|
||||
lay.addWidget(title)
|
||||
lay.addWidget(sub)
|
||||
|
||||
|
||||
class WorkspaceTab(QWidget):
|
||||
status_message = Signal(str)
|
||||
open_chat = Signal(str, dict) # kind, conversation — open a thread in Cowork
|
||||
@@ -98,12 +117,23 @@ class WorkspaceTab(QWidget):
|
||||
self._sidebar = sidebar
|
||||
|
||||
root = QVBoxLayout(self)
|
||||
# Title row — the drawing puts "+ Project mới" up here beside the title,
|
||||
# not at the foot of the project list where it read as belonging to the
|
||||
# list's own controls.
|
||||
self._header = QLabel()
|
||||
self._header.setStyleSheet("font-weight:700; font-size:15px;")
|
||||
self._new_btn = QPushButton()
|
||||
self._new_btn.setIcon(icon("plus"))
|
||||
self._new_btn.setObjectName("primary")
|
||||
self._new_btn.clicked.connect(self._create)
|
||||
title_row = QHBoxLayout()
|
||||
title_row.addWidget(self._header)
|
||||
title_row.addStretch(1)
|
||||
title_row.addWidget(self._new_btn)
|
||||
root.addLayout(title_row)
|
||||
self._hint = QLabel()
|
||||
self._hint.setObjectName("hint")
|
||||
self._hint.setWordWrap(True)
|
||||
root.addWidget(self._header)
|
||||
root.addWidget(self._hint)
|
||||
|
||||
self._split = QSplitter(Qt.Horizontal)
|
||||
@@ -115,6 +145,9 @@ class WorkspaceTab(QWidget):
|
||||
ll = QVBoxLayout(left)
|
||||
ll.setContentsMargins(0, 0, 0, 0)
|
||||
left_hdr = QHBoxLayout()
|
||||
self._projects_hdr = QLabel()
|
||||
self._projects_hdr.setObjectName("navSectionHdr")
|
||||
left_hdr.addWidget(self._projects_hdr)
|
||||
self._proj_collapse_btn = QPushButton()
|
||||
self._proj_collapse_btn.setIcon(collapse_left_icon())
|
||||
self._proj_collapse_btn.setFixedWidth(28)
|
||||
@@ -126,15 +159,13 @@ class WorkspaceTab(QWidget):
|
||||
self.project_list.currentItemChanged.connect(self._on_select)
|
||||
ll.addWidget(self.project_list, 1)
|
||||
btns = QHBoxLayout()
|
||||
self._new_btn = QPushButton()
|
||||
self._new_btn.setIcon(icon("plus"))
|
||||
self._new_btn.setObjectName("primary")
|
||||
self._new_btn.clicked.connect(self._create)
|
||||
# Delete stays under the list it acts on. The drawing does not show it,
|
||||
# but it does not show it moved either, and dropping a control is not
|
||||
# something a layout pass gets to do.
|
||||
self._del_btn = QPushButton()
|
||||
self._del_btn.setIcon(icon("trash"))
|
||||
self._del_btn.clicked.connect(self._delete)
|
||||
btns.addWidget(self._new_btn, 1)
|
||||
btns.addWidget(self._del_btn)
|
||||
btns.addWidget(self._del_btn, 1)
|
||||
ll.addLayout(btns)
|
||||
self._projects_panel = left
|
||||
|
||||
@@ -240,6 +271,8 @@ class WorkspaceTab(QWidget):
|
||||
rl.addWidget(self._instr_lbl)
|
||||
rl.addWidget(self.instr_edit)
|
||||
|
||||
self._folder_hdr = QLabel()
|
||||
rl.addWidget(self._folder_hdr)
|
||||
folder_row = QHBoxLayout()
|
||||
self.folder_lbl = QLabel()
|
||||
self.folder_lbl.setObjectName("hint")
|
||||
@@ -394,6 +427,8 @@ class WorkspaceTab(QWidget):
|
||||
def _retranslate(self) -> None:
|
||||
self._header.setText(tr("workspace.header"))
|
||||
self._hint.setText(tr("workspace.hint"))
|
||||
self._projects_hdr.setText(tr("workspace.projects_heading").upper())
|
||||
self._folder_hdr.setText(tr("workspace.folder_label"))
|
||||
self._new_btn.setText(tr("workspace.new_project"))
|
||||
self._del_btn.setText(tr("workspace.delete"))
|
||||
self._name_lbl.setText(tr("workspace.name"))
|
||||
@@ -491,21 +526,43 @@ class WorkspaceTab(QWidget):
|
||||
from ..core.projects import list_projects
|
||||
|
||||
keep = self._current_id
|
||||
counts = self._project_counts()
|
||||
self.project_list.blockSignals(True)
|
||||
self.project_list.clear()
|
||||
row_to_select = 0
|
||||
for i, p in enumerate(list_projects()):
|
||||
item = QListWidgetItem(p.name)
|
||||
chats, tasks = counts.get(p.project_id, (0, 0))
|
||||
item = QListWidgetItem()
|
||||
item.setData(Qt.UserRole, p.project_id)
|
||||
if p.description:
|
||||
item.setToolTip(p.description)
|
||||
self.project_list.addItem(item)
|
||||
row = _ProjectRow(p.name, tr("workspace.counts", chats=chats, tasks=tasks))
|
||||
item.setSizeHint(row.sizeHint())
|
||||
self.project_list.setItemWidget(item, row)
|
||||
if p.project_id == keep:
|
||||
row_to_select = i
|
||||
self.project_list.blockSignals(False)
|
||||
self.project_list.setCurrentRow(row_to_select)
|
||||
self._load_current()
|
||||
|
||||
@staticmethod
|
||||
def _project_counts():
|
||||
"""{project_id: (chats, tasks)} — read once per refresh, not per row."""
|
||||
from ..core.history import list_conversations
|
||||
from ..core.tasks import list_tasks
|
||||
|
||||
out: dict = {}
|
||||
for conv in list_conversations():
|
||||
pid = conv.get("project_id") or "default"
|
||||
chats, tasks = out.get(pid, (0, 0))
|
||||
out[pid] = (chats + 1, tasks)
|
||||
for task in list_tasks():
|
||||
pid = task.get("project_id") or "default"
|
||||
chats, tasks = out.get(pid, (0, 0))
|
||||
out[pid] = (chats, tasks + 1)
|
||||
return out
|
||||
|
||||
def _selected_id(self) -> str:
|
||||
item = self.project_list.currentItem()
|
||||
return item.data(Qt.UserRole) if item else ""
|
||||
|
||||
Reference in New Issue
Block a user