diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5b24c4759..d97236eab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,8 +20,8 @@ If you are on Linux or macOS, you can easily accomplish that by using [SDKMAN](h ```bash curl -s "https://get.sdkman.io" | bash . "$HOME/.sdkman/bin/sdkman-init.sh" -sdk install java 25.0.1-graalce -sdk default java 25.0.1-graalce +sdk install java 25.0.2-graalce +sdk default java 25.0.2-graalce ``` On Windows, you have to manually install a JDK, e.g. from [Adoptium](https://adoptium.net/temurin/releases/?version=25). diff --git a/app/src/main/java/io/xpipe/app/browser/menu/impl/GradleRunMenuProvider.java b/app/src/main/java/io/xpipe/app/browser/menu/impl/GradleRunMenuProvider.java new file mode 100644 index 000000000..0b44ec376 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/browser/menu/impl/GradleRunMenuProvider.java @@ -0,0 +1,93 @@ +package io.xpipe.app.browser.menu.impl; + +import io.xpipe.app.browser.file.BrowserEntry; +import io.xpipe.app.browser.file.BrowserFileSystemTabModel; +import io.xpipe.app.browser.menu.BrowserMenuCategory; +import io.xpipe.app.browser.menu.BrowserMenuLeafProvider; +import io.xpipe.app.comp.RegionBuilder; +import io.xpipe.app.comp.base.ModalOverlay; +import io.xpipe.app.core.AppI18n; +import io.xpipe.app.ext.FileKind; +import io.xpipe.app.platform.LabelGraphic; +import io.xpipe.app.process.CommandBuilder; +import io.xpipe.core.OsType; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.value.ObservableValue; +import javafx.scene.control.TextField; + +import java.util.List; + +public class GradleRunMenuProvider implements BrowserMenuLeafProvider { + + @Override + public boolean isApplicable(BrowserFileSystemTabModel model, List entries) { + if (model.getFileSystem().getShell().isEmpty()) { + return false; + } + + if (entries.size() != 1) { + return false; + } + + if (entries.getFirst().getRawFileEntry().getKind() != FileKind.FILE) { + return false; + } + + OsType.Any osType = model.getFileSystem().getShell().orElseThrow().getOsType(); + var ext = switch (osType) { + case OsType.Windows ignored -> "gradlew.bat"; + default -> "gradlew"; + }; + + if (!entries.getFirst().getFileName().equalsIgnoreCase(ext)) { + return false; + } + + return true; + } + + @Override + public ObservableValue getName(BrowserFileSystemTabModel model, List entries) { + return AppI18n.observable("runTask"); + } + + @Override + public BrowserMenuCategory getCategory() { + return BrowserMenuCategory.CUSTOM; + } + + @Override + public LabelGraphic getIcon() { + return new LabelGraphic.IconGraphic("mdi2e-elephant"); + } + + @Override + public void execute(BrowserFileSystemTabModel model, List entries) { + var tasks = new SimpleStringProperty(); + var modal = ModalOverlay.of( + "gradleTasks", + RegionBuilder.of(() -> { + var creationName = new TextField(); + creationName.textProperty().bindBidirectional(tasks); + return creationName; + }) + .prefWidth(350)); + modal.withDefaultButtons(() -> { + var fixedTasks = tasks.getValue(); + if (fixedTasks == null) { + return; + } + + var parent = entries.getFirst().getRawFileEntry().getPath().getParent(); + var command = model.getFileSystem().getShell().orElseThrow().command(CommandBuilder.of() + .add("sh") + .addFile(entries.getFirst().getRawFileEntry().getPath()) + .add(fixedTasks) + ); + + model.openTerminalAsync(fixedTasks, parent, command, true); + }); + modal.show(); + } + +} diff --git a/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java b/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java index aef2e1e8c..0e3537356 100644 --- a/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java +++ b/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java @@ -193,6 +193,16 @@ public class AppMainWindow { shown = true; } + public void hide() { + PlatformThread.runLaterIfNeeded(() -> { + if (!stage.isShowing()) { + return; + } + + stage.hide(); + }); + } + public void focus() { if (AppPrefs.get() != null && !AppPrefs.get().focusWindowOnNotifications().get()) { diff --git a/app/src/main/java/io/xpipe/app/platform/PlatformState.java b/app/src/main/java/io/xpipe/app/platform/PlatformState.java index 65034da67..101e48c48 100644 --- a/app/src/main/java/io/xpipe/app/platform/PlatformState.java +++ b/app/src/main/java/io/xpipe/app/platform/PlatformState.java @@ -113,12 +113,13 @@ public enum PlatformState { } } - if (SystemUtils.IS_OS_WINDOWS) { - // This is primarily intended to fix Windows unified stage transparency issues - // (https://bugs.openjdk.org/browse/JDK-8329382) - // But apparently it can also occur without a custom stage on Windows - System.setProperty("prism.forceUploadingPainter", "true"); - } + // This issue is now fixed in 27-ea+4 + // if (SystemUtils.IS_OS_WINDOWS) { + // This is primarily intended to fix Windows unified stage transparency issues + // (https://bugs.openjdk.org/browse/JDK-8329382) + // But apparently it can also occur without a custom stage on Windows + // System.setProperty("prism.forceUploadingPainter", "true"); + // } if (AppPrefs.get() != null && AppPrefs.get().disableHardwareAcceleration().get()) { diff --git a/app/src/main/java/io/xpipe/app/platform/PlatformThread.java b/app/src/main/java/io/xpipe/app/platform/PlatformThread.java index 26646a0d9..f79ce42e8 100644 --- a/app/src/main/java/io/xpipe/app/platform/PlatformThread.java +++ b/app/src/main/java/io/xpipe/app/platform/PlatformThread.java @@ -266,7 +266,8 @@ public class PlatformThread { return false; } - if (AppOperationMode.isInShutdown()) { + // Some other components might already be disposed + if (AppOperationMode.isInShutdownHook()) { return false; } diff --git a/app/src/main/java/io/xpipe/app/prefs/ExternalEditorType.java b/app/src/main/java/io/xpipe/app/prefs/ExternalEditorType.java index 795ae83b6..6b0f194ad 100644 --- a/app/src/main/java/io/xpipe/app/prefs/ExternalEditorType.java +++ b/app/src/main/java/io/xpipe/app/prefs/ExternalEditorType.java @@ -23,7 +23,6 @@ import java.util.Optional; import java.util.function.Supplier; public interface ExternalEditorType extends PrefsChoiceValue { - ExternalEditorType NOTEPAD = new WindowsType() { @Override @@ -280,6 +279,70 @@ public interface ExternalEditorType extends PrefsChoiceValue { LinuxPathType KIRO_LINUX = new LinuxPathType("app.kiro", "kiro", "https://kiro.dev/"); + LinuxType NEOVIM_LINUX = new LinuxType("app.neovim", "nvim", "https://neovim.io/", null) { + @Override + public void launch(Path file) throws Exception { + TerminalLaunch.builder() + .title(file.toString()) + .localScript(sc -> new ShellScript(CommandBuilder.of() + .addFile(getExecutable()) + .addFile(file.toString()) + .buildFull(sc))) + .logIfEnabled(false) + .preferTabs(false) + .pauseOnExit(false) + .launch(); + } + }; + + WindowsType NEOVIM_WINDOWS = new WindowsType() { + @Override + public String getId() { + return "app.neovim"; + } + + @Override + public boolean detach() { + return false; + } + + @Override + public String getExecutable() { + return "nvim"; + } + + + @Override + public String getWebsite() { + return "https://neovim.io/"; + } + + + @Override + public Optional determineInstallation() { + var programFiles = AppSystemInfo.ofWindows().getProgramFiles().resolve("Neovim", "bin").resolve("nvim.exe"); + if (Files.exists(programFiles)) { + return Optional.of(programFiles); + } + return Optional.empty(); + } + + @Override + public void launch(Path file) throws Exception { + TerminalLaunch.builder() + .title(file.toString()) + .localScript(sc -> new ShellScript(CommandBuilder.of() + .addFile(findExecutable().toString()) + .addFile(file) + .buildFull(sc))) + .logIfEnabled(false) + .preferTabs(false) + .pauseOnExit(false) + .launch(); + } + + }; + WindowsType ZED_WINDOWS = new WindowsType() { @Override @@ -356,6 +419,21 @@ public interface ExternalEditorType extends PrefsChoiceValue { ExternalEditorType WINDSURF_MACOS = new MacOsEditor("app.windsurf", "Windsurf", "https://windsurf.com/editor"); ExternalEditorType KIRO_MACOS = new MacOsEditor("app.kiro", "Kiro", "https://kiro.dev/"); ExternalEditorType TRAE_MACOS = new MacOsEditor("app.trae", "Trae", "https://www.trae.ai/"); + ExternalEditorType NEOVIM_MACOS = new MacOsEditor("app.neovim", "Neovim", "https://neovim.io/") { + @Override + public void launch(Path file) throws Exception { + TerminalLaunch.builder() + .title(file.toString()) + .localScript(sc -> new ShellScript(CommandBuilder.of() + .addFile("nvim") + .addFile(file.toString()) + .buildFull(sc))) + .logIfEnabled(false) + .preferTabs(false) + .pauseOnExit(false) + .launch(); + } + }; ExternalEditorType CUSTOM = new ExternalEditorType() { @Override @@ -380,6 +458,7 @@ public interface ExternalEditorType extends PrefsChoiceValue { .localScript(sc -> new ShellScript(command.buildFull(sc))) .logIfEnabled(false) .preferTabs(false) + .pauseOnExit(false) .launch(); } else { ExternalApplicationHelper.startAsync(command); @@ -422,7 +501,8 @@ public interface ExternalEditorType extends PrefsChoiceValue { VSCODE_INSIDERS_WINDOWS, VSCODE_WINDOWS, NOTEPADPLUSPLUS, - NOTEPAD); + NOTEPAD, + NEOVIM_WINDOWS); List LINUX_EDITORS = List.of( ExternalEditorType.WINDSURF_LINUX, ExternalEditorType.KIRO_LINUX, @@ -435,6 +515,7 @@ public interface ExternalEditorType extends PrefsChoiceValue { PLUMA, LEAFPAD, MOUSEPAD, + NEOVIM_LINUX, GNOME, ExternalEditorType.COSMIC_EDIT, ExternalEditorType.WESTON_EDITOR, @@ -451,6 +532,7 @@ public interface ExternalEditorType extends PrefsChoiceValue { VSCODE_MACOS, SUBLIME_MACOS, ZED_MACOS, + NEOVIM_MACOS, TEXT_EDIT); List CROSS_PLATFORM_EDITORS = List.of(FLEET, INTELLIJ, PYCHARM, WEBSTORM, CLION); 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 e42691ff6..13eb99a4f 100644 --- a/app/src/main/java/io/xpipe/app/prefs/TerminalCategory.java +++ b/app/src/main/java/io/xpipe/app/prefs/TerminalCategory.java @@ -117,7 +117,7 @@ public class TerminalCategory extends AppPrefsCategory { "If you can read this, the terminal integration works", false))) .preferTabs(false) .logIfEnabled(false) - .alwaysKeepOpen(true) + .pauseOnExit(true) .launch(); } }); diff --git a/app/src/main/java/io/xpipe/app/prefs/TroubleshootCategory.java b/app/src/main/java/io/xpipe/app/prefs/TroubleshootCategory.java index 47cc3eabe..fbc21d7bc 100644 --- a/app/src/main/java/io/xpipe/app/prefs/TroubleshootCategory.java +++ b/app/src/main/java/io/xpipe/app/prefs/TroubleshootCategory.java @@ -73,7 +73,7 @@ public class TroubleshootCategory extends AppPrefsCategory { var script = AppInstallation.ofCurrent().getDaemonDebugScriptPath(); TerminalLaunch.builder() .title(AppNames.ofCurrent().getName() + " Debug") - .alwaysKeepOpen(true) + .pauseOnExit(true) .localScript(sc -> new ShellScript( sc.getShellDialect().runScriptCommand(sc, script.toString()))) .launch(); diff --git a/app/src/main/java/io/xpipe/app/pwman/BitwardenPasswordManager.java b/app/src/main/java/io/xpipe/app/pwman/BitwardenPasswordManager.java index aec36f929..cf51d02a8 100644 --- a/app/src/main/java/io/xpipe/app/pwman/BitwardenPasswordManager.java +++ b/app/src/main/java/io/xpipe/app/pwman/BitwardenPasswordManager.java @@ -92,7 +92,7 @@ public class BitwardenPasswordManager implements PasswordManager { .localScript(script) .logIfEnabled(false) .preferTabs(false) - .alwaysKeepOpen(true) + .pauseOnExit(true) .launch(); return null; } diff --git a/app/src/main/java/io/xpipe/app/pwman/DashlanePasswordManager.java b/app/src/main/java/io/xpipe/app/pwman/DashlanePasswordManager.java index e2155eade..267bd2d31 100644 --- a/app/src/main/java/io/xpipe/app/pwman/DashlanePasswordManager.java +++ b/app/src/main/java/io/xpipe/app/pwman/DashlanePasswordManager.java @@ -48,7 +48,7 @@ public class DashlanePasswordManager implements PasswordManager { .title("Dashlane login") .localScript(script) .logIfEnabled(false) - .alwaysKeepOpen(true) + .pauseOnExit(true) .launch(); return null; } diff --git a/app/src/main/java/io/xpipe/app/pwman/KeeperPasswordManager.java b/app/src/main/java/io/xpipe/app/pwman/KeeperPasswordManager.java index 01c552ce0..814dba92d 100644 --- a/app/src/main/java/io/xpipe/app/pwman/KeeperPasswordManager.java +++ b/app/src/main/java/io/xpipe/app/pwman/KeeperPasswordManager.java @@ -105,7 +105,7 @@ public class KeeperPasswordManager implements PasswordManager { .title("Keeper login") .localScript(script) .logIfEnabled(false) - .alwaysKeepOpen(true) + .pauseOnExit(true) .launch(); return null; } diff --git a/app/src/main/java/io/xpipe/app/pwman/LastpassPasswordManager.java b/app/src/main/java/io/xpipe/app/pwman/LastpassPasswordManager.java index e9c006923..7b4139fb0 100644 --- a/app/src/main/java/io/xpipe/app/pwman/LastpassPasswordManager.java +++ b/app/src/main/java/io/xpipe/app/pwman/LastpassPasswordManager.java @@ -54,7 +54,7 @@ public class LastpassPasswordManager implements PasswordManager { .title("LastPass login") .localScript(script) .logIfEnabled(false) - .alwaysKeepOpen(true) + .pauseOnExit(true) .launch(); } return null; diff --git a/app/src/main/java/io/xpipe/app/terminal/TerminalDockHubComp.java b/app/src/main/java/io/xpipe/app/terminal/TerminalDockHubComp.java index 1337d672b..f416f7e8e 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TerminalDockHubComp.java +++ b/app/src/main/java/io/xpipe/app/terminal/TerminalDockHubComp.java @@ -56,7 +56,7 @@ public class TerminalDockHubComp extends SimpleRegionBuilder { Platform.runLater(() -> { update(stack); }); - }, Duration.ofMillis(100)); + }, Duration.ofMillis(500)); } }; var update = new ChangeListener() { diff --git a/app/src/main/java/io/xpipe/app/terminal/TerminalDockHubManager.java b/app/src/main/java/io/xpipe/app/terminal/TerminalDockHubManager.java index 048b58129..011c5a36f 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TerminalDockHubManager.java +++ b/app/src/main/java/io/xpipe/app/terminal/TerminalDockHubManager.java @@ -110,9 +110,9 @@ public class TerminalDockHubManager { private final AppLayoutModel.QueueEntry queueEntry = new AppLayoutModel.QueueEntry( AppI18n.observable("toggleTerminalDock"), new LabelGraphic.NodeGraphic(() -> { var inner = new FontIcon(); - inner.iconCodeProperty().bind(PlatformThread.sync(Bindings.createObjectBinding(() -> { - return detached.get() || minimized.get() || !showing.get() ? MaterialDesignC.CONSOLE_LINE : MaterialDesignC.CONSOLE; - }, detached, minimized, showing))); + inner.iconCodeProperty().bind(Bindings.createObjectBinding(() -> { + return detached.get() || minimized.get() ? MaterialDesignC.CONSOLE_LINE : MaterialDesignC.CONSOLE; + }, detached, minimized)); inner.getStyleClass().add("graphic"); inner.getStyleClass().add("terminal-dock-button"); return inner; diff --git a/app/src/main/java/io/xpipe/app/terminal/TerminalDockView.java b/app/src/main/java/io/xpipe/app/terminal/TerminalDockView.java index 6a6c07e43..e2904aeb8 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TerminalDockView.java +++ b/app/src/main/java/io/xpipe/app/terminal/TerminalDockView.java @@ -72,6 +72,8 @@ public class TerminalDockView { public synchronized void trackTerminal(ControllableTerminalSession terminal, boolean dock) { if (viewActive && dock && viewBounds != null) { + terminal.own(); + // Bring main window to foreground since initial launch NativeWinWindowControl.MAIN_WINDOW.activate(); @@ -79,11 +81,6 @@ public class TerminalDockView { // We always want to show the terminal though terminal.show(); - terminal.own(); - - // Bring terminal window in front of main window - terminal.focus(); - terminal.updatePosition(windowBoundsFunction.apply(viewBounds)); updateCustomBounds(); } diff --git a/app/src/main/java/io/xpipe/app/terminal/TerminalLaunch.java b/app/src/main/java/io/xpipe/app/terminal/TerminalLaunch.java index f5f5f89b2..4894d45a6 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TerminalLaunch.java +++ b/app/src/main/java/io/xpipe/app/terminal/TerminalLaunch.java @@ -37,7 +37,7 @@ public class TerminalLaunch { boolean logIfEnabled = true; @Builder.Default - boolean alwaysKeepOpen = false; + boolean pauseOnExit = AppPrefs.get().terminalAlwaysPauseOnExit().getValue(); ExternalTerminalType terminal; @@ -68,8 +68,7 @@ public class TerminalLaunch { getFullTitle(), directory, request != null ? request : UUID.randomUUID(), - logIfEnabled, - alwaysKeepOpen, + logIfEnabled, pauseOnExit, command); TerminalLauncher.open(List.of(pane), preferTabs, type); } diff --git a/app/src/main/java/io/xpipe/app/terminal/TerminalLauncher.java b/app/src/main/java/io/xpipe/app/terminal/TerminalLauncher.java index 510cf1ab7..5e62664e5 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TerminalLauncher.java +++ b/app/src/main/java/io/xpipe/app/terminal/TerminalLauncher.java @@ -143,8 +143,6 @@ public class TerminalLauncher { config.getProcessControl() instanceof ShellControl ? type.additionalInitCommands() : TerminalInitFunction.none()); - var alwaysPromptRestart = config.isAlwaysKeepOpen() - || AppPrefs.get().terminalAlwaysPauseOnExit().getValue(); TerminalLauncherManager.submitAsync( config.getRequest(), config.getProcessControl(), terminalConfig, config.getDirectory(), latch); var effectivePreferTabs = @@ -152,7 +150,7 @@ public class TerminalLauncher { var paneIndex = configs.indexOf(config); var paneConfig = TerminalPaneConfiguration.create( - config.getRequest(), entry, config.getTitle(), paneIndex, effectivePreferTabs, alwaysPromptRestart); + config.getRequest(), entry, config.getTitle(), paneIndex, effectivePreferTabs, config.isAlwaysKeepOpen()); paneList.add(paneConfig); } diff --git a/app/src/main/java/io/xpipe/app/terminal/WarpTerminalType.java b/app/src/main/java/io/xpipe/app/terminal/WarpTerminalType.java index 052674398..9f21b1527 100644 --- a/app/src/main/java/io/xpipe/app/terminal/WarpTerminalType.java +++ b/app/src/main/java/io/xpipe/app/terminal/WarpTerminalType.java @@ -89,9 +89,9 @@ public interface WarpTerminalType extends ExternalTerminalType, TrackableTermina var scriptArg = URLEncoder.encode(movedScriptFile.toString(), StandardCharsets.UTF_8); if (!configuration.isPreferTabs()) { - DesktopHelper.openUrl("warp://action/new_window?path=" + scriptArg); + DesktopHelper.openAssociatedApplication("warp://action/new_window?path=" + scriptArg); } else { - DesktopHelper.openUrl("warp://action/new_tab?path=" + scriptArg); + DesktopHelper.openAssociatedApplication("warp://action/new_tab?path=" + scriptArg); } } } @@ -125,9 +125,9 @@ public interface WarpTerminalType extends ExternalTerminalType, TrackableTermina public void launch(TerminalLaunchConfiguration configuration) { var pane = configuration.single(); if (!configuration.isPreferTabs()) { - DesktopHelper.openUrl("warp://action/new_window?path=" + pane.getScriptFile()); + DesktopHelper.openAssociatedApplication("warp://action/new_window?path=" + pane.getScriptFile()); } else { - DesktopHelper.openUrl("warp://action/new_tab?path=" + pane.getScriptFile()); + DesktopHelper.openAssociatedApplication("warp://action/new_tab?path=" + pane.getScriptFile()); } } } diff --git a/app/src/main/java/io/xpipe/app/terminal/WezTerminalType.java b/app/src/main/java/io/xpipe/app/terminal/WezTerminalType.java index db5953c77..83362f556 100644 --- a/app/src/main/java/io/xpipe/app/terminal/WezTerminalType.java +++ b/app/src/main/java/io/xpipe/app/terminal/WezTerminalType.java @@ -12,6 +12,7 @@ import io.xpipe.app.util.ThreadHelper; import io.xpipe.app.util.WindowsRegistry; import io.xpipe.core.FilePath; +import io.xpipe.core.OsType; import lombok.SneakyThrows; import java.io.IOException; @@ -56,7 +57,11 @@ public interface WezTerminalType extends ExternalTerminalType, TrackableTerminal } default Path getSocketDir() { - return AppSystemInfo.ofCurrent().getUserHome().resolve(".local", "share", "wezterm"); + if (OsType.ofLocal() == OsType.LINUX) { + return Path.of(System.getenv("XDG_RUNTIME_DIR"), "wezterm"); + } else { + return AppSystemInfo.ofCurrent().getUserHome().resolve(".local", "share", "wezterm"); + } } default Optional waitForInstanceStart(int count) { @@ -114,6 +119,7 @@ public interface WezTerminalType extends ExternalTerminalType, TrackableTerminal default void launch(TerminalLaunchConfiguration configuration) throws Exception { var base = getWeztermCommandBase(); var activeSocket = waitForInstanceStart(1); + var tabid = "0"; // Always start a new window for split panes as we can't find the pane index to start with if (activeSocket.isEmpty() || configuration.getPanes().size() > 1 || !configuration.isPreferTabs()) { var gui = CommandBuilder.of().add(base.buildSimple().replace("wezterm.exe", "wezterm-gui.exe")); @@ -122,24 +128,36 @@ public interface WezTerminalType extends ExternalTerminalType, TrackableTerminal .add("start", "--always-new-process") .add(configuration.getPanes().getFirst().getDialectLaunchCommand()); ExternalApplicationHelper.startAsync(command); + activeSocket = waitForInstanceStart(50); + if (activeSocket.isEmpty()) { + return; + } } else { var command = CommandBuilder.of() .add(base) .add("cli", "spawn") .add(configuration.getPanes().getFirst().getDialectLaunchCommand()); command.fixedEnvironment("WEZTERM_UNIX_SOCKET", activeSocket.get().toString()); - LocalShell.getShell() + tabid = LocalShell.getShell() .command(command) .withWorkingDirectory(FilePath.of(getSocketDir())) - .execute(); + .readStdoutOrThrow(); } - if (configuration.getPanes().size() > 1) { - activeSocket = waitForInstanceStart(50); - if (activeSocket.isEmpty()) { - return; - } + var titleCommand = CommandBuilder.of() + .add(base) + .add("cli", "set-tab-title") + .add("--tab-id", tabid) + .addQuoted(configuration.getColoredTitle()); + titleCommand.fixedEnvironment("WEZTERM_UNIX_SOCKET", activeSocket.get().toString()); + // Sometimes the tab ids don't exist even though it just returned them to us + // So just ignore any errors + LocalShell.getShell() + .command(titleCommand) + .withWorkingDirectory(FilePath.of(getSocketDir())) + .executeAndCheck(); + if (configuration.getPanes().size() > 1) { var direction = AppPrefs.get().terminalSplitStrategy().getValue(); var directionIterator = direction.iterator(); for (int i = 1; i < configuration.getPanes().size(); i++) { @@ -163,8 +181,7 @@ public interface WezTerminalType extends ExternalTerminalType, TrackableTerminal activeSocket.get().toString())) .withWorkingDirectory(FilePath.of(getSocketDir())) .execute(); - directionIterator.next(); - } + directionIterator.next(); } } } diff --git a/app/src/main/java/io/xpipe/app/update/AppDistributionType.java b/app/src/main/java/io/xpipe/app/update/AppDistributionType.java index 9f834415d..1a0c74a90 100644 --- a/app/src/main/java/io/xpipe/app/update/AppDistributionType.java +++ b/app/src/main/java/io/xpipe/app/update/AppDistributionType.java @@ -28,7 +28,10 @@ public enum AppDistributionType implements Translatable { HOMEBREW("homebrew", true, () -> { var pkg = AppNames.ofCurrent().getKebapName(); return new CommandUpdater( - ShellScript.lines("brew upgrade --cask xpipe-io/tap/" + pkg, AppRestart.getTerminalRestartCommand())); + ShellScript.lines( + "brew upgrade --cask xpipe-io/tap/" + pkg, + "if [ \"$?\" != 0 ]; then echo \"Update failed ...\"; read key; fi", + AppRestart.getTerminalRestartCommand())); }), APT_REPO("apt", true, () -> { var pkg = AppNames.ofCurrent().getKebapName(); @@ -36,6 +39,7 @@ public enum AppDistributionType implements Translatable { "echo \"+ sudo apt update && sudo apt install -y " + pkg + "\"", "sudo apt update", "sudo apt install -y " + pkg, + "if [ \"$?\" != 0 ]; then echo \"Update failed ...\"; read key; fi", AppRestart.getTerminalRestartCommand())); }), RPM_REPO("rpm", true, () -> { @@ -43,13 +47,15 @@ public enum AppDistributionType implements Translatable { return new CommandUpdater(ShellScript.lines( "echo \"+ sudo yum upgrade " + pkg + " --refresh -y\"", "sudo yum upgrade " + pkg + " --refresh -y", + "if [ \"$?\" != 0 ]; then echo \"Update failed ...\"; read key; fi", AppRestart.getTerminalRestartCommand())); }), AUR("aur", true, () -> { var pkg = AppNames.ofCurrent().getKebapName(); return new CommandUpdater(ShellScript.lines( - "echo \"+ git clone https://aur.archlinux.org/" + pkg + " . && makepkg -si\"", - "cd $(mktemp -d) && git clone https://aur.archlinux.org/" + pkg + " . && makepkg -si --noconfirm", + "echo \"+ git -c core.autocrlf=false clone https://aur.archlinux.org/" + pkg + " . && makepkg -si\"", + "cd $(mktemp -d) && git -c core.autocrlf=false clone https://aur.archlinux.org/" + pkg + " . && makepkg -si --noconfirm", + "if [ \"$?\" != 0 ]; then echo \"Update failed ...\"; read key; fi", AppRestart.getTerminalRestartCommand())); }), WEBTOP("webtop", true, () -> new WebtopUpdater()), diff --git a/app/src/main/java/io/xpipe/app/update/AppInstaller.java b/app/src/main/java/io/xpipe/app/update/AppInstaller.java index 0009c236d..6b769d376 100644 --- a/app/src/main/java/io/xpipe/app/update/AppInstaller.java +++ b/app/src/main/java/io/xpipe/app/update/AppInstaller.java @@ -229,7 +229,7 @@ public class AppInstaller { runinstaller if [ "$?" != 0 ]; then echo "Update failed ..." - read -rs -k 1 key + read key fi """, file, file, AppRestart.getTerminalRestartCommand())); AppOperationMode.executeAfterShutdown(() -> { diff --git a/app/src/main/java/io/xpipe/app/util/DesktopHelper.java b/app/src/main/java/io/xpipe/app/util/DesktopHelper.java index ed98ae824..1bb181303 100644 --- a/app/src/main/java/io/xpipe/app/util/DesktopHelper.java +++ b/app/src/main/java/io/xpipe/app/util/DesktopHelper.java @@ -16,7 +16,7 @@ import java.util.List; public class DesktopHelper { - public static void openUrl(String uri) { + public static void openBrowser(String uri) { if (uri == null) { return; } @@ -56,6 +56,30 @@ public class DesktopHelper { }); } + public static void openAssociatedApplication(String uri) { + if (uri == null) { + return; + } + + URI parsed; + try { + parsed = URI.create(uri); + } catch (IllegalArgumentException e) { + ErrorEventFactory.fromThrowable("Invalid URI: " + uri, e.getCause() != null ? e.getCause() : e) + .handle(); + return; + } + + // Windows URL open always uses browser + if (OsType.ofLocal() == OsType.WINDOWS) { + LocalExec.executeAsync("rundll32", "url.dll,FileProtocolHandler", parsed.toString()); + return; + } + + // Other OS use associated app + openBrowser(uri); + } + public static void browseFile(Path file) { if (file == null || !Files.exists(file)) { return; diff --git a/app/src/main/java/io/xpipe/app/util/Hyperlinks.java b/app/src/main/java/io/xpipe/app/util/Hyperlinks.java index 0de806ecc..649f0a30a 100644 --- a/app/src/main/java/io/xpipe/app/util/Hyperlinks.java +++ b/app/src/main/java/io/xpipe/app/util/Hyperlinks.java @@ -11,6 +11,6 @@ public class Hyperlinks { public static final String GITHUB_WEBTOP = "https://github.com/xpipe-io/xpipe-webtop"; public static void open(String uri) { - DesktopHelper.openUrl(uri); + DesktopHelper.openBrowser(uri); } } diff --git a/app/src/main/java/module-info.java b/app/src/main/java/module-info.java index f310696e6..d1ecd7c85 100644 --- a/app/src/main/java/module-info.java +++ b/app/src/main/java/module-info.java @@ -128,6 +128,7 @@ open module io.xpipe.app { uses CloudSetupProvider; provides ActionProvider with + GradleRunMenuProvider, RefreshHubLeafProvider, SetupToolActionProvider, XPipeUrlProvider, diff --git a/build.gradle b/build.gradle index d1621c264..aa47d3683 100644 --- a/build.gradle +++ b/build.gradle @@ -296,13 +296,13 @@ project.ext { } // JavaFX config - devJavafxVersion = '26-ea+19' + devJavafxVersion = '27-ea+4' platformName = getPlatformName() useBundledJavaFx = fullVersion bundledJdkJavaFx = ModuleFinder.ofSystem().find("javafx.base").isPresent() // Define a custom JavaFX SDK location - // customJavaFxLibsPath = file("C:\\Projects\\jfx\\build\\sdk\\lib") - // customJavaFxJmodsPath = file("C:\\Projects\\jfx\\build\\jmods") + customJavaFxLibsPath = null; // file("C:\\Projects\\jfx\\build\\sdk\\lib") + customJavaFxJmodsPath = null; // file("C:\\Projects\\jfx\\build\\jmods") // Other deeplApiKey = findProperty('DEEPL_API_KEY') != null ? findProperty('DEEPL_API_KEY') : "" diff --git a/dist/build.gradle b/dist/build.gradle index 1f7c58a86..c70933656 100644 --- a/dist/build.gradle +++ b/dist/build.gradle @@ -1,7 +1,7 @@ plugins { id 'org.beryx.jlink' version '3.2.1' - id("com.netflix.nebula.ospackage") version "12.1.1" + id("com.netflix.nebula.ospackage") version "12.2.0" id 'org.gradle.crypto.checksum' version '1.4.0' id 'signing' } diff --git a/dist/changelog/21.2.1.md b/dist/changelog/21.2.1.md deleted file mode 100644 index 61b7c22b5..000000000 --- a/dist/changelog/21.2.1.md +++ /dev/null @@ -1,8 +0,0 @@ -- Fix Pageant and custom SSH agents on Windows not working -- Fix Pageant integration using wrong named pipe when multiple users were logged in on Windows -- Fix plain directory sync not working for OneDrive directories -- Fix terminal docking not working after window had been minimized to system tray -- Fix Windows vault lock and hibernation lock not working after window had been minimized to system tray -- Fix NullPointer when launching docked terminal via a desktop shortcut when XPipe was not running yet -- Fix desktop shortcuts for actions not applying any custom workspace directories -- Fix various other NullPointers diff --git a/dist/changelog/21.2.md b/dist/changelog/21.2.md index 754f9a598..8cca86f48 100644 --- a/dist/changelog/21.2.md +++ b/dist/changelog/21.2.md @@ -1,9 +1,5 @@ -- Fix various SSH agents not working if the socket path contained spaces - Fix terminal docking not working properly on multiple displays with different scale factor -- Fix terminal docking sometimes not automatically reattaching windows that were moved back to the dock -- The terminal dock indicator icon will now show a different icon to indicate when the terminal is detached +- Fix terminal docking not automatically reattaching windows that were moved back to the dock +- Fix automatic updater not pausing terminal session when update failed - Automatically delete corrupted git index file when detected -- Fix terminal test button not showing for the terminal selection in the settings menu -- Fix network switch ports being included in total connection count -- Fix various NullPointers -- Fix some broken documentation links \ No newline at end of file +- diff --git a/dist/changelog/21.3.md b/dist/changelog/21.3.md new file mode 100644 index 000000000..73ce0ebe9 --- /dev/null +++ b/dist/changelog/21.3.md @@ -0,0 +1 @@ +- Add support for neovim editor (Thanks to @leycm) diff --git a/dist/jpackage.gradle b/dist/jpackage.gradle index e754d3eac..9700ab2c4 100644 --- a/dist/jpackage.gradle +++ b/dist/jpackage.gradle @@ -47,7 +47,7 @@ jlink { options.addAll('--strip-native-debug-symbols', 'exclude-debuginfo-files') } - if (hasProperty("customJavaFxJmodsPath")) { + if (customJavaFxJmodsPath != null) { addExtraModulePath(customJavaFxJmodsPath.toString()) } else if (useBundledJavaFx && !bundledJdkJavaFx) { addExtraModulePath(layout.projectDirectory.dir("javafx/${platformName}/${arch}").toString()) diff --git a/dist/licenses/graalvm.properties b/dist/licenses/graalvm.properties index 4aec72c40..ae86cefde 100644 --- a/dist/licenses/graalvm.properties +++ b/dist/licenses/graalvm.properties @@ -1,4 +1,4 @@ name=GraalVM Community -version=25.0.1 +version=25.0.2 license=GPL2 with the Classpath Exception link=https://www.graalvm.org/ \ No newline at end of file diff --git a/ext/base/src/main/java/io/xpipe/ext/base/script/RunTerminalScriptActionProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/script/RunTerminalScriptActionProvider.java index 12312908d..bf16a9937 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/script/RunTerminalScriptActionProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/script/RunTerminalScriptActionProvider.java @@ -34,7 +34,7 @@ public class RunTerminalScriptActionProvider implements ActionProvider { .entry(ref.get()) .title(scriptStore.get().getName()) .command(sc.command(script)) - .alwaysKeepOpen(true) + .pauseOnExit(true) .launch(); } diff --git a/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanContainerLogsActionProvider.java b/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanContainerLogsActionProvider.java index dd2279998..06de720fc 100644 --- a/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanContainerLogsActionProvider.java +++ b/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanContainerLogsActionProvider.java @@ -49,7 +49,7 @@ public class PodmanContainerLogsActionProvider implements HubLeafProvider