diff --git a/.agents/plans/PLANS.md b/.agents/plans/PLANS.md index 7bec3d5..0dc382e 100644 --- a/.agents/plans/PLANS.md +++ b/.agents/plans/PLANS.md @@ -14,7 +14,7 @@ Persistent plans for multi-step work on **blxcode**. Individual plans live as Ma | done | [plan-manager.md](plan-manager.md) | Plan Manager fuer `.agents/plans`: Manage-Tab wie Memory Files, plan-linked Tasks gruppiert, Agent-Toolcalls, System-Prompt-Update und shared Context fuer BLXCode Agent plus Terminal-Handoff | | planned | [kanban-board-view.md](kanban-board-view.md) | Kanban-View im Plans-Panel fuer alle Plan-Tasks eines Workspaces: Status-Spalten, Drag-and-Drop fuer Karten und Spalten, Spalten ein-/ausblenden und Markdown-Writeback | | done | [per-turn-chat-metrics.md](per-turn-chat-metrics.md) | Per-Turn Metriken (in/out/ttft/tok/s/cost) statt globaler Footer; Tool- und Subagent-Turns; Session-Gesamtkosten im Chatlog-Titel; Persistenz via workbench.json | -| planned | [settings-tabs-themes-refactor.md](settings-tabs-themes-refactor.md) | Dynamische Workbench-Tabs (Main/Settings/File-Stub); Settings inline statt Modal; Appearance mit ~12 Themes (Default blxcode-dark); Sidebar bleibt sichtbar | +| done | [settings-tabs-themes-refactor.md](settings-tabs-themes-refactor.md) | Dynamische Workbench-Tabs (Main/Settings/File-Stub); Settings inline statt Modal; Appearance mit 20 Themes (Default blxcode-dark); Sidebar bleibt sichtbar | | done | [auto-update-github-releases.md](auto-update-github-releases.md) | Tauri Updater + signed latest.json, themed Leptos dialog/banner, Settings auto-check, hybrid release manifest flow, docs, and i18n implemented | | done | [coordinated-subagents.md](coordinated-subagents.md) | Coordinated Subagents fuer BLXCode Agent mit Rollen, i18n Live-Subcards, Provider-Reuse, Environment Detection, Shell/Git/Web Toolsets und scoped Toolgruppen | | done | [better-harness.md](better-harness.md) | BetterHarness: Shrink system prompt by extracting tool docs into 6 embedded Core Skills; Skills tab gets Core/User sub-tabs | diff --git a/.agents/plans/settings-tabs-themes-refactor.md b/.agents/plans/settings-tabs-themes-refactor.md index 4fc925e..cc5ce29 100644 --- a/.agents/plans/settings-tabs-themes-refactor.md +++ b/.agents/plans/settings-tabs-themes-refactor.md @@ -1,8 +1,10 @@ # Settings-Refactor: Tabs + Theme-System +**Status: done** (2026-05-22) + ## Summary -Refactor der BLXCode Settings von Modal-Overlay zu einem **dynamischen Tab-System** in der Workbench-Mitte: Main-Tab (fix, Terminals/Workspaces), Settings als schliessbarer Singleton-Tab, und vorbereitete **File-Tabs** (Explorer-Klick oeffnet Tab mit Stub-Editor). Settings blendet nur das Right Panel aus; linke Sidebar bleibt immer sichtbar. Neuer Appearance/Theme-Tab mit ~12 App-Themes, Suche, Dark/Light-Filter und Preview-Karten. Default-Theme `blxcode-dark` (Anzeigename „BLXCode“) ist der heutige Look 1:1. +Refactor der BLXCode Settings von Modal-Overlay zu einem **dynamischen Tab-System** in der Workbench-Mitte: Main-Tab (fix, Terminals/Workspaces), Settings als schliessbarer Singleton-Tab, und vorbereitete **File-Tabs** (Explorer-Klick oeffnet Tab mit Stub-Editor). Settings blendet nur das Right Panel aus; linke Sidebar bleibt immer sichtbar. Neuer Appearance/Theme-Tab mit **20 App-Themes**, Suche, Dark/Light-Filter und Preview-Karten. Default-Theme `blxcode-dark` (Anzeigename „BLXCode“) ist der heutige Look 1:1. ## Decisions @@ -11,7 +13,7 @@ Refactor der BLXCode Settings von Modal-Overlay zu einem **dynamischen Tab-Syste - File-Tabs: schliessbar, **Dedupe** bei gleichem `(workspace_id, rel_path)` solange Tab offen ist; Editor-Inhalt ist initial ein Stub (Follow-up: echter Editor). - Settings ist **Singleton** — erneutes Oeffnen fokussiert bestehenden Tab. - Main-Tab ist **fix** (Index 0, nicht schliessbar). -- **~12 Starter-Themes** im ersten Release. +- **~20 Starter-Themes** im ersten Release (12 MVP + 8 moderne Ergaenzungen: Rosé Pine, Rosé Pine Dawn, Everforest Dark, Kanagawa, GitHub Dark, Night Owl, Ayu Mirage, Catppuccin Frappé). - Default-Theme: **`blxcode-dark`** (Anzeigename **„BLXCode“**). Erststart und fehlender localStorage-Eintrag → immer dieses Theme; kein visueller Regressions-Diff. - `:root`-Tokens werden **1:1** nach `[data-theme="blxcode-dark"]` kopiert; `:root` bleibt als Fallback. - Workbench-Tab-Zustand ist **session-only** (nicht in `WorkbenchSnapshot`). @@ -181,10 +183,10 @@ Terminal: `public/terminal_bootstrap.mjs` — Theme-Map + `blxcode-theme-changed ## Tasks -- [ ] `tab-infra` - Dynamisches Tab-Modell (Main/Settings/File), CRUD-API, TabStrip/Host, FileTabPane-Stub, Explorer-Hook, RightPanel nur bei Settings ausblenden -- [ ] `settings-inline` - SettingsChrome aus Modal extrahieren → settings/ Modul; HarnessHost bereinigen; open_settings → Tab oeffnen; Escape/Chords anpassen -- [ ] `theme-tokens` - themes/tokens.css mit 12 Theme-Sets; blxcode-dark 1:1 aus aktuellem :root; data-theme auf html; THEME_STORAGE_KEY + ThemeService; Boot-Script in index.html -- [ ] `theme-pane-ui` - HarnessSettingsCategory::Appearance + ThemePane mit Suche, Dark/Light-Filter, Preview-Grid; i18n fuer alle Locales -- [ ] `terminal-theme-sync` - terminal_bootstrap.mjs: Theme-Map + blxcode-theme-changed Event fuer live xterm-Updates -- [ ] `css-polish` - Tab-Strip mit Scroll, Settings-Inline, Theme-Grid CSS; kritische hardcoded Farben tokenisieren -- [ ] `manual-qa` - Abnahme: dynamische Tabs, Dedupe, Main nicht schliessbar, Settings/File/Theme-Flows, Sidebar sichtbar, wasm32 check +- [x] `tab-infra` - Dynamisches Tab-Modell (Main/Settings/File), CRUD-API, TabStrip/Host, FileTabPane-Stub, Explorer-Hook, RightPanel nur bei Settings ausblenden +- [x] `settings-inline` - SettingsChrome aus Modal extrahieren → settings/ Modul; HarnessHost bereinigen; open_settings → Tab oeffnen; Escape/Chords anpassen +- [x] `theme-tokens` - themes/tokens.css mit 20 Theme-Sets; blxcode-dark 1:1 aus aktuellem :root; data-theme auf html; THEME_STORAGE_KEY + ThemeService; Boot-Script in index.html +- [x] `theme-pane-ui` - HarnessSettingsCategory::Appearance + ThemePane mit Suche, Dark/Light-Filter, Preview-Grid; i18n fuer alle Locales +- [x] `terminal-theme-sync` - terminal_bootstrap.mjs: Theme-Map + blxcode-theme-changed Event fuer live xterm-Updates +- [x] `css-polish` - Tab-Strip mit Scroll, Settings-Inline, Theme-Grid CSS; kritische hardcoded Farben tokenisieren +- [x] `manual-qa` - Abnahme: dynamische Tabs, Dedupe, Main nicht schliessbar, Settings/File/Theme-Flows, Sidebar sichtbar, wasm32 check diff --git a/.agents/rules/rule-theme-tokens.md b/.agents/rules/rule-theme-tokens.md new file mode 100644 index 0000000..db604ee --- /dev/null +++ b/.agents/rules/rule-theme-tokens.md @@ -0,0 +1,21 @@ +# Theme tokens + +## Ziel + +App-weite Themes über `html[data-theme]` und semantische CSS-Custom-Properties. Keine hardcodierten Farben in UI-Stylesheets. + +## Vorgaben + +- **Neue UI** nutzt ausschließlich `var(--token)` aus [`themes/tokens.css`](../../themes/tokens.css). +- **Keine** `var(--token, #literal)`-Fallbacks in Komponenten-CSS. +- **Theme-Wechsel** läuft über [`ThemeService`](../../src/workbench/theme_service.rs) (`blxcode-theme-changed` Event). +- **JS-Bridges** (xterm, Graph 3D) lesen Tokens via `getComputedStyle(document.documentElement)` — nicht hardcodieren. +- **Rust/SVG** (Memory-Graph 2D): Farben aus `read_css_var()` + Reaktion auf `ThemeService::active_theme_id`. + +## Bewusste Ausnahmen + +Siehe [`docs/THEME_EXCEPTIONS.md`](../../docs/THEME_EXCEPTIONS.md). + +## CI + +`scripts/lint_theme_tokens.sh` vor Merge ausführen. diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ca5903..e99f505 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Boot loading screen**: new branded loading experience that paints before the WASM bundle is ready and stays on screen until the workbench is mounted. `index.html` ships a static `#blx-static-boot` section (logo, eyebrow, faux workbench preview, animated rail) so the first paint happens immediately on Trunk-served HTML; once Leptos mounts, the static node is removed and `BootLoadingScreen` (`src/boot_loading.rs`) takes over with phased copy (`Starting BLXCode` → `Restoring workspace` → `Opening workbench`). 306 lines of dedicated CSS in `styles.css` add the radial-gradient backdrop, frame-enter / sheen / rail keyframes, and the sidebar/main/panel preview skeleton. The `App` boot fallback now renders `` instead of the prior empty `app-shell--boot` div. - **Agent question card (`harness.ask_user`)**: new client-side tool that lets the coordinator agent ask a clarifying multiple-choice question and receive the user's answer as a structured tool result. Backend registers `harness.ask_user` (`src-tauri/src/agent/tools.rs`) with a JSON schema accepting `question`, optional `header` (≤12 chars), 2–4 `options` (label + optional description), `multiSelect`, and `allowOther`; the tool sits in the `CoordinatorHarness` group (subagents excluded) and the system prompt instructs the model to use it only when 2–4 distinct options would unblock progress. Frontend adds a new `TimelineItem::AskUser` variant and `ask_user_card/` component (Angular-style folder with co-located CSS) rendering a chat bubble with numbered option buttons, single- or multi-select mode, an optional free-text “Other” field, and a cancel button; the card submits via `agent_submit_tool_result` and transitions the row state to `Answered { selected, other }` or `Cancelled` so the bubble stays visible with disabled controls. Persistence drops `Open` ask-user rows from the saved timeline (the awaiting backend loop is dead after reload). Tool-result payload: `{ "selected": [...], "other": "...", "cancelled": false }`; on cancel: `ok=false` + `{ "cancelled": true }`. 8 new i18n keys (`AgAskUser*`) added in `en_us.rs` and `de_de.rs`; other locales receive English placeholders pending a `render_i18n_locales_from_en.py` pass. - **Centralized API Keys settings**: new **Settings → API Keys** pane (`src/workbench/api_keys_pane/`) replaces per-provider key inputs that previously lived under Settings → Agent. Backend module `src-tauri/src/api_keys.rs` exposes `api_keys_status` (catalog of LLM + search providers with masked values, env-var hints, and `comingSoon` placeholders for Google / Mistral / **Grok xAI**) and `api_keys_apply` (batch `set` / `delete` actions). Active keys still use the existing OS keyring accounts (`agent:*`, `agent:web:*`) with optional `BLX_*` env fallback; resolve order is keyring-first so UI-saved secrets are not overridden by shell env. The pane uses draft state, a single **Save** / **Discard** footer, row-level remove/undo, and shared `settings-field-card` / `api-keys-row` styling with brand icons (`public/brand-icons/`: OpenRouter, Anthropic, OpenAI, Google/Gemini, Mistral, Groq, Tavily, Brave). 15 new `ApiKeys*` i18n keys in all 13 locales. -- **Settings revamp (docked center tab)**: harness settings moved from a modal overlay into a **center workbench tab** (`SettingsDock` in `harness_ui.rs`). Sidebar categories: **App**, **Appearance** (stub), **API Keys**, **Workspace**, **BLXCode Agent**. Legacy `HarnessSettingsCategory` values (`Image`, `Voice`, `Memory`) still deserialize and route to the correct pane for older snapshots. Command palette / tmux chords open or focus a singleton settings tab per workspace. +- **Settings revamp (docked center tab)**: harness settings moved from a modal overlay into a **center workbench tab** (`SettingsDock` in `harness_ui.rs`). Sidebar categories: **App**, **Appearance**, **API Keys**, **Workspace**, **BLXCode Agent**. Legacy `HarnessSettingsCategory` values (`Image`, `Voice`, `Memory`) still deserialize and route to the correct pane for older snapshots. Command palette / tmux chords open or focus a singleton settings tab per workspace. +- **App theme system & Appearance tab**: twenty selectable themes (`blxcode-dark` default **BLXCode**, plus BLXCode Light, Dracula, Gruvbox, Solarized, Nord, One Dark, Catppuccin, Tokyo Night, **Rosé Pine**, **Rosé Pine Dawn**, **Everforest Dark**, **Kanagawa**, **GitHub Dark**, **Night Owl**, **Ayu Mirage**, **Catppuccin Frappé**) apply app-wide via `html[data-theme]` and `themes/tokens.css`. New `src/theme/` catalog + `ThemeService` (`src/workbench/theme_service.rs`, context in `src/app.rs`) persist the choice to `blxcode_theme_v1`, set `data-theme` synchronously, and dispatch `blxcode-theme-changed` for JS bridges. `index.html` runs an inline boot script before CSS load to prevent theme flash. **Settings → Appearance** (`src/workbench/appearance_settings_pane/`) ships a hero preview, search, All/Dark/Light filters, and preview cards with roomier card padding and title/description spacing; theme names/descriptions are fully i18n (`ThemeName*` / `ThemeDesc*` + UI chrome keys in all 13 locales). Terminals (`terminal_bootstrap.mjs`), Memory graph 2D/3D, agent-terminal accent borders, and component CSS consume the same tokens. Lint: `scripts/lint_theme_tokens.sh`; contributor rule `.agents/rules/rule-theme-tokens.md`; documented exceptions in `docs/THEME_EXCEPTIONS.md`. - **BLXCode Agent settings pane**: `src/workbench/agent_provider_pane/` — responsive grid. **Text** (top-left): provider + **thinking level** pickers (`harness-level-picker` icons), muted `text-xs` API-key status line → Settings → API Keys, shared **`AgentModelPicker`** (catalog rows, pricing-only detail sub-row, custom model id, refresh). **Image** (top-right): `AgentImageColumn` in `harness_image_pane` — provider dropdown, **quality level** picker, `AgentModelPicker`, auto-save; **fal.ai** provider + key via API Keys catalog. **Voice** (bottom, full width): `AgentVoiceColumn` in `harness_voice_pane` — unified STT/TTS provider (OpenAI / OpenRouter / **AWS Polly**), `AgentModelPicker` for speech + TTS models, recording quality, behavior (post-STT flow, gender filter, fixed 6-voice catalog per provider, TTS autoplay). Web Tools below the grid (Tavily / Brave / disabled); one **Save** / **Discard** footer for text provider + web backend. Replaced standalone `model_picker/` with `agent_model_picker/`. - **Voice in App settings**: STT language mode + push-to-talk hotkey moved to **Settings → App** (`voice_app_controls`); full voice provider/model/voice UI lives only under BLXCode Agent. - **Workspace category colors**: former **Settings → Memory → Color presets** moved to **Settings → Workspace** section **Category colors** (`workspace_settings_pane/category_colors.rs`, `WsSectionCategoryColors` + related i18n). Same preset list/edit/add/reset backed by `memory_color_presets` in `WorkbenchService`. @@ -25,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - **`ChatUsageStats` schema migrated**: `total_cost_usd: f64` and `current_turn_generation: u64` added; legacy `ttft_sum_ms` / `ttft_sample_count` fields removed (TTFT now lives per-row). Older `workbench.json` snapshots deserialize cleanly thanks to `#[serde(default)]` and rewrite without the removed fields on next save. `record_chat_turn_usage` signature changed to accept the new `turn_generation` and `cost_usd` and to return `bool` so callers can drop stale events. +- **Global CSS tokenization for themes**: `styles.css` hardcoded colors replaced with semantic `var(--*)` tokens; `themes/tokens.css` loaded before `styles.css`. Component sheets (`ask_user_card`, `plans-panel`, `api_keys_pane`, `agent_provider_pane`, `update_dialog`, `sidebar_resizer`, `turn_metrics_bar`, `harness_image_pane`) no longer use literal fallbacks in `var()`. `scripts/tokenize_styles_css.py` heuristics no longer map `color:` properties to overlay tokens. +- **Appearance theme cards**: increased inner padding, grid gap, and title/description spacing; active checkmark uses semi-transparent accent fill. - **Workspace settings layout aligned with API Keys**: the Workspace pane (`src/workbench/workspace_settings_pane/`) uses `harness-subpane` sections (**Paths & sandbox**, **Embedded browser**, **Category colors**), `api-keys-list` / `api-keys-row` field cards, and one footer **Save** / **Discard** for project directory, agent sandbox root, and embedded-browser URL. Category color edits apply immediately (same as former Memory tab). Shared footer/field CSS in `workspace_settings_pane.css` and `api_keys_pane.css`. - **BLXCode Agent API-key hints**: relocation lines under Text / Image / Voice use `0.75rem` + `--text-muted` (`.agent-provider-pane__key-row`) instead of body-sized copy. - **Voice catalog UX**: six fixed voices per provider (OpenAI + AWS Polly active; OpenRouter shows OpenAI set disabled with hint); gender filter always visible; disabled cards keep layout (no hide-on-disable). @@ -32,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- **BLXCode theme color regression after tokenization**: bulk CSS tokenization had mapped many text labels (settings radio/checkbox copy, hints, hook badges) to `var(--overlay-2)`, making them nearly invisible on dark backgrounds; restored semantic text/control tokens (`--text-secondary`, `--text-hint`, `--accent-control`, `--success-surface`, `--hook-brand-*`) and fixed ~99 mis-mapped `color:` rules in `styles.css`. - **`scripts/tools/render_i18n_locales_from_en.py`**: `GoogleTranslator.translate()` returning `None` (recent `deep-translator` behaviour for short tokens like `"in"`, `"out"`, `"ttft"` and the bare `"—"` glyph) was cached and later crashed `rust_escape` with `TypeError: 'NoneType' object is not iterable`. The script now treats empty / `None` responses as "no translation available" and falls back to the source string; `emit_locale_rs` hard-fails with a readable `SystemExit` if a `None` still slips through. ### Removed diff --git a/docs/README.md b/docs/README.md index 7bfc95e..7ed781d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -10,7 +10,7 @@ Welcome to the BLXCode docs. User guides explain how to run and use the app; dev | Topic | Guide | |-------|--------| -| Settings (API Keys, BLXCode Agent, Workspace) | [Settings](user/settings.md) | +| Settings (API Keys, BLXCode Agent, Workspace, **Appearance / themes**) | [Settings](user/settings.md) · [Appearance & Themes](user/appearance-themes.md) | | Workbench, terminals, sidebar, handoff | [Workspaces](user/workspaces.md) | | Memory, learnings, graph, categories | [Memory And Tasks](user/memory-and-tasks.md) | | Markdown plans and plan-linked tasks | [Plans](user/plans.md) | @@ -28,7 +28,8 @@ Welcome to the BLXCode docs. User guides explain how to run and use the app; dev ## User docs - [Getting Started](user/getting-started.md) — prerequisites, run BLXCode, first workspace, where data lives. -- [Settings](user/settings.md) — docked center-tab settings, API Keys, BLXCode Agent grid, Workspace. +- [Settings](user/settings.md) — docked center-tab settings, API Keys, BLXCode Agent grid, Workspace, **Appearance / themes**. +- [Appearance & Themes](user/appearance-themes.md) — theme picker, presets, persistence, exceptions. - [Workspaces](user/workspaces.md) — creation, terminal grids, sidebar explorer, Git graph, handoff, persistence. - [Memory And Tasks](user/memory-and-tasks.md) — Memory panel (Files, Graph, Search), dynamic categories, tasks, agent memory tools. - [Plans](user/plans.md) — `.agents/plans/`, Kanban board, task syntax, Plans panel, agent tools. @@ -52,6 +53,7 @@ Welcome to the BLXCode docs. User guides explain how to run and use the app; dev - [Tauri IPC](developer/tauri-ipc.md) — command registration, wrappers, command groups. - [Voice Architecture](developer/voice.md) — STT/TTS modules and flows. - [Internationalization](developer/i18n.md) — locales, EULA content, translation workflow. +- [Themes](developer/themes.md) — tokens, `ThemeService`, adding themes, lint rules. - [Contributing](developer/contributing.md) — code style, rules, testing, pull request checklist. ## Project principles diff --git a/docs/THEME_EXCEPTIONS.md b/docs/THEME_EXCEPTIONS.md new file mode 100644 index 0000000..73f1d11 --- /dev/null +++ b/docs/THEME_EXCEPTIONS.md @@ -0,0 +1,13 @@ +# Theme exceptions + +These surfaces **do not** follow the app theme selector. This is intentional. + +| Surface | Reason | +|---------|--------| +| **Embedded browser iframe content** (Linux) | Foreign document — only app chrome (toolbar, frame) uses theme tokens. | +| **Native child webview** (Windows/macOS) | Rendered outside SPA CSS. | +| **`--memory-category-color`** | User-defined category swatch (data), not an app theme choice. | +| **`public/flags/*.svg`** | National flag colors must stay accurate. | +| **Third-party CDN CSS** (`xterm.min.css`) | Overridden via app CSS + xterm JS `theme` object synced to tokens. | + +Everything else in the workbench should use tokens from `themes/tokens.css`. diff --git a/docs/developer/agent-harness.md b/docs/developer/agent-harness.md index 4b47713..b5ed3f3 100644 --- a/docs/developer/agent-harness.md +++ b/docs/developer/agent-harness.md @@ -34,7 +34,8 @@ src-tauri/src/agent/ src/skills_rules/store.rs # CORE_SKILLS, core SkillSourceKind, availability src-tauri/src/api_keys.rs # Central key catalog, resolve, api_keys_status/apply src/workbench/ - harness_ui.rs # SettingsDock, App/Appearance panes + harness_ui.rs # SettingsDock, App pane + appearance_settings_pane/ # Theme picker (Settings → Appearance) agent_provider_pane/ # BLXCode Agent grid (text, web footer) harness_image_pane/ # AgentImageColumn harness_voice_pane/ # AgentVoiceColumn diff --git a/docs/developer/architecture.md b/docs/developer/architecture.md index 0478eed..6840e73 100644 --- a/docs/developer/architecture.md +++ b/docs/developer/architecture.md @@ -15,7 +15,7 @@ Leptos UI ## Frontend Entry Points - `src/main.rs`: mounts the Leptos app. -- `src/app.rs`: sets up i18n, EULA gating, and renders `WorkbenchShell`. +- `src/app.rs`: sets up i18n, **ThemeService**, EULA gating, and renders `WorkbenchShell`. - `src/workbench/mod.rs`: workbench context, state hydration, auto-save, embedded browser event handling. - `src/workbench/state.rs`: workspace state, snapshots, workspace creation draft, layout and browser state. - `src/tauri_bridge.rs`: typed wrappers around Tauri `invoke()` calls. @@ -198,6 +198,12 @@ The browser host supports native child webviews on platforms where Tauri's unsta The i18n service lives under `src/i18n/` and `src/service/`. Locale tables are Rust source files, while EULA source content is Markdown under `content/eula/`. +## Theming + +Themes are frontend-only. `ThemeService` (`src/workbench/theme_service.rs`) sets `html[data-theme]` from `themes/tokens.css` and persists to `localStorage`. The Appearance settings pane reads the catalog from `src/theme/catalog.rs`. JavaScript subsystems (xterm, 3D memory graph) listen for `blxcode-theme-changed` and read computed CSS variables. + +See [Themes](themes.md) and [Theme exceptions](../THEME_EXCEPTIONS.md). + ## Boundaries To Preserve - UI code should not perform native filesystem or keyring operations directly. diff --git a/docs/developer/i18n.md b/docs/developer/i18n.md index ef3894a..8314a13 100644 --- a/docs/developer/i18n.md +++ b/docs/developer/i18n.md @@ -20,6 +20,8 @@ BLXCode keeps UI strings and EULA content in explicit locale sources so translat 4. Use `i18n.tr(I18nKey::YourKey)()` from Leptos views. 5. Run a frontend check. +Appearance theme strings use paired keys per catalog id (`ThemeNameBlxcodeDark`, `ThemeDescBlxcodeDark`, …) wired through `src/theme/i18n.rs`. UI chrome keys (`AppearanceHeroTitle`, `AppearanceFilterDark`, …) live beside other settings strings in `en_us.rs`. + ```bash cargo check -p blxcode-ui --target wasm32-unknown-unknown ``` diff --git a/docs/developer/themes.md b/docs/developer/themes.md new file mode 100644 index 0000000..0dd7c41 --- /dev/null +++ b/docs/developer/themes.md @@ -0,0 +1,70 @@ +# Themes + +BLXCode themes are **CSS custom properties** switched at runtime via `html[data-theme]`. No backend involvement. + +## Key files + +| Path | Role | +|------|------| +| `themes/tokens.css` | Token definitions per theme (`[data-theme="…"]`) | +| `styles.css` | Layout rules consuming `var(--*)` | +| `src/theme/catalog.rs` | `AppTheme` metadata (id, mode, preview colors) | +| `src/theme/i18n.rs` | Maps theme id → `I18nKey` for name/description | +| `src/workbench/theme_service.rs` | `ThemeService`: persist, apply, dispatch event | +| `src/workbench/appearance_settings_pane/` | Settings UI | +| `index.html` | Anti-flash boot script + CSS load order | +| `public/terminal_bootstrap.mjs` | xterm palette from computed tokens | +| `frontend-js/graph3d_entry.mjs` | 3D graph colors + theme listener | +| `src/workbench/memory_graph/mod.rs` | 2D SVG reads tokens via `getComputedStyle` | + +## Runtime flow + +```text +index.html boot script + -> document.documentElement.dataset.theme +themes/tokens.css (before styles.css) + -> [data-theme] { --bg-app, --accent, … } +ThemeService::set_theme (App root context) + -> localStorage blxcode_theme_v1 + -> dataset.theme + -> CustomEvent blxcode-theme-changed +terminal_bootstrap.mjs / graph3d_entry.mjs + -> refresh instances from getComputedStyle +``` + +`ThemeService` is provided in `src/app.rs` (same level as `I18nService`) so EULA and boot screens inherit the active theme. + +## Adding a theme + +1. Add an `AppTheme` entry in `src/theme/catalog.rs` (include `ThemePreviewColors`). +2. Add `ThemeName*` / `ThemeDesc*` keys to `src/i18n/keys.rs` and all locale files. +3. Add `theme_name_key` / `theme_desc_key` arms in `src/theme/i18n.rs`. +4. Add a `[data-theme="your-id"]` block in `themes/tokens.css` with **all** extended tokens (overlays, terminal, agent accents, git lanes, semantic colors). +5. Register the id in the boot script `VALID` map in `index.html`. +6. Extend xterm / graph theme maps if you add new token names (prefer reusing existing tokens). + +Default theme id: `blxcode-dark` (`DEFAULT_THEME_ID`). + +## Styling rules + +- Use semantic tokens (`--accent`, `--overlay-2`, `--danger`, …) — not raw hex in component CSS. +- Component CSS lives beside components; global tokens live in `themes/tokens.css`. +- No `var(--token, #fallback)` literals in `src/**/*.css`. +- Run `scripts/lint_theme_tokens.sh` before merge. +- See `.agents/rules/rule-theme-tokens.md`. + +## Tokenization tooling + +`scripts/tokenize_styles_css.py` replaces hardcoded colors in `styles.css` with `var(--*)` references. Re-run after large CSS edits. + +## Exceptions + +Surfaces that cannot follow `data-theme` are documented in [THEME_EXCEPTIONS.md](../THEME_EXCEPTIONS.md). + +## i18n + +Appearance chrome uses keys such as `AppearanceHeroTitle`, `AppearanceFilterDark`, etc. Theme catalog strings use paired `ThemeName*` / `ThemeDesc*` keys per theme id. Regenerate missing locale rows with: + +```bash +python scripts/tools/render_i18n_locales_from_en.py +``` diff --git a/docs/user/appearance-themes.md b/docs/user/appearance-themes.md new file mode 100644 index 0000000..7c71238 --- /dev/null +++ b/docs/user/appearance-themes.md @@ -0,0 +1,57 @@ +# Appearance & Themes + +BLXCode ships **12 app themes**. Colors apply across the workbench — sidebar, settings, terminals, memory graph, and agent panels — via a shared token system. + +## Open the theme picker + +1. Open **Settings** (command palette → **Open Settings**, or your configured shortcut). +2. Select **Appearance** in the left sidebar. + +The pane shows: + +- A **hero preview** of the active theme +- A **search** field (filters by translated name and description) +- **All / Dark / Light** filters with counts +- A **grid of theme cards** with mini layout previews + +Click a card to apply the theme immediately. The active card shows an **ACTIVE** badge and accent border. + +## Default theme + +**BLXCode** (`blxcode-dark`) is the default — the same deep dark look BLXCode used before themes shipped. First launch and a cleared `localStorage` always fall back to this theme. + +## Available themes + +| Theme | Mode | +|-------|------| +| **BLXCode** | Dark | +| BLXCode Light | Light | +| Dracula | Dark | +| Gruvbox Dark / Light | Dark / Light | +| Solarized Dark / Light | Dark / Light | +| Nord | Dark | +| One Dark | Dark | +| Catppuccin Mocha / Latte | Dark / Light | +| Tokyo Night | Dark | + +Theme names and descriptions follow your **Settings → App → Language** choice. + +## Persistence + +The selected theme is stored in browser `localStorage` under `blxcode_theme_v1` and restored on reload. A small inline script in `index.html` applies the saved theme before CSS loads to avoid a flash of the wrong colors. + +## What themes do not change + +Some surfaces are intentionally outside the theme selector: + +- **Embedded browser page content** (Linux iframe) — only the app chrome around the page follows the theme. +- **Native child webviews** on Windows/macOS — outside SPA styling. +- **Memory category swatches** you set under Workspace → Category colors — user data, not app chrome. +- **Flag icons** in the language picker — national colors stay accurate. + +See [Theme exceptions](../THEME_EXCEPTIONS.md) for the full list. + +## See also + +- [Settings](settings.md) — all settings categories +- [UI Language](language.md) — locale picker (App tab) diff --git a/docs/user/settings.md b/docs/user/settings.md index 6e35c65..02a29c8 100644 --- a/docs/user/settings.md +++ b/docs/user/settings.md @@ -7,13 +7,26 @@ BLXCode opens settings in a **center workbench tab** (not a modal). The command | Category | What it configures | |----------|-------------------| | **App** | UI language, STT language + push-to-talk, keyboard shortcut mode, notifications, terminal hooks, app updates | -| **Appearance** | Placeholder (themes planned) | +| **Appearance** | App themes — 12 presets, search, Dark/Light filters; see [Appearance & Themes](appearance-themes.md) | | **API Keys** | All provider secrets in one pane — see below | | **Workspace** | Default project directory, agent sandbox root, embedded browser URL, **category colors** for Memory | | **BLXCode Agent** | Text, image, and voice inference — see below | Legacy saved categories (`Image`, `Voice`, `Memory`) still open the correct pane. +## Appearance + +**Settings → Appearance** lets you pick an app theme: + +- **BLXCode** (default) — the original dark workbench look +- Eleven additional dark/light presets (Dracula, Gruvbox, Solarized, Nord, One Dark, Catppuccin, Tokyo Night, BLXCode Light) +- Search and **All / Dark / Light** filters +- Instant preview on each card; choice persists across restarts + +Themes affect sidebar, panels, terminals, graphs, and settings chrome. Embedded web pages, native webviews, and your Memory category color swatches are documented exceptions. + +Full guide: [Appearance & Themes](appearance-themes.md). + ## API Keys **Settings → API Keys** is the only place to enter provider secrets. diff --git a/frontend-js/graph3d_entry.mjs b/frontend-js/graph3d_entry.mjs index 936ed4b..5afaaed 100644 --- a/frontend-js/graph3d_entry.mjs +++ b/frontend-js/graph3d_entry.mjs @@ -5,6 +5,29 @@ import SpriteText from "three-spritetext"; const instances = new Map(); let nextId = 1; +function readCssVar(name, fallback = "") { + try { + const v = getComputedStyle(document.documentElement).getPropertyValue(name).trim(); + return v || fallback; + } catch (_) { + return fallback; + } +} + +function applyGraph3dTheme(rec) { + if (!rec?.graph) return; + rec.graph.backgroundColor("rgba(0,0,0,0)"); + rec.graph.linkColor(() => readCssVar("--overlay-4", "rgba(255,255,255,0.14)")); + rec.graph.nodeThreeObject(makeNodeObject); + rec.graph.refresh(); +} + +window.addEventListener("blxcode-theme-changed", () => { + for (const rec of instances.values()) { + applyGraph3dTheme(rec); + } +}); + function cleanLabel(raw) { const tail = String(raw || "") .replace(/\\/g, "/") @@ -99,9 +122,9 @@ function makeNodeObject(node) { }), ); const label = new SpriteText(wrapLabel(node.label || node.id)); - label.color = "rgba(238,239,245,0.92)"; - label.backgroundColor = "rgba(8,10,16,0.58)"; - label.borderColor = "rgba(255,255,255,0.14)"; + label.color = readCssVar("--text", "rgba(238,239,245,0.92)"); + label.backgroundColor = readCssVar("--scrim-bg", "rgba(8,10,16,0.58)"); + label.borderColor = readCssVar("--overlay-4", "rgba(255,255,255,0.14)"); label.borderWidth = 0.25; label.borderRadius = 2; label.padding = 2.6; @@ -345,7 +368,7 @@ window.__blxcodeGraph3d = { .nodeId("id") .nodeLabel("label") .nodeThreeObject(makeNodeObject) - .linkColor(() => "rgba(190,205,235,0.28)") + .linkColor(() => readCssVar("--overlay-4", "rgba(255,255,255,0.14)")) .linkOpacity(0.34) .linkWidth(1) .linkCurvature((link) => linkWobble(link)) diff --git a/index.html b/index.html index cd81263..3440eee 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,34 @@ href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,500;0,600;0,700;1,400&display=swap" rel="stylesheet" /> + + @@ -19,6 +47,8 @@ + + 40: + print(f" … and {len(bad) - 40} more") + sys.exit(1) +print("OK: styles.css") +PY +fail=$? + +if [[ $fail -ne 0 ]]; then + echo "styles.css lint failed (hardcoded colors remain — extend scripts/tokenize_styles_css.py)" + exit 1 +fi + +echo "Checking src/**/*.css for var(..., #literal) fallbacks…" +if rg -n 'var\([^)]+,\s*#' src --glob '*.css' >/tmp/theme-lint-fallbacks.txt 2>/dev/null; then + echo "WARN: literal fallbacks in var():" + cat /tmp/theme-lint-fallbacks.txt + exit 1 +fi + +echo "All theme token lint checks passed." diff --git a/scripts/tokenize_styles_css.py b/scripts/tokenize_styles_css.py new file mode 100644 index 0000000..4f0bfd8 --- /dev/null +++ b/scripts/tokenize_styles_css.py @@ -0,0 +1,261 @@ +#!/usr/bin/env python3 +"""Replace hardcoded colors in styles.css with var() refs outside the :root block.""" + +from __future__ import annotations + +from pathlib import Path + +import re + +# Absolute path keeps cwd-independent behaviour. +_REPO = Path(__file__).resolve().parent.parent +STYLES = _REPO / "styles.css" + +# 1-indexed line numbers inclusive; content inside must not be rewritten. +SKIP_LINE_NUMBERS = set(range(6, 19)) + +# Exact-match replacements as specified (order here is re-sorted below). +_PAIR_SPECS: list[tuple[str, str]] = [ + ("rgba(255, 255, 255, 0.04)", "var(--overlay-1)"), + ("rgba(255, 255, 255, 0.07)", "var(--overlay-2)"), + ("rgba(255, 255, 255, 0.1)", "var(--overlay-3)"), + ("rgba(255, 255, 255, 0.14)", "var(--overlay-4)"), + ("rgba(255, 255, 255, 0.2)", "var(--overlay-5)"), + ("rgba(255, 255, 255, 0.28)", "var(--overlay-6)"), + ("rgba(255, 255, 255, 0.06)", "var(--overlay-2)"), + ("rgba(255, 255, 255, 0.08)", "var(--overlay-2)"), + ("rgba(255, 255, 255, 0.05)", "var(--overlay-1)"), + ("rgba(255, 255, 255, 0.12)", "var(--overlay-3)"), + ("rgba(255, 255, 255, 0.025)", "var(--overlay-1)"), + ("rgba(255, 255, 255, 0.03)", "var(--overlay-1)"), + ("rgba(255, 255, 255, 0.02)", "var(--overlay-1)"), + ("rgba(255, 255, 255, 0.045)", "var(--overlay-1)"), + ("rgba(255, 255, 255, 0.035)", "var(--overlay-1)"), + ("rgba(255, 255, 255, 0.055)", "var(--overlay-2)"), + ("rgba(255, 255, 255, 0.13)", "var(--overlay-4)"), + ("rgba(88, 166, 255, 0.16)", "var(--accent-soft)"), + ("rgba(88, 166, 255, 0.12)", "var(--accent-cool-soft)"), + ("rgba(88, 166, 255, 0.08)", "var(--accent-soft)"), + ("rgba(88, 166, 255, 0.075)", "var(--accent-soft)"), + ("rgba(88, 166, 255, 0.26)", "var(--accent-soft)"), + ("rgba(91, 157, 255, 0.08)", "var(--accent-soft)"), + ("#ffffff", "var(--text-bright)"), + ("#fff", "var(--text-bright)"), + ("#e57373", "var(--danger)"), + ("#58a6ff", "var(--accent)"), + ("#5b9dff", "var(--accent-hover)"), + ("#7ab8ff", "var(--accent-hover)"), + ("#4ea1ff", "var(--accent)"), + ("#8bc4ff", "var(--accent-cool)"), + ("#2c2f36", "var(--bg-panel-header)"), + ("#1e2128", "var(--bg-panel)"), + ("#050608", "var(--bg-app)"), + ("#17181d", "var(--bg-raised)"), + ("#101114", "var(--bg-raised)"), + ("#0e0e10", "var(--on-accent)"), + ("#4a9eff", "var(--git-lane-0)"), + ("#a371f7", "var(--git-lane-1)"), + ("#89ddff", "var(--git-lane-5)"), + ("#8ec5ff", "var(--accent-cool)"), + ("rgba(0, 0, 0, 0.45)", "var(--scrim-bg)"), + ("rgba(0, 0, 0, 0.42)", "var(--scrim-bg)"), + ("rgba(0, 0, 0, 0.28)", "var(--scrim-bg)"), + ("rgba(18, 18, 22, 0.62)", "var(--scrim-bg)"), + # Pass 2 — extended semantic mappings + ("rgba(238, 239, 245, 0.95)", "var(--text)"), + ("rgba(238, 239, 245, 0.92)", "var(--text)"), + ("rgba(241, 242, 245, 0.55)", "var(--text-muted)"), + ("rgba(241, 242, 245, 0.72)", "var(--text-muted)"), + ("rgba(241, 242, 245, 0.9)", "var(--text)"), + ("rgba(160, 165, 180, 0.7)", "var(--text-muted)"), + ("rgba(190, 194, 208, 0.78)", "var(--text-muted)"), + ("rgba(255, 255, 255, 0.09)", "var(--overlay-2)"), + ("rgba(255, 255, 255, 0.065)", "var(--overlay-2)"), + ("rgba(255, 255, 255, 0.042)", "var(--overlay-1)"), + ("rgba(255, 255, 255, 0.015)", "var(--overlay-1)"), + ("rgba(255, 255, 255, 0.01)", "var(--overlay-1)"), + ("rgba(255, 255, 255, 0.55)", "var(--overlay-5)"), + ("rgba(0, 0, 0, 0.25)", "var(--scrim-bg)"), + ("rgba(0, 0, 0, 0.2)", "var(--scrim-bg)"), + ("rgba(0, 0, 0, 0.34)", "var(--scrim-bg)"), + ("rgba(0, 0, 0, 0.32)", "var(--scrim-bg)"), + ("rgba(0, 0, 0, 0.48)", "var(--scrim-bg)"), + ("rgba(0, 0, 0, 0.5)", "var(--scrim-bg)"), + ("rgba(0, 0, 0, 0.58)", "var(--scrim-bg)"), + ("rgba(0, 0, 0, 0.18)", "var(--scrim-bg)"), + ("rgba(9, 10, 13, 0.96)", "var(--bg-app)"), + ("rgba(9, 10, 13, 0.95)", "var(--bg-app)"), + ("rgba(9, 10, 13, 0.58)", "var(--bg-app)"), + ("rgba(21, 23, 29, 0.93)", "var(--bg-panel)"), + ("rgba(12, 13, 18, 0.93)", "var(--bg-raised)"), + ("rgba(16, 17, 22, 0.74)", "var(--bg-panel)"), + ("rgba(14, 15, 20, 0.92)", "var(--bg-panel)"), + ("rgba(18, 19, 24, 0.88)", "var(--bg-panel)"), + ("rgba(11, 12, 16, 0.9)", "var(--bg-raised)"), + ("rgba(5, 6, 8, 0.42)", "var(--bg-app)"), + ("rgba(24, 26, 34, 0.96)", "var(--bg-panel-header)"), + ("rgba(155, 211, 255, 0.22)", "var(--accent-cool-soft)"), + ("rgba(155, 211, 255, 0.16)", "var(--accent-cool-soft)"), + ("rgba(114, 160, 255, 0.9)", "var(--accent)"), + ("rgba(114, 160, 255, 0.55)", "var(--accent-soft)"), + ("rgba(114, 160, 255, 0.4)", "var(--accent-soft)"), + ("rgba(114, 160, 255, 0.18)", "var(--accent-soft)"), + ("rgba(160, 180, 255, 0.85)", "var(--accent-cool)"), + ("rgba(120, 160, 255, 0.35)", "var(--accent-soft)"), + ("rgba(91, 157, 255, 0.12)", "var(--accent-soft)"), + ("rgba(91, 157, 255, 0.2)", "var(--accent-soft)"), + ("rgba(88, 166, 255, 0.18)", "var(--accent-soft)"), + ("rgba(88, 166, 255, 0.32)", "var(--accent-soft)"), + ("rgba(88, 166, 255, 0.3)", "var(--accent-soft)"), + ("rgba(88, 166, 255, 0.42)", "var(--accent-soft)"), + ("rgba(88, 166, 255, 0.11)", "var(--accent-soft)"), + ("rgba(88, 166, 255, 0)", "transparent"), + ("rgba(78, 161, 255, 0.08)", "var(--accent-soft)"), + ("rgba(99, 230, 190, 0.12)", "var(--accent-cool-soft)"), + ("rgba(104, 220, 159, 0.08)", "var(--success)"), + ("rgba(248, 113, 113, 0.08)", "var(--danger-soft)"), + ("rgba(255, 80, 64, 0.1)", "var(--danger-soft)"), + ("#e8954a", "var(--agent-claude)"), + ("#ff9aa2", "var(--danger)"), + ("#ffb1b1", "var(--danger)"), + ("#ffb4a8", "var(--danger)"), + ("#86e7b2", "var(--success)"), + ("#8fd9c4", "var(--syntax-type)"), + ("#0d0e12", "var(--on-accent)"), + ("#000", "var(--bg-app)"), + ("#0a0a0a", "var(--bg-app)"), + ("#67e8f9", "var(--syntax-type)"), + ("#cbd5e1", "var(--text-muted)"), + ("#fca5a5", "var(--danger)"), + ("#d1d5db", "var(--text-muted)"), + ("#fbbf24", "var(--warning)"), + ("#86efac", "var(--success)"), + ("#d8e7ff", "var(--accent-cool)"), + ("#c4a8ff", "var(--syntax-keyword)"), + ("#9cd2ff", "var(--accent-cool)"), + ("#ffd166", "var(--warning)"), + ("#63e6be", "var(--syntax-type)"), + ("#eaf4ff", "var(--accent-cool-soft)"), + ("#ffc04d", "var(--warning)"), + ("#e8b84a", "var(--warning)"), + ("#ff9a9a", "var(--danger)"), + ("#9aa", "var(--text-faint)"), + ("#9aa4b2", "var(--text-muted)"), + ("#8a8f98", "var(--text-muted)"), + ("#7dd3fc", "var(--accent-cool)"), + ("#2a2a2a", "var(--border)"), +] + +# Longest needles first avoids e.g. #fff mangling #ffffff. +REPLACEMENTS = sorted(_PAIR_SPECS, key=lambda p: len(p[0]), reverse=True) + +_RGBA_RE = re.compile(r"rgba?\([^)]+\)") +_HEX_RE = re.compile(r"#[0-9a-fA-F]{3,8}\b") + + +def _heuristic_token(literal: str, *, for_text: bool = False) -> str: + low = literal.lower().replace(" ", "") + if for_text: + if low.startswith("rgba(255,255,255") or low.startswith("rgb(255,255,255"): + # High-alpha white literals are label text, not overlay fills. + if ",0.9" in low or ",0.92" in low or ",0.95" in low or ",0.88" in low: + return "var(--text-secondary)" + if ",0.75" in low or ",0.8" in low or ",0.85" in low: + return "var(--text-hint)" + return "var(--text-muted)" + if low.startswith("rgba(0,0,0") or low.startswith("rgb(0,0,0"): + return "var(--text-muted)" + if low.startswith("#"): + if low in ("#000", "#000000"): + return "var(--text)" + return "var(--text-muted)" + return "var(--text-muted)" + if low.startswith("rgba(255,255,255") or low.startswith("rgb(255,255,255"): + return "var(--overlay-2)" + if low.startswith("rgba(0,0,0") or low.startswith("rgb(0,0,0"): + return "var(--scrim-bg)" + if "166,255" in low or "157,255" in low or "144,255" in low or "160,255" in low: + return "var(--accent-soft)" + if "230,190" in low or "220,159" in low or "fb950" in low: + return "var(--success)" + if "113,113" in low or "248,113" in low or "255,80" in low: + return "var(--danger-soft)" + if low.startswith("#"): + if low in ("#000", "#000000"): + return "var(--bg-app)" + return "var(--text-muted)" + if "9,10,13" in low or "5,6," in low or "12,13,18" in low or "20,22,30" in low: + return "var(--bg-app)" + return "var(--overlay-2)" + + +_TEXT_COLOR_PROPS = frozenset( + { + "color", + "caret-color", + "text-decoration-color", + "-webkit-text-fill-color", + } +) + + +def _line_sets_text_color(line: str) -> bool: + if ":" not in line: + return False + prop = line.split(":", 1)[0].strip().lower() + return prop in _TEXT_COLOR_PROPS + + +def _heuristic_pass(line: str) -> tuple[str, int]: + total = 0 + for_text = _line_sets_text_color(line) + out = line + + def repl_rgba(m: re.Match[str]) -> str: + nonlocal total + total += 1 + return _heuristic_token(m.group(0), for_text=for_text) + + out = _RGBA_RE.sub(repl_rgba, out) + + def repl_hex(m: re.Match[str]) -> str: + nonlocal total + total += 1 + return _heuristic_token(m.group(0), for_text=for_text) + + out = _HEX_RE.sub(repl_hex, out) + return out, total + + +def transform_line(line_no: int, line: str) -> tuple[str, int]: + if line_no in SKIP_LINE_NUMBERS: + return line, 0 + total = 0 + out = line + for old, new in REPLACEMENTS: + cnt = out.count(old) + if cnt: + total += cnt + out = out.replace(old, new) + out, extra = _heuristic_pass(out) + total += extra + return out, total + + +def main() -> None: + text = STYLES.read_text(encoding="utf-8") + lines = text.splitlines(keepends=True) + + rebuilt: list[str] = [] + grand_total = 0 + for i, ln in enumerate(lines, start=1): + new_ln, cnt = transform_line(i, ln) + grand_total += cnt + rebuilt.append(new_ln) + + STYLES.write_text("".join(rebuilt), encoding="utf-8", newline="\n") + print(f"tokenize_styles_css: {grand_total} replacement(s)") + + +if __name__ == "__main__": + main() diff --git a/src/app.rs b/src/app.rs index 0fa5756..884aab9 100644 --- a/src/app.rs +++ b/src/app.rs @@ -7,6 +7,7 @@ use crate::i18n::{localized_eula_html, I18nKey}; use crate::open_http::dom_click_http_url_from_mouse_event; use crate::quit::request_app_quit; use crate::service::I18nService; +use crate::workbench::ThemeService; use crate::workbench::WorkbenchShell; use leptos::prelude::*; use leptos::task::spawn_local; @@ -17,7 +18,9 @@ use wasm_bindgen::JsCast; #[component] pub fn App() -> impl IntoView { let i18n = I18nService::new(); + let theme = ThemeService::new(); provide_context(i18n); + provide_context(theme); Effect::new(move |_| { remove_static_boot_screen(); diff --git a/src/config/app.config.rs b/src/config/app.config.rs index f395c2b..0191e40 100644 --- a/src/config/app.config.rs +++ b/src/config/app.config.rs @@ -11,6 +11,9 @@ pub const GITIGNORE_PROMPT_ANSWER_NO: &str = "no"; /// `localStorage` key for UI locale (BCP-47, e.g. `de-DE`, `en-US`). pub const I18N_LOCALE_STORAGE_KEY: &str = "blxcode_locale_v1"; +/// `localStorage` key for the active app theme id (e.g. `blxcode-dark`). +pub const THEME_STORAGE_KEY: &str = "blxcode_theme_v1"; + /// `localStorage` key for memory graph rendering mode (`2d` / `3d`). pub const GRAPH_MODE_STORAGE_KEY: &str = "blxcode_memory_graph_mode_v1"; diff --git a/src/i18n/keys.rs b/src/i18n/keys.rs index ba8c5f1..43b3256 100644 --- a/src/i18n/keys.rs +++ b/src/i18n/keys.rs @@ -326,6 +326,58 @@ pub enum I18nKey { HsCatImage, AppHeading, AppearanceHeading, + AppearanceHeroTitle, + AppearanceHeroSubtitle, + AppearanceSearchPlaceholder, + AppearanceSearchAria, + AppearanceFilterAll, + AppearanceFilterDark, + AppearanceFilterLight, + AppearanceFilterAria, + AppearanceActiveBadge, + AppearanceActivePreviewLabel, + AppearanceThemeGridAria, + AppearanceThemeSelectAria, + ThemeNameBlxcodeDark, + ThemeDescBlxcodeDark, + ThemeNameBlxcodeLight, + ThemeDescBlxcodeLight, + ThemeNameDracula, + ThemeDescDracula, + ThemeNameGruvboxDark, + ThemeDescGruvboxDark, + ThemeNameGruvboxLight, + ThemeDescGruvboxLight, + ThemeNameSolarizedDark, + ThemeDescSolarizedDark, + ThemeNameSolarizedLight, + ThemeDescSolarizedLight, + ThemeNameNord, + ThemeDescNord, + ThemeNameOneDark, + ThemeDescOneDark, + ThemeNameCatppuccinMocha, + ThemeDescCatppuccinMocha, + ThemeNameCatppuccinLatte, + ThemeDescCatppuccinLatte, + ThemeNameTokyoNight, + ThemeDescTokyoNight, + ThemeNameRosePine, + ThemeDescRosePine, + ThemeNameRosePineDawn, + ThemeDescRosePineDawn, + ThemeNameEverforestDark, + ThemeDescEverforestDark, + ThemeNameKanagawa, + ThemeDescKanagawa, + ThemeNameGithubDark, + ThemeDescGithubDark, + ThemeNameNightOwl, + ThemeDescNightOwl, + ThemeNameAyuMirage, + ThemeDescAyuMirage, + ThemeNameCatppuccinFrappe, + ThemeDescCatppuccinFrappe, ApiKeysHeading, ApiKeysLlmSubhead, ApiKeysSearchSubhead, diff --git a/src/i18n/locales/de_de.rs b/src/i18n/locales/de_de.rs index 36aadbf..bee85fd 100644 --- a/src/i18n/locales/de_de.rs +++ b/src/i18n/locales/de_de.rs @@ -318,6 +318,58 @@ pub fn msg(key: I18nKey) -> &'static str { I18nKey::HsCatImage => "Bild", I18nKey::AppHeading => "App", I18nKey::AppearanceHeading => "Erscheinungsbild", + I18nKey::AppearanceHeroTitle => "Machen Sie es zu Ihrem.", + I18nKey::AppearanceHeroSubtitle => "{n} Themen – Farben werden sofort auf die gesamte Arbeitsfläche angewendet.", + I18nKey::AppearanceSearchPlaceholder => "Themen suchen…", + I18nKey::AppearanceSearchAria => "Suchthemen", + I18nKey::AppearanceFilterAll => "Alle", + I18nKey::AppearanceFilterDark => "Dunkel", + I18nKey::AppearanceFilterLight => "Licht", + I18nKey::AppearanceFilterAria => "Filtern Sie Themen nach Darstellungsmodus", + I18nKey::AppearanceActiveBadge => "AKTIV", + I18nKey::AppearanceActivePreviewLabel => "Vorschau des aktiven Themes", + I18nKey::AppearanceThemeGridAria => "Themenauswahl", + I18nKey::AppearanceThemeSelectAria => "Wählen Sie das Thema {Name} aus", + I18nKey::ThemeNameBlxcodeDark => "BLXCode", + I18nKey::ThemeDescBlxcodeDark => "Deep Dark Workbench – der Standard-BLXCode-Look", + I18nKey::ThemeNameBlxcodeLight => "BLXCode Licht", + I18nKey::ThemeDescBlxcodeLight => "Saubere helle Oberflächen mit kühlen blauen Akzenten", + I18nKey::ThemeNameDracula => "Dracula", + I18nKey::ThemeDescDracula => "Lila-rosa Akzente auf einer satten dunklen Basis", + I18nKey::ThemeNameGruvboxDark => "Gruvbox Dark", + I18nKey::ThemeDescGruvboxDark => "Warmes Retro-Dunkel mit gedämpften Erdtönen", + I18nKey::ThemeNameGruvboxLight => "Gruvbox-Licht", + I18nKey::ThemeDescGruvboxLight => "Sanftes Pergamentlicht mit warmen Akzenten", + I18nKey::ThemeNameSolarizedDark => "Solarisierte Dunkelheit", + I18nKey::ThemeDescSolarizedDark => "Präzises Dunkel mit ausgewogenem Cyan und Orange", + I18nKey::ThemeNameSolarizedLight => "Solarisiertes Licht", + I18nKey::ThemeDescSolarizedLight => "Kontrastarmes Licht für lange Sitzungen", + I18nKey::ThemeNameNord => "Nord", + I18nKey::ThemeDescNord => "Arktische blaugraue Ruhe", + I18nKey::ThemeNameOneDark => "Ein Dunkler", + I18nKey::ThemeDescOneDark => "Von Atomen inspiriertes, ausgewogenes Dunkel", + I18nKey::ThemeNameCatppuccinMocha => "Catppuccin Mokka", + I18nKey::ThemeDescCatppuccinMocha => "Pastelldunkel mit sanftem Kontrast", + I18nKey::ThemeNameCatppuccinLatte => "Catppuccin Latte", + I18nKey::ThemeDescCatppuccinLatte => "Gemütliches helles Pastell", + I18nKey::ThemeNameTokyoNight => "Tokio-Nacht", + I18nKey::ThemeDescTokyoNight => "Tiefblaue Nacht mit Neonakzenten", + I18nKey::ThemeNameRosePine => "Rosé-Kiefer", + I18nKey::ThemeDescRosePine => "Gedecktes Rose und Iris auf weicher dunkler Basis", + I18nKey::ThemeNameRosePineDawn => "Rosé Pine Dawn", + I18nKey::ThemeDescRosePineDawn => "Warmes Papierlicht mit Pine- und Iris-Akzenten", + I18nKey::ThemeNameEverforestDark => "Immerwald dunkel", + I18nKey::ThemeDescEverforestDark => "Ruhige Waldgrüntöne auf weichem Anthrazit", + I18nKey::ThemeNameKanagawa => "Kanagawa", + I18nKey::ThemeDescKanagawa => "Japanisches Tuschemal-Dunkel mit Wellenblau", + I18nKey::ThemeNameGithubDark => "GitHub Dark", + I18nKey::ThemeDescGithubDark => "Knackiges Developer-Dunkel mit vertrautem Blau", + I18nKey::ThemeNameNightOwl => "Nachteule", + I18nKey::ThemeDescNightOwl => "Tiefes Navy-Editor-Dunkel für wenig Licht", + I18nKey::ThemeNameAyuMirage => "Ayu Mirage", + I18nKey::ThemeDescAyuMirage => "Warme Dämmerung mit Cyan- und Amber-Akzenten", + I18nKey::ThemeNameCatppuccinFrappe => "Catppuccin Frappé", + I18nKey::ThemeDescCatppuccinFrappe => "Ausgewogenes Pastell-Dunkel zwischen Mocha und Latte", I18nKey::ApiKeysHeading => "API-Schlüssel", I18nKey::ApiKeysLlmSubhead => "LLM-Anbieter", I18nKey::ApiKeysSearchSubhead => "Such-Anbieter", diff --git a/src/i18n/locales/en_us.rs b/src/i18n/locales/en_us.rs index 8977c24..f426afe 100644 --- a/src/i18n/locales/en_us.rs +++ b/src/i18n/locales/en_us.rs @@ -356,6 +356,60 @@ Classic: Ctrl+O quick open, Ctrl+` new terminal, Ctrl+Shift+P palette." I18nKey::HsCatImage => "Image", I18nKey::AppHeading => "App", I18nKey::AppearanceHeading => "Appearance", + I18nKey::AppearanceHeroTitle => "Make it yours.", + I18nKey::AppearanceHeroSubtitle => "{n} themes — colors apply instantly across the workbench.", + I18nKey::AppearanceSearchPlaceholder => "Search themes…", + I18nKey::AppearanceSearchAria => "Search themes", + I18nKey::AppearanceFilterAll => "All", + I18nKey::AppearanceFilterDark => "Dark", + I18nKey::AppearanceFilterLight => "Light", + I18nKey::AppearanceFilterAria => "Filter themes by appearance mode", + I18nKey::AppearanceActiveBadge => "ACTIVE", + I18nKey::AppearanceActivePreviewLabel => "Active theme preview", + I18nKey::AppearanceThemeGridAria => "Theme selection", + I18nKey::AppearanceThemeSelectAria => "Select theme {name}", + I18nKey::ThemeNameBlxcodeDark => "BLXCode", + I18nKey::ThemeDescBlxcodeDark => { + "Deep dark workbench — the default BLXCode look" + } + I18nKey::ThemeNameBlxcodeLight => "BLXCode Light", + I18nKey::ThemeDescBlxcodeLight => "Clean light surfaces with cool blue accents", + I18nKey::ThemeNameDracula => "Dracula", + I18nKey::ThemeDescDracula => "Purple-pink accents on a rich dark base", + I18nKey::ThemeNameGruvboxDark => "Gruvbox Dark", + I18nKey::ThemeDescGruvboxDark => "Warm retro dark with muted earth tones", + I18nKey::ThemeNameGruvboxLight => "Gruvbox Light", + I18nKey::ThemeDescGruvboxLight => "Soft parchment light with warm highlights", + I18nKey::ThemeNameSolarizedDark => "Solarized Dark", + I18nKey::ThemeDescSolarizedDark => "Precision dark with balanced cyan and orange", + I18nKey::ThemeNameSolarizedLight => "Solarized Light", + I18nKey::ThemeDescSolarizedLight => "Low-contrast light built for long sessions", + I18nKey::ThemeNameNord => "Nord", + I18nKey::ThemeDescNord => "Arctic blue-gray calm", + I18nKey::ThemeNameOneDark => "One Dark", + I18nKey::ThemeDescOneDark => "Atom-inspired balanced dark", + I18nKey::ThemeNameCatppuccinMocha => "Catppuccin Mocha", + I18nKey::ThemeDescCatppuccinMocha => "Pastel dark with soft contrast", + I18nKey::ThemeNameCatppuccinLatte => "Catppuccin Latte", + I18nKey::ThemeDescCatppuccinLatte => "Cozy light pastel", + I18nKey::ThemeNameTokyoNight => "Tokyo Night", + I18nKey::ThemeDescTokyoNight => "Deep blue night with neon accents", + I18nKey::ThemeNameRosePine => "Rosé Pine", + I18nKey::ThemeDescRosePine => "Muted rose and iris on a soft dark base", + I18nKey::ThemeNameRosePineDawn => "Rosé Pine Dawn", + I18nKey::ThemeDescRosePineDawn => "Warm paper light with pine and iris accents", + I18nKey::ThemeNameEverforestDark => "Everforest Dark", + I18nKey::ThemeDescEverforestDark => "Calm forest greens on a soft charcoal base", + I18nKey::ThemeNameKanagawa => "Kanagawa", + I18nKey::ThemeDescKanagawa => "Japanese ink-wash dark with wave-blue highlights", + I18nKey::ThemeNameGithubDark => "GitHub Dark", + I18nKey::ThemeDescGithubDark => "Crisp developer dark with familiar blue accents", + I18nKey::ThemeNameNightOwl => "Night Owl", + I18nKey::ThemeDescNightOwl => "Deep navy editor dark tuned for low-light coding", + I18nKey::ThemeNameAyuMirage => "Ayu Mirage", + I18nKey::ThemeDescAyuMirage => "Warm dusk palette with cyan and amber pops", + I18nKey::ThemeNameCatppuccinFrappe => "Catppuccin Frappé", + I18nKey::ThemeDescCatppuccinFrappe => "Balanced pastel dark between Mocha and Latte", I18nKey::ApiKeysHeading => "API Keys", I18nKey::ApiKeysLlmSubhead => "LLM Providers", I18nKey::ApiKeysSearchSubhead => "Search Providers", diff --git a/src/i18n/locales/es_es.rs b/src/i18n/locales/es_es.rs index e702ae2..995ce43 100644 --- a/src/i18n/locales/es_es.rs +++ b/src/i18n/locales/es_es.rs @@ -318,6 +318,58 @@ pub fn msg(key: I18nKey) -> &'static str { I18nKey::HsCatImage => "Imagen", I18nKey::AppHeading => "Aplicación", I18nKey::AppearanceHeading => "Apariencia", + I18nKey::AppearanceHeroTitle => "Hazlo tuyo.", + I18nKey::AppearanceHeroSubtitle => "{n} temas: los colores se aplican instantáneamente en todo el banco de trabajo.", + I18nKey::AppearanceSearchPlaceholder => "Buscar temas…", + I18nKey::AppearanceSearchAria => "Buscar temas", + I18nKey::AppearanceFilterAll => "Todo", + I18nKey::AppearanceFilterDark => "Oscuro", + I18nKey::AppearanceFilterLight => "Luz", + I18nKey::AppearanceFilterAria => "Filtrar temas por modo de apariencia", + I18nKey::AppearanceActiveBadge => "ACTIVO", + I18nKey::AppearanceActivePreviewLabel => "Vista previa del tema activo", + I18nKey::AppearanceThemeGridAria => "Selección de tema", + I18nKey::AppearanceThemeSelectAria => "Seleccione el tema {nombre}", + I18nKey::ThemeNameBlxcodeDark => "Código BLX", + I18nKey::ThemeDescBlxcodeDark => "Banco de trabajo oscuro y profundo: el aspecto predeterminado de BLXCode", + I18nKey::ThemeNameBlxcodeLight => "Luz de código BLX", + I18nKey::ThemeDescBlxcodeLight => "Superficies claras y limpias con toques azules fríos", + I18nKey::ThemeNameDracula => "Drácula", + I18nKey::ThemeDescDracula => "Detalles de color rosa púrpura sobre una rica base oscura", + I18nKey::ThemeNameGruvboxDark => "Gruvbox Oscuro", + I18nKey::ThemeDescGruvboxDark => "Cálido retro oscuro con tonos tierra apagados", + I18nKey::ThemeNameGruvboxLight => "Luz Gruvbox", + I18nKey::ThemeDescGruvboxLight => "Luz suave como pergamino con reflejos cálidos", + I18nKey::ThemeNameSolarizedDark => "Solarizado Oscuro", + I18nKey::ThemeDescSolarizedDark => "Oscuro de precisión con cian y naranja equilibrados", + I18nKey::ThemeNameSolarizedLight => "Luz solarizada", + I18nKey::ThemeDescSolarizedLight => "Luz de bajo contraste diseñada para sesiones largas", + I18nKey::ThemeNameNord => "Norte", + I18nKey::ThemeDescNord => "Calma gris azulada del Ártico", + I18nKey::ThemeNameOneDark => "uno oscuro", + I18nKey::ThemeDescOneDark => "Oscuro equilibrado inspirado en el átomo", + I18nKey::ThemeNameCatppuccinMocha => "Catuchino Moca", + I18nKey::ThemeDescCatppuccinMocha => "Pastel oscuro con suave contraste.", + I18nKey::ThemeNameCatppuccinLatte => "Catuchin Latte", + I18nKey::ThemeDescCatppuccinLatte => "Pastel claro y acogedor", + I18nKey::ThemeNameTokyoNight => "Noche de Tokio", + I18nKey::ThemeDescTokyoNight => "Noche azul profunda con detalles en neón.", + I18nKey::ThemeNameRosePine => "Pino rosado", + I18nKey::ThemeDescRosePine => "Rosa apagada e iris sobre una base oscura y suave.", + I18nKey::ThemeNameRosePineDawn => "Rosado Pino Amanecer", + I18nKey::ThemeDescRosePineDawn => "Cálida luz de papel con detalles en pino e iris", + I18nKey::ThemeNameEverforestDark => "Bosque eterno oscuro", + I18nKey::ThemeDescEverforestDark => "Verdes del bosque tranquilos sobre una suave base de carbón", + I18nKey::ThemeNameKanagawa => "Kanagawa", + I18nKey::ThemeDescKanagawa => "Lavado de tinta japonesa oscuro con reflejos azul ondulado", + I18nKey::ThemeNameGithubDark => "GitHub oscuro", + I18nKey::ThemeDescGithubDark => "Desarrollador nítido oscuro con acentos azules familiares", + I18nKey::ThemeNameNightOwl => "Ave nocturna", + I18nKey::ThemeDescNightOwl => "Editor Deep Navy optimizado para codificación con poca luz", + I18nKey::ThemeNameAyuMirage => "Espejismo Ayu", + I18nKey::ThemeDescAyuMirage => "Paleta cálida de atardecer con toques de cian y ámbar", + I18nKey::ThemeNameCatppuccinFrappe => "Catuchin Frappé", + I18nKey::ThemeDescCatppuccinFrappe => "Pastel oscuro equilibrado entre Mocha y Latte", I18nKey::ApiKeysHeading => "Claves API", I18nKey::ApiKeysLlmSubhead => "Proveedores LLM", I18nKey::ApiKeysSearchSubhead => "Proveedores de búsqueda", diff --git a/src/i18n/locales/fr_fr.rs b/src/i18n/locales/fr_fr.rs index 28ff5a0..657689f 100644 --- a/src/i18n/locales/fr_fr.rs +++ b/src/i18n/locales/fr_fr.rs @@ -320,6 +320,58 @@ pub fn msg(key: I18nKey) -> &'static str { I18nKey::HsCatImage => "Image", I18nKey::AppHeading => "Application", I18nKey::AppearanceHeading => "Apparence", + I18nKey::AppearanceHeroTitle => "Faites-le vôtre.", + I18nKey::AppearanceHeroSubtitle => "{n} thèmes : les couleurs s'appliquent instantanément sur l'atelier.", + I18nKey::AppearanceSearchPlaceholder => "Thèmes de recherche…", + I18nKey::AppearanceSearchAria => "Thèmes de recherche", + I18nKey::AppearanceFilterAll => "Tous", + I18nKey::AppearanceFilterDark => "Sombre", + I18nKey::AppearanceFilterLight => "Lumière", + I18nKey::AppearanceFilterAria => "Filtrer les thèmes par mode d'apparence", + I18nKey::AppearanceActiveBadge => "ACTIF", + I18nKey::AppearanceActivePreviewLabel => "Aperçu du thème actif", + I18nKey::AppearanceThemeGridAria => "Sélection de thème", + I18nKey::AppearanceThemeSelectAria => "Sélectionnez le thème {nom}", + I18nKey::ThemeNameBlxcodeDark => "Code BLX", + I18nKey::ThemeDescBlxcodeDark => "Établi sombre et profond : l'apparence BLXCode par défaut", + I18nKey::ThemeNameBlxcodeLight => "Lumière de code BLX", + I18nKey::ThemeDescBlxcodeLight => "Nettoyer les surfaces claires avec des accents bleus froids", + I18nKey::ThemeNameDracula => "Dracula", + I18nKey::ThemeDescDracula => "Accents rose-violet sur une base riche et sombre", + I18nKey::ThemeNameGruvboxDark => "Gruvbox Sombre", + I18nKey::ThemeDescGruvboxDark => "Chaud, rétro, sombre, avec des tons terre discrets", + I18nKey::ThemeNameGruvboxLight => "Lumière Gruvbox", + I18nKey::ThemeDescGruvboxLight => "Lumière douce parchemin avec des reflets chaleureux", + I18nKey::ThemeNameSolarizedDark => "Sombre solarisé", + I18nKey::ThemeDescSolarizedDark => "Sombre de précision avec un cyan et un orange équilibrés", + I18nKey::ThemeNameSolarizedLight => "Lumière solarisée", + I18nKey::ThemeDescSolarizedLight => "Lumière à faible contraste conçue pour les longues sessions", + I18nKey::ThemeNameNord => "Nord", + I18nKey::ThemeDescNord => "Calme bleu-gris arctique", + I18nKey::ThemeNameOneDark => "Un sombre", + I18nKey::ThemeDescOneDark => "Foncé équilibré inspiré de l'atome", + I18nKey::ThemeNameCatppuccinMocha => "Catppuccin Moka", + I18nKey::ThemeDescCatppuccinMocha => "Pastel foncé avec un contraste doux", + I18nKey::ThemeNameCatppuccinLatte => "Latte Catppuccin", + I18nKey::ThemeDescCatppuccinLatte => "Pastel clair et douillet", + I18nKey::ThemeNameTokyoNight => "La nuit de Tokyo", + I18nKey::ThemeDescTokyoNight => "Nuit bleu profond avec des accents néon", + I18nKey::ThemeNameRosePine => "Pin Rosé", + I18nKey::ThemeDescRosePine => "Rose et iris discrets sur une base sombre et douce", + I18nKey::ThemeNameRosePineDawn => "Rosé Pin Dawn", + I18nKey::ThemeDescRosePineDawn => "Lampe en papier chaude aux accents de pin et d'iris", + I18nKey::ThemeNameEverforestDark => "Forêt éternelle sombre", + I18nKey::ThemeDescEverforestDark => "Verts forestiers calmes sur une base de charbon de bois doux", + I18nKey::ThemeNameKanagawa => "Kanagawa", + I18nKey::ThemeDescKanagawa => "Lavis d'encre japonaise foncé avec reflets bleu vague", + I18nKey::ThemeNameGithubDark => "GitHub sombre", + I18nKey::ThemeDescGithubDark => "Révélateur net et foncé avec des accents bleus familiers", + I18nKey::ThemeNameNightOwl => "Oiseau de nuit", + I18nKey::ThemeDescNightOwl => "Éditeur Deep Navy optimisé pour le codage en basse lumière", + I18nKey::ThemeNameAyuMirage => "Ayu Mirage", + I18nKey::ThemeDescAyuMirage => "Palette crépusculaire chaleureuse avec des touches de cyan et d'ambre", + I18nKey::ThemeNameCatppuccinFrappe => "Catppuccin Frappé", + I18nKey::ThemeDescCatppuccinFrappe => "Pastel foncé équilibré entre Moka et Latte", I18nKey::ApiKeysHeading => "Clés API", I18nKey::ApiKeysLlmSubhead => "Fournisseurs LLM", I18nKey::ApiKeysSearchSubhead => "Fournisseurs de recherche", diff --git a/src/i18n/locales/hu_hu.rs b/src/i18n/locales/hu_hu.rs index 90b4c8a..4bfe230 100644 --- a/src/i18n/locales/hu_hu.rs +++ b/src/i18n/locales/hu_hu.rs @@ -318,6 +318,58 @@ pub fn msg(key: I18nKey) -> &'static str { I18nKey::HsCatImage => "Kép", I18nKey::AppHeading => "App", I18nKey::AppearanceHeading => "Megjelenés", + I18nKey::AppearanceHeroTitle => "Legyen a tiéd.", + I18nKey::AppearanceHeroSubtitle => "{n} téma – a színek azonnal megjelennek a munkaasztalon.", + I18nKey::AppearanceSearchPlaceholder => "Témák keresése…", + I18nKey::AppearanceSearchAria => "Témák keresése", + I18nKey::AppearanceFilterAll => "Minden", + I18nKey::AppearanceFilterDark => "Sötét", + I18nKey::AppearanceFilterLight => "Fény", + I18nKey::AppearanceFilterAria => "Témák szűrése megjelenési mód szerint", + I18nKey::AppearanceActiveBadge => "AKTÍV", + I18nKey::AppearanceActivePreviewLabel => "Aktív téma előnézete", + I18nKey::AppearanceThemeGridAria => "Témaválasztás", + I18nKey::AppearanceThemeSelectAria => "Válassz témát: {name}", + I18nKey::ThemeNameBlxcodeDark => "BLXCode", + I18nKey::ThemeDescBlxcodeDark => "Mélysötét munkapad – az alapértelmezett BLXCode megjelenés", + I18nKey::ThemeNameBlxcodeLight => "BLXCode Light", + I18nKey::ThemeDescBlxcodeLight => "Tisztítsa meg a világos felületeket hideg kék árnyalatokkal", + I18nKey::ThemeNameDracula => "Drakula", + I18nKey::ThemeDescDracula => "Lilás-rózsaszín akcentusok gazdag sötét alapon", + I18nKey::ThemeNameGruvboxDark => "Gruvbox Dark", + I18nKey::ThemeDescGruvboxDark => "Meleg retró sötét, tompa földszínekkel", + I18nKey::ThemeNameGruvboxLight => "Gruvbox Light", + I18nKey::ThemeDescGruvboxLight => "Puha pergamen lámpa meleg fényekkel", + I18nKey::ThemeNameSolarizedDark => "Solarized Dark", + I18nKey::ThemeDescSolarizedDark => "Precíziós sötét, kiegyensúlyozott cián és narancs", + I18nKey::ThemeNameSolarizedLight => "Szolárizált Fény", + I18nKey::ThemeDescSolarizedLight => "Alacsony kontrasztú fény hosszú munkamenetekhez készült", + I18nKey::ThemeNameNord => "Nord", + I18nKey::ThemeDescNord => "Sarkvidéki kék-szürke nyugalom", + I18nKey::ThemeNameOneDark => "Egy Sötét", + I18nKey::ThemeDescOneDark => "Atom ihlette kiegyensúlyozott sötét", + I18nKey::ThemeNameCatppuccinMocha => "Catppuccin Mocha", + I18nKey::ThemeDescCatppuccinMocha => "Pasztell sötét, lágy kontraszt", + I18nKey::ThemeNameCatppuccinLatte => "Catppuccin Latte", + I18nKey::ThemeDescCatppuccinLatte => "Hangulatos világos pasztell", + I18nKey::ThemeNameTokyoNight => "Tokiói éjszaka", + I18nKey::ThemeDescTokyoNight => "Mélykék éjszaka neon akcentussal", + I18nKey::ThemeNameRosePine => "Rosé Pine", + I18nKey::ThemeDescRosePine => "Tompa rózsa és írisz puha, sötét alapon", + I18nKey::ThemeNameRosePineDawn => "Rosé Pine Dawn", + I18nKey::ThemeDescRosePineDawn => "Meleg papírból készült lámpa fenyő és írisz díszítéssel", + I18nKey::ThemeNameEverforestDark => "Everforest Dark", + I18nKey::ThemeDescEverforestDark => "Nyugodt erdei zöldek puha szén alapon", + I18nKey::ThemeNameKanagawa => "Kanagawa", + I18nKey::ThemeDescKanagawa => "Japán tintamosás sötét, hullámkék kiemelésekkel", + I18nKey::ThemeNameGithubDark => "GitHub Dark", + I18nKey::ThemeDescGithubDark => "Éles előhívó sötét, ismerős kék akcentusokkal", + I18nKey::ThemeNameNightOwl => "Éjszakai bagoly", + I18nKey::ThemeDescNightOwl => "Mély sötétkék szerkesztő, sötétre hangolva gyenge fényű kódoláshoz", + I18nKey::ThemeNameAyuMirage => "Ayu Mirage", + I18nKey::ThemeDescAyuMirage => "Meleg szürkületi paletta cián és borostyán árnyalatokkal", + I18nKey::ThemeNameCatppuccinFrappe => "Catppuccin Frappé", + I18nKey::ThemeDescCatppuccinFrappe => "Kiegyensúlyozott pasztell sötét a Mocha és a Latte között", I18nKey::ApiKeysHeading => "API-kulcsok", I18nKey::ApiKeysLlmSubhead => "LLM-szolgáltatók", I18nKey::ApiKeysSearchSubhead => "Keresőszolgáltatók", diff --git a/src/i18n/locales/it_it.rs b/src/i18n/locales/it_it.rs index 8e7c7de..943ebec 100644 --- a/src/i18n/locales/it_it.rs +++ b/src/i18n/locales/it_it.rs @@ -318,6 +318,58 @@ pub fn msg(key: I18nKey) -> &'static str { I18nKey::HsCatImage => "Immagine", I18nKey::AppHeading => "App", I18nKey::AppearanceHeading => "Aspetto", + I18nKey::AppearanceHeroTitle => "Rendilo tuo.", + I18nKey::AppearanceHeroSubtitle => "{n} temi: i colori si applicano istantaneamente all'ambiente di lavoro.", + I18nKey::AppearanceSearchPlaceholder => "Cerca temi...", + I18nKey::AppearanceSearchAria => "Cerca temi", + I18nKey::AppearanceFilterAll => "Tutto", + I18nKey::AppearanceFilterDark => "Buio", + I18nKey::AppearanceFilterLight => "Leggero", + I18nKey::AppearanceFilterAria => "Filtra i temi in base alla modalità di aspetto", + I18nKey::AppearanceActiveBadge => "ATTIVO", + I18nKey::AppearanceActivePreviewLabel => "Anteprima del tema attivo", + I18nKey::AppearanceThemeGridAria => "Selezione del tema", + I18nKey::AppearanceThemeSelectAria => "Seleziona il tema {nome}", + I18nKey::ThemeNameBlxcodeDark => "Codice BLX", + I18nKey::ThemeDescBlxcodeDark => "Banco da lavoro scuro e profondo: l'aspetto BLXCode predefinito", + I18nKey::ThemeNameBlxcodeLight => "Luce del codice BLX", + I18nKey::ThemeDescBlxcodeLight => "Pulisci le superfici chiare con accenti blu freddi", + I18nKey::ThemeNameDracula => "Dracula", + I18nKey::ThemeDescDracula => "Accenti viola-rosa su una ricca base scura", + I18nKey::ThemeNameGruvboxDark => "Gruvbox scuro", + I18nKey::ThemeDescGruvboxDark => "Caldo retro scuro con tenui tonalità della terra", + I18nKey::ThemeNameGruvboxLight => "Gruvbox Luce", + I18nKey::ThemeDescGruvboxLight => "Luce morbida pergamena con riflessi caldi", + I18nKey::ThemeNameSolarizedDark => "Scuro Solarizzato", + I18nKey::ThemeDescSolarizedDark => "Scuro di precisione con ciano e arancione bilanciati", + I18nKey::ThemeNameSolarizedLight => "Luce Solarizzata", + I18nKey::ThemeDescSolarizedLight => "Luce a basso contrasto pensata per lunghe sessioni", + I18nKey::ThemeNameNord => "Nord", + I18nKey::ThemeDescNord => "Calma artica blu-grigia", + I18nKey::ThemeNameOneDark => "Uno scuro", + I18nKey::ThemeDescOneDark => "Dark equilibrato di ispirazione Atom", + I18nKey::ThemeNameCatppuccinMocha => "Moka Catppuccin", + I18nKey::ThemeDescCatppuccinMocha => "Pastello scuro con contrasto morbido", + I18nKey::ThemeNameCatppuccinLatte => "Catppuccin Latte", + I18nKey::ThemeDescCatppuccinLatte => "Pastello chiaro accogliente", + I18nKey::ThemeNameTokyoNight => "La notte di Tokio", + I18nKey::ThemeDescTokyoNight => "Blu notte profondo con accenti neon", + I18nKey::ThemeNameRosePine => "Pino rosato", + I18nKey::ThemeDescRosePine => "Rosa tenue e iris su una morbida base scura", + I18nKey::ThemeNameRosePineDawn => "Alba di pino rosato", + I18nKey::ThemeDescRosePineDawn => "Calda luce di carta con accenti di pino e iris", + I18nKey::ThemeNameEverforestDark => "Everforest scuro", + I18nKey::ThemeDescEverforestDark => "Calmi verdi di bosco su una morbida base di carbone", + I18nKey::ThemeNameKanagawa => "Kanagawa", + I18nKey::ThemeDescKanagawa => "Inchiostro giapponese scuro con riflessi blu ondulati", + I18nKey::ThemeNameGithubDark => "GitHub scuro", + I18nKey::ThemeDescGithubDark => "Sviluppatore nitido scuro con accenti blu familiari", + I18nKey::ThemeNameNightOwl => "Nottambulo", + I18nKey::ThemeDescNightOwl => "Editor Deep Navy ottimizzato per la codifica in condizioni di scarsa illuminazione", + I18nKey::ThemeNameAyuMirage => "Ayu Miraggio", + I18nKey::ThemeDescAyuMirage => "Tavolozza calda e crepuscolare con tocchi di ciano e ambra", + I18nKey::ThemeNameCatppuccinFrappe => "Catppuccin Frappé", + I18nKey::ThemeDescCatppuccinFrappe => "Scuro pastello equilibrato tra Moka e Latte", I18nKey::ApiKeysHeading => "Chiavi API", I18nKey::ApiKeysLlmSubhead => "Provider LLM", I18nKey::ApiKeysSearchSubhead => "Provider di ricerca", diff --git a/src/i18n/locales/ja_jp.rs b/src/i18n/locales/ja_jp.rs index 28823d5..33077b9 100644 --- a/src/i18n/locales/ja_jp.rs +++ b/src/i18n/locales/ja_jp.rs @@ -312,6 +312,58 @@ pub fn msg(key: I18nKey) -> &'static str { I18nKey::HsCatImage => "画像", I18nKey::AppHeading => "アプリ", I18nKey::AppearanceHeading => "外観", + I18nKey::AppearanceHeroTitle => "それをあなたのものにしてください。", + I18nKey::AppearanceHeroSubtitle => "{n} 個のテーマ — 色はワークベンチ全体に即座に適用されます。", + I18nKey::AppearanceSearchPlaceholder => "テーマを検索…", + I18nKey::AppearanceSearchAria => "テーマを検索する", + I18nKey::AppearanceFilterAll => "全て", + I18nKey::AppearanceFilterDark => "暗い", + I18nKey::AppearanceFilterLight => "ライト", + I18nKey::AppearanceFilterAria => "外観モードでテーマをフィルタリングする", + I18nKey::AppearanceActiveBadge => "アクティブ", + I18nKey::AppearanceActivePreviewLabel => "アクティブなテーマのプレビュー", + I18nKey::AppearanceThemeGridAria => "テーマの選択", + I18nKey::AppearanceThemeSelectAria => "テーマ {名前} を選択してください", + I18nKey::ThemeNameBlxcodeDark => "BLXコード", + I18nKey::ThemeDescBlxcodeDark => "真っ暗なワークベンチ — デフォルトの BLXCode の外観", + I18nKey::ThemeNameBlxcodeLight => "BLXコードライト", + I18nKey::ThemeDescBlxcodeLight => "クールなブルーのアクセントを備えたクリーンな明るい表面", + I18nKey::ThemeNameDracula => "ドラキュラ", + I18nKey::ThemeDescDracula => "豊かなダークベースにパープルピンクのアクセント", + I18nKey::ThemeNameGruvboxDark => "グラブボックス ダーク", + I18nKey::ThemeDescGruvboxDark => "落ち着いたアースカラーの温かみのあるレトロダーク", + I18nKey::ThemeNameGruvboxLight => "グラブボックス ライト", + I18nKey::ThemeDescGruvboxLight => "温かみのあるハイライトを備えた柔らかな羊皮紙の光", + I18nKey::ThemeNameSolarizedDark => "ソラライズドダーク", + I18nKey::ThemeDescSolarizedDark => "シアンとオレンジのバランスが取れた正確なダーク", + I18nKey::ThemeNameSolarizedLight => "ソラリゼーションライト", + I18nKey::ThemeDescSolarizedLight => "長時間のセッション向けに構築された低コントラストのライト", + I18nKey::ThemeNameNord => "ノルド", + I18nKey::ThemeDescNord => "北極のブルーグレーの穏やかな", + I18nKey::ThemeNameOneDark => "ワンダーク", + I18nKey::ThemeDescOneDark => "アトムにインスピレーションを得たバランスのとれたダーク", + I18nKey::ThemeNameCatppuccinMocha => "キャットプッチンモカ", + I18nKey::ThemeDescCatppuccinMocha => "柔らかいコントラストのパステルダーク", + I18nKey::ThemeNameCatppuccinLatte => "キャットプッチンラテ", + I18nKey::ThemeDescCatppuccinLatte => "心地よい淡いパステルカラー", + I18nKey::ThemeNameTokyoNight => "東京の夜", + I18nKey::ThemeDescTokyoNight => "ネオンがアクセントのディープブルーの夜", + I18nKey::ThemeNameRosePine => "ローズパイン", + I18nKey::ThemeDescRosePine => "柔らかなダークベースに落ち着いたローズとアイリス", + I18nKey::ThemeNameRosePineDawn => "ローズパインの夜明け", + I18nKey::ThemeDescRosePineDawn => "松と菖蒲のアクセントが付いた温かみのあるペーパーライト", + I18nKey::ThemeNameEverforestDark => "エバーフォレスト・ダーク", + I18nKey::ThemeDescEverforestDark => "柔らかな炭ベースに落ち着いたフォレストグリーン", + I18nKey::ThemeNameKanagawa => "神奈川県", + I18nKey::ThemeDescKanagawa => "ウェーブブルーのハイライトを備えたダークな日本の水墨画", + I18nKey::ThemeNameGithubDark => "GitHub ダーク", + I18nKey::ThemeDescGithubDark => "おなじみのブルーのアクセントが付いた鮮明なデベロッパーダーク", + I18nKey::ThemeNameNightOwl => "ナイトフクロウ", + I18nKey::ThemeDescNightOwl => "暗い場所でのコーディング向けに調整されたディープネイビーのエディターダーク", + I18nKey::ThemeNameAyuMirage => "アユミラージュ", + I18nKey::ThemeDescAyuMirage => "シアンとアンバーのポップな温かみのある夕暮れのパレット", + I18nKey::ThemeNameCatppuccinFrappe => "カプッチンフラッペ", + I18nKey::ThemeDescCatppuccinFrappe => "モカとラテの間のバランスのとれたパステルダーク", I18nKey::ApiKeysHeading => "APIキー", I18nKey::ApiKeysLlmSubhead => "LLMプロバイダー", I18nKey::ApiKeysSearchSubhead => "検索プロバイダー", diff --git a/src/i18n/locales/ko_kr.rs b/src/i18n/locales/ko_kr.rs index 24a03a1..c447e15 100644 --- a/src/i18n/locales/ko_kr.rs +++ b/src/i18n/locales/ko_kr.rs @@ -312,6 +312,58 @@ pub fn msg(key: I18nKey) -> &'static str { I18nKey::HsCatImage => "영상", I18nKey::AppHeading => "앱", I18nKey::AppearanceHeading => "모양새", + I18nKey::AppearanceHeroTitle => "그것을 당신의 것으로 만드십시오.", + I18nKey::AppearanceHeroSubtitle => "{n} 테마 — 색상이 작업대 전체에 즉시 적용됩니다.", + I18nKey::AppearanceSearchPlaceholder => "테마 검색…", + I18nKey::AppearanceSearchAria => "테마 검색", + I18nKey::AppearanceFilterAll => "모두", + I18nKey::AppearanceFilterDark => "어두운", + I18nKey::AppearanceFilterLight => "빛", + I18nKey::AppearanceFilterAria => "모양 모드별로 테마 필터링", + I18nKey::AppearanceActiveBadge => "활동적인", + I18nKey::AppearanceActivePreviewLabel => "활성 테마 미리보기", + I18nKey::AppearanceThemeGridAria => "테마 선택", + I18nKey::AppearanceThemeSelectAria => "테마 선택 {이름}", + I18nKey::ThemeNameBlxcodeDark => "BLX코드", + I18nKey::ThemeDescBlxcodeDark => "깊고 어두운 작업대 - 기본 BLXCode 모양", + I18nKey::ThemeNameBlxcodeLight => "BLX코드 라이트", + I18nKey::ThemeDescBlxcodeLight => "시원한 파란색 액센트로 깨끗한 조명 표면", + I18nKey::ThemeNameDracula => "드라큘라", + I18nKey::ThemeDescDracula => "진한 다크 베이스에 퍼플 핑크 액센트", + I18nKey::ThemeNameGruvboxDark => "그루브박스 다크", + I18nKey::ThemeDescGruvboxDark => "차분한 흙빛 톤의 따뜻한 레트로 다크", + I18nKey::ThemeNameGruvboxLight => "그루브박스 라이트", + I18nKey::ThemeDescGruvboxLight => "따뜻한 하이라이트를 더한 부드러운 양피지 조명", + I18nKey::ThemeNameSolarizedDark => "솔라라이즈 다크", + I18nKey::ThemeDescSolarizedDark => "청록색과 오렌지색이 균형을 이룬 정밀한 어둠", + I18nKey::ThemeNameSolarizedLight => "태양광", + I18nKey::ThemeDescSolarizedLight => "장시간 세션을 위해 제작된 저대비 조명", + I18nKey::ThemeNameNord => "노르드", + I18nKey::ThemeDescNord => "차분한 북극 청회색", + I18nKey::ThemeNameOneDark => "원 다크", + I18nKey::ThemeDescOneDark => "원자에서 영감을 받은 균형 잡힌 다크", + I18nKey::ThemeNameCatppuccinMocha => "캣푸친 모카", + I18nKey::ThemeDescCatppuccinMocha => "부드러운 대비가 있는 파스텔톤의 어두운 색상", + I18nKey::ThemeNameCatppuccinLatte => "캣푸친 라떼", + I18nKey::ThemeDescCatppuccinLatte => "포근한 파스텔톤", + I18nKey::ThemeNameTokyoNight => "도쿄 나이트", + I18nKey::ThemeDescTokyoNight => "네온 액센트가 있는 깊고 푸른 밤", + I18nKey::ThemeNameRosePine => "로제 파인", + I18nKey::ThemeDescRosePine => "부드러운 다크 베이스에 은은한 장미와 아이리스", + I18nKey::ThemeNameRosePineDawn => "로제 파인 던", + I18nKey::ThemeDescRosePineDawn => "소나무와 붓꽃으로 포인트를 준 따뜻한 종이 조명", + I18nKey::ThemeNameEverforestDark => "에버포레스트 다크", + I18nKey::ThemeDescEverforestDark => "부드러운 숯 베이스에 차분한 숲속 나물", + I18nKey::ThemeNameKanagawa => "가나가와", + I18nKey::ThemeDescKanagawa => "물결 모양의 파란색 하이라이트가 있는 어두운 일본 수묵화", + I18nKey::ThemeNameGithubDark => "GitHub 다크", + I18nKey::ThemeDescGithubDark => "친숙한 파란색 액센트가 있는 선명한 현상액", + I18nKey::ThemeNameNightOwl => "올빼미", + I18nKey::ThemeDescNightOwl => "저조도 코딩을 위해 어둡게 조정된 Deep Navy 편집기", + I18nKey::ThemeNameAyuMirage => "은어 미라지", + I18nKey::ThemeDescAyuMirage => "청록색과 호박색 팝이 포함된 따뜻한 황혼 팔레트", + I18nKey::ThemeNameCatppuccinFrappe => "캣푸친 프라페", + I18nKey::ThemeDescCatppuccinFrappe => "모카와 라떼 사이의 균형잡힌 파스텔 다크", I18nKey::ApiKeysHeading => "API 키", I18nKey::ApiKeysLlmSubhead => "LLM 제공업체", I18nKey::ApiKeysSearchSubhead => "검색 제공업체", diff --git a/src/i18n/locales/pl_pl.rs b/src/i18n/locales/pl_pl.rs index a9373c4..486ec7d 100644 --- a/src/i18n/locales/pl_pl.rs +++ b/src/i18n/locales/pl_pl.rs @@ -316,6 +316,58 @@ pub fn msg(key: I18nKey) -> &'static str { I18nKey::HsCatImage => "Obraz", I18nKey::AppHeading => "Aplikacja", I18nKey::AppearanceHeading => "Wygląd", + I18nKey::AppearanceHeroTitle => "Zrób to swoje.", + I18nKey::AppearanceHeroSubtitle => "Motywy {n} — kolory są natychmiast stosowane w całym środowisku pracy.", + I18nKey::AppearanceSearchPlaceholder => "Wyszukaj motywy…", + I18nKey::AppearanceSearchAria => "Wyszukaj motywy", + I18nKey::AppearanceFilterAll => "Wszystko", + I18nKey::AppearanceFilterDark => "Ciemny", + I18nKey::AppearanceFilterLight => "Światło", + I18nKey::AppearanceFilterAria => "Filtruj motywy według trybu wyglądu", + I18nKey::AppearanceActiveBadge => "AKTYWNY", + I18nKey::AppearanceActivePreviewLabel => "Podgląd aktywnego motywu", + I18nKey::AppearanceThemeGridAria => "Wybór tematu", + I18nKey::AppearanceThemeSelectAria => "Wybierz motyw {nazwa}", + I18nKey::ThemeNameBlxcodeDark => "Kod BLX", + I18nKey::ThemeDescBlxcodeDark => "Głęboko ciemny stół warsztatowy — domyślny wygląd BLXCode", + I18nKey::ThemeNameBlxcodeLight => "Światło BLXCode", + I18nKey::ThemeDescBlxcodeLight => "Czyste jasne powierzchnie z chłodnymi niebieskimi akcentami", + I18nKey::ThemeNameDracula => "Dracula", + I18nKey::ThemeDescDracula => "Fioletowo-różowe akcenty na bogatej ciemnej bazie", + I18nKey::ThemeNameGruvboxDark => "Gruvbox Ciemny", + I18nKey::ThemeDescGruvboxDark => "Ciepły, ciemny retro ze stonowanymi odcieniami ziemi", + I18nKey::ThemeNameGruvboxLight => "Światło Gruvboxa", + I18nKey::ThemeDescGruvboxLight => "Miękkie pergaminowe światło z ciepłymi refleksami", + I18nKey::ThemeNameSolarizedDark => "Słoneczna ciemność", + I18nKey::ThemeDescSolarizedDark => "Precyzyjna czerń ze zrównoważonym cyjanem i pomarańczą", + I18nKey::ThemeNameSolarizedLight => "Solarne światło", + I18nKey::ThemeDescSolarizedLight => "Światło o niskim kontraście stworzone z myślą o długich sesjach", + I18nKey::ThemeNameNord => "Nord", + I18nKey::ThemeDescNord => "Arktyczny niebiesko-szary spokój", + I18nKey::ThemeNameOneDark => "Jeden Ciemny", + I18nKey::ThemeDescOneDark => "Zrównoważona ciemność inspirowana atomem", + I18nKey::ThemeNameCatppuccinMocha => "Mokka Catppuccina", + I18nKey::ThemeDescCatppuccinMocha => "Pastelowa ciemność z delikatnym kontrastem", + I18nKey::ThemeNameCatppuccinLatte => "Catpuccino Latte", + I18nKey::ThemeDescCatppuccinLatte => "Przytulny, jasny pastel", + I18nKey::ThemeNameTokyoNight => "Noc w Tokio", + I18nKey::ThemeDescTokyoNight => "Głęboki błękit nocy z neonowymi akcentami", + I18nKey::ThemeNameRosePine => "Sosna Różowa", + I18nKey::ThemeDescRosePine => "Stonowana róża i irys na miękkiej ciemnej bazie", + I18nKey::ThemeNameRosePineDawn => "Świt sosny różanej", + I18nKey::ThemeDescRosePineDawn => "Ciepłe, papierowe światło z akcentami sosny i irysa", + I18nKey::ThemeNameEverforestDark => "Everforest Ciemność", + I18nKey::ThemeDescEverforestDark => "Spokojna, leśna zieleń na miękkiej bazie węgla drzewnego", + I18nKey::ThemeNameKanagawa => "Kanagawa", + I18nKey::ThemeDescKanagawa => "Japoński ciemny atrament z refleksami w kolorze falistego błękitu", + I18nKey::ThemeNameGithubDark => "Ciemny GitHub", + I18nKey::ThemeDescGithubDark => "Wyraźny ciemny wywoływacz ze znanymi niebieskimi akcentami", + I18nKey::ThemeNameNightOwl => "Nocna Sowa", + I18nKey::ThemeDescNightOwl => "Ciemny edytor w kolorze głębokiego granatu, dostosowany do kodowania przy słabym oświetleniu", + I18nKey::ThemeNameAyuMirage => "Ayu Miraż", + I18nKey::ThemeDescAyuMirage => "Ciepła paleta zmierzchu z cyjanowymi i bursztynowymi akcentami", + I18nKey::ThemeNameCatppuccinFrappe => "Catppuccin Frappé", + I18nKey::ThemeDescCatppuccinFrappe => "Zrównoważony pastelowy odcień pomiędzy Mokką i Latte", I18nKey::ApiKeysHeading => "Klucze API", I18nKey::ApiKeysLlmSubhead => "Dostawcy LLM", I18nKey::ApiKeysSearchSubhead => "Dostawcy wyszukiwania", diff --git a/src/i18n/locales/pt_br.rs b/src/i18n/locales/pt_br.rs index 5f27952..285baf2 100644 --- a/src/i18n/locales/pt_br.rs +++ b/src/i18n/locales/pt_br.rs @@ -318,6 +318,58 @@ pub fn msg(key: I18nKey) -> &'static str { I18nKey::HsCatImage => "Imagem", I18nKey::AppHeading => "Aplicativo", I18nKey::AppearanceHeading => "Aparência", + I18nKey::AppearanceHeroTitle => "Faça com que seja seu.", + I18nKey::AppearanceHeroSubtitle => "{n} temas — as cores são aplicadas instantaneamente em todo o ambiente de trabalho.", + I18nKey::AppearanceSearchPlaceholder => "Pesquisar temas…", + I18nKey::AppearanceSearchAria => "Temas de pesquisa", + I18nKey::AppearanceFilterAll => "Todos", + I18nKey::AppearanceFilterDark => "Escuro", + I18nKey::AppearanceFilterLight => "Luz", + I18nKey::AppearanceFilterAria => "Filtrar temas por modo de aparência", + I18nKey::AppearanceActiveBadge => "ATIVO", + I18nKey::AppearanceActivePreviewLabel => "Visualização do tema ativo", + I18nKey::AppearanceThemeGridAria => "Seleção de tema", + I18nKey::AppearanceThemeSelectAria => "Selecione o tema {nome}", + I18nKey::ThemeNameBlxcodeDark => "Código BLX", + I18nKey::ThemeDescBlxcodeDark => "Bancada de trabalho escura e profunda – a aparência padrão do BLXCode", + I18nKey::ThemeNameBlxcodeLight => "Luz BLXCode", + I18nKey::ThemeDescBlxcodeLight => "Limpe superfícies claras com detalhes em azul legal", + I18nKey::ThemeNameDracula => "Drácula", + I18nKey::ThemeDescDracula => "Acentos roxo-rosa em uma rica base escura", + I18nKey::ThemeNameGruvboxDark => "Gruvbox Escuro", + I18nKey::ThemeDescGruvboxDark => "Escuro retrô quente com tons de terra suaves", + I18nKey::ThemeNameGruvboxLight => "Luz Gruvbox", + I18nKey::ThemeDescGruvboxLight => "Luz suave em pergaminho com destaques quentes", + I18nKey::ThemeNameSolarizedDark => "Escuro Solarizado", + I18nKey::ThemeDescSolarizedDark => "Escuro de precisão com ciano e laranja equilibrados", + I18nKey::ThemeNameSolarizedLight => "Luz Solarizada", + I18nKey::ThemeDescSolarizedLight => "Luz de baixo contraste desenvolvida para sessões longas", + I18nKey::ThemeNameNord => "Norte", + I18nKey::ThemeDescNord => "Calma azul acinzentada do Ártico", + I18nKey::ThemeNameOneDark => "Um escuro", + I18nKey::ThemeDescOneDark => "Escuro equilibrado inspirado no Atom", + I18nKey::ThemeNameCatppuccinMocha => "Catppuccin Mocha", + I18nKey::ThemeDescCatppuccinMocha => "Pastel escuro com contraste suave", + I18nKey::ThemeNameCatppuccinLatte => "Catppuccin Latte", + I18nKey::ThemeDescCatppuccinLatte => "Pastel claro aconchegante", + I18nKey::ThemeNameTokyoNight => "Noite de Tóquio", + I18nKey::ThemeDescTokyoNight => "Noite azul profunda com detalhes em neon", + I18nKey::ThemeNameRosePine => "Pinho Rosé", + I18nKey::ThemeDescRosePine => "Rosa e íris suaves em uma base escura e suave", + I18nKey::ThemeNameRosePineDawn => "Rosé Pinho Dawn", + I18nKey::ThemeDescRosePineDawn => "Luz de papel quente com detalhes em pinho e íris", + I18nKey::ThemeNameEverforestDark => "Floresta Eterna Escura", + I18nKey::ThemeDescEverforestDark => "Verdes florestais calmos sobre uma base de carvão macio", + I18nKey::ThemeNameKanagawa => "Kanagawa", + I18nKey::ThemeDescKanagawa => "Tinta japonesa escura com reflexos em azul ondulado", + I18nKey::ThemeNameGithubDark => "GitHub escuro", + I18nKey::ThemeDescGithubDark => "Revelador nítido escuro com detalhes azuis familiares", + I18nKey::ThemeNameNightOwl => "Coruja Noturna", + I18nKey::ThemeDescNightOwl => "Editor da Marinha Profunda ajustado para codificação com pouca luz", + I18nKey::ThemeNameAyuMirage => "Ayu Miragem", + I18nKey::ThemeDescAyuMirage => "Paleta de crepúsculo quente com toques de ciano e âmbar", + I18nKey::ThemeNameCatppuccinFrappe => "Frappé de Catppuccin", + I18nKey::ThemeDescCatppuccinFrappe => "Escuro pastel equilibrado entre Mocha e Latte", I18nKey::ApiKeysHeading => "Chaves de API", I18nKey::ApiKeysLlmSubhead => "Provedores LLM", I18nKey::ApiKeysSearchSubhead => "Provedores de busca", diff --git a/src/i18n/locales/ru_ru.rs b/src/i18n/locales/ru_ru.rs index 062dd23..d9c19ec 100644 --- a/src/i18n/locales/ru_ru.rs +++ b/src/i18n/locales/ru_ru.rs @@ -316,6 +316,58 @@ pub fn msg(key: I18nKey) -> &'static str { I18nKey::HsCatImage => "Изображение", I18nKey::AppHeading => "Приложение", I18nKey::AppearanceHeading => "Внешний вид", + I18nKey::AppearanceHeroTitle => "Сделайте это своим.", + I18nKey::AppearanceHeroSubtitle => "{n} тем — цвета мгновенно применяются по всему рабочему столу.", + I18nKey::AppearanceSearchPlaceholder => "Поиск тем…", + I18nKey::AppearanceSearchAria => "Поиск тем", + I18nKey::AppearanceFilterAll => "Все", + I18nKey::AppearanceFilterDark => "Темный", + I18nKey::AppearanceFilterLight => "Свет", + I18nKey::AppearanceFilterAria => "Фильтрация тем по режиму внешнего вида", + I18nKey::AppearanceActiveBadge => "АКТИВНЫЙ", + I18nKey::AppearanceActivePreviewLabel => "Предварительный просмотр активной темы", + I18nKey::AppearanceThemeGridAria => "Выбор темы", + I18nKey::AppearanceThemeSelectAria => "Выберите тему {имя}", + I18nKey::ThemeNameBlxcodeDark => "BLXКод", + I18nKey::ThemeDescBlxcodeDark => "Глубокий темный рабочий стол — внешний вид BLXCode по умолчанию.", + I18nKey::ThemeNameBlxcodeLight => "BLXКод Свет", + I18nKey::ThemeDescBlxcodeLight => "Чистые светлые поверхности с холодными синими акцентами", + I18nKey::ThemeNameDracula => "Дракула", + I18nKey::ThemeDescDracula => "Пурпурно-розовые акценты на насыщенной темной базе.", + I18nKey::ThemeNameGruvboxDark => "Грувбокс Темный", + I18nKey::ThemeDescGruvboxDark => "Теплый темный ретро с приглушенными земляными тонами", + I18nKey::ThemeNameGruvboxLight => "Грувбокс Лайт", + I18nKey::ThemeDescGruvboxLight => "Мягкий пергаментный свет с теплыми бликами", + I18nKey::ThemeNameSolarizedDark => "Соляризованная тьма", + I18nKey::ThemeDescSolarizedDark => "Precision Dark со сбалансированным голубым и оранжевым", + I18nKey::ThemeNameSolarizedLight => "Соляризованный свет", + I18nKey::ThemeDescSolarizedLight => "Низкоконтрастный свет, созданный для длительных сеансов", + I18nKey::ThemeNameNord => "Норд", + I18nKey::ThemeDescNord => "Арктическое сине-серое спокойствие", + I18nKey::ThemeNameOneDark => "Одна тьма", + I18nKey::ThemeDescOneDark => "Сбалансированная тьма в стиле атома", + I18nKey::ThemeNameCatppuccinMocha => "Катпучин Мокко", + I18nKey::ThemeDescCatppuccinMocha => "Пастельный темный с мягким контрастом", + I18nKey::ThemeNameCatppuccinLatte => "Катпучин Латте", + I18nKey::ThemeDescCatppuccinLatte => "Уютная светлая пастель", + I18nKey::ThemeNameTokyoNight => "Токио Ночь", + I18nKey::ThemeDescTokyoNight => "Глубокая синяя ночь с неоновыми акцентами", + I18nKey::ThemeNameRosePine => "Розе Пайн", + I18nKey::ThemeDescRosePine => "Приглушенная роза и ирис на мягкой темной базе", + I18nKey::ThemeNameRosePineDawn => "Розе Пайн Даун", + I18nKey::ThemeDescRosePineDawn => "Теплый бумажный свет с акцентами сосны и ириса", + I18nKey::ThemeNameEverforestDark => "Эверфорест Темный", + I18nKey::ThemeDescEverforestDark => "Спокойная лесная зелень на мягкой угольной базе.", + I18nKey::ThemeNameKanagawa => "Канагава", + I18nKey::ThemeDescKanagawa => "Японская тушь темного оттенка с волнистыми голубыми бликами", + I18nKey::ThemeNameGithubDark => "GitHub Темный", + I18nKey::ThemeDescGithubDark => "Яркий темный проявитель со знакомыми синими акцентами", + I18nKey::ThemeNameNightOwl => "Ночная сова", + I18nKey::ThemeDescNightOwl => "Редактор темно-синего цвета, настроенный для кодирования при слабом освещении", + I18nKey::ThemeNameAyuMirage => "Аю Мираж", + I18nKey::ThemeDescAyuMirage => "Теплая сумеречная палитра с голубыми и янтарными оттенками", + I18nKey::ThemeNameCatppuccinFrappe => "Катпучин Фраппе", + I18nKey::ThemeDescCatppuccinFrappe => "Сбалансированный пастельный темный оттенок между мокко и латте.", I18nKey::ApiKeysHeading => "API-ключи", I18nKey::ApiKeysLlmSubhead => "Поставщики LLM", I18nKey::ApiKeysSearchSubhead => "Поисковые поставщики", diff --git a/src/i18n/locales/zh_cn.rs b/src/i18n/locales/zh_cn.rs index 1a9baf9..bfda633 100644 --- a/src/i18n/locales/zh_cn.rs +++ b/src/i18n/locales/zh_cn.rs @@ -310,6 +310,58 @@ pub fn msg(key: I18nKey) -> &'static str { I18nKey::HsCatImage => "图像", I18nKey::AppHeading => "应用程序", I18nKey::AppearanceHeading => "外观", + I18nKey::AppearanceHeroTitle => "让它成为你的。", + I18nKey::AppearanceHeroSubtitle => "{n} 个主题 — 颜色立即应用于整个工作台。", + I18nKey::AppearanceSearchPlaceholder => "搜索主题...", + I18nKey::AppearanceSearchAria => "搜索主题", + I18nKey::AppearanceFilterAll => "全部", + I18nKey::AppearanceFilterDark => "黑暗的", + I18nKey::AppearanceFilterLight => "光", + I18nKey::AppearanceFilterAria => "按外观模式过滤主题", + I18nKey::AppearanceActiveBadge => "积极的", + I18nKey::AppearanceActivePreviewLabel => "活动主题预览", + I18nKey::AppearanceThemeGridAria => "主题选择", + I18nKey::AppearanceThemeSelectAria => "选择主题{名称}", + I18nKey::ThemeNameBlxcodeDark => "BLX代码", + I18nKey::ThemeDescBlxcodeDark => "深暗工作台 — 默认的 BLXCode 外观", + I18nKey::ThemeNameBlxcodeLight => "BLX码光", + I18nKey::ThemeDescBlxcodeLight => "干净的浅色表面搭配冷蓝色调", + I18nKey::ThemeNameDracula => "德古拉", + I18nKey::ThemeDescDracula => "浓郁的深色底色上点缀着紫粉色", + I18nKey::ThemeNameGruvboxDark => "Gruvbox 深色", + I18nKey::ThemeDescGruvboxDark => "温暖复古的深色搭配柔和的大地色调", + I18nKey::ThemeNameGruvboxLight => "Gruvbox 灯", + I18nKey::ThemeDescGruvboxLight => "柔和的羊皮纸光,带有温暖的亮点", + I18nKey::ThemeNameSolarizedDark => "晒黑", + I18nKey::ThemeDescSolarizedDark => "精确的深色与平衡的青色和橙色", + I18nKey::ThemeNameSolarizedLight => "日光照射", + I18nKey::ThemeDescSolarizedLight => "低对比度灯光专为长时间会议而设计", + I18nKey::ThemeNameNord => "诺德", + I18nKey::ThemeDescNord => "北极蓝灰色 平静", + I18nKey::ThemeNameOneDark => "一暗", + I18nKey::ThemeDescOneDark => "受原子启发的平衡暗色", + I18nKey::ThemeNameCatppuccinMocha => "卡布奇诺摩卡", + I18nKey::ThemeDescCatppuccinMocha => "柔和的深色与柔和的对比", + I18nKey::ThemeNameCatppuccinLatte => "卡布奇拿铁", + I18nKey::ThemeDescCatppuccinLatte => "舒适的浅色粉彩", + I18nKey::ThemeNameTokyoNight => "东京之夜", + I18nKey::ThemeDescTokyoNight => "深蓝色的夜晚,霓虹灯点缀", + I18nKey::ThemeNameRosePine => "玫瑰松", + I18nKey::ThemeDescRosePine => "柔和的深色底调中蕴藏着柔和的玫瑰和鸢尾花香", + I18nKey::ThemeNameRosePineDawn => "松黎明桃红葡萄酒", + I18nKey::ThemeDescRosePineDawn => "带有松木和鸢尾花装饰的温暖纸灯", + I18nKey::ThemeNameEverforestDark => "无尽森林黑暗", + I18nKey::ThemeDescEverforestDark => "柔软的木炭底座上呈现出平静的森林绿意", + I18nKey::ThemeNameKanagawa => "神奈川", + I18nKey::ThemeDescKanagawa => "日本水墨深色,带有波浪蓝色亮点", + I18nKey::ThemeNameGithubDark => "GitHub 黑暗", + I18nKey::ThemeDescGithubDark => "清爽的显影剂深色,带有熟悉的蓝色调", + I18nKey::ThemeNameNightOwl => "猫头鹰", + I18nKey::ThemeDescNightOwl => "深海军蓝编辑器针对低光编码进行了暗调", + I18nKey::ThemeNameAyuMirage => "鲇蜃楼", + I18nKey::ThemeDescAyuMirage => "带有青色和琥珀色流行色的温暖黄昏调色板", + I18nKey::ThemeNameCatppuccinFrappe => "卡布奇星冰沙", + I18nKey::ThemeDescCatppuccinFrappe => "摩卡咖啡和拿铁咖啡之间平衡的柔和深色", I18nKey::ApiKeysHeading => "API 密钥", I18nKey::ApiKeysLlmSubhead => "LLM 提供商", I18nKey::ApiKeysSearchSubhead => "搜索提供商", diff --git a/src/i18n/locales/zh_tw.rs b/src/i18n/locales/zh_tw.rs index 2b33d32..8e9221f 100644 --- a/src/i18n/locales/zh_tw.rs +++ b/src/i18n/locales/zh_tw.rs @@ -310,6 +310,58 @@ pub fn msg(key: I18nKey) -> &'static str { I18nKey::HsCatImage => "影像", I18nKey::AppHeading => "應用程式", I18nKey::AppearanceHeading => "外觀", + I18nKey::AppearanceHeroTitle => "讓它成為你的。", + I18nKey::AppearanceHeroSubtitle => "{n} 個主題 — 顏色立即套用於整個工作台。", + I18nKey::AppearanceSearchPlaceholder => "搜尋主題...", + I18nKey::AppearanceSearchAria => "搜尋主題", + I18nKey::AppearanceFilterAll => "全部", + I18nKey::AppearanceFilterDark => "黑暗的", + I18nKey::AppearanceFilterLight => "光", + I18nKey::AppearanceFilterAria => "依外觀模式濾波主題", + I18nKey::AppearanceActiveBadge => "積極的", + I18nKey::AppearanceActivePreviewLabel => "活動主題預覽", + I18nKey::AppearanceThemeGridAria => "主題選擇", + I18nKey::AppearanceThemeSelectAria => "選擇主題{名稱}", + I18nKey::ThemeNameBlxcodeDark => "BLX程式碼", + I18nKey::ThemeDescBlxcodeDark => "深暗工作台 — 預設的 BLXCode 外觀", + I18nKey::ThemeNameBlxcodeLight => "BLX碼光", + I18nKey::ThemeDescBlxcodeLight => "乾淨的淺色表面搭配冷藍色調", + I18nKey::ThemeNameDracula => "德古拉", + I18nKey::ThemeDescDracula => "濃鬱的深色底色上點綴著紫粉色", + I18nKey::ThemeNameGruvboxDark => "Gruvbox 深色", + I18nKey::ThemeDescGruvboxDark => "溫暖復古的深色搭配柔和的大地色調", + I18nKey::ThemeNameGruvboxLight => "Gruvbox 燈", + I18nKey::ThemeDescGruvboxLight => "柔和的羊皮紙光,帶有溫暖的亮點", + I18nKey::ThemeNameSolarizedDark => "曬黑", + I18nKey::ThemeDescSolarizedDark => "精確的深色與平衡的青色和橙色", + I18nKey::ThemeNameSolarizedLight => "日光照射", + I18nKey::ThemeDescSolarizedLight => "低對比燈光專為長時間會議而設計", + I18nKey::ThemeNameNord => "諾德", + I18nKey::ThemeDescNord => "北極藍灰色 平靜", + I18nKey::ThemeNameOneDark => "一暗", + I18nKey::ThemeDescOneDark => "受原子啟發的平衡暗色", + I18nKey::ThemeNameCatppuccinMocha => "卡布奇諾摩卡", + I18nKey::ThemeDescCatppuccinMocha => "柔和的深色與柔和的對比", + I18nKey::ThemeNameCatppuccinLatte => "卡布奇拿鐵", + I18nKey::ThemeDescCatppuccinLatte => "舒適的淺色粉彩", + I18nKey::ThemeNameTokyoNight => "東京之夜", + I18nKey::ThemeDescTokyoNight => "深藍色的夜晚,霓虹燈點綴", + I18nKey::ThemeNameRosePine => "玫瑰松", + I18nKey::ThemeDescRosePine => "柔和的深色基調中蘊藏著柔和的玫瑰和鳶尾花香", + I18nKey::ThemeNameRosePineDawn => "松黎明桃紅葡萄酒", + I18nKey::ThemeDescRosePineDawn => "帶有鬆木和鳶尾花裝飾的溫暖紙燈", + I18nKey::ThemeNameEverforestDark => "無盡森林黑暗", + I18nKey::ThemeDescEverforestDark => "柔軟的木炭底座上呈現出平靜的森林綠意", + I18nKey::ThemeNameKanagawa => "神奈川", + I18nKey::ThemeDescKanagawa => "日本水墨深色,帶有波浪藍色亮點", + I18nKey::ThemeNameGithubDark => "GitHub 黑暗", + I18nKey::ThemeDescGithubDark => "清爽的顯影劑深色,帶有熟悉的藍色調", + I18nKey::ThemeNameNightOwl => "貓頭鷹", + I18nKey::ThemeDescNightOwl => "深海軍藍編輯器針對低光編碼進行了暗調", + I18nKey::ThemeNameAyuMirage => "鮎樓", + I18nKey::ThemeDescAyuMirage => "帶有青色和琥珀色流行色的溫暖黃昏調色板", + I18nKey::ThemeNameCatppuccinFrappe => "卡布奇星冰沙", + I18nKey::ThemeDescCatppuccinFrappe => "摩卡咖啡和拿鐵咖啡之間平衡的柔和深色", I18nKey::ApiKeysHeading => "API 金鑰", I18nKey::ApiKeysLlmSubhead => "LLM 供應商", I18nKey::ApiKeysSearchSubhead => "搜尋供應商", diff --git a/src/main.rs b/src/main.rs index 2ebdf0f..918957e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ mod memory_paths; mod open_http; mod quit; mod service; +mod theme; mod skills_rules_wire; mod tauri_bridge; mod workbench; diff --git a/src/theme/catalog.rs b/src/theme/catalog.rs new file mode 100644 index 0000000..c0502a0 --- /dev/null +++ b/src/theme/catalog.rs @@ -0,0 +1,249 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ThemeMode { + Dark, + Light, +} + +/// Colors for the mini preview mockup on theme cards. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ThemePreviewColors { + pub sidebar: &'static str, + pub background: &'static str, + pub accent: &'static str, + pub text: &'static str, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AppTheme { + pub id: &'static str, + pub mode: ThemeMode, + pub preview: ThemePreviewColors, +} + +pub const DEFAULT_THEME_ID: &str = "blxcode-dark"; + +pub const THEMES: &[AppTheme] = &[ + AppTheme { + id: "blxcode-dark", + mode: ThemeMode::Dark, + preview: ThemePreviewColors { + sidebar: "#101116", + background: "#15171d", + accent: "#58a6ff", + text: "#f1f2f5", + }, + }, + AppTheme { + id: "blxcode-light", + mode: ThemeMode::Light, + preview: ThemePreviewColors { + sidebar: "#eef0f4", + background: "#ffffff", + accent: "#0969da", + text: "#1a1d24", + }, + }, + AppTheme { + id: "dracula", + mode: ThemeMode::Dark, + preview: ThemePreviewColors { + sidebar: "#21222c", + background: "#282a36", + accent: "#bd93f9", + text: "#f8f8f2", + }, + }, + AppTheme { + id: "gruvbox-dark", + mode: ThemeMode::Dark, + preview: ThemePreviewColors { + sidebar: "#1d2021", + background: "#282828", + accent: "#fe8019", + text: "#ebdbb2", + }, + }, + AppTheme { + id: "gruvbox-light", + mode: ThemeMode::Light, + preview: ThemePreviewColors { + sidebar: "#ebdbb2", + background: "#fbf1c7", + accent: "#af3a03", + text: "#3c3836", + }, + }, + AppTheme { + id: "solarized-dark", + mode: ThemeMode::Dark, + preview: ThemePreviewColors { + sidebar: "#00212b", + background: "#002b36", + accent: "#268bd2", + text: "#839496", + }, + }, + AppTheme { + id: "solarized-light", + mode: ThemeMode::Light, + preview: ThemePreviewColors { + sidebar: "#eee8d5", + background: "#fdf6e3", + accent: "#268bd2", + text: "#657b83", + }, + }, + AppTheme { + id: "nord", + mode: ThemeMode::Dark, + preview: ThemePreviewColors { + sidebar: "#2e3440", + background: "#3b4252", + accent: "#88c0d0", + text: "#eceff4", + }, + }, + AppTheme { + id: "one-dark", + mode: ThemeMode::Dark, + preview: ThemePreviewColors { + sidebar: "#21252b", + background: "#282c34", + accent: "#61afef", + text: "#abb2bf", + }, + }, + AppTheme { + id: "catppuccin-mocha", + mode: ThemeMode::Dark, + preview: ThemePreviewColors { + sidebar: "#181825", + background: "#1e1e2e", + accent: "#cba6f7", + text: "#cdd6f4", + }, + }, + AppTheme { + id: "catppuccin-latte", + mode: ThemeMode::Light, + preview: ThemePreviewColors { + sidebar: "#e6e9ef", + background: "#eff1f5", + accent: "#8839ef", + text: "#4c4f69", + }, + }, + AppTheme { + id: "tokyo-night", + mode: ThemeMode::Dark, + preview: ThemePreviewColors { + sidebar: "#16161e", + background: "#1a1b26", + accent: "#7aa2f7", + text: "#c0caf5", + }, + }, + AppTheme { + id: "rose-pine", + mode: ThemeMode::Dark, + preview: ThemePreviewColors { + sidebar: "#191724", + background: "#1f1d2e", + accent: "#c4a7e7", + text: "#e0def4", + }, + }, + AppTheme { + id: "rose-pine-dawn", + mode: ThemeMode::Light, + preview: ThemePreviewColors { + sidebar: "#f2e9e1", + background: "#faf4ed", + accent: "#286983", + text: "#575279", + }, + }, + AppTheme { + id: "everforest-dark", + mode: ThemeMode::Dark, + preview: ThemePreviewColors { + sidebar: "#2d353b", + background: "#343f44", + accent: "#a7c080", + text: "#d3c6aa", + }, + }, + AppTheme { + id: "kanagawa", + mode: ThemeMode::Dark, + preview: ThemePreviewColors { + sidebar: "#16161d", + background: "#1f1f28", + accent: "#7e9cd8", + text: "#dcd7ba", + }, + }, + AppTheme { + id: "github-dark", + mode: ThemeMode::Dark, + preview: ThemePreviewColors { + sidebar: "#0d1117", + background: "#161b22", + accent: "#58a6ff", + text: "#e6edf3", + }, + }, + AppTheme { + id: "night-owl", + mode: ThemeMode::Dark, + preview: ThemePreviewColors { + sidebar: "#011627", + background: "#011627", + accent: "#82aaff", + text: "#d6deeb", + }, + }, + AppTheme { + id: "ayu-mirage", + mode: ThemeMode::Dark, + preview: ThemePreviewColors { + sidebar: "#1a1f29", + background: "#1f2430", + accent: "#5ccfe6", + text: "#cbccc6", + }, + }, + AppTheme { + id: "catppuccin-frappe", + mode: ThemeMode::Dark, + preview: ThemePreviewColors { + sidebar: "#232634", + background: "#303446", + accent: "#ca9ee6", + text: "#c6d0f5", + }, + }, +]; + +#[must_use] +pub fn theme_by_id(id: &str) -> Option<&'static AppTheme> { + THEMES.iter().find(|t| t.id == id) +} + +#[must_use] +pub fn is_valid_theme_id(id: &str) -> bool { + theme_by_id(id).is_some() +} + +#[must_use] +#[allow(dead_code)] +pub fn themes_for_mode(mode: Option) -> Vec { + match mode { + None => THEMES.iter().copied().collect(), + Some(m) => THEMES + .iter() + .copied() + .filter(|t| t.mode == m) + .collect(), + } +} diff --git a/src/theme/i18n.rs b/src/theme/i18n.rs new file mode 100644 index 0000000..7f60ece --- /dev/null +++ b/src/theme/i18n.rs @@ -0,0 +1,55 @@ +use crate::i18n::I18nKey; + +#[must_use] +pub fn theme_name_key(theme_id: &str) -> Option { + Some(match theme_id { + "blxcode-dark" => I18nKey::ThemeNameBlxcodeDark, + "blxcode-light" => I18nKey::ThemeNameBlxcodeLight, + "dracula" => I18nKey::ThemeNameDracula, + "gruvbox-dark" => I18nKey::ThemeNameGruvboxDark, + "gruvbox-light" => I18nKey::ThemeNameGruvboxLight, + "solarized-dark" => I18nKey::ThemeNameSolarizedDark, + "solarized-light" => I18nKey::ThemeNameSolarizedLight, + "nord" => I18nKey::ThemeNameNord, + "one-dark" => I18nKey::ThemeNameOneDark, + "catppuccin-mocha" => I18nKey::ThemeNameCatppuccinMocha, + "catppuccin-latte" => I18nKey::ThemeNameCatppuccinLatte, + "tokyo-night" => I18nKey::ThemeNameTokyoNight, + "rose-pine" => I18nKey::ThemeNameRosePine, + "rose-pine-dawn" => I18nKey::ThemeNameRosePineDawn, + "everforest-dark" => I18nKey::ThemeNameEverforestDark, + "kanagawa" => I18nKey::ThemeNameKanagawa, + "github-dark" => I18nKey::ThemeNameGithubDark, + "night-owl" => I18nKey::ThemeNameNightOwl, + "ayu-mirage" => I18nKey::ThemeNameAyuMirage, + "catppuccin-frappe" => I18nKey::ThemeNameCatppuccinFrappe, + _ => return None, + }) +} + +#[must_use] +pub fn theme_desc_key(theme_id: &str) -> Option { + Some(match theme_id { + "blxcode-dark" => I18nKey::ThemeDescBlxcodeDark, + "blxcode-light" => I18nKey::ThemeDescBlxcodeLight, + "dracula" => I18nKey::ThemeDescDracula, + "gruvbox-dark" => I18nKey::ThemeDescGruvboxDark, + "gruvbox-light" => I18nKey::ThemeDescGruvboxLight, + "solarized-dark" => I18nKey::ThemeDescSolarizedDark, + "solarized-light" => I18nKey::ThemeDescSolarizedLight, + "nord" => I18nKey::ThemeDescNord, + "one-dark" => I18nKey::ThemeDescOneDark, + "catppuccin-mocha" => I18nKey::ThemeDescCatppuccinMocha, + "catppuccin-latte" => I18nKey::ThemeDescCatppuccinLatte, + "tokyo-night" => I18nKey::ThemeDescTokyoNight, + "rose-pine" => I18nKey::ThemeDescRosePine, + "rose-pine-dawn" => I18nKey::ThemeDescRosePineDawn, + "everforest-dark" => I18nKey::ThemeDescEverforestDark, + "kanagawa" => I18nKey::ThemeDescKanagawa, + "github-dark" => I18nKey::ThemeDescGithubDark, + "night-owl" => I18nKey::ThemeDescNightOwl, + "ayu-mirage" => I18nKey::ThemeDescAyuMirage, + "catppuccin-frappe" => I18nKey::ThemeDescCatppuccinFrappe, + _ => return None, + }) +} diff --git a/src/theme/mod.rs b/src/theme/mod.rs new file mode 100644 index 0000000..106ebec --- /dev/null +++ b/src/theme/mod.rs @@ -0,0 +1,7 @@ +mod catalog; +mod i18n; + +pub use catalog::{ + is_valid_theme_id, theme_by_id, AppTheme, ThemeMode, DEFAULT_THEME_ID, THEMES, +}; +pub use i18n::{theme_desc_key, theme_name_key}; diff --git a/src/workbench/agent_panel/ask_user_card/ask_user_card.css b/src/workbench/agent_panel/ask_user_card/ask_user_card.css index 91510a7..54dc3f9 100644 --- a/src/workbench/agent_panel/ask_user_card/ask_user_card.css +++ b/src/workbench/agent_panel/ask_user_card/ask_user_card.css @@ -4,13 +4,13 @@ gap: 0.55rem; padding: 0.7rem 0.85rem 0.8rem; border-radius: 10px; - border: 1px solid var(--agent-accent, rgba(120, 140, 255, 0.45)); + border: 1px solid var(--accent-soft); background: linear-gradient( 180deg, - rgba(96, 116, 220, 0.08) 0%, - rgba(96, 116, 220, 0.02) 100% + var(--accent-soft) 0%, + transparent 100% ); - box-shadow: 0 0 0 1px rgba(120, 140, 255, 0.08) inset; + box-shadow: 0 0 0 1px var(--overlay-3) inset; } .ask-user-card[data-open="false"] { @@ -30,14 +30,14 @@ letter-spacing: 0.04em; padding: 0.12rem 0.42rem; border-radius: 999px; - background: rgba(120, 140, 255, 0.18); - color: var(--agent-accent-text, #c9d4ff); + background: var(--accent-soft); + color: var(--accent-cool); } .ask-user-card__title { font-size: 0.72rem; font-weight: 600; - color: var(--ui-fg-muted, #8a93b5); + color: var(--text-muted); letter-spacing: 0.02em; } @@ -51,27 +51,27 @@ border-radius: 6px; border: 1px solid transparent; background: transparent; - color: var(--ui-fg-muted, #8a93b5); + color: var(--text-muted); cursor: pointer; } .ask-user-card__cancel:hover { - background: rgba(255, 100, 100, 0.14); - color: #ff8a8a; - border-color: rgba(255, 100, 100, 0.3); + background: var(--danger-soft); + color: var(--danger); + border-color: var(--danger); } .ask-user-card__question { margin: 0; font-size: 0.92rem; line-height: 1.35; - color: var(--ui-fg, #e8ecff); + color: var(--text); } .ask-user-card__hint { margin: 0; font-size: 0.7rem; - color: var(--ui-fg-muted, #8a93b5); + color: var(--text-muted); } .ask-user-card__options { @@ -94,17 +94,17 @@ width: 100%; padding: 0.55rem 0.7rem; border-radius: 8px; - border: 1px solid rgba(120, 140, 255, 0.22); - background: rgba(20, 24, 44, 0.55); - color: var(--ui-fg, #e8ecff); + border: 1px solid var(--border-strong); + background: var(--bg-raised); + color: var(--text); text-align: left; cursor: pointer; transition: background 0.12s ease, border-color 0.12s ease; } .ask-user-card__option-btn:hover:not(:disabled) { - background: rgba(120, 140, 255, 0.15); - border-color: var(--agent-accent, rgba(120, 140, 255, 0.55)); + background: var(--accent-soft); + border-color: var(--accent); } .ask-user-card__option-btn:disabled { @@ -113,8 +113,8 @@ } .ask-user-card__option-btn.is-selected { - background: rgba(120, 140, 255, 0.22); - border-color: var(--agent-accent, rgba(120, 140, 255, 0.7)); + background: var(--accent-soft); + border-color: var(--accent-hover); } .ask-user-card__option-num { @@ -125,10 +125,10 @@ min-width: 1.3rem; height: 1.3rem; border-radius: 5px; - background: rgba(120, 140, 255, 0.18); + background: var(--accent-soft); font-size: 0.72rem; font-weight: 600; - color: var(--agent-accent-text, #c9d4ff); + color: var(--accent-cool); } .ask-user-card__option-body { @@ -148,7 +148,7 @@ .ask-user-card__option-desc { font-size: 0.72rem; line-height: 1.3; - color: var(--ui-fg-muted, #8a93b5); + color: var(--text-muted); } .ask-user-card__check { @@ -159,7 +159,7 @@ display: inline-flex; align-items: center; justify-content: center; - color: var(--agent-accent-text, #c9d4ff); + color: var(--accent-cool); font-size: 0.85rem; line-height: 1; } @@ -168,16 +168,16 @@ width: 100%; padding: 0.45rem 0.6rem; border-radius: 6px; - border: 1px solid rgba(120, 140, 255, 0.22); - background: rgba(20, 24, 44, 0.7); - color: var(--ui-fg, #e8ecff); + border: 1px solid var(--border-strong); + background: var(--bg-panel); + color: var(--text); font-size: 0.82rem; font-family: inherit; } .ask-user-card__other:focus { outline: none; - border-color: var(--agent-accent, rgba(120, 140, 255, 0.7)); + border-color: var(--accent); } .ask-user-card__other:disabled { @@ -193,32 +193,32 @@ .ask-user-card__send { padding: 0.4rem 0.9rem; border-radius: 6px; - border: 1px solid var(--agent-accent, rgba(120, 140, 255, 0.55)); - background: rgba(120, 140, 255, 0.28); - color: var(--ui-fg, #e8ecff); + border: 1px solid var(--accent); + background: var(--accent-soft); + color: var(--text); font-size: 0.8rem; font-weight: 600; cursor: pointer; } .ask-user-card__send:hover { - background: rgba(120, 140, 255, 0.4); + background: var(--accent-cool-soft); } .ask-user-card__status { margin: 0; font-size: 0.72rem; - color: var(--ui-fg-muted, #8a93b5); + color: var(--text-muted); } .ask-user-card__status--answered { - color: var(--agent-accent-text, #c9d4ff); + color: var(--accent-cool); } .ask-user-card__status--cancelled { - color: #ff9a9a; + color: var(--danger); } .ask-user-card__status-summary { - color: var(--ui-fg, #e8ecff); + color: var(--text); } diff --git a/src/workbench/agent_panel/turn_metrics_bar/turn_metrics_bar.css b/src/workbench/agent_panel/turn_metrics_bar/turn_metrics_bar.css index f12ef65..46224ba 100644 --- a/src/workbench/agent_panel/turn_metrics_bar/turn_metrics_bar.css +++ b/src/workbench/agent_panel/turn_metrics_bar/turn_metrics_bar.css @@ -12,7 +12,7 @@ font-family: ui-monospace, "SF Mono", "Menlo", "Consolas", monospace; font-size: 0.7rem; line-height: 1.2; - color: var(--text-muted, #8a8f98); + color: var(--text-muted); } .turn-metrics-bar__cell { @@ -22,14 +22,14 @@ } .turn-metrics-bar__cell strong { - color: var(--text, #c9cdd4); + color: var(--text); font-weight: 500; } .turn-metrics-bar__cell--cost strong { /* Subtle accent so the operator can scan for cost when monitoring a long chat. Tied to the existing accent variable when present. */ - color: var(--accent, #7dd3fc); + color: var(--accent); } .turn-metrics-bar__sep { @@ -58,6 +58,6 @@ font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.06em; - color: var(--text-muted, #8a8f98); + color: var(--text-muted); margin-right: 0.4rem; } diff --git a/src/workbench/agent_provider_pane/agent_provider_pane.css b/src/workbench/agent_provider_pane/agent_provider_pane.css index 0f4635c..f2c7107 100644 --- a/src/workbench/agent_provider_pane/agent_provider_pane.css +++ b/src/workbench/agent_provider_pane/agent_provider_pane.css @@ -154,8 +154,8 @@ margin: 0; padding: 0.5rem 0.6rem; border-radius: 6px; - border: 1px solid rgba(255, 255, 255, 0.06); - background: rgba(255, 255, 255, 0.03); + border: 1px solid var(--border); + background: var(--overlay-1); font-size: 0.74rem; line-height: 1.45; } @@ -178,13 +178,13 @@ gap: 0.35rem 0.65rem; font-size: 0.75rem; line-height: 1.35; - color: var(--text-muted, rgba(170, 175, 190, 0.72)); + color: var(--text-muted); } .agent-provider-pane__key-status { font-family: var(--font-mono); font-size: 0.72rem; - color: var(--text-muted, rgba(170, 175, 190, 0.65)); + color: var(--text-muted); } .agent-provider-pane__actions { diff --git a/src/workbench/api_keys_pane/api_keys_pane.css b/src/workbench/api_keys_pane/api_keys_pane.css index 71bca41..61ae0f9 100644 --- a/src/workbench/api_keys_pane/api_keys_pane.css +++ b/src/workbench/api_keys_pane/api_keys_pane.css @@ -16,8 +16,8 @@ gap: 0.35rem; padding: 0.55rem 0.65rem; border-radius: 6px; - background: rgba(255, 255, 255, 0.03); - border: 1px solid rgba(255, 255, 255, 0.06); + background: var(--overlay-1); + border: 1px solid var(--border); } .api-keys-row--coming-soon { @@ -25,8 +25,8 @@ } .api-keys-row--marked-delete { - border-color: rgba(248, 113, 113, 0.45); - background: rgba(248, 113, 113, 0.06); + border-color: color-mix(in srgb, var(--danger) 45%, transparent); + background: var(--danger-soft); } .api-keys-row__head { @@ -51,13 +51,13 @@ display: grid; place-items: center; border-radius: 999px; - background: rgba(255, 255, 255, 0.92); + background: var(--text-bright); overflow: hidden; } .api-keys-row__brand--fallback { - background: rgba(255, 255, 255, 0.08); - color: var(--text-muted, rgba(255, 255, 255, 0.65)); + background: var(--overlay-2); + color: var(--text-muted); } .api-keys-row__brand-img { @@ -72,7 +72,7 @@ } .api-keys-row__masked { - font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace); + font-family: var(--font-mono); font-size: 0.78rem; opacity: 0.82; } @@ -81,17 +81,17 @@ font-size: 0.72rem; padding: 0.1rem 0.45rem; border-radius: 999px; - background: rgba(255, 255, 255, 0.08); + background: var(--overlay-2); text-transform: uppercase; letter-spacing: 0.04em; } .api-keys-row__badge--warn { - background: rgba(248, 113, 113, 0.18); + background: var(--danger-soft); } .api-keys-row__badge--muted { - background: rgba(255, 255, 255, 0.04); + background: var(--overlay-1); opacity: 0.7; } diff --git a/src/workbench/appearance_settings_pane/appearance_settings_pane.css b/src/workbench/appearance_settings_pane/appearance_settings_pane.css new file mode 100644 index 0000000..e9c95b8 --- /dev/null +++ b/src/workbench/appearance_settings_pane/appearance_settings_pane.css @@ -0,0 +1,245 @@ +.appearance-pane { + display: flex; + flex-direction: column; + gap: 1.25rem; + min-height: 0; +} + +.appearance-hero { + display: flex; + align-items: stretch; + justify-content: space-between; + gap: 1.25rem; + padding-bottom: 0.25rem; +} + +.appearance-hero__copy { + display: flex; + flex-direction: column; + justify-content: center; + gap: 0.45rem; + min-width: 0; +} + +.appearance-hero__title { + margin: 0; + font-size: 1.35rem; + font-weight: 650; + letter-spacing: -0.02em; + color: var(--text); +} + +.appearance-hero__subtitle { + margin: 0; + max-width: 36rem; + color: var(--text-muted); + font-size: 0.92rem; + line-height: 1.5; +} + +.appearance-hero__preview { + position: relative; + flex: 0 0 min(220px, 38%); + min-height: 96px; + border-radius: 12px; + overflow: hidden; + border: 1px solid var(--border-strong); + background: + linear-gradient(135deg, var(--preview-sidebar, var(--bg-raised)) 0 28%, transparent 28%), + linear-gradient(180deg, var(--preview-bg, var(--bg-panel)) 0%, var(--preview-accent, var(--accent)) 180%); + filter: saturate(1.05); +} + +.appearance-hero__preview::after { + content: ""; + position: absolute; + inset: 0; + backdrop-filter: blur(10px); + background: color-mix(in srgb, var(--preview-bg, var(--bg-panel)) 35%, transparent); +} + +.appearance-hero__preview-badge { + position: absolute; + top: 0.55rem; + right: 0.55rem; + z-index: 1; + padding: 0.15rem 0.45rem; + border-radius: 999px; + font-size: 0.62rem; + font-weight: 700; + letter-spacing: 0.08em; + color: var(--text-bright); + background: color-mix(in srgb, var(--accent) 72%, transparent); + border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent); +} + +.appearance-toolbar { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 0.75rem; +} + +.appearance-search { + display: flex; + align-items: center; + gap: 0.55rem; + flex: 1 1 220px; + min-width: 0; + padding: 0.35rem 0.65rem; + border-radius: 10px; + border: 1px solid var(--border); + background: var(--bg-raised); +} + +.appearance-search__icon { + display: inline-flex; + color: var(--text-faint); +} + +.appearance-search__input { + flex: 1; + min-width: 0; + border: 0; + background: transparent; + padding: 0.2rem 0; +} + +.appearance-filter-group { + display: inline-flex; + flex-wrap: wrap; + gap: 0.35rem; + padding: 0.2rem; + border-radius: 10px; + border: 1px solid var(--border); + background: var(--bg-raised); +} + +.appearance-filter-btn { + appearance: none; + border: 0; + border-radius: 8px; + padding: 0.35rem 0.7rem; + font: inherit; + font-size: 0.82rem; + color: var(--text-muted); + background: transparent; + cursor: pointer; +} + +.appearance-filter-btn:hover { + color: var(--text); + background: var(--overlay-2); +} + +.appearance-filter-btn--active { + color: var(--text); + background: var(--accent-soft); + box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent); +} + +.appearance-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); + gap: 1.1rem; +} + +.theme-card { + display: flex; + flex-direction: column; + gap: 0.85rem; + width: 100%; + padding: 0.9rem 1rem 1.05rem; + border-radius: 12px; + border: 1px solid var(--border); + background: var(--bg-raised); + text-align: left; + cursor: pointer; + transition: border-color 120ms ease, box-shadow 120ms ease; +} + +.theme-card:hover { + border-color: var(--border-strong); + box-shadow: 0 8px 24px color-mix(in srgb, var(--bg-app) 55%, transparent); +} + +.theme-card--active { + border-color: color-mix(in srgb, var(--accent) 55%, transparent); + box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 45%, transparent); +} + +.theme-card__preview { + position: relative; + height: 72px; + border-radius: 8px; + overflow: hidden; + border: 1px solid var(--border); + background: + linear-gradient(90deg, var(--preview-sidebar, var(--bg-panel-header)) 0 22%, transparent 22%), + linear-gradient(180deg, var(--preview-bg, var(--bg-panel)) 0%, color-mix(in srgb, var(--preview-accent, var(--accent)) 22%, var(--preview-bg, var(--bg-panel))) 100%); +} + +.theme-card__badge { + position: absolute; + top: 0.35rem; + right: 0.35rem; + z-index: 1; + padding: 0.1rem 0.35rem; + border-radius: 999px; + font-size: 0.58rem; + font-weight: 700; + letter-spacing: 0.07em; + color: var(--text-bright); + background: color-mix(in srgb, var(--accent) 75%, transparent); +} + +.theme-card__check { + position: absolute; + right: 0.4rem; + bottom: 0.35rem; + display: none; + align-items: center; + justify-content: center; + width: 1.2rem; + height: 1.2rem; + border-radius: 999px; + color: var(--text-bright); + background: color-mix(in srgb, var(--accent) 78%, transparent); + border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent); +} + +.theme-card--active .theme-card__check { + display: inline-flex; +} + +.theme-card__meta { + display: flex; + flex-direction: column; + gap: 0.4rem; + padding: 0.05rem 0.15rem 0; +} + +.theme-card__name { + font-size: 0.92rem; + font-weight: 650; + line-height: 1.35; + color: var(--text); +} + +.theme-card__desc { + font-size: 0.78rem; + line-height: 1.55; + color: var(--text-muted); +} + +@media (max-width: 640px) { + .appearance-hero { + flex-direction: column; + } + + .appearance-hero__preview { + flex-basis: auto; + width: 100%; + } +} diff --git a/src/workbench/appearance_settings_pane/mod.rs b/src/workbench/appearance_settings_pane/mod.rs new file mode 100644 index 0000000..519a9a4 --- /dev/null +++ b/src/workbench/appearance_settings_pane/mod.rs @@ -0,0 +1,192 @@ +mod theme_preview_card; + +use leptos::prelude::*; +use leptos_icons::Icon as LxIcon; + +use crate::i18n::I18nKey; +use crate::service::I18nService; +use crate::theme::{theme_desc_key, theme_name_key, ThemeMode, THEMES}; +use crate::workbench::theme_service::{theme_count, ThemeService}; + +use theme_preview_card::ThemePreviewCard; + +#[derive(Clone, Copy, PartialEq, Eq)] +enum ModeFilter { + All, + Dark, + Light, +} + +#[component] +pub fn AppearanceSettingsPane() -> impl IntoView { + let i18n = expect_context::(); + let theme_svc = expect_context::(); + let search_query = RwSignal::new(String::new()); + let mode_filter = RwSignal::new(ModeFilter::All); + + let filtered_themes = Memo::new(move |_| { + let query = search_query.get().trim().to_lowercase(); + let mode = mode_filter.get(); + THEMES + .iter() + .copied() + .filter(|t| match mode { + ModeFilter::All => true, + ModeFilter::Dark => t.mode == ThemeMode::Dark, + ModeFilter::Light => t.mode == ThemeMode::Light, + }) + .filter(|t| { + if query.is_empty() { + return true; + } + let name = theme_name_key(t.id) + .map(|k| i18n.tr(k)().to_lowercase()) + .unwrap_or_default(); + let desc = theme_desc_key(t.id) + .map(|k| i18n.tr(k)().to_lowercase()) + .unwrap_or_default(); + name.contains(&query) || desc.contains(&query) + }) + .collect::>() + }); + + let dark_count = Memo::new(|_| THEMES.iter().filter(|t| t.mode == ThemeMode::Dark).count()); + let light_count = Memo::new(|_| THEMES.iter().filter(|t| t.mode == ThemeMode::Light).count()); + let total_count = theme_count(); + + let active_theme = theme_svc.active_theme(); + + view! { +
+
+
+

+ {move || i18n.tr(I18nKey::AppearanceHeroTitle)()} +

+

+ {move || { + i18n.tr(I18nKey::AppearanceHeroSubtitle)() + .replace("{n}", &total_count.to_string()) + }} +

+
+
+ + {move || i18n.tr(I18nKey::AppearanceActiveBadge)()} + +
+
+ +
+ +
+ + + +
+
+ +
+ + } + } + /> +
+
+ } +} + +fn event_target_value(ev: &leptos::ev::Event) -> Option { + use wasm_bindgen::JsCast; + ev.target() + .and_then(|t| t.dyn_into::().ok()) + .map(|el| el.value()) +} diff --git a/src/workbench/appearance_settings_pane/theme_preview_card.rs b/src/workbench/appearance_settings_pane/theme_preview_card.rs new file mode 100644 index 0000000..d20bbde --- /dev/null +++ b/src/workbench/appearance_settings_pane/theme_preview_card.rs @@ -0,0 +1,73 @@ +use leptos::prelude::*; +use leptos_icons::Icon as LxIcon; + +use crate::i18n::I18nKey; +use crate::service::I18nService; +use crate::theme::{theme_desc_key, theme_name_key, AppTheme}; + +#[component] +pub fn ThemePreviewCard( + theme: AppTheme, + #[prop(into)] active: Signal, + on_select: impl Fn(String) + 'static, +) -> impl IntoView { + let i18n = expect_context::(); + let theme_id = theme.id.to_string(); + let theme_id_for_click = theme_id.clone(); + let theme_id_for_style = theme.id; + + view! { + + } +} diff --git a/src/workbench/harness_image_pane/harness_image_pane.css b/src/workbench/harness_image_pane/harness_image_pane.css index 9712a3f..6c03a53 100644 --- a/src/workbench/harness_image_pane/harness_image_pane.css +++ b/src/workbench/harness_image_pane/harness_image_pane.css @@ -3,6 +3,6 @@ .image-pane__loading, .image-pane__status { font-size: 0.8rem; - color: var(--text-muted, #9aa); + color: var(--text-muted); margin: 0.4rem 0 0; } diff --git a/src/workbench/harness_ui.rs b/src/workbench/harness_ui.rs index cadf804..1e21944 100644 --- a/src/workbench/harness_ui.rs +++ b/src/workbench/harness_ui.rs @@ -692,7 +692,7 @@ pub fn SettingsDock( }.into_any(), HarnessSettingsCategory::Appearance => view! { - + }.into_any(), HarnessSettingsCategory::ApiKeys => view! { @@ -1108,21 +1108,6 @@ fn AppSettingsPane() -> impl IntoView { } } -#[component] -fn AppearanceSettingsPane() -> impl IntoView { - let i18n = expect_context::(); - view! { -
-

- - {move || i18n.tr(I18nKey::AppearanceHeading)()} -

-
- } -} - #[component] fn ApiKeysSettingsPane() -> impl IntoView { view! { } diff --git a/src/workbench/memory_graph/mod.rs b/src/workbench/memory_graph/mod.rs index 30b007a..2d9dfe6 100644 --- a/src/workbench/memory_graph/mod.rs +++ b/src/workbench/memory_graph/mod.rs @@ -14,6 +14,7 @@ use crate::workbench::memory_panel::{ expand_files_group_for_path, load_note, refresh_graph, MemoryState, MemoryView, }; use crate::workbench::WorkbenchService; +use crate::workbench::ThemeService; use gloo_timers::future::TimeoutFuture; use leptos::html; use leptos::leptos_dom::helpers::window_event_listener_untyped; @@ -446,6 +447,7 @@ fn Graph2dView( let user_interacted = RwSignal::new(false); let last_node_set: RwSignal> = RwSignal::new(Vec::new()); let hovered: RwSignal> = RwSignal::new(None); + let theme_svc = expect_context::(); let last_zoom_tick = RwSignal::new(zoom_tick.get_untracked()); let last_reset_tick = RwSignal::new(reset_tick.get_untracked()); @@ -612,13 +614,18 @@ fn Graph2dView( { let s = state.clone(); + let theme_svc = theme_svc; move || { + let _theme = theme_svc.active_theme_id().get(); + let edge_default = read_css_var("--overlay-3"); + let edge_active = read_css_var("--accent-cool"); + let edge_dim = read_css_var("--overlay-1"); let graph = s.graph.get(); let graph = configured_graph(wb, graph); let edges = graph.as_ref().map(|g| g.edges.clone()).unwrap_or_default(); let pos = layout.get(); let hov = hovered.get(); - edges.into_iter().filter_map(|e| { + edges.into_iter().filter_map(move |e| { let (sx, sy) = *pos.get(&e.source)?; let (tx, ty) = *pos.get(&e.target)?; let incident = match hov.as_deref() { @@ -626,11 +633,11 @@ fn Graph2dView( None => true, }; let (stroke, width) = if hov.is_none() { - ("rgba(255,255,255,0.18)", "1") + (edge_default.clone(), "1".to_string()) } else if incident { - ("rgba(180,210,255,0.85)", "1.6") + (edge_active.clone(), "1.6".to_string()) } else { - ("rgba(255,255,255,0.04)", "1") + (edge_dim.clone(), "1".to_string()) }; Some(view! { { let s = state.clone(); + let theme_svc = theme_svc; move || { + let _theme = theme_svc.active_theme_id().get(); + let stroke_bright = read_css_var("--text-bright"); + let stroke_muted = read_css_var("--text-muted"); + let stroke_faint = read_css_var("--text-faint"); + let label_color = read_css_var("--text"); let pos = layout.get(); let graph = s.graph.get(); let graph = configured_graph(wb, graph); @@ -682,13 +695,13 @@ fn Graph2dView( _ => base_fill, }; let stroke = if is_selected { - "rgba(255,255,255,1)" + stroke_bright.clone() } else { match focus_state { - NodeFocus::Hovered => "rgba(255,255,255,0.95)", - NodeFocus::Neighbor => "rgba(255,255,255,0.6)", - NodeFocus::Dim => "rgba(255,255,255,0.08)", - NodeFocus::Normal => "rgba(255,255,255,0.4)", + NodeFocus::Hovered => stroke_bright.clone(), + NodeFocus::Neighbor => stroke_muted.clone(), + NodeFocus::Dim => fade_color(&stroke_faint, 0.35), + NodeFocus::Normal => fade_color(&stroke_muted, 0.65), } }; let stroke_width = if is_selected || matches!(focus_state, NodeFocus::Hovered) { "1.6" } else { "0.5" }; @@ -724,7 +737,7 @@ fn Graph2dView( x=(x + radius + 3.0).to_string() y=(y + 3.0).to_string() font-size="9" - fill="rgba(238,239,245,0.95)" + fill=label_color.clone() opacity=label_opacity.to_string() >{label} })} @@ -987,9 +1000,32 @@ pub(crate) fn graph_category_for_path(path: &str) -> String { "memory".to_string() } +fn read_css_var(name: &str) -> String { + let Some(window) = web_sys::window() else { + return String::new(); + }; + let Some(document) = window.document() else { + return String::new(); + }; + let Some(root) = document.document_element() else { + return String::new(); + }; + let Ok(Some(style)) = window.get_computed_style(&root) else { + return String::new(); + }; + let Ok(value) = style.get_property_value(name) else { + return String::new(); + }; + value.trim().to_string() +} + fn cluster_color(tags: &[String], orphan: bool) -> String { if orphan { - return "rgba(170,170,185,0.55)".to_string(); + let faint = read_css_var("--text-faint"); + if faint.is_empty() { + return "rgba(170,170,185,0.55)".to_string(); + } + return fade_color(&faint, 0.55); } let hue = tags.first().map_or(215.0, |tag| stable_hue(tag)); format!("hsla({hue:.0}, 70%, 64%, 0.9)") diff --git a/src/workbench/mod.rs b/src/workbench/mod.rs index 88f63cf..f7d6f45 100644 --- a/src/workbench/mod.rs +++ b/src/workbench/mod.rs @@ -7,6 +7,7 @@ mod agent_model_picker; mod agent_provider_pane; mod api_keys_pane; mod workspace_settings_pane; +mod appearance_settings_pane; mod app_prefs; mod browser_tab; mod chat_markdown; @@ -31,12 +32,15 @@ pub mod state; mod voice_app_controls; mod terminal_cell; mod terminal_glue; +mod theme_service; mod toast; mod update_dialog; mod update_service; mod workspace_panel; pub use agent_panel::AgentPanelDock; +pub use appearance_settings_pane::AppearanceSettingsPane; +pub use theme_service::ThemeService; pub use agent_provider_pane::AgentProviderPane; pub use api_keys_pane::ApiKeysPane; pub use workspace_settings_pane::WorkspaceSettingsPane; diff --git a/src/workbench/plans_panel/plans-panel.css b/src/workbench/plans_panel/plans-panel.css index 1d6c6c8..330492a 100644 --- a/src/workbench/plans_panel/plans-panel.css +++ b/src/workbench/plans_panel/plans-panel.css @@ -28,7 +28,7 @@ .blx-sr-card__badge[data-kind="plan"] { background: var(--accent-soft); color: var(--accent-cool); - border-color: rgba(88, 166, 255, 0.3); + border-color: var(--accent-cool-soft); } .blx-plans-group { @@ -70,7 +70,7 @@ height: 15px; padding: 0 0.3rem; border-radius: 999px; - background: rgba(255, 255, 255, 0.07); + background: var(--overlay-2); color: var(--text); font-size: 0.62rem; letter-spacing: 0; @@ -124,7 +124,7 @@ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - font-family: var(--blx-font-mono, var(--font-mono, monospace)); + font-family: var(--font-mono); } .blx-plans-card__protected { @@ -133,7 +133,7 @@ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - color: #ffc04d; + color: var(--warning); font-size: 0.66rem; font-weight: 500; } @@ -145,13 +145,13 @@ padding: 0.55rem; border: 1px dashed var(--border); border-radius: 0.45rem; - background: rgba(255, 255, 255, 0.025); + background: var(--overlay-1); } .blx-plans-card__rename-input { flex: 1 1 auto; min-width: 0; - background: rgba(0, 0, 0, 0.22); + background: var(--overlay-6); border: 1px solid var(--border); border-radius: 0.4rem; color: var(--text); @@ -162,8 +162,8 @@ .blx-plans-card__rename-input:focus { outline: none; - border-color: rgba(88, 166, 255, 0.56); - box-shadow: 0 0 0 2px rgba(88, 166, 255, 0.18); + border-color: var(--border-focus); + box-shadow: 0 0 0 2px var(--accent-soft); } .blx-plans-task-summary { @@ -178,7 +178,7 @@ display: inline-flex; align-items: center; gap: 0.18rem; - font-family: var(--blx-font-mono, var(--font-mono, monospace)); + font-family: var(--font-mono); font-size: 0.72rem; font-weight: 700; line-height: 1; @@ -195,27 +195,27 @@ } .blx-plans-task-stat--total { - color: #cbd5e1; + color: var(--text-muted); } .blx-plans-task-stat--pending { - color: #e5e7eb; + color: var(--text); } .blx-plans-task-stat--in-progress { - color: #60a5fa; + color: var(--accent); } .blx-plans-task-stat--blocked { - color: #fbbf24; + color: var(--warning); } .blx-plans-task-stat--completed { - color: #4ade80; + color: var(--success); } .blx-plans-task-stat--cancelled { - color: #f87171; + color: var(--danger); } .blx-plans-state-line { @@ -223,12 +223,12 @@ grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 1px; height: 3px; - background: rgba(255, 255, 255, 0.05); + background: var(--overlay-2); } .blx-plans-state-line__seg { opacity: 0.22; - background: rgba(255, 255, 255, 0.18); + background: var(--overlay-4); } .blx-plans-state-line__seg--active { @@ -236,35 +236,35 @@ } .blx-plans-state-line__seg--pending { - background: #e5e7eb; + background: var(--text-muted); } .blx-plans-state-line__seg--in-progress { - background: #60a5fa; + background: var(--accent); } .blx-plans-state-line__seg--blocked { - background: #fbbf24; + background: var(--warning); } .blx-plans-state-line__seg--completed { - background: #4ade80; + background: var(--success); } .blx-plans-state-line__seg--cancelled { - background: #f87171; + background: var(--danger); } .blx-plans-card[data-state="blocked"] { - border-color: rgba(251, 191, 36, 0.3); + border-color: color-mix(in srgb, var(--warning) 30%, transparent); } .blx-plans-card[data-state="in-progress"] { - border-color: rgba(96, 165, 250, 0.32); + border-color: color-mix(in srgb, var(--accent) 32%, transparent); } .blx-plans-card[data-state="completed"] { - border-color: rgba(74, 222, 128, 0.26); + border-color: color-mix(in srgb, var(--success) 26%, transparent); } @media (max-width: 720px) { diff --git a/src/workbench/sidebar_resizer/sidebar-resizer.css b/src/workbench/sidebar_resizer/sidebar-resizer.css index 1070f3f..6be3b9e 100644 --- a/src/workbench/sidebar_resizer/sidebar-resizer.css +++ b/src/workbench/sidebar_resizer/sidebar-resizer.css @@ -13,7 +13,7 @@ .workbench-sidebar__resizer:hover, .workbench-sidebar__resizer--active { - background: rgba(255, 255, 255, 0.04); + background: var(--overlay-1); border-bottom-color: var(--border); } @@ -28,6 +28,6 @@ .workbench-sidebar__resizer:hover .workbench-sidebar__resizer-grip, .workbench-sidebar__resizer--active .workbench-sidebar__resizer-grip { - background: var(--accent, var(--text)); + background: var(--accent); opacity: 1; } diff --git a/src/workbench/theme_service.rs b/src/workbench/theme_service.rs new file mode 100644 index 0000000..8b15d36 --- /dev/null +++ b/src/workbench/theme_service.rs @@ -0,0 +1,103 @@ +//! App theme selection persisted in `localStorage` and applied via `data-theme` on ``. + +use crate::config::THEME_STORAGE_KEY; +use crate::theme::{theme_by_id, DEFAULT_THEME_ID, THEMES}; +use js_sys; +use leptos::prelude::*; +use wasm_bindgen::JsValue; + +#[allow(dead_code)] +pub const THEME_CHANGED_EVENT: &str = "blxcode-theme-changed"; // terminal_bootstrap.mjs / graph3d + +#[derive(Clone, Copy)] +pub struct ThemeService { + active_theme_id: RwSignal, +} + +impl ThemeService { + #[must_use] + pub fn new() -> Self { + let id = read_theme_storage(); + apply_theme_to_dom(&id); + Self { + active_theme_id: RwSignal::new(id), + } + } + + #[must_use] + pub fn active_theme_id(&self) -> RwSignal { + self.active_theme_id + } + + #[must_use] + pub fn active_theme(&self) -> impl Fn() -> &'static crate::theme::AppTheme + Copy { + let sig = self.active_theme_id; + move || { + theme_by_id(&sig.get()).unwrap_or_else(|| { + theme_by_id(DEFAULT_THEME_ID).expect("default theme exists") + }) + } + } + + pub fn set_theme(&self, theme_id: &str) { + let id = if crate::theme::is_valid_theme_id(theme_id) { + theme_id.to_string() + } else { + DEFAULT_THEME_ID.to_string() + }; + self.active_theme_id.set(id.clone()); + apply_theme_to_dom(&id); + write_theme_storage(&id); + dispatch_theme_changed(&id); + } +} + +impl Default for ThemeService { + fn default() -> Self { + Self::new() + } +} + +fn read_theme_storage() -> String { + web_sys::window() + .and_then(|w| w.local_storage().ok().flatten()) + .and_then(|s| s.get_item(THEME_STORAGE_KEY).ok().flatten()) + .filter(|id| crate::theme::is_valid_theme_id(id)) + .unwrap_or_else(|| DEFAULT_THEME_ID.to_string()) +} + +fn write_theme_storage(theme_id: &str) { + if let Some(w) = web_sys::window() { + if let Ok(Some(s)) = w.local_storage() { + let _ = s.set_item(THEME_STORAGE_KEY, theme_id); + } + } +} + +fn apply_theme_to_dom(theme_id: &str) { + if let Some(doc) = web_sys::window().and_then(|w| w.document()) { + if let Some(root) = doc.document_element() { + let _ = root.set_attribute("data-theme", theme_id); + } + } +} + +fn dispatch_theme_changed(theme_id: &str) { + if let Some(w) = web_sys::window() { + let detail = js_sys::Object::new(); + let _ = js_sys::Reflect::set( + &detail, + &JsValue::from_str("themeId"), + &JsValue::from_str(theme_id), + ); + if let Ok(ev) = web_sys::CustomEvent::new("blxcode-theme-changed") { + let _ = js_sys::Reflect::set(&ev, &JsValue::from_str("detail"), &detail); + let _ = w.dispatch_event(&ev); + } + } +} + +#[must_use] +pub fn theme_count() -> usize { + THEMES.len() +} diff --git a/src/workbench/update_dialog.css b/src/workbench/update_dialog.css index e0130ea..50b13c8 100644 --- a/src/workbench/update_dialog.css +++ b/src/workbench/update_dialog.css @@ -11,8 +11,8 @@ border: 1px solid var(--border-strong); border-radius: 4px; background: var(--bg-panel); - color: var(--fg-main); - box-shadow: 0 18px 60px rgba(0, 0, 0, 0.42); + color: var(--text); + box-shadow: 0 18px 60px var(--scrim-bg); } .blx-update-banner__icon { @@ -53,7 +53,7 @@ padding: 0.6rem 0.7rem; border: 1px solid var(--border); border-radius: 4px; - background: var(--bg-subtle); + background: var(--bg-raised); font-size: 0.86rem; } @@ -69,7 +69,7 @@ .blx-update-notes h3 { margin: 0 0 0.45rem; font-size: 0.78rem; - color: var(--fg-muted); + color: var(--text-muted); } .blx-update-notes p, @@ -81,7 +81,7 @@ } .blx-update-error { - color: #ff7b72; + color: var(--danger); } .blx-update-progress { @@ -90,7 +90,7 @@ } .blx-update-progress__row { - color: var(--fg-muted); + color: var(--text-muted); font-size: 0.76rem; } diff --git a/styles.css b/styles.css index 1f37c6b..bce8dd6 100644 --- a/styles.css +++ b/styles.css @@ -4,30 +4,6 @@ */ :root { - color-scheme: dark; - --bg-app: #090a0d; - --bg-raised: #101116; - --bg-panel: #15171d; - --bg-panel-header: #191b21; - --border: rgba(255, 255, 255, 0.085); - --border-strong: rgba(255, 255, 255, 0.16); - --border-focus: #58a6ff; - - /* Text */ - --text: #f1f2f5; - --text-muted: #a3a7b3; - --text-faint: #676c78; - - --accent: #58a6ff; - --accent-hover: #7ab8ff; - --accent-soft: rgba(88, 166, 255, 0.16); - --accent-cool: #9bd3ff; - --accent-cool-soft: rgba(88, 166, 255, 0.12); - --syntax-type: #63e6be; - --syntax-keyword: #f2a3ff; - - --font-mono: "JetBrains Mono", "SF Mono", ui-monospace, monospace; - font-family: var(--font-mono); font-size: 15px; line-height: 1.55; @@ -83,24 +59,24 @@ body { @keyframes agent-orb-pulse { 0% { box-shadow: - inset 0 1px 0 rgba(255, 255, 255, 0.13), - 0 20px 45px rgba(0, 0, 0, 0.42), - 0 0 0 8px rgba(88, 166, 255, 0.075), - 0 0 0 0 rgba(88, 166, 255, 0.26); + inset 0 1px 0 var(--overlay-4), + 0 20px 45px var(--scrim-bg), + 0 0 0 8px var(--accent-soft), + 0 0 0 0 var(--accent-soft); } 70% { box-shadow: - inset 0 1px 0 rgba(255, 255, 255, 0.13), - 0 20px 45px rgba(0, 0, 0, 0.42), - 0 0 0 8px rgba(88, 166, 255, 0.075), - 0 0 0 18px rgba(88, 166, 255, 0); + inset 0 1px 0 var(--overlay-4), + 0 20px 45px var(--scrim-bg), + 0 0 0 8px var(--accent-soft), + 0 0 0 18px transparent; } 100% { box-shadow: - inset 0 1px 0 rgba(255, 255, 255, 0.13), - 0 20px 45px rgba(0, 0, 0, 0.42), - 0 0 0 8px rgba(88, 166, 255, 0.075), - 0 0 0 0 rgba(88, 166, 255, 0); + inset 0 1px 0 var(--overlay-4), + 0 20px 45px var(--scrim-bg), + 0 0 0 8px var(--accent-soft), + 0 0 0 0 transparent; } } @@ -114,7 +90,7 @@ body { .app-shell { height: 100%; min-height: 0; - background: rgba(9, 10, 13, 0.96); + background: var(--bg-app); } .app-shell--boot { @@ -122,9 +98,9 @@ body { min-height: 0; background: linear-gradient( 165deg, - #17181d 0%, + var(--bg-raised) 0%, var(--bg-app) 38%, - #050608 100% + var(--bg-app) 100% ); } @@ -175,8 +151,8 @@ body { overflow: hidden; color: var(--text); background: - radial-gradient(circle at 50% 40%, rgba(88, 166, 255, 0.12), transparent 32rem), - linear-gradient(165deg, #17181d 0%, var(--bg-app) 44%, #050608 100%); + radial-gradient(circle at 50% 40%, var(--accent-cool-soft), transparent 32rem), + linear-gradient(165deg, var(--bg-raised) 0%, var(--bg-app) 44%, var(--bg-app) 100%); } .blx-boot--static { @@ -188,8 +164,8 @@ body { inset: 0; opacity: 0.32; background-image: - linear-gradient(rgba(255, 255, 255, 0.042) 1px, transparent 1px), - linear-gradient(90deg, rgba(255, 255, 255, 0.035) 1px, transparent 1px); + linear-gradient(var(--overlay-1) 1px, transparent 1px), + linear-gradient(90deg, var(--overlay-1) 1px, transparent 1px); background-size: 42px 42px; mask-image: radial-gradient(circle at center, black, transparent 72%); pointer-events: none; @@ -204,11 +180,11 @@ body { border: 1px solid var(--border-strong); border-radius: 8px; background: - linear-gradient(180deg, rgba(21, 23, 29, 0.93), rgba(12, 13, 18, 0.93)), - rgba(9, 10, 13, 0.95); + linear-gradient(180deg, var(--bg-panel), var(--bg-raised)), + var(--bg-app); box-shadow: - 0 24px 70px rgba(0, 0, 0, 0.45), - inset 0 1px 0 rgba(255, 255, 255, 0.055); + 0 24px 70px var(--scrim-bg), + inset 0 1px 0 var(--overlay-2); animation: blx-boot-frame-enter 0.28s ease-out both; } @@ -217,7 +193,7 @@ body { position: absolute; inset: 0; border-radius: inherit; - border-top: 1px solid rgba(155, 211, 255, 0.22); + border-top: 1px solid var(--accent-cool-soft); pointer-events: none; } @@ -234,12 +210,12 @@ body { flex: 0 0 auto; display: grid; place-items: center; - border: 1px solid rgba(255, 255, 255, 0.09); + border: 1px solid var(--overlay-2); border-radius: 8px; background: - linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.01)), - rgba(9, 10, 13, 0.58); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06); + linear-gradient(180deg, var(--overlay-1), var(--overlay-1)), + var(--bg-app); + box-shadow: inset 0 1px 0 var(--overlay-2); } .blx-boot__mark { @@ -247,7 +223,7 @@ body { height: 4.35rem; display: block; object-fit: contain; - filter: drop-shadow(0 10px 20px rgba(0, 0, 0, 0.48)); + filter: drop-shadow(0 10px 20px var(--scrim-bg)); } .blx-boot__copy { @@ -288,16 +264,16 @@ body { padding: 0.45rem; border: 1px solid var(--border); border-radius: 6px; - background: rgba(5, 6, 8, 0.42); + background: var(--bg-app); } .blx-boot__preview-sidebar, .blx-boot__preview-main, .blx-boot__preview-panel { min-width: 0; - border: 1px solid rgba(255, 255, 255, 0.065); + border: 1px solid var(--overlay-2); border-radius: 4px; - background: rgba(16, 17, 22, 0.74); + background: var(--bg-panel); overflow: hidden; } @@ -334,7 +310,7 @@ body { display: block; min-height: 0.72rem; border-radius: 4px; - background: rgba(255, 255, 255, 0.055); + background: var(--overlay-2); overflow: hidden; } @@ -374,7 +350,7 @@ body { background: linear-gradient( 90deg, transparent, - rgba(155, 211, 255, 0.16), + var(--accent-cool-soft), transparent ); animation: blx-boot-sheen 1.9s ease-in-out infinite; @@ -384,7 +360,7 @@ body { height: 0.22rem; overflow: hidden; border-radius: 4px; - background: rgba(255, 255, 255, 0.07); + background: var(--overlay-2); } .blx-boot__rail span { @@ -611,7 +587,7 @@ body { } .workbench-sidebar__add-btn:hover { - background: rgba(255, 255, 255, 0.06); + background: var(--overlay-2); color: var(--text); } @@ -672,7 +648,7 @@ body { .workbench-sidebar__resizer:hover, .workbench-sidebar__resizer--active { - background: rgba(255, 255, 255, 0.04); + background: var(--overlay-1); border-bottom-color: var(--border); } @@ -756,13 +732,13 @@ body { font-size: 0.62rem; font-weight: 700; line-height: 1; - color: #fff; + color: var(--text-bright); letter-spacing: -0.02em; } .workbench-sidebar__badge--total { - background-color: #e8954a; - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25); + background-color: var(--agent-claude); + box-shadow: 0 0 0 1px var(--scrim-bg); } .workbench-sidebar__close { @@ -794,8 +770,8 @@ body { } .workbench-sidebar__close:hover { - background: rgba(255, 255, 255, 0.08); - color: var(--text-bright, #fff); + background: var(--overlay-2); + color: var(--text-bright, var(--text-bright)); } .workbench-sidebar--collapsed .workbench-sidebar__close { @@ -842,8 +818,8 @@ body { } .workbench-sidebar--collapsed .workbench-sidebar__row--active { - border-color: rgba(114, 160, 255, 0.55); - box-shadow: inset 0 0 0 1px rgba(114, 160, 255, 0.22); + border-color: var(--accent-soft); + box-shadow: inset 0 0 0 1px var(--accent-soft); } .workbench-sidebar--collapsed .workbench-sidebar__icon { @@ -854,7 +830,7 @@ body { height: 1.55rem; border-radius: 4px; color: var(--text); - background: rgba(255, 255, 255, 0.07); + background: var(--overlay-2); font-family: var(--font-mono); font-size: 0.68rem; font-weight: 700; @@ -896,13 +872,13 @@ body { .workbench-sidebar__collapsed-add:hover, .workbench-sidebar__collapsed-add:focus-visible { - background: rgba(255, 255, 255, 0.06); + background: var(--overlay-2); color: var(--text); border-color: var(--border-strong); } .workbench-sidebar__row:hover { - background: rgba(255, 255, 255, 0.04); + background: var(--overlay-1); } .workbench-sidebar__row--active { @@ -952,7 +928,7 @@ body { border: 1px solid var(--border-strong); border-radius: 4px; background: var(--bg-panel); - box-shadow: 0 14px 32px rgba(0, 0, 0, 0.45); + box-shadow: 0 14px 32px var(--scrim-bg); } .workspace-context-menu__item { @@ -971,16 +947,16 @@ body { .workspace-context-menu__item:hover, .workspace-context-menu__item:focus-visible { - background: rgba(255, 255, 255, 0.07); + background: var(--overlay-2); } .workspace-context-menu__item--danger { - color: #ffb4a8; + color: var(--danger); } .workspace-context-menu__item--danger:hover, .workspace-context-menu__item--danger:focus-visible { - background: rgba(255, 80, 64, 0.1); + background: var(--danger-soft); } .workspace-rename-backdrop { @@ -989,7 +965,7 @@ body { z-index: 1100; display: grid; place-items: center; - background: rgba(5, 6, 10, 0.48); + background: var(--bg-app); backdrop-filter: blur(3px); } @@ -998,7 +974,7 @@ body { border: 1px solid var(--border-strong); border-radius: 4px; background: var(--bg-panel); - box-shadow: 0 24px 70px rgba(0, 0, 0, 0.58); + box-shadow: 0 24px 70px var(--scrim-bg); overflow: hidden; } @@ -1039,7 +1015,7 @@ body { .workspace-rename-dialog__close:focus-visible { color: var(--text); border-color: var(--border); - background: rgba(255, 255, 255, 0.06); + background: var(--overlay-2); } .workspace-rename-dialog__body { @@ -1064,13 +1040,13 @@ body { padding: 0.58rem 0.65rem; font: inherit; color: var(--text); - background: rgba(0, 0, 0, 0.18); + background: var(--scrim-bg); outline: none; } .workspace-rename-dialog__input:focus { - border-color: rgba(114, 160, 255, 0.82); - box-shadow: 0 0 0 2px rgba(114, 160, 255, 0.18); + border-color: var(--accent-soft); + box-shadow: 0 0 0 2px var(--accent-soft); } .workspace-rename-dialog__actions { @@ -1099,12 +1075,12 @@ body { .workspace-rename-dialog__btn--ghost:hover, .workspace-rename-dialog__btn--ghost:focus-visible { color: var(--text); - background: rgba(255, 255, 255, 0.06); + background: var(--overlay-2); } .workspace-rename-dialog__btn--primary { color: white; - border-color: rgba(74, 144, 255, 0.72); + border-color: var(--accent-soft); background: var(--accent); } @@ -1177,8 +1153,8 @@ body { darker than the surrounding chrome so the empty state still reads as a distinct surface without bleeding the wallpaper through. */ background: - linear-gradient(180deg, rgba(20, 22, 30, 0.88), rgba(12, 13, 18, 0.88)), - rgba(10, 11, 15, 0.9); + linear-gradient(180deg, var(--bg-app), var(--bg-app)), + var(--overlay-2); backdrop-filter: blur(14px); display: flex; flex-direction: column; @@ -1191,7 +1167,7 @@ body { .workbench-empty-editor__lead { margin: 0; font-size: 0.98rem; - color: rgba(238, 239, 245, 0.9); + color: var(--text-muted); line-height: 1.45; text-align: center; } @@ -1209,13 +1185,13 @@ body { height: auto; object-fit: contain; display: block; - filter: drop-shadow(0 10px 28px rgba(0, 0, 0, 0.45)); + filter: drop-shadow(0 10px 28px var(--scrim-bg)); } .workbench-empty-editor__note { margin: 0 0 1.1rem; font-size: 0.82rem; - color: rgba(190, 194, 208, 0.74); + color: var(--text-muted); line-height: 1.45; text-align: center; } @@ -1233,8 +1209,8 @@ body { overflow-y: auto; padding: 0.55rem 0.5rem; border-radius: 10px; - border: 1px solid rgba(255, 255, 255, 0.09); - background: rgba(0, 0, 0, 0.2); + border: 1px solid var(--overlay-2); + background: var(--scrim-bg); display: flex; flex-direction: column; gap: 0.4rem; @@ -1276,7 +1252,7 @@ button.workbench-recent-remove { border: none; border-radius: 8px; background: transparent; - color: rgba(190, 194, 208, 0.5); + color: var(--text-muted); cursor: pointer; transition: color 0.12s ease, @@ -1285,13 +1261,13 @@ button.workbench-recent-remove { button.workbench-recent-remove:hover, button.workbench-recent-remove:focus-visible { - color: rgba(255, 200, 190, 0.95); - background: rgba(255, 80, 80, 0.14); + color: var(--text-muted); + background: var(--danger-soft); outline: none; } button.workbench-recent-remove:focus-visible { - box-shadow: 0 0 0 2px rgba(120, 160, 255, 0.35); + box-shadow: 0 0 0 2px var(--accent-soft); } .harness-sheet--quickopen .harness-cmd-list { @@ -1302,7 +1278,7 @@ button.workbench-recent-remove:focus-visible { margin: 0; padding: 0.75rem 0.55rem; font-size: 0.82rem; - color: rgba(190, 194, 208, 0.72); + color: var(--text-muted); list-style: none; } @@ -1311,7 +1287,7 @@ button.workbench-recent-remove:focus-visible { margin: 0 auto; width: min(100%, 28rem); padding: 1.15rem 1rem 0.35rem; - border-top: 1px solid rgba(255, 255, 255, 0.09); + border-top: 1px solid var(--overlay-2); display: flex; flex-direction: column; gap: 0.55rem; @@ -1324,7 +1300,7 @@ button.workbench-recent-remove:focus-visible { gap: 1rem; font-size: 0.82rem; color: var(--text-muted); - text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45); + text-shadow: 0 1px 2px var(--scrim-bg); } .workbench-shortcut-li { @@ -1352,12 +1328,12 @@ button.workbench-shortcut-row--action { button.workbench-shortcut-row--action:hover, button.workbench-shortcut-row--action:focus-visible { - background: rgba(255, 255, 255, 0.06); + background: var(--overlay-2); outline: none; } button.workbench-shortcut-row--action:focus-visible { - box-shadow: 0 0 0 2px rgba(120, 160, 255, 0.35); + box-shadow: 0 0 0 2px var(--accent-soft); } .harness-sheet--quickopen { @@ -1368,7 +1344,7 @@ button.workbench-shortcut-row--action:focus-visible { margin: 0 0 0.65rem; font-size: 1rem; font-weight: 600; - color: rgba(238, 239, 245, 0.95); + color: var(--text); } .harness-quickopen-section { @@ -1376,7 +1352,7 @@ button.workbench-shortcut-row--action:focus-visible { font-size: 0.72rem; letter-spacing: 0.04em; text-transform: uppercase; - color: rgba(190, 194, 208, 0.65); + color: var(--text-muted); } .harness-quickopen-path { @@ -1396,7 +1372,7 @@ button.workbench-shortcut-row--action:focus-visible { padding: 0 0.75rem; border-radius: 6px; border: 1px solid var(--border-strong); - background: rgba(36, 40, 52, 0.9); + background: var(--overlay-2); color: var(--text); font: inherit; font-size: 0.82rem; @@ -1404,7 +1380,7 @@ button.workbench-shortcut-row--action:focus-visible { } .harness-quickopen-path__btn:hover { - background: rgba(50, 56, 72, 0.95); + background: var(--overlay-2); } .harness-quickopen-wizard { @@ -1412,16 +1388,16 @@ button.workbench-shortcut-row--action:focus-visible { width: 100%; padding: 0.45rem 0.55rem; border-radius: 6px; - border: 1px dashed rgba(255, 255, 255, 0.14); + border: 1px dashed var(--overlay-4); background: transparent; - color: rgba(218, 221, 232, 0.88); + color: var(--text-muted); font: inherit; font-size: 0.82rem; cursor: pointer; } .harness-quickopen-wizard:hover { - background: rgba(255, 255, 255, 0.04); + background: var(--overlay-1); } .workbench-shortcut-row--spacer { @@ -1447,7 +1423,7 @@ button.workbench-shortcut-row--action:focus-visible { } .workbench-shortcut-row__label { - color: rgba(218, 221, 232, 0.82); + color: var(--text-muted); } .workbench-shortcut-row__keys { @@ -1464,7 +1440,7 @@ button.workbench-shortcut-row--action:focus-visible { font-weight: 500; line-height: 1; color: var(--text); - background: rgba(22, 24, 31, 0.78); + background: var(--overlay-2); border: 1px solid var(--border-strong); border-radius: 3px; padding: 0.2rem 0.35rem; @@ -1498,7 +1474,7 @@ button.workbench-shortcut-row--action:focus-visible { padding: 0.55rem 0.7rem; border: 1px solid var(--border); border-radius: 0.5rem; - background: rgba(255, 255, 255, 0.02); + background: var(--overlay-1); } .app-prefs-toggle-cell .app-prefs-toggle, @@ -1526,7 +1502,7 @@ button.workbench-shortcut-row--action:focus-visible { padding: 0.55rem 0.75rem; border-radius: 0.5rem; border: 1px solid var(--border); - background: rgba(255, 255, 255, 0.025); + background: var(--overlay-1); font-size: 0.85rem; margin: 0; } @@ -1553,12 +1529,12 @@ button.workbench-shortcut-row--action:focus-visible { } .app-prefs-version__row--available { - border-color: rgba(104, 220, 159, 0.35); - background: rgba(104, 220, 159, 0.08); + border-color: var(--success-border); + background: var(--success-surface); } .app-prefs-version__row--available dd { - color: #86e7b2; + color: var(--success-text); } .app-prefs-radio { @@ -1567,7 +1543,7 @@ button.workbench-shortcut-row--action:focus-visible { gap: 0.45rem; cursor: pointer; font-size: 0.88rem; - color: rgba(218, 221, 232, 0.9); + color: var(--text-secondary); } .app-prefs-toggle-grid--triple { @@ -1594,11 +1570,11 @@ button.workbench-shortcut-row--action:focus-visible { font-family: inherit; } .app-prefs-hotkey-capture--recording { - border-color: var(--accent, #4ea1ff); - background: rgba(78, 161, 255, 0.08); + border-color: var(--accent, var(--accent)); + background: var(--accent-soft); } .app-prefs-radio input { - accent-color: var(--accent); + accent-color: var(--accent-control); } .workbench-right-slot { @@ -1666,7 +1642,7 @@ button.workbench-shortcut-row--action:focus-visible { .workbench-right-rail-tab:hover { color: var(--text); - background: rgba(255, 255, 255, 0.06); + background: var(--overlay-2); border-color: var(--border); } @@ -1745,7 +1721,7 @@ button.workbench-shortcut-row--action:focus-visible { min-height: 48px; padding: 0.42rem 0.55rem; border-bottom: 1px solid var(--border); - background: rgba(13, 14, 18, 0.96); + background: var(--overlay-2); box-sizing: border-box; } @@ -1795,10 +1771,10 @@ button.workbench-shortcut-row--action:focus-visible { min-width: 0; height: 34px; padding: 0.18rem; - border: 1px solid rgba(255, 255, 255, 0.09); + border: 1px solid var(--overlay-2); border-radius: 4px; - background: rgba(0, 0, 0, 0.34); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04); + background: var(--scrim-bg); + box-shadow: inset 0 1px 0 var(--overlay-1); overflow: hidden; } @@ -1833,18 +1809,18 @@ button.workbench-shortcut-row--action:focus-visible { .workbench-right-tab:hover { color: var(--text); - background: rgba(255, 255, 255, 0.055); + background: var(--overlay-2); } .workbench-right-tab--active { color: var(--accent); flex: 1 1 8.5rem; min-width: 7.5rem; - border-color: rgba(88, 166, 255, 0.42); - background: rgba(88, 166, 255, 0.11); + border-color: var(--accent-soft); + background: var(--accent-soft); box-shadow: - inset 0 0 0 1px rgba(88, 166, 255, 0.08), - 0 0 22px rgba(88, 166, 255, 0.08); + inset 0 0 0 1px var(--accent-soft), + 0 0 22px var(--accent-soft); } .workbench-right-tab__icon { @@ -1896,17 +1872,17 @@ button.workbench-shortcut-row--action:focus-visible { gap: 0.75rem; padding: 0.75rem; box-sizing: border-box; - background: rgba(9, 10, 13, 0.42); + background: var(--bg-app); } .workbench-agent-pane--drop-active, .workbench-agent-pane--drop-reject { - outline: 2px dashed rgba(88, 166, 255, 0.82); + outline: 2px dashed var(--accent-soft); outline-offset: -0.45rem; } .workbench-agent-pane--drop-reject { - outline-color: rgba(248, 113, 113, 0.8); + outline-color: var(--danger-soft); } .agent-drop-overlay { @@ -1917,14 +1893,14 @@ button.workbench-shortcut-row--action:focus-visible { place-items: center; pointer-events: none; border-radius: 4px; - border: 1px dashed rgba(88, 166, 255, 0.58); - background: rgba(6, 12, 20, 0.72); - box-shadow: inset 0 0 0 999px rgba(88, 166, 255, 0.08); + border: 1px dashed var(--accent-soft); + background: var(--overlay-2); + box-shadow: inset 0 0 0 999px var(--accent-soft); } .workbench-agent-pane--drop-reject .agent-drop-overlay { - border-color: rgba(248, 113, 113, 0.62); - box-shadow: inset 0 0 0 999px rgba(248, 113, 113, 0.08); + border-color: var(--danger-soft); + box-shadow: inset 0 0 0 999px var(--danger-soft); } .agent-drop-overlay span { @@ -1933,8 +1909,8 @@ button.workbench-shortcut-row--action:focus-visible { color: var(--text); padding: 0.48rem 0.72rem; border-radius: 4px; - border: 1px solid rgba(255, 255, 255, 0.12); - background: rgba(0, 0, 0, 0.34); + border: 1px solid var(--overlay-3); + background: var(--scrim-bg); } .workbench-agent-scope-hint { @@ -1974,14 +1950,14 @@ button.workbench-shortcut-row--action:focus-visible { height: 7.2rem; border-radius: 50%; padding: 0; - background: rgba(88, 166, 255, 0.08); - border: 1px solid rgba(88, 166, 255, 0.26); + background: var(--accent-soft); + border: 1px solid var(--accent-soft); color: inherit; cursor: pointer; box-shadow: - inset 0 1px 0 rgba(255, 255, 255, 0.12), - 0 20px 45px rgba(0, 0, 0, 0.42), - 0 0 0 8px rgba(255, 255, 255, 0.025); + inset 0 1px 0 var(--overlay-3), + 0 20px 45px var(--scrim-bg), + 0 0 0 8px var(--overlay-1); transition: border-color 0.14s ease, box-shadow 0.14s ease, @@ -1994,18 +1970,18 @@ button.workbench-shortcut-row--action:focus-visible { position: absolute; inset: 0.72rem; border-radius: inherit; - border: 1px solid rgba(88, 166, 255, 0.18); + border: 1px solid var(--accent-soft); } .agent-hero__orb:hover, .agent-hero__orb:focus-visible, .agent-hero__orb--active { - border-color: rgba(88, 166, 255, 0.62); - background: rgba(88, 166, 255, 0.13); + border-color: var(--accent-soft); + background: var(--accent-soft); box-shadow: - inset 0 1px 0 rgba(255, 255, 255, 0.13), - 0 20px 45px rgba(0, 0, 0, 0.42), - 0 0 0 8px rgba(88, 166, 255, 0.075); + inset 0 1px 0 var(--overlay-4), + 0 20px 45px var(--scrim-bg), + 0 0 0 8px var(--accent-soft); } .agent-hero__orb:active, @@ -2022,12 +1998,12 @@ button.workbench-shortcut-row--action:focus-visible { width: 2.9rem; height: 2.9rem; border-radius: 4px; - color: #0d0e12; + color: var(--on-accent); background: var(--accent-cool); font-size: 1.72rem; font-weight: 900; line-height: 1; - box-shadow: 0 8px 22px rgba(0, 0, 0, 0.34); + box-shadow: 0 8px 22px var(--scrim-bg); } .agent-hero__meta { @@ -2076,8 +2052,8 @@ button.workbench-shortcut-row--action:focus-visible { height: 2.35rem; flex-shrink: 0; box-shadow: - inset 0 1px 0 rgba(255, 255, 255, 0.1), - 0 6px 14px rgba(0, 0, 0, 0.32); + inset 0 1px 0 var(--overlay-3), + 0 6px 14px var(--scrim-bg); } .agent-hero--compact .agent-hero__orb::after { @@ -2113,8 +2089,8 @@ button.workbench-shortcut-row--action:focus-visible { .agent-section { border: 1px solid var(--border); border-radius: 4px; - background: rgba(9, 10, 13, 0.48); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.045); + background: var(--bg-app); + box-shadow: inset 0 1px 0 var(--overlay-1); } .agent-section__head { @@ -2138,7 +2114,7 @@ button.workbench-shortcut-row--action:focus-visible { .agent-section__head--toggle:hover, .agent-section__head--toggle:focus-visible { - background: rgba(255, 255, 255, 0.035); + background: var(--overlay-1); } .agent-section__head h3 { @@ -2181,15 +2157,15 @@ button.workbench-shortcut-row--action:focus-visible { margin-left: 0.6rem; padding: 0.12rem 0.45rem; border-radius: 999px; - background: rgba(125, 211, 252, 0.08); + background: var(--overlay-2); font-family: ui-monospace, "SF Mono", "Menlo", "Consolas", monospace; font-size: 0.7rem; line-height: 1.2; - color: var(--text-muted, #8a8f98); + color: var(--text-muted, var(--text-muted)); } .agent-chat-head__cost strong { - color: var(--accent, #7dd3fc); + color: var(--accent, var(--accent-cool)); font-weight: 600; } @@ -2217,7 +2193,7 @@ button.workbench-shortcut-row--action:focus-visible { .agent-chat-head__reset:focus-visible { color: var(--text); border-color: var(--border); - background: rgba(255, 255, 255, 0.06); + background: var(--overlay-2); } .agent-chat-head__reset:disabled { @@ -2242,13 +2218,13 @@ button.workbench-shortcut-row--action:focus-visible { .agent-chat-head__image-mode:focus-visible { color: var(--text); border-color: var(--border); - background: rgba(255, 255, 255, 0.06); + background: var(--overlay-2); } .agent-chat-head__image-mode--active { - color: var(--accent, #4ea1ff); - border-color: var(--accent, #4ea1ff); - background: rgba(78, 161, 255, 0.14); + color: var(--accent, var(--accent)); + border-color: var(--accent, var(--accent)); + background: var(--overlay-2); } .agent-chat-head__image-mode:disabled { @@ -2260,13 +2236,13 @@ button.workbench-shortcut-row--action:focus-visible { flex-shrink: 0; margin: 0; font-size: 0.72rem; - color: var(--accent, #4ea1ff); + color: var(--accent, var(--accent)); padding: 0.18rem 0.75rem; border-inline: 1px solid var(--border); border-radius: 0; - background: rgba(78, 161, 255, 0.08); + background: var(--accent-soft); border-top: 0; - border-bottom: 1px solid rgba(78, 161, 255, 0.22); + border-bottom: 1px solid var(--overlay-2); } .agent-chat-line--image .agent-chat-body { @@ -2278,7 +2254,7 @@ button.workbench-shortcut-row--action:focus-visible { .agent-chat-image-prompt { margin: 0; font-size: 0.82rem; - color: var(--text-muted, #9aa); + color: var(--text-muted, var(--text-faint)); } .agent-chat-image-prompt__label { @@ -2289,13 +2265,13 @@ button.workbench-shortcut-row--action:focus-visible { .agent-chat-image { max-width: min(100%, 480px); border-radius: 6px; - border: 1px solid var(--border, #2a2a2a); - background: #0a0a0a; + border: 1px solid var(--border, var(--border)); + background: var(--bg-app); } .agent-chat-image-missing { font-style: italic; - color: var(--text-muted, #9aa); + color: var(--text-muted, var(--text-faint)); margin: 0; } @@ -2377,7 +2353,7 @@ button.workbench-shortcut-row--action:focus-visible { } .agent-task--active { - background: rgba(88, 166, 255, 0.08); + background: var(--accent-soft); } .agent-context-item__mark { @@ -2400,7 +2376,7 @@ button.workbench-shortcut-row--action:focus-visible { .agent-context-item__remove:focus-visible { color: var(--text); border-color: var(--border); - background: rgba(255, 255, 255, 0.06); + background: var(--overlay-2); } .agent-context-image { @@ -2408,7 +2384,7 @@ button.workbench-shortcut-row--action:focus-visible { } .agent-context-image__mark { - background: #67e8f9; + background: var(--syntax-type); } .agent-context-image__open { @@ -2437,13 +2413,13 @@ button.workbench-shortcut-row--action:focus-visible { } .agent-context-image__status--pending { - color: #8bc4ff; - background: rgba(88, 166, 255, 0.16); + color: var(--accent-cool); + background: var(--accent-soft); } .agent-context-image__status--read { - color: #cbd5e1; - background: rgba(148, 163, 184, 0.14); + color: var(--text-muted); + background: var(--overlay-2); } .agent-image-preview-backdrop { @@ -2453,7 +2429,7 @@ button.workbench-shortcut-row--action:focus-visible { display: grid; place-items: center; padding: 1.4rem; - background: rgba(0, 0, 0, 0.58); + background: var(--scrim-bg); backdrop-filter: blur(3px); } @@ -2464,8 +2440,8 @@ button.workbench-shortcut-row--action:focus-visible { flex-direction: column; border-radius: 6px; border: 1px solid var(--border); - background: rgba(13, 15, 20, 0.97); - box-shadow: 0 24px 70px rgba(0, 0, 0, 0.54); + background: var(--overlay-2); + box-shadow: 0 24px 70px var(--scrim-bg); overflow: hidden; } @@ -2502,7 +2478,7 @@ button.workbench-shortcut-row--action:focus-visible { place-items: center; padding: 0.85rem; overflow: auto; - background: rgba(0, 0, 0, 0.28); + background: var(--scrim-bg); } .agent-image-preview__stage img { @@ -2520,8 +2496,8 @@ button.workbench-shortcut-row--action:focus-visible { } .agent-image-preview__remove { - color: #fca5a5; - border-color: rgba(248, 113, 113, 0.26); + color: var(--danger); + border-color: var(--danger-soft); } .agent-task__body { @@ -2540,13 +2516,13 @@ button.workbench-shortcut-row--action:focus-visible { width: 0.42rem; height: 0.42rem; border-radius: 50%; - background: rgba(255, 255, 255, 0.22); - box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08); + background: var(--overlay-2); + box-shadow: 0 0 0 1px var(--overlay-2); } .agent-task--active .agent-task__mark { background: var(--accent); - box-shadow: 0 0 0 4px rgba(88, 166, 255, 0.12); + box-shadow: 0 0 0 4px var(--accent-cool-soft); } .agent-task strong { @@ -2576,32 +2552,32 @@ button.workbench-shortcut-row--action:focus-visible { letter-spacing: 0.04em; text-transform: uppercase; white-space: nowrap; - border: 1px solid rgba(255, 255, 255, 0.08); + border: 1px solid var(--overlay-2); } .agent-task__status--pending { - color: #d1d5db; - background: rgba(148, 163, 184, 0.14); + color: var(--text-muted); + background: var(--overlay-2); } .agent-task__status--in-progress { - color: #8bc4ff; - background: rgba(88, 166, 255, 0.16); + color: var(--accent-cool); + background: var(--accent-soft); } .agent-task__status--blocked { - color: #fbbf24; - background: rgba(245, 158, 11, 0.16); + color: var(--warning); + background: var(--overlay-2); } .agent-task__status--completed { - color: #86efac; - background: rgba(34, 197, 94, 0.16); + color: var(--success); + background: var(--overlay-2); } .agent-task__status--cancelled { - color: #fca5a5; - background: rgba(239, 68, 68, 0.14); + color: var(--danger); + background: var(--overlay-2); } .workbench-agent-input { @@ -2613,15 +2589,15 @@ button.workbench-shortcut-row--action:focus-visible { padding: 0.58rem 0.65rem; border-radius: 4px; border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.28); + background: var(--scrim-bg); color: var(--text); resize: vertical; line-height: 1.45; } .workbench-agent-input:focus { - border-color: rgba(88, 166, 255, 0.52); - box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.08); + border-color: var(--accent-soft); + box-shadow: 0 0 0 3px var(--accent-soft); } .workbench-agent-input--single { @@ -2665,7 +2641,7 @@ button.workbench-shortcut-row--action:focus-visible { .workbench-mini-btn--primary { background: var(--accent); - color: #101114; + color: var(--bg-raised); border-color: var(--accent); font-weight: 800; } @@ -2689,7 +2665,7 @@ button.workbench-shortcut-row--action:focus-visible { .workbench-mini-btn--primary:disabled { background: color-mix(in srgb, var(--accent) 42%, transparent); border-color: color-mix(in srgb, var(--accent) 42%, transparent); - color: color-mix(in srgb, #101114 55%, transparent); + color: color-mix(in srgb, var(--bg-raised) 55%, transparent); } .workbench-mini-btn--primary:disabled:hover { @@ -2720,8 +2696,8 @@ button.workbench-shortcut-row--action:focus-visible { flex-shrink: 0; border: 1px solid var(--border); border-radius: 4px 4px 0 0; - background: rgba(9, 10, 13, 0.48); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.045); + background: var(--bg-app); + box-shadow: inset 0 1px 0 var(--overlay-1); } .workbench-agent-chat-log { @@ -2732,7 +2708,7 @@ button.workbench-shortcut-row--action:focus-visible { border: 1px solid var(--border); border-top: 0; border-radius: 0 0 4px 4px; - background: rgba(9, 10, 13, 0.32); + background: var(--bg-app); } .workbench-agent-transcript { @@ -2742,11 +2718,11 @@ button.workbench-shortcut-row--action:focus-visible { margin: 0; padding: 0; white-space: pre-wrap; - color: rgba(241, 242, 245, 0.9); + color: var(--text); } .workbench-agent-markdown { - color: rgba(241, 242, 245, 0.9); + color: var(--text); font-size: 0.76rem; line-height: 1.66; overflow-wrap: anywhere; @@ -2774,7 +2750,7 @@ button.workbench-shortcut-row--action:focus-visible { .workbench-agent-markdown h3, .workbench-agent-markdown h4 { margin: 0.7rem 0 0.38rem; - color: rgba(248, 249, 252, 0.98); + color: var(--text-muted); line-height: 1.22; } @@ -2797,7 +2773,7 @@ button.workbench-shortcut-row--action:focus-visible { .workbench-agent-markdown hr { margin: 0.65rem 0; border: 0; - border-top: 1px solid rgba(255, 255, 255, 0.08); + border-top: 1px solid var(--overlay-2); } .workbench-agent-markdown ul, @@ -2812,9 +2788,9 @@ button.workbench-shortcut-row--action:focus-visible { .workbench-agent-markdown code { font-family: var(--font-mono); font-size: 0.72rem; - color: #d8e7ff; - background: rgba(91, 124, 250, 0.1); - border: 1px solid rgba(120, 150, 255, 0.14); + color: var(--accent-cool); + background: var(--overlay-2); + border: 1px solid var(--overlay-2); border-radius: 0.28rem; padding: 0.08rem 0.24rem; } @@ -2824,9 +2800,9 @@ button.workbench-shortcut-row--action:focus-visible { padding: 0.65rem 0.72rem; border-radius: 0.5rem; background: - linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.03)); - border: 1px solid rgba(125, 145, 255, 0.12); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03); + linear-gradient(180deg, var(--overlay-1), var(--overlay-1)); + border: 1px solid var(--overlay-2); + box-shadow: inset 0 1px 0 var(--overlay-1); } .workbench-agent-markdown pre code { @@ -2835,7 +2811,7 @@ button.workbench-shortcut-row--action:focus-visible { border: 0; background: transparent; border-radius: 0; - color: rgba(241, 242, 245, 0.92); + color: var(--text-muted); line-height: 1.58; } @@ -2843,10 +2819,10 @@ button.workbench-shortcut-row--action:focus-visible { .workbench-md-fence { margin: 0.42rem 0; border-radius: 0.5rem; - border: 1px solid rgba(125, 145, 255, 0.12); + border: 1px solid var(--overlay-2); background: - linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.03)); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03); + linear-gradient(180deg, var(--overlay-1), var(--overlay-1)); + box-shadow: inset 0 1px 0 var(--overlay-1); } .workbench-md-fence__summary { @@ -2861,7 +2837,7 @@ button.workbench-shortcut-row--action:focus-visible { font-family: var(--font-mono); font-size: 0.7rem; font-weight: 650; - color: rgba(218, 226, 245, 0.88); + color: var(--text-muted); user-select: none; } @@ -2877,7 +2853,7 @@ button.workbench-shortcut-row--action:focus-visible { font-size: 0.68rem; letter-spacing: 0.02em; text-transform: lowercase; - color: rgba(180, 198, 235, 0.72); + color: var(--text-muted); } .workbench-md-fence__summary::-webkit-details-marker { @@ -2901,7 +2877,7 @@ button.workbench-shortcut-row--action:focus-visible { } .workbench-md-fence__body { - border-top: 1px solid rgba(125, 145, 255, 0.1); + border-top: 1px solid var(--overlay-2); } .workbench-md-fence__body pre { @@ -2914,19 +2890,19 @@ button.workbench-shortcut-row--action:focus-visible { .workbench-agent-markdown blockquote { margin-left: 0; padding: 0.1rem 0 0.1rem 0.78rem; - border-left: 2px solid rgba(88, 166, 255, 0.42); - color: rgba(241, 242, 245, 0.76); - background: linear-gradient(90deg, rgba(88, 166, 255, 0.07), transparent 55%); + border-left: 2px solid var(--accent-soft); + color: var(--text-muted); + background: linear-gradient(90deg, var(--accent-soft), transparent 55%); } .workbench-agent-markdown a { - color: #8bc4ff; - text-decoration-color: rgba(139, 196, 255, 0.45); + color: var(--accent-cool); + text-decoration-color: var(--text-muted); } .workbench-agent-markdown a[href^="blxmemory:"] { - color: #c4a8ff; - text-decoration-color: rgba(196, 168, 255, 0.5); + color: var(--syntax-keyword); + text-decoration-color: var(--text-muted); } .workbench-agent-markdown table { @@ -2936,12 +2912,12 @@ button.workbench-shortcut-row--action:focus-visible { table-layout: fixed; border-radius: 0.42rem; overflow: hidden; - border: 1px solid rgba(255, 255, 255, 0.07); + border: 1px solid var(--overlay-2); } .workbench-agent-markdown th, .workbench-agent-markdown td { - border: 1px solid rgba(255, 255, 255, 0.07); + border: 1px solid var(--overlay-2); padding: 0.28rem 0.34rem; text-align: left; vertical-align: top; @@ -2949,8 +2925,8 @@ button.workbench-shortcut-row--action:focus-visible { } .workbench-agent-markdown th { - color: rgba(241, 242, 245, 0.96); - background: rgba(255, 255, 255, 0.055); + color: var(--text-muted); + background: var(--overlay-2); font-weight: 760; } @@ -2960,7 +2936,7 @@ button.workbench-shortcut-row--action:focus-visible { color: var(--syntax-type); margin: 0.3rem 0 0.3rem 1.1rem; padding: 0.12rem 0 0.12rem 0.65rem; - border-left: 1px solid rgba(99, 230, 190, 0.34); + border-left: 1px solid var(--success); background: transparent; } @@ -2977,9 +2953,9 @@ button.workbench-shortcut-row--action:focus-visible { margin: 0.55rem 0 0.2rem; padding: 0.45rem 0.7rem; border-radius: 8px; - background: rgba(120, 160, 255, 0.07); - border: 1px solid rgba(120, 160, 255, 0.18); - color: var(--text-muted, #9aa4b2); + background: var(--accent-soft); + border: 1px solid var(--accent-soft); + color: var(--text-muted, var(--text-muted)); font-size: 0.82rem; width: fit-content; } @@ -3048,7 +3024,7 @@ button.workbench-shortcut-row--action:focus-visible { } .agent-thinking-title--active .agent-thinking-title__label { - color: rgba(170, 200, 255, 0.95); + color: var(--text-muted); } .agent-thinking-title__chevron { @@ -3064,13 +3040,13 @@ button.workbench-shortcut-row--action:focus-visible { .agent-thinking-card__body { margin: 0.4rem 0 0; padding: 0.55rem 0.75rem; - background: rgba(20, 24, 34, 0.55); - border: 1px solid rgba(140, 160, 200, 0.16); + background: var(--overlay-2); + border: 1px solid var(--overlay-2); border-radius: 8px; font-family: var(--font-mono); font-size: 0.7rem; line-height: 1.55; - color: rgba(220, 226, 240, 0.78); + color: var(--text-muted); white-space: pre-wrap; word-break: break-word; max-height: 18rem; @@ -3078,8 +3054,8 @@ button.workbench-shortcut-row--action:focus-visible { } .agent-chat-line--thinking .agent-chat-body::before { - background: rgba(140, 160, 200, 0.7); - box-shadow: 0 0 0 3px rgba(140, 160, 200, 0.12); + background: var(--overlay-2); + box-shadow: 0 0 0 3px var(--overlay-2); } .agent-tool-list { @@ -3103,26 +3079,26 @@ button.workbench-shortcut-row--action:focus-visible { display: inline-block; max-width: 100%; border-radius: 0.5rem; - border: 1px solid rgba(255, 255, 255, 0.06); - background: rgba(255, 255, 255, 0.025); + border: 1px solid var(--overlay-2); + background: var(--overlay-1); transition: background 120ms ease, border-color 120ms ease; } .agent-tool-row:hover { - background: rgba(255, 255, 255, 0.045); + background: var(--overlay-1); } .agent-tool-row--pending { - border-color: rgba(140, 170, 255, 0.28); + border-color: var(--text-muted); } .agent-tool-row--ok { - border-color: rgba(99, 230, 190, 0.32); + border-color: var(--success); } .agent-tool-row--fail { - border-color: rgba(255, 120, 120, 0.42); - background: rgba(255, 80, 80, 0.06); + border-color: var(--text-muted); + background: var(--danger-soft); } .agent-tool-row__head { @@ -3150,27 +3126,27 @@ button.workbench-shortcut-row--action:focus-visible { width: 1rem; height: 1rem; border-radius: 0.28rem; - background: rgba(255, 255, 255, 0.05); - color: var(--syntax-type, #8fd9c4); + background: var(--overlay-1); + color: var(--syntax-type, var(--syntax-type)); flex: 0 0 auto; } .agent-tool-row--fail .agent-tool-row__icon { - color: #ff9aa2; - background: rgba(255, 120, 120, 0.12); + color: var(--danger); + background: var(--overlay-2); } .agent-tool-row__label { font-size: 0.72rem; font-weight: 600; - color: rgba(241, 242, 245, 0.92); + color: var(--text-muted); flex: 0 0 auto; } .agent-tool-row__arg { font-family: var(--font-mono); font-size: 0.66rem; - color: var(--text-faint, rgba(241, 242, 245, 0.55)); + color: var(--text-faint, var(--text-muted)); flex: 0 1 auto; min-width: 0; max-width: min(22rem, 52vw); @@ -3186,17 +3162,17 @@ button.workbench-shortcut-row--action:focus-visible { width: 1rem; height: 1rem; border-radius: 999px; - color: var(--syntax-type, #8fd9c4); + color: var(--syntax-type, var(--syntax-type)); flex: 0 0 auto; } .agent-tool-row--pending .agent-tool-row__status { - color: rgba(160, 180, 255, 0.85); + color: var(--accent-cool); animation: agent-tool-spin 1.4s linear infinite; } .agent-tool-row--fail .agent-tool-row__status { - color: #ff9aa2; + color: var(--danger); } .agent-tool-row__detail { @@ -3205,7 +3181,7 @@ button.workbench-shortcut-row--action:focus-visible { font-family: var(--font-mono); font-size: 0.66rem; line-height: 1.5; - color: rgba(241, 242, 245, 0.72); + color: var(--text-muted); white-space: pre-wrap; word-break: break-word; } @@ -3221,28 +3197,28 @@ button.workbench-shortcut-row--action:focus-visible { font-family: var(--font-mono); font-size: 0.62rem; font-weight: 600; - color: rgba(241, 242, 245, 0.9); - background: rgba(255, 255, 255, 0.08); - border: 1px solid rgba(255, 255, 255, 0.1); + color: var(--text); + background: var(--overlay-2); + border: 1px solid var(--overlay-3); flex: 0 0 auto; } .agent-tool-row--merged.agent-tool-row--pending .agent-tool-row__count { - color: rgba(160, 180, 255, 0.95); - background: rgba(140, 170, 255, 0.16); - border-color: rgba(140, 170, 255, 0.32); + color: var(--text-muted); + background: var(--overlay-2); + border-color: var(--text-muted); } .agent-tool-row--merged.agent-tool-row--ok .agent-tool-row__count { - color: rgba(143, 217, 196, 0.95); - background: rgba(99, 230, 190, 0.14); - border-color: rgba(99, 230, 190, 0.34); + color: var(--text-muted); + background: var(--success); + border-color: var(--success); } .agent-tool-row--merged.agent-tool-row--fail .agent-tool-row__count { - color: #ff9aa2; - background: rgba(255, 120, 120, 0.14); - border-color: rgba(255, 120, 120, 0.4); + color: var(--danger); + background: var(--overlay-2); + border-color: var(--text-muted); } .agent-chat-actions { @@ -3265,22 +3241,22 @@ button.workbench-shortcut-row--action:focus-visible { width: 1.5rem; height: 1.5rem; padding: 0; - border: 1px solid rgba(255, 255, 255, 0.08); + border: 1px solid var(--overlay-2); border-radius: 0.4rem; - background: rgba(255, 255, 255, 0.025); - color: rgba(241, 242, 245, 0.72); + background: var(--overlay-1); + color: var(--text-muted); cursor: pointer; transition: background 120ms ease, border-color 120ms ease, color 120ms ease; } .agent-chat-action:hover { - background: rgba(255, 255, 255, 0.06); - border-color: rgba(255, 255, 255, 0.16); - color: rgba(241, 242, 245, 0.95); + background: var(--overlay-2); + border-color: var(--text-muted); + color: var(--text-muted); } .agent-chat-action:focus-visible { - outline: 2px solid rgba(143, 217, 196, 0.6); + outline: 2px solid var(--overlay-2); outline-offset: 1px; } @@ -3291,24 +3267,24 @@ button.workbench-shortcut-row--action:focus-visible { gap: 0.32rem; padding: 0.42rem 0.6rem 0.32rem; margin-top: 0.18rem; - border-top: 1px solid rgba(255, 255, 255, 0.05); + border-top: 1px solid var(--overlay-1); font-family: var(--font-mono); font-size: 0.62rem; - color: var(--text-faint, rgba(241, 242, 245, 0.55)); + color: var(--text-faint, var(--text-muted)); } .agent-chat-usage strong { - color: rgba(241, 242, 245, 0.88); + color: var(--text-muted); font-weight: 600; } .agent-chat-usage__turns { - color: rgba(143, 217, 196, 0.85); + color: var(--text-muted); font-weight: 600; } .agent-chat-usage__sep { - color: rgba(241, 242, 245, 0.28); + color: var(--text-muted); } .agent-tool-row__chevron { @@ -3317,7 +3293,7 @@ button.workbench-shortcut-row--action:focus-visible { justify-content: center; width: 0.9rem; height: 0.9rem; - color: var(--text-faint, rgba(241, 242, 245, 0.55)); + color: var(--text-faint, var(--text-muted)); flex: 0 0 auto; } @@ -3336,13 +3312,13 @@ button.workbench-shortcut-row--action:focus-visible { align-items: center; gap: 0.42rem; font-size: 0.66rem; - color: rgba(241, 242, 245, 0.78); + color: var(--text-muted); } .agent-tool-row__sub-index { font-family: var(--font-mono); font-size: 0.6rem; - color: var(--text-faint, rgba(241, 242, 245, 0.45)); + color: var(--text-faint, var(--overlay-2)); } .agent-tool-row__sub-status { @@ -3351,22 +3327,22 @@ button.workbench-shortcut-row--action:focus-visible { justify-content: center; width: 0.9rem; height: 0.9rem; - color: var(--syntax-type, #8fd9c4); + color: var(--syntax-type, var(--syntax-type)); } .agent-tool-row__sub--pending .agent-tool-row__sub-status { - color: rgba(160, 180, 255, 0.85); + color: var(--accent-cool); animation: agent-tool-spin 1.4s linear infinite; } .agent-tool-row__sub--fail .agent-tool-row__sub-status { - color: #ff9aa2; + color: var(--danger); } .agent-tool-row__sub-arg { font-family: var(--font-mono); font-size: 0.66rem; - color: var(--text-faint, rgba(241, 242, 245, 0.62)); + color: var(--text-faint, var(--overlay-2)); min-width: 0; overflow: hidden; text-overflow: ellipsis; @@ -3380,7 +3356,7 @@ button.workbench-shortcut-row--action:focus-visible { .agent-subagent-card { margin-top: 0.28rem; font-size: 0.7rem; - color: rgba(241, 242, 245, 0.85); + color: var(--text-muted); } .agent-subagent-card__summary { @@ -3398,13 +3374,13 @@ button.workbench-shortcut-row--action:focus-visible { .agent-subagent-card__name { font-weight: 600; - color: rgba(241, 242, 245, 0.95); + color: var(--text-muted); } .agent-subagent-card__status { font-family: var(--font-mono); font-size: 0.62rem; - color: var(--text-faint, rgba(241, 242, 245, 0.55)); + color: var(--text-faint, var(--text-muted)); text-transform: lowercase; } @@ -3419,7 +3395,7 @@ button.workbench-shortcut-row--action:focus-visible { width: 0.28rem; height: 0.28rem; border-radius: 50%; - background: rgba(160, 180, 255, 0.85); + background: var(--accent-cool); animation: agent-subagent-pulse 1.1s ease-in-out infinite; } @@ -3439,14 +3415,14 @@ button.workbench-shortcut-row--action:focus-visible { .agent-subagent-card__thinking { margin: 0.18rem 0 0.24rem 0.9rem; font-size: 0.66rem; - color: rgba(160, 180, 255, 0.78); + color: var(--text-muted); } .agent-subagent-card__thinking summary { cursor: pointer; list-style: none; font-style: italic; - color: rgba(160, 180, 255, 0.85); + color: var(--accent-cool); } .agent-subagent-card__thinking summary::-webkit-details-marker { @@ -3454,7 +3430,7 @@ button.workbench-shortcut-row--action:focus-visible { } .agent-subagent-card__thinking--done summary { - color: rgba(241, 242, 245, 0.55); + color: var(--text-muted); } .agent-subagent-card__thinking-body { @@ -3463,9 +3439,9 @@ button.workbench-shortcut-row--action:focus-visible { font-family: var(--font-mono); font-size: 0.64rem; line-height: 1.45; - color: rgba(241, 242, 245, 0.7); - background: rgba(140, 170, 255, 0.06); - border-left: 2px solid rgba(140, 170, 255, 0.32); + color: var(--text-muted); + background: var(--overlay-2); + border-left: 2px solid var(--overlay-2); white-space: pre-wrap; word-break: break-word; } @@ -3476,8 +3452,8 @@ button.workbench-shortcut-row--action:focus-visible { font-family: var(--font-mono); font-size: 0.66rem; line-height: 1.5; - color: rgba(241, 242, 245, 0.82); - background: rgba(255, 255, 255, 0.025); + color: var(--text-muted); + background: var(--overlay-1); border-radius: 0.32rem; white-space: pre-wrap; word-break: break-word; @@ -3487,7 +3463,7 @@ button.workbench-shortcut-row--action:focus-visible { margin: 0.18rem 0 0.24rem 0.9rem; font-size: 0.68rem; line-height: 1.45; - color: rgba(241, 242, 245, 0.72); + color: var(--text-muted); white-space: pre-wrap; } @@ -3500,7 +3476,7 @@ button.workbench-shortcut-row--action:focus-visible { gap: 0.12rem; font-family: var(--font-mono); font-size: 0.64rem; - color: rgba(241, 242, 245, 0.72); + color: var(--text-muted); } .agent-subagent-card__tools > li { @@ -3520,9 +3496,9 @@ button.workbench-shortcut-row--action:focus-visible { font-family: var(--font-mono); font-size: 0.58rem; font-weight: 600; - color: rgba(143, 217, 196, 0.95); - background: rgba(99, 230, 190, 0.12); - border: 1px solid rgba(99, 230, 190, 0.3); + color: var(--text-muted); + background: var(--accent-cool-soft); + border: 1px solid var(--success); } .agent-tool-row__sub-detail { @@ -3532,7 +3508,7 @@ button.workbench-shortcut-row--action:focus-visible { font-family: var(--font-mono); font-size: 0.62rem; line-height: 1.45; - color: rgba(241, 242, 245, 0.65); + color: var(--text-muted); white-space: pre-wrap; word-break: break-word; } @@ -3578,22 +3554,22 @@ button.workbench-shortcut-row--action:focus-visible { width: 1.05rem; height: 1.05rem; padding: 0; - border: 1px solid rgba(255, 255, 255, 0.14); + border: 1px solid var(--overlay-4); border-radius: 0.28rem; - background: rgba(255, 255, 255, 0.04); + background: var(--overlay-1); color: var(--accent); cursor: pointer; transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease; } .agent-chat-tts-btn:hover { - background: rgba(88, 166, 255, 0.12); - border-color: rgba(88, 166, 255, 0.35); - color: #9cd2ff; + background: var(--accent-cool-soft); + border-color: var(--accent-soft); + color: var(--accent-cool); } .agent-chat-tts-btn:focus-visible { - outline: 2px solid rgba(88, 166, 255, 0.55); + outline: 2px solid var(--accent-soft); outline-offset: 1px; } @@ -3601,7 +3577,7 @@ button.workbench-shortcut-row--action:focus-visible { position: relative; min-width: 0; padding-left: 0.74rem; - border-left: 1px solid rgba(255, 255, 255, 0.2); + border-left: 1px solid var(--overlay-5); } .agent-chat-body::before { @@ -3613,7 +3589,7 @@ button.workbench-shortcut-row--action:focus-visible { height: 0.36rem; border-radius: 50%; background: var(--accent); - box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.13); + box-shadow: 0 0 0 3px var(--accent-soft); } .agent-chat-line strong { @@ -3626,7 +3602,7 @@ button.workbench-shortcut-row--action:focus-visible { .agent-chat-line p { margin: 0; - color: rgba(241, 242, 245, 0.84); + color: var(--text-muted); font-size: 0.76rem; line-height: 1.5; } @@ -3636,11 +3612,11 @@ button.workbench-shortcut-row--action:focus-visible { } .agent-chat-line--user strong { - color: #ffd166; + color: var(--warning); } .agent-chat-line--user .agent-chat-body { - border-left-color: rgba(88, 166, 255, 0.32); + border-left-color: var(--accent-soft); } .agent-chat-line--user .agent-chat-body::before { @@ -3648,16 +3624,16 @@ button.workbench-shortcut-row--action:focus-visible { } .agent-chat-line--tool strong { - color: rgba(99, 230, 190, 0.88); + color: var(--success); } .agent-chat-line--tool .agent-chat-body { - border-left-color: rgba(99, 230, 190, 0.22); + border-left-color: var(--success); } .agent-chat-line--tool .agent-chat-body::before { - background: #63e6be; - box-shadow: 0 0 0 3px rgba(99, 230, 190, 0.12); + background: var(--syntax-type); + box-shadow: 0 0 0 3px var(--accent-cool-soft); } .agent-compose { @@ -3815,8 +3791,8 @@ button.workbench-shortcut-row--action:focus-visible { -45deg, var(--bg-raised), var(--bg-raised) 8px, - rgba(255, 255, 255, 0.02) 8px, - rgba(255, 255, 255, 0.02) 16px + var(--overlay-1) 8px, + var(--overlay-1) 16px ); } @@ -3929,7 +3905,7 @@ button.workbench-shortcut-row--action:focus-visible { inset: 0; border: none; padding: 0; - background: rgba(6, 6, 8, 0.55); + background: var(--overlay-2); backdrop-filter: blur(3px); cursor: pointer; } @@ -3945,7 +3921,7 @@ button.workbench-shortcut-row--action:focus-visible { border-radius: 4px; border: 1px solid var(--border-strong); background: var(--bg-panel); - box-shadow: 0 22px 50px rgba(0, 0, 0, 0.5); + box-shadow: 0 22px 50px var(--scrim-bg); padding: 0.65rem; } @@ -4019,7 +3995,7 @@ button.workbench-shortcut-row--action:focus-visible { } .harness-cmd-btn:hover { - background: rgba(255, 255, 255, 0.04); + background: var(--overlay-1); } .harness-cmd-btn--active { @@ -4054,7 +4030,7 @@ button.workbench-shortcut-row--action:focus-visible { border-radius: 4px; border: 1px solid var(--border-strong); background: var(--bg-panel); - box-shadow: 0 22px 50px rgba(0, 0, 0, 0.5); + box-shadow: 0 22px 50px var(--scrim-bg); } .harness-settings-head { @@ -4284,7 +4260,7 @@ button.workbench-shortcut-row--action:focus-visible { margin: 0.45rem 0 0; font-size: 0.76rem; line-height: 1.5; - color: #ff7a7a; + color: var(--text-muted); white-space: pre-wrap; } @@ -4293,7 +4269,7 @@ button.workbench-shortcut-row--action:focus-visible { padding: 1.05rem 1.1rem; border: 1px solid var(--border); border-radius: 0.7rem; - background: var(--bg-raised, rgba(255, 255, 255, 0.018)); + background: var(--bg-raised, var(--overlay-2)); display: flex; flex-direction: column; gap: 0.65rem; @@ -4392,7 +4368,7 @@ button.workbench-shortcut-row--action:focus-visible { display: grid; place-items: center; border-radius: 999px; - background: rgba(255, 255, 255, 0.92); + background: var(--overlay-2); overflow: hidden; } @@ -4420,7 +4396,7 @@ button.workbench-shortcut-row--action:focus-visible { border-radius: 6px; border: 1px solid var(--border-strong); background: var(--bg-raised); - box-shadow: 0 12px 26px rgba(0, 0, 0, 0.34); + box-shadow: 0 12px 26px var(--scrim-bg); } .harness-provider-option { @@ -4505,7 +4481,7 @@ button.workbench-shortcut-row--action:focus-visible { .workbench-plain-pre { font-family: var(--font-mono); font-size: 0.68rem; - background: rgba(0, 0, 0, 0.25); + background: var(--scrim-bg); border-radius: 4px; padding: 0.45rem; overflow: auto; @@ -4558,8 +4534,8 @@ button.workbench-shortcut-row--action:focus-visible { } .harness-hooks__item:hover { - border-color: var(--border-strong, rgba(255, 255, 255, 0.12)); - background: rgba(255, 255, 255, 0.035); + border-color: var(--border-strong); + background: var(--overlay-1); } .harness-hooks__main { @@ -4577,8 +4553,8 @@ button.workbench-shortcut-row--action:focus-visible { display: grid; place-items: center; border-radius: 999px; - background: rgba(255, 255, 255, 0.92); - border: 1px solid rgba(255, 255, 255, 0.08); + background: var(--hook-brand-bg); + border: 1px solid var(--border); overflow: hidden; } @@ -4591,7 +4567,7 @@ button.workbench-shortcut-row--action:focus-visible { .harness-hooks__fallback { display: grid; place-items: center; - color: #14161d; + color: var(--hook-brand-fg); } .harness-hooks__copy { @@ -4627,16 +4603,16 @@ button.workbench-shortcut-row--action:focus-visible { padding: 0.2rem; border-radius: 4px; cursor: default; - border: 1px solid rgba(255, 122, 122, 0.32); - color: #ff9a9a; - background: rgba(255, 122, 122, 0.08); + border: 1px solid var(--danger-border-soft); + color: var(--danger-text-soft); + background: var(--danger-surface); font-size: 0.72rem; } .harness-hooks__status--ok { - border-color: rgba(104, 220, 159, 0.28); - color: #86e7b2; - background: rgba(104, 220, 159, 0.08); + border-color: var(--success-border); + color: var(--success-text); + background: var(--success-surface); } .workbench-plain-input { @@ -4652,8 +4628,8 @@ button.workbench-shortcut-row--action:focus-visible { } .workbench-plain-input:focus { outline: none; - border-color: rgba(88, 166, 255, 0.55); - box-shadow: 0 0 0 2px rgba(88, 166, 255, 0.18); + border-color: var(--accent-soft); + box-shadow: 0 0 0 2px var(--accent-soft); } @media (max-width: 720px) { @@ -4696,7 +4672,7 @@ button.workbench-shortcut-row--action:focus-visible { .eula-scrim { position: absolute; inset: 0; - background: rgba(18, 18, 22, 0.62); + background: var(--scrim-bg); backdrop-filter: blur(4px); animation: eula-scrim-enter 0.25s ease-out both; } @@ -4737,7 +4713,7 @@ button.workbench-shortcut-row--action:focus-visible { background: var(--bg-panel); color: var(--text); border: 1px solid var(--border-strong); - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(255, 255, 255, 0.04); + box-shadow: 0 12px 40px var(--scrim-bg), 0 0 0 1px var(--overlay-1); overflow: hidden; animation: eula-sheet-enter 0.28s ease-out both; } @@ -4921,7 +4897,7 @@ button.workbench-shortcut-row--action:focus-visible { } .eula-btn--primary { - color: #0e0e10; + color: var(--on-accent); background: var(--accent); border-color: var(--accent); } @@ -5028,12 +5004,12 @@ button.workbench-shortcut-row--action:focus-visible { background: radial-gradient( ellipse 120% 80% at 50% -20%, - rgba(129, 180, 255, 0.12), + var(--overlay-2), transparent 55% ), - rgba(0, 0, 0, 0.2); + var(--scrim-bg); text-align: center; - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06); + box-shadow: inset 0 1px 0 var(--overlay-2); } .auth-device-code-copy { @@ -5048,7 +5024,7 @@ button.workbench-shortcut-row--action:focus-visible { padding: 0; border-radius: 4px; border: 1px solid var(--border); - background: rgba(0, 0, 0, 0.35); + background: var(--scrim-bg); color: var(--accent); cursor: pointer; transition: @@ -5120,7 +5096,7 @@ button.workbench-shortcut-row--action:focus-visible { .auth-login-error { margin: 0; font-size: 0.86rem; - color: #e57373; + color: var(--danger); } input, @@ -5162,10 +5138,10 @@ button { flex: 0 0 auto; min-width: 0; padding: 0.32rem 0.35rem 0; - border-bottom: 1px solid rgba(255, 255, 255, 0.07); + border-bottom: 1px solid var(--overlay-2); background: - linear-gradient(180deg, rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0)), - rgba(0, 0, 0, 0.18); + linear-gradient(180deg, var(--overlay-1), var(--overlay-2)), + var(--scrim-bg); } .workspace-center-tabs__strip { @@ -5201,14 +5177,14 @@ button { .workspace-center-tab:hover { color: var(--text); - background: rgba(255, 255, 255, 0.055); + background: var(--overlay-2); } .workspace-center-tab--active { color: var(--accent); - border-color: rgba(88, 166, 255, 0.32); - background: rgba(88, 166, 255, 0.11); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04); + border-color: var(--accent-soft); + background: var(--accent-soft); + box-shadow: inset 0 1px 0 var(--overlay-1); } .workspace-center-tab__icon, @@ -5237,7 +5213,7 @@ button { .workspace-center-tab__close:hover, .workspace-center-tab__close:focus-visible { color: var(--text); - background: rgba(255, 255, 255, 0.1); + background: var(--overlay-3); } .workspace-center-tab-body { @@ -5269,7 +5245,7 @@ button { flex: 1 1 0; min-height: 0; height: 100%; - background: rgba(0, 0, 0, 0.12); + background: var(--scrim-bg); } .harness-settings-grid--docked .harness-settings-detail { @@ -5282,7 +5258,7 @@ button { min-height: 0; display: flex; flex-direction: column; - background: rgba(8, 10, 14, 0.62); + background: var(--overlay-2); } .file-preview__header { @@ -5294,7 +5270,7 @@ button { min-height: 42px; padding: 0.45rem 0.65rem; border-bottom: 1px solid var(--border); - background: rgba(0, 0, 0, 0.2); + background: var(--scrim-bg); } .file-preview__title { @@ -5327,17 +5303,17 @@ button { } .file-preview__status--error { - color: #ff8c8c; + color: var(--text-muted); } .file-preview__notice { flex: 0 0 auto; margin-bottom: 0; padding: 0.48rem 0.58rem; - border: 1px solid rgba(251, 191, 36, 0.28); + border: 1px solid var(--overlay-2); border-radius: 4px; - background: rgba(251, 191, 36, 0.08); - color: #f6d58a; + background: var(--overlay-2); + color: var(--text-muted); } .file-preview__content { @@ -5350,7 +5326,7 @@ button { font-size: 0.78rem; line-height: 1.55; white-space: pre; - color: #d9e2f1; + color: var(--text-muted); background: transparent; } @@ -5366,7 +5342,7 @@ button { .ws-term-grid__resize:hover, .ws-term-grid__resize:focus-visible { opacity: 1; - background: rgba(114, 160, 255, 0.28); + background: var(--accent-soft); } .ws-term-grid__resize--col { @@ -5413,77 +5389,77 @@ button { border: 1px solid var(--border); border-radius: 4px; background: - linear-gradient(180deg, rgba(18, 19, 24, 0.88), rgba(11, 12, 16, 0.9)), - rgba(14, 15, 20, 0.92); + linear-gradient(180deg, var(--bg-panel), var(--bg-raised)), + var(--bg-panel); backdrop-filter: blur(8px); overflow: hidden; } .ws-term-cell--active { - border-color: rgba(114, 160, 255, 0.9); + border-color: var(--accent); box-shadow: - inset 0 0 0 1px rgba(114, 160, 255, 0.4), - 0 0 0 1px rgba(114, 160, 255, 0.18); + inset 0 0 0 1px var(--accent-soft), + 0 0 0 1px var(--accent-soft); } .ws-term-cell--active.ws-term-cell--agent-claude { - border-color: rgba(232, 149, 74, 0.92); + border-color: color-mix(in srgb, var(--agent-claude) 92%, transparent); box-shadow: - inset 0 0 0 1px rgba(232, 149, 74, 0.42), - 0 0 0 1px rgba(232, 149, 74, 0.2); + inset 0 0 0 1px color-mix(in srgb, var(--agent-claude) 42%, transparent), + 0 0 0 1px color-mix(in srgb, var(--agent-claude) 20%, transparent); } .ws-term-cell--active.ws-term-cell--agent-codex { - border-color: rgba(61, 184, 168, 0.92); + border-color: color-mix(in srgb, var(--agent-codex) 92%, transparent); box-shadow: - inset 0 0 0 1px rgba(61, 184, 168, 0.42), - 0 0 0 1px rgba(61, 184, 168, 0.2); + inset 0 0 0 1px color-mix(in srgb, var(--agent-codex) 42%, transparent), + 0 0 0 1px color-mix(in srgb, var(--agent-codex) 20%, transparent); } .ws-term-cell--active.ws-term-cell--agent-gemini { - border-color: rgba(91, 156, 245, 0.92); + border-color: color-mix(in srgb, var(--agent-gemini) 92%, transparent); box-shadow: - inset 0 0 0 1px rgba(91, 156, 245, 0.42), - 0 0 0 1px rgba(91, 156, 245, 0.2); + inset 0 0 0 1px color-mix(in srgb, var(--agent-gemini) 42%, transparent), + 0 0 0 1px color-mix(in srgb, var(--agent-gemini) 20%, transparent); } .ws-term-cell--active.ws-term-cell--agent-opencode { - border-color: rgba(166, 124, 240, 0.92); + border-color: color-mix(in srgb, var(--agent-copilot) 92%, transparent); box-shadow: - inset 0 0 0 1px rgba(166, 124, 240, 0.42), - 0 0 0 1px rgba(166, 124, 240, 0.2); + inset 0 0 0 1px color-mix(in srgb, var(--agent-copilot) 42%, transparent), + 0 0 0 1px color-mix(in srgb, var(--agent-copilot) 20%, transparent); } .ws-term-cell--active.ws-term-cell--agent-cursor { - border-color: rgba(94, 207, 122, 0.92); + border-color: color-mix(in srgb, var(--agent-cursor) 92%, transparent); box-shadow: - inset 0 0 0 1px rgba(94, 207, 122, 0.42), - 0 0 0 1px rgba(94, 207, 122, 0.2); + inset 0 0 0 1px color-mix(in srgb, var(--agent-cursor) 42%, transparent), + 0 0 0 1px color-mix(in srgb, var(--agent-cursor) 20%, transparent); } .ws-term-cell--active .ws-term-cell__head { - border-color: rgba(114, 160, 255, 0.55); - background: rgba(24, 26, 34, 0.96); + border-color: var(--accent-soft); + background: var(--bg-panel-header); } .ws-term-cell--active.ws-term-cell--agent-claude .ws-term-cell__head { - border-color: rgba(232, 149, 74, 0.55); + border-color: color-mix(in srgb, var(--agent-claude) 55%, transparent); } .ws-term-cell--active.ws-term-cell--agent-codex .ws-term-cell__head { - border-color: rgba(61, 184, 168, 0.55); + border-color: color-mix(in srgb, var(--agent-codex) 55%, transparent); } .ws-term-cell--active.ws-term-cell--agent-gemini .ws-term-cell__head { - border-color: rgba(91, 156, 245, 0.55); + border-color: color-mix(in srgb, var(--agent-gemini) 55%, transparent); } .ws-term-cell--active.ws-term-cell--agent-opencode .ws-term-cell__head { - border-color: rgba(166, 124, 240, 0.55); + border-color: color-mix(in srgb, var(--agent-copilot) 55%, transparent); } .ws-term-cell--active.ws-term-cell--agent-cursor .ws-term-cell__head { - border-color: rgba(94, 207, 122, 0.55); + border-color: color-mix(in srgb, var(--agent-cursor) 55%, transparent); } /* Pulse the titlebar of any terminal that has unread notifications so the @@ -5494,42 +5470,42 @@ button { * but the focused-and-unread combination is also covered by the * `--active` styles above which take priority on `border-color`. */ .ws-term-cell--has-unread .ws-term-cell__head { - border-color: rgba(232, 149, 74, 0.85); - background: rgba(38, 28, 20, 0.96); + border-color: var(--text-muted); + background: var(--overlay-2); animation: ws-term-head-pulse 1.4s ease-in-out infinite; } .ws-term-cell--has-unread.ws-term-cell--agent-claude .ws-term-cell__head { - border-color: rgba(232, 149, 74, 0.85); - box-shadow: 0 0 0 1px rgba(232, 149, 74, 0.35); + border-color: var(--text-muted); + box-shadow: 0 0 0 1px var(--overlay-2); } .ws-term-cell--has-unread.ws-term-cell--agent-codex .ws-term-cell__head { - border-color: rgba(61, 184, 168, 0.85); - box-shadow: 0 0 0 1px rgba(61, 184, 168, 0.35); + border-color: var(--text-muted); + box-shadow: 0 0 0 1px var(--overlay-2); } .ws-term-cell--has-unread.ws-term-cell--agent-gemini .ws-term-cell__head { - border-color: rgba(91, 156, 245, 0.85); - box-shadow: 0 0 0 1px rgba(91, 156, 245, 0.35); + border-color: var(--text-muted); + box-shadow: 0 0 0 1px var(--overlay-2); } .ws-term-cell--has-unread.ws-term-cell--agent-opencode .ws-term-cell__head { - border-color: rgba(166, 124, 240, 0.85); - box-shadow: 0 0 0 1px rgba(166, 124, 240, 0.35); + border-color: var(--text-muted); + box-shadow: 0 0 0 1px var(--overlay-2); } .ws-term-cell--has-unread.ws-term-cell--agent-cursor .ws-term-cell__head { - border-color: rgba(94, 207, 122, 0.85); - box-shadow: 0 0 0 1px rgba(94, 207, 122, 0.35); + border-color: var(--text-muted); + box-shadow: 0 0 0 1px var(--overlay-2); } @keyframes ws-term-head-pulse { 0%, 100% { - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.28), 0 0 0 1px rgba(232, 149, 74, 0.15); + box-shadow: 0 1px 3px var(--scrim-bg), 0 0 0 1px var(--overlay-2); } 50% { - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.28), 0 0 0 3px rgba(232, 149, 74, 0.45); + box-shadow: 0 1px 3px var(--scrim-bg), 0 0 0 3px var(--overlay-2); } } @@ -5544,8 +5520,8 @@ button { font-size: 0.72rem; border: 1px solid var(--border); border-radius: 4px; - background: rgba(20, 22, 28, 0.92); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.28); + background: var(--overlay-2); + box-shadow: 0 1px 3px var(--scrim-bg); } .ws-term-cell__title { @@ -5581,8 +5557,8 @@ button { padding: 0.05rem 0.35rem; border-radius: 4px; font-size: 0.62rem; - background: rgba(120, 90, 220, 0.25); - color: #c9b8ff; + background: var(--overlay-2); + color: var(--text-muted); } .ws-term-cell__tool { @@ -5603,14 +5579,14 @@ button { .ws-term-cell__tool:focus-visible { color: var(--text); border-color: var(--border); - background: rgba(255, 255, 255, 0.06); + background: var(--overlay-2); } .ws-term-cell__tool--danger:hover, .ws-term-cell__tool--danger:focus-visible { - color: #ffb4a8; - border-color: rgba(255, 120, 96, 0.35); - background: rgba(255, 80, 64, 0.1); + color: var(--danger); + border-color: var(--text-muted); + background: var(--danger-soft); } .ws-term-cell__slot { @@ -5623,9 +5599,9 @@ button { padding: 0.35rem 0.5rem; font-size: 0.72rem; line-height: 1.35; - color: #e57373; + color: var(--danger); border-bottom: 1px solid var(--border); - background: rgba(229, 115, 115, 0.06); + background: var(--overlay-2); } .ws-term-cell__xterm { @@ -5634,14 +5610,14 @@ button { min-width: 0; margin: 0 8px 8px; border-radius: 4px; - background: rgba(8, 9, 12, 0.74); + background: var(--overlay-2); overflow: hidden; } .ws-term-cell__xterm .xterm { height: 100%; width: 100%; - background: rgba(8, 9, 12, 0.42) !important; + background: var(--term-surface) !important; } .ws-term-cell__xterm .xterm-viewport, @@ -5659,7 +5635,7 @@ button { border-radius: 6px; border: 1px solid var(--border-strong); background: var(--bg-panel); - box-shadow: 0 22px 50px rgba(0, 0, 0, 0.5); + box-shadow: 0 22px 50px var(--scrim-bg); } .wz-head { @@ -5704,7 +5680,7 @@ button { .wz-error { margin: 0.35rem 0 0; font-size: 0.78rem; - color: #e57373; + color: var(--danger); } /* --- Virtual directory browser (cwd input dropdown) --- */ @@ -5724,7 +5700,7 @@ button { background: var(--bg-panel); border: 1px solid var(--border-strong); border-radius: 4px; - box-shadow: 0 8px 22px rgba(0, 0, 0, 0.45); + box-shadow: 0 8px 22px var(--scrim-bg); overflow: hidden; } @@ -5825,14 +5801,14 @@ button { padding: 0.45rem 0.25rem; border-radius: 4px; border: 1px solid var(--border); - background: rgba(255, 255, 255, 0.02); + background: var(--overlay-1); cursor: pointer; color: var(--text-muted); font: inherit; } .wz-template-card:hover { - background: rgba(255, 255, 255, 0.05); + background: var(--overlay-1); } .wz-template-card--active { @@ -5873,7 +5849,7 @@ button { } .wz-danger { - color: #e57373 !important; + color: var(--danger) !important; } .wz-agent-list { @@ -5893,7 +5869,7 @@ button { padding: 0.45rem 0.5rem; border: 1px solid var(--border); border-radius: 4px; - background: rgba(255, 255, 255, 0.02); + background: var(--overlay-1); } .wz-agent-row__meta { @@ -5921,7 +5897,7 @@ button { padding: 0; border-radius: 4px; border: 1px solid var(--border); - background: rgba(255, 255, 255, 0.04); + background: var(--overlay-1); color: var(--text); cursor: pointer; font: inherit; @@ -5937,7 +5913,7 @@ button { padding: 0.65rem; border-radius: 4px; border: 1px solid var(--border); - background: rgba(255, 255, 255, 0.03); + background: var(--overlay-1); align-self: start; } @@ -5988,7 +5964,7 @@ button { overflow: auto; background: radial-gradient( ellipse at top, - rgba(255, 255, 255, 0.025), + var(--overlay-1), transparent 60% ); } @@ -6027,13 +6003,13 @@ button { } .ws-config__step--active { - color: var(--text-bright, #fff); - background: rgba(255, 255, 255, 0.05); + color: var(--text-bright, var(--text-bright)); + background: var(--overlay-1); border-color: var(--border-strong); } .ws-config__step--done { - color: var(--text-bright, #fff); + color: var(--text-bright, var(--text-bright)); opacity: 0.85; } @@ -6044,20 +6020,20 @@ button { align-items: center; justify-content: center; border-radius: 3px; - background: rgba(255, 255, 255, 0.08); + background: var(--overlay-2); color: var(--text-muted); font-weight: 700; font-size: 0.72rem; } .ws-config__step--active .ws-config__step-num { - background: var(--text-bright, #fff); - color: #000; + background: var(--text-bright, var(--text-bright)); + color: var(--bg-app); } .ws-config__step--done .ws-config__step-num { - background: rgba(255, 255, 255, 0.18); - color: var(--text-bright, #fff); + background: var(--overlay-2); + color: var(--text-bright, var(--text-bright)); } .ws-config__step-sep { @@ -6100,8 +6076,8 @@ button { .ws-config__field { width: 100%; - background: var(--bg-elevated, rgba(255, 255, 255, 0.025)); - color: var(--text-bright, #fff); + background: var(--bg-elevated, var(--overlay-1)); + color: var(--text-bright, var(--text-bright)); border: 1px solid var(--border); border-radius: 4px; padding: 0.65rem 0.85rem; @@ -6118,8 +6094,8 @@ button { .ws-config__field:focus { border-color: var(--border-strong); - background: rgba(255, 255, 255, 0.04); - box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.05); + background: var(--overlay-1); + box-shadow: 0 0 0 3px var(--overlay-1); } .ws-config__cwd { @@ -6145,7 +6121,7 @@ button { .ws-config__error { margin: 0; font-size: 0.78rem; - color: #e57373; + color: var(--danger); } .ws-config__hint { @@ -6167,22 +6143,22 @@ button { align-items: center; gap: 0.4rem; padding: 0.65rem 0.5rem 0.5rem; - background: var(--bg-elevated, rgba(255, 255, 255, 0.025)); + background: var(--bg-elevated, var(--overlay-1)); border: 1px solid var(--border); border-radius: 4px; - color: var(--text-bright, #fff); + color: var(--text-bright, var(--text-bright)); cursor: pointer; transition: border-color 120ms, background 120ms, transform 120ms; } .ws-config__layout-card:hover { - background: rgba(255, 255, 255, 0.05); + background: var(--overlay-1); border-color: var(--border-strong); } .ws-config__layout-card--active { - border-color: var(--text-bright, #fff); - background: rgba(255, 255, 255, 0.07); + border-color: var(--text-bright, var(--text-bright)); + background: var(--overlay-2); } .ws-config__layout-mini { @@ -6203,14 +6179,14 @@ button { } .ws-config__layout-card--active .ws-config__layout-cell { - background: var(--text-bright, #fff); + background: var(--text-bright, var(--text-bright)); opacity: 1; } .ws-config__layout-num { font-size: 0.85rem; font-weight: 600; - color: var(--text-bright, #fff); + color: var(--text-bright, var(--text-bright)); } /* --- fleet step --- */ @@ -6242,23 +6218,23 @@ button { background: transparent; border: 1px solid var(--border); border-radius: 4px; - color: var(--text-bright, #fff); + color: var(--text-bright, var(--text-bright)); cursor: pointer; transition: background 120ms, border-color 120ms; } .ws-config__chip:hover { - background: rgba(255, 255, 255, 0.05); + background: var(--overlay-1); border-color: var(--border-strong); } .ws-config__chip--danger { - color: #e57373; - border-color: rgba(229, 115, 115, 0.4); + color: var(--danger); + border-color: var(--text-muted); } .ws-config__chip--danger:hover { - background: rgba(229, 115, 115, 0.08); + background: var(--overlay-2); } .ws-config__agent-list { @@ -6275,7 +6251,7 @@ button { align-items: center; gap: 0.75rem; padding: 0.6rem 0.8rem; - background: var(--bg-elevated, rgba(255, 255, 255, 0.025)); + background: var(--bg-elevated, var(--overlay-1)); border: 1px solid var(--border); border-radius: 4px; } @@ -6307,7 +6283,7 @@ button { border-radius: 4px; border: 1px solid var(--border); background: transparent; - color: var(--text-bright, #fff); + color: var(--text-bright, var(--text-bright)); font-size: 0.95rem; cursor: pointer; display: inline-flex; @@ -6316,7 +6292,7 @@ button { } .ws-config__stepper:hover { - background: rgba(255, 255, 255, 0.05); + background: var(--overlay-1); border-color: var(--border-strong); } @@ -6346,25 +6322,25 @@ button { border-radius: 4px; border: 1px solid var(--border); background: transparent; - color: var(--text-bright, #fff); + color: var(--text-bright, var(--text-bright)); cursor: pointer; transition: background 120ms, border-color 120ms, opacity 120ms; } .ws-config__btn--ghost:hover { - background: rgba(255, 255, 255, 0.05); + background: var(--overlay-1); border-color: var(--border-strong); } .ws-config__btn--primary { - background: var(--text-bright, #fff); - color: #000; - border-color: var(--text-bright, #fff); + background: var(--text-bright, var(--text-bright)); + color: var(--bg-app); + border-color: var(--text-bright, var(--text-bright)); font-weight: 600; } .ws-config__btn--primary:hover:not(:disabled) { - background: #f0f0f0; + background: var(--text-muted); } .ws-config__btn:disabled { @@ -6382,12 +6358,12 @@ button { background: transparent; border: 1px solid transparent; border-radius: 4px; - color: var(--text-bright, #fff); + color: var(--text-bright, var(--text-bright)); cursor: pointer; } .wz-cwd-browser__tool:hover { - background: rgba(255, 255, 255, 0.06); + background: var(--overlay-2); border-color: var(--border); } @@ -6406,13 +6382,13 @@ button { gap: 0.3rem; padding: 0.4rem 0.5rem; border-bottom: 1px solid var(--border); - background: rgba(255, 255, 255, 0.02); + background: var(--overlay-1); } .wz-cwd-browser__newinput { flex: 1; background: var(--bg-panel); - color: var(--text-bright, #fff); + color: var(--text-bright, var(--text-bright)); border: 1px solid var(--border); border-radius: 4px; padding: 0.3rem 0.5rem; @@ -6431,8 +6407,8 @@ button { padding: 0.3rem 0.6rem; border-radius: 4px; border: 1px solid var(--border); - background: var(--text-bright, #fff); - color: #000; + background: var(--text-bright, var(--text-bright)); + color: var(--bg-app); cursor: pointer; font-weight: 600; } @@ -6451,8 +6427,8 @@ button { margin: 0; padding: 0.3rem 0.6rem; font-size: 0.75rem; - color: #e57373; - background: rgba(229, 115, 115, 0.08); + color: var(--danger); + background: var(--overlay-2); border-bottom: 1px solid var(--border); } @@ -6525,7 +6501,7 @@ button { margin: 0; font-size: 0.95rem; font-weight: 600; - color: rgba(238, 239, 245, 0.92); + color: var(--text); } .workbench-right-memory__empty-lead { @@ -6533,7 +6509,7 @@ button { max-width: 22rem; font-size: 0.82rem; line-height: 1.5; - color: rgba(190, 194, 208, 0.78); + color: var(--text-muted); } .workbench-right-memory__create-btn { @@ -6541,21 +6517,21 @@ button { padding: 0.45rem 0.95rem; font: inherit; font-size: 0.85rem; - color: rgba(238, 239, 245, 0.95); - background: rgba(255, 255, 255, 0.06); - border: 1px solid rgba(255, 255, 255, 0.14); + color: var(--text); + background: var(--overlay-2); + border: 1px solid var(--overlay-4); border-radius: 6px; cursor: pointer; transition: background 120ms ease, border-color 120ms ease; } .workbench-right-memory__create-btn:hover { - background: rgba(255, 255, 255, 0.1); - border-color: rgba(255, 255, 255, 0.22); + background: var(--overlay-3); + border-color: var(--text-muted); } .workbench-right-memory__create-btn:focus-visible { - outline: 2px solid var(--accent, #5b9dff); + outline: 2px solid var(--accent, var(--accent-hover)); outline-offset: 2px; } @@ -6572,7 +6548,7 @@ button { min-height: 0; height: 100%; position: relative; - color: rgba(238, 239, 245, 0.92); + color: var(--text); font-size: 0.85rem; } @@ -6580,7 +6556,7 @@ button { display: flex; gap: 0.15rem; padding: 0.4rem 0.5rem 0; - border-bottom: 1px solid rgba(255, 255, 255, 0.06); + border-bottom: 1px solid var(--overlay-2); } .workbench-memory__tab { @@ -6590,17 +6566,17 @@ button { padding: 0.4rem 0.65rem; font: inherit; font-size: 0.78rem; - color: rgba(190, 194, 208, 0.78); + color: var(--text-muted); background: transparent; border: none; border-bottom: 2px solid transparent; cursor: pointer; transition: color 120ms ease, border-color 120ms ease; } -.workbench-memory__tab:hover { color: rgba(238, 239, 245, 0.95); } +.workbench-memory__tab:hover { color: var(--text); } .workbench-memory__tab--active { - color: rgba(238, 239, 245, 1); - border-bottom-color: var(--accent, #5b9dff); + color: var(--text-muted); + border-bottom-color: var(--accent, var(--accent-hover)); } .workbench-memory__error { @@ -6610,11 +6586,11 @@ button { gap: 0.5rem; margin: 0.4rem 0.5rem 0; padding: 0.4rem 0.55rem; - background: rgba(255, 90, 90, 0.12); - border: 1px solid rgba(255, 90, 90, 0.32); + background: var(--overlay-2); + border: 1px solid var(--overlay-2); border-radius: 5px; font-size: 0.78rem; - color: rgba(255, 200, 200, 0.95); + color: var(--text-muted); } .workbench-memory__error-dismiss { background: transparent; @@ -6644,7 +6620,7 @@ button { gap: 0.5rem; padding: 1rem 1.25rem; text-align: center; - background: rgba(20, 22, 30, 0.65); + background: var(--bg-app); backdrop-filter: blur(2px); pointer-events: none; } @@ -6658,7 +6634,7 @@ button { max-width: 22rem; font-size: 0.8rem; line-height: 1.45; - color: rgba(190, 194, 208, 0.78); + color: var(--text-muted); } /* --- Files view ----------------------------------------------------- */ @@ -6677,8 +6653,8 @@ button { display: flex; flex-direction: column; min-height: 0; - border-right: 1px solid rgba(255, 255, 255, 0.06); - background: rgba(255, 255, 255, 0.015); + border-right: 1px solid var(--overlay-2); + background: var(--overlay-1); } .workbench-memory-files__tree--collapsed { @@ -6689,7 +6665,7 @@ button { display: flex; gap: 0.3rem; padding: 0.45rem 0.5rem; - border-bottom: 1px solid rgba(255, 255, 255, 0.05); + border-bottom: 1px solid var(--overlay-1); } .workbench-memory-files__new--collapsed { @@ -6704,25 +6680,25 @@ button { font: inherit; font-size: 0.78rem; color: inherit; - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.1); + background: var(--overlay-1); + border: 1px solid var(--overlay-3); border-radius: 4px; } .workbench-memory-files__new-input:focus { outline: none; - border-color: var(--accent, #5b9dff); + border-color: var(--accent, var(--accent-hover)); } .workbench-memory-files__new-btn { padding: 0 0.6rem; font: inherit; color: inherit; - background: rgba(255, 255, 255, 0.06); - border: 1px solid rgba(255, 255, 255, 0.12); + background: var(--overlay-2); + border: 1px solid var(--overlay-3); border-radius: 4px; cursor: pointer; } .workbench-memory-files__new-btn:hover { - background: rgba(255, 255, 255, 0.1); + background: var(--overlay-3); } .workbench-memory-files__collapse-btn { @@ -6733,14 +6709,14 @@ button { align-items: center; justify-content: center; color: inherit; - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.12); + background: var(--overlay-1); + border: 1px solid var(--overlay-3); border-radius: 4px; cursor: pointer; } .workbench-memory-files__collapse-btn:hover { - background: rgba(255, 255, 255, 0.1); + background: var(--overlay-3); } .workbench-memory-files__list { @@ -6767,10 +6743,10 @@ button { display: none; } .workbench-memory-files__group-head--active .workbench-memory-files__group-index { - color: rgba(140, 190, 255, 0.95); + color: var(--text-muted); } .workbench-memory-files__group-head--sidebar-hidden .workbench-memory-files__group-index { - color: rgba(160, 165, 180, 0.48); + color: var(--text-muted); font-style: italic; } .workbench-memory-files__group-chevron { @@ -6781,7 +6757,7 @@ button { width: 1.35rem; height: 1.35rem; padding: 0; - color: rgba(160, 165, 180, 0.85); + color: var(--text-muted); background: transparent; border: none; border-radius: 4px; @@ -6792,8 +6768,8 @@ button { transform: rotate(90deg); } .workbench-memory-files__group-chevron:hover { - background: rgba(255, 255, 255, 0.05); - color: rgba(220, 223, 235, 0.95); + background: var(--overlay-1); + color: var(--text-muted); } .workbench-memory-files__group-index { flex: 1; @@ -6802,7 +6778,7 @@ button { font: inherit; font-size: 0.78rem; font-weight: 600; - color: rgba(190, 194, 208, 0.92); + color: var(--text-muted); background: transparent; border: none; border-radius: 4px; @@ -6813,8 +6789,8 @@ button { white-space: nowrap; } .workbench-memory-files__group-index:hover { - background: rgba(255, 255, 255, 0.04); - color: rgba(238, 239, 245, 0.95); + background: var(--overlay-1); + color: var(--text); } .workbench-memory-files__group-add { flex: 0 0 auto; @@ -6824,7 +6800,7 @@ button { width: 1.35rem; height: 1.35rem; padding: 0; - color: rgba(160, 165, 180, 0.7); + color: var(--text-muted); background: transparent; border: none; border-radius: 4px; @@ -6838,8 +6814,8 @@ button { opacity: 1; } .workbench-memory-files__group-add:hover { - background: rgba(255, 255, 255, 0.06); - color: rgba(238, 239, 245, 0.95); + background: var(--overlay-2); + color: var(--text); } .workbench-memory-files__item { display: flex; @@ -6854,7 +6830,7 @@ button { } .workbench-memory-files__item--active { - background: rgba(91, 157, 255, 0.12); + background: var(--accent-soft); } .workbench-memory-files__name { flex: 1; @@ -6873,7 +6849,7 @@ button { border-radius: 4px; } .workbench-memory-files__name:hover { - background: rgba(255, 255, 255, 0.04); + background: var(--overlay-1); } .workbench-memory-files__name-text { overflow: hidden; @@ -6882,20 +6858,20 @@ button { } .workbench-memory-files__folder { font-size: 0.7rem; - color: rgba(160, 165, 180, 0.7); + color: var(--text-muted); } .workbench-memory-files__action { padding: 0.2rem 0.35rem; font-size: 0.78rem; - color: rgba(190, 194, 208, 0.7); + color: var(--text-muted); background: transparent; border: none; border-radius: 4px; cursor: pointer; } .workbench-memory-files__action:hover { - background: rgba(255, 255, 255, 0.06); - color: rgba(238, 239, 245, 0.95); + background: var(--overlay-2); + color: var(--text); } .workbench-memory-files__badge { @@ -6909,17 +6885,17 @@ button { font-size: 0.72rem; font-weight: 800; letter-spacing: 0.02em; - color: rgba(238, 239, 245, 0.92); - background: linear-gradient(180deg, rgba(91, 157, 255, 0.16), rgba(91, 157, 255, 0.08)); - border: 1px solid rgba(91, 157, 255, 0.2); + color: var(--text); + background: linear-gradient(180deg, var(--accent-soft), var(--accent-soft)); + border: 1px solid var(--accent-soft); border-radius: 999px; cursor: pointer; - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05); + box-shadow: inset 0 1px 0 var(--overlay-1); } .workbench-memory-files__badge:hover { - background: linear-gradient(180deg, rgba(91, 157, 255, 0.22), rgba(91, 157, 255, 0.12)); - border-color: rgba(91, 157, 255, 0.34); + background: linear-gradient(180deg, var(--accent-soft), var(--accent-soft)); + border-color: var(--accent-soft); } .memory-context-menu { @@ -6960,7 +6936,7 @@ button { width: 1.45rem; height: 1.45rem; border-radius: 4px; - border: 1px solid rgba(255, 255, 255, 0.24); + border: 1px solid var(--overlay-2); background: var(--memory-swatch); cursor: pointer; } @@ -6985,13 +6961,13 @@ button { font: inherit; font-size: 0.78rem; color: inherit; - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.18); + background: var(--overlay-1); + border: 1px solid var(--overlay-2); border-radius: 4px; } .workbench-memory-files__rename-input:focus { outline: none; - border-color: var(--accent, #5b9dff); + border-color: var(--accent, var(--accent-hover)); } /* --- Editor pane ---------------------------------------------------- */ @@ -7005,7 +6981,7 @@ button { display: flex; align-items: center; justify-content: center; - color: rgba(160, 165, 180, 0.7); + color: var(--text-muted); font-size: 0.82rem; } .workbench-memory-editor__bar { @@ -7013,7 +6989,7 @@ button { align-items: center; gap: 0.5rem; padding: 0.35rem 0.55rem; - border-bottom: 1px solid rgba(255, 255, 255, 0.05); + border-bottom: 1px solid var(--overlay-1); font-size: 0.75rem; } .workbench-memory-editor__path { @@ -7022,10 +6998,10 @@ button { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - color: rgba(190, 194, 208, 0.85); + color: var(--text-muted); } .workbench-memory-editor__flag { - color: rgba(255, 200, 120, 0.85); + color: var(--text-muted); font-size: 0.7rem; } .workbench-memory-editor__preview-btn { @@ -7033,13 +7009,13 @@ button { font: inherit; font-size: 0.72rem; color: inherit; - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.12); + background: var(--overlay-1); + border: 1px solid var(--overlay-3); border-radius: 4px; cursor: pointer; } .workbench-memory-editor__preview-btn:hover { - background: rgba(255, 255, 255, 0.08); + background: var(--overlay-2); } .workbench-memory-editor__textarea { flex: 1; @@ -7066,14 +7042,14 @@ button { .workbench-memory-editor__preview h1, .workbench-memory-editor__preview h2, .workbench-memory-editor__preview h3 { margin: 0.6em 0 0.3em; } -.workbench-memory-editor__preview a { color: var(--accent, #5b9dff); } +.workbench-memory-editor__preview a { color: var(--accent, var(--accent-hover)); } .workbench-memory-editor__preview a[href^="blxmemory:"] { - color: #b49bff; - text-decoration-color: rgba(180, 155, 255, 0.45); + color: var(--text-muted); + text-decoration-color: var(--text-muted); } .workbench-memory-editor__preview code { font-family: ui-monospace, monospace; - background: rgba(255, 255, 255, 0.06); + background: var(--overlay-2); padding: 0.1em 0.3em; border-radius: 3px; font-size: 0.85em; @@ -7087,7 +7063,7 @@ button { padding: 0.35rem 0.55rem 0 0.55rem; } .workbench-memory-editor__backlinks { - border-top: 1px solid rgba(255, 255, 255, 0.06); + border-top: 1px solid var(--overlay-2); padding: 0.4rem 0.55rem 0.55rem; font-size: 0.78rem; } @@ -7096,7 +7072,7 @@ button { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; - color: rgba(160, 165, 180, 0.7); + color: var(--text-muted); } .workbench-memory-editor__backlinks-list { margin: 0; @@ -7110,14 +7086,14 @@ button { background: none; border: none; padding: 0.2rem 0.3rem; - color: var(--accent, #5b9dff); + color: var(--accent, var(--accent-hover)); font: inherit; font-size: 0.78rem; text-align: left; cursor: pointer; border-radius: 3px; } -.workbench-memory-editor__backlink:hover { background: rgba(91, 157, 255, 0.1); } +.workbench-memory-editor__backlink:hover { background: var(--accent-soft); } .workbench-memory-files__rename { flex: 1; display: flex; @@ -7125,8 +7101,8 @@ button { padding: 0.2rem 0.3rem; } .workbench-memory-files__action--danger:hover { - background: rgba(255, 90, 90, 0.14); - color: rgba(255, 200, 200, 0.95); + background: var(--overlay-2); + color: var(--text-muted); } .workbench-memory__tab-icon { display: inline-flex; @@ -7155,8 +7131,8 @@ button { min-height: 0; position: relative; overflow: hidden; - background: radial-gradient(circle at 50% 45%, rgba(91, 157, 255, 0.08), rgba(255, 255, 255, 0.015) 42%, rgba(0, 0, 0, 0.08)); - border: 1px solid rgba(255, 255, 255, 0.05); + background: radial-gradient(circle at 50% 45%, var(--accent-soft), var(--overlay-1) 42%, var(--scrim-bg)); + border: 1px solid var(--overlay-1); border-radius: 6px; } @@ -7193,25 +7169,25 @@ button { justify-content: center; width: 1.75rem; height: 1.75rem; - background: rgba(255, 255, 255, 0.04); - color: rgba(238, 239, 245, 0.85); - border: 1px solid rgba(255, 255, 255, 0.08); + background: var(--overlay-1); + color: var(--text-muted); + border: 1px solid var(--overlay-2); border-radius: 4px; padding: 0; font-size: 0.72rem; cursor: pointer; } .workbench-memory-graph__btn:hover { - background: rgba(255, 255, 255, 0.08); + background: var(--overlay-2); } .workbench-memory-graph__btn--active { - color: rgba(255, 255, 255, 0.96); - border-color: rgba(91, 157, 255, 0.42); - background: rgba(91, 157, 255, 0.14); + color: var(--text-muted); + border-color: var(--accent-soft); + background: var(--accent-soft); } .workbench-memory-graph__warning { margin: 0 0 0.4rem; - color: rgba(255, 214, 150, 0.9); + color: var(--text-muted); font-size: 0.72rem; } .workbench-memory-graph__empty { @@ -7219,13 +7195,13 @@ button { display: flex; align-items: center; justify-content: center; - color: rgba(160, 165, 180, 0.7); + color: var(--text-muted); font-size: 0.82rem; } .workbench-memory-graph__legend { padding: 0.4rem 0.2rem 0; font-size: 0.72rem; - color: rgba(160, 165, 180, 0.6); + color: var(--text-muted); } .workbench-memory-graph-preview { position: relative; @@ -7234,10 +7210,10 @@ button { display: flex; flex-direction: column; overflow: hidden; - background: rgba(18, 20, 27, 0.94); - border: 1px solid rgba(255, 255, 255, 0.1); + background: var(--overlay-2); + border: 1px solid var(--overlay-3); border-radius: 6px; - box-shadow: 0 12px 32px rgba(0, 0, 0, 0.32); + box-shadow: 0 12px 32px var(--scrim-bg); backdrop-filter: blur(18px); } .workbench-memory-graph-preview__head { @@ -7246,14 +7222,14 @@ button { justify-content: space-between; gap: 0.65rem; padding: 0.55rem 0.65rem; - border-bottom: 1px solid rgba(255, 255, 255, 0.08); + border-bottom: 1px solid var(--overlay-2); } .workbench-memory-graph-preview__title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - color: rgba(238, 239, 245, 0.94); + color: var(--text-muted); font-size: 0.78rem; font-weight: 650; } @@ -7269,14 +7245,14 @@ button { width: 1.55rem; height: 1.55rem; border-radius: 4px; - border: 1px solid rgba(255, 255, 255, 0.08); - background: rgba(255, 255, 255, 0.04); - color: rgba(238, 239, 245, 0.84); + border: 1px solid var(--overlay-2); + background: var(--overlay-1); + color: var(--text-muted); cursor: pointer; } .workbench-memory-graph-preview__btn:hover { - background: rgba(255, 255, 255, 0.08); - color: rgba(255, 255, 255, 0.96); + background: var(--overlay-2); + color: var(--text-muted); } .workbench-memory-graph-preview__body { padding: 0.75rem 0.85rem 0.9rem; @@ -7288,7 +7264,7 @@ button { } .workbench-memory-graph-preview__loading { margin: 0; - color: rgba(160, 165, 180, 0.75); + color: var(--text-muted); } /* --- Agent context handoff (graph preview + terminal titlebar) ----- */ @@ -7302,17 +7278,17 @@ button { right: 0; min-width: 12rem; max-width: 18rem; - background: rgba(20, 22, 28, 0.98); - border: 1px solid rgba(255, 255, 255, 0.1); + background: var(--overlay-2); + border: 1px solid var(--overlay-3); border-radius: 6px; - box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45); + box-shadow: 0 6px 18px var(--scrim-bg); padding: 0.35rem; z-index: 40; font-size: 0.78rem; } .workbench-handoff-menu__head { padding: 0.2rem 0.4rem 0.35rem; - color: rgba(180, 185, 200, 0.75); + color: var(--text-muted); font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em; @@ -7334,24 +7310,24 @@ button { border-radius: 4px; border: 1px solid transparent; background: transparent; - color: rgba(232, 234, 242, 0.92); + color: var(--text-muted); cursor: pointer; text-align: left; font: inherit; } .workbench-handoff-menu__item:hover, .workbench-handoff-menu__item:focus-visible { - background: rgba(255, 255, 255, 0.06); - border-color: rgba(255, 255, 255, 0.1); + background: var(--overlay-2); + border-color: var(--overlay-3); } .workbench-handoff-menu__empty { padding: 0.4rem 0.5rem; - color: rgba(170, 175, 190, 0.7); + color: var(--text-muted); } .workbench-handoff-menu__separator { height: 1px; margin: 0.35rem -0.1rem; - background: rgba(255, 255, 255, 0.08); + background: var(--overlay-2); } .blx-toast-host { position: fixed; @@ -7369,20 +7345,20 @@ button { border-radius: 6px; font-size: 0.78rem; line-height: 1.35; - border: 1px solid rgba(255, 255, 255, 0.12); - box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4); + border: 1px solid var(--overlay-3); + box-shadow: 0 6px 18px var(--scrim-bg); animation: blx-toast-in 0.22s ease-out; pointer-events: auto; } .blx-toast--success { - color: rgba(210, 245, 215, 0.96); - background: rgba(28, 52, 38, 0.96); - border-color: rgba(100, 180, 120, 0.35); + color: var(--text-muted); + background: var(--overlay-2); + border-color: var(--text-muted); } .blx-toast--error { - color: rgba(255, 210, 210, 0.96); - background: rgba(52, 28, 28, 0.96); - border-color: rgba(200, 100, 100, 0.35); + color: var(--text-muted); + background: var(--overlay-2); + border-color: var(--text-muted); } @keyframes blx-toast-in { from { @@ -7400,16 +7376,16 @@ button { gap: 0.5rem; margin-top: 0.45rem; font-size: 0.82rem; - color: rgba(232, 234, 242, 0.92); + color: var(--text-secondary); cursor: pointer; } .app-prefs-toggle input { - accent-color: rgba(120, 180, 255, 0.85); + accent-color: var(--accent-control); } .app-prefs-hint { margin: 0.2rem 0 0 1.35rem; font-size: 0.72rem; - color: rgba(170, 175, 190, 0.75); + color: var(--text-hint); line-height: 1.35; } @@ -7426,13 +7402,13 @@ button { font: inherit; font-size: 0.82rem; color: inherit; - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.1); + background: var(--overlay-1); + border: 1px solid var(--overlay-3); border-radius: 5px; } .workbench-memory-search__input:focus { outline: none; - border-color: var(--accent, #5b9dff); + border-color: var(--accent, var(--accent-hover)); } .workbench-memory-search__filters { display: flex; @@ -7446,21 +7422,21 @@ button { font-size: 0.72rem; font-weight: 600; letter-spacing: 0.02em; - color: rgba(200, 204, 218, 0.88); - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.1); + color: var(--text-muted); + background: var(--overlay-1); + border: 1px solid var(--overlay-3); border-radius: 999px; cursor: pointer; } .workbench-memory-search__filter:hover { - background: rgba(255, 255, 255, 0.07); - border-color: rgba(255, 255, 255, 0.16); - color: rgba(238, 239, 245, 0.95); + background: var(--overlay-2); + border-color: var(--text-muted); + color: var(--text); } .workbench-memory-search__filter--active { - color: rgba(238, 239, 245, 0.96); - background: linear-gradient(180deg, rgba(91, 157, 255, 0.2), rgba(91, 157, 255, 0.1)); - border-color: rgba(91, 157, 255, 0.35); + color: var(--text-muted); + background: linear-gradient(180deg, var(--accent-soft), var(--accent-soft)); + border-color: var(--accent-soft); } .workbench-memory-search__results { flex: 1; @@ -7477,14 +7453,14 @@ button { align-items: stretch; gap: 0.25rem; padding: 0; - background: rgba(255, 255, 255, 0.03); - border: 1px solid rgba(255, 255, 255, 0.06); + background: var(--overlay-1); + border: 1px solid var(--overlay-2); border-radius: 5px; overflow: hidden; } .workbench-memory-search__hit:hover { - background: rgba(255, 255, 255, 0.06); - border-color: rgba(255, 255, 255, 0.12); + background: var(--overlay-2); + border-color: var(--overlay-3); } .workbench-memory-search__hit-graph { flex: 0 0 auto; @@ -7494,22 +7470,22 @@ button { width: 2rem; padding: 0; border: none; - border-left: 1px solid rgba(255, 255, 255, 0.06); - background: rgba(255, 255, 255, 0.02); - color: var(--accent, #5b9dff); + border-left: 1px solid var(--overlay-2); + background: var(--overlay-1); + color: var(--accent, var(--accent-hover)); cursor: pointer; } .workbench-memory-search__hit-graph:hover { - background: rgba(91, 157, 255, 0.12); - color: rgba(200, 225, 255, 0.98); + background: var(--accent-soft); + color: var(--text-muted); } .workbench-memory-search__hit-path { font-size: 0.74rem; - color: rgba(160, 165, 180, 0.75); + color: var(--text-muted); } .workbench-memory-search__hit-snippet { font-size: 0.8rem; - color: rgba(238, 239, 245, 0.92); + color: var(--text); margin-top: 0.15rem; white-space: pre-wrap; } @@ -7525,7 +7501,7 @@ button { .workbench-memory-agents__lead { margin: 0 0 0.6rem; font-size: 0.78rem; - color: rgba(190, 194, 208, 0.8); + color: var(--text-muted); line-height: 1.45; } .workbench-memory-agents__list { @@ -7543,8 +7519,8 @@ button { align-items: center; gap: 0.5rem; padding: 0.4rem 0.55rem; - background: rgba(255, 255, 255, 0.025); - border: 1px solid rgba(255, 255, 255, 0.06); + background: var(--overlay-1); + border: 1px solid var(--overlay-2); border-radius: 5px; } .workbench-memory-agents__name { @@ -7561,29 +7537,29 @@ button { border-radius: 999px; } .workbench-memory-agents__status--installed { - color: rgba(160, 230, 180, 0.95); - background: rgba(70, 200, 110, 0.12); + color: var(--text-muted); + background: var(--overlay-2); } .workbench-memory-agents__status--missing { - color: rgba(200, 200, 210, 0.7); - background: rgba(255, 255, 255, 0.04); + color: var(--text-muted); + background: var(--overlay-1); } .workbench-memory-agents__btn { padding: 0.25rem 0.55rem; font: inherit; font-size: 0.74rem; color: inherit; - background: rgba(255, 255, 255, 0.05); - border: 1px solid rgba(255, 255, 255, 0.12); + background: var(--overlay-1); + border: 1px solid var(--overlay-3); border-radius: 4px; cursor: pointer; } .workbench-memory-agents__btn:hover { - background: rgba(255, 255, 255, 0.1); + background: var(--overlay-3); } .workbench-memory-agents__btn--ghost { background: transparent; - border-color: rgba(255, 255, 255, 0.08); + border-color: var(--text-muted); } .workbench-memory-search__hit-btn { flex: 1; @@ -7623,7 +7599,7 @@ button { display: flex; flex-direction: column; gap: 0.6rem; - border: 1px solid rgba(255, 255, 255, 0.07); + border: 1px solid var(--overlay-2); border-radius: 0.6rem; padding: 0.85rem 1rem; } @@ -7639,12 +7615,12 @@ button { } .voice-pane__field > label { font-size: 0.8rem; - color: rgba(255, 255, 255, 0.6); + color: var(--text-muted); letter-spacing: 0.02em; } .voice-pane__select { - background: rgba(255, 255, 255, 0.03); - border: 1px solid rgba(255, 255, 255, 0.1); + background: var(--overlay-1); + border: 1px solid var(--overlay-3); color: inherit; border-radius: 0.4rem; padding: 0.4rem 0.6rem; @@ -7661,8 +7637,8 @@ button { .voice-pane__model-custom .voice-pane__input { flex: 1; } .voice-pane__input, .voice-pane__hotkey-capture { - background: rgba(255, 255, 255, 0.03); - border: 1px solid rgba(255, 255, 255, 0.1); + background: var(--overlay-1); + border: 1px solid var(--overlay-3); color: inherit; border-radius: 0.4rem; padding: 0.4rem 0.6rem; @@ -7673,8 +7649,8 @@ button { } .voice-pane__input { cursor: text; } .voice-pane__hotkey-capture--recording { - border-color: #5b9dff; - background: rgba(91, 157, 255, 0.08); + border-color: var(--accent-hover); + background: var(--accent-soft); } .voice-pane__provider-row, .voice-pane__quality-row, @@ -7685,8 +7661,8 @@ button { gap: 0.4rem; } .voice-pane__choice { - background: rgba(255, 255, 255, 0.03); - border: 1px solid rgba(255, 255, 255, 0.1); + background: var(--overlay-1); + border: 1px solid var(--overlay-3); color: inherit; padding: 0.35rem 0.7rem; border-radius: 0.4rem; @@ -7694,8 +7670,8 @@ button { cursor: pointer; } .voice-pane__choice--active { - background: rgba(91, 157, 255, 0.15); - border-color: #5b9dff; + background: var(--accent-soft); + border-color: var(--accent-hover); } .voice-pane__voice-grid { display: grid; @@ -7732,13 +7708,13 @@ button { justify-content: space-between; gap: 0.4rem; padding: 0.4rem 0.55rem; - border: 1px solid rgba(255, 255, 255, 0.1); + border: 1px solid var(--overlay-3); border-radius: 0.45rem; - background: rgba(255, 255, 255, 0.02); + background: var(--overlay-1); } .voice-pane__voice-card--active { - border-color: #5b9dff; - background: rgba(91, 157, 255, 0.08); + border-color: var(--accent-hover); + background: var(--accent-soft); } .voice-pane__voice-pick { background: transparent; @@ -7757,11 +7733,11 @@ button { } .voice-pane__voice-gender { font-size: 0.7rem; - color: rgba(255, 255, 255, 0.5); + color: var(--text-muted); } .voice-pane__voice-preview { - background: rgba(255, 255, 255, 0.05); - border: 1px solid rgba(255, 255, 255, 0.1); + background: var(--overlay-1); + border: 1px solid var(--overlay-3); color: inherit; border-radius: 0.35rem; padding: 0.25rem 0.4rem; @@ -7776,15 +7752,15 @@ button { .voice-pane__hint { margin: 0; font-size: 0.78rem; - color: rgba(255, 255, 255, 0.55); + color: var(--text); } .voice-pane__hint--small { font-size: 0.72rem; - color: rgba(255, 255, 255, 0.4); + color: var(--text-muted); } .voice-pane__loading, .voice-pane__status { - color: rgba(255, 255, 255, 0.55); + color: var(--text); font-size: 0.85rem; } @@ -7869,13 +7845,13 @@ button { letter-spacing: 0.05em; } .blx-sr-tab:hover:not(.blx-sr-tab--active) { - background: rgba(255, 255, 255, 0.035); + background: var(--overlay-1); color: var(--text); } .blx-sr-tab--active { background: var(--accent-soft); color: var(--accent-cool); - border-color: rgba(88, 166, 255, 0.32); + border-color: var(--accent-soft); } .blx-sr-tab__count { display: inline-flex; @@ -7887,14 +7863,14 @@ button { border-radius: 999px; font-size: 0.65rem; font-weight: 600; - background: rgba(255, 255, 255, 0.08); + background: var(--overlay-2); color: var(--text); letter-spacing: 0; text-transform: none; } .blx-sr-tab--active .blx-sr-tab__count { - background: rgba(88, 166, 255, 0.28); - color: #eaf4ff; + background: var(--accent-soft); + color: var(--accent-cool-soft); } /* Body -------------------------------------------------------------------- */ @@ -7916,13 +7892,13 @@ button { font-size: 0.78rem; color: var(--text-muted); border-radius: 0.4rem; - background: rgba(255, 255, 255, 0.025); + background: var(--overlay-1); border: 1px solid var(--border); } .blx-sr-pane__err { - background: rgba(220, 60, 60, 0.1); - color: #ffb1b1; - border-color: rgba(220, 60, 60, 0.35); + background: var(--overlay-2); + color: var(--danger); + border-color: var(--text-muted); } /* Empty state ------------------------------------------------------------- */ @@ -7936,7 +7912,7 @@ button { padding: 2rem 1rem; border: 1px dashed var(--border-strong); border-radius: 0.6rem; - background: rgba(255, 255, 255, 0.015); + background: var(--overlay-1); } .blx-sr-empty__icon { display: inline-flex; @@ -7967,14 +7943,14 @@ button { } .blx-sr-card:hover { border-color: var(--border-strong); - background: #131520; + background: var(--text-muted); } .blx-sr-card--open { - border-color: rgba(88, 166, 255, 0.38); - background: #131826; + border-color: var(--accent-soft); + background: var(--text-muted); box-shadow: - 0 0 0 1px rgba(88, 166, 255, 0.12), - 0 8px 24px rgba(0, 0, 0, 0.32); + 0 0 0 1px var(--accent-cool-soft), + 0 8px 24px var(--scrim-bg); } .blx-sr-card--composer { border-style: dashed; @@ -8027,14 +8003,14 @@ button { width: 28px; height: 28px; border-radius: 0.4rem; - background: rgba(255, 255, 255, 0.04); + background: var(--overlay-1); color: var(--text-muted); border: 1px solid var(--border); } .blx-sr-card--open .blx-sr-card__icon { background: var(--accent-soft); color: var(--accent-cool); - border-color: rgba(88, 166, 255, 0.3); + border-color: var(--accent-soft); } .blx-sr-card__main { @@ -8062,7 +8038,7 @@ button { width: 100%; background: transparent; border: none; - border-bottom: 1px solid rgba(88, 166, 255, 0.26); + border-bottom: 1px solid var(--accent-soft); color: var(--text); font: inherit; font-size: 0.86rem; @@ -8074,7 +8050,7 @@ button { } .blx-sr-card__title-input:focus { outline: none; - border-bottom-color: rgba(88, 166, 255, 0.72); + border-bottom-color: var(--accent-soft); } .blx-sr-card__summary { font-size: 0.76rem; @@ -8091,7 +8067,7 @@ button { font-size: 0.6rem; padding: 0.12rem 0.4rem; border-radius: 0.3rem; - background: rgba(255, 255, 255, 0.06); + background: var(--overlay-2); color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.06em; @@ -8102,32 +8078,32 @@ button { .blx-sr-card__badge[data-kind="core"] { background: var(--accent-soft); color: var(--accent-cool); - border-color: rgba(88, 166, 255, 0.3); + border-color: var(--accent-soft); } .blx-sr-card__badge[data-kind="rule"] { background: var(--accent-soft); color: var(--accent-cool); - border-color: rgba(88, 166, 255, 0.3); + border-color: var(--accent-soft); } .blx-sr-card__badge[data-kind="git"] { - background: rgba(242, 163, 255, 0.12); - color: #f2a3ff; - border-color: rgba(242, 163, 255, 0.3); + background: var(--overlay-2); + color: var(--text-muted); + border-color: var(--text-muted); } .blx-sr-card__badge[data-kind="npm"] { - background: rgba(220, 60, 60, 0.14); - color: #ff9a9a; - border-color: rgba(220, 60, 60, 0.32); + background: var(--overlay-2); + color: var(--danger); + border-color: var(--text-muted); } .blx-sr-card__badge[data-kind="local"] { - background: rgba(255, 192, 77, 0.12); - color: #ffc04d; - border-color: rgba(255, 192, 77, 0.32); + background: var(--overlay-2); + color: var(--warning); + border-color: var(--text-muted); } .blx-sr-card__badge[data-kind="agent"] { - background: rgba(99, 230, 190, 0.12); + background: var(--accent-cool-soft); color: var(--syntax-type); - border-color: rgba(99, 230, 190, 0.32); + border-color: var(--success); } .blx-sr-card__warn { @@ -8137,9 +8113,9 @@ button { margin: 0; padding: 0.4rem 0.75rem; font-size: 0.72rem; - color: #ffc04d; - background: rgba(255, 192, 77, 0.06); - border-top: 1px solid rgba(255, 192, 77, 0.18); + color: var(--warning); + background: var(--overlay-2); + border-top: 1px solid var(--overlay-2); } /* Expanded body (email-client style reader) ------------------------------- */ @@ -8185,13 +8161,13 @@ button { .blx-sr-card__md ul, .blx-sr-card__md ol { padding-left: 1.2rem; margin: 0.35rem 0; } .blx-sr-card__md code { - background: rgba(255, 255, 255, 0.06); + background: var(--overlay-2); padding: 0.05rem 0.3rem; border-radius: 0.25rem; font-size: 0.78rem; } .blx-sr-card__md pre { - background: rgba(0, 0, 0, 0.4); + background: var(--scrim-bg); padding: 0.6rem 0.7rem; border-radius: 0.4rem; overflow-x: auto; @@ -8206,7 +8182,7 @@ button { max-height: 360px; resize: vertical; overflow-y: auto; - background: rgba(0, 0, 0, 0.28); + background: var(--scrim-bg); border: 1px solid var(--border); border-radius: 0.45rem; color: var(--text); @@ -8217,12 +8193,12 @@ button { } .blx-sr-card__editor:focus { outline: none; - border-color: rgba(88, 166, 255, 0.56); - box-shadow: 0 0 0 2px rgba(88, 166, 255, 0.18); + border-color: var(--accent-soft); + box-shadow: 0 0 0 2px var(--accent-soft); } .blx-sr-card__error { margin: -0.1rem 0 0; - color: #ffb1b1; + color: var(--danger); font-size: 0.76rem; } .blx-sr-card__actions { @@ -8243,14 +8219,14 @@ button { align-items: center; width: var(--switch-w); height: var(--switch-h); - background: rgba(255, 255, 255, 0.1); + background: var(--overlay-3); border-radius: 999px; cursor: pointer; transition: background 160ms ease, box-shadow 160ms ease; border: 1px solid var(--border); flex-shrink: 0; } -.blx-switch:hover { background: rgba(255, 255, 255, 0.14); } +.blx-switch:hover { background: var(--overlay-4); } .blx-switch:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--accent-soft); @@ -8262,11 +8238,11 @@ button { width: var(--thumb); height: var(--thumb); border-radius: 50%; - background: #d6dae3; + background: var(--text-muted); transform: translateY(-50%); transition: transform 180ms cubic-bezier(0.4, 0, 0.2, 1), background 160ms ease; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.45); + box-shadow: 0 1px 2px var(--scrim-bg); } .blx-switch--on { background: var(--accent); @@ -8275,12 +8251,12 @@ button { .blx-switch--on:hover { background: var(--accent-hover); } .blx-switch--on .blx-switch__thumb { transform: translate(calc(var(--switch-w) - var(--thumb) - 4px), -50%); - background: #ffffff; + background: var(--text-bright); } /* Buttons ----------------------------------------------------------------- */ .blx-sr-btn { - background: rgba(255, 255, 255, 0.04); + background: var(--overlay-1); border: 1px solid var(--border); color: var(--text); border-radius: 0.4rem; @@ -8295,7 +8271,7 @@ button { transition: background 140ms ease, border-color 140ms ease, color 140ms ease; } .blx-sr-btn:hover:not(:disabled) { - background: rgba(255, 255, 255, 0.08); + background: var(--overlay-2); border-color: var(--border-strong); } .blx-sr-btn:focus-visible { @@ -8309,13 +8285,13 @@ button { } .blx-sr-btn--primary { background: var(--accent-soft); - border-color: rgba(88, 166, 255, 0.35); + border-color: var(--accent-soft); color: var(--accent-cool); } .blx-sr-btn--primary:hover:not(:disabled) { - background: rgba(88, 166, 255, 0.26); + background: var(--accent-soft); border-color: var(--accent); - color: #eaf4ff; + color: var(--accent-cool-soft); } .blx-sr-btn--icon { padding: 0.32rem; @@ -8333,25 +8309,25 @@ button { color: var(--text-muted); } .blx-sr-btn--ghost:hover:not(:disabled) { - background: rgba(255, 255, 255, 0.04); + background: var(--overlay-1); color: var(--text); } .blx-sr-btn--danger { - background: rgba(220, 60, 60, 0.12); - border-color: rgba(220, 60, 60, 0.32); - color: #ffb1b1; + background: var(--overlay-2); + border-color: var(--text-muted); + color: var(--danger); } .blx-sr-btn--danger:hover:not(:disabled) { - background: rgba(220, 60, 60, 0.22); - border-color: rgba(220, 60, 60, 0.5); - color: #ffd0d0; + background: var(--overlay-2); + border-color: var(--text-muted); + color: var(--text-muted); } /* Install dialog */ .blx-sr-dialog__backdrop { position: fixed; inset: 0; - background: rgba(0, 0, 0, 0.55); + background: var(--scrim-bg); z-index: 90; } .blx-sr-dialog { @@ -8361,10 +8337,10 @@ button { transform: translate(-50%, -50%); width: min(440px, calc(100vw - 2rem)); max-height: calc(100vh - 2rem); - background: #1a1f2b; - border: 1px solid rgba(255, 255, 255, 0.12); + background: var(--text-muted); + border: 1px solid var(--overlay-3); border-radius: 0.6rem; - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.45); + box-shadow: 0 12px 40px var(--scrim-bg); display: flex; flex-direction: column; z-index: 100; @@ -8372,7 +8348,7 @@ button { } .blx-sr-dialog__header { padding: 0.7rem 0.9rem; - border-bottom: 1px solid rgba(255, 255, 255, 0.08); + border-bottom: 1px solid var(--overlay-2); } .blx-sr-dialog__header h2 { margin: 0; @@ -8394,11 +8370,11 @@ button { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; - color: rgba(255, 255, 255, 0.55); + color: var(--text); } .blx-sr-dialog__row input { - background: rgba(0, 0, 0, 0.25); - border: 1px solid rgba(255, 255, 255, 0.12); + background: var(--scrim-bg); + border: 1px solid var(--overlay-3); border-radius: 0.35rem; color: inherit; padding: 0.4rem 0.55rem; @@ -8406,38 +8382,38 @@ button { } .blx-sr-dialog__row input:focus { outline: none; - border-color: #6aa1ff; - box-shadow: 0 0 0 2px rgba(106, 161, 255, 0.25); + border-color: var(--text-muted); + box-shadow: 0 0 0 2px var(--overlay-2); } .blx-sr-dialog__segmented { display: inline-flex; - border: 1px solid rgba(255, 255, 255, 0.12); + border: 1px solid var(--overlay-3); border-radius: 0.4rem; overflow: hidden; } .blx-sr-seg { background: transparent; border: none; - color: rgba(255, 255, 255, 0.65); + color: var(--text-muted); padding: 0.3rem 0.7rem; font-size: 0.78rem; cursor: pointer; } .blx-sr-seg + .blx-sr-seg { - border-left: 1px solid rgba(255, 255, 255, 0.12); + border-left: 1px solid var(--overlay-3); } .blx-sr-seg--active { - background: rgba(120, 160, 255, 0.25); - color: #ffffff; + background: var(--accent-soft); + color: var(--text-bright); } .blx-sr-dialog__err { margin: 0; - color: #ffb1b1; + color: var(--danger); font-size: 0.78rem; } .blx-sr-dialog__footer { padding: 0.6rem 0.9rem; - border-top: 1px solid rgba(255, 255, 255, 0.08); + border-top: 1px solid var(--overlay-2); display: flex; justify-content: flex-end; gap: 0.4rem; @@ -8585,7 +8561,7 @@ button { } .project-explorer__row:hover { - background: rgba(255, 255, 255, 0.06); + background: var(--overlay-2); } .project-explorer__row--file { @@ -8661,38 +8637,38 @@ button { .git-graph__line--c0, .git-graph__dot--c0 { - stroke: #4a9eff; - fill: #4a9eff; + stroke: var(--git-lane-0); + fill: var(--git-lane-0); } .git-graph__line--c1, .git-graph__dot--c1 { - stroke: #e8b84a; - fill: #e8b84a; + stroke: var(--warning); + fill: var(--warning); } .git-graph__line--c2, .git-graph__dot--c2 { - stroke: #5fd38d; - fill: #5fd38d; + stroke: var(--text-muted); + fill: var(--text-muted); } .git-graph__line--c3, .git-graph__dot--c3 { - stroke: #c792ea; - fill: #c792ea; + stroke: var(--text-muted); + fill: var(--text-muted); } .git-graph__line--c4, .git-graph__dot--c4 { - stroke: #f78c6c; - fill: #f78c6c; + stroke: var(--text-muted); + fill: var(--text-muted); } .git-graph__line--c5, .git-graph__dot--c5 { - stroke: #89ddff; - fill: #89ddff; + stroke: var(--git-lane-5); + fill: var(--git-lane-5); } .git-graph__connector { @@ -8705,7 +8681,7 @@ button { } .git-graph__dot-inner { - fill: var(--bg, #1a1f2e); + fill: var(--bg, var(--text-muted)); stroke: none; } @@ -8733,9 +8709,9 @@ button { font-size: 0.62rem; padding: 0.05rem 0.35rem; border-radius: 3px; - background: rgba(74, 158, 255, 0.2); - color: #8ec5ff; - border: 1px solid rgba(74, 158, 255, 0.35); + background: var(--overlay-2); + color: var(--accent-cool); + border: 1px solid var(--overlay-2); } .git-graph__meta { @@ -8760,10 +8736,10 @@ button { justify-content: space-between; gap: 0.5rem; padding: 0.4rem 0.6rem; - background: rgba(248, 113, 113, 0.08); - border-bottom: 1px solid rgba(248, 113, 113, 0.25); + background: var(--danger-soft); + border-bottom: 1px solid var(--danger-soft); font-size: 0.78rem; - color: #f87171; + color: var(--text-muted); } .workbench-plans__error-dismiss { @@ -8825,8 +8801,8 @@ button { min-height: 0; height: 100%; overflow: hidden; - border-right: 1px solid rgba(255, 255, 255, 0.06); - background: rgba(255, 255, 255, 0.015); + border-right: 1px solid var(--overlay-2); + background: var(--overlay-1); } .workbench-plans-manage__tree--collapsed { @@ -8838,7 +8814,7 @@ button { align-items: center; gap: 0.3rem; padding: 0.45rem 0.5rem; - border-bottom: 1px solid rgba(255, 255, 255, 0.05); + border-bottom: 1px solid var(--overlay-1); } .workbench-plans-manage__new--collapsed { @@ -8850,8 +8826,8 @@ button { .workbench-plans-manage__new-input { flex: 1 1 auto; min-width: 0; - background: var(--blx-bg-elev, #1e2128); - border: 1px solid var(--blx-border-subtle, #2c2f36); + background: var(--blx-bg-elev, var(--bg-panel)); + border: 1px solid var(--blx-border-subtle, var(--bg-panel-header)); border-radius: 4px; color: var(--blx-fg); padding: 0.25rem 0.4rem; @@ -8863,8 +8839,8 @@ button { padding: 0 0.6rem; font: inherit; color: inherit; - background: rgba(255, 255, 255, 0.06); - border: 1px solid rgba(255, 255, 255, 0.12); + background: var(--overlay-2); + border: 1px solid var(--overlay-3); border-radius: 4px; cursor: pointer; display: inline-flex; @@ -8874,7 +8850,7 @@ button { .workbench-plans-manage__new-btn:hover, .workbench-plans-manage__refresh:hover { - background: rgba(255, 255, 255, 0.1); + background: var(--overlay-3); } .workbench-plans-manage__collapse-btn { @@ -8885,14 +8861,14 @@ button { align-items: center; justify-content: center; color: inherit; - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.12); + background: var(--overlay-1); + border: 1px solid var(--overlay-3); border-radius: 4px; cursor: pointer; } .workbench-plans-manage__collapse-btn:hover { - background: rgba(255, 255, 255, 0.1); + background: var(--overlay-3); } .workbench-plans-manage__list { @@ -8909,7 +8885,7 @@ button { align-items: center; gap: 0.3rem 0.4rem; padding: 0.42rem 0.55rem; - border-bottom: 1px solid rgba(255, 255, 255, 0.07); + border-bottom: 1px solid var(--overlay-2); font-size: 0.8rem; transition: background 0.1s ease; } @@ -8921,15 +8897,15 @@ button { } .workbench-plans-manage__row:hover { - background: rgba(255, 255, 255, 0.06); + background: var(--overlay-2); } .workbench-plans-manage__row--active { - background: rgba(125, 211, 252, 0.08); + background: var(--overlay-2); } .workbench-plans-manage__row--active:hover { - background: rgba(125, 211, 252, 0.14); + background: var(--overlay-2); } .workbench-plans-manage__row-main { @@ -8970,7 +8946,7 @@ button { .workbench-plans-manage__row-action:hover { color: var(--blx-fg); - background: rgba(255, 255, 255, 0.04); + background: var(--overlay-1); } .workbench-plans-manage__badge { @@ -8984,22 +8960,22 @@ button { font-size: 0.72rem; font-weight: 800; letter-spacing: 0.02em; - color: rgba(238, 239, 245, 0.92); - background: linear-gradient(180deg, rgba(91, 157, 255, 0.16), rgba(91, 157, 255, 0.08)); - border: 1px solid rgba(91, 157, 255, 0.2); + color: var(--text); + background: linear-gradient(180deg, var(--accent-soft), var(--accent-soft)); + border: 1px solid var(--accent-soft); border-radius: 999px; cursor: pointer; - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05); + box-shadow: inset 0 1px 0 var(--overlay-1); } .workbench-plans-manage__badge:hover { - background: linear-gradient(180deg, rgba(91, 157, 255, 0.22), rgba(91, 157, 255, 0.12)); - border-color: rgba(91, 157, 255, 0.34); + background: linear-gradient(180deg, var(--accent-soft), var(--accent-soft)); + border-color: var(--accent-soft); } .workbench-plans-manage__row--active .workbench-plans-manage__badge { - background: linear-gradient(180deg, rgba(125, 211, 252, 0.28), rgba(125, 211, 252, 0.14)); - border-color: rgba(125, 211, 252, 0.45); + background: linear-gradient(180deg, var(--overlay-2), var(--overlay-2)); + border-color: var(--text-muted); } .workbench-plans-manage__rename { @@ -9013,8 +8989,8 @@ button { .workbench-plans-manage__rename-input { flex: 1 1 auto; min-width: 0; - background: var(--blx-bg-elev, #1e2128); - border: 1px solid var(--blx-border-subtle, #2c2f36); + background: var(--blx-bg-elev, var(--bg-panel)); + border: 1px solid var(--blx-border-subtle, var(--bg-panel-header)); border-radius: 3px; color: var(--blx-fg); padding: 0.2rem 0.35rem; @@ -9023,7 +8999,7 @@ button { .workbench-plans-manage__rename-btn { background: transparent; - border: 1px solid var(--blx-border-subtle, #2c2f36); + border: 1px solid var(--blx-border-subtle, var(--bg-panel-header)); border-radius: 3px; color: inherit; cursor: pointer; @@ -9037,7 +9013,7 @@ button { min-height: 0; height: 100%; overflow: hidden; - border-left: 1px solid var(--blx-border-subtle, #2c2f36); + border-left: 1px solid var(--blx-border-subtle, var(--bg-panel-header)); } .workbench-plans-manage__editor-empty { @@ -9052,7 +9028,7 @@ button { align-items: center; gap: 0.4rem; padding: 0.35rem 0.5rem; - border-bottom: 1px solid var(--blx-border-subtle, #2c2f36); + border-bottom: 1px solid var(--blx-border-subtle, var(--bg-panel-header)); font-size: 0.78rem; } @@ -9062,7 +9038,7 @@ button { } .workbench-plans-manage__protected { - color: #fbbf24; + color: var(--warning); font-size: 0.72rem; } @@ -9072,7 +9048,7 @@ button { .workbench-plans-manage__btn { background: transparent; - border: 1px solid var(--blx-border-subtle, #2c2f36); + border: 1px solid var(--blx-border-subtle, var(--bg-panel-header)); border-radius: 4px; color: var(--blx-fg); cursor: pointer; @@ -9084,19 +9060,19 @@ button { } .workbench-plans-manage__btn--primary { - background: rgba(125, 211, 252, 0.12); - border-color: rgba(125, 211, 252, 0.4); + background: var(--overlay-2); + border-color: var(--text-muted); } .workbench-plans-manage__btn--primary:hover { - background: rgba(125, 211, 252, 0.22); + background: var(--overlay-2); } .workbench-plans-manage__textarea { flex: 1 1 auto; min-height: 0; width: 100%; - background: var(--blx-bg-elev, #1e2128); + background: var(--blx-bg-elev, var(--bg-panel)); color: var(--blx-fg); border: 0; padding: 0.6rem 0.8rem; diff --git a/themes/tokens.css b/themes/tokens.css new file mode 100644 index 0000000..e98b6b7 --- /dev/null +++ b/themes/tokens.css @@ -0,0 +1,1504 @@ +/* + * blxcode — global theme tokens (20 app themes) + * Default / blxcode-dark matches styles.css :root color tokens 1:1; extended tokens complete the set. + */ + +:root, +[data-theme="blxcode-dark"] { + color-scheme: dark; + + --bg-app: #090a0d; + --bg-raised: #101116; + --bg-panel: #15171d; + --bg-panel-header: #191b21; + + --border: rgba(255, 255, 255, 0.085); + --border-strong: rgba(255, 255, 255, 0.16); + --border-focus: #58a6ff; + + --text: #f1f2f5; + --text-muted: #a3a7b3; + --text-faint: #676c78; + --text-bright: #ffffff; + + --accent: #58a6ff; + --accent-hover: #7ab8ff; + --accent-soft: rgba(88, 166, 255, 0.16); + --accent-cool: #9bd3ff; + --accent-cool-soft: rgba(88, 166, 255, 0.12); + + --syntax-type: #63e6be; + --syntax-keyword: #f2a3ff; + + --font-mono: "JetBrains Mono", "SF Mono", ui-monospace, monospace; + + --overlay-1: rgba(255, 255, 255, 0.04); + --overlay-2: rgba(255, 255, 255, 0.07); + --overlay-3: rgba(255, 255, 255, 0.1); + --overlay-4: rgba(255, 255, 255, 0.14); + --overlay-5: rgba(255, 255, 255, 0.2); + --overlay-6: rgba(255, 255, 255, 0.28); + + --danger: #f85149; + --danger-soft: rgba(248, 81, 73, 0.18); + --warning: #d4a017; + --success: #3fb950; + + --term-fg: #f1f2f5; + --term-bg: #090a0d; + --term-surface: #15171d; + --term-cursor: #58a6ff; + + --on-accent: #0a0b0e; + --scrim-bg: rgba(0, 0, 0, 0.55); + + --agent-claude: #d97757; + --agent-codex: #10a37f; + --agent-gemini: #4285f4; + --agent-copilot: #6e40c9; + --agent-cursor: #a78bfa; + + --git-lane-0: #58a6ff; + --git-lane-1: #a371f7; + --git-lane-2: #3fb950; + --git-lane-3: #d4a017; + --git-lane-4: #f85149; + --git-lane-5: #79c0ff; + + --fg-main: var(--text); + + --text-secondary: rgba(232, 234, 242, 0.92); + --text-hint: rgba(170, 175, 190, 0.75); + --accent-control: rgba(120, 180, 255, 0.85); + --success-surface: rgba(104, 220, 159, 0.08); + --success-border: rgba(104, 220, 159, 0.35); + --success-text: #86e7b2; + --danger-surface: rgba(255, 122, 122, 0.08); + --danger-border-soft: rgba(255, 122, 122, 0.32); + --danger-text-soft: #ff9a9a; + --hook-brand-bg: rgba(255, 255, 255, 0.92); + --hook-brand-fg: #14161d; +} + +[data-theme="blxcode-light"] { + color-scheme: light; + + --bg-app: #f6f8fc; + --bg-raised: #eef0f4; + --bg-panel: #ffffff; + --bg-panel-header: #e8ebf1; + + --border: rgba(0, 0, 0, 0.085); + --border-strong: rgba(0, 0, 0, 0.14); + --border-focus: #0969da; + + --text: #1a1d24; + --text-muted: #656b76; + --text-faint: #8b909a; + --text-bright: #0d0f14; + + --accent: #0969da; + --accent-hover: #0550ae; + --accent-soft: rgba(9, 105, 218, 0.12); + --accent-cool: #4da0ff; + --accent-cool-soft: rgba(9, 105, 218, 0.08); + + --syntax-type: #0d7d6b; + --syntax-keyword: #a21caf; + + --overlay-1: rgba(0, 0, 0, 0.03); + --overlay-2: rgba(0, 0, 0, 0.055); + --overlay-3: rgba(0, 0, 0, 0.08); + --overlay-4: rgba(0, 0, 0, 0.11); + --overlay-5: rgba(0, 0, 0, 0.15); + --overlay-6: rgba(0, 0, 0, 0.22); + + --danger: #cf222e; + --danger-soft: rgba(207, 34, 46, 0.12); + --warning: #9a6700; + --success: #1a7f37; + + --term-fg: #1a1d24; + --term-bg: #ffffff; + --term-surface: #f6f8fc; + --term-cursor: #0969da; + + --on-accent: #ffffff; + --scrim-bg: rgba(0, 0, 0, 0.35); + + --agent-claude: #b45309; + --agent-codex: #047857; + --agent-gemini: #1a73e8; + --agent-copilot: #5b21b6; + --agent-cursor: #6d28d9; + + --git-lane-0: #0969da; + --git-lane-1: #8250df; + --git-lane-2: #1a7f37; + --git-lane-3: #9a6700; + --git-lane-4: #cf222e; + --git-lane-5: #0d7d6b; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 90%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 12%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: var(--success); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 30%, transparent); + --danger-text-soft: var(--danger); + --hook-brand-bg: var(--bg-panel); + --hook-brand-fg: var(--text); +} + + +/* Dracula — https://draculatheme.com */ +[data-theme="dracula"] { + color-scheme: dark; + + --bg-app: #191a21; + --bg-raised: #21222c; + --bg-panel: #282a36; + --bg-panel-header: #2f3142; + + --border: rgba(255, 255, 255, 0.08); + --border-strong: rgba(255, 255, 255, 0.15); + --border-focus: #bd93f9; + + --text: #f8f8f2; + --text-muted: #bfbfc7; + --text-faint: #6272a4; + --text-bright: #ffffff; + + --accent: #bd93f9; + --accent-hover: #d4b5ff; + --accent-soft: rgba(189, 147, 249, 0.18); + --accent-cool: #8be9fd; + --accent-cool-soft: rgba(139, 233, 253, 0.12); + + --syntax-type: #8be9fd; + --syntax-keyword: #ff79c6; + + --overlay-1: rgba(255, 255, 255, 0.04); + --overlay-2: rgba(255, 255, 255, 0.07); + --overlay-3: rgba(255, 255, 255, 0.1); + --overlay-4: rgba(255, 255, 255, 0.14); + --overlay-5: rgba(255, 255, 255, 0.2); + --overlay-6: rgba(255, 255, 255, 0.28); + + --danger: #ff5555; + --danger-soft: rgba(255, 85, 85, 0.2); + --warning: #ffb86c; + --success: #50fa7b; + + --term-fg: #f8f8f2; + --term-bg: #191a21; + --term-surface: #282a36; + --term-cursor: #bd93f9; + + --on-accent: #191a21; + --scrim-bg: rgba(0, 0, 0, 0.55); + + --agent-claude: #ffb86c; + --agent-codex: #50fa7b; + --agent-gemini: #8be9fd; + --agent-copilot: #bd93f9; + --agent-cursor: #ff79c6; + + --git-lane-0: #bd93f9; + --git-lane-1: #8be9fd; + --git-lane-2: #50fa7b; + --git-lane-3: #ffb86c; + --git-lane-4: #ff5555; + --git-lane-5: #f1fa8c; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 88%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 10%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: color-mix(in srgb, var(--success) 75%, var(--text)); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 32%, transparent); + --danger-text-soft: color-mix(in srgb, var(--danger) 70%, var(--text)); + --hook-brand-bg: color-mix(in srgb, var(--text-bright) 92%, transparent); + --hook-brand-fg: var(--bg-app); +} + + +/* Gruvbox Dark — https://github.com/morhetz/gruvbox */ +[data-theme="gruvbox-dark"] { + color-scheme: dark; + + --bg-app: #1d2021; + --bg-raised: #282828; + --bg-panel: #282828; + --bg-panel-header: #32302f; + + --border: rgba(251, 241, 199, 0.08); + --border-strong: rgba(251, 241, 199, 0.14); + --border-focus: #fe8019; + + --text: #ebdbb2; + --text-muted: #bdae93; + --text-faint: #928374; + --text-bright: #fbf1c7; + + --accent: #fe8019; + --accent-hover: #fabd2f; + --accent-soft: rgba(254, 128, 25, 0.18); + --accent-cool: #83a598; + --accent-cool-soft: rgba(131, 165, 152, 0.14); + + --syntax-type: #83a598; + --syntax-keyword: #fb4934; + + --overlay-1: rgba(251, 241, 199, 0.04); + --overlay-2: rgba(251, 241, 199, 0.07); + --overlay-3: rgba(251, 241, 199, 0.1); + --overlay-4: rgba(251, 241, 199, 0.13); + --overlay-5: rgba(251, 241, 199, 0.18); + --overlay-6: rgba(251, 241, 199, 0.26); + + --danger: #fb4934; + --danger-soft: rgba(251, 73, 52, 0.2); + --warning: #fabd2f; + --success: #b8bb26; + + --term-fg: #ebdbb2; + --term-bg: #1d2021; + --term-surface: #282828; + --term-cursor: #fe8019; + + --on-accent: #1d2021; + --scrim-bg: rgba(0, 0, 0, 0.55); + + --agent-claude: #d79921; + --agent-codex: #689d6a; + --agent-gemini: #458588; + --agent-copilot: #b16286; + --agent-cursor: #fabd2f; + + --git-lane-0: #fe8019; + --git-lane-1: #83a598; + --git-lane-2: #b8bb26; + --git-lane-3: #fabd2f; + --git-lane-4: #fb4934; + --git-lane-5: #d3869b; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 88%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 10%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: color-mix(in srgb, var(--success) 75%, var(--text)); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 32%, transparent); + --danger-text-soft: color-mix(in srgb, var(--danger) 70%, var(--text)); + --hook-brand-bg: color-mix(in srgb, var(--text-bright) 92%, transparent); + --hook-brand-fg: var(--bg-app); +} + + +/* Gruvbox Light */ +[data-theme="gruvbox-light"] { + color-scheme: light; + + --bg-app: #f9f5d7; + --bg-raised: #ebdbb2; + --bg-panel: #fbf1c7; + --bg-panel-header: #ece2bd; + + --border: rgba(60, 56, 54, 0.1); + --border-strong: rgba(60, 56, 54, 0.16); + --border-focus: #af3a03; + + --text: #3c3836; + --text-muted: #665c54; + --text-faint: #928374; + --text-bright: #282828; + + --accent: #af3a03; + --accent-hover: #9d0006; + --accent-soft: rgba(175, 58, 3, 0.12); + --accent-cool: #076678; + --accent-cool-soft: rgba(7, 102, 120, 0.1); + + --syntax-type: #076678; + --syntax-keyword: #9d0006; + + --overlay-1: rgba(0, 0, 0, 0.035); + --overlay-2: rgba(0, 0, 0, 0.065); + --overlay-3: rgba(0, 0, 0, 0.095); + --overlay-4: rgba(0, 0, 0, 0.12); + --overlay-5: rgba(0, 0, 0, 0.17); + --overlay-6: rgba(0, 0, 0, 0.24); + + --danger: #9d0006; + --danger-soft: rgba(157, 0, 6, 0.12); + --warning: #b57614; + --success: #79740e; + + --term-fg: #3c3836; + --term-bg: #fbf1c7; + --term-surface: #f9f5d7; + --term-cursor: #af3a03; + + --on-accent: #fbf1c7; + --scrim-bg: rgba(60, 56, 54, 0.35); + + --agent-claude: #af3a03; + --agent-codex: #427b58; + --agent-gemini: #076678; + --agent-copilot: #8f3f71; + --agent-cursor: #79740e; + + --git-lane-0: #076678; + --git-lane-1: #8f3f71; + --git-lane-2: #79740e; + --git-lane-3: #b57614; + --git-lane-4: #9d0006; + --git-lane-5: #427b58; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 90%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 12%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: var(--success); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 30%, transparent); + --danger-text-soft: var(--danger); + --hook-brand-bg: var(--bg-panel); + --hook-brand-fg: var(--text); +} + + +/* Solarized Dark — ethanschoonover.com/solarized */ +[data-theme="solarized-dark"] { + color-scheme: dark; + + --bg-app: #001a22; + --bg-raised: #00212b; + --bg-panel: #002b36; + --bg-panel-header: #073642; + + --border: rgba(253, 246, 227, 0.07); + --border-strong: rgba(253, 246, 227, 0.12); + --border-focus: #268bd2; + + --text: #839496; + --text-muted: #586e75; + --text-faint: #657b83; + --text-bright: #eee8d5; + + --accent: #268bd2; + --accent-hover: #2aa198; + --accent-soft: rgba(38, 139, 210, 0.18); + --accent-cool: #2aa198; + --accent-cool-soft: rgba(42, 161, 152, 0.14); + + --syntax-type: #2aa198; + --syntax-keyword: #859900; + + --overlay-1: rgba(253, 246, 227, 0.04); + --overlay-2: rgba(253, 246, 227, 0.07); + --overlay-3: rgba(253, 246, 227, 0.1); + --overlay-4: rgba(253, 246, 227, 0.13); + --overlay-5: rgba(253, 246, 227, 0.18); + --overlay-6: rgba(253, 246, 227, 0.26); + + --danger: #dc322f; + --danger-soft: rgba(220, 50, 47, 0.2); + --warning: #b58900; + --success: #859900; + + --term-fg: #839496; + --term-bg: #001a22; + --term-surface: #002b36; + --term-cursor: #268bd2; + + --on-accent: #fdf6e3; + --scrim-bg: rgba(0, 0, 0, 0.55); + + --agent-claude: #cb4b16; + --agent-codex: #859900; + --agent-gemini: #268bd2; + --agent-copilot: #6c71c4; + --agent-cursor: #2aa198; + + --git-lane-0: #268bd2; + --git-lane-1: #6c71c4; + --git-lane-2: #859900; + --git-lane-3: #b58900; + --git-lane-4: #dc322f; + --git-lane-5: #2aa198; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 88%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 10%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: color-mix(in srgb, var(--success) 75%, var(--text)); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 32%, transparent); + --danger-text-soft: color-mix(in srgb, var(--danger) 70%, var(--text)); + --hook-brand-bg: color-mix(in srgb, var(--text-bright) 92%, transparent); + --hook-brand-fg: var(--bg-app); +} + + +/* Solarized Light */ +[data-theme="solarized-light"] { + color-scheme: light; + + --bg-app: #eee8d5; + --bg-raised: #e9e4cf; + --bg-panel: #fdf6e3; + --bg-panel-header: #e7e0c9; + + --border: rgba(0, 43, 54, 0.1); + --border-strong: rgba(0, 43, 54, 0.14); + --border-focus: #268bd2; + + --text: #657b83; + --text-muted: #586e75; + --text-faint: #93a1a1; + --text-bright: #002b36; + + --accent: #268bd2; + --accent-hover: #227dbc; + --accent-soft: rgba(38, 139, 210, 0.12); + --accent-cool: #2aa198; + --accent-cool-soft: rgba(42, 161, 152, 0.1); + + --syntax-type: #2aa198; + --syntax-keyword: #859900; + + --overlay-1: rgba(0, 43, 54, 0.03); + --overlay-2: rgba(0, 43, 54, 0.056); + --overlay-3: rgba(0, 43, 54, 0.082); + --overlay-4: rgba(0, 43, 54, 0.11); + --overlay-5: rgba(0, 43, 54, 0.15); + --overlay-6: rgba(0, 43, 54, 0.21); + + --danger: #dc322f; + --danger-soft: rgba(220, 50, 47, 0.12); + --warning: #b58900; + --success: #859900; + + --term-fg: #586e75; + --term-bg: #fdf6e3; + --term-surface: #eee8d5; + --term-cursor: #268bd2; + + --on-accent: #fdf6e3; + --scrim-bg: rgba(0, 43, 54, 0.32); + + --agent-claude: #cb4b16; + --agent-codex: #859900; + --agent-gemini: #268bd2; + --agent-copilot: #6c71c4; + --agent-cursor: #2aa198; + + --git-lane-0: #268bd2; + --git-lane-1: #6c71c4; + --git-lane-2: #859900; + --git-lane-3: #b58900; + --git-lane-4: #dc322f; + --git-lane-5: #2aa198; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 90%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 12%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: var(--success); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 30%, transparent); + --danger-text-soft: var(--danger); + --hook-brand-bg: var(--bg-panel); + --hook-brand-fg: var(--text); +} + + +/* Nord — https://www.nordtheme.com */ +[data-theme="nord"] { + color-scheme: dark; + + --bg-app: #2e3440; + --bg-raised: #3b4252; + --bg-panel: #3b4252; + --bg-panel-header: #434c5e; + + --border: rgba(236, 239, 244, 0.08); + --border-strong: rgba(236, 239, 244, 0.14); + --border-focus: #88c0d0; + + --text: #eceff4; + --text-muted: #d8dee9; + --text-faint: #4c566a; + --text-bright: #ffffff; + + --accent: #88c0d0; + --accent-hover: #8fbcbb; + --accent-soft: rgba(136, 192, 208, 0.18); + --accent-cool: #81a1c1; + --accent-cool-soft: rgba(129, 161, 193, 0.14); + + --syntax-type: #8fbcbb; + --syntax-keyword: #81a1c1; + + --overlay-1: rgba(236, 239, 244, 0.04); + --overlay-2: rgba(236, 239, 244, 0.07); + --overlay-3: rgba(236, 239, 244, 0.1); + --overlay-4: rgba(236, 239, 244, 0.13); + --overlay-5: rgba(236, 239, 244, 0.18); + --overlay-6: rgba(236, 239, 244, 0.26); + + --danger: #bf616a; + --danger-soft: rgba(191, 97, 106, 0.2); + --warning: #ebcb8b; + --success: #a3be8c; + + --term-fg: #eceff4; + --term-bg: #2e3440; + --term-surface: #3b4252; + --term-cursor: #88c0d0; + + --on-accent: #2e3440; + --scrim-bg: rgba(20, 24, 32, 0.58); + + --agent-claude: #d08770; + --agent-codex: #a3be8c; + --agent-gemini: #5e81ac; + --agent-copilot: #b48ead; + --agent-cursor: #88c0d0; + + --git-lane-0: #88c0d0; + --git-lane-1: #81a1c1; + --git-lane-2: #a3be8c; + --git-lane-3: #ebcb8b; + --git-lane-4: #bf616a; + --git-lane-5: #b48ead; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 88%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 10%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: color-mix(in srgb, var(--success) 75%, var(--text)); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 32%, transparent); + --danger-text-soft: color-mix(in srgb, var(--danger) 70%, var(--text)); + --hook-brand-bg: color-mix(in srgb, var(--text-bright) 92%, transparent); + --hook-brand-fg: var(--bg-app); +} + + +/* One Dark — Atom baseline palette */ +[data-theme="one-dark"] { + color-scheme: dark; + + --bg-app: #21252b; + --bg-raised: #282c34; + --bg-panel: #282c34; + --bg-panel-header: #353b45; + + --border: rgba(171, 178, 191, 0.08); + --border-strong: rgba(171, 178, 191, 0.15); + --border-focus: #61afef; + + --text: #abb2bf; + --text-muted: #828997; + --text-faint: #5c6370; + --text-bright: #dcdfe4; + + --accent: #61afef; + --accent-hover: #73b6f2; + --accent-soft: rgba(97, 175, 239, 0.18); + --accent-cool: #56b6c2; + --accent-cool-soft: rgba(86, 182, 194, 0.14); + + --syntax-type: #56b6c2; + --syntax-keyword: #c678dd; + + --overlay-1: rgba(255, 255, 255, 0.035); + --overlay-2: rgba(255, 255, 255, 0.06); + --overlay-3: rgba(255, 255, 255, 0.085); + --overlay-4: rgba(255, 255, 255, 0.12); + --overlay-5: rgba(255, 255, 255, 0.17); + --overlay-6: rgba(255, 255, 255, 0.24); + + --danger: #e06c75; + --danger-soft: rgba(224, 108, 117, 0.2); + --warning: #e5c07b; + --success: #98c379; + + --term-fg: #abb2bf; + --term-bg: #21252b; + --term-surface: #282c34; + --term-cursor: #61afef; + + --on-accent: #21252b; + --scrim-bg: rgba(0, 0, 0, 0.55); + + --agent-claude: #d19a66; + --agent-codex: #98c379; + --agent-gemini: #61afef; + --agent-copilot: #c678dd; + --agent-cursor: #56b6c2; + + --git-lane-0: #61afef; + --git-lane-1: #c678dd; + --git-lane-2: #98c379; + --git-lane-3: #e5c07b; + --git-lane-4: #e06c75; + --git-lane-5: #56b6c2; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 88%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 10%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: color-mix(in srgb, var(--success) 75%, var(--text)); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 32%, transparent); + --danger-text-soft: color-mix(in srgb, var(--danger) 70%, var(--text)); + --hook-brand-bg: color-mix(in srgb, var(--text-bright) 92%, transparent); + --hook-brand-fg: var(--bg-app); +} + + +/* Catppuccin Mocha — github.com/catppuccin/catppuccin */ +[data-theme="catppuccin-mocha"] { + color-scheme: dark; + + --bg-app: #181825; + --bg-raised: #181825; + --bg-panel: #1e1e2e; + --bg-panel-header: #262637; + + --border: rgba(205, 214, 244, 0.08); + --border-strong: rgba(205, 214, 244, 0.14); + --border-focus: #cba6f7; + + --text: #cdd6f4; + --text-muted: #bac2de; + --text-faint: #6c7086; + --text-bright: #f5e0dc; + + --accent: #cba6f7; + --accent-hover: #d4b4f9; + --accent-soft: rgba(203, 166, 247, 0.18); + --accent-cool: #89dceb; + --accent-cool-soft: rgba(137, 220, 235, 0.12); + + --syntax-type: #94e2d5; + --syntax-keyword: #f38ba8; + + --overlay-1: rgba(205, 214, 244, 0.04); + --overlay-2: rgba(205, 214, 244, 0.07); + --overlay-3: rgba(205, 214, 244, 0.1); + --overlay-4: rgba(205, 214, 244, 0.13); + --overlay-5: rgba(205, 214, 244, 0.18); + --overlay-6: rgba(205, 214, 244, 0.26); + + --danger: #f38ba8; + --danger-soft: rgba(243, 139, 168, 0.2); + --warning: #f9e2af; + --success: #a6e3a1; + + --term-fg: #cdd6f4; + --term-bg: #181825; + --term-surface: #1e1e2e; + --term-cursor: #cba6f7; + + --on-accent: #181825; + --scrim-bg: rgba(0, 0, 0, 0.55); + + --agent-claude: #fab387; + --agent-codex: #a6e3a1; + --agent-gemini: #89b4fa; + --agent-copilot: #cba6f7; + --agent-cursor: #f5c2e7; + + --git-lane-0: #89b4fa; + --git-lane-1: #cba6f7; + --git-lane-2: #a6e3a1; + --git-lane-3: #f9e2af; + --git-lane-4: #f38ba8; + --git-lane-5: #94e2d5; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 88%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 10%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: color-mix(in srgb, var(--success) 75%, var(--text)); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 32%, transparent); + --danger-text-soft: color-mix(in srgb, var(--danger) 70%, var(--text)); + --hook-brand-bg: color-mix(in srgb, var(--text-bright) 92%, transparent); + --hook-brand-fg: var(--bg-app); +} + + +[data-theme="catppuccin-latte"] { + color-scheme: light; + + --bg-app: #e6e9ef; + --bg-raised: #dce0e8; + --bg-panel: #eff1f5; + --bg-panel-header: #dce0e8; + + --border: rgba(76, 79, 105, 0.1); + --border-strong: rgba(76, 79, 105, 0.15); + --border-focus: #8839ef; + + --text: #4c4f69; + --text-muted: #5c5f77; + --text-faint: #8c8fa1; + --text-bright: #181825; + + --accent: #8839ef; + --accent-hover: #7838d8; + --accent-soft: rgba(136, 57, 239, 0.12); + --accent-cool: #209fb5; + --accent-cool-soft: rgba(32, 159, 181, 0.1); + + --syntax-type: #179299; + --syntax-keyword: #d20f39; + + --overlay-1: rgba(0, 0, 0, 0.03); + --overlay-2: rgba(0, 0, 0, 0.055); + --overlay-3: rgba(0, 0, 0, 0.08); + --overlay-4: rgba(0, 0, 0, 0.11); + --overlay-5: rgba(0, 0, 0, 0.15); + --overlay-6: rgba(0, 0, 0, 0.22); + + --danger: #d20f39; + --danger-soft: rgba(210, 15, 57, 0.12); + --warning: #df8e1d; + --success: #40a02b; + + --term-fg: #4c4f69; + --term-bg: #eff1f5; + --term-surface: #e6e9ef; + --term-cursor: #8839ef; + + --on-accent: #eff1f5; + --scrim-bg: rgba(76, 79, 105, 0.35); + + --agent-claude: #dc8a78; + --agent-codex: #40a02b; + --agent-gemini: #209fb5; + --agent-copilot: #8839ef; + --agent-cursor: #8839ef; + + --git-lane-0: #1e66f5; + --git-lane-1: #8839ef; + --git-lane-2: #40a02b; + --git-lane-3: #df8e1d; + --git-lane-4: #d20f39; + --git-lane-5: #179299; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 90%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 12%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: var(--success); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 30%, transparent); + --danger-text-soft: var(--danger); + --hook-brand-bg: var(--bg-panel); + --hook-brand-fg: var(--text); +} + + +/* Tokyo Night — github.com/folke/tokyonight.nvim */ +[data-theme="tokyo-night"] { + color-scheme: dark; + + --bg-app: #16161e; + --bg-raised: #16161e; + --bg-panel: #1a1b26; + --bg-panel-header: #1f2335; + + --border: rgba(192, 202, 245, 0.08); + --border-strong: rgba(192, 202, 245, 0.14); + --border-focus: #7aa2f7; + + --text: #c0caf5; + --text-muted: #a9b1d6; + --text-faint: #565f89; + --text-bright: #e0eaf8; + + --accent: #7aa2f7; + --accent-hover: #8db0f9; + --accent-soft: rgba(122, 162, 247, 0.18); + --accent-cool: #7dcfff; + --accent-cool-soft: rgba(125, 207, 255, 0.12); + + --syntax-type: #7dcfff; + --syntax-keyword: #bb9af7; + + --overlay-1: rgba(192, 202, 245, 0.04); + --overlay-2: rgba(192, 202, 245, 0.07); + --overlay-3: rgba(192, 202, 245, 0.1); + --overlay-4: rgba(192, 202, 245, 0.13); + --overlay-5: rgba(192, 202, 245, 0.18); + --overlay-6: rgba(192, 202, 245, 0.26); + + --danger: #f7768e; + --danger-soft: rgba(247, 118, 142, 0.2); + --warning: #e0af68; + --success: #9ece6a; + + --term-fg: #c0caf5; + --term-bg: #16161e; + --term-surface: #1a1b26; + --term-cursor: #7aa2f7; + + --on-accent: #16161e; + --scrim-bg: rgba(0, 0, 0, 0.56); + + --agent-claude: #ff9e64; + --agent-codex: #9ece6a; + --agent-gemini: #7aa2f7; + --agent-copilot: #bb9af7; + --agent-cursor: #7dcfff; + + --git-lane-0: #7aa2f7; + --git-lane-1: #bb9af7; + --git-lane-2: #9ece6a; + --git-lane-3: #e0af68; + --git-lane-4: #f7768e; + --git-lane-5: #73daca; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 88%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 10%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: color-mix(in srgb, var(--success) 75%, var(--text)); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 32%, transparent); + --danger-text-soft: color-mix(in srgb, var(--danger) 70%, var(--text)); + --hook-brand-bg: color-mix(in srgb, var(--text-bright) 92%, transparent); + --hook-brand-fg: var(--bg-app); +} + + + +/* Rosé Pine — rosepinetheme.com */ +[data-theme="rose-pine"] { + color-scheme: dark; + + --bg-app: #191724; + --bg-raised: #1f1d2e; + --bg-panel: #26233a; + --bg-panel-header: #21202e; + + --border: rgba(224, 222, 244, 0.08); + --border-strong: rgba(224, 222, 244, 0.14); + --border-focus: #c4a7e7; + + --text: #e0def4; + --text-muted: #908caa; + --text-faint: #6e6a86; + --text-bright: #faf4ed; + + --accent: #c4a7e7; + --accent-hover: #d0b8ed; + --accent-soft: rgba(196, 167, 231, 0.18); + --accent-cool: #9ccfd8; + --accent-cool-soft: rgba(156, 207, 216, 0.14); + + --syntax-type: #9ccfd8; + --syntax-keyword: #ebbcba; + + --overlay-1: rgba(224, 222, 244, 0.04); + --overlay-2: rgba(224, 222, 244, 0.07); + --overlay-3: rgba(224, 222, 244, 0.1); + --overlay-4: rgba(224, 222, 244, 0.13); + --overlay-5: rgba(224, 222, 244, 0.18); + --overlay-6: rgba(224, 222, 244, 0.26); + + --danger: #eb6f92; + --danger-soft: rgba(235, 111, 146, 0.2); + --warning: #f6c177; + --success: #31748f; + + --term-fg: #e0def4; + --term-bg: #191724; + --term-surface: #26233a; + --term-cursor: #c4a7e7; + + --on-accent: #191724; + --scrim-bg: rgba(0, 0, 0, 0.56); + + --agent-claude: #ebbcba; + --agent-codex: #9ccfd8; + --agent-gemini: #31748f; + --agent-copilot: #c4a7e7; + --agent-cursor: #eb6f92; + + --git-lane-0: #9ccfd8; + --git-lane-1: #c4a7e7; + --git-lane-2: #31748f; + --git-lane-3: #f6c177; + --git-lane-4: #eb6f92; + --git-lane-5: #ebbcba; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 88%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 10%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: color-mix(in srgb, var(--success) 75%, var(--text)); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 32%, transparent); + --danger-text-soft: color-mix(in srgb, var(--danger) 70%, var(--text)); + --hook-brand-bg: color-mix(in srgb, var(--text-bright) 92%, transparent); + --hook-brand-fg: var(--bg-app); +} + + +/* Rosé Pine Dawn */ +[data-theme="rose-pine-dawn"] { + color-scheme: light; + + --bg-app: #faf4ed; + --bg-raised: #fffaf3; + --bg-panel: #ffffff; + --bg-panel-header: #f2e9e1; + + --border: rgba(87, 82, 121, 0.1); + --border-strong: rgba(87, 82, 121, 0.16); + --border-focus: #286983; + + --text: #575279; + --text-muted: #9893a5; + --text-faint: #797593; + --text-bright: #1f1d2e; + + --accent: #286983; + --accent-hover: #1f5570; + --accent-soft: rgba(40, 105, 131, 0.14); + --accent-cool: #56949f; + --accent-cool-soft: rgba(86, 148, 159, 0.12); + + --syntax-type: #56949f; + --syntax-keyword: #907aa0; + + --overlay-1: rgba(87, 82, 121, 0.04); + --overlay-2: rgba(87, 82, 121, 0.07); + --overlay-3: rgba(87, 82, 121, 0.1); + --overlay-4: rgba(87, 82, 121, 0.13); + --overlay-5: rgba(87, 82, 121, 0.18); + --overlay-6: rgba(87, 82, 121, 0.24); + + --danger: #b4637a; + --danger-soft: rgba(180, 99, 122, 0.14); + --warning: #ea9d34; + --success: #286983; + + --term-fg: #575279; + --term-bg: #faf4ed; + --term-surface: #fffaf3; + --term-cursor: #286983; + + --on-accent: #faf4ed; + --scrim-bg: rgba(31, 29, 46, 0.45); + + --agent-claude: #d7827e; + --agent-codex: #56949f; + --agent-gemini: #286983; + --agent-copilot: #907aa0; + --agent-cursor: #b4637a; + + --git-lane-0: #286983; + --git-lane-1: #907aa0; + --git-lane-2: #56949f; + --git-lane-3: #ea9d34; + --git-lane-4: #b4637a; + --git-lane-5: #d7827e; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 90%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 12%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: var(--success); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 30%, transparent); + --danger-text-soft: var(--danger); + --hook-brand-bg: var(--bg-panel); + --hook-brand-fg: var(--text); +} + + +/* Everforest Dark — github.com/sainnhe/everforest */ +[data-theme="everforest-dark"] { + color-scheme: dark; + + --bg-app: #2d353b; + --bg-raised: #343f44; + --bg-panel: #3d484d; + --bg-panel-header: #343f44; + + --border: rgba(211, 198, 170, 0.08); + --border-strong: rgba(211, 198, 170, 0.14); + --border-focus: #a7c080; + + --text: #d3c6aa; + --text-muted: #a7c080; + --text-faint: #7a8478; + --text-bright: #fdf6e3; + + --accent: #a7c080; + --accent-hover: #b5cc92; + --accent-soft: rgba(167, 192, 128, 0.18); + --accent-cool: #7fbbb3; + --accent-cool-soft: rgba(127, 187, 179, 0.14); + + --syntax-type: #7fbbb3; + --syntax-keyword: #e67e80; + + --overlay-1: rgba(211, 198, 170, 0.04); + --overlay-2: rgba(211, 198, 170, 0.07); + --overlay-3: rgba(211, 198, 170, 0.1); + --overlay-4: rgba(211, 198, 170, 0.13); + --overlay-5: rgba(211, 198, 170, 0.18); + --overlay-6: rgba(211, 198, 170, 0.26); + + --danger: #e67e80; + --danger-soft: rgba(230, 126, 128, 0.2); + --warning: #dbbc7f; + --success: #83c092; + + --term-fg: #d3c6aa; + --term-bg: #2d353b; + --term-surface: #3d484d; + --term-cursor: #a7c080; + + --on-accent: #2d353b; + --scrim-bg: rgba(0, 0, 0, 0.56); + + --agent-claude: #e69875; + --agent-codex: #83c092; + --agent-gemini: #7fbbb3; + --agent-copilot: #d699b6; + --agent-cursor: #a7c080; + + --git-lane-0: #7fbbb3; + --git-lane-1: #d699b6; + --git-lane-2: #83c092; + --git-lane-3: #dbbc7f; + --git-lane-4: #e67e80; + --git-lane-5: #a7c080; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 88%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 10%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: color-mix(in srgb, var(--success) 75%, var(--text)); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 32%, transparent); + --danger-text-soft: color-mix(in srgb, var(--danger) 70%, var(--text)); + --hook-brand-bg: color-mix(in srgb, var(--text-bright) 92%, transparent); + --hook-brand-fg: var(--bg-app); +} + + +/* Kanagawa — github.com/rebelot/kanagawa.nvim */ +[data-theme="kanagawa"] { + color-scheme: dark; + + --bg-app: #16161d; + --bg-raised: #181820; + --bg-panel: #1f1f28; + --bg-panel-header: #1a1a22; + + --border: rgba(220, 215, 186, 0.08); + --border-strong: rgba(220, 215, 186, 0.14); + --border-focus: #7e9cd8; + + --text: #dcd7ba; + --text-muted: #c8c093; + --text-faint: #727169; + --text-bright: #f2ecbc; + + --accent: #7e9cd8; + --accent-hover: #8faae0; + --accent-soft: rgba(126, 156, 216, 0.18); + --accent-cool: #7fb4ca; + --accent-cool-soft: rgba(127, 180, 202, 0.14); + + --syntax-type: #7fb4ca; + --syntax-keyword: #957fb8; + + --overlay-1: rgba(220, 215, 186, 0.04); + --overlay-2: rgba(220, 215, 186, 0.07); + --overlay-3: rgba(220, 215, 186, 0.1); + --overlay-4: rgba(220, 215, 186, 0.13); + --overlay-5: rgba(220, 215, 186, 0.18); + --overlay-6: rgba(220, 215, 186, 0.26); + + --danger: #c34043; + --danger-soft: rgba(195, 64, 67, 0.2); + --warning: #ffa066; + --success: #98bb6c; + + --term-fg: #dcd7ba; + --term-bg: #16161d; + --term-surface: #1f1f28; + --term-cursor: #7e9cd8; + + --on-accent: #16161d; + --scrim-bg: rgba(0, 0, 0, 0.56); + + --agent-claude: #ffa066; + --agent-codex: #98bb6c; + --agent-gemini: #7e9cd8; + --agent-copilot: #957fb8; + --agent-cursor: #7fb4ca; + + --git-lane-0: #7e9cd8; + --git-lane-1: #957fb8; + --git-lane-2: #98bb6c; + --git-lane-3: #ffa066; + --git-lane-4: #c34043; + --git-lane-5: #7fb4ca; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 88%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 10%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: color-mix(in srgb, var(--success) 75%, var(--text)); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 32%, transparent); + --danger-text-soft: color-mix(in srgb, var(--danger) 70%, var(--text)); + --hook-brand-bg: color-mix(in srgb, var(--text-bright) 92%, transparent); + --hook-brand-fg: var(--bg-app); +} + + +/* GitHub Dark — primer design tokens */ +[data-theme="github-dark"] { + color-scheme: dark; + + --bg-app: #0d1117; + --bg-raised: #010409; + --bg-panel: #161b22; + --bg-panel-header: #21262d; + + --border: rgba(240, 246, 252, 0.1); + --border-strong: rgba(240, 246, 252, 0.16); + --border-focus: #58a6ff; + + --text: #e6edf3; + --text-muted: #8b949e; + --text-faint: #6e7681; + --text-bright: #f0f6fc; + + --accent: #58a6ff; + --accent-hover: #79b8ff; + --accent-soft: rgba(88, 166, 255, 0.16); + --accent-cool: #79c0ff; + --accent-cool-soft: rgba(121, 192, 255, 0.12); + + --syntax-type: #79c0ff; + --syntax-keyword: #d2a8ff; + + --overlay-1: rgba(240, 246, 252, 0.04); + --overlay-2: rgba(240, 246, 252, 0.07); + --overlay-3: rgba(240, 246, 252, 0.1); + --overlay-4: rgba(240, 246, 252, 0.13); + --overlay-5: rgba(240, 246, 252, 0.18); + --overlay-6: rgba(240, 246, 252, 0.26); + + --danger: #f85149; + --danger-soft: rgba(248, 81, 73, 0.18); + --warning: #d29922; + --success: #3fb950; + + --term-fg: #e6edf3; + --term-bg: #0d1117; + --term-surface: #161b22; + --term-cursor: #58a6ff; + + --on-accent: #0d1117; + --scrim-bg: rgba(0, 0, 0, 0.6); + + --agent-claude: #ffa657; + --agent-codex: #3fb950; + --agent-gemini: #58a6ff; + --agent-copilot: #d2a8ff; + --agent-cursor: #79c0ff; + + --git-lane-0: #58a6ff; + --git-lane-1: #d2a8ff; + --git-lane-2: #3fb950; + --git-lane-3: #d29922; + --git-lane-4: #f85149; + --git-lane-5: #79c0ff; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 88%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 10%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: color-mix(in srgb, var(--success) 75%, var(--text)); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 32%, transparent); + --danger-text-soft: color-mix(in srgb, var(--danger) 70%, var(--text)); + --hook-brand-bg: color-mix(in srgb, var(--text-bright) 92%, transparent); + --hook-brand-fg: var(--bg-app); +} + + +/* Night Owl — github.com/sdras/night-owl-vscode-theme */ +[data-theme="night-owl"] { + color-scheme: dark; + + --bg-app: #011627; + --bg-raised: #011627; + --bg-panel: #0b2942; + --bg-panel-header: #0d2137; + + --border: rgba(214, 222, 235, 0.08); + --border-strong: rgba(214, 222, 235, 0.14); + --border-focus: #82aaff; + + --text: #d6deeb; + --text-muted: #89a4bb; + --text-faint: #637777; + --text-bright: #ffffff; + + --accent: #82aaff; + --accent-hover: #9bb8ff; + --accent-soft: rgba(130, 170, 255, 0.18); + --accent-cool: #7fdbca; + --accent-cool-soft: rgba(127, 219, 202, 0.14); + + --syntax-type: #7fdbca; + --syntax-keyword: #c792ea; + + --overlay-1: rgba(214, 222, 235, 0.04); + --overlay-2: rgba(214, 222, 235, 0.07); + --overlay-3: rgba(214, 222, 235, 0.1); + --overlay-4: rgba(214, 222, 235, 0.13); + --overlay-5: rgba(214, 222, 235, 0.18); + --overlay-6: rgba(214, 222, 235, 0.26); + + --danger: #ff6363; + --danger-soft: rgba(255, 99, 99, 0.2); + --warning: #ffcb8b; + --success: #c5e478; + + --term-fg: #d6deeb; + --term-bg: #011627; + --term-surface: #0b2942; + --term-cursor: #82aaff; + + --on-accent: #011627; + --scrim-bg: rgba(0, 0, 0, 0.58); + + --agent-claude: #ffcb8b; + --agent-codex: #c5e478; + --agent-gemini: #82aaff; + --agent-copilot: #c792ea; + --agent-cursor: #7fdbca; + + --git-lane-0: #82aaff; + --git-lane-1: #c792ea; + --git-lane-2: #c5e478; + --git-lane-3: #ffcb8b; + --git-lane-4: #ff6363; + --git-lane-5: #7fdbca; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 88%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 10%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: color-mix(in srgb, var(--success) 75%, var(--text)); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 32%, transparent); + --danger-text-soft: color-mix(in srgb, var(--danger) 70%, var(--text)); + --hook-brand-bg: color-mix(in srgb, var(--text-bright) 92%, transparent); + --hook-brand-fg: var(--bg-app); +} + + +/* Ayu Mirage — github.com/ayu-theme/ayu-colors */ +[data-theme="ayu-mirage"] { + color-scheme: dark; + + --bg-app: #1f2430; + --bg-raised: #1a1f29; + --bg-panel: #242936; + --bg-panel-header: #1f2430; + + --border: rgba(203, 204, 198, 0.08); + --border-strong: rgba(203, 204, 198, 0.14); + --border-focus: #5ccfe6; + + --text: #cbccc6; + --text-muted: #707a8c; + --text-faint: #5c6773; + --text-bright: #f3f4f5; + + --accent: #5ccfe6; + --accent-hover: #73d8ec; + --accent-soft: rgba(92, 207, 230, 0.18); + --accent-cool: #95e6cb; + --accent-cool-soft: rgba(149, 230, 203, 0.14); + + --syntax-type: #95e6cb; + --syntax-keyword: #ffcc66; + + --overlay-1: rgba(203, 204, 198, 0.04); + --overlay-2: rgba(203, 204, 198, 0.07); + --overlay-3: rgba(203, 204, 198, 0.1); + --overlay-4: rgba(203, 204, 198, 0.13); + --overlay-5: rgba(203, 204, 198, 0.18); + --overlay-6: rgba(203, 204, 198, 0.26); + + --danger: #f28779; + --danger-soft: rgba(242, 135, 121, 0.2); + --warning: #ffcc66; + --success: #87d96c; + + --term-fg: #cbccc6; + --term-bg: #1f2430; + --term-surface: #242936; + --term-cursor: #5ccfe6; + + --on-accent: #1f2430; + --scrim-bg: rgba(0, 0, 0, 0.56); + + --agent-claude: #ffcc66; + --agent-codex: #87d96c; + --agent-gemini: #5ccfe6; + --agent-copilot: #d4bfff; + --agent-cursor: #95e6cb; + + --git-lane-0: #5ccfe6; + --git-lane-1: #d4bfff; + --git-lane-2: #87d96c; + --git-lane-3: #ffcc66; + --git-lane-4: #f28779; + --git-lane-5: #95e6cb; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 88%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 10%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: color-mix(in srgb, var(--success) 75%, var(--text)); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 32%, transparent); + --danger-text-soft: color-mix(in srgb, var(--danger) 70%, var(--text)); + --hook-brand-bg: color-mix(in srgb, var(--text-bright) 92%, transparent); + --hook-brand-fg: var(--bg-app); +} + + +/* Catppuccin Frappé — catppuccin.com */ +[data-theme="catppuccin-frappe"] { + color-scheme: dark; + + --bg-app: #232634; + --bg-raised: #292c3c; + --bg-panel: #303446; + --bg-panel-header: #292c3c; + + --border: rgba(198, 208, 245, 0.08); + --border-strong: rgba(198, 208, 245, 0.14); + --border-focus: #ca9ee6; + + --text: #c6d0f5; + --text-muted: #a5adce; + --text-faint: #737994; + --text-bright: #ffffff; + + --accent: #ca9ee6; + --accent-hover: #d4b0eb; + --accent-soft: rgba(202, 158, 230, 0.18); + --accent-cool: #99d1db; + --accent-cool-soft: rgba(153, 209, 219, 0.14); + + --syntax-type: #99d1db; + --syntax-keyword: #ca9ee6; + + --overlay-1: rgba(198, 208, 245, 0.04); + --overlay-2: rgba(198, 208, 245, 0.07); + --overlay-3: rgba(198, 208, 245, 0.1); + --overlay-4: rgba(198, 208, 245, 0.13); + --overlay-5: rgba(198, 208, 245, 0.18); + --overlay-6: rgba(198, 208, 245, 0.26); + + --danger: #e78284; + --danger-soft: rgba(231, 130, 132, 0.2); + --warning: #e5c890; + --success: #a6d189; + + --term-fg: #c6d0f5; + --term-bg: #232634; + --term-surface: #303446; + --term-cursor: #ca9ee6; + + --on-accent: #232634; + --scrim-bg: rgba(0, 0, 0, 0.56); + + --agent-claude: #ef9f76; + --agent-codex: #a6d189; + --agent-gemini: #99d1db; + --agent-copilot: #ca9ee6; + --agent-cursor: #babbf1; + + --git-lane-0: #99d1db; + --git-lane-1: #ca9ee6; + --git-lane-2: #a6d189; + --git-lane-3: #e5c890; + --git-lane-4: #e78284; + --git-lane-5: #babbf1; + + --fg-main: var(--text); + --text-secondary: color-mix(in srgb, var(--text) 92%, transparent); + --text-hint: color-mix(in srgb, var(--text-muted) 88%, transparent); + --accent-control: color-mix(in srgb, var(--accent) 85%, transparent); + --success-surface: color-mix(in srgb, var(--success) 10%, transparent); + --success-border: color-mix(in srgb, var(--success) 35%, transparent); + --success-text: color-mix(in srgb, var(--success) 75%, var(--text)); + --danger-surface: color-mix(in srgb, var(--danger) 10%, transparent); + --danger-border-soft: color-mix(in srgb, var(--danger) 32%, transparent); + --danger-text-soft: color-mix(in srgb, var(--danger) 70%, var(--text)); + --hook-brand-bg: color-mix(in srgb, var(--text-bright) 92%, transparent); + --hook-brand-fg: var(--bg-app); +}