diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserBreadcrumbBar.java b/app/src/main/java/io/xpipe/app/browser/BrowserBreadcrumbBar.java index 5b8b55a1..8afc08ec 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserBreadcrumbBar.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserBreadcrumbBar.java @@ -4,7 +4,6 @@ import atlantafx.base.controls.Breadcrumbs; import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.util.PlatformThread; import io.xpipe.app.fxcomps.util.SimpleChangeListener; -import io.xpipe.app.util.ThreadHelper; import io.xpipe.core.impl.FileNames; import javafx.scene.Node; import javafx.scene.control.Button; @@ -81,9 +80,7 @@ public class BrowserBreadcrumbBar extends SimpleComp { } breadcrumbs.selectedCrumbProperty().addListener((obs, old, val) -> { - ThreadHelper.runFailableAsync(() -> { - model.cdSync(val != null ? val.getValue() : null); - }); + model.cdAsync(val != null ? val.getValue() : null); }); return breadcrumbs; diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserFileListCompEntry.java b/app/src/main/java/io/xpipe/app/browser/BrowserFileListCompEntry.java index 5514c1cf..e14f1979 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserFileListCompEntry.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserFileListCompEntry.java @@ -200,7 +200,7 @@ public class BrowserFileListCompEntry { return; } - model.getFileSystemModel().cdSync(item.getRawFileEntry().getPath()); + model.getFileSystemModel().cdAsync(item.getRawFileEntry().getPath()); } }; DROP_TIMER.schedule(activeTask, 1000); diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserFileListModel.java b/app/src/main/java/io/xpipe/app/browser/BrowserFileListModel.java index c9af527b..a6a6e393 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserFileListModel.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserFileListModel.java @@ -128,7 +128,7 @@ public final class BrowserFileListModel { } if (entry.getRawFileEntry().resolved().getKind() == FileKind.DIRECTORY) { - fileSystemModel.cdSync(entry.getRawFileEntry().resolved().getPath()); + fileSystemModel.cdAsync(entry.getRawFileEntry().resolved().getPath()); } } } diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserFileOverviewComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserFileOverviewComp.java index bb35dd13..f16e95bb 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserFileOverviewComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserFileOverviewComp.java @@ -31,7 +31,7 @@ public class BrowserFileOverviewComp extends SimpleComp { var icon = BrowserIcons.createIcon(entry); var l = new Button(entry.getPath(), icon.createRegion()); l.setOnAction(event -> { - model.cdSync(entry.getPath()); + model.cdAsync(entry.getPath()); event.consume(); }); l.setAlignment(Pos.CENTER_LEFT); diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserNavBar.java b/app/src/main/java/io/xpipe/app/browser/BrowserNavBar.java index 457d154f..a5df651d 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserNavBar.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserNavBar.java @@ -11,6 +11,7 @@ import io.xpipe.app.fxcomps.impl.PrettyImageComp; import io.xpipe.app.fxcomps.impl.StackComp; import io.xpipe.app.fxcomps.impl.TextFieldComp; import io.xpipe.app.fxcomps.util.SimpleChangeListener; +import io.xpipe.app.util.BusyProperty; import io.xpipe.app.util.ThreadHelper; import javafx.application.Platform; import javafx.beans.binding.Bindings; @@ -45,8 +46,10 @@ public class BrowserNavBar extends SimpleComp { }); path.addListener((observable, oldValue, newValue) -> { ThreadHelper.runFailableAsync(() -> { - var changed = model.cdSyncOrRetry(newValue, true); - changed.ifPresent(s -> Platform.runLater(() -> path.set(s))); + BusyProperty.execute(model.getBusy(), () -> { + var changed = model.cdSyncOrRetry(newValue, true); + changed.ifPresent(s -> Platform.runLater(() -> path.set(s))); + }); }); }); @@ -88,7 +91,7 @@ public class BrowserNavBar extends SimpleComp { () -> { var icon = model.getCurrentDirectory() != null ? FileIconManager.getFileIcon(model.getCurrentDirectory(), false) - : "home_icon.png"; + : "home_icon.svg"; return icon; }, model.getCurrentPath()); diff --git a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemComp.java b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemComp.java index e44efd79..69c50d1f 100644 --- a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemComp.java +++ b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemComp.java @@ -43,7 +43,7 @@ public class OpenFileSystemComp extends SimpleComp { private Region createContent() { var overview = new Button(null, new FontIcon("mdi2m-monitor")); - overview.setOnAction(e -> model.cdSync(null)); + overview.setOnAction(e -> model.cdAsync(null)); overview.disableProperty().bind(model.getInOverview()); overview.setAccessibleText("System overview"); diff --git a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemModel.java b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemModel.java index bd405197..1c381e6c 100644 --- a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemModel.java +++ b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemModel.java @@ -107,6 +107,14 @@ public final class OpenFileSystemModel { return new FileSystem.FileEntry(fileSystem, currentPath.get(), null, false, false, 0, null, FileKind.DIRECTORY); } + public void cdAsync(String path) { + ThreadHelper.runFailableAsync(() -> { + BusyProperty.execute(busy, () -> { + cdSync(path); + }); + }); + } + public void cdSync(String path) { cdSyncOrRetry(path, false).ifPresent(s -> cdSyncOrRetry(s, false)); } diff --git a/app/src/main/java/io/xpipe/app/comp/store/DsStoreProviderChoiceComp.java b/app/src/main/java/io/xpipe/app/comp/store/DsStoreProviderChoiceComp.java index 73a6095c..55a4b367 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/DsStoreProviderChoiceComp.java +++ b/app/src/main/java/io/xpipe/app/comp/store/DsStoreProviderChoiceComp.java @@ -26,10 +26,11 @@ public class DsStoreProviderChoiceComp extends Comp Predicate filter; Property provider; + boolean staticDisplay; private Region createDefaultNode() { return JfxHelper.createNamedEntry( - AppI18n.get("selectType"), AppI18n.get("selectTypeDescription"), "machine_icon.png"); + AppI18n.get("selectType"), AppI18n.get("selectTypeDescription"), "connection_icon.svg"); } private List getProviders() { @@ -50,7 +51,7 @@ public class DsStoreProviderChoiceComp extends Comp var comboBox = new CustomComboBoxBuilder<>(provider, this::createGraphic, createDefaultNode(), v -> true); comboBox.setAccessibleNames(dataStoreProvider -> dataStoreProvider.getDisplayName()); getProviders().stream() - .filter(p -> AppPrefs.get().developerShowHiddenProviders().get() || p.canManuallyCreate()) + .filter(p -> AppPrefs.get().developerShowHiddenProviders().get() || p.canManuallyCreate() || staticDisplay) .forEach(comboBox::add); ComboBox cb = comboBox.build(); cb.getStyleClass().add("data-source-type"); diff --git a/app/src/main/java/io/xpipe/app/comp/store/GuiDsStoreCreator.java b/app/src/main/java/io/xpipe/app/comp/store/GuiDsStoreCreator.java index 8dad7240..d7b43845 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/GuiDsStoreCreator.java +++ b/app/src/main/java/io/xpipe/app/comp/store/GuiDsStoreCreator.java @@ -240,7 +240,7 @@ public class GuiDsStoreCreator extends MultiStepComp.Step> { var layout = new BorderPane(); layout.getStyleClass().add("store-creator"); layout.setPadding(new Insets(20)); - var providerChoice = new DsStoreProviderChoiceComp(filter, provider); + var providerChoice = new DsStoreProviderChoiceComp(filter, provider, provider.getValue() != null); if (provider.getValue() != null) { providerChoice.apply(struc -> struc.get().setDisable(true)); } diff --git a/app/src/main/java/io/xpipe/app/exchange/AskpassExchangeImpl.java b/app/src/main/java/io/xpipe/app/exchange/AskpassExchangeImpl.java index d721f482..17332e41 100644 --- a/app/src/main/java/io/xpipe/app/exchange/AskpassExchangeImpl.java +++ b/app/src/main/java/io/xpipe/app/exchange/AskpassExchangeImpl.java @@ -1,69 +1,20 @@ package io.xpipe.app.exchange; -import io.xpipe.app.core.AppI18n; -import io.xpipe.app.core.AppWindowHelper; import io.xpipe.app.core.mode.OperationMode; -import io.xpipe.app.fxcomps.impl.SecretFieldComp; +import io.xpipe.app.util.AskpassAlert; import io.xpipe.beacon.BeaconHandler; import io.xpipe.beacon.exchange.AskpassExchange; -import io.xpipe.core.util.SecretValue; -import javafx.beans.property.SimpleObjectProperty; -import javafx.scene.control.Alert; -import javafx.scene.layout.StackPane; - -import java.util.HashMap; -import java.util.Map; public class AskpassExchangeImpl extends AskpassExchange implements MessageExchangeImpl { - private final Map requestToId = new HashMap<>(); - private final Map passwords = new HashMap<>(); - @Override public Response handleRequest(BeaconHandler handler, Request msg) { if (OperationMode.get().equals(OperationMode.BACKGROUND)) { OperationMode.switchTo(OperationMode.TRAY); } - // SecretValue set = AppCache.get(msg.getId(), SecretValue.class, () -> null); - // if (set != null) { - // return Response.builder().value(set).build(); - // } - - if (requestToId.containsKey(msg.getRequest())) { - var id = requestToId.remove(msg.getRequest()); - passwords.remove(id); - } - - if (passwords.containsKey(msg.getId())) { - return Response.builder() - .value(passwords.get(msg.getId()).getSecretValue()) - .build(); - } - - var prop = new SimpleObjectProperty(); - var r = AppWindowHelper.showBlockingAlert(alert -> { - alert.setTitle(AppI18n.get("askpassAlertTitle")); - alert.setHeaderText(msg.getPrompt()); - alert.setAlertType(Alert.AlertType.CONFIRMATION); - - var text = new SecretFieldComp(prop).createRegion(); - alert.getDialogPane().setContent(new StackPane(text)); - }) - .filter(b -> b.getButtonData().isDefaultButton() && prop.getValue() != null) - .map(t -> { - // AppCache.update(msg.getId(), prop.getValue()); - return prop.getValue(); - }) - .orElse(null); - - // If the result is null, assume that the operation was aborted by the user - if (r != null) { - passwords.put(msg.getId(), r); - requestToId.put(msg.getRequest(), msg.getId()); - } - + var r = AskpassAlert.query(msg.getPrompt(), msg.getRequest(), msg.getId()); return Response.builder().value(r != null ? r.getSecretValue() : null).build(); } } diff --git a/app/src/main/java/io/xpipe/app/issue/ErrorEvent.java b/app/src/main/java/io/xpipe/app/issue/ErrorEvent.java index fabf09e5..5a24b4f6 100644 --- a/app/src/main/java/io/xpipe/app/issue/ErrorEvent.java +++ b/app/src/main/java/io/xpipe/app/issue/ErrorEvent.java @@ -22,13 +22,16 @@ public class ErrorEvent { @Builder.Default private final boolean reportable = true; - private Throwable throwable; + private final Throwable throwable; @Singular private List attachments; private String userReport; + @Singular + private final List customActions; + public void attachUserReport(String text) { userReport = text; } diff --git a/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java b/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java index 49984261..dde68b4b 100644 --- a/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java +++ b/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java @@ -9,6 +9,7 @@ import io.xpipe.core.process.ShellDialects; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Comparator; import java.util.Optional; public abstract class ExternalApplicationType implements PrefsChoiceValue { @@ -55,13 +56,14 @@ public abstract class ExternalApplicationType implements PrefsChoiceValue { } // Check if returned paths are actually valid + // Also sort them by length to prevent finding a deeply buried app var valid = path.lines().filter(s -> { try { return Files.exists(Path.of(s)); } catch (Exception ex) { return false; } - }).toList(); + }).sorted(Comparator.comparingInt(value -> value.length())).toList(); // Prefer app in proper applications directory var app = valid.stream().filter(s -> s.startsWith("/Applications")).findFirst(); diff --git a/app/src/main/java/io/xpipe/app/storage/DataStorage.java b/app/src/main/java/io/xpipe/app/storage/DataStorage.java index c5e1980b..8a9a9d2e 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStorage.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStorage.java @@ -101,14 +101,18 @@ public abstract class DataStorage { } public synchronized Optional getParent(DataStoreEntry entry, boolean display) { - if (!entry.getState().isUsable()) { + if (entry.getState() == DataStoreEntry.State.LOAD_FAILED) { return Optional.empty(); } - var provider = entry.getProvider(); - var parent = - display ? provider.getDisplayParent(entry.getStore()) : provider.getLogicalParent(entry.getStore()); - return parent != null ? getStoreEntryIfPresent(parent) : Optional.empty(); + try { + var provider = entry.getProvider(); + var parent = + display ? provider.getDisplayParent(entry.getStore()) : provider.getLogicalParent(entry.getStore()); + return parent != null ? getStoreEntryIfPresent(parent) : Optional.empty(); + } catch (Exception ex) { + return Optional.empty(); + } } public synchronized List getStoreChildren(DataStoreEntry entry, boolean display, boolean deep) { diff --git a/app/src/main/java/io/xpipe/app/util/AskpassAlert.java b/app/src/main/java/io/xpipe/app/util/AskpassAlert.java index 431c0900..0242de04 100644 --- a/app/src/main/java/io/xpipe/app/util/AskpassAlert.java +++ b/app/src/main/java/io/xpipe/app/util/AskpassAlert.java @@ -3,7 +3,6 @@ package io.xpipe.app.util; import io.xpipe.app.core.AppI18n; import io.xpipe.app.core.AppWindowHelper; import io.xpipe.app.fxcomps.impl.SecretFieldComp; -import io.xpipe.core.store.DataStore; import io.xpipe.core.util.SecretValue; import javafx.beans.property.SimpleObjectProperty; import javafx.scene.control.Alert; @@ -19,9 +18,9 @@ public class AskpassAlert { private static final Map requestToId = new HashMap<>(); private static final Map passwords = new HashMap<>(); - public static SecretValue query(String prompt, DataStore store) { + public static SecretValue query(String prompt, Object key) { var rid = UUID.randomUUID(); - var secretId = UUID.nameUUIDFromBytes(ByteBuffer.allocate(4).putInt(store.hashCode()).array()); + var secretId = UUID.nameUUIDFromBytes(ByteBuffer.allocate(4).putInt(key.hashCode()).array()); return query(prompt, rid, secretId); } @@ -39,6 +38,8 @@ public class AskpassAlert { var r = AppWindowHelper.showBlockingAlert(alert -> { alert.setTitle(AppI18n.get("askpassAlertTitle")); alert.setHeaderText(prompt); +// alert.getDialogPane().setHeader( +// AppWindowHelper.alertContentText(prompt)); alert.setAlertType(Alert.AlertType.CONFIRMATION); var text = new SecretFieldComp(prop).createRegion(); diff --git a/app/src/main/java/io/xpipe/app/util/FileOpener.java b/app/src/main/java/io/xpipe/app/util/FileOpener.java index 9a67e347..7589bd26 100644 --- a/app/src/main/java/io/xpipe/app/util/FileOpener.java +++ b/app/src/main/java/io/xpipe/app/util/FileOpener.java @@ -22,10 +22,11 @@ public class FileOpener { } var file = entry.getPath(); + var key = entry.getPath().hashCode() + entry.getFileSystem().hashCode(); FileBridge.get() .openIO( FileNames.getFileName(file), - file, + key, () -> { return entry.getFileSystem().openInput(file); }, @@ -40,10 +41,11 @@ public class FileOpener { } var file = entry.getPath(); + var key = entry.getPath().hashCode() + entry.getFileSystem().hashCode(); FileBridge.get() .openIO( FileNames.getFileName(file), - file, + key, () -> { return entry.getFileSystem().openInput(file); }, diff --git a/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategy.java b/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategy.java index ccac00ac..0b8fd762 100644 --- a/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategy.java +++ b/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategy.java @@ -17,18 +17,19 @@ import java.util.function.Supplier; @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes({ - @JsonSubTypes.Type(value = SecretRetrievalStrategy.None.class), - @JsonSubTypes.Type(value = SecretRetrievalStrategy.Unsupported.class), - @JsonSubTypes.Type(value = SecretRetrievalStrategy.Reference.class), - @JsonSubTypes.Type(value = SecretRetrievalStrategy.InPlace.class), - @JsonSubTypes.Type(value = SecretRetrievalStrategy.Prompt.class), - @JsonSubTypes.Type(value = SecretRetrievalStrategy.CustomCommand.class), - @JsonSubTypes.Type(value = SecretRetrievalStrategy.PasswordManager.class) + @JsonSubTypes.Type(value = SecretRetrievalStrategy.None.class), + @JsonSubTypes.Type(value = SecretRetrievalStrategy.Reference.class), + @JsonSubTypes.Type(value = SecretRetrievalStrategy.InPlace.class), + @JsonSubTypes.Type(value = SecretRetrievalStrategy.Prompt.class), + @JsonSubTypes.Type(value = SecretRetrievalStrategy.CustomCommand.class), + @JsonSubTypes.Type(value = SecretRetrievalStrategy.PasswordManager.class) }) public interface SecretRetrievalStrategy { SecretValue retrieve(String displayName, DataStore store) throws Exception; + boolean supportsLocalAskpass(); + @JsonTypeName("none") public static class None implements SecretRetrievalStrategy { @@ -36,14 +37,10 @@ public interface SecretRetrievalStrategy { public SecretValue retrieve(String displayName, DataStore store) { return null; } - } - - @JsonTypeName("unsupported") - public static class Unsupported implements SecretRetrievalStrategy { @Override - public SecretValue retrieve(String displayName, DataStore store) { - throw new UnsupportedOperationException(); + public boolean supportsLocalAskpass() { + return true; } } @@ -61,6 +58,11 @@ public interface SecretRetrievalStrategy { public SecretValue retrieve(String displayName, DataStore store) { return supplier.get(); } + + @Override + public boolean supportsLocalAskpass() { + return false; + } } @JsonTypeName("inPlace") @@ -80,6 +82,11 @@ public interface SecretRetrievalStrategy { public SecretValue retrieve(String displayName, DataStore store) { return value; } + + @Override + public boolean supportsLocalAskpass() { + return false; + } } @JsonTypeName("prompt") @@ -89,6 +96,11 @@ public interface SecretRetrievalStrategy { public SecretValue retrieve(String displayName, DataStore store) { return AskpassAlert.query(displayName, store); } + + @Override + public boolean supportsLocalAskpass() { + return true; + } } @JsonTypeName("passwordManager") @@ -110,6 +122,11 @@ public interface SecretRetrievalStrategy { return SecretHelper.encrypt(cc.readStdoutOrThrow()); } } + + @Override + public boolean supportsLocalAskpass() { + return false; + } } @JsonTypeName("customCommand") @@ -126,5 +143,10 @@ public interface SecretRetrievalStrategy { return SecretHelper.encrypt(cc.readStdoutOrThrow()); } } + + @Override + public boolean supportsLocalAskpass() { + return false; + } } } diff --git a/app/src/main/resources/io/xpipe/app/resources/img/connection_icon-dark.svg b/app/src/main/resources/io/xpipe/app/resources/img/connection_icon-dark.svg new file mode 100644 index 00000000..2f5dcf64 --- /dev/null +++ b/app/src/main/resources/io/xpipe/app/resources/img/connection_icon-dark.svg @@ -0,0 +1,56 @@ + + + + + + + connection + + + + + connection + + + + diff --git a/app/src/main/resources/io/xpipe/app/resources/img/connection_icon.svg b/app/src/main/resources/io/xpipe/app/resources/img/connection_icon.svg new file mode 100644 index 00000000..43153f58 --- /dev/null +++ b/app/src/main/resources/io/xpipe/app/resources/img/connection_icon.svg @@ -0,0 +1,56 @@ + + + + + + + connection + + + + + connection + + + + diff --git a/app/src/main/resources/io/xpipe/app/resources/img/home_icon-dark.svg b/app/src/main/resources/io/xpipe/app/resources/img/home_icon-dark.svg new file mode 100644 index 00000000..5444866e --- /dev/null +++ b/app/src/main/resources/io/xpipe/app/resources/img/home_icon-dark.svg @@ -0,0 +1,56 @@ + + + + + + + home + + + + + home + + + + diff --git a/app/src/main/resources/io/xpipe/app/resources/img/home_icon.png b/app/src/main/resources/io/xpipe/app/resources/img/home_icon.png deleted file mode 100644 index c8f5fe9c..00000000 Binary files a/app/src/main/resources/io/xpipe/app/resources/img/home_icon.png and /dev/null differ diff --git a/app/src/main/resources/io/xpipe/app/resources/img/home_icon.svg b/app/src/main/resources/io/xpipe/app/resources/img/home_icon.svg new file mode 100644 index 00000000..380a865f --- /dev/null +++ b/app/src/main/resources/io/xpipe/app/resources/img/home_icon.svg @@ -0,0 +1,56 @@ + + + + + + + home + + + + + home + + + + diff --git a/app/src/main/resources/io/xpipe/app/resources/img/machine_icon.png b/app/src/main/resources/io/xpipe/app/resources/img/machine_icon.png deleted file mode 100644 index e1a23719..00000000 Binary files a/app/src/main/resources/io/xpipe/app/resources/img/machine_icon.png and /dev/null differ diff --git a/app/src/main/resources/io/xpipe/app/resources/web/github-markdown-dark.css b/app/src/main/resources/io/xpipe/app/resources/web/github-markdown-dark.css index 864d027b..de117201 100644 --- a/app/src/main/resources/io/xpipe/app/resources/web/github-markdown-dark.css +++ b/app/src/main/resources/io/xpipe/app/resources/web/github-markdown-dark.css @@ -1,11 +1,16 @@ +html { + font-family: Roboto; +} + .markdown-body { color-scheme: dark; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; margin: 0; + padding: 1em; color: #c9d1d9; font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"; - font-size: 16px; + font-size: 14px; line-height: 1.5; word-wrap: break-word; } diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/AskpassExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/AskpassExchange.java index bb15c824..6d3b3b8c 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/AskpassExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/AskpassExchange.java @@ -7,6 +7,8 @@ import lombok.NonNull; import lombok.Value; import lombok.extern.jackson.Jacksonized; +import java.util.UUID; + public class AskpassExchange implements MessageExchange { @Override @@ -19,10 +21,10 @@ public class AskpassExchange implements MessageExchange { @Value public static class Request implements RequestMessage { @NonNull - String id; + UUID id; @NonNull - String request; + UUID request; String prompt; } diff --git a/core/src/main/java/io/xpipe/core/process/CommandBuilder.java b/core/src/main/java/io/xpipe/core/process/CommandBuilder.java index 9569f2a6..aaee90a9 100644 --- a/core/src/main/java/io/xpipe/core/process/CommandBuilder.java +++ b/core/src/main/java/io/xpipe/core/process/CommandBuilder.java @@ -45,6 +45,11 @@ public class CommandBuilder { return this; } + public CommandBuilder remove(String s) { + elements.removeIf(element -> element instanceof Fixed fixed && s.equals(fixed.string)); + return this; + } + public CommandBuilder addQuoted(String s) { elements.add(new Fixed("\"" + s + "\"")); return this; diff --git a/core/src/main/java/io/xpipe/core/process/ElevationResult.java b/core/src/main/java/io/xpipe/core/process/ElevationResult.java new file mode 100644 index 00000000..71c148c6 --- /dev/null +++ b/core/src/main/java/io/xpipe/core/process/ElevationResult.java @@ -0,0 +1,9 @@ +package io.xpipe.core.process; + +import lombok.Value; + +@Value +public class ElevationResult { + String value; + boolean promptsUserInput; +} diff --git a/core/src/main/java/io/xpipe/core/process/ShellControl.java b/core/src/main/java/io/xpipe/core/process/ShellControl.java index 62f263b9..3e06be8c 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellControl.java +++ b/core/src/main/java/io/xpipe/core/process/ShellControl.java @@ -88,6 +88,8 @@ public interface ShellControl extends ProcessControl { } } + ElevationResult elevateCommand(String input) throws Exception; + void restart() throws Exception; OsType getOsType(); diff --git a/core/src/main/java/io/xpipe/core/store/LaunchableStore.java b/core/src/main/java/io/xpipe/core/store/LaunchableStore.java index 3c0672f0..539ba969 100644 --- a/core/src/main/java/io/xpipe/core/store/LaunchableStore.java +++ b/core/src/main/java/io/xpipe/core/store/LaunchableStore.java @@ -2,5 +2,9 @@ package io.xpipe.core.store; public interface LaunchableStore extends DataStore { + default boolean canLaunch() { + return true; + } + String prepareLaunchCommand(String displayName) throws Exception; } diff --git a/core/src/main/java/io/xpipe/core/util/UuidHelper.java b/core/src/main/java/io/xpipe/core/util/UuidHelper.java index ff03f8c2..2457aa57 100644 --- a/core/src/main/java/io/xpipe/core/util/UuidHelper.java +++ b/core/src/main/java/io/xpipe/core/util/UuidHelper.java @@ -1,10 +1,15 @@ package io.xpipe.core.util; +import java.nio.charset.StandardCharsets; import java.util.Optional; import java.util.UUID; public class UuidHelper { + public static UUID generateFromObject(Object o) { + return UUID.nameUUIDFromBytes(o.toString().getBytes(StandardCharsets.UTF_8)); + } + public static Optional parse(String s) { try { return Optional.of(UUID.fromString(s)); diff --git a/core/src/main/java/io/xpipe/core/util/XPipeInstallation.java b/core/src/main/java/io/xpipe/core/util/XPipeInstallation.java index 5418b764..46922dce 100644 --- a/core/src/main/java/io/xpipe/core/util/XPipeInstallation.java +++ b/core/src/main/java/io/xpipe/core/util/XPipeInstallation.java @@ -178,7 +178,9 @@ public class XPipeInstallation { } public static String getLocalDefaultCliExecutable() { - Path path = ModuleHelper.isImage() ? getCurrentInstallationBasePath() : Path.of(getLocalDefaultInstallationBasePath(true)); + Path path = ModuleHelper.isImage() + ? getCurrentInstallationBasePath() + : Path.of(getLocalDefaultInstallationBasePath(true)); return path.resolve(getRelativeCliExecutablePath(OsType.getLocal())).toString(); } diff --git a/dist/changelogs/1.5.0.md b/dist/changelogs/1.5.0.md index dbcf2048..93cfb05d 100644 --- a/dist/changelogs/1.5.0.md +++ b/dist/changelogs/1.5.0.md @@ -63,6 +63,7 @@ This also comes with full support of the feature set for these environments - Implement a new internal API to better assemble complex commands - Rework os detection logic for passthrough environments like Cygwin and MSYS2 - Fix desktop directory not being determined correctly on Windows when it was moved from the default location +- Fix various checks in file browser not being applied properly and leading to wrong error messages - Rework threading in navigation bar in browser to improve responsiveness - Recheck if prepared update is still the latest one prior to installing it - Properly use shell script file extension for external editor when creating shell environments diff --git a/ext/base/src/main/java/io/xpipe/ext/base/action/LaunchAction.java b/ext/base/src/main/java/io/xpipe/ext/base/action/LaunchAction.java index 338e8ae9..329ab7ab 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/action/LaunchAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/LaunchAction.java @@ -57,13 +57,14 @@ public class LaunchAction implements ActionProvider { @Override public DefaultDataStoreCallSite getDefaultDataStoreCallSite() { return new DefaultDataStoreCallSite() { + @Override public boolean isApplicable(LaunchableStore o) { return DataStorage.get() .getStoreEntryIfPresent(o) .orElseThrow() .getState() - .isUsable(); + .isUsable() && o.canLaunch(); } @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/browser/FollowLinkAction.java b/ext/base/src/main/java/io/xpipe/ext/base/browser/FollowLinkAction.java index 4719b2bf..593024bc 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/browser/FollowLinkAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/browser/FollowLinkAction.java @@ -20,7 +20,7 @@ public class FollowLinkAction implements LeafAction { @Override public void execute(OpenFileSystemModel model, List entries) { var target = FileNames.getParent(entries.get(0).getRawFileEntry().resolved().getPath()); - model.cdSync(target); + model.cdAsync(target); } @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenDirectoryAction.java b/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenDirectoryAction.java index 37c54d7b..398db85b 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenDirectoryAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenDirectoryAction.java @@ -16,7 +16,7 @@ public class OpenDirectoryAction implements LeafAction { @Override public void execute(OpenFileSystemModel model, List entries) { - model.cdSync(entries.get(0).getRawFileEntry().getPath()); + model.cdAsync(entries.get(0).getRawFileEntry().getPath()); } @Override