diff --git a/app/src/main/java/io/xpipe/app/core/AppConfigurationDialog.java b/app/src/main/java/io/xpipe/app/core/AppConfigurationDialog.java index 2c3dc90ac..65adb412f 100644 --- a/app/src/main/java/io/xpipe/app/core/AppConfigurationDialog.java +++ b/app/src/main/java/io/xpipe/app/core/AppConfigurationDialog.java @@ -6,12 +6,10 @@ import io.xpipe.app.comp.base.ScrollComp; import io.xpipe.app.core.window.AppDialog; import io.xpipe.app.platform.OptionsBuilder; import io.xpipe.app.platform.PlatformState; -import io.xpipe.app.prefs.EditorCategory; -import io.xpipe.app.prefs.PasswordManagerCategory; -import io.xpipe.app.prefs.PersonalizationCategory; -import io.xpipe.app.prefs.TerminalCategory; +import io.xpipe.app.prefs.*; import io.xpipe.app.util.DocumentationLink; +import io.xpipe.core.OsType; import javafx.application.Platform; import javafx.scene.layout.Region; @@ -32,14 +30,20 @@ public class AppConfigurationDialog { var options = new OptionsBuilder() .sub(PersonalizationCategory.languageChoice()) .sub(PersonalizationCategory.themeChoice()) - .sub(TerminalCategory.terminalChoice(false)) + .sub(TerminalCategory.terminalChoice(false)); + + if (OsType.ofLocal() != OsType.WINDOWS && AppPrefs.get().terminalMultiplexer().getValue() != null) { + options.sub(TerminalCategory.terminalMultiplexerChoice()); + } + + var optionsComp = options .sub(EditorCategory.editorChoice()) .sub(PasswordManagerCategory.passwordManagerChoice()) .buildComp(); - options.style("initial-setup"); - options.style("prefs-container"); + optionsComp.style("initial-setup"); + optionsComp.style("prefs-container"); - var scroll = new ScrollComp(options); + var scroll = new ScrollComp(optionsComp); scroll.apply(struc -> { struc.prefHeightProperty().bind(((Region) struc.getContent()).heightProperty()); }); diff --git a/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java b/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java index 4b03d4d9c..8316638d7 100644 --- a/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java +++ b/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java @@ -849,6 +849,7 @@ public final class AppPrefs { spiceClient.setValue(ExternalSpiceClient.determineDefault(spiceClient.getValue())); vncClient.setValue(ExternalVncClient.determineDefault(vncClient.getValue())); passwordManager.setValue(PasswordManager.determineDefault(passwordManager.getValue())); + terminalMultiplexer.setValue(TerminalMultiplexer.determineDefault(terminalMultiplexer.getValue())); PrefsProvider.getAll().forEach(prov -> prov.initDefaultValues()); } diff --git a/app/src/main/java/io/xpipe/app/prefs/TerminalCategory.java b/app/src/main/java/io/xpipe/app/prefs/TerminalCategory.java index dd1aa355f..13f96639a 100644 --- a/app/src/main/java/io/xpipe/app/prefs/TerminalCategory.java +++ b/app/src/main/java/io/xpipe/app/prefs/TerminalCategory.java @@ -168,7 +168,7 @@ public class TerminalCategory extends AppPrefsCategory { .sub(terminalChoice(true)) .sub(terminalPrompt()) .sub(terminalProxy()) - .sub(terminalMultiplexer()) + .sub(terminalMultiplexerChoice()) // .sub(terminalInitScript()) .title("sessionLogging") .sub(new OptionsBuilder() @@ -281,7 +281,7 @@ public class TerminalCategory extends AppPrefsCategory { prefs.terminalInitScript); } - private OptionsBuilder terminalMultiplexer() { + public static OptionsBuilder terminalMultiplexerChoice() { var prefs = AppPrefs.get(); var choiceBuilder = OptionsChoiceBuilder.builder() .property(prefs.terminalMultiplexer) @@ -310,7 +310,7 @@ public class TerminalCategory extends AppPrefsCategory { }) .build(); var choice = choiceBuilder.build().buildComp(); - choice.maxWidth(getCompWidth()); + choice.maxWidth(600); var options = new OptionsBuilder() .name("terminalMultiplexer") .description( diff --git a/app/src/main/java/io/xpipe/app/terminal/ScreenTerminalMultiplexer.java b/app/src/main/java/io/xpipe/app/terminal/ScreenTerminalMultiplexer.java index a43080c16..e6be8e976 100644 --- a/app/src/main/java/io/xpipe/app/terminal/ScreenTerminalMultiplexer.java +++ b/app/src/main/java/io/xpipe/app/terminal/ScreenTerminalMultiplexer.java @@ -1,9 +1,6 @@ package io.xpipe.app.terminal; -import io.xpipe.app.process.CommandSupport; -import io.xpipe.app.process.ScriptHelper; -import io.xpipe.app.process.ShellControl; -import io.xpipe.app.process.ShellScript; +import io.xpipe.app.process.*; import com.fasterxml.jackson.annotation.JsonTypeName; import lombok.Builder; @@ -26,6 +23,11 @@ public class ScreenTerminalMultiplexer implements TerminalMultiplexer { return "https://www.gnu.org/software/screen/manual/screen.html"; } + @Override + public boolean shouldSelect() throws Exception { + return false; + } + @Override public void checkSupported(ShellControl sc) throws Exception { CommandSupport.isInPathOrThrow(sc, "screen"); diff --git a/app/src/main/java/io/xpipe/app/terminal/TerminalMultiplexer.java b/app/src/main/java/io/xpipe/app/terminal/TerminalMultiplexer.java index ec48189e5..5184dba3b 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TerminalMultiplexer.java +++ b/app/src/main/java/io/xpipe/app/terminal/TerminalMultiplexer.java @@ -1,9 +1,13 @@ package io.xpipe.app.terminal; +import io.xpipe.app.issue.ErrorEvent; +import io.xpipe.app.issue.ErrorEventFactory; +import io.xpipe.app.process.LocalShell; import io.xpipe.app.process.ShellControl; import io.xpipe.app.process.ShellScript; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.xpipe.core.OsType; import java.util.ArrayList; import java.util.List; @@ -19,10 +23,35 @@ public interface TerminalMultiplexer { return l; } + static TerminalMultiplexer determineDefault(TerminalMultiplexer existing) { + if (OsType.ofLocal() == OsType.WINDOWS) { + return existing; + } + + try { + if (existing != null && existing.shouldSelect()) { + return existing; + } + + var all = List.of(new TmuxTerminalMultiplexer(), new ZellijTerminalMultiplexer(), new ScreenTerminalMultiplexer()); + for (TerminalMultiplexer terminalMultiplexer : all) { + if (terminalMultiplexer.shouldSelect()) { + return terminalMultiplexer; + } + } + } catch (Exception e) { + ErrorEventFactory.fromThrowable(e).handle(); + } + + return null; + } + boolean supportsSplitView(); String getDocsLink(); + boolean shouldSelect() throws Exception; + void checkSupported(ShellControl sc) throws Exception; ShellScript launchForExistingSession(ShellControl control, TerminalLaunchConfiguration config); diff --git a/app/src/main/java/io/xpipe/app/terminal/TmuxTerminalMultiplexer.java b/app/src/main/java/io/xpipe/app/terminal/TmuxTerminalMultiplexer.java index 7164899b2..eda909009 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TmuxTerminalMultiplexer.java +++ b/app/src/main/java/io/xpipe/app/terminal/TmuxTerminalMultiplexer.java @@ -2,6 +2,7 @@ package io.xpipe.app.terminal; import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.process.CommandSupport; +import io.xpipe.app.process.LocalShell; import io.xpipe.app.process.ShellControl; import io.xpipe.app.process.ShellScript; @@ -27,6 +28,11 @@ public class TmuxTerminalMultiplexer implements TerminalMultiplexer { return "https://github.com/tmux/tmux/wiki/Getting-Started"; } + @Override + public boolean shouldSelect() throws Exception { + return LocalShell.getShell().view().findProgram("tmux").isPresent(); + } + @Override public void checkSupported(ShellControl sc) throws Exception { CommandSupport.isInPathOrThrow(sc, "tmux"); diff --git a/app/src/main/java/io/xpipe/app/terminal/ZellijTerminalMultiplexer.java b/app/src/main/java/io/xpipe/app/terminal/ZellijTerminalMultiplexer.java index a8e79a1bf..8c31b60ae 100644 --- a/app/src/main/java/io/xpipe/app/terminal/ZellijTerminalMultiplexer.java +++ b/app/src/main/java/io/xpipe/app/terminal/ZellijTerminalMultiplexer.java @@ -29,6 +29,11 @@ public class ZellijTerminalMultiplexer implements TerminalMultiplexer { return "https://zellij.dev/"; } + @Override + public boolean shouldSelect() throws Exception { + return LocalShell.getShell().view().findProgram("zellij").isPresent(); + } + @Override public void checkSupported(ShellControl sc) throws Exception { CommandSupport.isInPathOrThrow(sc, "zellij");