From 06d9c777fcb4b6b75c9d838504c2ce32a0a65954 Mon Sep 17 00:00:00 2001 From: crschnick Date: Wed, 18 Oct 2023 04:17:34 +0000 Subject: [PATCH] Small fixes and polishing --- .../xpipe/app/comp/base/SideMenuBarComp.java | 9 + .../comp/storage/store/StoreEntryComp.java | 4 +- .../comp/storage/store/StoreEntryWrapper.java | 5 + .../java/io/xpipe/app/core/AppGreetings.java | 5 +- .../main/java/io/xpipe/app/core/AppTheme.java | 25 +- .../io/xpipe/app/fxcomps/util/Shortcuts.java | 5 + .../xpipe/app/issue/GuiErrorHandlerBase.java | 4 + .../io/xpipe/app/storage/DataStorage.java | 292 +++++++++--------- .../xpipe/app/storage/DataStoreEntryRef.java | 4 + .../io/xpipe/app/storage/StandardStorage.java | 9 +- .../java/io/xpipe/app/util/Hyperlinks.java | 1 + build.gradle | 2 +- .../io/xpipe/core/process/ShellDialect.java | 4 + .../ext/base/script/SimpleScriptStore.java | 16 +- .../script/SimpleScriptStoreProvider.java | 2 +- .../base/resources/lang/executionType_en.md | 2 + .../ext/base/resources/lang/script_en.md | 5 + 17 files changed, 228 insertions(+), 166 deletions(-) create mode 100644 ext/base/src/main/resources/io/xpipe/ext/base/resources/lang/script_en.md diff --git a/app/src/main/java/io/xpipe/app/comp/base/SideMenuBarComp.java b/app/src/main/java/io/xpipe/app/comp/base/SideMenuBarComp.java index bd13ccd6..7d1a6f62 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/SideMenuBarComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/SideMenuBarComp.java @@ -80,6 +80,15 @@ public class SideMenuBarComp extends Comp> { vbox.getChildren().add(b.createRegion()); } + { + var b = new IconButtonComp("mdi2c-comment-processing-outline", () -> Hyperlinks.open(Hyperlinks.ROADMAP)) + .apply(new FancyTooltipAugment<>("roadmap")); + b.apply(struc -> { + AppFont.setSize(struc.get(), 2); + }); + vbox.getChildren().add(b.createRegion()); + } + { var b = new IconButtonComp("mdi2u-update", () -> UpdateAvailableAlert.showIfNeeded()) .apply(new FancyTooltipAugment<>("updateAvailableTooltip")); diff --git a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryComp.java b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryComp.java index cf203968..ef517f03 100644 --- a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryComp.java +++ b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryComp.java @@ -380,7 +380,9 @@ public abstract class StoreEntryComp extends SimpleComp { } var del = new MenuItem(AppI18n.get("remove"), new FontIcon("mdal-delete_outline")); - del.disableProperty().bind(wrapper.getDeletable().not()); + del.disableProperty().bind(Bindings.createBooleanBinding(() -> { + return !wrapper.getDeletable().get() && !AppPrefs.get().developerDisableGuiRestrictions().get(); + }, wrapper.getDeletable(), AppPrefs.get().developerDisableGuiRestrictions())); del.setOnAction(event -> wrapper.delete()); contextMenu.getItems().add(del); diff --git a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryWrapper.java b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryWrapper.java index 2e4b129d..3fec04c3 100644 --- a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryWrapper.java +++ b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryWrapper.java @@ -195,6 +195,11 @@ public class StoreEntryWrapper { } public void executeDefaultAction() throws Exception { + if (getEntry().getValidity() == DataStoreEntry.Validity.INCOMPLETE) { + editDialog(); + return; + } + var found = getDefaultActionProvider().getValue(); entry.updateLastUsed(); if (found != null) { diff --git a/app/src/main/java/io/xpipe/app/core/AppGreetings.java b/app/src/main/java/io/xpipe/app/core/AppGreetings.java index 2b7abed0..7a7d75bd 100644 --- a/app/src/main/java/io/xpipe/app/core/AppGreetings.java +++ b/app/src/main/java/io/xpipe/app/core/AppGreetings.java @@ -6,6 +6,7 @@ import io.xpipe.app.core.mode.OperationMode; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.util.BindingsHelper; import io.xpipe.app.issue.ErrorEvent; +import io.xpipe.app.util.Hyperlinks; import io.xpipe.app.util.MarkdownHelper; import javafx.beans.property.SimpleBooleanProperty; import javafx.geometry.Insets; @@ -110,9 +111,7 @@ public class AppGreetings { temp, MarkdownHelper.toHtml(Files.readString(file), UnaryOperator.identity())); }); - App.getApp() - .getHostServices() - .showDocument(temp.toUri().toString()); + Hyperlinks.open(temp.toUri().toString()); } catch (IOException e) { ErrorEvent.fromThrowable(e).handle(); } diff --git a/app/src/main/java/io/xpipe/app/core/AppTheme.java b/app/src/main/java/io/xpipe/app/core/AppTheme.java index e35abad8..bcac047a 100644 --- a/app/src/main/java/io/xpipe/app/core/AppTheme.java +++ b/app/src/main/java/io/xpipe/app/core/AppTheme.java @@ -36,6 +36,10 @@ public class AppTheme { private static final PseudoClass PERFORMANCE = PseudoClass.getPseudoClass("performance"); public static void initThemeHandlers(Window stage) { + if (AppPrefs.get() == null) { + return; + } + SimpleChangeListener.apply(AppPrefs.get().theme, t -> { Theme.ALL.forEach(theme -> stage.getScene().getRoot().getStyleClass().remove(theme.getCssId())); if (t == null) { @@ -73,17 +77,20 @@ public class AppTheme { t.apply(); TrackEvent.debug("Set theme " + t.getId() + " for scene"); - detector.registerListener(dark -> { - PlatformThread.runLaterIfNeeded(() -> { - if (dark && !AppPrefs.get().theme.getValue().isDark()) { - AppPrefs.get().theme.setValue(Theme.getDefaultDarkTheme()); - } + // The gnome detector sometimes runs into issues, also it's not that important + if (!OsType.getLocal().equals(OsType.LINUX)) { + detector.registerListener(dark -> { + PlatformThread.runLaterIfNeeded(() -> { + if (dark && !AppPrefs.get().theme.getValue().isDark()) { + AppPrefs.get().theme.setValue(Theme.getDefaultDarkTheme()); + } - if (!dark && AppPrefs.get().theme.getValue().isDark()) { - AppPrefs.get().theme.setValue(Theme.getDefaultLightTheme()); - } + if (!dark && AppPrefs.get().theme.getValue().isDark()) { + AppPrefs.get().theme.setValue(Theme.getDefaultLightTheme()); + } + }); }); - }); + } AppPrefs.get().theme.addListener((c, o, n) -> { changeTheme(n); diff --git a/app/src/main/java/io/xpipe/app/fxcomps/util/Shortcuts.java b/app/src/main/java/io/xpipe/app/fxcomps/util/Shortcuts.java index f39faea8..316c4e95 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/util/Shortcuts.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/util/Shortcuts.java @@ -24,6 +24,11 @@ public class Shortcuts { public static void addShortcut(T region, KeyCombination comb, Consumer exec) { var filter = new EventHandler() { public void handle(KeyEvent ke) { + var target = ke.getTarget(); + if (!region.isVisible() || !region.isManaged() || region.isDisabled()) { + return; + } + if (comb.match(ke)) { exec.accept(region); ke.consume(); diff --git a/app/src/main/java/io/xpipe/app/issue/GuiErrorHandlerBase.java b/app/src/main/java/io/xpipe/app/issue/GuiErrorHandlerBase.java index 64090ce8..e618a394 100644 --- a/app/src/main/java/io/xpipe/app/issue/GuiErrorHandlerBase.java +++ b/app/src/main/java/io/xpipe/app/issue/GuiErrorHandlerBase.java @@ -10,6 +10,10 @@ import java.util.function.Consumer; public class GuiErrorHandlerBase { protected boolean startupGui(Consumer onFail) { + if (PlatformState.getCurrent() == PlatformState.EXITED) { + return false; + } + if (PlatformState.getCurrent() == PlatformState.NOT_INITIALIZED) { try { CountDownLatch latch = new CountDownLatch(1); diff --git a/app/src/main/java/io/xpipe/app/storage/DataStorage.java b/app/src/main/java/io/xpipe/app/storage/DataStorage.java index 1665a40a..a178ce87 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStorage.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStorage.java @@ -37,7 +37,9 @@ public abstract class DataStorage { private static DataStorage INSTANCE; protected final Path dir; + @Getter protected final List storeCategories; + @Getter protected final Set storeEntries; @Getter @@ -159,6 +161,8 @@ public abstract class DataStorage { listeners.forEach(storageListener -> storageListener.onStoreAdd(toAdd)); refreshValidities(true); } + + saveAsync(); } public void updateCategory(DataStoreEntry entry, DataStoreCategory newCategory) { @@ -171,6 +175,7 @@ public abstract class DataStorage { var toAdd = Stream.concat(Stream.of(entry), children.stream()).toArray(DataStoreEntry[]::new); listeners.forEach(storageListener -> storageListener.onStoreAdd(toAdd)); + saveAsync(); } public boolean refreshChildren(DataStoreEntry e) { @@ -228,6 +233,15 @@ public abstract class DataStorage { return !newChildren.isEmpty(); } + public void deleteChildren(DataStoreEntry e) { + var c = getDeepStoreChildren(e); + c.forEach(entry -> entry.finalizeEntry()); + this.storeEntries.removeAll(c); + this.listeners.forEach(l -> l.onStoreRemove(c.toArray(DataStoreEntry[]::new))); + refreshValidities(false); + saveAsync(); + } + public void deleteWithChildren(DataStoreEntry... entries) { var toDelete = Arrays.stream(entries) .flatMap(entry -> { @@ -244,15 +258,130 @@ public abstract class DataStorage { saveAsync(); } - public void deleteChildren(DataStoreEntry e) { - var c = getDeepStoreChildren(e); - c.forEach(entry -> entry.finalizeEntry()); - this.storeEntries.removeAll(c); - this.listeners.forEach(l -> l.onStoreRemove(c.toArray(DataStoreEntry[]::new))); + + public DataStoreCategory addStoreCategoryIfNotPresent(@NonNull DataStoreCategory cat) { + if (storeCategories.contains(cat)) { + return cat; + } + + var byId = getStoreCategoryIfPresent(cat.getUuid()).orElse(null); + if (byId != null) { + return byId; + } + + addStoreCategory(cat); + return cat; + } + + public void addStoreCategory(@NonNull DataStoreCategory cat) { + cat.setDirectory(getCategoriesDir().resolve(cat.getUuid().toString())); + this.storeCategories.add(cat); + saveAsync(); + + this.listeners.forEach(l -> l.onCategoryAdd(cat)); + } + + public DataStoreEntry addStoreEntryIfNotPresent(@NonNull DataStoreEntry e) { + if (storeEntries.contains(e)) { + return e; + } + + var byId = getStoreEntryIfPresent(e.getUuid()).orElse(null); + if (byId != null) { + return byId; + } + + var syntheticParent = getSyntheticParent(e); + if (syntheticParent.isPresent()) { + addStoreEntryIfNotPresent(syntheticParent.get()); + } + + var displayParent = syntheticParent.or(() -> getDisplayParent(e)); + if (displayParent.isPresent()) { + displayParent.get().setExpanded(true); + } + + e.setDirectory(getStoresDir().resolve(e.getUuid().toString())); + this.storeEntries.add(e); + displayParent.ifPresent(p -> { + p.setChildrenCache(null); + }); + saveAsync(); + + this.listeners.forEach(l -> l.onStoreAdd(e)); + e.initializeEntry(); + refreshValidities(true); + return e; + } + + public void addStoreEntriesIfNotPresent(@NonNull DataStoreEntry... es) { + for (DataStoreEntry e : es) { + if (storeEntries.contains(e) || getStoreEntryIfPresent(e.getStore()).isPresent()) { + return; + } + + var syntheticParent = getSyntheticParent(e); + if (syntheticParent.isPresent()) { + addStoreEntryIfNotPresent(syntheticParent.get()); + } + + var displayParent = syntheticParent.or(() -> getDisplayParent(e)); + if (displayParent.isPresent()) { + displayParent.get().setExpanded(true); + } + + e.setDirectory(getStoresDir().resolve(e.getUuid().toString())); + this.storeEntries.add(e); + displayParent.ifPresent(p -> { + p.setChildrenCache(null); + }); + } + this.listeners.forEach(l -> l.onStoreAdd(es)); + for (DataStoreEntry e : es) { + e.initializeEntry(); + } + refreshValidities(true); + saveAsync(); + } + + public DataStoreEntry addStoreIfNotPresent(@NonNull String name, DataStore store) { + var f = getStoreEntryIfPresent(store); + if (f.isPresent()) { + return f.get(); + } + + var e = DataStoreEntry.createNew(UUID.randomUUID(), selectedCategory.getUuid(), name, store); + addStoreEntryIfNotPresent(e); + return e; + } + + public void deleteStoreEntry(@NonNull DataStoreEntry store) { + store.finalizeEntry(); + this.storeEntries.remove(store); + getDisplayParent(store).ifPresent(p -> p.setChildrenCache(null)); + this.listeners.forEach(l -> l.onStoreRemove(store)); refreshValidities(false); saveAsync(); } + public void deleteStoreCategory(@NonNull DataStoreCategory cat) { + if (cat.getUuid().equals(DEFAULT_CATEGORY_UUID) || cat.getUuid().equals(ALL_CONNECTIONS_CATEGORY_UUID)) { + return; + } + + storeEntries.forEach(entry -> { + if (entry.getCategoryUuid().equals(cat.getUuid())) { + entry.setCategoryUuid(DEFAULT_CATEGORY_UUID); + } + }); + + storeCategories.remove(cat); + saveAsync(); + this.listeners.forEach(l -> l.onCategoryRemove(cat)); + } + + // Get operations + public boolean isRootEntry(DataStoreEntry entry) { var noParent = DataStorage.get().getDisplayParent(entry).isEmpty(); var diffParentCategory = DataStorage.get() @@ -312,8 +441,9 @@ public abstract class DataStorage { public Set getDeepStoreChildren(DataStoreEntry entry) { var set = new HashSet(); - getStoreChildren(entry).forEach(entry1 -> { - set.addAll(getDeepStoreChildren(entry1)); + getStoreChildren(entry).forEach(c -> { + set.add(c); + set.addAll(getDeepStoreChildren(c)); }); return set; } @@ -435,112 +565,6 @@ public abstract class DataStorage { .findFirst(); } - public DataStoreCategory addStoreCategoryIfNotPresent(@NonNull DataStoreCategory cat) { - if (storeCategories.contains(cat)) { - return cat; - } - - var byId = getStoreCategoryIfPresent(cat.getUuid()).orElse(null); - if (byId != null) { - return byId; - } - - addStoreCategory(cat); - return cat; - } - - public void addStoreCategory(@NonNull DataStoreCategory cat) { - cat.setDirectory(getCategoriesDir().resolve(cat.getUuid().toString())); - this.storeCategories.add(cat); - saveAsync(); - - this.listeners.forEach(l -> l.onCategoryAdd(cat)); - } - - public DataStoreEntry addStoreEntryIfNotPresent(@NonNull DataStoreEntry e) { - if (storeEntries.contains(e)) { - return e; - } - - var byId = getStoreEntryIfPresent(e.getUuid()).orElse(null); - if (byId != null) { - return byId; - } - - var syntheticParent = getSyntheticParent(e); - if (syntheticParent.isPresent()) { - addStoreEntryIfNotPresent(syntheticParent.get()); - } - - var displayParent = syntheticParent.or(() -> getDisplayParent(e)); - if (displayParent.isPresent()) { - displayParent.get().setExpanded(true); - } - - e.setDirectory(getStoresDir().resolve(e.getUuid().toString())); - this.storeEntries.add(e); - displayParent.ifPresent(p -> { - p.setChildrenCache(null); - }); - saveAsync(); - - this.listeners.forEach(l -> l.onStoreAdd(e)); - e.initializeEntry(); - refreshValidities(true); - return e; - } - - public DataStoreEntry getOrCreateNewSyntheticEntry(DataStoreEntry parent, String name, DataStore store) { - var uuid = UuidHelper.generateFromObject(parent.getUuid(), name); - var found = getStoreEntryIfPresent(uuid); - if (found.isPresent()) { - return found.get(); - } - - return DataStoreEntry.createNew(uuid, parent.getCategoryUuid(), name, store); - } - - public void addStoreEntriesIfNotPresent(@NonNull DataStoreEntry... es) { - for (DataStoreEntry e : es) { - if (storeEntries.contains(e) || getStoreEntryIfPresent(e.getStore()).isPresent()) { - return; - } - - var syntheticParent = getSyntheticParent(e); - if (syntheticParent.isPresent()) { - addStoreEntryIfNotPresent(syntheticParent.get()); - } - - var displayParent = syntheticParent.or(() -> getDisplayParent(e)); - if (displayParent.isPresent()) { - displayParent.get().setExpanded(true); - } - - e.setDirectory(getStoresDir().resolve(e.getUuid().toString())); - this.storeEntries.add(e); - displayParent.ifPresent(p -> { - p.setChildrenCache(null); - }); - } - this.listeners.forEach(l -> l.onStoreAdd(es)); - for (DataStoreEntry e : es) { - e.initializeEntry(); - } - refreshValidities(true); - saveAsync(); - } - - public DataStoreEntry addStoreIfNotPresent(@NonNull String name, DataStore store) { - var f = getStoreEntryIfPresent(store); - if (f.isPresent()) { - return f.get(); - } - - var e = DataStoreEntry.createNew(UUID.randomUUID(), selectedCategory.getUuid(), name, store); - addStoreEntryIfNotPresent(e); - return e; - } - public Optional getStoreDisplayName(DataStore store) { if (store == null) { return Optional.empty(); @@ -557,35 +581,20 @@ public abstract class DataStorage { return store.getProvider().browserDisplayName(store.getStore()); } - public void deleteStoreEntry(@NonNull DataStoreEntry store) { - store.finalizeEntry(); - this.storeEntries.remove(store); - getDisplayParent(store).ifPresent(p -> p.setChildrenCache(null)); - this.listeners.forEach(l -> l.onStoreRemove(store)); - refreshValidities(false); - saveAsync(); - } - - public void deleteStoreCategory(@NonNull DataStoreCategory cat) { - if (cat.getUuid().equals(DEFAULT_CATEGORY_UUID) || cat.getUuid().equals(ALL_CONNECTIONS_CATEGORY_UUID)) { - return; - } - - storeEntries.forEach(entry -> { - if (entry.getCategoryUuid().equals(cat.getUuid())) { - entry.setCategoryUuid(DEFAULT_CATEGORY_UUID); - } - }); - - storeCategories.remove(cat); - saveAsync(); - this.listeners.forEach(l -> l.onCategoryRemove(cat)); - } - public Optional getStoreEntryIfPresent(UUID id) { return storeEntries.stream().filter(e -> e.getUuid().equals(id)).findAny(); } + public DataStoreEntry getOrCreateNewSyntheticEntry(DataStoreEntry parent, String name, DataStore store) { + var uuid = UuidHelper.generateFromObject(parent.getUuid(), name); + var found = getStoreEntryIfPresent(uuid); + if (found.isPresent()) { + return found.get(); + } + + return DataStoreEntry.createNew(uuid, parent.getCategoryUuid(), name, store); + } + public DataStoreEntry getStoreEntry(UUID id) { return getStoreEntryIfPresent(id).orElseThrow(); } @@ -594,11 +603,4 @@ public abstract class DataStorage { return getStoreEntryIfPresent(LOCAL_ID).orElse(null); } - public Set getStoreEntries() { - return storeEntries; - } - - public List getStoreCategories() { - return storeCategories; - } } diff --git a/app/src/main/java/io/xpipe/app/storage/DataStoreEntryRef.java b/app/src/main/java/io/xpipe/app/storage/DataStoreEntryRef.java index f2d3db18..1c2e45a6 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStoreEntryRef.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStoreEntryRef.java @@ -10,6 +10,10 @@ public class DataStoreEntryRef { @NonNull DataStoreEntry entry; + public DataStoreEntryRef(@NonNull DataStoreEntry entry) { + this.entry = entry; + } + public void checkComplete() throws Exception { getStore().checkComplete(); } diff --git a/app/src/main/java/io/xpipe/app/storage/StandardStorage.java b/app/src/main/java/io/xpipe/app/storage/StandardStorage.java index 8e0ad0db..b9efe601 100644 --- a/app/src/main/java/io/xpipe/app/storage/StandardStorage.java +++ b/app/src/main/java/io/xpipe/app/storage/StandardStorage.java @@ -247,9 +247,7 @@ public class StandardStorage extends DataStorage { ErrorEvent.fromThrowable(ex).terminal(true).build().handle(); } - { var hasFixedLocal = storeEntries.stream().anyMatch(dataStoreEntry -> dataStoreEntry.getUuid().equals(LOCAL_ID)); - // storeEntries.removeIf(dataStoreEntry -> !dataStoreEntry.getUuid().equals(LOCAL_ID) && dataStoreEntry.getStore() instanceof LocalStore); if (!hasFixedLocal) { var e = DataStoreEntry.createNew( LOCAL_ID, DataStorage.DEFAULT_CATEGORY_UUID, "Local Machine", new LocalStore()); @@ -264,7 +262,6 @@ public class StandardStorage extends DataStorage { if (storeEntries.stream().noneMatch(entry -> entry.getColor() != null)) { local.setColor(DataStoreColor.BLUE); } - } // Refresh to update state storeEntries.forEach(dataStoreEntry -> dataStoreEntry.refresh()); @@ -278,6 +275,12 @@ public class StandardStorage extends DataStorage { refreshValidities(true); + // Save to apply changes + if (!hasFixedLocal) { + storeEntries.removeIf(dataStoreEntry -> !dataStoreEntry.getUuid().equals(LOCAL_ID) && dataStoreEntry.getStore() instanceof LocalStore); + save(); + } + deleteLeftovers(); } diff --git a/app/src/main/java/io/xpipe/app/util/Hyperlinks.java b/app/src/main/java/io/xpipe/app/util/Hyperlinks.java index b6f7c634..5855c606 100644 --- a/app/src/main/java/io/xpipe/app/util/Hyperlinks.java +++ b/app/src/main/java/io/xpipe/app/util/Hyperlinks.java @@ -7,6 +7,7 @@ public class Hyperlinks { public static final String WEBSITE = "https://xpipe.io"; public static final String DOCUMENTATION = "https://docs.xpipe.io"; public static final String GITHUB = "https://github.com/xpipe-io/xpipe"; + public static final String ROADMAP = "https://xpipe.kampsite.co/"; public static final String PRIVACY = "https://github.com/xpipe-io/xpipe/blob/master/PRIVACY.md"; public static final String TOS = "https://github.com/xpipe-io/xpipe/blob/master/app/src/main/resources/io/xpipe/app/resources/misc/tos.md"; public static final String SECURITY = "https://github.com/xpipe-io/xpipe/blob/master/SECURITY.md"; diff --git a/build.gradle b/build.gradle index b3c280cb..248c08ee 100644 --- a/build.gradle +++ b/build.gradle @@ -44,7 +44,7 @@ project.ext { productName = isStage ? 'XPipe PTB' : 'XPipe' kebapProductName = isStage ? 'xpipe-ptb' : 'xpipe' publisher = 'XPipe UG (haftungsbeschränkt)' - shortDescription = 'The shell connection hub and remote file browser for your entire infrastructure' + shortDescription = 'Your entire server infrastructure at your fingertips' longDescription = 'XPipe is a new type of shell connection hub and remote file manager that allows you to access your entire sever infrastructure from your local machine. It works on top of your installed command-line programs that you normally use to connect and does not require any setup on your remote systems.' website = 'https://xpipe.io' sourceWebsite = 'https://github.com/xpipe-io/xpipe' diff --git a/core/src/main/java/io/xpipe/core/process/ShellDialect.java b/core/src/main/java/io/xpipe/core/process/ShellDialect.java index c147d567..a91cd43e 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellDialect.java +++ b/core/src/main/java/io/xpipe/core/process/ShellDialect.java @@ -34,6 +34,10 @@ public interface ShellDialect { return other.equals(this); } + default ShellDialect getDumbReplacementDialect() { + return this; + } + String getCatchAllVariable(); CommandControl queryVersion(ShellControl shellControl); diff --git a/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStore.java b/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStore.java index d506da6c..cf686e09 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStore.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStore.java @@ -13,6 +13,7 @@ import lombok.extern.jackson.Jacksonized; import java.util.LinkedHashSet; import java.util.List; +import java.util.stream.Collectors; @SuperBuilder @Getter @@ -29,9 +30,18 @@ public class SimpleScriptStore extends ScriptStore { } private String assemble(ShellControl shellControl, ExecutionType type) { - var targetType = type == ExecutionType.TERMINAL_ONLY ? shellControl.getTargetTerminalShellDialect() : shellControl.getShellDialect(); - if ((executionType == type || executionType == ExecutionType.BOTH) && minimumDialect.isCompatibleTo(targetType)) { - var script = ScriptHelper.createExecScript(targetType, shellControl, commands); + var targetType = type == ExecutionType.TERMINAL_ONLY + ? shellControl.getTargetTerminalShellDialect() + : shellControl.getShellDialect(); + if ((executionType == type || executionType == ExecutionType.BOTH) + && minimumDialect.isCompatibleTo(targetType)) { + var shebang = commands.startsWith("#"); + // Fix new lines and shebang + var fixedCommands = commands.lines() + .skip(shebang ? 1 : 0) + .collect(Collectors.joining( + shellControl.getShellDialect().getNewLine().getNewLineString())); + var script = ScriptHelper.createExecScript(targetType, shellControl, fixedCommands); return targetType.sourceScriptCommand(shellControl, script); } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStoreProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStoreProvider.java index 074b709d..8bab6629 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStoreProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStoreProvider.java @@ -145,7 +145,7 @@ public class SimpleScriptStoreProvider implements DataStoreProvider { .nonNull() .name("scriptContents") .description("scriptContentsDescription") - .longDescription("proc:environmentScript") + .longDescription("base:script") .addComp( new IntegratedTextAreaComp(commandProp, false, "commands", Bindings.createStringBinding(() -> { return dialect.getValue() != null diff --git a/ext/base/src/main/resources/io/xpipe/ext/base/resources/lang/executionType_en.md b/ext/base/src/main/resources/io/xpipe/ext/base/resources/lang/executionType_en.md index 789f29f9..c34e58bf 100644 --- a/ext/base/src/main/resources/io/xpipe/ext/base/resources/lang/executionType_en.md +++ b/ext/base/src/main/resources/io/xpipe/ext/base/resources/lang/executionType_en.md @@ -6,6 +6,8 @@ Only afterward will a separate connection be made in the actual terminal. The file browser for example entirely uses the dumb background mode to handle its operations, so if you want your script environment to apply to the file browser session, it should run in the dumb mode. +If you want the script to be run when you open the connection in a terminal, then choose the terminal mode. + ### Blocking commands Blocking commands that require user input can freeze the shell process when XPipe starts it up internally first in the background. diff --git a/ext/base/src/main/resources/io/xpipe/ext/base/resources/lang/script_en.md b/ext/base/src/main/resources/io/xpipe/ext/base/resources/lang/script_en.md new file mode 100644 index 00000000..ee7969f5 --- /dev/null +++ b/ext/base/src/main/resources/io/xpipe/ext/base/resources/lang/script_en.md @@ -0,0 +1,5 @@ +## Init script + +The contents of the script to run. You can choose to either edit this in place or use the external edit button in the top right corner to launch an external text editor. + +You don't have to specify a shebang line for shells that support it, one is added automatically with the appropriate shell type.