diff --git a/AGENTS.md b/AGENTS.md
index bf6e1ab4..8fb4cd82 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -8,14 +8,14 @@ Tauri-based cross-platform SQL database GUI client with Vue 3 + TypeScript front
**Before adding or changing client UI, read [DESIGN.md](DESIGN.md).** It is the canonical client design specification; feature design documents describe behavior within that system.
-- Preserve SQLKit's existing **blue primary**, light/dark/system themes, compact desktop workspace, and shared shadcn-vue/Radix Vue components. Learn restraint and semantic color ownership from console-serverlessinsight; do not copy its violet brand or monochrome primary buttons.
-- Use neutral surfaces, typography, spacing, and borders for hierarchy. Keep accent areas scarce (roughly 10% of application chrome); use at most one solid primary action per operation context. Secondary toolbar actions stay neutral.
-- Colors have fixed roles: `primary` = primary actions/interaction; `success` = actual success/connected/completed; `destructive` = errors/destructive actions. Ordinary information, counts, boolean values, database properties, and file formats are neutral. Do not assign a color to each feature or add a separate AI palette.
-- Reuse semantic CSS tokens from `src/assets/index.css` and their `uno.config.ts` mappings. Do not add palette classes or literal colors for existing UI semantics. `accent` is a neutral interaction surface, not a second brand color.
-- `warning` and `info` are **not currently registered** semantic colors. Any new semantic token must include light/dark definitions, necessary foreground pairs, UnoCSS mappings, shared variants where needed, contrast verification, and a DESIGN.md update in the same change.
+- Preserve SQLKit's existing **blue primary**, light/dark/system themes, compact desktop workspace, and shared shadcn-vue/Radix Vue components. Keep semantic color ownership consistent and retain SQLKit's blue brand and primary actions.
+- Use neutral surfaces, typography, spacing, and borders for hierarchy. Keep accent areas scarce (roughly 10% of application chrome); use at most one solid primary action per operation context. Keep Execute compact and unfilled. Preserve the original functional icon and data colors documented in DESIGN.md.
+- Colors have fixed roles: `primary` = primary actions/interaction; `success` = actual success/connected/completed; `destructive` = errors/destructive actions. Preserve existing boolean, key/constraint, type, filter, permission, format, and icon recognition colors, with labels or distinct glyphs alongside them. Do not tint whole features or add a separate AI palette. Recognition colors describe content and actions independently of operational status; do not flatten them to gray during surface refactors.
+- Reuse semantic CSS tokens from `src/assets/index.css` and their `uno.config.ts` mappings. Preserve existing palette utilities for functional recognition; a token migration must retain the original hues and theme variants. `accent` is a neutral interaction surface, not a second brand color.
+- `warning` is registered with light/dark foreground, muted-background, and border pairs; `info` is **not registered**. Use `primary-text` and `destructive-text` for readable colored text, reserving the corresponding base tokens for solid actions. Any new semantic token must include light/dark definitions, necessary foreground pairs, UnoCSS mappings, shared variants where needed, contrast verification, and a DESIGN.md update in the same change.
- Non-interactive badges explicitly use `outline`/`secondary` by default; status colors require real state and accompanying text or icons. Syntax highlighting, data charts, diffs, and small vendor logos have the bounded exceptions documented in DESIGN.md.
- Provide visible keyboard focus, accessible icon-button names, loading/empty/error states, readable contrast, and localized copy. Reuse runtime platform detection for shortcuts and native window behavior.
-- Existing hardcoded colors and component defaults are migration debt, not permission to repeat them. Distinguish documented targets from implemented behavior, and verify the affected themes, window sizes, and interactions when changing UI.
+- Existing functional recognition colors are intentional compatibility constraints. Avoid new unrelated hardcoded colors. Distinguish documented targets from implemented behavior, and verify the affected themes, window sizes, and interactions when changing UI.
## Build/Lint/Test Commands
diff --git a/DESIGN.md b/DESIGN.md
index 1bed51ef..b9e2af0e 100644
--- a/DESIGN.md
+++ b/DESIGN.md
@@ -3,13 +3,13 @@ name: SQLKit
description: A restrained, dense, cross-platform database workspace centered on SQL and data
colors:
primary-light: "hsl(221.2 83.2% 45%)"
- primary-dark: "hsl(217.2 91.2% 45%)"
+ primary-dark: "hsl(221.21 83.19% 53.33%)"
background-light: "hsl(0 0% 100%)"
- background-dark: "hsl(222.2 84% 4.9%)"
+ background-dark: "hsl(220.00 12.00% 9.80%)"
foreground-light: "hsl(222.2 84% 4.9%)"
- foreground-dark: "hsl(210 40% 98%)"
+ foreground-dark: "hsl(217.50 17.39% 90.98%)"
card-light: "hsl(0 0% 100%)"
- card-dark: "hsl(222.2 47% 8%)"
+ card-dark: "hsl(220.00 11.39% 15.49%)"
rounded:
lg: "8px"
md: "6px"
@@ -22,22 +22,17 @@ rounded:
**Design direction: A quiet SQL workspace.** Users should notice connection context, SQL, and query results first, actions second, and brand expression last. Support long sessions of writing, inspecting, and comparing data through high information density, clear hierarchy, stable layouts, and predictable interactions.
-This file defines the design constraints for new and modified SQLKit client UI, alongside [AGENTS.md](AGENTS.md). Preserve the existing blue theme, light / dark / system modes, compact desktop layout, and shadcn-vue / Radix Vue component system. Establishing this specification does not mean existing screens have already been migrated.
+This file defines the design constraints for new and modified SQLKit client UI, alongside [AGENTS.md](AGENTS.md). Preserve the existing blue theme, light / dark / system modes, compact desktop layout, and shadcn-vue / Radix Vue component system. The graphite theme and semantic color migration apply to the client shell, shared controls, SQL editor, connections, grids, transfers, settings, and AI surfaces.
-### Sources and adaptations
+### Design principles
-The reference project is `console-serverlessinsight`, reviewed on 2026-10-19. The main sources are its `AGENTS.md` section on color rules (V3 · Mono + Violet Accent), `apps/console/src/assets/css/themes/violet.css`, `apps/console/uno.config.ts`, `docs/console-ui-design.md`, and `apps/console/PRODUCT.md`. These paths record provenance only; SQLKit has no runtime dependency on the reference repository.
+The client uses a restrained desktop workspace with softly cool graphite surfaces in dark mode and blue primary actions in both themes.
-| Reference principle | SQLKit adaptation |
-| --- | --- |
-| Give colors fixed semantics rather than assigning them to modules | Blue for primary actions and interaction, green for success, red for errors or danger; neutrals for ordinary information |
-| Let neutral surfaces dominate and keep accents scarce | Preserve SQLKit's slightly cool neutral tokens rather than copying the reference project's zero-chroma grays |
-| Restrict brand color to small areas | Keep SQLKit's blue primary buttons, limiting their count and area; do not copy violet or monochrome primary buttons |
-| Use semantic status tokens across light and dark themes | Reuse success / destructive; fill gaps such as warning through the process below |
-| Use progressive disclosure, a stable app shell, and keyboard access | Apply these to the connection tree, query tabs, editor, result grids, and desktop window behavior |
-| Require non-color signals and readable contrast | Treat these as acceptance criteria; existing tokens do not prove compliance |
-
-The reference project's early colorful diagrams, login gradients, and CSS glass and ambient-light effects are not design references for SQLKit's workspace.
+- Give colors fixed semantics: blue for interaction, green for actual success, red for errors and danger, amber for actual warnings, and neutrals for ordinary information.
+- Keep accents scarce and let typography, spacing, and surface hierarchy organize the workspace.
+- Use shared semantic tokens for light, dark, and system appearance, including the SQL editor and overlays.
+- Preserve compact layouts, progressive disclosure, predictable keyboard access, and readable contrast.
+- Keep decorative gradients, ambient effects, and glass treatments out of the database workspace.
### Specification and implementation
@@ -54,68 +49,88 @@ The table records CSS HSL components, used as `hsl(var(--token))`. The frontmatt
| Token | Light | Dark | Role |
| --- | --- | --- | --- |
-| `--primary` | `221.2 83.2% 45%` | `217.2 91.2% 45%` | Primary actions, links, interaction emphasis |
-| `--primary-foreground` | `0 0% 100%` | `0 0% 100%` | Text on solid primary backgrounds |
-| `--background` | `0 0% 100%` | `222.2 84% 4.9%` | Main workspace |
-| `--foreground` | `222.2 84% 4.9%` | `210 40% 98%` | Body text and important data |
-| `--card` / `--popover` | `0 0% 100%` | `222.2 47% 8%` | Containers and overlays |
-| `--secondary` / `--muted` | `210 40% 96.1%` | `217.2 32.6% 16%` | Secondary surfaces and neutral states |
-| `--muted-foreground` | `215.4 16.3% 46.9%` | `215 20.2% 65.1%` | Supporting text; never the sole carrier of critical information |
-| `--accent` | `210 40% 96.1%` | `217.2 32.6% 17.5%` | Neutral interaction surface, **not a second brand color** |
-| `--border` | `214.3 31.8% 91.4%` | `217.2 32.6% 17.5%` | Structural separation |
-| `--input` | `214.3 31.8% 80%` | `217.2 32.6% 28%` | Input boundaries |
-| `--ring` | `220 13% 60%` | `212.7 26.8% 68%` | Keyboard focus; retain the current neutral color |
-| `--success` | `142.1 70.6% 35.5%` | `142.1 70% 45%` | Success, connected, completed |
-| `--success-muted` | `138 76% 95%` | `142 35% 16%` | Local success feedback background |
-| `--success-border` | `140 58% 85%` | `142 30% 25%` | Success feedback boundary |
-| `--destructive` | `0 84.2% 60.2%` | `0 62.8% 30.6%` | Errors, failures, destructive actions |
+| `--background` | `0 0% 100%` | `220.00 12.00% 9.80%` | Workspace and editor |
+| `--chrome` | `210.00 40.00% 98.04%` | `217.50 12.50% 12.55%` | Title bar, navigation, sidebar, toolbars |
+| `--foreground` | `222.2 84% 4.9%` | `217.50 17.39% 90.98%` | Body text and data |
+| `--card` | `0 0% 100%` | `220.00 11.39% 15.49%` | Containers |
+| `--popover` | `0 0% 100%` | `220.00 11.39% 15.49%` | Menus, dialogs and editor widgets |
+| `--primary` | `221.2 83.2% 45%` | `221.21 83.19% 53.33%` | Solid primary actions |
+| `--primary-foreground` | `0 0% 100%` | `0.00 0.00% 100.00%` | Text on primary fills |
+| `--primary-text` | `224.28 76.33% 48.04%` | `218.46 100.00% 77.06%` | Links and small active indicators |
+| `--selection` | `220.00 100.00% 95.29%` | `215.71 35.59% 23.14%` | Text and grid selection |
+| `--secondary` | `210 40% 96.1%` | `220.00 11.39% 15.49%` | Secondary surfaces |
+| `--muted` | `210 40% 96.1%` | `220.00 11.39% 15.49%` | Subdued surfaces |
+| `--muted-foreground` | `213.33 16.82% 41.96%` | `217.89 11.80% 68.43%` | Supporting text |
+| `--accent` | `210 40% 96.1%` | `216.92 13.13% 19.41%` | Neutral hover and selected controls |
+| `--border` | `214.3 31.8% 91.4%` | `217.50 13.33% 23.53%` | Structural separation |
+| `--input` | `213.75 13.91% 54.90%` | `215.56 11.79% 44.90%` | Control boundaries |
+| `--ring` | `215.38 16.32% 46.86%` | `217.89 11.80% 68.43%` | Keyboard focus |
+| `--destructive` | `0.00 73.71% 41.76%` | `0.00 53.53% 47.25%` | Solid destructive actions |
+| `--destructive-foreground` | `0.00 0.00% 100.00%` | `0.00 0.00% 100.00%` | Text on destructive fills |
+| `--destructive-text` | `0.00 73.71% 41.76%` | `4.82 81.16% 72.94%` | Readable error text and icons |
+| `--destructive-muted` | `0.00 85.71% 97.25%` | `352.94 19.10% 17.45%` | Subdued error feedback |
+| `--destructive-border` | `0.00 96.30% 89.41%` | `3.40 29.61% 35.10%` | Error feedback boundary |
+| `--success` | `142.43 71.81% 29.22%` | `143.02 47.25% 64.31%` | Success text and connected/completed signals |
+| `--success-foreground` | `0.00 0.00% 100.00%` | `220.00 12.00% 9.80%` | Text on success fills |
+| `--success-muted` | `138.46 76.47% 96.67%` | `148.42 22.89% 16.27%` | Success feedback surface |
+| `--success-border` | `141.00 78.95% 85.10%` | `141.67 23.08% 30.59%` | Success feedback boundary |
+| `--warning` | `35.65 100.00% 27.06%` | `36.61 79.73% 70.98%` | Actual warnings and risk signals |
+| `--warning-foreground` | `0.00 0.00% 100.00%` | `220.00 12.00% 9.80%` | Text on warning fills |
+| `--warning-muted` | `39.00 100.00% 96.08%` | `36.67 20.45% 17.25%` | Warning feedback surface |
+| `--warning-border` | `39.10 67.94% 74.31%` | `37.36 32.12% 32.35%` | Warning feedback boundary |
+
+Dark mode uses the approved softly cool graphite palette. Preserve blue primary actions. Use `primary-text` for blue text and icons, and `destructive-text` for error text and icons; solid fill tokens are not readable-text tokens. `success` and `warning` are paired with dark foregrounds in dark mode and white foregrounds in light mode.
### Semantic ownership
| Visual signal | Allowed meaning | Must not represent |
| --- | --- | --- |
-| Blue `primary` | Primary actions, links, selected controls, small active indicators | Success, ordinary badges, large panel backgrounds |
-| Neutrals `foreground / muted / secondary / accent` | Body text, supporting information, toolbars, navigation backgrounds, counts, ordinary attributes, disconnected states | Important errors or risks hidden through low contrast |
-| Green `success` | Successful connection tests, connected states, successful execution, completed transfers | Execute buttons, boolean true, auto-increment attributes, decorative selling points |
-| Red `destructive` | Execution failures, connection failures, deletion confirmations | Ordinary task counts, disconnected states, boolean false, decoration |
-| Amber `warning` (not yet implemented) | Actual warnings, risks, exceptional states requiring attention | JSON format, primary keys, ordinary unsaved indicators, arbitrary toolbar buttons |
+| Blue `primary / primary-text` | Primary actions, links, selected controls, small active indicators | Success, ordinary badges, large panel backgrounds |
+| Neutrals `foreground / muted / secondary / accent` | Body text, supporting information, toolbars, navigation backgrounds, layout hierarchy, disconnected states | Important errors or risks hidden through low contrast |
+| Green `success` | Successful connection tests, connected states, successful execution, completed transfers | Filled Execute buttons or decorative selling points; existing green recognition cues are independent of this status role |
+| Red `destructive / destructive-text` | Execution failures, connection failures, deletion confirmations | Disconnected states or decoration; existing false-value cues are independent of this status role |
+| Amber `warning` | Actual warnings, risks, exceptional states requiring attention | New decorative fills; existing key, format, and unsaved cues retain their recognition colors |
-Use neutral icons and text for ordinary information; do not introduce a separate sky-blue palette. SQLKit's `primary` already owns blue interaction emphasis. There are currently no `--info` or `--warning` mappings. Add tokens only for semantics that require a distinct role.
+Preserve established icon and data colors during surface or theme refactors. Use neutral text for ordinary prose; recognition colors for actions, types, attributes, and values remain useful and must not be flattened to gray. SQLKit's `primary` already owns blue interaction emphasis. Warning includes foreground, muted-background, and border pairs in both themes. There is no `--info` mapping; ordinary information remains neutral. Add tokens only for semantics that require a distinct role.
### Color budget
1. **Start with neutrals, then add emphasis.** Most backgrounds, borders, body text, and icons in a resting workspace use existing neutral tokens. Establish hierarchy through typography, spacing, and position first.
2. **Aim for accents to occupy no more than roughly 10% of the UI.** This is a review heuristic for the app shell, toolbars, panels, and forms, not a pixel-counting requirement. Evaluate SQL syntax highlighting, actual data charts, and local error feedback separately for readability; these exceptions must not expand decorative color use.
-3. **Use at most one solid primary action per operation context.** This is usually Execute in a query workspace or Save in a form dialog. Other actions use outline / secondary / ghost. An independent dialog may have its own primary action. Focus rings and status dots do not count as a second solid primary action.
+3. **Use at most one solid primary action per operation context.** Use a solid action only when the context benefits from it, such as Save in a form dialog. Query toolbar actions, including Execute, use compact ghost buttons with the original colored icons and subtle hover surfaces. An independent dialog may have its own primary action. Focus rings and status dots do not count as a second solid primary action.
4. **Success, warning, and failure may coexist when backed by actual state.** Default to an icon / status dot with text, adding a local, subdued background when needed. Do not tint an entire row, card, or panel.
-5. **Features do not own colors.** Do not give copy / export / format / EXPLAIN or individual filter operators a separate color. The result copy/export icons have the bounded exception below.
+5. **Preserve information-bearing color.** Existing object, action, value, attribute, filter, and vendor cues are part of the product vocabulary. Keep their original hues and combine them with distinct glyphs or labels. Use restraint on large surfaces rather than removing these cues.
### Allowed and avoided treatments
| Context | Default treatment | Avoid |
| --- | --- | --- |
-| Toolbars | Neutral icons and text; `accent` on hover | Blue / purple / orange / green buttons side by side |
+| Query toolbar | Small colored action icons, ghost buttons, subtle hover feedback | Solid blue Execute tiles or colored button backgrounds |
| Navigation / tabs | Neutral backgrounds and font weight; small blue indicators when needed | Solid saturated blue blocks or multicolor navigation entries |
| Attribute badges | Explicitly select `outline` or `secondary` on `Badge` | Solid blue badges chosen merely because that variant is the default |
-| Connection cards | Name, engine icon, status text / dot | A different colored background for each database engine |
+| Connection cards | Name, engine icon, status text / dot | Extending small vendor icon colors across entire connection cards |
| Alerts / notifications | Neutral surfaces, status icons, readable text | Large saturated red or green backgrounds |
| AI assistant / Ultimate indicators | The same neutral containers and small primary accents | A separate purple theme, rainbow gradients, glowing borders |
### Color exceptions
-- Result copy/export icons retain their established format colors: sky blue for CSV, amber for JSON, and emerald for INSERT, including the overflow menu and light/dark variants. This is an icon-only recognition cue; buttons and menu surfaces remain neutral, and labels still identify each action. These colors do not indicate status or extend to other format controls.
+- Preserve the object browser's existing palette: green table groups and objects, purple views, indigo materialized views, blue procedures, amber functions, yellow database icons, and sky schema folders. Keep the original category/leaf shades. Table groups use `table-properties`, table leaves use `table`, and materialized views use `layers`, so identification also has a shape cue.
+- Preserve the query toolbar's original emerald Execute, violet EXPLAIN, amber Format, and sky Save colors, including their light/dark variants and existing ANALYZE state. Execute stays a compact ghost button with no persistent solid primary fill. Accessible labels, pressed semantics, and distinct glyphs remain required.
+- Preserve existing colors for boolean values, primary/foreign keys, uniqueness, identity, filter operators, execution-plan details, permission levels, transfer roles, favorites, and unsaved markers. These encode content or recognition independently of operational success/failure. Keep the existing labels and symbols alongside color.
+- Result copy/export icons retain their established sky CSV, amber JSON, and emerald INSERT colors. Preserve format labels and scope information.
+- Existing palette utilities for these recognition cues remain valid. This surface refactor must not replace them with a new palette or collapse them to neutral tokens. A future migration to shared recognition tokens must preserve the existing hues and light/dark variants and be reviewed separately.
- Monaco SQL syntax highlighting, code diffs, and actual data charts may use multiple colors when those colors explain content. Do not extend their palettes into application controls.
- Existing database vendor logos may retain identifying colors inside small icons. Do not extend those colors to cards, buttons, or navigation backgrounds.
- Charts should emphasize the series of interest with primary and use neutrals for the others. When multiple series need distinction, a dedicated chart palette is allowed with labels, legends, or line styles. `--chart-1` through `--chart-5` exist in CSS but are not registered as UnoCSS color names; do not assume `text-chart-1` works.
-- Data diffs may use success / destructive for additions and deletions, accompanied by `+ / −` or text. Ordinary boolean values do not represent success or failure.
+- Data diffs may use success / destructive for additions and deletions, accompanied by `+ / −` or text. Boolean recognition colors describe values independently of success or failure.
### Token implementation process
1. Check whether an existing role can express the meaning before considering opacity variants. Opacity does not waive contrast requirements: text must remain readable against the actual composited background.
-2. Do not add hex / RGB / HSL literals or palette utilities such as `blue-500`, `green-600`, and `violet-*` in business components to replace existing semantic roles. Isolate chart, syntax, and vendor-asset exceptions in their respective modules.
+2. Do not add hex / RGB / HSL literals or palette utilities such as `blue-500`, `green-600`, and `violet-*` in business components to replace existing semantic roles. Preserve established recognition utilities described above; isolate new chart, syntax, and vendor-asset exceptions in their respective modules.
3. When adding a semantic token, include all of the following in the same change: `:root` and `.dark` definitions in `src/assets/index.css`, necessary foreground / subdued-background pairs, `uno.config.ts` mappings, shared component variants where needed, light/dark verification, and this document's update.
-4. Do not submit UI that depends on unimplemented utilities such as `text-warning` or `bg-info` before their mappings exist. Warning is a known gap; do not substitute `primary` or `success` for it.
-5. Check all consumers before changing shared tokens. Fixing semantic color readability must not also replace the blue brand or reset the entire neutral palette.
+4. Do not submit UI that depends on unimplemented utilities such as `text-warning` or `bg-info` before their mappings exist. Use `warning` for actual risk; preserve the existing attribute recognition palette.
+5. Check all consumers before changing shared tokens. Preserve the approved graphite surface hierarchy and blue brand when adjusting individual semantic colors.
## Typography
@@ -218,7 +233,7 @@ Interactive components must cover default, hover, focus-visible, active / select
### Forms, connection settings, and dialogs
-- Settings opens on General, grouping theme, application updates, and Account & Plan. Keep account controls in this first category rather than a separate settings tab.
+- Settings opens on General, grouping theme, application updates, and Account & Plan. Keep account controls in this first category, with About at the bottom after Account & Plan. About reads the running Tauri app version, with the package version as a browser fallback. Opening General checks for updates without starting installation; show checking, available, current, and failure states beside the version. Keep repository and website links; omit unmaintained community links.
- Reuse 32px Input / Select controls and the existing label / helper / error structure. Placeholders do not replace labels.
- Group host, port, authentication, database, and related fields by task. Progressively reveal advanced SSL / SSH settings, retaining the conditional-field behavior in [SSL_UI_DESIGN.md](docs/SSL_UI_DESIGN.md).
- Place validation near the field and explain the cause and remedy. Preserve entered content after failure; handle sensitive values under existing security policies.
@@ -227,8 +242,8 @@ Interactive components must cover default, hover, focus-visible, active / select
### Badges, notifications, and task states
-- Non-interactive attributes default to `Badge variant="outline"` or `secondary`. Status uses a dot / icon with text. The shared Badge still defaults to solid primary; callers must explicitly select an appropriate variant.
-- Use success for connected / completed, neutrals for disconnected / not started, and destructive for failed. Standardize warning treatment once shared tokens exist.
+- Non-interactive attributes default to `Badge variant="outline"` or `secondary`. Status uses a dot / icon with text. The shared Badge defaults to neutral secondary. Explicitly use success / warning / destructive variants only for real states; those variants use subdued semantic pairs.
+- Use success for connected / completed, neutrals for disconnected / not started, and destructive for failed. Use the shared warning tokens for actual risks.
- Count badges are neutral by default. Task count is not failure count; `taskCount > 0` alone does not justify red.
- Toasts use neutral backgrounds and status icons. Errors requiring action need readable explanations and Retry / View details paths rather than a brief flash. Prefer keeping execution feedback in the relevant result region.
- Do not duplicate toasts for normal progress and success. Avoid simultaneously repeating the same event in local status, notifications, and the task panel.
@@ -239,12 +254,12 @@ Interactive components must cover default, hover, focus-visible, active / select
- Result column headers show the database-provided type on the same line as the name when metadata is available, using small neutral text. Keep numeric headers right-aligned, truncate long types with the full value available on hover, and omit type labels when metadata is absent. Sortable columns always show a neutral sort indicator, with directional arrows for active sorting; do not hide it until hover.
- Keep the result-set selector inside the existing result toolbar. Collapse secondary export actions into a menu at narrow panel widths to preserve a single toolbar row.
- Use the shared pagination footer and state model in table browsing and query result grids, starting at 100 rows per page. Keep first / previous / next / last, direct page entry, common page-size presets, custom sizes, and the row range consistent. Wrap footer groups at narrow widths; keep navigation reachable for empty results and disable it while loading or showing an error. Table browsing fetches one page at a time; query grids page returned rows locally and additionally offer all returned rows. Copy / export retains its stated data scope. See [RESULT_PAGINATION_DESIGN.md](docs/RESULT_PAGINATION_DESIGN.md).
-- Keep Monaco's theme integration (currently `vs / vs-dark`) and user font-size settings. Syntax highlighting is outside the application decoration color budget; reducing colors must not damage syntax recognition.
+- Use the token-driven `sqlkit-light / sqlkit-dark` Monaco themes and preserve user font-size settings. Both inherit Monaco's built-in grammar support; editor, gutter, active-line, selection, completion and hover surfaces read the active CSS theme. Syntax hues live in `--syntax-keyword / string / number / comment` in the same CSS token source. Syntax highlighting is outside the application decoration color budget; reducing colors must not damage syntax recognition.
- Keep data central: neutral headers, subtle hover, stable selection, and clear sorting / filtering indicators. Preserve virtual scrolling; do not rebuild the whole grid for animation.
-- Distinguish `NULL`, empty strings, and booleans. Show true / false or equivalent text / symbols; do not default to red / green boolean mapping.
-- Primary keys, unique constraints, and auto-increment are attributes, not warnings or success. Use neutral identifiers such as a key icon or PK / UNIQUE / identity text.
+- Distinguish `NULL`, empty strings, and booleans. Preserve the existing green true / red false colors and check / cross symbols; color supplements the value glyphs.
+- Preserve existing key / constraint / auto-increment colors and PK / UNIQUE / identity labels. These attribute cues do not imply operational warnings or success.
- Show direction and priority for multi-column sorting. Preserve the capability limits in [GRID_SORT_DESIGN.md](docs/GRID_SORT_DESIGN.md): sorting applies to returned results / the current page, not the whole database or SQL re-execution.
-- Filters use operator text and removable entries, not a rainbow palette for eq / like / gt and other operators.
+- Filters retain their existing operator colors, operator text, and removable entries so repeated conditions remain easy to scan.
- Group formatting, execution plans, copying, and exporting by action. Identify CSV / JSON / INSERT by name. Make export scope and the currently displayed scope explicit.
- Provide details / copying for long text, JSON, and BLOBs rather than leaving an unrecoverable ellipsis. Clearly distinguish editable and read-only grid states.
@@ -252,8 +267,8 @@ Interactive components must cover default, hover, focus-visible, active / select
- AI uses the same neutral surfaces and primary color as the query page. Distinguish user messages, model messages, and code blocks through position, labels, and typography.
- Clearly separate generated SQL awaiting execution from executed results. Execution targets, risk messages, and confirmation follow the ordinary SQL workflow.
-- Imports / exports retain the scopes and steps in [TRANSFER_SCOPE_DESIGN.md](docs/TRANSFER_SCOPE_DESIGN.md). Express step state through names, numbers, and progress rather than a color per step.
-- Task panels clearly show running, completed, failed, and cancelled states. Keep rows neutral and color only local status signals. Normally running tasks must not appear as error alerts.
+- Imports / exports retain the scopes and steps in [TRANSFER_SCOPE_DESIGN.md](docs/TRANSFER_SCOPE_DESIGN.md). Preserve step recognition colors alongside names and numbers; express execution state through progress and status labels.
+- Task panels clearly show running, completed, failed, and cancelled states. Keep rows neutral and preserve local role icons and status signals. Normally running tasks must not appear as error alerts.
### Empty, loading, and error states
@@ -286,8 +301,8 @@ Interactive components must cover default, hover, focus-visible, active / select
### Don't
-- Do not assign a color to every feature, database type, filter operator, or file format.
-- Do not use success green for Execute, error red for ordinary counts, or warning colors for ordinary attributes.
+- Do not add decorative color to large feature surfaces or remove established type, filter, or format recognition cues during theme work.
+- Do not turn recognition colors into large filled actions or infer execution status from attribute/value colors. Preserve the compact Execute action and existing icon colors.
- Do not replace SQLKit's blue theme, force pure-gray neutral surfaces, or switch to monochrome primary buttons to match the reference project.
- Do not add rainbow badges, gradient AI panels, large colored icon containers, or light effects without functional value.
- Do not use historical hardcoding, default Badge styling, or unimplemented tokens as the design basis for new UI.
@@ -306,16 +321,10 @@ Check each affected surface. Documentation-only changes need source, link, and i
- [ ] Data scope, sorting scope, and execution status match actual capabilities; visual changes preserve functional semantics.
- [ ] UI implementation changes run `npm run lint:check` and `npm run build`; behavior changes also run relevant tests, and layout changes include actual screenshots at relevant themes and window sizes.
-### Existing UI migration priorities (not implemented in this change)
+### Theme maintenance
-| Priority | Current state | Follow-up |
-| --- | --- | --- |
-| P1 | Existing foreground pairs such as success / destructive need individual verification | Check contrast on actual backgrounds; adjust semantic pairs when needed while retaining the blue brand |
-| P1 | Success / warning in `ui/badge` and notifications still use fixed palettes; a global warning token is missing | Add light/dark semantic tokens and mappings first, then migrate shared components and check all consumers |
-| P2 | Some `QueriesPage.vue` toolbar buttons use purple / orange | Use neutral toolbars and primary for the main action |
-| P2 | `grid/FilterBar.vue` assigns colors to operators; grid booleans and some attributes use red / green / amber | Use neutral text, symbols, and attribute icons; color only actual status |
-| P2 | `AppHeader.vue` uses red for ordinary task counts; some connection-page icon containers use multiple colors | Make counts neutral; distinguish connection cards by name / engine icon / actual status |
-| P2 | Shared Badge defaults to solid primary; some close buttons appear only on hover | Explicitly select neutral variants for non-interactive badges and make close buttons visible on keyboard focus |
-| P2 | Narrow windows and reduced-motion behavior have not been fully checked against this specification | Verify and fix affected workflows; do not claim the current client already satisfies every requirement |
-
-Migrate incrementally by shared component or business area as it changes. Check each change against this specification; establishing the rules does not include a bulk rewrite of business UI.
+- Keep the CSS token source, UnoCSS mappings, Monaco adapter, and this specification synchronized.
+- Shared badges default to neutral secondary. Preserve established icon, database-attribute, boolean, filter, permission, format, and vendor colors during theme changes. Use explicit labels or symbols alongside them. Surface tokens and readable semantic feedback remain coordinated across themes.
+- Theme preview tiles scope the same light/dark tokens instead of maintaining a second miniature palette.
+- Automated contrast checks cover text on workspace, chrome, raised, selection, and semantic feedback surfaces, plus foregrounds on solid actions. Editor theme tests cover light/dark switching and token inheritance.
+- Continue to verify real content, narrow windows, keyboard focus, system appearance changes, and native window controls on the target operating systems. Theme tests do not replace native-platform interaction checks.
diff --git a/eslint.config.js b/eslint.config.js
index 9f0f19f9..25b754e5 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -23,6 +23,7 @@ export default antfu({
'**/dist/**',
'**/node_modules/**',
'**/.tauri/**',
+ '.impeccable/hook.cache.json',
'AGENTS.md',
'docs/**',
'src-tauri/gen/**',
diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs
index 3143f071..c47d87f7 100644
--- a/src-tauri/src/commands/mod.rs
+++ b/src-tauri/src/commands/mod.rs
@@ -18,9 +18,3 @@ pub use query::*;
pub use server::*;
pub use store::*;
pub use transfer::*;
-
-/// Returns the current app version from Cargo.toml.
-#[tauri::command]
-pub fn get_app_version() -> String {
- env!("CARGO_PKG_VERSION").to_string()
-}
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index d018c89e..a1298c46 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -389,7 +389,6 @@ pub fn run() {
commands::auto_map_migration_columns,
commands::generate_ddl_for_objects,
commands::execute_sql_content,
- commands::get_app_version,
crate::consume_pending_auth,
crate::entitlement::refresh_entitlement,
crate::entitlement::get_entitlement,
diff --git a/src/__tests__/dataGridPaging.test.ts b/src/__tests__/dataGridPaging.test.ts
index 34ee1616..f00440bf 100644
--- a/src/__tests__/dataGridPaging.test.ts
+++ b/src/__tests__/dataGridPaging.test.ts
@@ -92,13 +92,13 @@ describe('paged result grid', () => {
const firstRow = wrapper.get('[data-index="0"]')
firstRow.findComponent({ name: 'Checkbox' }).vm.$emit('update:checked', true)
await wrapper.vm.$nextTick()
- expect(wrapper.get('[data-index="0"]').classes()).toContain('bg-primary/[0.08]')
+ expect(wrapper.get('[data-index="0"]').classes()).toContain('bg-selection')
const scroller = wrapper.find('.overflow-auto').element as HTMLDivElement
scroller.scrollTop = 320
wrapper.findComponent({ name: 'PaginationControls' }).vm.$emit('pageChange', 2)
await wrapper.vm.$nextTick()
expect(scroller.scrollTop).toBe(0)
- expect(wrapper.get('[data-index="0"]').classes()).not.toContain('bg-primary/[0.08]')
+ expect(wrapper.get('[data-index="0"]').classes()).not.toContain('bg-selection')
expect(wrapper.get('[data-index="0"]').text()).toContain('135')
})
diff --git a/src/__tests__/theme.test.ts b/src/__tests__/theme.test.ts
new file mode 100644
index 00000000..5c9b9902
--- /dev/null
+++ b/src/__tests__/theme.test.ts
@@ -0,0 +1,117 @@
+/** @jest-environment jsdom */
+import { readFileSync } from 'node:fs'
+import { join } from 'node:path'
+import { applyEditorTheme } from '@/composables/editorTheme'
+
+const css = readFileSync(join(__dirname, '../assets/index.css'), 'utf8')
+const parseTokens = (block: string) => Object.fromEntries(
+ [...block.matchAll(/--([\w-]+): ([^;]+);/g)].map(([, name, value]) => [name, value]),
+)
+const themes = {
+ light: parseTokens(css.slice(css.indexOf('{') + 1, css.indexOf('}'))),
+ dark: parseTokens(css.match(/\.dark \{([^}]+)\}/)![1]),
+}
+
+const luminance = (hsl: string) => {
+ const [h, s, l] = hsl.match(/[\d.]+/g)!.map(Number)
+ const saturation = s / 100
+ const light = l / 100
+ const chroma = (1 - Math.abs(2 * light - 1)) * saturation
+ const x = chroma * (1 - Math.abs((h / 60) % 2 - 1))
+ const m = light - chroma / 2
+ const channels = [
+ [chroma, x, 0],
+ [x, chroma, 0],
+ [0, chroma, x],
+ [0, x, chroma],
+ [x, 0, chroma],
+ [chroma, 0, x],
+ ][Math.floor(h / 60) % 6]
+ return channels.map(value => value + m)
+ .map(value => value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4)
+ .reduce((sum, value, index) => sum + value * [0.2126, 0.7152, 0.0722][index], 0)
+}
+
+const contrast = (a: string, b: string) => {
+ const values = [luminance(a), luminance(b)].sort((a, b) => a - b)
+ return (values[1] + 0.05) / (values[0] + 0.05)
+}
+
+describe.each(['light', 'dark'] as const)('%s theme', (name) => {
+ const tokens = themes[name]
+
+ it.each(['background', 'chrome', 'card', 'popover', 'accent', 'selection'])(
+ 'keeps normal and supporting text readable on %s',
+ (surface) => {
+ ;['foreground', 'muted-foreground', 'primary-text', 'destructive-text'].forEach((text) => {
+ expect(contrast(tokens[text], tokens[surface])).toBeGreaterThanOrEqual(4.5)
+ })
+ },
+ )
+
+ it.each(['primary', 'destructive', 'success', 'warning'])(
+ 'provides a readable foreground on solid %s',
+ (role) => {
+ expect(contrast(tokens[`${role}-foreground`], tokens[role])).toBeGreaterThanOrEqual(4.5)
+ },
+ )
+
+ it.each(['success', 'warning', 'destructive'])(
+ 'keeps %s feedback readable on its subdued surface',
+ (role) => {
+ const text = role === 'destructive' ? 'destructive-text' : role
+ expect(contrast(tokens[text], tokens[`${role}-muted`])).toBeGreaterThanOrEqual(4.5)
+ },
+ )
+
+ it.each(['keyword', 'string', 'number', 'comment'])(
+ 'keeps SQL %s tokens readable',
+ (token) => {
+ expect(contrast(tokens[`syntax-${token}`], tokens.background)).toBeGreaterThanOrEqual(4.5)
+ },
+ )
+})
+
+describe('monaco theme integration', () => {
+ const monaco = { editor: { defineTheme: jest.fn(), setTheme: jest.fn() } }
+ const applyTheme = (dark: boolean) => {
+ document.documentElement.removeAttribute('style')
+ Object.entries(dark ? themes.dark : themes.light).forEach(([key, value]) => {
+ document.documentElement.style.setProperty(`--${key}`, value)
+ })
+ return applyEditorTheme(monaco as unknown as Parameters
+
{{ promptSaveError }}
+
{{ formErrors.name }}
@@ -335,7 +335,7 @@ watch(() => props.open, (open) => { 'cursor-not-allowed opacity-60': isApiCompatibilityReadonly, }" /> -+
{{ formErrors.apiCompatibility }}
@@ -408,7 +408,7 @@ watch(() => props.open, (open) => { :placeholder="t('pages.settings.ai.form.baseUrlPlaceholder')" :class="{ 'border-destructive': formErrors.baseUrl }" /> -+
{{ formErrors.baseUrl }}
diff --git a/src/views/setting/provider-list.vue b/src/views/setting/provider-list.vue index a2604552..76585228 100644 --- a/src/views/setting/provider-list.vue +++ b/src/views/setting/provider-list.vue @@ -231,7 +231,7 @@ const handleSync = async (id: string) => {