diff --git a/gui.py b/gui.py
index fb59115..48bc345 100644
--- a/gui.py
+++ b/gui.py
@@ -45,23 +45,48 @@ GPU_STATUS_INTERVAL_MS = 1000
# --- Visual theme ------------------------------------------------------
# An explicit dark palette rather than relying on the OS theme, so the app
# looks the same everywhere instead of "Qt follows Windows' dark app theme".
-BG_APP = "#0b0d12"
-BG_PANEL = "#12151c"
-BG_ELEVATED = "#181c25"
-BORDER = "#262b36"
-BORDER_STRONG = "#333949"
-TEXT_PRIMARY = "#e6e8ee"
-TEXT_SECONDARY = "#8b92a5"
-ACCENT = "#6366f1"
-ACCENT_HOVER = "#7b7ef4"
-ACCENT_PRESSED = "#4f52d6"
+# Brand palette: jet black / blue slate / silver / white / coral glow.
+JET_BLACK = "#2d3142"
+BLUE_SLATE = "#4f5d75"
+SILVER = "#bfc0c0"
+WHITE = "#ffffff"
+CORAL_GLOW = "#ef8354"
+
+BG_APP = "#242735" # jet black, darkened further for the outer chrome
+BG_PANEL = JET_BLACK
+BG_ELEVATED = BLUE_SLATE
+BORDER = "rgba(191, 192, 192, 0.35)" # silver, translucent
+BORDER_STRONG = "rgba(191, 192, 192, 0.55)"
+TEXT_PRIMARY = WHITE
+TEXT_SECONDARY = SILVER
+ACCENT = CORAL_GLOW
+ACCENT_HOVER = "#ff9768"
+ACCENT_PRESSED = "#cb6f47"
+
+# Green -> yellow -> red ramp used to color the GPU/VRAM load readout.
+LOAD_LOW = (0x22, 0xc5, 0x5e)
+LOAD_MID = (0xea, 0xb3, 0x08)
+LOAD_HIGH = (0xef, 0x44, 0x44)
+
+
+def _load_color(pct: float) -> str:
+ """Hex color for a 0-100 load percentage, interpolated low->mid->high."""
+ pct = max(0.0, min(100.0, pct))
+ if pct <= 50:
+ c1, c2, t = LOAD_LOW, LOAD_MID, pct / 50.0
+ else:
+ c1, c2, t = LOAD_MID, LOAD_HIGH, (pct - 50) / 50.0
+ r = round(c1[0] + (c2[0] - c1[0]) * t)
+ g = round(c1[1] + (c2[1] - c1[1]) * t)
+ b = round(c1[2] + (c2[2] - c1[2]) * t)
+ return f"#{r:02x}{g:02x}{b:02x}"
# (accent, background) per chat role, used for the accent stripe and tinted
# card background of each message.
ROLE_STYLES = {
- "user": ("#60a5fa", "#141b2b"),
- "assistant": ("#34d399", "#0f211c"),
- "system": ("#9aa1b4", "#161922"),
+ "user": (CORAL_GLOW, "#3a2c26"),
+ "assistant": (SILVER, "#2f3342"),
+ "system": (BLUE_SLATE, "#262a36"),
}
_STYLESHEET = f"""
@@ -75,25 +100,15 @@ QWidget {{
font-size: 10.5pt;
}}
-#header {{
- border-bottom: 1px solid {BORDER};
-}}
-
#titleLabel {{
font-size: 15pt;
font-weight: 600;
color: {TEXT_PRIMARY};
}}
-#subtitleLabel {{
- font-size: 9pt;
- color: {TEXT_SECONDARY};
-}}
-
#badge {{
background-color: {BG_ELEVATED};
border: 1px solid {BORDER};
- border-radius: 11px;
padding: 4px 12px;
color: {TEXT_SECONDARY};
font-size: 9pt;
@@ -102,14 +117,12 @@ QWidget {{
QTextBrowser#chatView {{
background-color: {BG_PANEL};
border: 1px solid {BORDER};
- border-radius: 14px;
padding: 10px;
}}
QLineEdit#messageInput {{
background-color: {BG_ELEVATED};
border: 1px solid {BORDER};
- border-radius: 19px;
padding: 0 16px;
color: {TEXT_PRIMARY};
selection-background-color: {ACCENT};
@@ -125,9 +138,8 @@ QLineEdit#messageInput:disabled {{
QPushButton#sendButton {{
background-color: {ACCENT};
- color: #ffffff;
+ color: {WHITE};
border: none;
- border-radius: 19px;
padding: 0 22px;
font-weight: 600;
}}
@@ -165,7 +177,6 @@ QScrollBar:vertical {{
QScrollBar::handle:vertical {{
background: {BORDER_STRONG};
- border-radius: 5px;
min-height: 24px;
}}
@@ -205,7 +216,7 @@ def _dark_palette() -> QPalette:
palette.setColor(QPalette.ColorRole.Button, QColor(BG_ELEVATED))
palette.setColor(QPalette.ColorRole.ButtonText, QColor(TEXT_PRIMARY))
palette.setColor(QPalette.ColorRole.Highlight, QColor(ACCENT))
- palette.setColor(QPalette.ColorRole.HighlightedText, QColor("#ffffff"))
+ palette.setColor(QPalette.ColorRole.HighlightedText, QColor(WHITE))
return palette
@@ -220,14 +231,14 @@ def _make_app_icon() -> QIcon:
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
gradient = QLinearGradient(0, 0, size, size)
- gradient.setColorAt(0.0, QColor(ACCENT_HOVER))
- gradient.setColorAt(1.0, QColor(ACCENT_PRESSED))
+ gradient.setColorAt(0.0, QColor(JET_BLACK))
+ gradient.setColorAt(1.0, QColor(CORAL_GLOW))
path = QPainterPath()
- path.addRoundedRect(2, 2, size - 4, size - 4, 16, 16)
+ path.addRect(2, 2, size - 4, size - 4)
painter.fillPath(path, gradient)
- painter.setPen(QColor("#ffffff"))
+ painter.setPen(QColor(WHITE))
painter.setFont(QFont("Segoe UI", 28, QFont.Weight.Bold))
painter.drawText(pixmap.rect(), Qt.AlignmentFlag.AlignCenter, "D")
painter.end()
@@ -273,8 +284,10 @@ _CODE_FG = "#f8f8f2"
_CODE_BLOCK_FORMAT = QTextBlockFormat()
_CODE_BLOCK_FORMAT.setBackground(QColor(_CODE_BG))
_CODE_BLOCK_FORMAT.setLeftMargin(10)
-_CODE_BLOCK_FORMAT.setTopMargin(1)
-_CODE_BLOCK_FORMAT.setBottomMargin(1)
+# Each source line is its own QTextBlock (see _append_markdown_block); Qt's
+# block background doesn't paint into top/bottom margins, so any non-zero
+# margin here left a sliver of the message card's own background showing
+# between every line, reading as horizontal gaps through the code block.
_CODE_CHAR_FORMAT = QTextCharFormat()
_CODE_CHAR_FORMAT.setFontFamilies(["Consolas", "Courier New"])
@@ -418,10 +431,7 @@ class ChatWindow(QMainWindow):
title_box.setSpacing(0)
title_label = QLabel("Dev Assist")
title_label.setObjectName("titleLabel")
- subtitle_label = QLabel("Local LLM · tool-augmented")
- subtitle_label.setObjectName("subtitleLabel")
title_box.addWidget(title_label)
- title_box.addWidget(subtitle_label)
header_layout.addLayout(title_box)
header_layout.addStretch(1)
@@ -504,7 +514,13 @@ class ChatWindow(QMainWindow):
used_gb = mem.used / (1024**3)
total_gb = mem.total / (1024**3)
- self.gpu_status_label.setText(f"GPU {util.gpu}% · VRAM {used_gb:.1f}/{total_gb:.1f} GB")
+ vram_pct = (mem.used / mem.total * 100) if mem.total else 0.0
+ gpu_color = _load_color(util.gpu)
+ vram_color = _load_color(vram_pct)
+ self.gpu_status_label.setText(
+ f'GPU {util.gpu}% · '
+ f'VRAM {used_gb:.1f}/{total_gb:.1f} GB'
+ )
def _set_input_enabled(self, enabled: bool) -> None:
self.input_box.setEnabled(enabled)