mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 08:07:49 +00:00
feat(themes): Built-in Solarized and One light/dark themes (#85)
Adds Solarized Light, Solarized Dark, One Light and One Dark as built-in themes across the workbench, editor, terminal and Mermaid, keeping the original palettes (MIT notices in THIRD_PARTY_LICENSES.md, mapping in docs/themes.md). - Native window colours are read from tokens.css at build time (`theme_frame.rs`) instead of a second colour table. - Startup restores the built-in palette before first paint. - Editor schemes, the terminal and Mermaid refresh on switches between themes of the same kind. - The plugin packages of the same themes are removed. - A test requires every built-in palette theme to define each token (only listed ones may inherit from the base theme), so new tokens can't silently miss them. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VUYsdXA2nkF5v6zsC93dpn
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
convertIcls,
|
||||
@@ -77,73 +77,6 @@ describe("theme plugin template", () => {
|
||||
});
|
||||
});
|
||||
|
||||
/** Theme packages in plugins/: every theme they contribute, with its editor scheme. */
|
||||
function pluginThemes() {
|
||||
return readdirSync("plugins", { withFileTypes: true })
|
||||
.filter((d) => d.isDirectory() && existsSync(`plugins/${d.name}/plugin.toml`))
|
||||
.flatMap((d) => {
|
||||
const dir = `plugins/${d.name}`;
|
||||
const manifest = read(`${dir}/plugin.toml`);
|
||||
return manifest
|
||||
.split("[[contributes.themes]]")
|
||||
.slice(1)
|
||||
.map((entry) => ({
|
||||
dir,
|
||||
manifest,
|
||||
path: /^path\s*=\s*"([^"]+)"/m.exec(entry)?.[1],
|
||||
scheme: /^editor-scheme\s*=\s*"([^"]+)"/m.exec(entry)?.[1],
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
describe("theme plugins in plugins/", () => {
|
||||
const themes = pluginThemes();
|
||||
|
||||
it("include Solarized and One, light and dark", () => {
|
||||
expect(themes.map((t) => `${t.dir}/${t.path}`)).toEqual(
|
||||
expect.arrayContaining([
|
||||
"plugins/theme-solarized/themes/solarized-light.theme.json",
|
||||
"plugins/theme-solarized/themes/solarized-dark.theme.json",
|
||||
"plugins/theme-one/themes/one-light.theme.json",
|
||||
"plugins/theme-one/themes/one-dark.theme.json",
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it.each(themes.map((t) => [`${t.dir}/${t.path}`, t] as const))(
|
||||
"%s sets every UI token and its scheme every editor token",
|
||||
(_name, t) => {
|
||||
// A failure here means tokens.css gained (or lost) a themeable token: add it to the theme too.
|
||||
expect(t.path && t.scheme, "theme and editor-scheme paths").toBeTruthy();
|
||||
expect(t.manifest).not.toMatch(/^wasm\s*=/m);
|
||||
const ui = JSON.parse(stripJsonComments(read(`${t.dir}/${t.path}`))) as {
|
||||
id: string;
|
||||
name: string;
|
||||
kind: string;
|
||||
tokens: Record<string, string>;
|
||||
};
|
||||
const ed = JSON.parse(stripJsonComments(read(`${t.dir}/${t.scheme}`))) as { tokens: Record<string, string> };
|
||||
expect(ui.id).toMatch(/^[a-z0-9][a-z0-9._-]*$/);
|
||||
expect(ui.name.trim()).not.toBe("");
|
||||
expect(["light", "dark"]).toContain(ui.kind);
|
||||
expect(sorted(Object.keys(ui.tokens))).toEqual(sorted(sets.ui));
|
||||
expect(sorted(Object.keys(ed.tokens))).toEqual(sorted(sets.scheme));
|
||||
for (const [k, v] of Object.entries({ ...ui.tokens, ...ed.tokens })) {
|
||||
expect(/[;{}<>\\\n]|url\(|image\(/i.test(v), `${k}: ${v}`).toBe(false);
|
||||
// var() may only point at tokens the IDE defines.
|
||||
for (const [, ref] of v.matchAll(/var\((--[a-z0-9-]+)/g)) expect(css, `${k} → ${ref}`).toContain(`${ref}:`);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it("ship the licence of the palettes they port", () => {
|
||||
for (const dir of new Set(themes.map((t) => t.dir))) {
|
||||
if (dir === "plugins/theme-solarized" || dir === "plugins/theme-one")
|
||||
expect(read(`${dir}/LICENSE`), dir).toContain("MIT License");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("IntelliJ theme import", () => {
|
||||
const themeJson = `{
|
||||
// comment
|
||||
|
||||
Reference in New Issue
Block a user