From 0de7d36c4f608b5bec403a724546650d14ba388c Mon Sep 17 00:00:00 2001 From: crschnick Date: Tue, 25 Jun 2024 03:00:24 +0000 Subject: [PATCH] Various fixes --- .../impl/ConnectionInfoExchangeImpl.java | 19 ++ .../impl/ConnectionQueryExchangeImpl.java | 6 +- .../app/browser/BrowserBookmarkComp.java | 3 +- .../app/browser/BrowserSelectionListComp.java | 3 +- .../xpipe/app/browser/BrowserWelcomeComp.java | 6 +- .../browser/file/BrowserFileOverviewComp.java | 2 +- .../java/io/xpipe/app/comp/AppLayoutComp.java | 20 ++ .../xpipe/app/comp/base/ListBoxViewComp.java | 23 +- .../app/comp/store/StoreCreationComp.java | 7 +- .../app/comp/store/StoreCreationMenu.java | 20 +- .../app/comp/store/StoreEntryListComp.java | 11 +- .../app/comp/store/StoreSectionComp.java | 6 +- .../app/comp/store/StoreSectionMiniComp.java | 18 +- .../main/java/io/xpipe/app/core/AppTheme.java | 12 +- .../app/core/window/AppWindowBounds.java | 22 +- .../app/ext/DataStoreCreationCategory.java | 13 + .../io/xpipe/app/ext/DataStoreProvider.java | 38 ++- .../xpipe/app/ext/DataStoreUsageCategory.java | 20 ++ .../app/fxcomps/impl/DataStoreChoiceComp.java | 3 +- .../fxcomps/impl/DataStoreListChoiceComp.java | 3 +- .../app/fxcomps/impl/StoreCategoryComp.java | 2 +- app/src/main/java/module-info.java | 1 + .../io/xpipe/app/resources/misc/api.md | 238 ++++++++++++++++++ .../resources/style/error-handler-comp.css | 1 + .../xpipe/app/resources/style/scrollbar.css | 3 +- .../app/resources/style/store-entry-comp.css | 15 +- .../resources/style/store-mini-section.css | 18 +- .../io/xpipe/app/resources/style/style.css | 2 +- .../beacon/api/ConnectionInfoExchange.java | 56 +++++ beacon/src/main/java/module-info.java | 1 + .../io/xpipe/core/util/JacksonMapper.java | 1 + .../DesktopApplicationStoreProvider.java | 13 +- .../desktop/DesktopCommandStoreProvider.java | 13 +- .../DesktopEnvironmentStoreProvider.java | 13 +- .../base/script/ScriptGroupStoreProvider.java | 13 +- .../script/SimpleScriptStoreProvider.java | 5 +- .../AbstractServiceGroupStoreProvider.java | 6 + .../service/AbstractServiceStoreProvider.java | 6 + .../service/CustomServiceStoreProvider.java | 5 +- openapi.yaml | 91 +++++++ 40 files changed, 627 insertions(+), 131 deletions(-) create mode 100644 app/src/main/java/io/xpipe/app/beacon/impl/ConnectionInfoExchangeImpl.java create mode 100644 app/src/main/java/io/xpipe/app/ext/DataStoreCreationCategory.java create mode 100644 app/src/main/java/io/xpipe/app/ext/DataStoreUsageCategory.java create mode 100644 beacon/src/main/java/io/xpipe/beacon/api/ConnectionInfoExchange.java diff --git a/app/src/main/java/io/xpipe/app/beacon/impl/ConnectionInfoExchangeImpl.java b/app/src/main/java/io/xpipe/app/beacon/impl/ConnectionInfoExchangeImpl.java new file mode 100644 index 000000000..2c08b75c4 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/beacon/impl/ConnectionInfoExchangeImpl.java @@ -0,0 +1,19 @@ +package io.xpipe.app.beacon.impl; + +import com.sun.net.httpserver.HttpExchange; +import io.xpipe.app.storage.DataStorage; +import io.xpipe.beacon.BeaconClientException; +import io.xpipe.beacon.api.ConnectionInfoExchange; + +public class ConnectionInfoExchangeImpl extends ConnectionInfoExchange { + + @Override + public Object handle(HttpExchange exchange, Request msg) throws BeaconClientException { + var e = DataStorage.get() + .getStoreEntryIfPresent(msg.getConnection()) + .orElseThrow(() -> new BeaconClientException("Unknown connection")); + + return Response.builder().lastModified(e.getLastModified()).lastUsed(e.getLastUsed()).connection(e.getCategoryUuid()).category(DataStorage.get() + .getStorePath(DataStorage.get().getStoreCategoryIfPresent(e.getCategoryUuid()).orElseThrow())).name(DataStorage.get().getStorePath(e)).rawData(e.getStore()).usageCategory(e.getProvider().getUsageCategory()).type(e.getProvider().getId()).build(); + } +} diff --git a/app/src/main/java/io/xpipe/app/beacon/impl/ConnectionQueryExchangeImpl.java b/app/src/main/java/io/xpipe/app/beacon/impl/ConnectionQueryExchangeImpl.java index 20662bae2..109a98780 100644 --- a/app/src/main/java/io/xpipe/app/beacon/impl/ConnectionQueryExchangeImpl.java +++ b/app/src/main/java/io/xpipe/app/beacon/impl/ConnectionQueryExchangeImpl.java @@ -15,9 +15,9 @@ public class ConnectionQueryExchangeImpl extends ConnectionQueryExchange { @Override public Object handle(HttpExchange exchange, Request msg) { - var catMatcher = Pattern.compile(toRegex("all connections/" + msg.getCategoryFilter())); - var conMatcher = Pattern.compile(toRegex(msg.getConnectionFilter())); - var typeMatcher = Pattern.compile(toRegex(msg.getTypeFilter())); + var catMatcher = Pattern.compile(toRegex("all connections/" + msg.getCategoryFilter().toLowerCase())); + var conMatcher = Pattern.compile(toRegex(msg.getConnectionFilter().toLowerCase())); + var typeMatcher = Pattern.compile(toRegex(msg.getTypeFilter().toLowerCase())); List found = new ArrayList<>(); for (DataStoreEntry storeEntry : DataStorage.get().getStoreEntries()) { diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserBookmarkComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserBookmarkComp.java index c42a8490d..2a18d4643 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserBookmarkComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserBookmarkComp.java @@ -70,8 +70,7 @@ public final class BrowserBookmarkComp extends SimpleComp { category, StoreViewState.get().getEntriesListUpdateObservable()), augment, - entryWrapper -> action.accept(entryWrapper, busy), - true); + entryWrapper -> action.accept(entryWrapper, busy)); var r = section.vgrow().createRegion(); r.getStyleClass().add("bookmark-list"); diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserSelectionListComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserSelectionListComp.java index 59b07136a..c97c15f60 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserSelectionListComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserSelectionListComp.java @@ -59,7 +59,8 @@ public class BrowserSelectionListComp extends SimpleComp { l.textProperty().bind(PlatformThread.sync(nameTransformation.apply(entry))); return l; }); - }) + }, + false) .styleClass("selected-file-list"); return c.createRegion(); } diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserWelcomeComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserWelcomeComp.java index 5a8cd7afc..d0a2416fd 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserWelcomeComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserWelcomeComp.java @@ -101,7 +101,7 @@ public class BrowserWelcomeComp extends SimpleComp { var disable = new SimpleBooleanProperty(); var entryButton = entryButton(e, disable); var dirButton = dirButton(e, disable); - return new HorizontalComp(List.of(entryButton, dirButton, Comp.hspacer(10))).apply(struc -> { + return new HorizontalComp(List.of(entryButton, dirButton)).apply(struc -> { ((Region) struc.get().getChildren().get(0)) .prefHeightProperty() .bind(struc.get().heightProperty()); @@ -109,7 +109,7 @@ public class BrowserWelcomeComp extends SimpleComp { .prefHeightProperty() .bind(struc.get().heightProperty()); }); - }) + }, true) .apply(struc -> { VBox vBox = (VBox) struc.get().getContent(); vBox.setSpacing(10); @@ -119,7 +119,7 @@ public class BrowserWelcomeComp extends SimpleComp { var layout = new VBox(); layout.getStyleClass().add("welcome"); - layout.setPadding(new Insets(40, 40, 40, 50)); + layout.setPadding(new Insets(60, 40, 40, 50)); layout.setSpacing(18); layout.getChildren().add(hbox); layout.getChildren().add(Comp.separator().hide(empty).createRegion()); diff --git a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileOverviewComp.java b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileOverviewComp.java index b451f16c1..2aaca8796 100644 --- a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileOverviewComp.java +++ b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileOverviewComp.java @@ -50,7 +50,7 @@ public class BrowserFileOverviewComp extends SimpleComp { }; if (grow) { - var c = new ListBoxViewComp<>(list, list, factory).styleClass("overview-file-list"); + var c = new ListBoxViewComp<>(list, list, factory, true).styleClass("overview-file-list"); return c.createRegion(); } else { var c = new VBoxViewComp<>(list, list, factory).styleClass("overview-file-list"); diff --git a/app/src/main/java/io/xpipe/app/comp/AppLayoutComp.java b/app/src/main/java/io/xpipe/app/comp/AppLayoutComp.java index 695d2d44a..6661ddf39 100644 --- a/app/src/main/java/io/xpipe/app/comp/AppLayoutComp.java +++ b/app/src/main/java/io/xpipe/app/comp/AppLayoutComp.java @@ -13,7 +13,9 @@ import io.xpipe.app.storage.DataStorage; import javafx.beans.binding.Bindings; import javafx.beans.value.ObservableValue; import javafx.scene.control.ButtonBase; +import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; @@ -57,8 +59,26 @@ public class AppLayoutComp extends Comp> { if (shortcut != null && shortcut.match(event)) { ((ButtonBase) node).fire(); event.consume(); + return; } }); + if (event.isConsumed()) { + return; + } + + var forward = new KeyCodeCombination(KeyCode.TAB, KeyCombination.CONTROL_DOWN); + if (forward.match(event)) { + var next = (model.getEntries().indexOf(model.getSelected().getValue()) + 1) % 3; + model.getSelected().setValue(model.getEntries().get(next)); + return; + } + + var back = new KeyCodeCombination(KeyCode.TAB, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN); + if (back.match(event)) { + var next = (model.getEntries().indexOf(model.getSelected().getValue()) + 2) % 3; + model.getSelected().setValue(model.getEntries().get(next)); + return; + } }); AppFont.normal(pane); pane.getStyleClass().add("layout"); diff --git a/app/src/main/java/io/xpipe/app/comp/base/ListBoxViewComp.java b/app/src/main/java/io/xpipe/app/comp/base/ListBoxViewComp.java index c2c967701..011cd6f65 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/ListBoxViewComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/ListBoxViewComp.java @@ -5,11 +5,12 @@ import io.xpipe.app.fxcomps.CompStructure; import io.xpipe.app.fxcomps.SimpleCompStructure; import io.xpipe.app.fxcomps.util.DerivedObservableList; import io.xpipe.app.fxcomps.util.PlatformThread; - import javafx.application.Platform; +import javafx.beans.binding.Bindings; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.css.PseudoClass; +import javafx.scene.control.ScrollBar; import javafx.scene.control.ScrollPane; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; @@ -29,11 +30,13 @@ public class ListBoxViewComp extends Comp> { private final ObservableList all; private final Function> compFunction; private final int limit = Integer.MAX_VALUE; + private final boolean scrollBar; - public ListBoxViewComp(ObservableList shown, ObservableList all, Function> compFunction) { + public ListBoxViewComp(ObservableList shown, ObservableList all, Function> compFunction, boolean scrollBar) { this.shown = PlatformThread.sync(shown); this.all = PlatformThread.sync(all); this.compFunction = compFunction; + this.scrollBar = scrollBar; } @Override @@ -56,10 +59,24 @@ public class ListBoxViewComp extends Comp> { }); var scroll = new ScrollPane(vbox); + if (scrollBar) { + scroll.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); + scroll.skinProperty().subscribe(newValue -> { + if (newValue != null) { + ScrollBar bar = (ScrollBar) scroll.lookup(".scroll-bar:vertical"); + bar.opacityProperty().bind(Bindings.createDoubleBinding(() -> { + var v = bar.getVisibleAmount(); + return v < 1.0 ? 1.0 : 0.0; + }, bar.visibleAmountProperty())); + } + }); + } else { + scroll.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + scroll.setFitToHeight(true); + } scroll.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); scroll.setFitToWidth(true); scroll.getStyleClass().add("list-box-view-comp"); - return new SimpleCompStructure<>(scroll); } diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreCreationComp.java b/app/src/main/java/io/xpipe/app/comp/store/StoreCreationComp.java index cb3391141..a125f1230 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreCreationComp.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreCreationComp.java @@ -6,6 +6,7 @@ import io.xpipe.app.comp.base.ErrorOverlayComp; import io.xpipe.app.comp.base.PopupMenuButtonComp; import io.xpipe.app.core.AppI18n; import io.xpipe.app.core.window.AppWindowHelper; +import io.xpipe.app.ext.DataStoreCreationCategory; import io.xpipe.app.ext.DataStoreProvider; import io.xpipe.app.ext.DataStoreProviders; import io.xpipe.app.fxcomps.Comp; @@ -129,7 +130,7 @@ public class StoreCreationComp extends DialogComp { if ((provider.getValue().getCreationCategory() == null || !provider.getValue() .getCreationCategory() - .equals(DataStoreProvider.CreationCategory.SCRIPT)) + .equals(DataStoreCreationCategory.SCRIPT)) && rootCategory.equals(DataStorage.get().getAllScriptsCategory())) { targetCategory = DataStorage.get() .getDefaultConnectionsCategory() @@ -170,11 +171,11 @@ public class StoreCreationComp extends DialogComp { e); } - public static void showCreation(DataStoreProvider selected, DataStoreProvider.CreationCategory category) { + public static void showCreation(DataStoreProvider selected, DataStoreCreationCategory category) { showCreation(selected != null ? selected.defaultStore() : null, category); } - public static void showCreation(DataStore base, DataStoreProvider.CreationCategory category) { + public static void showCreation(DataStore base, DataStoreCreationCategory category) { show( null, base != null ? DataStoreProviders.byStore(base) : null, diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreCreationMenu.java b/app/src/main/java/io/xpipe/app/comp/store/StoreCreationMenu.java index f5f0c40eb..1e2f026fc 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreCreationMenu.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreCreationMenu.java @@ -1,7 +1,7 @@ package io.xpipe.app.comp.store; import io.xpipe.app.core.AppI18n; -import io.xpipe.app.ext.DataStoreProvider; +import io.xpipe.app.ext.DataStoreCreationCategory; import io.xpipe.app.ext.DataStoreProviders; import io.xpipe.app.fxcomps.impl.PrettyImageHelper; import io.xpipe.app.util.ScanAlert; @@ -26,35 +26,35 @@ public class StoreCreationMenu { menu.getItems().add(automatically); menu.getItems().add(new SeparatorMenuItem()); - menu.getItems().add(category("addHost", "mdi2h-home-plus", DataStoreProvider.CreationCategory.HOST, "ssh")); + menu.getItems().add(category("addHost", "mdi2h-home-plus", DataStoreCreationCategory.HOST, "ssh")); menu.getItems() - .add(category("addDesktop", "mdi2c-camera-plus", DataStoreProvider.CreationCategory.DESKTOP, null)); + .add(category("addDesktop", "mdi2c-camera-plus", DataStoreCreationCategory.DESKTOP, null)); menu.getItems() - .add(category("addShell", "mdi2t-text-box-multiple", DataStoreProvider.CreationCategory.SHELL, null)); + .add(category("addShell", "mdi2t-text-box-multiple", DataStoreCreationCategory.SHELL, null)); menu.getItems() .add(category( - "addScript", "mdi2s-script-text-outline", DataStoreProvider.CreationCategory.SCRIPT, "script")); + "addScript", "mdi2s-script-text-outline", DataStoreCreationCategory.SCRIPT, "script")); menu.getItems() - .add(category("addService", "mdi2c-cloud-braces", DataStoreProvider.CreationCategory.SERVICE, null)); + .add(category("addService", "mdi2c-cloud-braces", DataStoreCreationCategory.SERVICE, null)); menu.getItems() .add(category( - "addTunnel", "mdi2v-vector-polyline-plus", DataStoreProvider.CreationCategory.TUNNEL, null)); + "addTunnel", "mdi2v-vector-polyline-plus", DataStoreCreationCategory.TUNNEL, null)); menu.getItems() .add(category( - "addCommand", "mdi2c-code-greater-than", DataStoreProvider.CreationCategory.COMMAND, "cmd")); + "addCommand", "mdi2c-code-greater-than", DataStoreCreationCategory.COMMAND, "cmd")); menu.getItems() - .add(category("addDatabase", "mdi2d-database-plus", DataStoreProvider.CreationCategory.DATABASE, null)); + .add(category("addDatabase", "mdi2d-database-plus", DataStoreCreationCategory.DATABASE, null)); } private static MenuItem category( - String name, String graphic, DataStoreProvider.CreationCategory category, String defaultProvider) { + String name, String graphic, DataStoreCreationCategory category, String defaultProvider) { var sub = DataStoreProviders.getAll().stream() .filter(dataStoreProvider -> category.equals(dataStoreProvider.getCreationCategory())) .toList(); diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryListComp.java b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryListComp.java index 775e41078..6eaa98f10 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryListComp.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryListComp.java @@ -4,15 +4,11 @@ import io.xpipe.app.comp.base.ListBoxViewComp; import io.xpipe.app.comp.base.MultiContentComp; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.SimpleComp; -import io.xpipe.app.fxcomps.impl.HorizontalComp; - import javafx.beans.binding.Bindings; import javafx.beans.value.ObservableValue; -import javafx.geometry.Insets; import javafx.scene.layout.Region; import java.util.LinkedHashMap; -import java.util.List; public class StoreEntryListComp extends SimpleComp { @@ -28,10 +24,9 @@ public class StoreEntryListComp extends SimpleComp { .getList(), (StoreSection e) -> { var custom = StoreSection.customSection(e, true).hgrow(); - return new HorizontalComp(List.of(Comp.hspacer(8), custom, Comp.hspacer(10))) - .styleClass("top"); - }) - .apply(struc -> ((Region) struc.get().getContent()).setPadding(new Insets(8, 0, 8, 0))); + return custom; + }, + true); return content.styleClass("store-list-comp"); } diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreSectionComp.java b/app/src/main/java/io/xpipe/app/comp/store/StoreSectionComp.java index 1c147133b..e6844a96d 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreSectionComp.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreSectionComp.java @@ -136,7 +136,8 @@ public class StoreSectionComp extends Comp> { var content = new ListBoxViewComp<>( listSections.getList(), section.getAllChildren().getList(), (StoreSection e) -> { return StoreSection.customSection(e, false).apply(GrowAugment.create(true, false)); - }) + }, + false) .minHeight(0) .hgrow(); @@ -150,9 +151,8 @@ public class StoreSectionComp extends Comp> { var full = new VerticalComp(List.of( topEntryList, Comp.separator().hide(expanded.not()), - new HorizontalComp(List.of(content)) + content .styleClass("children-content") - .apply(struc -> struc.get().setFillHeight(true)) .hide(Bindings.or( Bindings.not(section.getWrapper().getExpanded()), Bindings.size(section.getShownChildren().getList()) diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreSectionMiniComp.java b/app/src/main/java/io/xpipe/app/comp/store/StoreSectionMiniComp.java index ce5507b30..4367b46ed 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreSectionMiniComp.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreSectionMiniComp.java @@ -9,7 +9,6 @@ import io.xpipe.app.fxcomps.impl.IconButtonComp; import io.xpipe.app.fxcomps.impl.PrettyImageHelper; import io.xpipe.app.fxcomps.impl.VerticalComp; import io.xpipe.app.storage.DataStoreColor; - import javafx.beans.binding.Bindings; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; @@ -20,7 +19,6 @@ import javafx.scene.layout.VBox; import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -36,17 +34,14 @@ public class StoreSectionMiniComp extends Comp> { private final StoreSection section; private final BiConsumer>> augment; private final Consumer action; - private final boolean condensedStyle; public StoreSectionMiniComp( StoreSection section, BiConsumer>> augment, - Consumer action, - boolean condensedStyle) { + Consumer action) { this.section = section; this.augment = augment; this.action = action; - this.condensedStyle = condensedStyle; } @Override @@ -139,22 +134,19 @@ public class StoreSectionMiniComp extends Comp> { : section.getShownChildren(); var content = new ListBoxViewComp<>( listSections.getList(), section.getAllChildren().getList(), (StoreSection e) -> { - return new StoreSectionMiniComp(e, this.augment, this.action, this.condensedStyle); - }) + return new StoreSectionMiniComp(e, this.augment, this.action); + }, + section.getWrapper() == null) .minHeight(0) .hgrow(); - list.add(new HorizontalComp(List.of(content)) + list.add(content .styleClass("children-content") - .apply(struc -> struc.get().setFillHeight(true)) .hide(Bindings.or( Bindings.not(expanded), Bindings.size(section.getAllChildren().getList()).isEqualTo(0)))); var vert = new VerticalComp(list); - if (condensedStyle) { - vert.styleClass("condensed"); - } return vert.styleClass("store-section-mini-comp") .apply(struc -> { struc.get().setFillWidth(true); 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 4ac460cef..236749bae 100644 --- a/app/src/main/java/io/xpipe/app/core/AppTheme.java +++ b/app/src/main/java/io/xpipe/app/core/AppTheme.java @@ -42,11 +42,15 @@ public class AppTheme { private static boolean init; public static void initThemeHandlers(Stage stage) { - if (AppPrefs.get() == null) { - return; - } - Runnable r = () -> { + if (AppPrefs.get() == null) { + var def = Theme.getDefaultLightTheme(); + stage.getScene().getRoot().getStyleClass().add(def.getCssId()); + stage.getScene().getRoot().pseudoClassStateChanged(LIGHT, true); + stage.getScene().getRoot().pseudoClassStateChanged(DARK, false); + return; + } + AppPrefs.get().theme.subscribe(t -> { Theme.ALL.forEach( theme -> stage.getScene().getRoot().getStyleClass().remove(theme.getCssId())); diff --git a/app/src/main/java/io/xpipe/app/core/window/AppWindowBounds.java b/app/src/main/java/io/xpipe/app/core/window/AppWindowBounds.java index 73fdd7f22..c12fd733e 100644 --- a/app/src/main/java/io/xpipe/app/core/window/AppWindowBounds.java +++ b/app/src/main/java/io/xpipe/app/core/window/AppWindowBounds.java @@ -2,11 +2,9 @@ package io.xpipe.app.core.window; import io.xpipe.app.core.App; import io.xpipe.core.process.OsType; - import javafx.geometry.Rectangle2D; import javafx.stage.Screen; import javafx.stage.Stage; -import javafx.stage.Window; import java.util.Arrays; import java.util.List; @@ -57,7 +55,14 @@ public class AppWindowBounds { var stage = new Stage() { @Override public void centerOnScreen() { - centerToMainWindow(this); + if (App.getApp() == null) { + super.centerOnScreen(); + return; + } + + var stage = App.getApp().getStage(); + this.setX(stage.getX() + stage.getWidth() / 2 - this.getWidth() / 2); + this.setY(stage.getY() + stage.getHeight() / 2 - this.getHeight() / 2); clampWindow(this).ifPresent(rectangle2D -> { this.setX(rectangle2D.getMinX()); this.setY(rectangle2D.getMinY()); @@ -69,17 +74,6 @@ public class AppWindowBounds { return stage; } - public static void centerToMainWindow(Window childStage) { - if (App.getApp() == null) { - childStage.centerOnScreen(); - return; - } - - var stage = App.getApp().getStage(); - childStage.setX(stage.getX() + stage.getWidth() / 2 - childStage.getWidth() / 2); - childStage.setY(stage.getY() + stage.getHeight() / 2 - childStage.getHeight() / 2); - } - public static Optional clampWindow(Stage stage) { if (!areNumbersValid(stage.getWidth(), stage.getHeight())) { return Optional.empty(); diff --git a/app/src/main/java/io/xpipe/app/ext/DataStoreCreationCategory.java b/app/src/main/java/io/xpipe/app/ext/DataStoreCreationCategory.java new file mode 100644 index 000000000..46c304c11 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/ext/DataStoreCreationCategory.java @@ -0,0 +1,13 @@ +package io.xpipe.app.ext; + +public enum DataStoreCreationCategory { + HOST, + DATABASE, + SHELL, + SERVICE, + COMMAND, + TUNNEL, + SCRIPT, + CLUSTER, + DESKTOP +} diff --git a/app/src/main/java/io/xpipe/app/ext/DataStoreProvider.java b/app/src/main/java/io/xpipe/app/ext/DataStoreProvider.java index 3c041878d..88a0c61eb 100644 --- a/app/src/main/java/io/xpipe/app/ext/DataStoreProvider.java +++ b/app/src/main/java/io/xpipe/app/ext/DataStoreProvider.java @@ -51,6 +51,10 @@ public interface DataStoreProvider { throw new ExtensionException( String.format("Store class %s is not a Jacksonized value", storeClass.getSimpleName())); } + + if (getUsageCategory() == null) { + throw new ExtensionException("Provider %s does not have the usage category".formatted(getId())); + } } } @@ -126,7 +130,28 @@ public interface DataStoreProvider { return null; } - default CreationCategory getCreationCategory() { + default DataStoreCreationCategory getCreationCategory() { + return null; + } + + default DataStoreUsageCategory getUsageCategory() { + var cc = getCreationCategory(); + if (cc == DataStoreCreationCategory.SHELL || cc == DataStoreCreationCategory.HOST) { + return DataStoreUsageCategory.SHELL; + } + + if (cc == DataStoreCreationCategory.COMMAND) { + return DataStoreUsageCategory.COMMAND; + } + + if (cc == DataStoreCreationCategory.SCRIPT) { + return DataStoreUsageCategory.SCRIPT; + } + + if (cc == DataStoreCreationCategory.DATABASE) { + return DataStoreUsageCategory.DATABASE; + } + return null; } @@ -209,15 +234,4 @@ public interface DataStoreProvider { List> getStoreClasses(); - enum CreationCategory { - HOST, - DATABASE, - SHELL, - SERVICE, - COMMAND, - TUNNEL, - SCRIPT, - CLUSTER, - DESKTOP - } } diff --git a/app/src/main/java/io/xpipe/app/ext/DataStoreUsageCategory.java b/app/src/main/java/io/xpipe/app/ext/DataStoreUsageCategory.java new file mode 100644 index 000000000..d88f724df --- /dev/null +++ b/app/src/main/java/io/xpipe/app/ext/DataStoreUsageCategory.java @@ -0,0 +1,20 @@ +package io.xpipe.app.ext; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public enum DataStoreUsageCategory { + @JsonProperty("shell") + SHELL, + @JsonProperty("tunnel") + TUNNEL, + @JsonProperty("script") + SCRIPT, + @JsonProperty("database") + DATABASE, + @JsonProperty("command") + COMMAND, + @JsonProperty("desktop") + DESKTOP, + @JsonProperty("group") + GROUP; +} diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/DataStoreChoiceComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/DataStoreChoiceComp.java index d07347234..13702de4b 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/DataStoreChoiceComp.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/DataStoreChoiceComp.java @@ -108,8 +108,7 @@ public class DataStoreChoiceComp extends SimpleComp { selected.setValue(storeEntryWrapper.getEntry().ref()); popover.hide(); } - }, - true); + }); var category = new DataStoreCategoryChoiceComp( initialCategory != null ? initialCategory.getRoot() : null, StoreViewState.get().getActiveCategory(), diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/DataStoreListChoiceComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/DataStoreListChoiceComp.java index 3799e110c..86b5152ba 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/DataStoreListChoiceComp.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/DataStoreListChoiceComp.java @@ -49,7 +49,8 @@ public class DataStoreListChoiceComp extends SimpleComp { selectedList.remove(t); }); return new HorizontalComp(List.of(label, Comp.hspacer(), delete)).styleClass("entry"); - }) + }, + true) .padding(new Insets(0)) .apply(struc -> struc.get().setMinHeight(0)) .apply(struc -> ((VBox) struc.get().getContent()).setSpacing(5)); diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/StoreCategoryComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/StoreCategoryComp.java index c50305f9a..2eb8fb41e 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/StoreCategoryComp.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/StoreCategoryComp.java @@ -113,7 +113,7 @@ public class StoreCategoryComp extends SimpleComp { var l = category.getChildren() .sorted(Comparator.comparing(storeCategoryWrapper -> storeCategoryWrapper.nameProperty().getValue().toLowerCase(Locale.ROOT))); - var children = new ListBoxViewComp<>(l, l, storeCategoryWrapper -> new StoreCategoryComp(storeCategoryWrapper)); + var children = new ListBoxViewComp<>(l, l, storeCategoryWrapper -> new StoreCategoryComp(storeCategoryWrapper), false); var emptyBinding = Bindings.isEmpty(category.getChildren()); var v = new VerticalComp(List.of(categoryButton, children.hide(emptyBinding))); diff --git a/app/src/main/java/module-info.java b/app/src/main/java/module-info.java index 9cd509b3f..b27be6e48 100644 --- a/app/src/main/java/module-info.java +++ b/app/src/main/java/module-info.java @@ -134,6 +134,7 @@ open module io.xpipe.app { ShellStopExchangeImpl, ShellExecExchangeImpl, ConnectionQueryExchangeImpl, + ConnectionInfoExchangeImpl, DaemonOpenExchangeImpl, DaemonFocusExchangeImpl, DaemonStatusExchangeImpl, diff --git a/app/src/main/resources/io/xpipe/app/resources/misc/api.md b/app/src/main/resources/io/xpipe/app/resources/misc/api.md index 52c00a92c..4660fb53c 100644 --- a/app/src/main/resources/io/xpipe/app/resources/misc/api.md +++ b/app/src/main/resources/io/xpipe/app/resources/misc/api.md @@ -447,6 +447,174 @@ curl -X POST http://localhost:21723/connection/query \ +## Connection information + + + +`POST /connection/info` + +Queries detailed information about a connection. + +> Body parameter + +```json +{ + "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b" +} +``` + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[ConnectionInfoRequest](#schemaconnectioninforequest)|true|none| + +> Example responses + +> 200 Response + +```json +{ + "connection": "string", + "category": [ + "string" + ], + "name": [ + "string" + ], + "type": "string", + "rawData": {}, + "usageCategory": "shell", + "lastModified": "string", + "lastUsed": "string" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|The query was successful. The body contains the detailed connection information.|[ConnectionInfoResponse](#schemaconnectioninforesponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Please check error message and your parameters.|[ClientErrorResponse](#schemaclienterrorresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Authorization failed. Please supply a `Bearer` token via the `Authorization` header.|None| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Authorization failed. Please supply a valid `Bearer` token via the `Authorization` header.|None| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|The requested resource could not be found.|None| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal error.|[ServerErrorResponse](#schemaservererrorresponse)| + + + +
+ +Code samples + +```javascript +const inputBody = '{ + "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b" +}'; +const headers = { + 'Content-Type':'application/json', + 'Accept':'application/json', + 'Authorization':'Bearer {access-token}' +}; + +fetch('http://localhost:21723/connection/info', +{ + method: 'POST', + body: inputBody, + headers: headers +}) +.then(function(res) { + return res.json(); +}).then(function(body) { + console.log(body); +}); + +``` + +```python +import requests +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': 'Bearer {access-token}' +} + +data = """ +{ + "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b" +} +""" +r = requests.post('http://localhost:21723/connection/info', headers = headers, data = data) + +print(r.json()) + +``` + +```java +var uri = URI.create("http://localhost:21723/connection/info"); +var client = HttpClient.newHttpClient(); +var request = HttpRequest + .newBuilder() + .uri(uri) + .header("Content-Type", "application/json") + .header("Accept", "application/json") + .header("Authorization", "Bearer {access-token}") + .POST(HttpRequest.BodyPublishers.ofString(""" +{ + "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b" +} + """)) + .build(); +var response = client.send(request, HttpResponse.BodyHandlers.ofString()); +System.out.println(response.statusCode()); +System.out.println(response.body()); + +``` + +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + "Authorization": []string{"Bearer {access-token}"}, + } + + data := bytes.NewBuffer([]byte{jsonReq}) + req, err := http.NewRequest("POST", "http://localhost:21723/connection/info", data) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} + +``` + +```shell +# You can also use wget +curl -X POST http://localhost:21723/connection/info \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'Authorization: Bearer {access-token}' \ + --data ' +{ + "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b" +} +' + +``` + +
+ ## Start shell connection @@ -1868,6 +2036,76 @@ curl -X POST http://localhost:21723/fs/script \ |» name|[string]|true|none|The full connection name path as an array| |» type|string|true|none|The type identifier of the connection| +

ConnectionInfoRequest

+ + + + + + +```json +{ + "connection": "string" +} + +``` + +

Properties

+ +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|connection|string|true|none|The unique id of the connection| + +

ConnectionInfoResponse

+ + + + + + +```json +{ + "connection": "string", + "category": [ + "string" + ], + "name": [ + "string" + ], + "type": "string", + "rawData": {}, + "usageCategory": "shell", + "lastModified": "string", + "lastUsed": "string" +} + +``` + +

Properties

+ +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|connection|string|true|none|The unique id of the connection| +|category|[string]|true|none|The full category path as an array| +|name|[string]|true|none|The full connection name path as an array| +|type|string|true|none|The type identifier of the connection| +|rawData|object|true|none|The raw internal configuration data for the connection. The schema for these is internal and should not be relied upon.| +|usageCategory|string|true|none|The category of how this connection can be used.| +|lastModified|string|true|none|The timestamp of when the connection configuration was last modified in ISO 8601.| +|lastUsed|string|true|none|The timestamp of when the connection was last launched in ISO 8601.| + +#### Enumerated Values + +|Property|Value| +|---|---| +|usageCategory|shell| +|usageCategory|tunnel| +|usageCategory|script| +|usageCategory|database| +|usageCategory|command| +|usageCategory|desktop| +|usageCategory|group| +

HandshakeRequest

diff --git a/app/src/main/resources/io/xpipe/app/resources/style/error-handler-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/error-handler-comp.css index bd56cf7d2..3b08cdd1e 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/error-handler-comp.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/error-handler-comp.css @@ -9,6 +9,7 @@ .error-handler-comp .details { -fx-padding: 0 1.5em 1.0em 1.5em; + -fx-background-color: transparent; } .error-report .report { diff --git a/app/src/main/resources/io/xpipe/app/resources/style/scrollbar.css b/app/src/main/resources/io/xpipe/app/resources/style/scrollbar.css index d92df89d2..05cf4145f 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/scrollbar.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/scrollbar.css @@ -1,6 +1,7 @@ .scroll-bar:vertical { + -fx-min-width: 0.4em; -fx-pref-width: 0.4em; - -fx-padding: 0.4em 1 0.3em 1; + -fx-padding: 0.1em 1 0.1em 1; -fx-background-color: transparent; } diff --git a/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css index 77235336d..933c5783e 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css @@ -1,8 +1,13 @@ -.store-list-comp .top { - -fx-border-width: 0 0 0.5em 0; - -fx-background-insets: 0 0 0.5em 0; - -fx-border-color: transparent; - -fx-background-color: transparent; +.store-list-comp.scroll-pane > .viewport .list-box-content { + -fx-spacing: 8; +} + +.store-list-comp.scroll-pane { + -fx-padding: 8 2 0 6; +} + +.store-list-comp.scroll-pane .scroll-bar:vertical { + -fx-padding: 0.6em 1 0.3em 1; } /* Grid */ diff --git a/app/src/main/resources/io/xpipe/app/resources/style/store-mini-section.css b/app/src/main/resources/io/xpipe/app/resources/style/store-mini-section.css index 06372a562..bff23af37 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/store-mini-section.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/store-mini-section.css @@ -36,12 +36,8 @@ -fx-opacity: 0.2; } -.store-section-mini-comp.condensed .expand-button { - -fx-background-radius: 0; -} - .store-section-mini-comp .expand-button { - -fx-background-radius: 4 0 0 4; + -fx-background-radius: 0; } .store-section-mini-comp .quick-access-button:hover, .root:key-navigation .store-section-mini-comp .quick-access-button:focused { @@ -66,10 +62,6 @@ } .store-section-mini-comp:root > .children-content { - -fx-padding: 0.5em 1em 0.5em 1em; -} - -.store-section-mini-comp.condensed:root > .children-content { -fx-padding: 0; } @@ -102,18 +94,12 @@ -fx-border-width: 0; } -.store-section-mini-comp.condensed:top { +.store-section-mini-comp:top { -fx-border-radius: 0; -fx-border-width: 1px 1 1px 0px; -fx-border-color: -color-border-default; } -.store-section-mini-comp:top { - -fx-border-radius: 4 0 0 4; - -fx-border-width: 1 1 1 1; - -fx-border-color: -color-border-default; -} - .store-section-mini-comp:root .list-box-view-comp .list-box-content { -fx-spacing: 0.4em; } 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 5ca46f97a..d63e07ad4 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 @@ -32,7 +32,7 @@ -fx-border-radius: 0 10 0 0; -fx-border-width: 1 1 0 0; -fx-border-color: -color-border-default; - -fx-padding: 0 2 0 0; + -fx-padding: 0 0 0 0; } .root:seamless-frame.layout > .background > * { diff --git a/beacon/src/main/java/io/xpipe/beacon/api/ConnectionInfoExchange.java b/beacon/src/main/java/io/xpipe/beacon/api/ConnectionInfoExchange.java new file mode 100644 index 000000000..1e0d6d1c1 --- /dev/null +++ b/beacon/src/main/java/io/xpipe/beacon/api/ConnectionInfoExchange.java @@ -0,0 +1,56 @@ +package io.xpipe.beacon.api; + +import io.xpipe.beacon.BeaconInterface; +import io.xpipe.core.store.StorePath; +import lombok.Builder; +import lombok.NonNull; +import lombok.Value; +import lombok.extern.jackson.Jacksonized; + +import java.time.Instant; +import java.util.UUID; + +public class ConnectionInfoExchange extends BeaconInterface { + + @Override + public String getPath() { + return "/connection/info"; + } + + @Jacksonized + @Builder + @Value + public static class Request { + @NonNull + UUID connection; + } + + @Jacksonized + @Builder + @Value + public static class Response { + @NonNull + UUID connection; + + @NonNull + StorePath category; + + @NonNull + StorePath name; + + @NonNull + String type; + + @NonNull + Object rawData; + + @NonNull + Object usageCategory; + + @NonNull + Instant lastUsed; + + @NonNull + Instant lastModified; + } +} diff --git a/beacon/src/main/java/module-info.java b/beacon/src/main/java/module-info.java index 98f47d2ea..7cc558c9a 100644 --- a/beacon/src/main/java/module-info.java +++ b/beacon/src/main/java/module-info.java @@ -37,6 +37,7 @@ open module io.xpipe.beacon { DaemonStopExchange, HandshakeExchange, ConnectionQueryExchange, + ConnectionInfoExchange, AskpassExchange, TerminalWaitExchange, TerminalLaunchExchange, diff --git a/core/src/main/java/io/xpipe/core/util/JacksonMapper.java b/core/src/main/java/io/xpipe/core/util/JacksonMapper.java index 0b3dd93c6..ccb028425 100644 --- a/core/src/main/java/io/xpipe/core/util/JacksonMapper.java +++ b/core/src/main/java/io/xpipe/core/util/JacksonMapper.java @@ -27,6 +27,7 @@ public class JacksonMapper { objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); objectMapper.disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE); + objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); objectMapper.setVisibility(objectMapper .getSerializationConfig() .getDefaultVisibilityChecker() diff --git a/ext/base/src/main/java/io/xpipe/ext/base/desktop/DesktopApplicationStoreProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/desktop/DesktopApplicationStoreProvider.java index 447bf1843..ec9b14e49 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/desktop/DesktopApplicationStoreProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/desktop/DesktopApplicationStoreProvider.java @@ -3,9 +3,7 @@ package io.xpipe.ext.base.desktop; import io.xpipe.app.browser.session.BrowserSessionModel; import io.xpipe.app.comp.store.StoreEntryWrapper; import io.xpipe.app.comp.store.StoreViewState; -import io.xpipe.app.ext.ActionProvider; -import io.xpipe.app.ext.DataStoreProvider; -import io.xpipe.app.ext.GuiDialog; +import io.xpipe.app.ext.*; import io.xpipe.app.fxcomps.impl.DataStoreChoiceComp; import io.xpipe.app.storage.ContextualFileReference; import io.xpipe.app.storage.DataStoreEntry; @@ -22,6 +20,11 @@ import java.util.List; public class DesktopApplicationStoreProvider implements DataStoreProvider { + @Override + public DataStoreUsageCategory getUsageCategory() { + return DataStoreUsageCategory.DESKTOP; + } + @Override public ActionProvider.Action browserAction( BrowserSessionModel sessionModel, DataStoreEntry store, BooleanProperty busy) { @@ -46,8 +49,8 @@ public class DesktopApplicationStoreProvider implements DataStoreProvider { } @Override - public CreationCategory getCreationCategory() { - return CreationCategory.DESKTOP; + public DataStoreCreationCategory getCreationCategory() { + return DataStoreCreationCategory.DESKTOP; } @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/desktop/DesktopCommandStoreProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/desktop/DesktopCommandStoreProvider.java index 78baedf90..44b92f053 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/desktop/DesktopCommandStoreProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/desktop/DesktopCommandStoreProvider.java @@ -4,9 +4,7 @@ import io.xpipe.app.browser.session.BrowserSessionModel; import io.xpipe.app.comp.base.IntegratedTextAreaComp; import io.xpipe.app.comp.store.StoreEntryWrapper; import io.xpipe.app.comp.store.StoreViewState; -import io.xpipe.app.ext.ActionProvider; -import io.xpipe.app.ext.DataStoreProvider; -import io.xpipe.app.ext.GuiDialog; +import io.xpipe.app.ext.*; import io.xpipe.app.fxcomps.impl.DataStoreChoiceComp; import io.xpipe.app.storage.DataStoreEntry; import io.xpipe.app.util.DataStoreFormatter; @@ -23,6 +21,11 @@ import java.util.List; public class DesktopCommandStoreProvider implements DataStoreProvider { + @Override + public DataStoreUsageCategory getUsageCategory() { + return DataStoreUsageCategory.DESKTOP; + } + @Override public ActionProvider.Action browserAction( BrowserSessionModel sessionModel, DataStoreEntry store, BooleanProperty busy) { @@ -47,8 +50,8 @@ public class DesktopCommandStoreProvider implements DataStoreProvider { } @Override - public CreationCategory getCreationCategory() { - return CreationCategory.DESKTOP; + public DataStoreCreationCategory getCreationCategory() { + return DataStoreCreationCategory.DESKTOP; } @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/desktop/DesktopEnvironmentStoreProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/desktop/DesktopEnvironmentStoreProvider.java index dc9972ba5..526b13b17 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/desktop/DesktopEnvironmentStoreProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/desktop/DesktopEnvironmentStoreProvider.java @@ -5,9 +5,7 @@ import io.xpipe.app.comp.base.IntegratedTextAreaComp; import io.xpipe.app.comp.store.StoreEntryWrapper; import io.xpipe.app.comp.store.StoreViewState; import io.xpipe.app.core.AppExtensionManager; -import io.xpipe.app.ext.ActionProvider; -import io.xpipe.app.ext.DataStoreProvider; -import io.xpipe.app.ext.GuiDialog; +import io.xpipe.app.ext.*; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.impl.ChoiceComp; import io.xpipe.app.fxcomps.impl.DataStoreChoiceComp; @@ -30,6 +28,11 @@ import java.util.List; public class DesktopEnvironmentStoreProvider implements DataStoreProvider { + @Override + public DataStoreUsageCategory getUsageCategory() { + return DataStoreUsageCategory.DESKTOP; + } + @Override public ActionProvider.Action browserAction( BrowserSessionModel sessionModel, DataStoreEntry store, BooleanProperty busy) { @@ -75,8 +78,8 @@ public class DesktopEnvironmentStoreProvider implements DataStoreProvider { } @Override - public CreationCategory getCreationCategory() { - return CreationCategory.DESKTOP; + public DataStoreCreationCategory getCreationCategory() { + return DataStoreCreationCategory.DESKTOP; } @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptGroupStoreProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptGroupStoreProvider.java index d1dba8e02..ebbe867f0 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptGroupStoreProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptGroupStoreProvider.java @@ -3,9 +3,7 @@ package io.xpipe.ext.base.script; import io.xpipe.app.comp.base.SystemStateComp; import io.xpipe.app.comp.store.StoreEntryWrapper; import io.xpipe.app.comp.store.StoreViewState; -import io.xpipe.app.ext.DataStoreProvider; -import io.xpipe.app.ext.EnabledStoreProvider; -import io.xpipe.app.ext.GuiDialog; +import io.xpipe.app.ext.*; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.impl.DataStoreChoiceComp; import io.xpipe.app.storage.DataStoreEntry; @@ -23,14 +21,19 @@ import java.util.List; public class ScriptGroupStoreProvider implements EnabledStoreProvider, DataStoreProvider { + @Override + public DataStoreUsageCategory getUsageCategory() { + return DataStoreUsageCategory.GROUP; + } + @Override public Comp stateDisplay(StoreEntryWrapper w) { return new SystemStateComp(new SimpleObjectProperty<>(SystemStateComp.State.SUCCESS)); } @Override - public CreationCategory getCreationCategory() { - return CreationCategory.SCRIPT; + public DataStoreCreationCategory getCreationCategory() { + return DataStoreCreationCategory.SCRIPT; } @Override 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 0faa37dcc..2a11010a8 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 @@ -7,6 +7,7 @@ import io.xpipe.app.comp.store.StoreEntryWrapper; import io.xpipe.app.comp.store.StoreViewState; import io.xpipe.app.core.AppExtensionManager; import io.xpipe.app.core.AppI18n; +import io.xpipe.app.ext.DataStoreCreationCategory; import io.xpipe.app.ext.DataStoreProvider; import io.xpipe.app.ext.EnabledParentStoreProvider; import io.xpipe.app.ext.GuiDialog; @@ -82,8 +83,8 @@ public class SimpleScriptStoreProvider implements EnabledParentStoreProvider, Da } @Override - public CreationCategory getCreationCategory() { - return CreationCategory.SCRIPT; + public DataStoreCreationCategory getCreationCategory() { + return DataStoreCreationCategory.SCRIPT; } @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/service/AbstractServiceGroupStoreProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/service/AbstractServiceGroupStoreProvider.java index 726456863..164927050 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/service/AbstractServiceGroupStoreProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/service/AbstractServiceGroupStoreProvider.java @@ -7,6 +7,7 @@ import io.xpipe.app.comp.store.StoreEntryWrapper; import io.xpipe.app.comp.store.StoreSection; import io.xpipe.app.comp.store.StoreViewState; import io.xpipe.app.ext.DataStoreProvider; +import io.xpipe.app.ext.DataStoreUsageCategory; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStoreEntry; @@ -18,6 +19,11 @@ import javafx.beans.property.SimpleObjectProperty; public abstract class AbstractServiceGroupStoreProvider implements DataStoreProvider { + @Override + public DataStoreUsageCategory getUsageCategory() { + return DataStoreUsageCategory.GROUP; + } + @Override public StoreEntryComp customEntryComp(StoreSection sec, boolean preferLarge) { var t = createToggleComp(sec); 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 979f0875a..eb7053f33 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 @@ -6,6 +6,7 @@ import io.xpipe.app.comp.store.StoreEntryWrapper; import io.xpipe.app.comp.store.StoreSection; import io.xpipe.app.ext.ActionProvider; import io.xpipe.app.ext.DataStoreProvider; +import io.xpipe.app.ext.DataStoreUsageCategory; import io.xpipe.app.ext.SingletonSessionStoreProvider; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.storage.DataStorage; @@ -21,6 +22,11 @@ import java.util.List; public abstract class AbstractServiceStoreProvider implements SingletonSessionStoreProvider, DataStoreProvider { + @Override + public DataStoreUsageCategory getUsageCategory() { + return DataStoreUsageCategory.TUNNEL; + } + @Override public ActionProvider.Action launchAction(DataStoreEntry store) { return new ActionProvider.Action() { diff --git a/ext/base/src/main/java/io/xpipe/ext/base/service/CustomServiceStoreProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/service/CustomServiceStoreProvider.java index f65c4d7b9..151d1b823 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/service/CustomServiceStoreProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/service/CustomServiceStoreProvider.java @@ -1,6 +1,7 @@ package io.xpipe.ext.base.service; import io.xpipe.app.comp.store.StoreViewState; +import io.xpipe.app.ext.DataStoreCreationCategory; import io.xpipe.app.ext.GuiDialog; import io.xpipe.app.fxcomps.impl.DataStoreChoiceComp; import io.xpipe.app.storage.DataStoreEntry; @@ -16,8 +17,8 @@ import java.util.List; public class CustomServiceStoreProvider extends AbstractServiceStoreProvider { @Override - public CreationCategory getCreationCategory() { - return CreationCategory.SERVICE; + public DataStoreCreationCategory getCreationCategory() { + return DataStoreCreationCategory.SERVICE; } @Override diff --git a/openapi.yaml b/openapi.yaml index d00f5fd8e..bb59612c6 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -118,6 +118,39 @@ paths: $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' + /connection/info: + post: + summary: Connection information + description: | + Queries detailed information about a connection. + operationId: connectionInfo + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ConnectionInfoRequest' + examples: + simple: + summary: Standard + value: { "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b" } + responses: + '200': + description: The query was successful. The body contains the detailed connection information. + content: + application/json: + schema: + $ref: '#/components/schemas/ConnectionInfoResponse' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalServerError' /shell/start: post: summary: Start shell connection @@ -520,6 +553,64 @@ components: - type required: - found + ConnectionInfoRequest: + type: object + properties: + connection: + type: string + description: The unique id of the connection + required: + - connection + ConnectionInfoResponse: + type: object + properties: + connection: + type: string + description: The unique id of the connection + category: + type: array + description: The full category path as an array + items: + type: string + description: Individual category name + name: + type: array + description: The full connection name path as an array + items: + type: string + description: Individual connection name + type: + type: string + description: The type identifier of the connection + rawData: + type: object + description: The raw internal configuration data for the connection. The schema for these is internal and should not be relied upon. + usageCategory: + type: string + description: The category of how this connection can be used. + enum: + - shell + - tunnel + - script + - database + - command + - desktop + - group + lastModified: + type: string + description: The timestamp of when the connection configuration was last modified in ISO 8601. + lastUsed: + type: string + description: The timestamp of when the connection was last launched in ISO 8601. + required: + - connection + - category + - name + - type + - rawData + - usageCategory + - lastUsed + - lastModified HandshakeRequest: type: object properties: