From de8a4e79ff5cc871ea147671e0dc889f80564156 Mon Sep 17 00:00:00 2001 From: Benjamin Schaaf Date: Tue, 9 Jun 2026 16:00:58 +1000 Subject: [PATCH 1/2] Don't include icons for labels with 1 or fewer uses They also aren't included in the list of labels, so no reason to bloat the icons for insignificant labels. Additionally, this improves exclusions and aliases of icons. C, C# and C++ icons look very silly next to their names, so exclude those. --- label-icons-config.json | 6 +- label-icons.json | 38 ---------- static/label-icons.svg | 148 ------------------------------------- util/build-label-icons.mjs | 7 +- 4 files changed, 8 insertions(+), 191 deletions(-) diff --git a/label-icons-config.json b/label-icons-config.json index 8714418ae..96a344240 100644 --- a/label-icons-config.json +++ b/label-icons-config.json @@ -1,9 +1,11 @@ { "_": "Manually maintained set of icons to exclude and an alias map from labels to icon/language ids", - "exclude": ["ai"], + "exclude": ["ai", "c", "c#", "c++"], "aliases": { "javascript": "js", "ecmascript": "js", - "ecmascript6": "js" + "ecmascript6": "js", + "cpp": "c++", + "mac": "apple" } } diff --git a/label-icons.json b/label-icons.json index 1a2a7730c..fbcf38078 100644 --- a/label-icons.json +++ b/label-icons.json @@ -4,27 +4,16 @@ "arduino": "cyan", "asciidoc": "cyan", "astro": "graphite", - "audio": "red", "azure": "sky", "bazel": "green", "binary": "graphite", - "blade": "orange", - "bower": "yellow", - "c": "purple", - "c#": "blue", - "c++": "sky", - "cairo": "graphite", "clojure": "purple", "cmake": "blue", "coffeescript": "orange", "composer": "orange", - "cpp": "sky", "crystal": "graphite", "css": "blue", "csv": "green", - "cuda": "green", - "dart": "cyan", - "delphi": "red", "diff": "yellow", "dlang": "red", "docker": "sky", @@ -33,14 +22,11 @@ "ember": "green", "erlang": "red", "eslint": "purple", - "excel": "green", "font": "green", "fortran": "red", "git": "orange", - "gleam": "pink", "go": "sky", "graphql": "pink", - "graphviz": "cyan", "groovy": "cyan", "haml": "yellow", "haskell": "purple", @@ -52,21 +38,16 @@ "jq": "orange", "js": "yellow", "json": "green", - "jsp": "purple", "jsx": "blue", "julia": "purple", - "kdl": "purple", "kotlin": "blue", "less": "blue", - "liquid": "green", "lisp": "red", "log": "graphite", - "lsl": "cyan", "lua": "blue", "markdown": "graphite", "markup": "purple", "matlab": "orange", - "maven": "red", "mdx": "cyan", "nginx": "green", "nim": "yellow", @@ -74,24 +55,15 @@ "note": "green", "npm": "red", "nsis": "purple", - "ocaml": "yellow", - "odin": "blue", - "parquet": "sky", - "pawn": "orange", "pdf": "red", "perl": "graphite", "php": "blue", - "postcss": "red", "powershell": "blue", "preferences": "yellow", - "prisma": "graphite", "protobuf": "graphite", - "pug": "yellow", "puppet": "purple", "python": "yellow", - "qt": "green", "r": "blue", - "racket": "red", "rails": "red", "react": "sky", "restructuredtext": "sky", @@ -103,7 +75,6 @@ "scss": "pink", "settings": "graphite", "shell": "green", - "slim": "cyan", "solidity": "graphite", "source": "sky", "sql": "blue", @@ -120,24 +91,15 @@ "textile": "pink", "todo": "green", "toml": "cyan", - "travis": "orange", "twig": "green", "typescript": "sky", - "typst": "cyan", "unreal": "graphite", - "v": "cyan", "vala": "sky", "verilog": "blue", "vhdl": "blue", "vim": "green", "vue": "cyan", - "vyper": "graphite", - "wast": "purple", - "webpack": "blue", - "wgsl": "sky", "windows": "sky", - "wit": "pink", "yaml": "yellow", - "yarn": "blue", "zig": "yellow" } diff --git a/static/label-icons.svg b/static/label-icons.svg index 17527e642..9ffe25eed 100644 --- a/static/label-icons.svg +++ b/static/label-icons.svg @@ -18,9 +18,6 @@ - - - @@ -32,24 +29,6 @@ - - - - - - - - - - - - - - - - - - @@ -62,9 +41,6 @@ - - - @@ -74,21 +50,6 @@ - - - - - - - - - - - - - - - @@ -115,9 +76,6 @@ - - - @@ -127,12 +85,6 @@ - - - - - - @@ -163,9 +115,6 @@ - - - @@ -202,39 +151,24 @@ - - - - - - - - - - - - - - - @@ -248,15 +182,6 @@ - - - - - - - - - @@ -279,19 +204,6 @@ - - - - - - - - - - - - @@ -301,18 +213,12 @@ - - - - - - @@ -322,9 +228,6 @@ - - - @@ -332,17 +235,9 @@ - - - - - - - - @@ -379,9 +274,6 @@ - - - @@ -431,32 +323,16 @@ - - - - - - - - - - - - @@ -472,36 +348,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/util/build-label-icons.mjs b/util/build-label-icons.mjs index edde7b617..09bd5bd21 100644 --- a/util/build-label-icons.mjs +++ b/util/build-label-icons.mjs @@ -64,7 +64,7 @@ function main() { ensureDir(path.dirname(spritePath)) /** @type {Set} */ - const usedLabels = new Set() + const usedLabels = {} /** @type {Record} */ let aliases = {} /** @type {Set} */ @@ -106,7 +106,8 @@ function main() { const key = label.trim().toLowerCase() if (!key) continue const canonical = (aliases[key] && aliases[key].trim().toLowerCase()) || key - usedLabels.add(canonical) + usedLabels[canonical] ??= 0 + usedLabels[canonical] += 1 } } } @@ -140,7 +141,7 @@ function main() { const typeKey = type.trim().toLowerCase() // If we have a set of used labels, restrict icons to those labels (or alias targets) - if (usedLabels.size > 0 && !usedLabels.has(typeKey)) { + if (usedLabels.size > 0 && (!(typeKey in usedLabels) || usedLabels[typeKey] <= 1)) { continue } From ec6f794d81988a8bf0ca9dc5c72a7d70260196b5 Mon Sep 17 00:00:00 2001 From: herr kaste Date: Wed, 24 Jun 2026 15:08:47 +0200 Subject: [PATCH 2/2] Prune low-use label icons during build Keep build-label-icons focused on shipping the complete used-label icon set, then derive the low-usage subset during the Eleventy build. This lets the generated labels page, search index metadata, and copied SVG sprite agree with the current build dataset. Also keep the manual C-family exclusions and drop the unused mac to apple alias. --- eleventy.config.mjs | 29 +++++++- eleventy.filters.mjs | 79 +++++++++++++++++++-- label-icons-config.json | 3 +- label-icons.json | 34 ++++++++++ static/label-icons.svg | 136 +++++++++++++++++++++++++++++++++++++ util/build-label-icons.mjs | 7 +- 6 files changed, 275 insertions(+), 13 deletions(-) diff --git a/eleventy.config.mjs b/eleventy.config.mjs index 731a27ed0..e8ce4d235 100644 --- a/eleventy.config.mjs +++ b/eleventy.config.mjs @@ -23,6 +23,7 @@ const FEATURED_LABELS = [ 'theme', ] const LABELS_RANK = new Map(FEATURED_LABELS.map((label, index) => [label, index])) +const LABEL_ICON_MINIMUM_USAGE = 2 const MS_IN_DAY = 24 * 60 * 60 * 1000 const MAGIC_FRESHNESS_WINDOW_DAYS = 365 * 2 // bonus for packages that had updates @@ -375,6 +376,7 @@ export default async function (eleventyConfig) { const siteOrigin = isProd ? prodOrigin : devOrigin const staticOutputDir = isProd ? 'static_' + util.gitHash : 'static' const bundledScriptEntries = new Set() + let labelIcons = null eleventyConfig.addPassthroughCopy( { static: staticOutputDir }, @@ -389,6 +391,11 @@ export default async function (eleventyConfig) { eleventyConfig.on('eleventy.after', async ({ directories } = {}) => { const outputDir = directories?.output ?? '_site' await writeVendorModules(path.join(outputDir, staticOutputDir, 'vendor')) + writePrunedLabelIconSprite( + 'static/label-icons.svg', + path.join(outputDir, staticOutputDir, 'label-icons.svg'), + labelIcons?.sources, + ) if (!isProd) { return @@ -514,6 +521,8 @@ export default async function (eleventyConfig) { const packages = all_packages.map(packageData) const packagesWithMagic = computeMagicMetadata(packages) + const labels = util.collectLabels(all_packages) + labelIcons = filters.configureLabelIcons(labels, { minimumUsage: LABEL_ICON_MINIMUM_USAGE }) const livingHomePackages = packages.filter(pkg => !pkg.removed) @@ -603,9 +612,7 @@ export default async function (eleventyConfig) { } } - eleventyConfig.addCollection('labels', () => { - return util.collectLabels(all_packages) - }) + eleventyConfig.addCollection('labels', () => labels) eleventyConfig.addCollection('libraries', () => { return Object.values(workspace.libraries) @@ -668,6 +675,7 @@ export default async function (eleventyConfig) { // Register all named exports from external module as filters for (const [name, fn] of Object.entries(filters)) { + if (name === 'configureLabelIcons') continue eleventyConfig.addFilter(name, fn) } @@ -686,6 +694,21 @@ export default async function (eleventyConfig) { } } +function writePrunedLabelIconSprite(sourcePath, outputPath, visibleSources) { + if (!(visibleSources instanceof Set) || !fs.existsSync(sourcePath)) { + return + } + + const source = fs.readFileSync(sourcePath, 'utf8') + const pruned = source.replace( + /]*\bid="label-icon-([^"]+)"[^>]*>[\s\S]*?<\/symbol>\r?\n?/g, + (symbol, iconSource) => visibleSources.has(iconSource) ? symbol : '', + ) + + fs.mkdirSync(path.dirname(outputPath), { recursive: true }) + fs.writeFileSync(outputPath, pruned, 'utf8') +} + async function bundleJs(staticOutputDir, entries, isProd) { if (!entries.size) { return diff --git a/eleventy.filters.mjs b/eleventy.filters.mjs index ec8247587..2d3058962 100644 --- a/eleventy.filters.mjs +++ b/eleventy.filters.mjs @@ -14,6 +14,8 @@ const configPath = path.join(__dirname, 'label-icons-config.json') let labelIconSourceSet = new Set() let labelIconAliases = {} let labelIconTints = {} +let labelIconVisibleSourceSet = null +let labelIconVisibleTints = null const longDateFormatter = new Intl.DateTimeFormat('en-US', { dateStyle: 'long' }) const compactNumberFormatter = new Intl.NumberFormat('en', { notation: 'compact' }) @@ -67,7 +69,17 @@ export function label_icon_aliases_json() { } export function label_icon_tints_json() { - return JSON.stringify(labelIconTints) + return JSON.stringify(activeLabelIconTints()) +} + +export function configureLabelIcons(labels, { minimumUsage = 1 } = {}) { + labelIconVisibleSourceSet = visibleLabelIconSources(labels, minimumUsage) + labelIconVisibleTints = labelIconTintsForSources(labelIconVisibleSourceSet) + + return { + sources: labelIconVisibleSourceSet, + tints: labelIconVisibleTints, + } } export function label_normalization_note(changes) { @@ -102,7 +114,7 @@ export function search_index_json(packages) { return JSON.stringify({ packages: packages.map(compactSearchPackage), label_icon_aliases: labelIconAliases, - label_icon_tints: labelIconTints, + label_icon_tints: activeLabelIconTints(), }) } @@ -187,7 +199,30 @@ function joinAsSentenceList(parts) { return `${parts.slice(0, -1).join(', ')}, and ${parts.at(-1)}` } -function canonicalLabel(label) { +function visibleLabelIconSources(labels, minimumUsage) { + const counts = new Map() + const threshold = Math.max(1, Number(minimumUsage) || 1) + + for (const item of labels ?? []) { + const key = typeof item?.key === 'string' ? item.key : String(item ?? '') + const canonical = sourceLabelFor(key) + if (!canonical) continue + + const count = Number(item?.count ?? 1) + counts.set(canonical, (counts.get(canonical) ?? 0) + (Number.isFinite(count) ? count : 1)) + } + + const sources = new Set() + for (const source of labelIconSourceSet) { + if ((counts.get(source) ?? 0) >= threshold) { + sources.add(source) + } + } + + return sources +} + +function sourceLabelFor(label) { if (typeof label !== 'string') return '' const normalized = label.trim().toLowerCase() if (!normalized) return '' @@ -204,6 +239,42 @@ function canonicalLabel(label) { return '' } +function labelIconTintsForSources(sources) { + const tints = {} + for (const source of sources) { + if (Object.prototype.hasOwnProperty.call(labelIconTints, source)) { + tints[source] = labelIconTints[source] + } + } + return tints +} + +function activeLabelIconSourceSet() { + return labelIconVisibleSourceSet ?? labelIconSourceSet +} + +function activeLabelIconTints() { + return labelIconVisibleTints ?? labelIconTints +} + +function canonicalLabel(label) { + if (typeof label !== 'string') return '' + const normalized = label.trim().toLowerCase() + if (!normalized) return '' + + const sourceSet = activeLabelIconSourceSet() + const alias = labelIconAliases[normalized] + if (alias && sourceSet.has(alias)) { + return alias + } + + if (sourceSet.has(normalized)) { + return normalized + } + + return '' +} + export function label_icon_id(label) { const canonical = canonicalLabel(label) if (!canonical) return '' @@ -213,7 +284,7 @@ export function label_icon_id(label) { export function label_icon_tint(label) { const canonical = canonicalLabel(label) if (!canonical) return '' - return labelIconTints[canonical] ?? '' + return activeLabelIconTints()[canonical] ?? '' } // number formatting with grouping (e.g. 10,000) diff --git a/label-icons-config.json b/label-icons-config.json index 96a344240..8952e5758 100644 --- a/label-icons-config.json +++ b/label-icons-config.json @@ -5,7 +5,6 @@ "javascript": "js", "ecmascript": "js", "ecmascript6": "js", - "cpp": "c++", - "mac": "apple" + "cpp": "c++" } } diff --git a/label-icons.json b/label-icons.json index fbcf38078..784ca9a6f 100644 --- a/label-icons.json +++ b/label-icons.json @@ -4,9 +4,13 @@ "arduino": "cyan", "asciidoc": "cyan", "astro": "graphite", + "audio": "red", "azure": "sky", "bazel": "green", "binary": "graphite", + "blade": "orange", + "bower": "yellow", + "cairo": "graphite", "clojure": "purple", "cmake": "blue", "coffeescript": "orange", @@ -14,6 +18,9 @@ "crystal": "graphite", "css": "blue", "csv": "green", + "cuda": "green", + "dart": "cyan", + "delphi": "red", "diff": "yellow", "dlang": "red", "docker": "sky", @@ -22,11 +29,14 @@ "ember": "green", "erlang": "red", "eslint": "purple", + "excel": "green", "font": "green", "fortran": "red", "git": "orange", + "gleam": "pink", "go": "sky", "graphql": "pink", + "graphviz": "cyan", "groovy": "cyan", "haml": "yellow", "haskell": "purple", @@ -38,16 +48,21 @@ "jq": "orange", "js": "yellow", "json": "green", + "jsp": "purple", "jsx": "blue", "julia": "purple", + "kdl": "purple", "kotlin": "blue", "less": "blue", + "liquid": "green", "lisp": "red", "log": "graphite", + "lsl": "cyan", "lua": "blue", "markdown": "graphite", "markup": "purple", "matlab": "orange", + "maven": "red", "mdx": "cyan", "nginx": "green", "nim": "yellow", @@ -55,15 +70,24 @@ "note": "green", "npm": "red", "nsis": "purple", + "ocaml": "yellow", + "odin": "blue", + "parquet": "sky", + "pawn": "orange", "pdf": "red", "perl": "graphite", "php": "blue", + "postcss": "red", "powershell": "blue", "preferences": "yellow", + "prisma": "graphite", "protobuf": "graphite", + "pug": "yellow", "puppet": "purple", "python": "yellow", + "qt": "green", "r": "blue", + "racket": "red", "rails": "red", "react": "sky", "restructuredtext": "sky", @@ -75,6 +99,7 @@ "scss": "pink", "settings": "graphite", "shell": "green", + "slim": "cyan", "solidity": "graphite", "source": "sky", "sql": "blue", @@ -91,15 +116,24 @@ "textile": "pink", "todo": "green", "toml": "cyan", + "travis": "orange", "twig": "green", "typescript": "sky", + "typst": "cyan", "unreal": "graphite", + "v": "cyan", "vala": "sky", "verilog": "blue", "vhdl": "blue", "vim": "green", "vue": "cyan", + "vyper": "graphite", + "wast": "purple", + "webpack": "blue", + "wgsl": "sky", "windows": "sky", + "wit": "pink", "yaml": "yellow", + "yarn": "blue", "zig": "yellow" } diff --git a/static/label-icons.svg b/static/label-icons.svg index 9ffe25eed..52e0e3305 100644 --- a/static/label-icons.svg +++ b/static/label-icons.svg @@ -18,6 +18,9 @@ + + + @@ -29,6 +32,15 @@ + + + + + + + + + @@ -50,6 +62,21 @@ + + + + + + + + + + + + + + + @@ -76,6 +103,9 @@ + + + @@ -85,6 +115,12 @@ + + + + + + @@ -115,6 +151,9 @@ + + + @@ -151,24 +190,39 @@ + + + + + + + + + + + + + + + @@ -182,6 +236,15 @@ + + + + + + + + + @@ -204,6 +267,19 @@ + + + + + + + + + + + + @@ -213,12 +289,18 @@ + + + + + + @@ -228,6 +310,9 @@ + + + @@ -235,9 +320,17 @@ + + + + + + + + @@ -274,6 +367,9 @@ + + + @@ -323,16 +419,32 @@ + + + + + + + + + + + + @@ -348,12 +460,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/util/build-label-icons.mjs b/util/build-label-icons.mjs index 09bd5bd21..edde7b617 100644 --- a/util/build-label-icons.mjs +++ b/util/build-label-icons.mjs @@ -64,7 +64,7 @@ function main() { ensureDir(path.dirname(spritePath)) /** @type {Set} */ - const usedLabels = {} + const usedLabels = new Set() /** @type {Record} */ let aliases = {} /** @type {Set} */ @@ -106,8 +106,7 @@ function main() { const key = label.trim().toLowerCase() if (!key) continue const canonical = (aliases[key] && aliases[key].trim().toLowerCase()) || key - usedLabels[canonical] ??= 0 - usedLabels[canonical] += 1 + usedLabels.add(canonical) } } } @@ -141,7 +140,7 @@ function main() { const typeKey = type.trim().toLowerCase() // If we have a set of used labels, restrict icons to those labels (or alias targets) - if (usedLabels.size > 0 && (!(typeKey in usedLabels) || usedLabels[typeKey] <= 1)) { + if (usedLabels.size > 0 && !usedLabels.has(typeKey)) { continue }