From 5001ce192d60fb118bd03a28db12a01710a3df0b Mon Sep 17 00:00:00 2001 From: Olivier Date: Fri, 22 May 2026 09:09:33 +0200 Subject: [PATCH] feat: auto-map all 3098 simple-icons via import * and word-boundary detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace hand-curated named imports with `import * as si from 'simple-icons'`. BRANDS keeps only entries requiring custom aliases, absent icons, or colour overrides. AUTO_SI covers all remaining simple-icons (title >= 4 chars) with pre-compiled word-boundary regexes to avoid substring false positives. detectBrands() now runs two passes: 1. BRANDS — curated aliases (pve → Proxmox, unifi → Ubiquiti, k3s → k8s…) 2. AUTO_SI — automatic match on any icon title as a whole word Bundle: +5 MB minified / +2 MB gzip (acceptable for a self-hosted tool). Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/brandIcons.js | 428 ++++++++++++++----------------------- 1 file changed, 163 insertions(+), 265 deletions(-) diff --git a/frontend/src/brandIcons.js b/frontend/src/brandIcons.js index 2e58183..ae1a1b3 100644 --- a/frontend/src/brandIcons.js +++ b/frontend/src/brandIcons.js @@ -1,44 +1,7 @@ -import { - siProxmox, siDocker, - siSynology, siTruenas, - siSchneiderelectric, - siUbiquiti, siMikrotik, siCisco, siTplink, siAsus, siNetgear, siPfsense, siOpnsense, siOpenwrt, - siFortinet, - siApache, siNginx, siTraefikproxy, siApacheguacamole, - siAuthelia, siKeycloak, siAuthentik, siOkta, siAuth0, - siOrange, siOvh, - siBitwarden, siVaultwarden, si1password, siKeepassxc, siVault, - siMariadb, - siKubernetes, - siPrometheus, siGrafana, siDatadog, siNetdata, siCheckmk, siIcinga, siInfluxdb, siVictoriametrics, - siOpsgenie, siPagerduty, - siElastic, siKibana, siLogstash, siSplunk, siGraylog, siJaeger, siOpentelemetry, - siApple, - siDebian, siUbuntu, siFirefox, - siCentos, siRedhat, siFedora, siAlpinelinux, siArchlinux, siOpensuse, siFreebsd, - siAlmalinux, siRockylinux, siNixos, siGentoo, siVoidlinux, siSlackware, - siSuse, siManjaro, siLinuxmint, siZorin, siPopos, siDeepin, siElementary, - siMxlinux, siSolus, siEndeavouros, siArtixlinux, siAsahilinux, - siKubuntu, siLubuntu, siXubuntu, siUbuntumate, - siBsd, siOpenbsd, siNetbsd, - siKalilinux, siParrotsecurity, siTails, siQubesos, siReactos, - siMacos, siIos, siLinux, - siAndroid, - siAnsible, - siDell, siHp, - siRaspberrypi, siArduino, - siNextcloud, siPaperlessngx, siUptimekuma, siMaterialformkdocs, - siWordpress, siGhost, siGrav, siJekyll, siHugo, siHexo, siDrupal, siJoomla, siTypo3, siOctobercms, siTextpattern, - siMatomo, siPlausibleanalytics, - siSamsung, siLg, siSony, siPanasonic, siSharp, siToshiba, siVestel, - siChromecast, siAppletv, siAmazonfiretv, siRoku, siKodi, - siJellyfin, siHomeassistant, siPhilipshue, siXiaomi, - siRadarr, siSonarr, siTransmission, - siExcalidraw, - siKde, -} from 'simple-icons' +import * as si from 'simple-icons' + +// ── Icônes personnalisées (absentes de simple-icons) ──────────────────────── -// Icônes personnalisées (absentes de simple-icons) — même format { title, hex, path }. const ICON_FREE = { title: 'Free', hex: 'CD1126', @@ -75,7 +38,6 @@ const ICON_MAIL = { path: 'M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z', } -// UPS custom icons (absents de simple-icons) const ICON_EATON = { title: 'Eaton', hex: 'E22000', @@ -94,7 +56,6 @@ const ICON_VERTIV = { path: 'M20 3H4v4h16V3zm0 5H4v4h16V8zm0 5H4v4h16v-4zM6 5h2v2H6zm0 5h2v2H6zm0 5h2v2H6z', } -// Monitoring custom icons (absents de simple-icons) const ICON_ZABBIX = { title: 'Zabbix', hex: 'D40000', @@ -125,251 +86,165 @@ const ICON_WINDOWS = { path: 'M0 3.449L9.75 2.1v9.451H0m10.949-9.602L24 0v11.4h-13.051M0 12.6h9.75v9.451L0 20.699M10.949 12.6H24V24l-13.051-1.8', } -// Ordre : du plus spécifique au plus générique pour éviter les faux positifs. +// ── BRANDS : alias custom, icônes absentes de simple-icons, couleurs override ─ +// Les ~3000 autres icônes simple-icons sont couvertes automatiquement via AUTO_SI. const BRANDS = [ - // Hyperviseurs / virtualisation - { kw: ['proxmox', 'pve'], icon: siProxmox }, - { kw: ['docker'], icon: siDocker }, - - // NAS - { kw: ['synology', 'dsm'], icon: siSynology }, - { kw: ['truenas', 'freenas'], icon: siTruenas }, - - // Onduleurs (UPS) - { kw: ['apc', 'schneider electric', 'symmetra', 'smart-ups', 'easy ups', 'galaxy ups'], icon: siSchneiderelectric }, - { kw: ['eaton', 'powerware', 'eaton ups'], icon: ICON_EATON }, - { kw: ['riello', 'riello ups'], icon: ICON_RIELLO }, - { kw: ['vertiv', 'liebert', 'avocent', 'geist'], icon: ICON_VERTIV }, - + // Virtualisation / NAS + { kw: ['proxmox', 'pve'], icon: si.siProxmox }, + { kw: ['truenas', 'freenas'], icon: si.siTruenas }, + // Onduleurs + { kw: ['apc', 'schneider electric', 'symmetra', 'smart-ups', 'easy ups', 'galaxy ups'], icon: si.siSchneiderelectric }, + { kw: ['eaton', 'powerware', 'eaton ups'], icon: ICON_EATON }, + { kw: ['riello', 'riello ups'], icon: ICON_RIELLO }, + { kw: ['vertiv', 'liebert', 'avocent', 'geist'], icon: ICON_VERTIV }, // Réseau - { kw: ['ubiquiti', 'unifi', 'usg', 'udm'], icon: siUbiquiti }, - { kw: ['mikrotik', 'routeros'], icon: siMikrotik }, - { kw: ['cisco'], icon: siCisco }, - { kw: ['tp-link', 'tplink', 'tp link'], icon: siTplink }, - { kw: ['asus'], icon: siAsus }, - { kw: ['netgear'], icon: siNetgear }, - { kw: ['pfsense'], icon: siPfsense }, - { kw: ['opnsense'], icon: siOpnsense }, - { kw: ['openwrt'], icon: siOpenwrt }, - - // Sécurité réseau - { kw: ['fortinet', 'fortigate', 'fortios', 'fortimanager', 'fortiauthenticator'], icon: siFortinet }, - + { kw: ['ubiquiti', 'unifi', 'usg', 'udm'], icon: si.siUbiquiti }, + { kw: ['mikrotik', 'routeros'], icon: si.siMikrotik }, + { kw: ['tp-link', 'tplink', 'tp link'], icon: si.siTplink }, + { kw: ['fortinet', 'fortigate', 'fortios', 'fortimanager', 'fortiauthenticator'], icon: si.siFortinet }, // FAI français - { kw: ['orange', 'sosh', 'livebox'], icon: siOrange }, - { kw: ['ovh', 'ovhcloud', 'kimsufi', 'soyoustart'], icon: siOvh }, - { kw: ['freebox', 'free mobile', 'free telecom', 'iliad'], icon: ICON_FREE }, - { kw: ['bouygues', 'bbox'], icon: ICON_BOUYGUES }, - { kw: ['sfr', 'red by sfr', 'sfr box'], icon: ICON_SFR }, - - // Serveurs web / proxy - { kw: ['apache', 'apache2', 'httpd'], icon: siApache }, - { kw: ['nginx'], icon: siNginx }, - { kw: ['traefik'], icon: siTraefikproxy }, - { kw: ['guacamole'], icon: siApacheguacamole }, - - // Bastions / jump hosts - { kw: ['bastion', 'jumphost', 'jump host', 'jump server', 'teleport', 'bastillion'], icon: ICON_BASTION }, - - // Auth / SSO / IDP - { kw: ['authelia'], icon: siAuthelia }, - { kw: ['keycloak'], icon: siKeycloak }, - { kw: ['authentik'], icon: siAuthentik }, - { kw: ['okta'], icon: siOkta }, - { kw: ['auth0'], icon: siAuth0 }, - - // Coffres-forts de mots de passe - { kw: ['vaultwarden'], icon: siVaultwarden }, - { kw: ['bitwarden'], icon: siBitwarden }, - { kw: ['1password', 'onepassword'], icon: si1password }, - { kw: ['keepass', 'keepassxc'], icon: siKeepassxc }, - { kw: ['hashicorp vault', 'hashicorp'], icon: siVault }, - - // Archivage - { kw: ['archive', 'archiver', 'archivage', 'archivar', 'archivebox'], icon: ICON_ARCHIVE }, - - // Messagerie + { kw: ['orange', 'sosh', 'livebox'], icon: si.siOrange }, + { kw: ['ovh', 'ovhcloud', 'kimsufi', 'soyoustart'], icon: si.siOvh }, + { kw: ['freebox', 'free mobile', 'free telecom', 'iliad'], icon: ICON_FREE }, + { kw: ['bouygues', 'bbox'], icon: ICON_BOUYGUES }, + { kw: ['sfr', 'red by sfr', 'sfr box'], icon: ICON_SFR }, + // Web / proxy (titre simple-icons differ de l'alias courant) + { kw: ['traefik'], icon: si.siTraefikproxy }, + { kw: ['guacamole'], icon: si.siApacheguacamole }, + // Bastion / jump host + { kw: ['bastion', 'jumphost', 'jump host', 'jump server', 'teleport', 'bastillion'], icon: ICON_BASTION }, + // Messagerie / archivage { kw: ['mail', 'smtp', 'imap', 'postfix', 'dovecot', 'mailcow', 'mailu', 'roundcube'], icon: ICON_MAIL }, - - // Bases de données - { kw: ['mariadb', 'maria db'], icon: siMariadb }, - + { kw: ['archive', 'archiver', 'archivage', 'archivar', 'archivebox'], icon: ICON_ARCHIVE }, + // Passwords + { kw: ['1password', 'onepassword'], icon: si.si1password }, + { kw: ['keepass', 'keepassxc'], icon: si.siKeepassxc }, + { kw: ['hashicorp vault', 'hashicorp'], icon: si.siVault }, // Orchestration - { kw: ['kubernetes', 'k8s', 'kubectl', 'k3s'], icon: siKubernetes }, - - // Monitoring / Observabilité - { kw: ['zabbix'], icon: ICON_ZABBIX }, - { kw: ['centreon'], icon: ICON_CENTREON }, - { kw: ['nagios', 'nagiosxi', 'nagios xi'], icon: ICON_NAGIOS }, - { kw: ['prtg', 'paessler'], icon: ICON_PRTG }, - { kw: ['prometheus'], icon: siPrometheus }, - { kw: ['grafana'], icon: siGrafana }, - { kw: ['datadog'], icon: siDatadog }, - { kw: ['netdata'], icon: siNetdata }, - { kw: ['checkmk', 'check_mk'], icon: siCheckmk }, - { kw: ['icinga', 'icinga2'], icon: { ...siIcinga, hex: '3F5FBA' } }, - { kw: ['influxdb', 'influx db'], icon: siInfluxdb }, - { kw: ['victoriametrics', 'victoria metrics'], icon: siVictoriametrics }, - + { kw: ['kubernetes', 'k8s', 'kubectl', 'k3s'], icon: si.siKubernetes }, + // Monitoring (icônes custom) + { kw: ['zabbix'], icon: ICON_ZABBIX }, + { kw: ['centreon'], icon: ICON_CENTREON }, + { kw: ['nagios', 'nagiosxi', 'nagios xi'], icon: ICON_NAGIOS }, + { kw: ['prtg', 'paessler'], icon: ICON_PRTG }, + // Monitoring (couleurs override) + { kw: ['icinga', 'icinga2'], icon: { ...si.siIcinga, hex: '3F5FBA' } }, + { kw: ['opsgenie'], icon: { ...si.siOpsgenie, hex: '2684FF' } }, + { kw: ['victoria metrics', 'victoriametrics'], icon: si.siVictoriametrics }, + // Logs / traces (alias + couleurs override) + { kw: ['elastic', 'elasticsearch', 'elastic stack', 'elk'], icon: si.siElastic }, + { kw: ['splunk'], icon: { ...si.siSplunk, hex: '65A637' } }, + { kw: ['opentelemetry', 'otel'], icon: { ...si.siOpentelemetry, hex: '425CC7' } }, + // Apple (couleur override + aliases) + { kw: ['apple', 'iphone', 'ipad', 'ipados', 'macbook', 'imac', 'mac mini', 'mac pro', 'mac studio', 'macos', 'mac os', 'icloud', 'airpods', 'airdrop'], icon: { ...si.siApple, hex: '555555' } }, + // Windows (icône custom) + { kw: ['windows', 'win10', 'win11', 'winserver', 'windows server'], icon: ICON_WINDOWS }, + // Serveurs (aliases) + { kw: ['dell', 'idrac', 'poweredge'], icon: si.siDell }, + { kw: ['hp', 'hpe', 'proliant', 'ilo', 'hewlett'], icon: si.siHp }, + // SBC + { kw: ['raspberry', 'raspberrypi', 'rpi', 'raspi'], icon: si.siRaspberrypi }, + // Bureau (titre trop court pour l'auto-détection) + { kw: ['kde', 'plasma'], icon: si.siKde }, + // Self-hosted (aliases utiles) + { kw: ['paperless', 'paperless-ng', 'paperless-ngx'], icon: si.siPaperlessngx }, + { kw: ['uptime-kuma', 'uptimekuma', 'uptime kuma'], icon: si.siUptimekuma }, + { kw: ['mkdocs', 'material for mkdocs'], icon: si.siMaterialformkdocs }, + // TV / Domotique + { kw: ['samsung', 'tizen'], icon: si.siSamsung }, + { kw: ['lg', 'webos'], icon: si.siLg }, + { kw: ['sony', 'bravia'], icon: { ...si.siSony, hex: '1A1A1A' } }, + { kw: ['chromecast', 'google tv'], icon: si.siChromecast }, + { kw: ['android tv', 'androidtv'], icon: si.siAndroid }, + { kw: ['fire tv', 'firetv', 'amazon fire'], icon: si.siAmazonfiretv }, + { kw: ['philips hue', 'hue bridge', 'hue hub'], icon: si.siPhilipshue }, + { kw: ['homeassistant', 'home assistant', 'hassio', 'hass'], icon: si.siHomeassistant }, + { kw: ['xiaomi', 'mi home', 'yeelight'], icon: si.siXiaomi }, // Alerting - { kw: ['opsgenie'], icon: { ...siOpsgenie, hex: '2684FF' } }, - { kw: ['pagerduty', 'pager duty'], icon: siPagerduty }, - - // Logs / Traces - { kw: ['elasticsearch', 'elastic stack', 'elk'], icon: siElastic }, - { kw: ['kibana'], icon: siKibana }, - { kw: ['logstash'], icon: siLogstash }, - { kw: ['splunk'], icon: { ...siSplunk, hex: '65A637' } }, - { kw: ['graylog'], icon: siGraylog }, - { kw: ['jaeger'], icon: siJaeger }, - { kw: ['opentelemetry', 'otel'], icon: { ...siOpentelemetry, hex: '425CC7' } }, - - // Écosystème Apple - { kw: ['apple', 'iphone', 'ipad', 'ipados', 'macbook', 'imac', 'mac mini', 'mac pro', 'mac studio', 'macos', 'mac os', 'ios', 'icloud', 'airpods', 'airdrop'], icon: { ...siApple, hex: '555555' } }, - - // OS / distros - { kw: ['windows', 'win10', 'win11', 'winserver', 'windows server'], icon: ICON_WINDOWS }, - { kw: ['debian'], icon: siDebian }, - { kw: ['ubuntu'], icon: siUbuntu }, - - // Navigateurs - { kw: ['firefox'], icon: siFirefox }, - - // Automatisation - { kw: ['ansible'], icon: siAnsible }, - - // Serveurs - { kw: ['dell', 'idrac', 'poweredge'], icon: siDell }, - { kw: ['proliant', 'ilo', 'hewlett'], icon: siHp }, - - // SBC / DIY - { kw: ['raspberry', 'raspberrypi', 'rpi', 'raspi'], icon: siRaspberrypi }, - { kw: ['arduino'], icon: siArduino }, - - // Environnements de bureau - { kw: ['kde', 'plasma', 'kde desktop'], icon: siKde }, - - // Outils - { kw: ['excalidraw'], icon: siExcalidraw }, - - // Productivité / self-hosted - { kw: ['nextcloud'], icon: siNextcloud }, - { kw: ['paperless', 'paperless-ng', 'paperless-ngx'], icon: siPaperlessngx }, - { kw: ['uptime-kuma', 'uptimekuma', 'uptime kuma'], icon: siUptimekuma }, - { kw: ['mkdocs', 'material for mkdocs'], icon: siMaterialformkdocs }, - - // CMS / Blog - { kw: ['wordpress'], icon: siWordpress }, - { kw: ['ghost'], icon: siGhost }, - { kw: ['grav'], icon: siGrav }, - { kw: ['jekyll'], icon: siJekyll }, - { kw: ['hugo'], icon: siHugo }, - { kw: ['hexo'], icon: siHexo }, - { kw: ['drupal'], icon: siDrupal }, - { kw: ['joomla'], icon: siJoomla }, - { kw: ['typo3'], icon: siTypo3 }, - { kw: ['octobercms', 'october cms'], icon: siOctobercms }, - { kw: ['textpattern'], icon: siTextpattern }, - - // Analytique - { kw: ['matomo'], icon: siMatomo }, - { kw: ['plausible'], icon: siPlausibleanalytics }, - - // TV connectées — fabricants - { kw: ['samsung', 'tizen', 'samsung tv'], icon: siSamsung }, - { kw: ['lg', 'webos', 'lg tv'], icon: siLg }, - { kw: ['sony', 'bravia'], icon: { ...siSony, hex: '1A1A1A' } }, - { kw: ['panasonic'], icon: siPanasonic }, - { kw: ['sharp'], icon: siSharp }, - { kw: ['toshiba'], icon: siToshiba }, - { kw: ['vestel'], icon: siVestel }, - - // TV connectées — boîtiers / écosystèmes - { kw: ['chromecast', 'google tv'], icon: siChromecast }, - { kw: ['android tv', 'androidtv'], icon: siAndroid }, - { kw: ['apple tv', 'appletv'], icon: siAppletv }, - { kw: ['fire tv', 'firetv', 'amazon fire'], icon: siAmazonfiretv }, - { kw: ['roku'], icon: siRoku }, - { kw: ['kodi'], icon: siKodi }, - - // Médias / torrent - { kw: ['radarr'], icon: siRadarr }, - { kw: ['sonarr'], icon: siSonarr }, - { kw: ['transmission'], icon: siTransmission }, - - // Médias / domotique - { kw: ['jellyfin'], icon: siJellyfin }, - { kw: ['homeassistant', 'home assistant', 'hassio', 'hass'], icon: siHomeassistant }, - { kw: ['philips hue', 'hue bridge', 'hue hub'], icon: siPhilipshue }, - { kw: ['xiaomi', 'mi home', 'yeelight'], icon: siXiaomi }, + { kw: ['pagerduty', 'pager duty'], icon: si.siPagerduty }, ] +// Slugs couverts par BRANDS → exclus de l'auto-détection pour éviter les doublons +const _BRANDS_SLUGS = new Set( + BRANDS.flatMap(b => (b.icon && b.icon.slug) ? [b.icon.slug] : []) +) + +// Toutes les icônes simple-icons (title >= 4 chars) non couvertes par BRANDS. +// Regex pré-compilée avec frontières de mots pour éviter les faux positifs +// (ex. "known" ne doit pas matcher dans "unknown"). +const AUTO_SI = Object.values(si) + .filter(icon => icon.title.length >= 4 && !_BRANDS_SLUGS.has(icon.slug)) + .map(icon => { + const escaped = icon.title.toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + return { icon, re: new RegExp('(? b.kw.some(kw => text.includes(kw))).map(b => b.icon) + + const seen = new Set() + const results = [] + + // Pass 1 : BRANDS — aliases custom, icônes personnalisées, couleurs overridées + for (const b of BRANDS) { + if (b.kw.some(kw => text.includes(kw))) { + const key = b.icon.slug ?? b.icon.title + if (!seen.has(key)) { + seen.add(key) + results.push(b.icon) + } + } + } + + // Pass 2 : auto-détection sur l'ensemble des icônes simple-icons (frontières de mots) + for (const { icon, re } of AUTO_SI) { + if (!seen.has(icon.slug) && re.test(text)) { + seen.add(icon.slug) + results.push(icon) + } + } + + return results }