Merge branch 'FEAT/Dashy-V3' into FEATURE/environment-variables
This commit is contained in:
@@ -64,7 +64,7 @@ export default {
|
||||
return this.$store.getters.pageInfo;
|
||||
},
|
||||
sections() {
|
||||
return this.$store.getters.pageInfo;
|
||||
return this.$store.getters.sections;
|
||||
},
|
||||
visibleComponents() {
|
||||
return this.$store.getters.visibleComponents;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"home": {
|
||||
"no-results": "لا نتائج للبحث",
|
||||
"no-data": "لم يتم تكوين بيانات"
|
||||
"no-data": "لا يوجد بيانات"
|
||||
},
|
||||
"search": {
|
||||
"search-label": "بحث",
|
||||
@@ -33,11 +33,11 @@
|
||||
},
|
||||
"config": {
|
||||
"main-tab": "القائمة الرئيسية",
|
||||
"view-config-tab": "مشاهدة ملف Config",
|
||||
"edit-config-tab": "تحرير التكوين",
|
||||
"view-config-tab": "مشاهده ملف الإعدادات",
|
||||
"edit-config-tab": "تغير ملف الاع",
|
||||
"custom-css-tab": "الأنماط المخصصة",
|
||||
"heading": "خيارات الإعداد",
|
||||
"download-config-button": "تنزيل Config",
|
||||
"download-config-button": "تنزيل ملف الإعدادات",
|
||||
"edit-config-button": "تحرير التكوين",
|
||||
"edit-css-button": "تحرير CSS مخصص",
|
||||
"cloud-sync-button": "قم بتمكين Cloud Sync",
|
||||
@@ -84,7 +84,7 @@
|
||||
"sign-in-welcome": "مرحبًا {username}!"
|
||||
},
|
||||
"updates": {
|
||||
"app-version-note": "نسخة متهورة",
|
||||
"app-version-note": "ملاحظة نسخة التطبيق",
|
||||
"up-to-date": "حتى الآن",
|
||||
"out-of-date": "التحديث متاح",
|
||||
"unsupported-version-l1": "أنت تستخدم إصدارًا غير مدعوم من Dashy",
|
||||
|
||||
@@ -1,421 +1,448 @@
|
||||
{
|
||||
"home":
|
||||
{
|
||||
"no-results": "keine Suchergebnisse",
|
||||
"no-data": "keine Daten konfiguriert",
|
||||
"no-items-section": "Noch keine Elemente zum Anzeigen"
|
||||
},
|
||||
"search":
|
||||
{
|
||||
"search-label": "Suche",
|
||||
"search-placeholder": "Tippen um zu filtern",
|
||||
"clear-search-tooltip": "Suchfeld leeren",
|
||||
"enter-to-search-web": "Drücke Enter um das Internet zu durchsuchen"
|
||||
},
|
||||
"login":
|
||||
{
|
||||
"title": "Dashy",
|
||||
"username-label": "Benutzername",
|
||||
"password-label": "Passwort",
|
||||
"login-button": "Anmelden",
|
||||
"remember-me-label": "Angemeldet bleiben für",
|
||||
"remember-me-never": "Niemals",
|
||||
"remember-me-hour": "4 Stunden",
|
||||
"remember-me-day": "1 Tag",
|
||||
"remember-me-week": "1 Woche",
|
||||
"remember-me-long-time": "Eine lange Zeit",
|
||||
"error-missing-username": "Benutzername fehlt",
|
||||
"error-missing-password": "Passwort fehlt",
|
||||
"error-incorrect-username": "Benutzer nicht gefunden",
|
||||
"error-incorrect-password": "Falsches Passwort",
|
||||
"success-message": "Anmeldung läuft...",
|
||||
"logout-message": "Abgemeldet",
|
||||
"already-logged-in-title": "Bereits angemeldet",
|
||||
"already-logged-in-text": "Angemeldet als",
|
||||
"proceed-to-dashboard": "Zum Dashboard fortfahren",
|
||||
"log-out-button": "Abmelden",
|
||||
"proceed-guest-button": "Als Gast fortfahren"
|
||||
},
|
||||
"config":
|
||||
{
|
||||
"main-tab": "Hauptmenü",
|
||||
"view-config-tab": "Konfiguration",
|
||||
"edit-config-tab": "Konfiguration bearbeiten",
|
||||
"custom-css-tab": "eigene CSS",
|
||||
"heading": "Konfigurationseinstellungen",
|
||||
"download-config-button": "Konfigurationsdownload",
|
||||
"edit-config-button": "Konfiguration bearbeiten",
|
||||
"edit-css-button": "CSS bearbeiten",
|
||||
"cloud-sync-button": "Cloud-Synchronisation aktivieren",
|
||||
"edit-cloud-sync-button": "Cloud-Synchronisation bearbeiten",
|
||||
"rebuild-app-button": "Anwendung neu kompilieren",
|
||||
"change-language-button": "App-Sprache ändern",
|
||||
"reset-settings-button": "lokale Einstellungen zurücksetzen",
|
||||
"app-info-button": "App Informationen",
|
||||
"backup-note": "Es wird empfohlen ein Backup der Konfiguration zu erstellen bevor Änderungen durchgeführt werden.",
|
||||
"reset-config-msg-l1": "Dadurch werden alle Benutzereinstellungen aus dem lokalen Speicher entfernt, dies hat jedoch keine Auswirkungen auf Ihre Datei 'conf.yml'.",
|
||||
"reset-config-msg-l2": "Sie sollten zuerst alle Änderungen, die Sie lokal vorgenommen haben, sichern, wenn Sie sie in Zukunft wiederverwenden möchten.",
|
||||
"reset-config-msg-l3": "Sind Sie sicher, dass Sie fortfahren möchten?",
|
||||
"data-cleared-msg": "Daten erfolgreich gelöscht",
|
||||
"actions-label": "Aktionen",
|
||||
"copy-config-label": "Konfiguration kopieren",
|
||||
"data-copied-msg": "Konfiguration wurde in die Zwischenablage kopiert",
|
||||
"reset-config-label": "Konfiguration zurücksetzen",
|
||||
"css-save-btn": "Änderungen speichern",
|
||||
"css-note-label": "Bemerkung",
|
||||
"css-note-l1": "Sie müssen die Seite aktualisieren, damit Ihre Änderungen wirksam werden.",
|
||||
"css-note-l2": "Stilüberschreibungen werden nur lokal gespeichert, daher wird empfohlen vorher eine Kopie Ihres CSS zu erstellen.",
|
||||
"css-note-l3": "Um alle benutzerdefinierten Stile zu entfernen löschen Sie den Inhalt und klicken Sie auf Änderungen speichern."
|
||||
},
|
||||
"alternate-views": {
|
||||
"alternate-view-heading": "Ansicht wechseln",
|
||||
"default": "Standard",
|
||||
"workspace": "Arbeitsplatz",
|
||||
"minimal": "Minimal"
|
||||
},
|
||||
"settings":
|
||||
{
|
||||
"theme-label": "Design",
|
||||
"layout-label": "Layout",
|
||||
"layout-auto": "Auto",
|
||||
"layout-horizontal": "Horizontal",
|
||||
"layout-vertical": "Vertikal",
|
||||
"item-size-label": "Itemgröße",
|
||||
"item-size-small": "klein",
|
||||
"item-size-medium": "mittel",
|
||||
"item-size-large": "groß",
|
||||
"config-launcher-label": "Konfiguration",
|
||||
"config-launcher-tooltip": "Konfiguration aktualisieren",
|
||||
"sign-out-tooltip": "Abmelden",
|
||||
"sign-in-tooltip": "Anmelden",
|
||||
"sign-in-welcome": "Hallo {username}!"
|
||||
},
|
||||
"updates":
|
||||
{
|
||||
"app-version-note": "Dashy Version",
|
||||
"up-to-date": "Aktuell",
|
||||
"out-of-date": "Update verfügbar",
|
||||
"unsupported-version-l1": "Sie verwenden eine nicht unterstützte Version von Dashy",
|
||||
"unsupported-version-l2": "Für die beste Erfahrung und aktuelle Sicherheitspatches aktualisieren Sie bitte auf"
|
||||
},
|
||||
"language-switcher":
|
||||
{
|
||||
"title": "Applikationssprache ändern",
|
||||
"dropdown-label": "Sprache auswählen",
|
||||
"save-button": "Speichern",
|
||||
"success-msg": "Sprache geändert auf"
|
||||
},
|
||||
"theme-maker":
|
||||
{
|
||||
"title": "Design Konfigurator",
|
||||
"export-button": "Benutzerdefinierte Variablen exportieren",
|
||||
"reset-button": "CSS zurücksetzen für",
|
||||
"show-all-button": "Alle Variablen anzeigen",
|
||||
"change-fonts-button": "Schriftart ändern",
|
||||
"save-button": "Speichern",
|
||||
"cancel-button": "Abbrechen",
|
||||
"saved-toast": "{theme} wurde erfolgreich aktualisiert",
|
||||
"copied-toast": "Designdaten für {theme} wurden in die Zwischenablage kopiert.",
|
||||
"reset-toast": "Benutzerdefinierte Farben für {theme} wurden entfernt"
|
||||
},
|
||||
"config-editor":
|
||||
{
|
||||
"save-location-label": "Speicherort",
|
||||
"location-local-label": "Lokal anwenden",
|
||||
"location-disk-label": "Änderungen in die Konfigurationsdatei schreiben",
|
||||
"save-button": "Änderungen speichern",
|
||||
"preview-button": "Vorschau der Änderungen",
|
||||
"valid-label": "Syntax ist gültig",
|
||||
"status-success-msg": "Aufgabe abgeschlossen",
|
||||
"status-fail-msg": "Aufgabe fehlgeschlagen",
|
||||
"success-msg-disk": "Konfigurationsdatei wurde erfolgreich auf die Festplatte geschrieben",
|
||||
"success-msg-local": "Lokale Änderungen wurden erfolgreich gespeichert",
|
||||
"success-note-l1": "Die Applikation sollte automatisch re-kompiliert werden.",
|
||||
"success-note-l2": "Dies kann bis zu einer Minute dauern.",
|
||||
"success-note-l3": "Sie müssen die Seite aktualisieren damit die Änderungen wirksam werden.",
|
||||
"error-msg-save-mode": "Bitte wählen Sie einen Speichermodus: Lokal oder Datei",
|
||||
"error-msg-cannot-save": "Beim Speichern der Konfiguration ist ein Fehler aufgetreten",
|
||||
"error-msg-bad-json": "Fehler in JSON-Daten, möglicherweise fehlerhafter Syntax",
|
||||
"warning-msg-validation": "Validierungswarnung",
|
||||
"not-admin-note": "Änderungen können nicht auf die Festplatte gespeichert werden, da Sie nicht als Administrator angemeldet sind"
|
||||
},
|
||||
"app-rebuild":
|
||||
{
|
||||
"title": "Applikation re-kompilieren",
|
||||
"rebuild-note-l1": "Damit die in die Datei conf.yml geschriebenen Änderungen wirksam werden ist ein re-kompilieren erforderlich.",
|
||||
"rebuild-note-l2": "Dies sollte automatisch passieren, aber falls nicht können Sie es hier manuell starten.",
|
||||
"rebuild-note-l3": "Dies ist bei lokal gespeicherten Änderungen nicht erforderlich.",
|
||||
"rebuild-button": "Start Kompilierung",
|
||||
"rebuilding-status-1": "Baue...",
|
||||
"rebuilding-status-2": "Das kann ein paar Minuten dauern",
|
||||
"error-permission": "Sie sind nicht berechtigt diese Aktion auszulösen",
|
||||
"success-msg": "Kompilierung erfolgreich abgeschlossen",
|
||||
"fail-msg": "Kompilierung fehlgeschlagen",
|
||||
"reload-note": "Ein neu Laden der Seite ist erforderlich, damit die Änderungen wirksam werden.",
|
||||
"reload-button": "Seite neu laden"
|
||||
},
|
||||
"cloud-sync":
|
||||
{
|
||||
"title": "Cloud Backup & Wiederherstellung",
|
||||
"intro-l1": "Cloud-Backup und Wiederherstellung ist eine optionale Funktion mit der Sie Ihre Konfiguration in das Internet hochladen und dann auf einem anderen Gerät oder einer anderen Dashy-Instanz wiederherstellen können.",
|
||||
"intro-l2": "Alle Daten sind vollständig Ende-zu-Ende mit AES verschlüsselt. Ihr Passwort wird als Schlüssel verwendet.",
|
||||
"intro-l3": "Weitere Informationen finden Sie im",
|
||||
"backup-title-setup": "Backup erstellen",
|
||||
"backup-title-update": "Backup aktualisieren",
|
||||
"password-label-setup": "Passwort auswählen",
|
||||
"password-label-update": "Passwort eingeben",
|
||||
"backup-button-setup": "Backup",
|
||||
"backup-button-update": "Backup aktualisieren",
|
||||
"backup-id-label": "Ihre Backup ID",
|
||||
"backup-id-note": "Diese wird zusammen mit dem Passwort benötigt um Ihr Backup wiederherzustellen. Bewahren Sie sie zusammen mit Ihrem Passwort an einem sicheren Ort auf.",
|
||||
"restore-title": "Backup wiederherstellen",
|
||||
"restore-id-label": "ID wiederherstellen",
|
||||
"restore-password-label": "Passwort",
|
||||
"restore-button": "Wiederherstellen",
|
||||
"backup-error-unknown": "Anfrage kann nicht verarbeitet werden",
|
||||
"backup-error-password": "Falsches Passwort. Bitte geben Sie Ihr aktuelles Passwort ein.",
|
||||
"backup-success-msg": "Erfolgreich abgeschlossen",
|
||||
"restore-success-msg": "Konfiguration erfolgreich wiederhergestellt"
|
||||
},
|
||||
"menu":
|
||||
{
|
||||
"open-section-title": "Öffnen in",
|
||||
"sametab": "Aktueller Tab",
|
||||
"newtab": "Neuer Tab",
|
||||
"modal": "Popup Modal",
|
||||
"workspace": "Arbeitsflächenansicht",
|
||||
"options-section-title": "Optionen",
|
||||
"edit-item": "Bearbeiten",
|
||||
"move-item": "Kopieren oder Verschieben",
|
||||
"remove-item": "Entfernen"
|
||||
},
|
||||
"context-menus":
|
||||
{
|
||||
"item":
|
||||
{
|
||||
"open-section-title": "Öffnen in",
|
||||
"sametab": "Aktueller Tab",
|
||||
"newtab": "Neuer Tab",
|
||||
"modal": "Popup Modal",
|
||||
"workspace": "Arbeitsflächenansicht",
|
||||
"clipboard": "In Zwischenablage kopieren",
|
||||
"options-section-title": "Optionen",
|
||||
"edit-item": "Bearbeiten",
|
||||
"move-item": "Kopieren oder Verschieben",
|
||||
"remove-item": "Entfernen",
|
||||
"copied-toast": "URL wurde in die Zwischenablage kopiert"
|
||||
},
|
||||
"section":
|
||||
{
|
||||
"open-section": "Sektion öffnen",
|
||||
"edit-section": "Bearbeiten",
|
||||
"expand-collapse": "Aus- / Einklappen",
|
||||
"move-section": "Verschieben nach",
|
||||
"remove-section": "Entfernen"
|
||||
}
|
||||
},
|
||||
"interactive-editor":
|
||||
{
|
||||
"menu":
|
||||
{
|
||||
"start-editing-tooltip": "Interaktiven Editor starten",
|
||||
"edit-site-data-subheading": "Seiteninformationen bearbeiten",
|
||||
"edit-page-info-btn": "Seiteninformationen bearbeiten",
|
||||
"edit-page-info-tooltip": "Applikationstitel, Beschreibung, Nav. links, Fußzeile, etc.",
|
||||
"edit-app-config-btn": "Applikationskonfiguration bearbeiten",
|
||||
"edit-app-config-tooltip": "Alle anderen Konfigurationsoptionen",
|
||||
"edit-pages-btn": "Seiten bearbeiten",
|
||||
"edit-pages-tooltip": "Hinzufügen oder entfernen von zusätzlichen Ansichten",
|
||||
"config-save-methods-subheading": "Speicheroptionen der Konfiguration",
|
||||
"save-locally-btn": "Lokal speichern",
|
||||
"save-locally-tooltip": "Konfiguration lokal im Browser speichern. Dies hat keinen Einfluss auf die Konfigurationsdatei, aber Änderungen werden nur in diesem Browser gespeichert",
|
||||
"save-disk-btn": "Auf Festplatte speichern",
|
||||
"save-disk-tooltip": "Konfigurationsdatei conf.yml speichern. Dies erzeugt ein Backup und überschreibt dann die existierende Konfigurationsdatei",
|
||||
"export-config-btn": "Konfiguration exportieren",
|
||||
"export-config-tooltip": "Konfiguration anzeigen und exportieren, entweder in eine Datei oder in die Zwischenablage",
|
||||
"cloud-backup-btn": "Cloud-Backup starten",
|
||||
"cloud-backup-tooltip": "Speichert ein verrschlüsseltes Backup in die Cloud",
|
||||
"edit-raw-config-btn": "Konfiguration als Rohdaten bearbeiten",
|
||||
"edit-raw-config-tooltip": "Anzeigen und bearbeiten der Konfiguration als Rohdaten im JSON-Editor",
|
||||
"cancel-changes-btn": "Verwerfen",
|
||||
"cancel-changes-tooltip": "Modifikationen zurücksetzen und Bearbeitungsmodus schließen. Dies hat keinen Einfluss auf die Konfigurationsdatei",
|
||||
"edit-mode-name": "Bearbeitung",
|
||||
"edit-mode-subtitle": "Sie sind im Bearbeitungsmodus",
|
||||
"edit-mode-description": "Das bedeutet, dass Änderungen an der Konfigurationsdatei vorgenommen werden können. Änderungen können vor dem Speichern betrachtet werden.",
|
||||
"save-stage-btn": "Speichern",
|
||||
"cancel-stage-btn": "Abbrechen",
|
||||
"save-locally-warning": "Wenn Sie fortfahren werden die Änderungen nur in Ihrem Browser gespeichert. Um die Konfiguration auf anderen Geräten zu nutzen sollten Sie sie exportieren. Möchten Sie fortfahren?"
|
||||
},
|
||||
"edit-item":
|
||||
{
|
||||
"missing-title-err": "Ein Titel is zwingend notwendig"
|
||||
},
|
||||
"edit-section":
|
||||
{
|
||||
"edit-section-title": "Sektion bearbeiten",
|
||||
"add-section-title": "Neue Sektion hinzufügen",
|
||||
"edit-tooltip": "Klicken zum Bearbeiten oder Rechtsklick für weitere Optionen",
|
||||
"remove-confirm": "Sind Sie sicher, dass sie diese Sektion entfernen möchten? Diese Aktion kann nicht rückgänging gemacht werden."
|
||||
},
|
||||
"edit-app-config":
|
||||
{
|
||||
"warning-msg-title": "Ab hier ist Vorsicht geboten",
|
||||
"warning-msg-l1": "Die folgenden Optionen sind für fortgeschrittene Konfigurationen.",
|
||||
"warning-msg-l2": "Sollten Felder unklar sein, konsultieren Sie die",
|
||||
"warning-msg-docs": "Dokumentation",
|
||||
"warning-msg-l3": "um unbeabsichtigte Folgen zu vermeiden."
|
||||
},
|
||||
"export":
|
||||
{
|
||||
"export-title": "Konfiguration exportieren",
|
||||
"copy-clipboard-btn": "In Zwischenablage kopieren",
|
||||
"copy-clipboard-tooltip": "Applikationskonfiguration als YAML in Zwischenablage kopieren",
|
||||
"download-file-btn": "Datei herunterladen",
|
||||
"download-file-tooltip": "Applikationskonfiguration auf Ihr Gerät herunterladen",
|
||||
"view-title": "Konfiguration anzeigen"
|
||||
}
|
||||
},
|
||||
"widgets":
|
||||
{
|
||||
"general":
|
||||
{
|
||||
"loading": "Lade...",
|
||||
"show-more": "Details",
|
||||
"show-less": "Weniger anzeigen",
|
||||
"open-link": "Weiterlesen"
|
||||
},
|
||||
"pi-hole":
|
||||
{
|
||||
"status-heading": "Status"
|
||||
},
|
||||
"stat-ping":
|
||||
{
|
||||
"up": "Online",
|
||||
"down": "Offline"
|
||||
},
|
||||
"net-data":
|
||||
{
|
||||
"cpu-chart-title": "CPU Historie",
|
||||
"mem-chart-title": "Speichernutzung",
|
||||
"mem-breakdown-title": "Speichernutzung",
|
||||
"load-chart-title": "Systemlast"
|
||||
},
|
||||
"glances":
|
||||
{
|
||||
"disk-space-free": "Frei",
|
||||
"disk-space-used": "Genutzt",
|
||||
"disk-mount-point": "Mount Punkt",
|
||||
"disk-file-system": "Dateisystem,",
|
||||
"disk-io-read": "Lesen",
|
||||
"disk-io-write": "Schreiben",
|
||||
"system-load-desc": "Prozesse in Warteschlange. (Durchschnitt aller Kerne)"
|
||||
},
|
||||
"system-info":
|
||||
{
|
||||
"uptime": "Uptime"
|
||||
},
|
||||
"flight-data":
|
||||
{
|
||||
"arrivals": "Ankünfte",
|
||||
"departures": "Abflüge"
|
||||
},
|
||||
"tfl-status":
|
||||
{
|
||||
"good-service-all": "Guter Service auf allen Leitungen",
|
||||
"good-service-rest": "Guter Service auf allen anderen Leitungen"
|
||||
},
|
||||
"synology-download":
|
||||
{
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"downloaded": "Heruntergeladen",
|
||||
"uploaded": "Hochgeladen",
|
||||
"remaining": "Verbleibend",
|
||||
"up": "Hoch",
|
||||
"down": "Runter"
|
||||
},
|
||||
"gluetun-status":
|
||||
{
|
||||
"vpn-ip": "VPN IP",
|
||||
"country": "Land",
|
||||
"region": "Bundesland",
|
||||
"city": "Stadt",
|
||||
"post-code": "Postleitzahl",
|
||||
"location": "Standort",
|
||||
"timezone": "Zeitzone",
|
||||
"organization": "Organisation"
|
||||
},
|
||||
"nextcloud":
|
||||
{
|
||||
"active": "Aktiv",
|
||||
"and": "und",
|
||||
"applications": "Anwendungen",
|
||||
"available": "Verfügbar",
|
||||
"away": "Abwesend",
|
||||
"cache-full": "CACHE VOLL",
|
||||
"chat-room": "Chatraum",
|
||||
"delete-all": "Alle löschen",
|
||||
"delete-notification": "Benachrichtigung löschen",
|
||||
"disabled": "Deaktivert",
|
||||
"disk-quota": "Disk Quota",
|
||||
"disk-space": "Disk Speicherplatz",
|
||||
"dnd": "Nicht stören",
|
||||
"email": "EMail",
|
||||
"enabled": "Aktiviert",
|
||||
"federated-shares-ucfirst": "Verbundsfreigaben",
|
||||
"federated-shares": "Verbundsfreigaben",
|
||||
"files": "Dateien{plural}",
|
||||
"free": "Frei",
|
||||
"groups": "Gruppen",
|
||||
"hit-rate": "Trefferrate",
|
||||
"hits": "Treffer",
|
||||
"home": "Zuhause",
|
||||
"in": "in",
|
||||
"keys": "Schlüssel",
|
||||
"last-24-hours": "Letzte 24 Stunden",
|
||||
"last-5-minutes": "in den letzten 5 Minuten",
|
||||
"last-hour": "in der letzten Stunde",
|
||||
"last-login": "Letzte Anmeldung",
|
||||
"last-restart": "Letzter Neustart",
|
||||
"load-averages": "Systemlast aller CPU-Kerne",
|
||||
"local-shares": "Lokale Freigaben",
|
||||
"local": "Lokal",
|
||||
"max-keys": "Maximale Schlüssel",
|
||||
"memory-used": "Speuchernutzung",
|
||||
"memory-utilisation": "Speuchernutzung",
|
||||
"memory": "Speicher",
|
||||
"misses": "Fehlschläge",
|
||||
"no-notifications": "Keine Benachrichtigungen",
|
||||
"no-pending-updates": "Keine ausstehenden Aktualisierungen",
|
||||
"nothing-to-show": "Momentan gibt es hier nichts zu zeigen",
|
||||
"of-which": "welche",
|
||||
"of": "von",
|
||||
"offline": "Offline",
|
||||
"online": "Online",
|
||||
"other": "Andere",
|
||||
"overall": "Insgesamt",
|
||||
"private-link": "privater Link",
|
||||
"public-link": "öffentlicher Link",
|
||||
"quota-enabled": "Disk Quota ist {nicht}aktiviert für diesen Benutzer",
|
||||
"received": "Empfangen",
|
||||
"scripts": "Skripte",
|
||||
"sent": "Gesendet",
|
||||
"started": "Gestartet",
|
||||
"storages-by-type": "Speicher nach Typ",
|
||||
"storages": "Speicher{plural}",
|
||||
"strings-use": "Strings benutzen",
|
||||
"tasks": "Aufgaben",
|
||||
"total-files": "Dateien gesamt",
|
||||
"total-users": "Benutzer gesamt",
|
||||
"total": "Insgesamt",
|
||||
"until": "Bis",
|
||||
"updates-available-for": "Aktualisierungen sind verfügbar für",
|
||||
"updates-available": "Aktualisierungen{plural} verfügbar",
|
||||
"used": "benutzt",
|
||||
"user": "Benutzer",
|
||||
"using": "nutzt",
|
||||
"version": "Version",
|
||||
"wasted": "verschwendet"
|
||||
}
|
||||
"home": {
|
||||
"no-results": "keine Suchergebnisse",
|
||||
"no-data": "keine Daten konfiguriert",
|
||||
"no-items-section": "Noch keine Elemente zum Anzeigen"
|
||||
},
|
||||
"search": {
|
||||
"search-label": "Suche",
|
||||
"search-placeholder": "Tippe um zu filtern",
|
||||
"clear-search-tooltip": "Suchfeld leeren",
|
||||
"enter-to-search-web": "Drücke Enter um das Internet zu durchsuchen"
|
||||
},
|
||||
"splash-screen": {
|
||||
"loading": "Lädt"
|
||||
},
|
||||
"login": {
|
||||
"title": "Dashy",
|
||||
"guest-label": "Gastzugriff",
|
||||
"username-label": "Benutzername",
|
||||
"password-label": "Passwort",
|
||||
"login-button": "Anmelden",
|
||||
"remember-me-label": "Angemeldet bleiben für",
|
||||
"remember-me-never": "Niemals",
|
||||
"remember-me-hour": "4 Stunden",
|
||||
"remember-me-day": "1 Tag",
|
||||
"remember-me-week": "1 Woche",
|
||||
"remember-me-long-time": "Eine lange Zeit",
|
||||
"error-missing-username": "Benutzername fehlt",
|
||||
"error-missing-password": "Passwort fehlt",
|
||||
"error-incorrect-username": "Benutzer nicht gefunden",
|
||||
"error-incorrect-password": "Falsches Passwort",
|
||||
"success-message": "Anmeldung läuft...",
|
||||
"logout-message": "Abgemeldet",
|
||||
"already-logged-in-title": "Bereits angemeldet",
|
||||
"already-logged-in-text": "Angemeldet als",
|
||||
"proceed-to-dashboard": "Zum Dashboard fortfahren",
|
||||
"log-out-button": "Abmelden",
|
||||
"proceed-guest-button": "Als Gast fortfahren",
|
||||
"guest-intro-1": "Diese Instanz hat Gastzugriffe aktiviert",
|
||||
"guest-intro-2": "Gäste haben lesenden Zugriff auf Dashboards, können also keine Veränderungen auf die Festplatte schreiben.",
|
||||
"error": "Error",
|
||||
"error-no-user-configured": "Authentifizierung ist nicht aktiviert, oder es sind keine Benutzer konfiguriert",
|
||||
"error-go-home-button": "Geh nach Hause",
|
||||
"logged-in-guest": "Als Gast eingeloggt, Umleitung...",
|
||||
"error-guest-access": "Gastzugriff nicht erlaubt"
|
||||
},
|
||||
"app-info": {
|
||||
"title": "App Info",
|
||||
"error-log": "Fehlerprotokoll",
|
||||
"no-errors": "Keine kürzlichen Fehler erkannt",
|
||||
"help-support": "Hilfe & Support",
|
||||
"help-support-description": "Um beim Betrieb oder der Konfiguration von Dashy Hilfe zu bekommen, siehe die",
|
||||
"help-support-discussions": "Diskussionen",
|
||||
"support-dashy": "Dashy unterstützen",
|
||||
"support-dashy-description": "Für Wege, sich zu beteiligen, besuchen Sie die",
|
||||
"support-dashy-link": "Contributions Seite",
|
||||
"report-bug": "Melde einen Fehler",
|
||||
"report-bug-description": "Wenn Sie glauben, einen Fehler gefunden zu haben, dann bitte",
|
||||
"report-bug-link": "öffne ein Issue",
|
||||
"more-info": "Mehr Informationen",
|
||||
"source": "Quellcode",
|
||||
"documentation": "Dokumentation",
|
||||
"privacy-and-security": "Datenschutz & Sicherheit",
|
||||
"privacy-and-security-l1": "Für eine Übersicht, wie Dashy mit Ihren Daten umgeht, siehe die",
|
||||
"privacy-and-security-privacy-policy": "Datenschutzerklärung",
|
||||
"privacy-and-security-advice": "Für Anleitung, um Ihr Dashboard zu sichern, konsultieren Sie die",
|
||||
"privacy-and-security-advice-link": "Management Dokumentation",
|
||||
"privacy-and-security-security-issue": "Wenn Sie glauben, eine mögliche Sicherheitslücke gefunden zu haben, melde sie wie beschrieben in unserer",
|
||||
"privacy-and-security-security-policy": "Security Policy",
|
||||
"license": "Lizenz",
|
||||
"license-under": "Lizensiert unter",
|
||||
"licence-third-party": "Für Lizenzen von Drittanbietermodulen, siehe",
|
||||
"licence-third-party-link": "Legal",
|
||||
"list-contributors": "Für eine vollstandige Liste aller Beteiligten und Dank, siehe",
|
||||
"list-contributors-link": "Credits",
|
||||
"version": "Version"
|
||||
},
|
||||
"config": {
|
||||
"main-tab": "Hauptmenü",
|
||||
"view-config-tab": "Konfiguration",
|
||||
"edit-config-tab": "Konfiguration bearbeiten",
|
||||
"custom-css-tab": "eigene CSS",
|
||||
"heading": "Konfigurationseinstellungen",
|
||||
"download-config-button": "Konfigurationsdownload",
|
||||
"edit-config-button": "Konfiguration bearbeiten",
|
||||
"edit-css-button": "CSS bearbeiten",
|
||||
"cloud-sync-button": "Cloud-Synchronisation aktivieren",
|
||||
"edit-cloud-sync-button": "Cloud-Synchronisation bearbeiten",
|
||||
"rebuild-app-button": "Anwendung neu kompilieren",
|
||||
"change-language-button": "App-Sprache ändern",
|
||||
"reset-settings-button": "lokale Einstellungen zurücksetzen",
|
||||
"disabled-note": "Einige Konfigurationsoptionen wurden vom Administrator deaktivert",
|
||||
"small-screen-note": "Sie benutzen einen sehr kleinen Bildschirm. Einige Seiten in diesem Menü sind dafür möglicherweise nicht ideal.",
|
||||
"app-info-button": "App Informationen",
|
||||
"backup-note": "Es wird empfohlen ein Backup der Konfiguration zu erstellen bevor Änderungen durchgeführt werden.",
|
||||
"reset-config-msg-l1": "Dadurch werden alle Benutzereinstellungen aus dem lokalen Speicher entfernt, dies hat jedoch keine Auswirkungen auf Ihre Datei 'conf.yml'.",
|
||||
"reset-config-msg-l2": "Sie sollten zuerst alle Änderungen, die Sie lokal vorgenommen haben, sichern, wenn Sie sie in Zukunft wiederverwenden möchten.",
|
||||
"reset-config-msg-l3": "Sind Sie sicher, dass Sie fortfahren möchten?",
|
||||
"data-cleared-msg": "Daten erfolgreich gelöscht",
|
||||
"actions-label": "Aktionen",
|
||||
"copy-config-label": "Konfiguration kopieren",
|
||||
"data-copied-msg": "Konfiguration wurde in die Zwischenablage kopiert",
|
||||
"reset-config-label": "Konfiguration zurücksetzen",
|
||||
"css-save-btn": "Änderungen speichern",
|
||||
"css-note-label": "Bemerkung",
|
||||
"css-note-l1": "Sie müssen die Seite aktualisieren, damit Ihre Änderungen wirksam werden.",
|
||||
"css-note-l2": "Stilüberschreibungen werden nur lokal gespeichert, daher wird empfohlen vorher eine Kopie Ihres CSS zu erstellen.",
|
||||
"css-note-l3": "Um alle benutzerdefinierten Stile zu entfernen löschen Sie den Inhalt und klicken Sie auf Änderungen speichern.",
|
||||
"custom-css": {
|
||||
"title": "Eigenes CSS",
|
||||
"base-theme": "Basisdesign"
|
||||
}
|
||||
},
|
||||
"alternate-views": {
|
||||
"alternate-view-heading": "Ansicht wechseln",
|
||||
"default": "Standard",
|
||||
"workspace": "Arbeitsplatz",
|
||||
"minimal": "Minimal"
|
||||
},
|
||||
"settings": {
|
||||
"theme-label": "Design",
|
||||
"layout-label": "Layout",
|
||||
"layout-auto": "Auto",
|
||||
"layout-horizontal": "Horizontal",
|
||||
"layout-vertical": "Vertikal",
|
||||
"item-size-label": "Itemgröße",
|
||||
"item-size-small": "klein",
|
||||
"item-size-medium": "mittel",
|
||||
"item-size-large": "groß",
|
||||
"config-launcher-label": "Konfiguration",
|
||||
"config-launcher-tooltip": "Konfiguration aktualisieren",
|
||||
"sign-out-tooltip": "Abmelden",
|
||||
"sign-in-tooltip": "Anmelden",
|
||||
"sign-in-welcome": "Hallo {username}!",
|
||||
"hide": "Verstecke",
|
||||
"open": "Öffne"
|
||||
},
|
||||
"updates": {
|
||||
"app-version-note": "Dashy Version",
|
||||
"up-to-date": "Aktuell",
|
||||
"out-of-date": "Update verfügbar",
|
||||
"unsupported-version-l1": "Sie verwenden eine nicht unterstützte Version von Dashy",
|
||||
"unsupported-version-l2": "Für die beste Erfahrung und aktuelle Sicherheitspatches aktualisieren Sie bitte auf"
|
||||
},
|
||||
"language-switcher": {
|
||||
"title": "Applikationssprache ändern",
|
||||
"dropdown-label": "Sprache auswählen",
|
||||
"save-button": "Speichern",
|
||||
"success-msg": "Sprache geändert auf"
|
||||
},
|
||||
"theme-maker": {
|
||||
"title": "Design Konfigurator",
|
||||
"export-button": "Benutzerdefinierte Variablen exportieren",
|
||||
"reset-button": "CSS zurücksetzen für",
|
||||
"show-all-button": "Alle Variablen anzeigen",
|
||||
"change-fonts-button": "Schriftart ändern",
|
||||
"save-button": "Speichern",
|
||||
"cancel-button": "Abbrechen",
|
||||
"saved-toast": "{theme} wurde erfolgreich aktualisiert",
|
||||
"copied-toast": "Designdaten für {theme} wurden in die Zwischenablage kopiert.",
|
||||
"reset-toast": "Benutzerdefinierte Farben für {theme} wurden entfernt"
|
||||
},
|
||||
"config-editor": {
|
||||
"save-location-label": "Speicherort",
|
||||
"location-local-label": "Lokal anwenden",
|
||||
"location-disk-label": "Änderungen in die Konfigurationsdatei schreiben",
|
||||
"save-button": "Änderungen speichern",
|
||||
"preview-button": "Vorschau der Änderungen",
|
||||
"valid-label": "Syntax ist gültig",
|
||||
"status-success-msg": "Aufgabe abgeschlossen",
|
||||
"status-fail-msg": "Aufgabe fehlgeschlagen",
|
||||
"success-msg-disk": "Konfigurationsdatei wurde erfolgreich auf die Festplatte geschrieben",
|
||||
"success-msg-local": "Lokale Änderungen wurden erfolgreich gespeichert",
|
||||
"success-note-l1": "Die Applikation sollte automatisch re-kompiliert werden.",
|
||||
"success-note-l2": "Dies kann bis zu einer Minute dauern.",
|
||||
"success-note-l3": "Sie müssen die Seite aktualisieren damit die Änderungen wirksam werden.",
|
||||
"error-msg-save-mode": "Bitte wählen Sie einen Speichermodus: Lokal oder Datei",
|
||||
"error-msg-cannot-save": "Beim Speichern der Konfiguration ist ein Fehler aufgetreten",
|
||||
"error-msg-bad-json": "Fehler in JSON-Daten, möglicherweise fehlerhafter Syntax",
|
||||
"warning-msg-validation": "Validierungswarnung",
|
||||
"not-admin-note": "Änderungen können nicht auf die Festplatte gespeichert werden, da Sie nicht als Administrator angemeldet sind"
|
||||
},
|
||||
"app-rebuild": {
|
||||
"title": "Applikation neu kompilieren",
|
||||
"rebuild-note-l1": "Damit die in die Datei conf.yml geschriebenen Änderungen wirksam werden ist ein Neukompilieren erforderlich.",
|
||||
"rebuild-note-l2": "Dies sollte automatisch passieren, aber falls nicht können Sie es hier manuell starten.",
|
||||
"rebuild-note-l3": "Dies ist bei lokal gespeicherten Änderungen nicht erforderlich.",
|
||||
"rebuild-button": "Starte Kompilierung",
|
||||
"rebuilding-status-1": "Baue...",
|
||||
"rebuilding-status-2": "Das kann ein paar Minuten dauern",
|
||||
"error-permission": "Sie sind nicht berechtigt diese Aktion auszulösen",
|
||||
"success-msg": "Kompilierung erfolgreich abgeschlossen",
|
||||
"fail-msg": "Kompilierung fehlgeschlagen",
|
||||
"reload-note": "Ein neu Laden der Seite ist erforderlich, damit die Änderungen wirksam werden.",
|
||||
"reload-button": "Seite neu laden"
|
||||
},
|
||||
"cloud-sync": {
|
||||
"title": "Cloud Backup & Wiederherstellung",
|
||||
"intro-l1": "Cloud-Backup und Wiederherstellung ist eine optionale Funktion mit der Sie Ihre Konfiguration in das Internet hochladen und dann auf einem anderen Gerät oder einer anderen Dashy-Instanz wiederherstellen können.",
|
||||
"intro-l2": "Alle Daten sind vollständig Ende-zu-Ende mit AES verschlüsselt. Ihr Passwort wird als Schlüssel verwendet.",
|
||||
"intro-l3": "Weitere Informationen finden Sie in der",
|
||||
"intro-docs": "Dokumentation",
|
||||
"backup-title-setup": "Backup erstellen",
|
||||
"backup-title-update": "Backup aktualisieren",
|
||||
"password-label-setup": "Passwort auswählen",
|
||||
"password-label-update": "Passwort eingeben",
|
||||
"backup-button-setup": "Backup",
|
||||
"backup-button-update": "Backup aktualisieren",
|
||||
"backup-id-label": "Ihre Backup ID",
|
||||
"backup-id-note": "Diese wird zusammen mit dem Passwort benötigt um Ihr Backup wiederherzustellen. Bewahren Sie sie zusammen mit Ihrem Passwort an einem sicheren Ort auf.",
|
||||
"restore-title": "Backup wiederherstellen",
|
||||
"restore-id-label": "ID wiederherstellen",
|
||||
"restore-password-label": "Passwort",
|
||||
"restore-button": "Wiederherstellen",
|
||||
"backup-missing-password": "Passwort fehlt",
|
||||
"backup-error-unknown": "Anfrage kann nicht verarbeitet werden",
|
||||
"backup-error-password": "Falsches Passwort. Bitte geben Sie Ihr aktuelles Passwort ein.",
|
||||
"backup-success-msg": "Erfolgreich abgeschlossen",
|
||||
"restore-success-msg": "Konfiguration erfolgreich wiederhergestellt"
|
||||
},
|
||||
"menu": {
|
||||
"open-section-title": "Öffne in",
|
||||
"sametab": "Aktueller Tab",
|
||||
"newtab": "Neuer Tab",
|
||||
"modal": "Popup Modal",
|
||||
"workspace": "Arbeitsflächenansicht",
|
||||
"options-section-title": "Optionen",
|
||||
"edit-item": "Bearbeiten",
|
||||
"move-item": "Kopieren oder Verschieben",
|
||||
"remove-item": "Entfernen"
|
||||
},
|
||||
"context-menus": {
|
||||
"item": {
|
||||
"open-section-title": "Öffnen in",
|
||||
"sametab": "Aktueller Tab",
|
||||
"newtab": "Neuer Tab",
|
||||
"modal": "Popup Modal",
|
||||
"workspace": "Arbeitsflächenansicht",
|
||||
"clipboard": "In Zwischenablage kopieren",
|
||||
"options-section-title": "Optionen",
|
||||
"edit-item": "Bearbeiten",
|
||||
"move-item": "Kopieren oder Verschieben",
|
||||
"remove-item": "Entfernen",
|
||||
"copied-toast": "URL wurde in die Zwischenablage kopiert"
|
||||
},
|
||||
"section": {
|
||||
"open-section": "Sektion öffnen",
|
||||
"edit-section": "Bearbeiten",
|
||||
"expand-collapse": "Aus- / Einklappen",
|
||||
"move-section": "Verschieben nach",
|
||||
"remove-section": "Entfernen"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"dev-by": "Entwickelt von",
|
||||
"licensed-under": "Lizensiert unter",
|
||||
"get-the": "Hole dir den",
|
||||
"source-code": "Quellcode"
|
||||
},
|
||||
"interactive-editor": {
|
||||
"menu": {
|
||||
"start-editing-tooltip": "Interaktiven Editor starten",
|
||||
"edit-site-data-subheading": "Seiteninformationen bearbeiten",
|
||||
"edit-page-info-btn": "Seiteninformationen bearbeiten",
|
||||
"edit-page-info-tooltip": "Applikationstitel, Beschreibung, Nav. links, Fußzeile, etc.",
|
||||
"edit-app-config-btn": "Applikationskonfiguration bearbeiten",
|
||||
"edit-app-config-tooltip": "Alle anderen Konfigurationsoptionen",
|
||||
"edit-pages-btn": "Seiten bearbeiten",
|
||||
"edit-pages-tooltip": "Hinzufügen oder entfernen von zusätzlichen Ansichten",
|
||||
"config-save-methods-subheading": "Speicheroptionen der Konfiguration",
|
||||
"save-locally-btn": "Lokal speichern",
|
||||
"save-locally-tooltip": "Konfiguration lokal im Browser speichern. Dies hat keinen Einfluss auf die Konfigurationsdatei, aber Änderungen werden nur in diesem Browser gespeichert",
|
||||
"save-disk-btn": "Auf Festplatte speichern",
|
||||
"save-disk-tooltip": "Konfigurationsdatei conf.yml speichern. Dies erzeugt ein Backup und überschreibt dann die existierende Konfigurationsdatei",
|
||||
"export-config-btn": "Konfiguration exportieren",
|
||||
"export-config-tooltip": "Konfiguration anzeigen und exportieren, entweder in eine Datei oder in die Zwischenablage",
|
||||
"cloud-backup-btn": "Cloud-Backup starten",
|
||||
"cloud-backup-tooltip": "Speichert ein verrschlüsseltes Backup in die Cloud",
|
||||
"edit-raw-config-btn": "Konfiguration als Rohdaten bearbeiten",
|
||||
"edit-raw-config-tooltip": "Anzeigen und bearbeiten der Konfiguration als Rohdaten im JSON-Editor",
|
||||
"cancel-changes-btn": "Verwerfen",
|
||||
"cancel-changes-tooltip": "Modifikationen zurücksetzen und Bearbeitungsmodus schließen. Dies hat keinen Einfluss auf die Konfigurationsdatei",
|
||||
"edit-mode-name": "Bearbeitung",
|
||||
"edit-mode-subtitle": "Sie sind im Bearbeitungsmodus",
|
||||
"edit-mode-description": "Das bedeutet, dass Änderungen an der Konfigurationsdatei vorgenommen werden können. Änderungen können vor dem Speichern betrachtet werden.",
|
||||
"save-stage-btn": "Speichern",
|
||||
"cancel-stage-btn": "Abbrechen",
|
||||
"save-locally-warning": "Wenn Sie fortfahren werden die Änderungen nur in Ihrem Browser gespeichert. Um die Konfiguration auf anderen Geräten zu nutzen sollten Sie sie exportieren. Möchten Sie fortfahren?"
|
||||
},
|
||||
"edit-item": {
|
||||
"missing-title-err": "Ein Titel is zwingend notwendig"
|
||||
},
|
||||
"edit-section": {
|
||||
"edit-section-title": "Sektion bearbeiten",
|
||||
"add-section-title": "Neue Sektion hinzufügen",
|
||||
"edit-tooltip": "Klicken zum Bearbeiten oder Rechtsklick für weitere Optionen",
|
||||
"remove-confirm": "Sind Sie sicher, dass sie diese Sektion entfernen möchten? Diese Aktion kann nicht rückgänging gemacht werden."
|
||||
},
|
||||
"edit-app-config": {
|
||||
"warning-msg-title": "Ab hier ist Vorsicht geboten",
|
||||
"warning-msg-l1": "Die folgenden Optionen sind für fortgeschrittene Konfigurationen.",
|
||||
"warning-msg-l2": "Sollten Felder unklar sein, konsultieren Sie die",
|
||||
"warning-msg-docs": "Dokumentation",
|
||||
"warning-msg-l3": "um unbeabsichtigte Folgen zu vermeiden."
|
||||
},
|
||||
"export": {
|
||||
"export-title": "Konfiguration exportieren",
|
||||
"copy-clipboard-btn": "In Zwischenablage kopieren",
|
||||
"copy-clipboard-tooltip": "Applikationskonfiguration als YAML in Zwischenablage kopieren",
|
||||
"download-file-btn": "Datei herunterladen",
|
||||
"download-file-tooltip": "Applikationskonfiguration auf Ihr Gerät herunterladen",
|
||||
"view-title": "Konfiguration anzeigen"
|
||||
}
|
||||
},
|
||||
"widgets": {
|
||||
"general": {
|
||||
"loading": "Lade...",
|
||||
"show-more": "Details",
|
||||
"cpu-details": "Details CPU",
|
||||
"mem-details": "Details Arbeitsspeicher",
|
||||
"show-less": "Weniger anzeigen",
|
||||
"open-link": "Weiterlesen"
|
||||
},
|
||||
"pi-hole": {
|
||||
"status-heading": "Status"
|
||||
},
|
||||
"stat-ping": {
|
||||
"up": "Online",
|
||||
"down": "Offline"
|
||||
},
|
||||
"net-data": {
|
||||
"cpu-chart-title": "CPU Historie",
|
||||
"mem-chart-title": "Speichernutzung",
|
||||
"mem-breakdown-title": "Speichernutzung",
|
||||
"load-chart-title": "Systemlast"
|
||||
},
|
||||
"glances": {
|
||||
"disk-space-free": "Frei",
|
||||
"disk-space-used": "Genutzt",
|
||||
"disk-mount-point": "Mountpunkt",
|
||||
"disk-file-system": "Dateisystem,",
|
||||
"disk-io-read": "Lesen",
|
||||
"disk-io-write": "Schreiben",
|
||||
"system-load-desc": "Prozesse in der Warteschlange, Durchschnitt aller Kerne"
|
||||
},
|
||||
"system-info": {
|
||||
"uptime": "Uptime"
|
||||
},
|
||||
"flight-data": {
|
||||
"arrivals": "Ankünfte",
|
||||
"departures": "Abflüge"
|
||||
},
|
||||
"tfl-status": {
|
||||
"good-service-all": "Guter Service auf allen Leitungen",
|
||||
"good-service-rest": "Guter Service auf allen anderen Leitungen"
|
||||
},
|
||||
"synology-download": {
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"downloaded": "Heruntergeladen",
|
||||
"uploaded": "Hochgeladen",
|
||||
"remaining": "Verbleibend",
|
||||
"up": "Hoch",
|
||||
"down": "Runter"
|
||||
},
|
||||
"gluetun-status": {
|
||||
"vpn-ip": "VPN IP",
|
||||
"country": "Land",
|
||||
"region": "Bundesland",
|
||||
"city": "Stadt",
|
||||
"post-code": "Postleitzahl",
|
||||
"location": "Standort",
|
||||
"timezone": "Zeitzone",
|
||||
"organization": "Organisation"
|
||||
},
|
||||
"nextcloud": {
|
||||
"active": "Aktiv",
|
||||
"and": "und",
|
||||
"applications": "Anwendungen",
|
||||
"available": "Verfügbar",
|
||||
"away": "Abwesend",
|
||||
"cache-full": "CACHE VOLL",
|
||||
"chat-room": "Chatraum",
|
||||
"delete-all": "Alle löschen",
|
||||
"delete-notification": "Benachrichtigung löschen",
|
||||
"disabled": "deaktivert",
|
||||
"disk-quota": "Disk Quota",
|
||||
"disk-space": "Disk Speicherplatz",
|
||||
"dnd": "Nicht stören",
|
||||
"email": "E-Mail",
|
||||
"enabled": "aktiviert",
|
||||
"federated-shares-ucfirst": "Föderierte Freigaben",
|
||||
"federated-shares": "föderierte Freigaben",
|
||||
"files": "Dateien",
|
||||
"free": "frei",
|
||||
"groups": "Gruppen",
|
||||
"hit-rate": "Trefferrate",
|
||||
"hits": "Treffer",
|
||||
"home": "Zuhause",
|
||||
"in": "in",
|
||||
"keys": "Schlüssel",
|
||||
"last-24-hours": "letzte 24 Stunden",
|
||||
"last-5-minutes": "in den letzten 5 Minuten",
|
||||
"last-hour": "in der letzten Stunde",
|
||||
"last-login": "Letzte Anmeldung",
|
||||
"last-restart": "Letzter Neustart",
|
||||
"load-averages": "Systemlast aller CPU-Kerne",
|
||||
"local-shares": "Lokale Freigaben",
|
||||
"local": "lokal",
|
||||
"max-keys": "Maximale Schlüssel",
|
||||
"memory-used": "Speichernutzung",
|
||||
"memory-utilisation": "Speichernutzung",
|
||||
"memory": "Speicher",
|
||||
"misses": "Fehlschläge",
|
||||
"no-notifications": "Keine Benachrichtigungen",
|
||||
"no-pending-updates": "keine ausstehenden Aktualisierungen",
|
||||
"nothing-to-show": "Momentan gibt es hier nichts zu zeigen",
|
||||
"of-which": "welche",
|
||||
"of": "von",
|
||||
"offline": "Offline",
|
||||
"online": "Online",
|
||||
"other": "andere",
|
||||
"overall": "Insgesamt",
|
||||
"private-link": "privater Link",
|
||||
"public-link": "öffentlicher Link",
|
||||
"quota-enabled": "Disk Quota ist {nicht}aktiviert für diesen Benutzer",
|
||||
"received": "empfangen",
|
||||
"scripts": "Skripte",
|
||||
"sent": "gesendet",
|
||||
"started": "gestartet",
|
||||
"storages-by-type": "Speicher nach Typ",
|
||||
"storages": "Speicher",
|
||||
"strings-use": "Strings benutzen",
|
||||
"tasks": "Aufgaben",
|
||||
"total-files": "Dateien gesamt",
|
||||
"total-users": "Benutzer gesamt",
|
||||
"total": "insgesamt",
|
||||
"until": "Bis",
|
||||
"updates-available-for": "Aktualisierungen sind verfügbar für",
|
||||
"updates-available": "Aktualisierungen verfügbar",
|
||||
"used": "benutzt",
|
||||
"user": "Benutzer",
|
||||
"using": "nutzt",
|
||||
"version": "Version",
|
||||
"wasted": "verschwendet"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,8 +63,8 @@
|
||||
"privacy-and-security": "Privacy & Security",
|
||||
"privacy-and-security-l1": "For a break-down of how your data is managed by Dashy, see the",
|
||||
"privacy-and-security-privacy-policy": "Privacy Policy",
|
||||
"app-info.privacy-and-security-advice": "For advise in securing your dashboard, you can reference the",
|
||||
"app-info.privacy-and-security-advice-link": "Management Docs",
|
||||
"privacy-and-security-advice": "For advise in securing your dashboard, you can reference the",
|
||||
"privacy-and-security-advice-link": "Management Docs",
|
||||
"privacy-and-security-security-issue": "If you've found a potential security issue, report it following our",
|
||||
"privacy-and-security-security-policy": "Security Policy",
|
||||
"license": "License",
|
||||
|
||||
@@ -63,8 +63,8 @@
|
||||
"privacy-and-security": "Privacidade e seguridade",
|
||||
"privacy-and-security-l1": "Para obter detalles sobre como os teus datos son xestionados por Dashy, consulta a",
|
||||
"privacy-and-security-privacy-policy": "Política de privacidade",
|
||||
"app-info.privacy-and-security-advice": "Para consellos sobre como asegurar o teu panel de control, consulta os",
|
||||
"app-info.privacy-and-security-advice-link": "Documentos de xestión",
|
||||
"privacy-and-security-advice": "Para consellos sobre como asegurar o teu panel de control, consulta os",
|
||||
"privacy-and-security-advice-link": "Documentos de xestión",
|
||||
"privacy-and-security-security-issue": "Se atopaches un problema de seguridade potencial, informa seguindo a nosa",
|
||||
"privacy-and-security-security-policy": "Política de seguridade",
|
||||
"license": "Licenza",
|
||||
|
||||
@@ -63,8 +63,8 @@
|
||||
"privacy-and-security": "プライバシーとセキュリティー",
|
||||
"privacy-and-security-l1": "Daisyがどのようにあなたのデータを管理するかについての概要は、こちらをご覧ください: ",
|
||||
"privacy-and-security-privacy-policy": "プライバシーポリシー",
|
||||
"app-info.privacy-and-security-advice": "ダッシュボードを安全に保つためのアドバイスは、こちらを参照してください: ",
|
||||
"app-info.privacy-and-security-advice-link": "管理ドキュメント",
|
||||
"privacy-and-security-advice": "ダッシュボードを安全に保つためのアドバイスは、こちらを参照してください: ",
|
||||
"privacy-and-security-advice-link": "管理ドキュメント",
|
||||
"privacy-and-security-security-issue": "セキュリティー問題の可能性のある事象を見つけた場合、セキュリティーポリシーに従ってください: ",
|
||||
"privacy-and-security-security-policy": "セキュリティーポリシー",
|
||||
"license": "ライセンス",
|
||||
|
||||
448
src/assets/locales/ro.json
Normal file
448
src/assets/locales/ro.json
Normal file
@@ -0,0 +1,448 @@
|
||||
{
|
||||
"home": {
|
||||
"no-results": "Niciun rezultat găsit",
|
||||
"no-data": "Nicio dată configurată",
|
||||
"no-items-section": "Niciun element de afișat încă"
|
||||
},
|
||||
"search": {
|
||||
"search-label": "Caută",
|
||||
"search-placeholder": "Începeți să tastați pentru a filtra",
|
||||
"clear-search-tooltip": "Șterge căutarea",
|
||||
"enter-to-search-web": "Apasă enter pentru a căuta pe web"
|
||||
},
|
||||
"splash-screen": {
|
||||
"loading": "Încărcare"
|
||||
},
|
||||
"login": {
|
||||
"title": "Dashy",
|
||||
"guest-label": "Acces Vizitator",
|
||||
"username-label": "Nume utilizator",
|
||||
"password-label": "Parolă",
|
||||
"login-button": "Autentificare",
|
||||
"remember-me-label": "Ține-mă minte pentru",
|
||||
"remember-me-never": "Niciodată",
|
||||
"remember-me-hour": "4 Ore",
|
||||
"remember-me-day": "1 Zi",
|
||||
"remember-me-week": "1 Săptămână",
|
||||
"remember-me-long-time": "O perioadă lungă",
|
||||
"error-missing-username": "Lipsește numele de utilizator",
|
||||
"error-missing-password": "Lipsește parola",
|
||||
"error-incorrect-username": "Utilizator negăsit",
|
||||
"error-incorrect-password": "Parolă incorectă",
|
||||
"success-message": "Autentificare...",
|
||||
"logout-message": "Deconectat",
|
||||
"already-logged-in-title": "Deja autentificat",
|
||||
"already-logged-in-text": "Ești autentificat ca",
|
||||
"proceed-to-dashboard": "Continuă către Tabloul de bord",
|
||||
"log-out-button": "Deconectare",
|
||||
"proceed-guest-button": "Continuă ca Vizitator",
|
||||
"guest-intro-1": "Această instanță are acces pentru vizitatori activat.",
|
||||
"guest-intro-2": "Vizitatorii au acces doar pentru vizualizare la tablourile de bord, deci nu pot scrie modificări pe disc.",
|
||||
"error": "Eroare",
|
||||
"error-no-user-configured": "Autentificarea nu este activată, sau nu au fost configurați utilizatori",
|
||||
"error-go-home-button": "Mergi la Pagina Principală",
|
||||
"logged-in-guest": "Autentificat ca Vizitator, Redirecționare...",
|
||||
"error-guest-access": "Acces Vizitator Interzis"
|
||||
},
|
||||
"app-info": {
|
||||
"title": "Informații Aplicație",
|
||||
"error-log": "Jurnal Erori",
|
||||
"no-errors": "Nicio eroare recentă detectată",
|
||||
"help-support": "Ajutor & Suport",
|
||||
"help-support-description" : "Pentru suport în utilizarea sau configurarea Dashy, consultați",
|
||||
"help-support-discussions": "Discuțiile",
|
||||
"support-dashy": "Suport pentru Dashy",
|
||||
"support-dashy-description": "Pentru moduri în care poți contribui, verificați",
|
||||
"support-dashy-link": "Pagina de Contribuții",
|
||||
"report-bug": "Raportează o Eroare",
|
||||
"report-bug-description": "Dacă crezi că ai găsit o eroare, atunci te rog",
|
||||
"report-bug-link": "deschide o Problemă",
|
||||
"more-info": "Mai Multe Informații",
|
||||
"source": "Sursă",
|
||||
"documentation": "Documentație",
|
||||
"privacy-and-security": "Confidențialitate & Securitate",
|
||||
"privacy-and-security-l1": "Pentru o descompunere a modului în care datele tale sunt gestionate de Dashy, consultați",
|
||||
"privacy-and-security-privacy-policy": "Politica de Confidențialitate",
|
||||
"privacy-and-security-advice": "Pentru sfaturi în securizarea tabloului tău de bord, poți consulta",
|
||||
"privacy-and-security-advice-link": "Documentele de Management",
|
||||
"privacy-and-security-security-issue": "Dacă ați descoperit o potențială problemă de securitate, raportați-o urmând",
|
||||
"privacy-and-security-security-policy": "Politica de Securitate",
|
||||
"license": "Licență",
|
||||
"license-under": "Licențiat sub",
|
||||
"licence-third-party": "Pentru licențele modulelor terțe părți, vă rugăm să consultați",
|
||||
"licence-third-party-link": "Legal",
|
||||
"list-contributors": "Pentru lista completă a contribuitorilor și mulțumiri, vedeți",
|
||||
"list-contributors-link": "Credite",
|
||||
"version": "Versiune"
|
||||
},
|
||||
"config": {
|
||||
"main-tab": "Meniu Principal",
|
||||
"view-config-tab": "Vizualizare Configurație",
|
||||
"edit-config-tab": "Editare Configurație",
|
||||
"custom-css-tab": "Stiluri Personalizate",
|
||||
"heading": "Opțiuni de Configurare",
|
||||
"download-config-button": "Vizualizați / Exportați Configurația",
|
||||
"edit-config-button": "Editare Configurație",
|
||||
"edit-css-button": "Editare CSS Personalizat",
|
||||
"cloud-sync-button": "Activează Sincronizarea în Cloud",
|
||||
"edit-cloud-sync-button": "Editare Sincronizare în Cloud",
|
||||
"rebuild-app-button": "Reconstruire Aplicație",
|
||||
"change-language-button": "Schimbă Limba Aplicației",
|
||||
"reset-settings-button": "Resetează Setările Locale",
|
||||
"disabled-note": "Unele caracteristici de configurare au fost dezactivate de administratorul tău",
|
||||
"small-screen-note": "Utilizați un ecran foarte mic, și unele ecrane din acest meniu s-ar putea să nu fie optimale",
|
||||
"app-info-button": "Informații Aplicație",
|
||||
"backup-note": "Este recomandat să faceți o copie de siguranță a configurației înainte de a face modificări.",
|
||||
"reset-config-msg-l1": "Aceasta va elimina toate setările utilizatorilor din stocarea locală, dar nu va afecta fișierul 'conf.yml'.",
|
||||
"reset-config-msg-l2": "Ar trebui să faceți mai întâi o copie de siguranță a oricăror modificări pe care le-ați făcut local, dacă doriți să le utilizați în viitor.",
|
||||
"reset-config-msg-l3": "Sunteți sigur că doriți să continuați?",
|
||||
"data-cleared-msg": "Datele au fost șterse cu succes",
|
||||
"actions-label": "Acțiuni",
|
||||
"copy-config-label": "Copiază Configurația",
|
||||
"data-copied-msg": "Configurația a fost copiată în clipboard",
|
||||
"reset-config-label": "Resetează Configurația",
|
||||
"css-save-btn": "Salvează Modificările",
|
||||
"css-note-label": "Notă",
|
||||
"css-note-l1": "Va trebui să reîmprospătați pagina pentru ca modificările să aibă efect.",
|
||||
"css-note-l2": "Suprascrierile de stiluri sunt stocate doar local, deci este recomandat să faceți o copie a CSS-ului dvs.",
|
||||
"css-note-l3": "Pentru a elimina toate stilurile personalizate, ștergeți conținutul și apăsați 'Salvează Modificările'",
|
||||
"custom-css": {
|
||||
"title": "CSS Personalizat",
|
||||
"base-theme": "Tema de Bază"
|
||||
}
|
||||
},
|
||||
"alternate-views": {
|
||||
"alternate-view-heading": "Schimbă Vederea",
|
||||
"default": "Implicit",
|
||||
"workspace": "Spațiu de Lucru",
|
||||
"minimal": "Minimal"
|
||||
},
|
||||
"settings": {
|
||||
"theme-label": "Temă",
|
||||
"layout-label": "Aspect",
|
||||
"layout-auto": "Automat",
|
||||
"layout-horizontal": "Orizontal",
|
||||
"layout-vertical": "Vertical",
|
||||
"item-size-label": "Dimensiune Element",
|
||||
"item-size-small": "Mic",
|
||||
"item-size-medium": "Mediu",
|
||||
"item-size-large": "Mare",
|
||||
"config-launcher-label": "Config",
|
||||
"config-launcher-tooltip": "Actualizează Configurația",
|
||||
"sign-out-tooltip": "Deconectare",
|
||||
"sign-in-tooltip": "Conectare",
|
||||
"sign-in-welcome": "Bună {username}!",
|
||||
"hide": "Ascunde",
|
||||
"open": "Deschide"
|
||||
},
|
||||
"updates": {
|
||||
"app-version-note": "Versiune Dashy",
|
||||
"up-to-date": "Actualizat",
|
||||
"out-of-date": "Actualizare Disponibilă",
|
||||
"unsupported-version-l1": "Utilizați o versiune nesuportată de Dashy",
|
||||
"unsupported-version-l2": "Pentru cea mai bună experiență și patch-uri de securitate recente, vă rugăm să actualizați la"
|
||||
},
|
||||
"language-switcher": {
|
||||
"title": "Schimbă Limba Aplicației",
|
||||
"dropdown-label": "Selectați o Limbă",
|
||||
"save-button": "Salvează",
|
||||
"success-msg": "Limba Actualizată la"
|
||||
},
|
||||
"theme-maker": {
|
||||
"title": "Configurator de Temă",
|
||||
"export-button": "Exportă Variabile Personalizate",
|
||||
"reset-button": "Resetează Stilurile pentru",
|
||||
"show-all-button": "Arată Toate Variabilele",
|
||||
"change-fonts-button": "Schimbă Fonturile",
|
||||
"save-button": "Salvează",
|
||||
"cancel-button": "Anulează",
|
||||
"saved-toast": "{theme} Actualizat cu Succes",
|
||||
"copied-toast": "Datele temei pentru {theme} au fost copiate în clipboard",
|
||||
"reset-toast": "Culorile Personalizate pentru {theme} au fost Eliminate"
|
||||
},
|
||||
"config-editor": {
|
||||
"save-location-label": "Locația de Salvare",
|
||||
"location-local-label": "Aplică Local",
|
||||
"location-disk-label": "Scrie Modificările în Fișierul de Configurație",
|
||||
"save-button": "Salvează Modificările",
|
||||
"preview-button": "Previzualizează Modificările",
|
||||
"valid-label": "Configurația este Valabilă",
|
||||
"status-success-msg": "Sarcină Completată",
|
||||
"status-fail-msg": "Sarcină Eșuată",
|
||||
"success-msg-disk": "Fișierul de configurație a fost scris pe disc cu succes",
|
||||
"success-msg-local": "Modificările locale au fost salvate cu succes",
|
||||
"success-note-l1": "Aplicația ar trebui să se reconstruiască automat.",
|
||||
"success-note-l2": "Aceasta poate dura până la un minut.",
|
||||
"success-note-l3": "Va trebui să reîmprospătați pagina pentru ca modificările să aibă efect.",
|
||||
"error-msg-save-mode": "Vă rugăm să selectați un Mod de Salvare: Local sau Fișier",
|
||||
"error-msg-cannot-save": "A apărut o eroare la salvarea configurației",
|
||||
"error-msg-bad-json": "Eroare în JSON, posibil malformat",
|
||||
"warning-msg-validation": "Avertisment de Validare",
|
||||
"not-admin-note": "Nu puteți scrie modificările pe disc deoarece nu sunteți conectat ca administrator"
|
||||
},
|
||||
"app-rebuild": {
|
||||
"title": "Reconstruire Aplicație",
|
||||
"rebuild-note-l1": "O reconstruire este necesară pentru ca modificările scrise în fișierul conf.yml să aibă efect.",
|
||||
"rebuild-note-l2": "Aceasta ar trebui să se întâmple automat, dar dacă nu s-a întâmplat, o puteți declanșa manual aici.",
|
||||
"rebuild-note-l3": "Aceasta nu este necesară pentru modificările stocate local.",
|
||||
"rebuild-button": "Începeți Reconstruirea",
|
||||
"rebuilding-status-1": "Se reconstruiește...",
|
||||
"rebuilding-status-2": "Aceasta poate dura câteva minute",
|
||||
"error-permission": "Nu aveți permisiunea de a declanșa această acțiune",
|
||||
"success-msg": "Reconstruirea a fost completată cu succes",
|
||||
"fail-msg": "Operațiunea de reconstruire a eșuat",
|
||||
"reload-note": "Este necesară reîncărcarea paginii pentru ca modificările să aibă efect",
|
||||
"reload-button": "Reîncarcă Pagina"
|
||||
},
|
||||
"cloud-sync": {
|
||||
"title": "Backup și Restaurare în Cloud",
|
||||
"intro-l1": "Backup-ul și restaurarea în cloud este o caracteristică opțională, care vă permite să încărcați configurația pe internet și apoi să o restaurați pe orice alt dispozitiv sau instanță de Dashy.",
|
||||
"intro-l2": "Toate datele sunt criptate end-to-end cu AES, folosind parola dumneavoastră ca cheie.",
|
||||
"intro-l3": "Pentru mai multe informații, vă rugăm să consultați",
|
||||
"intro-docs": "documentația",
|
||||
"backup-title-setup": "Creați un Backup",
|
||||
"backup-title-update": "Actualizați Backup-ul",
|
||||
"password-label-setup": "Alegeți o Parolă",
|
||||
"password-label-update": "Introduceți Parola",
|
||||
"backup-button-setup": "Backup",
|
||||
"backup-button-update": "Actualizați Backup-ul",
|
||||
"backup-id-label": "ID-ul Dvs. de Backup",
|
||||
"backup-id-note": "Acesta este utilizat pentru a restaura din backup-uri mai târziu. Deci păstrați-l, împreună cu parola dvs., într-un loc sigur.",
|
||||
"restore-title": "Restaurare Backup",
|
||||
"restore-id-label": "ID de Restaurare",
|
||||
"restore-password-label": "Parola",
|
||||
"restore-button": "Restaurare",
|
||||
"backup-missing-password": "Lipsește Parola",
|
||||
"backup-error-unknown": "Imposibil de procesat solicitarea",
|
||||
"backup-error-password": "Parolă incorectă. Vă rugăm să introduceți parola actuală.",
|
||||
"backup-success-msg": "Finalizat cu Succes",
|
||||
"restore-success-msg": "Configurația a fost Restaurată cu Succes"
|
||||
},
|
||||
"menu": {
|
||||
"open-section-title": "Deschide În",
|
||||
"sametab": "Tab Curent",
|
||||
"newtab": "Tab Nou",
|
||||
"modal": "Modal Pop-Up",
|
||||
"workspace": "Vizualizare Spațiu de Lucru",
|
||||
"options-section-title": "Opțiuni",
|
||||
"edit-item": "Editare",
|
||||
"move-item": "Copiază sau Mută",
|
||||
"remove-item": "Șterge"
|
||||
},
|
||||
"context-menus": {
|
||||
"item": {
|
||||
"open-section-title": "Deschide În",
|
||||
"sametab": "Tab Curent",
|
||||
"newtab": "Tab Nou",
|
||||
"modal": "Modal Pop-Up",
|
||||
"workspace": "Vizualizare Spațiu de Lucru",
|
||||
"clipboard": "Copiază în Clipboard",
|
||||
"options-section-title": "Opțiuni",
|
||||
"edit-item": "Editare",
|
||||
"move-item": "Copiază sau Mută",
|
||||
"remove-item": "Șterge",
|
||||
"copied-toast": "URL-ul a fost copiat în clipboard"
|
||||
},
|
||||
"section": {
|
||||
"open-section": "Deschide Secțiunea",
|
||||
"edit-section": "Editare",
|
||||
"expand-collapse": "Extinde / Colapsează",
|
||||
"move-section": "Mută La",
|
||||
"remove-section": "Șterge"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"dev-by": "Dezvoltat de",
|
||||
"licensed-under": "Licențiat sub",
|
||||
"get-the": "Obțineți",
|
||||
"source-code": "Codul Sursă"
|
||||
},
|
||||
"interactive-editor": {
|
||||
"menu": {
|
||||
"start-editing-tooltip": "Intră în Editorul Interactiv",
|
||||
"edit-site-data-subheading": "Editează Datele Site-ului",
|
||||
"edit-page-info-btn": "Editează Informațiile Paginii",
|
||||
"edit-page-info-tooltip": "Titlul aplicației, descriere, link-uri de navigare, textul din footer, etc.",
|
||||
"edit-app-config-btn": "Editează Configurația Aplicației",
|
||||
"edit-app-config-tooltip": "Toate celelalte opțiuni de configurare a aplicației",
|
||||
"edit-pages-btn": "Editează Paginile",
|
||||
"edit-pages-tooltip": "Adaugă sau elimină vizualizări suplimentare",
|
||||
"config-save-methods-subheading": "Opțiuni de Salvare a Configurației",
|
||||
"save-locally-btn": "Salvează Local",
|
||||
"save-locally-tooltip": "Salvează configurația local, în stocarea browserului. Aceasta nu va afecta fișierul tău de configurare, dar schimbările vor fi salvate doar pe acest dispozitiv",
|
||||
"save-disk-btn": "Salvează pe Disc",
|
||||
"save-disk-tooltip": "Salvează configurația în fișierul conf.yml de pe disc. Aceasta va face un backup și apoi va suprascrie configurația existentă",
|
||||
"export-config-btn": "Exportă Configurația",
|
||||
"export-config-tooltip": "Vizualizează și exportă noua configurație, fie într-un fișier, fie în clipboard",
|
||||
"cloud-backup-btn": "Backup în Cloud",
|
||||
"cloud-backup-tooltip": "Salvează un backup criptat al configurației în cloud",
|
||||
"edit-raw-config-btn": "Editează Configurația Brută",
|
||||
"edit-raw-config-tooltip": "Vizualizează și modifică configurația brută prin editorul JSON",
|
||||
"cancel-changes-btn": "Anulează Modificările",
|
||||
"cancel-changes-tooltip": "Resetează modificările curente și ieși din Modul de Editare. Aceasta nu va afecta configurația salvată",
|
||||
"edit-mode-name": "Mod de Editare",
|
||||
"edit-mode-subtitle": "Ești în Modul de Editare",
|
||||
"edit-mode-description": "Aceasta înseamnă că poți face modificări la configurația ta și să previzualizezi rezultatele, dar până nu salvezi, niciuna dintre modificările tale nu va fi păstrată.",
|
||||
"save-stage-btn": "Salvează",
|
||||
"cancel-stage-btn": "Anulează",
|
||||
"save-locally-warning": "Dacă vei continua, modificările vor fi salvate doar în browserul tău. Ar trebui să exporți o copie a configurației tale pentru utilizare pe alte mașini. Dorești să continui?"
|
||||
},
|
||||
"edit-item": {
|
||||
"missing-title-err": "Este necesar un titlu pentru element"
|
||||
},
|
||||
"edit-section": {
|
||||
"edit-section-title": "Editează Secțiunea",
|
||||
"add-section-title": "Adaugă o Secțiune Nouă",
|
||||
"edit-tooltip": "Clic pentru a edita, sau clic dreapta pentru mai multe opțiuni",
|
||||
"remove-confirm": "Ești sigur că vrei să elimini această secțiune? Această acțiune poate fi anulată mai târziu."
|
||||
},
|
||||
"edit-app-config": {
|
||||
"warning-msg-title": "Procedează cu Atenție",
|
||||
"warning-msg-l1": "Următoarele opțiuni sunt pentru configurarea avansată a aplicației.",
|
||||
"warning-msg-l2": "Dacă nu ești sigur în legătură cu oricare dintre câmpuri, te rog să consulți",
|
||||
"warning-msg-docs": "documentația",
|
||||
"warning-msg-l3": "pentru a evita consecințele nedorite."
|
||||
},
|
||||
"export": {
|
||||
"export-title": "Exportă Configurația",
|
||||
"copy-clipboard-btn": "Copiază în Clipboard",
|
||||
"copy-clipboard-tooltip": "Copiază toată configurația aplicației în clipboard-ul sistemului, în format YAML",
|
||||
"download-file-btn": "Descarcă ca Fișier",
|
||||
"download-file-tooltip": "Descarcă toată configurația aplicației pe dispozitivul tău, într-un fișier YAML",
|
||||
"view-title": "Vizualizează Configurația"
|
||||
}
|
||||
},
|
||||
"widgets": {
|
||||
"general": {
|
||||
"loading": "Se încarcă...",
|
||||
"show-more": "Extinde Detaliile",
|
||||
"cpu-details": "Detalii CPU",
|
||||
"mem-details": "Detalii Memorie",
|
||||
"show-less": "Arată Mai Puțin",
|
||||
"open-link": "Continuă Citirea"
|
||||
},
|
||||
"pi-hole": {
|
||||
"status-heading": "Stare"
|
||||
},
|
||||
"stat-ping": {
|
||||
"up": "Online",
|
||||
"down": "Offline"
|
||||
},
|
||||
"net-data": {
|
||||
"cpu-chart-title": "Istoric CPU",
|
||||
"mem-chart-title": "Utilizare Memorie",
|
||||
"mem-breakdown-title": "Detalii Memorie",
|
||||
"load-chart-title": "Încărcare Sistem"
|
||||
},
|
||||
"glances": {
|
||||
"disk-space-free": "Liber",
|
||||
"disk-space-used": "Utilizat",
|
||||
"disk-mount-point": "Punct de Montare",
|
||||
"disk-file-system": "Sistem de Fișiere",
|
||||
"disk-io-read": "Citire",
|
||||
"disk-io-write": "Scriere",
|
||||
"system-load-desc": "Numărul de procese așteptând în coada de execuție, mediat pe toate nucleele"
|
||||
},
|
||||
"system-info": {
|
||||
"uptime": "Timp de Funcționare"
|
||||
},
|
||||
"flight-data": {
|
||||
"arrivals": "Sosiri",
|
||||
"departures": "Plecări"
|
||||
},
|
||||
"tfl-status": {
|
||||
"good-service-all": "Servicii Bune pe Toate Liniile",
|
||||
"good-service-rest": "Servicii Bune pe Celelalte Linii"
|
||||
},
|
||||
"synology-download": {
|
||||
"download": "Descărcare",
|
||||
"upload": "Încărcare",
|
||||
"downloaded": "Descărcat",
|
||||
"uploaded": "Încărcat",
|
||||
"remaining": "Rămas",
|
||||
"up": "Sus",
|
||||
"down": "Jos"
|
||||
},
|
||||
"gluetun-status": {
|
||||
"vpn-ip": "IP VPN",
|
||||
"country": "Țară",
|
||||
"region": "Regiune",
|
||||
"city": "Oraș",
|
||||
"post-code": "Cod Poștal",
|
||||
"location": "Locație",
|
||||
"timezone": "Fus Orar",
|
||||
"organization": "Organizație"
|
||||
},
|
||||
"nextcloud": {
|
||||
"active": "activ",
|
||||
"and": "și",
|
||||
"applications": "aplicații",
|
||||
"available": "disponibil",
|
||||
"away": "Plecat",
|
||||
"cache-full": "CACHE PLIN",
|
||||
"chat-room": "camera de chat",
|
||||
"delete-all": "Șterge tot",
|
||||
"delete-notification": "Șterge notificarea",
|
||||
"disabled": "dezactivat",
|
||||
"disk-quota": "Cotă de Disc",
|
||||
"disk-space": "Spațiu pe Disc",
|
||||
"dnd": "Nu Deranja",
|
||||
"email": "email",
|
||||
"enabled": "activat",
|
||||
"federated-shares-ucfirst": "Partajări Federate",
|
||||
"federated-shares": "partajări federate",
|
||||
"files": "fișier{plural}",
|
||||
"free": "liber",
|
||||
"groups": "grupuri",
|
||||
"hit-rate": "rata de accesare",
|
||||
"hits": "accesări",
|
||||
"home": "acasă",
|
||||
"in": "în",
|
||||
"keys": "chei",
|
||||
"last-24-hours": "ultimele 24 de ore",
|
||||
"last-5-minutes": "în ultimele 5 minute",
|
||||
"last-hour": "în ultima oră",
|
||||
"last-login": "Ultima autentificare",
|
||||
"last-restart": "Ultimul restart",
|
||||
"load-averages": "Mediile de Încărcare pe toate nucleele CPU",
|
||||
"local-shares": "Partajări Locale",
|
||||
"local": "local",
|
||||
"max-keys": "chei maxime",
|
||||
"memory-used": "memorie utilizată",
|
||||
"memory-utilisation": "utilizarea memoriei",
|
||||
"memory": "memorie",
|
||||
"misses": "rateuri",
|
||||
"no-notifications": "Fără notificări",
|
||||
"no-pending-updates": "fără actualizări în așteptare",
|
||||
"nothing-to-show": "Nimic de afișat aici în acest moment",
|
||||
"of-which": "din care",
|
||||
"of": "din",
|
||||
"offline": "Deconectat",
|
||||
"online": "Conectat",
|
||||
"other": "alt",
|
||||
"overall": "În total",
|
||||
"private-link": "link privat",
|
||||
"public-link": "link public",
|
||||
"quota-enabled": "Cota de Disc este {not}activată pentru acest utilizator",
|
||||
"received": "primit",
|
||||
"scripts": "scripturi",
|
||||
"sent": "trimis",
|
||||
"started": "Început",
|
||||
"storages-by-type": "Stocări pe tip",
|
||||
"storages": "stocare{plural}",
|
||||
"strings-use": "utilizare șiruri",
|
||||
"tasks": "Sarcini",
|
||||
"total-files": "total fișiere",
|
||||
"total-users": "total utilizatori",
|
||||
"total": "total",
|
||||
"until": "Până la",
|
||||
"updates-available-for": "Actualizări disponibile pentru",
|
||||
"updates-available": "actualizare{plural} disponibilă",
|
||||
"used": "utilizat",
|
||||
"user": "utilizator",
|
||||
"using": "utilizând",
|
||||
"version": "versiune",
|
||||
"wasted": "pierdut"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,8 +63,8 @@
|
||||
"privacy-and-security": "Конфіденційність та безпека",
|
||||
"privacy-and-security-l1": "Для детальної інформації про те, як Dashy керує вашими даними, див.",
|
||||
"privacy-and-security-privacy-policy": "Політика конфіденційності",
|
||||
"app-info.privacy-and-security-advice": "Щоб отримати поради щодо захисту вашої інформаційної панелі, ви можете звернутися до розділу",
|
||||
"app-info.privacy-and-security-advice-link": "Документи керування",
|
||||
"privacy-and-security-advice": "Щоб отримати поради щодо захисту вашої інформаційної панелі, ви можете звернутися до розділу",
|
||||
"privacy-and-security-advice-link": "Документи керування",
|
||||
"privacy-and-security-security-issue": "Якщо ви виявили потенційну проблему з безпекою, повідомте про це до розділу",
|
||||
"privacy-and-security-security-policy": "Політика безпеки",
|
||||
"license": "Ліцензія",
|
||||
|
||||
@@ -63,8 +63,8 @@
|
||||
"privacy-and-security": "隐私与安全",
|
||||
"privacy-and-security-l1": "关于Dashy如何管理您的数据的详细信息,请参阅",
|
||||
"privacy-and-security-privacy-policy": "隐私政策",
|
||||
"app-info.privacy-and-security-advice": "如果您需要保护您的仪表盘,请参考",
|
||||
"app-info.privacy-and-security-advice-link": "管理文档",
|
||||
"privacy-and-security-advice": "如果您需要保护您的仪表盘,请参考",
|
||||
"privacy-and-security-advice-link": "管理文档",
|
||||
"privacy-and-security-security-issue": "如果您发现潜在的安全问题,请遵循我们的",
|
||||
"privacy-and-security-security-policy": "安全政策",
|
||||
"license": "许可证",
|
||||
|
||||
@@ -63,8 +63,8 @@
|
||||
"privacy-and-security": "隱私權和安全性",
|
||||
"privacy-and-security-l1": "若要了解 Dashy 是如何管理您的資料,請參閱",
|
||||
"privacy-and-security-privacy-policy": "隱私權政策",
|
||||
"app-info.privacy-and-security-advice": "關於提升儀錶板安全性的建議,請參閱",
|
||||
"app-info.privacy-and-security-advice-link": "管理文件",
|
||||
"privacy-and-security-advice": "關於提升儀錶板安全性的建議,請參閱",
|
||||
"privacy-and-security-advice-link": "管理文件",
|
||||
"privacy-and-security-security-issue": "若您找到潛在的安全問題,請回報,並遵照我們的",
|
||||
"privacy-and-security-security-policy": "安全政策",
|
||||
"license": "授權條款",
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<!-- License -->
|
||||
<h3>{{ $t('app-info.license') }}</h3>
|
||||
{{ $t('app-info.license-under') }} <a href="https://github.com/Lissy93/dashy/blob/master/LICENSE">MIT X11</a>.
|
||||
Copyright <a href="https://aliciasykes.com">Alicia Sykes</a> © 2021.<br>
|
||||
Copyright <a href="https://aliciasykes.com">Alicia Sykes</a> © {{new Date().getFullYear()}}.<br>
|
||||
{{ $t('app-info.licence-third-party') }} <a href="https://github.com/Lissy93/dashy/blob/master/.github/LEGAL.md">{{ $t('app-info.licence-third-party-link') }}</a>.<br>
|
||||
{{ $t('app-info.list-contributors') }} <a href="https://github.com/Lissy93/dashy/blob/master/docs/credits.md">{{ $t('app-info.list-contributors-link') }}</a>.
|
||||
<!-- App Version -->
|
||||
|
||||
@@ -155,12 +155,23 @@ export default {
|
||||
},
|
||||
/* When restored data is revieved, then save to local storage, and apply it in state */
|
||||
applyRestoredData(config, backupId) {
|
||||
// Store restored data in local storage
|
||||
localStorage.setItem(localStorageKeys.CONF_SECTIONS, JSON.stringify(config.sections));
|
||||
localStorage.setItem(localStorageKeys.APP_CONFIG, JSON.stringify(config.appConfig));
|
||||
localStorage.setItem(localStorageKeys.PAGE_INFO, JSON.stringify(config.pageInfo));
|
||||
if (config.appConfig.theme) {
|
||||
localStorage.setItem(localStorageKeys.THEME, config.appConfig.theme);
|
||||
const isSubPage = !!this.$store.state.currentConfigInfo.confId;
|
||||
if (isSubPage) { // Apply to sub-page only
|
||||
const subConfigId = this.$store.state.currentConfigInfo.confId;
|
||||
const sectionStorageKey = `${localStorageKeys.CONF_SECTIONS}-${subConfigId}`;
|
||||
const pageInfoStorageKey = `${localStorageKeys.PAGE_INFO}-${subConfigId}`;
|
||||
const themeStoreKey = `${localStorageKeys.THEME}-${subConfigId}`;
|
||||
localStorage.setItem(sectionStorageKey, JSON.stringify(config.sections));
|
||||
localStorage.setItem(pageInfoStorageKey, JSON.stringify(config.pageInfo));
|
||||
localStorage.setItem(themeStoreKey, config.appConfig.theme);
|
||||
} else { // Apply to main config
|
||||
localStorage.setItem(localStorageKeys.CONF_SECTIONS, JSON.stringify(config.sections));
|
||||
localStorage.setItem(localStorageKeys.APP_CONFIG, JSON.stringify(config.appConfig));
|
||||
localStorage.setItem(localStorageKeys.PAGE_INFO, JSON.stringify(config.pageInfo));
|
||||
localStorage.setItem(localStorageKeys.CONF_PAGES, JSON.stringify(config.pages || []));
|
||||
if (config.appConfig.theme) {
|
||||
localStorage.setItem(localStorageKeys.THEME, config.appConfig.theme);
|
||||
}
|
||||
}
|
||||
// Save hashed token in local storage
|
||||
this.setBackupIdLocally(backupId, this.restorePassword);
|
||||
|
||||
@@ -115,7 +115,9 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.jsonData = this.config;
|
||||
const jsonData = { ...this.config };
|
||||
jsonData.sections = jsonData.sections.map(({ filteredItems, ...section }) => section);
|
||||
this.jsonData = jsonData;
|
||||
if (!this.allowWriteToDisk) this.saveMode = 'local';
|
||||
},
|
||||
methods: {
|
||||
@@ -141,7 +143,11 @@ export default {
|
||||
this.$modal.hide(modalNames.CONF_EDITOR);
|
||||
},
|
||||
writeToDisk() {
|
||||
this.writeConfigToDisk(this.config);
|
||||
const newData = this.jsonData;
|
||||
this.writeConfigToDisk(newData);
|
||||
// this.$store.commit(StoreKeys.SET_APP_CONFIG, newData.appConfig);
|
||||
this.$store.commit(StoreKeys.SET_PAGE_INFO, newData.pageInfo);
|
||||
this.$store.commit(StoreKeys.SET_SECTIONS, newData.sections);
|
||||
},
|
||||
saveLocally() {
|
||||
const msg = this.$t('interactive-editor.menu.save-locally-warning');
|
||||
|
||||
@@ -94,6 +94,7 @@ export default {
|
||||
const raw = rawAppConfig;
|
||||
const isEmptyObject = (obj) => (typeof obj === 'object' && Object.keys(obj).length === 0);
|
||||
const isEmpty = (value) => (value === undefined || isEmptyObject(value));
|
||||
|
||||
// Delete empty values
|
||||
Object.keys(raw).forEach(key => {
|
||||
if (isEmpty(raw[key])) delete raw[key];
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
:target="anchorTarget"
|
||||
:class="`item ${makeClassList}`"
|
||||
v-tooltip="getTooltipOptions()"
|
||||
rel="noopener noreferrer" tabindex="0"
|
||||
:rel="`${item.rel || 'noopener noreferrer'}`"
|
||||
tabindex="0"
|
||||
:id="`link-${item.id}`"
|
||||
:style="customStyle"
|
||||
>
|
||||
@@ -258,6 +259,9 @@ export default {
|
||||
overflow: hidden;
|
||||
span.text {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,8 +74,8 @@
|
||||
</div>
|
||||
<!-- Modal for opening in modal view -->
|
||||
<IframeModal
|
||||
:ref="`iframeModal`"
|
||||
:name="`iframeModal`"
|
||||
:ref="`iframeModal-${groupId}`"
|
||||
:name="`iframeModal-${groupId}`"
|
||||
@closed="$emit('itemClicked')"
|
||||
/>
|
||||
<!-- Edit item menu -->
|
||||
@@ -213,7 +213,7 @@ export default {
|
||||
methods: {
|
||||
/* Opens the iframe modal */
|
||||
triggerModal(url) {
|
||||
this.$refs.iframeModal.show(url);
|
||||
this.$refs[`iframeModal-${this.groupId}`].show(url);
|
||||
},
|
||||
/* Sorts items alphabetically using the title attribute */
|
||||
sortAlphabetically(items) {
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
<template>
|
||||
<!-- User Footer -->
|
||||
<footer v-if="text && text !== '' && visible" v-html="text"></footer>
|
||||
<!-- Default Footer -->
|
||||
<footer v-else-if="visible">
|
||||
<span v-if="$store.state.currentConfigInfo" class="path-to-config">
|
||||
Using: {{ $store.state.currentConfigInfo.confPath }}
|
||||
</span>
|
||||
<span>
|
||||
{{ $t('footer.dev-by') }} <a :href="authorUrl">{{authorName}}</a>.
|
||||
{{ $t('footer.licensed-under') }} <a :href="licenseUrl">{{license}}</a>
|
||||
{{ showCopyright? '©': '' }} {{date}}.
|
||||
{{ $t('footer.get-the') }} <a :href="repoUrl">{{ $t('footer.source-code') }}</a>.
|
||||
<footer v-if="visible">
|
||||
<!-- User-defined footer -->
|
||||
<span v-if="text" v-html="text"></span>
|
||||
<!-- Default footer -->
|
||||
<span v-else>
|
||||
<a :href="defaultInfo.projectUrl">Dashy</a> is free & open source
|
||||
- licensed under <a :href="defaultInfo.licenseUrl">{{defaultInfo.license}}</a>,
|
||||
© <a :href="defaultInfo.authorUrl">{{defaultInfo.authorName}}</a> {{defaultInfo.date}}.
|
||||
Get support on GitHub, at <a :href="defaultInfo.repoUrl">{{defaultInfo.repoName}}</a>.
|
||||
</span>
|
||||
</footer>
|
||||
</template>
|
||||
@@ -23,13 +20,20 @@ export default {
|
||||
name: 'Footer',
|
||||
props: {
|
||||
text: String,
|
||||
authorName: { type: String, default: 'Alicia Sykes' },
|
||||
authorUrl: { type: String, default: 'https://aliciasykes.com' },
|
||||
license: { type: String, default: 'MIT' },
|
||||
licenseUrl: { type: String, default: 'https://gist.github.com/Lissy93/143d2ee01ccc5c052a17' },
|
||||
date: { type: String, default: `${new Date().getFullYear()}` },
|
||||
showCopyright: { type: Boolean, default: true },
|
||||
repoUrl: { type: String, default: 'https://github.com/lissy93/dashy' },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultInfo: {
|
||||
authorName: 'Alicia Sykes',
|
||||
authorUrl: 'https://as93.net',
|
||||
license: 'MIT',
|
||||
licenseUrl: 'https://gist.github.com/Lissy93/143d2ee01ccc5c052a17',
|
||||
date: `${new Date().getFullYear()}`,
|
||||
repoUrl: 'https://github.com/lissy93/dashy',
|
||||
repoName: 'Lissy93/Dashy',
|
||||
projectUrl: 'https://dashy.to',
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
visible() {
|
||||
@@ -56,7 +60,7 @@ footer {
|
||||
display: none;
|
||||
}
|
||||
span.path-to-config {
|
||||
float: right;
|
||||
float: left;
|
||||
font-size: 0.75rem;
|
||||
margin: 0.1rem 0.5rem 0 0;
|
||||
opacity: var(--dimming-factor);
|
||||
|
||||
@@ -66,7 +66,7 @@ export default {
|
||||
span.subtitle {
|
||||
color: var(--heading-text-color);
|
||||
font-style: italic;
|
||||
text-shadow: 1px 1px 2px #130f23;
|
||||
text-shadow: 1px 1px 2px #130f2347;
|
||||
opacity: var(--dimming-factor);
|
||||
}
|
||||
img.site-logo {
|
||||
|
||||
@@ -95,7 +95,8 @@ export default {
|
||||
},
|
||||
/* If configured, launch specific app when hotkey pressed */
|
||||
handleHotKey(key) {
|
||||
const usersHotKeys = this.getCustomKeyShortcuts();
|
||||
const sections = this.$store.getters.sections || [];
|
||||
const usersHotKeys = this.getCustomKeyShortcuts(sections);
|
||||
usersHotKeys.forEach((hotkey) => {
|
||||
if (hotkey.hotkey === parseInt(key, 10)) {
|
||||
if (hotkey.url) window.open(hotkey.url, '_blank');
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
:value="$store.getters.theme"
|
||||
class="theme-dropdown"
|
||||
:tabindex="-2"
|
||||
@input="themeChanged"
|
||||
@input="themeChangedInUI"
|
||||
/>
|
||||
</div>
|
||||
<IconPalette
|
||||
@@ -28,18 +28,13 @@
|
||||
<script>
|
||||
|
||||
import CustomThemeMaker from '@/components/Settings/CustomThemeMaker';
|
||||
import {
|
||||
LoadExternalTheme,
|
||||
ApplyLocalTheme,
|
||||
ApplyCustomVariables,
|
||||
} from '@/utils/ThemeHelper';
|
||||
import Defaults, { localStorageKeys } from '@/utils/defaults';
|
||||
import Keys from '@/utils/StoreMutations';
|
||||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
import IconPalette from '@/assets/interface-icons/config-color-palette.svg';
|
||||
import ThemingMixin from '@/mixins/ThemingMixin';
|
||||
|
||||
export default {
|
||||
name: 'ThemeSelector',
|
||||
mixins: [ThemingMixin],
|
||||
props: {
|
||||
hidePallete: Boolean,
|
||||
},
|
||||
@@ -47,101 +42,16 @@ export default {
|
||||
CustomThemeMaker,
|
||||
IconPalette,
|
||||
},
|
||||
watch: {
|
||||
/* When theme in VueX store changes, then update theme */
|
||||
themeFromStore(newTheme) {
|
||||
this.selectedTheme = newTheme;
|
||||
this.updateTheme(newTheme);
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedTheme: '',
|
||||
themeConfiguratorOpen: false, // Control the opening of theme config popup
|
||||
themeHelper: new LoadExternalTheme(),
|
||||
ApplyLocalTheme,
|
||||
ApplyCustomVariables,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
/* Get appConfig from store */
|
||||
appConfig() {
|
||||
return this.$store.getters.appConfig;
|
||||
},
|
||||
/* Get users theme from store */
|
||||
themeFromStore() {
|
||||
return this.$store.getters.theme;
|
||||
},
|
||||
/* Combines all theme names (builtin and user defined) together */
|
||||
themeNames: function themeNames() {
|
||||
const externalThemeNames = Object.keys(this.externalThemes);
|
||||
const specialThemes = ['custom'];
|
||||
return [...this.extraThemeNames, ...externalThemeNames,
|
||||
...Defaults.builtInThemes, ...specialThemes];
|
||||
},
|
||||
extraThemeNames() {
|
||||
const userThemes = this.appConfig.cssThemes || [];
|
||||
if (typeof userThemes === 'string') return [userThemes];
|
||||
return userThemes;
|
||||
},
|
||||
/* Returns an array of links to external CSS from the Config */
|
||||
externalThemes() {
|
||||
const availibleThemes = {};
|
||||
if (this.appConfig && this.appConfig.externalStyleSheet) {
|
||||
const externals = this.appConfig.externalStyleSheet;
|
||||
if (Array.isArray(externals)) {
|
||||
externals.forEach((ext, i) => {
|
||||
availibleThemes[`External Stylesheet ${i + 1}`] = ext;
|
||||
});
|
||||
} else if (typeof externals === 'string') {
|
||||
availibleThemes['External Stylesheet'] = this.appConfig.externalStyleSheet;
|
||||
} else {
|
||||
ErrorHandler('External stylesheets must be of type string or string[]');
|
||||
}
|
||||
}
|
||||
// availibleThemes.Default = '#';
|
||||
return availibleThemes;
|
||||
},
|
||||
},
|
||||
computed: {},
|
||||
mounted() {
|
||||
const initialTheme = this.getInitialTheme();
|
||||
this.selectedTheme = initialTheme;
|
||||
// Quicker loading, if the theme is local we can apply it immidiatley
|
||||
if (this.isThemeLocal(initialTheme)) {
|
||||
this.updateTheme(initialTheme);
|
||||
}
|
||||
|
||||
// If it's an external stylesheet, then wait for promise to resolve
|
||||
if (this.externalThemes && Object.entries(this.externalThemes).length > 0) {
|
||||
const added = Object.keys(this.externalThemes).map(
|
||||
name => this.themeHelper.add(name, this.externalThemes[name]),
|
||||
);
|
||||
// Once, added, then apply users initial theme
|
||||
Promise.all(added).then(() => {
|
||||
this.updateTheme(initialTheme);
|
||||
});
|
||||
}
|
||||
this.initializeTheme();
|
||||
},
|
||||
methods: {
|
||||
/* Called when dropdown changed
|
||||
* Updates store, which will in turn update theme through watcher
|
||||
*/
|
||||
themeChanged() {
|
||||
const pageId = this.$store.state.currentConfigInfo?.pageId || null;
|
||||
this.$store.commit(Keys.SET_THEME, { theme: this.selectedTheme, pageId });
|
||||
this.updateTheme(this.selectedTheme);
|
||||
},
|
||||
/* Returns the initial theme */
|
||||
getInitialTheme() {
|
||||
const localTheme = localStorage[localStorageKeys.THEME];
|
||||
if (localTheme && localTheme !== 'undefined') return localTheme;
|
||||
return this.appConfig.theme || Defaults.theme;
|
||||
},
|
||||
/* Determines if a given theme is local / not a custom user stylesheet */
|
||||
isThemeLocal(themeToCheck) {
|
||||
const localThemes = [...Defaults.builtInThemes, ...this.extraThemeNames];
|
||||
return localThemes.includes(themeToCheck);
|
||||
},
|
||||
/* Opens the theme color configurator popup */
|
||||
openThemeConfigurator() {
|
||||
this.$store.commit(Keys.SET_MODAL_OPEN, true);
|
||||
@@ -154,24 +64,6 @@ export default {
|
||||
this.themeConfiguratorOpen = false;
|
||||
}
|
||||
},
|
||||
/* Updates theme. Checks if the new theme is local or external,
|
||||
and calls appropirate updating function. Updates local storage */
|
||||
updateTheme(newTheme) {
|
||||
if (newTheme === 'Default') {
|
||||
this.resetToDefault();
|
||||
this.themeHelper.theme = 'Default';
|
||||
} else if (this.isThemeLocal(newTheme)) {
|
||||
this.ApplyLocalTheme(newTheme);
|
||||
} else {
|
||||
this.themeHelper.theme = newTheme;
|
||||
}
|
||||
this.ApplyCustomVariables(newTheme);
|
||||
// localStorage.setItem(localStorageKeys.THEME, newTheme);
|
||||
},
|
||||
/* Removes any applied themes */
|
||||
resetToDefault() {
|
||||
document.getElementsByTagName('html')[0].removeAttribute('data-theme');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -74,6 +74,7 @@ export default {
|
||||
this.jokeLine2 = data.delivery;
|
||||
} else if (this.jokeType === 'single') {
|
||||
this.jokeLine1 = data.joke;
|
||||
this.jokeLine2 = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -94,7 +94,7 @@ export default {
|
||||
}
|
||||
},
|
||||
processData(data) {
|
||||
this.data = data.data.sort((a, b) => a.vmid > b.vmid);
|
||||
this.data = data.data.sort((a, b) => Number(a.vmid) > Number(b.vmid));
|
||||
if (this.hideTemplates) {
|
||||
this.data = this.data.filter(item => item.template !== 1);
|
||||
}
|
||||
|
||||
242
src/components/Widgets/UptimeKuma.vue
Normal file
242
src/components/Widgets/UptimeKuma.vue
Normal file
@@ -0,0 +1,242 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="monitors">
|
||||
<div v-for="(monitor, index) in monitors" :key="index" class="item-wrapper">
|
||||
<div class="item monitor-row">
|
||||
<div class="title-title"><span class="text">{{ monitor.name }}</span></div>
|
||||
<div class="monitors-container">
|
||||
<div class="status-container">
|
||||
<span class="status-pill" :class="[monitor.statusClass]">{{ monitor.status }}</span>
|
||||
</div>
|
||||
<div class="status-container">
|
||||
<span class="response-time">{{ monitor.responseTime }}ms</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-if="errorMessage">
|
||||
<div class="error-message">
|
||||
<span class="text">{{ errorMessage }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* A simple example which you can use as a template for creating your own widget.
|
||||
* Takes two optional parameters (`text` and `count`), and fetches a list of images
|
||||
* from dummyapis.com, then renders the results to the UI.
|
||||
*/
|
||||
import WidgetMixin from '@/mixins/WidgetMixin';
|
||||
|
||||
export default {
|
||||
mixins: [WidgetMixin],
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
monitors: null,
|
||||
errorMessage: null,
|
||||
errorMessageConstants: {
|
||||
missingApiKey: 'No API key set',
|
||||
missingUrl: 'No URL set',
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetchData();
|
||||
},
|
||||
computed: {
|
||||
/* Get API key for access to instance */
|
||||
apiKey() {
|
||||
const { apiKey } = this.options;
|
||||
|
||||
return apiKey;
|
||||
},
|
||||
/* Get instance URL */
|
||||
url() {
|
||||
const { url } = this.options;
|
||||
|
||||
return url;
|
||||
},
|
||||
/* Create authorisation header for the instance from the apiKey */
|
||||
authHeaders() {
|
||||
if (!this.options.apiKey) {
|
||||
return {};
|
||||
}
|
||||
const encoded = window.btoa(`:${this.options.apiKey}`);
|
||||
return { Authorization: `Basic ${encoded}` };
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/* The update() method extends mixin, used to update the data.
|
||||
* It's called by parent component, when the user presses update
|
||||
*/
|
||||
update() {
|
||||
this.startLoading();
|
||||
this.fetchData();
|
||||
},
|
||||
/* Make the data request to the computed API endpoint */
|
||||
fetchData() {
|
||||
const { authHeaders, url } = this;
|
||||
|
||||
if (!this.optionsValid({ authHeaders, url })) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.makeRequest(url, authHeaders)
|
||||
.then(this.processData);
|
||||
},
|
||||
/* Convert API response data into a format to be consumed by the UI */
|
||||
processData(response) {
|
||||
const monitorRows = this.getMonitorRows(response);
|
||||
|
||||
const monitors = new Map();
|
||||
|
||||
for (let index = 0; index < monitorRows.length; index += 1) {
|
||||
const row = monitorRows[index];
|
||||
this.processRow(row, monitors);
|
||||
}
|
||||
|
||||
this.monitors = Array.from(monitors.values());
|
||||
},
|
||||
getMonitorRows(response) {
|
||||
return response.split('\n').filter(row => row.startsWith('monitor_'));
|
||||
},
|
||||
processRow(row, monitors) {
|
||||
const dataType = this.getRowDataType(row);
|
||||
const monitorName = this.getRowMonitorName(row);
|
||||
|
||||
if (!monitors.has(monitorName)) {
|
||||
monitors.set(monitorName, { name: monitorName });
|
||||
}
|
||||
|
||||
const monitor = monitors.get(monitorName);
|
||||
const value = this.getRowValue(row);
|
||||
|
||||
const updated = this.setMonitorValue(dataType, monitor, value);
|
||||
|
||||
monitors.set(monitorName, updated);
|
||||
},
|
||||
setMonitorValue(key, monitor, value) {
|
||||
const copy = { ...monitor };
|
||||
switch (key) {
|
||||
case 'monitor_cert_days_remaining': {
|
||||
copy.certDaysRemaining = value;
|
||||
break;
|
||||
}
|
||||
case 'monitor_cert_is_valid': {
|
||||
copy.certValid = value;
|
||||
break;
|
||||
}
|
||||
case 'monitor_response_time': {
|
||||
copy.responseTime = value;
|
||||
break;
|
||||
}
|
||||
case 'monitor_status': {
|
||||
copy.status = value === '1' ? 'Up' : 'Down';
|
||||
copy.statusClass = copy.status.toLowerCase();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return copy;
|
||||
},
|
||||
getRowValue(row) {
|
||||
return this.getValueWithRegex(row, /\b\d+\b$/);
|
||||
},
|
||||
getRowMonitorName(row) {
|
||||
return this.getValueWithRegex(row, /monitor_name="([^"]+)"/);
|
||||
},
|
||||
getRowDataType(row) {
|
||||
return this.getValueWithRegex(row, /^(.*?)\{/);
|
||||
},
|
||||
getValueWithRegex(string, regex) {
|
||||
const result = string.match(regex);
|
||||
|
||||
const isArray = Array.isArray(result);
|
||||
|
||||
if (!isArray) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return result.length > 1 ? result[1] : result[0];
|
||||
},
|
||||
optionsValid({ url, authHeaders }) {
|
||||
const errors = [];
|
||||
if (url === undefined) {
|
||||
errors.push(this.errorMessageConstants.missingUrl);
|
||||
}
|
||||
|
||||
if (authHeaders === undefined) {
|
||||
errors.push(this.errorMessageConstants.missingApiKey);
|
||||
}
|
||||
|
||||
if (errors.length === 0) { return true; }
|
||||
|
||||
this.errorMessage = errors.join('\n');
|
||||
return false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.status-pill {
|
||||
border-radius: 50em;
|
||||
box-sizing: border-box;
|
||||
font-size: 0.75em;
|
||||
display: inline-block;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: baseline;
|
||||
padding: .35em .65em;
|
||||
margin: 1em 0.5em;
|
||||
min-width: 64px;
|
||||
|
||||
&.up {
|
||||
background-color: rgb(92, 221, 139);
|
||||
color: black;
|
||||
}
|
||||
|
||||
&.down {
|
||||
background-color: rgb(220, 53, 69);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
div.item.monitor-row:hover {
|
||||
background-color: var(--item-background);
|
||||
color: var(--current-color);
|
||||
opacity: 1;
|
||||
|
||||
div.title-title>span.text {
|
||||
color: var(--current-color);
|
||||
}
|
||||
}
|
||||
|
||||
.monitors-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.monitor-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.35em 0.5em;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.title-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
@@ -115,6 +115,7 @@ const COMPAT = {
|
||||
'synology-download': 'SynologyDownload',
|
||||
'system-info': 'SystemInfo',
|
||||
'tfl-status': 'TflStatus',
|
||||
'uptime-kuma': 'UptimeKuma',
|
||||
'wallet-balance': 'WalletBalance',
|
||||
weather: 'Weather',
|
||||
'weather-forecast': 'WeatherForecast',
|
||||
@@ -205,14 +206,16 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/media-queries.scss';
|
||||
@import "@/styles/media-queries.scss";
|
||||
|
||||
.widget-base {
|
||||
position: relative;
|
||||
padding: 0.75rem 0.5rem 0.5rem 0.5rem;
|
||||
background: var(--widget-base-background);
|
||||
box-shadow: var(--widget-base-shadow, none);
|
||||
|
||||
// Refresh and full-page action buttons
|
||||
button.action-btn {
|
||||
button.action-btn {
|
||||
height: 1rem;
|
||||
min-width: auto;
|
||||
width: 1.75rem;
|
||||
@@ -223,21 +226,26 @@ export default {
|
||||
border: none;
|
||||
opacity: var(--dimming-factor);
|
||||
color: var(--widget-text-color);
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
color: var(--widget-background-color);
|
||||
}
|
||||
|
||||
&.update-btn {
|
||||
right: -0.25rem;
|
||||
}
|
||||
|
||||
&.open-btn {
|
||||
right: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Optional widget label
|
||||
.widget-label {
|
||||
color: var(--widget-text-color);
|
||||
}
|
||||
|
||||
// Actual widget container
|
||||
.widget-wrap {
|
||||
&.has-error {
|
||||
@@ -245,9 +253,11 @@ export default {
|
||||
opacity: 0.5;
|
||||
border-radius: var(--curve-factor);
|
||||
background: #ffff0040;
|
||||
|
||||
&:hover { background: none; }
|
||||
}
|
||||
}
|
||||
|
||||
// Error message output
|
||||
.widget-error {
|
||||
p.error-msg {
|
||||
@@ -256,12 +266,14 @@ export default {
|
||||
font-size: 1rem;
|
||||
margin: 0 auto 0.5rem auto;
|
||||
}
|
||||
|
||||
p.error-output {
|
||||
font-family: var(--font-monospace);
|
||||
color: var(--widget-text-color);
|
||||
font-size: 0.85rem;
|
||||
margin: 0.5rem auto;
|
||||
}
|
||||
|
||||
p.retry-link {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
@@ -270,14 +282,17 @@ export default {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Loading spinner
|
||||
.loading {
|
||||
margin: 0.2rem auto;
|
||||
text-align: center;
|
||||
|
||||
svg.loader {
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
// Hide widget contents while loading
|
||||
&.is-loading {
|
||||
.widget-wrap {
|
||||
@@ -285,5 +300,4 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* A Vue directive to trigger an event when the user
|
||||
* clicks anywhere other than the specified elements
|
||||
* Used to close context menus popup modals and tips
|
||||
* Dashy: Licensed under MIT - (C) Alicia Sykes 2022
|
||||
* Dashy: Licensed under MIT - (C) Alicia Sykes 2024
|
||||
*/
|
||||
|
||||
const instances = []; // List of click event instances
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* A Vue directive to call event when element is long-pressed
|
||||
* Used to open context menus on touch-enabled devices
|
||||
* Inspired by: FeliciousX/vue-directive-long-press
|
||||
* Dashy: Licensed under MIT - (C) Alicia Sykes 2022
|
||||
* Dashy: Licensed under MIT - (C) Alicia Sykes 2024
|
||||
*/
|
||||
|
||||
const LONG_PRESS_DEFAULT_DELAY = 750;
|
||||
|
||||
26
src/main.js
26
src/main.js
@@ -13,14 +13,16 @@ import TreeView from 'vue-json-tree-view';
|
||||
|
||||
// Import base Dashy components and utils
|
||||
import Dashy from '@/App.vue'; // Main Dashy Vue app
|
||||
import router from '@/router'; // Router, for navigation
|
||||
import store from '@/store'; // Store, for local state management
|
||||
import router from '@/router'; // Router, for navigation
|
||||
import serviceWorker from '@/utils/InitServiceWorker'; // Service worker initialization
|
||||
import { messages } from '@/utils/languages'; // Language texts
|
||||
import ErrorReporting from '@/utils/ErrorReporting'; // Error reporting initializer (off)
|
||||
import clickOutside from '@/directives/ClickOutside'; // Directive for closing popups, modals, etc
|
||||
import { toastedOptions, tooltipOptions, language as defaultLanguage } from '@/utils/defaults';
|
||||
import { initKeycloakAuth, isKeycloakEnabled } from '@/utils/KeycloakAuth';
|
||||
import { initHeaderAuth, isHeaderAuthEnabled } from '@/utils/HeaderAuth';
|
||||
import Keys from '@/utils/StoreMutations';
|
||||
|
||||
// Initialize global Vue components
|
||||
Vue.use(VueI18n);
|
||||
@@ -58,11 +60,17 @@ const mount = () => new Vue({
|
||||
store, router, render, i18n,
|
||||
}).$mount('#app');
|
||||
|
||||
// If Keycloak not enabled, then proceed straight to the app
|
||||
if (!isKeycloakEnabled()) {
|
||||
mount();
|
||||
} else { // Keycloak is enabled, redirect to KC login page
|
||||
initKeycloakAuth()
|
||||
.then(() => mount())
|
||||
.catch(() => window.location.reload());
|
||||
}
|
||||
store.dispatch(Keys.INITIALIZE_CONFIG).then(() => {
|
||||
// Keycloak is enabled, redirect to KC login page
|
||||
if (isKeycloakEnabled()) {
|
||||
initKeycloakAuth()
|
||||
.then(() => mount())
|
||||
.catch(() => window.location.reload());
|
||||
} else if (isHeaderAuthEnabled()) {
|
||||
initHeaderAuth()
|
||||
.then(() => mount())
|
||||
.catch(() => window.location.reload());
|
||||
} else { // If Keycloak not enabled, then proceed straight to the app
|
||||
mount();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -21,12 +21,22 @@ export default {
|
||||
return;
|
||||
}
|
||||
// 1. Get the config, and strip appConfig if is sub-page
|
||||
const isSubPag = !!this.$store.state.currentConfigInfo;
|
||||
const isSubPag = !!this.$store.state.currentConfigInfo.confId;
|
||||
const jsonConfig = config;
|
||||
if (isSubPag) delete jsonConfig.appConfig;
|
||||
jsonConfig.sections = jsonConfig.sections.map(({ filteredItems, ...section }) => section);
|
||||
// If a sub-config, then remove appConfig, and check path isn't an external URL
|
||||
if (isSubPag) {
|
||||
delete jsonConfig.appConfig;
|
||||
if (this.$store.state.currentConfigInfo.confPath.includes('http')) {
|
||||
ErrorHandler('Cannot save to an external URL');
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 2. Convert JSON into YAML
|
||||
const yamlOptions = {};
|
||||
const yaml = jsYaml.dump(jsonConfig, yamlOptions);
|
||||
const strjsonConfig = JSON.stringify(jsonConfig);
|
||||
const jsonObj = JSON.parse(strjsonConfig);
|
||||
const yaml = jsYaml.dump(jsonObj, yamlOptions);
|
||||
// 3. Prepare the request
|
||||
const baseUrl = process.env.VUE_APP_DOMAIN || window.location.origin;
|
||||
const endpoint = `${baseUrl}${serviceEndpoints.save}`;
|
||||
@@ -63,20 +73,39 @@ export default {
|
||||
ErrorHandler('Unable to save changes locally, this feature has been disabled');
|
||||
return;
|
||||
}
|
||||
localStorage.setItem(localStorageKeys.CONF_SECTIONS, JSON.stringify(config.sections));
|
||||
localStorage.setItem(localStorageKeys.PAGE_INFO, JSON.stringify(config.pageInfo));
|
||||
localStorage.setItem(localStorageKeys.APP_CONFIG, JSON.stringify(config.appConfig));
|
||||
|
||||
const isSubPag = !!this.$store.state.currentConfigInfo.confId;
|
||||
if (isSubPag) { // Save for sub-page only
|
||||
const configId = this.$store.state.currentConfigInfo.confId;
|
||||
const localStorageKeySections = `${localStorageKeys.CONF_SECTIONS}-${configId}`;
|
||||
const localStorageKeyPageInfo = `${localStorageKeys.PAGE_INFO}-${configId}`;
|
||||
localStorage.setItem(localStorageKeySections, JSON.stringify(config.sections));
|
||||
localStorage.setItem(localStorageKeyPageInfo, JSON.stringify(config.pageInfo));
|
||||
} else { // Or save to main config
|
||||
localStorage.setItem(localStorageKeys.CONF_SECTIONS, JSON.stringify(config.sections));
|
||||
localStorage.setItem(localStorageKeys.PAGE_INFO, JSON.stringify(config.pageInfo));
|
||||
localStorage.setItem(localStorageKeys.APP_CONFIG, JSON.stringify(config.appConfig));
|
||||
}
|
||||
|
||||
if (config.appConfig.theme) {
|
||||
localStorage.setItem(localStorageKeys.THEME, config.appConfig.theme);
|
||||
}
|
||||
InfoHandler('Config has succesfully been saved in browser storage', 'Config Update');
|
||||
InfoHandler('Config has successfully been saved in browser storage', 'Config Update');
|
||||
this.showToast(this.$t('config-editor.success-msg-local'), true);
|
||||
this.$store.commit(StoreKeys.SET_EDIT_MODE, false);
|
||||
},
|
||||
carefullyClearLocalStorage() {
|
||||
// Delete the main keys
|
||||
localStorage.removeItem(localStorageKeys.PAGE_INFO);
|
||||
localStorage.removeItem(localStorageKeys.APP_CONFIG);
|
||||
localStorage.removeItem(localStorageKeys.CONF_SECTIONS);
|
||||
// Then, if we've got any sub-pages, delete those too
|
||||
(this.$store.getters.pages || []).forEach((page) => {
|
||||
const localStorageKeySections = `${localStorageKeys.CONF_SECTIONS}-${page.id}`;
|
||||
const localStorageKeyPageInfo = `${localStorageKeys.PAGE_INFO}-${page.id}`;
|
||||
localStorage.removeItem(localStorageKeySections);
|
||||
localStorage.removeItem(localStorageKeyPageInfo);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -6,7 +6,6 @@ import Defaults, { localStorageKeys, iconCdns } from '@/utils/defaults';
|
||||
import Keys from '@/utils/StoreMutations';
|
||||
import { searchTiles } from '@/utils/Search';
|
||||
import { checkItemVisibility } from '@/utils/CheckItemVisibility';
|
||||
import { GetTheme, ApplyLocalTheme, ApplyCustomVariables } from '@/utils/ThemeHelper';
|
||||
|
||||
const HomeMixin = {
|
||||
props: {
|
||||
@@ -36,22 +35,28 @@ const HomeMixin = {
|
||||
searchValue: '',
|
||||
}),
|
||||
async mounted() {
|
||||
await this.getConfigForRoute();
|
||||
// await this.getConfigForRoute();
|
||||
},
|
||||
watch: {
|
||||
async $route() {
|
||||
await this.getConfigForRoute();
|
||||
this.setTheme();
|
||||
this.loadUpConfig();
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
this.loadUpConfig();
|
||||
},
|
||||
methods: {
|
||||
async getConfigForRoute() {
|
||||
this.$store.commit(Keys.SET_CURRENT_SUB_PAGE, this.subPageInfo);
|
||||
if (this.subPageInfo && this.subPageInfo.confPath) { // Get config for sub-page
|
||||
await this.$store.dispatch(Keys.INITIALIZE_MULTI_PAGE_CONFIG, this.subPageInfo.confPath);
|
||||
} else { // Otherwise, use main config
|
||||
this.$store.commit(Keys.USE_MAIN_CONFIG);
|
||||
}
|
||||
/* When page loaded / sub-page changed, initiate config fetch */
|
||||
async loadUpConfig() {
|
||||
const subPage = this.determineConfigFile();
|
||||
await this.$store.dispatch(Keys.INITIALIZE_CONFIG, subPage);
|
||||
},
|
||||
/* Based on the current route, get which config to display, null will use default */
|
||||
determineConfigFile() {
|
||||
const pagePath = this.$router.currentRoute.path;
|
||||
const isSubPage = new RegExp((/(home|workspace|minimal)\/[a-zA-Z0-9-]+/g)).test(pagePath);
|
||||
const subPageName = isSubPage ? pagePath.split('/').pop() : null;
|
||||
return subPageName;
|
||||
},
|
||||
/* TEMPORARY: If on sub-page, check if custom theme is set and return it */
|
||||
getSubPageTheme() {
|
||||
@@ -63,9 +68,9 @@ const HomeMixin = {
|
||||
}
|
||||
},
|
||||
setTheme() {
|
||||
const theme = this.getSubPageTheme() || GetTheme();
|
||||
ApplyLocalTheme(theme);
|
||||
ApplyCustomVariables(theme);
|
||||
// const theme = this.getSubPageTheme() || GetTheme();
|
||||
// ApplyLocalTheme(theme);
|
||||
// ApplyCustomVariables(theme);
|
||||
},
|
||||
updateModalVisibility(modalState) {
|
||||
this.$store.commit('SET_MODAL_OPEN', modalState);
|
||||
|
||||
143
src/mixins/ThemingMixin.js
Normal file
143
src/mixins/ThemingMixin.js
Normal file
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* This mixin can be extended by any component or view which needs to manage themes
|
||||
* It handles fetching and applying themes from the store, updating themes,
|
||||
* applying custom CSS variables and loading external stylesheets.
|
||||
* */
|
||||
|
||||
import Keys from '@/utils/StoreMutations';
|
||||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
import { builtInThemes, localStorageKeys, mainCssVars } from '@/utils/defaults';
|
||||
|
||||
const ThemingMixin = {
|
||||
data: () => ({
|
||||
selectedTheme: '', // Used only to bind current them to theme dropdown
|
||||
}),
|
||||
computed: {
|
||||
/* This is the theme from the central store. When it changes, the UI will update */
|
||||
themeFromStore() {
|
||||
return this.$store.getters.theme;
|
||||
},
|
||||
appConfig() {
|
||||
return this.$store.getters.appConfig;
|
||||
},
|
||||
/* Any extra user-defined themes, to add to dropdown */
|
||||
extraThemeNames() {
|
||||
const userThemes = this.appConfig?.cssThemes || [];
|
||||
if (typeof userThemes === 'string') return [userThemes];
|
||||
return userThemes;
|
||||
},
|
||||
/* If user specified external stylesheet(s), format and return */
|
||||
externalThemes() {
|
||||
const availableThemes = {};
|
||||
if (this.appConfig?.externalStyleSheet) {
|
||||
const externals = this.appConfig.externalStyleSheet;
|
||||
if (Array.isArray(externals)) {
|
||||
externals.forEach((ext, i) => {
|
||||
availableThemes[`External Stylesheet ${i + 1}`] = ext;
|
||||
});
|
||||
} else if (typeof externals === 'string') {
|
||||
availableThemes['External Stylesheet'] = this.appConfig.externalStyleSheet;
|
||||
} else {
|
||||
ErrorHandler('External stylesheets must be of type string or string[]');
|
||||
}
|
||||
}
|
||||
return availableThemes;
|
||||
},
|
||||
/* Combines all theme names for dropdown (built-in, user-defined and stylesheets) */
|
||||
themeNames() {
|
||||
const externalThemeNames = Object.keys(this.externalThemes);
|
||||
return [...this.extraThemeNames, ...externalThemeNames, ...builtInThemes];
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
/* When theme in VueX store changes, then update theme */
|
||||
themeFromStore(newTheme) {
|
||||
if (newTheme) {
|
||||
this.resetToDefault();
|
||||
this.selectedTheme = newTheme;
|
||||
this.updateTheme(newTheme);
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/* Called when user changes theme through the UI
|
||||
* Updates store, which will in turn update theme through watcher
|
||||
*/
|
||||
themeChangedInUI() {
|
||||
this.$store.commit(Keys.SET_THEME, this.selectedTheme); // Update store
|
||||
this.updateTheme(this.selectedTheme); // Apply theme to UI
|
||||
},
|
||||
/**
|
||||
* Gets any custom styles the user has applied, wither from local storage, or from the config
|
||||
* @returns {object} An array of objects, one for each theme, containing kvps for variables
|
||||
*/
|
||||
getCustomColors() {
|
||||
const localColors = JSON.parse(localStorage[localStorageKeys.CUSTOM_COLORS] || '{}');
|
||||
const configColors = this.appConfig.customColors || {};
|
||||
return Object.assign(configColors, localColors);
|
||||
},
|
||||
/* Gets user custom color preferences for current theme, and applies to DOM */
|
||||
applyCustomVariables(theme) {
|
||||
mainCssVars.forEach((vName) => { document.documentElement.style.removeProperty(`--${vName}`); });
|
||||
const themeColors = this.getCustomColors()[theme];
|
||||
if (themeColors) {
|
||||
Object.keys(themeColors).forEach((customVar) => {
|
||||
document.documentElement.style.setProperty(`--${customVar}`, themeColors[customVar]);
|
||||
});
|
||||
}
|
||||
},
|
||||
/* Sets the theme, by updating data-theme attribute on the html tag */
|
||||
applyLocalTheme(newTheme) {
|
||||
const htmlTag = document.getElementsByTagName('html')[0];
|
||||
if (htmlTag.hasAttribute('data-theme')) htmlTag.removeAttribute('data-theme');
|
||||
htmlTag.setAttribute('data-theme', newTheme);
|
||||
},
|
||||
/* If using an external stylesheet, load it in */
|
||||
applyRemoteTheme(href) {
|
||||
this.resetToDefault();
|
||||
const element = document.createElement('link');
|
||||
element.setAttribute('rel', 'stylesheet');
|
||||
element.setAttribute('type', 'text/css');
|
||||
element.setAttribute('id', 'user-defined-stylesheet');
|
||||
element.setAttribute('href', href);
|
||||
document.getElementsByTagName('head')[0].appendChild(element);
|
||||
},
|
||||
/* Determines if a given theme is local / not a custom user stylesheet */
|
||||
isThemeLocal(themeToCheck) {
|
||||
const localThemes = [...builtInThemes, ...this.extraThemeNames];
|
||||
return localThemes.includes(themeToCheck);
|
||||
},
|
||||
/* Updates theme. Checks if the new theme is local or external,
|
||||
and calls appropriate updating function. Updates local storage */
|
||||
updateTheme(newTheme) {
|
||||
if (newTheme.toLowerCase() === 'default') {
|
||||
this.resetToDefault();
|
||||
} else if (this.isThemeLocal(newTheme)) {
|
||||
this.applyLocalTheme(newTheme);
|
||||
} else if (this.externalThemes[newTheme]) {
|
||||
this.applyRemoteTheme(this.externalThemes[newTheme]);
|
||||
}
|
||||
this.applyCustomVariables(newTheme);
|
||||
},
|
||||
/* Removes any applied themes, and deletes any externally loaded stylesheets */
|
||||
resetToDefault() {
|
||||
const externalStyles = document.getElementById('user-defined-stylesheet');
|
||||
if (externalStyles) document.getElementsByTagName('head')[0].removeChild(externalStyles);
|
||||
document.getElementsByTagName('html')[0].removeAttribute('data-theme');
|
||||
},
|
||||
/* Call within mounted hook within a page to apply the correct theme */
|
||||
initializeTheme() {
|
||||
const initialTheme = this.themeFromStore;
|
||||
this.selectedTheme = initialTheme;
|
||||
const hasExternal = this.externalThemes && Object.entries(this.externalThemes).length > 0;
|
||||
|
||||
if (this.isThemeLocal(initialTheme)) {
|
||||
this.updateTheme(initialTheme);
|
||||
} else if (hasExternal) {
|
||||
this.applyRemoteTheme(this.externalThemes[initialTheme]);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default ThemingMixin;
|
||||
@@ -14,23 +14,9 @@ import Home from '@/views/Home.vue';
|
||||
|
||||
// Import helper functions, config data and defaults
|
||||
import { isAuthEnabled, isLoggedIn, isGuestAccessEnabled } from '@/utils/Auth';
|
||||
import { makePageSlug, makePageName } from '@/utils/ConfigHelpers';
|
||||
import { metaTagData, startingView, routePaths } from '@/utils/defaults';
|
||||
import { metaTagData, startingView as defaultStartingView, routePaths } from '@/utils/defaults';
|
||||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
|
||||
// Import data from users conf file. Note that rebuild is required for this to update.
|
||||
import conf from '../public/conf.yml';
|
||||
|
||||
if (!conf) {
|
||||
ErrorHandler('You\'ve not got any data in your config file yet.');
|
||||
}
|
||||
|
||||
// Assign top-level config fields, check not null
|
||||
const config = conf || {};
|
||||
const pages = config.pages || [];
|
||||
const pageInfo = config.pageInfo || {};
|
||||
const appConfig = config.appConfig || {};
|
||||
|
||||
Vue.use(Router);
|
||||
const progress = new Progress({ color: 'var(--progress-bar)' });
|
||||
|
||||
@@ -42,16 +28,15 @@ const isAuthenticated = () => {
|
||||
return (!authEnabled || userLoggedIn || guestEnabled);
|
||||
};
|
||||
|
||||
/* Get the users chosen starting view from app config, or return default */
|
||||
const getStartingView = () => appConfig.startingView || startingView;
|
||||
// Get the default starting view from environmental variable
|
||||
const startingView = process.env.VUE_APP_STARTING_VIEW || defaultStartingView;
|
||||
|
||||
/**
|
||||
* Returns the component that should be rendered at the base path,
|
||||
* Defaults to Home, but the user can change this to Workspace of Minimal
|
||||
*/
|
||||
const getStartingComponent = () => {
|
||||
const usersPreference = getStartingView();
|
||||
switch (usersPreference) {
|
||||
switch (startingView) {
|
||||
case 'minimal': return () => import('./views/Minimal.vue');
|
||||
case 'workspace': return () => import('./views/Workspace.vue');
|
||||
default: return Home;
|
||||
@@ -59,71 +44,23 @@ const getStartingComponent = () => {
|
||||
};
|
||||
|
||||
/* Returns the meta tags for each route */
|
||||
const makeMetaTags = (defaultTitle) => ({
|
||||
title: pageInfo.title || defaultTitle,
|
||||
metaTags: metaTagData,
|
||||
});
|
||||
|
||||
const makeSubConfigPath = (rawPath) => {
|
||||
if (!rawPath) return '';
|
||||
if (rawPath.startsWith('/') || rawPath.startsWith('http')) return rawPath;
|
||||
else return `/${rawPath}`;
|
||||
};
|
||||
|
||||
/* For each additional config file, create routes for home, minimal and workspace views */
|
||||
const makeMultiPageRoutes = (userPages) => {
|
||||
// If no multi pages specified, or is not array, then return nothing
|
||||
if (!userPages || !Array.isArray(userPages)) return [];
|
||||
const multiPageRoutes = [];
|
||||
// For each user page, create an additional route
|
||||
userPages.forEach((page) => {
|
||||
if (!page.name || !page.path) { // Sumin not right, show warning
|
||||
ErrorHandler('Additional pages must have both a `name` and `path`');
|
||||
}
|
||||
// Props to be passed to home mixin
|
||||
const subPageInfo = {
|
||||
subPageInfo: {
|
||||
confPath: makeSubConfigPath(page.path),
|
||||
pageId: makePageName(page.name),
|
||||
pageTitle: page.name,
|
||||
},
|
||||
};
|
||||
// Create route for default homepage
|
||||
multiPageRoutes.push({
|
||||
path: makePageSlug(page.name, 'home'),
|
||||
name: `${subPageInfo.subPageInfo.pageId}-home`,
|
||||
component: Home,
|
||||
props: subPageInfo,
|
||||
});
|
||||
// Create route for the workspace view
|
||||
multiPageRoutes.push({
|
||||
path: makePageSlug(page.name, 'workspace'),
|
||||
name: `${subPageInfo.subPageInfo.pageId}-workspace`,
|
||||
component: () => import('./views/Workspace.vue'),
|
||||
props: subPageInfo,
|
||||
});
|
||||
// Create route for the minimal view
|
||||
multiPageRoutes.push({
|
||||
path: makePageSlug(page.name, 'minimal'),
|
||||
name: `${subPageInfo.subPageInfo.pageId}-minimal`,
|
||||
component: () => import('./views/Minimal.vue'),
|
||||
props: subPageInfo,
|
||||
});
|
||||
});
|
||||
return multiPageRoutes;
|
||||
const makeMetaTags = (defaultTitle) => {
|
||||
const userTitle = process.env.VUE_APP_TITLE || '';
|
||||
const title = userTitle ? `${userTitle} | ${defaultTitle}` : defaultTitle;
|
||||
return { title, metaTags: metaTagData };
|
||||
};
|
||||
|
||||
/* Routing mode, can be either 'hash', 'history' or 'abstract' */
|
||||
const mode = appConfig.routingMode || 'history';
|
||||
const mode = process.env.VUE_APP_ROUTING_MODE || 'history';
|
||||
|
||||
/* List of all routes, props, components and metadata */
|
||||
const router = new Router({
|
||||
mode,
|
||||
routes: [
|
||||
...makeMultiPageRoutes(pages),
|
||||
// ...makeMultiPageRoutes(pages),
|
||||
{ // The default view can be customized by the user
|
||||
path: '/',
|
||||
name: `landing-page-${getStartingView()}`,
|
||||
name: `landing-page-${startingView}`,
|
||||
component: getStartingComponent(),
|
||||
meta: makeMetaTags('Home Page'),
|
||||
},
|
||||
@@ -197,7 +134,7 @@ const router = new Router({
|
||||
* if so, then ensure that they are correctly logged in as a valid user
|
||||
* If not logged in, prevent all access and redirect them to login page
|
||||
* */
|
||||
router.beforeEach((to, from, next) => {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
progress.start();
|
||||
if (to.name !== 'login' && !isAuthenticated()) next({ name: 'login' });
|
||||
else next();
|
||||
|
||||
162
src/store.js
162
src/store.js
@@ -4,22 +4,21 @@ import Vuex from 'vuex';
|
||||
import axios from 'axios';
|
||||
import yaml from 'js-yaml';
|
||||
import Keys from '@/utils/StoreMutations';
|
||||
import ConfigAccumulator from '@/utils/ConfigAccumalator';
|
||||
import { componentVisibility } from '@/utils/ConfigHelpers';
|
||||
import { makePageName, formatConfigPath, componentVisibility } from '@/utils/ConfigHelpers';
|
||||
import { applyItemId } from '@/utils/SectionHelpers';
|
||||
import filterUserSections from '@/utils/CheckSectionVisibility';
|
||||
import ErrorHandler, { InfoHandler, InfoKeys } from '@/utils/ErrorHandler';
|
||||
import { isUserAdmin } from '@/utils/Auth';
|
||||
import { localStorageKeys } from './utils/defaults';
|
||||
import { localStorageKeys, theme as defaultTheme } from './utils/defaults';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
const {
|
||||
INITIALIZE_CONFIG,
|
||||
INITIALIZE_MULTI_PAGE_CONFIG,
|
||||
INITIALIZE_ROOT_CONFIG,
|
||||
SET_CONFIG,
|
||||
SET_REMOTE_CONFIG,
|
||||
SET_CURRENT_SUB_PAGE,
|
||||
SET_ROOT_CONFIG,
|
||||
SET_CURRENT_CONFIG_INFO,
|
||||
SET_MODAL_OPEN,
|
||||
SET_LANGUAGE,
|
||||
SET_ITEM_LAYOUT,
|
||||
@@ -45,11 +44,11 @@ const {
|
||||
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
config: {}, // The current config, rendered to the UI
|
||||
remoteConfig: {}, // The configuration stored on the server
|
||||
config: {}, // The current config being used, and rendered to the UI
|
||||
rootConfig: null, // Always the content of main config file, never used directly
|
||||
editMode: false, // While true, the user can drag and edit items + sections
|
||||
modalOpen: false, // KB shortcut functionality will be disabled when modal is open
|
||||
currentConfigInfo: undefined, // For multi-page support, will store info about config file
|
||||
currentConfigInfo: {}, // For multi-page support, will store info about config file
|
||||
navigateConfToTab: undefined, // Used to switch active tab in config modal
|
||||
},
|
||||
getters: {
|
||||
@@ -68,17 +67,14 @@ const store = new Vuex.Store({
|
||||
return filterUserSections(state.config.sections || []);
|
||||
},
|
||||
pages(state) {
|
||||
return state.remoteConfig.pages || [];
|
||||
return state.config.pages || [];
|
||||
},
|
||||
theme(state) {
|
||||
let localTheme = null;
|
||||
if (state.currentConfigInfo?.pageId) {
|
||||
const themeStoreKey = `${localStorageKeys.THEME}-${state.currentConfigInfo?.pageId}`;
|
||||
localTheme = localStorage[themeStoreKey];
|
||||
} else {
|
||||
localTheme = localStorage[localStorageKeys.THEME];
|
||||
}
|
||||
return localTheme || state.config.appConfig.theme;
|
||||
const localStorageKey = state.currentConfigInfo.confId
|
||||
? `${localStorageKeys.THEME}-${state.currentConfigInfo.confId}` : localStorageKeys.THEME;
|
||||
const localTheme = localStorage[localStorageKey];
|
||||
// Return either theme from local storage, or from appConfig
|
||||
return localTheme || state.config.appConfig.theme || defaultTheme;
|
||||
},
|
||||
webSearch(state, getters) {
|
||||
return getters.appConfig.webSearch || {};
|
||||
@@ -146,14 +142,18 @@ const store = new Vuex.Store({
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
/* Set the master config */
|
||||
[SET_ROOT_CONFIG](state, config) {
|
||||
if (!config.appConfig) config.appConfig = {};
|
||||
state.config = config;
|
||||
},
|
||||
/* The config to display and edit. Will differ from ROOT_CONFIG when using multi-page */
|
||||
[SET_CONFIG](state, config) {
|
||||
if (!config.appConfig) config.appConfig = {};
|
||||
state.config = config;
|
||||
},
|
||||
[SET_REMOTE_CONFIG](state, config) {
|
||||
const notNullConfig = config || {};
|
||||
if (!notNullConfig.appConfig) notNullConfig.appConfig = {};
|
||||
state.remoteConfig = notNullConfig;
|
||||
[SET_CURRENT_CONFIG_INFO](state, subConfigInfo) {
|
||||
state.currentConfigInfo = subConfigInfo;
|
||||
},
|
||||
[SET_LANGUAGE](state, lang) {
|
||||
const newConfig = state.config;
|
||||
@@ -276,12 +276,13 @@ const store = new Vuex.Store({
|
||||
config.sections = applyItemId(config.sections);
|
||||
state.config = config;
|
||||
},
|
||||
[SET_THEME](state, themOps) {
|
||||
const { theme, pageId } = themOps;
|
||||
[SET_THEME](state, theme) {
|
||||
const newConfig = { ...state.config };
|
||||
newConfig.appConfig.theme = theme;
|
||||
state.config = newConfig;
|
||||
const themeStoreKey = pageId ? `${localStorageKeys.THEME}-${pageId}` : localStorageKeys.THEME;
|
||||
const pageId = state.currentConfigInfo.confId;
|
||||
const themeStoreKey = pageId
|
||||
? `${localStorageKeys.THEME}-${pageId}` : localStorageKeys.THEME;
|
||||
localStorage.setItem(themeStoreKey, theme);
|
||||
InfoHandler('Theme updated', InfoKeys.VISUAL);
|
||||
},
|
||||
@@ -306,42 +307,89 @@ const store = new Vuex.Store({
|
||||
[CONF_MENU_INDEX](state, index) {
|
||||
state.navigateConfToTab = index;
|
||||
},
|
||||
[SET_CURRENT_SUB_PAGE](state, subPageObject) {
|
||||
if (!subPageObject) {
|
||||
// Set theme back to primary when navigating to index page
|
||||
const defaulTheme = localStorage.getItem(localStorageKeys.PRIMARY_THEME);
|
||||
if (defaulTheme) state.config.appConfig.theme = defaulTheme;
|
||||
}
|
||||
state.currentConfigInfo = subPageObject;
|
||||
},
|
||||
[USE_MAIN_CONFIG](state) {
|
||||
if (state.remoteConfig) {
|
||||
state.config = state.remoteConfig;
|
||||
} else {
|
||||
this.dispatch(Keys.INITIALIZE_CONFIG);
|
||||
}
|
||||
/* Set config to rootConfig, by calling initialize with no params */
|
||||
async [USE_MAIN_CONFIG]() {
|
||||
this.dispatch(Keys.INITIALIZE_CONFIG);
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
/* Called when app first loaded. Reads config and sets state */
|
||||
async [INITIALIZE_CONFIG]({ commit }) {
|
||||
// Get the config file from the server and store it for use by the accumulator
|
||||
commit(SET_REMOTE_CONFIG, yaml.load((await axios.get('/conf.yml')).data));
|
||||
const deepCopy = (json) => JSON.parse(JSON.stringify(json));
|
||||
const config = deepCopy(new ConfigAccumulator().config());
|
||||
commit(SET_CONFIG, config);
|
||||
/* Fetches the root config file, only ever called by INITIALIZE_CONFIG */
|
||||
async [INITIALIZE_ROOT_CONFIG]({ commit }) {
|
||||
// Load and parse config from root config file
|
||||
const configFilePath = process.env.VUE_APP_CONFIG_PATH || '/conf.yml';
|
||||
const data = await yaml.load((await axios.get(configFilePath)).data);
|
||||
// Replace missing root properties with empty objects
|
||||
if (!data.appConfig) data.appConfig = {};
|
||||
if (!data.pageInfo) data.pageInfo = {};
|
||||
if (!data.sections) data.sections = [];
|
||||
// Set the state, and return data
|
||||
commit(SET_ROOT_CONFIG, data);
|
||||
return data;
|
||||
},
|
||||
/* Fetch config for a sub-page (sections and pageInfo only) */
|
||||
async [INITIALIZE_MULTI_PAGE_CONFIG]({ commit, state }, configPath) {
|
||||
axios.get(configPath).then((response) => {
|
||||
const subConfig = yaml.load(response.data);
|
||||
const pageTheme = subConfig.appConfig?.theme;
|
||||
subConfig.appConfig = state.config.appConfig; // Always use parent appConfig
|
||||
if (pageTheme) subConfig.appConfig.theme = pageTheme; // Apply page theme override
|
||||
commit(SET_CONFIG, subConfig);
|
||||
}).catch((err) => {
|
||||
ErrorHandler(`Unable to load config from '${configPath}'`, err);
|
||||
});
|
||||
/**
|
||||
* Fetches config and updates state
|
||||
* If not on sub-page, will trigger the fetch of main config, then use that
|
||||
* If using sub-page config, then fetch that sub-config, then
|
||||
* override certain fields (appConfig, pages) and update config
|
||||
*/
|
||||
async [INITIALIZE_CONFIG]({ commit, state }, subConfigId) {
|
||||
const rootConfig = state.rootConfig || await this.dispatch(Keys.INITIALIZE_ROOT_CONFIG);
|
||||
if (!subConfigId) { // Use root config as config
|
||||
commit(SET_CONFIG, rootConfig);
|
||||
commit(SET_CURRENT_CONFIG_INFO, {});
|
||||
|
||||
let localSections = [];
|
||||
const localSectionsRaw = localStorage[localStorageKeys.CONF_SECTIONS];
|
||||
if (localSectionsRaw) {
|
||||
try {
|
||||
const json = JSON.parse(localSectionsRaw);
|
||||
if (json.length >= 1) localSections = json;
|
||||
} catch (e) {
|
||||
ErrorHandler('Malformed section data in local storage');
|
||||
}
|
||||
}
|
||||
if (localSections.length > 0) {
|
||||
rootConfig.sections = localSections;
|
||||
}
|
||||
return rootConfig;
|
||||
} else {
|
||||
// Find and format path to fetch sub-config from
|
||||
const subConfigPath = formatConfigPath(rootConfig?.pages?.find(
|
||||
(page) => makePageName(page.name) === subConfigId,
|
||||
)?.path);
|
||||
|
||||
if (!subConfigPath) {
|
||||
ErrorHandler(`Unable to find config for '${subConfigId}'`);
|
||||
return null;
|
||||
}
|
||||
|
||||
axios.get(subConfigPath).then((response) => {
|
||||
// Parse the YAML
|
||||
const configContent = yaml.load(response.data) || {};
|
||||
// Certain values must be inherited from root config
|
||||
const theme = configContent?.appConfig?.theme || rootConfig.appConfig?.theme || 'default';
|
||||
configContent.appConfig = rootConfig.appConfig;
|
||||
configContent.pages = rootConfig.pages;
|
||||
configContent.appConfig.theme = theme;
|
||||
|
||||
// Load local sections if they exist
|
||||
const localSectionsRaw = localStorage[`${localStorageKeys.CONF_SECTIONS}-${subConfigId}`];
|
||||
if (localSectionsRaw) {
|
||||
try {
|
||||
const json = JSON.parse(localSectionsRaw);
|
||||
if (json.length >= 1) configContent.sections = json;
|
||||
} catch (e) {
|
||||
ErrorHandler('Malformed section data in local storage for sub-config');
|
||||
}
|
||||
}
|
||||
// Set the config
|
||||
commit(SET_CONFIG, configContent);
|
||||
commit(SET_CURRENT_CONFIG_INFO, { confPath: subConfigPath, confId: subConfigId });
|
||||
}).catch((err) => {
|
||||
ErrorHandler(`Unable to load config from '${subConfigPath}'`, err);
|
||||
});
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
modules: {},
|
||||
|
||||
@@ -1619,6 +1619,229 @@ html[data-theme='lissy'] {
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='glass'],
|
||||
html[data-theme='glass-2'],
|
||||
html[data-theme='neomorphic'] {
|
||||
--primary: #fff;
|
||||
--item-group-outer-background: rgba(0, 0, 0, 0.25);
|
||||
--item-group-background: transparent;
|
||||
--item-group-heading-text-color: #fff;
|
||||
--item-group-heading-text-color-hover: #ffffffd6;
|
||||
--item-group-shadow: 5px 2px 20px rgba(0, 0, 0, 0.5);
|
||||
--background: #190842;
|
||||
--background-darker: #190842;
|
||||
--settings-background: transparent;
|
||||
--search-container-background: transparent;
|
||||
--font-headings: 'Segoe UI', 'Ariel', 'sans-serif';
|
||||
--font-body: 'Roboto', 'Segoe UI', 'Ariel', 'sans-serif';
|
||||
--minimal-view-background-color: transparent;
|
||||
--minimal-view-group-background: rgba(255, 255, 255, 0.15);
|
||||
--minimal-view-section-heading-background: rgba(255, 255, 255, 0.15);
|
||||
--minimal-view-section-heading-color: rgba(255, 255, 255, 0.15);
|
||||
--config-settings-background: #16073de3;
|
||||
--cloud-backup-background: #16073de3;
|
||||
|
||||
@mixin item-transition-styles($bg: transparent, $hover-bg: rgba(255, 255, 255, 0.15), $hover-shadow: rgba(0, 0, 0, 0.75)) {
|
||||
background: $bg;
|
||||
border: 1px solid transparent;
|
||||
box-shadow: none;
|
||||
transition: 0.2s all ease-in-out;
|
||||
|
||||
&:hover {
|
||||
border-radius: 0.35rem;
|
||||
box-shadow: 0 4px 30px $hover-shadow;
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.19);
|
||||
background: $hover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin transform-scale($normal-scale: 1, $hover-scale: 1.25) {
|
||||
transition: 0.1s all ease-in-out;
|
||||
transform: scale($normal-scale);
|
||||
|
||||
&:hover {
|
||||
transform: scale($hover-scale);
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background-size: cover;
|
||||
background-color: #090317;
|
||||
.home {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-outer, header, .dashy-modal, .dashy-modal .tabs {
|
||||
background: transparent;
|
||||
// backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
// Minimal view components
|
||||
.minimal-section-inner, div.minimal-section-heading {
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(145, 145, 145, 0.45);
|
||||
border-bottom: none;
|
||||
|
||||
&.selected {
|
||||
border: 1px solid rgba(145, 145, 145, 0.45);
|
||||
background: var(--minimal-view-group-background);
|
||||
}
|
||||
}
|
||||
|
||||
.minimal-section-heading {
|
||||
color: var(--minimal-view-section-heading-background);
|
||||
|
||||
&.selected {
|
||||
.section-icon, .section-title {
|
||||
color: var(--primary) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
--glass-button-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
||||
--glass-button-hover-shadow: 2px 2px 5px rgba(0, 0, 0, 0.7);
|
||||
|
||||
// Forms and inputs
|
||||
button.save-button,
|
||||
.action-buttons button,
|
||||
.cloud-backup-restore-wrapper button,
|
||||
.tab__nav__item,
|
||||
div.input-container input.input-field,
|
||||
form.normal input,
|
||||
.nav-outer nav .nav-item,
|
||||
div.edit-mode-bottom-banner .edit-banner-section button,
|
||||
.v-select.theme-dropdown.vs__dropdown-toggle,
|
||||
.theme-dropdown div.vs__dropdown-toggle,
|
||||
.config-buttons > svg,
|
||||
.display-options svg,
|
||||
form.minimal input,
|
||||
a.config-button, button.config-button {
|
||||
border-radius: 0.35rem;
|
||||
box-shadow: var(--glass-button-shadow);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.19);
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
transition: all 0.2s ease-in-out;
|
||||
&:hover, &.selected {
|
||||
box-shadow: var(--glass-button-hover-shadow);
|
||||
border: 1px solid rgba(255, 255, 255, 0.25) !important;
|
||||
background: #ffffff42 !important;
|
||||
color: var(--primary) !important;
|
||||
path { fill: var(--primary); }
|
||||
}
|
||||
}
|
||||
|
||||
.tab__nav__items {
|
||||
gap: 1rem;
|
||||
margin: 0.5rem 0 0;
|
||||
.tab__nav__item {
|
||||
padding: 0.5rem 0.5rem;
|
||||
&:hover, .active, .active:hover {
|
||||
background: #ffffff42 !important;
|
||||
span { color: var(--primary) !important; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-options-container .config-buttons, div.cloud-backup-restore-wrapper {
|
||||
background: none;
|
||||
}
|
||||
|
||||
// Item and collapsable specific styles
|
||||
.item {
|
||||
@include item-transition-styles(transparent, rgba(255, 255, 255, 0.15), rgba(0, 0, 0, 0.75));
|
||||
.item-icon {
|
||||
@include transform-scale(1.1, 1.25);
|
||||
}
|
||||
}
|
||||
|
||||
.collapsable {
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid rgba(0, 0, 0, 0.45);
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
// Modal specific styles
|
||||
.dashy-modal {
|
||||
box-shadow: 0 20px 40px -2px #000000b8, 1px 1px 6px #000000a6 !important;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
background: var(--config-settings-background);
|
||||
}
|
||||
|
||||
.theme-configurator-wrapper, .view-switcher {
|
||||
backdrop-filter: blur(10px);
|
||||
background: var(--config-settings-background);
|
||||
border: 1px solid rgba(255, 255, 255, 0.19);
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.edit-mode-top-banner {
|
||||
backdrop-filter: blur(10px);
|
||||
background: #ffffff6b;
|
||||
border-bottom: 1px solid black;
|
||||
|
||||
span { color: #eaff9d; }
|
||||
}
|
||||
|
||||
div.edit-mode-bottom-banner, .add-new-section {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
backdrop-filter: blur(50px);
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='glass'] {
|
||||
body {
|
||||
background: url('https://zeabur.com/images/bg.png') center center no-repeat;
|
||||
background-size: cover;
|
||||
background-color: #090317;
|
||||
.home {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='glass-2'] {
|
||||
body {
|
||||
background: url('https://i.ibb.co/FnLH6bj/dashy-glass.jpg') center center no-repeat;
|
||||
background-size: cover;
|
||||
background-color: #090317;
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='neomorphic'] {
|
||||
--primary: #fff;
|
||||
--item-group-outer-background: rgba(255, 255, 255, 0.15);
|
||||
--item-group-background: transparent;
|
||||
--item-group-heading-text-color: #fff;
|
||||
--item-group-shadow: 5px 2px 20px rgba(0, 0, 0, 0.5);
|
||||
--background: #5b56f7;
|
||||
// --background: #4bdbfd;
|
||||
--background-darker: #12103c;
|
||||
--settings-background: transparent;
|
||||
--search-container-background: transparent;
|
||||
--font-headings: 'Segoe UI', 'Ariel', 'sans-serif';
|
||||
--font-body: 'Roboto', 'Segoe UI', 'Ariel', 'sans-serif';
|
||||
--minimal-view-background-color: transparent;
|
||||
--minimal-view-group-background: rgba(255, 255, 255, 0.15);
|
||||
--minimal-view-section-heading-background: rgba(255, 255, 255, 0.15);
|
||||
--minimal-view-section-heading-color: rgba(255, 255, 255, 0.15);
|
||||
--config-settings-background: #1fb8f4e3;
|
||||
--cloud-backup-background: #16073de3;
|
||||
|
||||
--glass-button-shadow: 0px 1px 5px rgba(0, 0, 0, 0.5);
|
||||
--glass-button-hover-shadow: 2px 2px 5px rgba(0, 0, 0, 0.7);
|
||||
body {
|
||||
background: var(--background);
|
||||
}
|
||||
.item:hover { box-shadow: 0 3px 10px rgba(0, 0, 0, 0.5); }
|
||||
.collapsable { border: 1px solid rgba(255, 255, 255, 0.25) !important; }
|
||||
}
|
||||
|
||||
|
||||
html[data-theme='cherry-blossom'] {
|
||||
--primary: #e1e8ee;
|
||||
--background: #11171d;
|
||||
|
||||
@@ -11,6 +11,8 @@ const getAppConfig = () => {
|
||||
return config.appConfig || {};
|
||||
};
|
||||
|
||||
// const appConfig = $store.getters.appConfig || {};
|
||||
|
||||
/**
|
||||
* Called when the user is still using array for users, prints warning
|
||||
* This was a breaking change, implemented in V 1.6.5
|
||||
|
||||
@@ -16,11 +16,9 @@ import ErrorHandler from '@/utils/ErrorHandler';
|
||||
import { applyItemId } from '@/utils/SectionHelpers';
|
||||
import $store from '@/store';
|
||||
|
||||
import buildConf from '../../public/conf.yml';
|
||||
|
||||
export default class ConfigAccumulator {
|
||||
constructor() {
|
||||
this.conf = $store.state.remoteConfig;
|
||||
this.conf = $store.state.config;
|
||||
}
|
||||
|
||||
pages() {
|
||||
@@ -33,9 +31,10 @@ export default class ConfigAccumulator {
|
||||
// Set app config from file
|
||||
if (this.conf && this.conf.appConfig) {
|
||||
appConfigFile = this.conf.appConfig;
|
||||
} else if (buildConf && buildConf.appConfig) {
|
||||
appConfigFile = buildConf.appConfig;
|
||||
}
|
||||
// else if (buildConf && buildConf.appConfig) {
|
||||
// appConfigFile = buildConf.appConfig;
|
||||
// }
|
||||
// Fill in defaults if anything missing
|
||||
let usersAppConfig = defaultAppConfig;
|
||||
if (localStorage[localStorageKeys.APP_CONFIG]) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import ConfigAccumulator from '@/utils/ConfigAccumalator';
|
||||
// import $store from '@/store';
|
||||
import filterUserSections from '@/utils/CheckSectionVisibility';
|
||||
import { languages } from '@/utils/languages';
|
||||
import {
|
||||
visibleComponents,
|
||||
localStorageKeys,
|
||||
theme as defaultTheme,
|
||||
language as defaultLanguage,
|
||||
} from '@/utils/defaults';
|
||||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
@@ -26,6 +26,13 @@ export const makePageSlug = (pageName, pageType) => {
|
||||
return `/${pageType}/${formattedName}`;
|
||||
};
|
||||
|
||||
/* Put fetch path for additional configs in correct format */
|
||||
export const formatConfigPath = (configPath) => {
|
||||
if (configPath.includes('http')) return configPath;
|
||||
if (configPath.substring(0, 1) !== '/') return `/${configPath}`;
|
||||
return configPath;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initiates the Accumulator class and generates a complete config object
|
||||
* Self-executing function, returns the full user config as a JSON object
|
||||
@@ -67,34 +74,12 @@ export const componentVisibility = (appConfig) => {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the users saved theme, first looks for local storage theme,
|
||||
* then looks at user's appConfig, and finally checks the defaults
|
||||
* @returns {string} Name of theme to apply
|
||||
*/
|
||||
export const getTheme = () => {
|
||||
const localTheme = localStorage[localStorageKeys.THEME];
|
||||
const appConfigTheme = config.appConfig.theme;
|
||||
return localTheme || appConfigTheme || defaultTheme;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets any custom styles the user has applied, wither from local storage, or from the config
|
||||
* @returns {object} An array of objects, one for each theme, containing kvps for variables
|
||||
*/
|
||||
export const getCustomColors = () => {
|
||||
const localColors = JSON.parse(localStorage[localStorageKeys.CUSTOM_COLORS] || '{}');
|
||||
const configColors = config.appConfig.customColors || {};
|
||||
return Object.assign(configColors, localColors);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a list of items which the user has assigned a hotkey to
|
||||
* So that when the hotkey is pressed, the app/ service can be launched
|
||||
*/
|
||||
export const getCustomKeyShortcuts = () => {
|
||||
export const getCustomKeyShortcuts = (sections) => {
|
||||
const results = [];
|
||||
const sections = config.sections || [];
|
||||
sections.forEach((section) => {
|
||||
const itemsWithHotKeys = section.items.filter(item => item.hotkey);
|
||||
results.push(itemsWithHotKeys.map(item => ({ hotkey: item.hotkey, url: item.url })));
|
||||
|
||||
@@ -534,6 +534,37 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"enableHeaderAuth": {
|
||||
"title": "Enable HeaderAuth?",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "If set to true, enable Header Authentication. See appConfig.auth.headerAuth"
|
||||
},
|
||||
"headerAuth": {
|
||||
"type": "object",
|
||||
"description": "Configuration for headerAuth",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"proxyWhitelist"
|
||||
],
|
||||
"properties": {
|
||||
"userHeader": {
|
||||
"title": "User Header",
|
||||
"type": "string",
|
||||
"description": "Header name which contains username",
|
||||
"default": "REMOTE_USER"
|
||||
},
|
||||
"proxyWhitelist": {
|
||||
"title": "Upstream Proxy Auth Trust",
|
||||
"type": "array",
|
||||
"description": "Upstream proxy servers to expect authenticated requests from",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "IPs of upstream proxies that will be trusted"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"enableKeycloak": {
|
||||
"title": "Enable Keycloak?",
|
||||
"type": "boolean",
|
||||
@@ -982,6 +1013,11 @@
|
||||
"type": "number",
|
||||
"description": "A numeric shortcut key, between 0 and 9. Useful for quickly launching frequently used applications"
|
||||
},
|
||||
"rel": {
|
||||
"title": "rel",
|
||||
"type": "string",
|
||||
"description": "The rel attribute for the link. For specifying the relationship between the current document and the linked document"
|
||||
},
|
||||
"tags": {
|
||||
"title": "Tags",
|
||||
"type": "array",
|
||||
@@ -1086,6 +1122,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"filteredItems": {
|
||||
"title": "Filtered Items - temp",
|
||||
"type": "array",
|
||||
"description": "This attribute will be deprecated in the next release - do not use!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export const welcomeMsg = () => {
|
||||
/* Prints warning message, usually when there is a configuration error */
|
||||
export const warningMsg = (message, stack) => {
|
||||
console.info(
|
||||
`\n%c⚠️ Warning ⚠️%c \n${message} \n\n%cThis is likely not an issue with Dashy, but rather your configuration. If you think it is a bug, please open a ticket on GitHub: https://git.io/JukXk`,
|
||||
`\n%c⚠️ Warning ⚠️%c \n${message} \n\n%cThis is likely not an issue with Dashy, but rather your configuration.\nIf you think it is a bug, please open a ticket on GitHub: https://git.io/JukXk`,
|
||||
"color:#ceb73f; background: #ceb73f33; font-size:1.5rem; padding:0.15rem; margin: 1rem auto; font-family: Rockwell, Tahoma, 'Trebuchet MS', Helvetica; border: 2px solid #ceb73f; border-radius: 4px; font-weight: bold; text-shadow: 1px 1px 1px #000000bf;",
|
||||
'font-weight: bold; font-size: 1rem;color: #ceb73f;',
|
||||
"color: #ceb73f; font-size: 0.75rem; font-family: Tahoma, 'Trebuchet MS', Helvetica;",
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
|
||||
/* eslint-disable global-require */
|
||||
|
||||
import ConfigAccumulator from '@/utils/ConfigAccumalator';
|
||||
import $store from '@/store';
|
||||
import { sentryDsn } from '@/utils/defaults';
|
||||
|
||||
const ErrorReporting = (Vue, router) => {
|
||||
// Fetch users config
|
||||
const appConfig = new ConfigAccumulator().appConfig() || {};
|
||||
const appConfig = $store.getters.appConfig || {};
|
||||
// Check if error reporting is enabled. Only proceed if user has turned it on.
|
||||
if (appConfig.enableErrorReporting) {
|
||||
// Get current app version
|
||||
|
||||
78
src/utils/HeaderAuth.js
Normal file
78
src/utils/HeaderAuth.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import axios from 'axios';
|
||||
import sha256 from 'crypto-js/sha256';
|
||||
import ConfigAccumulator from '@/utils/ConfigAccumalator';
|
||||
import { cookieKeys, localStorageKeys, serviceEndpoints } from '@/utils/defaults';
|
||||
import { InfoHandler, ErrorHandler, InfoKeys } from '@/utils/ErrorHandler';
|
||||
import { logout } from '@/utils/Auth';
|
||||
|
||||
const getAppConfig = () => {
|
||||
const Accumulator = new ConfigAccumulator();
|
||||
const config = Accumulator.config();
|
||||
return config.appConfig || {};
|
||||
};
|
||||
|
||||
class HeaderAuth {
|
||||
constructor() {
|
||||
const { auth } = getAppConfig();
|
||||
const {
|
||||
userHeader, proxyWhitelist,
|
||||
} = auth.headerAuth;
|
||||
this.userHeader = userHeader;
|
||||
this.proxyWhitelist = proxyWhitelist;
|
||||
this.users = auth.users;
|
||||
}
|
||||
|
||||
/* eslint-disable class-methods-use-this */
|
||||
login() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const baseUrl = process.env.VUE_APP_DOMAIN || window.location.origin;
|
||||
axios.get(`${baseUrl}${serviceEndpoints.getUser}`).then((response) => {
|
||||
if (!response.data) {
|
||||
reject(Error('Error, expected data nout returned'));
|
||||
} else if (response.data.errorMsg) {
|
||||
reject(response.data.errorMsg);
|
||||
} else {
|
||||
try {
|
||||
this.users.forEach((user) => {
|
||||
if (user.user.toLowerCase() === response.data.user.toLowerCase()) { // User found
|
||||
const strAndUpper = (input) => input.toString().toUpperCase();
|
||||
const sha = strAndUpper(sha256(strAndUpper(user.user) + strAndUpper(user.hash)));
|
||||
document.cookie = `${cookieKeys.AUTH_TOKEN}=${sha};`;
|
||||
localStorage.setItem(localStorageKeys.USERNAME, user.user);
|
||||
InfoHandler(`Successfully signed in as ${response.data.user}`, InfoKeys.AUTH);
|
||||
resolve(response.data.user);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
logout() {
|
||||
logout();
|
||||
}
|
||||
}
|
||||
|
||||
export const isHeaderAuthEnabled = () => {
|
||||
const { auth } = getAppConfig();
|
||||
if (!auth) return false;
|
||||
return auth.enableHeaderAuth || false;
|
||||
};
|
||||
|
||||
let headerAuth;
|
||||
|
||||
export const initHeaderAuth = () => {
|
||||
headerAuth = new HeaderAuth();
|
||||
return headerAuth.login();
|
||||
};
|
||||
|
||||
// TODO: Find where this is implemented
|
||||
export const getHeaderAuth = () => {
|
||||
if (!headerAuth) {
|
||||
ErrorHandler("HeaderAuth not initialized, can't get instance of class");
|
||||
}
|
||||
return headerAuth;
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Dashy: Licensed under MIT, (C) Alicia Sykes 2021 <https://aliciasykes.com> */
|
||||
/* Dashy: Licensed under MIT, (C) Alicia Sykes 2024 <https://aliciasykes.com> */
|
||||
|
||||
/* Tile filtering utility */
|
||||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// A list of mutation names
|
||||
const KEY_NAMES = [
|
||||
'INITIALIZE_CONFIG',
|
||||
'INITIALIZE_ROOT_CONFIG',
|
||||
'INITIALIZE_MULTI_PAGE_CONFIG',
|
||||
'SET_CONFIG',
|
||||
'SET_REMOTE_CONFIG',
|
||||
'SET_ROOT_CONFIG',
|
||||
'SET_CURRENT_CONFIG_INFO',
|
||||
'SET_CURRENT_SUB_PAGE',
|
||||
'SET_MODAL_OPEN',
|
||||
'SET_LANGUAGE',
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
import { getTheme, getCustomColors } from '@/utils/ConfigHelpers';
|
||||
import { mainCssVars } from '@/utils/defaults';
|
||||
|
||||
/* Returns users current theme */
|
||||
export const GetTheme = () => getTheme();
|
||||
|
||||
/* Gets user custom color preferences for current theme, and applies to DOM */
|
||||
export const ApplyCustomVariables = (theme) => {
|
||||
mainCssVars.forEach((vName) => { document.documentElement.style.removeProperty(`--${vName}`); });
|
||||
const themeColors = getCustomColors()[theme];
|
||||
if (themeColors) {
|
||||
Object.keys(themeColors).forEach((customVar) => {
|
||||
document.documentElement.style.setProperty(`--${customVar}`, themeColors[customVar]);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/* Sets the theme, by updating data-theme attribute on the html tag */
|
||||
export const ApplyLocalTheme = (newTheme) => {
|
||||
const htmlTag = document.getElementsByTagName('html')[0];
|
||||
if (htmlTag.hasAttribute('data-theme')) htmlTag.removeAttribute('data-theme');
|
||||
htmlTag.setAttribute('data-theme', newTheme);
|
||||
};
|
||||
|
||||
/**
|
||||
* A function for pre-loading, and easy switching of external stylesheets
|
||||
* External CSS is preloaded to avoid FOUC
|
||||
*/
|
||||
export const LoadExternalTheme = function th() {
|
||||
/* Preload selected external theme */
|
||||
const preloadTheme = (href) => {
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.type = 'text/css';
|
||||
link.href = href;
|
||||
document.head.appendChild(link);
|
||||
return new Promise((resolve, reject) => {
|
||||
link.onload = e => {
|
||||
const { sheet } = e.target;
|
||||
sheet.disabled = true;
|
||||
resolve(sheet);
|
||||
};
|
||||
link.onerror = reject;
|
||||
});
|
||||
};
|
||||
|
||||
/* Check theme is selected, and it exists */
|
||||
const checkTheme = (themes, name) => {
|
||||
if ((!name) || (name !== 'custom' && !themes[name])) {
|
||||
ErrorHandler(`Theme: '${name || '[not selected]'}' does not exist.`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/* Disable all but selected theme */
|
||||
const selectTheme = (themes, name) => {
|
||||
if (checkTheme(themes, name)) {
|
||||
const t = themes; // To avoid ESLint complaining about mutating a param
|
||||
Object.keys(themes).forEach(n => { t[n].disabled = (n !== name); });
|
||||
}
|
||||
};
|
||||
|
||||
const themes = {};
|
||||
|
||||
return {
|
||||
add(name, href) { return preloadTheme(href).then(s => { themes[name] = s; }); },
|
||||
set theme(name) { selectTheme(themes, name); },
|
||||
get theme() { return Object.keys(themes).find(n => !themes[n].disabled); },
|
||||
};
|
||||
};
|
||||
@@ -28,9 +28,9 @@ module.exports = {
|
||||
openingMethod: 'newtab',
|
||||
/* The page paths for each route within the app for the router */
|
||||
routePaths: {
|
||||
home: '/home',
|
||||
minimal: '/minimal',
|
||||
workspace: '/workspace',
|
||||
home: '/home/:config?/',
|
||||
minimal: '/minimal/:config?/',
|
||||
workspace: '/workspace/:config?/',
|
||||
about: '/about',
|
||||
login: '/login',
|
||||
download: '/download',
|
||||
@@ -44,10 +44,12 @@ module.exports = {
|
||||
rebuild: '/config-manager/rebuild',
|
||||
systemInfo: '/system-info',
|
||||
corsProxy: '/cors-proxy',
|
||||
getUser: '/get-user',
|
||||
},
|
||||
/* List of built-in themes, to be displayed within the theme-switcher dropdown */
|
||||
builtInThemes: [
|
||||
'default',
|
||||
'glass',
|
||||
'callisto',
|
||||
'material',
|
||||
'material-dark',
|
||||
@@ -85,6 +87,8 @@ module.exports = {
|
||||
'adventure-basic',
|
||||
'basic',
|
||||
'tama',
|
||||
'neomorphic',
|
||||
'glass-2',
|
||||
],
|
||||
/* Default color options for the theme configurator swatches */
|
||||
swatches: [
|
||||
@@ -120,6 +124,7 @@ module.exports = {
|
||||
PRIMARY_THEME: 'primaryTheme',
|
||||
CUSTOM_COLORS: 'customColors',
|
||||
CONF_SECTIONS: 'confSections',
|
||||
CONF_PAGES: 'confPages',
|
||||
CONF_WIDGETS: 'confSections',
|
||||
PAGE_INFO: 'pageInfo',
|
||||
APP_CONFIG: 'appConfig',
|
||||
@@ -184,7 +189,7 @@ module.exports = {
|
||||
// delay: { show: 380, hide: 0 },
|
||||
},
|
||||
/* Server location of the Backup & Sync cloud function */
|
||||
backupEndpoint: 'https://dashy-sync-service.as93.net',
|
||||
backupEndpoint: 'https://sync-service.dashy.to',
|
||||
/* Available services for fetching favicon icon for user apps */
|
||||
faviconApiEndpoints: {
|
||||
allesedv: 'https://f1.allesedv.com/128/$URL',
|
||||
|
||||
@@ -19,6 +19,7 @@ import pl from '@/assets/locales/pl.json';
|
||||
import pt from '@/assets/locales/pt.json';
|
||||
import gl from '@/assets/locales/gl.json';
|
||||
import ru from '@/assets/locales/ru.json';
|
||||
import ro from '@/assets/locales/ro.json';
|
||||
import sk from '@/assets/locales/sk.json';
|
||||
import sl from '@/assets/locales/sl.json';
|
||||
import sv from '@/assets/locales/sv.json';
|
||||
@@ -151,6 +152,12 @@ export const languages = [
|
||||
locale: ru,
|
||||
flag: '🇷🇺',
|
||||
},
|
||||
{ // Romanian
|
||||
name: 'Romana',
|
||||
code: 'ro',
|
||||
locale: ro,
|
||||
flag: '🇷🇴',
|
||||
},
|
||||
{ // Slovak
|
||||
name: 'Slovenčina',
|
||||
code: 'sk',
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
<section class="license">
|
||||
<h2>License</h2>
|
||||
<code>
|
||||
Copyright © 2021 Alicia Sykes (https://aliciasykes.com)
|
||||
Copyright © {{new Date().getFullYear()}} Alicia Sykes (https://aliciasykes.com)
|
||||
</code>
|
||||
<br><br>
|
||||
<code>
|
||||
|
||||
@@ -119,12 +119,16 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
layoutOrientation(layout) {
|
||||
localStorage.setItem(localStorageKeys.LAYOUT_ORIENTATION, layout);
|
||||
this.layout = layout;
|
||||
if (layout) {
|
||||
localStorage.setItem(localStorageKeys.LAYOUT_ORIENTATION, layout);
|
||||
this.layout = layout;
|
||||
}
|
||||
},
|
||||
iconSize(size) {
|
||||
localStorage.setItem(localStorageKeys.ICON_SIZE, size);
|
||||
this.itemSizeBound = size;
|
||||
if (size) {
|
||||
localStorage.setItem(localStorageKeys.ICON_SIZE, size);
|
||||
this.itemSizeBound = size;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
:class="`item-group-container ${!tabbedView ? 'showing-all' : ''}`">
|
||||
<!-- Section heading buttons -->
|
||||
<MinimalHeading
|
||||
v-for="(section, index) in getSections(sections)"
|
||||
v-for="(section, index) in sections"
|
||||
:key="`heading-${index}`"
|
||||
:index="index"
|
||||
:title="section.name"
|
||||
@@ -29,7 +29,7 @@
|
||||
/>
|
||||
<!-- Section item groups -->
|
||||
<MinimalSection
|
||||
v-for="(section, index) in getSections(sections)"
|
||||
v-for="(section, index) in sections"
|
||||
:key="`body-${index}`"
|
||||
:index="index"
|
||||
:title="section.name"
|
||||
@@ -57,7 +57,6 @@ import HomeMixin from '@/mixins/HomeMixin';
|
||||
import MinimalSection from '@/components/MinimalView/MinimalSection.vue';
|
||||
import MinimalHeading from '@/components/MinimalView/MinimalHeading.vue';
|
||||
import MinimalSearch from '@/components/MinimalView/MinimalSearch.vue';
|
||||
import { localStorageKeys } from '@/utils/defaults';
|
||||
import ConfigLauncher from '@/components/Settings/ConfigLauncher';
|
||||
|
||||
export default {
|
||||
@@ -83,17 +82,6 @@ export default {
|
||||
sectionSelected(index) {
|
||||
this.selectedSection = index;
|
||||
},
|
||||
/* Returns sections from local storage if available, otherwise uses the conf.yml */
|
||||
getSections(sections) {
|
||||
// If the user has stored sections in local storage, return those
|
||||
const localSections = localStorage[localStorageKeys.CONF_SECTIONS];
|
||||
if (localSections) {
|
||||
const json = JSON.parse(localSections);
|
||||
if (json.length >= 1) return json;
|
||||
}
|
||||
// Otherwise, return the usuall data from conf.yml
|
||||
return sections;
|
||||
},
|
||||
/* Clears input field, once a searched item is opened */
|
||||
finishedSearching() {
|
||||
if (this.$refs.filterComp) this.$refs.filterComp.clearMinFilterInput();
|
||||
|
||||
@@ -19,7 +19,6 @@ import WebContent from '@/components/Workspace/WebContent';
|
||||
import WidgetView from '@/components/Workspace/WidgetView';
|
||||
import MultiTaskingWebComtent from '@/components/Workspace/MultiTaskingWebComtent';
|
||||
import Defaults from '@/utils/defaults';
|
||||
import { GetTheme, ApplyLocalTheme, ApplyCustomVariables } from '@/utils/ThemeHelper';
|
||||
|
||||
export default {
|
||||
name: 'Workspace',
|
||||
@@ -27,9 +26,6 @@ export default {
|
||||
data: () => ({
|
||||
url: '',
|
||||
widgets: null,
|
||||
GetTheme,
|
||||
ApplyLocalTheme,
|
||||
ApplyCustomVariables,
|
||||
}),
|
||||
computed: {
|
||||
sections() {
|
||||
@@ -89,6 +85,9 @@ export default {
|
||||
|
||||
<style scoped lang="scss">
|
||||
.work-space {
|
||||
min-height: calc(100vh - var(--footer-height));
|
||||
min-height: fit-content;
|
||||
}
|
||||
:global(footer) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user