feat(settings): Open Source Licenses section in System tab (#477)

* feat(licenses): tooling and initial data generation

Adds the maintainer-only generator that produces src/data/licenses.json:
- src-tauri/about.toml + about.hbs: cargo-about config + handlebars template
  for the Rust-side license enumeration
- scripts/generate-licenses.mjs: orchestrator that runs cargo-about and
  license-checker-rseidelsohn (via npx, no devDep), merges the outputs into
  a single per-crate JSON with full license texts, and writes the result to
  src/data/licenses.json

The script is invoked directly with `node scripts/generate-licenses.mjs` —
no npm script wrapper on purpose, since adding one to package.json would
trigger the nix-npm-deps-hash-sync workflow on every push.

Initial generation covers 575 cargo crates + 71 npm packages (646 entries
total, all with full license text bundled, ~1.4 MB JSON).

* feat(licenses): Settings panel UI

Adds a new Open Source Licenses section under Settings → System,
sitting below Contributors. Components:

- LicensesPanel.tsx: search input, curated highlight block of ~10 key
  dependencies (Tauri, React, rodio, symphonia, etc.), TanStack-Virtual
  list of all 600+ entries
- LicenseTextModal.tsx: full-screen-ish modal showing the bundled license
  text plus name/version/license-id badges + repository link
- licensesData.ts: lazy dynamic-import loader (Vite emits the JSON as a
  separate chunk, so the heavy ~1.4 MB payload is only loaded when the
  user actually opens the panel — no runtime fetch, the data is fixed
  into the build artifact)

The panel registers itself in the Settings in-page search index under
the System tab.

* feat(licenses): i18n in 8 locales

Adds the `licenses` namespace (title, intro, highlights, search
placeholder, no-results, loading / load error, no-license-text,
view-source, total line, generated-at) across en, de, fr, nl, zh, nb,
ru, es. License names themselves (MIT, Apache-2.0, GPL-3.0, …) stay
universal and are rendered as-is.

* docs(release): document licenses regeneration step

Adds a Step A.3 to the release SOP describing the maintainer-only
`node scripts/generate-licenses.mjs` workflow, the cargo-about
prerequisite, and explicitly notes why no npm script wrapper exists
(would trigger the nix-npm-deps-hash-sync workflow).
This commit is contained in:
Frank Stellmacher
2026-05-06 14:17:13 +02:00
committed by GitHub
parent d48ea819c1
commit 8692e50603
17 changed files with 963 additions and 1 deletions
+13
View File
@@ -1796,4 +1796,17 @@ export const deTranslation = {
exitPsysonic: 'Psysonic beenden',
nothingPlaying: 'Nichts wird abgespielt',
},
licenses: {
title: 'Open-Source-Lizenzen',
intro: 'Psysonic basiert auf Open-Source-Software. Die untenstehende Liste zeigt jede Abhängigkeit, die mit der App ausgeliefert wird, inklusive Version, Lizenz und vollständigem Lizenztext.',
highlights: 'Wichtige Abhängigkeiten',
searchPlaceholder: 'Suche nach Name, Version oder Lizenz…',
noResults: 'Keine Treffer.',
loading: 'Lizenzen werden geladen…',
loadError: 'Lizenzdaten konnten nicht geladen werden.',
noLicenseText: 'Für diese Abhängigkeit ist kein Lizenztext gebündelt.',
viewSource: 'Quelle anzeigen',
totalLine: '{{total}} Abhängigkeiten — {{cargo}} Cargo, {{npm}} npm',
generatedAt: 'Zuletzt generiert {{date}}',
},
};
+13
View File
@@ -1803,4 +1803,17 @@ export const enTranslation = {
exitPsysonic: 'Exit Psysonic',
nothingPlaying: 'Nothing playing',
},
licenses: {
title: 'Open Source Licenses',
intro: 'Psysonic is built on open-source software. The list below shows every dependency that ships in the app, with its version, license, and full license text.',
highlights: 'Key dependencies',
searchPlaceholder: 'Search by name, version or license…',
noResults: 'No matches.',
loading: 'Loading licenses…',
loadError: 'Could not load license data.',
noLicenseText: 'No license text bundled for this dependency.',
viewSource: 'View source',
totalLine: '{{total}} dependencies — {{cargo}} cargo, {{npm}} npm',
generatedAt: 'Last generated {{date}}',
},
};
+13
View File
@@ -1765,4 +1765,17 @@ export const esTranslation = {
exitPsysonic: 'Salir de Psysonic',
nothingPlaying: 'Nada en reproducción',
},
licenses: {
title: 'Licencias de Código Abierto',
intro: 'Psysonic se basa en software de código abierto. La lista siguiente muestra cada dependencia incluida con la aplicación, con su versión, licencia y el texto completo de la licencia.',
highlights: 'Dependencias principales',
searchPlaceholder: 'Buscar por nombre, versión o licencia…',
noResults: 'Sin resultados.',
loading: 'Cargando licencias…',
loadError: 'No se pudieron cargar los datos de licencia.',
noLicenseText: 'No hay texto de licencia incluido para esta dependencia.',
viewSource: 'Ver fuente',
totalLine: '{{total}} dependencias — {{cargo}} cargo, {{npm}} npm',
generatedAt: 'Última generación {{date}}',
},
};
+13
View File
@@ -1760,4 +1760,17 @@ export const frTranslation = {
exitPsysonic: 'Quitter Psysonic',
nothingPlaying: 'Aucune lecture',
},
licenses: {
title: 'Licences Open Source',
intro: 'Psysonic est construit sur des logiciels open source. La liste ci-dessous montre chaque dépendance livrée avec l\'application, avec sa version, sa licence et le texte de licence complet.',
highlights: 'Dépendances principales',
searchPlaceholder: 'Rechercher par nom, version ou licence…',
noResults: 'Aucun résultat.',
loading: 'Chargement des licences…',
loadError: 'Impossible de charger les données de licence.',
noLicenseText: 'Aucun texte de licence groupé pour cette dépendance.',
viewSource: 'Voir la source',
totalLine: '{{total}} dépendances — {{cargo}} cargo, {{npm}} npm',
generatedAt: 'Dernière génération {{date}}',
},
};
+13
View File
@@ -1759,4 +1759,17 @@ export const nbTranslation = {
exitPsysonic: 'Avslutt Psysonic',
nothingPlaying: 'Ingenting spilles',
},
licenses: {
title: 'Åpen kildekode-lisenser',
intro: 'Psysonic er bygget på åpen kildekode-programvare. Listen nedenfor viser hver avhengighet som leveres med appen, med versjon, lisens og full lisenstekst.',
highlights: 'Sentrale avhengigheter',
searchPlaceholder: 'Søk etter navn, versjon eller lisens…',
noResults: 'Ingen treff.',
loading: 'Laster lisenser…',
loadError: 'Kunne ikke laste inn lisensdata.',
noLicenseText: 'Ingen lisenstekst inkludert for denne avhengigheten.',
viewSource: 'Vis kilde',
totalLine: '{{total}} avhengigheter — {{cargo}} cargo, {{npm}} npm',
generatedAt: 'Sist generert {{date}}',
},
};
+13
View File
@@ -1759,4 +1759,17 @@ export const nlTranslation = {
exitPsysonic: 'Psysonic afsluiten',
nothingPlaying: 'Niets wordt afgespeeld',
},
licenses: {
title: 'Open-source licenties',
intro: 'Psysonic is gebouwd op open-source software. De onderstaande lijst toont elke afhankelijkheid die met de app wordt meegeleverd, met versie, licentie en volledige licentietekst.',
highlights: 'Belangrijke afhankelijkheden',
searchPlaceholder: 'Zoek op naam, versie of licentie…',
noResults: 'Geen resultaten.',
loading: 'Licenties laden…',
loadError: 'Licentiegegevens konden niet worden geladen.',
noLicenseText: 'Geen licentietekst meegeleverd voor deze afhankelijkheid.',
viewSource: 'Bron bekijken',
totalLine: '{{total}} afhankelijkheden — {{cargo}} cargo, {{npm}} npm',
generatedAt: 'Laatst gegenereerd {{date}}',
},
};
+13
View File
@@ -1846,4 +1846,17 @@ export const ruTranslation = {
exitPsysonic: 'Закрыть Psysonic',
nothingPlaying: 'Ничего не воспроизводится',
},
licenses: {
title: 'Лицензии открытого кода',
intro: 'Psysonic построен на программном обеспечении с открытым исходным кодом. Список ниже показывает каждую зависимость, поставляемую с приложением, с версией, лицензией и полным текстом лицензии.',
highlights: 'Ключевые зависимости',
searchPlaceholder: 'Поиск по имени, версии или лицензии…',
noResults: 'Нет совпадений.',
loading: 'Загрузка лицензий…',
loadError: 'Не удалось загрузить данные о лицензиях.',
noLicenseText: 'Текст лицензии для этой зависимости не включён.',
viewSource: 'Открыть исходник',
totalLine: '{{total}} зависимостей — {{cargo}} cargo, {{npm}} npm',
generatedAt: 'Последняя генерация {{date}}',
},
};
+13
View File
@@ -1752,4 +1752,17 @@ export const zhTranslation = {
exitPsysonic: '退出 Psysonic',
nothingPlaying: '当前没有播放',
},
licenses: {
title: '开源许可证',
intro: 'Psysonic 基于开源软件构建。下方列表显示了应用程序中包含的每个依赖项的版本、许可证以及完整的许可证文本。',
highlights: '主要依赖项',
searchPlaceholder: '按名称、版本或许可证搜索…',
noResults: '没有匹配项。',
loading: '加载许可证…',
loadError: '无法加载许可证数据。',
noLicenseText: '此依赖项未包含许可证文本。',
viewSource: '查看源代码',
totalLine: '{{total}} 个依赖项 — {{cargo}} cargo, {{npm}} npm',
generatedAt: '最后生成时间 {{date}}',
},
};