chore: sync local working copy as of 2026-08-15

The Gitea repo was initialised from an earlier snapshot, so main and the
machine this runs on had drifted apart in 153 files before any UI work
started. This commit brings the branch up to the local tree as it stood
on 2026-08-15 21:31 (from cowork_local.7z), so the redesign that follows
shows up as its own reviewable diff instead of being mixed in with the
pre-existing divergence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
NamPDT
2026-08-17 11:38:18 +09:00
co-authored by Claude Opus 5
parent 414eaddca3
commit 291a611737
96 changed files with 12491 additions and 2937 deletions
+32 -28
View File
@@ -8,36 +8,40 @@ from pathlib import Path
from PySide6.QtCore import QEvent, QObject, QPointF, QRectF, Qt, Signal
from PySide6.QtGui import QColor, QPainter, QPen
from PySide6.QtWidgets import (
QAbstractSpinBox, QComboBox, QDoubleSpinBox, QFrame, QGraphicsDropShadowEffect,
QAbstractSpinBox, QComboBox, QDoubleSpinBox, QFrame,
QHBoxLayout, QLabel, QListWidget, QListWidgetItem, QPushButton, QSizePolicy,
QVBoxLayout, QWidget,
)
from ..core.flows import STEP_DONE, STEP_ERROR, STEP_PENDING, STEP_RUNNING
from ..theme import ACCENT
from ..theme import current_palette
from .icons import DOT_BLUE, DOT_GREEN, DOT_GREY, DOT_RED, dot_icon, icon
def _style_card(frame: QFrame) -> None:
"""Give a stat/budget card its surface. Flat by design: the raised surface
plus a hairline is what separates it from the page — the old drop shadow
made a grid of these look like it was hovering off the screen."""
p = current_palette()
frame.setStyleSheet(
f"QFrame {{ background: {p.surface}; border: 1px solid {p.border};"
f" border-radius: {p.radius_lg}px; }}")
class StatCard(QFrame):
"""A titled value card (e.g. token count + its cost as the subtitle) —
shared by Dashboard and Monitoring's token/cost displays."""
def __init__(self):
super().__init__()
self.setFrameShape(QFrame.StyledPanel)
self.setStyleSheet(
"QFrame { border: 1px solid rgba(140,146,152,0.35); border-radius: 16px; }")
shadow = QGraphicsDropShadowEffect(self)
shadow.setBlurRadius(18)
shadow.setOffset(0, 3)
shadow.setColor(QColor(0, 0, 0, 60))
self.setGraphicsEffect(shadow)
self.setFrameShape(QFrame.NoFrame)
_style_card(self)
lay = QVBoxLayout(self)
self.title_lbl = QLabel("")
self.title_lbl.setObjectName("hint")
self.title_lbl.setStyleSheet("border: none;")
self.value_lbl = QLabel("—")
self.value_lbl.setStyleSheet("border: none; font-size: 20px; font-weight: 700;")
self.value_lbl.setStyleSheet("border: none; font-size: 22px; font-weight: 600;")
self.sub_lbl = QLabel("")
self.sub_lbl.setObjectName("hint")
self.sub_lbl.setStyleSheet("border: none;")
@@ -66,20 +70,14 @@ class BudgetCard(QFrame):
def __init__(self):
super().__init__()
self.setFrameShape(QFrame.StyledPanel)
self.setStyleSheet(
"QFrame { border: 1px solid rgba(140,146,152,0.35); border-radius: 16px; }")
shadow = QGraphicsDropShadowEffect(self)
shadow.setBlurRadius(18)
shadow.setOffset(0, 3)
shadow.setColor(QColor(0, 0, 0, 60))
self.setGraphicsEffect(shadow)
self.setFrameShape(QFrame.NoFrame)
_style_card(self)
lay = QVBoxLayout(self)
self.title_lbl = QLabel("")
self.title_lbl.setObjectName("hint")
self.title_lbl.setStyleSheet("border: none;")
self.value_lbl = QLabel("—")
self._value_style = "border: none; font-size: 20px; font-weight: 700;"
self._value_style = "border: none; font-size: 22px; font-weight: 600;"
self.value_lbl.setStyleSheet(self._value_style)
self.sub_lbl = QLabel("")
self.sub_lbl.setObjectName("hint")
@@ -109,7 +107,7 @@ class BudgetCard(QFrame):
self.title_lbl.setText(title)
self.value_lbl.setText(value)
self.value_lbl.setStyleSheet(
self._value_style + (" color: #E5484D;" if warn else ""))
self._value_style + (f" color: {current_palette().danger};" if warn else ""))
self.sub_lbl.setText(sub)
@@ -185,15 +183,16 @@ class CollapseStrip(QWidget):
p = QPainter(self)
p.setRenderHint(QPainter.Antialiasing)
w = self.width()
accent = QColor(ACCENT) if self._hover else QColor("#8b8d98")
tok = current_palette()
accent = QColor(tok.accent) if self._hover else QColor(tok.text_faint)
# A small rounded "button" at the top carries the expand arrow so the
# collapsed panel always shows a clear, clickable affordance.
bw = min(w - 2.0, 16.0)
btn = QRectF((w - bw) / 2.0, 6.0, bw, 18.0)
p.setPen(QPen(QColor(139, 144, 150, 130), 1.0))
p.setBrush(QColor(155, 160, 166, 70) if self._hover else QColor(155, 160, 166, 32))
p.drawRoundedRect(btn, 4.0, 4.0)
p.setPen(QPen(QColor(tok.border_strong), 1.0))
p.setBrush(QColor(tok.hover if self._hover else tok.surface))
p.drawRoundedRect(btn, float(tok.radius_sm), float(tok.radius_sm))
cx = w / 2.0
cy = btn.center().y()
@@ -211,7 +210,7 @@ class CollapseStrip(QWidget):
# thin handle line below the button
p.setPen(Qt.NoPen)
p.setBrush(QColor(155, 160, 166, 90))
p.setBrush(QColor(tok.border_strong))
line_w = 2.0
x = (w - line_w) / 2.0
ltop = btn.bottom() + 6.0
@@ -226,7 +225,12 @@ class PlanSection(QWidget):
close). Hidden until it has steps; updated in place as the agent calls
``update_plan``."""
_COLORS = {STEP_RUNNING: ACCENT, STEP_DONE: "#6fe3a4", STEP_ERROR: "#ef6368"}
@staticmethod
def _step_color(status: str) -> str | None:
"""Row text colour per step status; None leaves the default. Resolved
per call so it follows a live theme switch."""
p = current_palette()
return {STEP_RUNNING: p.accent, STEP_DONE: p.success, STEP_ERROR: p.danger}.get(status)
@staticmethod
def _step_icon(status: str):
@@ -272,7 +276,7 @@ class PlanSection(QWidget):
continue
status = str((s or {}).get("status", STEP_PENDING)).strip().lower()
item = QListWidgetItem(self._step_icon(status), f" {title}")
color = self._COLORS.get(status)
color = self._step_color(status)
if color:
item.setForeground(QColor(color))
self.list.addItem(item)