diff --git a/app/src/main/java/io/xpipe/app/comp/base/SideMenuBarComp.java b/app/src/main/java/io/xpipe/app/comp/base/SideMenuBarComp.java index f0192ba7b..0b4ba1f7c 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/SideMenuBarComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/SideMenuBarComp.java @@ -108,21 +108,7 @@ public class SideMenuBarComp extends RegionBuilder { b.apply(struc -> { struc.setOnAction(e -> { struc.setDisable(true); - ThreadHelper.runAsync(() -> { - try { - var r = item.getAction().get(); - if (r) { - Platform.runLater(() -> { - queueEntries.remove(item); - }); - } - } catch (Throwable t) { - Platform.runLater(() -> { - queueEntries.remove(item); - }); - throw t; - } - }); + item.execute(); struc.setDisable(false); e.consume(); }); diff --git a/app/src/main/java/io/xpipe/app/core/AppLayoutModel.java b/app/src/main/java/io/xpipe/app/core/AppLayoutModel.java index 021a18666..a360a0bfb 100644 --- a/app/src/main/java/io/xpipe/app/core/AppLayoutModel.java +++ b/app/src/main/java/io/xpipe/app/core/AppLayoutModel.java @@ -196,18 +196,26 @@ public class AppLayoutModel { public void execute() { ThreadHelper.runAsync(() -> { - try { - var r = getAction().get(); - if (r) { - AppLayoutModel.get().getQueueEntries().remove(this); - } - } catch (Throwable t) { - AppLayoutModel.get().getQueueEntries().remove(this); - throw t; - } + executeSync(); }); } + public void executeSync() { + try { + var r = getAction().get(); + if (r) { + PlatformThread.runLaterIfNeeded(() -> { + AppLayoutModel.get().getQueueEntries().remove(this); + }); + } + } catch (Throwable t) { + PlatformThread.runLaterIfNeeded(() -> { + AppLayoutModel.get().getQueueEntries().remove(this); + }); + throw t; + } + } + public void hide() { AppLayoutModel.get().getQueueEntries().remove(this); } diff --git a/app/src/main/java/io/xpipe/app/pwman/KeePassXcPasswordManager.java b/app/src/main/java/io/xpipe/app/pwman/KeePassXcPasswordManager.java index 3bbba2eb7..54846c623 100644 --- a/app/src/main/java/io/xpipe/app/pwman/KeePassXcPasswordManager.java +++ b/app/src/main/java/io/xpipe/app/pwman/KeePassXcPasswordManager.java @@ -146,7 +146,7 @@ public class KeePassXcPasswordManager implements PasswordManager { if (!valid) { ErrorEventFactory.preconfigure(ErrorEventFactory.fromThrowable(first) .description("KeePassXC association for " - + available.getFirst().getKey() + " failed") + + available.getFirst().getId() + " failed") .expected()); throw first; } 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 c779ae35e..8be452e73 100644 --- a/app/src/main/java/io/xpipe/app/pwman/KeeperPasswordManager.java +++ b/app/src/main/java/io/xpipe/app/pwman/KeeperPasswordManager.java @@ -82,6 +82,10 @@ public class KeeperPasswordManager implements PasswordManager { @Override public synchronized CredentialResult retrieveCredentials(String rawKey) { + return retrieveCredentials(rawKey, 0); + } + + private CredentialResult retrieveCredentials(String rawKey, int retries) { // The copy UID button copies the whole URL in the Keeper UI. Why? ... rawKey = rawKey.replaceFirst("https://\\w+\\.\\w+/vault/#detail/", ""); @@ -173,7 +177,7 @@ public class KeeperPasswordManager implements PasswordManager { .add(b); var queryCommand = sc.command(fullB); queryCommand.sensitive(); - queryCommand.killOnTimeout(CountDown.of().start(15_000)); + queryCommand.killOnTimeout(CountDown.of().start(25_000)); var result = queryCommand.readStdoutAndStderr(); var exitCode = queryCommand.getExitCode(); @@ -208,7 +212,11 @@ public class KeeperPasswordManager implements PasswordManager { if (exitCode != 0 || (outLines.size() > 0 && outLines.getLast().contains("Invalid entry"))) { // Another password prompt was made var wrongPw = out.contains("Enter password for") || exitCode == CommandControl.EXIT_TIMEOUT_EXIT_CODE; - if (wrongPw) { + if (wrongPw && hasCompletedRequestInSession) { + if (retries == 0) { + return retrieveCredentials(rawKey, retries + 1); + } + SecretManager.clearAll(KEEPER_PASSWORD_ID); ErrorEventFactory.fromMessage("Master password was not accepted by Keeper. Is it correct?") .expected() @@ -257,6 +265,6 @@ public class KeeperPasswordManager implements PasswordManager { @Override public Duration getCacheDuration() { - return (mfa != null && mfa && getTotpDurationIndex() < 1) ? Duration.ofDays(10) : Duration.ofSeconds(3); + return (mfa != null && mfa && getTotpDurationIndex() < 1) ? Duration.ofDays(10) : Duration.ofSeconds(30); } } diff --git a/app/src/main/java/io/xpipe/app/secret/SecretPasswordManagerStrategy.java b/app/src/main/java/io/xpipe/app/secret/SecretPasswordManagerStrategy.java index ecae0069c..092b3ff54 100644 --- a/app/src/main/java/io/xpipe/app/secret/SecretPasswordManagerStrategy.java +++ b/app/src/main/java/io/xpipe/app/secret/SecretPasswordManagerStrategy.java @@ -103,8 +103,9 @@ public class SecretPasswordManagerStrategy implements SecretRetrievalStrategy { @Override public Duration cacheDuration() { - // To reduce password manager access, cache it for a few seconds - return Duration.ofSeconds(15); + // To reduce password manager access, cache it + var pm = AppPrefs.get().passwordManager().getValue(); + return pm != null ? pm.getCacheDuration() : Duration.ofSeconds(15); } @Override 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 3395bbc3b..ecb1da750 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TerminalDockHubComp.java +++ b/app/src/main/java/io/xpipe/app/terminal/TerminalDockHubComp.java @@ -9,9 +9,13 @@ import javafx.beans.value.ObservableValue; import javafx.event.EventHandler; import javafx.geometry.Bounds; import javafx.scene.Parent; +import javafx.scene.control.Button; +import javafx.scene.layout.Background; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; +import javafx.scene.paint.Color; import javafx.stage.WindowEvent; +import org.kordamp.ikonli.javafx.FontIcon; import java.util.concurrent.atomic.AtomicReference; @@ -68,6 +72,11 @@ public class TerminalDockHubComp extends SimpleRegionBuilder { @Override public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { if (newValue) { + var selected = s.getScene().getRoot().lookup(".icon-button-comp:hover"); + if (selected instanceof Button b && b.getGraphic() instanceof FontIcon fi && fi.getIconLiteral().equals("mdi2c-console")) { + return; + } + model.onFocusGain(); } else { Platform.runLater(() -> { 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 4be774478..0295746f4 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TerminalDockHubManager.java +++ b/app/src/main/java/io/xpipe/app/terminal/TerminalDockHubManager.java @@ -4,6 +4,7 @@ import io.xpipe.app.comp.base.ModalOverlay; import io.xpipe.app.core.AppI18n; import io.xpipe.app.core.AppLayoutModel; import io.xpipe.app.core.window.AppDialog; +import io.xpipe.app.core.window.AppMainWindow; import io.xpipe.app.platform.LabelGraphic; import io.xpipe.app.platform.NativeWinWindowControl; import io.xpipe.app.platform.PlatformThread; @@ -14,7 +15,9 @@ import io.xpipe.core.OsType; import javafx.application.Platform; import javafx.beans.property.BooleanProperty; +import javafx.beans.property.Property; import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleObjectProperty; import javafx.collections.ListChangeListener; import lombok.Getter; @@ -204,6 +207,8 @@ public class TerminalDockHubManager { } public void refreshDockStatus() { + dockModel.clearDeadTerminals(); + var running = dockModel.isRunning(); if (!running) { minimized.set(false); @@ -212,7 +217,7 @@ public class TerminalDockHubManager { } minimized.set(dockModel.isMinimized()); - detached.set(dockModel.isCustomBounds()); + detached.set(dockModel.isCustomBounds() || dockModel.isMinimized()); } public void openTerminal(UUID request) { 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 e7a828ceb..d849d186c 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TerminalDockView.java +++ b/app/src/main/java/io/xpipe/app/terminal/TerminalDockView.java @@ -28,6 +28,10 @@ public class TerminalDockView { this.windowBoundsFunction = windowBoundsFunction; } + public synchronized void clearDeadTerminals() { + terminalInstances.removeIf(controllableTerminalSession -> !controllableTerminalSession.getTerminalProcess().isAlive()); + } + public synchronized boolean isRunning() { return terminalInstances.stream().anyMatch(terminal -> terminal.isRunning()); } @@ -215,7 +219,7 @@ public class TerminalDockView { } public void attach() { - TrackEvent.withTrace("Terminal view clicked").handle(); + TrackEvent.withTrace("Terminal view attached").handle(); terminalInstances.forEach(terminalInstance -> { terminalInstance.show(); diff --git a/dist/changelog/21.0.md b/dist/changelog/21.0.md index d4faa98cc..a6e4e4cee 100644 --- a/dist/changelog/21.0.md +++ b/dist/changelog/21.0.md @@ -64,3 +64,4 @@ The scripting system has been completely reworked with the goal of becoming simp - Fix hetzner cloud integration sometimes not stripping away subnet mask from determined IP address - Fix predefined categories being able to be moved - Fix terminal sessions not applying for Konsole +- Fix rbash shell detection not working diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/PasswordManagerIdentityStore.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/PasswordManagerIdentityStore.java index 6f5ce8b3a..0843baa5c 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/PasswordManagerIdentityStore.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/PasswordManagerIdentityStore.java @@ -40,7 +40,9 @@ public class PasswordManagerIdentityStore extends IdentityStore var instant = getCache("lastQueried", Instant.class, null); if (instant != null) { var now = Instant.now(); - if (Duration.between(instant, now).toSeconds() < 15) { + var pm = AppPrefs.get().passwordManager().getValue(); + var cacheDuration = pm != null ? pm.getCacheDuration().toSeconds() : 15; + if (Duration.between(instant, now).toSeconds() < cacheDuration) { return false; } } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/ssh/SshIdentityStateManager.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/ssh/SshIdentityStateManager.java index 196e2d991..029372c36 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/ssh/SshIdentityStateManager.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/ssh/SshIdentityStateManager.java @@ -233,7 +233,6 @@ public class SshIdentityStateManager { } if (sc.getOsType() == OsType.WINDOWS) { - stopWindowsAgents(true, true, true); checkLocalAgentIdentities(null); } else { checkLocalAgentIdentities( diff --git a/ext/base/src/main/java/io/xpipe/ext/base/service/FixedServiceStore.java b/ext/base/src/main/java/io/xpipe/ext/base/service/FixedServiceStore.java index 213102653..8d1bb919d 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/service/FixedServiceStore.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/service/FixedServiceStore.java @@ -16,7 +16,7 @@ import lombok.extern.jackson.Jacksonized; import java.util.OptionalInt; -@SuperBuilder +@SuperBuilder(toBuilder = true) @Getter @Jacksonized @JsonTypeName("fixedService") @@ -48,6 +48,12 @@ public class FixedServiceStore extends AbstractServiceStore implements FixedChil return false; } + @Override + public FixedChildStore merge(FixedChildStore other) { + var o = (FixedServiceStore) other; + return toBuilder().tunnelToLocalhost(o.tunnelToLocalhost).build(); + } + @Override public void checkComplete() throws Throwable { super.checkComplete(); diff --git a/version b/version index 84c15a108..6d2f6e5fd 100644 --- a/version +++ b/version @@ -1 +1 @@ -21.0-1 +21.0-2