mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-22 07:30:24 +00:00
Small fixes and polishing
This commit is contained in:
parent
7995d95b8d
commit
06d9c777fc
17 changed files with 228 additions and 166 deletions
|
@ -80,6 +80,15 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
|
||||||
vbox.getChildren().add(b.createRegion());
|
vbox.getChildren().add(b.createRegion());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var b = new IconButtonComp("mdi2c-comment-processing-outline", () -> Hyperlinks.open(Hyperlinks.ROADMAP))
|
||||||
|
.apply(new FancyTooltipAugment<>("roadmap"));
|
||||||
|
b.apply(struc -> {
|
||||||
|
AppFont.setSize(struc.get(), 2);
|
||||||
|
});
|
||||||
|
vbox.getChildren().add(b.createRegion());
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var b = new IconButtonComp("mdi2u-update", () -> UpdateAvailableAlert.showIfNeeded())
|
var b = new IconButtonComp("mdi2u-update", () -> UpdateAvailableAlert.showIfNeeded())
|
||||||
.apply(new FancyTooltipAugment<>("updateAvailableTooltip"));
|
.apply(new FancyTooltipAugment<>("updateAvailableTooltip"));
|
||||||
|
|
|
@ -380,7 +380,9 @@ public abstract class StoreEntryComp extends SimpleComp {
|
||||||
}
|
}
|
||||||
|
|
||||||
var del = new MenuItem(AppI18n.get("remove"), new FontIcon("mdal-delete_outline"));
|
var del = new MenuItem(AppI18n.get("remove"), new FontIcon("mdal-delete_outline"));
|
||||||
del.disableProperty().bind(wrapper.getDeletable().not());
|
del.disableProperty().bind(Bindings.createBooleanBinding(() -> {
|
||||||
|
return !wrapper.getDeletable().get() && !AppPrefs.get().developerDisableGuiRestrictions().get();
|
||||||
|
}, wrapper.getDeletable(), AppPrefs.get().developerDisableGuiRestrictions()));
|
||||||
del.setOnAction(event -> wrapper.delete());
|
del.setOnAction(event -> wrapper.delete());
|
||||||
contextMenu.getItems().add(del);
|
contextMenu.getItems().add(del);
|
||||||
|
|
||||||
|
|
|
@ -195,6 +195,11 @@ public class StoreEntryWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void executeDefaultAction() throws Exception {
|
public void executeDefaultAction() throws Exception {
|
||||||
|
if (getEntry().getValidity() == DataStoreEntry.Validity.INCOMPLETE) {
|
||||||
|
editDialog();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var found = getDefaultActionProvider().getValue();
|
var found = getDefaultActionProvider().getValue();
|
||||||
entry.updateLastUsed();
|
entry.updateLastUsed();
|
||||||
if (found != null) {
|
if (found != null) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import io.xpipe.app.core.mode.OperationMode;
|
||||||
import io.xpipe.app.fxcomps.Comp;
|
import io.xpipe.app.fxcomps.Comp;
|
||||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
|
import io.xpipe.app.util.Hyperlinks;
|
||||||
import io.xpipe.app.util.MarkdownHelper;
|
import io.xpipe.app.util.MarkdownHelper;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
|
@ -110,9 +111,7 @@ public class AppGreetings {
|
||||||
temp,
|
temp,
|
||||||
MarkdownHelper.toHtml(Files.readString(file), UnaryOperator.identity()));
|
MarkdownHelper.toHtml(Files.readString(file), UnaryOperator.identity()));
|
||||||
});
|
});
|
||||||
App.getApp()
|
Hyperlinks.open(temp.toUri().toString());
|
||||||
.getHostServices()
|
|
||||||
.showDocument(temp.toUri().toString());
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
ErrorEvent.fromThrowable(e).handle();
|
ErrorEvent.fromThrowable(e).handle();
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,10 @@ public class AppTheme {
|
||||||
private static final PseudoClass PERFORMANCE = PseudoClass.getPseudoClass("performance");
|
private static final PseudoClass PERFORMANCE = PseudoClass.getPseudoClass("performance");
|
||||||
|
|
||||||
public static void initThemeHandlers(Window stage) {
|
public static void initThemeHandlers(Window stage) {
|
||||||
|
if (AppPrefs.get() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SimpleChangeListener.apply(AppPrefs.get().theme, t -> {
|
SimpleChangeListener.apply(AppPrefs.get().theme, t -> {
|
||||||
Theme.ALL.forEach(theme -> stage.getScene().getRoot().getStyleClass().remove(theme.getCssId()));
|
Theme.ALL.forEach(theme -> stage.getScene().getRoot().getStyleClass().remove(theme.getCssId()));
|
||||||
if (t == null) {
|
if (t == null) {
|
||||||
|
@ -73,6 +77,8 @@ public class AppTheme {
|
||||||
t.apply();
|
t.apply();
|
||||||
TrackEvent.debug("Set theme " + t.getId() + " for scene");
|
TrackEvent.debug("Set theme " + t.getId() + " for scene");
|
||||||
|
|
||||||
|
// The gnome detector sometimes runs into issues, also it's not that important
|
||||||
|
if (!OsType.getLocal().equals(OsType.LINUX)) {
|
||||||
detector.registerListener(dark -> {
|
detector.registerListener(dark -> {
|
||||||
PlatformThread.runLaterIfNeeded(() -> {
|
PlatformThread.runLaterIfNeeded(() -> {
|
||||||
if (dark && !AppPrefs.get().theme.getValue().isDark()) {
|
if (dark && !AppPrefs.get().theme.getValue().isDark()) {
|
||||||
|
@ -84,6 +90,7 @@ public class AppTheme {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
AppPrefs.get().theme.addListener((c, o, n) -> {
|
AppPrefs.get().theme.addListener((c, o, n) -> {
|
||||||
changeTheme(n);
|
changeTheme(n);
|
||||||
|
|
|
@ -24,6 +24,11 @@ public class Shortcuts {
|
||||||
public static <T extends Region> void addShortcut(T region, KeyCombination comb, Consumer<T> exec) {
|
public static <T extends Region> void addShortcut(T region, KeyCombination comb, Consumer<T> exec) {
|
||||||
var filter = new EventHandler<KeyEvent>() {
|
var filter = new EventHandler<KeyEvent>() {
|
||||||
public void handle(KeyEvent ke) {
|
public void handle(KeyEvent ke) {
|
||||||
|
var target = ke.getTarget();
|
||||||
|
if (!region.isVisible() || !region.isManaged() || region.isDisabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (comb.match(ke)) {
|
if (comb.match(ke)) {
|
||||||
exec.accept(region);
|
exec.accept(region);
|
||||||
ke.consume();
|
ke.consume();
|
||||||
|
|
|
@ -10,6 +10,10 @@ import java.util.function.Consumer;
|
||||||
public class GuiErrorHandlerBase {
|
public class GuiErrorHandlerBase {
|
||||||
|
|
||||||
protected boolean startupGui(Consumer<Throwable> onFail) {
|
protected boolean startupGui(Consumer<Throwable> onFail) {
|
||||||
|
if (PlatformState.getCurrent() == PlatformState.EXITED) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (PlatformState.getCurrent() == PlatformState.NOT_INITIALIZED) {
|
if (PlatformState.getCurrent() == PlatformState.NOT_INITIALIZED) {
|
||||||
try {
|
try {
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
|
|
@ -37,7 +37,9 @@ public abstract class DataStorage {
|
||||||
|
|
||||||
private static DataStorage INSTANCE;
|
private static DataStorage INSTANCE;
|
||||||
protected final Path dir;
|
protected final Path dir;
|
||||||
|
@Getter
|
||||||
protected final List<DataStoreCategory> storeCategories;
|
protected final List<DataStoreCategory> storeCategories;
|
||||||
|
@Getter
|
||||||
protected final Set<DataStoreEntry> storeEntries;
|
protected final Set<DataStoreEntry> storeEntries;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -159,6 +161,8 @@ public abstract class DataStorage {
|
||||||
listeners.forEach(storageListener -> storageListener.onStoreAdd(toAdd));
|
listeners.forEach(storageListener -> storageListener.onStoreAdd(toAdd));
|
||||||
refreshValidities(true);
|
refreshValidities(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateCategory(DataStoreEntry entry, DataStoreCategory newCategory) {
|
public void updateCategory(DataStoreEntry entry, DataStoreCategory newCategory) {
|
||||||
|
@ -171,6 +175,7 @@ public abstract class DataStorage {
|
||||||
|
|
||||||
var toAdd = Stream.concat(Stream.of(entry), children.stream()).toArray(DataStoreEntry[]::new);
|
var toAdd = Stream.concat(Stream.of(entry), children.stream()).toArray(DataStoreEntry[]::new);
|
||||||
listeners.forEach(storageListener -> storageListener.onStoreAdd(toAdd));
|
listeners.forEach(storageListener -> storageListener.onStoreAdd(toAdd));
|
||||||
|
saveAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean refreshChildren(DataStoreEntry e) {
|
public boolean refreshChildren(DataStoreEntry e) {
|
||||||
|
@ -228,6 +233,15 @@ public abstract class DataStorage {
|
||||||
return !newChildren.isEmpty();
|
return !newChildren.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void deleteChildren(DataStoreEntry e) {
|
||||||
|
var c = getDeepStoreChildren(e);
|
||||||
|
c.forEach(entry -> entry.finalizeEntry());
|
||||||
|
this.storeEntries.removeAll(c);
|
||||||
|
this.listeners.forEach(l -> l.onStoreRemove(c.toArray(DataStoreEntry[]::new)));
|
||||||
|
refreshValidities(false);
|
||||||
|
saveAsync();
|
||||||
|
}
|
||||||
|
|
||||||
public void deleteWithChildren(DataStoreEntry... entries) {
|
public void deleteWithChildren(DataStoreEntry... entries) {
|
||||||
var toDelete = Arrays.stream(entries)
|
var toDelete = Arrays.stream(entries)
|
||||||
.flatMap(entry -> {
|
.flatMap(entry -> {
|
||||||
|
@ -244,15 +258,130 @@ public abstract class DataStorage {
|
||||||
saveAsync();
|
saveAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteChildren(DataStoreEntry e) {
|
|
||||||
var c = getDeepStoreChildren(e);
|
public DataStoreCategory addStoreCategoryIfNotPresent(@NonNull DataStoreCategory cat) {
|
||||||
c.forEach(entry -> entry.finalizeEntry());
|
if (storeCategories.contains(cat)) {
|
||||||
this.storeEntries.removeAll(c);
|
return cat;
|
||||||
this.listeners.forEach(l -> l.onStoreRemove(c.toArray(DataStoreEntry[]::new)));
|
}
|
||||||
|
|
||||||
|
var byId = getStoreCategoryIfPresent(cat.getUuid()).orElse(null);
|
||||||
|
if (byId != null) {
|
||||||
|
return byId;
|
||||||
|
}
|
||||||
|
|
||||||
|
addStoreCategory(cat);
|
||||||
|
return cat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addStoreCategory(@NonNull DataStoreCategory cat) {
|
||||||
|
cat.setDirectory(getCategoriesDir().resolve(cat.getUuid().toString()));
|
||||||
|
this.storeCategories.add(cat);
|
||||||
|
saveAsync();
|
||||||
|
|
||||||
|
this.listeners.forEach(l -> l.onCategoryAdd(cat));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataStoreEntry addStoreEntryIfNotPresent(@NonNull DataStoreEntry e) {
|
||||||
|
if (storeEntries.contains(e)) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
var byId = getStoreEntryIfPresent(e.getUuid()).orElse(null);
|
||||||
|
if (byId != null) {
|
||||||
|
return byId;
|
||||||
|
}
|
||||||
|
|
||||||
|
var syntheticParent = getSyntheticParent(e);
|
||||||
|
if (syntheticParent.isPresent()) {
|
||||||
|
addStoreEntryIfNotPresent(syntheticParent.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
var displayParent = syntheticParent.or(() -> getDisplayParent(e));
|
||||||
|
if (displayParent.isPresent()) {
|
||||||
|
displayParent.get().setExpanded(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
e.setDirectory(getStoresDir().resolve(e.getUuid().toString()));
|
||||||
|
this.storeEntries.add(e);
|
||||||
|
displayParent.ifPresent(p -> {
|
||||||
|
p.setChildrenCache(null);
|
||||||
|
});
|
||||||
|
saveAsync();
|
||||||
|
|
||||||
|
this.listeners.forEach(l -> l.onStoreAdd(e));
|
||||||
|
e.initializeEntry();
|
||||||
|
refreshValidities(true);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addStoreEntriesIfNotPresent(@NonNull DataStoreEntry... es) {
|
||||||
|
for (DataStoreEntry e : es) {
|
||||||
|
if (storeEntries.contains(e) || getStoreEntryIfPresent(e.getStore()).isPresent()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var syntheticParent = getSyntheticParent(e);
|
||||||
|
if (syntheticParent.isPresent()) {
|
||||||
|
addStoreEntryIfNotPresent(syntheticParent.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
var displayParent = syntheticParent.or(() -> getDisplayParent(e));
|
||||||
|
if (displayParent.isPresent()) {
|
||||||
|
displayParent.get().setExpanded(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
e.setDirectory(getStoresDir().resolve(e.getUuid().toString()));
|
||||||
|
this.storeEntries.add(e);
|
||||||
|
displayParent.ifPresent(p -> {
|
||||||
|
p.setChildrenCache(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.listeners.forEach(l -> l.onStoreAdd(es));
|
||||||
|
for (DataStoreEntry e : es) {
|
||||||
|
e.initializeEntry();
|
||||||
|
}
|
||||||
|
refreshValidities(true);
|
||||||
|
saveAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataStoreEntry addStoreIfNotPresent(@NonNull String name, DataStore store) {
|
||||||
|
var f = getStoreEntryIfPresent(store);
|
||||||
|
if (f.isPresent()) {
|
||||||
|
return f.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
var e = DataStoreEntry.createNew(UUID.randomUUID(), selectedCategory.getUuid(), name, store);
|
||||||
|
addStoreEntryIfNotPresent(e);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteStoreEntry(@NonNull DataStoreEntry store) {
|
||||||
|
store.finalizeEntry();
|
||||||
|
this.storeEntries.remove(store);
|
||||||
|
getDisplayParent(store).ifPresent(p -> p.setChildrenCache(null));
|
||||||
|
this.listeners.forEach(l -> l.onStoreRemove(store));
|
||||||
refreshValidities(false);
|
refreshValidities(false);
|
||||||
saveAsync();
|
saveAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void deleteStoreCategory(@NonNull DataStoreCategory cat) {
|
||||||
|
if (cat.getUuid().equals(DEFAULT_CATEGORY_UUID) || cat.getUuid().equals(ALL_CONNECTIONS_CATEGORY_UUID)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
storeEntries.forEach(entry -> {
|
||||||
|
if (entry.getCategoryUuid().equals(cat.getUuid())) {
|
||||||
|
entry.setCategoryUuid(DEFAULT_CATEGORY_UUID);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
storeCategories.remove(cat);
|
||||||
|
saveAsync();
|
||||||
|
this.listeners.forEach(l -> l.onCategoryRemove(cat));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get operations
|
||||||
|
|
||||||
public boolean isRootEntry(DataStoreEntry entry) {
|
public boolean isRootEntry(DataStoreEntry entry) {
|
||||||
var noParent = DataStorage.get().getDisplayParent(entry).isEmpty();
|
var noParent = DataStorage.get().getDisplayParent(entry).isEmpty();
|
||||||
var diffParentCategory = DataStorage.get()
|
var diffParentCategory = DataStorage.get()
|
||||||
|
@ -312,8 +441,9 @@ public abstract class DataStorage {
|
||||||
|
|
||||||
public Set<DataStoreEntry> getDeepStoreChildren(DataStoreEntry entry) {
|
public Set<DataStoreEntry> getDeepStoreChildren(DataStoreEntry entry) {
|
||||||
var set = new HashSet<DataStoreEntry>();
|
var set = new HashSet<DataStoreEntry>();
|
||||||
getStoreChildren(entry).forEach(entry1 -> {
|
getStoreChildren(entry).forEach(c -> {
|
||||||
set.addAll(getDeepStoreChildren(entry1));
|
set.add(c);
|
||||||
|
set.addAll(getDeepStoreChildren(c));
|
||||||
});
|
});
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
@ -435,112 +565,6 @@ public abstract class DataStorage {
|
||||||
.findFirst();
|
.findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataStoreCategory addStoreCategoryIfNotPresent(@NonNull DataStoreCategory cat) {
|
|
||||||
if (storeCategories.contains(cat)) {
|
|
||||||
return cat;
|
|
||||||
}
|
|
||||||
|
|
||||||
var byId = getStoreCategoryIfPresent(cat.getUuid()).orElse(null);
|
|
||||||
if (byId != null) {
|
|
||||||
return byId;
|
|
||||||
}
|
|
||||||
|
|
||||||
addStoreCategory(cat);
|
|
||||||
return cat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addStoreCategory(@NonNull DataStoreCategory cat) {
|
|
||||||
cat.setDirectory(getCategoriesDir().resolve(cat.getUuid().toString()));
|
|
||||||
this.storeCategories.add(cat);
|
|
||||||
saveAsync();
|
|
||||||
|
|
||||||
this.listeners.forEach(l -> l.onCategoryAdd(cat));
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataStoreEntry addStoreEntryIfNotPresent(@NonNull DataStoreEntry e) {
|
|
||||||
if (storeEntries.contains(e)) {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
var byId = getStoreEntryIfPresent(e.getUuid()).orElse(null);
|
|
||||||
if (byId != null) {
|
|
||||||
return byId;
|
|
||||||
}
|
|
||||||
|
|
||||||
var syntheticParent = getSyntheticParent(e);
|
|
||||||
if (syntheticParent.isPresent()) {
|
|
||||||
addStoreEntryIfNotPresent(syntheticParent.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
var displayParent = syntheticParent.or(() -> getDisplayParent(e));
|
|
||||||
if (displayParent.isPresent()) {
|
|
||||||
displayParent.get().setExpanded(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.setDirectory(getStoresDir().resolve(e.getUuid().toString()));
|
|
||||||
this.storeEntries.add(e);
|
|
||||||
displayParent.ifPresent(p -> {
|
|
||||||
p.setChildrenCache(null);
|
|
||||||
});
|
|
||||||
saveAsync();
|
|
||||||
|
|
||||||
this.listeners.forEach(l -> l.onStoreAdd(e));
|
|
||||||
e.initializeEntry();
|
|
||||||
refreshValidities(true);
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataStoreEntry getOrCreateNewSyntheticEntry(DataStoreEntry parent, String name, DataStore store) {
|
|
||||||
var uuid = UuidHelper.generateFromObject(parent.getUuid(), name);
|
|
||||||
var found = getStoreEntryIfPresent(uuid);
|
|
||||||
if (found.isPresent()) {
|
|
||||||
return found.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
return DataStoreEntry.createNew(uuid, parent.getCategoryUuid(), name, store);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addStoreEntriesIfNotPresent(@NonNull DataStoreEntry... es) {
|
|
||||||
for (DataStoreEntry e : es) {
|
|
||||||
if (storeEntries.contains(e) || getStoreEntryIfPresent(e.getStore()).isPresent()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var syntheticParent = getSyntheticParent(e);
|
|
||||||
if (syntheticParent.isPresent()) {
|
|
||||||
addStoreEntryIfNotPresent(syntheticParent.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
var displayParent = syntheticParent.or(() -> getDisplayParent(e));
|
|
||||||
if (displayParent.isPresent()) {
|
|
||||||
displayParent.get().setExpanded(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.setDirectory(getStoresDir().resolve(e.getUuid().toString()));
|
|
||||||
this.storeEntries.add(e);
|
|
||||||
displayParent.ifPresent(p -> {
|
|
||||||
p.setChildrenCache(null);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.listeners.forEach(l -> l.onStoreAdd(es));
|
|
||||||
for (DataStoreEntry e : es) {
|
|
||||||
e.initializeEntry();
|
|
||||||
}
|
|
||||||
refreshValidities(true);
|
|
||||||
saveAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataStoreEntry addStoreIfNotPresent(@NonNull String name, DataStore store) {
|
|
||||||
var f = getStoreEntryIfPresent(store);
|
|
||||||
if (f.isPresent()) {
|
|
||||||
return f.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
var e = DataStoreEntry.createNew(UUID.randomUUID(), selectedCategory.getUuid(), name, store);
|
|
||||||
addStoreEntryIfNotPresent(e);
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<String> getStoreDisplayName(DataStore store) {
|
public Optional<String> getStoreDisplayName(DataStore store) {
|
||||||
if (store == null) {
|
if (store == null) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
|
@ -557,35 +581,20 @@ public abstract class DataStorage {
|
||||||
return store.getProvider().browserDisplayName(store.getStore());
|
return store.getProvider().browserDisplayName(store.getStore());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteStoreEntry(@NonNull DataStoreEntry store) {
|
|
||||||
store.finalizeEntry();
|
|
||||||
this.storeEntries.remove(store);
|
|
||||||
getDisplayParent(store).ifPresent(p -> p.setChildrenCache(null));
|
|
||||||
this.listeners.forEach(l -> l.onStoreRemove(store));
|
|
||||||
refreshValidities(false);
|
|
||||||
saveAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void deleteStoreCategory(@NonNull DataStoreCategory cat) {
|
|
||||||
if (cat.getUuid().equals(DEFAULT_CATEGORY_UUID) || cat.getUuid().equals(ALL_CONNECTIONS_CATEGORY_UUID)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
storeEntries.forEach(entry -> {
|
|
||||||
if (entry.getCategoryUuid().equals(cat.getUuid())) {
|
|
||||||
entry.setCategoryUuid(DEFAULT_CATEGORY_UUID);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
storeCategories.remove(cat);
|
|
||||||
saveAsync();
|
|
||||||
this.listeners.forEach(l -> l.onCategoryRemove(cat));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<DataStoreEntry> getStoreEntryIfPresent(UUID id) {
|
public Optional<DataStoreEntry> getStoreEntryIfPresent(UUID id) {
|
||||||
return storeEntries.stream().filter(e -> e.getUuid().equals(id)).findAny();
|
return storeEntries.stream().filter(e -> e.getUuid().equals(id)).findAny();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DataStoreEntry getOrCreateNewSyntheticEntry(DataStoreEntry parent, String name, DataStore store) {
|
||||||
|
var uuid = UuidHelper.generateFromObject(parent.getUuid(), name);
|
||||||
|
var found = getStoreEntryIfPresent(uuid);
|
||||||
|
if (found.isPresent()) {
|
||||||
|
return found.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
return DataStoreEntry.createNew(uuid, parent.getCategoryUuid(), name, store);
|
||||||
|
}
|
||||||
|
|
||||||
public DataStoreEntry getStoreEntry(UUID id) {
|
public DataStoreEntry getStoreEntry(UUID id) {
|
||||||
return getStoreEntryIfPresent(id).orElseThrow();
|
return getStoreEntryIfPresent(id).orElseThrow();
|
||||||
}
|
}
|
||||||
|
@ -594,11 +603,4 @@ public abstract class DataStorage {
|
||||||
return getStoreEntryIfPresent(LOCAL_ID).orElse(null);
|
return getStoreEntryIfPresent(LOCAL_ID).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<DataStoreEntry> getStoreEntries() {
|
|
||||||
return storeEntries;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<DataStoreCategory> getStoreCategories() {
|
|
||||||
return storeCategories;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,10 @@ public class DataStoreEntryRef<T extends DataStore> {
|
||||||
@NonNull
|
@NonNull
|
||||||
DataStoreEntry entry;
|
DataStoreEntry entry;
|
||||||
|
|
||||||
|
public DataStoreEntryRef(@NonNull DataStoreEntry entry) {
|
||||||
|
this.entry = entry;
|
||||||
|
}
|
||||||
|
|
||||||
public void checkComplete() throws Exception {
|
public void checkComplete() throws Exception {
|
||||||
getStore().checkComplete();
|
getStore().checkComplete();
|
||||||
}
|
}
|
||||||
|
|
|
@ -247,9 +247,7 @@ public class StandardStorage extends DataStorage {
|
||||||
ErrorEvent.fromThrowable(ex).terminal(true).build().handle();
|
ErrorEvent.fromThrowable(ex).terminal(true).build().handle();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
var hasFixedLocal = storeEntries.stream().anyMatch(dataStoreEntry -> dataStoreEntry.getUuid().equals(LOCAL_ID));
|
var hasFixedLocal = storeEntries.stream().anyMatch(dataStoreEntry -> dataStoreEntry.getUuid().equals(LOCAL_ID));
|
||||||
// storeEntries.removeIf(dataStoreEntry -> !dataStoreEntry.getUuid().equals(LOCAL_ID) && dataStoreEntry.getStore() instanceof LocalStore);
|
|
||||||
if (!hasFixedLocal) {
|
if (!hasFixedLocal) {
|
||||||
var e = DataStoreEntry.createNew(
|
var e = DataStoreEntry.createNew(
|
||||||
LOCAL_ID, DataStorage.DEFAULT_CATEGORY_UUID, "Local Machine", new LocalStore());
|
LOCAL_ID, DataStorage.DEFAULT_CATEGORY_UUID, "Local Machine", new LocalStore());
|
||||||
|
@ -264,7 +262,6 @@ public class StandardStorage extends DataStorage {
|
||||||
if (storeEntries.stream().noneMatch(entry -> entry.getColor() != null)) {
|
if (storeEntries.stream().noneMatch(entry -> entry.getColor() != null)) {
|
||||||
local.setColor(DataStoreColor.BLUE);
|
local.setColor(DataStoreColor.BLUE);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh to update state
|
// Refresh to update state
|
||||||
storeEntries.forEach(dataStoreEntry -> dataStoreEntry.refresh());
|
storeEntries.forEach(dataStoreEntry -> dataStoreEntry.refresh());
|
||||||
|
@ -278,6 +275,12 @@ public class StandardStorage extends DataStorage {
|
||||||
|
|
||||||
refreshValidities(true);
|
refreshValidities(true);
|
||||||
|
|
||||||
|
// Save to apply changes
|
||||||
|
if (!hasFixedLocal) {
|
||||||
|
storeEntries.removeIf(dataStoreEntry -> !dataStoreEntry.getUuid().equals(LOCAL_ID) && dataStoreEntry.getStore() instanceof LocalStore);
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
deleteLeftovers();
|
deleteLeftovers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ public class Hyperlinks {
|
||||||
public static final String WEBSITE = "https://xpipe.io";
|
public static final String WEBSITE = "https://xpipe.io";
|
||||||
public static final String DOCUMENTATION = "https://docs.xpipe.io";
|
public static final String DOCUMENTATION = "https://docs.xpipe.io";
|
||||||
public static final String GITHUB = "https://github.com/xpipe-io/xpipe";
|
public static final String GITHUB = "https://github.com/xpipe-io/xpipe";
|
||||||
|
public static final String ROADMAP = "https://xpipe.kampsite.co/";
|
||||||
public static final String PRIVACY = "https://github.com/xpipe-io/xpipe/blob/master/PRIVACY.md";
|
public static final String PRIVACY = "https://github.com/xpipe-io/xpipe/blob/master/PRIVACY.md";
|
||||||
public static final String TOS = "https://github.com/xpipe-io/xpipe/blob/master/app/src/main/resources/io/xpipe/app/resources/misc/tos.md";
|
public static final String TOS = "https://github.com/xpipe-io/xpipe/blob/master/app/src/main/resources/io/xpipe/app/resources/misc/tos.md";
|
||||||
public static final String SECURITY = "https://github.com/xpipe-io/xpipe/blob/master/SECURITY.md";
|
public static final String SECURITY = "https://github.com/xpipe-io/xpipe/blob/master/SECURITY.md";
|
||||||
|
|
|
@ -44,7 +44,7 @@ project.ext {
|
||||||
productName = isStage ? 'XPipe PTB' : 'XPipe'
|
productName = isStage ? 'XPipe PTB' : 'XPipe'
|
||||||
kebapProductName = isStage ? 'xpipe-ptb' : 'xpipe'
|
kebapProductName = isStage ? 'xpipe-ptb' : 'xpipe'
|
||||||
publisher = 'XPipe UG (haftungsbeschränkt)'
|
publisher = 'XPipe UG (haftungsbeschränkt)'
|
||||||
shortDescription = 'The shell connection hub and remote file browser for your entire infrastructure'
|
shortDescription = 'Your entire server infrastructure at your fingertips'
|
||||||
longDescription = 'XPipe is a new type of shell connection hub and remote file manager that allows you to access your entire sever infrastructure from your local machine. It works on top of your installed command-line programs that you normally use to connect and does not require any setup on your remote systems.'
|
longDescription = 'XPipe is a new type of shell connection hub and remote file manager that allows you to access your entire sever infrastructure from your local machine. It works on top of your installed command-line programs that you normally use to connect and does not require any setup on your remote systems.'
|
||||||
website = 'https://xpipe.io'
|
website = 'https://xpipe.io'
|
||||||
sourceWebsite = 'https://github.com/xpipe-io/xpipe'
|
sourceWebsite = 'https://github.com/xpipe-io/xpipe'
|
||||||
|
|
|
@ -34,6 +34,10 @@ public interface ShellDialect {
|
||||||
return other.equals(this);
|
return other.equals(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default ShellDialect getDumbReplacementDialect() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
String getCatchAllVariable();
|
String getCatchAllVariable();
|
||||||
|
|
||||||
CommandControl queryVersion(ShellControl shellControl);
|
CommandControl queryVersion(ShellControl shellControl);
|
||||||
|
|
|
@ -13,6 +13,7 @@ import lombok.extern.jackson.Jacksonized;
|
||||||
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@SuperBuilder
|
@SuperBuilder
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -29,9 +30,18 @@ public class SimpleScriptStore extends ScriptStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String assemble(ShellControl shellControl, ExecutionType type) {
|
private String assemble(ShellControl shellControl, ExecutionType type) {
|
||||||
var targetType = type == ExecutionType.TERMINAL_ONLY ? shellControl.getTargetTerminalShellDialect() : shellControl.getShellDialect();
|
var targetType = type == ExecutionType.TERMINAL_ONLY
|
||||||
if ((executionType == type || executionType == ExecutionType.BOTH) && minimumDialect.isCompatibleTo(targetType)) {
|
? shellControl.getTargetTerminalShellDialect()
|
||||||
var script = ScriptHelper.createExecScript(targetType, shellControl, commands);
|
: shellControl.getShellDialect();
|
||||||
|
if ((executionType == type || executionType == ExecutionType.BOTH)
|
||||||
|
&& minimumDialect.isCompatibleTo(targetType)) {
|
||||||
|
var shebang = commands.startsWith("#");
|
||||||
|
// Fix new lines and shebang
|
||||||
|
var fixedCommands = commands.lines()
|
||||||
|
.skip(shebang ? 1 : 0)
|
||||||
|
.collect(Collectors.joining(
|
||||||
|
shellControl.getShellDialect().getNewLine().getNewLineString()));
|
||||||
|
var script = ScriptHelper.createExecScript(targetType, shellControl, fixedCommands);
|
||||||
return targetType.sourceScriptCommand(shellControl, script);
|
return targetType.sourceScriptCommand(shellControl, script);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -145,7 +145,7 @@ public class SimpleScriptStoreProvider implements DataStoreProvider {
|
||||||
.nonNull()
|
.nonNull()
|
||||||
.name("scriptContents")
|
.name("scriptContents")
|
||||||
.description("scriptContentsDescription")
|
.description("scriptContentsDescription")
|
||||||
.longDescription("proc:environmentScript")
|
.longDescription("base:script")
|
||||||
.addComp(
|
.addComp(
|
||||||
new IntegratedTextAreaComp(commandProp, false, "commands", Bindings.createStringBinding(() -> {
|
new IntegratedTextAreaComp(commandProp, false, "commands", Bindings.createStringBinding(() -> {
|
||||||
return dialect.getValue() != null
|
return dialect.getValue() != null
|
||||||
|
|
|
@ -6,6 +6,8 @@ Only afterward will a separate connection be made in the actual terminal.
|
||||||
|
|
||||||
The file browser for example entirely uses the dumb background mode to handle its operations, so if you want your script environment to apply to the file browser session, it should run in the dumb mode.
|
The file browser for example entirely uses the dumb background mode to handle its operations, so if you want your script environment to apply to the file browser session, it should run in the dumb mode.
|
||||||
|
|
||||||
|
If you want the script to be run when you open the connection in a terminal, then choose the terminal mode.
|
||||||
|
|
||||||
### Blocking commands
|
### Blocking commands
|
||||||
|
|
||||||
Blocking commands that require user input can freeze the shell process when XPipe starts it up internally first in the background.
|
Blocking commands that require user input can freeze the shell process when XPipe starts it up internally first in the background.
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
## Init script
|
||||||
|
|
||||||
|
The contents of the script to run. You can choose to either edit this in place or use the external edit button in the top right corner to launch an external text editor.
|
||||||
|
|
||||||
|
You don't have to specify a shebang line for shells that support it, one is added automatically with the appropriate shell type.
|
Loading…
Reference in a new issue