Skip to content

Commit cd248fa

Browse files
committed
Only ship OpenVSX themes.
1 parent 338ade0 commit cd248fa

10 files changed

Lines changed: 70 additions & 385 deletions

File tree

docs/specs/layout.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ Custom `mousetermTheme` extends dockview's `themeAbyss`:
279279
- Pane header height: `--dv-tabs-and-actions-container-height: 30px`
280280
- 6px padding around the dockview area (`p-1.5` on wrapper, `inset-1.5` on container)
281281

282-
Colors use a two-layer CSS variable strategy: `@theme --color-*` tokens → `var(--vscode-*, <fallback>)`. In VSCode, host theme variables take precedence. In standalone mode, fallback values apply (Dark+ defaults with `prefers-color-scheme: light` overrides). Tailwind v4 `@theme` block registers `--color-*` tokens as Tailwind colors (e.g., `bg-surface`, `text-foreground`, `border-border`). See `theme.css` for the full token map.
282+
Colors use a two-layer CSS variable strategy: `@theme --color-*` tokens → `var(--vscode-*, <fallback>)`. In VSCode, host theme variables take precedence. In standalone mode, fallback values apply with `prefers-color-scheme: light` overrides. Tailwind v4 `@theme` block registers `--color-*` tokens as Tailwind colors (e.g., `bg-surface`, `text-foreground`, `border-border`). See `theme.css` for the full token map.
283283

284284
Dockview's separator borders, sash handles, and groupview borders are all set to transparent/none — the 6px gap is the only visual separator between panes. All dockview container backgrounds are flattened to `var(--color-surface)`.
285285

docs/specs/theme.md

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@ The old three-layer system (`--vscode-*` → `--mt-*` → `--color-*`) had a red
3737

3838
### Light theme body class
3939

40-
`applyTheme()` adds `vscode-light` to `document.body.classList` for light themes and removes it for dark themes. `theme.css` has a `body.vscode-light` selector that switches all `--color-*` fallback values to the Light+ palette. Without this class, a light theme that doesn't explicitly define every key would get dark fallbacks for missing keys.
40+
`applyTheme()` adds `vscode-light` to `document.body.classList` for light themes and removes it for dark themes. `theme.css` has a `body.vscode-light` selector that switches all `--color-*` fallback values to the light fallback palette. Without this class, a light theme that doesn't explicitly define every key would get dark fallbacks for missing keys.
4141

4242
## Theme data model
4343

4444
```typescript
4545
interface MouseTermTheme {
46-
id: string; // "GitHub.github-vscode-theme.dark-default" or "builtin.dark-plus"
46+
id: string; // "GitHub.github-vscode-theme.github-dark-default"
4747
label: string; // "GitHub Dark Default"
4848
type: 'dark' | 'light';
4949
swatch: string; // editor.background — used for picker preview
@@ -55,7 +55,7 @@ interface MouseTermTheme {
5555
interface BundledOrigin { kind: 'bundled' }
5656
interface InstalledOrigin {
5757
kind: 'installed';
58-
extensionId: string; // "dracula-theme/theme-dracula"
58+
extensionId: string; // "publisher/theme-extension"
5959
installedAt: string; // ISO date
6060
}
6161
```
@@ -86,7 +86,7 @@ Not all VSCode theme color keys matter to MouseTerm — only the ~45 keys that a
8686

8787
### Conversion rule
8888

89-
For each key in the VSCode theme's `colors` object: if it's in `CONSUMED_VSCODE_KEYS`, emit `--vscode-${key.replace(/\./g, '-')}` → value. Keys not consumed by MouseTerm are silently dropped. Missing keys fall through to the `@theme` fallbacks in `theme.css` (Dark+ or Light+ defaults), which is the same behavior as VSCode itself.
89+
For each key in the VSCode theme's `colors` object: if it's in `CONSUMED_VSCODE_KEYS`, emit `--vscode-${key.replace(/\./g, '-')}` → value. Keys not consumed by MouseTerm are silently dropped. Missing keys fall through to the `@theme` fallbacks in `theme.css`, which is the same behavior as VSCode itself.
9090

9191
## Bundled themes
9292

