Merge branch 'services' into release-10.0

This commit is contained in:
crschnick 2024-06-02 14:07:48 +00:00
parent bc9b962be9
commit f707a0b1f5
56 changed files with 1347 additions and 93 deletions

View file

@ -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);
}

View file

@ -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));
}

View file

@ -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);
}

View file

@ -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,

View file

@ -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;

View file

@ -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,

View file

@ -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()));
}
}
}

View file

@ -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();
}

View file

@ -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") {

View file

@ -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()));
}
}
}
}
}

View file

@ -56,6 +56,10 @@ public class AskpassAlert {
@Override
public void handle(long now) {
if (!stage.isShowing()) {
return;
}
if (regainedFocusCount >= 2) {
return;
}

View file

@ -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();
}
}

View file

@ -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();

View file

@ -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

View file

@ -0,0 +1,6 @@
package io.xpipe.core.process;
public interface CommandFeedbackPredicate {
boolean test(CommandBuilder command) throws Exception;
}

View file

@ -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;
}
}

View file

@ -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();
}

View 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;
}
}

View 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;
}
}

View file

@ -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();
}
}

View 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();
}
}
}

View file

@ -0,0 +1,6 @@
package io.xpipe.core.store;
public interface SessionListener {
void onStateChange(boolean running);
}

View file

@ -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);
}
}
}

View file

@ -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

View file

@ -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) {

View file

@ -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;
}
}

View file

@ -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";
}
}

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}
}

View file

@ -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,

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 729 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -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

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 861 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 861 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 ...

View file

@ -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

View file

@ -1 +1 @@
9.4-3
9.4