From 9fdbf35fd5221b298546979ffa7e9f5c86a96dbc Mon Sep 17 00:00:00 2001 From: Rafael Staib Date: Fri, 12 Jun 2026 16:06:37 +0200 Subject: [PATCH 1/4] fix: key icon metadata and sprites to canonical SVG file names --- scripts/check-metadata.js | 24 +++++++-------- scripts/svg-sprite.js | 48 +++++++++++++++++------------- src/template/mapping.json | 60 +++++++++++++++++++------------------- src/template/metadata.json | 34 ++++++++++----------- 4 files changed, 85 insertions(+), 81 deletions(-) diff --git a/scripts/check-metadata.js b/scripts/check-metadata.js index 6505247b..8409ff43 100644 --- a/scripts/check-metadata.js +++ b/scripts/check-metadata.js @@ -8,12 +8,12 @@ const fs = require('fs'); const path = require('path'); -// Load the mapping file to get all icon names -const mappingPath = path.join(__dirname, '..', 'src', 'template', 'mapping.json'); +// Load the icons directory to get all icon names +const iconsDir = path.join(__dirname, '..', 'src', 'icons'); const metadataPath = path.join(__dirname, '..', 'src', 'template', 'metadata.json'); -if (!fs.existsSync(mappingPath)) { - console.error('Error: mapping.json not found at', mappingPath); +if (!fs.existsSync(iconsDir)) { + console.error('Error: icons directory not found at', iconsDir); process.exit(1); } @@ -22,17 +22,15 @@ if (!fs.existsSync(metadataPath)) { process.exit(1); } -const mapping = JSON.parse(fs.readFileSync(mappingPath, 'utf8')); const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8')); -// Collect all unique icon names from mapping -const allIconNames = new Set(); -Object.values(mapping).forEach(aliases => { - // For each code point, add the first alias (primary name) - if (aliases && aliases.length > 0) { - allIconNames.add(aliases[0]); - } -}); +// Collect all icon names from the actual SVG files. Metadata is keyed by the +// SVG file name, which is the canonical identity for each icon. +const allIconNames = new Set( + fs.readdirSync(iconsDir) + .filter(file => file.endsWith('.svg')) + .map(file => path.basename(file, '.svg')) +); // Find icons without metadata const missingMetadata = []; diff --git a/scripts/svg-sprite.js b/scripts/svg-sprite.js index 7b3688a6..81a7f377 100644 --- a/scripts/svg-sprite.js +++ b/scripts/svg-sprite.js @@ -65,31 +65,37 @@ let processedFiles = 0; const processedIcons = new Set(); Object.entries(mapping).forEach(([code, aliases]) => { - // Use the first alias as the primary SVG file name - const primaryAlias = aliases[0]; - + // The SVG file backing this icon may be named after any of its aliases, + // not necessarily the primary one, so pick whichever file actually exists. + const sourceAlias = aliases.find(alias => + fs.existsSync(path.resolve(iconsDir, `${alias}.svg`)) + ); + + if (!sourceAlias) { + console.warn(`Warning: no SVG file found for code ${code} (aliases: ${aliases.join(", ")})`); + return; + } + // Skip if we've already processed this icon - if (processedIcons.has(primaryAlias)) { + if (processedIcons.has(sourceAlias)) { return; } - + // Use path.resolve for cross-platform compatibility - const file = path.resolve(iconsDir, `${primaryAlias}.svg`); - - if (fs.existsSync(file)) { - processedFiles++; - processedIcons.add(primaryAlias); - - // Add sprite entries for all aliases of this icon - for (const name of aliases) { - // Use path.resolve for cross-platform compatibility - const svgPath = path.resolve(iconsDir, `${name}.svg`); - spriter.add( - svgPath, - name + ".svg", - fs.readFileSync(file, "utf-8"), - ); - } + const file = path.resolve(iconsDir, `${sourceAlias}.svg`); + + processedFiles++; + processedIcons.add(sourceAlias); + + // Add sprite entries for all aliases of this icon + for (const name of aliases) { + // Use path.resolve for cross-platform compatibility + const svgPath = path.resolve(iconsDir, `${name}.svg`); + spriter.add( + svgPath, + name + ".svg", + fs.readFileSync(file, "utf-8"), + ); } }); diff --git a/src/template/mapping.json b/src/template/mapping.json index 20cb1f8d..365f8374 100644 --- a/src/template/mapping.json +++ b/src/template/mapping.json @@ -14,8 +14,8 @@ "repo-delete" ], "60003": [ - "gist-fork", - "repo-forked" + "repo-forked", + "gist-fork" ], "60004": [ "git-pull-request", @@ -45,30 +45,30 @@ "mirror-public" ], "60010": [ + "star-empty", "star", "star-add", - "star-delete", - "star-empty" + "star-delete" ], "60011": [ "comment", "comment-add" ], "60012": [ - "alert", - "warning" + "warning", + "alert" ], "60013": [ "search", "search-save" ], "60014": [ - "log-out", - "sign-out" + "sign-out", + "log-out" ], "60015": [ - "log-in", - "sign-in" + "sign-in", + "log-in" ], "60016": [ "eye", @@ -96,9 +96,9 @@ "issue-opened" ], "60021": [ + "lock", "gist-private", "git-fork-private", - "lock", "mirror-private" ], "60022": [ @@ -107,12 +107,12 @@ "x" ], "60023": [ - "repo-sync", - "sync" + "sync", + "repo-sync" ], "60024": [ - "clone", - "desktop-download" + "desktop-download", + "clone" ], "60025": [ "beaker", @@ -126,13 +126,13 @@ "file" ], "60028": [ - "more", "ellipsis", + "more", "kebab-horizontal" ], "60029": [ - "mail-reply", - "reply" + "reply", + "mail-reply" ], "60030": [ "organization", @@ -161,9 +161,9 @@ "symbol-folder" ], "60036": [ + "github", "logo-github", - "mark-github", - "github" + "mark-github" ], "60037": [ "terminal", @@ -171,25 +171,25 @@ "repl" ], "60038": [ - "zap", - "symbol-event" + "symbol-event", + "zap" ], "60039": [ "error", "stop" ], "60040": [ - "variable", - "symbol-variable" + "symbol-variable", + "variable" ], "60042": [ - "array", - "symbol-array" + "symbol-array", + "array" ], "60043": [ + "symbol-namespace", "symbol-module", "symbol-package", - "symbol-namespace", "symbol-object" ], "60044": [ @@ -218,8 +218,8 @@ "symbol-text" ], "60052": [ - "symbol-reference", - "go-to-file" + "go-to-file", + "symbol-reference" ], "60053": [ "symbol-enum", @@ -344,8 +344,8 @@ "chrome-restore" ], "60092": [ - "circle-outline", "circle", + "circle-outline", "debug-breakpoint-unverified", "terminal-decoration-incomplete" ], diff --git a/src/template/metadata.json b/src/template/metadata.json index c03a14fb..5a92b586 100644 --- a/src/template/metadata.json +++ b/src/template/metadata.json @@ -82,7 +82,7 @@ "category": "ai", "description": "AI agent or bot (compact 12x12 variant)" }, - "alert": { + "warning": { "tags": [ "warning", "error", @@ -106,7 +106,7 @@ "category": "file", "description": "Archive or package items" }, - "array": { + "symbol-array": { "tags": [ "list", "collection", @@ -930,7 +930,7 @@ "category": "shape", "description": "Large filled circle" }, - "circle-outline": { + "circle": { "tags": [ "round", "shape", @@ -1061,7 +1061,7 @@ "category": "general", "description": "Clock face showing time" }, - "clone": { + "desktop-download": { "tags": [ "copy", "duplicate", @@ -2748,7 +2748,7 @@ "category": "git", "description": "GitHub Gist" }, - "gist-fork": { + "repo-forked": { "tags": [ "copy", "branch", @@ -2761,7 +2761,7 @@ "category": "git", "description": "Fork Gist" }, - "gist-private": { + "lock": { "tags": [ "lock", "secret", @@ -3939,7 +3939,7 @@ "category": "security", "description": "Small lock" }, - "log-in": { + "sign-in": { "tags": [ "sign in", "enter", @@ -3950,7 +3950,7 @@ "category": "user", "description": "Log in" }, - "log-out": { + "sign-out": { "tags": [ "sign out", "exit", @@ -3961,7 +3961,7 @@ "category": "user", "description": "Log out" }, - "logo-github": { + "github": { "tags": [ "brand", "cat", @@ -4004,7 +4004,7 @@ "category": "communication", "description": "Read mail" }, - "mail-reply": { + "reply": { "tags": [ "email", "respond", @@ -4191,7 +4191,7 @@ "category": "action", "description": "Mirror or reflect" }, - "more": { + "ellipsis": { "tags": [ "ellipsis", "menu", @@ -5038,7 +5038,7 @@ "category": "git", "description": "Selected repository" }, - "repo-sync": { + "sync": { "tags": [ "git", "refresh", @@ -5644,7 +5644,7 @@ "category": "general", "description": "Squirrel" }, - "star": { + "star-empty": { "tags": [ "favorite", "bookmark", @@ -5873,7 +5873,7 @@ "category": "symbol", "description": "Miscellaneous symbol" }, - "symbol-module": { + "symbol-namespace": { "tags": [ "code", "package", @@ -5928,7 +5928,7 @@ "category": "symbol", "description": "Property symbol" }, - "symbol-reference": { + "go-to-file": { "tags": [ "code", "link", @@ -6444,7 +6444,7 @@ "category": "status", "description": "Unverified" }, - "variable": { + "symbol-variable": { "tags": [ "code", "value", @@ -6789,7 +6789,7 @@ "category": "git", "description": "Git worktree (small variant)" }, - "zap": { + "symbol-event": { "tags": [ "flash", "electric", From 674cb9cf5d7b6d83460a0120b8343e2f1eb4618c Mon Sep 17 00:00:00 2001 From: Rafael Staib Date: Sun, 20 Sep 2026 17:16:10 +0200 Subject: [PATCH 2/4] Fix alphabetical sort order for renamed metadata entries The 17 icons renamed in #482 kept their old keys' positions in metadata.json instead of moving to the new key's alphabetical spot. --- src/template/metadata.json | 338 ++++++++++++++++++------------------- 1 file changed, 169 insertions(+), 169 deletions(-) diff --git a/src/template/metadata.json b/src/template/metadata.json index e1474bda..19c203e0 100644 --- a/src/template/metadata.json +++ b/src/template/metadata.json @@ -82,18 +82,6 @@ "category": "ai", "description": "AI agent or bot (compact 12x12 variant)" }, - "warning": { - "tags": [ - "warning", - "error", - "danger", - "notification", - "triangle", - "exclamation" - ], - "category": "status", - "description": "Alert or warning" - }, "archive": { "tags": [ "save", @@ -106,17 +94,6 @@ "category": "file", "description": "Archive or package items" }, - "symbol-array": { - "tags": [ - "list", - "collection", - "brackets", - "data", - "structure" - ], - "category": "symbol", - "description": "Array data structure" - }, "arrow-both": { "tags": [ "switch", @@ -910,6 +887,17 @@ "category": "window", "description": "Restore window button" }, + "circle": { + "tags": [ + "round", + "shape", + "empty", + "ring", + "hollow" + ], + "category": "shape", + "description": "Circle outline shape" + }, "circle-filled": { "tags": [ "dot", @@ -955,17 +943,6 @@ "category": "shape", "description": "Large filled circle" }, - "circle": { - "tags": [ - "round", - "shape", - "empty", - "ring", - "hollow" - ], - "category": "shape", - "description": "Circle outline shape" - }, "circle-slash": { "tags": [ "error", @@ -1086,17 +1063,6 @@ "category": "general", "description": "Clock face showing time" }, - "desktop-download": { - "tags": [ - "copy", - "duplicate", - "replicate", - "git", - "repo" - ], - "category": "action", - "description": "Clone or duplicate" - }, "close": { "tags": [ "remove", @@ -2097,6 +2063,17 @@ "category": "debug", "description": "Stop debugging" }, + "desktop-download": { + "tags": [ + "copy", + "duplicate", + "replicate", + "git", + "repo" + ], + "category": "action", + "description": "Clone or duplicate" + }, "developer-tools": { "tags": [ "developer", @@ -2325,6 +2302,17 @@ "category": "layout", "description": "Editor layout" }, + "ellipsis": { + "tags": [ + "ellipsis", + "menu", + "options", + "actions", + "dots" + ], + "category": "navigation", + "description": "More options" + }, "empty-window": { "tags": [ "blank", @@ -2989,31 +2977,6 @@ "category": "git", "description": "GitHub Gist" }, - "repo-forked": { - "tags": [ - "copy", - "branch", - "split", - "duplicate", - "share", - "code", - "pr" - ], - "category": "git", - "description": "Fork Gist" - }, - "lock": { - "tags": [ - "lock", - "secret", - "hidden", - "secure", - "code", - "pr" - ], - "category": "git", - "description": "Private Gist" - }, "gist-secret": { "tags": [ "lock", @@ -3276,6 +3239,17 @@ "category": "git", "description": "Pop git stash" }, + "github": { + "tags": [ + "brand", + "cat", + "octocat", + "git", + "social" + ], + "category": "brand", + "description": "GitHub logo" + }, "github-action": { "tags": [ "ci", @@ -3342,6 +3316,17 @@ "category": "action", "description": "Go to editing session" }, + "go-to-file": { + "tags": [ + "code", + "link", + "pointer", + "alias", + "use" + ], + "category": "symbol", + "description": "Reference symbol" + }, "go-to-search": { "tags": [ "find", @@ -4250,6 +4235,18 @@ "category": "general", "description": "Location or place" }, + "lock": { + "tags": [ + "lock", + "secret", + "hidden", + "secure", + "code", + "pr" + ], + "category": "git", + "description": "Private Gist" + }, "lock-small": { "tags": [ "secure", @@ -4262,39 +4259,6 @@ "category": "security", "description": "Small lock" }, - "sign-in": { - "tags": [ - "sign in", - "enter", - "access", - "auth", - "arrow" - ], - "category": "user", - "description": "Log in" - }, - "sign-out": { - "tags": [ - "sign out", - "exit", - "leave", - "disconnect", - "arrow" - ], - "category": "user", - "description": "Log out" - }, - "github": { - "tags": [ - "brand", - "cat", - "octocat", - "git", - "social" - ], - "category": "brand", - "description": "GitHub logo" - }, "magnet": { "tags": [ "stick", @@ -4327,17 +4291,6 @@ "category": "communication", "description": "Read mail" }, - "reply": { - "tags": [ - "email", - "respond", - "answer", - "back", - "arrow" - ], - "category": "communication", - "description": "Reply to mail" - }, "map": { "tags": [ "location", @@ -4591,17 +4544,6 @@ "category": "action", "description": "Mirror or reflect" }, - "ellipsis": { - "tags": [ - "ellipsis", - "menu", - "options", - "actions", - "dots" - ], - "category": "navigation", - "description": "More options" - }, "mortar-board": { "tags": [ "education", @@ -5376,6 +5318,17 @@ "category": "action", "description": "Replace all" }, + "reply": { + "tags": [ + "email", + "respond", + "answer", + "back", + "arrow" + ], + "category": "communication", + "description": "Reply to mail" + }, "repo": { "tags": [ "repository", @@ -5430,6 +5383,19 @@ "category": "git", "description": "Force push repository" }, + "repo-forked": { + "tags": [ + "copy", + "branch", + "split", + "duplicate", + "share", + "code", + "pr" + ], + "category": "git", + "description": "Fork Gist" + }, "repo-forked-compact": { "tags": [ "repository", @@ -5486,17 +5452,6 @@ "category": "git", "description": "Selected repository" }, - "sync": { - "tags": [ - "git", - "refresh", - "update", - "cycle", - "arrows" - ], - "category": "git", - "description": "Sync repository" - }, "report": { "tags": [ "document", @@ -5999,6 +5954,28 @@ "category": "security", "description": "Security or protection (compact variant)" }, + "sign-in": { + "tags": [ + "sign in", + "enter", + "access", + "auth", + "arrow" + ], + "category": "user", + "description": "Log in" + }, + "sign-out": { + "tags": [ + "sign out", + "exit", + "leave", + "disconnect", + "arrow" + ], + "category": "user", + "description": "Log out" + }, "skip": { "tags": [ "next", @@ -6183,6 +6160,17 @@ "category": "development", "description": "Surround with" }, + "symbol-array": { + "tags": [ + "list", + "collection", + "brackets", + "data", + "structure" + ], + "category": "symbol", + "description": "Array data structure" + }, "symbol-boolean": { "tags": [ "code", @@ -6260,6 +6248,17 @@ "category": "symbol", "description": "Enum member symbol" }, + "symbol-event": { + "tags": [ + "flash", + "electric", + "power", + "energy", + "fast" + ], + "category": "general", + "description": "Zap or flash" + }, "symbol-field": { "tags": [ "code", @@ -6402,17 +6401,6 @@ "category": "symbol", "description": "Property symbol" }, - "go-to-file": { - "tags": [ - "code", - "link", - "pointer", - "alias", - "use" - ], - "category": "symbol", - "description": "Reference symbol" - }, "symbol-ruler": { "tags": [ "code", @@ -6457,6 +6445,28 @@ "category": "symbol", "description": "Structure symbol" }, + "symbol-variable": { + "tags": [ + "code", + "value", + "store", + "data", + "box" + ], + "category": "symbol", + "description": "Variable" + }, + "sync": { + "tags": [ + "git", + "refresh", + "update", + "cycle", + "arrows" + ], + "category": "git", + "description": "Sync repository" + }, "sync-compact": { "tags": [ "refresh", @@ -6918,17 +6928,6 @@ "category": "status", "description": "Unverified" }, - "symbol-variable": { - "tags": [ - "code", - "value", - "store", - "data", - "box" - ], - "category": "symbol", - "description": "Variable" - }, "variable-group": { "tags": [ "code", @@ -7130,6 +7129,18 @@ "category": "tool", "description": "Magic wand" }, + "warning": { + "tags": [ + "warning", + "error", + "danger", + "notification", + "triangle", + "exclamation" + ], + "category": "status", + "description": "Alert or warning" + }, "warning-compact": { "tags": [ "alert", @@ -7287,17 +7298,6 @@ "category": "git", "description": "Git worktree (small variant)" }, - "symbol-event": { - "tags": [ - "flash", - "electric", - "power", - "energy", - "fast" - ], - "category": "general", - "description": "Zap or flash" - }, "zoom-in": { "tags": [ "magnify", From 4bde58283db37747c2c147b51f0cef11a13d57ba Mon Sep 17 00:00:00 2001 From: rstaib Date: Sun, 20 Sep 2026 17:26:58 +0200 Subject: [PATCH 3/4] Fix remaining alphabetical sort violations in metadata.json Nine icon entries (voice-mode* and a few compact variants) were appended at the end of the file instead of being inserted in alphabetical order. --- src/template/metadata.json | 218 ++++++++++++++++++------------------- 1 file changed, 109 insertions(+), 109 deletions(-) diff --git a/src/template/metadata.json b/src/template/metadata.json index 19c203e0..8ba7ea69 100644 --- a/src/template/metadata.json +++ b/src/template/metadata.json @@ -255,6 +255,17 @@ "category": "navigation", "description": "Arrow pointing up" }, + "arrow-up-compact": { + "tags": [ + "arrow", + "up", + "direction", + "point", + "compact" + ], + "category": "navigation", + "description": "Arrow pointing up (compact 12x12 variant)" + }, "ask": { "tags": [ "question", @@ -1141,6 +1152,17 @@ "category": "action", "description": "Download from cloud" }, + "cloud-download-compact": { + "tags": [ + "cloud", + "download", + "arrow", + "save", + "compact" + ], + "category": "cloud", + "description": "Cloud download (compact 12x12 variant)" + }, "cloud-small": { "tags": [ "online", @@ -1162,6 +1184,17 @@ "category": "action", "description": "Upload to cloud" }, + "cloud-upload-compact": { + "tags": [ + "cloud", + "upload", + "arrow", + "send", + "compact" + ], + "category": "cloud", + "description": "Cloud upload (compact 12x12 variant)" + }, "code": { "tags": [ "embed", @@ -4440,6 +4473,18 @@ "category": "device", "description": "Microphone or audio" }, + "mic-compact": { + "tags": [ + "microphone", + "audio", + "sound", + "voice", + "record", + "compact" + ], + "category": "media", + "description": "Microphone (compact 12x12 variant)" + }, "mic-download": { "tags": [ "microphone", @@ -7061,6 +7106,57 @@ "category": "device", "description": "Small VM icon" }, + "voice-mode": { + "tags": [ + "voice", + "audio", + "waveform", + "sound", + "microphone", + "speech" + ], + "category": "media", + "description": "Voice mode waveform indicator" + }, + "voice-mode-compact": { + "tags": [ + "voice", + "audio", + "waveform", + "sound", + "microphone", + "speech", + "compact" + ], + "category": "media", + "description": "Compact voice mode waveform indicator" + }, + "voice-mode-download": { + "tags": [ + "voice", + "audio", + "waveform", + "sound", + "download", + "save", + "arrow" + ], + "category": "media", + "description": "Voice mode waveform with download indicator" + }, + "voice-mode-download-compact": { + "tags": [ + "voice", + "audio", + "waveform", + "sound", + "download", + "save", + "compact" + ], + "category": "media", + "description": "Compact voice mode waveform with download indicator" + }, "vr": { "tags": [ "virtual", @@ -7298,6 +7394,19 @@ "category": "git", "description": "Git worktree (small variant)" }, + "xai": { + "tags": [ + "xai", + "grok", + "ai", + "logo", + "model", + "assistant", + "brand" + ], + "category": "brand", + "description": "xAI logo" + }, "zoom-in": { "tags": [ "magnify", @@ -7317,114 +7426,5 @@ ], "category": "action", "description": "Zoom out or reduce" - }, - "voice-mode": { - "tags": [ - "voice", - "audio", - "waveform", - "sound", - "microphone", - "speech" - ], - "category": "media", - "description": "Voice mode waveform indicator" - }, - "voice-mode-compact": { - "tags": [ - "voice", - "audio", - "waveform", - "sound", - "microphone", - "speech", - "compact" - ], - "category": "media", - "description": "Compact voice mode waveform indicator" - }, - "voice-mode-download": { - "tags": [ - "voice", - "audio", - "waveform", - "sound", - "download", - "save", - "arrow" - ], - "category": "media", - "description": "Voice mode waveform with download indicator" - }, - "voice-mode-download-compact": { - "tags": [ - "voice", - "audio", - "waveform", - "sound", - "download", - "save", - "compact" - ], - "category": "media", - "description": "Compact voice mode waveform with download indicator" - }, - "arrow-up-compact": { - "tags": [ - "arrow", - "up", - "direction", - "point", - "compact" - ], - "category": "navigation", - "description": "Arrow pointing up (compact 12x12 variant)" - }, - "mic-compact": { - "tags": [ - "microphone", - "audio", - "sound", - "voice", - "record", - "compact" - ], - "category": "media", - "description": "Microphone (compact 12x12 variant)" - }, - "cloud-download-compact": { - "tags": [ - "cloud", - "download", - "arrow", - "save", - "compact" - ], - "category": "cloud", - "description": "Cloud download (compact 12x12 variant)" - }, - "cloud-upload-compact": { - "tags": [ - "cloud", - "upload", - "arrow", - "send", - "compact" - ], - "category": "cloud", - "description": "Cloud upload (compact 12x12 variant)" - }, - "xai": { - "tags": [ - "xai", - "grok", - "ai", - "logo", - "model", - "assistant", - "brand" - ], - "category": "brand", - "description": "xAI logo" } } From 66054a51f82ac83b9473217882d6ca020dc717e0 Mon Sep 17 00:00:00 2001 From: Rafael Staib Date: Mon, 21 Sep 2026 23:53:57 +0200 Subject: [PATCH 4/4] Potential fix for pull request finding 'Fail build when mapped codepoints lack backing SVGs' Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- scripts/svg-sprite.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/svg-sprite.js b/scripts/svg-sprite.js index 81a7f377..0e04396f 100644 --- a/scripts/svg-sprite.js +++ b/scripts/svg-sprite.js @@ -72,8 +72,8 @@ Object.entries(mapping).forEach(([code, aliases]) => { ); if (!sourceAlias) { - console.warn(`Warning: no SVG file found for code ${code} (aliases: ${aliases.join(", ")})`); - return; + console.error(`Error: no SVG file found for code ${code} (aliases: ${aliases.join(", ")})`); + process.exit(1); } // Skip if we've already processed this icon