diff --git a/app/src/main/java/io/xpipe/app/beacon/BeaconRequestHandler.java b/app/src/main/java/io/xpipe/app/beacon/BeaconRequestHandler.java index 99b41370a..e33d3d680 100644 --- a/app/src/main/java/io/xpipe/app/beacon/BeaconRequestHandler.java +++ b/app/src/main/java/io/xpipe/app/beacon/BeaconRequestHandler.java @@ -111,8 +111,9 @@ public class BeaconRequestHandler implements HttpHandler { return; } catch (BeaconServerException serverException) { var cause = serverException.getCause() != null ? serverException.getCause() : serverException; - ErrorEvent.fromThrowable(cause).omit().handle(); - writeError(exchange, new BeaconServerErrorResponse(cause), 500); + var event = ErrorEvent.fromThrowable(cause).omit().handle(); + var link = event.getDocumentationLink() != null ? event.getDocumentationLink().getLink() : null; + writeError(exchange, new BeaconServerErrorResponse(cause, link), 500); return; } catch (IOException ex) { // Handle serialization errors as normal exceptions and other IO exceptions as assuming that the connection @@ -132,8 +133,9 @@ public class BeaconRequestHandler implements HttpHandler { } return; } catch (Throwable other) { - ErrorEvent.fromThrowable(other).omit().expected().handle(); - writeError(exchange, new BeaconServerErrorResponse(other), 500); + var event = ErrorEvent.fromThrowable(other).omit().expected().handle(); + var link = event.getDocumentationLink() != null ? event.getDocumentationLink().getLink() : null; + writeError(exchange, new BeaconServerErrorResponse(other, link), 500); return; } @@ -160,8 +162,9 @@ public class BeaconRequestHandler implements HttpHandler { ErrorEvent.fromThrowable(ioException).omit().expected().handle(); } } catch (Throwable other) { - ErrorEvent.fromThrowable(other).handle(); - writeError(exchange, new BeaconServerErrorResponse(other), 500); + var event = ErrorEvent.fromThrowable(other).handle(); + var link = event.getDocumentationLink() != null ? event.getDocumentationLink().getLink() : null; + writeError(exchange, new BeaconServerErrorResponse(other, link), 500); } } diff --git a/app/src/main/java/io/xpipe/app/comp/base/ModalOverlayComp.java b/app/src/main/java/io/xpipe/app/comp/base/ModalOverlayComp.java index 681431fff..7f4b3eb38 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/ModalOverlayComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/ModalOverlayComp.java @@ -200,7 +200,7 @@ public class ModalOverlayComp extends SimpleComp { } } content.getChildren().add(buttonBar); - AppFontSizes.base(buttonBar); + AppFontSizes.sm(buttonBar); } var modalBox = new ModalBox(pane, content) { diff --git a/app/src/main/java/io/xpipe/app/issue/ErrorEvent.java b/app/src/main/java/io/xpipe/app/issue/ErrorEvent.java index abbb79419..6c329fae8 100644 --- a/app/src/main/java/io/xpipe/app/issue/ErrorEvent.java +++ b/app/src/main/java/io/xpipe/app/issue/ErrorEvent.java @@ -162,8 +162,20 @@ public class ErrorEvent { return omit().expected(); } - public void handle() { - build().handle(); + public ErrorEvent handle() { + var event = build(); + event.handle(); + return event; + } + + public void expectedIfContains(String... s) { + var contains = throwable != null && throwable.getMessage() != null + && Arrays.stream(s).map(String::toLowerCase).anyMatch(string -> throwable.getMessage() + .toLowerCase(Locale.ROOT) + .endsWith(string)); + if (contains) { + expected(); + } } } } diff --git a/app/src/main/java/io/xpipe/app/issue/ErrorHandlerComp.java b/app/src/main/java/io/xpipe/app/issue/ErrorHandlerComp.java index f5b299127..47e4e4bc1 100644 --- a/app/src/main/java/io/xpipe/app/issue/ErrorHandlerComp.java +++ b/app/src/main/java/io/xpipe/app/issue/ErrorHandlerComp.java @@ -98,6 +98,7 @@ public class ErrorHandlerComp extends SimpleComp { var top = createTop(); var content = new VBox(top); var header = new Label(AppI18n.get("possibleActions")); + header.setPadding(new Insets(0, 0, 2, 3)); AppFontSizes.xl(header); var actionBox = new VBox(); actionBox.getStyleClass().add("actions"); diff --git a/beacon/src/main/java/io/xpipe/beacon/BeaconServerErrorResponse.java b/beacon/src/main/java/io/xpipe/beacon/BeaconServerErrorResponse.java index 28f634504..7d72af493 100644 --- a/beacon/src/main/java/io/xpipe/beacon/BeaconServerErrorResponse.java +++ b/beacon/src/main/java/io/xpipe/beacon/BeaconServerErrorResponse.java @@ -5,6 +5,8 @@ import lombok.Builder; import lombok.Value; import lombok.extern.jackson.Jacksonized; +import java.util.List; + @SuppressWarnings("ClassCanBeRecord") @Value @Builder @@ -13,8 +15,13 @@ import lombok.extern.jackson.Jacksonized; public class BeaconServerErrorResponse { Throwable error; + String documentationLink; public void throwError() throws BeaconServerException { - throw new BeaconServerException(error.getMessage(), error); + var message = error.getMessage(); + if (documentationLink != null) { + message = message + "\n\nFor more information, see: " + documentationLink; + } + throw new BeaconServerException(message, error); } } diff --git a/core/src/main/java/io/xpipe/core/process/ShellView.java b/core/src/main/java/io/xpipe/core/process/ShellView.java index 26ecb9751..36d874228 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellView.java +++ b/core/src/main/java/io/xpipe/core/process/ShellView.java @@ -17,6 +17,17 @@ public class ShellView { return shellControl.getShellDialect(); } + public FilePath writeTempTextFileDeterministic(String fileName, String text) throws Exception { + var hash = Math.abs(text.hashCode()); + var f = FilePath.of(fileName); + var target = FilePath.of(f.getBaseName().toString() + "-" + hash + f.getExtension()); + if (fileExists(target)) { + return target; + } + writeTextFile(target, text); + return target; + } + public byte[] readRawFile(FilePath path) throws Exception { var s = getDialect().getFileReadCommand(shellControl, path.toString()).readRawBytesOrThrow(); return s; diff --git a/dist/changelogs/16.0.md b/dist/changelogs/16.0.md index 9004196ad..e4e8742f1 100644 --- a/dist/changelogs/16.0.md +++ b/dist/changelogs/16.0.md @@ -4,25 +4,20 @@ XPipe 15 comes with many new features, performance improvements, and many bug fi You can now connect to devices in your tailnet via Tailscale SSH and your locally installed tailscale command-line client. This integration supports multiple accounts as well to switch between different tailnets. -## Custom icons +## Docker improvements -You can now add custom icons to use for your connections. This implementation replaces the old model of shipping the icons from https://github.com/selfhst/icons along XPipe. Instead, you can now dynamically add sources of icons. This can either be a local directory or a remote git repository that can be cloned and pulled by xpipe. XPipe will pick up any .svg files in there, rasterize them to cached .pngs, and display them in XPipe. As default icon sources, it will still come with https://github.com/selfhst/icons, but now it can fetch these icons at runtime. If you are using the git vault, you can also add icons to a synced directory in your git vault to have access to them on all systems. +This release introduces support for docker compose. Containers in compose projects are grouped together and can be managed all at the same time via the compose project entry. -Your existing custom icons set for connections are not lost, it just requires you to first update the icons and then restart XPipe. - -## Package manager repositories - -There is now an apt repository available at https://apt.xpipe.io and an rpm repository available at https://rpm.xpipe.io. You can add them as sources to apt or your rpm-based package manager. This allows you to also install and upgrade xpipe via your native package manager instead of using the built-in self-updater. - -## New docs - -There is a new documentation site https://docs.xpipe.io. The goal is to expand this over time to provide proper documentation for many features. If you're looking for documentation for a certain feature, let me know. +The container state information shown was also improved, always showing the container state in combination with the system information. ## Other +- The SSH gateway implementation has been reworked so that you can now use local SSH keys and other identities for connections with gateways +- Various speed improvements for shell operations - Add setting to disable HTTPs TLS verification for license activation API calls for cases where TLS traffic is decrypted in your organization ## Fixes +- Fixed application restart after update not applying current workspace directory - Fix custom service commands not launching properly with PowerShell as the local shell - Fix update check being influenced by the local GitHub rate limiting diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/LocalIdentityStoreProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/LocalIdentityStoreProvider.java index 9e04b396f..932c2e65a 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/LocalIdentityStoreProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/LocalIdentityStoreProvider.java @@ -2,6 +2,7 @@ package io.xpipe.ext.base.identity; import io.xpipe.app.comp.store.StoreEntryWrapper; import io.xpipe.app.core.AppI18n; +import io.xpipe.app.ext.DataStoreCreationCategory; import io.xpipe.app.ext.GuiDialog; import io.xpipe.app.storage.*; import io.xpipe.app.util.EncryptedValue; diff --git a/gradle/gradle_scripts/atlantafx-base-2.0.2.jar b/gradle/gradle_scripts/atlantafx-base-2.0.2.jar index 264de45e1..d4402176d 100644 Binary files a/gradle/gradle_scripts/atlantafx-base-2.0.2.jar and b/gradle/gradle_scripts/atlantafx-base-2.0.2.jar differ diff --git a/img/os/cachy.svg b/img/os/cachy.svg new file mode 100644 index 000000000..7dd83848f --- /dev/null +++ b/img/os/cachy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/lang/strings/translations_en.properties b/lang/strings/translations_en.properties index 7467581df..13fcfe0b0 100644 --- a/lang/strings/translations_en.properties +++ b/lang/strings/translations_en.properties @@ -140,7 +140,8 @@ clipboardActionDetectedTitle=Clipboard Action detected clipboardActionDetectedContent=XPipe detected content in your clipboard that can be opened. Do you want to open it now? Do you want to import your clipboard content? install=Install ... ignore=Ignore -possibleActions=Possible actions +#force +possibleActions=Available actions reportError=Report error reportOnGithub=Create an issue report on GitHub reportOnGithubDescription=Open a new issue in the GitHub repository