From 2fb5960e423fcb2365531414828cfb69a401e966 Mon Sep 17 00:00:00 2001 From: DidierSardine Date: Wed, 30 Sep 2026 16:10:13 +0200 Subject: [PATCH 1/9] Added new language: french - Added file age viewer with the Treemap --- appinfo/routes.php | 2 + l10n/de.js | 14 ++- l10n/de.json | 12 +- l10n/en.js | 14 ++- l10n/en.json | 12 +- l10n/es.js | 14 ++- l10n/es.json | 12 +- l10n/fr.js | 57 ++++++++++ l10n/fr.json | 57 ++++++++++ l10n/pt_PT.js | 14 ++- l10n/pt_PT.json | 12 +- src/components/FileAgeChart.vue | 140 ++++++++++++++++++++++++ src/components/FileAgeChartDropdown.vue | 54 +++++++++ src/components/Tab.vue | 35 ++++++ src/components/Tabs.vue | 58 ++++++++++ src/services/api.js | 18 +++ src/utils/colors.js | 65 +++++++++++ src/views/InstanceView.vue | 57 ++++++---- src/views/MyStorageView.vue | 71 +++++++----- 19 files changed, 655 insertions(+), 63 deletions(-) create mode 100644 l10n/fr.js create mode 100644 l10n/fr.json create mode 100644 src/components/FileAgeChart.vue create mode 100644 src/components/FileAgeChartDropdown.vue create mode 100644 src/components/Tab.vue create mode 100644 src/components/Tabs.vue create mode 100644 src/utils/colors.js diff --git a/appinfo/routes.php b/appinfo/routes.php index caaaecb..26c99be 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -47,6 +47,8 @@ // can highlight its whole region in the map (real WinDirStat behavior). ['name' => 'usage#map', 'url' => '/api/v1/map', 'verb' => 'GET'], + ['name' => 'usage#fileAges', 'url' => '/api/v1/file-ages', 'verb' => 'GET'], + // Admin-only: team folder overview (used/quota, files/trash/versions, linked groups). ['name' => 'adminApi#teamFolders', 'url' => '/api/v1/admin/teamfolders', 'verb' => 'GET'], diff --git a/l10n/de.js b/l10n/de.js index 5d67588..8574f39 100644 --- a/l10n/de.js +++ b/l10n/de.js @@ -42,6 +42,16 @@ OC.L10N.register( "This storage has not been fully scanned yet, so sizes are a lower bound. Background scanning fills this in over time; \"occ files_external:scan\" measures it right away." : "Dieser Speicher wurde noch nicht vollständig gescannt, daher sind die Größenangaben ein Mindestwert. Der Hintergrund-Scan vervollständigt sie mit der Zeit; \"occ files_external:scan\" ermittelt sie sofort.", "Open in Files" : "In Dateien öffnen", "Open the selected folder in Files" : "Ausgewählten Ordner in Dateien öffnen", - "You are not in the groups assigned to this team folder — this may not open in your own Files." : "Sie gehören nicht zu den Gruppen dieses Team-Ordners — dies wird möglicherweise nicht in Ihren eigenen Dateien geöffnet." + "You are not in the groups assigned to this team folder — this may not open in your own Files." : "Sie gehören nicht zu den Gruppen dieses Team-Ordners — dies wird möglicherweise nicht in Ihren eigenen Dateien geöffnet.", + "≤ 1 yr" : "≤ 1 Jahr", + "1-3 yrs" : "1-3 Jahre", + "3-6 yrs" : "3-6 Jahre", + "6-10 yrs" : "6-10 Jahre", + "> 10 yrs" : "> 10 Jahre", + "Files count" : "Anzahl Dateien", + "Files size" : "Dateigröße", + "Files percentages" : "Dateien in Prozent", + "Data" : "Daten", + "Files age": "Datei-Alter" }, -"nplurals=2; plural=(n != 1);"); +"nplurals=2; plural=(n != 1);"); \ No newline at end of file diff --git a/l10n/de.json b/l10n/de.json index 5a13d34..ad2ea90 100644 --- a/l10n/de.json +++ b/l10n/de.json @@ -41,7 +41,17 @@ "This storage has not been fully scanned yet, so sizes are a lower bound. Background scanning fills this in over time; \"occ files_external:scan\" measures it right away.": "Dieser Speicher wurde noch nicht vollständig gescannt, daher sind die Größenangaben ein Mindestwert. Der Hintergrund-Scan vervollständigt sie mit der Zeit; \"occ files_external:scan\" ermittelt sie sofort.", "Open in Files": "In Dateien öffnen", "Open the selected folder in Files": "Ausgewählten Ordner in Dateien öffnen", - "You are not in the groups assigned to this team folder — this may not open in your own Files.": "Sie gehören nicht zu den Gruppen dieses Team-Ordners — dies wird möglicherweise nicht in Ihren eigenen Dateien geöffnet." + "You are not in the groups assigned to this team folder — this may not open in your own Files.": "Sie gehören nicht zu den Gruppen dieses Team-Ordners — dies wird möglicherweise nicht in Ihren eigenen Dateien geöffnet.", + "≤ 1 yr": "≤ 1 Jahr", + "1-3 yrs": "1-3 Jahre", + "3-6 yrs": "3-6 Jahre", + "6-10 yrs": "6-10 Jahre", + "> 10 yrs": "> 10 Jahre", + "Files count": "Anzahl Dateien", + "Files size": "Dateigröße", + "Files percentages": "Dateien in Prozent", + "Data": "Daten", + "Files age": "Datei-Alter" }, "pluralForm": "nplurals=2; plural=(n != 1);" } diff --git a/l10n/en.js b/l10n/en.js index e3ba5dd..8894e79 100644 --- a/l10n/en.js +++ b/l10n/en.js @@ -42,6 +42,16 @@ OC.L10N.register( "This storage has not been fully scanned yet, so sizes are a lower bound. Background scanning fills this in over time; \"occ files_external:scan\" measures it right away." : "This storage has not been fully scanned yet, so sizes are a lower bound. Background scanning fills this in over time; \"occ files_external:scan\" measures it right away.", "Open in Files" : "Open in Files", "Open the selected folder in Files" : "Open the selected folder in Files", - "You are not in the groups assigned to this team folder — this may not open in your own Files." : "You are not in the groups assigned to this team folder — this may not open in your own Files." + "You are not in the groups assigned to this team folder — this may not open in your own Files." : "You are not in the groups assigned to this team folder — this may not open in your own Files.", + "≤ 1 yr" : "≤ 1 yr", + "1-3 yrs" : "1-3 yrs", + "3-6 yrs" : "3-6 yrs", + "6-10 yrs" : "6-10 yrs", + "> 10 yrs" : "> 10 yrs", + "Files count" : "Files count", + "Files size" : "Files size", + "Files percentages" : "Files percentages", + "Data" : "Data", + "Files age": "Files age" }, -"nplurals=2; plural=(n != 1);"); +"nplurals=2; plural=(n != 1);"); \ No newline at end of file diff --git a/l10n/en.json b/l10n/en.json index d0fce1b..fcce851 100644 --- a/l10n/en.json +++ b/l10n/en.json @@ -41,7 +41,17 @@ "This storage has not been fully scanned yet, so sizes are a lower bound. Background scanning fills this in over time; \"occ files_external:scan\" measures it right away.": "This storage has not been fully scanned yet, so sizes are a lower bound. Background scanning fills this in over time; \"occ files_external:scan\" measures it right away.", "Open in Files": "Open in Files", "Open the selected folder in Files": "Open the selected folder in Files", - "You are not in the groups assigned to this team folder — this may not open in your own Files.": "You are not in the groups assigned to this team folder — this may not open in your own Files." + "You are not in the groups assigned to this team folder — this may not open in your own Files.": "You are not in the groups assigned to this team folder — this may not open in your own Files.", + "≤ 1 yr": "≤ 1 yr", + "1-3 yrs": "1-3 yrs", + "3-6 yrs": "3-6 yrs", + "6-10 yrs": "6-10 yrs", + "> 10 yrs": "> 10 yrs", + "Files count": "Files count", + "Files size": "Files size", + "Files percentages": "Files percentages", + "Data": "Data", + "Files age": "Files age" }, "pluralForm": "nplurals=2; plural=(n != 1);" } diff --git a/l10n/es.js b/l10n/es.js index 6778221..5206999 100644 --- a/l10n/es.js +++ b/l10n/es.js @@ -42,6 +42,16 @@ OC.L10N.register( "This storage has not been fully scanned yet, so sizes are a lower bound. Background scanning fills this in over time; \"occ files_external:scan\" measures it right away." : "Este almacenamiento aún no se ha analizado por completo, por lo que los tamaños son un valor mínimo. El análisis en segundo plano los completa con el tiempo; \"occ files_external:scan\" los mide de inmediato.", "Open in Files" : "Abrir en Archivos", "Open the selected folder in Files" : "Abrir la carpeta seleccionada en Archivos", - "You are not in the groups assigned to this team folder — this may not open in your own Files." : "No perteneces a los grupos asignados a esta carpeta de equipo — puede que esto no se abra en tus propios Archivos." + "You are not in the groups assigned to this team folder — this may not open in your own Files." : "No perteneces a los grupos asignados a esta carpeta de equipo — puede que esto no se abra en tus propios Archivos.", + "≤ 1 yr" : "≤ 1 año", + "1-3 yrs" : "1-3 años", + "3-6 yrs" : "3-6 años", + "6-10 yrs" : "6-10 años", + "> 10 yrs" : "> 10 años", + "Files count" : "N.º de archivos", + "Files size" : "Tamaño de archivos", + "Files percentages" : "Porcentajes de archivos", + "Data" : "Datos", + "Files age": "Antigüedad ficheros" }, -"nplurals=2; plural=(n != 1);"); +"nplurals=2; plural=(n != 1);"); \ No newline at end of file diff --git a/l10n/es.json b/l10n/es.json index 0742a54..3d990f3 100644 --- a/l10n/es.json +++ b/l10n/es.json @@ -41,7 +41,17 @@ "This storage has not been fully scanned yet, so sizes are a lower bound. Background scanning fills this in over time; \"occ files_external:scan\" measures it right away.": "Este almacenamiento aún no se ha analizado por completo, por lo que los tamaños son un valor mínimo. El análisis en segundo plano los completa con el tiempo; \"occ files_external:scan\" los mide de inmediato.", "Open in Files": "Abrir en Archivos", "Open the selected folder in Files": "Abrir la carpeta seleccionada en Archivos", - "You are not in the groups assigned to this team folder — this may not open in your own Files.": "No perteneces a los grupos asignados a esta carpeta de equipo — puede que esto no se abra en tus propios Archivos." + "You are not in the groups assigned to this team folder — this may not open in your own Files.": "No perteneces a los grupos asignados a esta carpeta de equipo — puede que esto no se abra en tus propios Archivos.", + "≤ 1 yr": "≤ 1 año", + "1-3 yrs": "1-3 años", + "3-6 yrs": "3-6 años", + "6-10 yrs": "6-10 años", + "> 10 yrs": "> 10 años", + "Files count": "N.º de archivos", + "Files size": "Tamaño de archivos", + "Files percentages": "Porcentajes de archivos", + "Data": "Datos", + "Files age": "Antigüedad ficheros" }, "pluralForm": "nplurals=2; plural=(n != 1);" } diff --git a/l10n/fr.js b/l10n/fr.js new file mode 100644 index 0000000..6d6a6fc --- /dev/null +++ b/l10n/fr.js @@ -0,0 +1,57 @@ +OC.L10N.register( + "diskmap", + { + "My storage" : "Mon stockage", + "Whole server" : "Tout le serveur", + "Could not load the instance overview." : "Impossible de charger la vue d'ensemble de l'instance.", + "Could not load your storage overview." : "Impossible de charger la vue d'ensemble de votre stockage.", + "Could not load team folders." : "Impossible de charger les dossiers d'équipes.", + "No team folders found." : "Aucun dossier d'équipe trouvé.", + "Select a team folder to see details." : "Sélectionnez un dossier d'équipe pour voir les détails.", + "Reflects the file cache as of {date}." : "Reflète le cache des fichiers au {date}.", + "used" : "utilisé(s)", + "Quota" : "Quota", + "Unlimited" : "Illimité", + "occupancy" : "occupation", + "files" : "fichiers", + "trash" : "corbeille", + "versions" : "versions", + "Groups" : "Groupes", + "Could not load the largest files." : "Impossible de charger les fichiers les plus gros.", + "No files found." : "Aucun fichier trouvé.", + "Size" : "Taille", + "unknown" : "inconnu", + "Storage treemap" : "Arborescence des fichiers", + "Documents" : "Documents", + "Images" : "Images", + "Video" : "Vidéos", + "Archives & installers" : "Archives & installeurs", + "Other" : "Autre", + "Other small files ({count})" : "Autres petits fichiers ({count})", + "Composition" : "Composition", + "File count" : "Nombre de fichiers", + "Last modified" : "Dernière modification", + "More items not shown" : "Plus d'éléments non affichés", + "Expand" : "Étendre", + "Collapse" : "Replier", + "Opening…" : "Ouverture…", + "Could not load the folder tree." : "Impossible de charger l'arbre des dossiers.", + "No items found." : "Aucun élément trouvé.", + "Could not load external storages." : "Impossible de charger les stockages externes.", + "Not fully scanned yet — at least this much." : "Pas encore totalement analysé — au moins autant.", + "This storage has not been fully scanned yet, so sizes are a lower bound. Background scanning fills this in over time; \"occ files_external:scan\" measures it right away." : "Ce stockage n'a pas encoré été totalement scanné, les tailles indiquées constituent donc une valeur minimale. L'analyse d'arrière plan complète les informations au fil du temps; \"occ files_external:scan\" les mesures immédiatement.", + "Open in Files" : "Ouvrir dans Fichiers", + "Open the selected folder in Files" : "Ouvrir le dossier sélectionné dans Fichiers", + "You are not in the groups assigned to this team folder — this may not open in your own Files." : "Vous ne faîtes pas parti du groupe assigné à ce dossier d'équipe — cela peut ne pas s'ouvrir dans vos propres Fichiers.", + "≤ 1 yr" : "≤ 1 an", + "1-3 yrs" : "1-3 ans", + "3-6 yrs" : "3-6 ans", + "6-10 yrs" : "6-10 ans", + "> 10 yrs" : "> 10 ans", + "Files count" : "Nombre de fichiers", + "Files size" : "Taille des fichiers", + "Files percentages" : "Pourcentage des fichiers", + "Data" : "Données", + "Files age": "Age des fichiers" +}, +"nplurals=2; plural=(n != 1);"); \ No newline at end of file diff --git a/l10n/fr.json b/l10n/fr.json new file mode 100644 index 0000000..6ba14e4 --- /dev/null +++ b/l10n/fr.json @@ -0,0 +1,57 @@ +{ + "translations": { + "My storage": "Mon stockage", + "Whole server": "Tout le serveur", + "Could not load the instance overview.": "Impossible de charger la vue d'ensemble de l'instance.", + "Could not load your storage overview.": "Impossible de charger la vue d'ensemble de votre stockage.", + "Could not load team folders.": "Impossible de charger les dossiers d'équipes.", + "No team folders found.": "Aucun dossier d'équipe trouvé.", + "Select a team folder to see details.": "Sélectionnez un dossier d'équipe pour voir les détails.", + "Reflects the file cache as of {date}.": "Reflète le cache des fichiers au {date}.", + "used": "utilisé(s)", + "Quota": "Quota", + "Unlimited": "Illimité", + "occupancy": "occupation", + "files": "fichiers", + "trash": "corbeille", + "versions": "versions", + "Groups": "Groupes", + "Could not load the largest files.": "Impossible de charger les fichiers les plus gros.", + "No files found.": "Aucun fichier trouvé.", + "Size": "Taille", + "unknown": "inconnu", + "Storage treemap": "Arborescence des fichiers", + "Documents": "Documents", + "Images": "Images", + "Video": "Vidéos", + "Archives & installers": "Archives & installeurs", + "Other": "Autre", + "Other small files ({count})": "Autres petits fichiers ({count})", + "Composition": "Composition", + "File count": "Nombre de fichiers", + "Last modified": "Dernière modification", + "More items not shown": "Plus d'éléments non affichés", + "Expand": "Étendre", + "Collapse": "Replier", + "Opening…": "Ouverture…", + "Could not load the folder tree.": "Impossible de charger l'arbre des dossiers.", + "No items found.": "Aucun élément trouvé.", + "Could not load external storages.": "Impossible de charger les stockages externes.", + "Not fully scanned yet — at least this much.": "Pas encore totalement analysé — au moins autant.", + "This storage has not been fully scanned yet, so sizes are a lower bound. Background scanning fills this in over time; \"occ files_external:scan\" measures it right away.": "Ce stockage n'a pas encoré été totalement scanné, les tailles indiquées constituent donc une valeur minimale. L'analyse d'arrière plan complète les informations au fil du temps; \"occ files_external:scan\" les mesures immédiatement.", + "Open in Files": "Ouvrir dans Fichiers", + "Open the selected folder in Files": "Ouvrir le dossier sélectionné dans Fichiers", + "You are not in the groups assigned to this team folder — this may not open in your own Files.": "Vous ne faîtes pas parti du groupe assigné à ce dossier d'équipe — cela peut ne pas s'ouvrir dans vos propres Fichiers.", + "≤ 1 yr": "≤ 1 an", + "1-3 yrs": "1-3 ans", + "3-6 yrs": "3-6 ans", + "6-10 yrs": "6-10 ans", + "> 10 yrs": "> 10 ans", + "Files count": "Nombre de fichiers", + "Files size": "Taille des fichiers", + "Files percentages": "Pourcentage des fichiers", + "Data": "Données", + "Files age": "Age des fichiers" + }, + "pluralForm": "nplurals=2; plural=(n != 1);" +} \ No newline at end of file diff --git a/l10n/pt_PT.js b/l10n/pt_PT.js index 4b3be29..6c56b8e 100644 --- a/l10n/pt_PT.js +++ b/l10n/pt_PT.js @@ -42,6 +42,16 @@ OC.L10N.register( "This storage has not been fully scanned yet, so sizes are a lower bound. Background scanning fills this in over time; \"occ files_external:scan\" measures it right away." : "Este armazenamento ainda não foi totalmente analisado, por isso os tamanhos são um valor mínimo. A análise em segundo plano preenche-os com o tempo; \"occ files_external:scan\" mede-o de imediato.", "Open in Files" : "Abrir nos Ficheiros", "Open the selected folder in Files" : "Abrir a pasta selecionada nos Ficheiros", - "You are not in the groups assigned to this team folder — this may not open in your own Files." : "Não pertences aos grupos atribuídos a esta pasta de equipa — isto pode não abrir nos teus próprios Ficheiros." + "You are not in the groups assigned to this team folder — this may not open in your own Files." : "Não pertences aos grupos atribuídos a esta pasta de equipa — isto pode não abrir nos teus próprios Ficheiros.", + "≤ 1 yr" : "≤ 1 ano", + "1-3 yrs" : "1-3 anos", + "3-6 yrs" : "3-6 anos", + "6-10 yrs" : "6-10 anos", + "> 10 yrs" : "> 10 anos", + "Files count" : "N.º de ficheiros", + "Files size" : "Tamanho dos ficheiros", + "Files percentages" : "Percentagens de ficheiros", + "Data" : "Dados", + "Files age": "Antiguidade arquivos" }, -"nplurals=2; plural=(n != 1);"); +"nplurals=2; plural=(n != 1);"); \ No newline at end of file diff --git a/l10n/pt_PT.json b/l10n/pt_PT.json index ac21276..1b59ff5 100644 --- a/l10n/pt_PT.json +++ b/l10n/pt_PT.json @@ -41,7 +41,17 @@ "This storage has not been fully scanned yet, so sizes are a lower bound. Background scanning fills this in over time; \"occ files_external:scan\" measures it right away.": "Este armazenamento ainda não foi totalmente analisado, por isso os tamanhos são um valor mínimo. A análise em segundo plano preenche-os com o tempo; \"occ files_external:scan\" mede-o de imediato.", "Open in Files": "Abrir nos Ficheiros", "Open the selected folder in Files": "Abrir a pasta selecionada nos Ficheiros", - "You are not in the groups assigned to this team folder — this may not open in your own Files.": "Não pertences aos grupos atribuídos a esta pasta de equipa — isto pode não abrir nos teus próprios Ficheiros." + "You are not in the groups assigned to this team folder — this may not open in your own Files.": "Não pertences aos grupos atribuídos a esta pasta de equipa — isto pode não abrir nos teus próprios Ficheiros.", + "≤ 1 yr": "≤ 1 ano", + "1-3 yrs": "1-3 anos", + "3-6 yrs": "3-6 anos", + "6-10 yrs": "6-10 anos", + "> 10 yrs": "> 10 anos", + "Files count": "N.º de ficheiros", + "Files size": "Tamanho dos ficheiros", + "Files percentages": "Percentagens de ficheiros", + "Data": "Dados", + "Files age": "Antiguidade arquivos" }, "pluralForm": "nplurals=2; plural=(n != 1);" } diff --git a/src/components/FileAgeChart.vue b/src/components/FileAgeChart.vue new file mode 100644 index 0000000..0e9f174 --- /dev/null +++ b/src/components/FileAgeChart.vue @@ -0,0 +1,140 @@ + + + \ No newline at end of file diff --git a/src/components/FileAgeChartDropdown.vue b/src/components/FileAgeChartDropdown.vue new file mode 100644 index 0000000..21aca7e --- /dev/null +++ b/src/components/FileAgeChartDropdown.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/src/components/Tab.vue b/src/components/Tab.vue new file mode 100644 index 0000000..f3ba4b5 --- /dev/null +++ b/src/components/Tab.vue @@ -0,0 +1,35 @@ + + + + + \ No newline at end of file diff --git a/src/components/Tabs.vue b/src/components/Tabs.vue new file mode 100644 index 0000000..4bf8aed --- /dev/null +++ b/src/components/Tabs.vue @@ -0,0 +1,58 @@ + + + + + \ No newline at end of file diff --git a/src/services/api.js b/src/services/api.js index 68a0faf..9ae7a6c 100644 --- a/src/services/api.js +++ b/src/services/api.js @@ -88,6 +88,24 @@ export async function fetchMap(scope, identifier, params = {}) { return data } +/** + * Fetch the file-age histogram for a scope, optionally restricted to a + * subtree path: five buckets (≤1y, 1-3y, 3-6y, 6-10y, >10y) + the file + * total. Server-aggregated so the payload is constant whatever the scope + * holds (see UsageController::fileAges()). + * + * @param {string} scope 'user' | 'teamfolder' | 'storage' + * @param {string|number} identifier uid, team folder id, or numeric storage id + * @param {string} activeCategory CATEGORY_DOCUMENT, CATEGORY_IMAGE, CATEGORY_VIDEO, CATEGORY_ARCHIVE, CATEGORY_OTHER + * @param {object} params { path } + */ +export async function fetchFileAges(scope, identifier, activeCategory, params = {}) { + const { data } = await axios.get(base('/api/v1/file-ages'), { + params: { scope, identifier, activeCategory, ...params }, + }) + return data +} + /** * Fetch the whole-instance header total: files+trash+versions across every * user and team folder, plus the files-only figure the tree/map below diff --git a/src/utils/colors.js b/src/utils/colors.js new file mode 100644 index 0000000..acf8831 --- /dev/null +++ b/src/utils/colors.js @@ -0,0 +1,65 @@ +export function cssVar(name, el = document.querySelector('.app-diskmap')) { + return getComputedStyle(el).getPropertyValue(name).trim() || '#cccccc' +} + +const LIGHTNESS_PER_STEP = 9.4 + +export function hexToHsl(hex) { + let v = hex.replace(/^#/, '') + if (v.length === 3) v = [...v].map((c) => c + c).join('') + const n = parseInt(v, 16) + const r = ((n >> 16) & 255) / 255 + const g = ((n >> 8) & 255) / 255 + const b = (n & 255) / 255 + + const max = Math.max(r, g, b) + const min = Math.min(r, g, b) + const d = max - min + const l = (max + min) / 2 + + let h = 0, s = 0 + if (d !== 0) { + s = d / (1 - Math.abs(2 * l - 1)) + if (max === r) h = ((g - b) / d) % 6 + else if (max === g) h = (b - r) / d + 2 + else h = (r - g) / d + 4 + h = Math.round(h * 60) + if (h < 0) h += 360 + } + return { h, s: s * 100, l: l * 100 } +} + +export function hslToHex(h, s, l) { + h = ((h % 360) + 360) % 360 + s /= 100; l /= 100 + const c = (1 - Math.abs(2 * l - 1)) * s + const x = c * (1 - Math.abs((h / 60) % 2 - 1)) + const m = l - c / 2 + + let r = 0, g = 0, b = 0 + if (h < 60) { r = c; g = x; b = 0 } + else if (h < 120) { r = x; g = c; b = 0 } + else if (h < 180) { r = 0; g = c; b = x } + else if (h < 240) { r = 0; g = x; b = c } + else if (h < 300) { r = x; g = 0; b = c } + else { r = c; g = 0; b = x } + + const toHex = (v) => Math.round((v + m) * 255).toString(16).padStart(2, '0') + return '#' + toHex(r) + toHex(g) + toHex(b) +} + +// Change the shade of a color, with step in positive to make it lighter, negative to make it darker +export function shade(hex, step) { + const { h, s, l } = hexToHsl(hex) + const next = Math.min(100, Math.max(0, l + step * LIGHTNESS_PER_STEP)) + return hslToHex(h, s, next) +} + +/** Input a hex, a n, and a mode and it outputs a palette of shades of n elements around hex + * mode can be 'lighter' to do base -> lighter colors ; 'darker' to do base -> darker colors ; and 'both' do do darker -> base -> lighter + */ +export function palette(hex, n, mode = 'both') { + const offset = { lighter: 0, darker: -(n - 1), both: -(n - 1) / 2 }[mode] + ?? -(n - 1) / 2 + return Array.from({ length: n }, (_, i) => shade(hex, offset + i)) +} \ No newline at end of file diff --git a/src/views/InstanceView.vue b/src/views/InstanceView.vue index 65fa184..6609b38 100644 --- a/src/views/InstanceView.vue +++ b/src/views/InstanceView.vue @@ -16,13 +16,9 @@ {{ formatBytes(usedSize) }} {{ t('diskmap', 'used') }} · {{ formatBytes(filesSize) }} {{ t('diskmap', 'files') }} - -