Skip to content

feat(appearance): presets you can save, share and import - #668

Open
Adam-Dalloul wants to merge 1 commit into
spacering-net:mainfrom
Adam-Dalloul:feat/theme-engine-presets
Open

Adam-Dalloul wants to merge 1 commit into
spacering-net:mainfrom
Adam-Dalloul:feat/theme-engine-presets

Conversation

@Adam-Dalloul

Copy link
Copy Markdown
Contributor

This lets someone make codeg look like a different app in one click, and hand that look to someone else as a file.

What it adds

Settings > Appearance gets a Presets section: seven bundled looks (the stock one, a warm terminal, a monochrome ink look, a dense IDE, high contrast, a midnight chat client and a warm paper reading surface), a code colour picker per mode (any of Shiki's bundled themes), a density knob, a message text size, and Import / Export. Export writes the current look, including your own token edits, as <id>.codeg-preset.json. Import validates the file, previews what it changes, then applies it, or lists every problem next to the field it is in.

To make that possible, the surfaces that were still hard-coded now read tokens: message bubbles (user and assistant background, text, hairline, radius, padding), the composer box, the status bar, the code font in messages (--font-mono), message text size and density (--spacing). Their defaults point back at the shadcn tokens, so nothing changes until a preset or the Custom Style editor sets them, and the pre-paint script applies them the way it applies --primary today, so there is no flash on launch. Switching presets is CSS variable writes; the transcript only re-renders when the Shiki theme itself changes, and there is a test for that.

The format

A preset is one JSON document with schemaVersion: 1: an id and name, a base palette (one of the twelve), light and dark token overrides (the 31 shadcn colours plus the surface tokens above), shape (radius, bubble and composer radii), density, typography (font ids from the bundled and system list, message size and line height) and code theme ids. Validation is strict: unknown keys anywhere are errors, every value is syntax and range checked, and the file has a 32 KB cap. A newer schema version is refused, an older one migrates forward.

Boundary

Tokens only. A preset cannot carry CSS or script, so importing a stranger's file can change colours, fonts and spacing and nothing else. Custom CSS stays what it is, a local per-device escape hatch, and is never written into a preset. All seven bundled presets pass a WCAG AA contrast check on every text and surface pair in both modes; that check runs in the test suite against the real globals.css.

Out of scope: moving panels around. Every visible surface is themeable at the token level, the layout stays as it is.

The same document is what a preset gallery would serve later: an index of these files, checked by this exact validator, is a marketplace.

Route the surfaces that were still hard-coded through theme tokens (message
bubbles, the composer box, the status bar, the code font in messages, message
text size and density), give code blocks a per-mode Shiki theme, and add a
Presets section to Appearance: seven bundled looks, density and text knobs,
and JSON import/export.

A preset is one strictly validated document of tokens, font ids and code
theme ids, with a schema version and a size cap. It cannot carry CSS or
script; custom CSS stays a local escape hatch and is never exported. The
pre-paint script applies the new tokens like the existing ones, so a preset
paints before first paint, and the transcript only re-renders when the code
theme itself changes. Every bundled preset passes a WCAG AA contrast check on
its text and surface pairs in both modes, measured against globals.css.
xyalter pushed a commit to lesscodex/codeg that referenced this pull request Oct 31, 2026
…spacering-net#668)

## Result

When a thread continues on another provider, the client now shows what
went wrong instead of the raw provider JSON:

> The current provider can't read this thread's earlier reasoning, which
another provider or account encrypted. This happens after the provider
changes in a thread. Switch back to the previous provider or start a new
thread.

Before, Air showed the whole gateway error body:
`{"error":{"message":"Provider returned
error","code":400,"metadata":{"raw":"{\n \"error\": {...\"code\":
\"invalid_encrypted_content\"...`.

## Cause

Codex sends requests with `store: false` and resends the encrypted
reasoning of earlier turns with every request. Only the provider (and
account) that encrypted it can read it. After `providers/set` moves a
thread to another route, the new route rejects the history with
`invalid_encrypted_content`, and a retry fails the same way.

This is Codex behavior, not an adapter bug: `codex exec resume <id> -c
model_provider=<other>` sends the other provider's encrypted reasoning
too. Codex has no option to drop it, so the adapter only makes the error
readable and does not rewrite the history.

## Changes

- `readableServiceErrorMessage` recognizes `invalid_encrypted_content`
in three forms: the plain provider error `{error: {code}}`, the Codex
service envelope `{type: "error", status, error}`, and a gateway error
that carries it in `metadata.provider_error_code` or in the JSON of
`metadata.raw`.
- Other error text stays unchanged.
- The function is already used for every path a provider error reaches
the client, so ACP v1 (agent message and Air typed failure) and ACP v2
(agent message and idle error) all get the message.

## Verification

- New unit tests in `ServiceErrorMessage.test.ts`, and snapshot tests
for ACP v1 (legacy text and typed failure) and ACP v2. All 7 fail
without the change.
- `npm run typecheck` passes. `npm test`: 1877 passed, 36 skipped.
- End to end with real Codex and two local OpenAI-compatible gateways on
ACP v1 and v2: two prompts on gateway A, `providers/set` to gateway B,
which returns the gateway error above; the client gets the readable
message.
xyalter pushed a commit to lesscodex/codeg that referenced this pull request Oct 31, 2026
🤖 I have created a release *beep* *boop*
---


##
[2.3.0](agentclientprotocol/codex-acp@v2.2.2...v2.3.0)
(2026-10-26)


### Features

* add ACP v2 support again
([spacering-net#620](agentclientprotocol/codex-acp#620))
([9e1690f](agentclientprotocol/codex-acp@9e1690f))
* mark session list rows of worktrees Codex made (agentWorktree)
([spacering-net#631](agentclientprotocol/codex-acp#631))
([aa02d43](agentclientprotocol/codex-acp@aa02d43))
* narrow models to what the providers/set gateway lists
([spacering-net#627](agentclientprotocol/codex-acp#627))
([3f555f9](agentclientprotocol/codex-acp@3f555f9))
* report the model of each native subagent session
([spacering-net#678](agentclientprotocol/codex-acp#678))
([51005a6](agentclientprotocol/codex-acp@51005a6))
* return initial ACP v2 session commands
([spacering-net#652](agentclientprotocol/codex-acp#652))
([5e9b7c5](agentclientprotocol/codex-acp@5e9b7c5))
* support AIR sessionIndex extension
([spacering-net#590](agentclientprotocol/codex-acp#590)) v2
([spacering-net#622](agentclientprotocol/codex-acp#622))
([2fd758c](agentclientprotocol/codex-acp@2fd758c))
* support append-mode ACP system prompts
([spacering-net#653](agentclientprotocol/codex-acp#653))
([5993a12](agentclientprotocol/codex-acp@5993a12))


### Bug Fixes

* disable Codex's Visualize skill, whose output ACP clients show as raw
text
([spacering-net#667](agentclientprotocol/codex-acp#667))
([0ba94f5](agentclientprotocol/codex-acp@0ba94f5))
* display the selected wire provider as JetBrains Air Gateway
([spacering-net#589](agentclientprotocol/codex-acp#589))
([cc54abf](agentclientprotocol/codex-acp@cc54abf))
* explain Codex config load errors caused by a deleted working directory
([spacering-net#626](agentclientprotocol/codex-acp#626))
([a636804](agentclientprotocol/codex-acp@a636804))
* explain invalid_encrypted_content errors after a provider change
([spacering-net#668](agentclientprotocol/codex-acp#668))
([a77829f](agentclientprotocol/codex-acp@a77829f))
* hold a fork back from the session list until its session/fork response
is out
([spacering-net#642](agentclientprotocol/codex-acp#642))
([59974c7](agentclientprotocol/codex-acp@59974c7))
* include subagent edits in the agent file-change report
([spacering-net#632](agentclientprotocol/codex-acp#632))
([7d07123](agentclientprotocol/codex-acp@7d07123))
* leave codex exec runs out of the session list without sessionIndex
([spacering-net#639](agentclientprotocol/codex-acp#639))
([df75d2b](agentclientprotocol/codex-acp@df75d2b))
* migrate to ACP SDK 2.0
([spacering-net#649](agentclientprotocol/codex-acp#649))
([c8fd166](agentclientprotocol/codex-acp@c8fd166))
* publish the commands when the skills change
([spacering-net#643](agentclientprotocol/codex-acp#643))
([a62b568](agentclientprotocol/codex-acp@a62b568))
* report structured ACP v2 failures
([spacering-net#651](agentclientprotocol/codex-acp#651))
([5377bfe](agentclientprotocol/codex-acp@5377bfe))
* show a Codex subagent's follow-up turn in a new subagent session
([spacering-net#634](agentclientprotocol/codex-acp#634))
([7c761c7](agentclientprotocol/codex-acp@7c761c7))
* synchronize ACP v2 foreground state
([spacering-net#650](agentclientprotocol/codex-acp#650))
([986955a](agentclientprotocol/codex-acp@986955a))
* update codex to 0.164.0
([spacering-net#666](agentclientprotocol/codex-acp#666))
([e227c0e](agentclientprotocol/codex-acp@e227c0e))
* wait for the compact turn to finish
([spacering-net#654](agentclientprotocol/codex-acp#654))
([ced4c34](agentclientprotocol/codex-acp@ced4c34))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: acp-release-bot[bot] <246668977+acp-release-bot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant