Merge branch 'services' into release-10.0
|
@ -286,6 +286,10 @@ public class StoreCreationComp extends DialogComp {
|
|||
if (ex instanceof ValidationException) {
|
||||
ErrorEvent.expected(ex);
|
||||
skippable.set(false);
|
||||
} else if (ex instanceof StackOverflowError) {
|
||||
// Cycles in connection graphs can fail hard but are expected
|
||||
ErrorEvent.expected(ex);
|
||||
skippable.set(false);
|
||||
} else {
|
||||
skippable.set(true);
|
||||
}
|
||||
|
|
|
@ -40,12 +40,16 @@ public class StoreCreationMenu {
|
|||
|
||||
menu.getItems()
|
||||
.add(category(
|
||||
"addCommand", "mdi2c-code-greater-than", DataStoreProvider.CreationCategory.COMMAND, "cmd"));
|
||||
"addService", "mdi2c-cloud-braces", DataStoreProvider.CreationCategory.SERVICE, null));
|
||||
|
||||
menu.getItems()
|
||||
.add(category(
|
||||
"addTunnel", "mdi2v-vector-polyline-plus", DataStoreProvider.CreationCategory.TUNNEL, null));
|
||||
|
||||
menu.getItems()
|
||||
.add(category(
|
||||
"addCommand", "mdi2c-code-greater-than", DataStoreProvider.CreationCategory.COMMAND, "cmd"));
|
||||
|
||||
menu.getItems()
|
||||
.add(category("addDatabase", "mdi2d-database-plus", DataStoreProvider.CreationCategory.DATABASE, null));
|
||||
}
|
||||
|
|
|
@ -410,7 +410,6 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
var move = new Menu(AppI18n.get("moveTo"), new FontIcon("mdi2f-folder-move-outline"));
|
||||
StoreViewState.get()
|
||||
.getSortedCategories(wrapper.getCategory().getValue().getRoot())
|
||||
.getList()
|
||||
.forEach(storeCategoryWrapper -> {
|
||||
MenuItem m = new MenuItem();
|
||||
m.textProperty().setValue(" ".repeat(storeCategoryWrapper.getDepth()) + storeCategoryWrapper.getName().getValue());
|
||||
|
@ -418,7 +417,7 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
wrapper.moveTo(storeCategoryWrapper.getCategory());
|
||||
event.consume();
|
||||
});
|
||||
if (storeCategoryWrapper.getParent() == null) {
|
||||
if (storeCategoryWrapper.getParent() == null || storeCategoryWrapper.equals(wrapper.getCategory().getValue())) {
|
||||
m.setDisable(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -172,7 +172,6 @@ public class StoreSection {
|
|||
var ordered = sorted(cached, category);
|
||||
var filtered = ordered.filtered(
|
||||
section -> {
|
||||
var showFilter = filterString == null || section.matchesFilter(filterString.get());
|
||||
var matchesSelector = section.anyMatches(entryFilter);
|
||||
// Prevent updates for children on category switching by checking depth
|
||||
var showCategory = category == null
|
||||
|
@ -185,7 +184,7 @@ public class StoreSection {
|
|||
!DataStorage.get().isRootEntry(section.getWrapper().getEntry());
|
||||
var showProvider = section.getWrapper().getEntry().getProvider() == null ||
|
||||
section.getWrapper().getEntry().getProvider().shouldShow(section.getWrapper());
|
||||
return showFilter && matchesSelector && showCategory && notRoot && showProvider;
|
||||
return matchesSelector && showCategory && notRoot && showProvider;
|
||||
},
|
||||
category,
|
||||
filterString,
|
||||
|
|
|
@ -281,11 +281,9 @@ public class StoreViewState {
|
|||
public int compare(StoreCategoryWrapper o1, StoreCategoryWrapper o2) {
|
||||
var o1Root = o1.getRoot();
|
||||
var o2Root = o2.getRoot();
|
||||
|
||||
if (o1Root.equals(getAllConnectionsCategory()) && !o1Root.equals(o2Root)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (o2Root.equals(getAllConnectionsCategory()) && !o1Root.equals(o2Root)) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -302,6 +300,22 @@ public class StoreViewState {
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (o1.getDepth() > o2.getDepth()) {
|
||||
if (o1.getParent() == o2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return compare(o1.getParent(), o2);
|
||||
}
|
||||
|
||||
if (o1.getDepth() < o2.getDepth()) {
|
||||
if (o2.getParent() == o1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return compare(o1, o2.getParent());
|
||||
}
|
||||
|
||||
var parent = compare(o1.getParent(), o2.getParent());
|
||||
if (parent != 0) {
|
||||
return parent;
|
||||
|
|
|
@ -31,7 +31,9 @@ public interface DataStoreProvider {
|
|||
default boolean shouldShow(StoreEntryWrapper w) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
default void onChildrenRefresh(DataStoreEntry entry) {}
|
||||
|
||||
default ObservableBooleanValue busy(StoreEntryWrapper wrapper) {
|
||||
return new SimpleBooleanProperty(false);
|
||||
}
|
||||
|
@ -216,6 +218,7 @@ public interface DataStoreProvider {
|
|||
HOST,
|
||||
DATABASE,
|
||||
SHELL,
|
||||
SERVICE,
|
||||
COMMAND,
|
||||
TUNNEL,
|
||||
SCRIPT,
|
||||
|
|
|
@ -194,10 +194,10 @@ public interface ExternalEditorType extends PrefsChoiceValue {
|
|||
|
||||
@Override
|
||||
public void launch(Path file) throws Exception {
|
||||
ExternalApplicationHelper.startAsync(CommandBuilder.of()
|
||||
.add("open", "-a")
|
||||
.addQuoted(applicationName)
|
||||
.addFile(file.toString()));
|
||||
try (var sc = LocalShell.getShell().start()) {
|
||||
sc.executeSimpleCommand(CommandBuilder.of()
|
||||
.add("open", "-a").addQuoted(applicationName).addFile(file.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -446,6 +446,8 @@ public abstract class DataStorage {
|
|||
}
|
||||
});
|
||||
saveAsync();
|
||||
toAdd.forEach(dataStoreEntryRef -> dataStoreEntryRef.get().getProvider().onChildrenRefresh(dataStoreEntryRef.getEntry()));
|
||||
toUpdate.forEach(dataStoreEntryRef -> dataStoreEntryRef.getKey().getProvider().onChildrenRefresh(dataStoreEntryRef.getKey()));
|
||||
return !newChildren.isEmpty();
|
||||
}
|
||||
|
||||
|
|
|
@ -514,17 +514,11 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
|||
|
||||
@Override
|
||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||
try (ShellControl pc = LocalShell.getShell()) {
|
||||
var suffix = "\"" + configuration.getScriptFile().toString().replaceAll("\"", "\\\\\"") + "\"";
|
||||
pc.osascriptCommand(String.format(
|
||||
"""
|
||||
activate application "Terminal"
|
||||
delay 1
|
||||
tell app "Terminal" to do script %s
|
||||
""",
|
||||
suffix))
|
||||
.execute();
|
||||
}
|
||||
LocalShell.getShell()
|
||||
.executeSimpleCommand(CommandBuilder.of()
|
||||
.add("open", "-a")
|
||||
.addQuoted("Terminal.app")
|
||||
.addFile(configuration.getScriptFile()));
|
||||
}
|
||||
};
|
||||
ExternalTerminalType ITERM2 = new MacOsType("app.iterm2", "iTerm") {
|
||||
|
@ -550,26 +544,11 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
|||
|
||||
@Override
|
||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||
try (ShellControl pc = LocalShell.getShell()) {
|
||||
pc.osascriptCommand(String.format(
|
||||
"""
|
||||
if application "iTerm" is not running then
|
||||
launch application "iTerm"
|
||||
delay 1
|
||||
tell application "iTerm"
|
||||
tell current tab of current window
|
||||
close
|
||||
end tell
|
||||
end tell
|
||||
end if
|
||||
tell application "iTerm"
|
||||
activate
|
||||
create window with default profile command "%s"
|
||||
end tell
|
||||
""",
|
||||
configuration.getScriptFile().toString().replaceAll("\"", "\\\\\"")))
|
||||
.execute();
|
||||
}
|
||||
LocalShell.getShell()
|
||||
.executeSimpleCommand(CommandBuilder.of()
|
||||
.add("open", "-a")
|
||||
.addQuoted("iTerm.app")
|
||||
.addFile(configuration.getScriptFile()));
|
||||
}
|
||||
};
|
||||
ExternalTerminalType WARP = new MacOsType("app.warp", "Warp") {
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
package io.xpipe.app.terminal;
|
||||
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.prefs.ExternalApplicationHelper;
|
||||
import io.xpipe.app.prefs.ExternalApplicationType;
|
||||
import io.xpipe.app.util.LocalShell;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.app.util.WindowsRegistry;
|
||||
import io.xpipe.core.process.CommandBuilder;
|
||||
import io.xpipe.core.process.OsType;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
@ -26,7 +31,7 @@ public interface WezTerminalType extends ExternalTerminalType {
|
|||
|
||||
@Override
|
||||
default boolean isRecommended() {
|
||||
return false;
|
||||
return OsType.getLocal() != OsType.WINDOWS;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -51,25 +56,62 @@ public interface WezTerminalType extends ExternalTerminalType {
|
|||
|
||||
@Override
|
||||
protected Optional<Path> determineInstallation() {
|
||||
Optional<String> launcherDir;
|
||||
launcherDir = WindowsRegistry.local().readValue(
|
||||
WindowsRegistry.HKEY_LOCAL_MACHINE,
|
||||
"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{BCF6F0DA-5B9A-408D-8562-F680AE6E1EAF}_is1",
|
||||
"InstallLocation")
|
||||
.map(p -> p + "\\wezterm-gui.exe");
|
||||
return launcherDir.map(Path::of);
|
||||
try {
|
||||
var foundKey = WindowsRegistry.local().findKeyForEqualValueMatchRecursive(WindowsRegistry.HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", "http://wezfurlong.org/wezterm");
|
||||
if (foundKey.isPresent()) {
|
||||
var installKey = WindowsRegistry.local().readValue(
|
||||
foundKey.get().getHkey(),
|
||||
foundKey.get().getKey(),
|
||||
"InstallLocation");
|
||||
if (installKey.isPresent()) {
|
||||
return installKey.map(p -> p + "\\wezterm-gui.exe").map(Path::of);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ErrorEvent.fromThrowable(ex).omit().handle();
|
||||
}
|
||||
|
||||
try (ShellControl pc = LocalShell.getShell()) {
|
||||
if (pc.executeSimpleBooleanCommand(pc.getShellDialect().getWhichCommand("wezterm-gui"))) {
|
||||
return Optional.of(Path.of("wezterm-gui"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e).omit().handle();
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
class Linux extends SimplePathType implements WezTerminalType {
|
||||
class Linux extends ExternalApplicationType implements WezTerminalType {
|
||||
|
||||
public Linux() {
|
||||
super("app.wezterm", "wezterm-gui", true);
|
||||
super("app.wezterm");
|
||||
}
|
||||
|
||||
public boolean isAvailable() {
|
||||
try (ShellControl pc = LocalShell.getShell()) {
|
||||
return pc.executeSimpleBooleanCommand(pc.getShellDialect().getWhichCommand("wezterm")) &&
|
||||
pc.executeSimpleBooleanCommand(pc.getShellDialect().getWhichCommand("wezterm-gui"));
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e).omit().handle();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CommandBuilder toCommand(LaunchConfiguration configuration) {
|
||||
return CommandBuilder.of().add("start").addFile(configuration.getScriptFile());
|
||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||
var spawn = LocalShell.getShell().command(CommandBuilder.of().addFile("wezterm")
|
||||
.add("cli", "spawn")
|
||||
.addFile(configuration.getScriptFile()))
|
||||
.executeAndCheck();
|
||||
if (!spawn) {
|
||||
ExternalApplicationHelper.startAsync(CommandBuilder.of()
|
||||
.addFile("wezterm-gui")
|
||||
.add("start")
|
||||
.addFile(configuration.getScriptFile()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,20 +123,27 @@ public interface WezTerminalType extends ExternalTerminalType {
|
|||
|
||||
@Override
|
||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||
var path = LocalShell.getShell()
|
||||
.command(String.format(
|
||||
"mdfind -name '%s' -onlyin /Applications -onlyin ~/Applications -onlyin /System/Applications 2>/dev/null",
|
||||
applicationName))
|
||||
.readStdoutOrThrow();
|
||||
var c = CommandBuilder.of()
|
||||
.addFile(Path.of(path)
|
||||
.resolve("Contents")
|
||||
.resolve("MacOS")
|
||||
.resolve("wezterm-gui")
|
||||
.toString())
|
||||
.add("start")
|
||||
.add(configuration.getDialectLaunchCommand());
|
||||
ExternalApplicationHelper.startAsync(c);
|
||||
try (var sc = LocalShell.getShell()) {
|
||||
var path = sc.command(
|
||||
String.format("mdfind -name '%s' -onlyin /Applications -onlyin ~/Applications -onlyin /System/Applications 2>/dev/null",
|
||||
applicationName)).readStdoutOrThrow();
|
||||
var spawn = sc.command(CommandBuilder.of().addFile(Path.of(path)
|
||||
.resolve("Contents")
|
||||
.resolve("MacOS")
|
||||
.resolve("wezterm").toString())
|
||||
.add("cli", "spawn", "--pane-id", "0")
|
||||
.addFile(configuration.getScriptFile()))
|
||||
.executeAndCheck();
|
||||
if (!spawn) {
|
||||
ExternalApplicationHelper.startAsync(CommandBuilder.of()
|
||||
.addFile(Path.of(path)
|
||||
.resolve("Contents")
|
||||
.resolve("MacOS")
|
||||
.resolve("wezterm-gui").toString())
|
||||
.add("start")
|
||||
.addFile(configuration.getScriptFile()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,10 @@ public class AskpassAlert {
|
|||
|
||||
@Override
|
||||
public void handle(long now) {
|
||||
if (!stage.isShowing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (regainedFocusCount >= 2) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -28,10 +28,9 @@ public class FileOpener {
|
|||
try {
|
||||
editor.launch(Path.of(localFile).toRealPath());
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e)
|
||||
.description("Unable to launch editor "
|
||||
ErrorEvent.fromThrowable("Unable to launch editor "
|
||||
+ editor.toTranslatedString().getValue()
|
||||
+ ".\nMaybe try to use a different editor in the settings.")
|
||||
+ ".\nMaybe try to use a different editor in the settings.", e)
|
||||
.expected()
|
||||
.handle();
|
||||
}
|
||||
|
@ -52,8 +51,7 @@ public class FileOpener {
|
|||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e)
|
||||
.description("Unable to open file " + localFile)
|
||||
ErrorEvent.fromThrowable("Unable to open file " + localFile, e)
|
||||
.handle();
|
||||
}
|
||||
}
|
||||
|
@ -68,8 +66,7 @@ public class FileOpener {
|
|||
pc.executeSimpleCommand("open \"" + localFile + "\"");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e)
|
||||
.description("Unable to open file " + localFile)
|
||||
ErrorEvent.fromThrowable("Unable to open file " + localFile, e)
|
||||
.handle();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,14 @@ import java.util.Locale;
|
|||
|
||||
public class HostHelper {
|
||||
|
||||
private static int portCounter = 0;
|
||||
|
||||
public static int randomPort() {
|
||||
var p = 40000 + portCounter;
|
||||
portCounter = portCounter + 1 % 1000;
|
||||
return p;
|
||||
}
|
||||
|
||||
public static int findRandomOpenPortOnAllLocalInterfaces() throws IOException {
|
||||
try (ServerSocket socket = new ServerSocket(0)) {
|
||||
return socket.getLocalPort();
|
||||
|
|
|
@ -88,7 +88,6 @@ project.ext {
|
|||
arch = getArchName()
|
||||
privateExtensions = file("$rootDir/private_extensions.txt").exists() ? file("$rootDir/private_extensions.txt").readLines() : []
|
||||
isFullRelease = System.getenv('RELEASE') != null && Boolean.parseBoolean(System.getenv('RELEASE'))
|
||||
isPreRelease = System.getenv('PRERELEASE') != null && Boolean.parseBoolean(System.getenv('PRERELEASE'))
|
||||
isStage = System.getenv('STAGE') != null && Boolean.parseBoolean(System.getenv('STAGE'))
|
||||
rawVersion = file('version').text.trim()
|
||||
versionString = rawVersion + (isFullRelease || isStage ? '' : '-SNAPSHOT')
|
||||
|
@ -106,7 +105,7 @@ project.ext {
|
|||
website = 'https://xpipe.io'
|
||||
sourceWebsite = isStage ? 'https://github.com/xpipe-io/xpipe-ptb' : 'https://github.com/xpipe-io/xpipe'
|
||||
authors = 'Christopher Schnick'
|
||||
javafxVersion = '22.0.1'
|
||||
javafxVersion = '23-ea+18'
|
||||
platformName = getPlatformName()
|
||||
languages = ["en", "nl", "es", "fr", "de", "it", "pt", "ru", "ja", "zh", "tr", "da"]
|
||||
jvmRunArgs = [
|
||||
|
@ -159,6 +158,11 @@ if (isFullRelease && rawVersion.contains("-")) {
|
|||
throw new IllegalArgumentException("Releases must have canonical versions")
|
||||
}
|
||||
|
||||
|
||||
if (isStage && !rawVersion.contains("-")) {
|
||||
throw new IllegalArgumentException("Stage releases must have release numbers")
|
||||
}
|
||||
|
||||
def replaceVariablesInFileAsString(String f, Map<String, String> replacements) {
|
||||
def fileName = file(f).getName()
|
||||
def text = file(f).text
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package io.xpipe.core.process;
|
||||
|
||||
public interface CommandFeedbackPredicate {
|
||||
|
||||
boolean test(CommandBuilder command) throws Exception;
|
||||
}
|
|
@ -8,7 +8,7 @@ import io.xpipe.core.util.JacksonizedValue;
|
|||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
|
||||
@JsonTypeName("local")
|
||||
public class LocalStore extends JacksonizedValue implements ShellStore, StatefulDataStore<ShellStoreState> {
|
||||
public class LocalStore extends JacksonizedValue implements NetworkTunnelStore, ShellStore, StatefulDataStore<ShellStoreState> {
|
||||
|
||||
@Override
|
||||
public Class<ShellStoreState> getStateClass() {
|
||||
|
@ -23,4 +23,9 @@ public class LocalStore extends JacksonizedValue implements ShellStore, Stateful
|
|||
pc.withShellStateFail(this);
|
||||
return pc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStore getNetworkParent() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
public abstract class NetworkTunnelSession extends Session {
|
||||
|
||||
protected NetworkTunnelSession(SessionListener listener) {
|
||||
super(listener);
|
||||
}
|
||||
|
||||
public abstract int getLocalPort();
|
||||
|
||||
public abstract int getRemotePort();
|
||||
}
|
142
core/src/main/java/io/xpipe/core/store/NetworkTunnelStore.java
Normal file
|
@ -0,0 +1,142 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public interface NetworkTunnelStore extends DataStore {
|
||||
|
||||
static AtomicInteger portCounter = new AtomicInteger();
|
||||
|
||||
public static int randomPort() {
|
||||
var p = 40000 + portCounter.get();
|
||||
portCounter.set(portCounter.get() + 1 % 1000);
|
||||
return p;
|
||||
}
|
||||
|
||||
interface TunnelFunction {
|
||||
|
||||
NetworkTunnelSession create(int localPort, int remotePort);
|
||||
}
|
||||
|
||||
DataStore getNetworkParent();
|
||||
|
||||
default boolean requiresTunnel() {
|
||||
NetworkTunnelStore current = this;
|
||||
while (true) {
|
||||
var func = current.tunnelSession();
|
||||
if (func != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (current.getNetworkParent() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current.getNetworkParent() instanceof NetworkTunnelStore t) {
|
||||
current = t;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default boolean isLocallyTunneable() {
|
||||
NetworkTunnelStore current = this;
|
||||
while (true) {
|
||||
if (current.getNetworkParent() == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (current.getNetworkParent() instanceof NetworkTunnelStore t) {
|
||||
current = t;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default NetworkTunnelSession sessionChain(int local, int remotePort) throws Exception {
|
||||
if (!isLocallyTunneable()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
var running = new AtomicBoolean();
|
||||
var runningCounter = new AtomicInteger();
|
||||
var counter = new AtomicInteger();
|
||||
var sessions = new ArrayList<NetworkTunnelSession>();
|
||||
NetworkTunnelStore current = this;
|
||||
do {
|
||||
var func = current.tunnelSession();
|
||||
if (func == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var currentLocalPort = isLast(current) ? local : randomPort();
|
||||
var currentRemotePort = sessions.isEmpty() ? remotePort : sessions.getLast().getLocalPort();
|
||||
var t = func.create(currentLocalPort, currentRemotePort);
|
||||
t.addListener(r -> {
|
||||
if (r) {
|
||||
runningCounter.incrementAndGet();
|
||||
} else {
|
||||
runningCounter.decrementAndGet();
|
||||
}
|
||||
running.set(runningCounter.get() == counter.get());
|
||||
});
|
||||
t.start();
|
||||
sessions.add(t);
|
||||
counter.incrementAndGet();
|
||||
} while ((current = (NetworkTunnelStore) current.getNetworkParent()) != null);
|
||||
|
||||
if (sessions.size() == 1) {
|
||||
return sessions.getFirst();
|
||||
}
|
||||
|
||||
if (sessions.isEmpty()) {
|
||||
return new NetworkTunnelSession(null) {
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLocalPort() {
|
||||
return local;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRemotePort() {
|
||||
return remotePort;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return new SessionChain(running1 -> {}, sessions);
|
||||
}
|
||||
|
||||
default boolean isLast(NetworkTunnelStore tunnelStore) {
|
||||
NetworkTunnelStore current = tunnelStore;
|
||||
while ((current = (NetworkTunnelStore) current.getNetworkParent()) != null) {
|
||||
var func = current.tunnelSession();
|
||||
if (func != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default TunnelFunction tunnelSession() {
|
||||
return null;
|
||||
}
|
||||
}
|
23
core/src/main/java/io/xpipe/core/store/ServiceStore.java
Normal file
|
@ -0,0 +1,23 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import java.util.OptionalInt;
|
||||
|
||||
public interface ServiceStore extends SingletonSessionStore<SessionChain> {
|
||||
|
||||
NetworkTunnelStore getParent();
|
||||
|
||||
int getPort();
|
||||
|
||||
OptionalInt getTargetPort();
|
||||
|
||||
@Override
|
||||
default SessionChain newSession() throws Exception {
|
||||
var s = getParent().tunnelSession();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default Class<?> getSessionClass() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,10 +1,29 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
public abstract class Session {
|
||||
public abstract class Session implements AutoCloseable {
|
||||
|
||||
protected SessionListener listener;
|
||||
|
||||
protected Session(SessionListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void addListener(SessionListener n) {
|
||||
var current = this.listener;
|
||||
this.listener = running -> {
|
||||
current.onStateChange(running);
|
||||
n.onStateChange(running);
|
||||
};
|
||||
}
|
||||
|
||||
public abstract boolean isRunning();
|
||||
|
||||
public abstract void start() throws Exception;
|
||||
|
||||
public abstract void stop() throws Exception;
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
|
45
core/src/main/java/io/xpipe/core/store/SessionChain.java
Normal file
|
@ -0,0 +1,45 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SessionChain extends NetworkTunnelSession {
|
||||
|
||||
private final List<NetworkTunnelSession> sessions;
|
||||
private int runningCounter;
|
||||
|
||||
public SessionChain(SessionListener listener, List<NetworkTunnelSession> sessions) {
|
||||
super(listener);
|
||||
this.sessions = sessions;
|
||||
sessions.forEach(session -> session.addListener(running -> {
|
||||
runningCounter += running ? 1 : -1;
|
||||
}));
|
||||
}
|
||||
|
||||
public int getLocalPort() {
|
||||
return sessions.getFirst().getLocalPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRemotePort() {
|
||||
return sessions.getLast().getRemotePort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return sessions.stream().allMatch(session -> session.isRunning());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
for (Session session : sessions) {
|
||||
session.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
for (Session session : sessions) {
|
||||
session.stop();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
public interface SessionListener {
|
||||
|
||||
void onStateChange(boolean running);
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
public interface SingletonSessionStore<T extends Session> extends ExpandedLifecycleStore, InternalCacheDataStore {
|
||||
public interface SingletonSessionStore<T extends Session>
|
||||
extends ExpandedLifecycleStore, InternalCacheDataStore, SessionListener {
|
||||
|
||||
@Override
|
||||
default void finalizeValidate() throws Exception {
|
||||
|
@ -19,9 +20,10 @@ public interface SingletonSessionStore<T extends Session> extends ExpandedLifecy
|
|||
return getCache("sessionEnabled", Boolean.class, false);
|
||||
}
|
||||
|
||||
default void onSessionUpdate(boolean active) {
|
||||
setSessionEnabled(active);
|
||||
setCache("sessionRunning", active);
|
||||
@Override
|
||||
default void onStateChange(boolean running) {
|
||||
setSessionEnabled(running);
|
||||
setCache("sessionRunning", running);
|
||||
}
|
||||
|
||||
T newSession() throws Exception;
|
||||
|
@ -50,9 +52,9 @@ public interface SingletonSessionStore<T extends Session> extends ExpandedLifecy
|
|||
s = newSession();
|
||||
s.start();
|
||||
setCache("session", s);
|
||||
onSessionUpdate(true);
|
||||
onStateChange(true);
|
||||
} catch (Exception ex) {
|
||||
onSessionUpdate(false);
|
||||
onStateChange(false);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +67,7 @@ public interface SingletonSessionStore<T extends Session> extends ExpandedLifecy
|
|||
if (ex != null) {
|
||||
ex.stop();
|
||||
setCache("session", null);
|
||||
onSessionUpdate(false);
|
||||
onStateChange(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
20
dist/changelogs/9.4_incremental.md
vendored
|
@ -8,6 +8,8 @@ The file transfer mechanism when editing files had some flaws, which under rare
|
|||
|
||||
The entire transfer implementation has been rewritten to iron out these issues and increase reliability. Other file browser actions have also been made more reliable.
|
||||
|
||||
There seems to be another separate issue with a PowerShell bug when connecting to a Windows system, causing file uploads to be slow. For now, xpipe can fall back to pwsh if it is installed to work around this issue.
|
||||
|
||||
## Git vault improvements
|
||||
|
||||
The conflict resolution has been improved
|
||||
|
@ -15,11 +17,27 @@ The conflict resolution has been improved
|
|||
- In case of a merge conflict, overwriting local changes will now preserve all connections that are not added to the git vault, including local connections
|
||||
- You now have the option to force push changes when a conflict occurs while XPipe is saving while running, not requiring a restart anymore
|
||||
|
||||
## Terminal improvements
|
||||
|
||||
The terminal integration got reworked for some terminals:
|
||||
- iTerm can now launch tabs instead of individual windows. There were also a few issues fixed that prevented it from launching sometimes
|
||||
- WezTerm now supports tabs on Linux and macOS. The Windows installation detection has been improved to detect all installed versions
|
||||
- Terminal.app will now launch faster
|
||||
|
||||
## Other
|
||||
|
||||
- You can now add simple RDP connections without a file
|
||||
- Fix VMware Player/Workstation and MSYS2 not being detected on Windows. Now simply searching for connections should add them automatically if they are installed
|
||||
- The file browser sidebar now only contains connections that can be opened in it, reducing the amount of connection shown
|
||||
- Clarify error message for RealVNC servers, highlighting that RealVNC uses a proprietary protocol spec that can't be supported by third-party VNC clients like xpipe
|
||||
- Fix Linux builds containing unnecessary debug symbols
|
||||
- Fix AUR package also installing a debug package
|
||||
- Fix application restart not working properly on macOS
|
||||
- Fix possibility of selecting own children connections as hosts, causing a stack overflow. Please don't try to create cycles in your connection graphs
|
||||
- Fix vault secrets not correctly updating unless restarted when changing vault passphrase
|
||||
- Fix connection launcher desktop shortcuts and URLs not properly executing if xpipe is not running
|
||||
- Fix move to ... menu sometimes not ordering categories correctly
|
||||
- Fix SSH command failing on macOS with homebrew openssh package installed
|
||||
- Fix SSH connections not opening the correct shell environment on Windows when username contained spaces due to an OpenSSH bug
|
||||
- Fix SSH connections not opening the correct shell environment on Windows systems when username contained spaces due to an OpenSSH bug
|
||||
- Fix newly added connections not having the correct order
|
||||
- Fix error messages of external editor programs not being shown when they failed to start
|
||||
|
|
2
dist/jpackage.gradle
vendored
|
@ -58,7 +58,7 @@ jlink {
|
|||
]
|
||||
|
||||
if (org.gradle.internal.os.OperatingSystem.current().isLinux()) {
|
||||
options += ['--strip-native-debug-symbols']
|
||||
options.addAll('--strip-native-debug-symbols', 'exclude-debuginfo-files')
|
||||
}
|
||||
|
||||
if (useBundledJavaFx) {
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package io.xpipe.ext.base.service;
|
||||
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.app.util.HostHelper;
|
||||
import io.xpipe.app.util.Validators;
|
||||
import io.xpipe.core.store.*;
|
||||
import io.xpipe.core.util.JacksonizedValue;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
@SuperBuilder
|
||||
@Getter
|
||||
public abstract class AbstractServiceStore extends JacksonizedValue implements SingletonSessionStore<NetworkTunnelSession>, DataStore {
|
||||
|
||||
public abstract DataStoreEntryRef<NetworkTunnelStore> getHost();
|
||||
|
||||
private final Integer remotePort;
|
||||
private final Integer localPort;
|
||||
|
||||
@Override
|
||||
public void checkComplete() throws Throwable {
|
||||
Validators.nonNull(getHost());
|
||||
Validators.isType(getHost(), NetworkTunnelStore.class);
|
||||
Validators.nonNull(remotePort);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkTunnelSession newSession() throws Exception {
|
||||
var l = localPort != null ? localPort : HostHelper.findRandomOpenPortOnAllLocalInterfaces();
|
||||
return getHost().getStore().sessionChain(l, remotePort);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getSessionClass() {
|
||||
return NetworkTunnelSession.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package io.xpipe.ext.base.service;
|
||||
|
||||
import io.xpipe.app.comp.store.StoreEntryComp;
|
||||
import io.xpipe.app.comp.store.StoreEntryWrapper;
|
||||
import io.xpipe.app.comp.store.StoreSection;
|
||||
import io.xpipe.app.ext.DataStoreProvider;
|
||||
import io.xpipe.app.ext.SingletonSessionStoreProvider;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.util.DataStoreFormatter;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractServiceStoreProvider implements SingletonSessionStoreProvider, DataStoreProvider {
|
||||
|
||||
@Override
|
||||
public DataStoreEntry getSyntheticParent(DataStoreEntry store) {
|
||||
AbstractServiceStore s = store.getStore().asNeeded();
|
||||
return DataStorage.get().getOrCreateNewSyntheticEntry(s.getHost().get(), "Services", ServiceGroupStore.builder().parent(s.getHost()).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoreEntryComp customEntryComp(StoreSection sec, boolean preferLarge) {
|
||||
var toggle = createToggleComp(sec);
|
||||
toggle.setCustomVisibility(Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
AbstractServiceStore s = sec.getWrapper().getEntry().getStore().asNeeded();
|
||||
if (!s.getHost().getStore().requiresTunnel()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
sec.getWrapper().getCache()));
|
||||
return StoreEntryComp.create(sec.getWrapper(), toggle, preferLarge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSearchableTerms(DataStore store) {
|
||||
AbstractServiceStore s = store.asNeeded();
|
||||
return s.getLocalPort() != null ? List.of("" + s.getRemotePort(), "" + s.getLocalPort()) : List.of("" + s.getRemotePort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String summaryString(StoreEntryWrapper wrapper) {
|
||||
AbstractServiceStore s = wrapper.getEntry().getStore().asNeeded();
|
||||
return DataStoreFormatter.toApostropheName(s.getHost().get()) + " service";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableValue<String> informationString(StoreEntryWrapper wrapper) {
|
||||
AbstractServiceStore s = wrapper.getEntry().getStore().asNeeded();
|
||||
if (s.getLocalPort() != null) {
|
||||
return new SimpleStringProperty("Port " + s.getLocalPort() + " <- " + s.getRemotePort());
|
||||
} else {
|
||||
return new SimpleStringProperty("Port " + s.getRemotePort());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayIconFileName(DataStore store) {
|
||||
return "base:service_icon.svg";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package io.xpipe.ext.base.service;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.core.store.NetworkTunnelStore;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
@SuperBuilder
|
||||
@Getter
|
||||
@Jacksonized
|
||||
@JsonTypeName("service")
|
||||
public class CustomServiceStore extends AbstractServiceStore {
|
||||
|
||||
private final DataStoreEntryRef<NetworkTunnelStore> host;
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package io.xpipe.ext.base.service;
|
||||
|
||||
import io.xpipe.app.comp.store.StoreViewState;
|
||||
import io.xpipe.app.ext.GuiDialog;
|
||||
import io.xpipe.app.fxcomps.impl.DataStoreChoiceComp;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.util.OptionsBuilder;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.NetworkTunnelStore;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CustomServiceStoreProvider extends AbstractServiceStoreProvider {
|
||||
|
||||
@Override
|
||||
public CreationCategory getCreationCategory() {
|
||||
return CreationCategory.SERVICE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
CustomServiceStore st = store.getValue().asNeeded();
|
||||
var host = new SimpleObjectProperty<>(st.getHost());
|
||||
var localPort = new SimpleObjectProperty<>(st.getLocalPort());
|
||||
var remotePort = new SimpleObjectProperty<>(st.getRemotePort());
|
||||
|
||||
var q = new OptionsBuilder()
|
||||
.nameAndDescription("serviceHost")
|
||||
.addComp(
|
||||
DataStoreChoiceComp.other(
|
||||
host,
|
||||
NetworkTunnelStore.class,
|
||||
n -> n.getStore().isLocallyTunneable(),
|
||||
StoreViewState.get().getAllConnectionsCategory()),
|
||||
host)
|
||||
.nonNull()
|
||||
.nameAndDescription("serviceRemotePort")
|
||||
.addInteger(remotePort)
|
||||
.nonNull()
|
||||
.nameAndDescription("serviceLocalPort")
|
||||
.addInteger(localPort)
|
||||
.bind(
|
||||
() -> {
|
||||
return CustomServiceStore.builder()
|
||||
.host(host.get())
|
||||
.localPort(localPort.get())
|
||||
.remotePort(remotePort.get())
|
||||
.build();
|
||||
},
|
||||
store);
|
||||
return q.buildDialog();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStore defaultStore() {
|
||||
return CustomServiceStore.builder().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPossibleNames() {
|
||||
return List.of("service");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<?>> getStoreClasses() {
|
||||
return List.of(CustomServiceStore.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package io.xpipe.ext.base.service;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.NetworkTunnelStore;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
@SuperBuilder
|
||||
@Getter
|
||||
@Jacksonized
|
||||
@JsonTypeName("fixedService")
|
||||
public class FixedServiceStore extends AbstractServiceStore {
|
||||
|
||||
private final DataStoreEntryRef<NetworkTunnelStore> host;
|
||||
private final DataStoreEntryRef<? extends DataStore> parent;
|
||||
|
||||
@Override
|
||||
public DataStoreEntryRef<NetworkTunnelStore> getHost() {
|
||||
return host;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package io.xpipe.ext.base.service;
|
||||
|
||||
import io.xpipe.app.comp.store.StoreEntryWrapper;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class FixedServiceStoreProvider extends AbstractServiceStoreProvider {
|
||||
|
||||
@Override
|
||||
public DataStoreEntry getDisplayParent(DataStoreEntry store) {
|
||||
FixedServiceStore s = store.getStore().asNeeded();
|
||||
return s.getParent().get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPossibleNames() {
|
||||
return List.of("fixedService");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableValue<String> informationString(StoreEntryWrapper wrapper) {
|
||||
FixedServiceStore s = wrapper.getEntry().getStore().asNeeded();
|
||||
return new SimpleStringProperty("Port " + s.getRemotePort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<?>> getStoreClasses() {
|
||||
return List.of(FixedServiceStore.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package io.xpipe.ext.base.service;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.app.util.Validators;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.util.JacksonizedValue;
|
||||
import io.xpipe.ext.base.GroupStore;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
@Getter
|
||||
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
|
||||
@SuperBuilder
|
||||
@Jacksonized
|
||||
@JsonTypeName("serviceGroup")
|
||||
public class ServiceGroupStore extends JacksonizedValue implements DataStore, GroupStore<DataStore> {
|
||||
|
||||
DataStoreEntryRef<? extends DataStore> parent;
|
||||
|
||||
@Override
|
||||
public void checkComplete() throws Throwable {
|
||||
Validators.nonNull(parent);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package io.xpipe.ext.base.service;
|
||||
|
||||
import io.xpipe.app.comp.base.StoreToggleComp;
|
||||
import io.xpipe.app.comp.base.SystemStateComp;
|
||||
import io.xpipe.app.comp.store.StoreEntryComp;
|
||||
import io.xpipe.app.comp.store.StoreEntryWrapper;
|
||||
import io.xpipe.app.comp.store.StoreSection;
|
||||
import io.xpipe.app.comp.store.StoreViewState;
|
||||
import io.xpipe.app.ext.DataStoreProvider;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ServiceGroupStoreProvider implements DataStoreProvider {
|
||||
|
||||
@Override
|
||||
public StoreEntryComp customEntryComp(StoreSection sec, boolean preferLarge) {
|
||||
var t = createToggleComp(sec);
|
||||
return StoreEntryComp.create(sec.getWrapper(), t, preferLarge);
|
||||
}
|
||||
|
||||
private StoreToggleComp createToggleComp(StoreSection sec) {
|
||||
var enabled = new SimpleBooleanProperty();
|
||||
var t = new StoreToggleComp(null, sec, enabled, aBoolean -> {
|
||||
var children = DataStorage.get().getStoreChildren(sec.getWrapper().getEntry());
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
for (DataStoreEntry child : children) {
|
||||
if (child.getStore() instanceof AbstractServiceStore serviceStore) {
|
||||
if (aBoolean) {
|
||||
serviceStore.startSessionIfNeeded();
|
||||
} else {
|
||||
serviceStore.stopSessionIfNeeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
t.setCustomVisibility(Bindings.createBooleanBinding(() -> {
|
||||
var children = DataStorage.get().getStoreChildren(sec.getWrapper().getEntry());
|
||||
for (DataStoreEntry child : children) {
|
||||
if (child.getStore() instanceof AbstractServiceStore serviceStore) {
|
||||
if (serviceStore.getHost().getStore().requiresTunnel()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}, StoreViewState.get().getAllEntries().getList()));
|
||||
return t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comp<?> stateDisplay(StoreEntryWrapper w) {
|
||||
return new SystemStateComp(new SimpleObjectProperty<>(SystemStateComp.State.SUCCESS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayIconFileName(DataStore store) {
|
||||
return "base:serviceGroup_icon.svg";
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStore defaultStore() {
|
||||
return ServiceGroupStore.builder().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStoreEntry getDisplayParent(DataStoreEntry store) {
|
||||
ServiceGroupStore s = store.getStore().asNeeded();
|
||||
return s.getParent().get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPossibleNames() {
|
||||
return List.of("serviceGroup");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<?>> getStoreClasses() {
|
||||
return List.of(ServiceGroupStore.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package io.xpipe.ext.base.service;
|
||||
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.ext.ActionProvider;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.app.util.Hyperlinks;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import lombok.Value;
|
||||
|
||||
public class ServiceOpenAction implements ActionProvider {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "openWebsite";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionProvider.DataStoreCallSite<?> getDataStoreCallSite() {
|
||||
return new ActionProvider.DataStoreCallSite<AbstractServiceStore>() {
|
||||
|
||||
@Override
|
||||
public boolean isMajor(DataStoreEntryRef<AbstractServiceStore> o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canLinkTo() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionProvider.Action createAction(DataStoreEntryRef<AbstractServiceStore> store) {
|
||||
return new Action(store.getStore());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<AbstractServiceStore> getApplicableClass() {
|
||||
return AbstractServiceStore.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableValue<String> getName(DataStoreEntryRef<AbstractServiceStore> store) {
|
||||
return AppI18n.observable("openWebsite");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIcon(DataStoreEntryRef<AbstractServiceStore> store) {
|
||||
return "mdi2w-web";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Value
|
||||
static class Action implements ActionProvider.Action {
|
||||
|
||||
AbstractServiceStore serviceStore;
|
||||
|
||||
@Override
|
||||
public boolean requiresJavaFXPlatform() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
serviceStore.startSessionIfNeeded();
|
||||
var l = serviceStore.getSession().getLocalPort();
|
||||
Hyperlinks.open("http://localhost:" + l);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,10 @@ import io.xpipe.ext.base.desktop.DesktopCommandStoreProvider;
|
|||
import io.xpipe.ext.base.desktop.DesktopEnvironmentStoreProvider;
|
||||
import io.xpipe.ext.base.script.ScriptGroupStoreProvider;
|
||||
import io.xpipe.ext.base.script.SimpleScriptStoreProvider;
|
||||
import io.xpipe.ext.base.service.FixedServiceStoreProvider;
|
||||
import io.xpipe.ext.base.service.ServiceGroupStoreProvider;
|
||||
import io.xpipe.ext.base.service.ServiceOpenAction;
|
||||
import io.xpipe.ext.base.service.CustomServiceStoreProvider;
|
||||
import io.xpipe.ext.base.store.StorePauseAction;
|
||||
import io.xpipe.ext.base.store.StoreStartAction;
|
||||
import io.xpipe.ext.base.store.StoreStopAction;
|
||||
|
@ -18,6 +22,7 @@ open module io.xpipe.ext.base {
|
|||
exports io.xpipe.ext.base.script;
|
||||
exports io.xpipe.ext.base.store;
|
||||
exports io.xpipe.ext.base.desktop;
|
||||
exports io.xpipe.ext.base.service;
|
||||
|
||||
requires java.desktop;
|
||||
requires io.xpipe.core;
|
||||
|
@ -55,7 +60,7 @@ open module io.xpipe.ext.base {
|
|||
UnzipAction,
|
||||
JavapAction,
|
||||
JarAction;
|
||||
provides ActionProvider with
|
||||
provides ActionProvider with ServiceOpenAction,
|
||||
StoreStopAction,
|
||||
StoreStartAction,
|
||||
StorePauseAction,
|
||||
|
@ -67,7 +72,7 @@ open module io.xpipe.ext.base {
|
|||
EditStoreAction,
|
||||
DeleteStoreChildrenAction,
|
||||
BrowseStoreAction;
|
||||
provides DataStoreProvider with
|
||||
provides DataStoreProvider with ServiceGroupStoreProvider, CustomServiceStoreProvider, FixedServiceStoreProvider,
|
||||
SimpleScriptStoreProvider,
|
||||
DesktopEnvironmentStoreProvider,
|
||||
DesktopApplicationStoreProvider,
|
||||
|
|
After Width: | Height: | Size: 728 B |
After Width: | Height: | Size: 729 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,127 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="Layer_1"
|
||||
data-name="Layer 1"
|
||||
viewBox="0 0 219.6481 223.2565"
|
||||
version="1.1"
|
||||
sodipodi:docname="serviceGroup_icon-dark.svg"
|
||||
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
|
||||
width="219.6481"
|
||||
height="223.2565"
|
||||
inkscape:export-filename="C:\Projects\xpipe\xpipex\ext\proc\src\main\resources\io\xpipe\ext\proc\resources\img\sshConfig_icon-16.png"
|
||||
inkscape:export-xdpi="6.8799787"
|
||||
inkscape:export-ydpi="6.8799787"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview873"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="false"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.0944328"
|
||||
inkscape:cx="47.984352"
|
||||
inkscape:cy="99.310897"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
<defs
|
||||
id="defs856">
|
||||
<style
|
||||
id="style854">.cls-1{fill:#ac55ff;}</style>
|
||||
</defs>
|
||||
<title
|
||||
id="title858">ssh draft</title>
|
||||
<path
|
||||
class="cls-1"
|
||||
d="M 178.64,18.636496 H 26 a 26,26 0 0 0 -26,26 V 197.2565 a 26,26 0 0 0 26,26 h 152.64 a 26,26 0 0 0 26,-26 V 44.616496 a 26,26 0 0 0 -26,-25.98 z m 14,178.620004 a 14,14 0 0 1 -14,14 H 26 a 14,14 0 0 1 -14,-14 V 44.616496 a 14,14 0 0 1 14,-14 h 152.64 a 14,14 0 0 1 14,14 z"
|
||||
id="path860"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<metadata
|
||||
id="metadata932">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:title>ssh draft</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<circle
|
||||
style="fill:#2bb628;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.21292;stroke-linecap:round"
|
||||
id="path851"
|
||||
cx="177.72386"
|
||||
cy="41.92424"
|
||||
r="41.92424" />
|
||||
<g
|
||||
style="fill:#2bb628;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="g951"
|
||||
transform="matrix(1.7336676,0,0,1.7336676,23.823159,64.650554)">
|
||||
<use
|
||||
xlink:href="#A"
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
id="use922"
|
||||
width="100%"
|
||||
height="100%"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<symbol
|
||||
id="A"
|
||||
overflow="visible"
|
||||
style="fill:#2bb628;fill-opacity:1">
|
||||
<g
|
||||
fill="#0078d7"
|
||||
stroke="none"
|
||||
id="g934"
|
||||
style="fill:#2bb628;fill-opacity:1">
|
||||
<path
|
||||
d="M 61.112,67.724 C 54.781,72.602 47.355,74.919 39.969,74.919 29.418,74.919 19.272,70.244 12.292,61.138 0.685,45.894 3.606,24.106 18.825,12.439 25.156,7.561 32.582,5.244 39.968,5.244 c 10.551,0 20.697,4.675 27.677,13.78 11.647,15.244 8.685,37.073 -6.534,48.699 z M 71.704,15.65 C 63.871,5.285 51.818,0 40.009,0 31.568,0 22.883,2.764 15.7,8.252 -1.832,21.789 -5.241,46.789 8.314,64.35 16.146,74.716 27.956,80 40.009,80 48.45,80 57.135,77.236 64.318,71.748 81.85,58.415 85.218,33.211 71.704,15.65 Z"
|
||||
id="path924"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<path
|
||||
d="m 21.884,47.494 c -2.313,-2.317 -4.221,-4.431 -5.722,-6.545 -3.368,6.789 -4.423,13.781 -4.87,19.472 0.406,0.407 0.406,0.854 0.649,1.26 1.704,2.114 3.815,4.228 5.722,5.732 0,-4.675 0.406,-12.317 4.221,-19.919 z M 34.182,20.61 c -3.165,2.114 -6.331,4.878 -9.74,8.455 -1.705,1.911 -3.368,3.821 -4.667,5.732 1.461,2.317 3.165,4.675 5.479,6.992 1.704,-2.317 3.815,-4.878 6.331,-7.195 3.165,-2.967 6.128,-5.488 9.091,-7.398 l -6.493,-6.585 z m 33.136,-2.046 c -1.461,-1.911 -2.962,-3.577 -4.87,-5.081 -5.276,-0.854 -14.001,-0.691 -23.497,4.39 2.111,2.317 4.423,4.675 6.534,6.545 12.662,-6.951 21.833,-5.854 21.833,-5.854 z M 16.309,40.658 19.677,34.723 C 14.199,26.024 14.402,18.829 15.01,15.455 13.752,16.918 12.25,18.422 11.195,19.886 10.18,24.805 10.18,32 16.308,40.658 Z"
|
||||
id="path926"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<path
|
||||
d="m 31.922,47.946 -6.534,-5.935 -3.368,5.488 c 1.704,1.707 3.815,3.618 6.128,5.285 14.163,11.219 28.326,12.276 36.362,12.276 0.406,0 2.962,-3.374 4.423,-5.488 -3.652,0.854 -18.871,2.764 -37.011,-11.626 z"
|
||||
id="path928"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<path
|
||||
d="m 16.261,40.703 c 1.461,2.114 3.368,4.431 5.722,6.545 0.852,-1.911 1.907,-3.618 3.368,-5.488 -2.313,-2.317 -4.018,-4.675 -5.479,-6.992 -1.461,2.114 -2.557,4.024 -3.612,5.935 z M 40.663,27.224 c 13.108,12.073 28.326,22.236 32.344,25 a 16.32,16.32 0 0 0 0.852,-2.968 C 69.638,46.085 58.641,37.63 45.33,24.256 44.031,25.11 42.327,25.679 40.663,27.224 Z M 39.166,17.781 C 36,14.407 32.632,10.626 29.223,6.805 c -1.461,0.407 -3.165,1.057 -4.667,1.911 2.313,4.024 5.925,8.171 9.496,11.992 1.948,-1.057 3.449,-2.073 5.113,-2.927 z"
|
||||
id="path930"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<path
|
||||
d="m 39.159,17.77 c -1.704,0.854 -3.652,1.667 -5.113,2.927 l 6.615,6.504 4.829,-2.764 z m 16.93,17.956 c -3.166,2.561 -3.815,7.195 -1.461,10.366 2.557,3.374 7.183,3.821 10.551,1.463 3.165,-2.561 3.815,-7.195 1.461,-10.569 -2.516,-3.171 -7.142,-3.781 -10.551,-1.26 z m -15.42,18.763 c -2.963,2.317 -3.612,6.789 -1.258,9.756 2.313,2.967 6.777,3.618 9.74,1.26 2.962,-2.317 3.612,-6.545 1.258,-9.756 -2.313,-3.171 -6.574,-3.577 -9.74,-1.26 z M 14.8,32.451 c -4.667,3.618 -5.479,10.163 -1.907,14.837 3.612,4.675 10.145,5.488 14.812,1.91 C 32.372,45.62 33.184,39.036 29.612,34.361 26,29.686 19.223,28.833 14.8,32.451 Z"
|
||||
id="path932"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
</g>
|
||||
</symbol>
|
||||
</g>
|
||||
<g
|
||||
style="fill:#000000"
|
||||
id="g1061"
|
||||
transform="matrix(0.09937926,0,0,0.09937926,153.65686,17.537078)">
|
||||
<g
|
||||
id="g1051">
|
||||
<g
|
||||
id="g1049">
|
||||
<path
|
||||
d="m 487.2,69.7 c 0,12.9 -10.5,23.4 -23.4,23.4 h -322 c -12.9,0 -23.4,-10.5 -23.4,-23.4 0,-12.9 10.5,-23.4 23.4,-23.4 h 322.1 c 12.9,0.1 23.3,10.5 23.3,23.4 z m -23.3,92.6 H 141.8 c -12.9,0 -23.4,10.5 -23.4,23.4 0,12.9 10.5,23.4 23.4,23.4 h 322.1 c 12.9,0 23.4,-10.5 23.4,-23.4 -0.1,-12.9 -10.5,-23.4 -23.4,-23.4 z m 0,116 H 141.8 c -12.9,0 -23.4,10.5 -23.4,23.4 0,12.9 10.5,23.4 23.4,23.4 h 322.1 c 12.9,0 23.4,-10.5 23.4,-23.4 -0.1,-12.9 -10.5,-23.4 -23.4,-23.4 z m 0,116 H 141.8 c -12.9,0 -23.4,10.5 -23.4,23.4 0,12.9 10.5,23.4 23.4,23.4 h 322.1 c 12.9,0 23.4,-10.5 23.4,-23.4 -0.1,-12.9 -10.5,-23.4 -23.4,-23.4 z M 38.9,30.8 C 17.4,30.8 0,48.2 0,69.7 c 0,21.5 17.4,39 38.9,39 21.5,0 38.9,-17.5 38.9,-39 0,-21.5 -17.4,-38.9 -38.9,-38.9 z m 0,116 C 17.4,146.8 0,164.2 0,185.7 c 0,21.5 17.4,38.9 38.9,38.9 21.5,0 38.9,-17.4 38.9,-38.9 0,-21.5 -17.4,-38.9 -38.9,-38.9 z m 0,116 C 17.4,262.8 0,280.2 0,301.7 c 0,21.5 17.4,38.9 38.9,38.9 21.5,0 38.9,-17.4 38.9,-38.9 0,-21.5 -17.4,-38.9 -38.9,-38.9 z m 0,115.9 C 17.4,378.7 0,396.1 0,417.6 c 0,21.5 17.4,38.9 38.9,38.9 21.5,0 38.9,-17.4 38.9,-38.9 0,-21.4 -17.4,-38.9 -38.9,-38.9 z"
|
||||
id="path1047" />
|
||||
|
||||
</g>
|
||||
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 7.3 KiB |
|
@ -0,0 +1,130 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="Layer_1"
|
||||
data-name="Layer 1"
|
||||
viewBox="0 0 219.6481 223.2565"
|
||||
version="1.1"
|
||||
sodipodi:docname="serviceGroup_icon.svg"
|
||||
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
|
||||
width="219.6481"
|
||||
height="223.2565"
|
||||
inkscape:export-filename="C:\Projects\xpipe\xpipex\ext\proc\src\main\resources\io\xpipe\ext\proc\resources\img\sshConfig_icon-16.png"
|
||||
inkscape:export-xdpi="6.8799787"
|
||||
inkscape:export-ydpi="6.8799787"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview873"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="false"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.0944328"
|
||||
inkscape:cx="47.984352"
|
||||
inkscape:cy="99.310897"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
<defs
|
||||
id="defs856">
|
||||
<style
|
||||
id="style854">.cls-1{fill:#ac55ff;}</style>
|
||||
</defs>
|
||||
<title
|
||||
id="title858">ssh draft</title>
|
||||
<path
|
||||
class="cls-1"
|
||||
d="M 178.64,18.636496 H 26 a 26,26 0 0 0 -26,26 V 197.2565 a 26,26 0 0 0 26,26 h 152.64 a 26,26 0 0 0 26,-26 V 44.616496 a 26,26 0 0 0 -26,-25.98 z m 14,178.620004 a 14,14 0 0 1 -14,14 H 26 a 14,14 0 0 1 -14,-14 V 44.616496 a 14,14 0 0 1 14,-14 h 152.64 a 14,14 0 0 1 14,14 z"
|
||||
id="path860"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<metadata
|
||||
id="metadata932">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:title>ssh draft</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<circle
|
||||
style="fill:#2bb628;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.21292;stroke-linecap:round"
|
||||
id="path851"
|
||||
cx="177.72386"
|
||||
cy="41.92424"
|
||||
r="41.92424" />
|
||||
<g
|
||||
style="fill:#2bb628;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="g951"
|
||||
transform="matrix(1.7336676,0,0,1.7336676,23.823159,64.650554)">
|
||||
<use
|
||||
xlink:href="#A"
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
id="use922"
|
||||
width="100%"
|
||||
height="100%"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<symbol
|
||||
id="A"
|
||||
overflow="visible"
|
||||
style="fill:#2bb628;fill-opacity:1">
|
||||
<g
|
||||
fill="#0078d7"
|
||||
stroke="none"
|
||||
id="g934"
|
||||
style="fill:#2bb628;fill-opacity:1">
|
||||
<path
|
||||
d="M 61.112,67.724 C 54.781,72.602 47.355,74.919 39.969,74.919 29.418,74.919 19.272,70.244 12.292,61.138 0.685,45.894 3.606,24.106 18.825,12.439 25.156,7.561 32.582,5.244 39.968,5.244 c 10.551,0 20.697,4.675 27.677,13.78 11.647,15.244 8.685,37.073 -6.534,48.699 z M 71.704,15.65 C 63.871,5.285 51.818,0 40.009,0 31.568,0 22.883,2.764 15.7,8.252 -1.832,21.789 -5.241,46.789 8.314,64.35 16.146,74.716 27.956,80 40.009,80 48.45,80 57.135,77.236 64.318,71.748 81.85,58.415 85.218,33.211 71.704,15.65 Z"
|
||||
id="path924"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<path
|
||||
d="m 21.884,47.494 c -2.313,-2.317 -4.221,-4.431 -5.722,-6.545 -3.368,6.789 -4.423,13.781 -4.87,19.472 0.406,0.407 0.406,0.854 0.649,1.26 1.704,2.114 3.815,4.228 5.722,5.732 0,-4.675 0.406,-12.317 4.221,-19.919 z M 34.182,20.61 c -3.165,2.114 -6.331,4.878 -9.74,8.455 -1.705,1.911 -3.368,3.821 -4.667,5.732 1.461,2.317 3.165,4.675 5.479,6.992 1.704,-2.317 3.815,-4.878 6.331,-7.195 3.165,-2.967 6.128,-5.488 9.091,-7.398 l -6.493,-6.585 z m 33.136,-2.046 c -1.461,-1.911 -2.962,-3.577 -4.87,-5.081 -5.276,-0.854 -14.001,-0.691 -23.497,4.39 2.111,2.317 4.423,4.675 6.534,6.545 12.662,-6.951 21.833,-5.854 21.833,-5.854 z M 16.309,40.658 19.677,34.723 C 14.199,26.024 14.402,18.829 15.01,15.455 13.752,16.918 12.25,18.422 11.195,19.886 10.18,24.805 10.18,32 16.308,40.658 Z"
|
||||
id="path926"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<path
|
||||
d="m 31.922,47.946 -6.534,-5.935 -3.368,5.488 c 1.704,1.707 3.815,3.618 6.128,5.285 14.163,11.219 28.326,12.276 36.362,12.276 0.406,0 2.962,-3.374 4.423,-5.488 -3.652,0.854 -18.871,2.764 -37.011,-11.626 z"
|
||||
id="path928"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<path
|
||||
d="m 16.261,40.703 c 1.461,2.114 3.368,4.431 5.722,6.545 0.852,-1.911 1.907,-3.618 3.368,-5.488 -2.313,-2.317 -4.018,-4.675 -5.479,-6.992 -1.461,2.114 -2.557,4.024 -3.612,5.935 z M 40.663,27.224 c 13.108,12.073 28.326,22.236 32.344,25 a 16.32,16.32 0 0 0 0.852,-2.968 C 69.638,46.085 58.641,37.63 45.33,24.256 44.031,25.11 42.327,25.679 40.663,27.224 Z M 39.166,17.781 C 36,14.407 32.632,10.626 29.223,6.805 c -1.461,0.407 -3.165,1.057 -4.667,1.911 2.313,4.024 5.925,8.171 9.496,11.992 1.948,-1.057 3.449,-2.073 5.113,-2.927 z"
|
||||
id="path930"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<path
|
||||
d="m 39.159,17.77 c -1.704,0.854 -3.652,1.667 -5.113,2.927 l 6.615,6.504 4.829,-2.764 z m 16.93,17.956 c -3.166,2.561 -3.815,7.195 -1.461,10.366 2.557,3.374 7.183,3.821 10.551,1.463 3.165,-2.561 3.815,-7.195 1.461,-10.569 -2.516,-3.171 -7.142,-3.781 -10.551,-1.26 z m -15.42,18.763 c -2.963,2.317 -3.612,6.789 -1.258,9.756 2.313,2.967 6.777,3.618 9.74,1.26 2.962,-2.317 3.612,-6.545 1.258,-9.756 -2.313,-3.171 -6.574,-3.577 -9.74,-1.26 z M 14.8,32.451 c -4.667,3.618 -5.479,10.163 -1.907,14.837 3.612,4.675 10.145,5.488 14.812,1.91 C 32.372,45.62 33.184,39.036 29.612,34.361 26,29.686 19.223,28.833 14.8,32.451 Z"
|
||||
id="path932"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
</g>
|
||||
</symbol>
|
||||
</g>
|
||||
<g
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
id="g1061"
|
||||
transform="matrix(0.09937926,0,0,0.09937926,153.65686,17.537078)">
|
||||
<g
|
||||
id="g1051"
|
||||
style="fill:#ffffff;fill-opacity:1">
|
||||
<g
|
||||
id="g1049"
|
||||
style="fill:#ffffff;fill-opacity:1">
|
||||
<path
|
||||
d="m 487.2,69.7 c 0,12.9 -10.5,23.4 -23.4,23.4 h -322 c -12.9,0 -23.4,-10.5 -23.4,-23.4 0,-12.9 10.5,-23.4 23.4,-23.4 h 322.1 c 12.9,0.1 23.3,10.5 23.3,23.4 z m -23.3,92.6 H 141.8 c -12.9,0 -23.4,10.5 -23.4,23.4 0,12.9 10.5,23.4 23.4,23.4 h 322.1 c 12.9,0 23.4,-10.5 23.4,-23.4 -0.1,-12.9 -10.5,-23.4 -23.4,-23.4 z m 0,116 H 141.8 c -12.9,0 -23.4,10.5 -23.4,23.4 0,12.9 10.5,23.4 23.4,23.4 h 322.1 c 12.9,0 23.4,-10.5 23.4,-23.4 -0.1,-12.9 -10.5,-23.4 -23.4,-23.4 z m 0,116 H 141.8 c -12.9,0 -23.4,10.5 -23.4,23.4 0,12.9 10.5,23.4 23.4,23.4 h 322.1 c 12.9,0 23.4,-10.5 23.4,-23.4 -0.1,-12.9 -10.5,-23.4 -23.4,-23.4 z M 38.9,30.8 C 17.4,30.8 0,48.2 0,69.7 c 0,21.5 17.4,39 38.9,39 21.5,0 38.9,-17.5 38.9,-39 0,-21.5 -17.4,-38.9 -38.9,-38.9 z m 0,116 C 17.4,146.8 0,164.2 0,185.7 c 0,21.5 17.4,38.9 38.9,38.9 21.5,0 38.9,-17.4 38.9,-38.9 0,-21.5 -17.4,-38.9 -38.9,-38.9 z m 0,116 C 17.4,262.8 0,280.2 0,301.7 c 0,21.5 17.4,38.9 38.9,38.9 21.5,0 38.9,-17.4 38.9,-38.9 0,-21.5 -17.4,-38.9 -38.9,-38.9 z m 0,115.9 C 17.4,378.7 0,396.1 0,417.6 c 0,21.5 17.4,38.9 38.9,38.9 21.5,0 38.9,-17.4 38.9,-38.9 0,-21.4 -17.4,-38.9 -38.9,-38.9 z"
|
||||
id="path1047"
|
||||
style="fill:#ffffff;fill-opacity:1" />
|
||||
|
||||
</g>
|
||||
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 861 B |
After Width: | Height: | Size: 861 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.4 KiB |
|
@ -0,0 +1,100 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="Layer_1"
|
||||
data-name="Layer 1"
|
||||
viewBox="0 0 149.88004 149.87825"
|
||||
version="1.1"
|
||||
sodipodi:docname="service_icon-dark.svg"
|
||||
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
|
||||
inkscape:export-filename="C:\Projects\xpipe\xpipex\ext\proc\src\main\resources\io\xpipe\ext\proc\resources\img\ssh_icon-16.png"
|
||||
inkscape:export-xdpi="7.5076985"
|
||||
inkscape:export-ydpi="7.5076985"
|
||||
width="149.88004"
|
||||
height="149.87825"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview873"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="false"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.0472164"
|
||||
inkscape:cx="26.260093"
|
||||
inkscape:cy="42.016149"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
<defs
|
||||
id="defs856">
|
||||
<style
|
||||
id="style854">.cls-1{fill:#ac55ff;}</style>
|
||||
</defs>
|
||||
<title
|
||||
id="title858">ssh draft</title>
|
||||
<metadata
|
||||
id="metadata932">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:title>ssh draft</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
style="fill:#2bb628;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="g951"
|
||||
transform="matrix(1.8734782,0,0,1.8734782,-0.93633228,-0.9367391)">
|
||||
<use
|
||||
xlink:href="#A"
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
id="use922"
|
||||
width="100%"
|
||||
height="100%"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<symbol
|
||||
id="A"
|
||||
overflow="visible"
|
||||
style="fill:#2bb628;fill-opacity:1">
|
||||
<g
|
||||
fill="#0078d7"
|
||||
stroke="none"
|
||||
id="g934"
|
||||
style="fill:#2bb628;fill-opacity:1">
|
||||
<path
|
||||
d="M 61.112,67.724 C 54.781,72.602 47.355,74.919 39.969,74.919 29.418,74.919 19.272,70.244 12.292,61.138 0.685,45.894 3.606,24.106 18.825,12.439 25.156,7.561 32.582,5.244 39.968,5.244 c 10.551,0 20.697,4.675 27.677,13.78 11.647,15.244 8.685,37.073 -6.534,48.699 z M 71.704,15.65 C 63.871,5.285 51.818,0 40.009,0 31.568,0 22.883,2.764 15.7,8.252 -1.832,21.789 -5.241,46.789 8.314,64.35 16.146,74.716 27.956,80 40.009,80 48.45,80 57.135,77.236 64.318,71.748 81.85,58.415 85.218,33.211 71.704,15.65 Z"
|
||||
id="path924"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<path
|
||||
d="m 21.884,47.494 c -2.313,-2.317 -4.221,-4.431 -5.722,-6.545 -3.368,6.789 -4.423,13.781 -4.87,19.472 0.406,0.407 0.406,0.854 0.649,1.26 1.704,2.114 3.815,4.228 5.722,5.732 0,-4.675 0.406,-12.317 4.221,-19.919 z M 34.182,20.61 c -3.165,2.114 -6.331,4.878 -9.74,8.455 -1.705,1.911 -3.368,3.821 -4.667,5.732 1.461,2.317 3.165,4.675 5.479,6.992 1.704,-2.317 3.815,-4.878 6.331,-7.195 3.165,-2.967 6.128,-5.488 9.091,-7.398 l -6.493,-6.585 z m 33.136,-2.046 c -1.461,-1.911 -2.962,-3.577 -4.87,-5.081 -5.276,-0.854 -14.001,-0.691 -23.497,4.39 2.111,2.317 4.423,4.675 6.534,6.545 12.662,-6.951 21.833,-5.854 21.833,-5.854 z M 16.309,40.658 19.677,34.723 C 14.199,26.024 14.402,18.829 15.01,15.455 13.752,16.918 12.25,18.422 11.195,19.886 10.18,24.805 10.18,32 16.308,40.658 Z"
|
||||
id="path926"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<path
|
||||
d="m 31.922,47.946 -6.534,-5.935 -3.368,5.488 c 1.704,1.707 3.815,3.618 6.128,5.285 14.163,11.219 28.326,12.276 36.362,12.276 0.406,0 2.962,-3.374 4.423,-5.488 -3.652,0.854 -18.871,2.764 -37.011,-11.626 z"
|
||||
id="path928"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<path
|
||||
d="m 16.261,40.703 c 1.461,2.114 3.368,4.431 5.722,6.545 0.852,-1.911 1.907,-3.618 3.368,-5.488 -2.313,-2.317 -4.018,-4.675 -5.479,-6.992 -1.461,2.114 -2.557,4.024 -3.612,5.935 z M 40.663,27.224 c 13.108,12.073 28.326,22.236 32.344,25 a 16.32,16.32 0 0 0 0.852,-2.968 C 69.638,46.085 58.641,37.63 45.33,24.256 44.031,25.11 42.327,25.679 40.663,27.224 Z M 39.166,17.781 C 36,14.407 32.632,10.626 29.223,6.805 c -1.461,0.407 -3.165,1.057 -4.667,1.911 2.313,4.024 5.925,8.171 9.496,11.992 1.948,-1.057 3.449,-2.073 5.113,-2.927 z"
|
||||
id="path930"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<path
|
||||
d="m 39.159,17.77 c -1.704,0.854 -3.652,1.667 -5.113,2.927 l 6.615,6.504 4.829,-2.764 z m 16.93,17.956 c -3.166,2.561 -3.815,7.195 -1.461,10.366 2.557,3.374 7.183,3.821 10.551,1.463 3.165,-2.561 3.815,-7.195 1.461,-10.569 -2.516,-3.171 -7.142,-3.781 -10.551,-1.26 z m -15.42,18.763 c -2.963,2.317 -3.612,6.789 -1.258,9.756 2.313,2.967 6.777,3.618 9.74,1.26 2.962,-2.317 3.612,-6.545 1.258,-9.756 -2.313,-3.171 -6.574,-3.577 -9.74,-1.26 z M 14.8,32.451 c -4.667,3.618 -5.479,10.163 -1.907,14.837 3.612,4.675 10.145,5.488 14.812,1.91 C 32.372,45.62 33.184,39.036 29.612,34.361 26,29.686 19.223,28.833 14.8,32.451 Z"
|
||||
id="path932"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
</g>
|
||||
</symbol>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.4 KiB |
|
@ -0,0 +1,100 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="Layer_1"
|
||||
data-name="Layer 1"
|
||||
viewBox="0 0 149.88004 149.87825"
|
||||
version="1.1"
|
||||
sodipodi:docname="service_icon.svg"
|
||||
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
|
||||
inkscape:export-filename="C:\Projects\xpipe\xpipex\ext\proc\src\main\resources\io\xpipe\ext\proc\resources\img\ssh_icon-16.png"
|
||||
inkscape:export-xdpi="7.5076985"
|
||||
inkscape:export-ydpi="7.5076985"
|
||||
width="149.88004"
|
||||
height="149.87825"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview873"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="false"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.0472164"
|
||||
inkscape:cx="26.260093"
|
||||
inkscape:cy="42.016149"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
<defs
|
||||
id="defs856">
|
||||
<style
|
||||
id="style854">.cls-1{fill:#ac55ff;}</style>
|
||||
</defs>
|
||||
<title
|
||||
id="title858">ssh draft</title>
|
||||
<metadata
|
||||
id="metadata932">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:title>ssh draft</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
style="fill:#2bb628;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="g951"
|
||||
transform="matrix(1.8734782,0,0,1.8734782,-0.93633228,-0.9367391)">
|
||||
<use
|
||||
xlink:href="#A"
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
id="use922"
|
||||
width="100%"
|
||||
height="100%"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<symbol
|
||||
id="A"
|
||||
overflow="visible"
|
||||
style="fill:#2bb628;fill-opacity:1">
|
||||
<g
|
||||
fill="#0078d7"
|
||||
stroke="none"
|
||||
id="g934"
|
||||
style="fill:#2bb628;fill-opacity:1">
|
||||
<path
|
||||
d="M 61.112,67.724 C 54.781,72.602 47.355,74.919 39.969,74.919 29.418,74.919 19.272,70.244 12.292,61.138 0.685,45.894 3.606,24.106 18.825,12.439 25.156,7.561 32.582,5.244 39.968,5.244 c 10.551,0 20.697,4.675 27.677,13.78 11.647,15.244 8.685,37.073 -6.534,48.699 z M 71.704,15.65 C 63.871,5.285 51.818,0 40.009,0 31.568,0 22.883,2.764 15.7,8.252 -1.832,21.789 -5.241,46.789 8.314,64.35 16.146,74.716 27.956,80 40.009,80 48.45,80 57.135,77.236 64.318,71.748 81.85,58.415 85.218,33.211 71.704,15.65 Z"
|
||||
id="path924"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<path
|
||||
d="m 21.884,47.494 c -2.313,-2.317 -4.221,-4.431 -5.722,-6.545 -3.368,6.789 -4.423,13.781 -4.87,19.472 0.406,0.407 0.406,0.854 0.649,1.26 1.704,2.114 3.815,4.228 5.722,5.732 0,-4.675 0.406,-12.317 4.221,-19.919 z M 34.182,20.61 c -3.165,2.114 -6.331,4.878 -9.74,8.455 -1.705,1.911 -3.368,3.821 -4.667,5.732 1.461,2.317 3.165,4.675 5.479,6.992 1.704,-2.317 3.815,-4.878 6.331,-7.195 3.165,-2.967 6.128,-5.488 9.091,-7.398 l -6.493,-6.585 z m 33.136,-2.046 c -1.461,-1.911 -2.962,-3.577 -4.87,-5.081 -5.276,-0.854 -14.001,-0.691 -23.497,4.39 2.111,2.317 4.423,4.675 6.534,6.545 12.662,-6.951 21.833,-5.854 21.833,-5.854 z M 16.309,40.658 19.677,34.723 C 14.199,26.024 14.402,18.829 15.01,15.455 13.752,16.918 12.25,18.422 11.195,19.886 10.18,24.805 10.18,32 16.308,40.658 Z"
|
||||
id="path926"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<path
|
||||
d="m 31.922,47.946 -6.534,-5.935 -3.368,5.488 c 1.704,1.707 3.815,3.618 6.128,5.285 14.163,11.219 28.326,12.276 36.362,12.276 0.406,0 2.962,-3.374 4.423,-5.488 -3.652,0.854 -18.871,2.764 -37.011,-11.626 z"
|
||||
id="path928"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<path
|
||||
d="m 16.261,40.703 c 1.461,2.114 3.368,4.431 5.722,6.545 0.852,-1.911 1.907,-3.618 3.368,-5.488 -2.313,-2.317 -4.018,-4.675 -5.479,-6.992 -1.461,2.114 -2.557,4.024 -3.612,5.935 z M 40.663,27.224 c 13.108,12.073 28.326,22.236 32.344,25 a 16.32,16.32 0 0 0 0.852,-2.968 C 69.638,46.085 58.641,37.63 45.33,24.256 44.031,25.11 42.327,25.679 40.663,27.224 Z M 39.166,17.781 C 36,14.407 32.632,10.626 29.223,6.805 c -1.461,0.407 -3.165,1.057 -4.667,1.911 2.313,4.024 5.925,8.171 9.496,11.992 1.948,-1.057 3.449,-2.073 5.113,-2.927 z"
|
||||
id="path930"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
<path
|
||||
d="m 39.159,17.77 c -1.704,0.854 -3.652,1.667 -5.113,2.927 l 6.615,6.504 4.829,-2.764 z m 16.93,17.956 c -3.166,2.561 -3.815,7.195 -1.461,10.366 2.557,3.374 7.183,3.821 10.551,1.463 3.165,-2.561 3.815,-7.195 1.461,-10.569 -2.516,-3.171 -7.142,-3.781 -10.551,-1.26 z m -15.42,18.763 c -2.963,2.317 -3.612,6.789 -1.258,9.756 2.313,2.967 6.777,3.618 9.74,1.26 2.962,-2.317 3.612,-6.545 1.258,-9.756 -2.313,-3.171 -6.574,-3.577 -9.74,-1.26 z M 14.8,32.451 c -4.667,3.618 -5.479,10.163 -1.907,14.837 3.612,4.675 10.145,5.488 14.812,1.91 C 32.372,45.62 33.184,39.036 29.612,34.361 26,29.686 19.223,28.833 14.8,32.451 Z"
|
||||
id="path932"
|
||||
style="fill:#2bb628;fill-opacity:1" />
|
||||
</g>
|
||||
</symbol>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.4 KiB |
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-rc-1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -25,6 +25,7 @@ moveTo=Move to ...
|
|||
addDatabase=Database ...
|
||||
browseInternalStorage=Browse internal storage
|
||||
addTunnel=Tunnel ...
|
||||
addService=Service ...
|
||||
addScript=Script ...
|
||||
addHost=Remote Host ...
|
||||
addShell=Shell Environment ...
|
||||
|
|
|
@ -132,5 +132,16 @@ desktopCommand.displayName=Desktop command
|
|||
desktopCommand.displayDescription=Run a command in a remote desktop environment
|
||||
desktopCommandScript=Commands
|
||||
desktopCommandScriptDescription=The commands to run in the environment
|
||||
service.displayName=Service
|
||||
service.displayDescription=Forward a remote service to your local machine
|
||||
serviceLocalPort=Explicit local port
|
||||
serviceLocalPortDescription=The local port to forward to, otherwise a random one is used
|
||||
serviceRemotePort=Remote port
|
||||
serviceRemotePortDescription=The port on which the service is running on
|
||||
serviceHost=Service host
|
||||
serviceHostDescription=The host the service is running on
|
||||
openWebsite=Open website
|
||||
serviceGroup.displayName=Service group
|
||||
serviceGroup.displayDescription=Group multiple services into one category
|
||||
|
||||
|
||||
|
|