diff --git a/app/src/main/java/io/xpipe/app/core/AppStyle.java b/app/src/main/java/io/xpipe/app/core/AppStyle.java index ac792051f..e009c3f4b 100644 --- a/app/src/main/java/io/xpipe/app/core/AppStyle.java +++ b/app/src/main/java/io/xpipe/app/core/AppStyle.java @@ -103,6 +103,12 @@ public class AppStyle { STYLESHEET_CONTENTS.values().forEach(s -> { scene.getStylesheets().add(s); }); + if (AppPrefs.get() != null) { + var t = AppPrefs.get().theme.get(); + if (t != null) { + scene.getStylesheets().addAll(t.getAdditionalStylesheets()); + } + } TrackEvent.debug("Added stylesheets for scene"); scenes.add(scene); diff --git a/app/src/main/java/io/xpipe/app/core/AppTheme.java b/app/src/main/java/io/xpipe/app/core/AppTheme.java index 236749bae..cc0ed7593 100644 --- a/app/src/main/java/io/xpipe/app/core/AppTheme.java +++ b/app/src/main/java/io/xpipe/app/core/AppTheme.java @@ -59,6 +59,8 @@ public class AppTheme { } stage.getScene().getRoot().getStyleClass().add(t.getCssId()); + stage.getScene().getStylesheets().removeAll(t.getAdditionalStylesheets()); + stage.getScene().getStylesheets().addAll(t.getAdditionalStylesheets()); stage.getScene().getRoot().pseudoClassStateChanged(LIGHT, !t.isDark()); stage.getScene().getRoot().pseudoClassStateChanged(DARK, t.isDark()); }); @@ -175,8 +177,8 @@ public class AppTheme { private final String name; - public DerivedTheme(String id, String cssId, String name, atlantafx.base.theme.Theme theme) { - super(id, cssId, theme); + public DerivedTheme(String id, String cssId, String name, atlantafx.base.theme.Theme theme, String transparentColor) { + super(id, cssId, theme, transparentColor); this.name = name; } @@ -211,17 +213,17 @@ public class AppTheme { @AllArgsConstructor public static class Theme implements PrefsChoiceValue { - public static final Theme PRIMER_LIGHT = new Theme("light", "primer", new PrimerLight()); - public static final Theme PRIMER_DARK = new Theme("dark", "primer", new PrimerDark()); - public static final Theme NORD_LIGHT = new Theme("nordLight", "nord", new NordLight()); - public static final Theme NORD_DARK = new Theme("nordDark", "nord", new NordDark()); - public static final Theme CUPERTINO_LIGHT = new Theme("cupertinoLight", "cupertino", new CupertinoLight()); - public static final Theme CUPERTINO_DARK = new Theme("cupertinoDark", "cupertino", new CupertinoDark()); - public static final Theme DRACULA = new Theme("dracula", "dracula", new Dracula()); - public static final Theme MOCHA = new DerivedTheme("mocha", "primer", "Mocha", new PrimerDark()); + public static final Theme PRIMER_LIGHT = new Theme("light", "primer", new PrimerLight(), "#ffffff88"); + public static final Theme PRIMER_DARK = new Theme("dark", "primer", new PrimerDark(), "#0d111788"); + public static final Theme NORD_LIGHT = new Theme("nordLight", "nord", new NordLight(), "#ffffff88"); + public static final Theme NORD_DARK = new Theme("nordDark", "nord", new NordDark(), "#0d111788"); + public static final Theme CUPERTINO_LIGHT = new Theme("cupertinoLight", "cupertino", new CupertinoLight(), "#ffffff88"); + public static final Theme CUPERTINO_DARK = new Theme("cupertinoDark", "cupertino", new CupertinoDark(), "#0d111788"); + public static final Theme DRACULA = new Theme("dracula", "dracula", new Dracula(), "#0d111788"); + public static final Theme MOCHA = new DerivedTheme("mocha", "primer", "Mocha", new PrimerDark(), "#0d111788"); // Adjust this to create your own theme - public static final Theme CUSTOM = new DerivedTheme("custom", "primer", "Custom", new PrimerDark()); + public static final Theme CUSTOM = new DerivedTheme("custom", "primer", "Custom", new PrimerDark(), "#0d111788"); // Also include your custom theme here public static final List ALL = List.of( @@ -233,6 +235,8 @@ public class AppTheme { protected final atlantafx.base.theme.Theme theme; + protected final String transparentBackground; + static Theme getDefaultLightTheme() { return switch (OsType.getLocal()) { case OsType.Windows windows -> PRIMER_LIGHT; @@ -257,6 +261,10 @@ public class AppTheme { Application.setUserAgentStylesheet(theme.getUserAgentStylesheetBSS()); } + public List getAdditionalStylesheets() { + return List.of(Styles.toDataURI(".root { -color-bg-default-transparent: " + transparentBackground + "; }")); + } + @Override public ObservableValue toTranslatedString() { return new SimpleStringProperty(theme.getName()); diff --git a/app/src/main/java/io/xpipe/app/core/mode/OperationMode.java b/app/src/main/java/io/xpipe/app/core/mode/OperationMode.java index 874a45420..10e6f9950 100644 --- a/app/src/main/java/io/xpipe/app/core/mode/OperationMode.java +++ b/app/src/main/java/io/xpipe/app/core/mode/OperationMode.java @@ -94,13 +94,15 @@ public abstract class OperationMode { // Handle uncaught exceptions Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> { + // It seems like a few exceptions are thrown in the quantum renderer + // when in shutdown. We can ignore these + if (OperationMode.isInShutdown() && Platform.isFxApplicationThread() && ex instanceof NullPointerException) { + return; + } + ErrorEvent.fromThrowable(ex).unhandled(true).build().handle(); }); - // if (true) { - // throw new OutOfMemoryError(); - // } - TrackEvent.info("Initial setup"); AppProperties.init(); AppState.init(); diff --git a/app/src/main/java/io/xpipe/app/util/PlatformState.java b/app/src/main/java/io/xpipe/app/util/PlatformState.java index 09d77aa4a..1a46a499d 100644 --- a/app/src/main/java/io/xpipe/app/util/PlatformState.java +++ b/app/src/main/java/io/xpipe/app/util/PlatformState.java @@ -5,14 +5,11 @@ import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.TrackEvent; import io.xpipe.app.prefs.AppPrefs; import io.xpipe.core.process.OsType; - import javafx.application.Platform; - import lombok.Getter; import lombok.Setter; import java.awt.*; -import java.util.Optional; import java.util.concurrent.CountDownLatch; public enum PlatformState { @@ -24,8 +21,15 @@ public enum PlatformState { @Setter private static PlatformState current = PlatformState.NOT_INITIALIZED; - @Getter - private static Exception lastError; + private static Throwable lastError; + private static boolean expectedError; + + public static Throwable getLastError() { + if (expectedError) { + ErrorEvent.expected(lastError); + } + return lastError; + } public static void teardown() { // This is bad and can get sometimes stuck @@ -46,26 +50,25 @@ public enum PlatformState { public static void initPlatformOrThrow() throws Exception { initPlatformIfNeeded(); if (lastError != null) { - throw lastError; + throw lastError instanceof Exception e ? e : new Exception(lastError); } } public static boolean initPlatformIfNeeded() { if (current == NOT_INITIALIZED) { - var t = PlatformState.initPlatform().orElse(null); - lastError = t instanceof Exception e ? e : t != null ? new Exception(t) : null; + PlatformState.initPlatform(); } - return current == RUNNING; } - private static Optional initPlatform() { + private static void initPlatform() { if (current == EXITED) { - return Optional.of(new IllegalStateException("Platform has already exited")); + lastError = new IllegalStateException("Platform has already exited"); + return; } if (current == RUNNING) { - return Optional.empty(); + return; } try { @@ -85,11 +88,14 @@ public enum PlatformState { + " You don't have to install XPipe on any system like a server, a WSL distribution, a hypervisor, etc.," + " to have full access to that system, a shell connection to it is enough for XPipe to work from your local machine."; PlatformState.setCurrent(PlatformState.EXITED); - return Optional.of(ErrorEvent.expected(new UnsupportedOperationException(msg))); + expectedError = true; + lastError = new UnsupportedOperationException(msg); + return; } catch (Throwable t) { TrackEvent.warn(t.getMessage()); PlatformState.setCurrent(PlatformState.EXITED); - return Optional.of(t); + lastError = t; + return; } // Check if we have no fonts and set properties to load bundled ones @@ -121,23 +127,26 @@ public enum PlatformState { try { latch.await(); PlatformState.setCurrent(PlatformState.RUNNING); - return Optional.empty(); + return; } catch (InterruptedException e) { - return Optional.of(e); + lastError = e; + return; } } catch (Throwable t) { // Check if we already exited if ("Platform.exit has been called".equals(t.getMessage())) { PlatformState.setCurrent(PlatformState.EXITED); - return Optional.of(t); + lastError = t; + return; } else if ("Toolkit already initialized".equals(t.getMessage())) { PlatformState.setCurrent(PlatformState.RUNNING); - return Optional.empty(); + return; } else { // Platform initialization has failed in this case PlatformState.setCurrent(PlatformState.EXITED); TrackEvent.error(t.getMessage()); - return Optional.of(t); + lastError =t; + return; } } } diff --git a/app/src/main/resources/io/xpipe/app/resources/style/browser.css b/app/src/main/resources/io/xpipe/app/resources/style/browser.css index fcaf4e34e..f293bee56 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/browser.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/browser.css @@ -51,8 +51,9 @@ } .browser .welcome { - -fx-background-color: -color-bg-inset, -color-border-default, -color-bg-default; - -fx-background-insets: 0, 2.65em 0 0 0, 2.70em 0 0 0; + -fx-border-color: -color-bg-inset, -color-border-default; + -fx-border-width: 2.65em 0 0 0, 0.05em 0 0 0; + -fx-border-insets: 0, 2.65em 0 0 0; } .root:seamless-frame .browser .welcome { diff --git a/app/src/main/resources/io/xpipe/app/resources/style/style.css b/app/src/main/resources/io/xpipe/app/resources/style/style.css index d63e07ad4..f396dda45 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/style.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/style.css @@ -10,19 +10,19 @@ } .root:dark .background { - -fx-background-color: derive(-color-bg-default, 1%); + -fx-background-color: derive(-color-bg-default-transparent, 1%); } .root:light .background { - -fx-background-color: derive(-color-bg-default, -9%); + -fx-background-color: derive(-color-bg-default-transparent, -9%); } .root:dark.background { - -fx-background-color: derive(-color-bg-default, 1%); + -fx-background-color: derive(-color-bg-default-transparent, 1%); } .root:light.background { - -fx-background-color: derive(-color-bg-default, -9%); + -fx-background-color: derive(-color-bg-default-transparent, -9%); } .root:seamless-frame.layout > .background { diff --git a/lang/proc/strings/translations_da.properties b/lang/proc/strings/translations_da.properties index fcc52fe74..526340f7f 100644 --- a/lang/proc/strings/translations_da.properties +++ b/lang/proc/strings/translations_da.properties @@ -357,3 +357,5 @@ vmActions=VM-handlinger dockerContextActions=Kontekst-handlinger k8sPodActions=Pod-handlinger openVnc=Sæt VNC op +commandGroup.displayName=Kommandogruppe +commandGroup.displayDescription=Grupper tilgængelige kommandoer for et system diff --git a/lang/proc/strings/translations_de.properties b/lang/proc/strings/translations_de.properties index 771a30605..8d8b8cc05 100644 --- a/lang/proc/strings/translations_de.properties +++ b/lang/proc/strings/translations_de.properties @@ -335,3 +335,5 @@ vmActions=VM-Aktionen dockerContextActions=Kontextbezogene Aktionen k8sPodActions=Pod-Aktionen openVnc=VNC einrichten +commandGroup.displayName=Befehlsgruppe +commandGroup.displayDescription=Verfügbare Befehle für ein System gruppieren diff --git a/lang/proc/strings/translations_en.properties b/lang/proc/strings/translations_en.properties index cb7164d17..8d9905f74 100644 --- a/lang/proc/strings/translations_en.properties +++ b/lang/proc/strings/translations_en.properties @@ -332,4 +332,6 @@ containerActions=Container actions vmActions=VM actions dockerContextActions=Context actions k8sPodActions=Pod actions -openVnc=Set up VNC \ No newline at end of file +openVnc=Set up VNC +commandGroup.displayName=Command group +commandGroup.displayDescription=Group available commands for a system \ No newline at end of file diff --git a/lang/proc/strings/translations_es.properties b/lang/proc/strings/translations_es.properties index 0e02e909b..83a12ac97 100644 --- a/lang/proc/strings/translations_es.properties +++ b/lang/proc/strings/translations_es.properties @@ -331,3 +331,5 @@ vmActions=Acciones VM dockerContextActions=Acciones contextuales k8sPodActions=Acciones del pod openVnc=Configurar VNC +commandGroup.displayName=Grupo de comandos +commandGroup.displayDescription=Agrupa los comandos disponibles para un sistema diff --git a/lang/proc/strings/translations_fr.properties b/lang/proc/strings/translations_fr.properties index 2fd9315c2..20b4cc865 100644 --- a/lang/proc/strings/translations_fr.properties +++ b/lang/proc/strings/translations_fr.properties @@ -331,3 +331,5 @@ vmActions=Actions VM dockerContextActions=Actions contextuelles k8sPodActions=Actions de pods openVnc=Configurer VNC +commandGroup.displayName=Groupe de commande +commandGroup.displayDescription=Groupe de commandes disponibles pour un système diff --git a/lang/proc/strings/translations_it.properties b/lang/proc/strings/translations_it.properties index 7a0be9bb9..dbef50ac6 100644 --- a/lang/proc/strings/translations_it.properties +++ b/lang/proc/strings/translations_it.properties @@ -331,3 +331,5 @@ vmActions=Azioni della VM dockerContextActions=Azioni contestuali k8sPodActions=Azioni del pod openVnc=Configurare VNC +commandGroup.displayName=Gruppo di comando +commandGroup.displayDescription=Gruppo di comandi disponibili per un sistema diff --git a/lang/proc/strings/translations_ja.properties b/lang/proc/strings/translations_ja.properties index 9eb9681c9..79c161735 100644 --- a/lang/proc/strings/translations_ja.properties +++ b/lang/proc/strings/translations_ja.properties @@ -331,3 +331,5 @@ vmActions=VMアクション dockerContextActions=コンテキストアクション k8sPodActions=ポッドアクション openVnc=VNCを設定する +commandGroup.displayName=コマンドグループ +commandGroup.displayDescription=システムで使用可能なコマンドをグループ化する diff --git a/lang/proc/strings/translations_nl.properties b/lang/proc/strings/translations_nl.properties index a7d9c7f4a..7ea31c90b 100644 --- a/lang/proc/strings/translations_nl.properties +++ b/lang/proc/strings/translations_nl.properties @@ -331,3 +331,5 @@ vmActions=VM-acties dockerContextActions=Context acties k8sPodActions=Pod acties openVnc=VNC instellen +commandGroup.displayName=Opdrachtgroep +commandGroup.displayDescription=Groep beschikbare commando's voor een systeem diff --git a/lang/proc/strings/translations_pt.properties b/lang/proc/strings/translations_pt.properties index f700213bb..2d495a348 100644 --- a/lang/proc/strings/translations_pt.properties +++ b/lang/proc/strings/translations_pt.properties @@ -331,3 +331,5 @@ vmActions=Acções VM dockerContextActions=Acções de contexto k8sPodActions=Acções de pod openVnc=Configura o VNC +commandGroup.displayName=Grupo de comandos +commandGroup.displayDescription=Agrupa os comandos disponíveis para um sistema diff --git a/lang/proc/strings/translations_ru.properties b/lang/proc/strings/translations_ru.properties index a064f9a89..9b528e587 100644 --- a/lang/proc/strings/translations_ru.properties +++ b/lang/proc/strings/translations_ru.properties @@ -331,3 +331,5 @@ vmActions=Действия виртуальной машины dockerContextActions=Контекстные действия k8sPodActions=Действия в капсуле openVnc=Настройте VNC +commandGroup.displayName=Группа команд +commandGroup.displayDescription=Группа доступных команд для системы diff --git a/lang/proc/strings/translations_tr.properties b/lang/proc/strings/translations_tr.properties index 23214c0da..ce0890001 100644 --- a/lang/proc/strings/translations_tr.properties +++ b/lang/proc/strings/translations_tr.properties @@ -331,3 +331,5 @@ vmActions=VM eylemleri dockerContextActions=Bağlam eylemleri k8sPodActions=Pod eylemleri openVnc=VNC'yi ayarlama +commandGroup.displayName=Komuta grubu +commandGroup.displayDescription=Bir sistem için mevcut komutları gruplama diff --git a/lang/proc/strings/translations_zh.properties b/lang/proc/strings/translations_zh.properties index e46e8790c..36ba085f9 100644 --- a/lang/proc/strings/translations_zh.properties +++ b/lang/proc/strings/translations_zh.properties @@ -331,3 +331,5 @@ vmActions=虚拟机操作 dockerContextActions=上下文操作 k8sPodActions=Pod 操作 openVnc=设置 VNC +commandGroup.displayName=命令组 +commandGroup.displayDescription=系统可用命令组