diff --git a/app/src/main/java/io/xpipe/app/hub/comp/StoreFilterFieldComp.java b/app/src/main/java/io/xpipe/app/hub/comp/StoreFilterFieldComp.java index 03b943f80..d34e12b0f 100644 --- a/app/src/main/java/io/xpipe/app/hub/comp/StoreFilterFieldComp.java +++ b/app/src/main/java/io/xpipe/app/hub/comp/StoreFilterFieldComp.java @@ -8,6 +8,7 @@ import io.xpipe.app.comp.RegionDescriptor; import io.xpipe.app.comp.SimpleRegionBuilder; import io.xpipe.app.comp.base.IconButtonComp; import io.xpipe.app.comp.base.InputGroupComp; +import io.xpipe.app.core.AppFontSizes; import io.xpipe.app.core.AppI18n; import io.xpipe.app.core.AppOpenArguments; import io.xpipe.app.platform.PlatformThread; @@ -20,6 +21,7 @@ import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyEvent; import javafx.scene.layout.Region; import javafx.stage.PopupWindow; +import lombok.val; import java.util.List; import java.util.Objects; @@ -54,18 +56,25 @@ public class StoreFilterFieldComp extends SimpleRegionBuilder { } }); + var clearButton = new IconButtonComp("mdi2c-close", () -> { + field.clear(); + }).build(); + clearButton.setCursor(Cursor.DEFAULT); + AppFontSizes.sm(clearButton); var searchButton = new IconButtonComp("mdi2m-magnify", () -> { if (state.open()) { field.clear(); } }).build(); searchButton.setCursor(Cursor.DEFAULT); + AppFontSizes.sm(searchButton); var launchButton = new IconButtonComp("mdi2p-play", () -> { if (state.open()) { field.clear(); } }).build(); launchButton.setCursor(Cursor.DEFAULT); + AppFontSizes.sm(launchButton); field.setMinHeight(0); field.setMaxHeight(20000); @@ -75,7 +84,7 @@ public class StoreFilterFieldComp extends SimpleRegionBuilder { .bind(Bindings.createObjectBinding( () -> { if (!field.isFocused()) { - return null; + return state.getEffectiveFilter().get() != null ? clearButton : null; } if (state.getIsQuickConnectString().get() || state.getIsUrlString().get()) { 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 d10f997c8..616949ed0 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStorage.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStorage.java @@ -1089,8 +1089,8 @@ public abstract class DataStorage { } private DataStoreCategory getFallbackCategory(DataStoreCategory cat) { - var parent = getStoreCategoryIfPresent(cat.getParentCategory()).orElseThrow(); - if (parent.getParentCategory() != null) { + var parent = getStoreCategoryIfPresent(cat.getParentCategory()).orElse(null); + if (parent != null && parent.getParentCategory() != null) { return parent; } diff --git a/app/src/main/resources/io/xpipe/app/resources/style/data-store-list-choice-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/data-store-list-choice-comp.css index 2b012ff38..fe4190ccf 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/data-store-list-choice-comp.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/data-store-list-choice-comp.css @@ -5,4 +5,9 @@ -fx-padding: 0.5em; -fx-border-radius: 4px; -fx-background-radius: 4px; +} + +.root:nord .data-store-list-choice-comp .entry { + -fx-border-radius: 0; + -fx-background-radius: 0; } \ No newline at end of file diff --git a/app/src/main/resources/io/xpipe/app/resources/style/header-bars.css b/app/src/main/resources/io/xpipe/app/resources/style/header-bars.css index 9113e373f..50d179b05 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/header-bars.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/header-bars.css @@ -7,7 +7,7 @@ } .store-filter-comp { - -fx-padding: 0.3em 0.5em 0.3em 0.5em; + -fx-padding: 0.3em 0.0em 0.3em 0.5em; } .bar { 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 b8d762b31..235852c0b 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 @@ -26,6 +26,11 @@ -fx-border-color: -color-neutral-emphasis; } +.root:nord .edit-button.icon-button-comp { + -fx-background-radius: 0; + -fx-border-radius: 0; +} + .scan-list { -fx-background-radius: 4px; -fx-border-width: 1; diff --git a/app/src/main/resources/io/xpipe/app/resources/style/tile-button-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/tile-button-comp.css index 5368bf9e0..de8a79acf 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/tile-button-comp.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/tile-button-comp.css @@ -6,6 +6,7 @@ .root:nord .tile-button-comp { -fx-background-radius: 0; + -fx-border-radius: 0; } .tile-button-comp:hover:armed { diff --git a/app/src/main/resources/io/xpipe/app/resources/style/toggle-switch-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/toggle-switch-comp.css index 16d92fdeb..e70d21328 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/toggle-switch-comp.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/toggle-switch-comp.css @@ -28,6 +28,10 @@ -fx-padding: 0.7em; } +.root:windows:nord .toggle-switch-comp > .thumb { + -fx-background-radius: 0em; +} + .root:macos .toggle-switch-comp > .thumb-area { -fx-padding: 0.8em 1.4em; } @@ -38,6 +42,10 @@ -fx-padding: 0.8em; } +.root:macos:nord .toggle-switch-comp > .thumb { + -fx-background-radius: 0em; +} + .root:light .toggle-switch-comp > .thumb { -fx-background-color: -color-neutral-emphasis; } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/store/RestartableStore.java b/ext/base/src/main/java/io/xpipe/ext/base/store/RestartableStore.java new file mode 100644 index 000000000..bac5f6345 --- /dev/null +++ b/ext/base/src/main/java/io/xpipe/ext/base/store/RestartableStore.java @@ -0,0 +1,8 @@ +package io.xpipe.ext.base.store; + +import io.xpipe.app.ext.DataStore; + +public interface RestartableStore extends DataStore { + + void restart() throws Exception; +} diff --git a/ext/base/src/main/java/io/xpipe/ext/base/store/StoreRestartActionProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/store/StoreRestartActionProvider.java index dd49a1c13..24a124fc8 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/store/StoreRestartActionProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/store/StoreRestartActionProvider.java @@ -28,7 +28,7 @@ public class StoreRestartActionProvider implements HubLeafProvider, B @Override public boolean isApplicable(DataStoreEntryRef o) { - return o.getStore() instanceof StartableStore && o.getStore() instanceof StoppableStore; + return o.getStore() instanceof RestartableStore || (o.getStore() instanceof StartableStore && o.getStore() instanceof StoppableStore); } @Override @@ -63,7 +63,7 @@ public class StoreRestartActionProvider implements HubLeafProvider, B @Override public String getId() { - return "restartStore"; + return "restartAction"; } @Jacksonized @@ -72,8 +72,12 @@ public class StoreRestartActionProvider implements HubLeafProvider, B @Override public void executeImpl() throws Exception { - ((StoppableStore) ref.getStore()).stop(); - ((StartableStore) ref.getStore()).start(); + if (ref.getStore() instanceof RestartableStore r) { + r.restart(); + } else { + ((StoppableStore) ref.getStore()).stop(); + ((StartableStore) ref.getStore()).start(); + } } @Override diff --git a/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanCmdStore.java b/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanCmdStore.java index 804c92275..26b684bfe 100644 --- a/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanCmdStore.java +++ b/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanCmdStore.java @@ -30,6 +30,11 @@ public class PodmanCmdStore this.host = host; } + @Override + public boolean removeLeftovers() { + return false; + } + @Override public List> getDependencies() { return DataStoreDependencies.of(host); diff --git a/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanCommandView.java b/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanCommandView.java index d15bc03e2..04109e410 100644 --- a/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanCommandView.java +++ b/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanCommandView.java @@ -3,10 +3,12 @@ package io.xpipe.ext.system.podman; import io.xpipe.app.issue.ErrorEventFactory; import io.xpipe.app.process.*; +import io.xpipe.core.OsType; import lombok.NonNull; import lombok.Value; import java.util.List; +import java.util.Optional; import java.util.function.Consumer; public class PodmanCommandView extends CommandViewBase { @@ -67,6 +69,16 @@ public class PodmanCommandView extends CommandViewBase { public class Container extends CommandView { + public Optional queryLabel(String container, String label) throws Exception { + var command = build(b -> b.add("inspect") + .add(sc -> { + var quote = ShellDialects.isPowershell(sc) ? "'" : "\""; + return "--format=" + quote + "{{index (index .Config.Labels \\\"" + label + "\\\")}}" + quote; + }) + .addQuoted(container)); + return command.readStdoutIfPossible().filter(s -> !s.isBlank()); + } + public String queryState(String container) throws Exception { return build(commandBuilder -> commandBuilder.add( "ls", "-a", "-f", "name=\"^" + container + "$\"", "--format=\"{{.Status}}\"")) @@ -145,6 +157,18 @@ public class PodmanCommandView extends CommandViewBase { .execute(); } + public void restart(String container) throws Exception { + if (shellControl.getOsType() == OsType.LINUX) { + var systemd = queryLabel(container, "PODMAN_SYSTEMD_UNIT"); + if (systemd.isPresent()) { + shellControl.command(CommandBuilder.of().add("systemctl", "restart", "--user").addQuoted(systemd.get())).execute(); + return; + } + } + + build(commandBuilder -> commandBuilder.add("restart").addQuoted(container)).execute(); + } + public String port(String container) throws Exception { return build(commandBuilder -> commandBuilder.add("port").addQuoted(container)) .readStdoutOrThrow(); diff --git a/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanContainerStore.java b/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanContainerStore.java index 567ef140e..63ee3bfbb 100644 --- a/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanContainerStore.java +++ b/ext/system/src/main/java/io/xpipe/ext/system/podman/PodmanContainerStore.java @@ -9,6 +9,7 @@ import io.xpipe.app.util.Validators; import io.xpipe.ext.base.service.AbstractServiceStore; import io.xpipe.ext.base.service.FixedServiceCreatorStore; import io.xpipe.ext.base.service.MappedServiceStore; +import io.xpipe.ext.base.store.RestartableStore; import io.xpipe.ext.base.store.StartableStore; import io.xpipe.ext.base.store.StoppableStore; @@ -28,7 +29,7 @@ import java.util.regex.Pattern; @Value public class PodmanContainerStore implements StartableStore, - StoppableStore, + StoppableStore, RestartableStore, ShellStore, InternalCacheDataStore, FixedChildStore, @@ -77,6 +78,8 @@ public class PodmanContainerStore refreshContainerState(sc); } + + @Override public List> createFixedServices() throws Exception { return findServices().stream() @@ -160,13 +163,30 @@ public class PodmanContainerStore }; } - private void refreshContainerState(ShellControl sc) throws Exception { + private boolean refreshContainerState(ShellControl sc) throws Exception { var state = getState(); var view = new PodmanCommandView(sc).container(); var displayState = view.queryState(containerName); + if (displayState.isEmpty()) { + var newState = + state.toBuilder().containerState("Removed").running(false).build(); + setState(newState); + return false; + } + var running = displayState.startsWith("Up"); var newState = state.toBuilder().containerState(displayState).running(running).build(); setState(newState); + return true; + } + + @Override + public void restart() throws Exception { + stopSessionIfNeeded(); + var sc = getCmd().getStore().getHost().getStore().getOrStartSession(); + var view = commandView(sc); + view.restart(containerName); + refreshContainerState(sc); } }