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
+50 -57
View File
@@ -103,67 +103,58 @@ class HelpAgentWidget(QWidget):
self._apply_state()
# ---- theming ----------------------------------------------------------
def _compute_palette(self) -> Dict[str, str]:
"""Chat-body colours that FOLLOW the app's light/dark theme. The header
is intentionally NOT themed here (it stays a fixed light bar — see
_apply_style), only the conversation area adapts."""
from ..theme import resolve_theme
dark = resolve_theme(getattr(self.ctx.config, "theme", "system")) == "dark"
if dark:
return {
"panel_bg": "#16202b", "text": "#e3ebf5", "log_bg": "#0f1720",
"input_bg": "#1b2733", "border": "#33404d",
"user_bg": "#123a52", "user_label": "#58c0ee",
"bot_bg": "#232f3b", "bot_label": "#6fe3a4",
}
return {
"panel_bg": "#ffffff", "text": "#14212b", "log_bg": "#f7f9fb",
"input_bg": "#ffffff", "border": "#d5d9de",
"user_bg": "#dceff8", "user_label": "#0077B6",
"bot_bg": "#eef1f4", "bot_label": "#2f7d55",
}
def _compute_palette(self):
"""The app's design tokens for the theme in effect. The whole dock —
header included — follows the app theme; a header locked to a light
strip stranded a bright bar in the middle of the dark UI."""
from ..theme import palette
return palette(getattr(self.ctx.config, "theme", "system"))
def apply_theme(self) -> None:
"""Re-style + re-render when the app theme switches (called from
MainWindow._apply_theme). Header stays fixed; chat body re-colours."""
MainWindow._apply_theme). The whole dock re-colours, icons included —
icons are painted bitmaps, so they must be rebuilt, not restyled."""
self._pal = self._compute_palette()
self._apply_style()
muted = self._pal.text_muted
self.edge_tab.setIcon(icon("chevron-left", color=muted))
self.collapse_btn.setIcon(icon("chevron-right", color=muted))
self.min_btn.setIcon(icon("minus", color=muted))
self._render()
def _apply_style(self) -> None:
# The HEADER bar is a FIXED light strip in both themes (per request); only
# the chat body below follows the app's light/dark palette (self._pal).
from ..theme import ACCENT, ACCENT2, GRADIENT
"""The dock owns its own style sheet (it floats above the window, so the
app-wide sheet does not reach it cleanly) but draws every value from the
shared tokens — see theme.py."""
p = self._pal
r, rl = p.radius, p.radius_lg
self.setStyleSheet(f"""
/* Clean rounded app-icon badge (like image 2): a fixed light card
framing the icon — no QPushButton box. */
#helpLauncher {{ background: #e8f2fb; border: 1px solid #d3e3f2;
border-radius: 16px; }}
#helpLauncher:hover {{ background: #dcedfb; }}
#helpCollapseBtn, #helpEdgeTab {{ background: rgba(0,0,0,0.06); border: none;
border-radius: 6px; }}
#helpCollapseBtn:hover, #helpEdgeTab:hover {{ background: rgba(0,0,0,0.14); }}
#helpPanel {{ background: {p['panel_bg']}; border: 1px solid {p['border']};
border-radius: 14px; color: {p['text']}; }}
/* Faint-blue header bar — LOCKED light, dark title, in both themes.
The header AND its child labels set fixed backgrounds so the dark
theme never bleeds into the App-Assistant title strip. */
#helpHeader {{ background: #e8f2fb; border-bottom: 1px solid #d9e6f2;
border-top-left-radius: 14px; border-top-right-radius: 14px; }}
#helpHeader QLabel {{ background: transparent; color: #14212b; }}
#helpTitle {{ color: #14212b; font-weight: 700; font-size: 13px; background: transparent; }}
#helpMinBtn {{ background: transparent; border: none; }}
#helpMinBtn:hover {{ background: rgba(0,0,0,0.10); border-radius: 6px; }}
#helpLog {{ background: {p['log_bg']}; border: none; color: {p['text']}; padding: 4px 6px; }}
#helpInputRow {{ background: {p['panel_bg']}; border-bottom-left-radius: 14px;
border-bottom-right-radius: 14px; }}
#helpInput {{ border: 1px solid {p['border']}; border-radius: 8px; padding: 5px 8px;
background: {p['input_bg']}; color: {p['text']}; }}
#helpInput:focus {{ border: 1px solid {ACCENT}; }}
#helpSendBtn {{ background: {GRADIENT}; border: none; border-radius: 8px; }}
#helpSendBtn:hover {{ background: {ACCENT2}; }}
#helpSendBtn:disabled {{ background: #b7c0c9; }}
/* The app-icon badge that opens the dock: a plain card, no button box. */
#helpLauncher {{ background: {p.surface}; border: 1px solid {p.border};
border-radius: {rl}px; }}
#helpLauncher:hover {{ background: {p.hover}; }}
#helpCollapseBtn, #helpEdgeTab {{ background: {p.surface}; border: none;
border-radius: {r}px; }}
#helpCollapseBtn:hover, #helpEdgeTab:hover {{ background: {p.hover}; }}
#helpPanel {{ background: {p.surface}; border: 1px solid {p.border};
border-radius: {rl}px; color: {p.text}; }}
#helpHeader {{ background: {p.surface}; border-bottom: 1px solid {p.border};
border-top-left-radius: {rl}px; border-top-right-radius: {rl}px; }}
#helpHeader QLabel {{ background: transparent; color: {p.text}; }}
#helpTitle {{ color: {p.text}; font-weight: 600; font-size: 13px;
background: transparent; }}
#helpMinBtn {{ background: transparent; border: none; border-radius: {r}px; }}
#helpMinBtn:hover {{ background: {p.hover}; }}
#helpLog {{ background: {p.sunken}; border: none; color: {p.text};
padding: 4px 6px; }}
#helpInputRow {{ background: {p.surface};
border-bottom-left-radius: {rl}px; border-bottom-right-radius: {rl}px; }}
#helpInput {{ border: 1px solid {p.border}; border-radius: {r}px; padding: 5px 8px;
background: {p.surface_raised}; color: {p.text}; }}
#helpInput:focus {{ border: 1px solid {p.focus_ring}; }}
#helpSendBtn {{ background: {p.accent_solid}; border: none; border-radius: {r}px; }}
#helpSendBtn:hover {{ background: {p.accent_solid_hover}; }}
#helpSendBtn:disabled {{ background: {p.border_strong}; }}
""")
# ---- greeting / labels ------------------------------------------------
@@ -177,7 +168,7 @@ class HelpAgentWidget(QWidget):
# assistant back (chevron points left = "slide out").
self.edge_tab = QPushButton(self)
self.edge_tab.setObjectName("helpEdgeTab")
self.edge_tab.setIcon(icon("chevron-left", color="#5a6570"))
self.edge_tab.setIcon(icon("chevron-left", color=self._pal.text_muted))
self.edge_tab.setCursor(Qt.PointingHandCursor)
self.edge_tab.setToolTip(tr("help_agent.show_tooltip"))
self.edge_tab.clicked.connect(self._show_launcher)
@@ -186,7 +177,7 @@ class HelpAgentWidget(QWidget):
# A left-side chevron collapses the assistant to the edge…
self.collapse_btn = QPushButton(self)
self.collapse_btn.setObjectName("helpCollapseBtn")
self.collapse_btn.setIcon(icon("chevron-right", color="#5a6570"))
self.collapse_btn.setIcon(icon("chevron-right", color=self._pal.text_muted))
self.collapse_btn.setCursor(Qt.PointingHandCursor)
self.collapse_btn.setToolTip(tr("help_agent.hide_tooltip"))
self.collapse_btn.clicked.connect(self._hide_to_edge)
@@ -222,7 +213,7 @@ class HelpAgentWidget(QWidget):
hb.addWidget(self.title, 1)
self.min_btn = QPushButton(header)
self.min_btn.setObjectName("helpMinBtn")
self.min_btn.setIcon(icon("minus", color="#5a6570"))
self.min_btn.setIcon(icon("minus", color=self._pal.text_muted))
self.min_btn.setFixedSize(24, 24)
self.min_btn.setCursor(Qt.PointingHandCursor)
self.min_btn.setToolTip(tr("help_agent.collapse_tooltip"))
@@ -318,17 +309,19 @@ class HelpAgentWidget(QWidget):
p = self._pal
text = (content or "").replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
text = text.replace("\n", "<br>")
# bgcolor= is a solid-only HTML attribute, hence accent_wash (pre-blended)
# rather than the translucent accent_soft used in style sheets.
if who == "user":
align, bg, label_color = "right", p["user_bg"], p["user_label"]
align, bg, label_color = "right", p.accent_wash, p.accent
label = tr("chat.you")
else:
align, bg, label_color = "left", p["bot_bg"], p["bot_label"]
align, bg, label_color = "left", p.surface_raised, p.success
label = tr("help_agent.title")
return (
f'<table width="100%" cellspacing="0" cellpadding="0"><tr>'
f'<td align="{align}">'
f'<table width="80%" cellspacing="0" cellpadding="7" bgcolor="{bg}"><tr>'
f'<td style="color:{p["text"]};">'
f'<td style="color:{p.text};">'
f'<b style="color:{label_color};">{label}</b><br>{text}'
f'</td></tr></table></td></tr></table>'
'<div style="line-height:6px;">&nbsp;</div>' # gap between turns