diff --git a/.claude/workflows/antigravity-theme-tune.js b/.claude/workflows/antigravity-theme-tune.js new file mode 100644 index 0000000..5d28b0e --- /dev/null +++ b/.claude/workflows/antigravity-theme-tune.js @@ -0,0 +1,187 @@ +export const meta = { + name: 'antigravity-theme-tune', + description: 'Iteratively tune the Antigravity background theme against the live app via CDP screenshots', + whenToUse: 'Refine the translucent/background Antigravity theme to visual perfection: multi-lens critique of live screenshots, then synthesize structural CSS adjustments, looping until scores converge. Edits ONLY the Antigravity-private template; never touches Codex files or theme.json.', + phases: [ + { title: 'Setup', detail: 'render CSS from the Antigravity template, inject into live Antigravity, capture baseline shots + light/text scans' }, + { title: 'Critique', detail: 'parallel lenses score the screenshots and propose template adjustments' }, + { title: 'Synthesize', detail: 'merge adjustments, edit antigravity.template.css, re-render, re-inject, re-capture' }, + { title: 'Final', detail: 'capture final shots and write a report' }, + ], +}; + +// ---- config (args override) ---- +const REPO = (args && args.repoRoot) || '/Users/alysechen/alysechen/github/agent-theme'; +const LAB = `${REPO}/.theme-lab`; +const THEME = (args && args.theme) || 'changli'; +// The STRUCTURE template (Antigravity-private). The synthesizer edits ONLY this. +const TMPL = `${LAB}/antigravity.template.css`; +// Knob VALUES come from this theme.json — READ ONLY (shared with Codex; editing +// it would change Codex's look, which is explicitly forbidden). +const THEME_JSON = `${REPO}/themes/${THEME}/theme.json`; +const CSSOUT = `${LAB}/css/${THEME}-ag.css`; +// Antigravity harness: occluded-safe (NO_ACTIVATE) so we never steal the user's focus. +const HARNESS= `AGENT=antigravity NO_ACTIVATE=1 node ${LAB}/cdp.mjs`; +const RENDER = `node ${LAB}/render-antigravity.mjs ${THEME} ${CSSOUT}`; +const SHOTS = `${LAB}/shots/ag`; +const SRCIMG = `${LAB}/src/${THEME}-768.png`; +const ROUNDS = (args && args.rounds) ?? 4; +const THRESHOLD = (args && args.threshold) ?? 88; + +const LENSES = [ + { key: 'composition', title: 'Background composition & framing', + focus: `How the ${THEME} wallpaper reads behind the Antigravity agent UI: the centered chat-input card and the left conversation sidebar sit over the image — is the focal point balanced vs where text lands, is the legibility scrim (#root linear-gradient via --cl-scrim-top/-mid/-bot) balanced top-to-bottom? background-position is fixed by theme.json (do NOT change it). Knobs you MAY change in the template: the #root scrim gradient stops/opacities, any extra overlay rules.` }, + { key: 'readability', title: 'Text readability & contrast', + focus: `Legibility of every text level over the wallpaper + glass: sidebar items ("New Conversation", "Conversation History", "Settings"), the "Projects" section labels, project/conversation names, the grey timestamps (9d/10d), the chat placeholder ("Ask anything, @ to mention"), the model selector ("Gemini 3.5 Flash"), "Open IDE". Read the textscan json for any dark-on-dark text. Knobs: text-shadow rules in the template (.text-foreground / .text-muted-foreground / placeholder), the --cl-scrim-* darkening, and which --cl-ink level / glass token each surface maps to. Do NOT edit theme.json ink values.` }, + { key: 'glass', title: 'Glass panel quality & cohesion', + focus: `Frosted-glass surfaces: the conversation sidebar (.bg-sidebar), the centered chat-input card (.bg-card-border frame + .bg-card field), dropdowns/menus/dialogs ([role=menu], .bg-popover). Blur amount, tint opacity, border crispness, drop shadow — do they read as distinct elevated surfaces or muddy/over-transparent? Knobs: the backdrop-filter blur expressions, box-shadow, border rules, and which --cl-glass / --cl-glass-soft / --cl-glass-strong each maps to.` }, + { key: 'accent', title: 'Accent colour cohesion', + focus: `Antigravity's brand accent is mauve #8839EF; the theme remaps --primary and links/focus/selection to the ${THEME} accent via the accent block. Check: primary buttons, the send button, links, focus ring, ::selection, the model/agent selector chips — do they harmonise with the warm wallpaper or clash? Is on-accent text (--primary-foreground) legible? Knobs: the accent block rules (the accent VALUES come from theme.json; you may adjust which elements get the accent and the --primary-foreground contrast strategy in the template).` }, + { key: 'seams', title: 'Seams, leftover light surfaces & artifacts', + focus: `Hunt for any surface that did NOT get themed: read the lightscan json for opaque LIGHT rectangles (un-overridden shadcn/vscode tokens still showing Catppuccin Latte light colours), stray hard edges between sidebar↔main, the top bar (40px), the card border frame, scrollbars. Each light surface = a token/selector you must add to the template (override its shadcn --token to a --cl-glass* / transparent, or add a selector rule). Knobs: add token overrides in the html{} block, add module selector rules, border rules.` }, +]; + +const CRITIQUE_SCHEMA = { + type: 'object', additionalProperties: false, + required: ['dimension', 'score', 'summary', 'issues', 'adjustments'], + properties: { + dimension: { type: 'string' }, + score: { type: 'integer', minimum: 0, maximum: 100, description: '100 = perfect, nothing to improve on this dimension' }, + summary: { type: 'string' }, + issues: { type: 'array', items: { type: 'string' }, description: 'concrete problems seen in the screenshots / scans' }, + adjustments: { + type: 'array', + items: { + type: 'object', additionalProperties: false, + required: ['target', 'change', 'reason'], + properties: { + target: { type: 'string', description: 'a knob name like --cl-scrim-bot, a shadcn token like --muted, or a CSS selector to add/modify in antigravity.template.css' }, + change: { type: 'string', description: 'precise new value or rule, e.g. "rgba(13,9,6,.70)" or "add: backdrop-filter blur(10px)"' }, + reason: { type: 'string' }, + }, + }, + }, + }, +}; + +const SETUP_SCHEMA = { + type: 'object', additionalProperties: false, + required: ['ok', 'shotPrefix', 'notes'], + properties: { + ok: { type: 'boolean' }, + shotPrefix: { type: 'string', description: 'absolute path prefix of captured shots, without the -full.png suffix' }, + notes: { type: 'string' }, + }, +}; + +const SYNTH_SCHEMA = { + type: 'object', additionalProperties: false, + required: ['shotPrefix', 'appliedChanges', 'notes'], + properties: { + shotPrefix: { type: 'string', description: 'absolute path prefix of the NEW shots captured after re-injecting' }, + appliedChanges: { type: 'array', items: { type: 'string' } }, + notes: { type: 'string' }, + }, +}; + +const FINAL_SCHEMA = { + type: 'object', additionalProperties: false, + required: ['summary', 'reportPath', 'perDimension'], + properties: { + summary: { type: 'string' }, + reportPath: { type: 'string' }, + perDimension: { type: 'array', items: { + type: 'object', additionalProperties: false, + required: ['dimension', 'score'], + properties: { dimension: { type: 'string' }, score: { type: 'integer' } }, + } }, + }, +}; + +// ============================ run ============================ +phase('Setup'); +const setup = await agent( + `You tune a live Antigravity theme. Run EXACTLY these shell steps from ${REPO}: + 1. ${RENDER} + 2. ${HARNESS} port (must print a number; if it prints NONE/NO_PORT, STOP and return ok:false — Antigravity is not running with a debug port, or its window is not visible) + 3. ${HARNESS} inject ${CSSOUT} agent-theme-lab + 4. sleep 1 + 5. ${HARNESS} capture ${SHOTS}/r0 + 6. ${HARNESS} lightscan ${SHOTS}/r0-light.json + 7. ${HARNESS} textscan ${SHOTS}/r0-text.json + This writes ${SHOTS}/r0-full.png, ${SHOTS}/r0-sidebar.png, ${SHOTS}/r0-center.png + the two scans. + If any capture step HANGS or produces no file, the Antigravity window is occluded/minimised — return ok:false with that note. + Return ok:true and shotPrefix="${SHOTS}/r0".`, + { label: 'setup', phase: 'Setup', schema: SETUP_SCHEMA }, +); + +if (!setup || !setup.ok) { + return { aborted: true, reason: 'Setup failed (Antigravity debug port down or window not visible?). ' + (setup?.notes || ''), setup }; +} + +let shotPrefix = setup.shotPrefix; +const history = []; + +for (let r = 1; r <= ROUNDS; r++) { + phase('Critique'); + const crits = (await parallel(LENSES.map((L) => () => + agent( + `Critique the CURRENT Antigravity theme screenshots for ONE dimension: "${L.title}". + Look critically at these images (Read them): + - ${shotPrefix}-full.png (whole window) + - ${shotPrefix}-sidebar.png (left conversation sidebar, retina) + - ${shotPrefix}-center.png (centered chat-input card + main area, retina) + Also Read these scans for machine-detected problems: + - ${shotPrefix}-light.json (opaque LIGHT surfaces = un-themed Catppuccin-Latte leftovers) + - ${shotPrefix}-text.json (dark text that may be invisible) + Reference target art (the source character image): ${SRCIMG} + The tunable STRUCTURE is ${TMPL} — Read it so your adjustments name REAL selectors/tokens/knobs. + The knob VALUES resolve from ${THEME_JSON} — Read it to know the concrete colours, but you must NOT edit it (it is shared with Codex). + Focus ONLY on: ${L.focus} + Goal: a polished, cohesive, readable warm-dark "${THEME}" wallpaper theme for the Antigravity agent manager where the sidebar + centered chat card read as crisp frosted glass over the image, every text level is comfortably legible, the accent harmonises with the art, and NO light Catppuccin surfaces leak through. + Score 0-100 (be strict; 100 = truly nothing to improve on THIS dimension). List concrete issues you actually see, and precise adjustments naming knobs/tokens/selectors from ${TMPL}. dimension = "${L.key}".`, + { label: `crit:${L.key}@r${r}`, phase: 'Critique', schema: CRITIQUE_SCHEMA }, + ), + ))).filter(Boolean); + + if (!crits.length) { history.push({ round: r, error: 'no critiques' }); break; } + const scores = crits.map((c) => c.score); + const minScore = Math.min(...scores); + const avgScore = Math.round(scores.reduce((a, b) => a + b, 0) / scores.length); + history.push({ round: r, minScore, avgScore, dims: crits.map((c) => ({ d: c.dimension, s: c.score })) }); + log(`round ${r}: min=${minScore} avg=${avgScore} [${crits.map((c) => c.dimension + ':' + c.score).join(', ')}]`); + + if (minScore >= THRESHOLD) { log(`converged at round ${r} (min ${minScore} >= ${THRESHOLD})`); break; } + if (r === ROUNDS) break; // last round: critique only, no further synth + + phase('Synthesize'); + const allAdj = crits.flatMap((c) => c.adjustments.map((a) => ({ dim: c.dimension, ...a }))); + const synth = await agent( + `You are the synthesizer for round ${r} of Antigravity theme tuning. Merge these critique adjustments into the Antigravity STRUCTURE template and re-render. + Critiques (JSON): ${JSON.stringify(crits.map((c) => ({ dimension: c.dimension, score: c.score, issues: c.issues, adjustments: c.adjustments })))} + HARD ISOLATION RULES (must obey): + - Edit ONLY ${TMPL}. Do NOT edit ${THEME_JSON}, any themes/*/theme.json, theme.rs, codex.template.css, or ANY Codex/shipped file. This tuning must not change Codex's look at all. + - Keep every value expressed via the --cl-* knobs (or color-mix/calc of them) so the theme stays modular/per-theme. Only hardcode rgba when a knob genuinely cannot express it. + Steps: + 1. Read ${TMPL} and ${THEME_JSON} (for the concrete knob values, read-only). + 2. Decide a coherent merged set of changes. Resolve conflicts between lenses sensibly (readability wants darker scrim, composition wants the image visible — find balance). Prefer adjusting the html{} token overrides, the #root scrim, the module selector rules (blur/shadow/border), and text-shadow rules. Add NEW shadcn/vscode token overrides or NEW selector rules to kill any light leftovers found in the lightscan. Keep changes tasteful and incremental — do NOT overhaul everything at once. + 3. Apply them with the Edit tool to ${TMPL}. Keep it valid CSS. Keep the __INK__/__GLASS__/… placeholders and __HERO__/__ACCENT_BLOCK__/__BASECOLOR__/__POS__/__FIT__ intact (render-antigravity.mjs fills them). + 4. Run: cd ${REPO} && ${RENDER} && ${HARNESS} inject ${CSSOUT} agent-theme-lab && sleep 1 && ${HARNESS} capture ${SHOTS}/r${r} && ${HARNESS} lightscan ${SHOTS}/r${r}-light.json && ${HARNESS} textscan ${SHOTS}/r${r}-text.json + Return shotPrefix="${SHOTS}/r${r}" and the list of appliedChanges (human-readable). + Total candidate adjustments this round: ${allAdj.length}.`, + { label: `synth@r${r}`, phase: 'Synthesize', schema: SYNTH_SCHEMA }, + ); + if (!synth || !synth.shotPrefix) { history.push({ round: r, error: 'synth failed' }); break; } + shotPrefix = synth.shotPrefix; +} + +phase('Final'); +const final = await agent( + `Final review of the tuned Antigravity theme. + Read ${shotPrefix}-full.png, ${shotPrefix}-sidebar.png, ${shotPrefix}-center.png and the template ${TMPL}. + Write a concise markdown report to ${LAB}/REPORT-antigravity.md covering: final look per surface (wallpaper/sidebar/chat-card/text/accent/menus), the iteration score history (${JSON.stringify(history)}), the final set of shadcn/vscode token overrides + module rules, and any remaining nits (incl. the code-editor/Monaco caveat — syntax colours stay light unless the user sets Antigravity to a dark colour theme). + Return perDimension final scores (composition, readability, glass, accent, seams), a one-paragraph summary, and reportPath="${LAB}/REPORT-antigravity.md".`, + { label: 'final-report', phase: 'Final', schema: FINAL_SCHEMA }, +); + +return { history, final, lastShotPrefix: shotPrefix }; diff --git a/README.en.md b/README.en.md index b5ee67f..c02cef8 100644 --- a/README.en.md +++ b/README.en.md @@ -1,109 +1,88 @@ # Agent Theme > [!NOTE] -> 🎨 **Agent Theme** is a standalone theme companion app for Codex Desktop / Antigravity. -> It injects custom CSS into the agent's WebView via Chrome DevTools Protocol, creating frosted-glass + character background visual themes. -> Comes with 5 built-in themes, supports custom upload & crop — one-click theming without modifying agent source code. +> 🎨 **Agent Theme** is a standalone theming companion for **Codex Desktop / Antigravity**. +> It injects CSS into the agent's WebView at runtime via the Chrome DevTools Protocol to render a "character wallpaper + frosted-glass panels" look — **without touching the agent's source, fully reversible**. +> Ships **11** built-in anime themes, each colour-matched to its own background image, plus custom upload & crop. One click to reskin.
简体中文 | - English + English | + Releases
-## Table of Contents +Agent Theme is a standalone desktop app (Tauri v2). The agent (Codex Desktop / Antigravity) is launched with `--remote-debugging-port` to expose a CDP port; this app connects over WebSocket and uses `Page.addScriptToEvaluateOnNewDocument` to inject a script before the page loads — adding a background layer, overriding the agent UI's design tokens, and applying `backdrop-filter` frosted glass to each panel. The whole theme lives only at runtime: turn off the toggle or restart the agent and it disappears, **without modifying the agent's binary or any config file**. -- [Overview](#overview) -- [Features](#features) -- [Built-in Themes](#built-in-themes) -- [Quick Start](#quick-start) -- [Theme Management](#theme-management) -- [Architecture](#architecture) -- [Development](#development) -- [FAQ](#faq) -- [License](#license) +## Theme Showcase -## Overview +Every theme is **colour-matched individually** to its own background — glass tinted from the image's dark tones, accent taken from the character's signature colour, and the legibility scrim calibrated per-wallpaper by brightness — so chat text stays readable on any image instead of hiding behind a uniform dark overlay. Below is the actual look on Antigravity (sidebar & input text blurred for privacy): -Agent Theme is a standalone desktop application (Tauri v2) that works alongside Codex Desktop or Antigravity. It dynamically injects CSS styles via Chrome DevTools Protocol (CDP) to personalize the agent's appearance without modifying any source code. +| 长离 · Changli | 霜银 · Frost | +|---|---| +|  |  | -**How it works:** The agent is launched with the `--remote-debugging-port` flag to expose a CDP port. Agent Theme connects to this port via WebSocket, uses `Page.addScriptToEvaluateOnNewDocument` to inject JavaScript that automatically inserts a background image and semi-transparent CSS overlay on every page load, achieving a frosted-glass visual effect. +**11** built-in themes (backgrounds are the respective character artworks — see [Disclaimer](#disclaimer)): -## Features - -- 🎨 **5 Built-in Themes:** Changli, Nailin, Zani, Azur Lane, Carton — switch with one click -- 🖼️ **Custom Themes:** Drag-and-drop image upload, crop, and save as custom themes -- 🔄 **Multi-Agent Support:** Works with both Codex Desktop and Antigravity, freely switchable -- 🚀 **Scoped Restart:** Companion can restart the selected Codex or Antigravity app with a local debug port -- 🔌 **CDP Injection:** Themes are injected via Chrome DevTools Protocol — safe, reversible, no source modification -- 📊 **Live Status:** Real-time display of agent process status and CDP port binding -- 💾 **Persistent Config:** All settings saved to `~/.codex/agent-theme/config.json`, survives restarts -- 🔒 **Single Instance:** Prevents running multiple companion windows simultaneously - -## Built-in Themes - -| Theme ID | Chinese | English | Preview | -|----------|---------|---------|---------| -| `changli` | 长离 | Changli |  | -| `nailin` | 奈琳 | Nailin |  | -| `zani` | 扎妮 | Zani |  | -| `azurlane` | 碧蓝航线 | Azur Lane |  | -| `carton` | 纸箱 | Carton |  | +| ID | English | ID | English | +|---|---|---|---| +| `changli` | Changli | `nocturne` | Nocturne | +| `azurlane` | Azur Lane | `duet` | Duet | +| `sonata` | Sonata | `rose` | Rose | +| `zani` | Zani | `studio` | Studio | +| `nailin` | Nailin | `carton` | Carton | +| `frost` | Frost | | | -## Quick Start +> The same theme applies to both **Codex Desktop** and **Antigravity** — the two agents share theme.json's colour knobs, each injecting platform-appropriate token overrides. -### Prerequisites +## Features -- **macOS** (currently macOS-only; Windows/Linux support planned) -- **Codex Desktop** or **Antigravity** installed +- 🎨 **11 built-in themes** — each derives dark glass + accent + tiered text colours from its own background; one-click switch +- 🖼️ **Custom themes** — drag-drop an image → 1:1 crop → save as a local theme +- 🔄 **Dual-agent support** — works with both **Codex Desktop** and **Antigravity**, switchable from the top bar +- 🚀 **Scoped restart** — restart the current agent with the debug-port flag attached; only the two supported agents are touched, nothing else +- 🔌 **CDP runtime injection** — injected via Chrome DevTools Protocol, no source changes, clears back to the original look +- 📊 **Live status** — shows agent run state and CDP port binding in real time +- 💾 **Persistent config** — settings saved to `~/.codex/agent-theme/config.json` +- 🔒 **Single-instance guard** — prevents conflicting companion windows -### Installation +## Install 1. Download the latest `.dmg` from [Releases](https://github.com/Cmochance/agent-theme/releases) -2. Drag `Agent Theme Companion.app` into your Applications folder -3. On first launch, macOS Gatekeeper may block: right-click the app → choose "Open"; or go to `System Settings → Privacy & Security` and click "Open Anyway" -4. Launch Agent Theme Companion — the interface will display the agent's running status - -### Basic Usage +2. Drag `Agent Theme Companion.app` into Applications +3. If macOS Gatekeeper blocks it on first launch ("unidentified developer"): `right-click → Open` once, or go to `System Settings → Privacy & Security` and click "Open Anyway" +4. Launch — the UI shows the agent's run state -1. **Select Agent:** Choose Codex or Antigravity from the top switch bar -2. **Prepare Debug Port:** If the UI shows `No debug port`, click `Restart App`; the companion will restart the selected agent with local debug-port arguments -3. **Pick a Theme:** Click any theme card in the grid to preview and apply -4. **Toggle Switch:** The "Theme" toggle controls whether styles are injected; turn off to restore the agent's original appearance +> **macOS only** for now (`agent.rs` process detection & paths depend on `~/Library/Application Support/`). Windows / Linux support is planned. -### Local Debug Port Requirement +## Quick Start -If the agent is already running without a CDP port, Agent Theme cannot inject themes. Clicking `Restart App` stops the currently selected Codex or Antigravity process and starts it again with `--remote-debugging-port=0`. +1. **Pick an agent** — Codex or Antigravity from the top switcher +2. **Get a debug port** — if the UI shows `No debug port`, click `Restart App`; the companion stops the current agent and relaunches it with `--remote-debugging-port=0` +3. **Pick a theme** — click any card in the theme grid to preview & apply instantly +4. **Toggle** — the "Theme" switch controls injection; turning it off restores the agent's original look -Process management is scoped to the two supported agent apps. The companion does not clean lock files in the agent app data directory and does not modify the agent app bundle. Once the port is available, keep the "Theme" toggle enabled and click a theme card to inject it. +> Themes are injected via `Page.addScriptToEvaluateOnNewDocument` and take effect **only on page navigation / refresh**. They are lost when the agent restarts; the app re-injects automatically once a port is available — just keep the toggle on. ## Theme Management -### Using Built-in Themes - -Built-in themes are stored in the app's `themes/` resource directory. No additional configuration needed — select a theme and it takes effect immediately. - -### Creating Custom Themes +### Create a custom theme 1. Click the "+" card in the theme grid -2. Drag an image into the upload area (JPG/PNG, max 20MB) -3. Use the crop tool to adjust the background area -4. Click "Save & Apply" — the theme is saved to `~/.codex/agent-theme/themes/` - -### Deleting Custom Themes +2. Drop an image (JPG/PNG, ≤ 20MB) +3. Adjust the background region with the crop tool +4. "Save & Apply" — the theme is written to `~/.codex/agent-theme/themes/` -Hover over a custom theme card and click the delete button. Built-in themes cannot be deleted. +### theme.json structure -### Theme File Structure - -```json +```jsonc { "id": "changli", "displayName": { "zh": "长离 (Changli)", "en": "Changli" }, @@ -112,118 +91,115 @@ Hover over a custom theme card and click the delete button. Built-in themes cann "backgroundFit": "cover", "backgroundPosition": "50% 4%", "style": { - "ink": "#f4ebdf", - "accent": "#e08a55", - "glass": "rgba(30,21,14,.52)", - "glassStrong": "rgba(22,15,10,.72)", - "glassSoft": "rgba(26,18,12,.80)", - "blur": "26px", - "scrimTop": "rgba(18,12,8,.26)", - "scrimMid": "rgba(17,11,7,.34)", - "scrimBot": "rgba(11,7,5,.60)", + "ink": "#f4ebdf", "ink2": "rgba(244,235,223,.74)", + "ink3": "rgba(244,235,223,.56)", "ink4": "rgba(244,235,223,.40)", + "accent": "#e08a55", "accentSoft": "#e6b48a", "focus": "#ffce86", + "surface": "rgba(26,18,12,.50)", + "glass": "rgba(30,21,14,.60)", "glassSoft": "rgba(34,24,16,.52)", "glassStrong": "rgba(22,15,10,.78)", + "border": "rgba(255,228,201,.14)", "borderSoft": "rgba(255,228,201,.07)", "borderStrong": "rgba(255,228,201,.26)", + "blur": "6px", "hover": "rgba(255,236,210,.10)", "selection": "rgba(255,236,210,.16)", + "scrimTop": "rgba(18,12,8,.26)", "scrimMid": "rgba(17,11,7,.34)", "scrimBot": "rgba(11,7,5,.60)", "baseColor": "#160f0a" } } ``` -`background`/`backgroundFit`/`backgroundPosition` control the image itself; the optional `style` block drives the **modular Codex theme**: +`background` / `backgroundFit` / `backgroundPosition` control the wallpaper itself; the optional `style` block is a set of agent-agnostic **colour knobs** (`--cl-*`) that the Codex and Antigravity injectors each translate into platform-specific token overrides: -- **Token override** — for current Codex (Tailwind v4 + `--color-token-*` design tokens) the injection overrides semantic tokens rather than fixed container selectors, so it tracks app updates; main surfaces are made transparent to reveal the background image. -- **Per-module frosted glass** — sidebar (`glass`), composer input (`glassSoft`), and dialogs/menus (`glassStrong`) each use a `backdrop-filter` at a distinct opacity + `blur`, sampling the same background image behind them, so **each module's background matches perfectly by construction**. -- **Per-level text colours** — `ink`/`ink2`/`ink3`/`ink4` tune primary/secondary/tertiary/disabled text; `accent` unifies links, focus, and the send button into the character's signature colour. -- **Readability scrim** — `scrimTop`/`scrimMid`/`scrimBot` define a top-to-bottom darkening gradient that keeps text legible over the art. +- **Token overrides** — override the agent UI's semantic design tokens (not hard-coded container selectors), so they survive agent updates; the main surface is made transparent to reveal the wallpaper. +- **Per-panel frosted glass** — sidebar / input / dialogs each use a different `glass*` opacity + `blur` as a `backdrop-filter` that **samples the same wallpaper behind the panel** in real time, so each panel matches the background perfectly. +- **Tiered text colours** — `ink` / `ink2` / `ink3` / `ink4` map to body / secondary / tertiary / disabled; `accent` / `focus` unify links, focus, and the send button around the character's primary colour. +- **Legibility scrim** — `scrimTop` / `scrimMid` / `scrimBot` define a top-to-bottom darkening gradient. **Bright wallpapers need a stronger scrim** (or the chat text washes out), so each theme is calibrated per-image by brightness. -Omit `style` to fall back to neutral dark-glass defaults (works with any background image). The local, untracked `.theme-lab/` holds `build-assets.sh` (slices the source image into per-module crops at different sizes/opacities/masks) and `cdp.mjs` (the inject + screenshot tuning harness). +Omit `style` to fall back to neutral dark-glass defaults (works on any background). ## Architecture ``` agent-theme/ -├── src-tauri/ # Rust backend (Tauri v2) -│ ├── src/ -│ │ ├── main.rs # App entry point -│ │ ├── lib.rs # Tauri commands registration, lifecycle -│ │ ├── agent.rs # Agent process detection, launch, management -│ │ ├── cdp.rs # CDP WebSocket connection, theme inject/clear -│ │ ├── config.rs # Config read/write (AppConfig) -│ │ └── theme.rs # Theme discovery, CSS generation, custom theme CRUD -│ └── Cargo.toml -├── web/ # Frontend (Vanilla JS + esbuild) -│ ├── app.js # Main logic (status polling, theme switching, crop UI) -│ ├── index.html # Page structure -│ ├── style.css # Dark red-gold glassmorphism design system -│ └── dist/ # Build output (bundle.js) -└── themes/ # Built-in theme assets - ├── changli/ # Changli - ├── nailin/ # Nailin - ├── zani/ # Zani - ├── azurlane/ # Azur Lane - └── carton/ # Carton +├── src-tauri/ # Rust backend (Tauri v2) +│ └── src/ +│ ├── lib.rs # Tauri commands, lifecycle, state +│ ├── agent.rs # agent detect / launch / restart-with-debug-port +│ ├── cdp.rs # CDP WebSocket, theme inject / clear +│ ├── config.rs # config read/write (AppConfig) +│ └── theme.rs # theme discovery, CSS generation, custom-theme CRUD +├── src/ # frontend source (TypeScript + Svelte) +│ ├── App.svelte # root component +│ └── lib/ # types / tauri-commands / stores / actions / polling / components +├── index.html # Vite entry +├── web/ # Vite build output (Tauri frontendDist) +└── themes/