This commit is contained in:
crschnick
2026-03-13 23:19:56 +00:00
parent 318f4e9665
commit 80d6b49cf4
12 changed files with 110 additions and 34 deletions
@@ -1,6 +1,8 @@
package io.xpipe.app.action;
import io.xpipe.app.ext.DataStore;
import io.xpipe.app.ext.DataStoreProvider;
import io.xpipe.app.ext.DataStoreProviders;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
@@ -28,4 +30,10 @@ public interface QuickConnectProvider extends ActionProvider {
DataStore createStore(String arguments, DataStore existing);
String getPlaceholder();
boolean skipDialogIfPossible();
default void open(DataStoreEntry e) throws Exception {
e.getProvider().launch(e).run();
}
}
@@ -11,7 +11,7 @@ public class XPipeUrlProvider implements LauncherUrlProvider {
@Override
public String getPlaceholder() {
return "xpipe://action?<name>[&param=value]";
return "xpipe://action?<name>";
}
@Override
@@ -2,8 +2,10 @@ package io.xpipe.app.beacon;
import io.xpipe.app.beacon.mcp.AppMcpServer;
import io.xpipe.app.core.AppLocalTemp;
import io.xpipe.app.core.AppProperties;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.DocumentationLink;
import io.xpipe.beacon.BeaconConfig;
import io.xpipe.beacon.BeaconInterface;
@@ -170,13 +172,15 @@ public class AppBeaconServer {
}
private boolean handleCorsHeaders(HttpExchange exchange) throws IOException {
exchange.getResponseHeaders()
.add("Origin", "http://localhost:" + AppBeaconServer.get().getPort());
exchange.getResponseHeaders().add("Vary", "Origin");
exchange.getResponseHeaders().add("Access-Control-Allow-Origin", "*");
exchange.getResponseHeaders().add("Access-Control-Allow-Credentials", "true");
exchange.getResponseHeaders().add("Access-Control-Allow-Headers", "*");
exchange.getResponseHeaders().add("Access-Control-Allow-Methods", "*");
if (AppPrefs.get().enableHttpApi().get()) {
exchange.getResponseHeaders().add("Origin", "http://localhost:" + AppBeaconServer.get().getPort());
exchange.getResponseHeaders().add("Vary", "Origin");
exchange.getResponseHeaders().add("Access-Control-Allow-Origin", "*");
exchange.getResponseHeaders().add("Access-Control-Allow-Credentials", "true");
exchange.getResponseHeaders().add("Access-Control-Allow-Headers", "*");
exchange.getResponseHeaders().add("Access-Control-Allow-Methods", "*");
}
if (exchange.getRequestMethod().equals("OPTIONS")) {
exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, -1);
return true;
@@ -213,6 +213,7 @@ public class AppBaseMode extends AppOperationMode {
AppBeaconServer.reset();
KeePassXcPasswordManager.reset();
StoreViewState.reset();
StoreFilterState.reset();
AppLayoutModel.reset();
AppTheme.reset();
PlatformState.teardown();
@@ -15,6 +15,7 @@ import io.xpipe.app.platform.PlatformThread;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.ObservableSubscriber;
import javafx.beans.binding.Bindings;
import javafx.geometry.Bounds;
import javafx.scene.Cursor;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
@@ -51,6 +52,10 @@ public class StoreFilterFieldComp extends SimpleRegionBuilder {
field.focusedProperty().subscribe(focus -> {
if (focus) {
popover.hide();
} else {
if (state.getIsSearchString().get()) {
state.open();
}
}
});
@@ -114,9 +119,18 @@ public class StoreFilterFieldComp extends SimpleRegionBuilder {
state.getRawText().subscribe(val -> {
PlatformThread.runLaterIfNeeded(() -> {
var wasFocused = field.isFocused();
if (!wasFocused) {
field.requestFocus();
}
if (!Objects.equals(field.getText(), val) && !(val == null && "".equals(field.getText()))) {
field.setText(val);
}
if (!wasFocused) {
field.end();
}
});
});
@@ -125,7 +139,8 @@ public class StoreFilterFieldComp extends SimpleRegionBuilder {
});
var menuButton = new IconButtonComp("mdi2a-animation-play", () -> {
popover.show(field);
Bounds bounds = field.localToScreen(field.getBoundsInLocal());
popover.show(field, bounds.getMinX() + (field.getWidth() / 1.6), bounds.getMaxY() - 4.0);
});
menuButton.describe(d -> d.nameKey("quickConnect"));
menuButton.style("quick-connect-button");
@@ -84,10 +84,10 @@ public class StoreFilterState {
INSTANCE.recentUrls.setContent(recentUrls);
}
public void reset() {
AppCache.update("recentSearches", recentSearches);
AppCache.update("recentQuickConnections", recentQuickConnections);
AppCache.update("recentUrls", recentUrls);
public static void reset() {
AppCache.update("recentSearches", INSTANCE.recentSearches.getList());
AppCache.update("recentQuickConnections", INSTANCE.recentQuickConnections.getList());
AppCache.update("recentUrls", INSTANCE.recentUrls.getList());
INSTANCE = null;
}
@@ -135,6 +135,10 @@ public class StoreFilterState {
}
public boolean open() {
if (rawText.getValue() == null) {
return false;
}
if (isSearchString.getValue()) {
putFilter(rawText.getValue());
return false;
@@ -38,7 +38,7 @@ public class StoreFilterStateComp extends SimpleRegionBuilder {
var searches = state.getRecentSearches().getList();
var searchesEmpty = Bindings.isEmpty(searches);
var searchesList = new ListBoxViewComp<String>(searches, searches, s -> createButton(s, null), false);
var searchesList = new ListBoxViewComp<String>(searches, searches, s -> createButton(s, s), false);
var searchesPlaceholders = FXCollections.observableList(List.of(AppI18n.get("recentSearchesDescriptionNames"),
AppI18n.get("recentSearchesDescriptionTags"), AppI18n.get("recentSearchesDescriptionTypes")));
@@ -46,16 +46,16 @@ public class StoreFilterStateComp extends SimpleRegionBuilder {
var quickConnections = state.getRecentQuickConnections().getList();
var quickConnectionsEmpty = Bindings.isEmpty(quickConnections);
var quickConnectionsList = new ListBoxViewComp<String>(quickConnections, quickConnections, s -> createButton(s, null), false);
var quickConnectionsList = new ListBoxViewComp<String>(quickConnections, quickConnections, s -> createButton(s, s), false);
var quickConnectionsPlaceholders = FXCollections.observableArrayList(QuickConnectProvider.getAll().stream()
.map(p -> p.getPlaceholder())
.toList());
var quickConnectionsEmptyList = new ListBoxViewComp<String>(quickConnectionsPlaceholders, quickConnectionsPlaceholders, s -> createButton(s, null), false);
var quickConnectionsEmptyList = new ListBoxViewComp<String>(quickConnectionsPlaceholders, quickConnectionsPlaceholders, s -> createButton(s, s.split(" ")[0] + " "), false);
var urls = state.getRecentUrls().getList();
var urlsEmpty = Bindings.isEmpty(urls);
var urlList = new ListBoxViewComp<String>(urls, urls, s -> createButton(s, null), false);
var urlList = new ListBoxViewComp<String>(urls, urls, s -> createButton(s, s), false);
var urlPlaceholders = FXCollections.observableArrayList(LauncherUrlProvider.getAll().stream()
.map(p -> p.getPlaceholder())
@@ -71,23 +71,39 @@ public class StoreQuickConnect {
if (newStore.isComplete()) {
var existing = provider.get().findExisting(newStore);
if (existing.isPresent()) {
try {
existing.get().getProvider().launch(existing.get()).run();
return true;
} catch (Exception e) {
ErrorEventFactory.fromThrowable(e).handle();
return false;
}
ThreadHelper.runAsync(() -> {
try {
provider.get().open(existing.get());
} catch (Exception e) {
ErrorEventFactory.fromThrowable(e).handle();
}
});
return true;
}
}
DataStorage.get().updateEntryStore(quickConnectEntry, newStore);
if (provider.get().skipDialogIfPossible() && newStore.isComplete()) {
update(newStore);
ThreadHelper.runAsync(() -> {
try {
DataStorage.get().addStoreEntryInProgress(quickConnectEntry);
provider.get().open(quickConnectEntry);
} catch (Exception e) {
ErrorEventFactory.fromThrowable(e).handle();
} finally {
DataStorage.get().removeStoreEntryInProgress(quickConnectEntry);
}
});
return true;
}
var model = StoreCreationDialog.showEdit(quickConnectEntry, newStore, false, finished -> {
update(finished.getStore());
ThreadHelper.runAsync(() -> {
try {
DataStorage.get().addStoreEntryInProgress(quickConnectEntry);
quickConnectEntry.getProvider().launch(quickConnectEntry).run();
provider.get().open(quickConnectEntry);
} catch (Exception e) {
ErrorEventFactory.fromThrowable(e).handle();
} finally {
@@ -336,7 +336,7 @@ public abstract class DataStorage {
} catch (Exception e) {
return false;
}
} while ((c = DataStorage.get().getDefaultDisplayParent(c).orElse(null)) != null);
} while ((c = getDefaultDisplayParent(c).orElse(null)) != null);
return true;
}
@@ -359,8 +359,8 @@ public abstract class DataStorage {
newEntry.setIcon(icon, true);
}
var oldParent = DataStorage.get().getDefaultDisplayParent(entry);
var newParent = DataStorage.get().getDefaultDisplayParent(newEntry);
var oldParent = getDefaultDisplayParent(entry);
var newParent = getDefaultDisplayParent(newEntry);
var sameParent = Objects.equals(oldParent, newParent);
finalizeWithDependencies(entry);
@@ -457,7 +457,9 @@ public abstract class DataStorage {
}
entry.setStoreInternal(store, false);
saveAsync();
if (storeEntries.containsKey(entry)) {
saveAsync();
}
}
public void updateCategory(DataStoreCategory category, DataStoreCategory newCategory) {
@@ -470,7 +472,7 @@ public abstract class DataStorage {
public void updateCategoryConfig(DataStoreCategory category, DataStoreCategoryConfig config) {
if (category.setConfig(config)) {
// Update git remote if needed
DataStorage.get().saveAsync();
saveAsync();
}
}
@@ -22,6 +22,23 @@ public class HostHelper {
}
}
public static boolean isLocalhost(String host) {
host = host.strip();
if (host.equalsIgnoreCase("localhost")) {
return true;
}
if (host.equals("127.0.0.1")) {
return true;
}
if (host.equals("::1") || host.equals("[::1]")) {
return true;
}
return false;
}
public static boolean isLocalNetworkAddress(String host) {
Inet4Address inet4Address;
try {
@@ -34,6 +34,7 @@ public class MultiIdentityStoreProvider extends IdentityStoreProvider {
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
MultiIdentityStore st = (MultiIdentityStore) store.getValue();
var initialIdentities = new ArrayList<>(st.getAvailableIdentities());
var identities = new SimpleListProperty<>(FXCollections.observableArrayList(st.getAvailableIdentities()));
return new OptionsBuilder()
@@ -47,7 +48,14 @@ public class MultiIdentityStoreProvider extends IdentityStoreProvider {
for (DataStoreEntryRef<IdentityStore> identity : identities) {
uuids.add(identity.get().getUuid());
}
uuids.addAll(st.getIdentities());
for (UUID storeIdentity : st.getIdentities()) {
if (initialIdentities.stream().anyMatch(ref -> ref.get().getUuid().equals(storeIdentity))) {
var wasRemoved = identities.stream().noneMatch(ref -> ref.get().getUuid().equals(storeIdentity));
if (!wasRemoved) {
uuids.add(storeIdentity);
}
}
}
return MultiIdentityStore.builder()
.identities(new ArrayList<>(uuids))
+4 -3
View File
@@ -802,7 +802,8 @@ identityChoice=User identity
identityChoiceDescription=Choose a predefined identity or specify login details just for this connection
defineNewIdentityOrSelect=Enter new or choose existing
localIdentity.displayName=Local identity
localIdentity.displayDescription=Create a reusable identity for this local desktop
#force
localIdentity.displayDescription=Create a reusable identity for this local system
syncedIdentity.displayName=Synced identity
syncedIdentity.displayDescription=Create a reusable identity that is synced across systems
localIdentity=Local identity
@@ -2045,6 +2046,6 @@ recentSearchesDescriptionNames=<name>, e.g. "Local Machine"
recentSearchesDescriptionTags=<tag>, e.g. "my-custom-tag"
recentSearchesDescriptionTypes=<type>, e.g. "Docker container"
recentQuickConnections=Recent quick connections
recentQuickConnectionsDescription=Quick connect via connection string
recentQuickConnectionsDescription=Quick connect via string
recentUrls=Recent URLs
recentUrlsDescription=Quick connect via connection URLs
recentUrlsDescription=Quick connect via URLs