diff --git a/app/src/main/java/io/xpipe/app/core/check/AppJavaOptionsCheck.java b/app/src/main/java/io/xpipe/app/core/check/AppJavaOptionsCheck.java index 870ef3197..cf69ea529 100644 --- a/app/src/main/java/io/xpipe/app/core/check/AppJavaOptionsCheck.java +++ b/app/src/main/java/io/xpipe/app/core/check/AppJavaOptionsCheck.java @@ -20,7 +20,6 @@ public class AppJavaOptionsCheck { .formatted(env) + " This will forcefully apply all custom JVM options to XPipe and can cause a variety of different issues." + " Please remove this global environment variable and use local configuration instead for your other JVM programs.") - .noDefaultActions() .expected() .handle(); AppCache.update("javaOptionsWarningShown", true); diff --git a/app/src/main/java/io/xpipe/app/core/check/AppRosettaCheck.java b/app/src/main/java/io/xpipe/app/core/check/AppRosettaCheck.java index c97b14478..1e8185f69 100644 --- a/app/src/main/java/io/xpipe/app/core/check/AppRosettaCheck.java +++ b/app/src/main/java/io/xpipe/app/core/check/AppRosettaCheck.java @@ -2,6 +2,7 @@ package io.xpipe.app.core.check; import io.xpipe.app.core.AppProperties; import io.xpipe.app.issue.ErrorEvent; +import io.xpipe.app.util.DocumentationLink; import io.xpipe.app.util.LocalShell; import io.xpipe.core.process.OsType; @@ -27,7 +28,7 @@ public class AppRosettaCheck { ErrorEvent.fromMessage("You are running the Intel version of XPipe on an Apple Silicon system." + " There is a native build available that comes with much better performance." + " Please install that one instead.") - .noDefaultActions() + .documentationLink(DocumentationLink.MACOS_SETUP) .expected() .handle(); } diff --git a/app/src/main/java/io/xpipe/app/ext/ConnectionFileSystem.java b/app/src/main/java/io/xpipe/app/ext/ConnectionFileSystem.java index 35e1424c0..c59d622cb 100644 --- a/app/src/main/java/io/xpipe/app/ext/ConnectionFileSystem.java +++ b/app/src/main/java/io/xpipe/app/ext/ConnectionFileSystem.java @@ -1,6 +1,7 @@ package io.xpipe.app.ext; import io.xpipe.app.issue.ErrorEvent; +import io.xpipe.app.util.DocumentationLink; import io.xpipe.app.util.Hyperlinks; import io.xpipe.core.process.CommandBuilder; import io.xpipe.core.process.ShellControl; @@ -57,9 +58,11 @@ public class ConnectionFileSystem implements FileSystem { if (!shellControl.getTtyState().isPreservesOutput() || !shellControl.getTtyState().isSupportsInput()) { - throw ErrorEvent.expected(new UnsupportedOperationException( - "Shell has a PTY allocated and as a result does not support file system operations. For more information see " - + Hyperlinks.DOCS_TTY)); + var ex = new UnsupportedOperationException( + "Shell has a PTY allocated and as a result does not support file system operations."); + ErrorEvent.preconfigure(ErrorEvent.fromThrowable(ex) + .documentationLink(DocumentationLink.TTY)); + throw ex; } shellControl.checkLicenseOrThrow(); diff --git a/app/src/main/java/io/xpipe/app/issue/ErrorAction.java b/app/src/main/java/io/xpipe/app/issue/ErrorAction.java index b10c89f9d..58459534a 100644 --- a/app/src/main/java/io/xpipe/app/issue/ErrorAction.java +++ b/app/src/main/java/io/xpipe/app/issue/ErrorAction.java @@ -1,11 +1,12 @@ package io.xpipe.app.issue; import io.xpipe.app.core.AppI18n; +import io.xpipe.app.util.DocumentationLink; import io.xpipe.app.util.Hyperlinks; public interface ErrorAction { - static ErrorAction openDocumentation(String link) { + static ErrorAction openDocumentation(DocumentationLink link) { return new ErrorAction() { @Override public String getName() { @@ -19,53 +20,12 @@ public interface ErrorAction { @Override public boolean handle(ErrorEvent event) { - Hyperlinks.open(link); + link.open(); return false; } }; } - static ErrorAction reportOnGithub() { - return new ErrorAction() { - @Override - public String getName() { - return AppI18n.get("reportOnGithub"); - } - - @Override - public String getDescription() { - return AppI18n.get("reportOnGithubDescription"); - } - - @Override - public boolean handle(ErrorEvent event) { - var url = "https://github.com/xpipe-io/xpipe/issues/new"; - Hyperlinks.open(url); - return false; - } - }; - } - - static ErrorAction automaticallyReport() { - return new ErrorAction() { - @Override - public String getName() { - return AppI18n.get("reportError"); - } - - @Override - public String getDescription() { - return AppI18n.get("reportErrorDescription"); - } - - @Override - public boolean handle(ErrorEvent event) { - UserReportComp.show(event); - return true; - } - }; - } - static ErrorAction ignore() { return new ErrorAction() { @Override 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 21eb7d0ab..abbb79419 100644 --- a/app/src/main/java/io/xpipe/app/issue/ErrorEvent.java +++ b/app/src/main/java/io/xpipe/app/issue/ErrorEvent.java @@ -1,5 +1,6 @@ package io.xpipe.app.issue; +import io.xpipe.app.util.DocumentationLink; import lombok.Builder; import lombok.Getter; import lombok.Setter; @@ -23,9 +24,6 @@ public class ErrorEvent { @Builder.Default private final boolean reportable = true; - @Setter - private boolean disableDefaultActions; - private final Throwable throwable; @Singular @@ -43,6 +41,8 @@ public class ErrorEvent { @Singular private List attachments; + private DocumentationLink documentationLink; + private String email; private String userReport; private boolean unhandled; @@ -162,10 +162,6 @@ public class ErrorEvent { return omit().expected(); } - public ErrorEventBuilder noDefaultActions() { - return disableDefaultActions(true); - } - public void handle() { build().handle(); } 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 7bdf3b0f5..f5b299127 100644 --- a/app/src/main/java/io/xpipe/app/issue/ErrorHandlerComp.java +++ b/app/src/main/java/io/xpipe/app/issue/ErrorHandlerComp.java @@ -66,22 +66,6 @@ public class ErrorHandlerComp extends SimpleComp { return b.createRegion(); } - private Region createDetails() { - var content = new ErrorDetailsComp(event).prefWidth(600).prefHeight(750); - var modal = ModalOverlay.of("errorDetails", content); - var button = new ButtonComp( - null, - new SimpleObjectProperty<>(new LabelGraphic.NodeGraphic(() -> { - return createActionButtonGraphic(AppI18n.get("showDetails"), AppI18n.get("showDetailsDescription")); - })), - () -> { - modal.show(); - }); - var r = button.grow(true, false).createRegion(); - r.getStyleClass().add("details"); - return r; - } - private Region createTop() { var desc = event.getDescription(); if (desc == null && event.getThrowable() != null) { @@ -112,10 +96,10 @@ public class ErrorHandlerComp extends SimpleComp { @Override protected Region createSimple() { var top = createTop(); - var content = new VBox(top, new Separator(Orientation.HORIZONTAL)); + var content = new VBox(top); var header = new Label(AppI18n.get("possibleActions")); AppFontSizes.xl(header); - var actionBox = new VBox(header); + var actionBox = new VBox(); actionBox.getStyleClass().add("actions"); actionBox.setFillWidth(true); @@ -137,7 +121,6 @@ public class ErrorHandlerComp extends SimpleComp { return true; } }); - event.setDisableDefaultActions(true); } var custom = event.getCustomActions(); @@ -147,21 +130,17 @@ public class ErrorHandlerComp extends SimpleComp { actionBox.getChildren().add(ac); } - if (!event.isDisableDefaultActions()) { - for (var action : - List.of(ErrorAction.automaticallyReport(), ErrorAction.reportOnGithub(), ErrorAction.ignore())) { - var ac = createActionComp(action); - actionBox.getChildren().add(ac); - } - } else if (event.getCustomActions().isEmpty()) { - for (var action : List.of(ErrorAction.ignore())) { - var ac = createActionComp(action); - actionBox.getChildren().add(ac); - } + if (event.getDocumentationLink() != null) { + actionBox.getChildren().add(createActionComp(ErrorAction.openDocumentation(event.getDocumentationLink()))); + } + + if (actionBox.getChildren().size() > 0) { + actionBox.getChildren().addFirst(header); + content.getChildren().add(new Separator(Orientation.HORIZONTAL)); + actionBox.getChildren().get(1).getStyleClass().addAll(BUTTON_OUTLINED); + content.getChildren().addAll(actionBox); } - actionBox.getChildren().get(1).getStyleClass().addAll(BUTTON_OUTLINED, ACCENT); - content.getChildren().addAll(actionBox); content.getStyleClass().add("top"); content.setFillWidth(true); content.setMinHeight(Region.USE_PREF_SIZE); @@ -170,13 +149,6 @@ public class ErrorHandlerComp extends SimpleComp { layout.getChildren().add(content); layout.getStyleClass().add("error-handler-comp"); - if (event.getThrowable() != null) { - content.getChildren().add(new Separator(Orientation.HORIZONTAL)); - var details = createDetails(); - layout.getChildren().add(details); - layout.prefHeightProperty().bind(content.heightProperty().add(65).add(details.prefHeightProperty())); - } - return layout; } } diff --git a/app/src/main/java/io/xpipe/app/issue/ErrorHandlerDialog.java b/app/src/main/java/io/xpipe/app/issue/ErrorHandlerDialog.java index 533d8786b..d55cca013 100644 --- a/app/src/main/java/io/xpipe/app/issue/ErrorHandlerDialog.java +++ b/app/src/main/java/io/xpipe/app/issue/ErrorHandlerDialog.java @@ -1,5 +1,6 @@ package io.xpipe.app.issue; +import io.xpipe.app.comp.base.ModalButton; import io.xpipe.app.comp.base.ModalOverlay; import io.xpipe.app.core.mode.OperationMode; import io.xpipe.app.core.window.AppDialog; @@ -44,13 +45,25 @@ public class ErrorHandlerDialog { var comp = new ErrorHandlerComp(event, () -> { AppDialog.closeDialog(modal.get()); }); - comp.prefWidth(550); + comp.prefWidth(500); var headerId = event.isTerminal() ? "terminalErrorOccured" : "errorOccured"; - modal.set(ModalOverlay.of(headerId, comp, new LabelGraphic.NodeGraphic(() -> { + var errorModal = ModalOverlay.of(headerId, comp, new LabelGraphic.NodeGraphic(() -> { var graphic = new FontIcon("mdomz-warning"); graphic.setIconColor(Color.RED); return graphic; - }))); + })); + if (event.getThrowable() != null && event.isReportable()) { + errorModal.addButton(new ModalButton("stackTrace", () -> { + var content = new ErrorDetailsComp(event).prefWidth(600).prefHeight(750); + var detailsModal = ModalOverlay.of("errorDetails", content); + detailsModal.show(); + }, false, false)); + } + errorModal.addButton(new ModalButton("report", () -> { + UserReportComp.show(event); + }, false, false)); + errorModal.addButton(ModalButton.ok()); + modal.set(errorModal); AppDialog.showAndWait(modal.get()); if (comp.getTakenAction().getValue() == null) { ErrorAction.ignore().handle(event); diff --git a/app/src/main/java/io/xpipe/app/prefs/AboutCategory.java b/app/src/main/java/io/xpipe/app/prefs/AboutCategory.java index 4d443a753..a35e0f7e5 100644 --- a/app/src/main/java/io/xpipe/app/prefs/AboutCategory.java +++ b/app/src/main/java/io/xpipe/app/prefs/AboutCategory.java @@ -8,6 +8,7 @@ import io.xpipe.app.comp.base.VerticalComp; import io.xpipe.app.core.AppDistributionType; import io.xpipe.app.core.AppI18n; import io.xpipe.app.core.AppProperties; +import io.xpipe.app.util.DocumentationLink; import io.xpipe.app.util.Hyperlinks; import io.xpipe.app.util.JfxHelper; import io.xpipe.app.util.OptionsBuilder; @@ -48,7 +49,7 @@ public class AboutCategory extends AppPrefsCategory { null) .addComp( new TileButtonComp("privacy", "privacyDescription", "mdomz-privacy_tip", e -> { - Hyperlinks.open(Hyperlinks.DOCS_PRIVACY); + DocumentationLink.PRIVACY.open(); e.consume(); }) .grow(true, false), @@ -64,7 +65,7 @@ public class AboutCategory extends AppPrefsCategory { .grow(true, false)) .addComp( new TileButtonComp("eula", "eulaDescription", "mdi2c-card-text-outline", e -> { - Hyperlinks.open(Hyperlinks.DOCS_EULA); + DocumentationLink.EULA.open(); e.consume(); }) .grow(true, false), diff --git a/app/src/main/java/io/xpipe/app/prefs/SyncCategory.java b/app/src/main/java/io/xpipe/app/prefs/SyncCategory.java index a105f7a80..c7c5fcea8 100644 --- a/app/src/main/java/io/xpipe/app/prefs/SyncCategory.java +++ b/app/src/main/java/io/xpipe/app/prefs/SyncCategory.java @@ -4,6 +4,7 @@ import io.xpipe.app.comp.Comp; import io.xpipe.app.comp.base.*; import io.xpipe.app.core.AppI18n; import io.xpipe.app.storage.DataStorageSyncHandler; +import io.xpipe.app.util.DocumentationLink; import io.xpipe.app.util.Hyperlinks; import io.xpipe.app.util.OptionsBuilder; import io.xpipe.app.util.ThreadHelper; @@ -53,7 +54,7 @@ public class SyncCategory extends AppPrefsCategory { var remoteRepo = new TextFieldComp(prefs.storageGitRemote).hgrow(); var helpButton = new ButtonComp(AppI18n.observable("help"), new FontIcon("mdi2h-help-circle-outline"), () -> { - Hyperlinks.open(Hyperlinks.DOCS_SYNC); + DocumentationLink.SYNC.open(); }); var remoteRow = new HorizontalComp(List.of(remoteRepo, helpButton)).spacing(10); remoteRow.apply(struc -> struc.get().setAlignment(Pos.CENTER_LEFT)); diff --git a/app/src/main/java/io/xpipe/app/update/WebtopUpdater.java b/app/src/main/java/io/xpipe/app/update/WebtopUpdater.java index fa51c3722..3d8582178 100644 --- a/app/src/main/java/io/xpipe/app/update/WebtopUpdater.java +++ b/app/src/main/java/io/xpipe/app/update/WebtopUpdater.java @@ -1,6 +1,7 @@ package io.xpipe.app.update; import io.xpipe.app.comp.base.ModalButton; +import io.xpipe.app.util.DocumentationLink; import io.xpipe.app.util.Hyperlinks; import java.util.List; @@ -21,7 +22,7 @@ public class WebtopUpdater extends PortableUpdater { return; } - Hyperlinks.open(Hyperlinks.DOCS_WEBTOP_UPDATE); + DocumentationLink.WEBTOP_UPDATE.open(); }, false, false)); diff --git a/app/src/main/java/io/xpipe/app/util/AskpassAlert.java b/app/src/main/java/io/xpipe/app/util/AskpassAlert.java index 2854c7752..3d99ba5ba 100644 --- a/app/src/main/java/io/xpipe/app/util/AskpassAlert.java +++ b/app/src/main/java/io/xpipe/app/util/AskpassAlert.java @@ -17,6 +17,8 @@ import javafx.stage.Modality; import javafx.stage.Stage; import javafx.stage.Window; +import javax.print.Doc; + public class AskpassAlert { public static SecretQueryResult queryRaw(String prompt, InPlaceSecretValue secretValue) { @@ -33,7 +35,7 @@ public class AskpassAlert { alert.getButtonTypes().add(type); var button = alert.getDialogPane().lookupButton(type); button.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> { - Hyperlinks.open(Hyperlinks.DOCS_DOUBLE_PROMPT); + DocumentationLink.DOUBLE_PROMPT.open(); event.consume(); }); } diff --git a/app/src/main/java/io/xpipe/app/util/DocumentationLink.java b/app/src/main/java/io/xpipe/app/util/DocumentationLink.java new file mode 100644 index 000000000..38dcf5a6f --- /dev/null +++ b/app/src/main/java/io/xpipe/app/util/DocumentationLink.java @@ -0,0 +1,32 @@ +package io.xpipe.app.util; + +import lombok.Value; + +public enum DocumentationLink { + + INDEX(""), + TTY("troubleshoot/tty"), + MACOS_SETUP("guide/installation#macos"), + SSH_AGENT("troubleshoot/ssh-agent-socket"), + DOUBLE_PROMPT("troubleshoot/two-step-connections"), + LICENSE_ACTIVATION("troubleshoot/license-activation"), + PRIVACY("legal/privacy"), + EULA("legal/eula"), + WEBTOP_UPDATE("guide/webtop#updating"), + SYNC("guide/sync"), + SCRIPTING("guide/scripting"), + SSH("guide/ssh"); + + + private final String page; + + DocumentationLink(String page) {this.page = page;} + + public void open() { + Hyperlinks.open("https://docs.xpipe.io/" + page); + } + + public String getLink() { + return "https://docs.xpipe.io/" + page; + } +} diff --git a/app/src/main/java/io/xpipe/app/util/Hyperlinks.java b/app/src/main/java/io/xpipe/app/util/Hyperlinks.java index 9c25c5561..d398a4e02 100644 --- a/app/src/main/java/io/xpipe/app/util/Hyperlinks.java +++ b/app/src/main/java/io/xpipe/app/util/Hyperlinks.java @@ -5,16 +5,6 @@ import io.xpipe.app.issue.ErrorEvent; public class Hyperlinks { public static final String DOCS = "https://docs.xpipe.io"; - public static final String DOCS_DOUBLE_PROMPT = "https://docs.xpipe.io/troubleshoot/two-step-connections"; - public static final String DOCS_AGENT_SETUP = "https://docs.xpipe.io/troubleshoot/ssh-agent-socket"; - public static final String DOCS_TTY = "https://docs.xpipe.io/troubleshoot/tty"; - public static final String DOCS_LICENSE_ACTIVATION = "https://docs.xpipe.io/troubleshoot/license-activation"; - public static final String DOCS_PRIVACY = "https://docs.xpipe.io/legal/privacy-policy"; - public static final String DOCS_EULA = "https://docs.xpipe.io/legal/end-user-license-agreement"; - public static final String DOCS_SECURITY = "https://docs.xpipe.io/reference/security"; - public static final String DOCS_WEBTOP_UPDATE = "https://docs.xpipe.io/guide/webtop#updating"; - public static final String DOCS_SYNC = "https://docs.xpipe.io/guide/sync"; - public static final String DOCS_SCRIPTING = "https://docs.xpipe.io/guide/scriptin"; public static final String GITHUB = "https://github.com/xpipe-io/xpipe"; public static final String GITHUB_PTB = "https://github.com/xpipe-io/xpipe-ptb"; public static final String GITHUB_LATEST = "https://github.com/xpipe-io/xpipe/releases/latest"; diff --git a/app/src/main/resources/io/xpipe/app/resources/style/error-handler-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/error-handler-comp.css index 13098a9bd..73f2ad9b5 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/error-handler-comp.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/error-handler-comp.css @@ -1,5 +1,5 @@ .error-handler-comp .top { - -fx-spacing: 1em; + -fx-spacing: 0.7em; } .error-handler-comp .actions { diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/SshIdentityStateManager.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/SshIdentityStateManager.java index 37d89a08e..2b33fa238 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/SshIdentityStateManager.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/SshIdentityStateManager.java @@ -3,6 +3,7 @@ package io.xpipe.ext.base.identity; import io.xpipe.app.issue.ErrorAction; import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.util.CommandSupport; +import io.xpipe.app.util.DocumentationLink; import io.xpipe.app.util.Hyperlinks; import io.xpipe.core.process.CommandBuilder; import io.xpipe.core.process.OsType; @@ -73,7 +74,7 @@ public class SshIdentityStateManager { return true; } }; - event.customAction(shutdown).noDefaultActions().handle(); + event.customAction(shutdown).handle(); if (r.get()) { if (sc.getShellDialect().equals(ShellDialects.CMD)) { @@ -146,7 +147,7 @@ public class SshIdentityStateManager { var r = c.readStdoutAndStderr(); if (c.getExitCode() != 0) { var posixMessage = sc.getOsType() != OsType.WINDOWS - ? " and the SSH_AUTH_SOCK variable. See " + Hyperlinks.DOCS_AGENT_SETUP + " for details" + ? " and the SSH_AUTH_SOCK variable." : ""; var ex = new IllegalStateException("Unable to list agent identities via command ssh-add -l:\n" + r[0] @@ -154,9 +155,8 @@ public class SshIdentityStateManager { + r[1] + "\nPlease check your SSH agent CLI configuration%s.".formatted(posixMessage)); ErrorEvent.preconfigure(ErrorEvent.fromThrowable(ex) - .noDefaultActions() .expected() - .customAction(ErrorAction.openDocumentation(Hyperlinks.DOCS_AGENT_SETUP))); + .documentationLink(DocumentationLink.SSH_AGENT)); throw ex; } } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStoreProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStoreProvider.java index a9f445709..c6e2bc574 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStoreProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStoreProvider.java @@ -11,10 +11,7 @@ import io.xpipe.app.ext.DataStoreProvider; import io.xpipe.app.ext.EnabledParentStoreProvider; import io.xpipe.app.ext.GuiDialog; import io.xpipe.app.storage.DataStoreEntry; -import io.xpipe.app.util.DataStoreFormatter; -import io.xpipe.app.util.Hyperlinks; -import io.xpipe.app.util.OptionsBuilder; -import io.xpipe.app.util.Validator; +import io.xpipe.app.util.*; import io.xpipe.core.process.ShellDialect; import io.xpipe.core.process.ShellDialects; import io.xpipe.core.store.DataStore; @@ -36,7 +33,7 @@ public class SimpleScriptStoreProvider implements EnabledParentStoreProvider, Da @Override public String getHelpLink() { - return Hyperlinks.DOCS_SCRIPTING; + return DocumentationLink.SCRIPTING.getLink(); } @Override diff --git a/lang/strings/translations_en.properties b/lang/strings/translations_en.properties index f0a5c0408..57f232ed7 100644 --- a/lang/strings/translations_en.properties +++ b/lang/strings/translations_en.properties @@ -1355,3 +1355,5 @@ connectionsSelected=$NUMBER$ connections selected addConnections=Add connections browseDirectory=Browse directory openTerminal=Open terminal +documentation=Documentation +report=Report error