From ff3ba55e220a1def0d1c1411eedd5599c6e206ce Mon Sep 17 00:00:00 2001 From: Timon Home Date: Thu, 26 Mar 2026 23:50:54 +0100 Subject: [PATCH] Appearance Section better selection --- languages/de.json | 210 ++++++++++++++++++ package.json | 2 +- .../settings/sections/AppearanceSection.tsx | 195 ++++++---------- 3 files changed, 277 insertions(+), 130 deletions(-) create mode 100644 languages/de.json diff --git a/languages/de.json b/languages/de.json new file mode 100644 index 0000000..ee00324 --- /dev/null +++ b/languages/de.json @@ -0,0 +1,210 @@ +{ + "id": "de", + "name": "Deutsch", + "version": 1, + "author": "JRC", + "strings": { + "general.save": "Speichern", + "general.saved": "Gespeichert", + "general.cancel": "Abbrechen", + "general.delete": "Loeschen", + "general.close": "Schliessen", + "general.retry": "Erneut versuchen", + "general.copy": "Kopieren", + "general.enabled": "Aktiviert", + "general.disabled": "Deaktiviert", + "general.on": "An", + "general.off": "Aus", + "general.yes": "Ja", + "general.no": "Nein", + "general.add": "Hinzufuegen", + "general.noProfileSelected": "Kein Profil ausgewaehlt", + + "sidebar.newProfile": "Neues Profil", + "sidebar.fromTemplate": "Aus Vorlage", + "sidebar.noProfiles": "Noch keine Profile.", + "sidebar.utilities": "Werkzeuge", + "sidebar.faq": "FAQ", + "sidebar.settings": "Einstellungen", + "sidebar.developer": "Entwickler", + + "tabs.console": "Konsole", + "tabs.configure": "Konfigurieren", + "tabs.logs": "Protokolle", + "tabs.profile": "Profil", + + "console.run": "Starten", + "console.stop": "Stoppen", + "console.forceKill": "Sofort beenden", + "console.forceKillHint": "Sofort beenden (ohne sauberes Herunterfahren)", + "console.openWorkDir": "Arbeitsverzeichnis oeffnen", + "console.scrollToBottom": "nach unten scrollen", + "console.search": "Suchen (Strg+F)", + "console.clear": "Leeren (Strg+L)", + "console.lines": "Zeilen", + "console.noMatches": "Keine Treffer", + "console.searchPlaceholder": "Konsole durchsuchen... (Enter naechster, Shift+Enter vorheriger)", + "console.inputPlaceholder": "Befehl senden... (Hoch/Runter Verlauf, Strg+L leeren, Strg+F suchen)", + "console.inputDisabled": "Prozess starten um Befehle zu senden", + "console.waiting": "Warte auf Ausgabe...", + "console.notRunning": "Prozess laeuft nicht. Druecke Starten um zu beginnen.", + "console.noJar": "Keine JAR konfiguriert. Gehe zu Konfigurieren.", + "console.copyLine": "Zeile kopieren", + "console.copyAll": "Gesamte Ausgabe kopieren", + "console.pid": "PID", + + "config.general": "Allgemein", + "config.files": "Dateien & Pfade", + "config.jvm": "JVM-Argumente", + "config.props": "Eigenschaften (-D)", + "config.args": "Programmargumente", + "config.env": "Umgebung", + "config.unsavedChanges": "Ungespeicherte Aenderungen", + "config.restartNeeded": "Neustart erforderlich", + "config.autoStart": "Autostart", + "config.autoStartLabel": "Automatisch beim App-Start starten", + "config.autoStartHint": "Dieses Profil automatisch starten wenn JRC geoeffnet wird", + "config.autoRestart": "Auto-Neustart", + "config.autoRestartLabel": "JAR bei Absturz automatisch neustarten", + "config.autoRestartHint": "Startet den Prozess neu wenn er mit einem Fehlercode beendet wird", + "config.restartDelay": "Neustart-Verzoegerung", + "config.restartDelayHint": "Sekunden bis zum Neustart", + "config.logging": "Protokollierung", + "config.fileLoggingLabel": "Sitzungsprotokolle in Datei speichern", + "config.fileLoggingHint": "Konsolenausgabe in .log-Dateien im Konfigurationsverzeichnis pro Sitzung speichern", + "config.process": "Prozess", + "config.restartProcess": "Prozess neustarten", + "config.jarSelection": "JAR-Auswahl", + "config.workingDir": "Arbeitsverzeichnis", + "config.workingDirHint": "Leer lassen um das JAR-Verzeichnis zu verwenden", + "config.workingDirPlaceholder": "Standard: JAR-Verzeichnis", + "config.javaExec": "Java-Programm", + "config.javaExecHint": "Leer lassen um java aus dem PATH zu verwenden", + "config.javaExecPlaceholder": "java (verwendet System-PATH)", + "config.jvmArgsTitle": "JVM-Argumente", + "config.jvmArgsHint": "Flags die der JVM vor -jar uebergeben werden, z.B. -Xmx2g -XX:+UseG1GC", + "config.propsTitle": "Systemeigenschaften", + "config.propsHint": "Uebergeben als -Dkey=value. Spring-Profile, Ports, Log-Level, etc.", + "config.progArgsTitle": "Programmargumente", + "config.progArgsHint": "Angehaengt nach dem JAR-Pfad, z.B. --nogui --world myWorld", + "config.envTitle": "Umgebungsvariablen", + "config.envHint": "Werden in die Prozessumgebung eingefuegt. Ueberschreiben System-Umgebungsvariablen mit gleichem Schluessel.", + "config.commandPreview": "Befehlsvorschau", + "config.pendingArgTitle": "Nicht gespeicherte Eingabe", + "config.pendingArgMessage": "Du hast Text im Eingabefeld der noch nicht hinzugefuegt wurde.\n\nKlicke zuerst \"+ Hinzufuegen\", sonst wird er nicht uebernommen.\n\nTrotzdem wechseln?", + "config.pendingArgConfirm": "Wechseln", + "config.pendingArgCancel": "Bleiben", + + "profile.identity": "Profil-Identitaet", + "profile.name": "Name", + "profile.accentColour": "Akzentfarbe", + "profile.accentColourHint": "Wird in der Seitenleiste und als Tab-Hervorhebung verwendet.", + "profile.customColour": "Eigene Farbe waehlen", + "profile.dangerZone": "Gefahrenzone", + "profile.deleteProfile": "Profil loeschen", + "profile.deleteHint": "Entfernt dieses Profil und alle Einstellungen dauerhaft. Shift halten um Bestaetigung zu ueberspringen.", + "profile.deleteConfirmTitle": "Profil loeschen?", + "profile.deleteConfirmMessage": "\"{name}\" wird dauerhaft entfernt. Dies kann nicht rueckgaengig gemacht werden.", + + "logs.title": "Sitzungsprotokolle", + "logs.files": "Dateien", + "logs.refresh": "Aktualisieren", + "logs.openDir": "Protokollverzeichnis oeffnen", + "logs.noFiles": "Noch keine Protokolldateien. Starte und stoppe einen Prozess um eine zu erstellen.", + "logs.selectFile": "Waehle eine Protokolldatei um den Inhalt anzuzeigen", + "logs.loading": "Laden...", + "logs.deleteHint": "Protokolldatei loeschen (Shift halten um Bestaetigung zu ueberspringen)", + "logs.deleteTitle": "Protokolldatei loeschen?", + "logs.deleteMessage": "\"{name}\" wird dauerhaft geloescht.", + "logs.disabled": "Dateiprotokollierung ist fuer dieses Profil deaktiviert.", + "logs.disabledHint": "Aktiviere sie unter Konfigurieren > Allgemein > Sitzungsprotokolle in Datei speichern.", + + "settings.title": "Anwendungseinstellungen", + "settings.saved": "Einstellungen gespeichert", + "settings.unsaved": "Ungespeicherte Aenderungen", + "settings.saveChanges": "Aenderungen speichern", + + "settings.general": "Allgemein", + "settings.startup": "Autostart", + "settings.launchOnStartup": "Beim Windows-Start starten", + "settings.launchOnStartupHint": "Java Runner Client startet automatisch beim Anmelden", + "settings.startMinimized": "Minimiert im Infobereich starten", + "settings.startMinimizedHint": "Fenster erscheint nicht beim Start -- nur das Symbol im Infobereich", + "settings.minimizeToTray": "Beim Schliessen in den Infobereich minimieren", + "settings.minimizeToTrayHint": "Beim Schliessen des Fensters bleiben die App und laufende JARs im Hintergrund aktiv", + + "settings.console": "Konsole", + "settings.fontSize": "Schriftgroesse", + "settings.fontSizeHint": "Schriftgroesse der Konsolenausgabe in Pixeln", + "settings.lineNumbers": "Zeilennummern anzeigen", + "settings.lineNumbersHint": "Zeilennummernspalte in der Konsolenausgabe anzeigen", + "settings.timestamps": "Zeitstempel anzeigen", + "settings.timestampsHint": "Zeitstempel fuer jede Konsolenzeile anzeigen", + "settings.wordWrap": "Zeilenumbruch", + "settings.wordWrapHint": "Lange Zeilen umbrechen statt horizontal zu scrollen", + "settings.maxLines": "Maximale Zeilen im Puffer", + "settings.maxLinesHint": "Aeltere Zeilen werden verworfen wenn das Limit erreicht ist", + "settings.historySize": "Befehlsverlauf-Groesse", + "settings.historySizeHint": "Gespeicherte Befehle pro Sitzung (Hoch/Runter zum Navigieren)", + + "settings.appearance": "Darstellung", + "settings.theme": "Design", + "settings.themeHint": "Visuelles Design der Anwendung", + "settings.themeBuiltin": "Eingebaut", + "settings.themeCheckUpdate": "Nach Design-Update suchen", + "settings.language": "Sprache", + "settings.languageHint": "Anzeigesprache der Anwendung", + "settings.languageCheckUpdate": "Nach Sprach-Update suchen", + + "settings.advanced": "Erweitert", + "settings.devMode": "Entwickleroptionen", + "settings.devModeLabel": "Entwicklermodus umschalten (Rechte-Shift + 7)", + "settings.devModeHint": "Aktiviert den Entwickler-Tab und DevTools. Mit Vorsicht verwenden.", + "settings.restApi": "REST-API", + "settings.restApiLabel": "REST-API aktivieren", + "settings.restApiHint": "Stellt eine lokale HTTP-API fuer Automatisierung bereit (Standard-Port {port})", + "settings.restApiPort": "Port", + "settings.restApiPortHint": "Neustart erforderlich um den Port zu aendern", + + "settings.updates": "Aktualisierungen", + "settings.updateCenter": "Aktualisierungscenter", + "settings.checkAll": "Alle pruefen", + "settings.updateAll": "Alle aktualisieren", + "settings.upToDate": "Aktuell", + "settings.updateAvailable": "Update verfuegbar", + "settings.checking": "Pruefe...", + "settings.checkFailed": "Pruefung fehlgeschlagen", + + "settings.about": "Ueber", + "settings.version": "Version", + "settings.stack": "Technologie", + "settings.configPath": "Konfiguration", + + "release.title": "Release-Details", + "release.preRelease": "Vorabversion", + "release.stable": "Stabil", + "release.trustedDev": "Vertrauenswuerdiger Entwickler", + "release.automation": "Automatisiertes Release", + "release.unknownPublisher": "Unbekannter Herausgeber", + "release.unknownPublisherHint": "Dieses Release wurde von einem GitHub-Benutzer veroeffentlicht der nicht in der vertrauenswuerdigen Liste steht. Es wurde dennoch durch die GitHub-Repository-Sicherheit genehmigt.", + "release.downloads": "Downloads", + "release.otherAssets": "Weitere Dateien", + "release.releaseNotes": "Release-Hinweise", + "release.viewOnGithub": "Auf GitHub ansehen", + + "utilities.title": "Werkzeuge", + "utilities.activityLog": "Aktivitaetsprotokoll", + "utilities.processScanner": "Prozess-Scanner", + + "panels.settings": "Anwendungseinstellungen", + "panels.faq": "FAQ", + "panels.utilities": "Werkzeuge", + "panels.developer": "Entwickler", + + "dev.mode": "Entwicklermodus", + + "faq.searchPlaceholder": "FAQ durchsuchen...", + "faq.noResults": "Keine Ergebnisse gefunden.", + "faq.noItems": "Keine Eintraege in diesem Thema." + } +} diff --git a/package.json b/package.json index c22cfe8..e3cb1b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "java-runner-client", - "version": "2.2.1", + "version": "2.2.0", "description": "Run and manage Java processes with profiles, console I/O, and system tray support", "main": "dist/main/main.js", "scripts": { diff --git a/src/renderer/components/settings/sections/AppearanceSection.tsx b/src/renderer/components/settings/sections/AppearanceSection.tsx index 307ed40..300affe 100644 --- a/src/renderer/components/settings/sections/AppearanceSection.tsx +++ b/src/renderer/components/settings/sections/AppearanceSection.tsx @@ -1,26 +1,29 @@ -import React, { useState } from 'react'; -import { Section, Row } from '../SettingsRow'; +import React, { useState, useEffect } from 'react'; +import { Section } from '../SettingsRow'; import { useTheme } from '../../../hooks/ThemeProvider'; import { useTranslation } from '../../../i18n/I18nProvider'; -import { useDevMode } from '../../../hooks/useDevMode'; import { VscSync, VscCheck } from 'react-icons/vsc'; -import { LuDownload } from 'react-icons/lu'; import type { ThemeDefinition } from '../../../../main/shared/types/Theme.types'; import type { LanguageDefinition } from '../../../../main/shared/types/Language.types'; +import type { JRCEnvironment } from '../../../../main/shared/types/App.types'; type FetchState = 'idle' | 'loading' | 'done' | 'error'; export function AppearanceSection() { const { theme, setTheme, availableThemes, refreshThemes } = useTheme(); const { language, setLanguage, availableLanguages, refreshLanguages } = useTranslation(); - const devMode = useDevMode(); const [remoteThemes, setRemoteThemes] = useState([]); const [remoteLangs, setRemoteLangs] = useState([]); const [themeFetch, setThemeFetch] = useState('idle'); const [langFetch, setLangFetch] = useState('idle'); + const [isDev, setIsDev] = useState(false); const [devSynced, setDevSynced] = useState(false); + useEffect(() => { + window.env.get().then((env: JRCEnvironment) => setIsDev(env.type === 'dev')); + }, []); + const fetchThemes = async () => { setThemeFetch('loading'); const res = await window.api.fetchRemoteThemes(); @@ -35,13 +38,15 @@ export function AppearanceSection() { else setLangFetch('error'); }; - const installTheme = async (t: ThemeDefinition) => { + const selectTheme = async (t: ThemeDefinition) => { await window.api.installTheme(t); + await window.api.setActiveTheme(t.id); await refreshThemes(); }; - const installLang = async (l: LanguageDefinition) => { + const selectLang = async (l: LanguageDefinition) => { await window.api.installLanguage(l); + await window.api.setActiveLanguage(l.id); await refreshLanguages(); }; @@ -53,27 +58,42 @@ export function AppearanceSection() { setTimeout(() => setDevSynced(false), 2000); }; + const allThemes = mergeById(availableThemes, remoteThemes, (t) => t.id); + const allLangs = mergeById(availableLanguages, remoteLangs, (l) => l.id); + return ( <>
-
- {availableThemes.map((t) => ( +
+

Select a visual theme

+ +
+ {themeFetch === 'error' && ( +

Failed to fetch themes.

+ )} +
+ {allThemes.map((t) => ( ))}
-
+
+ +
+
+

Select a display language

- t.id} - getVersion={(t) => t.version} - getName={(t) => t.name} - getAuthor={(t) => t.author} - emptyLabel="No themes found on GitHub." - errorLabel="Failed to fetch themes." - />
-
- -
-
- {availableLanguages.map((l) => ( + {langFetch === 'error' && ( +

Failed to fetch languages.

+ )} +
+ {allLangs.map((l) => ( ))}
-
- - l.id} - getVersion={(l) => l.version} - getName={(l) => l.name} - getAuthor={(l) => l.author} - emptyLabel="No languages found on GitHub." - errorLabel="Failed to fetch languages." - /> -
- {devMode && ( + {isDev && (
- +
+
+

Sync local project files

+

+ Load themes and languages from /themes and /languages in the project root +

+
- +
)} ); } -function RemoteList({ - state, - items, - installed, - onInstall, - getId, - getVersion, - getName, - getAuthor, - emptyLabel, - errorLabel, -}: { - state: FetchState; - items: T[]; - installed: T[]; - onInstall: (item: T) => void; - getId: (item: T) => string; - getVersion: (item: T) => number; - getName: (item: T) => string; - getAuthor: (item: T) => string; - emptyLabel: string; - errorLabel: string; -}) { - if (state !== 'done' && state !== 'error') return null; - if (state === 'error') return

{errorLabel}

; - if (items.length === 0) return

{emptyLabel}

; - - return ( -
- {items.map((item) => { - const inst = installed.find((i) => getId(i) === getId(item)); - const isNewer = inst ? getVersion(item) > getVersion(inst) : true; - return ( -
-
-

{getName(item)}

-

- v{getVersion(item)} by {getAuthor(item)} -

-
- {inst && !isNewer ? ( - - Installed - - ) : ( - - )} -
- ); - })} -
- ); +/** Merge local + remote lists by ID, preferring remote version if newer */ +function mergeById(local: T[], remote: T[], getId: (item: T) => string): T[] { + const map = new Map(); + for (const item of local) map.set(getId(item), item); + for (const item of remote) map.set(getId(item), item); + return Array.from(map.values()); }