Add ssh config support plus polishing

This commit is contained in:
crschnick 2023-07-30 20:22:32 +00:00
parent c70d6da314
commit 5be0748c9f
13 changed files with 50 additions and 37 deletions

View file

@ -7,6 +7,7 @@ import io.xpipe.app.fxcomps.impl.TextAreaComp;
import io.xpipe.app.util.FileOpener; import io.xpipe.app.util.FileOpener;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.Property; import javafx.beans.property.Property;
import javafx.beans.value.ObservableValue;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
@ -18,9 +19,10 @@ public class IntegratedTextAreaComp extends SimpleComp {
private final Property<String> value; private final Property<String> value;
private final boolean lazy; private final boolean lazy;
private final String identifier; private final String identifier;
private final String fileType; private final ObservableValue<String> fileType;
public IntegratedTextAreaComp(Property<String> value, boolean lazy, String identifier, String fileType) { public IntegratedTextAreaComp(
Property<String> value, boolean lazy, String identifier, ObservableValue<String> fileType) {
this.value = value; this.value = value;
this.lazy = lazy; this.lazy = lazy;
this.identifier = identifier; this.identifier = identifier;
@ -47,12 +49,15 @@ public class IntegratedTextAreaComp extends SimpleComp {
} }
private Region createOpenButton(Region container) { private Region createOpenButton(Region container) {
var name = identifier + (fileType != null ? "." + fileType : "");
var button = new IconButtonComp( var button = new IconButtonComp(
"mdal-edit", "mdal-edit",
() -> FileOpener.openString(name, this, value.getValue(), (s) -> { () -> FileOpener.openString(
Platform.runLater(() -> value.setValue(s)); identifier + (fileType.getValue() != null ? "." + fileType.getValue() : ""),
})) this,
value.getValue(),
(s) -> {
Platform.runLater(() -> value.setValue(s));
}))
.createRegion(); .createRegion();
return button; return button;
} }

View file

@ -23,8 +23,8 @@ public class DenseStoreEntryComp extends StoreEntryComp {
grid.setHgap(8); grid.setHgap(8);
if (showIcon) { if (showIcon) {
var storeIcon = createIcon(30, 25); var storeIcon = createIcon(26, 21);
grid.getColumnConstraints().add(new ColumnConstraints(30)); grid.getColumnConstraints().add(new ColumnConstraints(26));
grid.add(storeIcon, 0, 0); grid.add(storeIcon, 0, 0);
GridPane.setHalignment(storeIcon, HPos.CENTER); GridPane.setHalignment(storeIcon, HPos.CENTER);
} else { } else {

View file

@ -17,10 +17,10 @@ public class StandardStoreEntryComp extends StoreEntryComp {
var name = createName().createRegion(); var name = createName().createRegion();
var grid = new GridPane(); var grid = new GridPane();
grid.setHgap(10); grid.setHgap(7);
grid.setVgap(0); grid.setVgap(0);
var storeIcon = createIcon(45, 35); var storeIcon = createIcon(50, 39);
grid.add(storeIcon, 0, 0, 1, 2); grid.add(storeIcon, 0, 0, 1, 2);
grid.getColumnConstraints().add(new ColumnConstraints(50)); grid.getColumnConstraints().add(new ColumnConstraints(50));

View file

@ -1,6 +1,5 @@
package io.xpipe.app.fxcomps.impl; package io.xpipe.app.fxcomps.impl;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.CompStructure; import io.xpipe.app.fxcomps.CompStructure;
import io.xpipe.app.fxcomps.SimpleCompStructure; import io.xpipe.app.fxcomps.SimpleCompStructure;
@ -37,8 +36,8 @@ public class ChoicePaneComp extends Comp<CompStructure<VBox>> {
cb.setConverter(new StringConverter<>() { cb.setConverter(new StringConverter<>() {
@Override @Override
public String toString(Entry object) { public String toString(Entry object) {
if (object == null) { if (object == null || object.name() == null) {
return AppI18n.get("app.none"); return "";
} }
return object.name().getValue(); return object.name().getValue();
@ -53,9 +52,11 @@ public class ChoicePaneComp extends Comp<CompStructure<VBox>> {
var vbox = new VBox(transformer.apply(cb)); var vbox = new VBox(transformer.apply(cb));
vbox.setFillWidth(true); vbox.setFillWidth(true);
cb.prefWidthProperty().bind(vbox.widthProperty()); cb.prefWidthProperty().bind(vbox.widthProperty());
SimpleChangeListener.apply(cb.valueProperty(), n-> { SimpleChangeListener.apply(cb.valueProperty(), n -> {
if (n == null) { if (n == null) {
vbox.getChildren().remove(1); if (vbox.getChildren().size() > 1) {
vbox.getChildren().remove(1);
}
} else { } else {
var region = n.comp().createRegion(); var region = n.comp().createRegion();
region.setPadding(new Insets(0, 0, 0, 10)); region.setPadding(new Insets(0, 0, 0, 10));

View file

@ -93,7 +93,7 @@ public class SvgView {
private WebView createWebView() { private WebView createWebView() {
var wv = new WebView(); 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 // overflow
wv.setPageFill(Color.valueOf("#00000001")); wv.setPageFill(Color.valueOf("#00000001"));
// wv.setPageFill(Color.BLACK); // wv.setPageFill(Color.BLACK);

View file

@ -69,12 +69,12 @@ public class JfxHelper {
return text; 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 graphic = new PrettyImageComp(new SimpleStringProperty(image), size, size).createRegion();
var hbox = new HBox(graphic, text); var hbox = new HBox(graphic, text);
hbox.setAlignment(Pos.CENTER_LEFT); hbox.setAlignment(Pos.CENTER_LEFT);
hbox.setSpacing(8); hbox.setSpacing(10);
// graphic.fitWidthProperty().bind(Bindings.createDoubleBinding(() -> header.getHeight() + // graphic.fitWidthProperty().bind(Bindings.createDoubleBinding(() -> header.getHeight() +
// desc.getHeight() + 2, // desc.getHeight() + 2,

View file

@ -48,11 +48,11 @@ public class OptionsBuilder {
public OptionsBuilder choice(IntegerProperty selectedIndex, Map<String, OptionsBuilder> options) { public OptionsBuilder choice(IntegerProperty selectedIndex, Map<String, OptionsBuilder> options) {
var list = options.entrySet().stream() var list = options.entrySet().stream()
.map(e -> new ChoicePaneComp.Entry( .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(); .toList();
var validatorList = var validatorList =
options.values().stream().map(builder -> builder.buildEffectiveValidator()).toList(); options.values().stream().map(builder -> builder != null ? builder.buildEffectiveValidator() : new SimpleValidator()).toList();
var selected = new SimpleObjectProperty<>(list.get(selectedIndex.getValue())); var selected = new SimpleObjectProperty<>(selectedIndex.getValue() != -1 ? list.get(selectedIndex.getValue()) : null);
selected.addListener((observable, oldValue, newValue) -> { selected.addListener((observable, oldValue, newValue) -> {
selectedIndex.setValue(newValue != null ? list.indexOf(newValue) : null); selectedIndex.setValue(newValue != null ? list.indexOf(newValue) : null);
}); });
@ -65,7 +65,11 @@ public class OptionsBuilder {
validatorMap.put(null, new SimpleValidator()); validatorMap.put(null, new SimpleValidator());
var orVal = new ExclusiveValidator<>(validatorMap, selected); 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); props.add(selectedIndex);
allValidators.add(orVal); allValidators.add(orVal);
pushComp(pane); pushComp(pane);

View file

@ -86,7 +86,7 @@ public class SecretRetrievalStrategyHelper {
? 1 ? 1
: strat instanceof SecretRetrievalStrategy.PasswordManager : strat instanceof SecretRetrievalStrategy.PasswordManager
? 2 ? 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() return new OptionsBuilder()
.choice(selected, map) .choice(selected, map)
.bindChoice( .bindChoice(
@ -97,6 +97,7 @@ public class SecretRetrievalStrategyHelper {
case 2 -> passwordManager; case 2 -> passwordManager;
case 3 -> customCommand; case 3 -> customCommand;
case 4 -> new SimpleObjectProperty<>(new SecretRetrievalStrategy.Prompt()); case 4 -> new SimpleObjectProperty<>(new SecretRetrievalStrategy.Prompt());
case 5 -> new SimpleObjectProperty<>();
default -> new SimpleObjectProperty<>(); default -> new SimpleObjectProperty<>();
}; };
}, },

View file

@ -1,6 +1,7 @@
package io.xpipe.core.dialog; package io.xpipe.core.dialog;
import io.xpipe.core.charsetter.Charsetter; import io.xpipe.core.charsetter.Charsetter;
import io.xpipe.core.util.FailableSupplier;
import io.xpipe.core.util.SecretValue; import io.xpipe.core.util.SecretValue;
import java.util.ArrayList; import java.util.ArrayList;
@ -457,11 +458,6 @@ public abstract class Dialog {
protected abstract DialogElement next(String answer) throws Exception; protected abstract DialogElement next(String answer) throws Exception;
public interface FailableSupplier<T> {
T get() throws Exception;
}
public static class Choice extends Dialog { public static class Choice extends Dialog {
private final ChoiceElement element; private final ChoiceElement element;

View file

@ -1,7 +1,7 @@
package io.xpipe.core.process; package io.xpipe.core.process;
import io.xpipe.core.dialog.Dialog;
import io.xpipe.core.util.FailableFunction; import io.xpipe.core.util.FailableFunction;
import io.xpipe.core.util.FailableSupplier;
import io.xpipe.core.util.SecretValue; import io.xpipe.core.util.SecretValue;
import io.xpipe.core.util.XPipeSystemId; import io.xpipe.core.util.XPipeSystemId;
import lombok.NonNull; import lombok.NonNull;
@ -99,7 +99,7 @@ public interface ShellControl extends ProcessControl {
default ShellControl elevationPassword(SecretValue value) { default ShellControl elevationPassword(SecretValue value) {
return elevationPassword(() -> value); return elevationPassword(() -> value);
} }
ShellControl elevationPassword(Dialog.FailableSupplier<SecretValue> value); ShellControl elevationPassword(FailableSupplier<SecretValue> value);
ShellControl initWith(String cmds); ShellControl initWith(String cmds);
@ -107,7 +107,7 @@ public interface ShellControl extends ProcessControl {
ShellControl startTimeout(int ms); ShellControl startTimeout(int ms);
Dialog.FailableSupplier<SecretValue> getElevationPassword(); FailableSupplier<SecretValue> getElevationPassword();
default ShellControl subShell(@NonNull ShellDialect type) { default ShellControl subShell(@NonNull ShellDialect type) {
return subShell(p -> type.getOpenCommand(), new TerminalOpenFunction() { return subShell(p -> type.getOpenCommand(), new TerminalOpenFunction() {

View file

@ -0,0 +1,6 @@
package io.xpipe.core.util;
public interface FailableSupplier<T> {
T get() throws Exception;
}

View file

@ -1,7 +1,5 @@
package io.xpipe.core.util; package io.xpipe.core.util;
import io.xpipe.core.dialog.Dialog;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@ -15,7 +13,7 @@ public class UuidHelper {
} }
} }
public static Optional<UUID> parse(Dialog.FailableSupplier<String> supplier) { public static Optional<UUID> parse(FailableSupplier<String> supplier) {
try { try {
var s = supplier.get(); var s = supplier.get();
return Optional.of(UUID.fromString(s)); return Optional.of(UUID.fromString(s));

View file

@ -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: The workflow is designed as follows:
1. You can list all available connections and their ids to use with `xpipe list` - You can list all available connections and their ids to use with `xpipe list`
2. Using the command `xpipe drain <id> <remote file path>`, you are able to forward the file contents to the stdout - Using the command `xpipe launch <id>`, you are able to log into a remote shell connection in your existing terminal session
3. Using the command `xpipe sink <id> <remote file path>`, you are able to forward content from your stdin to the remote file - Using the command `xpipe drain <id> <remote file path>`, you are able to forward the file contents to the stdout
- Using the command `xpipe sink <id> <remote file path>`, 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. 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 - 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 - Rework threading in navigation bar in browser to improve responsiveness
- Recheck if prepared update is still the latest one prior to installing it - 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 - 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 - 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 - Rework operation mode handling to properly honor the startup mode setting