diff --git a/app/src/main/java/io/xpipe/app/ext/ScanProvider.java b/app/src/main/java/io/xpipe/app/ext/ScanProvider.java index 3a13468c1..50cd72751 100644 --- a/app/src/main/java/io/xpipe/app/ext/ScanProvider.java +++ b/app/src/main/java/io/xpipe/app/ext/ScanProvider.java @@ -1,9 +1,11 @@ package io.xpipe.app.ext; +import io.xpipe.app.core.AppI18n; import io.xpipe.app.process.ShellControl; import io.xpipe.app.storage.DataStoreEntry; import io.xpipe.core.ModuleLayerLoader; +import javafx.beans.value.ObservableValue; import lombok.AllArgsConstructor; import lombok.Value; @@ -29,12 +31,24 @@ public abstract class ScanProvider { @Value @AllArgsConstructor public class ScanOpportunity { - String nameKey; + ObservableValue name; boolean disabled; String licenseFeatureId; public ScanOpportunity(String nameKey, boolean disabled) { - this.nameKey = nameKey; + this.name = AppI18n.observable(nameKey); + this.disabled = disabled; + this.licenseFeatureId = null; + } + + public ScanOpportunity(String nameKey, boolean disabled, String licenseFeatureId) { + this.name = AppI18n.observable(nameKey); + this.disabled = disabled; + this.licenseFeatureId = licenseFeatureId; + } + + public ScanOpportunity(ObservableValue name, boolean disabled) { + this.name = name; this.disabled = disabled; this.licenseFeatureId = null; } diff --git a/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationMenu.java b/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationMenu.java index d348bdf6e..db9e1d146 100644 --- a/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationMenu.java +++ b/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationMenu.java @@ -171,7 +171,7 @@ public class StoreCreationMenu { private static MenuItem networkScanMenu() { var menu = new MenuItem(); - menu.setGraphic(new FontIcon("mdi2t-toy-brick-plus-outline")); + menu.setGraphic(new FontIcon("mdi2a-access-point-plus")); menu.textProperty().bind(AppI18n.observable("addNetwork")); menu.setOnAction(event -> { ProcessControlProvider.get().createNetworkScanModal().show(); diff --git a/app/src/main/java/io/xpipe/app/util/ScanDialogBase.java b/app/src/main/java/io/xpipe/app/util/ScanDialogBase.java index 07f5164ed..b2d2b3eb9 100644 --- a/app/src/main/java/io/xpipe/app/util/ScanDialogBase.java +++ b/app/src/main/java/io/xpipe/app/util/ScanDialogBase.java @@ -10,14 +10,17 @@ import io.xpipe.app.issue.ErrorEventFactory; import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStoreEntryRef; +import javafx.beans.binding.Bindings; import javafx.beans.property.*; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; +import javafx.scene.control.Button; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import lombok.Getter; +import org.kordamp.ikonli.javafx.FontIcon; import java.util.ArrayList; import java.util.function.Function; @@ -30,6 +33,7 @@ public class ScanDialogBase { private final Runnable closeAction; private final ScanDialogAction action; private final ObservableList> entries; + private final boolean showButton; private final ObservableList available = FXCollections.synchronizedObservableList(FXCollections.observableArrayList()); private final ListProperty selected = @@ -42,11 +46,14 @@ public class ScanDialogBase { boolean expand, Runnable closeAction, ScanDialogAction action, - ObservableList> entries) { + ObservableList> entries, + boolean showButton + ) { this.expand = expand; this.closeAction = closeAction; this.action = action; this.entries = entries; + this.showButton = showButton; } public void finish() throws Exception { @@ -124,7 +131,7 @@ public class ScanDialogBase { VBox.setVgrow(stackPane, ALWAYS); Function nameFunc = (ScanProvider.ScanOpportunity s) -> { - var n = AppI18n.get(s.getNameKey()); + var n = s.getName().getValue(); if (s.getLicensedFeatureId() == null || s.isDisabled()) { return n; } @@ -141,8 +148,25 @@ public class ScanDialogBase { .createRegion(); stackPane.getChildren().add(r); - onUpdate(); - entries.addListener((ListChangeListener>) c -> onUpdate()); + if (showButton) { + var button = new Button(); + button.textProperty().bind(AppI18n.observable("start")); + button.setGraphic(new FontIcon("mdi2p-play")); + button.setOnAction(e -> { + onUpdate(); + }); + + var show = busy.not().and(Bindings.isEmpty(available)); + button.visibleProperty().bind(show); + button.managedProperty().bind(show); + + button.disableProperty().bind(Bindings.isEmpty(entries)); + + stackPane.getChildren().add(button); + } else { + onUpdate(); + entries.addListener((ListChangeListener>) c -> onUpdate()); + } var comp = new LoadingOverlayComp(Comp.of(() -> stackPane), busy, true).vgrow(); return comp; diff --git a/app/src/main/java/io/xpipe/app/util/ScanMultiDialogComp.java b/app/src/main/java/io/xpipe/app/util/ScanMultiDialogComp.java index 8d3e748a3..af71982ee 100644 --- a/app/src/main/java/io/xpipe/app/util/ScanMultiDialogComp.java +++ b/app/src/main/java/io/xpipe/app/util/ScanMultiDialogComp.java @@ -30,7 +30,8 @@ class ScanMultiDialogComp extends ModalOverlayContentComp { } }, action, - list); + list, + false); } void finish() { diff --git a/app/src/main/java/io/xpipe/app/util/ScanSingleDialogComp.java b/app/src/main/java/io/xpipe/app/util/ScanSingleDialogComp.java index bc47b97b2..6881f0a40 100644 --- a/app/src/main/java/io/xpipe/app/util/ScanSingleDialogComp.java +++ b/app/src/main/java/io/xpipe/app/util/ScanSingleDialogComp.java @@ -40,7 +40,8 @@ class ScanSingleDialogComp extends ModalOverlayContentComp { } }, action, - list); + list, + false); } void finish() { diff --git a/lang/strings/translations_en.properties b/lang/strings/translations_en.properties index ac0f142bf..27d1c2213 100644 --- a/lang/strings/translations_en.properties +++ b/lang/strings/translations_en.properties @@ -1589,3 +1589,11 @@ mcpClientConfigurationDetails=MCP client configuration mcpClientConfigurationDetailsDescription=Use this configuration data to connect to the XPipe MCP server from your MCP client of choice. switchHostAddress=Switch host address addAnotherHostName=Add another host name +addNetwork=Network scan ... +networkScan=Network scan +networkScanStore=Target host +networkScanStoreDescription=The host from which to scan the local network for SSH servers +useAsGateway=Use host as gateway +useAsGatewayDescription=Whether to use the target host as a gateway for the created SSH connections +networkScanPorts=Ports to scan +networkScanPortsDescription=The comma-separated list of SSH ports to include in the scan