diff --git a/app/src/main/java/io/xpipe/app/comp/base/IntegratedTextAreaComp.java b/app/src/main/java/io/xpipe/app/comp/base/IntegratedTextAreaComp.java index 0064438b7..b6c1b3908 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/IntegratedTextAreaComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/IntegratedTextAreaComp.java @@ -7,6 +7,7 @@ import io.xpipe.app.fxcomps.impl.TextAreaComp; import io.xpipe.app.util.FileOpener; import javafx.application.Platform; import javafx.beans.property.Property; +import javafx.beans.value.ObservableValue; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; @@ -18,9 +19,10 @@ public class IntegratedTextAreaComp extends SimpleComp { private final Property value; private final boolean lazy; private final String identifier; - private final String fileType; + private final ObservableValue fileType; - public IntegratedTextAreaComp(Property value, boolean lazy, String identifier, String fileType) { + public IntegratedTextAreaComp( + Property value, boolean lazy, String identifier, ObservableValue fileType) { this.value = value; this.lazy = lazy; this.identifier = identifier; @@ -47,12 +49,15 @@ public class IntegratedTextAreaComp extends SimpleComp { } private Region createOpenButton(Region container) { - var name = identifier + (fileType != null ? "." + fileType : ""); var button = new IconButtonComp( "mdal-edit", - () -> FileOpener.openString(name, this, value.getValue(), (s) -> { - Platform.runLater(() -> value.setValue(s)); - })) + () -> FileOpener.openString( + identifier + (fileType.getValue() != null ? "." + fileType.getValue() : ""), + this, + value.getValue(), + (s) -> { + Platform.runLater(() -> value.setValue(s)); + })) .createRegion(); return button; } diff --git a/app/src/main/java/io/xpipe/app/comp/storage/store/DenseStoreEntryComp.java b/app/src/main/java/io/xpipe/app/comp/storage/store/DenseStoreEntryComp.java index c5230dc37..af74ace7d 100644 --- a/app/src/main/java/io/xpipe/app/comp/storage/store/DenseStoreEntryComp.java +++ b/app/src/main/java/io/xpipe/app/comp/storage/store/DenseStoreEntryComp.java @@ -23,8 +23,8 @@ public class DenseStoreEntryComp extends StoreEntryComp { grid.setHgap(8); if (showIcon) { - var storeIcon = createIcon(30, 25); - grid.getColumnConstraints().add(new ColumnConstraints(30)); + var storeIcon = createIcon(26, 21); + grid.getColumnConstraints().add(new ColumnConstraints(26)); grid.add(storeIcon, 0, 0); GridPane.setHalignment(storeIcon, HPos.CENTER); } else { diff --git a/app/src/main/java/io/xpipe/app/comp/storage/store/StandardStoreEntryComp.java b/app/src/main/java/io/xpipe/app/comp/storage/store/StandardStoreEntryComp.java index b8a8625e8..e89099112 100644 --- a/app/src/main/java/io/xpipe/app/comp/storage/store/StandardStoreEntryComp.java +++ b/app/src/main/java/io/xpipe/app/comp/storage/store/StandardStoreEntryComp.java @@ -17,10 +17,10 @@ public class StandardStoreEntryComp extends StoreEntryComp { var name = createName().createRegion(); var grid = new GridPane(); - grid.setHgap(10); + grid.setHgap(7); grid.setVgap(0); - var storeIcon = createIcon(45, 35); + var storeIcon = createIcon(50, 39); grid.add(storeIcon, 0, 0, 1, 2); grid.getColumnConstraints().add(new ColumnConstraints(50)); diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/ChoicePaneComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/ChoicePaneComp.java index 0f34be519..f1cf85915 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/ChoicePaneComp.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/ChoicePaneComp.java @@ -1,6 +1,5 @@ package io.xpipe.app.fxcomps.impl; -import io.xpipe.app.core.AppI18n; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.CompStructure; import io.xpipe.app.fxcomps.SimpleCompStructure; @@ -37,8 +36,8 @@ public class ChoicePaneComp extends Comp> { cb.setConverter(new StringConverter<>() { @Override public String toString(Entry object) { - if (object == null) { - return AppI18n.get("app.none"); + if (object == null || object.name() == null) { + return ""; } return object.name().getValue(); @@ -53,9 +52,11 @@ public class ChoicePaneComp extends Comp> { var vbox = new VBox(transformer.apply(cb)); vbox.setFillWidth(true); cb.prefWidthProperty().bind(vbox.widthProperty()); - SimpleChangeListener.apply(cb.valueProperty(), n-> { + SimpleChangeListener.apply(cb.valueProperty(), n -> { if (n == null) { - vbox.getChildren().remove(1); + if (vbox.getChildren().size() > 1) { + vbox.getChildren().remove(1); + } } else { var region = n.comp().createRegion(); region.setPadding(new Insets(0, 0, 0, 10)); diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/SvgView.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/SvgView.java index 8b4aa1ba2..d51f89b2d 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/SvgView.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/SvgView.java @@ -93,7 +93,7 @@ public class SvgView { private WebView createWebView() { var wv = new WebView(); - // Sometimes a web view might not render when the background is said to transparent, at least according to stack + // Sometimes a web view might not render when the background is set to transparent, at least according to stack // overflow wv.setPageFill(Color.valueOf("#00000001")); // wv.setPageFill(Color.BLACK); diff --git a/app/src/main/java/io/xpipe/app/util/JfxHelper.java b/app/src/main/java/io/xpipe/app/util/JfxHelper.java index 6c60f2fb2..fe58f6b9d 100644 --- a/app/src/main/java/io/xpipe/app/util/JfxHelper.java +++ b/app/src/main/java/io/xpipe/app/util/JfxHelper.java @@ -69,12 +69,12 @@ public class JfxHelper { return text; } - var size = AppFont.getPixelSize(1) + AppFont.getPixelSize(-2) + 2; + var size = AppFont.getPixelSize(1) + AppFont.getPixelSize(-2) + 8; var graphic = new PrettyImageComp(new SimpleStringProperty(image), size, size).createRegion(); var hbox = new HBox(graphic, text); hbox.setAlignment(Pos.CENTER_LEFT); - hbox.setSpacing(8); + hbox.setSpacing(10); // graphic.fitWidthProperty().bind(Bindings.createDoubleBinding(() -> header.getHeight() + // desc.getHeight() + 2, diff --git a/app/src/main/java/io/xpipe/app/util/OptionsBuilder.java b/app/src/main/java/io/xpipe/app/util/OptionsBuilder.java index ac27b4df7..1722ccda3 100644 --- a/app/src/main/java/io/xpipe/app/util/OptionsBuilder.java +++ b/app/src/main/java/io/xpipe/app/util/OptionsBuilder.java @@ -48,11 +48,11 @@ public class OptionsBuilder { public OptionsBuilder choice(IntegerProperty selectedIndex, Map options) { var list = options.entrySet().stream() .map(e -> new ChoicePaneComp.Entry( - AppI18n.observable(e.getKey()), e.getValue().buildComp())) + AppI18n.observable(e.getKey()), e.getValue() != null ? e.getValue().buildComp() : Comp.empty())) .toList(); var validatorList = - options.values().stream().map(builder -> builder.buildEffectiveValidator()).toList(); - var selected = new SimpleObjectProperty<>(list.get(selectedIndex.getValue())); + options.values().stream().map(builder -> builder != null ? builder.buildEffectiveValidator() : new SimpleValidator()).toList(); + var selected = new SimpleObjectProperty<>(selectedIndex.getValue() != -1 ? list.get(selectedIndex.getValue()) : null); selected.addListener((observable, oldValue, newValue) -> { selectedIndex.setValue(newValue != null ? list.indexOf(newValue) : null); }); @@ -65,7 +65,11 @@ public class OptionsBuilder { validatorMap.put(null, new SimpleValidator()); var orVal = new ExclusiveValidator<>(validatorMap, selected); - options.values().forEach(builder -> props.addAll(builder.props)); + options.values().forEach(builder -> { + if (builder != null) { + props.addAll(builder.props); + } + }); props.add(selectedIndex); allValidators.add(orVal); pushComp(pane); diff --git a/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategyHelper.java b/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategyHelper.java index d5d415823..8a8a39d02 100644 --- a/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategyHelper.java +++ b/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategyHelper.java @@ -86,7 +86,7 @@ public class SecretRetrievalStrategyHelper { ? 1 : strat instanceof SecretRetrievalStrategy.PasswordManager ? 2 - : strat instanceof SecretRetrievalStrategy.CustomCommand ? 3 : strat instanceof SecretRetrievalStrategy.Prompt ? 4 : 0); + : strat instanceof SecretRetrievalStrategy.CustomCommand ? 3 : strat instanceof SecretRetrievalStrategy.Prompt ? 4 : strat == null ? -1 : 0); return new OptionsBuilder() .choice(selected, map) .bindChoice( @@ -97,6 +97,7 @@ public class SecretRetrievalStrategyHelper { case 2 -> passwordManager; case 3 -> customCommand; case 4 -> new SimpleObjectProperty<>(new SecretRetrievalStrategy.Prompt()); + case 5 -> new SimpleObjectProperty<>(); default -> new SimpleObjectProperty<>(); }; }, diff --git a/core/src/main/java/io/xpipe/core/dialog/Dialog.java b/core/src/main/java/io/xpipe/core/dialog/Dialog.java index 847d2f428..c2d9f950f 100644 --- a/core/src/main/java/io/xpipe/core/dialog/Dialog.java +++ b/core/src/main/java/io/xpipe/core/dialog/Dialog.java @@ -1,6 +1,7 @@ package io.xpipe.core.dialog; import io.xpipe.core.charsetter.Charsetter; +import io.xpipe.core.util.FailableSupplier; import io.xpipe.core.util.SecretValue; import java.util.ArrayList; @@ -457,11 +458,6 @@ public abstract class Dialog { protected abstract DialogElement next(String answer) throws Exception; - public interface FailableSupplier { - - T get() throws Exception; - } - public static class Choice extends Dialog { private final ChoiceElement element; diff --git a/core/src/main/java/io/xpipe/core/process/ShellControl.java b/core/src/main/java/io/xpipe/core/process/ShellControl.java index 196a589be..62f263b97 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellControl.java +++ b/core/src/main/java/io/xpipe/core/process/ShellControl.java @@ -1,7 +1,7 @@ package io.xpipe.core.process; -import io.xpipe.core.dialog.Dialog; import io.xpipe.core.util.FailableFunction; +import io.xpipe.core.util.FailableSupplier; import io.xpipe.core.util.SecretValue; import io.xpipe.core.util.XPipeSystemId; import lombok.NonNull; @@ -99,7 +99,7 @@ public interface ShellControl extends ProcessControl { default ShellControl elevationPassword(SecretValue value) { return elevationPassword(() -> value); } - ShellControl elevationPassword(Dialog.FailableSupplier value); + ShellControl elevationPassword(FailableSupplier value); ShellControl initWith(String cmds); @@ -107,7 +107,7 @@ public interface ShellControl extends ProcessControl { ShellControl startTimeout(int ms); - Dialog.FailableSupplier getElevationPassword(); + FailableSupplier getElevationPassword(); default ShellControl subShell(@NonNull ShellDialect type) { return subShell(p -> type.getOpenCommand(), new TerminalOpenFunction() { diff --git a/core/src/main/java/io/xpipe/core/util/FailableSupplier.java b/core/src/main/java/io/xpipe/core/util/FailableSupplier.java new file mode 100644 index 000000000..a530fbf76 --- /dev/null +++ b/core/src/main/java/io/xpipe/core/util/FailableSupplier.java @@ -0,0 +1,6 @@ +package io.xpipe.core.util; + +public interface FailableSupplier { + + T get() throws Exception; +} diff --git a/core/src/main/java/io/xpipe/core/util/UuidHelper.java b/core/src/main/java/io/xpipe/core/util/UuidHelper.java index 73903243f..ff03f8c2f 100644 --- a/core/src/main/java/io/xpipe/core/util/UuidHelper.java +++ b/core/src/main/java/io/xpipe/core/util/UuidHelper.java @@ -1,7 +1,5 @@ package io.xpipe.core.util; -import io.xpipe.core.dialog.Dialog; - import java.util.Optional; import java.util.UUID; @@ -15,7 +13,7 @@ public class UuidHelper { } } - public static Optional parse(Dialog.FailableSupplier supplier) { + public static Optional parse(FailableSupplier supplier) { try { var s = supplier.get(); return Optional.of(UUID.fromString(s)); diff --git a/dist/changelogs/1.5.0.md b/dist/changelogs/1.5.0.md index 985e40358..dbcf20489 100644 --- a/dist/changelogs/1.5.0.md +++ b/dist/changelogs/1.5.0.md @@ -29,9 +29,10 @@ To start off, it comes with a few new commands to read and write files on remote The workflow is designed as follows: -1. You can list all available connections and their ids to use with `xpipe list` -2. Using the command `xpipe drain `, you are able to forward the file contents to the stdout -3. Using the command `xpipe sink `, you are able to forward content from your stdin to the remote file +- You can list all available connections and their ids to use with `xpipe list` +- Using the command `xpipe launch `, you are able to log into a remote shell connection in your existing terminal session +- Using the command `xpipe drain `, you are able to forward the file contents to the stdout +- Using the command `xpipe sink `, you are able to forward content from your stdin to the remote file The id system is flexible, allowing you to only specify as much of the id as is necessary. @@ -64,6 +65,7 @@ This also comes with full support of the feature set for these environments - Fix desktop directory not being determined correctly on Windows when it was moved from the default location - Rework threading in navigation bar in browser to improve responsiveness - Recheck if prepared update is still the latest one prior to installing it +- Properly use shell script file extension for external editor when creating shell environments - Built-in documentation popups now honour the dark mode setting - Properly detect applications such as editors and terminals when they are present in the path on Windows - Rework operation mode handling to properly honor the startup mode setting