diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserBookmarkList.java b/app/src/main/java/io/xpipe/app/browser/BrowserBookmarkList.java index 80b5f9d19..ce8585f9f 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserBookmarkList.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserBookmarkList.java @@ -4,12 +4,16 @@ import io.xpipe.app.comp.storage.store.StoreEntryTree; import io.xpipe.app.comp.storage.store.StoreEntryWrapper; import io.xpipe.app.comp.storage.store.StoreViewState; import io.xpipe.app.fxcomps.SimpleComp; +import io.xpipe.app.fxcomps.impl.IconButtonComp; import io.xpipe.app.fxcomps.impl.PrettyImageComp; import io.xpipe.core.store.DataStore; import io.xpipe.core.store.ShellStore; import javafx.application.Platform; +import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; +import javafx.collections.SetChangeListener; +import javafx.css.PseudoClass; import javafx.geometry.Point2D; import javafx.scene.Node; import javafx.scene.control.TreeCell; @@ -110,9 +114,26 @@ final class BrowserBookmarkList extends SimpleComp { } var fileSystem = ((ShellStore) getItem().getEntry().getStore()); - model.openFileSystemAsync(fileSystem, null); + model.openFileSystemAsync(null, fileSystem, null); event.consume(); }); + var icon = new SimpleObjectProperty("mdal-keyboard_arrow_right"); + getPseudoClassStates().addListener((SetChangeListener) change -> { + if (change.getSet().contains(PseudoClass.getPseudoClass("expanded"))) { + icon.set("mdal-keyboard_arrow_down"); + } else { + icon.set("mdal-keyboard_arrow_right"); + } + }); + var button = new IconButtonComp(icon, + () -> { + getTreeItem().setExpanded(!getTreeItem().isExpanded()); + }) + .apply(struc -> struc.get().setPrefWidth(25)) + .grow(false, true) + .styleClass("expand-button"); + + setDisclosureNode(button.createRegion()); } @Override @@ -147,7 +168,7 @@ final class BrowserBookmarkList extends SimpleComp { return; } - Platform.runLater(() -> model.openExistingFileSystemIfPresent(store.asNeeded())); + Platform.runLater(() -> model.openExistingFileSystemIfPresent(null, store.asNeeded())); } }; DROP_TIMER.schedule(activeTask, 500); diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserComp.java index a4723c504..537450e42 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserComp.java @@ -6,12 +6,13 @@ import atlantafx.base.theme.Styles; import io.xpipe.app.browser.icon.DirectoryType; import io.xpipe.app.browser.icon.FileIconManager; import io.xpipe.app.browser.icon.FileType; +import io.xpipe.app.ext.DataStoreProviders; import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.SimpleCompStructure; import io.xpipe.app.fxcomps.augment.GrowAugment; +import io.xpipe.app.fxcomps.impl.FancyTooltipAugment; import io.xpipe.app.fxcomps.impl.PrettyImageComp; import io.xpipe.app.fxcomps.util.PlatformThread; -import io.xpipe.app.storage.DataStorage; import io.xpipe.app.util.BusyProperty; import io.xpipe.app.util.ThreadHelper; import javafx.application.Platform; @@ -231,14 +232,12 @@ public class BrowserComp extends SimpleComp { .bind(Bindings.createDoubleBinding( () -> model.getBusy().get() ? -1d : 0, PlatformThread.sync(model.getBusy()))); - var name = DataStorage.get().getStoreEntry(model.getStore()).getName(); - var image = DataStorage.get() - .getStoreEntry(model.getStore()) - .getProvider() + var image = DataStoreProviders.byStore(model.getStore()) .getDisplayIconFileName(model.getStore()); var logo = new PrettyImageComp(new SimpleStringProperty(image), 20, 20).createRegion(); - var label = new Label(name); + var label = new Label(model.getName()); + label.setTextOverrun(OverrunStyle.CENTER_ELLIPSIS); label.addEventHandler(DragEvent.DRAG_ENTERED, new EventHandler() { @Override public void handle(DragEvent mouseEvent) { @@ -254,6 +253,7 @@ public class BrowserComp extends SimpleComp { PlatformThread.sync(model.getBusy()))); tab.setGraphic(label); + new FancyTooltipAugment<>(new SimpleStringProperty(model.getName())).augment(label); GrowAugment.create(true, false).augment(new SimpleCompStructure<>(label)); tab.setContent(new OpenFileSystemComp(model).createSimple()); return tab; diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserModel.java b/app/src/main/java/io/xpipe/app/browser/BrowserModel.java index c8d62f7d6..6ddaf8eb5 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserModel.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserModel.java @@ -97,18 +97,18 @@ public class BrowserModel { }); } - public void openExistingFileSystemIfPresent(ShellStore store) { + public void openExistingFileSystemIfPresent(String name, ShellStore store) { var found = openFileSystems.stream() .filter(model -> Objects.equals(model.getStore(), store)) .findFirst(); if (found.isPresent()) { selected.setValue(found.get()); } else { - openFileSystemAsync(store, null); + openFileSystemAsync(name, store, null); } } - public void openFileSystemAsync(ShellStore store, String path) { + public void openFileSystemAsync(String name, ShellStore store, String path) { // // Prevent multiple tabs in non browser modes // if (!mode.equals(Mode.BROWSER)) { // ThreadHelper.runFailableAsync(() -> { @@ -127,7 +127,7 @@ public class BrowserModel { // } ThreadHelper.runFailableAsync(() -> { - var model = new OpenFileSystemModel(this, store); + var model = new OpenFileSystemModel(name, this, store); model.initFileSystem(); openFileSystems.add(model); selected.setValue(model); 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 912c65e53..024f47733 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserNavBar.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserNavBar.java @@ -38,7 +38,8 @@ public class BrowserNavBar extends SimpleComp { pathBar.pseudoClassStateChanged(INVISIBLE, !val); if (val) { Platform.runLater(() -> { - pathBar.selectAll(); + pathBar.end(); + pathBar.selectBackward(); }); } }); diff --git a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemCache.java b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemCache.java index 603838e83..e167a73a5 100644 --- a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemCache.java +++ b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemCache.java @@ -1,6 +1,8 @@ package io.xpipe.app.browser; import io.xpipe.app.util.ApplicationHelper; +import io.xpipe.core.process.ShellControl; +import io.xpipe.core.process.ShellDialect; import java.util.HashMap; import java.util.Map; @@ -9,11 +11,22 @@ public class OpenFileSystemCache { private final OpenFileSystemModel model; private final Map installedApplications = new HashMap<>(); + private String username; public OpenFileSystemCache(OpenFileSystemModel model) { this.model = model; } + public void init() throws Exception { + ShellControl sc = model.getFileSystem().getShell().get(); + ShellDialect d = sc.getShellDialect(); + username = sc.executeSimpleStringCommand(d.getPrintVariableCommand(d.getUsernameVariableName())); + } + + public boolean isRoot() { + return username.equals("root"); + } + public boolean isApplicationInPath(String app) { if (!installedApplications.containsKey(app)) { try { 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 706473bc7..c91138a13 100644 --- a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemModel.java +++ b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemModel.java @@ -44,11 +44,13 @@ public final class OpenFileSystemModel { private final Property savedState = new SimpleObjectProperty<>(); private final OpenFileSystemCache cache = new OpenFileSystemCache(this); private final Property overlay = new SimpleObjectProperty<>(); + private final String name; private boolean local; - public OpenFileSystemModel(BrowserModel browserModel, FileSystemStore store) { + public OpenFileSystemModel(String name, BrowserModel browserModel, FileSystemStore store) { this.browserModel = browserModel; this.store = store; + this.name = name != null ? name : DataStorage.get().getStoreEntry(store).getName(); fileList = new BrowserFileListModel(this); addListeners(); } @@ -336,6 +338,7 @@ public final class OpenFileSystemModel { fs.open(); this.fileSystem = fs; this.local = fs.getShell().map(shellControl -> shellControl.isLocal()).orElse(false); + this.cache.init(); }); } diff --git a/app/src/main/java/io/xpipe/app/comp/source/store/GuiDsStoreCreator.java b/app/src/main/java/io/xpipe/app/comp/source/store/GuiDsStoreCreator.java index 73df1e012..d52cbec4b 100644 --- a/app/src/main/java/io/xpipe/app/comp/source/store/GuiDsStoreCreator.java +++ b/app/src/main/java/io/xpipe/app/comp/source/store/GuiDsStoreCreator.java @@ -107,7 +107,7 @@ public class GuiDsStoreCreator extends MultiStepComp.Step> { show(null, null, null, filter, e -> { try { DataStorage.get().addStoreEntry(e); - ScanAlert.showAsync(e.getStore(), true); + // ScanAlert.showAsync(e.getStore(), true); } catch (Exception ex) { ErrorEvent.fromThrowable(ex).handle(); } diff --git a/app/src/main/java/io/xpipe/app/core/mode/BaseMode.java b/app/src/main/java/io/xpipe/app/core/mode/BaseMode.java index 66954cff4..e3b48d4f6 100644 --- a/app/src/main/java/io/xpipe/app/core/mode/BaseMode.java +++ b/app/src/main/java/io/xpipe/app/core/mode/BaseMode.java @@ -54,6 +54,7 @@ public class BaseMode extends OperationMode { SourceCollectionViewState.reset(); StoreViewState.reset(); DataStorage.reset(); + AppPrefs.reset(); AppExtensionManager.reset(); TrackEvent.info("mode", "Background mode shutdown finished"); } diff --git a/app/src/main/java/io/xpipe/app/launcher/LauncherInput.java b/app/src/main/java/io/xpipe/app/launcher/LauncherInput.java index e2aa88a35..4c794e988 100644 --- a/app/src/main/java/io/xpipe/app/launcher/LauncherInput.java +++ b/app/src/main/java/io/xpipe/app/launcher/LauncherInput.java @@ -117,7 +117,7 @@ public abstract class LauncherInput { } var dir = Files.isDirectory(file) ? file : file.getParent(); - BrowserModel.DEFAULT.openFileSystemAsync(ShellStore.createLocal(), dir.toString()); + BrowserModel.DEFAULT.openFileSystemAsync(null, ShellStore.createLocal(), dir.toString()); } @Override diff --git a/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java b/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java index f7345b5e4..57fe197b1 100644 --- a/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java +++ b/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java @@ -374,6 +374,11 @@ public class AppPrefs { PrefsProvider.getAll().forEach(prov -> prov.init()); } + public static void reset() { + INSTANCE.save(); + INSTANCE = null; + } + public static AppPrefs get() { return INSTANCE; } 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 fd2838047..93e15453e 100644 --- a/app/src/main/java/io/xpipe/app/update/AppInstaller.java +++ b/app/src/main/java/io/xpipe/app/update/AppInstaller.java @@ -171,13 +171,13 @@ public class AppInstaller { public void installRemote(ShellControl shellControl, String file) throws Exception { try (var pc = shellControl.subShell(ShellDialects.BASH).start()) { try (CommandControl c = pc.command("DEBIAN_FRONTEND=noninteractive apt-get remove -qy xpipe") - .elevated() + .elevated("xpipe") .start()) { c.discardOrThrow(); } try (CommandControl c = pc.command( "DEBIAN_FRONTEND=noninteractive apt-get install -qy \"" + file + "\"") - .elevated() + .elevated("xpipe") .start()) { c.discardOrThrow(); } @@ -219,7 +219,7 @@ public class AppInstaller { public void installRemote(ShellControl shellControl, String file) throws Exception { try (var pc = shellControl.subShell(ShellDialects.BASH).start()) { try (CommandControl c = pc.command("rpm -U -v --force \"" + file + "\"") - .elevated() + .elevated("xpipe") .start()) { c.discardOrThrow(); } @@ -257,7 +257,7 @@ public class AppInstaller { try (var pc = shellControl.subShell(ShellDialects.BASH).start()) { try (CommandControl c = pc.command( "installer -verboseR -allowUntrusted -pkg \"" + file + "\" -target /") - .elevated() + .elevated("xpipe") .start()) { c.discardOrThrow(); } diff --git a/app/src/main/java/io/xpipe/app/util/ScriptHelper.java b/app/src/main/java/io/xpipe/app/util/ScriptHelper.java index c62b793b3..d9db36c1b 100644 --- a/app/src/main/java/io/xpipe/app/util/ScriptHelper.java +++ b/app/src/main/java/io/xpipe/app/util/ScriptHelper.java @@ -57,11 +57,11 @@ public class ScriptHelper { content = content + "\n" + applyRcCommand + "\n"; } - if (login) { - var applyProfilesCommand = t.applyProfileFilesCommand(); - if (applyProfilesCommand != null) { - content = content + "\n" + applyProfilesCommand + "\n"; - } + // We just apply the profile files always, as we can't be sure that they definitely have been applied. + // Especially if we launch something that is not the system default shell + var applyProfilesCommand = t.applyProfileFilesCommand(); + if (applyProfilesCommand != null) { + content = content + "\n" + applyProfilesCommand + "\n"; } if (toExecuteInShell != null) { diff --git a/core/src/main/java/io/xpipe/core/process/CommandControl.java b/core/src/main/java/io/xpipe/core/process/CommandControl.java index 51df8207e..e121d9b16 100644 --- a/core/src/main/java/io/xpipe/core/process/CommandControl.java +++ b/core/src/main/java/io/xpipe/core/process/CommandControl.java @@ -60,11 +60,11 @@ public interface CommandControl extends ProcessControl { int getExitCode(); - default CommandControl elevated() { - return elevated((v) -> true); + default CommandControl elevated(String message) { + return elevated(message, (v) -> true); } - CommandControl elevated(FailableFunction elevationFunction); + CommandControl elevated(String message, FailableFunction elevationFunction); @Override CommandControl start() throws Exception; 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 e5107feea..8e0eb8572 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellControl.java +++ b/core/src/main/java/io/xpipe/core/process/ShellControl.java @@ -84,7 +84,7 @@ public interface ShellControl extends ProcessControl { OsType getOsType(); - ShellControl elevated(FailableFunction elevationFunction); + ShellControl elevated(String message, FailableFunction elevationFunction); ShellControl elevationPassword(SecretValue value); diff --git a/core/src/main/java/io/xpipe/core/process/ShellDialect.java b/core/src/main/java/io/xpipe/core/process/ShellDialect.java index cf3460204..828848c62 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellDialect.java +++ b/core/src/main/java/io/xpipe/core/process/ShellDialect.java @@ -100,6 +100,8 @@ public interface ShellDialect { String getPrintVariableCommand(String name); + String getUsernameVariableName(); + String getPrintExitCodeCommand(String prefix); default String getPrintEnvironmentVariableCommand(String name) { diff --git a/ext/base/src/main/java/io/xpipe/ext/base/actions/SampleAction.java b/ext/base/src/main/java/io/xpipe/ext/base/actions/SampleAction.java index 34c2e8bf4..c330fccff 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/actions/SampleAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/actions/SampleAction.java @@ -80,7 +80,7 @@ public class SampleAction implements ActionProvider { // sudo and the optional sudo password automatically provided by XPipe // by using the information from the connection store. // You can also set a custom working directory. - try (CommandControl cc = sc.command("kill ").elevated().workingDirectory("/").start()) { + try (CommandControl cc = sc.command("kill ").elevated("kill").workingDirectory("/").start()) { // Discard any output but throw an exception with the stderr contents if the exit code is not 0 cc.discardOrThrow(); } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenDirectoryInNewTabAction.java b/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenDirectoryInNewTabAction.java index 708b9ef86..3426cba1b 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenDirectoryInNewTabAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenDirectoryInNewTabAction.java @@ -15,7 +15,7 @@ public class OpenDirectoryInNewTabAction implements LeafAction { @Override public void execute(OpenFileSystemModel model, List entries) throws Exception { - model.getBrowserModel().openFileSystemAsync(model.getStore().asNeeded(), entries.get(0).getRawFileEntry().getPath()); + model.getBrowserModel().openFileSystemAsync(model.getName(), model.getStore().asNeeded(), entries.get(0).getRawFileEntry().getPath()); } @Override