From 4fcbe5a8794205a937fd9927beef565acb7284f9 Mon Sep 17 00:00:00 2001 From: NguyenND Date: Fri, 22 May 2026 10:02:16 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20update=20CLAUDE.md=20=E2=80=94=20themes?= =?UTF-8?q?,=20identifier=20quoting,=20SQL=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 16de520..bd53690 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -75,7 +75,7 @@ All user data lives under `~/.dbclient/`: The `ResultTableModel` (`app/models/result_table_model.py`) is a `QAbstractTableModel` — query results should always go through it rather than populating `QTableWidget` directly. ### Styling -`resources/style.qss` is a **Catppuccin Frappé** dark theme applied globally at startup in `main.py`. Widget-specific overrides belong here, not as inline `setStyleSheet()` calls. Key palette values: base `#303446`, surface0 `#414559`, surface1 `#51576d`, text `#c6d0f5`, blue `#8caaee`, green `#a6d189`, red `#e78284`, peach `#ef9f76`, mauve `#ca9ee6`, sky `#99d1db`. +Theming uses a single `resources/style_template.qss` with `@{token}` placeholders. `app/config/theme.py` defines seven palette dicts (`ALL_THEMES`) and `apply_qss(app)` substitutes tokens at runtime via regex — no duplicate `.qss` files. Available themes: `dark` (Catppuccin Frappé), `light` (Catppuccin Latte), `one_dark`, `nord`, `tokyo_night`, `dracula`, `github_light`. To add a new theme, add a palette dict to `ALL_THEMES` and register it in `_THEMES` in `preferences_dialog.py`. Widget-specific color overrides must call `get_palette()` at render time (not at import time) so they react to live theme switches. `is_dark()` returns True for all dark-family themes. ### Recent Files (`app/config/recent_files.py`) `load_recent()` / `add_recent(path)` / `clear_recent()` persist to `~/.dbclient/recent_files.json` (max 10 entries, dead paths filtered on load). Called from MainWindow File menu. @@ -103,7 +103,10 @@ All four drivers have a `_where(where: dict) → (clause_str, params)` static he `EditorTab._format_sql()` uses `sqlparse.format(..., reindent=True, keyword_case='upper')`. Requires `sqlparse>=0.5.0` in requirements.txt. ### Column statistics worker -`_StatsWorker` in `app/ui/column_stats_dialog.py` runs `COUNT/COUNT(col)/COUNT(DISTINCT)/MIN/MAX` in one query and AVG in a second (AVG silently returns None for non-numeric columns). Both queries are quoted with `"col"` and `"table"` to handle reserved words. +`_StatsWorker` in `app/ui/column_stats_dialog.py` runs `COUNT/COUNT(col)/COUNT(DISTINCT)/MIN/MAX` in one query and AVG in a second (AVG silently returns None for non-numeric columns). Identifiers are quoted via `driver.quote_identifier(name)` — backticks for MySQL, brackets for MSSQL, double-quotes for PostgreSQL/SQLite — defined on `BaseDriver` with per-driver overrides. + +### Identifier quoting +`BaseDriver.quote_identifier(name)` returns `"name"` by default. `MySQLDriver` overrides to `` `name` `` and `MSSQLDriver` to `[name]`. Always use this method when building dynamic SQL with column/table names — never hardcode a quote style. --- @@ -125,7 +128,7 @@ All four drivers have a `_where(where: dict) → (clause_str, params)` static he - Query history (auto-log with timestamp/duration/status, search, replay, persisted in `history.db`) - All 4 DB drivers (MySQL, PostgreSQL, SQLite, MSSQL) - Process list viewer (`app/ui/process_list.py`) with 5 s auto-refresh and kill query -- Import CSV/JSON dialog (`app/ui/import_dialog.py`) with preview and progress bar +- Import CSV/JSON/SQL dialog (`app/ui/import_dialog.py`): CSV/JSON inserts rows into a target table; SQL dump executes statements via `_SqlImportWorker` (sqlparse split, stop-on-error option, per-statement error summary) - Database dump export (`app/ui/dump_dialog.py`): schema/data/both, table selector, progress bar - Tools menu in MainWindow (Process List, Import CSV/JSON, Export Database Dump, User Management, Preferences) - Preferences dialog (`app/ui/preferences_dialog.py`): Ctrl+, / Tools → Preferences; font family/size/word-wrap, page size, timeout, history limit, auto-commit; live-applies to all open editors via `settings_applied` signal @@ -144,7 +147,7 @@ All four drivers have a `_where(where: dict) → (clause_str, params)` static he - Connection ping indicator: 30-second `_PingWorker` (QThread) pings each active connection via `test_connection()`; status dot (● green / ● red) shown in status bar right side with per-connection tooltip - Frozen first column in `TableViewer`: 📌 toggle button in toolbar; dual-view overlay (`_frozen_view` child of `_table_view`) with synced vertical scroll and row heights; event-filter updates geometry on resize - Keyboard navigation in dialogs: OK set as default button (Enter submits) in `RowDialog` and `ConnectionDialog`; focus jumps to first input field on open; Enter in password field submits `ConnectionDialog` -- Light / dark theme toggle: `app/config/theme.py` holds Catppuccin Frappé (dark) + Latte (light) palettes; `resources/style_light.qss` is the full Latte QSS; toggle via View → Switch Theme (Ctrl+Shift+T) or Preferences → Color theme; on switch, QSS is reloaded, syntax highlighter rebuilds rules via `update_theme()`, completer popup recolors, and model dirty/NULL colors update live +- Multi-theme support: 7 themes (Frappé, Latte, One Dark Pro, Nord, Tokyo Night, Dracula, GitHub Light) via `resources/style_template.qss` token substitution; select in Preferences → Color theme; Ctrl+Shift+T toggles dark↔light; syntax highlighter, completer popup, and model colors all update live via `get_palette()` ### Not Yet Implemented **High impact:**