Merge branch transfer-performance into master

This commit is contained in:
crschnick 2024-03-04 17:23:30 +00:00
parent bda50dba72
commit a7c046520c
25 changed files with 180 additions and 115 deletions

View file

@ -94,7 +94,7 @@ final class BrowserBookmarkComp extends SimpleComp {
event.consume();
});
});
});
}, true);
var category = new DataStoreCategoryChoiceComp(
StoreViewState.get().getAllConnectionsCategory(),
StoreViewState.get().getActiveCategory(),

View file

@ -445,7 +445,7 @@ final class BrowserFileListComp extends SimpleComp {
if (path.getRawFileEntry().resolved().getKind() == FileKind.DIRECTORY) {
setText("");
} else {
setText(byteCount(fileSize.longValue()));
setText(byteCount(fileSize.longValue(), true));
}
}
}

View file

@ -7,6 +7,7 @@ import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.SimpleCompStructure;
import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
import io.xpipe.app.fxcomps.impl.LabelComp;
import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.util.HumanReadableFormat;
import javafx.beans.binding.Bindings;
@ -46,16 +47,16 @@ public class BrowserStatusBarComp extends SimpleComp {
var transferredCount = PlatformThread.sync(Bindings.createStringBinding(
() -> {
return HumanReadableFormat.byteCount(
model.getProgress().getValue().getTransferred());
model.getProgress().getValue().getTransferred(), false);
},
model.getProgress()));
var allCount = PlatformThread.sync(Bindings.createStringBinding(
() -> {
return HumanReadableFormat.byteCount(
model.getProgress().getValue().getTotal());
model.getProgress().getValue().getTotal(), true);
},
model.getProgress()));
var progressComp = new LabelComp(Bindings.createStringBinding(
var progressComp = new LabelComp(BindingsHelper.persist(Bindings.createStringBinding(
() -> {
if (model.getProgress().getValue() == null
|| model.getProgress().getValue().done()) {
@ -69,7 +70,7 @@ public class BrowserStatusBarComp extends SimpleComp {
},
transferredCount,
allCount,
model.getProgress()));
model.getProgress())));
return progressComp;
}

View file

@ -20,7 +20,7 @@ import java.util.function.Consumer;
public class FileSystemHelper {
private static final int DEFAULT_BUFFER_SIZE = 16384;
private static final int DEFAULT_BUFFER_SIZE = 1024;
private static FileSystem localFileSystem;
public static String adjustPath(OpenFileSystemModel model, String path) {

View file

@ -35,6 +35,7 @@ import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import lombok.AccessLevel;
import lombok.experimental.FieldDefaults;
import net.synedra.validatorfx.GraphicDecorationStackPane;
import java.util.List;
import java.util.Objects;
@ -397,6 +398,9 @@ public class StoreCreationComp extends DialogComp {
var top = new VBox(providerChoice.createRegion(), new Spacer(7, Orientation.VERTICAL), sep);
top.getStyleClass().add("top");
layout.setTop(top);
return layout;
var valSp = new GraphicDecorationStackPane();
valSp.getChildren().add(layout);
return valSp;
}
}

View file

@ -18,26 +18,34 @@ import javafx.css.PseudoClass;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import lombok.Builder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiConsumer;
@Builder
public class StoreSectionMiniComp extends Comp<CompStructure<VBox>> {
public static final PseudoClass EXPANDED = PseudoClass.getPseudoClass("expanded");
private static final PseudoClass ODD = PseudoClass.getPseudoClass("odd-depth");
private static final PseudoClass EVEN = PseudoClass.getPseudoClass("even-depth");
private static final PseudoClass ROOT = PseudoClass.getPseudoClass("root");
private static final PseudoClass TOP = PseudoClass.getPseudoClass("top");
private static final PseudoClass SUB = PseudoClass.getPseudoClass("sub");
private final StoreSection section;
private final BiConsumer<StoreSection, Comp<CompStructure<Button>>> augment;
private final boolean condensedStyle;
@Builder.Default
private final BiConsumer<StoreSection, Comp<CompStructure<Button>>> augment = (section1, buttonComp) -> {};
public StoreSectionMiniComp(StoreSection section, BiConsumer<StoreSection, Comp<CompStructure<Button>>> augment, boolean condensedStyle) {
this.section = section;
this.augment = augment;
this.condensedStyle = condensedStyle;
}
public static Comp<?> createList(StoreSection top, BiConsumer<StoreSection, Comp<CompStructure<Button>>> augment) {
return new StoreSectionMiniComp(top, augment);
public static Comp<?> createList(StoreSection top, BiConsumer<StoreSection, Comp<CompStructure<Button>>> augment, boolean condensedStyle) {
return new StoreSectionMiniComp(top, augment, condensedStyle);
}
@Override
@ -104,10 +112,7 @@ public class StoreSectionMiniComp extends Comp<CompStructure<VBox>> {
section.getAllChildren())
: section.getShownChildren();
var content = new ListBoxViewComp<>(listSections, section.getAllChildren(), (StoreSection e) -> {
return StoreSectionMiniComp.builder()
.section(e)
.augment(this.augment)
.build();
return new StoreSectionMiniComp(e, this.augment, this.condensedStyle);
})
.minHeight(0)
.hgrow();
@ -119,9 +124,12 @@ public class StoreSectionMiniComp extends Comp<CompStructure<VBox>> {
Bindings.not(expanded),
Bindings.size(section.getAllChildren()).isEqualTo(0)))));
return new VerticalComp(list)
var vert = new VerticalComp(list);
if (condensedStyle) {
vert.styleClass("condensed");
}
return vert
.styleClass("store-section-mini-comp")
.styleClass(section.getWrapper() != null ? "sub" : "top")
.apply(struc -> {
struc.get().setFillWidth(true);
SimpleChangeListener.apply(expanded, val -> {
@ -129,6 +137,9 @@ public class StoreSectionMiniComp extends Comp<CompStructure<VBox>> {
});
struc.get().pseudoClassStateChanged(EVEN, section.getDepth() % 2 == 0);
struc.get().pseudoClassStateChanged(ODD, section.getDepth() % 2 != 0);
struc.get().pseudoClassStateChanged(ROOT, section.getDepth() == 0);
struc.get().pseudoClassStateChanged(TOP, section.getDepth() == 1);
struc.get().pseudoClassStateChanged(SUB, section.getDepth() > 1);
})
.apply(struc -> {
if (section.getWrapper() != null) {

View file

@ -22,9 +22,8 @@ public class AppShellCheck {
The most likely causes are:
- On Windows, an AntiVirus program might block required programs and commands
- The system shell is restricted or blocked
- The operating system is not supported
You can reach out to us if you want to properly diagnose the cause individually and hopefully fix it.
You can try to switch to the fallback shell by going to Settings -> Local Shell -> Enable Fallback Shell and restart.
"""
.formatted(
ProcessControlProvider.get()

View file

@ -100,7 +100,7 @@ public class DataStoreChoiceComp<T extends DataStore> extends SimpleComp {
if (!applicable.test(s.getWrapper())) {
comp.disable(new SimpleBooleanProperty(true));
}
});
}, false);
var category = new DataStoreCategoryChoiceComp(
initialCategory != null ? initialCategory.getRoot() : null,
StoreViewState.get().getActiveCategory(),

View file

@ -8,6 +8,7 @@ import io.xpipe.app.ext.PrefsHandler;
import io.xpipe.app.ext.PrefsProvider;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.ApplicationHelper;
import io.xpipe.app.util.PasswordLockSecretValue;
@ -22,6 +23,7 @@ import javafx.beans.value.ObservableValue;
import lombok.Getter;
import lombok.Value;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
@ -156,6 +158,8 @@ public class AppPrefs {
INSTANCE = new AppPrefs();
PrefsProvider.getAll().forEach(prov -> prov.addPrefs(INSTANCE.extensionHandler));
INSTANCE.loadLocal();
INSTANCE.fixInvalidLocalValues();
INSTANCE.vaultStorageHandler = new AppPrefsStorageHandler(INSTANCE.storageDirectory().getValue().resolve("preferences.json"));
}
public static void initSharedRemote() {
@ -427,14 +431,20 @@ public class AppPrefs {
loadValue(globalStorageHandler, value);
}
}
// How can this happen?
// Set to default if corrupted
private void fixInvalidLocalValues() {
// You can set the directory to empty in the settings
if (storageDirectory.get() == null) {
storageDirectory.setValue(DEFAULT_STORAGE_DIR);
}
vaultStorageHandler = new AppPrefsStorageHandler(storageDirectory().getValue().resolve("preferences.json"));
try {
Files.createDirectories(storageDirectory.get());
} catch (Exception e) {
ErrorEvent.fromThrowable(e).build().handle();
storageDirectory.setValue(DEFAULT_STORAGE_DIR);
}
}
private void loadSharedRemote() {

View file

@ -1,6 +1,7 @@
package io.xpipe.app.test;
import io.xpipe.app.core.mode.OperationMode;
import io.xpipe.core.process.OsType;
import lombok.SneakyThrows;
import org.junit.jupiter.api.BeforeAll;
@ -13,6 +14,7 @@ public class LocalExtensionTest extends ExtensionTest {
return;
}
OperationMode.init(new String[0]);
var mode = OsType.getLocal().equals(OsType.WINDOWS) ? "tray" : "background";
OperationMode.init(new String[] {"--mode", mode});
}
}

View file

@ -16,7 +16,7 @@ public final class HumanReadableFormat {
public static final DateTimeFormatter DAY_OF_WEEK = DateTimeFormatter.ofPattern("EEE");
public static final DateTimeFormatter HOUR_MINUTE = DateTimeFormatter.ofPattern("HH:mm");
public static String byteCount(long bytes) {
public static String byteCount(long bytes, boolean precise) {
if (-1024 < bytes && bytes < 1024) {
return bytes + " B";
}
@ -25,7 +25,8 @@ public final class HumanReadableFormat {
bytes /= 1024;
ci.next();
}
return String.format("%.1f %cB", bytes / 1024.0, ci.current());
var f = precise ? "%.1f" : "%.0f";
return String.format(f + " %cB", bytes / 1024.0, ci.current());
}
public static String date(LocalDateTime x) {

View file

@ -158,7 +158,7 @@ public class ScanAlert {
.addComp(Comp.of(() -> stackPane).vgrow())
.buildComp()
.prefWidth(500)
.prefHeight(650)
.prefHeight(680)
.apply(struc -> {
VBox.setVgrow(struc.get().getChildren().get(1), ALWAYS);
})

View file

@ -1,11 +1,13 @@
.bookmark-list > .categories {
-fx-padding: 0.7em 1em 0.7em 1em;
-fx-background-color: -color-neutral-subtle;
-fx-background-color: -color-bg-subtle;
-fx-border-color: -color-border-default;
-fx-border-width: 0 0 1 0;
}
.bookmark-list .filter-comp {
-fx-border-width: 0.05em;
-fx-border-width: 1;
-fx-border-radius: 0 4px 4px 0;
-fx-background-radius: 0 4px 4px 0;
-fx-background-color: -color-bg-default;

View file

@ -2,6 +2,7 @@
-fx-border-color: -color-border-default;
-fx-border-width: 1px 0 0 0;
-fx-padding: 1em;
-fx-background-color: -color-bg-subtle;
}
.transfer .button {
@ -146,6 +147,7 @@
.browser .status-bar {
-fx-border-width: 1 0 0 0;
-fx-border-color: -color-border-default;
-fx-font-family: Roboto;
}
.browser .breadcrumbs >.divider {

View file

@ -5,6 +5,8 @@
.choice-comp-content > .top {
-fx-padding: 0.5em 1em 0.5em 1em;
-fx-background-color: -color-neutral-subtle;
-fx-border-width: 1 0 1 0;
-fx-border-color: -color-border-default;
}
.choice-comp .filter-comp {

View file

@ -1,6 +1,7 @@
.scroll-bar:vertical {
-fx-pref-width: 0.3em;
-fx-padding: 0.3em 0 0.3em 0;
-fx-background-color: transparent;
}
.scroll-bar:horizontal {

View file

@ -1,92 +1,107 @@
.popover {
-fx-background-radius: 0;
-fx-background-radius: 0;
}
.popover > .content {
-fx-padding: 10px 0 0 0;
-fx-background-radius: 4px;
-fx-padding: 10px 0 0 0;
-fx-background-radius: 4px;
}
.store-mini-list-comp > * > * > .content {
-fx-spacing: 0.5em;
-fx-spacing: 0.5em;
}
.store-mini-list-comp .top {
-fx-border-color: transparent;
-fx-background-color: transparent;
.store-mini-list-comp:root {
-fx-border-color: transparent;
-fx-background-color: transparent;
}
.store-section-mini-comp .item {
-fx-padding: 0.25em 0.4em 0.25em 0.4em;
-fx-border-color: transparent;
-fx-background-color: transparent;
-fx-padding: 0.25em 0.4em 0.25em 0.4em;
-fx-border-color: transparent;
-fx-background-color: transparent;
}
.store-section-mini-comp .item:hover, .store-section-mini-comp .item:focused {
-fx-background-color: -color-accent-subtle;
-fx-background-color: -color-accent-subtle;
}
.root:light .store-section-mini-comp.sub:expanded:even-depth {
-fx-background-color: #9991;
-fx-border-color: -color-border-subtle;
.root:light .store-section-mini-comp:sub:expanded:even-depth {
-fx-background-color: #9991;
-fx-border-color: -color-border-subtle;
}
.root:dark .store-section-mini-comp.sub:expanded:even-depth {
-fx-background-color: #0002;
-fx-border-color: -color-border-subtle;
.root:dark .store-section-mini-comp:sub:expanded:even-depth {
-fx-background-color: #0002;
-fx-border-color: -color-border-subtle;
}
.store-section-mini-comp .expand-button:hover, .store-section-mini-comp .expand-button:focused {
-fx-background-color: -color-neutral-muted;
-fx-background-color: -color-neutral-muted;
}
.store-section-mini-comp .expand-button:disabled {
-fx-opacity: 0.2;
-fx-opacity: 0.2;
}
.store-section-mini-comp .separator {
-fx-padding: 0 0.75em 0 0.75em;
-fx-border-insets: 0px;
-fx-padding: 0 0.75em 0 0.75em;
-fx-border-insets: 0px;
}
.store-section-mini-comp > .content {
-fx-padding: 5px 0 5px 15px;
-fx-padding: 5px 0 5px 15px;
}
.store-section-mini-comp.top > .content {
.store-section-mini-comp:root > .content {
-fx-padding: 0.5em 1em 0.5em 1em;
}
.store-section-mini-comp.condensed:root > .content {
-fx-padding: 0;
}
.store-section-mini-comp .separator .line {
-fx-padding: 0;
-fx-border-insets: 0px;
-fx-background-color: -color-border-subtle;
-fx-opacity: 0.5;
-fx-pref-height: 1;
-fx-padding: 0;
-fx-border-insets: 0px;
-fx-background-color: -color-border-subtle;
-fx-opacity: 0.5;
-fx-pref-height: 1;
}
.root:light .top > .store-section-mini-comp {
-fx-background-color: #9991;
.root:light .store-section-mini-comp:top {
-fx-background-color: #9991;
}
.root:dark .top > .store-section-mini-comp {
-fx-background-color: #0001;
.root:dark .store-section-mini-comp:top {
-fx-background-color: #0001;
}
.store-section-mini-comp.sub:expanded {
-fx-border-radius: 2px;
-fx-border-width: 1px 0 1px 1px;
-fx-border-color: -color-border-default;
.store-section-mini-comp:sub:expanded {
-fx-border-radius: 4 0 0 4;
-fx-border-width: 1px 0 1px 1px;
-fx-border-color: -color-border-default;
}
.top > .store-section-mini-comp {
-fx-border-width: 1px 0 1px 0px;
-fx-border-color: -color-border-default;
.store-section-mini-comp:root {
-fx-border-width: 0;
}
.store-section-mini-comp.condensed:top {
-fx-border-radius: 0;
-fx-border-width: 1px 1 1px 0px;
-fx-border-color: -color-border-default;
}
.store-section-mini-comp:top {
-fx-border-radius: 4 0 0 4;
-fx-border-width: 1 1 1 1;
-fx-border-color: -color-border-default;
}
.store-section-mini-comp .list-box-view-comp .content {
-fx-spacing: 0.4em;
-fx-spacing: 0.4em;
}

View file

@ -1,5 +1,6 @@
package io.xpipe.beacon.test;
import io.xpipe.core.process.OsType;
import io.xpipe.core.util.JacksonMapper;
import io.xpipe.core.util.XPipeDaemonMode;
import org.junit.jupiter.api.AfterAll;
@ -10,7 +11,7 @@ public class BeaconDaemonExtensionTest {
@BeforeAll
public static void setup() throws Exception {
JacksonMapper.initModularized(ModuleLayer.boot());
BeaconDaemonController.start(XPipeDaemonMode.TRAY);
BeaconDaemonController.start(OsType.getLocal().equals(OsType.WINDOWS) ? XPipeDaemonMode.TRAY : XPipeDaemonMode.BACKGROUND);
}
@AfterAll

View file

@ -4,6 +4,20 @@ import lombok.NonNull;
public interface ShellOpenFunction {
static ShellOpenFunction unsupported() {
return new ShellOpenFunction() {
@Override
public CommandBuilder prepareWithoutInitCommand() {
throw new UnsupportedOperationException();
}
@Override
public CommandBuilder prepareWithInitCommand(@NonNull String command) {
throw new UnsupportedOperationException();
}
};
}
static ShellOpenFunction of(String b) {
return new ShellOpenFunction() {
@Override

View file

@ -1,24 +0,0 @@
## Windows updater issues
The last few versions of XPipe from 1.7.16 to 8.0.1 all had a self-updater on Windows that was not working properly. This was caused by a newly introduced JDK bug. This is now fixed from 8.0.2 onwards.
To upgrade to 8.0.2+, you have to do it manually by downloading and installing it from https://github.com/xpipe-io/xpipe/releases. There shouldn't be any more problems with 8.0.2+ after that.
This also fixes opened terminal windows closing down when XPipe is closed on Windows. They will now stay open.
## Git Vault Versioning
When upgrading XPipe versions across many systems, one problem could have been the git vault format being updated on one system and being pulled on another a system that is running an older version. This could have led to data corruption. From now on, there will be a warning shown when the versions do not match up. From there you can choose to temporarily disable the git vault during a session until you upgrade to the matching version on that system as well.
If this problem already happened to you, you should be able to reset the git repository to an older ref.
## Filtering for hosts
You can now search for IPs and hostnames in addition to the connection names to filter your connection list. The connection a display when a filter is active has also been improved.
## Other changes
- Fix fallback shell action throwing some errors initially
- Properly set TERM variable for powershell environments
- Fix concurrency issues when querying multiple secrets at the same time
- Fix some null pointers

View file

@ -4,11 +4,9 @@ The versioning scheme has also been changed to simplify version numbers. So we a
## Note on updating
The last few versions of XPipe from 1.7.16 to 8.0.1 all had a self-updater on Windows that was not working properly. This was caused by a newly introduced JDK bug. This is now fixed from 8.0.2 onwards.
The last few versions of XPipe from 1.7.16 to 8.0.1 all had a self-updater on Windows that was not working properly. This was caused by a newly introduced JDK bug. This is now fixed from 8.1 onwards. To upgrade to 8.1+ on Windows, you have to do it manually by downloading and installing it from https://github.com/xpipe-io/xpipe/releases. There shouldn't be any more problems with 8.1+ after that.
To upgrade to 8.0.2+ on Windows, you have to do it manually by downloading and installing it from https://github.com/xpipe-io/xpipe/releases. There shouldn't be any more problems with 8.0.2+ after that.
This also fixes opened terminal windows closing down when XPipe is closed. They will now stay open.
Not that versions <8.1 do not contain version information in the git vault. If you're on multiple systems that are synced with git, the git vault format can be updated on one system and being pulled on another a system that is running an older version. This can lead to data corruption. If this happens to you, you should be able to reset the git repository to a previous ref.
## New terminal launcher
@ -36,11 +34,6 @@ The file browser has been reworked in terms of performance and reliability. File
In terms of the interface, there is also now a progress indicator for files being transferred. For any file conflicts, there is now a new dialog to choose how to resolve any conflict when copying or moving files.
There are also a couple more changes included:
- Fix files in file browser not reloading content when trying to edit them multiple times in the session
- Add Open with ... action to open files with an arbitrary program
- The transfer pane now also allows file drops from outside the window to make it more intuitive
## Kubernetes configs and namespaces
This update adds support to also add connections from other kubeconfig files.
@ -91,10 +84,6 @@ Some Windows admins disable cmd on their systems for security reasons. Previousl
One common problem in the past has been to fact that Microsoft ships relatively outdated OpenSSH versions on Windows, which do not support newer features like FIDO2 keys. Due to the permissive license of OpenSSH and its Windows fork, XPipe can bundle the latest OpenSSH versions on Windows. There is now an option the settings menu to use the latest bundled OpenSSH version.
## Dependency upgrades
All dependencies have been upgraded to the latest version, coming with a few fixes and some new features. In particular, the JavaFX version has been bumped, which now allows for native system theme observation and the usage of accent colors. Around 10 dependency libraries have been removed as they are no longer necessary.
## Timeout handling
The timeout model has been reworked. It is now set to a fixed amount of 30s while any active password prompts do no longer count towards it, meaning you can take your time when typing your passwords. An increased timeout value also allows for better handling of third party authentication schemes that XPipe has no control over, e.g. ones that will open a website in your browser for authentication.

37
dist/changelogs/8.1_incremental.md vendored Normal file
View file

@ -0,0 +1,37 @@
## Windows updater issues
The last few versions of XPipe from 1.7.16 to 8.0.1 all had a self-updater on Windows that was not working properly. This was caused by a newly introduced JDK bug. This is now fixed from 8.1 onwards.
To upgrade to 8.1+, you have to do it manually by downloading and installing it from https://github.com/xpipe-io/xpipe/releases. There shouldn't be any more problems with 8.1+ after that.
This also fixes opened terminal windows closing down when XPipe is closed on Windows. They will now stay open.
## Git Vault Versioning
When upgrading XPipe versions across many systems, one problem could have been the git vault format being updated on one system and being pulled on another a system that is running an older version. This could have led to data corruption. From now on, there will be a warning shown when the versions do not match up. From there you can choose to temporarily disable the git vault during a session until you upgrade to the matching version on that system as well.
If this happens to you, you should be able to reset the git repository to a previous ref.
## Filtering for hosts
You can now search for IPs and hostnames in addition to the connection names to filter your connection list. The connection a display when a filter is active has also been improved.
## File browser transfer fixes
There was a regression in transfer speed in 8.0 causing transfers of large files being very slow. This is now fixed.
## Open directories in WSL
There is now a new action available in the file browser for directories on Windows systems that allows you to open that directory in a WSL session. This makes it easier to quickly use Linux tools in a certain directory you're currently in when on Windows.
## Other changes
- Fix fallback shell action throwing some errors initially
- Properly set TERM variable for powershell environments
- Improve styling in some areas
- Better validate invalid settings values on startup
- Fix concurrency issues when querying multiple secrets at the same time
- Fix red validation markers appearing in front of other UI elements
- Fix msys2, cygwin, and gitforwindows shell environments being shown for the wrong parent connection when located on remote systems
- Fix transferred files with BOM sometimes getting corrupted on Windows systems
- Fix some null pointers

View file

@ -4,9 +4,8 @@ The process of elevation is operating system specific.
### Linux & macOS
Any elevated command is executed with `sudo`.
If this is done on your local system, the optional `sudo` password is queried via XPipe itself.
For remote systems, the `sudo` password is determined from the connection information, i.e. the used login password.
Any elevated command is executed with `sudo`. The optional `sudo` password is queried via XPipe when needed.
You have the ability to adjust the elevation behavior in the settings to control whether you want to enter your password every time it is needed or if you want to cache it for the current session.
### Windows

View file

@ -28,7 +28,6 @@ testing {
systemProperty "io.xpipe.app.dataDir", "$projectDir/local/"
systemProperty "io.xpipe.app.logLevel", "trace"
systemProperty "io.xpipe.app.writeSysOut", "true"
systemProperty "io.xpipe.app.mode", "tray"
}
}
}

View file

@ -1 +1 @@
8.0.1
8.1