theme: upgrade palette from Catppuccin Mocha to Frappé
Replaces all dark-Mocha colours (#1e1e2e base, #313244 surface0, etc.) with the noticeably brighter Frappé variants (#303446 base, #414559 surface0, etc.) across the QSS and every hardcoded colour in Python source files (syntax highlighter, completer popup, log viewer, explain view, table viewer, schema browser, icons, etc.). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@ class ConnectionProfile:
|
||||
database: str = ""
|
||||
username: str = ""
|
||||
password: str = field(default="", repr=False)
|
||||
color: str = "#89b4fa"
|
||||
color: str = "#8caaee"
|
||||
id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
||||
ssl: bool = False
|
||||
ssl_cert: str = ""
|
||||
@@ -71,7 +71,7 @@ class ConnectionProfile:
|
||||
port=data.get("port", 3306),
|
||||
database=data.get("database", ""),
|
||||
username=data.get("username", ""),
|
||||
color=data.get("color", "#89b4fa"),
|
||||
color=data.get("color", "#8caaee"),
|
||||
ssl=data.get("ssl", False),
|
||||
ssl_cert=data.get("ssl_cert", ""),
|
||||
ssl_key=data.get("ssl_key", ""),
|
||||
|
||||
@@ -11,7 +11,7 @@ from PyQt6.QtGui import QColor, QFont
|
||||
class ResultTableModel(QAbstractTableModel):
|
||||
"""Immutable result-set model — replaces data via set_data()."""
|
||||
|
||||
NULL_COLOR = QColor("#6c7086") # muted grey for NULL
|
||||
NULL_COLOR = QColor("#737994") # muted grey for NULL
|
||||
NUM_ALIGN = Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter
|
||||
TEXT_ALIGN = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ from app.config.connections import save_profile
|
||||
class ColorButton(QPushButton):
|
||||
"""A button that shows a solid colour and opens a colour picker."""
|
||||
|
||||
def __init__(self, color: str = "#89b4fa", parent=None):
|
||||
def __init__(self, color: str = "#8caaee", parent=None):
|
||||
super().__init__(parent)
|
||||
self._color = color
|
||||
self.setFixedSize(32, 24)
|
||||
@@ -26,7 +26,7 @@ class ColorButton(QPushButton):
|
||||
|
||||
def _refresh(self):
|
||||
self.setStyleSheet(
|
||||
f"background-color:{self._color}; border:1px solid #45475a; border-radius:4px;"
|
||||
f"background-color:{self._color}; border:1px solid #51576d; border-radius:4px;"
|
||||
)
|
||||
|
||||
def _pick(self):
|
||||
|
||||
+11
-11
@@ -36,12 +36,12 @@ def _cost_color(val_str: str) -> QColor:
|
||||
try:
|
||||
v = float(str(val_str).replace(",", ""))
|
||||
except (ValueError, TypeError):
|
||||
return QColor("#cdd6f4") # neutral (Catppuccin text)
|
||||
return QColor("#c6d0f5") # neutral (Catppuccin text)
|
||||
if v <= 10:
|
||||
return QColor("#a6e3a1") # green — cheap
|
||||
return QColor("#a6d189") # green — cheap
|
||||
if v <= 1_000:
|
||||
return QColor("#f9e2af") # yellow — moderate
|
||||
return QColor("#f38ba8") # red — expensive
|
||||
return QColor("#e5c890") # yellow — moderate
|
||||
return QColor("#e78284") # red — expensive
|
||||
|
||||
|
||||
# ── Background worker ─────────────────────────────────────────────────────────
|
||||
@@ -101,9 +101,9 @@ def _build_mysql_tree(tree: QTreeWidget, cols: list, rows: list):
|
||||
good_types = {"const", "eq_ref", "ref", "system"}
|
||||
jt_lower = join_type.lower()
|
||||
if jt_lower in bad_types:
|
||||
item.setForeground(1, QBrush(QColor("#f38ba8")))
|
||||
item.setForeground(1, QBrush(QColor("#e78284")))
|
||||
elif jt_lower in good_types:
|
||||
item.setForeground(1, QBrush(QColor("#a6e3a1")))
|
||||
item.setForeground(1, QBrush(QColor("#a6d189")))
|
||||
|
||||
# Colour rows estimate
|
||||
item.setForeground(3, QBrush(_cost_color(est_rows)))
|
||||
@@ -203,9 +203,9 @@ def _build_sqlite_tree(tree: QTreeWidget, cols: list, rows: list):
|
||||
# Colour SCAN (bad) vs SEARCH/INDEX (good)
|
||||
detail_str = str(detail).upper()
|
||||
if "SCAN" in detail_str and "INDEX" not in detail_str:
|
||||
item.setForeground(1, QBrush(QColor("#f38ba8")))
|
||||
item.setForeground(1, QBrush(QColor("#e78284")))
|
||||
elif "INDEX" in detail_str or "SEARCH" in detail_str:
|
||||
item.setForeground(1, QBrush(QColor("#a6e3a1")))
|
||||
item.setForeground(1, QBrush(QColor("#a6d189")))
|
||||
|
||||
items[rid] = item
|
||||
parent_item = items.get(parent)
|
||||
@@ -334,8 +334,8 @@ class ExplainPanel(QWidget):
|
||||
legend_row = QHBoxLayout()
|
||||
legend_row.setContentsMargins(8, 2, 0, 4)
|
||||
for colour, label in [
|
||||
("#a6e3a1", "Efficient"), ("#f9e2af", "Moderate"),
|
||||
("#f38ba8", "Expensive / SCAN"),
|
||||
("#a6d189", "Efficient"), ("#e5c890", "Moderate"),
|
||||
("#e78284", "Expensive / SCAN"),
|
||||
]:
|
||||
dot = QLabel("●")
|
||||
dot.setStyleSheet(f"color: {colour};")
|
||||
@@ -414,7 +414,7 @@ class ExplainPanel(QWidget):
|
||||
self._status_lbl.setText(f"Error: {msg[:80]}")
|
||||
self._tree.clear()
|
||||
err_item = QTreeWidgetItem([f"⚠ {msg}"])
|
||||
err_item.setForeground(0, QBrush(QColor("#f38ba8")))
|
||||
err_item.setForeground(0, QBrush(QColor("#e78284")))
|
||||
self._tree.setColumnCount(1)
|
||||
self._tree.setHeaderLabels(["Error"])
|
||||
self._tree.addTopLevelItem(err_item)
|
||||
|
||||
@@ -30,14 +30,14 @@ from PyQt6.QtGui import (
|
||||
|
||||
from app.utils.logger import get_log_path
|
||||
|
||||
# ── Level colours (Catppuccin Mocha palette) ──────────────────────────────────
|
||||
# ── Level colours (Catppuccin Frappé palette) ─────────────────────────────────
|
||||
|
||||
_LEVEL_COLOURS: dict[str, str] = {
|
||||
"DEBUG": "#6c7086", # surface2 / dimmed
|
||||
"INFO": "#cdd6f4", # text (default)
|
||||
"WARNING": "#f9e2af", # yellow
|
||||
"ERROR": "#f38ba8", # red
|
||||
"CRITICAL": "#ff79c6", # pink / bright red
|
||||
"DEBUG": "#737994", # overlay0 / dimmed
|
||||
"INFO": "#c6d0f5", # text (default)
|
||||
"WARNING": "#e5c890", # yellow
|
||||
"ERROR": "#e78284", # red
|
||||
"CRITICAL": "#f4b8e4", # pink / bright
|
||||
}
|
||||
|
||||
_LEVEL_ORDER = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
||||
@@ -85,8 +85,8 @@ class _LogHighlighter(QSyntaxHighlighter):
|
||||
# Highlight search matches in bright yellow
|
||||
if self._search:
|
||||
search_fmt = QTextCharFormat()
|
||||
search_fmt.setBackground(QColor("#f9e2af"))
|
||||
search_fmt.setForeground(QColor("#1e1e2e"))
|
||||
search_fmt.setBackground(QColor("#e5c890"))
|
||||
search_fmt.setForeground(QColor("#303446"))
|
||||
idx = text.lower().find(self._search)
|
||||
while idx != -1:
|
||||
self.setFormat(idx, len(self._search), search_fmt)
|
||||
@@ -107,7 +107,7 @@ class _LevelBtn(QPushButton):
|
||||
def _apply_style(self, checked: bool):
|
||||
if checked:
|
||||
self.setStyleSheet(
|
||||
f"QPushButton {{ background: {self._colour}; color: #1e1e2e; "
|
||||
f"QPushButton {{ background: {self._colour}; color: #303446; "
|
||||
f"border: none; border-radius: 3px; font-weight: bold; }}"
|
||||
)
|
||||
else:
|
||||
|
||||
@@ -153,7 +153,7 @@ class QueryHistoryPanel(QWidget):
|
||||
for c, val in enumerate(items):
|
||||
item = QTableWidgetItem(val)
|
||||
if c == 3 and status == "ERROR":
|
||||
item.setForeground(QColor("#f38ba8"))
|
||||
item.setForeground(QColor("#e78284"))
|
||||
self._table.setItem(r, c, item)
|
||||
|
||||
def _filter(self, text: str):
|
||||
|
||||
@@ -96,7 +96,7 @@ class SchemaBrowser(QWidget):
|
||||
# Dim colour — show the profile colour but muted
|
||||
color = QColor(profile.color)
|
||||
color.setAlpha(140)
|
||||
item.setForeground(0, QBrush(QColor("#6c7086"))) # greyed out
|
||||
item.setForeground(0, QBrush(QColor("#737994"))) # greyed out
|
||||
item.setIcon(0, get_icon(profile.db_type, 16))
|
||||
item.setToolTip(0, self._connection_tooltip(profile))
|
||||
|
||||
@@ -350,9 +350,9 @@ class SchemaBrowser(QWidget):
|
||||
child = QTreeWidgetItem([label])
|
||||
child.setData(0, Qt.ItemDataRole.UserRole, (NT["column"], "", "", c.name))
|
||||
if c.is_primary_key:
|
||||
child.setForeground(0, QBrush(QColor("#f9e2af")))
|
||||
child.setForeground(0, QBrush(QColor("#e5c890")))
|
||||
elif c.is_foreign_key:
|
||||
child.setForeground(0, QBrush(QColor("#89dceb")))
|
||||
child.setForeground(0, QBrush(QColor("#99d1db")))
|
||||
item.addChild(child)
|
||||
|
||||
def _load_indexes(self, item, pid, db, table):
|
||||
@@ -386,7 +386,7 @@ class SchemaBrowser(QWidget):
|
||||
|
||||
def _show_error(self, item, msg):
|
||||
err = QTreeWidgetItem([f"❌ {msg}"])
|
||||
err.setForeground(0, QBrush(QColor("#f38ba8")))
|
||||
err.setForeground(0, QBrush(QColor("#e78284")))
|
||||
item.addChild(err)
|
||||
|
||||
# ── Context menu ──────────────────────────────────────────────────────────
|
||||
|
||||
@@ -53,19 +53,19 @@ class _CompletionPopup(QListWidget):
|
||||
|
||||
self.setStyleSheet("""
|
||||
QListWidget {
|
||||
background: #1e1e2e;
|
||||
color: #cdd6f4;
|
||||
border: 1px solid #45475a;
|
||||
background: #292c3c;
|
||||
color: #c6d0f5;
|
||||
border: 1px solid #51576d;
|
||||
border-radius: 4px;
|
||||
padding: 2px;
|
||||
outline: none;
|
||||
}
|
||||
QListWidget::item { padding: 2px 8px; }
|
||||
QListWidget::item:selected {
|
||||
background: #313244;
|
||||
color: #cdd6f4;
|
||||
background: #414559;
|
||||
color: #c6d0f5;
|
||||
}
|
||||
QListWidget::item:hover { background: #2a2a3c; }
|
||||
QListWidget::item:hover { background: #363a4f; }
|
||||
""")
|
||||
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
||||
|
||||
@@ -81,7 +81,7 @@ class CodeEditor(QPlainTextEdit):
|
||||
extra = []
|
||||
if not self.isReadOnly():
|
||||
sel = QTextEdit.ExtraSelection()
|
||||
sel.format.setBackground(QColor("#2a2a3c"))
|
||||
sel.format.setBackground(QColor("#3d4155"))
|
||||
sel.format.setProperty(QTextFormat.Property.FullWidthSelection, True)
|
||||
sel.cursor = self.textCursor()
|
||||
sel.cursor.clearSelection()
|
||||
@@ -90,7 +90,7 @@ class CodeEditor(QPlainTextEdit):
|
||||
|
||||
def line_number_area_paint_event(self, event):
|
||||
painter = QPainter(self._line_area)
|
||||
painter.fillRect(event.rect(), QColor("#1a1a2e"))
|
||||
painter.fillRect(event.rect(), QColor("#2a2d3e"))
|
||||
|
||||
block = self.firstVisibleBlock()
|
||||
number = block.blockNumber()
|
||||
@@ -100,7 +100,7 @@ class CodeEditor(QPlainTextEdit):
|
||||
|
||||
while block.isValid() and top <= event.rect().bottom():
|
||||
if block.isVisible() and bottom >= event.rect().top():
|
||||
painter.setPen(QColor("#45475a"))
|
||||
painter.setPen(QColor("#51576d"))
|
||||
painter.drawText(
|
||||
0, top, self._line_area.width() - 6,
|
||||
self.fontMetrics().height(),
|
||||
@@ -339,7 +339,7 @@ class FindBar(QWidget):
|
||||
self._do_find(backward=True)
|
||||
|
||||
def _set_no_match(self, no_match: bool):
|
||||
color = "#f38ba8" if no_match else ""
|
||||
color = "#e78284" if no_match else ""
|
||||
self._find_input.setStyleSheet(
|
||||
f"background: {color};" if no_match else ""
|
||||
)
|
||||
|
||||
@@ -17,15 +17,15 @@ def _fmt(color: str, bold: bool = False, italic: bool = False) -> QTextCharForma
|
||||
return f
|
||||
|
||||
|
||||
# ── Token categories (Catppuccin Mocha palette) ───────────────────────────────
|
||||
_KEYWORD_FMT = _fmt("#89b4fa", bold=True) # blue — DDL/DML/control
|
||||
_TYPE_FMT = _fmt("#fab387") # peach — data types
|
||||
_FUNCTION_FMT = _fmt("#a6e3a1") # green — functions
|
||||
_STRING_FMT = _fmt("#a6e3a1") # green — string literals
|
||||
_NUMBER_FMT = _fmt("#fab387") # orange — numeric literals
|
||||
_COMMENT_FMT = _fmt("#6c7086", italic=True) # grey — comments
|
||||
_OPERATOR_FMT = _fmt("#cba6f7") # mauve — operators/special
|
||||
_STAR_FMT = _fmt("#cba6f7", bold=True) # mauve — * wildcard
|
||||
# ── Token categories (Catppuccin Frappé palette) ─────────────────────────────
|
||||
_KEYWORD_FMT = _fmt("#8caaee", bold=True) # blue — DDL/DML/control
|
||||
_TYPE_FMT = _fmt("#ef9f76") # peach — data types
|
||||
_FUNCTION_FMT = _fmt("#a6d189") # green — functions
|
||||
_STRING_FMT = _fmt("#a6d189") # green — string literals
|
||||
_NUMBER_FMT = _fmt("#ef9f76") # orange — numeric literals
|
||||
_COMMENT_FMT = _fmt("#737994", italic=True) # grey — comments
|
||||
_OPERATOR_FMT = _fmt("#ca9ee6") # mauve — operators/special
|
||||
_STAR_FMT = _fmt("#ca9ee6", bold=True) # mauve — * wildcard
|
||||
|
||||
# ── Keyword lists ─────────────────────────────────────────────────────────────
|
||||
_KEYWORDS = [
|
||||
@@ -101,9 +101,9 @@ class SQLHighlighter(QSyntaxHighlighter):
|
||||
# Single-quoted strings
|
||||
(QRegularExpression(r"'[^'\\]*(?:\\.[^'\\]*)*'"), _STRING_FMT),
|
||||
# Double-quoted identifiers
|
||||
(QRegularExpression(r'"[^"]*"'), _fmt("#89dceb")),
|
||||
(QRegularExpression(r'"[^"]*"'), _fmt("#99d1db")),
|
||||
# Backtick identifiers (MySQL)
|
||||
(QRegularExpression(r"`[^`]*`"), _fmt("#89dceb")),
|
||||
(QRegularExpression(r"`[^`]*`"), _fmt("#99d1db")),
|
||||
# * wildcard
|
||||
(QRegularExpression(r"\bSELECT\s+\*|\*(?=\s*FROM)",
|
||||
QRegularExpression.PatternOption.CaseInsensitiveOption),
|
||||
|
||||
@@ -171,9 +171,9 @@ class TableStructureView(QWidget):
|
||||
item = QTableWidgetItem(str(val))
|
||||
item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
if col.is_primary_key and c == 0:
|
||||
item.setForeground(QColor("#f9e2af"))
|
||||
item.setForeground(QColor("#e5c890"))
|
||||
elif col.is_foreign_key and c == 0:
|
||||
item.setForeground(QColor("#89dceb"))
|
||||
item.setForeground(QColor("#99d1db"))
|
||||
t.setItem(r, c, item)
|
||||
|
||||
def _load_indexes(self):
|
||||
|
||||
@@ -148,17 +148,17 @@ class EditableTableModel(QAbstractTableModel):
|
||||
|
||||
if role == Qt.ItemDataRole.ForegroundRole:
|
||||
if self._is_deleted_row(r):
|
||||
return QBrush(QColor("#f38ba8")) # red — pending delete
|
||||
return QBrush(QColor("#e78284")) # red — pending delete
|
||||
if (r, c) in self._dirty:
|
||||
return QBrush(QColor("#fab387")) # orange — edited
|
||||
return QBrush(QColor("#ef9f76")) # orange — edited
|
||||
if val is None:
|
||||
return QBrush(QColor("#6c7086")) # grey — NULL
|
||||
return QBrush(QColor("#737994")) # grey — NULL
|
||||
|
||||
if role == Qt.ItemDataRole.BackgroundRole:
|
||||
if self._is_deleted_row(r):
|
||||
return QBrush(QColor("#2a1a1e"))
|
||||
return QBrush(QColor("#3a2530"))
|
||||
if (r, c) in self._dirty:
|
||||
return QBrush(QColor("#2a1f1a"))
|
||||
return QBrush(QColor("#3a2f25"))
|
||||
|
||||
return None
|
||||
|
||||
@@ -351,7 +351,7 @@ class TableViewer(QWidget):
|
||||
|
||||
# Separator
|
||||
sep = QLabel("|")
|
||||
sep.setStyleSheet("color:#45475a; padding:0 4px;")
|
||||
sep.setStyleSheet("color:#51576d; padding:0 4px;")
|
||||
|
||||
# CRUD buttons
|
||||
self._add_btn = QPushButton("➕ Add")
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ ICONS = {
|
||||
|
||||
|
||||
def make_icon(glyph: str, size: int = 20,
|
||||
fg: str = "#cdd6f4", bg: str = "transparent") -> QIcon:
|
||||
fg: str = "#c6d0f5", bg: str = "transparent") -> QIcon:
|
||||
"""Create a QIcon from a Unicode glyph."""
|
||||
px = QPixmap(QSize(size, size))
|
||||
px.fill(Qt.GlobalColor.transparent)
|
||||
|
||||
Reference in New Issue
Block a user