From 1dc5c4d84bfb670ac1266458dccb41f1d617da23 Mon Sep 17 00:00:00 2001 From: Rui Duarte Date: Wed, 27 May 2026 14:49:33 +0100 Subject: [PATCH] feat: add Zed icon theme support --- .gitignore | 1 + .vscodeignore | 2 + README.md | 14 ++++- extension.toml | 7 +++ scripts/build-icon-theme.mjs | 103 +++++++++++++++++++++++++++++++++++ 5 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 extension.toml diff --git a/.gitignore b/.gitignore index 55619fd..8228185 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ node_modules/ icons/ +icon_themes/ test/ .DS_Store *.vsix diff --git a/.vscodeignore b/.vscodeignore index 7800f0e..68cd2db 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -1,6 +1,8 @@ .vscode/** .github/ .gitignore +extension.toml +icon_themes/ node_modules/ package-lock.json scripts/ diff --git a/README.md b/README.md index bddbb83..5ace6fb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Pierre Icons for VS Code +# Pierre Icons -File icon theme for VS Code with three tiers and per-icon palette colors. +File icon theme for VS Code-compatible editors and Zed with three tiers and per-icon palette colors. ## Themes @@ -25,7 +25,7 @@ Minimal and default tiers are monochrome (gray `400` / `800`). The complete tier npm run build ``` -This reads source SVGs from `svgs/`, optimizes them with SVGO, stamps dark/light fill colors, and writes the output to `icons/`. Three theme JSON files are generated: `theme-minimal.json`, `theme-default.json`, and `theme-complete.json`. +This reads source SVGs from `svgs/`, optimizes them with SVGO, stamps dark/light fill colors, and writes the output to `icons/`. Three VS Code theme JSON files are generated: `theme-minimal.json`, `theme-default.json`, and `theme-complete.json`. The build also writes Zed icon theme JSON files to `icon_themes/`. For development, watch mode rebuilds on SVG changes: @@ -42,6 +42,14 @@ npm run watch 3. Press `F5` to launch an Extension Development Host. 4. Choose one of the Pierre Icons themes from **File Icon Theme**. +### Run from source (Zed) + +1. Run `npm ci` if dependencies are not installed. +2. Run `npm run build`. +3. In Zed, run `zed: install dev extension`. +4. Select this repository folder. +5. Choose one of the Pierre Icons themes from the Icon Theme Selector. + ### Package a VSIX ```bash diff --git a/extension.toml b/extension.toml new file mode 100644 index 0000000..922d9be --- /dev/null +++ b/extension.toml @@ -0,0 +1,7 @@ +id = "pierre-icons-theme" +name = "Pierre Icons" +version = "0.0.9" +schema_version = 1 +authors = ["Pierre Computer"] +description = "File icon theme backed by source SVGs." +repository = "https://github.com/pierrecomputer/vscode-icons" diff --git a/scripts/build-icon-theme.mjs b/scripts/build-icon-theme.mjs index 19c2424..5d0ccc6 100644 --- a/scripts/build-icon-theme.mjs +++ b/scripts/build-icon-theme.mjs @@ -12,6 +12,7 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const rootDir = path.resolve(__dirname, ".."); const iconOutputDir = path.join(rootDir, "icons"); +const zedThemeOutputDir = path.join(rootDir, "icon_themes"); const svgDir = path.join(rootDir, "svgs"); const tiers = { @@ -160,11 +161,93 @@ function buildTheme(icons, { colored = false } = {}) { return theme; } +function iconPath(name, { appearance, color, colored }) { + const mode = appearance === "light" ? "light" : "dark"; + + if (colored && color) { + return mode === "light" ? `./icons/${name}-color-light.svg` : `./icons/${name}-color.svg`; + } + + return mode === "light" ? `./icons/${name}-light.svg` : `./icons/${name}.svg`; +} + +function buildZedTheme(icons, { appearance, colored = false, themeName }) { + const fileIcons = { + default: { + path: iconPath("file-duo", { appearance, colored: false }), + }, + }; + const fileSuffixes = {}; + const fileStems = {}; + const namedDirectoryIcons = {}; + + for (const { + name, + color: iconColor, + fileExtensions: exts, + fileNames: names, + folderNames, + } of icons) { + const path = iconPath(name, { appearance, color: iconColor, colored }); + fileIcons[name] = { path }; + + if (exts) { + for (const ext of exts) { + fileSuffixes[ext] = name; + } + } + if (names) { + for (const fn of names) { + fileStems[fn] = name; + } + } + if (folderNames) { + for (const folder of folderNames) { + namedDirectoryIcons[folder] = { + collapsed: path, + expanded: path, + }; + } + } + } + + const theme = { + name: themeName, + appearance, + directory_icons: { + collapsed: iconPath("folder-duo", { appearance, colored: false }), + expanded: iconPath("folder-open-duo", { appearance, colored: false }), + }, + file_suffixes: fileSuffixes, + file_stems: fileStems, + file_icons: fileIcons, + }; + + if (Object.keys(namedDirectoryIcons).length > 0) { + theme.named_directory_icons = namedDirectoryIcons; + } + + return theme; +} + +function buildZedThemeFamily(icons, { themeName, colored = false }) { + return { + $schema: "https://zed.dev/schema/icon_themes/v0.3.0.json", + name: themeName, + author: "Pierre Computer", + themes: [ + buildZedTheme(icons, { appearance: "dark", colored, themeName }), + buildZedTheme(icons, { appearance: "light", colored, themeName }), + ], + }; +} + // --------------------------------------------------------------------------- // Build // --------------------------------------------------------------------------- await mkdir(iconOutputDir, { recursive: true }); +await mkdir(zedThemeOutputDir, { recursive: true }); const allIcons = new Map(); for (const icons of Object.values(tiers)) { @@ -191,9 +274,29 @@ const tierOptions = { complete: { colored: true }, }; +const zedThemeNames = { + minimal: "Pierre Icons (Minimal)", + default: "Pierre Icons", + complete: "Pierre Icons (Complete)", +}; + +const zedThemeFiles = { + minimal: "pierre-icons-minimal", + default: "pierre-icons", + complete: "pierre-icons-complete", +}; + for (const [name, icons] of Object.entries(tiers)) { const theme = buildTheme(icons, tierOptions[name]); const out = path.join(iconOutputDir, `theme-${name}.json`); await writeFile(out, `${JSON.stringify(theme, null, 2)}\n`); console.log(`Wrote ${path.relative(rootDir, out)}`); + + const zedTheme = buildZedThemeFamily(icons, { + themeName: zedThemeNames[name], + ...tierOptions[name], + }); + const zedOut = path.join(zedThemeOutputDir, `${zedThemeFiles[name]}.json`); + await writeFile(zedOut, `${JSON.stringify(zedTheme, null, 2)}\n`); + console.log(`Wrote ${path.relative(rootDir, zedOut)}`); }