From 4e7646bec12e2acbe808ad753ea0b5c3129c2b1c Mon Sep 17 00:00:00 2001 From: crschnick Date: Tue, 2 Sep 2025 11:44:17 +0000 Subject: [PATCH] Rework --- .../ComputeDirectorySizesActionProvider.java | 8 +++++ .../OpenFileNativeDetailsActionProvider.java | 4 +++ .../browser/file/BrowserBreadcrumbBar.java | 35 ++++++++++--------- .../app/browser/file/BrowserFileListComp.java | 27 +++++++++----- .../app/browser/file/BrowserFileOpener.java | 11 ++++-- .../ComputeDirectorySizesMenuProvider.java | 2 +- .../menu/impl/NewItemMenuProvider.java | 3 +- .../OpenTerminalInDirectoryMenuProvider.java | 4 +++ .../xpipe/app/ext/ConnectionFileSystem.java | 11 ++++++ .../java/io/xpipe/app/ext/FileSystem.java | 5 +++ .../io/xpipe/app/ext/WrapperFileSystem.java | 11 ++++++ 11 files changed, 90 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/io/xpipe/app/browser/action/impl/ComputeDirectorySizesActionProvider.java b/app/src/main/java/io/xpipe/app/browser/action/impl/ComputeDirectorySizesActionProvider.java index 257b5038a..bce7eb7f6 100644 --- a/app/src/main/java/io/xpipe/app/browser/action/impl/ComputeDirectorySizesActionProvider.java +++ b/app/src/main/java/io/xpipe/app/browser/action/impl/ComputeDirectorySizesActionProvider.java @@ -3,11 +3,14 @@ package io.xpipe.app.browser.action.impl; import io.xpipe.app.browser.action.BrowserAction; import io.xpipe.app.browser.action.BrowserActionProvider; import io.xpipe.app.browser.file.BrowserEntry; +import io.xpipe.app.browser.file.BrowserFileSystemTabModel; import io.xpipe.core.FileKind; import lombok.experimental.SuperBuilder; import lombok.extern.jackson.Jacksonized; +import java.util.List; + public class ComputeDirectorySizesActionProvider implements BrowserActionProvider { @Override @@ -15,6 +18,11 @@ public class ComputeDirectorySizesActionProvider implements BrowserActionProvide return "computeDirectorySizes"; } + @Override + public boolean isApplicable(BrowserFileSystemTabModel model, List entries) { + return model.getFileSystem().getShell().isPresent(); + } + @Jacksonized @SuperBuilder public static class Action extends BrowserAction { diff --git a/app/src/main/java/io/xpipe/app/browser/action/impl/OpenFileNativeDetailsActionProvider.java b/app/src/main/java/io/xpipe/app/browser/action/impl/OpenFileNativeDetailsActionProvider.java index 73920dfe5..ba86c5e5d 100644 --- a/app/src/main/java/io/xpipe/app/browser/action/impl/OpenFileNativeDetailsActionProvider.java +++ b/app/src/main/java/io/xpipe/app/browser/action/impl/OpenFileNativeDetailsActionProvider.java @@ -24,6 +24,10 @@ public class OpenFileNativeDetailsActionProvider implements BrowserActionProvide @Override public boolean isApplicable(BrowserFileSystemTabModel model, List entries) { + if (model.getFileSystem().getShell().isEmpty()) { + return false; + } + var sc = model.getFileSystem().getShell().orElseThrow(); return sc.getLocalSystemAccess().supportsFileSystemAccess(); } diff --git a/app/src/main/java/io/xpipe/app/browser/file/BrowserBreadcrumbBar.java b/app/src/main/java/io/xpipe/app/browser/file/BrowserBreadcrumbBar.java index 06739f2fe..1c2164bf5 100644 --- a/app/src/main/java/io/xpipe/app/browser/file/BrowserBreadcrumbBar.java +++ b/app/src/main/java/io/xpipe/app/browser/file/BrowserBreadcrumbBar.java @@ -28,10 +28,10 @@ public class BrowserBreadcrumbBar extends SimpleComp { @Override protected Region createSimple() { - Callback, ButtonBase> crumbFactory = crumb -> { - var name = crumb.getValue().equals("/") + Callback, ButtonBase> crumbFactory = crumb -> { + var name = crumb.getValue().toString().equals("/") ? "/" - : FilePath.of(crumb.getValue()).getFileName(); + : crumb.getValue().getFileName(); var btn = new Button(name, null); btn.setMnemonicParsing(false); btn.setFocusTraversable(false); @@ -41,10 +41,10 @@ public class BrowserBreadcrumbBar extends SimpleComp { } private Region createBreadcrumbs( - Callback, ButtonBase> crumbFactory, - Callback, ? extends Node> dividerFactory) { + Callback, ButtonBase> crumbFactory, + Callback, ? extends Node> dividerFactory) { - var breadcrumbs = new Breadcrumbs(); + var breadcrumbs = new Breadcrumbs(); breadcrumbs.setMinWidth(0); model.getCurrentPath().subscribe(val -> { PlatformThread.runLaterIfNeeded(() -> { @@ -59,7 +59,7 @@ public class BrowserBreadcrumbBar extends SimpleComp { return null; } - if (item.isFirst() && item.getValue().equals("/")) { + if (item.isFirst() && item.getValue().toString().equals("/")) { return new Label(""); } @@ -71,12 +71,8 @@ public class BrowserBreadcrumbBar extends SimpleComp { }); var elements = createBreadcumbHierarchy(val); - var modifiedElements = new ArrayList<>(elements); - if (val.toString().startsWith("/")) { - modifiedElements.addFirst("/"); - } - Breadcrumbs.BreadCrumbItem items = - Breadcrumbs.buildTreeModel(modifiedElements.toArray(String[]::new)); + Breadcrumbs.BreadCrumbItem items = + Breadcrumbs.buildTreeModel(elements.toArray(FilePath[]::new)); breadcrumbs.setSelectedCrumb(items); }); }); @@ -95,19 +91,24 @@ public class BrowserBreadcrumbBar extends SimpleComp { return breadcrumbs; } - private List createBreadcumbHierarchy(FilePath filePath) { - var f = filePath.toString() + "/"; - var list = new ArrayList(); + private List createBreadcumbHierarchy(FilePath filePath) { + var f = filePath.toDirectory().toString(); + var list = new ArrayList(); int lastElementStart = 0; for (int i = 0; i < f.length(); i++) { if (f.charAt(i) == '\\' || f.charAt(i) == '/') { if (i - lastElementStart > 0) { - list.add(f.substring(0, i)); + list.add(FilePath.of(f.substring(0, i))); } lastElementStart = i + 1; } } + + if (filePath.toString().startsWith("/")) { + list.addFirst(FilePath.of("/")); + } + return list; } } diff --git a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileListComp.java b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileListComp.java index 6032dc497..16885d482 100644 --- a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileListComp.java +++ b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileListComp.java @@ -229,18 +229,27 @@ public final class BrowserFileListComp extends SimpleComp { var m = fileList.getFileSystemModel(); var user = unix.getUser() != null ? unix.getUser() - : m.getCache().getUsers().getOrDefault(unix.getUid(), "?"); + : m.getCache() != null ? m.getCache().getUsers().getOrDefault(unix.getUid(), "?") : null; var group = unix.getGroup() != null ? unix.getGroup() - : m.getCache().getGroups().getOrDefault(unix.getGid(), "?"); - var uid = String.valueOf( - unix.getUid() != null ? unix.getUid() : m.getCache().getUidForUser(user)); - var gid = String.valueOf( - unix.getGid() != null ? unix.getGid() : m.getCache().getGidForGroup(group)); - if (uid.equals(gid) && user.equals(group)) { - return user + " [" + uid + "]"; + : m.getCache() != null ? m.getCache().getGroups().getOrDefault(unix.getGid(), "?") : null; + var uid = + unix.getUid() != null ? String.valueOf(unix.getUid()) : m.getCache() != null ? m.getCache().getUidForUser(user) : null; + var gid = + unix.getGid() != null ? String.valueOf(unix.getGid()) : m.getCache() != null ? m.getCache().getGidForGroup(group) : null; + + var userFormat = user + (uid != null ? " [" + uid + "]" : ""); + var groupFormat = group + (gid != null ? " [" + gid + "]" : ""); + + if (uid != null && uid.equals(gid) && user != null && user.equals(group)) { + return userFormat; } - return user + " [" + uid + "] / " + group + " [" + gid + "]"; + + if (uid == null && gid == null && user != null && user.equals(group)) { + return userFormat; + } + + return userFormat + " / " + groupFormat; } private void prepareTypedSelectionModel(TableView table) { diff --git a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileOpener.java b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileOpener.java index 36ccd0d64..4dc59de88 100644 --- a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileOpener.java +++ b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileOpener.java @@ -64,6 +64,10 @@ public class BrowserFileOpener { private static boolean requiresSudo(BrowserFileSystemTabModel model, FileInfo.Unix info, FilePath filePath) throws Exception { + if (model.getFileSystem().getShell().isEmpty() || model.getCache() == null) { + return false; + } + if (model.getCache().isRoot()) { return false; } @@ -168,7 +172,7 @@ public class BrowserFileOpener { } public static void openWithAnyApplication(BrowserFileSystemTabModel model, FileEntry entry) { - if (model.getFileSystem().getShell().orElseThrow().isLocal()) { + if (model.getFileSystem().getShell().isPresent() && model.getFileSystem().getShell().get().isLocal()) { FileOpener.openWithAnyApplication(entry.getPath().toString()); return; } @@ -217,7 +221,7 @@ public class BrowserFileOpener { } public static void openInDefaultApplication(BrowserFileSystemTabModel model, FileEntry entry) { - if (model.getFileSystem().getShell().orElseThrow().isLocal()) { + if (model.getFileSystem().getShell().isPresent() && model.getFileSystem().getShell().get().isLocal()) { FileOpener.openInDefaultApplication(entry.getPath().toString()); return; } @@ -270,7 +274,8 @@ public class BrowserFileOpener { if (editor == null) { return; } - if (model.getFileSystem().getShell().orElseThrow().isLocal()) { + + if (model.getFileSystem().getShell().isPresent() && model.getFileSystem().getShell().get().isLocal()) { FileOpener.openInTextEditor(entry.getPath().toString()); return; } diff --git a/app/src/main/java/io/xpipe/app/browser/menu/impl/ComputeDirectorySizesMenuProvider.java b/app/src/main/java/io/xpipe/app/browser/menu/impl/ComputeDirectorySizesMenuProvider.java index 600645441..f6bdde733 100644 --- a/app/src/main/java/io/xpipe/app/browser/menu/impl/ComputeDirectorySizesMenuProvider.java +++ b/app/src/main/java/io/xpipe/app/browser/menu/impl/ComputeDirectorySizesMenuProvider.java @@ -44,7 +44,7 @@ public class ComputeDirectorySizesMenuProvider implements BrowserMenuLeafProvide @Override public boolean isApplicable(BrowserFileSystemTabModel model, List entries) { - return entries.stream() + return model.getFileSystem().getShell().isPresent() && entries.stream() .allMatch(browserEntry -> browserEntry.getRawFileEntry().getKind() == FileKind.DIRECTORY); } diff --git a/app/src/main/java/io/xpipe/app/browser/menu/impl/NewItemMenuProvider.java b/app/src/main/java/io/xpipe/app/browser/menu/impl/NewItemMenuProvider.java index 9d92fd4e3..d90b34a14 100644 --- a/app/src/main/java/io/xpipe/app/browser/menu/impl/NewItemMenuProvider.java +++ b/app/src/main/java/io/xpipe/app/browser/menu/impl/NewItemMenuProvider.java @@ -188,7 +188,8 @@ public class NewItemMenuProvider implements BrowserMenuBranchProvider { @Override public boolean isApplicable(BrowserFileSystemTabModel model, List entries) { - return model.getFileSystem().getShell().orElseThrow().getOsType() != OsType.WINDOWS; + return model.getFileSystem().getShell().isEmpty() || + model.getFileSystem().getShell().orElseThrow().getOsType() != OsType.WINDOWS; } @Override diff --git a/app/src/main/java/io/xpipe/app/browser/menu/impl/OpenTerminalInDirectoryMenuProvider.java b/app/src/main/java/io/xpipe/app/browser/menu/impl/OpenTerminalInDirectoryMenuProvider.java index fbfed1947..521a4722f 100644 --- a/app/src/main/java/io/xpipe/app/browser/menu/impl/OpenTerminalInDirectoryMenuProvider.java +++ b/app/src/main/java/io/xpipe/app/browser/menu/impl/OpenTerminalInDirectoryMenuProvider.java @@ -44,6 +44,10 @@ public class OpenTerminalInDirectoryMenuProvider implements BrowserMenuLeafProvi @Override public boolean isApplicable(BrowserFileSystemTabModel model, List entries) { + if (model.getFileSystem().getShell().isEmpty()) { + return false; + } + return entries.stream().allMatch(entry -> entry.getRawFileEntry().getKind() == FileKind.DIRECTORY); } diff --git a/app/src/main/java/io/xpipe/app/ext/ConnectionFileSystem.java b/app/src/main/java/io/xpipe/app/ext/ConnectionFileSystem.java index 187fa137f..597325537 100644 --- a/app/src/main/java/io/xpipe/app/ext/ConnectionFileSystem.java +++ b/app/src/main/java/io/xpipe/app/ext/ConnectionFileSystem.java @@ -8,6 +8,7 @@ import io.xpipe.app.util.DocumentationLink; import io.xpipe.core.FilePath; import com.fasterxml.jackson.annotation.JsonIgnore; +import io.xpipe.core.OsType; import lombok.Getter; import java.io.InputStream; @@ -28,6 +29,16 @@ public class ConnectionFileSystem implements FileSystem { this.shellControl = shellControl; } + @Override + public Optional getOsType() { + return Optional.of(shellControl.getOsType()); + } + + @Override + public FilePath pwd() throws Exception { + return shellControl.view().pwd(); + } + @Override public FileSystem createTransferOptimizedFileSystem() throws Exception { // For local, we have our optimized streams regardless diff --git a/app/src/main/java/io/xpipe/app/ext/FileSystem.java b/app/src/main/java/io/xpipe/app/ext/FileSystem.java index af95756a0..34f15793a 100644 --- a/app/src/main/java/io/xpipe/app/ext/FileSystem.java +++ b/app/src/main/java/io/xpipe/app/ext/FileSystem.java @@ -3,6 +3,7 @@ package io.xpipe.app.ext; import io.xpipe.app.process.ShellControl; import io.xpipe.core.FileKind; import io.xpipe.core.FilePath; +import io.xpipe.core.OsType; import java.io.Closeable; import java.io.InputStream; @@ -15,6 +16,10 @@ import java.util.stream.Stream; public interface FileSystem extends Closeable, AutoCloseable { + Optional getOsType(); + + FilePath pwd() throws Exception; + FileSystem createTransferOptimizedFileSystem() throws Exception; long getFileSize(FilePath file) throws Exception; diff --git a/app/src/main/java/io/xpipe/app/ext/WrapperFileSystem.java b/app/src/main/java/io/xpipe/app/ext/WrapperFileSystem.java index a95316c4e..dca866af6 100644 --- a/app/src/main/java/io/xpipe/app/ext/WrapperFileSystem.java +++ b/app/src/main/java/io/xpipe/app/ext/WrapperFileSystem.java @@ -3,6 +3,7 @@ package io.xpipe.app.ext; import io.xpipe.app.process.ShellControl; import io.xpipe.core.FilePath; +import io.xpipe.core.OsType; import lombok.Getter; import java.io.IOException; @@ -24,6 +25,16 @@ public class WrapperFileSystem implements FileSystem { this.check = check; } + @Override + public Optional getOsType() { + return fs.getOsType(); + } + + @Override + public FilePath pwd() throws Exception { + return fs.pwd(); + } + @Override public FileSystem createTransferOptimizedFileSystem() { return this;