Skip to content

Let a UI theme declare its own code theme #1239

Description

@smsunarto

Summary

A custom UI theme can restyle every surface except the code and diff viewers, which stay on the library's default pierre-dark. I'd like a UI theme to be able to declare its own code theme.

Proposed API

One optional field on the appearance schema (packages/domain/src/app-theme.ts, today { themeId, customCss, faviconColor }), where each side is a bundled Shiki theme name or a path to a theme file:

codeTheme: z
  .object({
    dark: z.string().optional(),
    light: z.string().optional(),
  })
  .optional(),

Both sides are optional and resolved independently: an omitted dark falls back to pierre-dark, an omitted light to pierre-light. A dark-only theme declares dark alone and light mode is left untouched.

Built-in code theme

// plugin manifest — bb.themes entry
{
  "id": "my-theme",
  "name": "My Theme",
  "css": "themes/my-theme.css",
  "codeTheme": { "dark": "github-dark", "light": "github-light" }
}
bb theme code-theme github-dark github-light

Resolves through Shiki's bundle. Roughly 60 of those themes already ship as lazy chunks in the app (github-dark, nord, monokai, vitesse-dark, …) and only the two pierre-* are ever requested today, so this case adds no payload.

Custom code theme shipped with the UI theme

A bundled name gets close, but a UI theme ported from an editor theme wants its own colors — so the same field should also take a path, resolved the way css already is, relative to the plugin directory:

// plugin manifest — bb.themes entry
{
  "id": "my-theme",
  "name": "My Theme",
  "css": "themes/my-theme.css",
  "codeTheme": { "dark": "themes/code-theme.json" }
}

The file is a standard VS Code / TextMate theme JSON — { name, type, colors, tokenColors } — passed to loadTheme() and referenced by its registered name. Anyone porting an editor theme already has this file, so shipping it alongside the CSS is a copy step rather than new work, and it makes a UI theme self-contained: chrome and code colors travel together and stay in sync.

Settings: a second, separate selector

Code theme should be its own control, not folded into the UI theme picker. They are independent choices — someone may want one UI theme with a different code theme, and someone running no custom UI theme at all may still want their editor's syntax colors in diffs.

UI theme     [ My Theme                  ▾ ]
Code theme   [ Use UI theme's default     ▾ ]
                ├─ Use UI theme's default
                ├─ ── Built-in ──
                ├─ GitHub Dark
                ├─ Monokai
                ├─ Nord
                ├─ Vitesse Dark
                └─ Custom theme file…        (stretch goal)

"Use UI theme's default" is the default value, and it means: whatever the active UI theme declares in its codeTheme manifest field, falling back to the library default if it declares none. So a UI theme that ships code colors gets them automatically on selection, and selecting one that ships none changes nothing — no migration, no surprise for existing users.

Resolution and failure

Explicit user selection → active UI theme's codeTheme → library default. The user's choice wins because it is the more specific act; leaving the selector on "Use UI theme's default" is what hands control back to the UI theme.

Switching UI themes should not silently discard an explicit code-theme choice — keep it set, since it was chosen against the UI theme, not as part of it.

An unknown bundled name, an unreadable path, or a JSON that fails theme validation should log and fall back to the library default rather than failing the render.

Stretch goal: a code theme without authoring a UI theme

Everything above ties a code theme to a UI theme: the UI theme declares it — bundled name or shipped file — and the user selects the UI theme. That covers theme authors, but not the more common case — someone who likes bb's stock look and just wants their own editor's syntax colors in diffs. Authoring and installing a plugin to carry one JSON file is a lot of ceremony for that.

So: let a code theme be set on its own, pointing at a file the user already has.

bb theme code-theme ~/Downloads/my-editor-theme.json

In Settings, the "Custom theme file…" entry opens a native file picker. On selection the file is copied into <data-dir>/code-theme/<name>.json — copied rather than referenced, so the theme survives the source file moving or being deleted — and the entry then reads back as that theme's name, listed under a "Custom" group alongside the built-ins:

Code theme   [ My Editor Theme            ▾ ]
                ├─ Use UI theme's default
                ├─ ── Built-in ──
                ├─ GitHub Dark
                ├─ …
                ├─ ── Custom ──
                ├─ My Editor Theme
                └─ Custom theme file…

Anything already in <data-dir>/code-theme/ is listed too, mirroring how custom UI themes are discovered from <data-dir>/theme/<name>/theme.css — so dropping a file in that directory and picking it from the CLI are the same operation as using the picker.

Reading and validating a theme file is already needed for the case above, so what is left here is the file picker, the copy, and discovery — which is why it can land separately, and why it needs no schema change when it does.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions