diff --git a/README.md b/README.md index ec5020e95..15960dcda 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,7 @@ The distributed XPipe application consists out of two parts: - The closed-source extensions, mostly for professional edition features, which are not included in this repository Additional features are available in the professional edition. For more details see https://xpipe.io/pricing. +If your enterprise puts great emphasis on having access to the full source code, there are also full source-available enterprise options available. ## More links diff --git a/app/src/main/java/io/xpipe/app/beacon/AppBeaconServer.java b/app/src/main/java/io/xpipe/app/beacon/AppBeaconServer.java index 02b42f1e1..be4e4d8cd 100644 --- a/app/src/main/java/io/xpipe/app/beacon/AppBeaconServer.java +++ b/app/src/main/java/io/xpipe/app/beacon/AppBeaconServer.java @@ -1,5 +1,7 @@ package io.xpipe.app.beacon; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpServer; import io.xpipe.app.core.AppResources; import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.TrackEvent; @@ -8,13 +10,10 @@ import io.xpipe.beacon.BeaconConfig; import io.xpipe.beacon.BeaconInterface; import io.xpipe.core.process.OsType; import io.xpipe.core.util.XPipeInstallation; - -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpServer; import lombok.Getter; import java.io.IOException; -import java.net.InetAddress; +import java.net.Inet4Address; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -127,7 +126,7 @@ public class AppBeaconServer { } private void start() throws IOException { - server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), port), 10); + server = HttpServer.create(new InetSocketAddress(Inet4Address.getByAddress(new byte[]{ 0x7f,0x00,0x00,0x01 }), port), 10); BeaconInterface.getAll().forEach(beaconInterface -> { server.createContext(beaconInterface.getPath(), new BeaconRequestHandler<>(beaconInterface)); }); diff --git a/app/src/main/java/io/xpipe/app/beacon/BlobManager.java b/app/src/main/java/io/xpipe/app/beacon/BlobManager.java index 466ce056d..fc12a1b42 100644 --- a/app/src/main/java/io/xpipe/app/beacon/BlobManager.java +++ b/app/src/main/java/io/xpipe/app/beacon/BlobManager.java @@ -50,7 +50,7 @@ public class BlobManager { public Path newBlobFile() throws IOException { var file = TEMP.resolve(UUID.randomUUID().toString()); - Files.createDirectories(file.getParent()); + FileUtils.forceMkdir(file.getParent().toFile()); return file; } diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserTransferModel.java b/app/src/main/java/io/xpipe/app/browser/BrowserTransferModel.java index 816c6336e..4a89c7e07 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserTransferModel.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserTransferModel.java @@ -190,12 +190,11 @@ public class BrowserTransferModel { } public ObservableBooleanValue downloadFinished() { - return Bindings.createBooleanBinding( - () -> { - return progress.getValue() != null - && progress.getValue().done(); - }, - progress); + synchronized (progress) { + return Bindings.createBooleanBinding(() -> { + return progress.getValue() != null && progress.getValue().done(); + }, progress); + } } } } diff --git a/app/src/main/java/io/xpipe/app/browser/file/BrowserQuickAccessContextMenu.java b/app/src/main/java/io/xpipe/app/browser/file/BrowserQuickAccessContextMenu.java index a1c0d3ba1..dc4f4b3f1 100644 --- a/app/src/main/java/io/xpipe/app/browser/file/BrowserQuickAccessContextMenu.java +++ b/app/src/main/java/io/xpipe/app/browser/file/BrowserQuickAccessContextMenu.java @@ -79,7 +79,7 @@ public class BrowserQuickAccessContextMenu extends ContextMenu { getItems().addAll(r.getItems()); // Prevent NPE in show() - if (getScene() == null || anchor == null) { + if (getScene() == null) { return; } show(anchor, Side.RIGHT, 0, 0); diff --git a/app/src/main/java/io/xpipe/app/comp/base/ListBoxViewComp.java b/app/src/main/java/io/xpipe/app/comp/base/ListBoxViewComp.java index 18ae445ca..cc4921cac 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/ListBoxViewComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/ListBoxViewComp.java @@ -70,7 +70,9 @@ public class ListBoxViewComp extends Comp> { .bind(Bindings.createDoubleBinding( () -> { var v = bar.getVisibleAmount(); - return v < 1.0 ? 1.0 : 0.0; + // Check for rounding and accuracy issues + // It might not be exactly equal to 1.0 + return v < 0.99 ? 1.0 : 0.0; }, bar.visibleAmountProperty())); } diff --git a/app/src/main/java/io/xpipe/app/comp/base/MarkdownComp.java b/app/src/main/java/io/xpipe/app/comp/base/MarkdownComp.java index 6f43963ca..b977727df 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/MarkdownComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/MarkdownComp.java @@ -22,6 +22,7 @@ import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; import lombok.SneakyThrows; +import org.apache.commons.io.FileUtils; import java.io.IOException; import java.nio.file.Files; @@ -63,7 +64,7 @@ public class MarkdownComp extends Comp> { var html = MarkdownHelper.toHtml(markdown, s -> s, htmlTransformation, null); try { // Workaround for https://bugs.openjdk.org/browse/JDK-8199014 - Files.createDirectories(file.getParent()); + FileUtils.forceMkdir(file.getParent().toFile()); Files.writeString(file, html); return file; } catch (IOException e) { diff --git a/app/src/main/java/io/xpipe/app/comp/store/DenseStoreEntryComp.java b/app/src/main/java/io/xpipe/app/comp/store/DenseStoreEntryComp.java index bdc9d8546..8ace6c03c 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/DenseStoreEntryComp.java +++ b/app/src/main/java/io/xpipe/app/comp/store/DenseStoreEntryComp.java @@ -3,7 +3,6 @@ package io.xpipe.app.comp.store; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.augment.GrowAugment; import io.xpipe.app.fxcomps.util.PlatformThread; - import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleStringProperty; import javafx.geometry.HPos; @@ -41,9 +40,11 @@ public class DenseStoreEntryComp extends StoreEntryComp { .bind(PlatformThread.sync(Bindings.createStringBinding( () -> { var val = summary.getValue(); - if (val != null - && grid.isHover() - && getWrapper().getEntry().getProvider().alwaysShowSummary()) { + var p = getWrapper().getEntry().getProvider(); + if (val != null && grid.isHover() + && p.alwaysShowSummary()) { + return val; + } else if (info.getValue() == null && p.alwaysShowSummary()){ return val; } else { return info.getValue(); diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreCreationComp.java b/app/src/main/java/io/xpipe/app/comp/store/StoreCreationComp.java index ffcff4718..bca613053 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreCreationComp.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreCreationComp.java @@ -1,5 +1,6 @@ package io.xpipe.app.comp.store; +import atlantafx.base.controls.Spacer; import io.xpipe.app.comp.base.ButtonComp; import io.xpipe.app.comp.base.DialogComp; import io.xpipe.app.comp.base.ErrorOverlayComp; @@ -21,7 +22,6 @@ import io.xpipe.app.storage.DataStoreEntry; import io.xpipe.app.util.*; import io.xpipe.core.store.DataStore; import io.xpipe.core.util.ValidationException; - import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.property.*; @@ -33,8 +33,6 @@ import javafx.scene.layout.BorderPane; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; import javafx.stage.Stage; - -import atlantafx.base.controls.Spacer; import lombok.AccessLevel; import lombok.experimental.FieldDefaults; import net.synedra.validatorfx.GraphicDecorationStackPane; @@ -51,7 +49,7 @@ public class StoreCreationComp extends DialogComp { Stage window; BiConsumer consumer; Property provider; - Property store; + ObjectProperty store; Predicate filter; BooleanProperty busy = new SimpleBooleanProperty(); Property validator = new SimpleObjectProperty<>(new SimpleValidator()); @@ -60,6 +58,7 @@ public class StoreCreationComp extends DialogComp { ObservableValue entry; BooleanProperty changedSinceError = new SimpleBooleanProperty(); BooleanProperty skippable = new SimpleBooleanProperty(); + BooleanProperty connectable = new SimpleBooleanProperty(); StringProperty name; DataStoreEntry existingEntry; boolean staticDisplay; @@ -68,7 +67,7 @@ public class StoreCreationComp extends DialogComp { Stage window, BiConsumer consumer, Property provider, - Property store, + ObjectProperty store, Predicate filter, String initialName, DataStoreEntry existingEntry, @@ -96,6 +95,12 @@ public class StoreCreationComp extends DialogComp { } }); + this.provider.subscribe((n) -> { + if (n != null) { + connectable.setValue(n.canConnectDuringCreation()); + } + }); + this.apply(r -> { r.get().setPrefWidth(650); r.get().setPrefHeight(750); @@ -163,7 +168,12 @@ public class StoreCreationComp extends DialogComp { if (!DataStorage.get().getStoreEntries().contains(e)) { DataStorage.get().addStoreEntryIfNotPresent(newE); } else { - DataStorage.get().updateEntry(e, newE); + // We didn't change anything + if (e.getStore().equals(newE.getStore())) { + e.setName(newE.getName()); + } else { + DataStorage.get().updateEntry(e, newE); + } } }); }, @@ -239,7 +249,16 @@ public class StoreCreationComp extends DialogComp { finish(); } }) - .visible(skippable)); + .visible(skippable), + new ButtonComp(AppI18n.observable("connect"), null, () -> { + var temp = DataStoreEntry.createTempWrapper(store.getValue()); + var action = provider.getValue().launchAction(temp); + ThreadHelper.runFailableAsync(() -> { + action.execute(); + }); + }).hide(connectable.not().or(Bindings.createBooleanBinding(() -> { + return store.getValue() == null || !store.getValue().isComplete(); + }, store)))); } @Override @@ -393,11 +412,9 @@ public class StoreCreationComp extends DialogComp { private Region createLayout() { var layout = new BorderPane(); layout.getStyleClass().add("store-creator"); - layout.setPadding(new Insets(20)); var providerChoice = new StoreProviderChoiceComp(filter, provider, staticDisplay); - if (staticDisplay) { - providerChoice.apply(struc -> struc.get().setDisable(true)); - } else { + var showProviders = !staticDisplay && providerChoice.getProviders().size() > 1; + if (showProviders) { providerChoice.onSceneAssign(struc -> struc.get().requestFocus()); } providerChoice.apply(GrowAugment.create(true, false)); @@ -422,9 +439,14 @@ public class StoreCreationComp extends DialogComp { var sep = new Separator(); sep.getStyleClass().add("spacer"); - var top = new VBox(providerChoice.createRegion(), new Spacer(7, Orientation.VERTICAL), sep); + var top = new VBox(providerChoice.createRegion(), new Spacer(5, Orientation.VERTICAL), sep); top.getStyleClass().add("top"); - layout.setTop(top); + if (showProviders) { + layout.setTop(top); + layout.setPadding(new Insets(15, 20, 20, 20)); + } else { + layout.setPadding(new Insets(5, 20, 20, 20)); + } var valSp = new GraphicDecorationStackPane(); valSp.getChildren().add(layout); diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreCreationMenu.java b/app/src/main/java/io/xpipe/app/comp/store/StoreCreationMenu.java index 755c3b7b8..fe74fb481 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreCreationMenu.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreCreationMenu.java @@ -42,6 +42,8 @@ public class StoreCreationMenu { // menu.getItems().add(category("addCommand", "mdi2c-code-greater-than", DataStoreCreationCategory.COMMAND, "cmd")); + menu.getItems().add(category("addSerial", "mdi2s-serial-port", DataStoreCreationCategory.SERIAL, "serial")); + menu.getItems().add(category("addDatabase", "mdi2d-database-plus", DataStoreCreationCategory.DATABASE, null)); } diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryComp.java b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryComp.java index 94f33ba3d..2c88d7c9a 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryComp.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryComp.java @@ -482,7 +482,7 @@ public abstract class StoreEntryComp extends SimpleComp { sc.textProperty().bind(AppI18n.observable("base.createShortcut")); sc.setOnAction(event -> { ThreadHelper.runFailableAsync(() -> { - DesktopShortcuts.create( + DesktopShortcuts.createCliOpen( url, DataStorage.get() .getStoreEntryDisplayName( diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreProviderChoiceComp.java b/app/src/main/java/io/xpipe/app/comp/store/StoreProviderChoiceComp.java index d7b1e4d80..1833e589c 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreProviderChoiceComp.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreProviderChoiceComp.java @@ -29,7 +29,7 @@ public class StoreProviderChoiceComp extends Comp provider; boolean staticDisplay; - private List getProviders() { + public List getProviders() { return DataStoreProviders.getAll().stream() .filter(val -> filter == null || filter.test(val)) .toList(); diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreSection.java b/app/src/main/java/io/xpipe/app/comp/store/StoreSection.java index b2527bf91..1e7875f72 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreSection.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreSection.java @@ -163,10 +163,10 @@ public class StoreSection { var allChildren = all.filtered( other -> { // Legacy implementation that does not use children caches. Use for testing - // if (true) return DataStorage.get() - // .getDisplayParent(other.getEntry()) - // .map(found -> found.equals(e.getEntry())) - // .orElse(false); +// if (true) return DataStorage.get() +// .getDefaultDisplayParent(other.getEntry()) +// .map(found -> found.equals(e.getEntry())) +// .orElse(false); // is children. This check is fast as the children are cached in the storage return DataStorage.get().getStoreChildren(e.getEntry()).contains(other.getEntry()) diff --git a/app/src/main/java/io/xpipe/app/core/AppDataLock.java b/app/src/main/java/io/xpipe/app/core/AppDataLock.java index 8e3f93154..d84202530 100644 --- a/app/src/main/java/io/xpipe/app/core/AppDataLock.java +++ b/app/src/main/java/io/xpipe/app/core/AppDataLock.java @@ -1,6 +1,7 @@ package io.xpipe.app.core; import io.xpipe.app.issue.ErrorEvent; +import org.apache.commons.io.FileUtils; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; @@ -21,7 +22,7 @@ public class AppDataLock { public static boolean lock() { try { var file = getLockFile().toFile(); - Files.createDirectories(file.toPath().getParent()); + FileUtils.forceMkdir(file.getParentFile()); if (!Files.exists(file.toPath())) { try { // It is possible that another instance creates the lock at almost the same time diff --git a/app/src/main/java/io/xpipe/app/core/AppGreetings.java b/app/src/main/java/io/xpipe/app/core/AppGreetings.java index 1d94ed99d..b44ac8813 100644 --- a/app/src/main/java/io/xpipe/app/core/AppGreetings.java +++ b/app/src/main/java/io/xpipe/app/core/AppGreetings.java @@ -55,6 +55,12 @@ public class AppGreetings { if (set || AppProperties.get().isDevelopmentEnvironment()) { return; } + + if (AppProperties.get().isAutoAcceptEula()) { + AppCache.update("legalAccepted", true); + return; + } + var read = new SimpleBooleanProperty(); var accepted = new SimpleBooleanProperty(); AppWindowHelper.showBlockingAlert(alert -> { diff --git a/app/src/main/java/io/xpipe/app/core/AppLogs.java b/app/src/main/java/io/xpipe/app/core/AppLogs.java index efda9ad5f..b24d2190c 100644 --- a/app/src/main/java/io/xpipe/app/core/AppLogs.java +++ b/app/src/main/java/io/xpipe/app/core/AppLogs.java @@ -138,7 +138,7 @@ public class AppLogs { var shouldLogToFile = shouldWriteLogs(); if (shouldLogToFile) { try { - Files.createDirectories(usedLogsDir); + FileUtils.forceMkdir(usedLogsDir.toFile()); var file = usedLogsDir.resolve("xpipe.log"); var fos = new FileOutputStream(file.toFile(), true); var buf = new BufferedOutputStream(fos); diff --git a/app/src/main/java/io/xpipe/app/core/AppProperties.java b/app/src/main/java/io/xpipe/app/core/AppProperties.java index 8150cc878..0a18c8d45 100644 --- a/app/src/main/java/io/xpipe/app/core/AppProperties.java +++ b/app/src/main/java/io/xpipe/app/core/AppProperties.java @@ -37,11 +37,13 @@ public class AppProperties { boolean useVirtualThreads; boolean debugThreads; Path dataDir; + Path defaultDataDir; boolean showcase; AppVersion canonicalVersion; boolean locatePtb; boolean locatorVersionCheck; boolean isTest; + boolean autoAcceptEula; public AppProperties() { var appDir = Path.of(System.getProperty("user.dir")).resolve("app"); @@ -86,6 +88,7 @@ public class AppProperties { debugThreads = Optional.ofNullable(System.getProperty("io.xpipe.app.debugThreads")) .map(Boolean::parseBoolean) .orElse(false); + defaultDataDir = Path.of(System.getProperty("user.home"), isStaging() ? ".xpipe-ptb" : ".xpipe"); dataDir = Optional.ofNullable(System.getProperty("io.xpipe.app.dataDir")) .map(s -> { var p = Path.of(s); @@ -94,7 +97,7 @@ public class AppProperties { } return p; }) - .orElse(Path.of(System.getProperty("user.home"), isStaging() ? ".xpipe-ptb" : ".xpipe")); + .orElse(defaultDataDir); showcase = Optional.ofNullable(System.getProperty("io.xpipe.app.showcase")) .map(Boolean::parseBoolean) .orElse(false); @@ -107,6 +110,9 @@ public class AppProperties { .map(s -> !Boolean.parseBoolean(s)) .orElse(true); isTest = isJUnitTest(); + autoAcceptEula = Optional.ofNullable(System.getProperty("io.xpipe.app.acceptEula")) + .map(Boolean::parseBoolean) + .orElse(false); } private static boolean isJUnitTest() { diff --git a/app/src/main/java/io/xpipe/app/core/AppTheme.java b/app/src/main/java/io/xpipe/app/core/AppTheme.java index 05cdb6cb8..32b9cc0f8 100644 --- a/app/src/main/java/io/xpipe/app/core/AppTheme.java +++ b/app/src/main/java/io/xpipe/app/core/AppTheme.java @@ -113,9 +113,6 @@ public class AppTheme { } }); }); - } catch (UnsupportedOperationException ex) { - // The platform preferences are sometimes not initialized yet - ErrorEvent.fromThrowable(ex).expected().omit().handle(); } catch (Throwable t) { ErrorEvent.fromThrowable(t).omit().handle(); } @@ -139,9 +136,6 @@ public class AppTheme { } else { AppPrefs.get().theme.setValue(Theme.getDefaultLightTheme()); } - } catch (UnsupportedOperationException ex) { - // The platform preferences are sometimes not initialized yet - ErrorEvent.fromThrowable(ex).expected().omit().handle(); } catch (Exception ex) { // The color scheme query can fail if the toolkit is not initialized properly AppPrefs.get().theme.setValue(Theme.getDefaultLightTheme()); diff --git a/app/src/main/java/io/xpipe/app/core/check/AppUserDirectoryCheck.java b/app/src/main/java/io/xpipe/app/core/check/AppUserDirectoryCheck.java index acfa8ca33..0a3fa51c4 100644 --- a/app/src/main/java/io/xpipe/app/core/check/AppUserDirectoryCheck.java +++ b/app/src/main/java/io/xpipe/app/core/check/AppUserDirectoryCheck.java @@ -2,6 +2,7 @@ package io.xpipe.app.core.check; import io.xpipe.app.core.AppProperties; import io.xpipe.app.issue.ErrorEvent; +import org.apache.commons.io.FileUtils; import java.io.IOException; import java.nio.file.Files; @@ -12,18 +13,17 @@ public class AppUserDirectoryCheck { var dataDirectory = AppProperties.get().getDataDir(); try { - Files.createDirectories(dataDirectory); + FileUtils.forceMkdir(dataDirectory.toFile()); var testDirectory = dataDirectory.resolve("permissions_check"); - Files.createDirectories(testDirectory); - if (!Files.exists(testDirectory)) { - throw new IOException("Directory creation in user home directory failed silently"); - } + FileUtils.forceMkdir(testDirectory.toFile()); Files.delete(testDirectory); // if (true) throw new IOException(); } catch (IOException e) { - ErrorEvent.fromThrowable("Unable to access directory " + dataDirectory - + ". Please make sure that you have the appropriate permissions and no Antivirus program is blocking the access. " - + "In case you use cloud storage, verify that your cloud storage is working and you are logged in.", e) + ErrorEvent.fromThrowable( + new IOException( + "Unable to access directory " + dataDirectory + + ". Please make sure that you have the appropriate permissions and no Antivirus program is blocking the access. " + + "In case you use cloud storage, verify that your cloud storage is working and you are logged in.")) .term() .expected() .handle(); diff --git a/app/src/main/java/io/xpipe/app/ext/DataStoreCreationCategory.java b/app/src/main/java/io/xpipe/app/ext/DataStoreCreationCategory.java index 46c304c11..17c863dd9 100644 --- a/app/src/main/java/io/xpipe/app/ext/DataStoreCreationCategory.java +++ b/app/src/main/java/io/xpipe/app/ext/DataStoreCreationCategory.java @@ -9,5 +9,6 @@ public enum DataStoreCreationCategory { TUNNEL, SCRIPT, CLUSTER, - DESKTOP + DESKTOP, + SERIAL } diff --git a/app/src/main/java/io/xpipe/app/ext/DataStoreProvider.java b/app/src/main/java/io/xpipe/app/ext/DataStoreProvider.java index 6169cc37e..56db5c708 100644 --- a/app/src/main/java/io/xpipe/app/ext/DataStoreProvider.java +++ b/app/src/main/java/io/xpipe/app/ext/DataStoreProvider.java @@ -100,6 +100,10 @@ public interface DataStoreProvider { return Comp.empty(); } + default boolean canConnectDuringCreation() { + return false; + } + default Comp createInsightsComp(ObservableValue store) { var content = Bindings.createStringBinding( () -> { @@ -148,6 +152,10 @@ public interface DataStoreProvider { return DataStoreUsageCategory.DATABASE; } + if (cc == DataStoreCreationCategory.SERIAL) { + return DataStoreUsageCategory.SERIAL; + } + return null; } diff --git a/app/src/main/java/io/xpipe/app/ext/DataStoreUsageCategory.java b/app/src/main/java/io/xpipe/app/ext/DataStoreUsageCategory.java index d88f724df..7257840bf 100644 --- a/app/src/main/java/io/xpipe/app/ext/DataStoreUsageCategory.java +++ b/app/src/main/java/io/xpipe/app/ext/DataStoreUsageCategory.java @@ -16,5 +16,7 @@ public enum DataStoreUsageCategory { @JsonProperty("desktop") DESKTOP, @JsonProperty("group") - GROUP; + GROUP, + @JsonProperty("serial") + SERIAL; } diff --git a/app/src/main/java/io/xpipe/app/ext/EnabledStoreProvider.java b/app/src/main/java/io/xpipe/app/ext/EnabledStoreProvider.java index 35a11f689..2a9a4f899 100644 --- a/app/src/main/java/io/xpipe/app/ext/EnabledStoreProvider.java +++ b/app/src/main/java/io/xpipe/app/ext/EnabledStoreProvider.java @@ -11,7 +11,7 @@ public interface EnabledStoreProvider extends DataStoreProvider { @Override default StoreEntryComp customEntryComp(StoreSection sec, boolean preferLarge) { - if (sec.getWrapper().getValidity().getValue() != DataStoreEntry.Validity.COMPLETE) { + if (sec.getWrapper().getValidity().getValue() == DataStoreEntry.Validity.LOAD_FAILED) { return StoreEntryComp.create(sec, null, preferLarge); } 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 3ed8260f4..965f1147e 100644 --- a/app/src/main/java/io/xpipe/app/ext/ScanProvider.java +++ b/app/src/main/java/io/xpipe/app/ext/ScanProvider.java @@ -2,10 +2,8 @@ package io.xpipe.app.ext; import io.xpipe.app.storage.DataStoreEntry; import io.xpipe.core.process.ShellControl; -import io.xpipe.core.store.DataStore; import io.xpipe.core.util.FailableRunnable; import io.xpipe.core.util.ModuleLayerLoader; - import lombok.AllArgsConstructor; import lombok.Value; @@ -22,10 +20,6 @@ public abstract class ScanProvider { return ALL; } - public ScanOperation create(DataStore store) { - return null; - } - public ScanOperation create(DataStoreEntry entry, ShellControl sc) throws Exception { return null; } diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/ContextualFileReferenceChoiceComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/ContextualFileReferenceChoiceComp.java index 77d47c38e..50a5b0fdd 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/ContextualFileReferenceChoiceComp.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/ContextualFileReferenceChoiceComp.java @@ -1,5 +1,6 @@ package io.xpipe.app.fxcomps.impl; +import atlantafx.base.theme.Styles; import io.xpipe.app.browser.session.BrowserChooserComp; import io.xpipe.app.comp.base.ButtonComp; import io.xpipe.app.core.AppI18n; @@ -15,39 +16,29 @@ import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStoreEntryRef; import io.xpipe.core.store.FileNames; import io.xpipe.core.store.FileSystemStore; - import javafx.application.Platform; import javafx.beans.property.Property; import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.value.ObservableValue; import javafx.scene.control.Alert; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; - -import atlantafx.base.theme.Styles; import org.kordamp.ikonli.javafx.FontIcon; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import java.util.List; +import java.util.ArrayList; public class ContextualFileReferenceChoiceComp extends Comp> { private final Property> fileSystem; private final Property filePath; + private final boolean allowSync; public ContextualFileReferenceChoiceComp( - ObservableValue> fileSystem, Property filePath) { - this.fileSystem = new SimpleObjectProperty<>(); - fileSystem.subscribe(val -> { - this.fileSystem.setValue(val); - }); - this.filePath = filePath; - } - - public ContextualFileReferenceChoiceComp( - Property> fileSystem, Property filePath) { + Property> fileSystem, Property filePath, boolean allowSync + ) { + this.allowSync = allowSync; this.fileSystem = new SimpleObjectProperty<>(); fileSystem.subscribe(val -> { this.fileSystem.setValue(val); @@ -79,7 +70,7 @@ public class ContextualFileReferenceChoiceComp extends Comp> }, false); }) - .styleClass(Styles.CENTER_PILL) + .styleClass(allowSync ? Styles.CENTER_PILL : Styles.RIGHT_PILL) .grow(false, true); var gitShareButton = new ButtonComp(null, new FontIcon("mdi2g-git"), () -> { @@ -126,7 +117,13 @@ public class ContextualFileReferenceChoiceComp extends Comp> gitShareButton.tooltipKey("gitShareFileTooltip"); gitShareButton.styleClass(Styles.RIGHT_PILL).grow(false, true); - var layout = new HorizontalComp(List.of(fileNameComp, fileBrowseButton, gitShareButton)) + var nodes = new ArrayList>(); + nodes.add(fileNameComp); + nodes.add(fileBrowseButton); + if (allowSync) { + nodes.add(gitShareButton); + } + var layout = new HorizontalComp(nodes) .apply(struc -> struc.get().setFillHeight(true)); layout.apply(struc -> { diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/DataStoreListChoiceComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/DataStoreListChoiceComp.java index 552ac4ba7..2e901e5e8 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/DataStoreListChoiceComp.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/DataStoreListChoiceComp.java @@ -53,7 +53,7 @@ public class DataStoreListChoiceComp extends SimpleComp { }); return new HorizontalComp(List.of(label, Comp.hspacer(), delete)).styleClass("entry"); }, - true) + false) .padding(new Insets(0)) .apply(struc -> struc.get().setMinHeight(0)) .apply(struc -> ((VBox) struc.get().getContent()).setSpacing(5)); diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/FilterComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/FilterComp.java index a6a2db1f1..8a131e0f2 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/FilterComp.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/FilterComp.java @@ -10,6 +10,9 @@ import io.xpipe.app.fxcomps.util.PlatformThread; import javafx.beans.binding.Bindings; import javafx.beans.property.Property; import javafx.scene.Cursor; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseButton; import atlantafx.base.controls.CustomTextField; @@ -53,6 +56,13 @@ public class FilterComp extends Comp> { filter.focusedProperty())); filter.setAccessibleText("Filter"); + filter.addEventFilter(KeyEvent.KEY_PRESSED,event -> { + if (new KeyCodeCombination(KeyCode.ESCAPE).match(event)) { + filter.getScene().getRoot().requestFocus(); + event.consume(); + } + }); + filterText.subscribe(val -> { PlatformThread.runLaterIfNeeded(() -> { clear.setVisible(val != null); diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/IntComboFieldComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/IntComboFieldComp.java new file mode 100644 index 000000000..318888a82 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/IntComboFieldComp.java @@ -0,0 +1,79 @@ +package io.xpipe.app.fxcomps.impl; + +import io.xpipe.app.fxcomps.Comp; +import io.xpipe.app.fxcomps.CompStructure; +import io.xpipe.app.fxcomps.SimpleCompStructure; +import io.xpipe.app.fxcomps.util.PlatformThread; +import javafx.beans.property.Property; +import javafx.beans.value.ChangeListener; +import javafx.collections.FXCollections; +import javafx.scene.control.ComboBox; +import javafx.scene.control.skin.ComboBoxListViewSkin; +import javafx.scene.input.KeyEvent; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +import java.util.List; + +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +public class IntComboFieldComp extends Comp>> { + + Property value; + List predefined; + boolean allowNegative; + + public IntComboFieldComp(Property value, List predefined, boolean allowNegative) { + this.value = value; + this.predefined = predefined; + this.allowNegative = allowNegative; + } + + @Override + public CompStructure> createBase() { + var text = new ComboBox(); + text.setEditable(true); + text.setValue(value.getValue() != null ? value.getValue().toString() : null); + text.setItems(FXCollections.observableList(predefined.stream().map(integer -> "" + integer).toList())); + text.setMaxWidth(2000); + text.getStyleClass().add("int-combo-field-comp"); + text.setSkin(new ComboBoxListViewSkin<>(text)); + text.setVisibleRowCount(Math.min(10, predefined.size())); + + value.addListener((ChangeListener) (observableValue, oldValue, newValue) -> { + PlatformThread.runLaterIfNeeded(() -> { + if (newValue == null) { + text.setValue(""); + } else { + text.setValue(newValue.toString()); + } + }); + }); + + text.addEventFilter(KeyEvent.KEY_TYPED, keyEvent -> { + if (allowNegative) { + if (!"-0123456789".contains(keyEvent.getCharacter())) { + keyEvent.consume(); + } + } else { + if (!"0123456789".contains(keyEvent.getCharacter())) { + keyEvent.consume(); + } + } + }); + + text.valueProperty().addListener((observableValue, oldValue, newValue) -> { + if (newValue == null + || newValue.isEmpty() + || (allowNegative && "-".equals(newValue)) + || !newValue.matches("-?\\d+")) { + value.setValue(null); + return; + } + + int intValue = Integer.parseInt(newValue); + value.setValue(intValue); + }); + + return new SimpleCompStructure<>(text); + } +} diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/IntFieldComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/IntFieldComp.java index 6c6670015..0beffef90 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/IntFieldComp.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/IntFieldComp.java @@ -4,12 +4,10 @@ import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.CompStructure; import io.xpipe.app.fxcomps.SimpleCompStructure; import io.xpipe.app.fxcomps.util.PlatformThread; - import javafx.beans.property.Property; import javafx.beans.value.ChangeListener; import javafx.scene.control.TextField; import javafx.scene.input.KeyEvent; - import lombok.AccessLevel; import lombok.experimental.FieldDefaults; diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/StringSourceComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/StringSourceComp.java deleted file mode 100644 index 08252bb2c..000000000 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/StringSourceComp.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.xpipe.app.fxcomps.impl; - -import io.xpipe.app.fxcomps.SimpleComp; -import io.xpipe.app.storage.DataStoreEntryRef; -import io.xpipe.app.util.StringSource; -import io.xpipe.core.store.ShellStore; - -import javafx.beans.property.Property; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.value.ObservableValue; -import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.Region; -import javafx.scene.layout.StackPane; - -public class StringSourceComp extends SimpleComp { - - private final Property> fileSystem; - private final Property stringSource; - - public StringSourceComp( - ObservableValue> fileSystem, Property stringSource) { - this.stringSource = stringSource; - this.fileSystem = new SimpleObjectProperty<>(); - fileSystem.subscribe(val -> { - this.fileSystem.setValue(val.get().ref()); - }); - } - - @Override - protected Region createSimple() { - var inPlace = - new SimpleObjectProperty<>(stringSource.getValue() instanceof StringSource.InPlace i ? i.get() : null); - var fs = stringSource.getValue() instanceof StringSource.File f ? f.getFile() : null; - var file = new SimpleObjectProperty<>( - stringSource.getValue() instanceof StringSource.File f - ? f.getFile().serialize() - : null); - var showText = new SimpleBooleanProperty(inPlace.get() != null); - - var stringField = new TextAreaComp(inPlace); - stringField.hide(showText.not()); - var fileComp = new ContextualFileReferenceChoiceComp(fileSystem, file); - fileComp.hide(showText); - - var tr = stringField.createRegion(); - var button = new IconButtonComp("mdi2c-checkbox-marked-outline", () -> { - showText.set(!showText.getValue()); - }) - .createRegion(); - AnchorPane.setBottomAnchor(button, 10.0); - AnchorPane.setRightAnchor(button, 10.0); - var anchorPane = new AnchorPane(tr, button); - AnchorPane.setBottomAnchor(tr, 0.0); - AnchorPane.setTopAnchor(tr, 0.0); - AnchorPane.setLeftAnchor(tr, 0.0); - AnchorPane.setRightAnchor(tr, 0.0); - - var fr = fileComp.createRegion(); - - return new StackPane(tr, fr); - } -} diff --git a/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java b/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java index 788197266..cafa708a7 100644 --- a/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java +++ b/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java @@ -14,18 +14,16 @@ import io.xpipe.app.terminal.ExternalTerminalType; import io.xpipe.app.util.PasswordLockSecretValue; import io.xpipe.core.util.InPlaceSecretValue; import io.xpipe.core.util.ModuleHelper; - import javafx.beans.binding.Bindings; import javafx.beans.property.*; import javafx.beans.value.ObservableBooleanValue; import javafx.beans.value.ObservableDoubleValue; import javafx.beans.value.ObservableStringValue; import javafx.beans.value.ObservableValue; - import lombok.Getter; import lombok.Value; +import org.apache.commons.io.FileUtils; -import java.nio.file.Files; import java.nio.file.Path; import java.util.*; import java.util.stream.Stream; @@ -175,6 +173,7 @@ public class AppPrefs { new SecurityCategory(), new HttpApiCategory(), new WorkflowCategory(), + new WorkspacesCategory(), new TroubleshootCategory(), new DeveloperCategory()) .filter(appPrefsCategory -> appPrefsCategory.show()) @@ -489,7 +488,7 @@ public class AppPrefs { } try { - Files.createDirectories(storageDirectory.get()); + FileUtils.forceMkdir(storageDirectory.getValue().toFile()); } catch (Exception e) { ErrorEvent.fromThrowable(e).expected().build().handle(); storageDirectory.setValue(DEFAULT_STORAGE_DIR); diff --git a/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java b/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java index 82153394e..c32dd4254 100644 --- a/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java +++ b/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java @@ -114,14 +114,12 @@ public abstract class ExternalApplicationType implements PrefsChoiceValue { protected Optional determineFromPath() { // Try to locate if it is in the Path - try (var cc = LocalShell.getShell() - .command(CommandBuilder.ofFunction( - var1 -> var1.getShellDialect().getWhichCommand(executable))) + try (var sc = LocalShell.getShell() .start()) { - var out = cc.readStdoutDiscardErr(); - var exit = cc.getExitCode(); - if (exit == 0) { - var first = out.lines().findFirst(); + var out = sc.command(CommandBuilder.ofFunction( + var1 -> var1.getShellDialect().getWhichCommand(executable))).readStdoutIfPossible(); + if (out.isPresent()) { + var first = out.get().lines().findFirst(); if (first.isPresent()) { return first.map(String::trim).map(Path::of); } 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 0132f654c..8189efdfa 100644 --- a/app/src/main/java/io/xpipe/app/prefs/SyncCategory.java +++ b/app/src/main/java/io/xpipe/app/prefs/SyncCategory.java @@ -21,11 +21,7 @@ public class SyncCategory extends AppPrefsCategory { builder.addTitle("sync") .sub(new OptionsBuilder() .name("enableGitStorage") - .description( - AppProperties.get().isStaging() - && !prefs.developerMode().getValue() - ? "enableGitStoragePtbDisabled" - : "enableGitStorageDescription") + .description("enableGitStorageDescription") .addToggle(prefs.enableGitStorage) .disable(AppProperties.get().isStaging() && !prefs.developerMode().getValue()) diff --git a/app/src/main/java/io/xpipe/app/prefs/WorkspaceCreationAlert.java b/app/src/main/java/io/xpipe/app/prefs/WorkspaceCreationAlert.java new file mode 100644 index 000000000..3ff605f35 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/prefs/WorkspaceCreationAlert.java @@ -0,0 +1,75 @@ +package io.xpipe.app.prefs; + +import io.xpipe.app.core.AppFont; +import io.xpipe.app.core.AppI18n; +import io.xpipe.app.core.AppProperties; +import io.xpipe.app.core.mode.OperationMode; +import io.xpipe.app.core.window.AppWindowHelper; +import io.xpipe.app.issue.ErrorEvent; +import io.xpipe.app.util.DesktopHelper; +import io.xpipe.app.util.DesktopShortcuts; +import io.xpipe.app.util.OptionsBuilder; +import io.xpipe.app.util.ThreadHelper; +import io.xpipe.core.process.OsType; +import io.xpipe.core.util.XPipeInstallation; +import javafx.beans.property.SimpleObjectProperty; +import javafx.geometry.Insets; +import javafx.scene.control.ButtonType; +import org.apache.commons.io.FileUtils; + +import java.nio.file.Files; + +public class WorkspaceCreationAlert { + + public static void showAsync() { + ThreadHelper.runFailableAsync(() -> { + show(); + }); + } + + private static void show() throws Exception { + var name = new SimpleObjectProperty<>("New workspace"); + var path = new SimpleObjectProperty<>(AppProperties.get().getDataDir()); + var show = AppWindowHelper.showBlockingAlert(alert -> { + alert.setTitle(AppI18n.get("workspaceCreationAlertTitle")); + var content = new OptionsBuilder() + .nameAndDescription("workspaceName") + .addString(name) + .nameAndDescription("workspacePath") + .addPath(path) + .buildComp() + .minWidth(500) + .padding(new Insets(5, 20, 20, 20)) + .apply(struc -> AppFont.small(struc.get())) + .createRegion(); + alert.getButtonTypes().add(ButtonType.CANCEL); + alert.getButtonTypes().add(ButtonType.OK); + alert.getDialogPane().setContent(content); + }) + .map(b -> b.getButtonData().isDefaultButton()) + .orElse(false); + + if (!show || name.get() == null || path.get() == null) { + return; + } + + if (Files.exists(path.get()) && !FileUtils.isEmptyDirectory(path.get().toFile())) { + ErrorEvent.fromMessage("New workspace directory is not empty").expected().handle(); + return; + } + + var shortcutName = (AppProperties.get().isStaging() ? "XPipe PTB" : "XPipe") + " (" + name.get() + ")"; + var file = switch (OsType.getLocal()) { + case OsType.Windows w -> { + var exec = XPipeInstallation.getCurrentInstallationBasePath().resolve(XPipeInstallation.getDaemonExecutablePath(w)).toString(); + yield DesktopShortcuts.create(exec, "-Dio.xpipe.app.dataDir=\"" + path.get().toString() + "\" -Dio.xpipe.app.acceptEula=true", shortcutName); + } + default -> { + var exec = XPipeInstallation.getCurrentInstallationBasePath().resolve(XPipeInstallation.getRelativeCliExecutablePath(OsType.getLocal())).toString(); + yield DesktopShortcuts.create(exec, "-d \"" + path.get().toString() + "\" --accept-eula", shortcutName); + } + }; + DesktopHelper.browseFileInDirectory(file); + OperationMode.close(); + } +} diff --git a/app/src/main/java/io/xpipe/app/prefs/WorkspacesCategory.java b/app/src/main/java/io/xpipe/app/prefs/WorkspacesCategory.java new file mode 100644 index 000000000..2c186e008 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/prefs/WorkspacesCategory.java @@ -0,0 +1,26 @@ +package io.xpipe.app.prefs; + +import io.xpipe.app.comp.base.ButtonComp; +import io.xpipe.app.core.AppI18n; +import io.xpipe.app.fxcomps.Comp; +import io.xpipe.app.util.OptionsBuilder; + +public class WorkspacesCategory extends AppPrefsCategory { + + @Override + protected String getId() { + return "workspaces"; + } + + @Override + protected Comp create() { + return new OptionsBuilder() + .addTitle("manageWorkspaces") + .sub(new OptionsBuilder() + .nameAndDescription("workspaceAdd") + .addComp( + new ButtonComp(AppI18n.observable("addWorkspace"), + WorkspaceCreationAlert::showAsync))) + .buildComp(); + } +} diff --git a/app/src/main/java/io/xpipe/app/storage/ContextualFileReference.java b/app/src/main/java/io/xpipe/app/storage/ContextualFileReference.java index 063d4c8e3..e12bb513b 100644 --- a/app/src/main/java/io/xpipe/app/storage/ContextualFileReference.java +++ b/app/src/main/java/io/xpipe/app/storage/ContextualFileReference.java @@ -23,7 +23,7 @@ public class ContextualFileReference { private static String getDataDir() { if (DataStorage.get() == null) { - return lastDataDir != null ? lastDataDir : normalized(AppPrefs.DEFAULT_STORAGE_DIR); + return lastDataDir != null ? lastDataDir : normalized(AppPrefs.DEFAULT_STORAGE_DIR.resolve("data")); } return lastDataDir = normalized(DataStorage.get().getDataDir()); diff --git a/app/src/main/java/io/xpipe/app/storage/DataStorage.java b/app/src/main/java/io/xpipe/app/storage/DataStorage.java index 89d237cf1..e4377c42e 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStorage.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStorage.java @@ -273,6 +273,10 @@ public abstract class DataStorage { } public void updateEntry(DataStoreEntry entry, DataStoreEntry newEntry) { + var state = entry.getStorePersistentState(); + var newState = state.mergeCopy(newEntry.getStorePersistentState()); + newEntry.setStorePersistentState(newState); + var oldParent = DataStorage.get().getDefaultDisplayParent(entry); var newParent = DataStorage.get().getDefaultDisplayParent(newEntry); var sameParent = Objects.equals(oldParent, newParent); diff --git a/app/src/main/java/io/xpipe/app/storage/DataStoreEntry.java b/app/src/main/java/io/xpipe/app/storage/DataStoreEntry.java index 97c32e6e7..9e108efcf 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStoreEntry.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStoreEntry.java @@ -157,7 +157,7 @@ public class DataStoreEntry extends StorageElement { null, uuid, categoryUuid, - name, + name.trim(), Instant.now(), Instant.now(), storeFromNode, @@ -194,7 +194,7 @@ public class DataStoreEntry extends StorageElement { var categoryUuid = Optional.ofNullable(json.get("categoryUuid")) .map(jsonNode -> UUID.fromString(jsonNode.textValue())) .orElse(DataStorage.DEFAULT_CATEGORY_UUID); - var name = json.required("name").textValue(); + var name = json.required("name").textValue().trim(); var persistentState = stateJson.get("persistentState"); var lastUsed = Optional.ofNullable(stateJson.get("lastUsed")) diff --git a/app/src/main/java/io/xpipe/app/storage/StandardStorage.java b/app/src/main/java/io/xpipe/app/storage/StandardStorage.java index 447ef933c..9191c2f91 100644 --- a/app/src/main/java/io/xpipe/app/storage/StandardStorage.java +++ b/app/src/main/java/io/xpipe/app/storage/StandardStorage.java @@ -437,7 +437,7 @@ public class StandardStorage extends DataStorage { var s = Files.readString(file); vaultKey = new String(Base64.getDecoder().decode(s), StandardCharsets.UTF_8); } else { - Files.createDirectories(dir); + FileUtils.forceMkdir(dir.toFile()); vaultKey = UUID.randomUUID().toString(); Files.writeString(file, Base64.getEncoder().encodeToString(vaultKey.getBytes(StandardCharsets.UTF_8))); } @@ -458,7 +458,7 @@ public class StandardStorage extends DataStorage { Files.writeString(file, s); } } else { - Files.createDirectories(dir); + FileUtils.forceMkdir(dir.toFile()); var s = OsType.getLocal().getName(); Files.writeString(file, s); } diff --git a/app/src/main/java/io/xpipe/app/update/XPipeDistributionType.java b/app/src/main/java/io/xpipe/app/update/XPipeDistributionType.java index 410aa38a1..0d6c8d6e3 100644 --- a/app/src/main/java/io/xpipe/app/update/XPipeDistributionType.java +++ b/app/src/main/java/io/xpipe/app/update/XPipeDistributionType.java @@ -99,16 +99,13 @@ public enum XPipeDistributionType { // In theory, we can also add && !AppProperties.get().isStaging() here, but we want to replicate the // production behavior if (OsType.getLocal().equals(OsType.WINDOWS)) { - try (var chocoOut = - sc.command("choco search --local-only -r xpipe").start()) { - var out = chocoOut.readStdoutDiscardErr(); - if (chocoOut.getExitCode() == 0) { - var split = out.split("\\|"); - if (split.length == 2) { - var version = split[1]; - if (AppProperties.get().getVersion().equals(version)) { - return CHOCO; - } + var out = sc.command("choco search --local-only -r xpipe").readStdoutIfPossible(); + if (out.isPresent()) { + var split = out.get().split("\\|"); + if (split.length == 2) { + var version = split[1]; + if (AppProperties.get().getVersion().equals(version)) { + return CHOCO; } } } @@ -117,17 +114,15 @@ public enum XPipeDistributionType { // In theory, we can also add && !AppProperties.get().isStaging() here, but we want to replicate the // production behavior if (OsType.getLocal().equals(OsType.MACOS)) { - try (var brewOut = sc.command("brew list --casks --versions").start()) { - var out = brewOut.readStdoutDiscardErr(); - if (brewOut.getExitCode() == 0) { - if (out.lines().anyMatch(s -> { - var split = s.split(" "); - return split.length == 2 - && split[0].equals("xpipe") - && split[1].equals(AppProperties.get().getVersion()); - })) { - return HOMEBREW; - } + var out = sc.command("brew list --casks --versions").readStdoutIfPossible(); + if (out.isPresent()) { + if (out.get().lines().anyMatch(s -> { + var split = s.split(" "); + return split.length == 2 + && split[0].equals("xpipe") + && split[1].equals(AppProperties.get().getVersion()); + })) { + return HOMEBREW; } } } diff --git a/app/src/main/java/io/xpipe/app/util/DesktopHelper.java b/app/src/main/java/io/xpipe/app/util/DesktopHelper.java index 9185e57ba..190c20088 100644 --- a/app/src/main/java/io/xpipe/app/util/DesktopHelper.java +++ b/app/src/main/java/io/xpipe/app/util/DesktopHelper.java @@ -16,11 +16,10 @@ public class DesktopHelper { return Path.of(LocalShell.getLocalPowershell() .executeSimpleStringCommand("[Environment]::GetFolderPath([Environment+SpecialFolder]::Desktop)")); } else if (OsType.getLocal() == OsType.LINUX) { - try (var cmd = LocalShell.getShell().command("xdg-user-dir DESKTOP").start()) { - var read = cmd.readStdoutDiscardErr(); - var exit = cmd.getExitCode(); - if (exit == 0) { - return Path.of(read); + try (var sc = LocalShell.getShell().start()) { + var out = sc.command("xdg-user-dir DESKTOP").readStdoutIfPossible(); + if (out.isPresent()) { + return Path.of(out.get()); } } } @@ -34,12 +33,10 @@ public class DesktopHelper { .executeSimpleStringCommand( "(New-Object -ComObject Shell.Application).NameSpace('shell:Downloads').Self.Path")); } else if (OsType.getLocal() == OsType.LINUX) { - try (var cmd = - LocalShell.getShell().command("xdg-user-dir DOWNLOAD").start()) { - var read = cmd.readStdoutDiscardErr(); - var exit = cmd.getExitCode(); - if (exit == 0) { - return Path.of(read); + try (var sc = LocalShell.getShell().start()) { + var out = sc.command("xdg-user-dir DOWNLOAD").readStdoutIfPossible(); + if (out.isPresent()) { + return Path.of(out.get()); } } } diff --git a/app/src/main/java/io/xpipe/app/util/DesktopShortcuts.java b/app/src/main/java/io/xpipe/app/util/DesktopShortcuts.java index e93f1eee1..bde39a45f 100644 --- a/app/src/main/java/io/xpipe/app/util/DesktopShortcuts.java +++ b/app/src/main/java/io/xpipe/app/util/DesktopShortcuts.java @@ -4,27 +4,31 @@ import io.xpipe.core.process.OsType; import io.xpipe.core.util.XPipeInstallation; import java.nio.file.Files; +import java.nio.file.Path; public class DesktopShortcuts { - private static void createWindowsShortcut(String target, String name) throws Exception { + private static Path createWindowsShortcut(String executable, String args, String name) throws Exception { var icon = XPipeInstallation.getLocalDefaultInstallationIcon(); - var shortcutTarget = XPipeInstallation.getLocalDefaultCliExecutable(); var shortcutPath = DesktopHelper.getDesktopDirectory().resolve(name + ".lnk"); var content = String.format( """ - set "TARGET=%s" - set "SHORTCUT=%s" - set PWS=powershell.exe -ExecutionPolicy Restricted -NoLogo -NonInteractive -NoProfile - - %%PWS%% -Command "$ws = New-Object -ComObject WScript.Shell; $s = $ws.CreateShortcut('%%SHORTCUT%%'); $S.IconLocation='%s'; $S.WindowStyle=7; $S.TargetPath = '%%TARGET%%'; $S.Arguments = 'open %s'; $S.Save()" + $TARGET="%s" + $SHORTCUT="%s" + $ws = New-Object -ComObject WScript.Shell + $s = $ws.CreateShortcut("$SHORTCUT") + $S.IconLocation='%s' + $S.WindowStyle=7 + $S.TargetPath = "$TARGET" + $S.Arguments = '%s' + $S.Save() """, - shortcutTarget, shortcutPath, icon, target); - LocalShell.getShell().executeSimpleCommand(content); + executable, shortcutPath, icon, args); + LocalShell.getLocalPowershell().executeSimpleCommand(content); + return shortcutPath; } - private static void createLinuxShortcut(String target, String name) throws Exception { - var exec = XPipeInstallation.getLocalDefaultCliExecutable(); + private static Path createLinuxShortcut(String executable, String args, String name) throws Exception { var icon = XPipeInstallation.getLocalDefaultInstallationIcon(); var content = String.format( """ @@ -32,19 +36,19 @@ public class DesktopShortcuts { Type=Application Name=%s Comment=Open with XPipe - Exec="%s" open %s + Exec="%s" %s Icon=%s Terminal=false Categories=Utility;Development; """, - name, exec, target, icon); + name, executable, args, icon); var file = DesktopHelper.getDesktopDirectory().resolve(name + ".desktop"); Files.writeString(file, content); file.toFile().setExecutable(true); + return file; } - private static void createMacOSShortcut(String target, String name) throws Exception { - var exec = XPipeInstallation.getLocalDefaultCliExecutable(); + private static Path createMacOSShortcut(String executable, String args, String name) throws Exception { var icon = XPipeInstallation.getLocalDefaultInstallationIcon(); var base = DesktopHelper.getDesktopDirectory().resolve(name + ".app"); var content = String.format( @@ -52,18 +56,18 @@ public class DesktopShortcuts { #!/usr/bin/env sh "%s" open %s """, - exec, target); + executable, args); try (var pc = LocalShell.getShell()) { pc.getShellDialect().deleteFileOrDirectory(pc, base.toString()).executeAndCheck(); pc.executeSimpleCommand(pc.getShellDialect().getMkdirsCommand(base + "/Contents/MacOS")); pc.executeSimpleCommand(pc.getShellDialect().getMkdirsCommand(base + "/Contents/Resources")); - var executable = base + "/Contents/MacOS/" + name; + var macExec = base + "/Contents/MacOS/" + name; pc.getShellDialect() - .createScriptTextFileWriteCommand(pc, content, executable) + .createScriptTextFileWriteCommand(pc, content, macExec) .execute(); - pc.executeSimpleCommand("chmod ugo+x \"" + executable + "\""); + pc.executeSimpleCommand("chmod ugo+x \"" + macExec + "\""); pc.getShellDialect() .createTextFileWriteCommand(pc, "APPL????", base + "/Contents/PkgInfo") @@ -85,15 +89,21 @@ public class DesktopShortcuts { .execute(); pc.executeSimpleCommand("cp \"" + icon + "\" \"" + base + "/Contents/Resources/icon.icns\""); } + return base; } - public static void create(String target, String name) throws Exception { + public static Path createCliOpen(String action, String name) throws Exception { + var exec = XPipeInstallation.getLocalDefaultCliExecutable(); + return create(exec, "open " + action, name); + } + + public static Path create(String executable, String args, String name) throws Exception { if (OsType.getLocal().equals(OsType.WINDOWS)) { - createWindowsShortcut(target, name); + return createWindowsShortcut(executable, args, name); } else if (OsType.getLocal().equals(OsType.LINUX)) { - createLinuxShortcut(target, name); + return createLinuxShortcut(executable, args, name); } else { - createMacOSShortcut(target, name); + return createMacOSShortcut(executable, args, name); } } } diff --git a/app/src/main/java/io/xpipe/app/util/ScanAlert.java b/app/src/main/java/io/xpipe/app/util/ScanAlert.java index ef148f383..ddd0379e4 100644 --- a/app/src/main/java/io/xpipe/app/util/ScanAlert.java +++ b/app/src/main/java/io/xpipe/app/util/ScanAlert.java @@ -40,7 +40,7 @@ public class ScanAlert { }); } - private static void showForShellStore(DataStoreEntry initial) { + public static void showForShellStore(DataStoreEntry initial) { show(initial, (DataStoreEntry entry, ShellControl sc) -> { if (!sc.getShellDialect().getDumbMode().supportsAnyPossibleInteraction()) { return null; diff --git a/app/src/main/java/io/xpipe/app/util/ShellTemp.java b/app/src/main/java/io/xpipe/app/util/ShellTemp.java index fb5528455..9b8173fdd 100644 --- a/app/src/main/java/io/xpipe/app/util/ShellTemp.java +++ b/app/src/main/java/io/xpipe/app/util/ShellTemp.java @@ -26,7 +26,7 @@ public class ShellTemp { temp = temp.resolve(user != null ? user : "user"); try { - Files.createDirectories(temp); + FileUtils.forceMkdir(temp.toFile()); // We did not set this in earlier versions. If we are running as a different user, it might fail Files.setPosixFilePermissions(temp, PosixFilePermissions.fromString("rwxrwxrwx")); } catch (Exception e) { diff --git a/app/src/main/java/io/xpipe/app/util/StringSource.java b/app/src/main/java/io/xpipe/app/util/StringSource.java deleted file mode 100644 index b92a639be..000000000 --- a/app/src/main/java/io/xpipe/app/util/StringSource.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.xpipe.app.util; - -import io.xpipe.app.issue.ErrorEvent; -import io.xpipe.app.storage.ContextualFileReference; -import io.xpipe.core.store.ShellStore; - -import lombok.EqualsAndHashCode; -import lombok.Value; - -public abstract class StringSource { - - public abstract String get() throws Exception; - - @Value - @EqualsAndHashCode(callSuper = true) - public static class InPlace extends StringSource { - - String value; - - @Override - public String get() { - return value; - } - } - - @Value - @EqualsAndHashCode(callSuper = true) - public static class File extends StringSource { - - ShellStore host; - ContextualFileReference file; - - @Override - public String get() throws Exception { - if (host == null || file == null) { - return ""; - } - - try (var sc = host.control().start()) { - var path = file.toAbsoluteFilePath(sc); - if (!sc.getShellDialect().createFileExistsCommand(sc, path).executeAndCheck()) { - throw ErrorEvent.expected(new IllegalArgumentException("File " + path + " does not exist")); - } - - var abs = file.toAbsoluteFilePath(sc); - var content = sc.getShellDialect().getFileReadCommand(sc, abs).readStdoutOrThrow(); - return content; - } - } - } -} diff --git a/app/src/main/java/io/xpipe/app/util/WindowsRegistry.java b/app/src/main/java/io/xpipe/app/util/WindowsRegistry.java index ce952d374..a2aca1d11 100644 --- a/app/src/main/java/io/xpipe/app/util/WindowsRegistry.java +++ b/app/src/main/java/io/xpipe/app/util/WindowsRegistry.java @@ -145,23 +145,20 @@ public abstract class WindowsRegistry { .add("/v") .addQuoted(valueName); - String output; - try (var c = shellControl.command(command).start()) { - output = c.readStdoutDiscardErr(); - if (c.getExitCode() != 0) { - return Optional.empty(); - } + var output = shellControl.command(command).readStdoutIfPossible(); + if (output.isEmpty()) { + return Optional.empty(); } // Output has the following format: // \n\n\n\t\t - if (output.contains("\t")) { - String[] parsed = output.split("\t"); + if (output.get().contains("\t")) { + String[] parsed = output.get().split("\t"); return Optional.of(parsed[parsed.length - 1]); } - if (output.contains(" ")) { - String[] parsed = output.split(" "); + if (output.get().contains(" ")) { + String[] parsed = output.get().split(" "); return Optional.of(parsed[parsed.length - 1]); } @@ -176,14 +173,7 @@ public abstract class WindowsRegistry { .add("/v") .addQuoted(valueName) .add("/s"); - try (var c = shellControl.command(command).start()) { - var output = c.readStdoutDiscardErr(); - if (c.getExitCode() != 0) { - return Optional.empty(); - } else { - return Optional.of(output); - } - } + return shellControl.command(command).readStdoutIfPossible(); } @Override @@ -196,22 +186,17 @@ public abstract class WindowsRegistry { .add("/s") .add("/e") .add("/d"); - try (var c = shellControl.command(command).start()) { - var output = c.readStdoutDiscardErr(); - if (c.getExitCode() != 0) { + return shellControl.command(command).readStdoutIfPossible().flatMap(output -> { + return output.lines().findFirst().flatMap(s -> { + if (s.startsWith("HKEY_CURRENT_USER\\")) { + return Optional.of(new Key(HKEY_CURRENT_USER, s.replace("HKEY_CURRENT_USER\\", ""))); + } + if (s.startsWith("HKEY_LOCAL_MACHINE\\")) { + return Optional.of(new Key(HKEY_LOCAL_MACHINE, s.replace("HKEY_LOCAL_MACHINE\\", ""))); + } return Optional.empty(); - } else { - return output.lines().findFirst().flatMap(s -> { - if (s.startsWith("HKEY_CURRENT_USER\\")) { - return Optional.of(new Key(HKEY_CURRENT_USER, s.replace("HKEY_CURRENT_USER\\", ""))); - } - if (s.startsWith("HKEY_LOCAL_MACHINE\\")) { - return Optional.of(new Key(HKEY_LOCAL_MACHINE, s.replace("HKEY_LOCAL_MACHINE\\", ""))); - } - return Optional.empty(); - }); - } - } + }); + }); } } } diff --git a/app/src/main/resources/io/xpipe/app/resources/misc/welcome.md b/app/src/main/resources/io/xpipe/app/resources/misc/welcome.md index 3d99b6073..9b4c4dc32 100644 --- a/app/src/main/resources/io/xpipe/app/resources/misc/welcome.md +++ b/app/src/main/resources/io/xpipe/app/resources/misc/welcome.md @@ -1,11 +1,10 @@ ## Welcome -Thank you for using XPipe! +Welcome to XPipe! + You can view the development status, report issues, and more at the following places: - [GitHub Repository](https://github.com/xpipe-io/xpipe/) - [Discord Server](https://discord.gg/8y89vS8cRb) - [Slack Server](https://join.slack.com/t/XPipe/shared_invite/zt-1awjq0t5j-5i4UjNJfNe1VN4b_auu6Cg) -- [Email me](mailto://crschnick@xpipe.io) - -Note that the XPipe project currently is a one-man show, but I still try to respond to everything in time. +- [Email us](mailto://hello@xpipe.io) diff --git a/app/src/main/resources/io/xpipe/app/resources/style/section-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/section-comp.css index 9bc8f7283..df2abab39 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/section-comp.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/section-comp.css @@ -18,4 +18,4 @@ .options-comp .long-description { -fx-padding: 0 6 0 6; -} \ No newline at end of file +} diff --git a/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css index 29fb04993..5710d3738 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css @@ -25,14 +25,6 @@ -fx-text-fill: #ee4829; } -.store-entry-grid:incomplete .summary { - -fx-text-fill: #ee4829; -} - -.store-entry-grid:incomplete .information { - -fx-text-fill: #ee4829; -} - .store-entry-grid:incomplete .icon { -fx-opacity: 0.5; } diff --git a/app/src/main/resources/io/xpipe/app/resources/style/style.css b/app/src/main/resources/io/xpipe/app/resources/style/style.css index 0d3ea3492..7a27b0317 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/style.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/style.css @@ -101,7 +101,7 @@ -fx-border-color: -color-neutral-emphasis; } -.text { +* { -fx-font-smoothing-type: gray; } diff --git a/beacon/src/main/java/io/xpipe/beacon/BeaconClient.java b/beacon/src/main/java/io/xpipe/beacon/BeaconClient.java index 26867ef94..16dbbec7e 100644 --- a/beacon/src/main/java/io/xpipe/beacon/BeaconClient.java +++ b/beacon/src/main/java/io/xpipe/beacon/BeaconClient.java @@ -64,7 +64,8 @@ public class BeaconClient { var client = HttpClient.newHttpClient(); HttpResponse response; try { - var uri = URI.create("http://localhost:" + port + prov.getPath()); + // Use direct IP to prevent DNS lookups and potential blocks (e.g. portmaster) + var uri = URI.create("http://127.0.0.1:" + port + prov.getPath()); var builder = HttpRequest.newBuilder(); if (token != null) { builder.header("Authorization", "Bearer " + token); diff --git a/beacon/src/main/java/io/xpipe/beacon/BeaconServer.java b/beacon/src/main/java/io/xpipe/beacon/BeaconServer.java index 3de5f910b..838058c45 100644 --- a/beacon/src/main/java/io/xpipe/beacon/BeaconServer.java +++ b/beacon/src/main/java/io/xpipe/beacon/BeaconServer.java @@ -8,7 +8,7 @@ import io.xpipe.core.util.XPipeInstallation; import java.io.BufferedReader; import java.io.InputStreamReader; -import java.net.InetAddress; +import java.net.Inet4Address; import java.net.InetSocketAddress; import java.net.Socket; import java.util.List; @@ -20,7 +20,7 @@ public class BeaconServer { public static boolean isReachable(int port) { try (var socket = new Socket()) { - socket.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), port), 5000); + socket.connect(new InetSocketAddress(Inet4Address.getByAddress(new byte[]{ 0x7f,0x00,0x00,0x01 }), port), 5000); return true; } catch (Exception e) { return false; diff --git a/build.gradle b/build.gradle index 5a254d46c..d6ead746a 100644 --- a/build.gradle +++ b/build.gradle @@ -85,13 +85,13 @@ def getArchName() { def getPlatformName() { def currentOS = DefaultNativePlatform.currentOperatingSystem; - def platform + def platform = null if (currentOS.isWindows()) { platform = 'windows' - } else if (currentOS.isMacOsX()) { - platform = 'osx' - } else { + } else if (currentOS.isLinux()) { platform = 'linux' + } else if (currentOS.isMacOsX()) { + platform = 'osx' } return platform; } @@ -145,8 +145,8 @@ project.ext { "-Dvisualvm.display.name=XPipe", "-Djavafx.preloader=io.xpipe.app.core.AppPreloader" ] - // Disable this on Windows for now as it requires Windows 10+ - if (org.gradle.internal.os.OperatingSystem.current().isLinux() || org.gradle.internal.os.OperatingSystem.current().isMacOsX()) { + // Disable this for now as it requires Windows 10+ + if (!org.gradle.internal.os.OperatingSystem.current().isWindows()) { jvmRunArgs += ['-XX:+UseZGC'] } if (org.gradle.internal.os.OperatingSystem.current().isMacOsX()) { diff --git a/core/src/main/java/io/xpipe/core/process/CommandBuilder.java b/core/src/main/java/io/xpipe/core/process/CommandBuilder.java index 3e2904845..fa9b90a41 100644 --- a/core/src/main/java/io/xpipe/core/process/CommandBuilder.java +++ b/core/src/main/java/io/xpipe/core/process/CommandBuilder.java @@ -255,6 +255,10 @@ public class CommandBuilder { } public String buildFull(ShellControl sc) throws Exception { + if (sc == null) { + return buildSimple(); + } + var s = buildBase(sc); LinkedHashMap map = new LinkedHashMap<>(); for (var e : environmentVariables.entrySet()) { diff --git a/core/src/main/java/io/xpipe/core/process/CommandControl.java b/core/src/main/java/io/xpipe/core/process/CommandControl.java index 1e4dc6070..2e76586e0 100644 --- a/core/src/main/java/io/xpipe/core/process/CommandControl.java +++ b/core/src/main/java/io/xpipe/core/process/CommandControl.java @@ -1,16 +1,10 @@ package io.xpipe.core.process; -import io.xpipe.core.util.FailableConsumer; - -import com.fasterxml.jackson.databind.JsonNode; - import java.io.InputStream; -import java.io.InputStreamReader; import java.io.OutputStream; import java.nio.charset.Charset; import java.time.Duration; import java.util.Optional; -import java.util.function.Consumer; import java.util.function.Function; public interface CommandControl extends ProcessControl { @@ -70,32 +64,14 @@ public interface CommandControl extends ProcessControl { CommandControl elevated(ElevationFunction function); - void withStdoutOrThrow(FailableConsumer c); - String[] readStdoutAndStderr() throws Exception; - String readStdoutDiscardErr() throws Exception; - - String readStderrDiscardStdout() throws Exception; - void discardOrThrow() throws Exception; - void accumulateStdout(Consumer con); - - void accumulateStderr(Consumer con); - byte[] readRawBytesOrThrow() throws Exception; String readStdoutOrThrow() throws Exception; - JsonNode readStdoutJsonOrThrow() throws Exception; - - String readStderrOrThrow() throws Exception; - - String readStdoutAndWait() throws Exception; - - String readStderrAndWait() throws Exception; - Optional readStdoutIfPossible() throws Exception; default boolean discardAndCheckExit() throws ProcessOutputException { @@ -113,10 +89,6 @@ public interface CommandControl extends ProcessControl { } } - void discardOut(); - - void discardErr(); - enum TerminalExitMode { KEEP_OPEN, CLOSE diff --git a/core/src/main/java/io/xpipe/core/process/CommandFeedbackPredicate.java b/core/src/main/java/io/xpipe/core/process/CommandFeedbackPredicate.java deleted file mode 100644 index 24d569580..000000000 --- a/core/src/main/java/io/xpipe/core/process/CommandFeedbackPredicate.java +++ /dev/null @@ -1,6 +0,0 @@ -package io.xpipe.core.process; - -public interface CommandFeedbackPredicate { - - boolean test(CommandBuilder command); -} diff --git a/core/src/main/java/io/xpipe/core/process/OsType.java b/core/src/main/java/io/xpipe/core/process/OsType.java index 8b943fca7..3bc7ba70d 100644 --- a/core/src/main/java/io/xpipe/core/process/OsType.java +++ b/core/src/main/java/io/xpipe/core/process/OsType.java @@ -21,8 +21,10 @@ public interface OsType { return MACOS; } else if (osName.contains("win")) { return WINDOWS; - } else { + } else if (osName.contains("nux")) { return LINUX; + } else { + throw new UnsupportedOperationException("Unknown operating system"); } } @@ -181,19 +183,15 @@ public interface OsType { @Override public String determineOperatingSystemName(ShellControl pc) throws Exception { String type = "Unknown"; - try (CommandControl c = pc.command("uname -o").start()) { - var text = c.readStdoutDiscardErr(); - if (c.getExitCode() == 0) { - type = text.strip(); - } + var uname = pc.command("uname -o").readStdoutIfPossible(); + if (uname.isPresent()) { + type = uname.get(); } String version = "?"; - try (CommandControl c = pc.command("uname -r").start()) { - var text = c.readStdoutDiscardErr(); - if (c.getExitCode() == 0) { - version = text.strip(); - } + var unameR = pc.command("uname -r").readStdoutIfPossible(); + if (unameR.isPresent()) { + version = unameR.get(); } return type + " " + version; @@ -209,18 +207,14 @@ public interface OsType { @Override public String determineOperatingSystemName(ShellControl pc) throws Exception { - try (CommandControl c = pc.command("lsb_release -a").start()) { - var text = c.readStdoutDiscardErr(); - if (c.getExitCode() == 0) { - return PropertiesFormatsParser.parse(text, ":").getOrDefault("Description", "Unknown"); - } + var rel = pc.command("lsb_release -a").readStdoutIfPossible(); + if (rel.isPresent()) { + return PropertiesFormatsParser.parse(rel.get(), ":").getOrDefault("Description", "Unknown"); } - try (CommandControl c = pc.command("cat /etc/*release").start()) { - var text = c.readStdoutDiscardErr(); - if (c.getExitCode() == 0) { - return PropertiesFormatsParser.parse(text, "=").getOrDefault("PRETTY_NAME", "Unknown"); - } + var cat = pc.command("cat /etc/*release").readStdoutIfPossible(); + if (cat.isPresent()) { + return PropertiesFormatsParser.parse(cat.get(), "=").getOrDefault("PRETTY_NAME", "Unknown"); } return super.determineOperatingSystemName(pc); 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 3c1efe058..9c29ef9e7 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellControl.java +++ b/core/src/main/java/io/xpipe/core/process/ShellControl.java @@ -18,6 +18,8 @@ import java.util.function.Function; public interface ShellControl extends ProcessControl { + ShellTtyState getTtyState(); + void setNonInteractive(); boolean isInteractive(); @@ -113,25 +115,12 @@ public interface ShellControl extends ProcessControl { script)); } - default byte[] executeSimpleRawBytesCommand(String command) throws Exception { - try (CommandControl c = command(command).start()) { - return c.readRawBytesOrThrow(); - } - } - default String executeSimpleStringCommand(String command) throws Exception { try (CommandControl c = command(command).start()) { return c.readStdoutOrThrow(); } } - default Optional executeSimpleStringCommandAndCheck(String command) throws Exception { - try (CommandControl c = command(command).start()) { - var out = c.readStdoutDiscardErr(); - return c.getExitCode() == 0 ? Optional.of(out) : Optional.empty(); - } - } - default boolean executeSimpleBooleanCommand(String command) throws Exception { try (CommandControl c = command(command).start()) { return c.discardAndCheckExit(); @@ -150,20 +139,6 @@ public interface ShellControl extends ProcessControl { } } - default void executeSimpleCommand(String command, String failMessage) throws Exception { - try (CommandControl c = command(command).start()) { - c.discardOrThrow(); - } catch (ProcessOutputException out) { - throw ProcessOutputException.withPrefix(failMessage, out); - } - } - - default String executeSimpleStringCommand(ShellDialect type, String command) throws Exception { - try (var sub = subShell(type).start()) { - return sub.executeSimpleStringCommand(command); - } - } - ShellControl withSecurityPolicy(ShellSecurityPolicy policy); ShellSecurityPolicy getEffectiveSecurityPolicy(); diff --git a/core/src/main/java/io/xpipe/core/process/ShellDialect.java b/core/src/main/java/io/xpipe/core/process/ShellDialect.java index 231a7b30f..8c19b108e 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellDialect.java +++ b/core/src/main/java/io/xpipe/core/process/ShellDialect.java @@ -130,7 +130,7 @@ public interface ShellDialect { default void prepareCommandForShell(CommandBuilder b) {} - String prepareTerminalInitFileOpenCommand(ShellDialect parentDialect, ShellControl sc, String file); + String prepareTerminalInitFileOpenCommand(ShellDialect parentDialect, ShellControl sc, String file, boolean exit); String runScriptCommand(ShellControl parent, String file); @@ -184,5 +184,5 @@ public interface ShellDialect { String getDisplayName(); - boolean doesEchoInput(); + boolean doesEchoInputByDefault(); } diff --git a/core/src/main/java/io/xpipe/core/process/ShellDumbMode.java b/core/src/main/java/io/xpipe/core/process/ShellDumbMode.java index 20573ed72..3faa460c2 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellDumbMode.java +++ b/core/src/main/java/io/xpipe/core/process/ShellDumbMode.java @@ -8,6 +8,8 @@ public interface ShellDumbMode { return true; } + default void throwIfUnsupported() {} + default ShellDialect getSwitchDialect() { return null; } @@ -25,6 +27,14 @@ public interface ShellDumbMode { class Unsupported implements ShellDumbMode { + private final String message; + + public Unsupported(String message) {this.message = message;} + + public void throwIfUnsupported() { + throw new UnsupportedOperationException(message); + } + @Override public boolean supportsAnyPossibleInteraction() { return false; diff --git a/core/src/main/java/io/xpipe/core/process/ShellProperties.java b/core/src/main/java/io/xpipe/core/process/ShellProperties.java deleted file mode 100644 index b8df6d34d..000000000 --- a/core/src/main/java/io/xpipe/core/process/ShellProperties.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.xpipe.core.process; - -import lombok.Value; - -@Value -public class ShellProperties { - - ShellDialect dialect; - boolean ansiEscapes; -} diff --git a/core/src/main/java/io/xpipe/core/process/ShellTtyState.java b/core/src/main/java/io/xpipe/core/process/ShellTtyState.java new file mode 100644 index 000000000..d27745c57 --- /dev/null +++ b/core/src/main/java/io/xpipe/core/process/ShellTtyState.java @@ -0,0 +1,25 @@ +package io.xpipe.core.process; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +@Getter +public enum ShellTtyState { + + @JsonProperty("none") + NONE(true, false, false), + @JsonProperty("merged") + MERGED_STDERR(false, false, false), + @JsonProperty("pty") + PTY_ALLOCATED(false, true, true); + + private final boolean hasSeparateStreams; + private final boolean hasAnsiEscapes; + private final boolean echoesAllInput; + + ShellTtyState(boolean hasSeparateStreams, boolean hasAnsiEscapes, boolean echoesAllInput) { + this.hasSeparateStreams = hasSeparateStreams; + this.hasAnsiEscapes = hasAnsiEscapes; + this.echoesAllInput = echoesAllInput; + } +} diff --git a/core/src/main/java/io/xpipe/core/store/ConnectionFileSystem.java b/core/src/main/java/io/xpipe/core/store/ConnectionFileSystem.java index 22f0f42a3..c5331bbc9 100644 --- a/core/src/main/java/io/xpipe/core/store/ConnectionFileSystem.java +++ b/core/src/main/java/io/xpipe/core/store/ConnectionFileSystem.java @@ -1,9 +1,8 @@ package io.xpipe.core.store; +import com.fasterxml.jackson.annotation.JsonIgnore; import io.xpipe.core.process.CommandBuilder; import io.xpipe.core.process.ShellControl; - -import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; import java.io.InputStream; @@ -37,9 +36,10 @@ public class ConnectionFileSystem implements FileSystem { @Override public FileSystem open() throws Exception { shellControl.start(); - if (!shellControl.getShellDialect().getDumbMode().supportsAnyPossibleInteraction()) { + var d = shellControl.getShellDialect().getDumbMode(); + if (!d.supportsAnyPossibleInteraction()) { shellControl.close(); - throw new UnsupportedOperationException("System shell does not support file system interaction"); + d.throwIfUnsupported(); } return this; } diff --git a/core/src/main/java/io/xpipe/core/store/EnabledStoreState.java b/core/src/main/java/io/xpipe/core/store/EnabledStoreState.java index b805b7e96..e91e40b84 100644 --- a/core/src/main/java/io/xpipe/core/store/EnabledStoreState.java +++ b/core/src/main/java/io/xpipe/core/store/EnabledStoreState.java @@ -19,6 +19,6 @@ public class EnabledStoreState extends DataStoreState { @Override public DataStoreState mergeCopy(DataStoreState newer) { var n = (EnabledStoreState) newer; - return EnabledStoreState.builder().enabled(n.enabled).build(); + return EnabledStoreState.builder().enabled(enabled || n.enabled).build(); } } diff --git a/dist/changelogs/10.3.md b/dist/changelogs/10.3.md deleted file mode 100644 index 25037ee47..000000000 --- a/dist/changelogs/10.3.md +++ /dev/null @@ -1,67 +0,0 @@ -## A new HTTP API - -There is now a new HTTP API for the XPipe daemon, which allows you to programmatically manage remote systems. You can find details and an OpenAPI specification at the new API button in the sidebar. The API page contains everything you need to get started, including code samples for various different programming languages. - -To start off, you can query connections based on various filters. With the matched connections, you can start remote shell sessions for each one and run arbitrary commands in them. You get the command exit code and output as a response, allowing you to adapt your control flow based on command outputs. Any kind of passwords and other secrets are automatically provided by XPipe when establishing a shell connection. You can also access the file systems via these shell connections to read and write remote files. - -There already exists a community made [XPipe API library for python](https://github.com/coandco/python_xpipe_client) and a [python CLI client](https://github.com/coandco/xpipe_cli). These tools allow you to interact with the API more ergonomically and can also serve as an inspiration of what you can do with the new API. If you also built a tool to interact with the XPipe API, you can let me know and I can compile a list of community development projects. - -## Service integration - -Many systems run a variety of different services such as web services and others. There is now support to detect, forward, and open the services. For example, if you are running a web service on a remote container, you can automatically forward the service port via SSH tunnels, allowing you to access these services from your local machine, e.g. in a web browser. These service tunnels can be toggled at any time. The port forwarding supports specifying a custom local target port and also works for connections with multiple intermediate systems through chained tunnels. For containers, services are automatically detected via their exposed mapped ports. For other systems, you can manually add services via their port. - -You can use an unlimited amount of local services and one active tunneled service in the community edition. - -## Script rework - -The scripting system has been reworked. There have been several issues with it being clunky and not fun to use. The new system allows you to assign each script one of multiple execution types. Based on these execution types, you can make scripts active or inactive with a toggle. If they are active, the scripts will apply in the selected use cases. There currently are these types: -- Init scripts: When enabled, they will automatically run on init in all compatible shells. This is useful for setting things like aliases consistently -- Shell scripts: When enabled, they will be copied over to the target system and put into the PATH. You can then call them in a normal shell session by their name, e.g. `myscript.sh`, also with arguments. -- File scripts: When enabled, you can call them in the file browser with the selected files as arguments. Useful to perform common actions with files - -If you have existing scripts, they will have to be manually adjusted by setting their execution types. - -## Docker improvements - -The docker integration has been updated to support docker contexts. You can use the default context in the community edition, essentially being the same as before as XPipe previously only used the default context. Support for using multiple contexts is included in the professional edition. - -There's now support for Windows docker containers running on HyperV. - -Note that old docker container connections will be removed as they are incompatible with the new version. - -## Proxmox improvements - -You can now automatically open the Proxmox dashboard website through the new service integration. This will also work with the service tunneling feature for remote servers. - -You can now open VNC sessions to Proxmox VMs. - -The Proxmox professional license requirement has been reworked to support one non-enterprise PVE node in the community edition. - -## Better connection organization - -The toggle to show only running connections will now no longer actually remove the connections internally and instead just not display them. This will reduce git vault updates and is faster in general. - -You can now order connections relative to other sibling connections. This ordering will also persist when changing the global order in the top left. - -The UI has also been streamlined to make common actions and toggles more easily accessible. - -## Other - -- The title bar on Windows will now follow the appearance theme -- Several more actions have been added for podman containers -- Support VMs for tunneling -- Searching for connections has been improved to show children as well -- There is now an AppImage portable release -- The welcome screen will now also contain the option to straight up jump to the synchronization settings -- You can now launch xpipe in another data directory with `xpipe open -d ""` -- Add option to use double clicks to open connections instead of single clicks -- Add support for foot terminal -- Fix rare null pointers and freezes in file browser -- Fix PowerShell remote session file editing not transferring file correctly -- Fix elementary terminal not launching correctly -- Fix windows jumping around when created -- Fix kubernetes not elevating correctly for non-default contexts -- Fix ohmyzsh update notification freezing shell -- Fix file browser icons being broken for links -- The Linux installers now contain application icons from multiple sizes which should increase the icon display quality -- The Linux builds now list socat as a dependency such that the kitty terminal integration will work without issues diff --git a/dist/changelogs/10.3_incremental.md b/dist/changelogs/11.0.md similarity index 59% rename from dist/changelogs/10.3_incremental.md rename to dist/changelogs/11.0.md index 70979031f..3dc3e1336 100644 --- a/dist/changelogs/10.3_incremental.md +++ b/dist/changelogs/11.0.md @@ -1,3 +1,13 @@ +## Profiles + +You can now create multiple user profiles in the settings menu. + +This will create desktop shortcuts that you can use to start XPipe with different profiles active. + +## Serial connection support + +There is now support to add serial connections. + ## Scripting improvements The scripting system has been reworked in order to make it more intuitive and powerful. @@ -12,6 +22,13 @@ When multiple files are selected, a script is now called only once with all the ## Other +- Rework state information display for proxmox VMs +- Fix terminal exit not working properly in fish +- Fix renaming a connection clearing all state information +- Fix script enabled status being wrong after editing an enabled script - Fix download move operation failing when moving a directory that already existed in the downloads folder +- Fix some scrollbars are necessarily showing +- Improve error messages when system interaction was disabled for a system +- Don't show git all compatibility warnings on minor version updates - Enable ZGC on Linux and macOS - Some small appearance fixes \ No newline at end of file diff --git a/dist/changelogs/11.0_incremental.md b/dist/changelogs/11.0_incremental.md new file mode 100644 index 000000000..3dc3e1336 --- /dev/null +++ b/dist/changelogs/11.0_incremental.md @@ -0,0 +1,34 @@ +## Profiles + +You can now create multiple user profiles in the settings menu. + +This will create desktop shortcuts that you can use to start XPipe with different profiles active. + +## Serial connection support + +There is now support to add serial connections. + +## Scripting improvements + +The scripting system has been reworked in order to make it more intuitive and powerful. + +The script execution types have been renamed, the documentation has been improved, and a new execution type has been added. +The new runnable execution type will allow you to call a script from the connection hub directly in a dropdown for each connection when the script is active. +This will also replace the current terminal command functionality, which has been removed. + +Any file browser scripts are now grouped by the scripts groups they are in, improving the overview when having many file browser scripts. +Furthermore, you can now launch these scripts in the file browser either in the background if they are quiet or in a terminal if they are intended to be interactive. +When multiple files are selected, a script is now called only once with all the selected files as arguments. + +## Other + +- Rework state information display for proxmox VMs +- Fix terminal exit not working properly in fish +- Fix renaming a connection clearing all state information +- Fix script enabled status being wrong after editing an enabled script +- Fix download move operation failing when moving a directory that already existed in the downloads folder +- Fix some scrollbars are necessarily showing +- Improve error messages when system interaction was disabled for a system +- Don't show git all compatibility warnings on minor version updates +- Enable ZGC on Linux and macOS +- Some small appearance fixes \ No newline at end of file diff --git a/ext/base/src/main/java/io/xpipe/ext/base/action/RunScriptActionMenu.java b/ext/base/src/main/java/io/xpipe/ext/base/action/RunScriptActionMenu.java index 94808806c..8380aa77b 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/action/RunScriptActionMenu.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/RunScriptActionMenu.java @@ -3,6 +3,7 @@ package io.xpipe.ext.base.action; import io.xpipe.app.comp.store.StoreViewState; import io.xpipe.app.core.AppI18n; import io.xpipe.app.ext.ActionProvider; +import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.storage.DataStoreEntryRef; import io.xpipe.app.util.TerminalLauncher; import io.xpipe.core.process.ShellStoreState; @@ -17,7 +18,7 @@ import java.util.List; public class RunScriptActionMenu implements ActionProvider { @Value - private static class ScriptActionProvider implements ActionProvider { + private static class TerminalRunActionProvider implements ActionProvider { ScriptHierarchy hierarchy; @@ -41,10 +42,6 @@ public class RunScriptActionMenu implements ActionProvider { @Override public LeafDataStoreCallSite getLeafDataStoreCallSite() { - if (!hierarchy.isLeaf()) { - return null; - } - return new LeafDataStoreCallSite() { @Override public Action createAction(DataStoreEntryRef store) { @@ -53,12 +50,15 @@ public class RunScriptActionMenu implements ActionProvider { @Override public ObservableValue getName(DataStoreEntryRef store) { - return new SimpleStringProperty(hierarchy.getBase().get().getName()); + var t = AppPrefs.get().terminalType().getValue(); + return AppI18n.observable( + "executeInTerminal", + t != null ? t.toTranslatedString().getValue() : "?"); } @Override public String getIcon(DataStoreEntryRef store) { - return "mdi2p-play-box-multiple-outline"; + return "mdi2d-desktop-mac"; } @Override @@ -67,10 +67,91 @@ public class RunScriptActionMenu implements ActionProvider { } }; } + } + + @Value + private static class BackgroundRunActionProvider implements ActionProvider { + + ScriptHierarchy hierarchy; + + @Value + private class Action implements ActionProvider.Action { + + DataStoreEntryRef shellStore; + + @Override + public void execute() throws Exception { + try (var sc = shellStore.getStore().control().start()) { + var script = hierarchy.getLeafBase().getStore().assembleScriptChain(sc); + sc.command(script).execute(); + } + } + } + + @Override + public LeafDataStoreCallSite getLeafDataStoreCallSite() { + return new LeafDataStoreCallSite() { + @Override + public Action createAction(DataStoreEntryRef store) { + return new Action(store); + } + + @Override + public ObservableValue getName(DataStoreEntryRef store) { + return AppI18n.observable("executeInBackground"); + } + + @Override + public String getIcon(DataStoreEntryRef store) { + return "mdi2f-flip-to-back"; + } + + @Override + public Class getApplicableClass() { + return ShellStore.class; + } + }; + } + } + + @Value + private static class ScriptActionProvider implements ActionProvider { + + ScriptHierarchy hierarchy; + + private BranchDataStoreCallSite getLeafSite() { + return new BranchDataStoreCallSite() { + + @Override + public Class getApplicableClass() { + return ShellStore.class; + } + + @Override + public ObservableValue getName(DataStoreEntryRef store) { + return new SimpleStringProperty(hierarchy.getBase().get().getName()); + } + + @Override + public boolean isDynamicallyGenerated() { + return true; + } + + @Override + public String getIcon(DataStoreEntryRef store) { + return "mdi2p-play-box-multiple-outline"; + } + + @Override + public List getChildren(DataStoreEntryRef store) { + return List.of(new TerminalRunActionProvider(hierarchy), new BackgroundRunActionProvider(hierarchy)); + } + }; + } public BranchDataStoreCallSite getBranchDataStoreCallSite() { if (hierarchy.isLeaf()) { - return null; + return getLeafSite(); } return new BranchDataStoreCallSite() { diff --git a/ext/base/src/main/java/io/xpipe/ext/base/action/SampleStoreAction.java b/ext/base/src/main/java/io/xpipe/ext/base/action/SampleStoreAction.java index 27b2db2d5..77c8bc5eb 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/action/SampleStoreAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/SampleStoreAction.java @@ -10,13 +10,11 @@ import io.xpipe.core.process.ShellControl; import io.xpipe.core.process.ShellDialects; import io.xpipe.core.store.LocalStore; import io.xpipe.core.store.ShellStore; - import javafx.beans.value.ObservableValue; - import lombok.Value; import java.io.BufferedReader; -import java.io.InputStreamReader; +import java.io.StringReader; public class SampleStoreAction implements ActionProvider { @@ -83,23 +81,14 @@ public class SampleStoreAction implements ActionProvider { sc.executeSimpleStringCommand(sc.getShellDialect().getEchoCommand("hello!", false)); // You can also implement custom handling for more complex commands - try (CommandControl cc = sc.command("ls").start()) { - // Discard stderr - cc.discardErr(); - - // Read the stdout lines as a stream - BufferedReader reader = new BufferedReader(new InputStreamReader(cc.getStdout(), cc.getCharset())); - // We don't have to close this stream here, that will be automatically done by the command control - // after the try-with block - reader.lines().filter(s -> !s.isBlank()).forEach(s -> { - System.out.println(s); - }); - - // Waits for command completion and returns exit code - if (cc.getExitCode() != 0) { - // Handle failure - } - } + var lsOut = sc.command("ls").readStdoutOrThrow(); + // Read the stdout lines as a stream + BufferedReader reader = new BufferedReader(new StringReader(lsOut)); + // We don't have to close this stream here, that will be automatically done by the command control + // after the try-with block + reader.lines().filter(s -> !s.isBlank()).forEach(s -> { + System.out.println(s); + }); // Commands can also be more complex and span multiple lines. // In this case, XPipe will internally write a command to a script file and then execute the script diff --git a/ext/base/src/main/java/io/xpipe/ext/base/action/ScanStoreAction.java b/ext/base/src/main/java/io/xpipe/ext/base/action/ScanStoreAction.java index 7064ff551..a236f930c 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/action/ScanStoreAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/ScanStoreAction.java @@ -7,9 +7,7 @@ import io.xpipe.app.storage.DataStoreEntryRef; import io.xpipe.app.util.ScanAlert; import io.xpipe.core.process.ShellStoreState; import io.xpipe.core.store.ShellStore; - import javafx.beans.value.ObservableValue; - import lombok.Value; public class ScanStoreAction implements ActionProvider { @@ -67,7 +65,9 @@ public class ScanStoreAction implements ActionProvider { @Override public void execute() { - ScanAlert.showAsync(entry); + if (entry == null || entry.getStore() instanceof ShellStore) { + ScanAlert.showForShellStore(entry); + } } } } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/browser/ToFileCommandAction.java b/ext/base/src/main/java/io/xpipe/ext/base/browser/ToFileCommandAction.java index a9a1de88b..08001e17c 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/browser/ToFileCommandAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/browser/ToFileCommandAction.java @@ -16,12 +16,10 @@ public abstract class ToFileCommandAction implements LeafAction, ApplicationPath ShellControl sc = model.getFileSystem().getShell().orElseThrow(); for (BrowserEntry entry : entries) { var command = createCommand(model, entry); - try (var cc = sc.command(command) + var out = sc.command(command) .withWorkingDirectory(model.getCurrentDirectory().getPath()) - .start()) { - cc.discardErr(); - FileOpener.openCommandOutput(entry.getFileName(), entry, cc); - } + .readStdoutOrThrow(); + FileOpener.openReadOnlyString(out); } } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/desktop/DesktopEnvironmentStore.java b/ext/base/src/main/java/io/xpipe/ext/base/desktop/DesktopEnvironmentStore.java index e173422eb..a4b07e363 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/desktop/DesktopEnvironmentStore.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/desktop/DesktopEnvironmentStore.java @@ -97,7 +97,7 @@ public class DesktopEnvironmentStore extends JacksonizedValue var scriptFile = base.getStore().createScript(dialect, toExecute); var launchScriptFile = base.getStore() .createScript( - dialect, dialect.prepareTerminalInitFileOpenCommand(dialect, null, scriptFile.toString())); + dialect, dialect.prepareTerminalInitFileOpenCommand(dialect, null, scriptFile.toString(), false)); var launchConfig = new ExternalTerminalType.LaunchConfiguration(null, name, name, launchScriptFile, dialect); base.getStore().runDesktopScript(name, launchCommand.apply(launchConfig)); } diff --git a/gradle/gradle_scripts/javafx.gradle b/gradle/gradle_scripts/javafx.gradle index 243b8d91c..8720c92d0 100644 --- a/gradle/gradle_scripts/javafx.gradle +++ b/gradle/gradle_scripts/javafx.gradle @@ -4,10 +4,10 @@ def currentOS = DefaultNativePlatform.currentOperatingSystem; def platform = null if (currentOS.isWindows()) { platform = 'win' +} else if (currentOS.isLinux()) { + platform = 'linux' } else if (currentOS.isMacOsX()) { platform = 'mac' -} else { - platform = 'linux' } if (System.getProperty ("os.arch") == 'aarch64') { diff --git a/lang/app/strings/translations_da.properties b/lang/app/strings/translations_da.properties index b1347c8de..2a94ff634 100644 --- a/lang/app/strings/translations_da.properties +++ b/lang/app/strings/translations_da.properties @@ -488,3 +488,15 @@ closeOtherTabs=Luk andre faner closeAllTabs=Luk alle faner closeLeftTabs=Luk faner til venstre closeRightTabs=Luk faner til højre +addSerial=Seriel ... +connect=Forbind +workspaces=Arbejdsområder +manageWorkspaces=Administrer arbejdsområder +addWorkspace=Tilføj arbejdsområde ... +workspaceAdd=Tilføj et nyt arbejdsområde +workspaceAddDescription=Arbejdsområder er forskellige konfigurationer til at køre XPipe. Hvert arbejdsområde har et datakatalog, hvor alle data gemmes lokalt. Det omfatter forbindelsesdata, indstillinger og meget mere.\n\nHvis du bruger synkroniseringsfunktionen, kan du også vælge at synkronisere hvert arbejdsområde med et forskelligt git-repository. +workspaceName=Navn på arbejdsområde +workspaceNameDescription=Visningsnavnet på arbejdsområdet +workspacePath=Sti til arbejdsområde +workspacePathDescription=Placeringen af arbejdsområdets datakatalog +workspaceCreationAlertTitle=Oprettelse af arbejdsområde diff --git a/lang/app/strings/translations_de.properties b/lang/app/strings/translations_de.properties index d963e2345..bec06bc85 100644 --- a/lang/app/strings/translations_de.properties +++ b/lang/app/strings/translations_de.properties @@ -482,3 +482,15 @@ closeOtherTabs=Andere Tabs schließen closeAllTabs=Alle Registerkarten schließen closeLeftTabs=Tabs nach links schließen closeRightTabs=Tabs nach rechts schließen +addSerial=Serielle ... +connect=Verbinden +workspaces=Arbeitsbereiche +manageWorkspaces=Arbeitsbereiche verwalten +addWorkspace=Arbeitsbereich hinzufügen ... +workspaceAdd=Einen neuen Arbeitsbereich hinzufügen +workspaceAddDescription=Arbeitsbereiche sind unterschiedliche Konfigurationen für die Ausführung von XPipe. Jeder Arbeitsbereich hat ein Datenverzeichnis, in dem alle Daten lokal gespeichert werden. Dazu gehören Verbindungsdaten, Einstellungen und mehr.\n\nWenn du die Synchronisierungsfunktion verwendest, kannst du auch wählen, ob du jeden Arbeitsbereich mit einem anderen Git-Repository synchronisieren möchtest. +workspaceName=Name des Arbeitsbereichs +workspaceNameDescription=Der Anzeigename des Arbeitsbereichs +workspacePath=Pfad zum Arbeitsbereich +workspacePathDescription=Der Ort des Datenverzeichnisses des Arbeitsbereichs +workspaceCreationAlertTitle=Arbeitsbereich erstellen diff --git a/lang/app/strings/translations_en.properties b/lang/app/strings/translations_en.properties index 77d12ea51..0d3a8059d 100644 --- a/lang/app/strings/translations_en.properties +++ b/lang/app/strings/translations_en.properties @@ -486,3 +486,15 @@ closeOtherTabs=Close other tabs closeAllTabs=Close all tabs closeLeftTabs=Close tabs to the left closeRightTabs=Close tabs to the right +addSerial=Serial ... +connect=Connect +workspaces=Workspaces +manageWorkspaces=Manage workspaces +addWorkspace=Add workspace ... +workspaceAdd=Add a new workspace +workspaceAddDescription=Workspaces are distinct configurations for running XPipe. Every workspace has a data directory where all data is stored locally. This includes connection data, settings, and more.\n\nIf you use the synchronization feature, you can also choose to synchronize each workspace with a different git repository. +workspaceName=Workspace name +workspaceNameDescription=The display name of the workspace +workspacePath=Workspace path +workspacePathDescription=The location of the workspace data directory +workspaceCreationAlertTitle=Workspace creation diff --git a/lang/app/strings/translations_es.properties b/lang/app/strings/translations_es.properties index 9e70ffc59..d95a5fcb0 100644 --- a/lang/app/strings/translations_es.properties +++ b/lang/app/strings/translations_es.properties @@ -469,3 +469,15 @@ closeOtherTabs=Cerrar otras pestañas closeAllTabs=Cerrar todas las pestañas closeLeftTabs=Cerrar pestañas a la izquierda closeRightTabs=Cerrar pestañas a la derecha +addSerial=Serie ... +connect=Conecta +workspaces=Espacios de trabajo +manageWorkspaces=Gestionar espacios de trabajo +addWorkspace=Añadir espacio de trabajo ... +workspaceAdd=Añadir un nuevo espacio de trabajo +workspaceAddDescription=Los espacios de trabajo son configuraciones distintas para ejecutar XPipe. Cada espacio de trabajo tiene un directorio de datos donde se almacenan localmente todos los datos. Esto incluye datos de conexión, configuraciones y más.\n\nSi utilizas la función de sincronización, también puedes elegir sincronizar cada espacio de trabajo con un repositorio git diferente. +workspaceName=Nombre del espacio de trabajo +workspaceNameDescription=El nombre para mostrar del espacio de trabajo +workspacePath=Ruta del espacio de trabajo +workspacePathDescription=La ubicación del directorio de datos del espacio de trabajo +workspaceCreationAlertTitle=Creación de espacios de trabajo diff --git a/lang/app/strings/translations_fr.properties b/lang/app/strings/translations_fr.properties index 42c7e4413..4fbf5b9dd 100644 --- a/lang/app/strings/translations_fr.properties +++ b/lang/app/strings/translations_fr.properties @@ -469,3 +469,15 @@ closeOtherTabs=Fermer d'autres onglets closeAllTabs=Fermer tous les onglets closeLeftTabs=Ferme les onglets à gauche closeRightTabs=Ferme les onglets à droite +addSerial=Série ... +connect=Connecter +workspaces=Espaces de travail +manageWorkspaces=Gérer les espaces de travail +addWorkspace=Ajouter un espace de travail ... +workspaceAdd=Ajouter un nouvel espace de travail +workspaceAddDescription=Les espaces de travail sont des configurations distinctes pour l'exécution de XPipe. Chaque espace de travail possède un répertoire de données où toutes les données sont stockées localement. Cela comprend les données de connexion, les paramètres, et plus encore.\n\nSi tu utilises la fonctionnalité de synchronisation, tu peux aussi choisir de synchroniser chaque espace de travail avec un dépôt git différent. +workspaceName=Nom de l'espace de travail +workspaceNameDescription=Le nom d'affichage de l'espace de travail +workspacePath=Chemin d'accès à l'espace de travail +workspacePathDescription=L'emplacement du répertoire de données de l'espace de travail +workspaceCreationAlertTitle=Création d'un espace de travail diff --git a/lang/app/strings/translations_it.properties b/lang/app/strings/translations_it.properties index 28cd94e12..39c429568 100644 --- a/lang/app/strings/translations_it.properties +++ b/lang/app/strings/translations_it.properties @@ -469,3 +469,15 @@ closeOtherTabs=Chiudere altre schede closeAllTabs=Chiudi tutte le schede closeLeftTabs=Chiudere le schede a sinistra closeRightTabs=Chiudere le schede a destra +addSerial=Seriale ... +connect=Collegare +workspaces=Spazi di lavoro +manageWorkspaces=Gestire gli spazi di lavoro +addWorkspace=Aggiungi spazio di lavoro ... +workspaceAdd=Aggiungere un nuovo spazio di lavoro +workspaceAddDescription=Gli spazi di lavoro sono configurazioni distinte per l'esecuzione di XPipe. Ogni workspace ha una directory di dati in cui vengono memorizzati tutti i dati a livello locale. Questi includono i dati di connessione, le impostazioni e altro ancora.\n\nSe utilizzi la funzione di sincronizzazione, puoi anche scegliere di sincronizzare ogni workspace con un repository git diverso. +workspaceName=Nome dello spazio di lavoro +workspaceNameDescription=Il nome di visualizzazione dell'area di lavoro +workspacePath=Percorso dello spazio di lavoro +workspacePathDescription=La posizione della directory dei dati dell'area di lavoro +workspaceCreationAlertTitle=Creazione di uno spazio di lavoro diff --git a/lang/app/strings/translations_ja.properties b/lang/app/strings/translations_ja.properties index a295c1321..c8f13dc6f 100644 --- a/lang/app/strings/translations_ja.properties +++ b/lang/app/strings/translations_ja.properties @@ -469,3 +469,15 @@ closeOtherTabs=他のタブを閉じる closeAllTabs=すべてのタブを閉じる closeLeftTabs=タブを左に閉じる closeRightTabs=タブを右に閉じる +addSerial=シリアル ... +connect=接続する +workspaces=ワークスペース +manageWorkspaces=ワークスペースを管理する +addWorkspace=ワークスペースを追加する +workspaceAdd=新しいワークスペースを追加する +workspaceAddDescription=ワークスペースは、XPipeを実行するための個別の設定である。すべてのワークスペースには、すべてのデータがローカルに保存されるデータ・ディレクトリがある。これには、接続データや設定などが含まれる。\n\n同期機能を使えば、ワークスペースごとに異なるgitリポジトリと同期させることもできる。 +workspaceName=ワークスペース名 +workspaceNameDescription=ワークスペースの表示名 +workspacePath=ワークスペースのパス +workspacePathDescription=ワークスペースのデータディレクトリの場所 +workspaceCreationAlertTitle=ワークスペースの作成 diff --git a/lang/app/strings/translations_nl.properties b/lang/app/strings/translations_nl.properties index 375539cf6..e651cb8fb 100644 --- a/lang/app/strings/translations_nl.properties +++ b/lang/app/strings/translations_nl.properties @@ -469,3 +469,15 @@ closeOtherTabs=Andere tabbladen sluiten closeAllTabs=Alle tabbladen sluiten closeLeftTabs=Tabbladen naar links sluiten closeRightTabs=Tabbladen naar rechts sluiten +addSerial=Serieel ... +connect=Maak verbinding met +workspaces=Werkruimten +manageWorkspaces=Werkruimten beheren +addWorkspace=Werkruimte toevoegen ... +workspaceAdd=Een nieuwe werkruimte toevoegen +workspaceAddDescription=Workspaces zijn verschillende configuraties voor het uitvoeren van XPipe. Elke workspace heeft een datamap waar alle gegevens lokaal worden opgeslagen. Dit omvat verbindingsgegevens, instellingen en meer.\n\nAls je de synchronisatiefunctie gebruikt, kun je er ook voor kiezen om elke workspace met een andere git repository te synchroniseren. +workspaceName=Naam werkruimte +workspaceNameDescription=De weergavenaam van de werkruimte +workspacePath=Werkruimte pad +workspacePathDescription=De locatie van de gegevensmap van de werkruimte +workspaceCreationAlertTitle=Werkruimte maken diff --git a/lang/app/strings/translations_pt.properties b/lang/app/strings/translations_pt.properties index ebdad7e91..7fba4e1cb 100644 --- a/lang/app/strings/translations_pt.properties +++ b/lang/app/strings/translations_pt.properties @@ -469,3 +469,15 @@ closeOtherTabs=Fecha outros separadores closeAllTabs=Fecha todos os separadores closeLeftTabs=Fecha os separadores à esquerda closeRightTabs=Fecha os separadores à direita +addSerial=Série ... +connect=Liga-te +workspaces=Espaços de trabalho +manageWorkspaces=Gere espaços de trabalho +addWorkspace=Adiciona um espaço de trabalho ... +workspaceAdd=Adiciona um novo espaço de trabalho +workspaceAddDescription=Os espaços de trabalho são configurações distintas para executar o XPipe. Cada espaço de trabalho tem um diretório de dados onde todos os dados são armazenados localmente. Isto inclui dados de ligação, definições e muito mais.\n\nSe utilizares a funcionalidade de sincronização, também podes optar por sincronizar cada espaço de trabalho com um repositório git diferente. +workspaceName=Nome do espaço de trabalho +workspaceNameDescription=O nome de apresentação do espaço de trabalho +workspacePath=Caminho do espaço de trabalho +workspacePathDescription=A localização do diretório de dados do espaço de trabalho +workspaceCreationAlertTitle=Criação de espaço de trabalho diff --git a/lang/app/strings/translations_ru.properties b/lang/app/strings/translations_ru.properties index b99610f55..a8cb72d1f 100644 --- a/lang/app/strings/translations_ru.properties +++ b/lang/app/strings/translations_ru.properties @@ -469,3 +469,15 @@ closeOtherTabs=Закрыть другие вкладки closeAllTabs=Закрыть все вкладки closeLeftTabs=Закрыть вкладки слева closeRightTabs=Закрывать вкладки справа +addSerial=Серийный ... +connect=Connect +workspaces=Рабочие пространства +manageWorkspaces=Управляй рабочими пространствами +addWorkspace=Добавь рабочее пространство ... +workspaceAdd=Добавьте новое рабочее пространство +workspaceAddDescription=Рабочие пространства - это отдельные конфигурации для запуска XPipe. В каждом рабочем пространстве есть каталог данных, где все данные хранятся локально. Сюда входят данные о соединениях, настройки и многое другое.\n\nЕсли ты используешь функцию синхронизации, ты также можешь выбрать синхронизацию каждого рабочего пространства с отдельным git-репозиторием. +workspaceName=Имя рабочей области +workspaceNameDescription=Отображаемое имя рабочей области +workspacePath=Путь к рабочему пространству +workspacePathDescription=Расположение каталога данных рабочей области +workspaceCreationAlertTitle=Создание рабочего пространства diff --git a/lang/app/strings/translations_tr.properties b/lang/app/strings/translations_tr.properties index 1ef862bb3..8b970e969 100644 --- a/lang/app/strings/translations_tr.properties +++ b/lang/app/strings/translations_tr.properties @@ -470,3 +470,15 @@ closeOtherTabs=Diğer sekmeleri kapatın closeAllTabs=Tüm sekmeleri kapat closeLeftTabs=Sekmeleri sola doğru kapatın closeRightTabs=Sekmeleri sağa doğru kapatın +addSerial=Seri ... +connect=Bağlan +workspaces=Çalışma Alanları +manageWorkspaces=Çalışma alanlarını yönetme +addWorkspace=Çalışma alanı ekle ... +workspaceAdd=Yeni bir çalışma alanı ekleme +workspaceAddDescription=Çalışma alanları XPipe'ı çalıştırmak için farklı konfigürasyonlardır. Her çalışma alanı, tüm verilerin yerel olarak depolandığı bir veri dizinine sahiptir. Buna bağlantı verileri, ayarlar ve daha fazlası dahildir.\n\nSenkronizasyon özelliğini kullanırsanız, her çalışma alanını farklı bir git deposu ile senkronize etmeyi de seçebilirsiniz. +workspaceName=Çalışma alanı adı +workspaceNameDescription=Çalışma alanının görünen adı +workspacePath=Çalışma alanı yolu +workspacePathDescription=Çalışma alanı veri dizininin konumu +workspaceCreationAlertTitle=Çalışma alanı oluşturma diff --git a/lang/app/strings/translations_zh.properties b/lang/app/strings/translations_zh.properties index c2b843942..d9860737b 100644 --- a/lang/app/strings/translations_zh.properties +++ b/lang/app/strings/translations_zh.properties @@ -469,3 +469,15 @@ closeOtherTabs=关闭其他标签页 closeAllTabs=关闭所有标签页 closeLeftTabs=向左关闭标签 closeRightTabs=向右关闭标签页 +addSerial=串行 ... +connect=连接 +workspaces=工作空间 +manageWorkspaces=管理工作区 +addWorkspace=添加工作区 ... +workspaceAdd=添加新工作区 +workspaceAddDescription=工作区是运行 XPipe 的独特配置。每个工作区都有一个数据目录,本地存储所有数据。其中包括连接数据、设置等。\n\n如果使用同步功能,您还可以选择将每个工作区与不同的 git 仓库同步。 +workspaceName=工作区名称 +workspaceNameDescription=工作区的显示名称 +workspacePath=工作区路径 +workspacePathDescription=工作区数据目录的位置 +workspaceCreationAlertTitle=创建工作区 diff --git a/lang/proc/strings/fixed_en.properties b/lang/proc/strings/fixed_en.properties index ad78a823a..3a5058deb 100644 --- a/lang/proc/strings/fixed_en.properties +++ b/lang/proc/strings/fixed_en.properties @@ -1,3 +1,13 @@ wsl=Windows Subsystem for Linux docker=Docker -proxmox=Proxmox PVE \ No newline at end of file +proxmox=Proxmox PVE +xonXoff=XON/XOFF +rtsCts=RTS/CTS +dsrDtr=DSR/DTR +putty=PuTTY +screen=Screen +minicom=Minicom +odd=Odd +even=Even +mark=Mark +space=Space diff --git a/lang/proc/strings/translations_da.properties b/lang/proc/strings/translations_da.properties index 526340f7f..3b6128180 100644 --- a/lang/proc/strings/translations_da.properties +++ b/lang/proc/strings/translations_da.properties @@ -359,3 +359,19 @@ k8sPodActions=Pod-handlinger openVnc=Sæt VNC op commandGroup.displayName=Kommandogruppe commandGroup.displayDescription=Grupper tilgængelige kommandoer for et system +serial.displayName=Seriel forbindelse +serial.displayDescription=Åbn en seriel forbindelse i en terminal +serialPort=Seriel port +serialPortDescription=Den serielle port / enhed, der skal forbindes til +baudRate=Baud-hastighed +dataBits=Data-bits +stopBits=Stop-bits +parity=Paritet +flowControlWindow=Flow-kontrol +serialImplementation=Seriel implementering +serialImplementationDescription=Det værktøj, der skal bruges til at oprette forbindelse til den serielle port +serialHost=Vært +serialHostDescription=Systemet til at få adgang til den serielle port på +serialPortConfiguration=Konfiguration af seriel port +serialPortConfigurationDescription=Konfigurationsparametre for den tilsluttede serielle enhed +serialInformation=Seriel information diff --git a/lang/proc/strings/translations_de.properties b/lang/proc/strings/translations_de.properties index 8d8b8cc05..eea2a7d81 100644 --- a/lang/proc/strings/translations_de.properties +++ b/lang/proc/strings/translations_de.properties @@ -337,3 +337,19 @@ k8sPodActions=Pod-Aktionen openVnc=VNC einrichten commandGroup.displayName=Befehlsgruppe commandGroup.displayDescription=Verfügbare Befehle für ein System gruppieren +serial.displayName=Serielle Verbindung +serial.displayDescription=Eine serielle Verbindung in einem Terminal öffnen +serialPort=Serieller Anschluss +serialPortDescription=Der serielle Anschluss/das Gerät, mit dem eine Verbindung hergestellt werden soll +baudRate=Baudrate +dataBits=Datenbits +stopBits=Stoppbits +parity=Parität +flowControlWindow=Flusskontrolle +serialImplementation=Serielle Implementierung +serialImplementationDescription=Das Tool für die Verbindung mit der seriellen Schnittstelle +serialHost=Host +serialHostDescription=Das System für den Zugriff auf die serielle Schnittstelle auf +serialPortConfiguration=Konfiguration der seriellen Schnittstelle +serialPortConfigurationDescription=Konfigurationsparameter des angeschlossenen seriellen Geräts +serialInformation=Serielle Informationen diff --git a/lang/proc/strings/translations_en.properties b/lang/proc/strings/translations_en.properties index 8d9905f74..747aa3b31 100644 --- a/lang/proc/strings/translations_en.properties +++ b/lang/proc/strings/translations_en.properties @@ -334,4 +334,20 @@ dockerContextActions=Context actions k8sPodActions=Pod actions openVnc=Set up VNC commandGroup.displayName=Command group -commandGroup.displayDescription=Group available commands for a system \ No newline at end of file +commandGroup.displayDescription=Group available commands for a system +serial.displayName=Serial connection +serial.displayDescription=Open a serial connection in a terminal +serialPort=Serial port +serialPortDescription=The serial port / device to connect to +baudRate=Baud rate +dataBits=Data bits +stopBits=Stop bits +parity=Parity +flowControlWindow=Flow control +serialImplementation=Serial implementation +serialImplementationDescription=The tool to use to connect to the serial port +serialHost=Host +serialHostDescription=The system to access the serial port on +serialPortConfiguration=Serial port configuration +serialPortConfigurationDescription=Configuration parameters of the connected serial device +serialInformation=Serial information diff --git a/lang/proc/strings/translations_es.properties b/lang/proc/strings/translations_es.properties index 83a12ac97..66957c373 100644 --- a/lang/proc/strings/translations_es.properties +++ b/lang/proc/strings/translations_es.properties @@ -333,3 +333,19 @@ k8sPodActions=Acciones del pod openVnc=Configurar VNC commandGroup.displayName=Grupo de comandos commandGroup.displayDescription=Agrupa los comandos disponibles para un sistema +serial.displayName=Conexión en serie +serial.displayDescription=Abrir una conexión serie en un terminal +serialPort=Puerto serie +serialPortDescription=El puerto serie / dispositivo al que conectarse +baudRate=Velocidad en baudios +dataBits=Bits de datos +stopBits=Bits de parada +parity=Paridad +flowControlWindow=Control de flujo +serialImplementation=Aplicación en serie +serialImplementationDescription=La herramienta que hay que utilizar para conectarse al puerto serie +serialHost=Anfitrión +serialHostDescription=El sistema para acceder al puerto serie en +serialPortConfiguration=Configuración del puerto serie +serialPortConfigurationDescription=Parámetros de configuración del dispositivo serie conectado +serialInformation=Información en serie diff --git a/lang/proc/strings/translations_fr.properties b/lang/proc/strings/translations_fr.properties index 20b4cc865..d647b600a 100644 --- a/lang/proc/strings/translations_fr.properties +++ b/lang/proc/strings/translations_fr.properties @@ -333,3 +333,19 @@ k8sPodActions=Actions de pods openVnc=Configurer VNC commandGroup.displayName=Groupe de commande commandGroup.displayDescription=Groupe de commandes disponibles pour un système +serial.displayName=Connexion série +serial.displayDescription=Ouvrir une connexion série dans un terminal +serialPort=Port série +serialPortDescription=Le port série / le périphérique à connecter +baudRate=Taux de bauds +dataBits=Bits de données +stopBits=Bits d'arrêt +parity=Parité +flowControlWindow=Contrôle de flux +serialImplementation=Implémentation en série +serialImplementationDescription=L'outil à utiliser pour se connecter au port série +serialHost=Hôte +serialHostDescription=Le système pour accéder au port série sur +serialPortConfiguration=Configuration du port série +serialPortConfigurationDescription=Paramètres de configuration de l'appareil en série connecté +serialInformation=Informations en série diff --git a/lang/proc/strings/translations_it.properties b/lang/proc/strings/translations_it.properties index dbef50ac6..79665143b 100644 --- a/lang/proc/strings/translations_it.properties +++ b/lang/proc/strings/translations_it.properties @@ -333,3 +333,19 @@ k8sPodActions=Azioni del pod openVnc=Configurare VNC commandGroup.displayName=Gruppo di comando commandGroup.displayDescription=Gruppo di comandi disponibili per un sistema +serial.displayName=Connessione seriale +serial.displayDescription=Aprire una connessione seriale in un terminale +serialPort=Porta seriale +serialPortDescription=La porta seriale/dispositivo a cui connettersi +baudRate=Velocità di trasmissione +dataBits=Bit di dati +stopBits=Bit di stop +parity=Parità +flowControlWindow=Controllo del flusso +serialImplementation=Implementazione seriale +serialImplementationDescription=Lo strumento da utilizzare per collegarsi alla porta seriale +serialHost=Ospite +serialHostDescription=Il sistema per accedere alla porta seriale su +serialPortConfiguration=Configurazione della porta seriale +serialPortConfigurationDescription=Parametri di configurazione del dispositivo seriale collegato +serialInformation=Informazioni di serie diff --git a/lang/proc/strings/translations_ja.properties b/lang/proc/strings/translations_ja.properties index 79c161735..169a08617 100644 --- a/lang/proc/strings/translations_ja.properties +++ b/lang/proc/strings/translations_ja.properties @@ -333,3 +333,19 @@ k8sPodActions=ポッドアクション openVnc=VNCを設定する commandGroup.displayName=コマンドグループ commandGroup.displayDescription=システムで使用可能なコマンドをグループ化する +serial.displayName=シリアル接続 +serial.displayDescription=ターミナルでシリアル接続を開く +serialPort=シリアルポート +serialPortDescription=接続するシリアルポート/デバイス +baudRate=ボーレート +dataBits=データビット +stopBits=ストップビット +parity=パリティ +flowControlWindow=フロー制御 +serialImplementation=シリアルの実装 +serialImplementationDescription=シリアルポートに接続するために使用するツール +serialHost=ホスト +serialHostDescription=のシリアルポートにアクセスするシステム +serialPortConfiguration=シリアルポートの設定 +serialPortConfigurationDescription=接続されたシリアル・デバイスの設定パラメーター +serialInformation=シリアル情報 diff --git a/lang/proc/strings/translations_nl.properties b/lang/proc/strings/translations_nl.properties index 7ea31c90b..01330d2df 100644 --- a/lang/proc/strings/translations_nl.properties +++ b/lang/proc/strings/translations_nl.properties @@ -333,3 +333,19 @@ k8sPodActions=Pod acties openVnc=VNC instellen commandGroup.displayName=Opdrachtgroep commandGroup.displayDescription=Groep beschikbare commando's voor een systeem +serial.displayName=Seriële verbinding +serial.displayDescription=Een seriële verbinding in een terminal openen +serialPort=Seriële poort +serialPortDescription=De seriële poort / het apparaat waarmee verbinding moet worden gemaakt +baudRate=Baudrate +dataBits=Gegevensbits +stopBits=Stopbits +parity=Pariteit +flowControlWindow=Debietregeling +serialImplementation=Seriële implementatie +serialImplementationDescription=Het gereedschap om verbinding te maken met de seriële poort +serialHost=Host +serialHostDescription=Het systeem om toegang te krijgen tot de seriële poort op +serialPortConfiguration=Seriële poort configuratie +serialPortConfigurationDescription=Configuratieparameters van het aangesloten seriële apparaat +serialInformation=Seriële informatie diff --git a/lang/proc/strings/translations_pt.properties b/lang/proc/strings/translations_pt.properties index 2d495a348..2db462b80 100644 --- a/lang/proc/strings/translations_pt.properties +++ b/lang/proc/strings/translations_pt.properties @@ -333,3 +333,19 @@ k8sPodActions=Acções de pod openVnc=Configura o VNC commandGroup.displayName=Grupo de comandos commandGroup.displayDescription=Agrupa os comandos disponíveis para um sistema +serial.displayName=Ligação em série +serial.displayDescription=Abre uma ligação de série num terminal +serialPort=Porta de série +serialPortDescription=A porta de série / dispositivo a ligar +baudRate=Taxa de transmissão +dataBits=Bits de dados +stopBits=Bits de paragem +parity=Paridade +flowControlWindow=Controlo de fluxo +serialImplementation=Implementação em série +serialImplementationDescription=A ferramenta a utilizar para ligar à porta série +serialHost=Apresenta +serialHostDescription=O sistema para aceder à porta série em +serialPortConfiguration=Configuração da porta de série +serialPortConfigurationDescription=Parâmetros de configuração do dispositivo de série ligado +serialInformation=Informação de série diff --git a/lang/proc/strings/translations_ru.properties b/lang/proc/strings/translations_ru.properties index 9b528e587..501fc989d 100644 --- a/lang/proc/strings/translations_ru.properties +++ b/lang/proc/strings/translations_ru.properties @@ -333,3 +333,19 @@ k8sPodActions=Действия в капсуле openVnc=Настройте VNC commandGroup.displayName=Группа команд commandGroup.displayDescription=Группа доступных команд для системы +serial.displayName=Последовательное соединение +serial.displayDescription=Открыть последовательное соединение в терминале +serialPort=Последовательный порт +serialPortDescription=Последовательный порт/устройство, к которому нужно подключиться +baudRate=Скорость передачи данных +dataBits=Биты данных +stopBits=Стоп-биты +parity=Четность +flowControlWindow=Контроль потока +serialImplementation=Последовательная реализация +serialImplementationDescription=Инструмент, который нужно использовать для подключения к последовательному порту +serialHost=Хост +serialHostDescription=Система для доступа к последовательному порту на +serialPortConfiguration=Конфигурация последовательного порта +serialPortConfigurationDescription=Параметры конфигурации подключенного последовательного устройства +serialInformation=Серийная информация diff --git a/lang/proc/strings/translations_tr.properties b/lang/proc/strings/translations_tr.properties index ce0890001..23e8d557a 100644 --- a/lang/proc/strings/translations_tr.properties +++ b/lang/proc/strings/translations_tr.properties @@ -333,3 +333,19 @@ k8sPodActions=Pod eylemleri openVnc=VNC'yi ayarlama commandGroup.displayName=Komuta grubu commandGroup.displayDescription=Bir sistem için mevcut komutları gruplama +serial.displayName=Seri bağlantı +serial.displayDescription=Terminalde bir seri bağlantı açın +serialPort=Seri bağlantı noktası +serialPortDescription=Bağlanılacak seri port / cihaz +baudRate=Baud hızı +dataBits=Veri bitleri +stopBits=Dur bitleri +parity=Parite +flowControlWindow=Akış kontrolü +serialImplementation=Seri uygulama +serialImplementationDescription=Seri porta bağlanmak için kullanılacak araç +serialHost=Ev sahibi +serialHostDescription=Seri porta erişmek için sistem +serialPortConfiguration=Seri bağlantı noktası yapılandırması +serialPortConfigurationDescription=Bağlı seri cihazın konfigürasyon parametreleri +serialInformation=Seri bilgileri diff --git a/lang/proc/strings/translations_zh.properties b/lang/proc/strings/translations_zh.properties index 36ba085f9..f58045b0e 100644 --- a/lang/proc/strings/translations_zh.properties +++ b/lang/proc/strings/translations_zh.properties @@ -333,3 +333,19 @@ k8sPodActions=Pod 操作 openVnc=设置 VNC commandGroup.displayName=命令组 commandGroup.displayDescription=系统可用命令组 +serial.displayName=串行连接 +serial.displayDescription=在终端中打开串行连接 +serialPort=串行端口 +serialPortDescription=要连接的串行端口/设备 +baudRate=波特率 +dataBits=数据位 +stopBits=停止位 +parity=奇偶校验 +flowControlWindow=流量控制 +serialImplementation=串行实施 +serialImplementationDescription=用于连接串行端口的工具 +serialHost=主机 +serialHostDescription=访问串行端口的系统 +serialPortConfiguration=串行端口配置 +serialPortConfigurationDescription=所连接串行设备的配置参数 +serialInformation=序列信息 diff --git a/lang/proc/texts/serialImplementation_da.md b/lang/proc/texts/serialImplementation_da.md new file mode 100644 index 000000000..196b1b48e --- /dev/null +++ b/lang/proc/texts/serialImplementation_da.md @@ -0,0 +1,10 @@ +# Implementeringer + +XPipe uddelegerer den serielle håndtering til eksterne værktøjer. +Der er flere tilgængelige værktøjer, som XPipe kan uddelegere til, hver med deres egne fordele og ulemper. +For at bruge dem kræves det, at de er tilgængelige på værtssystemet. +De fleste muligheder burde være understøttet af alle værktøjer, men nogle mere eksotiske muligheder er det måske ikke. + +Før der oprettes forbindelse, kontrollerer XPipe, at det valgte værktøj er installeret og understøtter alle konfigurerede muligheder. +Hvis denne kontrol er vellykket, starter det valgte værktøj. + diff --git a/lang/proc/texts/serialImplementation_de.md b/lang/proc/texts/serialImplementation_de.md new file mode 100644 index 000000000..8ac511a41 --- /dev/null +++ b/lang/proc/texts/serialImplementation_de.md @@ -0,0 +1,10 @@ +# Implementierungen + +XPipe delegiert die serielle Verarbeitung an externe Tools. +Es gibt mehrere Tools, an die XPipe delegieren kann, jedes mit seinen eigenen Vor- und Nachteilen. +Um sie zu nutzen, müssen sie auf dem Hostsystem verfügbar sein. +Die meisten Optionen sollten von allen Tools unterstützt werden, aber einige exotischere Optionen sind es vielleicht nicht. + +Bevor eine Verbindung hergestellt wird, prüft XPipe, ob das ausgewählte Tool installiert ist und alle konfigurierten Optionen unterstützt. +Wenn diese Prüfung erfolgreich ist, wird das ausgewählte Tool gestartet. + diff --git a/lang/proc/texts/serialImplementation_en.md b/lang/proc/texts/serialImplementation_en.md new file mode 100644 index 000000000..834da94a8 --- /dev/null +++ b/lang/proc/texts/serialImplementation_en.md @@ -0,0 +1,10 @@ +# Implementations + +XPipe delegates the serial handling to external tools. +There are multiple available tools XPipe can delegate to, each with their own advantages and disadvantages. +To use them, it is required that they are available on the host system. +Most options should be supported by all tools, but some more exotic options might not be. + +Before connecting, XPipe will verify that the selected tool is installed and supports all configured options. +If that check is successful, the selected tool will launch. + diff --git a/lang/proc/texts/serialImplementation_es.md b/lang/proc/texts/serialImplementation_es.md new file mode 100644 index 000000000..6c99e0bfa --- /dev/null +++ b/lang/proc/texts/serialImplementation_es.md @@ -0,0 +1,10 @@ +# Implementaciones + +XPipe delega el manejo de la serie en herramientas externas. +Hay múltiples herramientas disponibles en las que XPipe puede delegar, cada una con sus propias ventajas e inconvenientes. +Para utilizarlas, es necesario que estén disponibles en el sistema anfitrión. +La mayoría de las opciones deberían estar soportadas por todas las herramientas, pero algunas opciones más exóticas podrían no estarlo. + +Antes de conectarse, XPipe comprobará que la herramienta seleccionada está instalada y admite todas las opciones configuradas. +Si la comprobación es correcta, se iniciará la herramienta seleccionada. + diff --git a/lang/proc/texts/serialImplementation_fr.md b/lang/proc/texts/serialImplementation_fr.md new file mode 100644 index 000000000..a9bacebee --- /dev/null +++ b/lang/proc/texts/serialImplementation_fr.md @@ -0,0 +1,10 @@ +# Implantations + +XPipe délègue la gestion de la série à des outils externes. +Il existe plusieurs outils disponibles auxquels XPipe peut déléguer, chacun ayant ses propres avantages et inconvénients. +Pour les utiliser, il faut qu'ils soient disponibles sur le système hôte. +La plupart des options devraient être supportées par tous les outils, mais certaines options plus exotiques pourraient ne pas l'être. + +Avant de se connecter, XPipe vérifie que l'outil sélectionné est installé et qu'il prend en charge toutes les options configurées. +Si cette vérification est concluante, l'outil sélectionné sera lancé. + diff --git a/lang/proc/texts/serialImplementation_it.md b/lang/proc/texts/serialImplementation_it.md new file mode 100644 index 000000000..3ec0d8bf8 --- /dev/null +++ b/lang/proc/texts/serialImplementation_it.md @@ -0,0 +1,10 @@ +# Implementazioni + +XPipe delega la gestione della serialità a strumenti esterni. +Esistono diversi strumenti a cui XPipe può delegare, ognuno con i propri vantaggi e svantaggi. +Per poterli utilizzare, è necessario che siano disponibili sul sistema host. +La maggior parte delle opzioni dovrebbe essere supportata da tutti gli strumenti, ma alcune opzioni più esotiche potrebbero non esserlo. + +Prima di connettersi, XPipe verifica che lo strumento selezionato sia installato e che supporti tutte le opzioni configurate. +Se la verifica ha esito positivo, lo strumento selezionato viene avviato. + diff --git a/lang/proc/texts/serialImplementation_ja.md b/lang/proc/texts/serialImplementation_ja.md new file mode 100644 index 000000000..d6fe6e650 --- /dev/null +++ b/lang/proc/texts/serialImplementation_ja.md @@ -0,0 +1,10 @@ +# 実装 + +XPipeはシリアル処理を外部ツールに委譲する。 +XPipeが委譲できるツールは複数あり、それぞれに長所と短所がある。 +それらを使用するには、ホストシステム上で使用可能であることが必要である。 +ほとんどのオプションはすべてのツールでサポートされているはずだが、よりエキゾチックなオプションはサポートされていないかもしれない。 + +接続する前に、XPipeは、選択したツールがインストールされ、設定されたすべてのオプションに対応しているかどうかを確認する。 +チェックが成功すると、選択したツールが起動する。 + diff --git a/lang/proc/texts/serialImplementation_nl.md b/lang/proc/texts/serialImplementation_nl.md new file mode 100644 index 000000000..9c9d5ada5 --- /dev/null +++ b/lang/proc/texts/serialImplementation_nl.md @@ -0,0 +1,10 @@ +# Implementaties + +XPipe delegeert de seriële afhandeling naar externe tools. +Er zijn meerdere beschikbare tools waaraan XPipe kan delegeren, elk met hun eigen voor- en nadelen. +Om ze te gebruiken is het vereist dat ze beschikbaar zijn op het hostsysteem. +De meeste opties zouden door alle gereedschappen ondersteund moeten worden, maar sommige meer exotische opties misschien niet. + +Voordat er verbinding wordt gemaakt, controleert XPipe of het geselecteerde gereedschap is geïnstalleerd en alle geconfigureerde opties ondersteunt. +Als die controle succesvol is, wordt het geselecteerde gereedschap gestart. + diff --git a/lang/proc/texts/serialImplementation_pt.md b/lang/proc/texts/serialImplementation_pt.md new file mode 100644 index 000000000..2b665a6cc --- /dev/null +++ b/lang/proc/texts/serialImplementation_pt.md @@ -0,0 +1,10 @@ +# Implementações + +XPipe delega o manuseio serial para ferramentas externas. +Existem várias ferramentas disponíveis para as quais o XPipe pode delegar, cada uma com suas próprias vantagens e desvantagens. +Para as utilizar, é necessário que estejam disponíveis no sistema anfitrião. +A maioria das opções deve ser suportada por todas as ferramentas, mas algumas opções mais exóticas podem não ser. + +Antes de ligar, o XPipe verifica se a ferramenta selecionada está instalada e se suporta todas as opções configuradas. +Se essa verificação for bem sucedida, a ferramenta selecionada será iniciada. + diff --git a/lang/proc/texts/serialImplementation_ru.md b/lang/proc/texts/serialImplementation_ru.md new file mode 100644 index 000000000..4af78f64f --- /dev/null +++ b/lang/proc/texts/serialImplementation_ru.md @@ -0,0 +1,10 @@ +# Реализации + +XPipe делегирует работу с последовательным интерфейсом внешним инструментам. +Существует несколько доступных инструментов, которым XPipe может делегировать свои полномочия, каждый из которых имеет свои преимущества и недостатки. +Чтобы использовать их, необходимо, чтобы они были доступны в хост-системе. +Большинство опций должны поддерживаться всеми инструментами, но некоторые более экзотические опции могут не поддерживаться. + +Перед подключением XPipe проверит, что выбранный инструмент установлен и поддерживает все настроенные опции. +Если проверка прошла успешно, выбранный инструмент будет запущен. + diff --git a/lang/proc/texts/serialImplementation_tr.md b/lang/proc/texts/serialImplementation_tr.md new file mode 100644 index 000000000..60a7f6bd0 --- /dev/null +++ b/lang/proc/texts/serialImplementation_tr.md @@ -0,0 +1,10 @@ +# Uygulamalar + +XPipe seri işlemeyi harici araçlara devreder. +XPipe'ın temsilci olarak kullanabileceği, her biri kendi avantaj ve dezavantajlarına sahip birden fazla araç mevcuttur. +Bunları kullanmak için, ana sistemde mevcut olmaları gerekir. +Çoğu seçenek tüm araçlar tarafından desteklenmelidir, ancak bazı daha egzotik seçenekler desteklenmeyebilir. + +XPipe bağlanmadan önce seçilen aracın yüklü olduğunu ve yapılandırılan tüm seçenekleri desteklediğini doğrular. +Bu kontrol başarılı olursa, seçilen araç başlatılır. + diff --git a/lang/proc/texts/serialImplementation_zh.md b/lang/proc/texts/serialImplementation_zh.md new file mode 100644 index 000000000..d04386559 --- /dev/null +++ b/lang/proc/texts/serialImplementation_zh.md @@ -0,0 +1,10 @@ +# 实现 + +XPipe将串行处理委托给外部工具。 +XPipe 可以委托多种可用工具,每种工具都有自己的优缺点。 +要使用这些工具,主机系统上必须有这些工具。 +大多数选项应为所有工具所支持,但一些较为特殊的选项可能不支持。 + +在连接之前,XPipe 将验证所选工具是否已安装并支持所有配置选项。 +如果检查成功,所选工具将启动。 + diff --git a/lang/proc/texts/serialPort_da.md b/lang/proc/texts/serialPort_da.md new file mode 100644 index 000000000..a4fc65eff --- /dev/null +++ b/lang/proc/texts/serialPort_da.md @@ -0,0 +1,20 @@ +## Windows + +På Windows-systemer henviser man typisk til serielle porte via `COM`. +XPipe understøtter også blot at angive indekset uden præfikset `COM`. +For at adressere porte større end 9 skal du bruge UNC-stiformen med `\\.\COM`. + +Hvis du har installeret en WSL1-distribution, kan du også henvise til de serielle porte inde fra WSL-distributionen via `/dev/ttyS`. +Dette virker dog ikke længere med WSL2. +Hvis du har et WSL1-system, kan du bruge det som vært for den serielle forbindelse og bruge tty-notationen til at få adgang til det med XPipe. + +## Linux + +På Linux-systemer kan du typisk få adgang til de serielle porte via `/dev/ttyS`. +Hvis du kender ID'et på den tilsluttede enhed, men ikke ønsker at holde styr på den serielle port, kan du også henvise til dem via `/dev/serial/by-id/`. +Du kan få en liste over alle tilgængelige serielle porte med deres ID'er ved at køre `ls /dev/serial/by-id/*`. + +## macOS + +På macOS kan de serielle portnavne være stort set hvad som helst, men har normalt formen `/dev/tty.`, hvor id er den interne enhedsidentifikator. +Ved at køre `ls /dev/tty.*` kan man finde tilgængelige serielle porte. diff --git a/lang/proc/texts/serialPort_de.md b/lang/proc/texts/serialPort_de.md new file mode 100644 index 000000000..ff0ad15c7 --- /dev/null +++ b/lang/proc/texts/serialPort_de.md @@ -0,0 +1,20 @@ +## Windows + +Auf Windows-Systemen bezeichnest du serielle Schnittstellen normalerweise mit `COM`. +XPipe unterstützt auch die bloße Angabe des Index ohne das Präfix `COM`. +Um Ports größer als 9 anzusprechen, musst du die UNC-Pfadform mit `\.\COM` verwenden. + +Wenn du eine WSL1-Distribution installiert hast, kannst du die seriellen Schnittstellen auch aus der WSL-Distribution heraus über `/dev/ttyS` ansprechen. +Das funktioniert allerdings nicht mehr mit WSL2. +Wenn du ein WSL1-System hast, kannst du dieses als Host für diese serielle Verbindung verwenden und die tty-Notation nutzen, um mit XPipe darauf zuzugreifen. + +## Linux + +Auf Linux-Systemen kannst du normalerweise über `/dev/ttyS` auf die seriellen Schnittstellen zugreifen. +Wenn du die ID des angeschlossenen Geräts kennst, dir aber die serielle Schnittstelle nicht merken willst, kannst du sie auch über `/dev/serial/by-id/` ansprechen. +Du kannst alle verfügbaren seriellen Schnittstellen mit ihren IDs auflisten, indem du `ls /dev/serial/by-id/*` ausführst. + +## macOS + +Unter macOS können die Namen der seriellen Schnittstellen so ziemlich alles sein, aber normalerweise haben sie die Form `/dev/tty.`, wobei id die interne Gerätekennung ist. +Wenn du `ls /dev/tty.*` ausführst, solltest du die verfügbaren seriellen Schnittstellen finden. diff --git a/lang/proc/texts/serialPort_en.md b/lang/proc/texts/serialPort_en.md new file mode 100644 index 000000000..ee9080503 --- /dev/null +++ b/lang/proc/texts/serialPort_en.md @@ -0,0 +1,20 @@ +## Windows + +On Windows systems you typically refer to serial ports via `COM`. +XPipe also supports just specifying the index without the `COM` prefix. +To address ports greater than 9, you have to use the UNC path form with `\\.\COM`. + +If you have a WSL1 distribution installed, you can also reference the serial ports from within the WSL distribution via `/dev/ttyS`. +This it does not work with WSL2 anymore though. +If you have a WSL1 system, you can use this one as the host for this serial connection and use the tty notation to access it with XPipe. + +## Linux + +On Linux systems you can typically access the serial ports via `/dev/ttyS`. +If you know the ID of the connected device but don't want to keep track of the serial port, you can also reference them via `/dev/serial/by-id/`. +You can list all available serial ports with their IDs by running `ls /dev/serial/by-id/*`. + +## macOS + +On macOS, the serial port names can be pretty much anything, but usually have the form of `/dev/tty.` where the id the internal device identifier. +Running `ls /dev/tty.*` should find available serial ports. diff --git a/lang/proc/texts/serialPort_es.md b/lang/proc/texts/serialPort_es.md new file mode 100644 index 000000000..9ce1d1ef0 --- /dev/null +++ b/lang/proc/texts/serialPort_es.md @@ -0,0 +1,20 @@ +## Windows + +En los sistemas Windows sueles referirte a los puertos serie mediante `COM`. +XPipe también admite sólo especificar el índice sin el prefijo `COM`. +Para dirigirte a puertos mayores de 9, tienes que utilizar la forma de ruta UNC con `COM`. + +Si tienes instalada una distribución WSL1, también puedes hacer referencia a los puertos serie desde dentro de la distribución WSL mediante `/dev/ttyS`. +Sin embargo, esto ya no funciona con WSL2. +Si tienes un sistema WSL1, puedes utilizarlo como host para esta conexión serie y utilizar la notación tty para acceder a él con XPipe. + +## Linux + +En los sistemas Linux normalmente puedes acceder a los puertos serie a través de `/dev/ttyS`. +Si conoces el ID del dispositivo conectado pero no quieres seguir la pista del puerto serie, también puedes referenciarlos mediante `/dev/serial/by-id/`. +Puedes listar todos los puertos serie disponibles con sus ID ejecutando `ls /dev/serial/by-id/*`. + +## macOS + +En macOS, los nombres de los puertos serie pueden ser prácticamente cualquier cosa, pero suelen tener la forma de `/dev/tty.` donde id es el identificador interno del dispositivo. +Ejecutando `ls /dev/tty.*` deberías encontrar los puertos serie disponibles. diff --git a/lang/proc/texts/serialPort_fr.md b/lang/proc/texts/serialPort_fr.md new file mode 100644 index 000000000..666f490e1 --- /dev/null +++ b/lang/proc/texts/serialPort_fr.md @@ -0,0 +1,20 @@ +## Windows + +Sur les systèmes Windows, tu fais généralement référence aux ports série via `COM`. +XPipe prend également en charge la spécification de l'index sans le préfixe `COM`. +Pour adresser des ports supérieurs à 9, il faut utiliser la forme de chemin UNC avec `\\N-COM`. + +Si tu as installé une distribution WSL1, tu peux aussi référencer les ports série à partir de la distribution WSL via `/dev/ttyS`. +Cela ne fonctionne plus avec WSL2. +Si tu as un système WSL1, tu peux utiliser celui-ci comme hôte pour cette connexion série et utiliser la notation tty pour y accéder avec XPipe. + +## Linux + +Sur les systèmes Linux, tu peux généralement accéder aux ports série via `/dev/ttyS`. +Si tu connais l'ID de l'appareil connecté mais que tu ne veux pas garder trace du port série, tu peux aussi les référencer via `/dev/serial/by-id/`. +Tu peux dresser la liste de tous les ports série disponibles avec leur ID en exécutant `ls /dev/serial/by-id/*`. + +## macOS + +Sur macOS, les noms des ports série peuvent être à peu près n'importe quoi, mais ils ont généralement la forme `/dev/tty.` où l'id l'identifiant interne du périphérique. +L'exécution de `ls /dev/tty.*` devrait permettre de trouver les ports série disponibles. diff --git a/lang/proc/texts/serialPort_it.md b/lang/proc/texts/serialPort_it.md new file mode 100644 index 000000000..c38e01bf6 --- /dev/null +++ b/lang/proc/texts/serialPort_it.md @@ -0,0 +1,20 @@ +## Windows + +Nei sistemi Windows di solito ci si riferisce alle porte seriali tramite `COM`. +XPipe supporta anche la semplice indicazione dell'indice senza il prefisso `COM`. +Per indirizzare le porte superiori a 9, devi utilizzare la forma UNC path con `\\.\COM`. + +Se hai installato una distribuzione WSL1, puoi anche fare riferimento alle porte seriali dall'interno della distribuzione WSL tramite `/dev/ttyS`. +Questo però non funziona più con WSL2. +Se hai un sistema WSL1, puoi usarlo come host per questa connessione seriale e utilizzare la notazione tty per accedervi con XPipe. + +## Linux + +Sui sistemi Linux puoi accedere alle porte seriali tramite `/dev/ttyS`. +Se conosci l'ID del dispositivo collegato ma non vuoi tenere traccia della porta seriale, puoi anche fare riferimento ad esso tramite `/dev/serial/by-id/`. +Puoi elencare tutte le porte seriali disponibili con i relativi ID eseguendo `ls /dev/serial/by-id/*`. + +## macOS + +Su macOS, i nomi delle porte seriali possono essere praticamente qualsiasi cosa, ma di solito hanno la forma di `/dev/tty.` dove l'id è l'identificatore interno del dispositivo. +L'esecuzione di `ls /dev/tty.*` dovrebbe trovare le porte seriali disponibili. diff --git a/lang/proc/texts/serialPort_ja.md b/lang/proc/texts/serialPort_ja.md new file mode 100644 index 000000000..1b601ade8 --- /dev/null +++ b/lang/proc/texts/serialPort_ja.md @@ -0,0 +1,20 @@ +## ウィンドウズ + +Windowsシステムでは、通常`COM`でシリアルポートを参照する。 +XPipeでは、`COM`という接頭辞なしでインデックスを指定することもできる。 +9以上のポートを指定するには、`COM`でUNCパス形式を使わなければならない。 + +WSL1ディストリビューションがインストールされている場合、WSLディストリビューション内から`/dev/ttyS`でシリアルポートを参照することもできる。 +しかし、これはWSL2では動作しない。 +WSL1システムを持っている場合は、このシステムをシリアル接続のホストとして使用し、XPipeでアクセスするためにtty記法を使用することができる。 + +## Linux + +Linuxシステムでは、通常`/dev/ttyS`経由でシリアルポートにアクセスできる。 +接続されているデバイスのIDは知っているが、シリアルポートを追跡したくない場合は、`/dev/serial/by-id/<デバイスID>`で参照することもできる。 +`ls /dev/serial/by-id/*`を実行すれば、利用可能なすべてのシリアルポートをID付きで一覧できる。 + +## macOS + +macOSでは、シリアルポート名はほとんど何でも良いが、通常は`/dev/tty.`の形をしており、idは内部デバイス識別子である。 +`ls /dev/tty.*`を実行すると、利用可能なシリアルポートが見つかるはずである。 diff --git a/lang/proc/texts/serialPort_nl.md b/lang/proc/texts/serialPort_nl.md new file mode 100644 index 000000000..6c906c4b9 --- /dev/null +++ b/lang/proc/texts/serialPort_nl.md @@ -0,0 +1,20 @@ +## Windows + +Op Windows systemen verwijs je meestal naar seriële poorten via `COM`. +XPipe ondersteunt ook het opgeven van de index zonder het `COM` voorvoegsel. +Om poorten groter dan 9 te adresseren, moet je de UNC pad vorm gebruiken met `\.\COM`. + +Als je een WSL1 distributie hebt geïnstalleerd, kun je de seriële poorten ook vanuit de WSL distributie benaderen via `/dev/ttyS`. +Dit werkt echter niet meer met WSL2. +Als je een WSL1 systeem hebt, kun je deze gebruiken als host voor deze seriële verbinding en de tty notatie gebruiken om deze te benaderen met XPipe. + +## Linux + +Op Linux systemen heb je meestal toegang tot de seriële poorten via `/dev/ttyS`. +Als je de ID van het aangesloten apparaat weet, maar de seriële poort niet wilt bijhouden, kun je ze ook benaderen via `/dev/serial/by-id/`. +Je kunt een lijst maken van alle beschikbare seriële poorten met hun ID's door `ls /dev/serial/by-id/*` uit te voeren. + +## macOS + +Op macOS kunnen de namen van de seriële poorten van alles zijn, maar meestal hebben ze de vorm `/dev/tty.` waarbij de id de interne apparaat-ID is. +Het uitvoeren van `ls /dev/tty.*` zou beschikbare seriële poorten moeten vinden. diff --git a/lang/proc/texts/serialPort_pt.md b/lang/proc/texts/serialPort_pt.md new file mode 100644 index 000000000..9a5873c86 --- /dev/null +++ b/lang/proc/texts/serialPort_pt.md @@ -0,0 +1,20 @@ +## Windows + +Nos sistemas Windows, normalmente referes-te às portas série através de `COM`. +O XPipe também suporta apenas a especificação do índice sem o prefixo `COM`. +Para endereçar portas maiores que 9, é necessário usar a forma de caminho UNC com `\\.\COM`. + +Se tiver uma distribuição WSL1 instalada, também pode referenciar as portas seriais de dentro da distribuição WSL via `/dev/ttyS`. +No entanto, isso não funciona mais com o WSL2. +Se tiveres um sistema WSL1, podes usar este como anfitrião para esta ligação série e usar a notação tty para aceder a ele com o XPipe. + +## Linux + +Em sistemas Linux podes tipicamente aceder às portas série via `/dev/ttyS`. +Se souberes o ID do dispositivo ligado mas não quiseres manter o registo da porta série, podes também referenciá-los através de `/dev/serial/by-id/`. +Podes listar todas as portas série disponíveis com os seus IDs ao correr `ls /dev/serial/by-id/*`. + +## macOS + +No macOS, os nomes das portas seriais podem ser praticamente qualquer coisa, mas geralmente têm a forma de `/dev/tty.` onde o id é o identificador interno do dispositivo. +Executar `ls /dev/tty.*` deve encontrar portas seriais disponíveis. diff --git a/lang/proc/texts/serialPort_ru.md b/lang/proc/texts/serialPort_ru.md new file mode 100644 index 000000000..53c287baf --- /dev/null +++ b/lang/proc/texts/serialPort_ru.md @@ -0,0 +1,20 @@ +## Windows + +В системах Windows ты обычно обращаешься к последовательным портам через `COM`. +XPipe также поддерживает простое указание индекса без префикса `COM`. +Чтобы обратиться к портам больше 9, тебе придется использовать форму UNC-пути с `\\\.\COM`. + +Если у тебя установлен дистрибутив WSL1, ты также можешь обращаться к последовательным портам из дистрибутива WSL через `/dev/ttyS`. +Однако с WSL2 это уже не работает. +Если у тебя есть система WSL1, ты можешь использовать ее в качестве хоста для этого последовательного соединения и использовать нотацию tty для доступа к ней с помощью XPipe. + +## Linux + +В Linux-системах ты обычно можешь получить доступ к последовательным портам через `/dev/ttyS`. +Если ты знаешь ID подключенного устройства, но не хочешь следить за последовательным портом, ты также можешь обратиться к ним через `/dev/serial/by-id/`. +Ты можешь перечислить все доступные последовательные порты с их идентификаторами, выполнив команду `ls /dev/serial/by-id/*`. + +## macOS + +В macOS имена последовательных портов могут быть практически любыми, но обычно имеют вид `/dev/tty.`, где id - внутренний идентификатор устройства. +Запуск `ls /dev/tty.*` должен найти доступные последовательные порты. diff --git a/lang/proc/texts/serialPort_tr.md b/lang/proc/texts/serialPort_tr.md new file mode 100644 index 000000000..d9458b92e --- /dev/null +++ b/lang/proc/texts/serialPort_tr.md @@ -0,0 +1,20 @@ +## Windows + +Windows sistemlerinde seri portlara genellikle `COM` ile başvurursunuz. +XPipe ayrıca `COM` öneki olmadan sadece dizinin belirtilmesini de destekler. +9'dan büyük portları adreslemek için, `\\.\COM` ile UNC yol formunu kullanmanız gerekir. + +Eğer bir WSL1 dağıtımı yüklüyse, seri portlara WSL dağıtımı içinden `/dev/ttyS` ile de başvurabilirsiniz. +Ancak bu artık WSL2 ile çalışmamaktadır. +Eğer bir WSL1 sisteminiz varsa, bunu seri bağlantı için ana bilgisayar olarak kullanabilir ve XPipe ile erişmek için tty gösterimini kullanabilirsiniz. + +## Linux + +Linux sistemlerinde seri portlara genellikle `/dev/ttyS` üzerinden erişebilirsiniz. +Eğer bağlı cihazın ID'sini biliyorsanız ancak seri portu takip etmek istemiyorsanız, `/dev/serial/by-id/` üzerinden de referans verebilirsiniz. +`ls /dev/serial/by-id/*` komutunu çalıştırarak mevcut tüm seri bağlantı noktalarını kimlikleriyle birlikte listeleyebilirsiniz. + +## macOS + +MacOS'ta seri bağlantı noktası adları hemen hemen her şey olabilir, ancak genellikle `/dev/tty.` biçimindedir; burada id dahili aygıt tanımlayıcısıdır. +`ls /dev/tty.*` çalıştırıldığında mevcut seri portlar bulunacaktır. diff --git a/lang/proc/texts/serialPort_zh.md b/lang/proc/texts/serialPort_zh.md new file mode 100644 index 000000000..908b955b6 --- /dev/null +++ b/lang/proc/texts/serialPort_zh.md @@ -0,0 +1,20 @@ +## 窗口 + +在 Windows 系统中,您通常通过 `COM` 引用串行端口。 +XPipe 也支持只指定索引而不使用 `COM` 前缀。 +要寻址大于 9 的端口,您必须使用 UNC 路径形式,即 `\.\COM`。 + +如果安装了 WSL1 发行版,也可以在 WSL 发行版中通过 `/dev/ttyS` 引用串行端口。 +不过,这种方法在 WSL2 中不再适用。 +如果您有 WSL1 系统,可以将其作为串行连接的主机,并使用 tty 符号通过 XPipe 访问。 + +## Linux + +在 Linux 系统中,通常可以通过 `/dev/ttyS` 访问串口。 +如果您知道所连接设备的 ID,但不想跟踪串行端口,也可以通过 `/dev/serial/by-id/` 引用它们。 +运行 `ls /dev/serial/by-id/*` 可以列出所有可用串行端口及其 ID。 + +## macOS + +在 macOS 上,串行端口名称几乎可以是任何名称,但通常采用 `/dev/tty.` 的形式,其中 id 是内部设备标识符。 +运行 `ls /dev/tty.*` 可以找到可用的串行端口。 diff --git a/lang/uacc/strings/translations_da.properties b/lang/uacc/strings/translations_da.properties index d42a235c0..9701941a5 100644 --- a/lang/uacc/strings/translations_da.properties +++ b/lang/uacc/strings/translations_da.properties @@ -1,18 +1,18 @@ communityDescription=Et power-værktøj til forbindelser, der er perfekt til dit personlige brug. -professionalDescription=Professionel forbindelsesstyring til hele din serverinfrastruktur. -#custom -buyProfessional=Prøv XPipe Professional +upgradeDescription=Professionel forbindelsesstyring til hele din serverinfrastruktur. +discoverPlans=Opdag planer extendProfessional=Opgrader til de nyeste professionelle funktioner communityItem1=Ubegrænsede forbindelser til ikke-kommercielle systemer og værktøjer communityItem2=Problemfri integration med dine installerede terminaler og editorer communityItem3=Fuldt funktionsdygtig ekstern filbrowser communityItem4=Kraftfuldt scripting-system til alle shells communityItem5=Git-integration til synkronisering og deling af forbindelsesoplysninger -professionalItem1=Alle funktioner i community-udgaven -professionalItem2=Ubegrænsede forbindelser til alle kommercielle systemer og værktøjer -professionalItem3=Understøttelse af virksomhedsgodkendelsesordninger til fjernforbindelser -professionalItem4=Inkluderer alle fremtidige funktioner i Professional Edition i 1 år -professionalItem5=Modtager funktions- og sikkerhedsopdateringer for evigt +upgradeItem1=Inkluderer alle funktioner i community-udgaven +upgradeItem2=Homelab-planen understøtter et ubegrænset antal hypervisorer og avanceret SSH-auth +upgradeItem3=Den professionelle plan understøtter desuden virksomhedsoperativsystemer og -værktøjer +upgradeItem4=Virksomhedsplanen kommer med fuld fleksibilitet til din individuelle brugssag +upgrade=Opgradering +upgradeTitle=Tilgængelige planer status=Status type=Skriv licenseAlertTitle=Kommerciel brug diff --git a/lang/uacc/strings/translations_de.properties b/lang/uacc/strings/translations_de.properties index 9f981451f..c476c25f4 100644 --- a/lang/uacc/strings/translations_de.properties +++ b/lang/uacc/strings/translations_de.properties @@ -1,19 +1,18 @@ communityDescription=Ein Power-Tool für Verbindungen, das perfekt für deine persönlichen Anwendungsfälle ist. -professionalDescription=Professionelles Verbindungsmanagement für deine gesamte Serverinfrastruktur. -buyProfessional=Probiere XPipe professional aus +upgradeDescription=Professionelles Verbindungsmanagement für deine gesamte Serverinfrastruktur. +discoverPlans=Pläne entdecken extendProfessional=Upgrade auf die neuesten professionellen Funktionen communityItem1=Unbegrenzte Verbindungen zu nicht-kommerziellen Systemen und Tools communityItem2=Nahtlose Integration mit deinen installierten Terminals und Editoren communityItem3=Voll funktionsfähiger Remote-Dateibrowser communityItem4=Leistungsstarkes Skripting-System für alle Shells communityItem5=Git-Integration für die Synchronisierung und den Austausch von Verbindungsinformationen -professionalItem1=Alle Funktionen der Community Edition -professionalItem2=Unbegrenzte Verbindungen zu allen kommerziellen Systemen und Tools -#custom -professionalItem3=Unterstützung für Enterprise Authentifizierungssysteme für Verbindungen -professionalItem4=Beinhaltet alle zukünftigen Funktionen der Professional Edition für 1 Jahr -#custom -professionalItem5=Erhält kontinuierlich Funktions- und Sicherheitsupdates +upgradeItem1=Enthält alle Funktionen der Community Edition +upgradeItem2=Der Homelab-Plan unterstützt eine unbegrenzte Anzahl von Hypervisoren und erweiterte SSH-Authentifizierung +upgradeItem3=Der professionelle Plan unterstützt zusätzlich die Betriebssysteme und Tools von Unternehmen +upgradeItem4=Der Enterprise Plan bietet dir volle Flexibilität für deinen individuellen Anwendungsfall +upgrade=Upgrade +upgradeTitle=Verfügbare Pläne status=Status type=Typ licenseAlertTitle=Kommerzielle Nutzung diff --git a/lang/uacc/strings/translations_en.properties b/lang/uacc/strings/translations_en.properties index c6cdd5243..bea217085 100644 --- a/lang/uacc/strings/translations_en.properties +++ b/lang/uacc/strings/translations_en.properties @@ -1,17 +1,18 @@ communityDescription=A connection power-tool perfect for your personal use cases. -professionalDescription=Professional connection management for your entire server infrastructure. -buyProfessional=Try XPipe professional +upgradeDescription=Professional connection management for your entire server infrastructure. +discoverPlans=Discover plans extendProfessional=Upgrade to latest professional features communityItem1=Unlimited connections to non-commercial systems and tools communityItem2=Seamless integration with your installed terminals and editors communityItem3=Fully featured remote file browser communityItem4=Powerful scripting system for all shells communityItem5=Git integration for synchronization and sharing connection information -professionalItem1=All community edition features -professionalItem2=Unlimited connections to all commercial systems and tools -professionalItem3=Support for enterprise authentication schemes for remote connections -professionalItem4=Includes all future professional edition features for 1 year -professionalItem5=Receives feature and security updates forever +upgradeItem1=Include all community edition features +upgradeItem2=The homelab plan supports unlimited hypervisors and advanced SSH auth +upgradeItem3=The professional plan additionally supports enterprise operating systems and tools +upgradeItem4=The enterprise plan comes with full flexibility for your individual use case +upgrade=Upgrade +upgradeTitle=Available plans status=Status type=Type licenseAlertTitle=Commercial usage diff --git a/lang/uacc/strings/translations_es.properties b/lang/uacc/strings/translations_es.properties index 2e39a95be..632eed1e5 100644 --- a/lang/uacc/strings/translations_es.properties +++ b/lang/uacc/strings/translations_es.properties @@ -1,17 +1,18 @@ communityDescription=Una herramienta de conexión perfecta para tus casos de uso personal. -professionalDescription=Gestión profesional de conexiones para toda tu infraestructura de servidores. -buyProfessional=Prueba XPipe profesional +upgradeDescription=Gestión profesional de conexiones para toda tu infraestructura de servidores. +discoverPlans=Descubrir planes extendProfessional=Actualiza a las últimas funciones profesionales communityItem1=Conexiones ilimitadas a sistemas y herramientas no comerciales communityItem2=Integración perfecta con tus terminales y editores instalados communityItem3=Navegador de archivos remoto con todas las funciones communityItem4=Potente sistema de scripts para todos los shells communityItem5=Integración de Git para sincronizar y compartir información de conexión -professionalItem1=Todas las funciones de la edición comunitaria -professionalItem2=Conexiones ilimitadas a todos los sistemas y herramientas comerciales -professionalItem3=Soporte de esquemas de autenticación empresarial para conexiones remotas -professionalItem4=Incluye todas las futuras funciones de la edición profesional durante 1 año -professionalItem5=Recibe actualizaciones de funciones y seguridad para siempre +upgradeItem1=Incluye todas las funciones de la edición comunitaria +upgradeItem2=El plan homelab admite hipervisores ilimitados y autenticación SSH avanzada +upgradeItem3=El plan profesional admite además sistemas operativos y herramientas empresariales +upgradeItem4=El plan de empresa ofrece total flexibilidad para tu caso de uso individual +upgrade=Actualiza +upgradeTitle=Planes disponibles status=Estado type=Escribe licenseAlertTitle=Uso comercial diff --git a/lang/uacc/strings/translations_fr.properties b/lang/uacc/strings/translations_fr.properties index 3a1a6a03f..dd0b7ab9e 100644 --- a/lang/uacc/strings/translations_fr.properties +++ b/lang/uacc/strings/translations_fr.properties @@ -1,17 +1,18 @@ communityDescription=Un outil puissant de connexion parfait pour tes cas d'utilisation personnels. -professionalDescription=Gestion professionnelle des connexions pour l'ensemble de ton infrastructure de serveurs. -buyProfessional=Essaie XPipe professional +upgradeDescription=Gestion professionnelle des connexions pour l'ensemble de ton infrastructure de serveurs. +discoverPlans=Découvrir les plans extendProfessional=Mise à jour vers les dernières fonctionnalités professionnelles communityItem1=Connexions illimitées à des systèmes et outils non commerciaux communityItem2=Intégration transparente avec les terminaux et les éditeurs que tu as installés communityItem3=Navigateur de fichiers à distance complet communityItem4=Système de script puissant pour tous les shells communityItem5=Intégration Git pour la synchronisation et le partage des informations de connexion -professionalItem1=Toutes les fonctionnalités de l'édition communautaire -professionalItem2=Connexions illimitées à tous les systèmes et outils commerciaux -professionalItem3=Prise en charge des schémas d'authentification d'entreprise pour les connexions à distance -professionalItem4=Comprend toutes les futures fonctionnalités de l'édition professionnelle pendant 1 an -professionalItem5=Reçoit les mises à jour des fonctionnalités et de la sécurité pour toujours +upgradeItem1=Inclut toutes les fonctionnalités de l'édition communautaire +upgradeItem2=Le plan homelab prend en charge un nombre illimité d'hyperviseurs et l'authentification SSH avancée +upgradeItem3=Le plan professionnel prend en outre en charge les systèmes d'exploitation et les outils de l'entreprise +upgradeItem4=Le plan d'entreprise est assorti d'une flexibilité totale pour ton cas d'utilisation individuel +upgrade=Mise à niveau +upgradeTitle=Plans disponibles status=Statut type=Type de texte licenseAlertTitle=Usage commercial diff --git a/lang/uacc/strings/translations_it.properties b/lang/uacc/strings/translations_it.properties index 2183db882..d29f18488 100644 --- a/lang/uacc/strings/translations_it.properties +++ b/lang/uacc/strings/translations_it.properties @@ -1,17 +1,18 @@ communityDescription=Uno strumento di connessione perfetto per i tuoi casi d'uso personali. -professionalDescription=Gestione professionale delle connessioni per tutta la tua infrastruttura server. -buyProfessional=Prova XPipe professional +upgradeDescription=Gestione professionale delle connessioni per tutta la tua infrastruttura server. +discoverPlans=Scoprire i piani extendProfessional=Aggiornamento alle ultime funzionalità professionali communityItem1=Connessioni illimitate a sistemi e strumenti non commerciali communityItem2=Integrazione perfetta con i terminali e gli editor installati communityItem3=Browser di file remoto completo communityItem4=Potente sistema di scripting per tutte le shell communityItem5=Integrazione Git per la sincronizzazione e la condivisione delle informazioni di connessione -professionalItem1=Tutte le caratteristiche della community edition -professionalItem2=Connessioni illimitate a tutti i sistemi e gli strumenti commerciali -professionalItem3=Supporto per gli schemi di autenticazione aziendale per le connessioni remote -professionalItem4=Include tutte le funzioni future dell'edizione professionale per 1 anno -professionalItem5=Riceve aggiornamenti di funzionalità e sicurezza per sempre +upgradeItem1=Include tutte le funzionalità della community edition +upgradeItem2=Il piano Homelab supporta un numero illimitato di hypervisor e l'autenticazione SSH avanzata +upgradeItem3=Il piano professionale supporta anche i sistemi operativi e gli strumenti aziendali +upgradeItem4=Il piano enterprise è dotato di una flessibilità totale per i tuoi casi d'uso individuali +upgrade=Aggiornamento +upgradeTitle=Piani disponibili status=Stato type=Tipo licenseAlertTitle=Uso commerciale diff --git a/lang/uacc/strings/translations_ja.properties b/lang/uacc/strings/translations_ja.properties index 24a3a1aeb..f0a657a6c 100644 --- a/lang/uacc/strings/translations_ja.properties +++ b/lang/uacc/strings/translations_ja.properties @@ -1,17 +1,18 @@ communityDescription=個人的なユースケースに最適な接続パワーツール。 -professionalDescription=サーバーインフラ全体のプロフェッショナルな接続管理 -buyProfessional=XPipeプロフェッショナルを試す +upgradeDescription=サーバーインフラ全体のプロフェッショナルな接続管理 +discoverPlans=プランを発見する extendProfessional=最新のプロフェッショナル機能にアップグレードする communityItem1=非商用システムやツールに無制限に接続できる communityItem2=インストールされている端末やエディターとのシームレスな統合 communityItem3=フル機能のリモートファイルブラウザ communityItem4=すべてのシェルのための強力なスクリプトシステム communityItem5=接続情報の同期と共有のためのGitの統合 -professionalItem1=コミュニティ版の全機能 -professionalItem2=すべての商用システムとツールに無制限に接続できる -professionalItem3=リモート接続における企業認証スキームのサポート -professionalItem4=将来のプロフェッショナル版の全機能を1年間含む -professionalItem5=機能とセキュリティのアップデートを永久に受け取る +upgradeItem1=コミュニティ版のすべての機能を含む +upgradeItem2=ホームラボプランは無制限のハイパーバイザーと高度なSSH認証をサポートする +upgradeItem3=プロフェッショナル・プランでは、エンタープライズ・オペレーティング・システムとツールもサポートする。 +upgradeItem4=エンタープライズ・プランには、個々のユースケースに対応する柔軟性が備わっている。 +upgrade=アップグレード +upgradeTitle=利用可能なプラン status=ステータス type=タイプ licenseAlertTitle=商用利用 diff --git a/lang/uacc/strings/translations_nl.properties b/lang/uacc/strings/translations_nl.properties index 26512fa8d..56f4b04fc 100644 --- a/lang/uacc/strings/translations_nl.properties +++ b/lang/uacc/strings/translations_nl.properties @@ -1,17 +1,18 @@ communityDescription=Een power-tool voor verbindingen, perfect voor persoonlijk gebruik. -professionalDescription=Professioneel verbindingsbeheer voor je hele serverinfrastructuur. -buyProfessional=Probeer XPipe professional +upgradeDescription=Professioneel verbindingsbeheer voor je hele serverinfrastructuur. +discoverPlans=Plannen ontdekken extendProfessional=Upgrade naar de nieuwste professionele functies communityItem1=Onbeperkte verbindingen met niet-commerciële systemen en hulpmiddelen communityItem2=Naadloze integratie met je geïnstalleerde terminals en editors communityItem3=Volledig uitgeruste bestandsbrowser op afstand communityItem4=Krachtig scriptsysteem voor alle shells communityItem5=Git-integratie voor synchronisatie en het delen van verbindingsinformatie -professionalItem1=Alle community-editie functies -professionalItem2=Onbeperkte verbindingen met alle commerciële systemen en tools -professionalItem3=Ondersteuning voor bedrijfsverificatieschema's voor verbindingen op afstand -professionalItem4=Inclusief alle toekomstige professional edition functies voor 1 jaar -professionalItem5=Ontvangt altijd functie- en beveiligingsupdates +upgradeItem1=Omvat alle functies van de community-editie +upgradeItem2=Het homelab plan ondersteunt onbeperkte hypervisors en geavanceerde SSH auth +upgradeItem3=Het professionele plan ondersteunt bovendien bedrijfsbesturingssystemen en -tools +upgradeItem4=Het enterprise plan biedt volledige flexibiliteit voor jouw individuele gebruikssituatie +upgrade=Upgrade +upgradeTitle=Beschikbare plannen status=Status type=Type licenseAlertTitle=Commercieel gebruik diff --git a/lang/uacc/strings/translations_pt.properties b/lang/uacc/strings/translations_pt.properties index 2c0460e30..10b0e785f 100644 --- a/lang/uacc/strings/translations_pt.properties +++ b/lang/uacc/strings/translations_pt.properties @@ -1,17 +1,18 @@ communityDescription=Uma ferramenta de ligação perfeita para os teus casos de utilização pessoal. -professionalDescription=Gestão profissional das ligações para toda a tua infraestrutura de servidores. -buyProfessional=Experimenta o XPipe professional +upgradeDescription=Gestão profissional das ligações para toda a tua infraestrutura de servidores. +discoverPlans=Descobre planos extendProfessional=Actualiza para as funcionalidades profissionais mais recentes communityItem1=Ligações ilimitadas a sistemas e ferramentas não comerciais communityItem2=Integração perfeita com os teus terminais e editores instalados communityItem3=Navegador de ficheiros remoto com todas as funcionalidades communityItem4=Poderoso sistema de scripting para todos os shells communityItem5=Integração Git para sincronização e partilha de informações de ligação -professionalItem1=Todas as funcionalidades da edição comunitária -professionalItem2=Ligações ilimitadas a todos os sistemas e ferramentas comerciais -professionalItem3=Suporte para esquemas de autenticação empresarial para ligações remotas -professionalItem4=Inclui todas as funcionalidades da futura edição profissional durante 1 ano -professionalItem5=Recebe actualizações de funcionalidades e de segurança para sempre +upgradeItem1=Inclui todas as funcionalidades da edição comunitária +upgradeItem2=O plano homelab suporta hipervisores ilimitados e autenticação SSH avançada +upgradeItem3=O plano profissional suporta adicionalmente sistemas operativos e ferramentas empresariais +upgradeItem4=O plano empresarial inclui total flexibilidade para o teu caso de utilização individual +upgrade=Actualiza-te +upgradeTitle=Planos disponíveis status=Estado type=Digita licenseAlertTitle=Utilização comercial diff --git a/lang/uacc/strings/translations_ru.properties b/lang/uacc/strings/translations_ru.properties index 3a7574c9a..d7f4bf5dc 100644 --- a/lang/uacc/strings/translations_ru.properties +++ b/lang/uacc/strings/translations_ru.properties @@ -1,17 +1,18 @@ communityDescription=Инструмент для соединения, идеально подходящий для твоих личных целей. -professionalDescription=Профессиональное управление соединениями для всей твоей серверной инфраструктуры. -buyProfessional=Попробуй XPipe professional +upgradeDescription=Профессиональное управление соединениями для всей твоей серверной инфраструктуры. +discoverPlans=Узнай планы extendProfessional=Обновись до последних профессиональных функций communityItem1=Неограниченное количество подключений к некоммерческим системам и инструментам communityItem2=Бесшовная интеграция с установленными у тебя терминалами и редакторами communityItem3=Полнофункциональный браузер удаленных файлов communityItem4=Мощная система скриптов для всех оболочек communityItem5=Интеграция Git для синхронизации и обмена информацией о соединениях -professionalItem1=Все возможности community edition -professionalItem2=Неограниченное подключение ко всем коммерческим системам и инструментам -professionalItem3=Поддержка корпоративных схем аутентификации для удаленных подключений -professionalItem4=Включает в себя все будущие возможности профессионального издания на 1 год -professionalItem5=Получает обновления функций и безопасности навсегда +upgradeItem1=Включи все возможности community edition +upgradeItem2=План homelab поддерживает неограниченное количество гипервизоров и расширенный SSH-аутинг +upgradeItem3=Профессиональный план дополнительно поддерживает корпоративные операционные системы и инструменты +upgradeItem4=Корпоративный тарифный план обеспечивает полную гибкость для твоего индивидуального использования +upgrade=Обновление +upgradeTitle=Доступные планы status=Статус type=Тип licenseAlertTitle=Коммерческое использование diff --git a/lang/uacc/strings/translations_tr.properties b/lang/uacc/strings/translations_tr.properties index c7b0b1134..fd6e75cf4 100644 --- a/lang/uacc/strings/translations_tr.properties +++ b/lang/uacc/strings/translations_tr.properties @@ -1,17 +1,18 @@ communityDescription=Kişisel kullanım durumlarınız için mükemmel bir bağlantı güç aracı. -professionalDescription=Tüm sunucu altyapınız için profesyonel bağlantı yönetimi. -buyProfessional=XPipe professional'ı deneyin +upgradeDescription=Tüm sunucu altyapınız için profesyonel bağlantı yönetimi. +discoverPlans=Planları keşfedin extendProfessional=En son profesyonel özelliklere yükseltme communityItem1=Ticari olmayan sistemlere ve araçlara sınırsız bağlantı communityItem2=Kurulu terminalleriniz ve editörlerinizle sorunsuz entegrasyon communityItem3=Tam özellikli uzak dosya tarayıcısı communityItem4=Tüm kabuklar için güçlü komut dosyası sistemi communityItem5=Senkronizasyon ve bağlantı bilgilerinin paylaşımı için Git entegrasyonu -professionalItem1=Tüm topluluk sürümü özellikleri -professionalItem2=Tüm ticari sistemlere ve araçlara sınırsız bağlantı -professionalItem3=Uzak bağlantılar için kurumsal kimlik doğrulama şemaları desteği -professionalItem4=Gelecekteki tüm profesyonel sürüm özelliklerini 1 yıl boyunca içerir -professionalItem5=Özellik ve güvenlik güncellemelerini sonsuza kadar alır +upgradeItem1=Tüm topluluk sürümü özelliklerini dahil edin +upgradeItem2=Homelab planı sınırsız hipervizörü ve gelişmiş SSH kimlik doğrulamasını destekler +upgradeItem3=Profesyonel plan ayrıca kurumsal işletim sistemlerini ve araçlarını da destekler +upgradeItem4=Kurumsal plan, bireysel kullanım durumunuz için tam esneklikle birlikte gelir +upgrade=Yükseltme +upgradeTitle=Mevcut planlar status=Durum type=Tip licenseAlertTitle=Ticari kullanım diff --git a/lang/uacc/strings/translations_zh.properties b/lang/uacc/strings/translations_zh.properties index 9234d1e04..500b58885 100644 --- a/lang/uacc/strings/translations_zh.properties +++ b/lang/uacc/strings/translations_zh.properties @@ -1,17 +1,18 @@ communityDescription=最适合您个人使用的连接动力工具。 -professionalDescription=为您的整个服务器基础设施提供专业的连接管理。 -buyProfessional=试用 XPipe 专业版 +upgradeDescription=为您的整个服务器基础设施提供专业的连接管理。 +discoverPlans=发现计划 extendProfessional=升级到最新的专业功能 communityItem1=无限连接非商业系统和工具 communityItem2=与已安装的终端和编辑器无缝集成 communityItem3=功能齐全的远程文件浏览器 communityItem4=适用于所有 shell 的强大脚本系统 communityItem5=Git 集成,用于同步和共享连接信息 -professionalItem1=所有社区版功能 -professionalItem2=无限连接所有商业系统和工具 -professionalItem3=支持用于远程连接的企业身份验证方案 -professionalItem4=包括未来 1 年专业版的所有功能 -professionalItem5=永久接收功能和安全更新 +upgradeItem1=包括所有社区版功能 +upgradeItem2=家庭实验室计划支持无限的管理程序和高级 SSH 验证 +upgradeItem3=专业计划还支持企业操作系统和工具 +upgradeItem4=企业计划具有充分的灵活性,可满足您的个性化需求 +upgrade=升级 +upgradeTitle=可用计划 status=状态 type=类型 licenseAlertTitle=商业用途 diff --git a/version b/version index 83efd4153..db6498e92 100644 --- a/version +++ b/version @@ -1 +1 @@ -10.3-1 +11.0-1