@@ -97,10 +97,6 @@ Bundled themes are extracted at build time by a Node.js script (`lib/scripts/bun
9797
| Extension | OpenVSX ID | Variants |
9898
|-----------|-----------|----------|
9999
| GitHub VSCode Theme | `GitHub/github-vscode-theme` | Dark Default, Light Default, Dark Dimmed, Dark High Contrast, Light High Contrast, Dark Colorblind, Light Colorblind, etc. |
100-
| Dracula | `dracula-theme/theme-dracula` | Dracula, Dracula Soft |
101-
| VSCode builtins | (hardcoded) | Dark+, Light+ |
102-
103-
Dark+ and Light+ are VSCode built-in themes not published to OpenVSX. Their values are hardcoded (from the existing `lib/.storybook/themes.ts`).
104100

105101
### Build script flow
106102

@@ -117,8 +113,6 @@ lib/scripts/bundle-themes.mjs
117113
| +- convertVscodeThemeColors(colors) -> vars
118114
| +- emit MouseTermTheme object
119115
|
120-
+- append hardcoded Dark+ and Light+ themes
121-
|
122116
+- write lib/src/lib/themes/bundled.json
123117
```
124118

@@ -231,6 +225,6 @@ user searches OpenVSX
231225

232226
**Why filter to `CONSUMED_VSCODE_KEYS` instead of passing all colors through?** VSCode themes can define 500+ color keys. Setting all of them as CSS variables would be wasteful (most are never read) and could cause unexpected interactions if VSCode adds new keys that happen to match future `--color-*` variables.
233227

234-
**Why set the `vscode-light` body class?** `theme.css` uses `body.vscode-light` to switch all `--color-*` fallback values to the Light+ palette. Without this class, a light theme that doesn't explicitly define every key would get dark fallbacks for the missing ones, creating a broken mixed appearance.
228+
**Why set the `vscode-light` body class?** `theme.css` uses `body.vscode-light` to switch all `--color-*` fallback values to the light fallback palette. Without this class, a light theme that doesn't explicitly define every key would get dark fallbacks for the missing ones, creating a broken mixed appearance.
235229

236230
**Why not use OpenVSX's direct file access instead of downloading the full VSIX?** OpenVSX doesn't expose individual theme files via API — you have to download the full VSIX. However, theme-only extensions are typically small (50-200 KB), so this is fine. The build script and runtime installer share the same extraction logic.

docs/specs/tutorial.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ The sandbox stays fully functional after completion. Running `tut` shows "Tutori
127127

128128
Implemented in `mouseterm-lib/lib/themes` and `mouseterm-lib/components/ThemePicker`.
129129

130-
Bundled themes are provided by `mouseterm-lib/lib/themes` and include Dark+, Light+, GitHub variants, and Dracula variants. Users can install additional themes from OpenVSX through the dropdown footer action.
130+
Bundled themes are provided by `mouseterm-lib/lib/themes` and include only GitHub variants. Users can install additional themes from OpenVSX through the dropdown footer action.
131131

132132
The picker appears only on `/playground`, inside `SiteHeader`, labeled `Theme:`. The trigger opens a dropdown of bundled and installed themes. The dropdown footer is always `Install theme from OpenVSX`, which opens the theme store dialog. Installed theme rows include an `X` delete control; deletion requires browser confirmation before removing the theme from localStorage. If the active installed theme is deleted, the picker falls back to the first bundled theme and applies it immediately.
133133

docs/specs/vscode.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ Example of the pattern:
231231
--color-surface: var(--mt-surface);
232232
```
233233

234-
Full mapping in `lib/src/theme.css` covers: surfaces (3), text (2), accent/borders (4), tabs (6), terminal bg/fg/cursor/selection (4), all 16 ANSI colors + bright variants, badges (2), semantic status (3), inputs (2), buttons (3), and selection (2). Dark mode fallbacks (VS Code Dark+ defaults) are in `:root`; light mode overrides (VS Code Light+ defaults) are in `body.vscode-light`; a standalone fallback uses `@media (prefers-color-scheme: light)` for non-VS Code contexts.
234+
Full mapping in `lib/src/theme.css` covers: surfaces (3), text (2), accent/borders (4), tabs (6), terminal bg/fg/cursor/selection (4), all 16 ANSI colors + bright variants, badges (2), semantic status (3), inputs (2), buttons (3), and selection (2). Dark mode fallbacks are in `:root`; light mode overrides are in `body.vscode-light`; a standalone fallback uses `@media (prefers-color-scheme: light)` for non-VS Code contexts.
235235

236236
A `MutationObserver` in `terminal-registry.ts` watches for VS Code theme changes on `body`/`html` (class and style attribute mutations) and live-updates all xterm.js instances.
237237

lib/.storybook/preview.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const preview: Preview = {
4343
},
4444
},
4545
initialGlobals: {
46-
theme: 'Dark+ (default)',
46+
theme: 'GitHub Dark Default',
4747
},
4848
decorators: [
4949
// Theme switcher: inject --vscode-* CSS variables

lib/scripts/bundle-themes.mjs

Lines changed: 13 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ const OUTPUT = resolve(__dirname, '../src/lib/themes/bundled.json');
2020
/** Extensions to download from OpenVSX. */
2121
const EXTENSIONS = [
2222
{ namespace: 'GitHub', name: 'github-vscode-theme' },
23-
{ namespace: 'dracula-theme', name: 'theme-dracula' },
23+
];
24+
25+
const PREFERRED_THEME_ORDER = [
26+
'GitHub.github-vscode-theme.github-dark-default',
2427
];
2528

2629
/**
@@ -121,123 +124,21 @@ async function fetchExtensionThemes(namespace, name) {
121124
return themes;
122125
}
123126

124-
/** VSCode built-in Dark+ and Light+ (not on OpenVSX). */
125-
function builtinThemes() {
126-
return [
127-
{
128-
id: 'builtin.dark-plus',
129-
label: 'Dark+ (default)',
130-
type: 'dark',
131-
swatch: '#1e1e1e',
132-
accent: '#007fd4',
133-
vars: {
134-
'--vscode-editor-background': '#1e1e1e',
135-
'--vscode-editor-foreground': '#cccccc',
136-
'--vscode-sideBar-background': '#252526',
137-
'--vscode-editorWidget-background': '#252526',
138-
'--vscode-descriptionForeground': '#858585',
139-
'--vscode-focusBorder': '#007fd4',
140-
'--vscode-panel-border': '#2b2b2b',
141-
'--vscode-tab-activeBackground': '#1e1e1e',
142-
'--vscode-tab-inactiveBackground': '#2d2d2d',
143-
'--vscode-tab-activeForeground': '#ffffff',
144-
'--vscode-tab-inactiveForeground': '#969696',
145-
'--vscode-list-activeSelectionBackground': '#094771',
146-
'--vscode-list-activeSelectionForeground': '#ffffff',
147-
'--vscode-terminal-background': '#1e1e1e',
148-
'--vscode-terminal-foreground': '#cccccc',
149-
'--vscode-badge-background': '#007acc',
150-
'--vscode-badge-foreground': '#ffffff',
151-
'--vscode-errorForeground': '#f48771',
152-
'--vscode-input-background': '#3c3c3c',
153-
'--vscode-input-border': '#3c3c3c',
154-
'--vscode-button-background': '#0e639c',
155-
'--vscode-button-foreground': '#ffffff',
156-
'--vscode-button-hoverBackground': '#1177bb',
157-
'--vscode-textLink-foreground': '#3794ff',
158-
'--vscode-terminal-ansiBlack': '#000000',
159-
'--vscode-terminal-ansiRed': '#cd3131',
160-
'--vscode-terminal-ansiGreen': '#0dbc79',
161-
'--vscode-terminal-ansiYellow': '#e5e510',
162-
'--vscode-terminal-ansiBlue': '#2472c8',
163-
'--vscode-terminal-ansiMagenta': '#bc3fbc',
164-
'--vscode-terminal-ansiCyan': '#11a8cd',
165-
'--vscode-terminal-ansiWhite': '#e5e5e5',
166-
'--vscode-terminal-ansiBrightBlack': '#666666',
167-
'--vscode-terminal-ansiBrightRed': '#f14c4c',
168-
'--vscode-terminal-ansiBrightGreen': '#23d18b',
169-
'--vscode-terminal-ansiBrightYellow': '#f5f543',
170-
'--vscode-terminal-ansiBrightBlue': '#3b8eea',
171-
'--vscode-terminal-ansiBrightMagenta': '#d670d6',
172-
'--vscode-terminal-ansiBrightCyan': '#29b8db',
173-
'--vscode-terminal-ansiBrightWhite': '#e5e5e5',
174-
'--vscode-terminalCursor-foreground': '#aeafad',
175-
'--vscode-terminal-selectionBackground': '#264f7840',
176-
},
177-
origin: { kind: 'bundled' },
178-
},
179-
{
180-
id: 'builtin.light-plus',
181-
label: 'Light+ (default)',
182-
type: 'light',
183-
swatch: '#ffffff',
184-
accent: '#0090f1',
185-
vars: {
186-
'--vscode-editor-background': '#ffffff',
187-
'--vscode-editor-foreground': '#333333',
188-
'--vscode-sideBar-background': '#f3f3f3',
189-
'--vscode-editorWidget-background': '#f3f3f3',
190-
'--vscode-descriptionForeground': '#717171',
191-
'--vscode-focusBorder': '#0090f1',
192-
'--vscode-panel-border': '#e5e5e5',
193-
'--vscode-tab-activeBackground': '#ffffff',
194-
'--vscode-tab-inactiveBackground': '#ececec',
195-
'--vscode-tab-activeForeground': '#333333',
196-
'--vscode-tab-inactiveForeground': '#8e8e8e',
197-
'--vscode-list-activeSelectionBackground': '#cce6ff',
198-
'--vscode-list-activeSelectionForeground': '#000000',
199-
'--vscode-terminal-background': '#ffffff',
200-
'--vscode-terminal-foreground': '#333333',
201-
'--vscode-badge-background': '#007acc',
202-
'--vscode-badge-foreground': '#ffffff',
203-
'--vscode-errorForeground': '#a1260d',
204-
'--vscode-input-background': '#ffffff',
205-
'--vscode-input-border': '#cecece',
206-
'--vscode-button-background': '#007acc',
207-
'--vscode-button-foreground': '#ffffff',
208-
'--vscode-button-hoverBackground': '#0062a3',
209-
'--vscode-textLink-foreground': '#006ab1',
210-
'--vscode-terminal-ansiBlack': '#000000',
211-
'--vscode-terminal-ansiRed': '#cd3131',
212-
'--vscode-terminal-ansiGreen': '#00bc00',
213-
'--vscode-terminal-ansiYellow': '#949800',
214-
'--vscode-terminal-ansiBlue': '#0451a5',
215-
'--vscode-terminal-ansiMagenta': '#bc05bc',
216-
'--vscode-terminal-ansiCyan': '#0598bc',
217-
'--vscode-terminal-ansiWhite': '#555555',
218-
'--vscode-terminal-ansiBrightBlack': '#666666',
219-
'--vscode-terminal-ansiBrightRed': '#cd3131',
220-
'--vscode-terminal-ansiBrightGreen': '#14ce14',
221-
'--vscode-terminal-ansiBrightYellow': '#b5ba00',
222-
'--vscode-terminal-ansiBrightBlue': '#0451a5',
223-
'--vscode-terminal-ansiBrightMagenta': '#bc05bc',
224-
'--vscode-terminal-ansiBrightCyan': '#0598bc',
225-
'--vscode-terminal-ansiBrightWhite': '#a5a5a5',
226-
'--vscode-terminalCursor-foreground': '#000000',
227-
'--vscode-terminal-selectionBackground': '#add6ff80',
228-
},
229-
origin: { kind: 'bundled' },
230-
},
231-
];
232-
}
233-
234127
async function main() {
235-
const allThemes = [...builtinThemes()];
128+
const allThemes = [];
236129

237130
for (const ext of EXTENSIONS) {
238131
const themes = await fetchExtensionThemes(ext.namespace, ext.name);
239132
allThemes.push(...themes);
240133
}
134+
allThemes.sort((a, b) => {
135+
const aIndex = PREFERRED_THEME_ORDER.indexOf(a.id);
136+
const bIndex = PREFERRED_THEME_ORDER.indexOf(b.id);
137+
if (aIndex === -1 && bIndex === -1) return 0;
138+
if (aIndex === -1) return 1;
139+
if (bIndex === -1) return -1;
140+
return aIndex - bIndex;
141+
});
241142

242143
console.log(`\nWriting ${allThemes.length} themes to ${OUTPUT}`);
243144
writeFileSync(OUTPUT, JSON.stringify(allThemes, null, 2) + '\n');

0 commit comments

Comments
 (0)