mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-09-30 00:56:56 +13:00
Rework terminal integrations
This commit is contained in:
parent
3293f27e6f
commit
91e1a37cdb
9 changed files with 707 additions and 347 deletions
|
@ -137,10 +137,10 @@ public class AppPrefs {
|
||||||
new AboutCategory(),
|
new AboutCategory(),
|
||||||
new SystemCategory(),
|
new SystemCategory(),
|
||||||
new AppearanceCategory(),
|
new AppearanceCategory(),
|
||||||
new SyncCategory(),
|
|
||||||
new VaultCategory(),
|
|
||||||
new TerminalCategory(),
|
new TerminalCategory(),
|
||||||
new EditorCategory(),
|
new EditorCategory(),
|
||||||
|
new SyncCategory(),
|
||||||
|
new VaultCategory(),
|
||||||
new LocalShellCategory(),
|
new LocalShellCategory(),
|
||||||
new SecurityCategory(),
|
new SecurityCategory(),
|
||||||
new PasswordManagerCategory(),
|
new PasswordManagerCategory(),
|
||||||
|
|
|
@ -5,9 +5,12 @@ import io.xpipe.app.core.AppI18n;
|
||||||
import io.xpipe.app.ext.PrefsChoiceValue;
|
import io.xpipe.app.ext.PrefsChoiceValue;
|
||||||
import io.xpipe.app.fxcomps.Comp;
|
import io.xpipe.app.fxcomps.Comp;
|
||||||
import io.xpipe.app.fxcomps.impl.ChoiceComp;
|
import io.xpipe.app.fxcomps.impl.ChoiceComp;
|
||||||
|
import io.xpipe.app.fxcomps.impl.HorizontalComp;
|
||||||
import io.xpipe.app.fxcomps.impl.StackComp;
|
import io.xpipe.app.fxcomps.impl.StackComp;
|
||||||
import io.xpipe.app.fxcomps.impl.TextFieldComp;
|
import io.xpipe.app.fxcomps.impl.TextFieldComp;
|
||||||
|
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||||
import io.xpipe.app.terminal.ExternalTerminalType;
|
import io.xpipe.app.terminal.ExternalTerminalType;
|
||||||
|
import io.xpipe.app.util.Hyperlinks;
|
||||||
import io.xpipe.app.util.OptionsBuilder;
|
import io.xpipe.app.util.OptionsBuilder;
|
||||||
import io.xpipe.app.util.TerminalLauncher;
|
import io.xpipe.app.util.TerminalLauncher;
|
||||||
import io.xpipe.app.util.ThreadHelper;
|
import io.xpipe.app.util.ThreadHelper;
|
||||||
|
@ -15,6 +18,8 @@ import io.xpipe.core.store.LocalStore;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.ListCell;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -26,6 +31,57 @@ public class TerminalCategory extends AppPrefsCategory {
|
||||||
return "terminal";
|
return "terminal";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Comp<?> terminalChoice() {
|
||||||
|
var prefs = AppPrefs.get();
|
||||||
|
var c = ChoiceComp.ofTranslatable(
|
||||||
|
prefs.terminalType, PrefsChoiceValue.getSupported(ExternalTerminalType.class), false);
|
||||||
|
c.apply(struc -> {
|
||||||
|
struc.get().setCellFactory(param -> {
|
||||||
|
return new ListCell<>() {
|
||||||
|
@Override
|
||||||
|
protected void updateItem(ExternalTerminalType item, boolean empty) {
|
||||||
|
super.updateItem(item, empty);
|
||||||
|
if (empty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setText(item.toTranslatedString().getValue());
|
||||||
|
if (item != ExternalTerminalType.CUSTOM) {
|
||||||
|
var graphic = new FontIcon(item.isRecommended() ? "mdi2c-check-decagram" : "mdi2a-alert-circle-check");
|
||||||
|
graphic.setFill(item.isRecommended() ? Color.GREEN : Color.ORANGE);
|
||||||
|
setGraphic(graphic);
|
||||||
|
} else {
|
||||||
|
setGraphic(new FontIcon("mdi2m-minus-circle"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var visit = new ButtonComp(AppI18n.observable("website"), new FontIcon("mdi2w-web"), () -> {
|
||||||
|
var t = prefs.terminalType().getValue();
|
||||||
|
if (t == null || t.getWebsite() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hyperlinks.open(t.getWebsite());
|
||||||
|
});
|
||||||
|
var visitVisible = BindingsHelper.persist(Bindings.createBooleanBinding(() -> {
|
||||||
|
var t = prefs.terminalType().getValue();
|
||||||
|
if (t == null || t.getWebsite() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}, prefs.terminalType()));
|
||||||
|
visit.visible(visitVisible);
|
||||||
|
|
||||||
|
return new HorizontalComp(List.of(c, visit)).apply(struc -> {
|
||||||
|
struc.get().setAlignment(Pos.CENTER_LEFT);
|
||||||
|
struc.get().setSpacing(10);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Comp<?> create() {
|
protected Comp<?> create() {
|
||||||
var prefs = AppPrefs.get();
|
var prefs = AppPrefs.get();
|
||||||
|
@ -46,8 +102,7 @@ public class TerminalCategory extends AppPrefsCategory {
|
||||||
.addTitle("terminalConfiguration")
|
.addTitle("terminalConfiguration")
|
||||||
.sub(new OptionsBuilder()
|
.sub(new OptionsBuilder()
|
||||||
.nameAndDescription("terminalEmulator")
|
.nameAndDescription("terminalEmulator")
|
||||||
.addComp(ChoiceComp.ofTranslatable(
|
.addComp(terminalChoice(), prefs.terminalType)
|
||||||
prefs.terminalType, PrefsChoiceValue.getSupported(ExternalTerminalType.class), false))
|
|
||||||
.nameAndDescription("customTerminalCommand")
|
.nameAndDescription("customTerminalCommand")
|
||||||
.addComp(new TextFieldComp(prefs.customTerminalCommand, true)
|
.addComp(new TextFieldComp(prefs.customTerminalCommand, true)
|
||||||
.apply(struc -> struc.get().setPromptText("myterminal -e $CMD"))
|
.apply(struc -> struc.get().setPromptText("myterminal -e $CMD"))
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
package io.xpipe.app.terminal;
|
||||||
|
|
||||||
|
import io.xpipe.app.util.LocalShell;
|
||||||
|
import io.xpipe.core.process.CommandBuilder;
|
||||||
|
|
||||||
|
public interface AlacrittyTerminalType extends ExternalTerminalType {
|
||||||
|
|
||||||
|
static class Windows extends SimplePathType implements AlacrittyTerminalType {
|
||||||
|
|
||||||
|
public Windows() {
|
||||||
|
super("app.alacritty", "alacritty", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CommandBuilder toCommand(LaunchConfiguration configuration) {
|
||||||
|
var b = CommandBuilder.of();
|
||||||
|
|
||||||
|
// if (configuration.getColor() != null) {
|
||||||
|
// b.add("-o")
|
||||||
|
// .addQuoted("colors.primary.background='%s'"
|
||||||
|
// .formatted(configuration.getColor().toHexString()));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Alacritty is bugged and will not accept arguments with spaces even if they are correctly passed/escaped
|
||||||
|
// So this will not work when the script file has spaces
|
||||||
|
return b.add("-t")
|
||||||
|
.addQuoted(configuration.getCleanTitle())
|
||||||
|
.add("-e")
|
||||||
|
.add(configuration.getDialectLaunchCommand());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Linux extends SimplePathType implements AlacrittyTerminalType {
|
||||||
|
|
||||||
|
public Linux() {
|
||||||
|
super("app.alacritty", "alacritty", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CommandBuilder toCommand(LaunchConfiguration configuration) {
|
||||||
|
return CommandBuilder.of()
|
||||||
|
.add("-t")
|
||||||
|
.addQuoted(configuration.getCleanTitle())
|
||||||
|
.add("-e")
|
||||||
|
.addFile(configuration.getScriptFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ExternalTerminalType ALACRITTY_WINDOWS = new Windows();
|
||||||
|
ExternalTerminalType ALACRITTY_LINUX = new Linux();
|
||||||
|
ExternalTerminalType ALACRITTY_MAC_OS = new MacOs();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default String getWebsite() {
|
||||||
|
return "https://github.com/alacritty/alacritty";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean isRecommended() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean supportsTabs() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean supportsColoredTitle() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MacOs extends MacOsType implements AlacrittyTerminalType {
|
||||||
|
|
||||||
|
public MacOs() {
|
||||||
|
super("app.alacritty", "Alacritty");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||||
|
LocalShell.getShell()
|
||||||
|
.executeSimpleCommand(CommandBuilder.of()
|
||||||
|
.add("open", "-a")
|
||||||
|
.addQuoted("Alacritty.app")
|
||||||
|
.add("-n", "--args", "-t")
|
||||||
|
.addQuoted(configuration.getCleanTitle())
|
||||||
|
.add("-e")
|
||||||
|
.addFile(configuration.getScriptFile()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package io.xpipe.app.terminal;
|
||||||
|
|
||||||
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
|
import io.xpipe.app.prefs.AppPrefs;
|
||||||
|
import io.xpipe.app.prefs.ExternalApplicationHelper;
|
||||||
|
import io.xpipe.app.prefs.ExternalApplicationType;
|
||||||
|
import io.xpipe.app.util.LocalShell;
|
||||||
|
import io.xpipe.core.process.OsType;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class CustomTerminalType extends ExternalApplicationType implements ExternalTerminalType {
|
||||||
|
|
||||||
|
public CustomTerminalType() {
|
||||||
|
super("app.custom");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsTabs() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsColoredTitle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||||
|
var custom = AppPrefs.get().customTerminalCommand().getValue();
|
||||||
|
if (custom == null || custom.isBlank()) {
|
||||||
|
throw ErrorEvent.expected(new IllegalStateException("No custom terminal command specified"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var format = custom.toLowerCase(Locale.ROOT).contains("$cmd") ? custom : custom + " $CMD";
|
||||||
|
try (var pc = LocalShell.getShell()) {
|
||||||
|
var toExecute = ExternalApplicationHelper.replaceFileArgument(format, "CMD", configuration.getScriptFile().toString());
|
||||||
|
// We can't be sure whether the command is blocking or not, so always make it not blocking
|
||||||
|
if (pc.getOsType().equals(OsType.WINDOWS)) {
|
||||||
|
toExecute = "start \"" + configuration.getCleanTitle() + "\" " + toExecute;
|
||||||
|
} else {
|
||||||
|
toExecute = "nohup " + toExecute + " </dev/null &>/dev/null & disown";
|
||||||
|
}
|
||||||
|
pc.executeSimpleCommand(toExecute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAvailable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,6 @@
|
||||||
package io.xpipe.app.terminal;
|
package io.xpipe.app.terminal;
|
||||||
|
|
||||||
import io.xpipe.app.ext.PrefsChoiceValue;
|
import io.xpipe.app.ext.PrefsChoiceValue;
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
|
||||||
import io.xpipe.app.prefs.AppPrefs;
|
|
||||||
import io.xpipe.app.prefs.ExternalApplicationHelper;
|
|
||||||
import io.xpipe.app.prefs.ExternalApplicationType;
|
import io.xpipe.app.prefs.ExternalApplicationType;
|
||||||
import io.xpipe.app.storage.DataStoreColor;
|
import io.xpipe.app.storage.DataStoreColor;
|
||||||
import io.xpipe.app.util.*;
|
import io.xpipe.app.util.*;
|
||||||
|
@ -23,6 +20,11 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
|
|
||||||
ExternalTerminalType CMD = new SimplePathType("app.cmd", "cmd.exe", true) {
|
ExternalTerminalType CMD = new SimplePathType("app.cmd", "cmd.exe", true) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -45,6 +47,11 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
|
|
||||||
ExternalTerminalType POWERSHELL = new SimplePathType("app.powershell", "powershell", true) {
|
ExternalTerminalType POWERSHELL = new SimplePathType("app.powershell", "powershell", true) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -80,6 +87,11 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
|
|
||||||
ExternalTerminalType PWSH = new SimplePathType("app.pwsh", "pwsh", true) {
|
ExternalTerminalType PWSH = new SimplePathType("app.pwsh", "pwsh", true) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -104,121 +116,17 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
ExternalTerminalType GNOME_TERMINAL = new PathCheckType("app.gnomeTerminal", "gnome-terminal", true) {
|
||||||
ExternalTerminalType ALACRITTY_WINDOWS = new SimplePathType("app.alacritty", "alacritty", false) {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsTabs() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsColoredTitle() {
|
public boolean supportsColoredTitle() {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected CommandBuilder toCommand(LaunchConfiguration configuration) {
|
|
||||||
var b = CommandBuilder.of();
|
|
||||||
if (configuration.getColor() != null) {
|
|
||||||
b.add("-o")
|
|
||||||
.addQuoted("colors.primary.background='%s'"
|
|
||||||
.formatted(configuration.getColor().toHexString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alacritty is bugged and will not accept arguments with spaces even if they are correctly passed/escaped
|
|
||||||
// So this will not work when the script file has spaces
|
|
||||||
return b.add("-t")
|
|
||||||
.addQuoted(configuration.getCleanTitle())
|
|
||||||
.add("-e")
|
|
||||||
.add(configuration.getDialectLaunchCommand());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ExternalTerminalType TABBY_WINDOWS = new WindowsType("app.tabby", "Tabby") {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsTabs() {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void execute(Path file, LaunchConfiguration configuration) throws Exception {
|
public boolean isRecommended() {
|
||||||
// Tabby has a very weird handling of output, even detaching with start does not prevent it from printing
|
|
||||||
if (configuration.getScriptDialect().equals(ShellDialects.CMD)) {
|
|
||||||
// It also freezes with any other input than .bat files, why?
|
|
||||||
LocalShell.getShell().executeSimpleCommand(CommandBuilder.of()
|
|
||||||
.addFile(file.toString())
|
|
||||||
.add("run")
|
|
||||||
.addFile(configuration.getScriptFile())
|
|
||||||
.discardOutput());
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalShell.getShell()
|
|
||||||
.executeSimpleCommand(CommandBuilder.of()
|
|
||||||
.addFile(file.toString())
|
|
||||||
.add("run")
|
|
||||||
.add(configuration.getDialectLaunchCommand())
|
|
||||||
.discardOutput());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Optional<Path> determineInstallation() {
|
|
||||||
var perUser = WindowsRegistry.readString(
|
|
||||||
WindowsRegistry.HKEY_CURRENT_USER,
|
|
||||||
"SOFTWARE\\71445fac-d6ef-5436-9da7-5a323762d7f5",
|
|
||||||
"InstallLocation")
|
|
||||||
.map(p -> p + "\\Tabby.exe")
|
|
||||||
.map(Path::of);
|
|
||||||
if (perUser.isPresent()) {
|
|
||||||
return perUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
var systemWide = WindowsRegistry.readString(
|
|
||||||
WindowsRegistry.HKEY_LOCAL_MACHINE,
|
|
||||||
"SOFTWARE\\71445fac-d6ef-5436-9da7-5a323762d7f5",
|
|
||||||
"InstallLocation")
|
|
||||||
.map(p -> p + "\\Tabby.exe")
|
|
||||||
.map(Path::of);
|
|
||||||
return systemWide;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ExternalTerminalType WEZ_WINDOWS = new WindowsType("app.wezterm", "wezterm-gui") {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsTabs() {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void execute(Path file, LaunchConfiguration configuration) throws Exception {
|
|
||||||
LocalShell.getShell().executeSimpleCommand(CommandBuilder.of().addFile(file.toString()).add("start").add(configuration.getDialectLaunchCommand()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Optional<Path> determineInstallation() {
|
|
||||||
Optional<String> launcherDir;
|
|
||||||
launcherDir = WindowsRegistry.readString(
|
|
||||||
WindowsRegistry.HKEY_LOCAL_MACHINE,
|
|
||||||
"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{BCF6F0DA-5B9A-408D-8562-F680AE6E1EAF}_is1",
|
|
||||||
"InstallLocation")
|
|
||||||
.map(p -> p + "\\wezterm-gui.exe");
|
|
||||||
return launcherDir.map(Path::of);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ExternalTerminalType WEZ_LINUX = new SimplePathType("app.wezterm", "wezterm-gui", true) {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsTabs() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected CommandBuilder toCommand(LaunchConfiguration configuration) {
|
|
||||||
return CommandBuilder.of().add("start").addFile(configuration.getScriptFile());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ExternalTerminalType GNOME_TERMINAL = new PathCheckType("app.gnomeTerminal", "gnome-terminal", true) {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -243,6 +151,11 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
};
|
};
|
||||||
ExternalTerminalType KONSOLE = new SimplePathType("app.konsole", "konsole", true) {
|
ExternalTerminalType KONSOLE = new SimplePathType("app.konsole", "konsole", true) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
return true;
|
return true;
|
||||||
|
@ -262,6 +175,16 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType XFCE = new SimplePathType("app.xfce", "xfce4-terminal", true) {
|
ExternalTerminalType XFCE = new SimplePathType("app.xfce", "xfce4-terminal", true) {
|
||||||
|
@Override
|
||||||
|
public boolean supportsColoredTitle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -278,6 +201,16 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType ELEMENTARY = new SimplePathType("app.elementaryTerminal", "io.elementary.terminal", true) {
|
ExternalTerminalType ELEMENTARY = new SimplePathType("app.elementaryTerminal", "io.elementary.terminal", true) {
|
||||||
|
@Override
|
||||||
|
public boolean supportsColoredTitle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -290,6 +223,16 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType TILIX = new SimplePathType("app.tilix", "tilix", true) {
|
ExternalTerminalType TILIX = new SimplePathType("app.tilix", "tilix", true) {
|
||||||
|
@Override
|
||||||
|
public boolean supportsColoredTitle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -306,6 +249,16 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType TERMINATOR = new SimplePathType("app.terminator", "terminator", true) {
|
ExternalTerminalType TERMINATOR = new SimplePathType("app.terminator", "terminator", true) {
|
||||||
|
@Override
|
||||||
|
public boolean supportsColoredTitle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -323,6 +276,16 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType TERMINOLOGY = new SimplePathType("app.terminology", "terminology", true) {
|
ExternalTerminalType TERMINOLOGY = new SimplePathType("app.terminology", "terminology", true) {
|
||||||
|
@Override
|
||||||
|
public boolean supportsColoredTitle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -340,6 +303,16 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType COOL_RETRO_TERM = new SimplePathType("app.coolRetroTerm", "cool-retro-term", true) {
|
ExternalTerminalType COOL_RETRO_TERM = new SimplePathType("app.coolRetroTerm", "cool-retro-term", true) {
|
||||||
|
@Override
|
||||||
|
public boolean supportsColoredTitle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -356,6 +329,16 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType GUAKE = new SimplePathType("app.guake", "guake", true) {
|
ExternalTerminalType GUAKE = new SimplePathType("app.guake", "guake", true) {
|
||||||
|
@Override
|
||||||
|
public boolean supportsColoredTitle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -372,28 +355,17 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
.addFile(configuration.getScriptFile());
|
.addFile(configuration.getScriptFile());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType ALACRITTY_LINUX = new SimplePathType("app.alacritty", "alacritty", true) {
|
ExternalTerminalType TILDA = new SimplePathType("app.tilda", "tilda", true) {
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsTabs() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsColoredTitle() {
|
public boolean supportsColoredTitle() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CommandBuilder toCommand(LaunchConfiguration configuration) {
|
public boolean isRecommended() {
|
||||||
return CommandBuilder.of()
|
return true;
|
||||||
.add("-t")
|
|
||||||
.addQuoted(configuration.getCleanTitle())
|
|
||||||
.add("-e")
|
|
||||||
.addFile(configuration.getScriptFile());
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
ExternalTerminalType TILDA = new SimplePathType("app.tilda", "tilda", true) {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -406,6 +378,16 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType XTERM = new SimplePathType("app.xterm", "xterm", true) {
|
ExternalTerminalType XTERM = new SimplePathType("app.xterm", "xterm", true) {
|
||||||
|
@Override
|
||||||
|
public boolean supportsColoredTitle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -422,6 +404,16 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType DEEPIN_TERMINAL = new SimplePathType("app.deepinTerminal", "deepin-terminal", true) {
|
ExternalTerminalType DEEPIN_TERMINAL = new SimplePathType("app.deepinTerminal", "deepin-terminal", true) {
|
||||||
|
@Override
|
||||||
|
public boolean supportsColoredTitle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -434,6 +426,16 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType Q_TERMINAL = new SimplePathType("app.qTerminal", "qterminal", true) {
|
ExternalTerminalType Q_TERMINAL = new SimplePathType("app.qTerminal", "qterminal", true) {
|
||||||
|
@Override
|
||||||
|
public boolean supportsColoredTitle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -446,6 +448,16 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType MACOS_TERMINAL = new MacOsType("app.macosTerminal", "Terminal") {
|
ExternalTerminalType MACOS_TERMINAL = new MacOsType("app.macosTerminal", "Terminal") {
|
||||||
|
@Override
|
||||||
|
public boolean supportsColoredTitle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -467,6 +479,16 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType ITERM2 = new MacOsType("app.iterm2", "iTerm") {
|
ExternalTerminalType ITERM2 = new MacOsType("app.iterm2", "iTerm") {
|
||||||
|
@Override
|
||||||
|
public boolean supportsColoredTitle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
return true;
|
return true;
|
||||||
|
@ -504,6 +526,16 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
};
|
};
|
||||||
ExternalTerminalType WARP = new MacOsType("app.warp", "Warp") {
|
ExternalTerminalType WARP = new MacOsType("app.warp", "Warp") {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsColoredTitle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecommended() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
return true;
|
return true;
|
||||||
|
@ -523,79 +555,18 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
.addFile(configuration.getScriptFile()));
|
.addFile(configuration.getScriptFile()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType TABBY_MAC_OS = new MacOsType("app.tabby", "Tabby") {
|
ExternalTerminalType CUSTOM = new CustomTerminalType();
|
||||||
@Override
|
|
||||||
public boolean supportsTabs() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
|
||||||
LocalShell.getShell()
|
|
||||||
.executeSimpleCommand(CommandBuilder.of()
|
|
||||||
.add("open", "-a")
|
|
||||||
.addQuoted("Tabby.app")
|
|
||||||
.add("-n", "--args", "run")
|
|
||||||
.addFile(configuration.getScriptFile()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ExternalTerminalType ALACRITTY_MACOS = new MacOsType("app.alacritty", "Alacritty") {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsTabs() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsColoredTitle() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
|
||||||
LocalShell.getShell()
|
|
||||||
.executeSimpleCommand(CommandBuilder.of()
|
|
||||||
.add("open", "-a")
|
|
||||||
.addQuoted("Alacritty.app")
|
|
||||||
.add("-n", "--args", "-t")
|
|
||||||
.addQuoted(configuration.getCleanTitle())
|
|
||||||
.add("-e")
|
|
||||||
.addFile(configuration.getScriptFile()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ExternalTerminalType WEZ_MACOS = new MacOsType("app.wezterm", "WezTerm") {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsTabs() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
|
||||||
var c = CommandBuilder.of()
|
|
||||||
.addFile(getApplicationPath()
|
|
||||||
.orElseThrow()
|
|
||||||
.resolve("Contents")
|
|
||||||
.resolve("MacOS")
|
|
||||||
.resolve("wezterm-gui")
|
|
||||||
.toString())
|
|
||||||
.add("start")
|
|
||||||
.add(configuration.getDialectLaunchCommand());
|
|
||||||
ExternalApplicationHelper.startAsync(c);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ExternalTerminalType CUSTOM = new CustomType();
|
|
||||||
List<ExternalTerminalType> WINDOWS_TERMINALS = List.of(
|
List<ExternalTerminalType> WINDOWS_TERMINALS = List.of(
|
||||||
TABBY_WINDOWS,
|
TabbyTerminalType.TABBY_WINDOWS,
|
||||||
ALACRITTY_WINDOWS,
|
AlacrittyTerminalType.ALACRITTY_WINDOWS,
|
||||||
WEZ_WINDOWS,
|
WezTerminalType.WEZTERM_WINDOWS,
|
||||||
WindowsTerminalType.WINDOWS_TERMINAL_PREVIEW,
|
WindowsTerminalType.WINDOWS_TERMINAL_PREVIEW,
|
||||||
WindowsTerminalType.WINDOWS_TERMINAL,
|
WindowsTerminalType.WINDOWS_TERMINAL,
|
||||||
CMD,
|
CMD,
|
||||||
PWSH,
|
PWSH,
|
||||||
POWERSHELL);
|
POWERSHELL);
|
||||||
List<ExternalTerminalType> LINUX_TERMINALS = List.of(
|
List<ExternalTerminalType> LINUX_TERMINALS = List.of(
|
||||||
WEZ_LINUX,
|
WezTerminalType.WEZTERM_LINUX,
|
||||||
KONSOLE,
|
KONSOLE,
|
||||||
XFCE,
|
XFCE,
|
||||||
ELEMENTARY,
|
ELEMENTARY,
|
||||||
|
@ -606,13 +577,13 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
TERMINOLOGY,
|
TERMINOLOGY,
|
||||||
COOL_RETRO_TERM,
|
COOL_RETRO_TERM,
|
||||||
GUAKE,
|
GUAKE,
|
||||||
ALACRITTY_LINUX,
|
AlacrittyTerminalType.ALACRITTY_LINUX,
|
||||||
TILDA,
|
TILDA,
|
||||||
XTERM,
|
XTERM,
|
||||||
DEEPIN_TERMINAL,
|
DEEPIN_TERMINAL,
|
||||||
Q_TERMINAL);
|
Q_TERMINAL);
|
||||||
List<ExternalTerminalType> MACOS_TERMINALS =
|
List<ExternalTerminalType> MACOS_TERMINALS =
|
||||||
List.of(ITERM2, TABBY_MAC_OS, ALACRITTY_MACOS, KittyTerminalType.KITTY_MACOS, WARP, WEZ_MACOS, MACOS_TERMINAL);
|
List.of(ITERM2, TabbyTerminalType.TABBY_MAC_OS, AlacrittyTerminalType.ALACRITTY_MAC_OS, KittyTerminalType.KITTY_MACOS, WARP, WezTerminalType.WEZTERM_MAC_OS, MACOS_TERMINAL);
|
||||||
|
|
||||||
@SuppressWarnings("TrivialFunctionalExpressionUsage")
|
@SuppressWarnings("TrivialFunctionalExpressionUsage")
|
||||||
List<ExternalTerminalType> ALL = ((Supplier<List<ExternalTerminalType>>) () -> {
|
List<ExternalTerminalType> ALL = ((Supplier<List<ExternalTerminalType>>) () -> {
|
||||||
|
@ -653,10 +624,14 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
|
|
||||||
boolean supportsTabs();
|
boolean supportsTabs();
|
||||||
|
|
||||||
default boolean supportsColoredTitle() {
|
default String getWebsite() {
|
||||||
return true;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isRecommended();
|
||||||
|
|
||||||
|
boolean supportsColoredTitle();
|
||||||
|
|
||||||
default boolean shouldClear() {
|
default boolean shouldClear() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -709,43 +684,6 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CustomType extends ExternalApplicationType implements ExternalTerminalType {
|
|
||||||
|
|
||||||
public CustomType() {
|
|
||||||
super("app.custom");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsTabs() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
|
||||||
var custom = AppPrefs.get().customTerminalCommand().getValue();
|
|
||||||
if (custom == null || custom.isBlank()) {
|
|
||||||
throw ErrorEvent.expected(new IllegalStateException("No custom terminal command specified"));
|
|
||||||
}
|
|
||||||
|
|
||||||
var format = custom.toLowerCase(Locale.ROOT).contains("$cmd") ? custom : custom + " $CMD";
|
|
||||||
try (var pc = LocalShell.getShell()) {
|
|
||||||
var toExecute = ExternalApplicationHelper.replaceFileArgument(format, "CMD", configuration.getScriptFile().toString());
|
|
||||||
// We can't be sure whether the command is blocking or not, so always make it not blocking
|
|
||||||
if (pc.getOsType().equals(OsType.WINDOWS)) {
|
|
||||||
toExecute = "start \"" + configuration.getCleanTitle() + "\" " + toExecute;
|
|
||||||
} else {
|
|
||||||
toExecute = "nohup " + toExecute + " </dev/null &>/dev/null & disown";
|
|
||||||
}
|
|
||||||
pc.executeSimpleCommand(toExecute);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAvailable() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class MacOsType extends ExternalApplicationType.MacApplication implements ExternalTerminalType {
|
abstract class MacOsType extends ExternalApplicationType.MacApplication implements ExternalTerminalType {
|
||||||
|
|
||||||
public MacOsType(String id, String applicationName) {
|
public MacOsType(String id, String applicationName) {
|
||||||
|
@ -776,4 +714,5 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
|
|
||||||
protected abstract CommandBuilder toCommand(LaunchConfiguration configuration) throws Exception;
|
protected abstract CommandBuilder toCommand(LaunchConfiguration configuration) throws Exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,83 +9,32 @@ import io.xpipe.core.process.CommandBuilder;
|
||||||
import io.xpipe.core.store.FilePath;
|
import io.xpipe.core.store.FilePath;
|
||||||
import io.xpipe.core.util.XPipeInstallation;
|
import io.xpipe.core.util.XPipeInstallation;
|
||||||
|
|
||||||
public class KittyTerminalType {
|
public interface KittyTerminalType extends ExternalTerminalType {
|
||||||
|
|
||||||
public static final ExternalTerminalType KITTY_LINUX = new ExternalTerminalType() {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
default boolean supportsColoredTitle() {
|
||||||
return "app.kitty";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsTabs() {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
default boolean isRecommended() {
|
||||||
try (var sc = LocalShell.getShell().start()) {
|
|
||||||
CommandSupport.isInPathOrThrow(sc, "kitty", "Kitty", null);
|
|
||||||
CommandSupport.isInPathOrThrow(sc, "socat", "socat", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
var toClose = prepare();
|
|
||||||
var socketWrite = CommandBuilder.of().add("socat", "-");
|
|
||||||
open(configuration, socketWrite);
|
|
||||||
if (toClose) {
|
|
||||||
closeInitial(socketWrite);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean prepare() throws Exception {
|
|
||||||
var socket = getSocket();
|
|
||||||
try (var sc = LocalShell.getShell().start()) {
|
|
||||||
if (sc.executeSimpleBooleanCommand("test -w " + sc.getShellDialect().fileArgument(socket))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sc.executeSimpleCommand(CommandBuilder.of().add("kitty").add("-o", "allow_remote_control=socket-only", "--listen-on", "unix:" + getSocket(), "--detach"));
|
|
||||||
ThreadHelper.sleep(1500);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final ExternalTerminalType KITTY_MACOS = new ExternalTerminalType.MacOsType("app.kitty", "kitty") {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsTabs() {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
default boolean supportsTabs() {
|
||||||
try (var sc = LocalShell.getShell().start()) {
|
|
||||||
CommandSupport.isInPathOrThrow(sc, "nc", "Netcat", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
var toClose = prepare();
|
|
||||||
var socketWrite = CommandBuilder.of().add("nc", "-U");
|
|
||||||
open(configuration, socketWrite);
|
|
||||||
if (toClose) {
|
|
||||||
closeInitial(socketWrite);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean prepare() throws Exception {
|
|
||||||
var socket = getSocket();
|
|
||||||
try (var sc = LocalShell.getShell().start()) {
|
|
||||||
if (sc.executeSimpleBooleanCommand("test -w " + sc.getShellDialect().fileArgument(socket))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sc.executeSimpleCommand(CommandBuilder.of().add("open", "-a", "kitty.app", "--args").add("-o", "allow_remote_control=socket-only", "--listen-on", "unix:" + getSocket()));
|
|
||||||
ThreadHelper.sleep(1000);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default String getWebsite() {
|
||||||
|
return "https://github.com/kovidgoyal/kitty";
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
public static final ExternalTerminalType KITTY_LINUX = new Linux();
|
||||||
|
|
||||||
|
public static final ExternalTerminalType KITTY_MACOS = new MacOs();
|
||||||
|
|
||||||
private static FilePath getSocket() throws Exception {
|
private static FilePath getSocket() throws Exception {
|
||||||
try (var sc = LocalShell.getShell().start()) {
|
try (var sc = LocalShell.getShell().start()) {
|
||||||
|
@ -132,4 +81,72 @@ public class KittyTerminalType {
|
||||||
sc.executeSimpleCommand(CommandBuilder.of().add("echo", "-en", echoString, "|").add(socketWrite).addFile(getSocket()));
|
sc.executeSimpleCommand(CommandBuilder.of().add("echo", "-en", echoString, "|").add(socketWrite).addFile(getSocket()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Linux implements KittyTerminalType {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "app.kitty";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||||
|
try (var sc = LocalShell.getShell().start()) {
|
||||||
|
CommandSupport.isInPathOrThrow(sc, "kitty", "Kitty", null);
|
||||||
|
CommandSupport.isInPathOrThrow(sc, "socat", "socat", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
var toClose = prepare();
|
||||||
|
var socketWrite = CommandBuilder.of().add("socat", "-");
|
||||||
|
open(configuration, socketWrite);
|
||||||
|
if (toClose) {
|
||||||
|
closeInitial(socketWrite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean prepare() throws Exception {
|
||||||
|
var socket = getSocket();
|
||||||
|
try (var sc = LocalShell.getShell().start()) {
|
||||||
|
if (sc.executeSimpleBooleanCommand("test -w " + sc.getShellDialect().fileArgument(socket))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sc.executeSimpleCommand(CommandBuilder.of().add("kitty").add("-o", "allow_remote_control=socket-only", "--listen-on", "unix:" + getSocket(), "--detach"));
|
||||||
|
ThreadHelper.sleep(1500);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MacOs extends MacOsType implements KittyTerminalType {
|
||||||
|
|
||||||
|
public MacOs() {super("app.kitty", "kitty");}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||||
|
try (var sc = LocalShell.getShell().start()) {
|
||||||
|
CommandSupport.isInPathOrThrow(sc, "nc", "Netcat", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
var toClose = prepare();
|
||||||
|
var socketWrite = CommandBuilder.of().add("nc", "-U");
|
||||||
|
open(configuration, socketWrite);
|
||||||
|
if (toClose) {
|
||||||
|
closeInitial(socketWrite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean prepare() throws Exception {
|
||||||
|
var socket = getSocket();
|
||||||
|
try (var sc = LocalShell.getShell().start()) {
|
||||||
|
if (sc.executeSimpleBooleanCommand("test -w " + sc.getShellDialect().fileArgument(socket))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sc.executeSimpleCommand(CommandBuilder.of().add("open", "-a", "kitty.app", "--args").add("-o", "allow_remote_control=socket-only", "--listen-on", "unix:" + getSocket()));
|
||||||
|
ThreadHelper.sleep(1000);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
package io.xpipe.app.terminal;
|
||||||
|
|
||||||
|
import io.xpipe.app.util.LocalShell;
|
||||||
|
import io.xpipe.app.util.WindowsRegistry;
|
||||||
|
import io.xpipe.core.process.CommandBuilder;
|
||||||
|
import io.xpipe.core.process.ShellDialects;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface TabbyTerminalType extends ExternalTerminalType {
|
||||||
|
|
||||||
|
static class Windows extends ExternalTerminalType.WindowsType implements TabbyTerminalType {
|
||||||
|
|
||||||
|
public Windows() {
|
||||||
|
super("app.tabby", "Tabby.exe");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void execute(Path file, LaunchConfiguration configuration) throws Exception {
|
||||||
|
// Tabby has a very weird handling of output, even detaching with start does not prevent it from printing
|
||||||
|
if (configuration.getScriptDialect().equals(ShellDialects.CMD)) {
|
||||||
|
// It also freezes with any other input than .bat files, why?
|
||||||
|
LocalShell.getShell().executeSimpleCommand(CommandBuilder.of()
|
||||||
|
.addFile(file.toString())
|
||||||
|
.add("run")
|
||||||
|
.addFile(configuration.getScriptFile())
|
||||||
|
.discardOutput());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is probably not going to work as it does not launch a bat file
|
||||||
|
LocalShell.getShell().executeSimpleCommand(CommandBuilder.of()
|
||||||
|
.addFile(file.toString())
|
||||||
|
.add("run")
|
||||||
|
.add(configuration.getDialectLaunchCommand())
|
||||||
|
.discardOutput());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Optional<Path> determineInstallation() {
|
||||||
|
var perUser = WindowsRegistry.readString(WindowsRegistry.HKEY_CURRENT_USER, "SOFTWARE\\71445fac-d6ef-5436-9da7-5a323762d7f5",
|
||||||
|
"InstallLocation").map(p -> p + "\\Tabby.exe").map(Path::of);
|
||||||
|
if (perUser.isPresent()) {
|
||||||
|
return perUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
var systemWide = WindowsRegistry.readString(WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\71445fac-d6ef-5436-9da7-5a323762d7f5",
|
||||||
|
"InstallLocation").map(p -> p + "\\Tabby.exe").map(Path::of);
|
||||||
|
return systemWide;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ExternalTerminalType TABBY_WINDOWS = new Windows();
|
||||||
|
|
||||||
|
ExternalTerminalType TABBY_MAC_OS = new MacOs();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default String getWebsite() {
|
||||||
|
return "https://tabby.sh";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean isRecommended() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean supportsTabs() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean supportsColoredTitle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MacOs extends MacOsType implements TabbyTerminalType {
|
||||||
|
|
||||||
|
public MacOs() {
|
||||||
|
super("app.tabby", "Tabby");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||||
|
LocalShell.getShell()
|
||||||
|
.executeSimpleCommand(CommandBuilder.of()
|
||||||
|
.add("open", "-a")
|
||||||
|
.addQuoted("Tabby.app")
|
||||||
|
.add("-n", "--args", "run")
|
||||||
|
.addFile(configuration.getScriptFile()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
94
app/src/main/java/io/xpipe/app/terminal/WezTerminalType.java
Normal file
94
app/src/main/java/io/xpipe/app/terminal/WezTerminalType.java
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
package io.xpipe.app.terminal;
|
||||||
|
|
||||||
|
import io.xpipe.app.prefs.ExternalApplicationHelper;
|
||||||
|
import io.xpipe.app.util.LocalShell;
|
||||||
|
import io.xpipe.app.util.WindowsRegistry;
|
||||||
|
import io.xpipe.core.process.CommandBuilder;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface WezTerminalType extends ExternalTerminalType {
|
||||||
|
|
||||||
|
static class Windows extends WindowsType implements WezTerminalType {
|
||||||
|
|
||||||
|
public Windows() {
|
||||||
|
super("app.wezterm", "wezterm-gui");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void execute(Path file, LaunchConfiguration configuration) throws Exception {
|
||||||
|
LocalShell.getShell().executeSimpleCommand(CommandBuilder.of().addFile(file.toString()).add("start").add(configuration.getDialectLaunchCommand()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Optional<Path> determineInstallation() {
|
||||||
|
Optional<String> launcherDir;
|
||||||
|
launcherDir = WindowsRegistry.readString(
|
||||||
|
WindowsRegistry.HKEY_LOCAL_MACHINE,
|
||||||
|
"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{BCF6F0DA-5B9A-408D-8562-F680AE6E1EAF}_is1",
|
||||||
|
"InstallLocation")
|
||||||
|
.map(p -> p + "\\wezterm-gui.exe");
|
||||||
|
return launcherDir.map(Path::of);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Linux extends SimplePathType implements WezTerminalType {
|
||||||
|
|
||||||
|
public Linux() {
|
||||||
|
super("app.wezterm", "wezterm-gui", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CommandBuilder toCommand(LaunchConfiguration configuration) {
|
||||||
|
return CommandBuilder.of().add("start").addFile(configuration.getScriptFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ExternalTerminalType WEZTERM_WINDOWS = new Windows();
|
||||||
|
ExternalTerminalType WEZTERM_LINUX = new Linux();
|
||||||
|
ExternalTerminalType WEZTERM_MAC_OS = new MacOs();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default String getWebsite() {
|
||||||
|
return "https://wezfurlong.org/wezterm/index.html";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean isRecommended() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean supportsTabs() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean supportsColoredTitle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MacOs extends MacOsType implements WezTerminalType {
|
||||||
|
|
||||||
|
public MacOs() {
|
||||||
|
super("app.wezterm", "WezTerm");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||||
|
var c = CommandBuilder.of()
|
||||||
|
.addFile(getApplicationPath()
|
||||||
|
.orElseThrow()
|
||||||
|
.resolve("Contents")
|
||||||
|
.resolve("MacOS")
|
||||||
|
.resolve("wezterm-gui")
|
||||||
|
.toString())
|
||||||
|
.add("start")
|
||||||
|
.add(configuration.getDialectLaunchCommand());
|
||||||
|
ExternalApplicationHelper.startAsync(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,41 +8,70 @@ import io.xpipe.core.store.FileNames;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public class WindowsTerminalType {
|
public interface WindowsTerminalType extends ExternalTerminalType {
|
||||||
|
|
||||||
public static final ExternalTerminalType WINDOWS_TERMINAL =
|
@Override
|
||||||
new ExternalTerminalType.SimplePathType("app.windowsTerminal", "wt.exe", false) {
|
default boolean isRecommended() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean supportsTabs() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean supportsColoredTitle() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final ExternalTerminalType WINDOWS_TERMINAL = new Standard();
|
||||||
|
|
||||||
|
public static final ExternalTerminalType WINDOWS_TERMINAL_PREVIEW = new Preview();
|
||||||
|
|
||||||
|
private static CommandBuilder toCommand(ExternalTerminalType.LaunchConfiguration configuration) throws Exception {
|
||||||
|
// A weird behavior in Windows Terminal causes the trailing
|
||||||
|
// backslash of a filepath to escape the closing quote in the title argument
|
||||||
|
// So just remove that slash
|
||||||
|
var fixedName = FileNames.removeTrailingSlash(configuration.getColoredTitle());
|
||||||
|
|
||||||
|
var toExec = !ShellDialects.isPowershell(LocalShell.getShell())
|
||||||
|
? CommandBuilder.of().addFile(configuration.getScriptFile())
|
||||||
|
: CommandBuilder.of()
|
||||||
|
.add("powershell", "-ExecutionPolicy", "Bypass", "-File")
|
||||||
|
.addFile(configuration.getScriptFile());
|
||||||
|
var cmd = CommandBuilder.of().add("-w", "1", "nt");
|
||||||
|
|
||||||
|
if (configuration.getColor() != null) {
|
||||||
|
cmd.add("--tabColor").addQuoted(configuration.getColor().toHexString());
|
||||||
|
}
|
||||||
|
return cmd.add("--title").addQuoted(fixedName).add(toExec);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Standard extends SimplePathType implements WindowsTerminalType {
|
||||||
|
|
||||||
|
public Standard() {super("app.windowsTerminal", "wt.exe", false);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getWebsite() {
|
||||||
|
return "https://aka.ms/terminal-preview";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CommandBuilder toCommand(LaunchConfiguration configuration) throws Exception {
|
protected CommandBuilder toCommand(LaunchConfiguration configuration) throws Exception {
|
||||||
return WindowsTerminalType.toCommand(configuration);
|
return WindowsTerminalType.toCommand(configuration);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Preview implements WindowsTerminalType {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public String getWebsite() {
|
||||||
return true;
|
return "https://aka.ms/terminal";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsColoredTitle() {
|
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final ExternalTerminalType WINDOWS_TERMINAL_PREVIEW = new ExternalTerminalType() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsTabs() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsColoredTitle() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void launch(ExternalTerminalType.LaunchConfiguration configuration) throws Exception {
|
|
||||||
LocalShell.getShell()
|
LocalShell.getShell()
|
||||||
.executeSimpleCommand(
|
.executeSimpleCommand(
|
||||||
CommandBuilder.of().addFile(getPath().toString()).add(toCommand(configuration)));
|
CommandBuilder.of().addFile(getPath().toString()).add(toCommand(configuration)));
|
||||||
|
@ -63,24 +92,5 @@ public class WindowsTerminalType {
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return "app.windowsTerminalPreview";
|
return "app.windowsTerminalPreview";
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
private static CommandBuilder toCommand(ExternalTerminalType.LaunchConfiguration configuration) throws Exception {
|
|
||||||
// A weird behavior in Windows Terminal causes the trailing
|
|
||||||
// backslash of a filepath to escape the closing quote in the title argument
|
|
||||||
// So just remove that slash
|
|
||||||
var fixedName = FileNames.removeTrailingSlash(configuration.getColoredTitle());
|
|
||||||
|
|
||||||
var toExec = !ShellDialects.isPowershell(LocalShell.getShell())
|
|
||||||
? CommandBuilder.of().addFile(configuration.getScriptFile())
|
|
||||||
: CommandBuilder.of()
|
|
||||||
.add("powershell", "-ExecutionPolicy", "Bypass", "-File")
|
|
||||||
.addFile(configuration.getScriptFile());
|
|
||||||
var cmd = CommandBuilder.of().add("-w", "1", "nt");
|
|
||||||
|
|
||||||
if (configuration.getColor() != null) {
|
|
||||||
cmd.add("--tabColor").addQuoted(configuration.getColor().toHexString());
|
|
||||||
}
|
|
||||||
return cmd.add("--title").addQuoted(fixedName).add(toExec);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue