This commit is contained in:
crschnick
2026-01-24 06:34:28 +00:00
parent f20edc9ee5
commit f970490213
14 changed files with 149 additions and 418 deletions
@@ -1,60 +0,0 @@
package io.xpipe.app.ext;
import io.xpipe.app.hub.comp.StoreEntryComp;
import io.xpipe.app.hub.comp.StoreSection;
import io.xpipe.app.hub.comp.StoreToggleComp;
import io.xpipe.app.hub.comp.StoreViewState;
import io.xpipe.app.platform.BindingsHelper;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.property.SimpleBooleanProperty;
public interface EnabledParentStoreProvider extends DataStoreProvider {
@Override
default StoreEntryComp customEntryComp(StoreSection sec, boolean preferLarge) {
if (sec.getWrapper().getValidity().getValue() == DataStoreEntry.Validity.LOAD_FAILED) {
return StoreEntryComp.create(sec, null, preferLarge);
}
EnabledStoreState initialState = sec.getWrapper().getEntry().getStorePersistentState();
var enabled = new SimpleBooleanProperty(initialState.isEnabled());
sec.getWrapper().getPersistentState().subscribe((newValue) -> {
EnabledStoreState s = sec.getWrapper().getEntry().getStorePersistentState();
enabled.set(s.isEnabled());
});
var toggle = StoreToggleComp.<StatefulDataStore<EnabledStoreState>>enableToggle(
null, sec, enabled, (s, aBoolean) -> {
var state = s.getState().toBuilder().enabled(aBoolean).build();
s.setState(state);
var children =
DataStorage.get().getStoreChildren(sec.getWrapper().getEntry());
ThreadHelper.runFailableAsync(() -> {
for (DataStoreEntry child : children) {
if (child.getStorePersistentState() instanceof EnabledStoreState enabledStoreState) {
child.setStorePersistentState(enabledStoreState.toBuilder()
.enabled(aBoolean)
.build());
}
}
});
});
var e = sec.getWrapper().getEntry();
var parent = DataStorage.get().getDefaultDisplayParent(e);
if (parent.isPresent()) {
var parentWrapper = StoreViewState.get().getEntryWrapper(parent.get());
// Disable selection if parent is already made enabled
toggle.setCustomVisibility(BindingsHelper.map(parentWrapper.getPersistentState(), o -> {
EnabledStoreState state = (EnabledStoreState) o;
return !state.isEnabled();
}));
}
return StoreEntryComp.create(sec, toggle, preferLarge);
}
}
@@ -1,25 +0,0 @@
package io.xpipe.ext.base.script;
import io.xpipe.app.storage.DataStoreEntryRef;
import lombok.Getter;
import lombok.Setter;
@Getter
public enum PredefinedScriptGroup {
MANAGEMENT("Management", "Sample management scripts", true),
FILES("Files", "Sample scripts for files", true);
private final String name;
private final String description;
private final boolean expanded;
@Setter
private DataStoreEntryRef<ScriptGroupStore> entry;
PredefinedScriptGroup(String name, String description, boolean expanded) {
this.name = name;
this.description = description;
this.expanded = expanded;
}
}
@@ -18,29 +18,24 @@ import java.util.function.Supplier;
@Getter
public enum PredefinedScriptStore {
APT_UPDATE("Apt upgrade", () -> ScriptStore.builder()
.group(PredefinedScriptGroup.MANAGEMENT.getEntry())
.textSource(ScriptTextSource.InPlace.builder().dialect(ShellDialects.SH).text(file("apt_upgrade.sh")).build())
.shellScript(true)
.runnableScript(true)
.build()),
REMOVE_CR("CRLF to LF", () -> ScriptStore.builder()
.group(PredefinedScriptGroup.FILES.getEntry())
.textSource(ScriptTextSource.InPlace.builder().dialect(ShellDialects.SH).text(file("crlf_to_lf.sh")).build())
.fileScript(true)
.shellScript(true)
.build()),
DIFF("Diff", () -> ScriptStore.builder()
.group(PredefinedScriptGroup.FILES.getEntry())
.textSource(ScriptTextSource.InPlace.builder().dialect(ShellDialects.SH).text(file("diff.sh")).build())
.fileScript(true)
.build()),
GIT_CONFIG("Git Config", () -> ScriptStore.builder()
.group(PredefinedScriptGroup.MANAGEMENT.getEntry())
.textSource(ScriptTextSource.InPlace.builder().text(file("git_config.sh")).build())
.runnableScript(true)
.build()),
SYSTEM_HEALTH_STATUS("System health status", () -> ScriptStore.builder()
.group(PredefinedScriptGroup.MANAGEMENT.getEntry())
.textSource(ScriptTextSource.InPlace.builder().dialect(ShellDialects.SH).text(file("system_health.sh")).build())
.initScript(true)
.build());
@@ -95,7 +95,7 @@ public class RunFileScriptMenuProvider implements BrowserMenuBranchProvider {
private BrowserMenuBranchProvider createActionForScriptHierarchy(ScriptHierarchy hierarchy) {
if (hierarchy.isLeaf()) {
return createActionForScript(hierarchy.getLeafBase());
return createActionForScript(hierarchy.getScript());
}
var list = hierarchy.getChildren().stream()
@@ -104,14 +104,17 @@ public class RunFileScriptMenuProvider implements BrowserMenuBranchProvider {
return new BrowserMenuBranchProvider() {
@Override
public LabelGraphic getIcon() {
if (!hierarchy.isLeaf()) {
return null;
}
return new LabelGraphic.CompGraphic(
PrettyImageHelper.ofFixedSize(hierarchy.getBase().get().getEffectiveIconFile(), 16, 16));
PrettyImageHelper.ofFixedSize(hierarchy.getScript().get().getEffectiveIconFile(), 16, 16));
}
@Override
public ObservableValue<String> getName(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
var b = hierarchy.getBase();
return new SimpleStringProperty(b != null ? b.get().getName() : null);
return new SimpleStringProperty(hierarchy.getName());
}
@Override
@@ -15,6 +15,7 @@ import io.xpipe.app.process.SystemState;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntryRef;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
@@ -172,7 +173,7 @@ public class RunScriptActionProviderMenu implements HubBranchProvider<ShellStore
public AbstractAction createAction(DataStoreEntryRef<ShellStore> ref) {
return RunTerminalScriptActionProvider.Action.builder()
.ref(ref)
.scriptStore(hierarchy.getLeafBase())
.scriptStore(hierarchy.getScript())
.build();
}
@@ -214,7 +215,7 @@ public class RunScriptActionProviderMenu implements HubBranchProvider<ShellStore
public RunTerminalScriptActionProvider.Action createBatchAction(DataStoreEntryRef<ShellStore> ref) {
return RunTerminalScriptActionProvider.Action.builder()
.ref(ref)
.scriptStore(hierarchy.getLeafBase())
.scriptStore(hierarchy.getScript())
.build();
}
}
@@ -228,7 +229,7 @@ public class RunScriptActionProviderMenu implements HubBranchProvider<ShellStore
public AbstractAction createAction(DataStoreEntryRef<ShellStore> ref) {
return RunHubScriptActionProvider.Action.builder()
.ref(ref)
.scriptStore(hierarchy.getLeafBase())
.scriptStore(hierarchy.getScript())
.build();
}
@@ -266,7 +267,7 @@ public class RunScriptActionProviderMenu implements HubBranchProvider<ShellStore
public AbstractAction createBatchAction(List<DataStoreEntryRef<ShellStore>> stores) {
return RunHubBatchScriptActionProvider.Action.builder()
.refs(stores)
.scriptStore(hierarchy.getLeafBase())
.scriptStore(hierarchy.getScript())
.build();
}
@@ -274,7 +275,7 @@ public class RunScriptActionProviderMenu implements HubBranchProvider<ShellStore
public RunHubScriptActionProvider.Action createBatchAction(DataStoreEntryRef<ShellStore> ref) {
return RunHubScriptActionProvider.Action.builder()
.ref(ref)
.scriptStore(hierarchy.getLeafBase())
.scriptStore(hierarchy.getScript())
.build();
}
}
@@ -289,7 +290,7 @@ public class RunScriptActionProviderMenu implements HubBranchProvider<ShellStore
public AbstractAction createAction(DataStoreEntryRef<ShellStore> ref) {
return RunBackgroundScriptActionProvider.Action.builder()
.ref(ref)
.scriptStore(hierarchy.getLeafBase())
.scriptStore(hierarchy.getScript())
.build();
}
@@ -327,7 +328,7 @@ public class RunScriptActionProviderMenu implements HubBranchProvider<ShellStore
public RunBackgroundScriptActionProvider.Action createBatchAction(DataStoreEntryRef<ShellStore> ref) {
return RunBackgroundScriptActionProvider.Action.builder()
.ref(ref)
.scriptStore(hierarchy.getLeafBase())
.scriptStore(hierarchy.getScript())
.build();
}
@@ -344,16 +345,16 @@ public class RunScriptActionProviderMenu implements HubBranchProvider<ShellStore
@Override
public ObservableValue<String> getName() {
return new SimpleStringProperty(hierarchy.getBase().get().getName());
return new ReadOnlyObjectWrapper<>(hierarchy.getName());
}
@Override
public LabelGraphic getIcon() {
if (hierarchy.isLeaf()) {
return new LabelGraphic.ImageGraphic(hierarchy.getBase().get().getEffectiveIconFile(), 16);
return new LabelGraphic.ImageGraphic(hierarchy.getScript().get().getEffectiveIconFile(), 16);
}
return new LabelGraphic.IconGraphic("mdi2p-play-box-multiple-outline");
return new LabelGraphic.ImageGraphic("base:scriptGroup_icon.svg", 16);
}
@Override
@@ -5,9 +5,7 @@ import io.xpipe.app.comp.base.*;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.ShellDialectChoiceComp;
import io.xpipe.app.ext.ShellDialectIcons;
import io.xpipe.app.hub.comp.StoreChoiceComp;
import io.xpipe.app.hub.comp.StoreCreationDialog;
import io.xpipe.app.hub.comp.StoreViewState;
import io.xpipe.app.hub.comp.*;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.platform.DerivedObservableList;
import io.xpipe.app.platform.LabelGraphic;
@@ -43,7 +41,7 @@ public class ScriptCollectionSourceImportDialog {
private final StringProperty filter = new SimpleStringProperty();
private final BooleanProperty busy = new SimpleBooleanProperty();
private final IntegerProperty count = new SimpleIntegerProperty();
private final ObjectProperty<DataStoreEntryRef<ScriptGroupStore>> targetGroup = new SimpleObjectProperty<>();
private final ObjectProperty<StoreCategoryWrapper> targetCategory = new SimpleObjectProperty<>();
public ScriptCollectionSourceImportDialog(DataStoreEntryRef<ScriptCollectionSourceStore> source) {
this.source = source;
@@ -98,33 +96,10 @@ public class ScriptCollectionSourceImportDialog {
stack.prefWidth(600);
stack.prefHeight(650);
var storeChoice = new StoreChoiceComp<>(
null,
targetGroup,
ScriptGroupStore.class,
null,
StoreViewState.get().getAllScriptsCategory(),
StoreViewState.get().getAllScriptsCategory()) {
@Override
protected String toName(DataStoreEntry entry) {
if (entry == null) {
return AppI18n.get("selectCategory");
} else {
return super.toName(entry);
}
}
@Override
protected String toGraphic(DataStoreEntry entry) {
if (entry == null) {
return "scriptGroup_icon.svg";
} else {
return super.toGraphic(entry);
}
}
};
storeChoice.hgrow();
storeChoice.maxHeight(100);
var catChoice = new DataStoreCategoryChoiceComp(StoreViewState.get().getAllScriptsCategory(),
StoreViewState.get().getActiveCategory(), targetCategory, false);
catChoice.hgrow();
catChoice.maxHeight(100);
var modal = ModalOverlay.of(
Bindings.createStringBinding(() -> {
@@ -134,19 +109,18 @@ public class ScriptCollectionSourceImportDialog {
null);
modal.addButtonBarComp(refresh);
modal.addButtonBarComp(filterField);
modal.addButtonBarComp(storeChoice);
modal.addButtonBarComp(catChoice);
modal.addButton(ModalButton.ok(() -> {
ThreadHelper.runAsync(() -> {
finish();
});
}))
.augment(button -> button.disableProperty().bind(Bindings.isEmpty(selected).or(targetGroup.isNull())));
.augment(button -> button.disableProperty().bind(Bindings.isEmpty(selected).or(targetCategory.isNull())));
modal.show();
}
private void finish() {
var targetCat = DataStorage.get().getStoreCategory(targetGroup.getValue().get());
StoreViewState.get().selectCategoryIntoViewIfNeeded(StoreViewState.get().getCategoryWrapper(targetCat));
StoreViewState.get().selectCategoryIntoViewIfNeeded(targetCategory.getValue());
var added = new ArrayList<DataStoreEntry>();
for (ScriptCollectionSourceEntry e : selected) {
@@ -159,7 +133,7 @@ public class ScriptCollectionSourceImportDialog {
continue;
}
var store = ScriptStore.builder().textSource(textSource).group(targetGroup.get()).build();
var store = ScriptStore.builder().textSource(textSource).build();
var entry = DataStoreEntry.createNew(name, store);
DataStorage.get().addStoreEntryIfNotPresent(entry);
added.add(entry);
@@ -21,28 +21,6 @@ public class ScriptDataStorageProvider extends DataStorageExtensionProvider {
return;
}
DataStorage.get()
.addStoreEntryIfNotPresent(DataStoreEntry.createNew(
UUID.fromString("a9945ad2-db61-4304-97d7-5dc4330691a7"),
DataStorage.CUSTOM_SCRIPTS_CATEGORY_UUID,
"My scripts",
ScriptGroupStore.builder().build()));
for (PredefinedScriptGroup value : PredefinedScriptGroup.values()) {
ScriptGroupStore store = ScriptGroupStore.builder()
.description(value.getDescription())
.build();
var e = DataStorage.get()
.addStoreEntryIfNotPresent(DataStoreEntry.createNew(
UUID.nameUUIDFromBytes(("a " + value.getName()).getBytes(StandardCharsets.UTF_8)),
DataStorage.PREDEFINED_SCRIPTS_CATEGORY_UUID,
value.getName(),
store));
DataStorage.get().updateEntryStore(e, store);
e.setExpanded(value.isExpanded());
value.setEntry(e.ref());
}
for (PredefinedScriptStore value : PredefinedScriptStore.values()) {
var previous = DataStorage.get().getStoreEntryIfPresent(value.getUuid());
var store = value.getScriptStore().get();
@@ -1,47 +0,0 @@
package io.xpipe.ext.base.script;
import io.xpipe.app.ext.EnabledStoreState;
import io.xpipe.app.ext.GroupStore;
import io.xpipe.app.ext.SelfReferentialStore;
import io.xpipe.app.ext.StatefulDataStore;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntryRef;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.app.util.Validators;
import lombok.EqualsAndHashCode;
import lombok.Singular;
import lombok.ToString;
import lombok.Value;
import lombok.experimental.SuperBuilder;
import lombok.extern.jackson.Jacksonized;
import java.util.LinkedHashSet;
import java.util.List;
@Value
@SuperBuilder
@Jacksonized
@JsonTypeName("scriptGroup")
public class ScriptGroupStore implements GroupStore<ScriptGroupStore>, SelfReferentialStore, StatefulDataStore<EnabledStoreState> {
DataStoreEntryRef<ScriptGroupStore> group;
String description;
@Override
public Class<EnabledStoreState> getStateClass() {
return EnabledStoreState.class;
}
@Override
public void checkComplete() throws Throwable {
if (group != null) {
Validators.isType(group, ScriptGroupStore.class);
}
}
@Override
public DataStoreEntryRef<ScriptGroupStore> getParent() {
return group;
}
}
@@ -1,102 +0,0 @@
package io.xpipe.ext.base.script;
import io.xpipe.app.comp.BaseRegionBuilder;
import io.xpipe.app.ext.*;
import io.xpipe.app.hub.comp.*;
import io.xpipe.app.platform.OptionsBuilder;
import io.xpipe.app.storage.DataStoreCategory;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.util.DocumentationLink;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import lombok.SneakyThrows;
import java.util.List;
public class ScriptGroupStoreProvider implements EnabledParentStoreProvider, DataStoreProvider {
@Override
public DocumentationLink getHelpLink() {
return DocumentationLink.SCRIPTING;
}
@Override
public BaseRegionBuilder<?,?> stateDisplay(StoreEntryWrapper w) {
return new SystemStateComp(new SimpleObjectProperty<>(SystemStateComp.State.SUCCESS));
}
@Override
public DataStoreCreationCategory getCreationCategory() {
return DataStoreCreationCategory.SCRIPT;
}
@Override
public DataStoreUsageCategory getUsageCategory() {
return DataStoreUsageCategory.GROUP;
}
@Override
public DataStoreEntry getDisplayParent(DataStoreEntry store) {
ScriptGroupStore scriptStore = store.getStore().asNeeded();
return scriptStore.getParent() != null ? scriptStore.getParent().get() : null;
}
@SneakyThrows
@Override
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
ScriptGroupStore st = store.getValue().asNeeded();
var group = new SimpleObjectProperty<>(st.getGroup());
var description = new SimpleObjectProperty<>(st.getDescription());
return new OptionsBuilder()
.name("description")
.description("scriptGroupDescriptionDescription")
.addString(description)
.name("scriptGroup")
.description("scriptGroupGroupDescription")
.addComp(
new StoreChoiceComp<>(
entry,
group,
ScriptGroupStore.class,
null,
StoreViewState.get().getAllScriptsCategory()),
group)
.bind(
() -> {
return ScriptGroupStore.builder()
.group(group.get())
.description(description.getValue())
.build();
},
store)
.buildDialog();
}
@Override
public ObservableValue<String> informationString(StoreSection section) {
ScriptGroupStore scriptStore =
section.getWrapper().getEntry().getStore().asNeeded();
return new SimpleStringProperty(scriptStore.getDescription());
}
@Override
public DataStore defaultStore(DataStoreCategory category) {
return ScriptGroupStore.builder().build();
}
@Override
public String getId() {
return "scriptGroup";
}
@Override
public List<Class<?>> getStoreClasses() {
return List.of(ScriptGroupStore.class);
}
}
@@ -1,81 +1,108 @@
package io.xpipe.ext.base.script;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreCategory;
import io.xpipe.app.storage.DataStoreEntryRef;
import lombok.Value;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.*;
import java.util.function.Predicate;
@Value
public class ScriptHierarchy {
DataStoreEntryRef<?> base;
String name;
DataStoreCategory category;
DataStoreEntryRef<ScriptStore> script;
List<ScriptHierarchy> children;
public static ScriptHierarchy buildEnabledHierarchy(Predicate<DataStoreEntryRef<ScriptStore>> include) {
var enabled = ScriptStoreSetup.getEnabledScripts();
var all = new HashSet<DataStoreEntryRef<?>>(ScriptStoreSetup.getEnabledScripts());
var enabled = ScriptStoreSetup.getEnabledScripts().stream().filter(include).toList();
// Add parents
var categories = new HashSet<DataStoreCategory>();
for (DataStoreEntryRef<ScriptStore> ref : enabled) {
DataStoreEntryRef<?> current = ref;
while (true) {
var parent = DataStorage.get().getDefaultDisplayParent(current.get());
if (parent.isPresent()) {
DataStoreEntryRef<ScriptGroupStore> next = parent.get().ref();
all.add(next);
current = next;
} else {
break;
var cat = DataStorage.get().getStoreCategory(ref.get());
var catParents = DataStorage.get().getCategoryParentHierarchy(cat);
categories.addAll(catParents);
}
var hierarchy = new ScriptHierarchy(null, null, null, new ArrayList<>());
while (true) {
var changed = false;
for (DataStoreCategory cat : categories) {
// We don't support the All Scripts root
if (cat.getParentCategory() == null) {
continue;
}
var toAdd = new ScriptHierarchy(cat.getName(), cat, null, new ArrayList<>());
if (cat.getParentCategory().equals(DataStorage.ALL_SCRIPTS_CATEGORY_UUID)) {
if (!hierarchy.getChildren().contains(toAdd)) {
hierarchy.getChildren().add(toAdd);
changed = true;
}
continue;
}
var parentHierarchy = findParent(hierarchy, cat);
if (parentHierarchy.isEmpty()) {
continue;
}
var alreadyAdded = parentHierarchy.get().getChildren().contains(toAdd);
if (alreadyAdded) {
continue;
}
parentHierarchy.get().getChildren().add(toAdd);
changed = true;
}
if (!changed) {
break;
}
}
var top = all.stream()
.filter(ref -> {
var parent = DataStorage.get().getDefaultDisplayParent(ref.get());
return parent.isEmpty();
})
.toList();
for (DataStoreEntryRef<ScriptStore> scriptRef : enabled) {
var scriptCategory = DataStorage.get().getStoreCategory(scriptRef.get());
var catHierarchy = findParent(hierarchy, scriptCategory);
if (catHierarchy.isEmpty()) {
continue;
}
var mapped = top.stream()
.map(ref -> buildHierarchy(ref, check -> {
if (!include.test(check.asNeeded())) {
return false;
}
var childTarget = catHierarchy.get().getChildren().stream()
.filter(child -> child.getCategory().equals(scriptCategory))
.findFirst();
if (childTarget.isEmpty()) {
continue;
}
return all.contains(check);
}))
.map(hierarchy -> condenseHierarchy(hierarchy))
.filter(hierarchy -> hierarchy.show())
.sorted(Comparator.comparing(scriptHierarchy ->
scriptHierarchy.getBase().get().getName().toLowerCase()))
.toList();
return condenseHierarchy(new ScriptHierarchy(null, mapped));
childTarget.get().getChildren().add(new ScriptHierarchy(scriptRef.get().getName(), null, scriptRef, List.of()));
}
return condenseHierarchy(hierarchy);
}
private static ScriptHierarchy buildHierarchy(
DataStoreEntryRef<?> ref, Predicate<DataStoreEntryRef<ScriptStore>> include) {
if (ref.getStore() instanceof ScriptGroupStore) {
var directChildren = DataStorage.get().getStoreChildren(ref.get()).stream()
.filter(entry -> entry.getValidity().isUsable() && entry.getStore() instanceof ScriptStore)
.map(dataStoreEntry -> dataStoreEntry.<ScriptStore>ref())
.toList();
var children = directChildren.stream()
.filter(include)
.map(c -> buildHierarchy(c, include))
.filter(hierarchy -> hierarchy.show())
.sorted(Comparator.comparing(scriptHierarchy ->
scriptHierarchy.getBase().get().getName().toLowerCase()))
.toList();
return new ScriptHierarchy(ref, children);
} else {
return new ScriptHierarchy(ref, List.of());
private static Optional<ScriptHierarchy> findParent(ScriptHierarchy hierarchy, DataStoreCategory category) {
if (category.equals(hierarchy.getCategory())) {
return Optional.of(hierarchy);
}
if (hierarchy.getChildren().stream().anyMatch(child -> category.equals(child.getCategory()))) {
return Optional.of(hierarchy);
}
var children = hierarchy.getChildren();
for (ScriptHierarchy child : children) {
var foundInChild = findParent(child, category);
if (foundInChild.isPresent()) {
return foundInChild;
}
}
return Optional.empty();
}
public static ScriptHierarchy condenseHierarchy(ScriptHierarchy hierarchy) {
@@ -83,9 +110,9 @@ public class ScriptHierarchy {
hierarchy.getChildren().stream().map(c -> condenseHierarchy(c)).toList();
if (children.size() == 1 && !children.getFirst().isLeaf()) {
var nestedChildren = children.getFirst().getChildren();
return new ScriptHierarchy(hierarchy.getBase(), nestedChildren);
return new ScriptHierarchy(children.getFirst().getName(), hierarchy.getCategory(), hierarchy.getScript(), nestedChildren);
} else {
return new ScriptHierarchy(hierarchy.getBase(), children);
return new ScriptHierarchy(hierarchy.getName(), hierarchy.getCategory(), hierarchy.getScript(), children);
}
}
@@ -94,14 +121,14 @@ public class ScriptHierarchy {
}
public boolean isEmptyBranch() {
return (base == null || base.getStore() instanceof ScriptGroupStore) && children.isEmpty();
if (category == null) {
return false;
}
return children.isEmpty();
}
public boolean isLeaf() {
return base != null && base.getStore() instanceof ScriptStore && children.isEmpty();
}
public DataStoreEntryRef<ScriptStore> getLeafBase() {
return base.asNeeded();
return script != null;
}
}
@@ -29,7 +29,6 @@ import java.util.SequencedCollection;
@JsonTypeName("script")
public class ScriptStore implements SelfReferentialStore, StatefulDataStore<EnabledStoreState> {
DataStoreEntryRef<ScriptGroupStore> group;
@Singular
List<DataStoreEntryRef<ScriptStore>> scripts;
String description;
@@ -99,8 +98,6 @@ public class ScriptStore implements SelfReferentialStore, StatefulDataStore<Enab
public void checkComplete() throws Throwable {
Validators.nonNull(textSource);
textSource.checkComplete();
Validators.nonNull(group);
Validators.isType(group, ScriptGroupStore.class);
if (!initScript && !shellScript && !fileScript && !runnableScript) {
throw new ValidationException(AppI18n.get("valueMustNotBeEmpty"));
}
@@ -6,11 +6,13 @@ import io.xpipe.app.comp.base.ListSelectorComp;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.*;
import io.xpipe.app.hub.comp.*;
import io.xpipe.app.platform.BindingsHelper;
import io.xpipe.app.platform.OptionsBuilder;
import io.xpipe.app.platform.OptionsChoiceBuilder;
import io.xpipe.app.platform.Validator;
import io.xpipe.app.process.OsFileSystem;
import io.xpipe.app.process.ShellDialects;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreCategory;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.util.*;
@@ -28,7 +30,29 @@ import java.util.Locale;
import java.util.function.Function;
import java.util.stream.Stream;
public class ScriptStoreProvider implements EnabledParentStoreProvider, DataStoreProvider {
public class ScriptStoreProvider implements DataStoreProvider {
@Override
public StoreEntryComp customEntryComp(StoreSection sec, boolean preferLarge) {
if (sec.getWrapper().getValidity().getValue() == DataStoreEntry.Validity.LOAD_FAILED) {
return StoreEntryComp.create(sec, null, preferLarge);
}
EnabledStoreState initialState = sec.getWrapper().getEntry().getStorePersistentState();
var enabled = new SimpleBooleanProperty(initialState.isEnabled());
sec.getWrapper().getPersistentState().subscribe((newValue) -> {
EnabledStoreState s = sec.getWrapper().getEntry().getStorePersistentState();
enabled.set(s.isEnabled());
});
var toggle = StoreToggleComp.<StatefulDataStore<EnabledStoreState>>enableToggle(
null, sec, enabled, (s, aBoolean) -> {
var state = s.getState().toBuilder().enabled(aBoolean).build();
s.setState(state);
});
return StoreEntryComp.create(sec, toggle, preferLarge);
}
@Override
public DocumentationLink getHelpLink() {
@@ -60,19 +84,12 @@ public class ScriptStoreProvider implements EnabledParentStoreProvider, DataStor
return DataStoreCreationCategory.SCRIPT;
}
@Override
public DataStoreEntry getDisplayParent(DataStoreEntry store) {
ScriptStore st = store.getStore().asNeeded();
return st.getGroup().get();
}
@SneakyThrows
@Override
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
ScriptStore st = store.getValue().asNeeded();
var textSource = new SimpleObjectProperty<>(st.getTextSource());
var group = new SimpleObjectProperty<>(st.getGroup());
var others = new SimpleListProperty<>(FXCollections.observableArrayList(new ArrayList<>(st.getEffectiveScripts())));
var textSourceChoice = OptionsChoiceBuilder.builder().property(textSource).available(ScriptTextSource.getClasses()).build();
@@ -137,23 +154,10 @@ public class ScriptStoreProvider implements EnabledParentStoreProvider, DataStor
scriptStore -> !scriptStore.get().equals(entry) && !others.contains(scriptStore),
StoreViewState.get().getAllScriptsCategory()),
others)
.name("scriptGroup")
.description("scriptGroupDescription")
.documentationLink(DocumentationLink.SCRIPTING_GROUPS)
.addComp(
new StoreChoiceComp<>(
null,
group,
ScriptGroupStore.class,
null,
StoreViewState.get().getAllScriptsCategory()),
group)
.nonNull()
.bind(
() -> {
return ScriptStore.builder()
.textSource(textSource.get())
.group(group.get())
.scripts(new ArrayList<>(others.get()))
.description(st.getDescription())
.initScript(selectedExecTypes.contains(0))
@@ -4,6 +4,8 @@ import io.xpipe.app.ext.StatefulDataStore;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.process.*;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreCategory;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.core.FilePath;
import lombok.SneakyThrows;
@@ -159,31 +161,16 @@ public class ScriptStoreSetup {
return targetDir;
}
public static Set<DataStoreEntryRef<ScriptStore>> getEnabledScripts() {
var l = new HashSet<DataStoreEntryRef<ScriptStore>>();
DataStorage.get().getStoreEntries().stream()
public static List<DataStoreEntryRef<ScriptStore>> getEnabledScripts() {
var l = DataStorage.get().getStoreEntries().stream()
.filter(dataStoreEntry -> dataStoreEntry.getValidity().isUsable()
&& dataStoreEntry.getStore() instanceof ScriptGroupStore g
&& g.getParent() == null)
.forEach(e -> addGroupChildren(e.ref(), l));
&& dataStoreEntry.getStore() instanceof ScriptStore ss
&& ss.getState().isEnabled())
.<DataStoreEntryRef<ScriptStore>>map(DataStoreEntry::ref)
.toList();
return l;
}
private static void addGroupChildren(DataStoreEntryRef<ScriptGroupStore> group, Set<DataStoreEntryRef<ScriptStore>> l) {
var children = DataStorage.get().getStoreChildren(group.get());
if (group.getStore().getState().isEnabled()) {
children.stream()
.filter(dataStoreEntry -> dataStoreEntry.getValidity().isUsable()
&& dataStoreEntry.getStore() instanceof ScriptStore)
.forEach(e -> l.add(e.ref()));
}
children.stream()
.filter(dataStoreEntry -> dataStoreEntry.getValidity().isUsable()
&& dataStoreEntry.getStore() instanceof ScriptGroupStore)
.forEach(e -> addGroupChildren(e.ref(), l));
}
public static List<DataStoreEntryRef<ScriptStore>> flatten(Collection<DataStoreEntryRef<ScriptStore>> scripts) {
var seen = new LinkedHashSet<DataStoreEntryRef<ScriptStore>>();
scripts.stream()
+1 -2
View File
@@ -62,8 +62,7 @@ open module io.xpipe.ext.base {
LocalIdentityStoreProvider,
SyncedIdentityStoreProvider,
PasswordManagerIdentityStoreProvider,
AbstractHostStoreProvider,
ScriptGroupStoreProvider;
AbstractHostStoreProvider;
provides DataStorageExtensionProvider with
ScriptDataStorageProvider;
}