fix gaps between dark text code snippet
This commit is contained in:
1 parent
71ee5b2213
commit
7543798a0e
1 file changed
+55
-39
@@ -45,23 +45,48 @@ GPU_STATUS_INTERVAL_MS = 1000
|
|||||||
# --- Visual theme ------------------------------------------------------
|
# --- Visual theme ------------------------------------------------------
|
||||||
# An explicit dark palette rather than relying on the OS theme, so the app
|
# 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".
|
# looks the same everywhere instead of "Qt follows Windows' dark app theme".
|
||||||
BG_APP = "#0b0d12"
|
# Brand palette: jet black / blue slate / silver / white / coral glow.
|
||||||
BG_PANEL = "#12151c"
|
JET_BLACK = "#2d3142"
|
||||||
BG_ELEVATED = "#181c25"
|
BLUE_SLATE = "#4f5d75"
|
||||||
BORDER = "#262b36"
|
SILVER = "#bfc0c0"
|
||||||
BORDER_STRONG = "#333949"
|
WHITE = "#ffffff"
|
||||||
TEXT_PRIMARY = "#e6e8ee"
|
CORAL_GLOW = "#ef8354"
|
||||||
TEXT_SECONDARY = "#8b92a5"
|
|
||||||
ACCENT = "#6366f1"
|
BG_APP = "#242735" # jet black, darkened further for the outer chrome
|
||||||
ACCENT_HOVER = "#7b7ef4"
|
BG_PANEL = JET_BLACK
|
||||||
ACCENT_PRESSED = "#4f52d6"
|
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
|
# (accent, background) per chat role, used for the accent stripe and tinted
|
||||||
# card background of each message.
|
# card background of each message.
|
||||||
ROLE_STYLES = {
|
ROLE_STYLES = {
|
||||||
"user": ("#60a5fa", "#141b2b"),
|
"user": (CORAL_GLOW, "#3a2c26"),
|
||||||
"assistant": ("#34d399", "#0f211c"),
|
"assistant": (SILVER, "#2f3342"),
|
||||||
"system": ("#9aa1b4", "#161922"),
|
"system": (BLUE_SLATE, "#262a36"),
|
||||||
}
|
}
|
||||||
|
|
||||||
_STYLESHEET = f"""
|
_STYLESHEET = f"""
|
||||||
@@ -75,25 +100,15 @@ QWidget {{
|
|||||||
font-size: 10.5pt;
|
font-size: 10.5pt;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
#header {{
|
|
||||||
border-bottom: 1px solid {BORDER};
|
|
||||||
}}
|
|
||||||
|
|
||||||
#titleLabel {{
|
#titleLabel {{
|
||||||
font-size: 15pt;
|
font-size: 15pt;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: {TEXT_PRIMARY};
|
color: {TEXT_PRIMARY};
|
||||||
}}
|
}}
|
||||||
|
|
||||||
#subtitleLabel {{
|
|
||||||
font-size: 9pt;
|
|
||||||
color: {TEXT_SECONDARY};
|
|
||||||
}}
|
|
||||||
|
|
||||||
#badge {{
|
#badge {{
|
||||||
background-color: {BG_ELEVATED};
|
background-color: {BG_ELEVATED};
|
||||||
border: 1px solid {BORDER};
|
border: 1px solid {BORDER};
|
||||||
border-radius: 11px;
|
|
||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
color: {TEXT_SECONDARY};
|
color: {TEXT_SECONDARY};
|
||||||
font-size: 9pt;
|
font-size: 9pt;
|
||||||
@@ -102,14 +117,12 @@ QWidget {{
|
|||||||
QTextBrowser#chatView {{
|
QTextBrowser#chatView {{
|
||||||
background-color: {BG_PANEL};
|
background-color: {BG_PANEL};
|
||||||
border: 1px solid {BORDER};
|
border: 1px solid {BORDER};
|
||||||
border-radius: 14px;
|
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
QLineEdit#messageInput {{
|
QLineEdit#messageInput {{
|
||||||
background-color: {BG_ELEVATED};
|
background-color: {BG_ELEVATED};
|
||||||
border: 1px solid {BORDER};
|
border: 1px solid {BORDER};
|
||||||
border-radius: 19px;
|
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
color: {TEXT_PRIMARY};
|
color: {TEXT_PRIMARY};
|
||||||
selection-background-color: {ACCENT};
|
selection-background-color: {ACCENT};
|
||||||
@@ -125,9 +138,8 @@ QLineEdit#messageInput:disabled {{
|
|||||||
|
|
||||||
QPushButton#sendButton {{
|
QPushButton#sendButton {{
|
||||||
background-color: {ACCENT};
|
background-color: {ACCENT};
|
||||||
color: #ffffff;
|
color: {WHITE};
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 19px;
|
|
||||||
padding: 0 22px;
|
padding: 0 22px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}}
|
}}
|
||||||
@@ -165,7 +177,6 @@ QScrollBar:vertical {{
|
|||||||
|
|
||||||
QScrollBar::handle:vertical {{
|
QScrollBar::handle:vertical {{
|
||||||
background: {BORDER_STRONG};
|
background: {BORDER_STRONG};
|
||||||
border-radius: 5px;
|
|
||||||
min-height: 24px;
|
min-height: 24px;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
@@ -205,7 +216,7 @@ def _dark_palette() -> QPalette:
|
|||||||
palette.setColor(QPalette.ColorRole.Button, QColor(BG_ELEVATED))
|
palette.setColor(QPalette.ColorRole.Button, QColor(BG_ELEVATED))
|
||||||
palette.setColor(QPalette.ColorRole.ButtonText, QColor(TEXT_PRIMARY))
|
palette.setColor(QPalette.ColorRole.ButtonText, QColor(TEXT_PRIMARY))
|
||||||
palette.setColor(QPalette.ColorRole.Highlight, QColor(ACCENT))
|
palette.setColor(QPalette.ColorRole.Highlight, QColor(ACCENT))
|
||||||
palette.setColor(QPalette.ColorRole.HighlightedText, QColor("#ffffff"))
|
palette.setColor(QPalette.ColorRole.HighlightedText, QColor(WHITE))
|
||||||
return palette
|
return palette
|
||||||
|
|
||||||
|
|
||||||
@@ -220,14 +231,14 @@ def _make_app_icon() -> QIcon:
|
|||||||
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
||||||
|
|
||||||
gradient = QLinearGradient(0, 0, size, size)
|
gradient = QLinearGradient(0, 0, size, size)
|
||||||
gradient.setColorAt(0.0, QColor(ACCENT_HOVER))
|
gradient.setColorAt(0.0, QColor(JET_BLACK))
|
||||||
gradient.setColorAt(1.0, QColor(ACCENT_PRESSED))
|
gradient.setColorAt(1.0, QColor(CORAL_GLOW))
|
||||||
|
|
||||||
path = QPainterPath()
|
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.fillPath(path, gradient)
|
||||||
|
|
||||||
painter.setPen(QColor("#ffffff"))
|
painter.setPen(QColor(WHITE))
|
||||||
painter.setFont(QFont("Segoe UI", 28, QFont.Weight.Bold))
|
painter.setFont(QFont("Segoe UI", 28, QFont.Weight.Bold))
|
||||||
painter.drawText(pixmap.rect(), Qt.AlignmentFlag.AlignCenter, "D")
|
painter.drawText(pixmap.rect(), Qt.AlignmentFlag.AlignCenter, "D")
|
||||||
painter.end()
|
painter.end()
|
||||||
@@ -273,8 +284,10 @@ _CODE_FG = "#f8f8f2"
|
|||||||
_CODE_BLOCK_FORMAT = QTextBlockFormat()
|
_CODE_BLOCK_FORMAT = QTextBlockFormat()
|
||||||
_CODE_BLOCK_FORMAT.setBackground(QColor(_CODE_BG))
|
_CODE_BLOCK_FORMAT.setBackground(QColor(_CODE_BG))
|
||||||
_CODE_BLOCK_FORMAT.setLeftMargin(10)
|
_CODE_BLOCK_FORMAT.setLeftMargin(10)
|
||||||
_CODE_BLOCK_FORMAT.setTopMargin(1)
|
# Each source line is its own QTextBlock (see _append_markdown_block); Qt's
|
||||||
_CODE_BLOCK_FORMAT.setBottomMargin(1)
|
# 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 = QTextCharFormat()
|
||||||
_CODE_CHAR_FORMAT.setFontFamilies(["Consolas", "Courier New"])
|
_CODE_CHAR_FORMAT.setFontFamilies(["Consolas", "Courier New"])
|
||||||
@@ -418,10 +431,7 @@ class ChatWindow(QMainWindow):
|
|||||||
title_box.setSpacing(0)
|
title_box.setSpacing(0)
|
||||||
title_label = QLabel("Dev Assist")
|
title_label = QLabel("Dev Assist")
|
||||||
title_label.setObjectName("titleLabel")
|
title_label.setObjectName("titleLabel")
|
||||||
subtitle_label = QLabel("Local LLM · tool-augmented")
|
|
||||||
subtitle_label.setObjectName("subtitleLabel")
|
|
||||||
title_box.addWidget(title_label)
|
title_box.addWidget(title_label)
|
||||||
title_box.addWidget(subtitle_label)
|
|
||||||
header_layout.addLayout(title_box)
|
header_layout.addLayout(title_box)
|
||||||
header_layout.addStretch(1)
|
header_layout.addStretch(1)
|
||||||
|
|
||||||
@@ -504,7 +514,13 @@ class ChatWindow(QMainWindow):
|
|||||||
|
|
||||||
used_gb = mem.used / (1024**3)
|
used_gb = mem.used / (1024**3)
|
||||||
total_gb = mem.total / (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 <span style="color:{gpu_color};">{util.gpu}%</span> · '
|
||||||
|
f'VRAM <span style="color:{vram_color};">{used_gb:.1f}/{total_gb:.1f} GB</span>'
|
||||||
|
)
|
||||||
|
|
||||||
def _set_input_enabled(self, enabled: bool) -> None:
|
def _set_input_enabled(self, enabled: bool) -> None:
|
||||||
self.input_box.setEnabled(enabled)
|
self.input_box.setEnabled(enabled)
|
||||||
|
|||||||
Reference in new issue
Block a user