Desktop rework

This commit is contained in:
crschnick 2024-04-16 08:58:48 +00:00
parent d9e23b9ebf
commit dd3ccf37d3
27 changed files with 1215 additions and 42 deletions

View file

@ -6,7 +6,6 @@ import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.core.process.ShellStoreState;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.layout.Region;
@ -25,16 +24,14 @@ public class SystemStateComp extends SimpleComp {
@Override
protected Region createSimple() {
var icon = PlatformThread.sync(Bindings.createStringBinding(
() -> {
return state.getValue() == State.FAILURE
? "mdi2l-lightning-bolt"
: state.getValue() == State.SUCCESS ? "mdal-check" : "mdsmz-remove";
},
state));
var fi = new FontIcon();
fi.getStyleClass().add("inner-icon");
icon.subscribe(val -> fi.setIconLiteral(val));
state.subscribe(s -> {
var i = s == State.FAILURE
? "mdi2l-lightning-bolt"
: s == State.SUCCESS ? "mdal-check" : "mdsmz-remove";
PlatformThread.runLaterIfNeeded(() -> fi.setIconLiteral(i));
});
var border = new FontIcon("mdi2c-circle-outline");
border.getStyleClass().add("outer-icon");

View file

@ -27,7 +27,7 @@ public class StoreCreationMenu {
menu.getItems().add(category("addHost", "mdi2h-home-plus", DataStoreProvider.CreationCategory.HOST, "ssh"));
menu.getItems()
.add(category("addVisual", "mdi2c-camera-plus", DataStoreProvider.CreationCategory.VISUAL, null));
.add(category("addDesktop", "mdi2c-camera-plus", DataStoreProvider.CreationCategory.DESKSTOP, null));
menu.getItems()
.add(category("addShell", "mdi2t-text-box-multiple", DataStoreProvider.CreationCategory.SHELL, null));

View file

@ -24,7 +24,6 @@ import javafx.beans.value.ObservableValue;
import java.util.List;
public interface DataStoreProvider {
default boolean editByDefault() {
return false;
}
@ -208,6 +207,6 @@ public interface DataStoreProvider {
TUNNEL,
SCRIPT,
CLUSTER,
VISUAL;
DESKSTOP;
}
}

View file

@ -0,0 +1,71 @@
package io.xpipe.app.ext;
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.fxcomps.Comp;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.core.store.SingletonToggleSessionStore;
import io.xpipe.core.store.ToggleSessionState;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleBooleanProperty;
public interface SingletonToggleSessionStoreProvider extends DataStoreProvider {
@Override
public default StoreEntryComp customEntryComp(StoreSection sec, boolean preferLarge) {
SingletonToggleSessionStore<?> s = sec.getWrapper().getEntry().getStore().asNeeded();
var enabled = new SimpleBooleanProperty();
sec.getWrapper().getPersistentState().subscribe((newValue) -> {
var rdps = (ToggleSessionState) newValue;
enabled.set(rdps.getEnabled() != null ? rdps.getEnabled() : false);
});
var t = new StoreToggleComp(null, sec, enabled, aBoolean -> {
var state = s.getState();
if (state.getEnabled() != aBoolean) {
state.setEnabled(aBoolean);
s.setState(state);
sec.getWrapper().getEntry().validate();
}
});
return StoreEntryComp.create(sec.getWrapper(), t, preferLarge);
}
public default Comp<?> stateDisplay(StoreEntryWrapper w) {
SingletonToggleSessionStore<?> st = w.getEntry().getStore().asNeeded();
return new SystemStateComp(
Bindings.createObjectBinding(
() -> {
ToggleSessionState s = (ToggleSessionState) w.getPersistentState().getValue();
if (s.getEnabled() == null || !s.getEnabled()) {
return SystemStateComp.State.OTHER;
}
return s.getRunning() != null && s.getRunning()
? SystemStateComp.State.SUCCESS
: SystemStateComp.State.FAILURE;
},
w.getPersistentState(),
w.getCache()));
}
@Override
public default void storageInit() {
for (DataStoreEntry e : DataStorage.get().getStoreEntries()) {
if (getStoreClasses().stream()
.anyMatch(aClass ->
e.getStore() != null && e.getStore().getClass().equals(aClass))) {
SingletonToggleSessionStore<?> tunnelStore = e.getStore().asNeeded();
var state = tunnelStore.getState();
state.setEnabled(false);
tunnelStore.setState(state);
}
}
}
}

View file

@ -51,6 +51,7 @@ public class ChoiceComp<T> extends Comp<CompStructure<ComboBox<T>>> {
@Override
public CompStructure<ComboBox<T>> createBase() {
var cb = new ComboBox<T>();
cb.setMaxWidth(2000);
cb.setConverter(new StringConverter<>() {
@Override
public String toString(T object) {

View file

@ -34,7 +34,7 @@ public abstract class ExternalApplicationType implements PrefsChoiceValue {
return getId();
}
public static class MacApplication extends ExternalApplicationType {
public static abstract class MacApplication extends ExternalApplicationType {
protected final String applicationName;

View file

@ -431,6 +431,10 @@ public class DataStoreEntry extends StorageElement {
}
public void validateOrThrow() throws Throwable {
if (store == null) {
return;
}
try {
store.checkComplete();
setInRefresh(true);

View file

@ -7,6 +7,7 @@ import io.xpipe.app.util.CommandSupport;
import io.xpipe.app.util.LocalShell;
import io.xpipe.core.process.*;
import io.xpipe.core.store.FilePath;
import io.xpipe.core.util.FailableFunction;
import lombok.Getter;
import lombok.Value;
import lombok.With;
@ -18,7 +19,6 @@ import java.util.ArrayList;
import java.util.Base64;
import java.util.Comparator;
import java.util.List;
import java.util.function.Supplier;
public interface ExternalTerminalType extends PrefsChoiceValue {
@ -152,6 +152,18 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
.envrironment("GNOME_TERMINAL_SCREEN", sc -> "");
}
}
@Override
public FailableFunction<LaunchConfiguration, String, Exception> remoteLaunchCommand() {
return launchConfiguration -> {
var toExecute = CommandBuilder.of()
.add(executable, "-v", "--title")
.addQuoted(launchConfiguration.getColoredTitle())
.add("--")
.addFile(launchConfiguration.getScriptFile());
return toExecute.buildSimple();
};
}
};
ExternalTerminalType KONSOLE = new SimplePathType("app.konsole", "konsole", true) {
@ -551,6 +563,17 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
.addQuoted("Warp.app")
.addFile(configuration.getScriptFile()));
}
@Override
public FailableFunction<LaunchConfiguration, String, Exception> remoteLaunchCommand() {
return launchConfiguration -> {
var toExecute = CommandBuilder.of()
.add("open", "-a")
.addQuoted("Warp.app")
.addFile(launchConfiguration.getScriptFile());
return toExecute.buildSimple();
};
}
};
ExternalTerminalType CUSTOM = new CustomTerminalType();
List<ExternalTerminalType> WINDOWS_TERMINALS = List.of(
@ -588,24 +611,31 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
WezTerminalType.WEZTERM_MAC_OS,
MACOS_TERMINAL);
@SuppressWarnings("TrivialFunctionalExpressionUsage")
List<ExternalTerminalType> ALL = ((Supplier<List<ExternalTerminalType>>) () -> {
var all = new ArrayList<ExternalTerminalType>();
if (OsType.getLocal().equals(OsType.WINDOWS)) {
all.addAll(WINDOWS_TERMINALS);
}
if (OsType.getLocal().equals(OsType.LINUX)) {
all.addAll(LINUX_TERMINALS);
}
if (OsType.getLocal().equals(OsType.MACOS)) {
all.addAll(MACOS_TERMINALS);
}
// Prefer with tabs
all.sort(Comparator.comparingInt(o -> (o.supportsTabs() ? -1 : 0)));
all.add(CUSTOM);
return all;
})
.get();
List<ExternalTerminalType> APPLICABLE = getTypes(OsType.getLocal(), false, true);
List<ExternalTerminalType> ALL = getTypes(null, false, true);
static List<ExternalTerminalType> getTypes(OsType osType, boolean remote, boolean custom) {
var all = new ArrayList<ExternalTerminalType>();
if (osType == null || osType.equals(OsType.WINDOWS)) {
all.addAll(WINDOWS_TERMINALS);
}
if (osType == null || osType.equals(OsType.LINUX)) {
all.addAll(LINUX_TERMINALS);
}
if (osType == null || osType.equals(OsType.MACOS)) {
all.addAll(MACOS_TERMINALS);
}
if (remote) {
all.removeIf(externalTerminalType -> externalTerminalType.remoteLaunchCommand() == null);
}
// Prefer recommended
all.sort(Comparator.comparingInt(o -> (o.isRecommended() ? -1 : 0)));
if (custom) {
all.add(CUSTOM);
}
return all;
}
static ExternalTerminalType determineDefault(ExternalTerminalType existing) {
// Check for incompatibility with fallback shell
@ -618,7 +648,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
return existing;
}
return ALL.stream()
return APPLICABLE.stream()
.filter(externalTerminalType -> !externalTerminalType.equals(CUSTOM))
.filter(terminalType -> terminalType.isAvailable())
.findFirst()
@ -641,6 +671,10 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
default void launch(LaunchConfiguration configuration) throws Exception {}
default FailableFunction<LaunchConfiguration, String, Exception> remoteLaunchCommand() {
return null;
}
abstract class WindowsType extends ExternalApplicationType.WindowsType implements ExternalTerminalType {
public WindowsType(String id, String executable) {
@ -715,6 +749,18 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
launch(configuration.getColoredTitle(), args);
}
@Override
public FailableFunction<LaunchConfiguration, String, Exception> remoteLaunchCommand() {
return launchConfiguration -> {
var args = toCommand(launchConfiguration);
args.add(0, executable);
if (explicityAsync) {
args = launchConfiguration.getScriptDialect().launchAsnyc(args);
}
return args.buildSimple();
};
}
protected abstract CommandBuilder toCommand(LaunchConfiguration configuration) throws Exception;
}
}

View file

@ -1,4 +1,4 @@
package io.xpipe.app.storage;
package io.xpipe.app.util;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
@ -6,8 +6,8 @@ import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.xpipe.app.util.PasswordLockSecretValue;
import io.xpipe.app.util.VaultKeySecretValue;
import io.xpipe.app.storage.*;
import io.xpipe.app.terminal.ExternalTerminalType;
import io.xpipe.core.store.LocalStore;
import io.xpipe.core.util.EncryptedSecretValue;
import io.xpipe.core.util.JacksonMapper;
@ -16,7 +16,7 @@ import io.xpipe.core.util.SecretValue;
import java.io.IOException;
import java.util.UUID;
public class StorageJacksonModule extends SimpleModule {
public class AppJacksonModule extends SimpleModule {
@Override
public void setupModule(SetupContext context) {
@ -29,6 +29,8 @@ public class StorageJacksonModule extends SimpleModule {
addDeserializer(ContextualFileReference.class, new LocalFileReferenceDeserializer());
addSerializer(DataStoreSecret.class, new DataStoreSecretSerializer());
addDeserializer(DataStoreSecret.class, new DataStoreSecretDeserializer());
addSerializer(ExternalTerminalType.class, new ExternalTerminalTypeSerializer());
addDeserializer(ExternalTerminalType.class, new ExternalTerminalTypeDeserializer());
context.addSerializers(_serializers);
context.addDeserializers(_deserializers);
@ -51,6 +53,24 @@ public class StorageJacksonModule extends SimpleModule {
}
}
public static class ExternalTerminalTypeSerializer extends JsonSerializer<ExternalTerminalType> {
@Override
public void serialize(ExternalTerminalType value, JsonGenerator jgen, SerializerProvider provider)
throws IOException {
jgen.writeString(value.getId());
}
}
public static class ExternalTerminalTypeDeserializer extends JsonDeserializer<ExternalTerminalType> {
@Override
public ExternalTerminalType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
var id = p.getValueAsString();
return ExternalTerminalType.ALL.stream().filter(terminalType -> terminalType.getId().equals(id)).findFirst().orElse(null);
}
}
public static class DataStoreSecretSerializer extends JsonSerializer<DataStoreSecret> {
@Override

View file

@ -7,7 +7,7 @@ import io.xpipe.app.ext.*;
import io.xpipe.app.issue.EventHandler;
import io.xpipe.app.issue.EventHandlerImpl;
import io.xpipe.app.storage.DataStateProviderImpl;
import io.xpipe.app.storage.StorageJacksonModule;
import io.xpipe.app.util.AppJacksonModule;
import io.xpipe.app.util.LicenseProvider;
import io.xpipe.app.util.ProxyManagerProviderImpl;
import io.xpipe.app.util.TerminalLauncher;
@ -114,8 +114,7 @@ open module io.xpipe.app {
uses LicenseProvider;
uses io.xpipe.app.util.LicensedFeature;
provides Module with
StorageJacksonModule;
provides Module with AppJacksonModule;
provides ModuleLayerLoader with
MessageExchangeImpls.Loader,
DataStoreProviders.Loader,

View file

@ -0,0 +1,51 @@
package io.xpipe.core.store;
public interface SingletonSessionStore<T extends SingletonSessionStore.Session> extends InternalCacheDataStore {
static abstract class Session {
public abstract boolean isRunning();
public abstract void start() throws Exception;
public abstract void stop() throws Exception;
}
void onSessionUpdate(boolean active);
T newSession() throws Exception;
Class<?> getSessionClass();
@SuppressWarnings("unchecked")
default T getSession() {
return (T) getCache("session", getSessionClass(), null);
}
default void startIfNeeded() throws Exception {
var s = getSession();
if (s != null) {
if (s.isRunning()) {
return;
}
s.start();
return;
}
s = newSession();
s.start();
setCache("session", s);
onSessionUpdate(true);
}
default void stopIfNeeded() throws Exception {
var ex = getSession();
setCache("session", null);
if (ex != null) {
ex.stop();
onSessionUpdate(false);
}
}
}

View file

@ -0,0 +1,33 @@
package io.xpipe.core.store;
public interface SingletonToggleSessionStore<T extends SingletonSessionStore.Session> extends SingletonSessionStore<T>, StatefulDataStore<ToggleSessionState>, ValidatableStore {
@Override
default Class<ToggleSessionState> getStateClass() {
return ToggleSessionState.class;
}
@Override
public default void onSessionUpdate(boolean active) {
var c = getState();
c.setRunning(active);
if (!active) {
c.setEnabled(false);
} else {
c.setEnabled(true);
}
setState(c);
}
@Override
public default void validate() throws Exception {
if (getState().getEnabled() != null) {
if (getState().getEnabled()) {
startIfNeeded();
} else {
stopIfNeeded();
}
}
}
}

View file

@ -0,0 +1,23 @@
package io.xpipe.core.store;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import lombok.extern.jackson.Jacksonized;
@Getter
@Setter
@SuperBuilder
@Jacksonized
public class ToggleSessionState extends DataStoreState {
Boolean enabled;
Boolean running;
@Override
public void merge(DataStoreState newer) {
var state = (ToggleSessionState) newer;
enabled = useNewer(enabled, state.enabled);
running = useNewer(running, state.running);
}
}

View file

@ -0,0 +1,36 @@
package io.xpipe.ext.base.desktop;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.app.storage.ContextualFileReference;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.util.Validators;
import io.xpipe.core.process.CommandBuilder;
import io.xpipe.core.store.DataStore;
import io.xpipe.core.util.JacksonizedValue;
import lombok.Getter;
import lombok.experimental.SuperBuilder;
import lombok.extern.jackson.Jacksonized;
@Getter
@SuperBuilder
@Jacksonized
@JsonTypeName("desktopApplication")
public class DesktopApplicationStore extends JacksonizedValue implements DataStore {
private final DataStoreEntryRef<DesktopBaseStore> desktop;
private final ContextualFileReference path;
private final String arguments;
@Override
public void checkComplete() throws Throwable {
Validators.nonNull(desktop);
Validators.isType(desktop, DesktopBaseStore.class);
Validators.nonNull(path);
}
public String getFullCommand() {
var builder = CommandBuilder.of().addFile(path.toAbsoluteFilePath(null)).add(arguments != null ? " " + arguments : "");
builder = desktop.getStore().getUsedDialect().launchAsnyc(builder);
return builder.buildSimple();
}
}

View file

@ -0,0 +1,124 @@
package io.xpipe.ext.base.desktop;
import io.xpipe.app.browser.session.BrowserSessionModel;
import io.xpipe.app.comp.store.StoreEntryWrapper;
import io.xpipe.app.comp.store.StoreViewState;
import io.xpipe.app.ext.ActionProvider;
import io.xpipe.app.ext.DataStoreProvider;
import io.xpipe.app.ext.GuiDialog;
import io.xpipe.app.fxcomps.impl.DataStoreChoiceComp;
import io.xpipe.app.storage.ContextualFileReference;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.util.DataStoreFormatter;
import io.xpipe.app.util.OptionsBuilder;
import io.xpipe.core.store.DataStore;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import java.util.List;
public class DesktopApplicationStoreProvider implements DataStoreProvider {
@Override
public ActionProvider.Action browserAction(BrowserSessionModel sessionModel, DataStoreEntry store, BooleanProperty busy) {
DesktopApplicationStore s = store.getStore().asNeeded();
return new ActionProvider.Action() {
@Override
public boolean requiresJavaFXPlatform() {
return false;
}
@Override
public void execute() throws Exception {
s.getDesktop().getStore().runScript(store.getName(), s.getDesktop().getStore().getUsedDialect(), s.getFullCommand());
}
};
}
@Override
public ActionProvider.Action launchAction(DataStoreEntry store) {
DesktopApplicationStore s = store.getStore().asNeeded();
return new ActionProvider.Action() {
@Override
public boolean requiresJavaFXPlatform() {
return false;
}
@Override
public void execute() throws Exception {
s.getDesktop().getStore().runScript(store.getName(), s.getDesktop().getStore().getUsedDialect(), s.getFullCommand());
}
};
}
@Override
public CreationCategory getCreationCategory() {
return CreationCategory.DESKSTOP;
}
@Override
public DataStoreEntry getDisplayParent(DataStoreEntry store) {
DesktopApplicationStore s = store.getStore().asNeeded();
return s.getDesktop().get();
}
@Override
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
DesktopApplicationStore st = (DesktopApplicationStore) store.getValue();
var host = new SimpleObjectProperty<>(st.getDesktop());
var path = new SimpleStringProperty(st.getPath() != null ? st.getPath().toAbsoluteFilePath(null) : null);
var args = new SimpleStringProperty(st.getArguments() != null ? st.getArguments() : null);
return new OptionsBuilder()
.nameAndDescription("desktopEnvironmentBase")
.addComp(
new DataStoreChoiceComp<>(
DataStoreChoiceComp.Mode.HOST,
entry,
host,
DesktopBaseStore.class,
desktopStoreDataStoreEntryRef -> desktopStoreDataStoreEntryRef.getStore().supportsDesktopAccess(),
StoreViewState.get().getAllConnectionsCategory()),
host)
.nonNull()
.nameAndDescription("desktopApplicationPath")
.addString(path)
.nonNull()
.nameAndDescription("desktopApplicationArguments")
.addString(args)
.bind(
() -> {
return DesktopApplicationStore.builder().desktop(host.get()).path(ContextualFileReference.of(path.get())).arguments(args.get()).build();
},
store)
.buildDialog();
}
public String summaryString(StoreEntryWrapper wrapper) {
DesktopApplicationStore s = wrapper.getEntry().getStore().asNeeded();
return DataStoreFormatter.toApostropheName(s.getDesktop().get()) + " config";
}
@Override
public String getDisplayIconFileName(DataStore store) {
return "base:desktopApplication_icon.svg";
}
@Override
public DataStore defaultStore() {
return DesktopApplicationStore.builder().build();
}
@Override
public List<String> getPossibleNames() {
return List.of("desktopApplication");
}
@Override
public List<Class<?>> getStoreClasses() {
return List.of(DesktopApplicationStore.class);
}
}

View file

@ -0,0 +1,19 @@
package io.xpipe.ext.base.desktop;
import io.xpipe.app.terminal.ExternalTerminalType;
import io.xpipe.core.process.OsType;
import io.xpipe.core.process.ShellDialect;
import io.xpipe.core.store.DataStore;
public interface DesktopBaseStore extends DataStore {
boolean supportsDesktopAccess();
void runScript(String name, ShellDialect dialect, String script) throws Exception;
void runTerminal(String name, ExternalTerminalType terminalType, ShellDialect dialect, String script) throws Exception;
ShellDialect getUsedDialect();
OsType getUsedOsType();
}

View file

@ -0,0 +1,88 @@
package io.xpipe.ext.base.desktop;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.terminal.ExternalTerminalType;
import io.xpipe.app.util.Validators;
import io.xpipe.core.process.OsType;
import io.xpipe.core.process.ShellDialect;
import io.xpipe.core.store.DataStore;
import io.xpipe.core.util.JacksonizedValue;
import io.xpipe.ext.base.SelfReferentialStore;
import io.xpipe.ext.base.script.ScriptStore;
import lombok.Getter;
import lombok.experimental.SuperBuilder;
import lombok.extern.jackson.Jacksonized;
import java.util.List;
import java.util.stream.Stream;
@Getter
@SuperBuilder
@Jacksonized
@JsonTypeName("desktopEnvironment")
public class DesktopEnvironmentStore extends JacksonizedValue implements DesktopBaseStore, DataStore, SelfReferentialStore {
private final DataStoreEntryRef<DesktopBaseStore> base;
private final ExternalTerminalType terminal;
private final ShellDialect dialect;
private final List<DataStoreEntryRef<ScriptStore>> scripts;
private final String initScript;
@Override
public void checkComplete() throws Throwable {
Validators.nonNull(base);
Validators.isType(base, DesktopBaseStore.class);
Validators.nonNull(terminal);
Validators.nonNull(dialect);
}
public List<DataStoreEntryRef<ScriptStore>> getEffectiveScripts() {
return scripts != null
? scripts.stream().filter(scriptStore -> scriptStore != null).toList()
: List.of();
}
public String getMergedInitCommands(String command) {
var f = ScriptStore.flatten(scripts);
var filtered = f.stream().filter(simpleScriptStore -> simpleScriptStore.getMinimumDialect().isCompatibleTo(dialect) && simpleScriptStore.getExecutionType().runInTerminal()).toList();
var initCommands = Stream.concat(filtered.stream().map(simpleScriptStore -> simpleScriptStore.getCommands()), command != null ? Stream.of(command) : Stream.of()).toList();
var joined = String.join(dialect.getNewLine().getNewLineString(), initCommands);
return !joined.isBlank() ? joined : null;
}
public void launch(String n, String commands) throws Exception {
var fullName = n + " [" + getSelfEntry().getName() + "]";
base.getStore().runScript(fullName, dialect, getMergedInitCommands(commands));
}
public void launchSelf() throws Exception {
var fullName = getSelfEntry().getName();
base.getStore().runTerminal(fullName, terminal, dialect, getMergedInitCommands(null));
}
@Override
public boolean supportsDesktopAccess() {
return base.getStore().supportsDesktopAccess();
}
@Override
public void runScript(String name, ShellDialect dialect, String script) throws Exception {
base.getStore().runScript(name, dialect, script);
}
@Override
public void runTerminal(String name, ExternalTerminalType terminalType, ShellDialect dialect, String script) throws Exception {
base.getStore().runTerminal(name,terminalType,dialect,script);
}
@Override
public ShellDialect getUsedDialect() {
return dialect;
}
@Override
public OsType getUsedOsType() {
return base != null ? base.getStore().getUsedOsType() : null;
}
}

View file

@ -0,0 +1,159 @@
package io.xpipe.ext.base.desktop;
import io.xpipe.app.browser.session.BrowserSessionModel;
import io.xpipe.app.comp.base.IntegratedTextAreaComp;
import io.xpipe.app.comp.store.StoreEntryWrapper;
import io.xpipe.app.comp.store.StoreViewState;
import io.xpipe.app.core.AppExtensionManager;
import io.xpipe.app.ext.ActionProvider;
import io.xpipe.app.ext.DataStoreProvider;
import io.xpipe.app.ext.GuiDialog;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.impl.ChoiceComp;
import io.xpipe.app.fxcomps.impl.DataStoreChoiceComp;
import io.xpipe.app.fxcomps.impl.DataStoreListChoiceComp;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.terminal.ExternalTerminalType;
import io.xpipe.app.util.DataStoreFormatter;
import io.xpipe.app.util.OptionsBuilder;
import io.xpipe.core.store.DataStore;
import io.xpipe.ext.base.script.ScriptStore;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import lombok.SneakyThrows;
import java.util.ArrayList;
import java.util.List;
public class DesktopEnvironmentStoreProvider implements DataStoreProvider {
@Override
public ActionProvider.Action browserAction(BrowserSessionModel sessionModel, DataStoreEntry store, BooleanProperty busy) {
return launchAction(store);
}
@Override
public ActionProvider.Action launchAction(DataStoreEntry store) {
DesktopEnvironmentStore s = store.getStore().asNeeded();
return new ActionProvider.Action() {
@Override
public boolean requiresJavaFXPlatform() {
return false;
}
@Override
public void execute() throws Exception {
s.launchSelf();
}
};
}
@Override
public CreationCategory getCreationCategory() {
return CreationCategory.DESKSTOP;
}
@Override
public DataStoreEntry getDisplayParent(DataStoreEntry store) {
DesktopEnvironmentStore s = store.getStore().asNeeded();
return s.getBase().get();
}
@Override
@SneakyThrows
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
DesktopEnvironmentStore st = (DesktopEnvironmentStore) store.getValue();
var host = new SimpleObjectProperty<>(st.getBase());
var terminal = new SimpleObjectProperty<>(st.getTerminal());
var dialect = new SimpleObjectProperty<>(st.getDialect());
var scripts = new SimpleListProperty<>(FXCollections.observableArrayList(new ArrayList<>(st.getEffectiveScripts())));
var initScript = new SimpleStringProperty(st.getInitScript());
Comp<?> dialectChoice = (Comp<?>) Class.forName(
AppExtensionManager.getInstance()
.getExtendedLayer()
.findModule("io.xpipe.ext.proc")
.orElseThrow(),
"io.xpipe.ext.proc.ShellDialectChoiceComp")
.getDeclaredConstructor(Property.class)
.newInstance(dialect);
return new OptionsBuilder()
.nameAndDescription("desktopHost")
.addComp(
new DataStoreChoiceComp<>(
DataStoreChoiceComp.Mode.HOST,
entry,
host,
DesktopBaseStore.class,
desktopStoreDataStoreEntryRef -> desktopStoreDataStoreEntryRef.getStore().supportsDesktopAccess(),
StoreViewState.get().getAllConnectionsCategory()),
host)
.nonNull()
.nameAndDescription("desktopTerminal")
.addComp(ChoiceComp.ofTranslatable(terminal, ExternalTerminalType.getTypes(st.getUsedOsType(), true, false),true), terminal)
.nonNull()
.nameAndDescription("desktopShellDialect")
.addComp(dialectChoice, dialect)
.nonNull()
.nameAndDescription("desktopSnippets")
.addComp(
new DataStoreListChoiceComp<>(
scripts,
ScriptStore.class,
scriptStore -> !scripts.contains(scriptStore),
StoreViewState.get().getAllScriptsCategory()),
scripts)
.nameAndDescription("desktopInitScript")
.longDescription("proc:environmentScript")
.addComp(
new IntegratedTextAreaComp(
initScript,
false,
"commands",
Bindings.createStringBinding(
() -> {
return dialect.getValue() != null
? dialect
.getValue()
.getScriptFileEnding()
: "sh";
},
dialect)),
initScript)
.bind(
() -> {
return DesktopEnvironmentStore.builder().base(host.get()).terminal(terminal.get()).dialect(dialect.get()).scripts(scripts.get()).initScript(initScript.get()).build();
},
store)
.buildDialog();
}
public String summaryString(StoreEntryWrapper wrapper) {
DesktopEnvironmentStore s = wrapper.getEntry().getStore().asNeeded();
return DataStoreFormatter.toApostropheName(s.getBase().get()) + " environment";
}
@Override
public String getDisplayIconFileName(DataStore store) {
return "base:desktopEnvironment_icon.svg";
}
@Override
public DataStore defaultStore() {
return DesktopEnvironmentStore.builder().build();
}
@Override
public List<String> getPossibleNames() {
return List.of("desktopEnvironment");
}
@Override
public List<Class<?>> getStoreClasses() {
return List.of(DesktopEnvironmentStore.class);
}
}

View file

@ -3,8 +3,10 @@ import io.xpipe.app.ext.ActionProvider;
import io.xpipe.app.ext.DataStoreProvider;
import io.xpipe.ext.base.action.*;
import io.xpipe.ext.base.browser.*;
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.desktop.DesktopApplicationStoreProvider;
import io.xpipe.ext.base.store.StorePauseAction;
import io.xpipe.ext.base.store.StoreStartAction;
import io.xpipe.ext.base.store.StoreStopAction;
@ -14,6 +16,7 @@ open module io.xpipe.ext.base {
exports io.xpipe.ext.base.action;
exports io.xpipe.ext.base.script;
exports io.xpipe.ext.base.store;
exports io.xpipe.ext.base.desktop;
requires java.desktop;
requires io.xpipe.core;
@ -64,6 +67,6 @@ open module io.xpipe.ext.base {
DeleteStoreChildrenAction,
BrowseStoreAction;
provides DataStoreProvider with
SimpleScriptStoreProvider,
SimpleScriptStoreProvider, DesktopEnvironmentStoreProvider, DesktopApplicationStoreProvider,
ScriptGroupStoreProvider;
}

View file

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 188.2 188.2"
xml:space="preserve"
sodipodi:docname="desktopApplication_icon-dark.svg"
width="188.2"
height="188.2"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs29" /><sodipodi:namedview
id="namedview27"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="1.6738281"
inkscape:cx="94.095684"
inkscape:cy="93.796968"
inkscape:window-width="1920"
inkscape:window-height="1057"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style2">
.st0{display:none;fill:#0C0C0C;}
.st1{fill:#AC55FF;}
</style>
<rect
class="st0"
width="512"
height="512"
id="rect4"
x="-161.89999"
y="-161.89999" />
<g
id="g24"
transform="translate(-161.9,-161.9)"
style="fill:#ba6600;fill-opacity:1">
<g
id="g10"
style="fill:#ba6600;fill-opacity:1">
<path
class="st1"
d="m 214.8,242.5 c -1.6,-1.4 -4.1,-1.2 -5.5,0.4 -1.3,1.5 -1.3,3.6 0,5.1 l 23.6,23.6 -23.6,23.6 c -1.6,1.4 -1.8,3.9 -0.4,5.5 1.4,1.6 3.9,1.8 5.5,0.4 0.2,-0.1 0.3,-0.3 0.4,-0.4 l 26.4,-26.4 c 1.5,-1.5 1.5,-4 0,-5.5 z"
id="path6"
style="fill:#ba6600;fill-opacity:1" />
<path
class="st1"
d="m 211.9,302.5 c -1.2,0 -2.4,-0.5 -3.3,-1.5 -0.7,-0.9 -1.1,-2 -1,-3.1 0.1,-1.1 0.6,-2.2 1.5,-2.9 l 23.3,-23.3 -23.3,-23.3 c -1.4,-1.6 -1.4,-4 0,-5.6 1.5,-1.8 4.2,-2 6,-0.5 v 0 l 26.4,26.4 c 1.7,1.7 1.7,4.4 0,6 L 215.1,301 c -0.1,0.1 -0.3,0.3 -0.4,0.4 -0.9,0.7 -1.8,1.1 -2.8,1.1 z m 0.4,-60.6 c -1,0 -2,0.4 -2.7,1.2 -1.1,1.3 -1.1,3.3 0,4.6 l 23.8,23.8 -23.8,23.9 c -0.7,0.6 -1.2,1.5 -1.2,2.4 -0.1,0.9 0.2,1.9 0.8,2.6 1.3,1.5 3.5,1.7 5,0.4 0.1,-0.1 0.3,-0.2 0.4,-0.4 L 241,274 c 1.4,-1.4 1.4,-3.6 0,-5 l -26.3,-26.3 c -0.8,-0.5 -1.6,-0.8 -2.4,-0.8 z"
id="path8"
style="fill:#ba6600;fill-opacity:1" />
</g>
<g
id="g16"
style="fill:#ba6600;fill-opacity:1">
<path
class="st1"
d="m 306.8,295 h -54.7 c -2.2,0 -3.9,1.7 -3.9,3.9 0,2.2 1.7,3.9 3.9,3.9 h 54.7 c 2.2,0 3.9,-1.7 3.9,-3.9 0,-2.1 -1.8,-3.9 -3.9,-3.9 z"
id="path12"
style="fill:#ba6600;fill-opacity:1" />
<path
class="st1"
d="m 306.8,303.2 h -54.7 c -2.4,0 -4.3,-1.9 -4.3,-4.3 0,-2.4 1.9,-4.3 4.3,-4.3 h 54.7 c 2.4,0 4.3,1.9 4.3,4.3 0,2.4 -2,4.3 -4.3,4.3 z m -54.7,-7.8 c -1.9,0 -3.5,1.6 -3.5,3.5 0,1.9 1.6,3.5 3.5,3.5 h 54.7 c 1.9,0 3.5,-1.6 3.5,-3.5 0,-1.9 -1.6,-3.5 -3.5,-3.5 z"
id="path14"
style="fill:#ba6600;fill-opacity:1" />
</g>
<g
id="g22"
style="fill:#ba6600;fill-opacity:1">
<path
class="st1"
d="M 326.3,162.3 H 185.7 c -12.9,0 -23.4,10.5 -23.4,23.4 v 140.6 c 0,12.9 10.5,23.4 23.4,23.4 h 140.6 c 12.9,0 23.4,-10.5 23.4,-23.4 V 185.7 c 0,-12.9 -10.5,-23.4 -23.4,-23.4 z m -140.6,7.8 h 140.6 c 8.6,0 15.6,7 15.6,15.6 v 15.6 H 170.1 v -15.6 c 0,-8.6 7,-15.6 15.6,-15.6 z M 326.3,341.9 H 185.7 c -8.6,0 -15.6,-7 -15.6,-15.6 V 209.1 h 171.8 v 117.1 c 0,8.7 -7,15.7 -15.6,15.7 z"
id="path18"
style="fill:#ba6600;fill-opacity:1" />
<path
class="st1"
d="M 326.3,350.1 H 185.7 c -13.1,0 -23.8,-10.7 -23.8,-23.8 V 185.7 c 0,-13.1 10.7,-23.8 23.8,-23.8 h 140.6 c 13.1,0 23.8,10.7 23.8,23.8 v 140.6 c 0,13.1 -10.7,23.8 -23.8,23.8 z M 185.7,162.7 c -12.7,0 -23.1,10.3 -23.1,23.1 v 140.6 c 0,12.7 10.3,23.1 23.1,23.1 h 140.6 c 12.7,0 23.1,-10.3 23.1,-23.1 V 185.7 c 0,-12.7 -10.3,-23.1 -23.1,-23.1 H 185.7 Z M 326.3,342.3 H 185.7 c -8.8,0 -16,-7.2 -16,-16 V 208.8 h 172.5 v 117.5 c 0.1,8.8 -7.1,16 -15.9,16 z M 170.5,209.5 v 116.8 c 0,8.4 6.8,15.2 15.2,15.2 h 140.6 c 8.4,0 15.2,-6.8 15.2,-15.2 V 209.5 Z m 171.8,-7.8 H 169.7 v -16 c 0,-8.8 7.2,-16 16,-16 h 140.6 c 8.8,0 16,7.2 16,16 z M 170.5,201 h 171 v -15.2 c 0,-8.4 -6.8,-15.2 -15.2,-15.2 H 185.7 c -8.4,0 -15.2,6.8 -15.2,15.2 z"
id="path20"
style="fill:#ba6600;fill-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 188.2 188.2"
xml:space="preserve"
sodipodi:docname="desktopApplication_icon.svg"
width="188.2"
height="188.2"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs29" /><sodipodi:namedview
id="namedview27"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="1.6738281"
inkscape:cx="94.095684"
inkscape:cy="93.796968"
inkscape:window-width="1920"
inkscape:window-height="1057"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style2">
.st0{display:none;fill:#0C0C0C;}
.st1{fill:#AC55FF;}
</style>
<rect
class="st0"
width="512"
height="512"
id="rect4"
x="-161.89999"
y="-161.89999" />
<g
id="g24"
transform="translate(-161.9,-161.9)"
style="fill:#ba6600;fill-opacity:1">
<g
id="g10"
style="fill:#ba6600;fill-opacity:1">
<path
class="st1"
d="m 214.8,242.5 c -1.6,-1.4 -4.1,-1.2 -5.5,0.4 -1.3,1.5 -1.3,3.6 0,5.1 l 23.6,23.6 -23.6,23.6 c -1.6,1.4 -1.8,3.9 -0.4,5.5 1.4,1.6 3.9,1.8 5.5,0.4 0.2,-0.1 0.3,-0.3 0.4,-0.4 l 26.4,-26.4 c 1.5,-1.5 1.5,-4 0,-5.5 z"
id="path6"
style="fill:#ba6600;fill-opacity:1" />
<path
class="st1"
d="m 211.9,302.5 c -1.2,0 -2.4,-0.5 -3.3,-1.5 -0.7,-0.9 -1.1,-2 -1,-3.1 0.1,-1.1 0.6,-2.2 1.5,-2.9 l 23.3,-23.3 -23.3,-23.3 c -1.4,-1.6 -1.4,-4 0,-5.6 1.5,-1.8 4.2,-2 6,-0.5 v 0 l 26.4,26.4 c 1.7,1.7 1.7,4.4 0,6 L 215.1,301 c -0.1,0.1 -0.3,0.3 -0.4,0.4 -0.9,0.7 -1.8,1.1 -2.8,1.1 z m 0.4,-60.6 c -1,0 -2,0.4 -2.7,1.2 -1.1,1.3 -1.1,3.3 0,4.6 l 23.8,23.8 -23.8,23.9 c -0.7,0.6 -1.2,1.5 -1.2,2.4 -0.1,0.9 0.2,1.9 0.8,2.6 1.3,1.5 3.5,1.7 5,0.4 0.1,-0.1 0.3,-0.2 0.4,-0.4 L 241,274 c 1.4,-1.4 1.4,-3.6 0,-5 l -26.3,-26.3 c -0.8,-0.5 -1.6,-0.8 -2.4,-0.8 z"
id="path8"
style="fill:#ba6600;fill-opacity:1" />
</g>
<g
id="g16"
style="fill:#ba6600;fill-opacity:1">
<path
class="st1"
d="m 306.8,295 h -54.7 c -2.2,0 -3.9,1.7 -3.9,3.9 0,2.2 1.7,3.9 3.9,3.9 h 54.7 c 2.2,0 3.9,-1.7 3.9,-3.9 0,-2.1 -1.8,-3.9 -3.9,-3.9 z"
id="path12"
style="fill:#ba6600;fill-opacity:1" />
<path
class="st1"
d="m 306.8,303.2 h -54.7 c -2.4,0 -4.3,-1.9 -4.3,-4.3 0,-2.4 1.9,-4.3 4.3,-4.3 h 54.7 c 2.4,0 4.3,1.9 4.3,4.3 0,2.4 -2,4.3 -4.3,4.3 z m -54.7,-7.8 c -1.9,0 -3.5,1.6 -3.5,3.5 0,1.9 1.6,3.5 3.5,3.5 h 54.7 c 1.9,0 3.5,-1.6 3.5,-3.5 0,-1.9 -1.6,-3.5 -3.5,-3.5 z"
id="path14"
style="fill:#ba6600;fill-opacity:1" />
</g>
<g
id="g22"
style="fill:#ba6600;fill-opacity:1">
<path
class="st1"
d="M 326.3,162.3 H 185.7 c -12.9,0 -23.4,10.5 -23.4,23.4 v 140.6 c 0,12.9 10.5,23.4 23.4,23.4 h 140.6 c 12.9,0 23.4,-10.5 23.4,-23.4 V 185.7 c 0,-12.9 -10.5,-23.4 -23.4,-23.4 z m -140.6,7.8 h 140.6 c 8.6,0 15.6,7 15.6,15.6 v 15.6 H 170.1 v -15.6 c 0,-8.6 7,-15.6 15.6,-15.6 z M 326.3,341.9 H 185.7 c -8.6,0 -15.6,-7 -15.6,-15.6 V 209.1 h 171.8 v 117.1 c 0,8.7 -7,15.7 -15.6,15.7 z"
id="path18"
style="fill:#ba6600;fill-opacity:1" />
<path
class="st1"
d="M 326.3,350.1 H 185.7 c -13.1,0 -23.8,-10.7 -23.8,-23.8 V 185.7 c 0,-13.1 10.7,-23.8 23.8,-23.8 h 140.6 c 13.1,0 23.8,10.7 23.8,23.8 v 140.6 c 0,13.1 -10.7,23.8 -23.8,23.8 z M 185.7,162.7 c -12.7,0 -23.1,10.3 -23.1,23.1 v 140.6 c 0,12.7 10.3,23.1 23.1,23.1 h 140.6 c 12.7,0 23.1,-10.3 23.1,-23.1 V 185.7 c 0,-12.7 -10.3,-23.1 -23.1,-23.1 H 185.7 Z M 326.3,342.3 H 185.7 c -8.8,0 -16,-7.2 -16,-16 V 208.8 h 172.5 v 117.5 c 0.1,8.8 -7.1,16 -15.9,16 z M 170.5,209.5 v 116.8 c 0,8.4 6.8,15.2 15.2,15.2 h 140.6 c 8.4,0 15.2,-6.8 15.2,-15.2 V 209.5 Z m 171.8,-7.8 H 169.7 v -16 c 0,-8.8 7.2,-16 16,-16 h 140.6 c 8.8,0 16,7.2 16,16 z M 170.5,201 h 171 v -15.2 c 0,-8.4 -6.8,-15.2 -15.2,-15.2 H 185.7 c -8.4,0 -15.2,6.8 -15.2,15.2 z"
id="path20"
style="fill:#ba6600;fill-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 205.60609 122.97245"
xml:space="preserve"
sodipodi:docname="desktopCommand_icon-dark.svg"
width="205.60609"
height="122.97245"
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\command_icon-16.png"
inkscape:export-xdpi="7.8066268"
inkscape:export-ydpi="7.8066268"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs21" /><sodipodi:namedview
id="namedview19"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="2.3671504"
inkscape:cx="-23.445912"
inkscape:cy="2.1122443"
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" />
<style
type="text/css"
id="style2">
.st0{fill:#0C0C0C;}
.st1{fill:#AC55FF;}
</style>
<g
id="g16"
transform="matrix(0.81,0,0,0.81,-104.55448,-145.88493)"
style="fill:#ba6600;fill-opacity:1">
<g
id="g8"
style="fill:#ba6600;fill-opacity:1">
<path
class="st1"
d="m 166.9,196.4 c -3.3,-2.8 -8.2,-2.4 -11,0.8 -2.5,2.9 -2.5,7.2 0,10.1 l 47,47.1 -47,47.1 c -3.3,2.8 -3.6,7.7 -0.8,11 2.8,3.3 7.7,3.6 11,0.8 0.3,-0.3 0.6,-0.5 0.8,-0.8 l 52.6,-52.6 c 3,-3 3,-7.9 0,-11 z"
id="path6"
style="fill:#ba6600;fill-opacity:1" />
</g>
<g
id="g14"
style="fill:#ba6600;fill-opacity:1">
<path
class="st1"
d="M 350.3,301.2 H 241.2 c -4.3,0 -7.8,3.5 -7.8,7.8 0,4.3 3.5,7.8 7.8,7.8 h 109.1 c 4.3,0 7.8,-3.5 7.8,-7.8 0,-4.3 -3.5,-7.8 -7.8,-7.8 z"
id="path10"
style="fill:#ba6600;fill-opacity:1" />
<path
class="st1"
d="M 350.3,317.5 H 241.2 c -4.7,0 -8.5,-3.8 -8.5,-8.5 0,-4.7 3.8,-8.5 8.5,-8.5 h 109.1 c 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 z M 241.2,301.9 c -3.9,0 -7,3.2 -7,7 0,3.8 3.2,7 7,7 h 109.1 c 3.9,0 7,-3.2 7,-7 0,-3.8 -3.2,-7 -7,-7 z"
id="path12"
style="fill:#ba6600;fill-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 205.60609 122.97245"
xml:space="preserve"
sodipodi:docname="desktopCommand_icon.svg"
width="205.60609"
height="122.97245"
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\command_icon-16.png"
inkscape:export-xdpi="7.8066268"
inkscape:export-ydpi="7.8066268"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs21" /><sodipodi:namedview
id="namedview19"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="2.3671504"
inkscape:cx="-23.445912"
inkscape:cy="2.1122443"
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" />
<style
type="text/css"
id="style2">
.st0{fill:#0C0C0C;}
.st1{fill:#AC55FF;}
</style>
<g
id="g16"
transform="matrix(0.81,0,0,0.81,-104.55448,-145.88493)"
style="fill:#ba6600;fill-opacity:1">
<g
id="g8"
style="fill:#ba6600;fill-opacity:1">
<path
class="st1"
d="m 166.9,196.4 c -3.3,-2.8 -8.2,-2.4 -11,0.8 -2.5,2.9 -2.5,7.2 0,10.1 l 47,47.1 -47,47.1 c -3.3,2.8 -3.6,7.7 -0.8,11 2.8,3.3 7.7,3.6 11,0.8 0.3,-0.3 0.6,-0.5 0.8,-0.8 l 52.6,-52.6 c 3,-3 3,-7.9 0,-11 z"
id="path6"
style="fill:#ba6600;fill-opacity:1" />
</g>
<g
id="g14"
style="fill:#ba6600;fill-opacity:1">
<path
class="st1"
d="M 350.3,301.2 H 241.2 c -4.3,0 -7.8,3.5 -7.8,7.8 0,4.3 3.5,7.8 7.8,7.8 h 109.1 c 4.3,0 7.8,-3.5 7.8,-7.8 0,-4.3 -3.5,-7.8 -7.8,-7.8 z"
id="path10"
style="fill:#ba6600;fill-opacity:1" />
<path
class="st1"
d="M 350.3,317.5 H 241.2 c -4.7,0 -8.5,-3.8 -8.5,-8.5 0,-4.7 3.8,-8.5 8.5,-8.5 h 109.1 c 4.7,0 8.5,3.8 8.5,8.5 0,4.7 -3.8,8.5 -8.5,8.5 z M 241.2,301.9 c -3.9,0 -7,3.2 -7,7 0,3.8 3.2,7 7,7 h 109.1 c 3.9,0 7,-3.2 7,-7 0,-3.8 -3.2,-7 -7,-7 z"
id="path12"
style="fill:#ba6600;fill-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="Layer_1"
data-name="Layer 1"
viewBox="0 0 204.68 206.97"
version="1.1"
sodipodi:docname="desktopEnvironment_icon-dark.svg"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
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="namedview15"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="2.0703483"
inkscape:cx="21.493968"
inkscape:cy="104.33027"
inkscape:window-width="1920"
inkscape:window-height="1057"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<defs
id="defs4">
<style
id="style2">.cls-1{fill:#ac55ff;}</style>
</defs>
<title
id="title6">shell environment v2</title>
<path
class="cls-1"
d="M215.09,242.25l0,0a4.28,4.28,0,0,0-6,6.05l23.32,23.34-23.3,23.32a4.28,4.28,0,0,0-.47,6,4.29,4.29,0,0,0,6,.46,4.56,4.56,0,0,0,.44-.44l26.36-26.36a4.29,4.29,0,0,0,0-6Z"
transform="translate(-161.92 -143.11)"
id="path8"
style="fill:#ba6600;fill-opacity:1" />
<path
class="cls-1"
d="M306.76,294.67H252.1a4.28,4.28,0,1,0,0,8.56h54.66a4.28,4.28,0,0,0,0-8.56Z"
transform="translate(-161.92 -143.11)"
id="path10"
style="fill:#ba6600;fill-opacity:1" />
<path
class="cls-1"
d="M366.61,182.47a39.37,39.37,0,0,0-72.94-20.55h-108a23.83,23.83,0,0,0-23.8,23.8V326.28a23.83,23.83,0,0,0,23.8,23.8H326.28a23.83,23.83,0,0,0,23.8-23.8V214.52A39.31,39.31,0,0,0,366.61,182.47Zm-45.39,10.44,8.52-22.41a1.89,1.89,0,0,1,2-1.46,1.86,1.86,0,0,1,1.85,1.81,3.62,3.62,0,0,1-.28,1.18l-8.52,22.41a1.94,1.94,0,0,1-3.81-.36A3.57,3.57,0,0,1,321.22,192.91ZM317.88,176a1.86,1.86,0,0,1,2,1.92c0,.82-.18,1.61-1.68,2.25l-11.47,5,11.47,5c1.5.65,1.68,1.43,1.68,2.25a1.86,1.86,0,0,1-2,1.92,4.19,4.19,0,0,1-2-.5l-14.5-6.66a2.22,2.22,0,0,1,0-4.06l14.5-6.66A4.19,4.19,0,0,1,317.88,176Zm-147.4,9.7a15.26,15.26,0,0,1,15.24-15.24h104A39.48,39.48,0,0,0,292.5,201h-122Zm171,140.56a15.26,15.26,0,0,1-15.24,15.24H185.72a15.26,15.26,0,0,1-15.24-15.24V209.52H298.67a39.33,39.33,0,0,0,42.85,9.63Zm11.56-139-14.5,6.66a4.21,4.21,0,0,1-2,.5,1.86,1.86,0,0,1-2-1.92c0-.82.18-1.6,1.67-2.25l11.47-5-11.47-5c-1.49-.64-1.67-1.43-1.67-2.25a1.86,1.86,0,0,1,2-1.92,4.21,4.21,0,0,1,2,.5l14.5,6.66a2.23,2.23,0,0,1,0,4.06Z"
transform="translate(-161.92 -143.11)"
id="path12"
style="fill:#ba6600;fill-opacity:1" />
<metadata
id="metadata923">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>shell environment v2</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="Layer_1"
data-name="Layer 1"
viewBox="0 0 204.68 206.97"
version="1.1"
sodipodi:docname="desktopEnvironment_icon.svg"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
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="namedview15"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="2.0703483"
inkscape:cx="21.493968"
inkscape:cy="104.33027"
inkscape:window-width="1920"
inkscape:window-height="1057"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<defs
id="defs4">
<style
id="style2">.cls-1{fill:#ac55ff;}</style>
</defs>
<title
id="title6">shell environment v2</title>
<path
class="cls-1"
d="M215.09,242.25l0,0a4.28,4.28,0,0,0-6,6.05l23.32,23.34-23.3,23.32a4.28,4.28,0,0,0-.47,6,4.29,4.29,0,0,0,6,.46,4.56,4.56,0,0,0,.44-.44l26.36-26.36a4.29,4.29,0,0,0,0-6Z"
transform="translate(-161.92 -143.11)"
id="path8"
style="fill:#ba6600;fill-opacity:1" />
<path
class="cls-1"
d="M306.76,294.67H252.1a4.28,4.28,0,1,0,0,8.56h54.66a4.28,4.28,0,0,0,0-8.56Z"
transform="translate(-161.92 -143.11)"
id="path10"
style="fill:#ba6600;fill-opacity:1" />
<path
class="cls-1"
d="M366.61,182.47a39.37,39.37,0,0,0-72.94-20.55h-108a23.83,23.83,0,0,0-23.8,23.8V326.28a23.83,23.83,0,0,0,23.8,23.8H326.28a23.83,23.83,0,0,0,23.8-23.8V214.52A39.31,39.31,0,0,0,366.61,182.47Zm-45.39,10.44,8.52-22.41a1.89,1.89,0,0,1,2-1.46,1.86,1.86,0,0,1,1.85,1.81,3.62,3.62,0,0,1-.28,1.18l-8.52,22.41a1.94,1.94,0,0,1-3.81-.36A3.57,3.57,0,0,1,321.22,192.91ZM317.88,176a1.86,1.86,0,0,1,2,1.92c0,.82-.18,1.61-1.68,2.25l-11.47,5,11.47,5c1.5.65,1.68,1.43,1.68,2.25a1.86,1.86,0,0,1-2,1.92,4.19,4.19,0,0,1-2-.5l-14.5-6.66a2.22,2.22,0,0,1,0-4.06l14.5-6.66A4.19,4.19,0,0,1,317.88,176Zm-147.4,9.7a15.26,15.26,0,0,1,15.24-15.24h104A39.48,39.48,0,0,0,292.5,201h-122Zm171,140.56a15.26,15.26,0,0,1-15.24,15.24H185.72a15.26,15.26,0,0,1-15.24-15.24V209.52H298.67a39.33,39.33,0,0,0,42.85,9.63Zm11.56-139-14.5,6.66a4.21,4.21,0,0,1-2,.5,1.86,1.86,0,0,1-2-1.92c0-.82.18-1.6,1.67-2.25l11.47-5-11.47-5c-1.49-.64-1.67-1.43-1.67-2.25a1.86,1.86,0,0,1,2-1.92,4.21,4.21,0,0,1,2,.5l14.5,6.66a2.23,2.23,0,0,1,0,4.06Z"
transform="translate(-161.92 -143.11)"
id="path12"
style="fill:#ba6600;fill-opacity:1" />
<metadata
id="metadata923">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>shell environment v2</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -430,6 +430,7 @@ goodMorning=Good morning
goodAfternoon=Good afternoon
goodEvening=Good evening
addVisual=Visual ...
addDesktop=Desktop ...
ssh=SSH
sshConfiguration=SSH Configuration
size=Size

View file

@ -106,6 +106,25 @@ openInTerminal=Open in terminal
file=File
directory=Directory
symbolicLink=Symbolic link
desktopEnvironment.displayName=Desktop environment
desktopEnvironment.displayDescription=Create a reusable desktop environment configuration
desktopHost=Desktop host
desktopHostDescription=The desktop connection to use as a base
desktopShellDialect=Shell dialect
desktopShellDialectDescription=The shell dialect to use to run scripts and applications
desktopSnippets=Script snippets
desktopSnippetsDescription=List of reusable script snippets to run first
desktopInitScript=Init script
desktopInitScriptDescription=Init commands specific to this environment
desktopTerminal=Terminal application
desktopTerminalDescription=The terminal to use on the desktop to start scripts in
desktopApplication.displayName=Desktop application
desktopApplication.displayDescription=Run an application in a desktop environment
desktopEnvironmentBase=Desktop environment
desktopEnvironmentBaseDescription=The desktop environment to run this application on
desktopApplicationPath=Application path
desktopApplicationPathDescription=The path of the executable to run
desktopApplicationArguments=Arguments
desktopApplicationArgumentsDescription=The optional arguments to pass to the application