mirror of
https://github.com/xpipe-io/xpipe.git
synced 2026-08-23 01:36:33 +00:00
Merge
This commit is contained in:
+4
-13
@@ -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).
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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
Reference in New Issue
Block a user