This commit is contained in:
crschnick
2026-08-05 19:53:02 +00:00
parent 3dee46d252
commit e16b277ec3
3441 changed files with 49722 additions and 50658 deletions
+4 -13
View File
@@ -5,10 +5,6 @@ There are no real formal contribution guidelines right now, they will maybe come
## Repository Structure
- [core](core) - Shared core classes of the XPipe Java API, XPipe extensions, and the XPipe daemon implementation.
This mainly concerns API classes not a lot of implementation.
- [beacon](beacon) - The XPipe beacon component is responsible for handling all communications between the XPipe
daemon and the client applications, for example APIs and the CLI
- [app](app) - Contains the XPipe daemon implementation and the XPipe desktop application
- [dist](dist) - Tools to create a distributable package of XPipe
- [ext](ext) - Available XPipe extensions. Essentially every concrete feature implementation is implemented as an extension
@@ -20,8 +16,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 26.0.1-zulu
sdk default java 26.0.1-zulu
sdk install java 26.0.2-zulu
sdk default java 26.0.2-zulu
```
On Windows, you have to manually install a JDK, e.g. from [Azul](https://www.azul.com/downloads/?version=java-26-sts&package=jdk#zulu).
@@ -46,7 +42,6 @@ So for example if you currently have XPipe `21.0` installed, you should run `git
You can use the gradle wrapper to build and run the project:
- `gradlew app:run` will run the desktop application. You can set various useful properties in `app/build.gradle`
- `gradlew clean dist` will create a distributable production version in `dist/build/dist/base`.
- `gradlew <project>:test` will run the tests of the specified project.
You are also able to properly debug the built production application:
- The `dist/build/dist/base/app/scripts/xpiped_debug` script will launch the application in debug mode and with a console attached to it
@@ -59,7 +54,7 @@ In case a dependency is (sadly) not modularized yet, module information is manua
Further, note that as this is a pretty complicated Java project that fully utilizes modularity,
many IDEs still have problems building this project properly.
For example, you can't build this project in eclipse or vscode as it will complain about missing modules.
For example, it might be difficult to build this project in eclipse or vscode as it will complain about missing modules.
The tested and recommended IDE is IntelliJ.
When setting up the project in IntelliJ, make sure that the correct JDK (Java 26)
is selected both for the project and for gradle itself.
@@ -70,7 +65,7 @@ Especially when starting out, it might be a good idea to start with easy tasks f
### Interacting via the HTTP API
You can create clients that communicate with the XPipe daemon via its HTTP API.
You can create external clients and tools that communicate with the XPipe daemon via its HTTP API.
To get started, see the [OpenAPI spec](https://docs.xpipe.io/api).
### Implementing support for a new editor
@@ -90,10 +85,6 @@ Once you created your custom classes, you have to register them in your module i
All actions that you can perform for certain connections in the connection overview tab are implemented using an [Action API](https://github.com/xpipe-io/xpipe/blob/master/app/src/main/java/io/xpipe/app/ext/ActionProvider.java). You can find a sample implementation [here](https://github.com/xpipe-io/xpipe/blob/master/ext/base/src/main/java/io/xpipe/ext/base/action/SampleAction.java) and many common action implementations [here](https://github.com/xpipe-io/xpipe/tree/master/ext/base/src/main/java/io/xpipe/ext/base/action).
### Adding more predefined scripts
You can add custom script definitions [here](https://github.com/xpipe-io/xpipe/tree/master/ext/base/src/main/java/io/xpipe/ext/base/script/PredefinedScriptStore.java) and [here](https://github.com/xpipe-io/xpipe/tree/master/ext/base/src/main/resources/io/xpipe/ext/base/resources/scripts).
### Adding more file icons for specific types
You can register file types [here](https://github.com/xpipe-io/xpipe/blob/master/app/src/main/resources/io/xpipe/app/resources/file_list.txt) and add the respective icons [here](https://github.com/xpipe-io/xpipe/tree/master/app/src/main/resources/io/xpipe/app/resources/img/browser).
+14 -14
View File
@@ -17,7 +17,7 @@ It currently supports:
- [Proxmox PVE](https://docs.xpipe.io/guide/proxmox), [Hyper-V](https://docs.xpipe.io/guide/hyperv), [KVM](https://docs.xpipe.io/guide/kvm), and [VMware Player/Workstation/Fusion](https://docs.xpipe.io/guide/vmware) virtual machines
- [Tailscale](https://docs.xpipe.io/guide/tailscale), [Netbird](https://docs.xpipe.io/guide/netbird), and [Teleport](https://docs.xpipe.io/guide/teleport) connections
- [AWS](https://docs.xpipe.io/guide/aws) and [Hetzner Cloud](https://docs.xpipe.io/guide/hcloud) servers
- [RDP](https://docs.xpipe.io/guide/rdp) and [VNC](https://docs.xpipe.io/guide/vnc) connections
- [RDP](https://docs.xpipe.io/guide/rdp) + gateways and [VNC](https://docs.xpipe.io/guide/vnc) connections
- Windows Subsystem for Linux, Cygwin, and MSYS2 environments
- [Kubernetes](https://docs.xpipe.io/guide/kubernetes) clusters, pods, and containers
- [Powershell Remote Sessions](https://docs.xpipe.io/guide/pssession)
@@ -28,55 +28,55 @@ It currently supports:
<a href="https://docs.xpipe.io/guide/ssh" target="_blank" rel="noopener">
<img src="https://github.com/xpipe-io/.github/raw/main/icons/ssh.svg" width=40 height=40 />
</a>
<a href="#"><img width=10 /></a>
<img src="https://github.com/xpipe-io/.github/raw/main/img/1x1.png" width=10 />
<a href="https://docs.xpipe.io/guide/docker" target="_blank" rel="noopener">
<img src="https://github.com/xpipe-io/.github/raw/main/icons/docker.svg" width=40 height=40 />
</a>
<a href="#"><img width=10 /></a>
<img src="https://github.com/xpipe-io/.github/raw/main/img/1x1.png" width=10 />
<a href="https://docs.xpipe.io/guide/docker#compose" target="_blank" rel="noopener">
<img src="https://github.com/xpipe-io/.github/raw/main/icons/compose.png" width=40 height=40 />
</a>
<a href="#"><img width=10 /></a>
<img src="https://github.com/xpipe-io/.github/raw/main/img/1x1.png" width=10 />
<a href="https://docs.xpipe.io/guide/lxc" target="_blank" rel="noopener">
<img src="https://github.com/xpipe-io/.github/raw/main/icons/lxd.svg" width=40 height=40 />
</a>
<a href="#"><img width=10 /></a>
<img src="https://github.com/xpipe-io/.github/raw/main/img/1x1.png" width=10 />
<a href="https://docs.xpipe.io/guide/podman" target="_blank" rel="noopener">
<img src="https://github.com/xpipe-io/.github/raw/main/icons/podman.svg" width=40 height=40 />
</a>
<a href="#"><img width=10 /></a>
<img src="https://github.com/xpipe-io/.github/raw/main/img/1x1.png" width=10 />
<a href="https://docs.xpipe.io/guide/aws" target="_blank" rel="noopener">
<img src="https://github.com/xpipe-io/.github/raw/main/icons/aws.svg" width=40 height=40 />
</a>
<a href="#"><img width=10 /></a>
<img src="https://github.com/xpipe-io/.github/raw/main/img/1x1.png" width=10 />
<a href="https://docs.xpipe.io/guide/kubernetes" target="_blank" rel="noopener">
<img src="https://github.com/xpipe-io/.github/raw/main/icons/k8s.svg" width=40 height=40 />
</a>
<a href="#"><img width=10 /></a>
<img src="https://github.com/xpipe-io/.github/raw/main/img/1x1.png" width=10 />
<a href="https://docs.xpipe.io/guide/proxmox" target="_blank" rel="noopener">
<img src="https://github.com/xpipe-io/.github/raw/main/icons/proxmox.svg" width=40 height=40 />
</a>
<a href="#"><img width=10 /></a>
<img src="https://github.com/xpipe-io/.github/raw/main/img/1x1.png" width=10 />
<a href="https://docs.xpipe.io/guide/vmware" target="_blank" rel="noopener">
<img src="https://github.com/xpipe-io/.github/raw/main/icons/vmware.svg" width=40 height=40 />
</a>
<a href="#"><img width=10 /></a>
<img src="https://github.com/xpipe-io/.github/raw/main/img/1x1.png" width=10 />
<a href="https://docs.xpipe.io/guide/kvm" target="_blank" rel="noopener">
<img src="https://github.com/xpipe-io/.github/raw/main/icons/virsh.png" width=40 height=40 />
</a>
<a href="#"><img width=10 /></a>
<img src="https://github.com/xpipe-io/.github/raw/main/img/1x1.png" width=10 />
<a href="https://docs.xpipe.io/guide/tailscale" target="_blank" rel="noopener">
<img src="https://github.com/xpipe-io/.github/raw/main/icons/tailscale.svg" width=40 height=40 />
</a>
<a href="#"><img width=10 /></a>
<img src="https://github.com/xpipe-io/.github/raw/main/img/1x1.png" width=10 />
<a href="https://docs.xpipe.io/guide/netbird" target="_blank" rel="noopener">
<img src="https://github.com/xpipe-io/.github/raw/main/icons/netbird.svg" width=40 height=40 />
</a>
<a href="#"><img width=10 /></a>
<img src="https://github.com/xpipe-io/.github/raw/main/img/1x1.png" width=10 />
<a href="https://docs.xpipe.io/guide/hcloud" target="_blank" rel="noopener">
<img src="https://github.com/xpipe-io/.github/raw/main/icons/hetzner.svg" width=40 height=40 />
</a>
<a href="#"><img width=10 /></a>
<img src="https://github.com/xpipe-io/.github/raw/main/img/1x1.png" width=10 />
<a href="https://docs.xpipe.io/guide/teleport" target="_blank" rel="noopener">
<img src="https://github.com/xpipe-io/.github/raw/main/icons/teleport.png" width=40 height=40 />
</a>
+28 -9
View File
@@ -48,28 +48,27 @@ dependencies {
api ('io.modelcontextprotocol.sdk:mcp-core:1.1.3') {
exclude group: "com.ethlo.time", module: "itu"
}
api ('io.modelcontextprotocol.sdk:mcp-json-jackson2:1.1.3') {
api ('io.modelcontextprotocol.sdk:mcp-json-jackson3:1.1.3') {
exclude group: "com.ethlo.time", module: "itu"
exclude group: "com.fasterxml.jackson.dataformat", module: "jackson-dataformat-yaml"
exclude group: "tools.jackson.dataformat", module: "jackson-dataformat-yaml"
}
api "io.projectreactor:reactor-core:3.7.9"
api "org.reactivestreams:reactive-streams:1.0.4"
api ("com.networknt:json-schema-validator:2.0.0") {
api ("com.networknt:json-schema-validator:3.0.6") {
exclude group: "com.ethlo.time", module: "itu"
exclude group: "com.fasterxml.jackson.dataformat", module: "jackson-dataformat-yaml"
exclude group: "tools.jackson.dataformat", module: "jackson-dataformat-yaml"
}
api "com.github.weisj:jsvg:2.1.0"
api 'io.xpipe:vernacular:1.18'
api 'io.xpipe:vernacular:1.19'
api 'org.bouncycastle:bcprov-jdk18on:1.84'
api 'info.picocli:picocli:4.7.7'
api 'org.apache.commons:commons-lang3:3.20.0'
api 'io.sentry:sentry:8.41.0'
api 'commons-io:commons-io:2.22.0'
api "com.fasterxml.jackson.core:jackson-databind:2.22.0"
api "tools.jackson.core:jackson-databind:3.2.0"
api "com.fasterxml.jackson.core:jackson-annotations:2.22"
api "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.22.0"
api "org.kordamp.ikonli:ikonli-material2-pack:12.4.0"
api "org.kordamp.ikonli:ikonli-materialdesign2-pack:12.4.0"
api 'org.kordamp.ikonli:ikonli-bootstrapicons-pack:12.4.0'
@@ -78,8 +77,8 @@ dependencies {
api "org.slf4j:slf4j-jdk-platform-logging:2.0.18"
api 'io.xpipe:modulefs:0.1.9'
api 'net.synedra:validatorfx:0.4.2'
api files("$rootDir/gradle/gradle_scripts/atlantafx-base-2.0.2.jar")
api("com.dlsc.atlantafx:themes:1.9.0") {
api 'io.xpipe:atlantafx-base:2.1.0'
api ("com.dlsc.atlantafx:themes:1.9.0") {
exclude group: "io.github.mkpaz", module: "atlantafx-base"
}
@@ -184,6 +183,26 @@ processResources {
into resourcesDir
}
}
doLast {
def resourcesDir = new File(sourceSets.main.output.resourcesDir, "io/xpipe/app/resources/third-party")
resourcesDir.mkdirs()
def home = System.getProperty("java.home")
def legalDir = file("$home/legal")
def l = new ArrayList<String>()
fileTree(legalDir).matching {
include "**/*.md"
}.visit { f ->
if (f.getFile().isFile()) {
l.add(f.getFile().text)
}
}
def text = String.join("\n\n", l)
file("$resourcesDir/openjdk-deps.license").text = text
}
}
distTar {
+2 -2
View File
@@ -1,6 +1,5 @@
package io.xpipe.app;
import io.xpipe.app.core.AppNames;
import io.xpipe.app.core.AppProperties;
import io.xpipe.app.core.mode.AppOperationMode;
@@ -14,7 +13,8 @@ public class Main {
}
if (args.length == 1 && (args[0].equals("--help") || args[0].equals("help"))) {
System.out.println("For a reference on how to use xpipe from the command-line, take a look at https://docs.xpipe.io/cli");
System.out.println(
"For a reference on how to use xpipe from the command-line, take a look at https://docs.xpipe.io/cli");
return;
}
@@ -3,15 +3,15 @@ package io.xpipe.app.action;
import io.xpipe.app.comp.BaseRegionBuilder;
import io.xpipe.app.comp.SimpleRegionBuilder;
import io.xpipe.app.comp.base.*;
import io.xpipe.app.ext.DataStore;
import io.xpipe.app.hub.action.BatchStoreAction;
import io.xpipe.app.hub.action.MultiStoreAction;
import io.xpipe.app.hub.action.StoreAction;
import io.xpipe.app.hub.comp.StoreChoiceComp;
import io.xpipe.app.hub.comp.StoreListChoiceComp;
import io.xpipe.app.hub.comp.StoreViewState;
import io.xpipe.app.hub.creation.StoreChoiceComp;
import io.xpipe.app.hub.list.StoreListChoiceComp;
import io.xpipe.app.hub.list.StoreViewState;
import io.xpipe.app.platform.OptionsBuilder;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.store.DataStore;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
@@ -4,14 +4,14 @@ import io.xpipe.app.comp.BaseRegionBuilder;
import io.xpipe.app.comp.RegionBuilder;
import io.xpipe.app.comp.SimpleRegionBuilder;
import io.xpipe.app.comp.base.ScrollComp;
import io.xpipe.app.ext.DataStore;
import io.xpipe.app.hub.action.BatchStoreAction;
import io.xpipe.app.hub.action.MultiStoreAction;
import io.xpipe.app.hub.action.StoreAction;
import io.xpipe.app.hub.comp.StoreListChoiceComp;
import io.xpipe.app.hub.comp.StoreViewState;
import io.xpipe.app.hub.list.StoreListChoiceComp;
import io.xpipe.app.hub.list.StoreViewState;
import io.xpipe.app.platform.OptionsBuilder;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.store.DataStore;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
@@ -1,35 +1,34 @@
package io.xpipe.app.action;
import io.xpipe.app.ext.DataStore;
import io.xpipe.app.hub.action.*;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.store.DataStore;
import io.xpipe.app.util.JacksonMapper;
import io.xpipe.app.util.UuidHelper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import tools.jackson.databind.DeserializationFeature;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.node.JsonNodeFactory;
import tools.jackson.databind.node.ObjectNode;
import java.util.ArrayList;
public class ActionJacksonMapper {
@SuppressWarnings("unchecked")
public static <T extends AbstractAction> T parse(JsonNode tree) throws JsonProcessingException {
public static <T extends AbstractAction> T parse(JsonNode tree) {
if (!tree.isObject()) {
return null;
}
var id = tree.get("id");
if (id == null || !id.isTextual()) {
if (id == null || !id.isString()) {
return null;
}
var provider = ActionProvider.ALL.stream()
.filter(actionProvider -> id.textValue().equals(actionProvider.getId()))
.filter(actionProvider -> id.stringValue().equals(actionProvider.getId()))
.findFirst();
if (provider.isEmpty()) {
return null;
@@ -43,10 +42,13 @@ public class ActionJacksonMapper {
var object = (ObjectNode) tree;
var ref = tree.get("ref");
var mapper = JacksonMapper.newMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
var mapper = JacksonMapper.getDefault()
.rebuild()
.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
.build();
if (ref != null && !ref.isArray() && StoreAction.class.isAssignableFrom(clazz.get())) {
validateRef(provider.get(), ref.asText());
validateRef(provider.get(), ref.asString());
var action = mapper.treeToValue(tree, clazz.get());
return (T) action;
}
@@ -60,7 +62,7 @@ public class ActionJacksonMapper {
var batchActions = new ArrayList<StoreAction<DataStore>>();
object.remove("ref");
for (JsonNode batchRef : ref) {
validateRef(provider.get(), batchRef.asText());
validateRef(provider.get(), batchRef.asString());
object.set("ref", batchRef);
var action = mapper.treeToValue(object, clazz.get());
batchActions.add((StoreAction<DataStore>) action);
@@ -70,7 +72,7 @@ public class ActionJacksonMapper {
var makeMulti = ref != null && ref.isArray() && MultiStoreAction.class.isAssignableFrom(clazz.get());
if (makeMulti) {
validateRef(provider.get(), ref.asText());
validateRef(provider.get(), ref.asString());
object.remove("ref");
object.set("refs", ref);
var action = mapper.treeToValue(object, clazz.get());
@@ -19,9 +19,7 @@ public class ActionPickComp extends ModalOverlayContentComp {
protected Region createSimple() {
var prop = new SimpleObjectProperty<>(action);
var top = new ActionConfigComp(prop);
var bottom = new ActionShortcutComp(prop, () -> {
getModalOverlay().close();
});
var bottom = new ActionShortcutComp(prop);
var options = new OptionsBuilder().addComp(top).addComp(bottom);
var scroll = new ScrollComp(options.buildComp());
return scroll.build();
@@ -1,8 +1,8 @@
package io.xpipe.app.action;
import io.xpipe.app.ext.ModuleLayerLoader;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.util.ModuleLayerLoader;
import java.util.*;
@@ -10,6 +10,13 @@ public interface ActionProvider {
List<ActionProvider> ALL = new ArrayList<>();
static ActionProvider byId(String id) {
return ALL.stream()
.filter(p -> p.getId() != null && p.getId().equals(id))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No action provider found for ID " + id));
}
static void initProviders() {
TrackEvent.trace("Starting action provider initialization");
for (ActionProvider actionProvider : ALL) {
@@ -30,11 +30,9 @@ import java.util.List;
public class ActionShortcutComp extends SimpleRegionBuilder {
private final Property<AbstractAction> action;
private final Runnable onCreateMacro;
public ActionShortcutComp(Property<AbstractAction> action, Runnable onCreateMacro) {
public ActionShortcutComp(Property<AbstractAction> action) {
this.action = action;
this.onCreateMacro = onCreateMacro;
}
@Override
@@ -64,8 +62,8 @@ public class ActionShortcutComp extends SimpleRegionBuilder {
});
var copyButton = new ButtonComp(null, new FontIcon("mdi2c-clipboard-multiple-outline"), () -> {
ClipboardHelper.copyUrl(url.getValue());
})
ClipboardHelper.copyUrl(url.getValue());
})
.describe(d -> d.nameKey("copyUrl"));
var field = new TextFieldComp(url);
field.apply(struc -> struc.setEditable(false));
@@ -75,17 +73,19 @@ public class ActionShortcutComp extends SimpleRegionBuilder {
return group;
}
private BaseRegionBuilder<?, ?> createCommandComp() {
var command = new SimpleStringProperty();
action.subscribe((v) -> {
var s = ActionUrls.toUrl(v);
ThreadHelper.runFailableAsync(() -> {
var exec = AppProperties.get().isStaging() ? "xpipe-ptb" : "xpipe";
var exec = AppProperties.get().isStaging() ? "xpipe-ptb" : "xpipe";
var inPath = LocalShell.getShell().view().findProgram(exec).isPresent();
var defaultDataDir = AppProperties.get().getDefaultDataDir().equals(AppProperties.get().getDataDir());
var defaultDataDir = AppProperties.get()
.getDefaultDataDir()
.equals(AppProperties.get().getDataDir());
var c = (inPath ? exec : "\"" + AppInstallation.ofCurrent().getCliExecutablePath() + "\"") + " open \""
+ s + "\"" + (!defaultDataDir ? " -d \"" + AppProperties.get().getDataDir() + "\"" : "");
+ s + "\""
+ (!defaultDataDir ? " -d \"" + AppProperties.get().getDataDir() + "\"" : "");
Platform.runLater(() -> {
command.set(c);
});
@@ -1,12 +1,12 @@
package io.xpipe.app.action;
import io.xpipe.app.util.Base64Helper;
import io.xpipe.app.util.JacksonMapper;
import io.xpipe.app.util.SecretValue;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.SneakyThrows;
import tools.jackson.core.type.TypeReference;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.node.ObjectNode;
import java.net.URLDecoder;
import java.net.URLEncoder;
@@ -21,8 +21,8 @@ public class ActionUrls {
}
private static List<String> nodeToString(JsonNode node) {
if (node.isTextual()) {
return List.of(encodeValue(node.asText()));
if (node.isString()) {
return List.of(encodeValue(node.asString()));
}
if (node.isArray()) {
@@ -36,7 +36,7 @@ public class ActionUrls {
return list;
}
var enc = SecretValue.toBase64e(node.toPrettyString().getBytes(StandardCharsets.UTF_8));
var enc = Base64Helper.toBase64Url(node.toPrettyString());
return List.of("~" + enc);
}
@@ -65,7 +65,7 @@ public class ActionUrls {
return encodedURL;
}
public static Optional<AbstractAction> parse(String queryString) throws Exception {
public static Optional<AbstractAction> parse(String queryString) {
var query = splitQuery(queryString);
var id = query.get("id");
@@ -99,7 +99,7 @@ public class ActionUrls {
var list = new ArrayList<>();
for (String s : entry.getValue()) {
if (s.startsWith("~")) {
var json = SecretValue.fromBase64e(s.substring(1));
var json = Base64Helper.fromBase64UrlString(s.substring(1));
var node = JacksonMapper.getDefault().readTree(json);
list.add(node);
} else {
@@ -1,8 +1,8 @@
package io.xpipe.app.action;
import io.xpipe.app.ext.DataStore;
import io.xpipe.app.hub.action.impl.OpenHubMenuLeafProvider;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.store.DataStore;
import java.util.List;
import java.util.Objects;
@@ -5,9 +5,9 @@ import io.xpipe.app.util.DataStoreFormatter;
import io.xpipe.app.util.JacksonMapper;
import io.xpipe.app.util.UuidHelper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.experimental.SuperBuilder;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.node.ObjectNode;
import java.util.*;
import java.util.stream.Collectors;
@@ -58,8 +58,8 @@ public abstract class SerializableAction extends AbstractAction {
.filter(s -> !s.equals("Store"))
.collect(Collectors.joining(" "));
if (property.getValue().isTextual()) {
var value = property.getValue().textValue();
if (property.getValue().isString()) {
var value = property.getValue().stringValue();
var uuid = UuidHelper.parse(value);
if (uuid.isPresent()) {
var refName = DataStorage.get()
@@ -77,7 +77,7 @@ public abstract class SerializableAction extends AbstractAction {
} else if (property.getValue().isArray()) {
var list = new ArrayList<String>();
for (JsonNode jsonNode : property.getValue()) {
var s = jsonNode.asText();
var s = jsonNode.asString();
if (!s.isEmpty()) {
list.add(s);
}
@@ -89,7 +89,7 @@ public abstract class SerializableAction extends AbstractAction {
} else if (property.getValue().isBoolean()) {
map.put(name, property.getValue().booleanValue() ? "Yes" : "No");
} else {
var value = property.getValue().asText();
var value = property.getValue().asString();
map.put(name, value);
}
}
@@ -1,6 +1,12 @@
package io.xpipe.app.action;
import io.xpipe.app.core.AppRestart;
import io.xpipe.app.core.window.AppDialog;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.Base64Helper;
import java.net.URI;
import java.nio.charset.StandardCharsets;
public class XPipeUrlProvider implements LauncherUrlProvider {
@@ -12,12 +18,32 @@ public class XPipeUrlProvider implements LauncherUrlProvider {
@Override
public AbstractAction createAction(URI uri) throws Exception {
var a = uri.getHost();
if (!"action".equals(a)) {
if ("webtop".equals(a)) {
AppDialog.information("webtopUrlDialog");
return null;
}
var query = uri.getQuery();
var action = ActionUrls.parse(query);
return action.orElse(null);
if ("action".equals(a)) {
var query = uri.getQuery();
var action = ActionUrls.parse(query);
return action.orElse(null);
}
if ("sync".equals(a)) {
var repo = new String(Base64Helper.fromBase64UrlString(uri.getPath()), StandardCharsets.UTF_8);
var alreadySynced = AppPrefs.get().storageGitRemote().getValue() != null;
if (alreadySynced && !repo.equals(AppPrefs.get().storageGitRemote().getValue())) {
AppDialog.information("syncUrlAlreadySynced");
return null;
}
AppPrefs.get().setFromExternal(AppPrefs.get().storageGitRemote(), repo);
AppPrefs.get().save();
AppRestart.restart();
return null;
}
return null;
}
}
@@ -1,7 +1,7 @@
package io.xpipe.app.beacon;
import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.store.ShellStore;
import lombok.Value;
@@ -1,6 +1,5 @@
package io.xpipe.app.beacon;
import com.sun.net.httpserver.HttpHandler;
import io.xpipe.app.beacon.mcp.AppMcpServer;
import io.xpipe.app.core.AppLocalTemp;
import io.xpipe.app.core.AppProperties;
@@ -11,6 +10,7 @@ import io.xpipe.app.util.DocumentationLink;
import io.xpipe.app.util.OsType;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import lombok.Getter;
@@ -47,6 +47,7 @@ public class AppBeaconServer {
@Getter
private String localAuthSecret;
private FileChannel localLockFileChannel;
private FileLock localLockFileLock;
@@ -57,7 +58,8 @@ public class AppBeaconServer {
public static void init() {
try {
// We already queried the beacon port at this point, so this will always work
INSTANCE = new AppBeaconServer(AppProperties.get().queryEffectiveBeaconPort(false).orElseThrow());
INSTANCE = new AppBeaconServer(
AppProperties.get().queryEffectiveBeaconPort(false).orElseThrow());
INSTANCE.initAuthSecret();
INSTANCE.start();
TrackEvent.withInfo("Started http server")
@@ -138,11 +140,12 @@ public class AppBeaconServer {
if (localLockFileChannel != null) {
localLockFileChannel.close();
}
} catch (IOException ignored) {}
} catch (IOException ignored) {
}
}
private void start() throws IOException {
executor = Executors.newFixedThreadPool(5, r -> {
executor = Executors.newFixedThreadPool(3, r -> {
Thread t = Executors.defaultThreadFactory().newThread(r);
t.setDaemon(true);
t.setName("http handler");
@@ -191,8 +194,7 @@ public class AppBeaconServer {
private boolean handleCorsHeaders(HttpExchange exchange) throws IOException {
if (AppPrefs.get().enableHttpApi().get()) {
exchange.getResponseHeaders()
.add("Origin", "http://localhost:" + getPort());
exchange.getResponseHeaders().add("Origin", "http://localhost:" + getPort());
exchange.getResponseHeaders().add("Vary", "Origin");
exchange.getResponseHeaders().add("Access-Control-Allow-Origin", "*");
exchange.getResponseHeaders().add("Access-Control-Allow-Credentials", "true");
@@ -3,10 +3,9 @@ package io.xpipe.app.beacon;
import io.xpipe.app.beacon.api.HandshakeExchange;
import io.xpipe.app.util.JacksonMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.SneakyThrows;
import tools.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
@@ -33,7 +32,8 @@ public class BeaconClient {
this.port = port;
}
public static BeaconClient establishConnection(int port, BeaconClientInformation information, Path authFile) throws Exception {
public static BeaconClient establishConnection(int port, BeaconClientInformation information, Path authFile)
throws Exception {
var client = new BeaconClient(port);
var auth = Files.readString(authFile);
HandshakeExchange.Response response = client.performRequest(HandshakeExchange.Request.builder()
@@ -96,7 +96,7 @@ public class BeaconClient {
}
var v = (RES) reader.readValue(body);
return v;
} catch (IOException ex) {
} catch (Exception ex) {
throw new BeaconConnectorException("Couldn't parse response", ex);
}
}
@@ -136,7 +136,7 @@ public class BeaconClient {
try {
var v = JacksonMapper.getDefault().readValue(response.body(), BeaconClientErrorResponse.class);
return Optional.of(v);
} catch (IOException ex) {
} catch (Exception ex) {
throw new BeaconConnectorException("Couldn't parse client error message", ex);
}
}
@@ -150,7 +150,7 @@ public class BeaconClient {
try {
var v = JacksonMapper.getDefault().readValue(response.body(), BeaconServerErrorResponse.class);
return Optional.of(v);
} catch (IOException ex) {
} catch (Exception ex) {
throw new BeaconConnectorException("Couldn't parse client error message", ex);
}
}
@@ -1,6 +1,6 @@
package io.xpipe.app.beacon;
import io.xpipe.app.util.ModuleLayerLoader;
import io.xpipe.app.ext.ModuleLayerLoader;
import com.sun.net.httpserver.HttpExchange;
import lombok.SneakyThrows;
@@ -4,8 +4,8 @@ import io.xpipe.app.core.mode.AppOperationMode;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.app.util.JacksonMapper;
import io.xpipe.app.util.ThreadHelper;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
@@ -1,16 +1,17 @@
package io.xpipe.app.beacon.api;
import com.fasterxml.jackson.databind.JsonNode;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.action.ActionJacksonMapper;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.core.AppCache;
import io.xpipe.app.core.window.AppDialog;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
import tools.jackson.databind.JsonNode;
public class ActionExchange extends BeaconInterface<ActionExchange.Request> {
@@ -1,6 +1,5 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.core.AppCache;
@@ -8,15 +7,17 @@ import io.xpipe.app.core.AppLayoutModel;
import io.xpipe.app.core.window.AppDialog;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.platform.LabelGraphic;
import io.xpipe.app.secret.InPlaceSecretValue;
import io.xpipe.app.secret.SecretManager;
import io.xpipe.app.secret.SecretQueryState;
import io.xpipe.app.terminal.TerminalView;
import io.xpipe.app.util.AskpassAlert;
import io.xpipe.app.util.GlobalTimer;
import io.xpipe.app.util.InPlaceSecretValue;
import io.xpipe.app.util.SecretValue;
import javafx.beans.property.SimpleStringProperty;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -1,11 +1,11 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreCategory;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -1,17 +1,17 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.JacksonMapper;
import io.xpipe.app.util.StorePath;
import com.fasterxml.jackson.databind.JsonNode;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
import tools.jackson.databind.JsonNode;
import java.time.Instant;
import java.util.ArrayList;
@@ -1,10 +1,10 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStorageQuery;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -1,11 +1,11 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreCategory;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -1,10 +1,10 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.core.mode.AppOperationMode;
import io.xpipe.app.core.window.AppMainWindow;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
@@ -1,11 +1,11 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.core.mode.AppOperationMode;
import io.xpipe.app.util.XPipeDaemonMode;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -1,16 +1,15 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.beacon.BeaconServerException;
import io.xpipe.app.core.AppOpenArguments;
import io.xpipe.app.core.AppProperties;
import io.xpipe.app.core.mode.AppOperationMode;
import io.xpipe.app.platform.PlatformInit;
import io.xpipe.app.util.FilePath;
import io.xpipe.app.util.OsType;
import io.xpipe.app.util.ThreadHelper;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -1,9 +1,9 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.core.mode.AppOperationMode;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
@@ -1,10 +1,10 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.core.mode.AppOperationMode;
import io.xpipe.app.util.ThreadHelper;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
@@ -1,11 +1,11 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.core.AppProperties;
import io.xpipe.app.core.AppVersion;
import io.xpipe.app.util.LicenseProvider;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -1,9 +1,9 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.beacon.BlobManager;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.SneakyThrows;
@@ -1,14 +1,14 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.AppBeaconServer;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.beacon.BlobManager;
import io.xpipe.app.ext.ShellFileSystem;
import io.xpipe.app.fs.ShellFileSystem;
import io.xpipe.app.util.FilePath;
import io.xpipe.app.util.FixedSizeInputStream;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.SneakyThrows;
@@ -1,12 +1,12 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.AppBeaconServer;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.beacon.BlobManager;
import io.xpipe.app.process.ScriptHelper;
import io.xpipe.app.util.FilePath;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.SneakyThrows;
@@ -1,12 +1,12 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.AppBeaconServer;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.beacon.BlobManager;
import io.xpipe.app.ext.ShellFileSystem;
import io.xpipe.app.fs.ShellFileSystem;
import io.xpipe.app.util.FilePath;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.SneakyThrows;
@@ -28,7 +28,7 @@ public class FsWriteExchange extends BeaconInterface<FsWriteExchange.Request> {
var shell = AppBeaconServer.get().getCache().getShellSession(msg.getStore());
var fs = new ShellFileSystem(shell.getControl());
try (var in = BlobManager.get().getBlob(msg.getBlob());
var os = fs.openOutput(msg.getPath(), BlobManager.get().getSize(msg.getBlob()))) {
var os = fs.openOutput(msg.getPath(), BlobManager.get().getSize(msg.getBlob()))) {
in.transferTo(os);
}
return Response.builder().build();
@@ -1,11 +1,10 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.*;
import io.xpipe.app.beacon.BeaconAuthMethod;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.prefs.AppPrefs;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -1,17 +1,15 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import com.fasterxml.jackson.databind.JsonNode;
import io.xpipe.app.storage.DataStorageSecret;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
import java.io.IOException;
import tools.jackson.databind.JsonNode;
public class SecretDecryptExchange extends BeaconInterface<SecretDecryptExchange.Request> {
@@ -21,13 +19,18 @@ public class SecretDecryptExchange extends BeaconInterface<SecretDecryptExchange
}
@Override
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException {
public Object handle(HttpExchange exchange, Request msg) throws BeaconClientException {
var secret = DataStorageSecret.deserialize(msg.getEncrypted());
if (secret == null) {
throw new BeaconClientException("Unable to parse secret");
}
if (secret.getInternalSecret() == null) {
throw new BeaconClientException("Unable to decrypt secret");
}
return Response.builder().decrypted(new String(secret.getSecret())).build();
return Response.builder()
.decrypted(new String(secret.getInternalSecret().getSecret()))
.build();
}
@Jacksonized
@@ -1,15 +1,21 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import com.fasterxml.jackson.databind.JsonNode;
import io.xpipe.app.secret.DataStorageAccessHandler;
import io.xpipe.app.secret.EncryptionPrincipal;
import io.xpipe.app.secret.InPlaceSecretValue;
import io.xpipe.app.storage.DataStorageSecret;
import io.xpipe.app.util.InPlaceSecretValue;
import io.xpipe.app.util.UuidHelper;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
import tools.jackson.databind.JsonNode;
import java.util.Set;
public class SecretEncryptExchange extends BeaconInterface<SecretEncryptExchange.Request> {
@@ -19,9 +25,37 @@ public class SecretEncryptExchange extends BeaconInterface<SecretEncryptExchange
}
@Override
public Object handle(HttpExchange exchange, Request msg) {
var secret = DataStorageSecret.ofCurrentSecret(InPlaceSecretValue.of(msg.getValue()));
return Response.builder().encrypted(secret.serialize(true)).build();
public Object handle(HttpExchange exchange, Request msg) throws BeaconClientException {
EncryptionPrincipal p;
if (msg.getPrincipal() != null) {
var byName = DataStorageAccessHandler.getInstance().getAllEncryptionPrincipals().stream()
.filter(encryptionPrincipal -> encryptionPrincipal.getName().equals(msg.getPrincipal()))
.findFirst();
if (byName.isPresent()) {
p = byName.get();
} else {
var uuid = UuidHelper.parse(msg.getPrincipal());
if (uuid.isPresent()) {
var principal = DataStorageAccessHandler.getInstance().getEncryptionPrincipal(uuid.get());
p = principal.orElse(null);
} else {
p = null;
}
}
if (p == null) {
throw new BeaconClientException("Unknown principal " + msg.getPrincipal());
}
if (!p.isAccessible()) {
throw new BeaconClientException("Principal " + p.getName() + " is not accessible");
}
} else {
p = DataStorageAccessHandler.getInstance().getEncryptAllPrincipal();
}
var secret = DataStorageSecret.of(InPlaceSecretValue.of(msg.getValue()), Set.of(p));
return Response.builder().encrypted(secret.serialize()).build();
}
@Jacksonized
@@ -30,6 +64,8 @@ public class SecretEncryptExchange extends BeaconInterface<SecretEncryptExchange
public static class Request {
@NonNull
String value;
String principal;
}
@Jacksonized
@@ -1,9 +1,9 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.AppBeaconServer;
import io.xpipe.app.beacon.BeaconInterface;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.SneakyThrows;
@@ -1,16 +1,16 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.AppBeaconServer;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.beacon.BeaconShellSession;
import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.store.ShellStore;
import io.xpipe.app.util.FilePath;
import io.xpipe.app.util.JacksonMapper;
import io.xpipe.app.util.OsType;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.SneakyThrows;
@@ -55,7 +55,7 @@ public class ShellStartExchange extends BeaconInterface<ShellStartExchange.Reque
AppBeaconServer.get().getCache().getShellSessions().add(new BeaconShellSession(e, control));
}
var ttyState =
JacksonMapper.getDefault().valueToTree(control.getTtyState()).asText();
JacksonMapper.getDefault().valueToTree(control.getTtyState()).asString();
return Response.builder()
.shellDialect(control.getShellDialect().getId())
.osType(control.getOsType())
@@ -1,9 +1,9 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.AppBeaconServer;
import io.xpipe.app.beacon.BeaconInterface;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.SneakyThrows;
@@ -1,11 +1,11 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.ext.ProcessControlProvider;
import io.xpipe.app.ext.ProcModuleProvider;
import io.xpipe.app.process.ShellDialects;
import io.xpipe.app.terminal.TerminalLauncherManager;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -38,7 +38,7 @@ public class SshLaunchExchange extends BeaconInterface<SshLaunchExchange.Request
// There are sometimes multiple requests by a terminal client (e.g. Termius)
// This might fail sometimes, but it is expected
var r = TerminalLauncherManager.sshLaunchExchange();
var c = ProcessControlProvider.get()
var c = ProcModuleProvider.get()
.getEffectiveLocalDialect()
.getOpenScriptCommand(r.toString())
.buildBaseParts(null);
@@ -1,20 +1,20 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import com.fasterxml.jackson.databind.JsonNode;
import io.xpipe.app.ext.DataStore;
import io.xpipe.app.ext.ValidationException;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.store.DataStore;
import io.xpipe.app.util.JacksonMapper;
import io.xpipe.app.util.ValidationException;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
import tools.jackson.databind.JsonNode;
import java.util.List;
import java.util.UUID;
@@ -1,11 +1,11 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.StorePath;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -1,10 +1,10 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStorageQuery;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -27,8 +27,7 @@ public class StoreQueryExchange extends BeaconInterface<StoreQueryExchange.Reque
@Override
public Object handle(HttpExchange exchange, Request msg) {
var found =
DataStorageQuery.queryEntry(msg.getCategoryFilter(), msg.getStoreFilter(), msg.getTypeFilter());
var found = DataStorageQuery.queryEntry(msg.getCategoryFilter(), msg.getStoreFilter(), msg.getTypeFilter());
return Response.builder()
.found(found.stream().map(entry -> entry.getUuid()).toList())
.build();
@@ -1,11 +1,11 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.ext.FixedHierarchyStore;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.store.FixedHierarchyStore;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -1,11 +1,11 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -1,17 +1,17 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.beacon.BeaconServerException;
import io.xpipe.app.core.AppCache;
import io.xpipe.app.core.window.AppDialog;
import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStorageQuery;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.store.ShellStore;
import io.xpipe.app.terminal.TerminalLauncherManager;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -1,11 +1,11 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.beacon.BeaconServerException;
import io.xpipe.app.terminal.TerminalLauncherManager;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -1,9 +1,9 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.prefs.AppPrefs;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -1,11 +1,11 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.terminal.TerminalLauncherManager;
import io.xpipe.app.terminal.TerminalView;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -1,10 +1,10 @@
package io.xpipe.app.beacon.api;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.beacon.BeaconServerException;
import io.xpipe.app.terminal.TerminalLauncherManager;
import com.sun.net.httpserver.HttpExchange;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
@@ -6,17 +6,17 @@ import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.ThreadHelper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import io.modelcontextprotocol.common.McpTransportContext;
import io.modelcontextprotocol.json.jackson2.JacksonMcpJsonMapper;
import io.modelcontextprotocol.json.jackson3.JacksonMcpJsonMapper;
import io.modelcontextprotocol.server.McpServerFeatures;
import io.modelcontextprotocol.server.McpSyncServer;
import io.modelcontextprotocol.spec.HttpHeaders;
import io.modelcontextprotocol.spec.McpSchema;
import lombok.SneakyThrows;
import lombok.Value;
import tools.jackson.databind.json.JsonMapper;
import java.io.IOException;
import java.util.ArrayList;
@@ -38,7 +38,7 @@ public class AppMcpServer {
@SneakyThrows
public static void init() {
var transportProvider = new HttpStreamableServerTransportProvider(
new JacksonMcpJsonMapper(new ObjectMapper()),
new JacksonMcpJsonMapper(JsonMapper.builder().build()),
"/mcp",
false,
(serverRequest) -> McpTransportContext.EMPTY,
@@ -1,12 +1,12 @@
package io.xpipe.app.beacon.mcp;
import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.process.ShellControl;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStorageQuery;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.store.ShellStore;
import io.xpipe.app.util.FilePath;
import io.modelcontextprotocol.server.McpSyncServerExchange;
@@ -1,31 +1,35 @@
package io.xpipe.app.beacon.mcp;
import io.xpipe.app.beacon.AppBeaconServer;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.core.AppExtensionManager;
import io.xpipe.app.core.AppNames;
import io.xpipe.app.ext.*;
import io.xpipe.app.hub.comp.StoreViewState;
import io.xpipe.app.fs.FileEntry;
import io.xpipe.app.fs.FileInfo;
import io.xpipe.app.fs.ShellFileSystem;
import io.xpipe.app.hub.list.StoreViewState;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.process.ScriptHelper;
import io.xpipe.app.process.ShellControl;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStorageQuery;
import io.xpipe.app.store.SingletonSessionStore;
import io.xpipe.app.terminal.TerminalLaunch;
import io.xpipe.app.util.CommandDialog;
import io.xpipe.app.util.HttpHelper;
import io.xpipe.app.beacon.BeaconClientException;
import io.xpipe.app.beacon.BeaconInterface;
import io.xpipe.app.util.FilePath;
import io.xpipe.app.util.HttpHelper;
import io.xpipe.app.util.JacksonMapper;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import io.modelcontextprotocol.json.jackson2.JacksonMcpJsonMapper;
import io.modelcontextprotocol.json.jackson3.JacksonMcpJsonMapper;
import io.modelcontextprotocol.server.McpServerFeatures;
import io.modelcontextprotocol.spec.McpSchema;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
import tools.jackson.databind.node.JsonNodeFactory;
import java.io.IOException;
import java.net.URI;
@@ -141,18 +145,13 @@ public final class McpTools {
continue;
}
var section = StoreViewState.get()
.getSectionForWrapper(StoreViewState.get().getEntryWrapper(e));
var info = section.isPresent()
? e.getProvider()
.informationString(section.get())
.getValue()
: null;
var wrapper = StoreViewState.get().getEntryWrapper(e);
var info = wrapper.getShownInformation().getValue();
var r = ConnectionResource.builder()
.name(e.getName())
.path(DataStorage.get().getStorePath(e).toString())
.information(info)
.information(info != null ? info.toJoinedString() : null)
.notes(e.getNotes())
.build();
list.add(r);
@@ -282,19 +281,20 @@ public final class McpTools {
throw new BeaconClientException("Path " + path + " does not exist");
}
var e = entry.get();
var map = new LinkedHashMap<String, Object>();
map.put("path", entry.get().getPath().toString());
map.put("size", entry.get().getSize());
if (entry.get().getInfo() instanceof FileInfo.Unix u) {
map.put("path", e.getPath().toString());
map.put("size", e.getSize());
if (e.getInfo() instanceof FileInfo.Unix u) {
map.put("permissions", u.getPermissions());
map.put("user", u.getUser());
map.put("group", u.getGroup());
} else if (entry.get().getInfo() instanceof FileInfo.Windows w) {
} else if (e.getInfo() instanceof FileInfo.Windows w) {
map.put("attributes", w.getAttributes());
}
map.put("type", entry.get().getKind().toString().toLowerCase());
map.put("date", entry.get().getDate().toString());
map.entrySet().removeIf(e -> e.getValue() == null);
map.put("type", e.getKind().toString().toLowerCase());
map.put("date", e.getDate() != null ? e.getDate().toString() : null);
map.entrySet().removeIf(me -> me.getValue() == null);
return McpSchema.CallToolResult.builder()
.structuredContent(map)
@@ -393,7 +393,7 @@ public final class McpTools {
var shellStore = req.getShellStoreRef(system, true);
var shellSession = AppBeaconServer.get().getCache().getOrStart(shellStore);
var r = ProcessControlProvider.get().executeMcpCommand(shellSession.getControl(), command);
var r = ProcModuleProvider.get().executeMcpCommand(shellSession.getControl(), command);
return r;
}))
.build();
@@ -8,21 +8,21 @@ import io.xpipe.app.comp.RegionBuilder;
import io.xpipe.app.comp.base.*;
import io.xpipe.app.core.AppFontSizes;
import io.xpipe.app.core.AppLayoutModel;
import io.xpipe.app.ext.FileSystemStore;
import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.hub.comp.StoreEntryWrapper;
import io.xpipe.app.hub.comp.StoreFilter;
import io.xpipe.app.hub.comp.StoreViewState;
import io.xpipe.app.hub.entry.StoreEntryWrapper;
import io.xpipe.app.hub.list.StoreFilter;
import io.xpipe.app.hub.list.StoreViewState;
import io.xpipe.app.platform.BindingsHelper;
import io.xpipe.app.platform.InputHelper;
import io.xpipe.app.platform.PlatformThread;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.store.FileSystemStore;
import io.xpipe.app.store.ShellStore;
import io.xpipe.app.util.FilePath;
import io.xpipe.app.util.FileReference;
import io.xpipe.app.util.ObservableSubscriber;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.app.util.FilePath;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
@@ -83,7 +83,11 @@ public class BrowserFileChooserSessionComp extends ModalOverlayContentComp {
});
selectionField.style("chooser-selection");
selectionField.hgrow();
var modal = ModalOverlay.of(save ? (directory ? "saveDirectoryTitle" : "saveFileTitle") : (directory ? "openDirectoryTitle" : "openFileTitle"), comp);
var modal = ModalOverlay.of(
save
? (directory ? "saveDirectoryTitle" : "saveFileTitle")
: (directory ? "openDirectoryTitle" : "openFileTitle"),
comp);
modal.setRequireCloseButtonForClose(true);
modal.addButtonBarComp(selectionField);
modal.addButton(new ModalButton("select", () -> model.finishChooser(), true, true));
@@ -2,15 +2,15 @@ package io.xpipe.app.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.file.BrowserFileSystemTabModel;
import io.xpipe.app.ext.FileKind;
import io.xpipe.app.ext.FileSystem;
import io.xpipe.app.ext.FileSystemStore;
import io.xpipe.app.fs.FileKind;
import io.xpipe.app.fs.FileSystem;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.store.FileSystemStore;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.FileReference;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.app.util.FailableFunction;
import io.xpipe.app.util.FilePath;
import io.xpipe.app.util.FileReference;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
@@ -43,9 +43,11 @@ public class BrowserFileChooserSessionModel extends BrowserAbstractSessionModel<
}
newModel.getFileList().getSelection().addListener((ListChangeListener<? super BrowserEntry>) c -> {
var updated = newModel.getFileList().getSelection().filtered(browserEntry ->
(directory && browserEntry.getRawFileEntry().getKind() == FileKind.DIRECTORY) ||
(!directory && browserEntry.getRawFileEntry().getKind() != FileKind.DIRECTORY));
var updated = newModel.getFileList()
.getSelection()
.filtered(browserEntry -> (directory
&& browserEntry.getRawFileEntry().getKind() == FileKind.DIRECTORY)
|| (!directory && browserEntry.getRawFileEntry().getKind() != FileKind.DIRECTORY));
if (!updated.isEmpty()) {
fileSelection.setAll(updated);
}
@@ -8,14 +8,13 @@ import io.xpipe.app.comp.SimpleRegionBuilder;
import io.xpipe.app.comp.base.*;
import io.xpipe.app.core.AppFontSizes;
import io.xpipe.app.core.AppLayoutModel;
import io.xpipe.app.core.AppSizeBreakpoints;
import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.hub.comp.StoreEntryWrapper;
import io.xpipe.app.hub.comp.StoreFilter;
import io.xpipe.app.hub.comp.StoreViewState;
import io.xpipe.app.hub.entry.StoreEntryWrapper;
import io.xpipe.app.hub.list.StoreFilter;
import io.xpipe.app.hub.list.StoreViewState;
import io.xpipe.app.platform.BindingsHelper;
import io.xpipe.app.platform.InputHelper;
import io.xpipe.app.platform.PlatformThread;
import io.xpipe.app.store.ShellStore;
import io.xpipe.app.util.ObservableSubscriber;
import io.xpipe.app.util.ThreadHelper;
@@ -5,16 +5,16 @@ import io.xpipe.app.browser.file.BrowserHistorySavedState;
import io.xpipe.app.browser.file.BrowserHistoryTabModel;
import io.xpipe.app.browser.file.BrowserTransferModel;
import io.xpipe.app.core.AppLayoutModel;
import io.xpipe.app.ext.FileSystem;
import io.xpipe.app.ext.FileSystemStore;
import io.xpipe.app.fs.FileSystem;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.store.FileSystemStore;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.app.util.FailableFunction;
import io.xpipe.app.util.FilePath;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
@@ -9,7 +9,6 @@ import io.xpipe.app.comp.base.StackComp;
import io.xpipe.app.core.AppFontSizes;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.core.AppLayoutModel;
import io.xpipe.app.core.AppSizeBreakpoints;
import io.xpipe.app.platform.LabelGraphic;
import io.xpipe.app.platform.MenuHelper;
import io.xpipe.app.platform.PlatformThread;
@@ -270,7 +269,9 @@ public class BrowserSessionTabsComp extends SimpleRegionBuilder {
.paddingProperty()
.bind(Bindings.createObjectBinding(
() -> {
if (!AppLayoutModel.get().getPortraitLayoutCollapsed().get()) {
if (!AppLayoutModel.get()
.getPortraitLayoutCollapsed()
.get()) {
return new Insets(2, 0, 4, -leftPadding.get() + 3);
} else {
return new Insets(2, 0, 4, -leftPadding.get() - 4);
@@ -281,7 +282,9 @@ public class BrowserSessionTabsComp extends SimpleRegionBuilder {
tabs.paddingProperty()
.bind(Bindings.createObjectBinding(
() -> {
if (!AppLayoutModel.get().getPortraitLayoutCollapsed().get()) {
if (!AppLayoutModel.get()
.getPortraitLayoutCollapsed()
.get()) {
return new Insets(0, 0, 0, -5);
} else {
return new Insets(0, 0, 0, 5);
@@ -510,8 +513,6 @@ public class BrowserSessionTabsComp extends SimpleRegionBuilder {
var color = tabModel.getColor();
if (color != null) {
c.getStyleClass().add(color.getId());
} else {
c.getStyleClass().add("gray");
}
c.addEventHandler(DragEvent.DRAG_ENTERED, de -> {
// Prevent switch when dragging local files into app
@@ -1,10 +1,10 @@
package io.xpipe.app.browser;
import io.xpipe.app.comp.BaseRegionBuilder;
import io.xpipe.app.ext.DataStore;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreColor;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.store.DataStore;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
@@ -5,9 +5,9 @@ import io.xpipe.app.browser.BrowserStoreSessionTab;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.file.BrowserFileSystemTabModel;
import io.xpipe.app.core.AppLayoutModel;
import io.xpipe.app.ext.FileSystemStore;
import io.xpipe.app.hub.action.StoreAction;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.store.FileSystemStore;
import io.xpipe.app.util.FilePath;
import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -4,7 +4,7 @@ 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.app.ext.FileKind;
import io.xpipe.app.fs.FileKind;
import lombok.experimental.SuperBuilder;
import lombok.extern.jackson.Jacksonized;
@@ -3,7 +3,7 @@ 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.ext.FileKind;
import io.xpipe.app.fs.FileKind;
import lombok.NonNull;
import lombok.experimental.SuperBuilder;
@@ -3,7 +3,7 @@ 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.ext.FileKind;
import io.xpipe.app.fs.FileKind;
import lombok.NonNull;
import lombok.experimental.SuperBuilder;
@@ -3,7 +3,7 @@ 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.ext.FileKind;
import io.xpipe.app.fs.FileKind;
import io.xpipe.app.util.FilePath;
import lombok.NonNull;
@@ -4,7 +4,7 @@ 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.app.ext.FileKind;
import io.xpipe.app.fs.FileKind;
import lombok.experimental.SuperBuilder;
import lombok.extern.jackson.Jacksonized;
@@ -5,7 +5,7 @@ import io.xpipe.app.browser.action.BrowserActionProvider;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.file.BrowserFileOpener;
import io.xpipe.app.browser.file.BrowserFileSystemTabModel;
import io.xpipe.app.ext.FileKind;
import io.xpipe.app.fs.FileKind;
import lombok.experimental.SuperBuilder;
import lombok.extern.jackson.Jacksonized;
@@ -4,7 +4,7 @@ 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.app.ext.FileKind;
import io.xpipe.app.fs.FileKind;
import io.xpipe.app.process.CommandBuilder;
import io.xpipe.app.process.LocalShell;
import io.xpipe.app.process.ShellControl;
@@ -5,7 +5,7 @@ import io.xpipe.app.browser.action.BrowserActionProvider;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.file.BrowserFileOpener;
import io.xpipe.app.browser.file.BrowserFileSystemTabModel;
import io.xpipe.app.ext.FileKind;
import io.xpipe.app.fs.FileKind;
import io.xpipe.app.util.OsType;
import lombok.experimental.SuperBuilder;
@@ -6,12 +6,12 @@ import io.xpipe.app.action.StoreContextAction;
import io.xpipe.app.browser.file.BrowserFileTransferOperation;
import io.xpipe.app.core.AppCache;
import io.xpipe.app.core.window.AppDialog;
import io.xpipe.app.ext.FileSystemStore;
import io.xpipe.app.process.ParentSystemAccess;
import io.xpipe.app.process.ShellControl;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.store.FileSystemStore;
import lombok.NonNull;
import lombok.experimental.SuperBuilder;
@@ -3,9 +3,9 @@ package io.xpipe.app.browser.file;
import io.xpipe.app.comp.SimpleRegionBuilder;
import io.xpipe.app.platform.PlatformThread;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.FilePath;
import io.xpipe.app.util.GlobalTimer;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.app.util.FilePath;
import javafx.application.Platform;
import javafx.css.PseudoClass;
@@ -1,7 +1,7 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.ext.FileEntry;
import io.xpipe.app.ext.ProcessControlProvider;
import io.xpipe.app.ext.ProcModuleProvider;
import io.xpipe.app.fs.FileEntry;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.platform.GlobalClipboard;
import io.xpipe.app.platform.GlobalObjectProperty;
@@ -120,7 +120,7 @@ public class BrowserClipboard {
public String toClipboardString() {
return entries.stream()
.map(fileEntry -> "\"" + fileEntry.getRawFileEntry().getPath() + "\"")
.collect(Collectors.joining(ProcessControlProvider.get()
.collect(Collectors.joining(ProcModuleProvider.get()
.getEffectiveLocalDialect()
.getNewLine()
.getNewLineString()));
@@ -1,7 +1,13 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.comp.*;
import io.xpipe.app.hub.comp.*;
import io.xpipe.app.hub.category.StoreCategoryWrapper;
import io.xpipe.app.hub.entry.StoreEntryWrapper;
import io.xpipe.app.hub.list.StoreFilter;
import io.xpipe.app.hub.section.StoreSection;
import io.xpipe.app.hub.section.StoreSectionMiniComp;
import io.xpipe.app.hub.section.StoreSectionSelector;
import io.xpipe.app.hub.section.StoreSectionState;
import io.xpipe.app.platform.PlatformThread;
import io.xpipe.app.storage.DataStoreEntry;
@@ -15,7 +21,6 @@ import javafx.scene.control.Button;
import javafx.scene.layout.Region;
import java.util.HashSet;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
@@ -68,7 +73,20 @@ public final class BrowserConnectionListComp extends SimpleRegionBuilder {
});
};
var sectionState = new StoreSectionState(filter, this::filter, category, FXCollections.emptyObservableList(), new ReadOnlyBooleanWrapper(true));
var selector = new StoreSectionSelector() {
@Override
public boolean excludeNonShown() {
return true;
}
@Override
public boolean matches(StoreEntryWrapper wrapper) {
return applicable.test(wrapper);
}
};
var sectionState = new StoreSectionState(
filter, selector, category, FXCollections.emptyObservableList(), new ReadOnlyBooleanWrapper(true));
var section = new StoreSectionMiniComp(
sectionState.getRootSection(),
augment,
@@ -4,10 +4,10 @@ import io.xpipe.app.comp.SimpleRegionBuilder;
import io.xpipe.app.comp.base.FilterComp;
import io.xpipe.app.comp.base.HorizontalComp;
import io.xpipe.app.core.AppFontSizes;
import io.xpipe.app.hub.comp.StoreCategoryChoiceComp;
import io.xpipe.app.hub.comp.StoreCategoryWrapper;
import io.xpipe.app.hub.comp.StoreFilter;
import io.xpipe.app.hub.comp.StoreViewState;
import io.xpipe.app.hub.category.StoreCategoryChoiceComp;
import io.xpipe.app.hub.category.StoreCategoryWrapper;
import io.xpipe.app.hub.list.StoreFilter;
import io.xpipe.app.hub.list.StoreViewState;
import io.xpipe.app.util.ObservableSubscriber;
import javafx.beans.property.Property;
@@ -2,8 +2,8 @@ package io.xpipe.app.browser.file;
import io.xpipe.app.browser.icon.BrowserIconDirectoryType;
import io.xpipe.app.browser.icon.BrowserIconFileType;
import io.xpipe.app.ext.FileEntry;
import io.xpipe.app.ext.FileKind;
import io.xpipe.app.fs.FileEntry;
import io.xpipe.app.fs.FileKind;
import lombok.Getter;
@@ -1,6 +1,6 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.ext.FileSystem;
import io.xpipe.app.fs.FileSystem;
import io.xpipe.app.util.FilePath;
import java.util.regex.Pattern;
@@ -19,13 +19,17 @@ public class BrowserFileDuplicates {
}
private static FilePath renameFile(FilePath target, boolean dir) {
var name = dir || target.isDotFile() ? target.getFileName() : target.getBaseName().getFileName();
var name = dir || target.isDotFile()
? target.getFileName()
: target.getBaseName().getFileName();
var pattern = Pattern.compile("(.+)_(\\d+)");
var matcher = pattern.matcher(name);
if (matcher.matches()) {
try {
var number = Integer.parseInt(matcher.group(2));
var suffix = dir || target.isDotFile() ? "" : target.getExtension().map(s -> "." + s).orElse("");
var suffix = dir || target.isDotFile()
? ""
: target.getExtension().map(s -> "." + s).orElse("");
var newFile = target.getParent().join(matcher.group(1) + "_" + (number + 1) + suffix);
return newFile;
} catch (NumberFormatException ignored) {
@@ -1,9 +1,9 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.core.window.AppDialog;
import io.xpipe.app.ext.ShellFileSystem;
import io.xpipe.app.ext.FileEntry;
import io.xpipe.app.ext.FileInfo;
import io.xpipe.app.fs.FileEntry;
import io.xpipe.app.fs.FileInfo;
import io.xpipe.app.fs.ShellFileSystem;
import io.xpipe.app.process.CommandBuilder;
import io.xpipe.app.process.ElevationFunction;
import io.xpipe.app.util.FilePath;
@@ -6,9 +6,9 @@ import io.xpipe.app.comp.SimpleRegionBuilder;
import io.xpipe.app.core.AppFontSizes;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.core.AppSizeBreakpoints;
import io.xpipe.app.ext.FileEntry;
import io.xpipe.app.ext.FileInfo;
import io.xpipe.app.ext.FileKind;
import io.xpipe.app.fs.FileEntry;
import io.xpipe.app.fs.FileInfo;
import io.xpipe.app.fs.FileKind;
import io.xpipe.app.platform.PlatformThread;
import io.xpipe.app.util.*;
@@ -2,12 +2,12 @@ package io.xpipe.app.browser.file;
import io.xpipe.app.browser.BrowserFullSessionModel;
import io.xpipe.app.core.AppSystemInfo;
import io.xpipe.app.ext.FileKind;
import io.xpipe.app.fs.FileKind;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.GlobalTimer;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.app.util.OsType;
import io.xpipe.app.util.ThreadHelper;
import javafx.scene.Node;
import javafx.scene.control.ContextMenu;
@@ -1,8 +1,8 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.browser.action.impl.MoveFileActionProvider;
import io.xpipe.app.ext.FileEntry;
import io.xpipe.app.ext.FileKind;
import io.xpipe.app.fs.FileEntry;
import io.xpipe.app.fs.FileKind;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.FilePath;
@@ -3,13 +3,13 @@ package io.xpipe.app.browser.file;
import io.xpipe.app.browser.icon.BrowserIconManager;
import io.xpipe.app.comp.base.LazyTextFieldComp;
import io.xpipe.app.comp.base.PrettyImageHelper;
import io.xpipe.app.ext.FileKind;
import io.xpipe.app.fs.FileKind;
import io.xpipe.app.platform.InputHelper;
import io.xpipe.app.platform.MenuHelper;
import io.xpipe.app.platform.PlatformThread;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.app.util.FilePath;
import io.xpipe.app.util.ThreadHelper;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
@@ -2,7 +2,7 @@ package io.xpipe.app.browser.file;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.core.window.AppDialog;
import io.xpipe.app.ext.FileEntry;
import io.xpipe.app.fs.FileEntry;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.FileBridge;
@@ -1,9 +1,9 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.core.window.AppDialog;
import io.xpipe.app.ext.ShellFileSystem;
import io.xpipe.app.ext.FileEntry;
import io.xpipe.app.ext.FileInfo;
import io.xpipe.app.fs.FileEntry;
import io.xpipe.app.fs.FileInfo;
import io.xpipe.app.fs.ShellFileSystem;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.process.CommandBuilder;
import io.xpipe.app.process.ElevationFunction;
@@ -6,7 +6,7 @@ import io.xpipe.app.comp.RegionBuilder;
import io.xpipe.app.comp.SimpleRegionBuilder;
import io.xpipe.app.comp.base.HorizontalComp;
import io.xpipe.app.comp.base.ListBoxViewComp;
import io.xpipe.app.ext.FileEntry;
import io.xpipe.app.fs.FileEntry;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
@@ -1,8 +1,8 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.ext.FileEntry;
import io.xpipe.app.ext.FileKind;
import io.xpipe.app.ext.FileSystem;
import io.xpipe.app.fs.FileEntry;
import io.xpipe.app.fs.FileKind;
import io.xpipe.app.fs.FileSystem;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.util.FilePath;
import io.xpipe.app.util.OsType;
@@ -1,29 +1,28 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.core.AppCache;
import io.xpipe.app.util.GlobalTimer;
import io.xpipe.app.util.FilePath;
import io.xpipe.app.util.GlobalTimer;
import io.xpipe.app.util.JacksonMapper;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import lombok.*;
import lombok.extern.jackson.Jacksonized;
import tools.jackson.core.JsonGenerator;
import tools.jackson.core.JsonParser;
import tools.jackson.databind.DeserializationContext;
import tools.jackson.databind.JavaType;
import tools.jackson.databind.SerializationContext;
import tools.jackson.databind.annotation.JsonDeserialize;
import tools.jackson.databind.annotation.JsonSerialize;
import tools.jackson.databind.deser.std.StdDeserializer;
import tools.jackson.databind.node.JsonNodeFactory;
import tools.jackson.databind.node.ObjectNode;
import tools.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
@@ -126,8 +125,7 @@ public class BrowserFileSystemSavedState {
}
@Override
public void serialize(BrowserFileSystemSavedState value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
public void serialize(BrowserFileSystemSavedState value, JsonGenerator gen, SerializationContext context) {
var node = JsonNodeFactory.instance.objectNode();
node.set("recentDirectories", JacksonMapper.getDefault().valueToTree(value.getRecentDirectories()));
gen.writeTree(node);
@@ -3,15 +3,14 @@ package io.xpipe.app.browser.file;
import io.xpipe.app.browser.BrowserFullSessionModel;
import io.xpipe.app.browser.menu.BrowserMenuProviders;
import io.xpipe.app.comp.*;
import io.xpipe.app.comp.augment.ContextMenuAugment;
import io.xpipe.app.comp.base.*;
import io.xpipe.app.core.AppFontSizes;
import io.xpipe.app.core.AppSizeBreakpoints;
import io.xpipe.app.platform.InputHelper;
import io.xpipe.app.platform.MenuHelper;
import io.xpipe.app.platform.PlatformThread;
import io.xpipe.app.util.GlobalTimer;
import io.xpipe.app.util.FilePath;
import io.xpipe.app.util.GlobalTimer;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ReadOnlyBooleanWrapper;
@@ -122,9 +121,13 @@ public class BrowserFileSystemTabComp extends SimpleRegionBuilder {
topBar.getChildren().setAll(leftBox, new Spacer(6), navBar.get(), new Spacer(6), rightBox);
topBar.setMinWidth(0);
var showAll = Bindings.createBooleanBinding(() -> {
return !AppSizeBreakpoints.portraitMode().get() || !navBar.textField().isFocused();
}, AppSizeBreakpoints.portraitMode(), navBar.textField().focusedProperty());
var showAll = Bindings.createBooleanBinding(
() -> {
return !AppSizeBreakpoints.portraitMode().get()
|| !navBar.textField().isFocused();
},
AppSizeBreakpoints.portraitMode(),
navBar.textField().focusedProperty());
leftBox.visibleProperty().bind(showAll);
leftBox.managedProperty().bind(leftBox.visibleProperty());
rightBox.visibleProperty().bind(showAll);
@@ -9,18 +9,23 @@ import io.xpipe.app.browser.menu.BrowserMenuItemProvider;
import io.xpipe.app.comp.BaseRegionBuilder;
import io.xpipe.app.core.window.AppMainWindow;
import io.xpipe.app.ext.*;
import io.xpipe.app.fs.FileEntry;
import io.xpipe.app.fs.FileKind;
import io.xpipe.app.fs.FileSystem;
import io.xpipe.app.fs.WrapperFileSystem;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.process.*;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.store.FileSystemStore;
import io.xpipe.app.terminal.*;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.app.util.FailableFunction;
import io.xpipe.app.util.FilePath;
import io.xpipe.app.util.OsType;
import io.xpipe.app.util.ThreadHelper;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
@@ -165,7 +170,7 @@ public final class BrowserFileSystemTabModel extends BrowserStoreSessionTab<File
getFileSystemNameSuffix().set(fs.getSuffix());
});
if (fs.getShell().isPresent()) {
ProcessControlProvider.get().withDefaultScripts(fs.getShell().get());
ProcModuleProvider.get().withDefaultScripts(fs.getShell().get());
}
fs.open();
@@ -483,8 +488,7 @@ public final class BrowserFileSystemTabModel extends BrowserStoreSessionTab<File
.map(shellControl -> shellControl.getShellDialect())
.orElse(null)
== ShellDialects.CMD) {
var env =
ProcessControlProvider.get().subShellEnvironment(getEntry().asNeeded(), ShellDialects.POWERSHELL);
var env = ProcModuleProvider.get().subShellEnvironment(getEntry().asNeeded(), ShellDialects.POWERSHELL);
var entry = DataStoreEntry.createNew(getName().getValue() + " (PowerShell)", env);
entry.setColor(DataStorage.get().getEffectiveColor(getEntry().get()));
entry.setCategoryUuid(getEntry().get().getCategoryUuid());
@@ -2,13 +2,13 @@ package io.xpipe.app.browser.file;
import io.xpipe.app.core.AppCache;
import io.xpipe.app.core.mode.AppOperationMode;
import io.xpipe.app.ext.FileEntry;
import io.xpipe.app.ext.FileKind;
import io.xpipe.app.ext.FileSystem;
import io.xpipe.app.fs.FileEntry;
import io.xpipe.app.fs.FileKind;
import io.xpipe.app.fs.FileSystem;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.util.FilePath;
import io.xpipe.app.util.GlobalTimer;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.app.util.FilePath;
import javafx.beans.property.BooleanProperty;
import javafx.beans.value.ChangeListener;
@@ -358,9 +358,12 @@ public class BrowserFileTransferOperation {
var targetId = targetFs.getIdentifier();
var fileSize = optimizedSourceFs.getFileSize(sourceFile.getPath());
var startTransferred = transferred.get();
if ((!optimizedSourceFs.hasAccurateProgress() || !targetFs.hasAccurateProgress()) && sourceId.isPresent() && targetId.isPresent()) {
if ((!optimizedSourceFs.hasAccurateProgress() || !targetFs.hasAccurateProgress())
&& sourceId.isPresent()
&& targetId.isPresent()) {
var mapKey = sourceId.get() + "-" + targetId.get();
Long cachedSpeed = AppCache.getNonNullMapEntry("transferSpeedEstimate", mapKey, Long.class, () -> null);
Long cachedSpeed =
AppCache.getNonNullMapEntry("transferSpeedEstimate", mapKey, Long.class, () -> null);
var counter = new AtomicInteger();
if (cachedSpeed != null) {
GlobalTimer.scheduleUntil(Duration.ofMillis(100), false, () -> {
@@ -369,9 +372,10 @@ public class BrowserFileTransferOperation {
}
// Divide by 9 and not 10 to overreport progress a bit. Better than underreporting
var addedProgress = Math.min(fileSize, (long) counter.incrementAndGet() * cachedSpeed / 9);
updateProgress(
new BrowserTransferProgress(sourceFile.getName(), startTransferred + addedProgress, totalSize.get()));
var addedProgress =
Math.min(fileSize, (long) counter.incrementAndGet() * cachedSpeed / 9);
updateProgress(new BrowserTransferProgress(
sourceFile.getName(), startTransferred + addedProgress, totalSize.get()));
return false;
});
} else {
@@ -379,16 +383,25 @@ public class BrowserFileTransferOperation {
}
}
var startProgress = new BrowserTransferProgress(source.getName(), transferred.get(), totalSize.get());
var startProgress =
new BrowserTransferProgress(source.getName(), transferred.get(), totalSize.get());
try {
transfer(sourceFile.getPath(), optimizedSourceFs, targetFile, targetFs, transferred, totalSize, fileSize);
transfer(
sourceFile.getPath(),
optimizedSourceFs,
targetFile,
targetFs,
transferred,
totalSize,
fileSize);
} finally {
transferRunning.set(false);
}
if (sourceId.isPresent() && targetId.isPresent()) {
var mapKey = sourceId.get() + "-" + targetId.get();
var speed = BrowserTransferProgress.estimateTransferSpeed(List.of(startProgress),
var speed = BrowserTransferProgress.estimateTransferSpeed(
List.of(startProgress),
new BrowserTransferProgress(sourceFile.getName(), transferred.get(), totalSize.get()));
AppCache.updateMapEntry("transferSpeedEstimate", mapKey, speed);
}
@@ -460,7 +473,8 @@ public class BrowserFileTransferOperation {
if (transferInline(sourceFile, sourceFs, targetFile, targetFs) || cancelled()) {
if (!cancelled()) {
transferred.addAndGet(fileSize);
updateProgress(new BrowserTransferProgress(sourceFile.getFileName(), transferred.get(), totalSize.get()));
updateProgress(
new BrowserTransferProgress(sourceFile.getFileName(), transferred.get(), totalSize.get()));
}
return;
}
@@ -483,7 +497,14 @@ public class BrowserFileTransferOperation {
}
outputStream = targetFs.openOutput(targetFile, fileSize);
transferFile(sourceFile, inputStream, outputStream, transferred, totalSize, fileSize, sourceFs.hasAccurateProgress() && targetFs.hasAccurateProgress());
transferFile(
sourceFile,
inputStream,
outputStream,
transferred,
totalSize,
fileSize,
sourceFs.hasAccurateProgress() && targetFs.hasAccurateProgress());
} catch (Exception ex) {
// Mark progress as finished to reset any progress display
updateProgress(BrowserTransferProgress.finished(sourceFile.getFileName(), transferred.get()));
@@ -586,7 +607,8 @@ public class BrowserFileTransferOperation {
transferred.addAndGet(read);
readCount.addAndGet(read);
if (reportProgress) {
updateProgress(new BrowserTransferProgress(sourceFile.getFileName(), transferred.get(), total.get()));
updateProgress(
new BrowserTransferProgress(sourceFile.getFileName(), transferred.get(), total.get()));
}
}
@@ -6,15 +6,15 @@ import io.xpipe.app.util.JacksonMapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.SneakyThrows;
import lombok.Value;
import tools.jackson.core.JsonParser;
import tools.jackson.databind.DeserializationContext;
import tools.jackson.databind.JavaType;
import tools.jackson.databind.annotation.JsonDeserialize;
import tools.jackson.databind.annotation.JsonSerialize;
import tools.jackson.databind.deser.std.StdDeserializer;
import tools.jackson.databind.node.ObjectNode;
import java.util.List;
@@ -19,7 +19,6 @@ import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.layout.*;
@@ -147,9 +146,12 @@ public class BrowserHistoryTabComp extends SimpleRegionBuilder {
}
});
})
.apply(button -> button.minWidthProperty().bind(Bindings.createDoubleBinding(() -> {
return AppSizeBreakpoints.portraitMode().get() ? 170.0 : 300.0;
}, AppSizeBreakpoints.portraitMode())))
.apply(button -> button.minWidthProperty()
.bind(Bindings.createDoubleBinding(
() -> {
return AppSizeBreakpoints.portraitMode().get() ? 170.0 : 300.0;
},
AppSizeBreakpoints.portraitMode())))
.describe(
d -> d.name(new ReadOnlyStringWrapper(DataStorage.get().getStoreEntryDisplayName(entry.get()))))
.disable(disable)
@@ -1,9 +1,9 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.ext.FileEntry;
import io.xpipe.app.ext.FileKind;
import io.xpipe.app.ext.FileSystem;
import io.xpipe.app.ext.LocalStore;
import io.xpipe.app.fs.FileEntry;
import io.xpipe.app.fs.FileKind;
import io.xpipe.app.fs.FileSystem;
import io.xpipe.app.store.LocalStore;
import io.xpipe.app.util.FilePath;
import java.nio.file.Files;
@@ -4,8 +4,8 @@ import io.xpipe.app.browser.icon.BrowserIconManager;
import io.xpipe.app.comp.RegionBuilder;
import io.xpipe.app.comp.RegionStructure;
import io.xpipe.app.comp.RegionStructureBuilder;
import io.xpipe.app.comp.augment.ContextMenuAugment;
import io.xpipe.app.comp.base.ButtonComp;
import io.xpipe.app.comp.base.ContextMenuAugment;
import io.xpipe.app.comp.base.PrettyImageHelper;
import io.xpipe.app.comp.base.TextFieldComp;
import io.xpipe.app.core.AppFontSizes;
@@ -170,7 +170,10 @@ public class BrowserNavBarComp extends RegionStructureBuilder<HBox, BrowserNavBa
pathBar.apply(struc -> {
struc.focusedProperty().subscribe(val -> {
struc.pseudoClassStateChanged(
INVISIBLE, !val && !model.getInOverview().get() && !AppSizeBreakpoints.portraitMode().get());
INVISIBLE,
!val
&& !model.getInOverview().get()
&& !AppSizeBreakpoints.portraitMode().get());
if (val) {
Platform.runLater(() -> {
@@ -181,7 +184,10 @@ public class BrowserNavBarComp extends RegionStructureBuilder<HBox, BrowserNavBa
struc.widthProperty().subscribe(val -> {
struc.pseudoClassStateChanged(
INVISIBLE, !struc.isFocused() && !model.getInOverview().get() && !AppSizeBreakpoints.portraitMode().get());
INVISIBLE,
!struc.isFocused()
&& !model.getInOverview().get()
&& !AppSizeBreakpoints.portraitMode().get());
});
struc.addEventHandler(KeyEvent.KEY_PRESSED, ke -> {
@@ -194,7 +200,11 @@ public class BrowserNavBarComp extends RegionStructureBuilder<HBox, BrowserNavBa
// Pseudo classes do not apply if set instantly before shown
// If we start a new tab with a directory set, we have to set the pseudo class one pulse later
Platform.runLater(() -> {
struc.pseudoClassStateChanged(INVISIBLE, !val && !struc.isFocused() && !AppSizeBreakpoints.portraitMode().get());
struc.pseudoClassStateChanged(
INVISIBLE,
!val
&& !struc.isFocused()
&& !AppSizeBreakpoints.portraitMode().get());
});
});
});
@@ -5,11 +5,11 @@ import io.xpipe.app.comp.SimpleRegionBuilder;
import io.xpipe.app.comp.base.SimpleTitledPaneComp;
import io.xpipe.app.comp.base.VerticalComp;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.FileEntry;
import io.xpipe.app.fs.FileEntry;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.platform.DerivedObservableList;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.app.util.FilePath;
import io.xpipe.app.util.ThreadHelper;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
@@ -3,8 +3,8 @@ package io.xpipe.app.browser.file;
import io.xpipe.app.browser.icon.BrowserIcons;
import io.xpipe.app.comp.base.ModalOverlay;
import io.xpipe.app.core.window.AppDialog;
import io.xpipe.app.ext.FileEntry;
import io.xpipe.app.ext.FileKind;
import io.xpipe.app.fs.FileEntry;
import io.xpipe.app.fs.FileKind;
import io.xpipe.app.platform.BooleanAnimationTimer;
import io.xpipe.app.platform.InputHelper;
import io.xpipe.app.prefs.AppPrefs;
@@ -3,7 +3,7 @@ package io.xpipe.app.browser.file;
import io.xpipe.app.comp.BaseRegionBuilder;
import io.xpipe.app.comp.RegionBuilder;
import io.xpipe.app.comp.SimpleRegionBuilder;
import io.xpipe.app.comp.augment.ContextMenuAugment;
import io.xpipe.app.comp.base.ContextMenuAugment;
import io.xpipe.app.comp.base.HorizontalComp;
import io.xpipe.app.comp.base.IconButtonComp;
import io.xpipe.app.comp.base.LabelComp;
@@ -41,7 +41,7 @@ public class BrowserStatusBarComp extends SimpleRegionBuilder {
createKillButton()));
bar.minWidth(0);
bar.spacing(15);
bar.style("status-bar");
bar.style("status-bar").style("color-box");
bar.apply(struc -> {
struc.widthProperty().subscribe(value -> {
@@ -42,10 +42,8 @@ public class BrowserTransferComp extends SimpleRegionBuilder {
.apply(struc -> struc.setTextAlignment(TextAlignment.CENTER))
.apply(struc -> struc.setContentDisplay(ContentDisplay.TOP))
.visible(model.getEmpty());
var backgroundStack = new StackComp(List.of(background))
.style("color-box")
.style("gray")
.style("download-background");
var backgroundStack =
new StackComp(List.of(background)).style("color-box").style("download-background");
var binding = DerivedObservableList.wrap(model.getItems(), true)
.mapped(item -> item.getBrowserEntry())
@@ -30,7 +30,10 @@ public class BrowserTransferProgress {
}
if (list.size() == 1) {
var ms = Math.max(1, now.getTimestamp().toEpochMilli() - list.getFirst().getTimestamp().toEpochMilli());
var ms = Math.max(
1,
now.getTimestamp().toEpochMilli()
- list.getFirst().getTimestamp().toEpochMilli());
return Math.round((now.getTransferred() - list.getFirst().getTransferred()) / (ms / 1000.0));
}
@@ -1,8 +1,8 @@
package io.xpipe.app.browser.icon;
import io.xpipe.app.core.AppResources;
import io.xpipe.app.ext.FileEntry;
import io.xpipe.app.ext.FileKind;
import io.xpipe.app.fs.FileEntry;
import io.xpipe.app.fs.FileKind;
import java.io.BufferedReader;
import java.io.InputStreamReader;

Some files were not shown because too many files have changed in this diff Show More