Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 5 additions & 1 deletion .claude/rules/data-grid.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ paths:
- **Cells are drawn, not mounted.** `tableView(_:viewFor:row:)` returns nil for data columns and `DataGridRowView` draws the visible cells, and every column stays attached. Never hide off-screen columns with `NSTableColumn.isHidden`: each write costs O(attached columns).
- **Find data columns through `DataGridColumnPool.presentsColumn` and `firstPresentedColumnIndex`**, never a fixed index into `tableColumns` (the row-number column and pool slots sit there too).
- **The header draws all of its own chrome** through `SortableHeaderChrome`: the cell never calls `super.draw`, and `highlightedTableColumn` stays unset, because AppKit paints a fixed-height band that does not fit the taller header. Sort state goes through `SortableHeaderView.applySortState(_:schema:)`, which also sets each header cell's accessibility sort direction.
- **Row selection is `NSTableRowView`'s own** unless the theme overrides `gridSelection` or `gridInactiveSelection`, so the default theme follows Accent and focus. The gutter and a selected column heading read `DataGridBodyChrome.selectionColor(emphasized:)` so they match the row. A theme change repaints visible rows; it never reloads data.
- **A themed header draws its own cells.** `NSTableHeaderView.draw` paints AppKit's band over the header fill before the cells, so while `dataGrid.headerBackground` is themed `SortableHeaderView` draws each column's header cell itself, the dragged one included; an unthemed header keeps `super.draw`.
- **Row selection is `NSTableRowView`'s own** unless the theme overrides `gridSelection` or `gridInactiveSelection`, so the default theme follows Accent and focus. The gutter and a selected column heading read `DataGridBodyChrome.selectionColor(emphasized:scope:)` so they match the row. A theme change repaints visible rows; it never reloads data.
- **A translucent theme selection is flattened once.** The row, the gutter and a heading sit on three different bases, so `DataGridBodyChrome.themedSelectionColor(emphasized:scope:)` lays the selection over the grid background and all three paint that opaque color with any alpha.
- **A range outline stays apart from its fill.** Natively a multi-cell range is outlined in the selection color at 70%. Under a themed `dataGrid.selection` that outline would vanish into the opaque fill, so `GridSelectionOverlay.rangeBorderColor(_:)` takes the active cell's border color instead (`dataGrid.focusBorder`, else Accent) at 70%.
- **The grid reads its colors through its `ThemeScope`.** `DataGridView(themeScope:)` hands it to `TableViewCoordinator`, and every grid color read goes through `coordinator.themeScope`, `KeyHandlingTableView.themeScope` and `SortableHeaderView.themeScope` included. `ThemeEngine.shared` supplies only the fonts and the cache counters that follow a system color change. A new scope repaints in place with no reload. Read `scope.resolved` once per draw, not once per cell. Scopes: `.claude/rules/theme.md`.
- **`gridStyleMask` stays empty; `DataGridBodyChrome` draws column separators and the stripes.** AppKit's vertical grid lines add one subview per column and make every layout pass quadratic. Take separator positions from `presentsColumn` and `rect(ofColumn:)`.
- **Scroll the grid with `NSView.scroll(_:)`**, never `clipView.scroll(to:)` plus `reflectScrolledClipView`, which leaves the header behind.
- **Accessibility goes through `DataGridCellAccessibilityView`**, mounted for on-screen rows once `DataGridAccessibility.isActive`; `NSTableView` builds its cell tree from views only. Keep that path working whenever row drawing changes.
3 changes: 3 additions & 0 deletions .claude/rules/data-sync-storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ This path holds user data. A silent wrong answer here looks exactly like an empt
| Connection passwords | Keychain | `ConnectionStorage` |
| Preferences | UserDefaults | `AppSettingsStorage`, `AppSettingsManager` |
| Editor and grid fonts | UserDefaults, device-local, never synced | `TypographySettings` through `AppSettingsManager` |
| Appearance mode (Auto, Light, Dark) | UserDefaults, device-local, never synced | `AppSettingsStorage` through `AppSettingsManager.appearanceMode` |
| Light and dark theme ids (iCloud-synced, category `appearance`) | UserDefaults | `AppearanceSettings` through `AppSettingsManager` |
| User themes (iCloud-synced, one `AppSettings` record `theme.<id>` each) | JSON, one file per theme | `ThemeCatalog` (sync rules: `.claude/rules/theme.md`, Sync) |
| Query history | SQLite FTS5 | `QueryHistoryStorage` |
| Tab state | JSON | `TabPersistenceCoordinator`, `TabDiskActor` |
| Filter defaults and presets | UserDefaults | `FilterSettingsStorage`, `FilterPresetStorage` |
Expand Down
4 changes: 3 additions & 1 deletion .claude/rules/editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ paths:

# Editor

- **`ThemeEngine.makeEditorTheme()` builds the `TableProEditorTheme` adapter** the editor draws with. Theme colors and their scope: `.claude/rules/theme.md`.
- **`EditorThemeFactory` builds the `EditorTheme` the editor draws with.** `make(resolved:highlightsCurrentLine:highlightsCurrentStatement:)` is pure over a `ResolvedTheme`. `ThemeEngine.makeEditorTheme()` and `TableProEditorTheme.make()` call it with the live theme and the editor settings; the theme editor preview calls it with a fixed scope's `resolved`. Theme colors and their scope: `.claude/rules/theme.md`.
- **Aliases and parameters take `editor.text`.** The `variables` capture (aliases, parameters, properties) is a name the user wrote, so it draws in the text color; `attributes` and `values` keep `editor.syntax.null`. Keywords draw bold.
- **A static code view highlights like the editor.** `HighlightedSQLTextView` and chat code blocks color `CodeBlockHighlighter.captures(in:language:)`, the editor's grammar, through the same `EditorTheme`, so a statement reads the same in a preview, a chat block and the editor. Only whole lines before its 10k-character cap get colors. Never add a second highlighter.
- **Fonts are not theme.** `TypographySettings` is device-local and stays out of `AppSettingsCategory.synced`: sync replaces a category wholesale, so an older Mac would reset it and each zoom press would push a record.
- **Two font domains.** A view that also wears the editor's colors (the SQL editor, `JSONCodeEditor`, previews) uses `editorFonts`. Every control that shows or edits a stored value uses `ThemeEngine.valueFont` or `valueFontSwiftUI` (the Data Grid Font), so a value reads the same in the grid, the inspector and a popover; a system text style there looks right only while the two settings are equal. `InspectorFieldRow` sets it for the whole inspector, and its `FieldEditorKind` switch is exhaustive on purpose.
- **Multi-cursor lives in `cursorPositions: [CursorPosition]`.**
Expand Down
Loading
Loading