docs: update CLAUDE.md — themes, identifier quoting, SQL import

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-22 10:02:16 -04:00
co-authored by Claude Sonnet 4.6
parent 5a536fb9e9
commit 4fcbe5a879
+7 -4
View File
@@ -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:**