From ed87bbd0c5a3a7d2677869dfb3f65ef6344c99dc Mon Sep 17 00:00:00 2001 From: crschnick Date: Wed, 9 Apr 2025 18:17:26 +0000 Subject: [PATCH] Rework [stage] --- .../file/BrowserConnectionListComp.java | 2 + .../xpipe/app/comp/base/SideMenuBarComp.java | 26 ++-- .../xpipe/app/comp/store/StoreChoiceComp.java | 2 + .../comp/store/StoreEntryBatchSelectComp.java | 2 - .../store/StoreEntryListStatusBarComp.java | 44 +++--- .../io/xpipe/app/comp/store/StoreSection.java | 14 +- .../xpipe/app/comp/store/StoreViewState.java | 18 ++- .../java/io/xpipe/app/ext/ActionProvider.java | 2 +- .../io/xpipe/app/ext/ContainerStoreState.java | 16 ++ .../java/io/xpipe/app/ext/ScanProvider.java | 4 +- .../xpipe/app/issue/ErrorHandlerDialog.java | 4 +- .../java/io/xpipe/app/util/CommandDialog.java | 93 +++++++---- .../java/io/xpipe/app/util/ScanDialog.java | 11 +- .../io/xpipe/app/util/ScanDialogAction.java | 10 +- .../io/xpipe/app/util/ScanDialogBase.java | 10 +- .../xpipe/app/util/ScanMultiDialogComp.java | 11 +- .../xpipe/app/util/ScanSingleDialogComp.java | 2 +- ...StoreFormat.java => StoreStateFormat.java} | 14 +- .../io/xpipe/app/resources/style/style.css | 5 + .../ext/base/action/RunScriptActionMenu.java | 145 ++++++++++++------ .../ext/base/action/ScanStoreAction.java | 4 +- .../service/AbstractServiceStoreProvider.java | 4 +- .../service/ServiceControlStoreProvider.java | 4 +- .../base/service/ServiceRefreshAction.java | 4 +- .../ext/base/store/ShellStoreProvider.java | 4 +- .../ext/base/store/StorePauseAction.java | 4 +- .../ext/base/store/StoreRestartAction.java | 4 +- .../ext/base/store/StoreStartAction.java | 4 +- .../xpipe/ext/base/store/StoreStopAction.java | 4 +- .../incus/IncusContainerStoreProvider.java | 2 +- .../ext/system/incus/IncusScanProvider.java | 2 +- .../system/lxd/LxdContainerStoreProvider.java | 4 +- .../xpipe/ext/system/lxd/LxdScanProvider.java | 2 +- .../podman/PodmanContainerStoreProvider.java | 4 +- .../ext/system/podman/PodmanScanProvider.java | 2 +- version | 2 +- 36 files changed, 316 insertions(+), 173 deletions(-) rename app/src/main/java/io/xpipe/app/util/{ShellStoreFormat.java => StoreStateFormat.java} (91%) diff --git a/app/src/main/java/io/xpipe/app/browser/file/BrowserConnectionListComp.java b/app/src/main/java/io/xpipe/app/browser/file/BrowserConnectionListComp.java index 4e2e56fff..d1d541861 100644 --- a/app/src/main/java/io/xpipe/app/browser/file/BrowserConnectionListComp.java +++ b/app/src/main/java/io/xpipe/app/browser/file/BrowserConnectionListComp.java @@ -16,6 +16,7 @@ import javafx.scene.control.Button; import javafx.scene.layout.Region; import java.util.HashSet; +import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Predicate; @@ -67,6 +68,7 @@ public final class BrowserConnectionListComp extends SimpleComp { var section = new StoreSectionMiniComp( StoreSection.createTopLevel( StoreViewState.get().getAllEntries(), + Set.of(), this::filter, filter, category, 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 8e02424f2..f6f31e20e 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 @@ -82,18 +82,20 @@ public class SideMenuBarComp extends Comp> { var queueButtons = new VBox(); queueEntries.addListener((ListChangeListener) c -> { - queueButtons.getChildren().clear(); - for (int i = c.getList().size() - 1; i >= 0; i--) { - var item = c.getList().get(i); - var b = new IconButtonComp(item.getIcon(), () -> { - item.getAction().run(); - queueEntries.remove(item); - }); - b.tooltip(item.getName()); - b.accessibleText(item.getName()); - var stack = createStyle(null, b); - queueButtons.getChildren().add(stack.createRegion()); - } + PlatformThread.runLaterIfNeeded(() -> { + queueButtons.getChildren().clear(); + for (int i = c.getList().size() - 1; i >= 0; i--) { + var item = c.getList().get(i); + var b = new IconButtonComp(item.getIcon(), () -> { + item.getAction().run(); + queueEntries.remove(item); + }); + b.tooltip(item.getName()); + b.accessibleText(item.getName()); + var stack = createStyle(null, b); + queueButtons.getChildren().add(stack.createRegion()); + } + }); }); vbox.getChildren().add(queueButtons); diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreChoiceComp.java b/app/src/main/java/io/xpipe/app/comp/store/StoreChoiceComp.java index 51f5368cd..3f31ef163 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreChoiceComp.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreChoiceComp.java @@ -35,6 +35,7 @@ import lombok.RequiredArgsConstructor; import org.kordamp.ikonli.javafx.FontIcon; import java.util.List; +import java.util.Set; import java.util.function.Predicate; @RequiredArgsConstructor @@ -103,6 +104,7 @@ public class StoreChoiceComp extends SimpleComp { var section = new StoreSectionMiniComp( StoreSection.createTopLevel( StoreViewState.get().getAllEntries(), + Set.of(), applicable, filterText, selectedCategory, diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryBatchSelectComp.java b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryBatchSelectComp.java index 2fad1facb..f6c00c673 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryBatchSelectComp.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryBatchSelectComp.java @@ -39,8 +39,6 @@ public class StoreEntryBatchSelectComp extends SimpleComp { section.getShownChildren().getList().addListener((ListChangeListener) c -> { if (cb.isSelected()) { StoreViewState.get().selectBatchMode(section); - } else { - StoreViewState.get().unselectBatchMode(section); } }); diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryListStatusBarComp.java b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryListStatusBarComp.java index 2d18353d3..eb6ed150f 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryListStatusBarComp.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryListStatusBarComp.java @@ -11,6 +11,8 @@ import io.xpipe.app.util.*; import io.xpipe.core.store.DataStore; import javafx.beans.binding.Bindings; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; @@ -46,7 +48,8 @@ public class StoreEntryListStatusBarComp extends SimpleComp { l.apply(struc -> { struc.get().setAlignment(Pos.CENTER); }); - var actions = new ToolbarComp(createActions()); + var busy = new SimpleBooleanProperty(); + var actions = new ToolbarComp(createActions(busy)); var close = new IconButtonComp("mdi2c-close", () -> { StoreViewState.get().getBatchMode().setValue(false); }); @@ -66,10 +69,11 @@ public class StoreEntryListStatusBarComp extends SimpleComp { bar.prefHeight(40); bar.styleClass("bar"); bar.styleClass("store-entry-list-status-bar"); + bar.disable(busy); return bar.createRegion(); } - private ObservableList> createActions() { + private ObservableList> createActions(BooleanProperty busy) { var l = new DerivedObservableList(FXCollections.observableArrayList(), true); StoreViewState.get().getEffectiveBatchModeSelection().getList().addListener((ListChangeListener< ? super StoreEntryWrapper>) @@ -77,7 +81,7 @@ public class StoreEntryListStatusBarComp extends SimpleComp { l.setContent(getCompatibleActionProviders()); }); return l.>mapped(actionProvider -> { - return buildButton(actionProvider); + return buildButton(actionProvider, busy); }) .getList(); } @@ -115,32 +119,30 @@ public class StoreEntryListStatusBarComp extends SimpleComp { } @SuppressWarnings("unchecked") - private Comp buildButton(ActionProvider p) { + private Comp buildButton(ActionProvider p, BooleanProperty busy) { ActionProvider.BatchDataStoreCallSite s = (ActionProvider.BatchDataStoreCallSite) p.getBatchDataStoreCallSite(); if (s == null) { return Comp.empty(); } - List> childrenRefs = - StoreViewState.get().getEffectiveBatchModeSelection().getList().stream() - .map(storeEntryWrapper -> storeEntryWrapper.getEntry().ref()) - .toList(); - var batchActions = s.getChildren(childrenRefs); + var childrenRefs = StoreViewState.get().getEffectiveBatchModeSelection() + .mapped(storeEntryWrapper -> storeEntryWrapper.getEntry().ref()); + var batchActions = s.getChildren(childrenRefs.getList()); var button = new ButtonComp( - s.getName(), new SimpleObjectProperty<>(new LabelGraphic.IconGraphic(s.getIcon())), () -> { + s.getName(), new SimpleObjectProperty<>(s.getIcon()), () -> { if (batchActions.size() > 0) { return; } - runActions(s); + runActions(s, busy); }); if (batchActions.size() > 0) { button.apply(new ContextMenuAugment<>( mouseEvent -> mouseEvent.getButton() == MouseButton.PRIMARY, keyEvent -> false, () -> { var cm = ContextMenuHelper.create(); - s.getChildren(childrenRefs).forEach(childProvider -> { - var menu = buildMenuItemForAction(childrenRefs, childProvider); + s.getChildren(childrenRefs.getList()).forEach(childProvider -> { + var menu = buildMenuItemForAction(childrenRefs.getList(), childProvider, busy); cm.getItems().add(menu); }); return cm; @@ -150,7 +152,7 @@ public class StoreEntryListStatusBarComp extends SimpleComp { } @SuppressWarnings("unchecked") - private MenuItem buildMenuItemForAction(List> batch, ActionProvider a) { + private MenuItem buildMenuItemForAction(List> batch, ActionProvider a, BooleanProperty busy) { ActionProvider.BatchDataStoreCallSite s = (ActionProvider.BatchDataStoreCallSite) a.getBatchDataStoreCallSite(); var name = s.getName(); @@ -159,19 +161,19 @@ public class StoreEntryListStatusBarComp extends SimpleComp { if (children.size() > 0) { var menu = new Menu(); menu.textProperty().bind(name); - menu.setGraphic(new LabelGraphic.IconGraphic(icon).createGraphicNode()); + menu.setGraphic(icon.createGraphicNode()); var items = children.stream() .filter(actionProvider -> actionProvider.getBatchDataStoreCallSite() != null) - .map(c -> buildMenuItemForAction(batch, c)) + .map(c -> buildMenuItemForAction(batch, c, busy)) .toList(); menu.getItems().addAll(items); return menu; } else { var item = new MenuItem(); item.textProperty().bind(name); - item.setGraphic(new LabelGraphic.IconGraphic(icon).createGraphicNode()); + item.setGraphic(icon.createGraphicNode()); item.setOnAction(event -> { - runActions(s); + runActions(s, busy); event.consume(); if (event.getTarget() instanceof Menu m) { m.getParentPopup().hide(); @@ -182,14 +184,16 @@ public class StoreEntryListStatusBarComp extends SimpleComp { } @SuppressWarnings("unchecked") - private void runActions(ActionProvider.BatchDataStoreCallSite s) { + private void runActions(ActionProvider.BatchDataStoreCallSite s, BooleanProperty busy) { ThreadHelper.runFailableAsync(() -> { var l = new ArrayList<>( StoreViewState.get().getEffectiveBatchModeSelection().getList()); var mapped = l.stream().map(w -> w.getEntry().ref()).toList(); var action = ((ActionProvider.BatchDataStoreCallSite) s).createAction(mapped); if (action != null) { - action.execute(); + BooleanScope.executeExclusive(busy, () -> { + action.execute(); + }); } }); } diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreSection.java b/app/src/main/java/io/xpipe/app/comp/store/StoreSection.java index 7b107989a..2b669798a 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreSection.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreSection.java @@ -19,6 +19,7 @@ import lombok.Getter; import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Set; import java.util.function.Predicate; import java.util.function.ToIntFunction; @@ -107,6 +108,7 @@ public class StoreSection { public static StoreSection createTopLevel( DerivedObservableList all, + Set selected, Predicate entryFilter, ObservableValue filterString, ObservableValue category, @@ -124,6 +126,7 @@ public class StoreSection { storeEntryWrapper, 1, all, + selected, entryFilter, filterString, category, @@ -152,6 +155,7 @@ public class StoreSection { StoreEntryWrapper e, int depth, DerivedObservableList all, + Set selected, Predicate entryFilter, ObservableValue filterString, ObservableValue category, @@ -188,21 +192,23 @@ public class StoreSection { var l = new ArrayList<>(parents); l.add(e); var cached = allChildren.mapped(c -> create( - l, c, depth + 1, all, entryFilter, filterString, category, visibilityObservable, updateObservable)); + l, c, depth + 1, all, selected, entryFilter, filterString, category, visibilityObservable, updateObservable)); var ordered = sorted(cached, category, updateObservable); var filtered = ordered.filtered( section -> { + var isBatchSelected = selected.contains(section.getWrapper()); + var matchesFilter = filterString == null || section.matchesFilter(filterString.getValue()) || l.stream().anyMatch(p -> p.matchesFilter(filterString.getValue())); - if (!matchesFilter) { + if (!isBatchSelected && !matchesFilter) { return false; } var hasFilter = filterString != null && filterString.getValue() != null && filterString.getValue().length() > 0; - if (!hasFilter) { + if (!isBatchSelected && !hasFilter) { var showProvider = true; try { showProvider = section.getWrapper() @@ -217,7 +223,7 @@ public class StoreSection { } var matchesSelector = section.anyMatches(entryFilter); - if (!matchesSelector) { + if (!isBatchSelected && !matchesSelector) { return false; } diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreViewState.java b/app/src/main/java/io/xpipe/app/comp/store/StoreViewState.java index 1677b5f57..752c2f001 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreViewState.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreViewState.java @@ -68,7 +68,7 @@ public class StoreViewState { } return true; - }); + }, entriesListVisibilityObservable, entriesListUpdateObservable); @Getter private StoreSection currentTopLevelSection; @@ -88,7 +88,7 @@ public class StoreViewState { INSTANCE.initSections(); INSTANCE.updateContent(); INSTANCE.initFilterListener(); - INSTANCE.initBatchListener(); + INSTANCE.initBatchListeners(); INSTANCE.initialized = true; } @@ -152,9 +152,17 @@ public class StoreViewState { } private void initSections() { + // Faster selection check with a hash set + var set = new HashSet<>(batchModeSelection.getList()); + batchModeSelection.getList().addListener((ListChangeListener) c -> { + set.clear(); + set.addAll(c.getList()); + }); + try { currentTopLevelSection = StoreSection.createTopLevel( allEntries, + set, storeEntryWrapper -> true, filter, activeCategory, @@ -186,10 +194,14 @@ public class StoreViewState { }); } - private void initBatchListener() { + private void initBatchListeners() { allEntries.getList().addListener((ListChangeListener) c -> { batchModeSelection.getList().retainAll(c.getList()); }); + + batchMode.addListener((observable, oldValue, newValue) -> { + batchModeSelection.getList().clear(); + }); } private void initContent() { diff --git a/app/src/main/java/io/xpipe/app/ext/ActionProvider.java b/app/src/main/java/io/xpipe/app/ext/ActionProvider.java index e4172c3e3..59d266468 100644 --- a/app/src/main/java/io/xpipe/app/ext/ActionProvider.java +++ b/app/src/main/java/io/xpipe/app/ext/ActionProvider.java @@ -203,7 +203,7 @@ public interface ActionProvider { ObservableValue getName(); - String getIcon(); + LabelGraphic getIcon(); Class getApplicableClass(); diff --git a/app/src/main/java/io/xpipe/app/ext/ContainerStoreState.java b/app/src/main/java/io/xpipe/app/ext/ContainerStoreState.java index 47a7fba3e..a739574d6 100644 --- a/app/src/main/java/io/xpipe/app/ext/ContainerStoreState.java +++ b/app/src/main/java/io/xpipe/app/ext/ContainerStoreState.java @@ -21,6 +21,22 @@ public class ContainerStoreState extends ShellStoreState { String containerState; Boolean shellMissing; + public boolean isExited() { + if (containerState == null) { + return false; + } + + return containerState.toLowerCase().contains("exited"); + } + + public boolean isRunning() { + if (containerState == null) { + return false; + } + + return containerState.toLowerCase().contains("running") || containerState.toLowerCase().contains("up"); + } + @Override public DataStoreState mergeCopy(DataStoreState newer) { var n = (ContainerStoreState) newer; diff --git a/app/src/main/java/io/xpipe/app/ext/ScanProvider.java b/app/src/main/java/io/xpipe/app/ext/ScanProvider.java index 23c569577..3e26d5cd6 100644 --- a/app/src/main/java/io/xpipe/app/ext/ScanProvider.java +++ b/app/src/main/java/io/xpipe/app/ext/ScanProvider.java @@ -31,13 +31,11 @@ public abstract class ScanProvider { public class ScanOpportunity { String nameKey; boolean disabled; - boolean defaultSelected; String licenseFeatureId; - public ScanOpportunity(String nameKey, boolean disabled, boolean defaultSelected) { + public ScanOpportunity(String nameKey, boolean disabled) { this.nameKey = nameKey; this.disabled = disabled; - this.defaultSelected = defaultSelected; this.licenseFeatureId = null; } diff --git a/app/src/main/java/io/xpipe/app/issue/ErrorHandlerDialog.java b/app/src/main/java/io/xpipe/app/issue/ErrorHandlerDialog.java index acd4649e8..e9663b285 100644 --- a/app/src/main/java/io/xpipe/app/issue/ErrorHandlerDialog.java +++ b/app/src/main/java/io/xpipe/app/issue/ErrorHandlerDialog.java @@ -46,7 +46,7 @@ public class ErrorHandlerDialog { var comp = new ErrorHandlerComp(event, () -> { AppDialog.closeDialog(modal.get()); }); - comp.prefWidth(500); + comp.prefWidth(event.getThrowable() != null ? 600 : 500); var headerId = event.isTerminal() ? "terminalErrorOccured" : "errorOccured"; var errorModal = ModalOverlay.of(headerId, comp, new LabelGraphic.NodeGraphic(() -> { var graphic = new FontIcon("mdomz-warning"); @@ -58,7 +58,7 @@ public class ErrorHandlerDialog { "stackTrace", () -> { var content = - new ErrorDetailsComp(event).prefWidth(600).prefHeight(750); + new ErrorDetailsComp(event).prefWidth(650).prefHeight(750); var detailsModal = ModalOverlay.of("errorDetails", content); detailsModal.show(); }, diff --git a/app/src/main/java/io/xpipe/app/util/CommandDialog.java b/app/src/main/java/io/xpipe/app/util/CommandDialog.java index 67ae55ce5..5b1eb0fa6 100644 --- a/app/src/main/java/io/xpipe/app/util/CommandDialog.java +++ b/app/src/main/java/io/xpipe/app/util/CommandDialog.java @@ -10,61 +10,86 @@ import javafx.scene.layout.StackPane; import org.apache.commons.lang3.exception.ExceptionUtils; +import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; public class CommandDialog { - public static void runAsyncAndShow(CommandControl cmd) { + public static void runAsyncAndShow(Map cmds) { ThreadHelper.runAsync(() -> { - run(cmd); + StringBuilder acc = new StringBuilder(); + for (var e : cmds.entrySet()) { + String out; + try { + out = e.getValue().readStdoutOrThrow(); + out = formatOutput(out); + } catch (ProcessOutputException ex) { + out = ex.getMessage(); + } catch (Throwable t) { + out = ExceptionUtils.getStackTrace(t); + } + + acc.append(e.getKey()).append(" (exit code ").append(e.getValue().getExitCode()).append("):\n").append(out).append("\n\n"); + } + show(acc.toString()); }); } - private static void run(CommandControl cmd) { - String out; - try { - out = cmd.readStdoutOrThrow(); - if (out.isEmpty()) { - out = ""; + public static void runAsyncAndShow(CommandControl cmd) { + ThreadHelper.runAsync(() -> { + String out; + try { + out = cmd.readStdoutOrThrow(); + out = formatOutput(out); + } catch (ProcessOutputException e) { + out = e.getMessage(); + } catch (Throwable t) { + out = ExceptionUtils.getStackTrace(t); } + show(out); + }); + } - if (out.length() > 10000) { - var counter = new AtomicInteger(); - var start = out.lines() - .filter(s -> { - counter.incrementAndGet(); - return true; - }) - .limit(100) - .collect(Collectors.joining("\n")); - var notShownLines = counter.get() - 100; - if (notShownLines > 0) { - out = start + "\n\n... " + notShownLines + " more lines"; - } else { - out = start; - } - } - - } catch (ProcessOutputException e) { - out = e.getMessage(); - } catch (Throwable t) { - out = ExceptionUtils.getStackTrace(t); - } - - String finalOut = out; + private static void show(String out) { var modal = ModalOverlay.of( "commandOutput", Comp.of(() -> { - var text = new TextArea(finalOut); + var text = new TextArea(out); text.setWrapText(true); text.setEditable(false); text.setPrefRowCount( - Math.max(8, (int) finalOut.lines().count())); + Math.max(8, (int) out.lines().count())); var sp = new StackPane(text); return sp; }) .prefWidth(650)); modal.show(); } + + private static String formatOutput(String out) { + if (out.isEmpty()) { + out = ""; + } + + if (out.length() > 10000) { + var counter = new AtomicInteger(); + var start = out.lines() + .filter(s -> { + counter.incrementAndGet(); + return true; + }) + .limit(100) + .collect(Collectors.joining("\n")); + var notShownLines = counter.get() - 100; + if (notShownLines > 0) { + out = start + "\n\n... " + notShownLines + " more lines"; + } else { + out = start; + } + } + + return out; + } } diff --git a/app/src/main/java/io/xpipe/app/util/ScanDialog.java b/app/src/main/java/io/xpipe/app/util/ScanDialog.java index 4e303240a..fe403dd82 100644 --- a/app/src/main/java/io/xpipe/app/util/ScanDialog.java +++ b/app/src/main/java/io/xpipe/app/util/ScanDialog.java @@ -2,6 +2,8 @@ package io.xpipe.app.util; import io.xpipe.app.comp.base.ModalButton; import io.xpipe.app.comp.base.ModalOverlay; +import io.xpipe.app.core.AppI18n; +import io.xpipe.app.core.AppLayoutModel; import io.xpipe.app.ext.ShellStore; import io.xpipe.app.storage.DataStoreEntry; import io.xpipe.app.storage.DataStoreEntryRef; @@ -41,10 +43,17 @@ public class ScanDialog { public static void showMulti(List> entries, ScanDialogAction action) { var comp = new ScanMultiDialogComp(entries, action); var modal = ModalOverlay.of("scanAlertTitle", comp); + var queueEntry = new AppLayoutModel.QueueEntry(AppI18n.observable("scanConnections"), new LabelGraphic.IconGraphic("mdi2l-layers-plus"), () -> {}); var button = new ModalButton( "ok", () -> { - comp.finish(); + modal.hide(); + AppLayoutModel.get().getQueueEntries().add(queueEntry); + ThreadHelper.runAsync(() -> { + comp.finish(); + modal.hide(); + AppLayoutModel.get().getQueueEntries().remove(queueEntry); + }); }, false, true); diff --git a/app/src/main/java/io/xpipe/app/util/ScanDialogAction.java b/app/src/main/java/io/xpipe/app/util/ScanDialogAction.java index 54f097810..92ebb7b73 100644 --- a/app/src/main/java/io/xpipe/app/util/ScanDialogAction.java +++ b/app/src/main/java/io/xpipe/app/util/ScanDialogAction.java @@ -38,10 +38,16 @@ public interface ScanDialogAction { sc.start(); ScanProvider.ScanOpportunity operation = scanProvider.create(entry, sc); if (operation != null) { - if (!operation.isDisabled() && operation.isDefaultSelected()) { + if (!operation.isDisabled()) { + selected.removeIf(o -> o.getProvider().equals(operation.getProvider()) && o.isDisabled()); + all.removeIf(o -> o.getProvider().equals(operation.getProvider()) && o.isDisabled()); + } + if (!operation.isDisabled() && selected.stream().noneMatch(o -> o.getProvider().equals(operation.getProvider()))) { selected.add(operation); } - all.add(operation); + if (!all.contains(operation) && all.stream().noneMatch(o -> o.getProvider().equals(operation.getProvider()))) { + all.add(operation); + } } } catch (Exception ex) { ErrorEvent.fromThrowable(ex).handle(); diff --git a/app/src/main/java/io/xpipe/app/util/ScanDialogBase.java b/app/src/main/java/io/xpipe/app/util/ScanDialogBase.java index d1ddfec45..afeb83534 100644 --- a/app/src/main/java/io/xpipe/app/util/ScanDialogBase.java +++ b/app/src/main/java/io/xpipe/app/util/ScanDialogBase.java @@ -71,6 +71,14 @@ public class ScanDialogBase { // Previous scan operation could have exited the shell var sc = entry.getStore().getOrStartSession(); + // Multi-selection compat check + if (entries.size() > 1) { + var supported = a.getProvider().create(entry.get(), sc); + if (supported == null || supported.isDisabled()) { + continue; + } + } + try { a.getProvider().scan(entry.get(), sc); } catch (Throwable ex) { @@ -109,7 +117,7 @@ public class ScanDialogBase { }); } - public Comp createContent() { + public Comp createComp() { StackPane stackPane = new StackPane(); stackPane.getStyleClass().add("scan-list"); VBox.setVgrow(stackPane, ALWAYS); diff --git a/app/src/main/java/io/xpipe/app/util/ScanMultiDialogComp.java b/app/src/main/java/io/xpipe/app/util/ScanMultiDialogComp.java index 9eed38e19..ced213bf6 100644 --- a/app/src/main/java/io/xpipe/app/util/ScanMultiDialogComp.java +++ b/app/src/main/java/io/xpipe/app/util/ScanMultiDialogComp.java @@ -1,7 +1,10 @@ package io.xpipe.app.util; import io.xpipe.app.comp.base.ModalOverlayContentComp; +import io.xpipe.app.core.AppI18n; +import io.xpipe.app.core.AppLayoutModel; import io.xpipe.app.ext.ShellStore; +import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.storage.DataStoreEntryRef; import javafx.beans.property.BooleanProperty; @@ -33,9 +36,11 @@ class ScanMultiDialogComp extends ModalOverlayContentComp { } void finish() { - ThreadHelper.runFailableAsync(() -> { + try { base.finish(); - }); + } catch (Exception e) { + ErrorEvent.fromThrowable(e).handle(); + } } BooleanProperty getBusy() { @@ -44,7 +49,7 @@ class ScanMultiDialogComp extends ModalOverlayContentComp { @Override protected Region createSimple() { - var list = base.createContent(); + var list = base.createComp(); var b = new OptionsBuilder() .name("scanAlertHeader") .description("scanAlertHeaderDescription") diff --git a/app/src/main/java/io/xpipe/app/util/ScanSingleDialogComp.java b/app/src/main/java/io/xpipe/app/util/ScanSingleDialogComp.java index d62af3764..506123530 100644 --- a/app/src/main/java/io/xpipe/app/util/ScanSingleDialogComp.java +++ b/app/src/main/java/io/xpipe/app/util/ScanSingleDialogComp.java @@ -57,7 +57,7 @@ class ScanSingleDialogComp extends ModalOverlayContentComp { @Override protected Region createSimple() { - var list = base.createContent(); + var list = base.createComp(); var b = new OptionsBuilder() .name("scanAlertChoiceHeader") .description("scanAlertChoiceHeaderDescription") diff --git a/app/src/main/java/io/xpipe/app/util/ShellStoreFormat.java b/app/src/main/java/io/xpipe/app/util/StoreStateFormat.java similarity index 91% rename from app/src/main/java/io/xpipe/app/util/ShellStoreFormat.java rename to app/src/main/java/io/xpipe/app/util/StoreStateFormat.java index 7b698fc02..06ee77cb7 100644 --- a/app/src/main/java/io/xpipe/app/util/ShellStoreFormat.java +++ b/app/src/main/java/io/xpipe/app/util/StoreStateFormat.java @@ -18,7 +18,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; @Value -public class ShellStoreFormat { +public class StoreStateFormat { public static ObservableValue shellEnvironment(StoreSection section, boolean includeOsName) { return Bindings.createStringBinding( @@ -28,7 +28,7 @@ public class ShellStoreFormat { var def = Boolean.TRUE.equals(s.getSetDefault()) ? AppI18n.get("default") : null; var name = DataStoreFormatter.join( (includeOsName ? formattedOsName(s.getOsName()) : null), s.getShellName()); - return new ShellStoreFormat(null, name, def).format(); + return new StoreStateFormat(null, name, def).format(); }, AppI18n.activeLanguage(), section.getWrapper().getPersistentState()); @@ -43,7 +43,7 @@ public class ShellStoreFormat { if (s.getShellDialect() != null && !s.getShellDialect().getDumbMode().supportsAnyPossibleInteraction()) { if (s.getOsName() != null) { - return new ShellStoreFormat( + return new StoreStateFormat( LicenseProvider.get().checkOsName(s.getOsName()), formattedOsName(s.getOsName()), info) @@ -51,10 +51,10 @@ public class ShellStoreFormat { } if (s.getShellDialect().equals(ShellDialects.NO_INTERACTION)) { - return new ShellStoreFormat(null, null, info).format(); + return new StoreStateFormat(null, null, info).format(); } - return new ShellStoreFormat( + return new StoreStateFormat( LicenseProvider.get() .getFeature(s.getShellDialect().getLicenseFeatureId()), s.getShellDialect().getDisplayName(), @@ -66,7 +66,7 @@ public class ShellStoreFormat { Stream.of(s.getTtyState() != null && s.getTtyState() != ShellTtyState.NONE ? "TTY" : null), info != null ? Arrays.stream(info) : Stream.of()) .toArray(String[]::new); - return new ShellStoreFormat( + return new StoreStateFormat( LicenseProvider.get().checkOsName(s.getOsName()), formattedOsName(s.getOsName()), joined) .format(); }); @@ -76,7 +76,7 @@ public class ShellStoreFormat { String name; String[] states; - public ShellStoreFormat(LicensedFeature licensedFeature, String name, String... states) { + public StoreStateFormat(LicensedFeature licensedFeature, String name, String... states) { this.licensedFeature = licensedFeature; this.name = name; this.states = states; diff --git a/app/src/main/resources/io/xpipe/app/resources/style/style.css b/app/src/main/resources/io/xpipe/app/resources/style/style.css index fab4fbcb6..32beacd6f 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/style.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/style.css @@ -125,6 +125,11 @@ -fx-border-color: -color-fg-subtle; } +.root.nord .icon-button-comp.batch-mode-button { + -fx-border-radius: 0; + -fx-background-radius: 0; +} + .batch-mode-button .ikonli-font-icon { -fx-icon-color: -color-fg-default; } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/action/RunScriptActionMenu.java b/ext/base/src/main/java/io/xpipe/ext/base/action/RunScriptActionMenu.java index f1133c6dd..e9abe79db 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/action/RunScriptActionMenu.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/RunScriptActionMenu.java @@ -10,6 +10,7 @@ import io.xpipe.app.storage.DataStoreEntryRef; import io.xpipe.app.terminal.TerminalLauncher; import io.xpipe.app.util.CommandDialog; import io.xpipe.app.util.LabelGraphic; +import io.xpipe.core.process.CommandControl; import io.xpipe.core.process.ShellTtyState; import io.xpipe.core.process.SystemState; import io.xpipe.ext.base.script.ScriptHierarchy; @@ -19,6 +20,7 @@ import javafx.beans.value.ObservableValue; import lombok.Value; +import java.util.LinkedHashMap; import java.util.List; public class RunScriptActionMenu implements ActionProvider { @@ -87,8 +89,8 @@ public class RunScriptActionMenu implements ActionProvider { } @Override - public String getIcon() { - return "mdi2c-code-greater-than"; + public LabelGraphic getIcon() { + return new LabelGraphic.IconGraphic("mdi2c-code-greater-than"); } @Override @@ -114,26 +116,18 @@ public class RunScriptActionMenu implements ActionProvider { ScriptHierarchy hierarchy; - @Value - private class Action implements ActionProvider.Action { - - DataStoreEntryRef shellStore; - - @Override - public void execute() throws Exception { - var sc = shellStore.getStore().getOrStartSession(); - var script = hierarchy.getLeafBase().getStore().assembleScriptChain(sc); - var cmd = sc.command(script); - CommandDialog.runAsyncAndShow(cmd); - } - } @Override public LeafDataStoreCallSite getLeafDataStoreCallSite() { return new LeafDataStoreCallSite() { @Override public Action createAction(DataStoreEntryRef store) { - return new Action(store); + return () -> { + var sc = store.getStore().getOrStartSession(); + var script = hierarchy.getLeafBase().getStore().assembleScriptChain(sc); + var cmd = sc.command(script); + CommandDialog.runAsyncAndShow(cmd); + }; } @Override @@ -163,8 +157,8 @@ public class RunScriptActionMenu implements ActionProvider { } @Override - public String getIcon() { - return "mdi2d-desktop-mac"; + public LabelGraphic getIcon() { + return new LabelGraphic.IconGraphic("mdi2d-desktop-mac"); } @Override @@ -173,8 +167,17 @@ public class RunScriptActionMenu implements ActionProvider { } @Override - public ActionProvider.Action createAction(DataStoreEntryRef store) { - return new Action(store); + public Action createAction(List> stores) { + return () -> { + var map = new LinkedHashMap(); + for (DataStoreEntryRef ref : stores) { + var sc = ref.getStore().getOrStartSession(); + var script = hierarchy.getLeafBase().getStore().assembleScriptChain(sc); + var cmd = sc.command(script); + map.put(ref.get().getName(), cmd); + } + CommandDialog.runAsyncAndShow(map); + }; } }; } @@ -239,8 +242,8 @@ public class RunScriptActionMenu implements ActionProvider { } @Override - public String getIcon() { - return "mdi2f-flip-to-back"; + public LabelGraphic getIcon() { + return new LabelGraphic.IconGraphic("mdi2f-flip-to-back"); } @Override @@ -341,8 +344,8 @@ public class RunScriptActionMenu implements ActionProvider { } @Override - public String getIcon() { - return "mdi2p-play-box-multiple-outline"; + public LabelGraphic getIcon() { + return new LabelGraphic.IconGraphic("mdi2p-play-box-multiple-outline"); } @Override @@ -416,8 +419,8 @@ public class RunScriptActionMenu implements ActionProvider { } @Override - public String getIcon() { - return "mdi2i-image-filter-none"; + public LabelGraphic getIcon() { + return new LabelGraphic.IconGraphic("mdi2i-image-filter-none"); } @Override @@ -426,13 +429,8 @@ public class RunScriptActionMenu implements ActionProvider { } @Override - public ActionProvider.Action createAction(DataStoreEntryRef store) { - return null; - } - - @Override - public List getChildren(List> batch) { - return List.of(); + public ActionProvider.Action createAction(List> stores) { + return new Action(); } }; } @@ -440,14 +438,6 @@ public class RunScriptActionMenu implements ActionProvider { private static class NoStateActionProvider implements ActionProvider { - private static class Action implements ActionProvider.Action { - - @Override - public void execute() { - StoreViewState.get().getAllScriptsCategory().select(); - } - } - @Override public LeafDataStoreCallSite getLeafDataStoreCallSite() { return new LeafDataStoreCallSite() { @@ -477,6 +467,36 @@ public class RunScriptActionMenu implements ActionProvider { } }; } + + @Override + public BatchDataStoreCallSite getBatchDataStoreCallSite() { + return new BatchDataStoreCallSite() { + @Override + public ObservableValue getName() { + return AppI18n.observable("noScriptStateAvailable"); + } + + @Override + public LabelGraphic getIcon() { + return new LabelGraphic.IconGraphic("mdi2i-image-filter-none"); + } + + @Override + public Class getApplicableClass() { + return ShellStore.class; + } + + @Override + public ActionProvider.Action createAction(DataStoreEntryRef store) { + return new Action() { + @Override + public void execute() { + store.get().validate(); + } + }; + } + }; + } } @Override @@ -569,19 +589,44 @@ public class RunScriptActionMenu implements ActionProvider { } @Override - public String getIcon() { - return "mdi2p-play-box-multiple-outline"; - } - - @Override - public Action createAction(DataStoreEntryRef store) { - return null; + public LabelGraphic getIcon() { + return new LabelGraphic.IconGraphic("mdi2p-play-box-multiple-outline"); } @Override public List getChildren(List> batch) { - var hierarchy = ScriptHierarchy.buildEnabledHierarchy(ref -> { - if (!ref.getStore().isRunnableScript()) { + var stateMissing = batch.stream().anyMatch(ref -> { + var state = ref.get().getStorePersistentState(); + if (state instanceof SystemState systemState) { + if (systemState.getShellDialect() == null) { + return true; + } + + if (systemState.getTtyState() == null || systemState.getTtyState() != ShellTtyState.NONE) { + return true; + } + } + return false; + }); + + if (stateMissing) { + return List.of(new NoStateActionProvider()); + } + + var hierarchy = ScriptHierarchy.buildEnabledHierarchy(scriptRef -> { + var compatible = batch.stream().allMatch(ref -> { + var state = ref.get().getStorePersistentState(); + if (state instanceof SystemState systemState) { + return scriptRef.getStore().getMinimumDialect().isCompatibleTo(systemState.getShellDialect()); + } else { + return false; + } + }); + if (!compatible) { + return false; + } + + if (!scriptRef.getStore().isRunnableScript()) { return false; } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/action/ScanStoreAction.java b/ext/base/src/main/java/io/xpipe/ext/base/action/ScanStoreAction.java index f11b2fee1..748d7dbe5 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/action/ScanStoreAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/ScanStoreAction.java @@ -75,8 +75,8 @@ public class ScanStoreAction implements ActionProvider { } @Override - public String getIcon() { - return "mdi2l-layers-plus"; + public LabelGraphic getIcon() { + return new LabelGraphic.IconGraphic("mdi2l-layers-plus"); } @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/service/AbstractServiceStoreProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/service/AbstractServiceStoreProvider.java index ebb0c8adf..c18f62987 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/service/AbstractServiceStoreProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/service/AbstractServiceStoreProvider.java @@ -10,7 +10,7 @@ import io.xpipe.app.ext.SingletonSessionStoreProvider; import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStoreEntry; import io.xpipe.app.util.DataStoreFormatter; -import io.xpipe.app.util.ShellStoreFormat; +import io.xpipe.app.util.StoreStateFormat; import io.xpipe.core.store.DataStore; import javafx.beans.binding.Bindings; @@ -127,7 +127,7 @@ public abstract class AbstractServiceStoreProvider implements SingletonSessionSt : s.isSessionRunning() ? AppI18n.get("active") : s.isSessionEnabled() ? AppI18n.get("starting") : AppI18n.get("inactive"); - return new ShellStoreFormat(null, desc, type, state).format(); + return new StoreStateFormat(null, desc, type, state).format(); }, section.getWrapper().getCache(), AppI18n.activeLanguage()); diff --git a/ext/base/src/main/java/io/xpipe/ext/base/service/ServiceControlStoreProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/service/ServiceControlStoreProvider.java index 131661eca..77ab3ea54 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/service/ServiceControlStoreProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/service/ServiceControlStoreProvider.java @@ -9,7 +9,7 @@ import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStoreEntry; import io.xpipe.app.util.DataStoreFormatter; import io.xpipe.app.util.OptionsBuilder; -import io.xpipe.app.util.ShellStoreFormat; +import io.xpipe.app.util.StoreStateFormat; import io.xpipe.core.store.DataStore; import javafx.beans.binding.Bindings; @@ -57,7 +57,7 @@ public class ServiceControlStoreProvider implements SingletonSessionStoreProvide var state = s.isSessionRunning() ? AppI18n.get("active") : s.isSessionEnabled() ? AppI18n.get("starting") : AppI18n.get("inactive"); - return new ShellStoreFormat(null, state).format(); + return new StoreStateFormat(null, state).format(); }, section.getWrapper().getCache(), AppPrefs.get().language()); diff --git a/ext/base/src/main/java/io/xpipe/ext/base/service/ServiceRefreshAction.java b/ext/base/src/main/java/io/xpipe/ext/base/service/ServiceRefreshAction.java index 1f9d38568..d9ae74615 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/service/ServiceRefreshAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/service/ServiceRefreshAction.java @@ -63,8 +63,8 @@ public class ServiceRefreshAction implements ActionProvider { } @Override - public String getIcon() { - return "mdi2w-web"; + public LabelGraphic getIcon() { + return new LabelGraphic.IconGraphic("mdi2w-web"); } @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/store/ShellStoreProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/store/ShellStoreProvider.java index d15ae07b0..b31bd2ef3 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/store/ShellStoreProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/store/ShellStoreProvider.java @@ -11,7 +11,7 @@ import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStoreEntry; import io.xpipe.app.terminal.TerminalLauncher; import io.xpipe.app.terminal.TerminalPromptManager; -import io.xpipe.app.util.ShellStoreFormat; +import io.xpipe.app.util.StoreStateFormat; import io.xpipe.ext.base.script.ScriptStoreSetup; import javafx.beans.property.BooleanProperty; @@ -60,6 +60,6 @@ public interface ShellStoreProvider extends DataStoreProvider { @Override default ObservableValue informationString(StoreSection section) { - return ShellStoreFormat.shellStore(section, state -> null); + return StoreStateFormat.shellStore(section, state -> null); } } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/store/StorePauseAction.java b/ext/base/src/main/java/io/xpipe/ext/base/store/StorePauseAction.java index 370ce1be4..39e8cb54a 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/store/StorePauseAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/store/StorePauseAction.java @@ -52,8 +52,8 @@ public class StorePauseAction implements ActionProvider { } @Override - public String getIcon() { - return "mdi2p-pause"; + public LabelGraphic getIcon() { + return new LabelGraphic.IconGraphic("mdi2p-pause"); } @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/store/StoreRestartAction.java b/ext/base/src/main/java/io/xpipe/ext/base/store/StoreRestartAction.java index 3181d2349..9e4872967 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/store/StoreRestartAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/store/StoreRestartAction.java @@ -58,8 +58,8 @@ public class StoreRestartAction implements ActionProvider { } @Override - public String getIcon() { - return "mdi2r-restart"; + public LabelGraphic getIcon() { + return new LabelGraphic.IconGraphic("mdi2r-restart"); } @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/store/StoreStartAction.java b/ext/base/src/main/java/io/xpipe/ext/base/store/StoreStartAction.java index 3b80b3d7f..0eb52102f 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/store/StoreStartAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/store/StoreStartAction.java @@ -52,8 +52,8 @@ public class StoreStartAction implements ActionProvider { } @Override - public String getIcon() { - return "mdi2p-play"; + public LabelGraphic getIcon() { + return new LabelGraphic.IconGraphic("mdi2p-play"); } @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/store/StoreStopAction.java b/ext/base/src/main/java/io/xpipe/ext/base/store/StoreStopAction.java index a65bcca75..eaea4c18b 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/store/StoreStopAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/store/StoreStopAction.java @@ -52,8 +52,8 @@ public class StoreStopAction implements ActionProvider { } @Override - public String getIcon() { - return "mdi2s-stop"; + public LabelGraphic getIcon() { + return new LabelGraphic.IconGraphic("mdi2s-stop"); } @Override diff --git a/ext/system/src/main/java/io/xpipe/ext/system/incus/IncusContainerStoreProvider.java b/ext/system/src/main/java/io/xpipe/ext/system/incus/IncusContainerStoreProvider.java index c1ddbdf48..c559d1cf5 100644 --- a/ext/system/src/main/java/io/xpipe/ext/system/incus/IncusContainerStoreProvider.java +++ b/ext/system/src/main/java/io/xpipe/ext/system/incus/IncusContainerStoreProvider.java @@ -85,7 +85,7 @@ public class IncusContainerStoreProvider implements ShellStoreProvider { public ObservableValue informationString(StoreSection section) { var c = (ContainerStoreState) section.getWrapper().getPersistentState().getValue(); var missing = c.getShellMissing() != null && c.getShellMissing() ? "No shell available" : null; - return ShellStoreFormat.shellStore(section, (ContainerStoreState s) -> + return StoreStateFormat.shellStore(section, (ContainerStoreState s) -> new String[] {missing, DataStoreFormatter.capitalize(s.getContainerState())}); } diff --git a/ext/system/src/main/java/io/xpipe/ext/system/incus/IncusScanProvider.java b/ext/system/src/main/java/io/xpipe/ext/system/incus/IncusScanProvider.java index a16910c7c..61c9155ea 100644 --- a/ext/system/src/main/java/io/xpipe/ext/system/incus/IncusScanProvider.java +++ b/ext/system/src/main/java/io/xpipe/ext/system/incus/IncusScanProvider.java @@ -14,7 +14,7 @@ public class IncusScanProvider extends ScanProvider { return null; } - return new ScanOpportunity("system.incusContainers", !new IncusCommandView(sc).isSupported(), true); + return new ScanOpportunity("system.incusContainers", !new IncusCommandView(sc).isSupported()); } @Override diff --git a/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdContainerStoreProvider.java b/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdContainerStoreProvider.java index 48eb94225..2b11ad495 100644 --- a/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdContainerStoreProvider.java +++ b/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdContainerStoreProvider.java @@ -9,7 +9,7 @@ import io.xpipe.app.ext.GuiDialog; import io.xpipe.app.storage.DataStoreEntry; import io.xpipe.app.util.DataStoreFormatter; import io.xpipe.app.util.OptionsBuilder; -import io.xpipe.app.util.ShellStoreFormat; +import io.xpipe.app.util.StoreStateFormat; import io.xpipe.core.store.DataStore; import io.xpipe.ext.base.identity.IdentityChoice; import io.xpipe.ext.base.store.ShellStoreProvider; @@ -82,7 +82,7 @@ public class LxdContainerStoreProvider implements ShellStoreProvider { public ObservableValue informationString(StoreSection section) { var c = (ContainerStoreState) section.getWrapper().getPersistentState().getValue(); var missing = c.getShellMissing() != null && c.getShellMissing() ? "No shell available" : null; - return ShellStoreFormat.shellStore(section, (ContainerStoreState s) -> + return StoreStateFormat.shellStore(section, (ContainerStoreState s) -> new String[] {missing, DataStoreFormatter.capitalize(s.getContainerState())}); } diff --git a/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdScanProvider.java b/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdScanProvider.java index dc2be80ee..9bf6025c3 100644 --- a/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdScanProvider.java +++ b/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdScanProvider.java @@ -15,7 +15,7 @@ public class LxdScanProvider extends ScanProvider { return null; } - return new ScanOpportunity("system.lxdContainers", !new LxdCommandView(sc).isSupported(), true); + return new ScanOpportunity("system.lxdContainers", !new LxdCommandView(sc).isSupported()); } @Override diff --git a/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanContainerStoreProvider.java b/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanContainerStoreProvider.java index 83c98faf8..3ee8e79a4 100644 --- a/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanContainerStoreProvider.java +++ b/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanContainerStoreProvider.java @@ -10,7 +10,7 @@ import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStoreEntry; import io.xpipe.app.util.DataStoreFormatter; import io.xpipe.app.util.OptionsBuilder; -import io.xpipe.app.util.ShellStoreFormat; +import io.xpipe.app.util.StoreStateFormat; import io.xpipe.app.util.SimpleValidator; import io.xpipe.core.store.DataStore; import io.xpipe.ext.base.service.FixedServiceGroupStore; @@ -83,7 +83,7 @@ public class PodmanContainerStoreProvider implements ShellStoreProvider { public ObservableValue informationString(StoreSection section) { var c = (ContainerStoreState) section.getWrapper().getPersistentState().getValue(); var missing = c.getShellMissing() != null && c.getShellMissing() ? "No shell available" : null; - return ShellStoreFormat.shellStore( + return StoreStateFormat.shellStore( section, (ContainerStoreState s) -> new String[] {missing, s.getContainerState()}); } diff --git a/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanScanProvider.java b/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanScanProvider.java index b01742a16..d807d8235 100644 --- a/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanScanProvider.java +++ b/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanScanProvider.java @@ -10,7 +10,7 @@ public class PodmanScanProvider extends ScanProvider { @Override public ScanOpportunity create(DataStoreEntry entry, ShellControl sc) throws Exception { var view = new PodmanCommandView(sc); - return new ScanOpportunity("system.podmanContainers", !view.isSupported(), true); + return new ScanOpportunity("system.podmanContainers", !view.isSupported()); } @Override diff --git a/version b/version index 8c72e21ec..a92b489a8 100644 --- a/version +++ b/version @@ -1 +1 @@ -16.0-21 +16.0-22