mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-25 09:00:26 +00:00
Try to fix push
This commit is contained in:
parent
4b6d0d5f28
commit
219a1a2c9f
23 changed files with 413 additions and 95 deletions
|
@ -132,7 +132,7 @@ application {
|
|||
|
||||
run {
|
||||
systemProperty 'io.xpipe.app.mode', 'gui'
|
||||
systemProperty 'io.xpipe.app.dataDir', "$projectDir/local3/"
|
||||
systemProperty 'io.xpipe.app.dataDir', "$projectDir/local_stage/"
|
||||
systemProperty 'io.xpipe.app.writeLogs', "true"
|
||||
systemProperty 'io.xpipe.app.writeSysOut', "true"
|
||||
systemProperty 'io.xpipe.app.developerMode', "true"
|
||||
|
|
|
@ -80,8 +80,8 @@ public class AppLayoutComp extends Comp<CompStructure<BorderPane>> {
|
|||
pane.setCenter(r);
|
||||
});
|
||||
pane.setCenter(selected.getValue().comp().createRegion());
|
||||
pane.setPrefWidth(1200);
|
||||
pane.setPrefHeight(700);
|
||||
pane.setPrefWidth(1280);
|
||||
pane.setPrefHeight(720);
|
||||
AppFont.normal(pane);
|
||||
return new SimpleCompStructure<>(pane);
|
||||
}
|
||||
|
|
|
@ -5,16 +5,12 @@ import io.xpipe.extension.fxcomps.Comp;
|
|||
import io.xpipe.extension.fxcomps.CompStructure;
|
||||
import io.xpipe.extension.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.extension.fxcomps.util.SimpleChangeListener;
|
||||
import javafx.animation.Animation;
|
||||
import javafx.animation.PauseTransition;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.util.Duration;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
|
||||
|
@ -80,17 +76,6 @@ public class LazyTextFieldComp extends Comp<LazyTextFieldComp.Structure> {
|
|||
currentValue.setValue(newValue);
|
||||
});
|
||||
|
||||
Animation delay = new PauseTransition(Duration.millis(800));
|
||||
delay.setOnFinished(e -> {
|
||||
r.setDisable(false);
|
||||
r.requestFocus();
|
||||
});
|
||||
sp.addEventFilter(MouseEvent.MOUSE_ENTERED, e -> {
|
||||
delay.playFromStart();
|
||||
});
|
||||
sp.addEventFilter(MouseEvent.MOUSE_EXITED, e -> {
|
||||
delay.stop();
|
||||
});
|
||||
r.focusedProperty().addListener((c, o, n) -> {
|
||||
if (!n) {
|
||||
r.setDisable(true);
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
package io.xpipe.app.comp.base;
|
||||
|
||||
import io.xpipe.extension.fxcomps.Comp;
|
||||
import io.xpipe.extension.fxcomps.CompStructure;
|
||||
import io.xpipe.extension.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.extension.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.extension.fxcomps.util.PlatformThread;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ListBoxViewComp<T> extends Comp<CompStructure<VBox>> {
|
||||
|
||||
private final ObservableList<T> shown;
|
||||
private final ObservableList<T> all;
|
||||
private final Function<T, Comp<?>> compFunction;
|
||||
|
||||
public ListBoxViewComp(
|
||||
ObservableList<T> shown, ObservableList<T> all, Function<T, Comp<?>> compFunction) {
|
||||
this.shown = PlatformThread.sync(shown);
|
||||
this.all = PlatformThread.sync(all);
|
||||
this.compFunction = compFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompStructure<VBox> createBase() {
|
||||
Map<T, Region> cache = new HashMap<>();
|
||||
|
||||
VBox listView = new VBox();
|
||||
listView.setFocusTraversable(false);
|
||||
|
||||
refresh(listView, shown, cache, false);
|
||||
listView.requestLayout();
|
||||
|
||||
shown.addListener((ListChangeListener<? super T>) (c) -> {
|
||||
refresh(listView, c.getList(), cache, true);
|
||||
});
|
||||
|
||||
all.addListener((ListChangeListener<? super T>) c -> {
|
||||
cache.keySet().retainAll(c.getList());
|
||||
});
|
||||
|
||||
return new SimpleCompStructure<>(listView);
|
||||
}
|
||||
|
||||
private void refresh(VBox listView, List<? extends T> c, Map<T, Region> cache, boolean asynchronous) {
|
||||
Runnable update = () -> {
|
||||
var newShown = c.stream()
|
||||
.map(v -> {
|
||||
if (!cache.containsKey(v)) {
|
||||
cache.put(v, compFunction.apply(v).createRegion());
|
||||
}
|
||||
|
||||
return cache.get(v);
|
||||
})
|
||||
.toList();
|
||||
|
||||
if (!listView.getChildren().equals(newShown)) {
|
||||
BindingsHelper.setContent(listView.getChildren(), newShown);
|
||||
listView.layout();
|
||||
}
|
||||
};
|
||||
|
||||
if (asynchronous) {
|
||||
Platform.runLater(update);
|
||||
} else {
|
||||
PlatformThread.runLaterIfNeeded(update);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package io.xpipe.app.comp.storage.store;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import io.xpipe.app.comp.base.LazyTextFieldComp;
|
||||
import io.xpipe.app.comp.base.LoadingOverlayComp;
|
||||
import io.xpipe.app.core.AppFont;
|
||||
|
@ -10,6 +11,7 @@ import io.xpipe.extension.I18n;
|
|||
import io.xpipe.extension.event.ErrorEvent;
|
||||
import io.xpipe.extension.fxcomps.Comp;
|
||||
import io.xpipe.extension.fxcomps.SimpleComp;
|
||||
import io.xpipe.extension.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.extension.fxcomps.augment.GrowAugment;
|
||||
import io.xpipe.extension.fxcomps.augment.PopupMenuAugment;
|
||||
import io.xpipe.extension.fxcomps.impl.FancyTooltipAugment;
|
||||
|
@ -100,6 +102,9 @@ public class StoreEntryComp extends SimpleComp {
|
|||
var imageComp = new PrettyImageComp(new SimpleStringProperty(img), 55, 45);
|
||||
var storeIcon = imageComp.createRegion();
|
||||
storeIcon.getStyleClass().add("icon");
|
||||
if (entry.getState().getValue().isUsable()) {
|
||||
new FancyTooltipAugment<>(new SimpleStringProperty(entry.getEntry().getProvider().getDisplayName())).augment(storeIcon);
|
||||
}
|
||||
return storeIcon;
|
||||
}
|
||||
|
||||
|
@ -117,6 +122,7 @@ public class StoreEntryComp extends SimpleComp {
|
|||
|
||||
var storeIcon = createIcon();
|
||||
|
||||
|
||||
grid.getColumnConstraints()
|
||||
.addAll(
|
||||
createShareConstraint(grid, STORE_TYPE_WIDTH), createShareConstraint(grid, NAME_WIDTH),
|
||||
|
@ -133,7 +139,7 @@ public class StoreEntryComp extends SimpleComp {
|
|||
AppFont.small(size);
|
||||
AppFont.small(date);
|
||||
|
||||
grid.getStyleClass().add("store-entry-comp");
|
||||
grid.getStyleClass().add("store-entry-grid");
|
||||
|
||||
grid.setOnMouseClicked(event -> {
|
||||
if (entry.getEditable().get()) {
|
||||
|
@ -143,7 +149,12 @@ public class StoreEntryComp extends SimpleComp {
|
|||
|
||||
applyState(grid);
|
||||
|
||||
return grid;
|
||||
var button = new JFXButton();
|
||||
button.setGraphic(grid);
|
||||
GrowAugment.create(true, false).augment(new SimpleCompStructure<>(grid));
|
||||
button.getStyleClass().add("store-entry-comp");
|
||||
button.setMaxWidth(2000);
|
||||
return button;
|
||||
}
|
||||
|
||||
private Comp<?> createButtonBar() {
|
||||
|
|
|
@ -4,7 +4,6 @@ import io.xpipe.app.comp.base.ListViewComp;
|
|||
import io.xpipe.app.comp.base.MultiContentComp;
|
||||
import io.xpipe.extension.fxcomps.Comp;
|
||||
import io.xpipe.extension.fxcomps.SimpleComp;
|
||||
import io.xpipe.extension.fxcomps.augment.GrowAugment;
|
||||
import io.xpipe.extension.fxcomps.util.BindingsHelper;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.value.ObservableBooleanValue;
|
||||
|
@ -15,14 +14,18 @@ import java.util.Map;
|
|||
public class StoreEntryListComp extends SimpleComp {
|
||||
|
||||
private Comp<?> createList() {
|
||||
var topLevel = StoreEntrySection.createTopLevels();
|
||||
var filtered = BindingsHelper.filteredContentBinding(
|
||||
topLevel,
|
||||
StoreViewState.get().getFilterString().map(s -> (storeEntrySection -> storeEntrySection.shouldShow(s))));
|
||||
var content = new ListViewComp<>(
|
||||
StoreViewState.get().getShownEntries(),
|
||||
StoreViewState.get().getAllEntries(),
|
||||
filtered,
|
||||
topLevel,
|
||||
null,
|
||||
(StoreEntryWrapper e) -> {
|
||||
return new StoreEntryComp(e).apply(GrowAugment.create(true, false));
|
||||
(StoreEntrySection e) -> {
|
||||
return e.comp(true);
|
||||
});
|
||||
return content;
|
||||
return content.styleClass("store-list-comp");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
package io.xpipe.app.comp.storage.store;
|
||||
|
||||
import io.xpipe.app.comp.base.ListBoxViewComp;
|
||||
import io.xpipe.app.comp.storage.StorageFilter;
|
||||
import io.xpipe.extension.fxcomps.Comp;
|
||||
import io.xpipe.extension.fxcomps.augment.GrowAugment;
|
||||
import io.xpipe.extension.fxcomps.impl.HorizontalComp;
|
||||
import io.xpipe.extension.fxcomps.impl.VerticalComp;
|
||||
import io.xpipe.extension.fxcomps.util.BindingsHelper;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.paint.Color;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class StoreEntrySection implements StorageFilter.Filterable {
|
||||
|
||||
public StoreEntrySection(StoreEntryWrapper entry, ObservableList<StoreEntrySection> children) {
|
||||
this.entry = entry;
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public static ObservableList<StoreEntrySection> createTopLevels() {
|
||||
var topLevel = BindingsHelper.mappedContentBinding(
|
||||
StoreViewState.get()
|
||||
.getAllEntries()
|
||||
.filtered(storeEntryWrapper ->
|
||||
!storeEntryWrapper.getEntry().getState().isUsable()
|
||||
|| storeEntryWrapper
|
||||
.getEntry()
|
||||
.getProvider()
|
||||
.getParent(storeEntryWrapper
|
||||
.getEntry()
|
||||
.getStore())
|
||||
== null),
|
||||
storeEntryWrapper -> create(storeEntryWrapper));
|
||||
var ordered = BindingsHelper.orderedContentBinding(
|
||||
topLevel,
|
||||
Comparator.<StoreEntrySection, Instant>comparing(storeEntrySection ->
|
||||
storeEntrySection.entry.lastAccessProperty().getValue())
|
||||
.reversed());
|
||||
return ordered;
|
||||
}
|
||||
|
||||
public static StoreEntrySection create(StoreEntryWrapper e) {
|
||||
if (!e.getEntry().getState().isUsable()) {
|
||||
return new StoreEntrySection(e, FXCollections.observableArrayList());
|
||||
}
|
||||
|
||||
var children = BindingsHelper.mappedContentBinding(
|
||||
StoreViewState.get()
|
||||
.getAllEntries()
|
||||
.filtered(other -> other.getEntry().getState().isUsable()
|
||||
&& e.getEntry()
|
||||
.getStore()
|
||||
.equals(other.getEntry()
|
||||
.getProvider()
|
||||
.getParent(other.getEntry().getStore()))),
|
||||
entry1 -> create(entry1));
|
||||
var ordered = BindingsHelper.orderedContentBinding(
|
||||
children,
|
||||
Comparator.<StoreEntrySection, Instant>comparing(storeEntrySection ->
|
||||
storeEntrySection.entry.lastAccessProperty().getValue())
|
||||
.reversed());
|
||||
return new StoreEntrySection(e, ordered);
|
||||
}
|
||||
|
||||
private final StoreEntryWrapper entry;
|
||||
private final ObservableList<StoreEntrySection> children;
|
||||
|
||||
public Comp<?> comp(boolean top) {
|
||||
var root = new StoreEntryComp(entry).apply(struc -> HBox.setHgrow(struc.get(), Priority.ALWAYS));
|
||||
var icon = Comp.of(() -> {
|
||||
var padding = new FontIcon("mdal-arrow_forward_ios");
|
||||
padding.setIconSize(14);
|
||||
var pain = new StackPane(padding);
|
||||
pain.setMinWidth(20);
|
||||
pain.setMaxHeight(20);
|
||||
return pain;
|
||||
});
|
||||
List<Comp<?>> topEntryList = top ? List.of(root) : List.of(icon, root);
|
||||
|
||||
if (children.size() == 0) {
|
||||
return new HorizontalComp(topEntryList);
|
||||
}
|
||||
|
||||
var all = BindingsHelper.orderedContentBinding(
|
||||
children,
|
||||
Comparator.comparing(storeEntrySection ->
|
||||
storeEntrySection.entry.lastAccessProperty().getValue()));
|
||||
var shown = BindingsHelper.filteredContentBinding(
|
||||
all,
|
||||
StoreViewState.get().getFilterString().map(s -> (storeEntrySection -> storeEntrySection.shouldShow(s))));
|
||||
var content = new ListBoxViewComp<>(shown, all, (StoreEntrySection e) -> {
|
||||
return e.comp(false).apply(GrowAugment.create(true, false));
|
||||
})
|
||||
.apply(struc -> HBox.setHgrow(struc.get(), Priority.ALWAYS))
|
||||
.apply(struc -> struc.get().backgroundProperty().set(Background.fill(Color.color(0, 0, 0, 0.01))));
|
||||
var spacer = Comp.of(() -> {
|
||||
var padding = new Region();
|
||||
padding.setMinWidth(25);
|
||||
padding.setMaxWidth(25);
|
||||
return padding;
|
||||
});
|
||||
return new VerticalComp(List.of(
|
||||
new HorizontalComp(topEntryList),
|
||||
new HorizontalComp(List.of(spacer, content))
|
||||
.apply(struc -> struc.get().setFillHeight(true))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldShow(String filter) {
|
||||
return entry.shouldShow(filter)
|
||||
|| children.stream().anyMatch(storeEntrySection -> storeEntrySection.shouldShow(filter));
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ import javafx.application.Platform;
|
|||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableBooleanValue;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
|
@ -95,6 +96,10 @@ public class StoreViewState {
|
|||
return filter;
|
||||
}
|
||||
|
||||
public ObservableValue<String> getFilterString() {
|
||||
return filter.filterProperty();
|
||||
}
|
||||
|
||||
public ObservableList<StoreEntryWrapper> getAllEntries() {
|
||||
return allEntries;
|
||||
}
|
||||
|
|
|
@ -78,6 +78,14 @@ public class App extends Application {
|
|||
focus();
|
||||
});
|
||||
appWindow.show();
|
||||
|
||||
// For demo purposes
|
||||
if (true) {
|
||||
stage.setX(310);
|
||||
stage.setY(178);
|
||||
stage.setWidth(1300);
|
||||
stage.setHeight(730);
|
||||
}
|
||||
}
|
||||
|
||||
public void focus() {
|
||||
|
|
|
@ -60,6 +60,7 @@ public class DataSourceCollection extends StorageElement {
|
|||
var json = mapper.readTree(dir.resolve("collection.json").toFile());
|
||||
var uuid = UUID.fromString(json.required("uuid").textValue());
|
||||
var name = json.required("name").textValue();
|
||||
Objects.requireNonNull(name);
|
||||
var lastModified = Instant.parse(json.required("lastModified").textValue());
|
||||
|
||||
JavaType listType = mapper.getTypeFactory().constructCollectionType(ArrayList.class, UUID.class);
|
||||
|
|
|
@ -82,7 +82,7 @@ public abstract class DataStorage {
|
|||
}
|
||||
|
||||
public DataSourceCollection getInternalCollection() {
|
||||
var found = sourceCollections.stream().filter(o -> o.getName().equals("Internal")).findAny();
|
||||
var found = sourceCollections.stream().filter(o -> o.getName() != null && o.getName().equals("Internal")).findAny();
|
||||
if (found.isPresent()) {
|
||||
return found.get();
|
||||
}
|
||||
|
|
|
@ -17,16 +17,16 @@ public class DataStorageWriter {
|
|||
public static JsonNode storeToNode(DataStore store) {
|
||||
var mapper = JacksonMapper.newMapper();
|
||||
var tree = mapper.valueToTree(store);
|
||||
return replaceReferencesWithIds(store, tree);
|
||||
return replaceReferencesWithIds(tree, true);
|
||||
}
|
||||
|
||||
public static JsonNode sourceToNode(DataSource<?> source) {
|
||||
var mapper = JacksonMapper.newMapper();
|
||||
var tree = mapper.valueToTree(source);
|
||||
return replaceReferencesWithIds(source, tree);
|
||||
return replaceReferencesWithIds(tree, true);
|
||||
}
|
||||
|
||||
private static JsonNode replaceReferencesWithIds(Object root, JsonNode node) {
|
||||
private static JsonNode replaceReferencesWithIds(JsonNode node, boolean isRoot) {
|
||||
var mapper = JacksonMapper.newMapper();
|
||||
|
||||
node = replaceReferencesWithIds(
|
||||
|
@ -38,7 +38,7 @@ public class DataStorageWriter {
|
|||
|
||||
try {
|
||||
var store = mapper.treeToValue(possibleReference, DataStore.class);
|
||||
if (root == null || !root.equals(store)) {
|
||||
if (!isRoot) {
|
||||
var found = DataStorage.get().getEntryByStore(store);
|
||||
return found.map(dataSourceEntry -> dataSourceEntry.getUuid());
|
||||
}
|
||||
|
@ -46,14 +46,14 @@ public class DataStorageWriter {
|
|||
}
|
||||
return Optional.empty();
|
||||
},
|
||||
"storeId");
|
||||
"storeId", isRoot);
|
||||
|
||||
node = replaceReferencesWithIds(
|
||||
node,
|
||||
possibleReference -> {
|
||||
try {
|
||||
var source = mapper.treeToValue(possibleReference, DataSource.class);
|
||||
if (root == null || !root.equals(source)) {
|
||||
if (!isRoot) {
|
||||
var found = DataStorage.get().getEntryBySource(source);
|
||||
return found.map(dataSourceEntry -> dataSourceEntry.getUuid());
|
||||
}
|
||||
|
@ -61,13 +61,13 @@ public class DataStorageWriter {
|
|||
}
|
||||
return Optional.empty();
|
||||
},
|
||||
"sourceId");
|
||||
"sourceId", isRoot);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private static JsonNode replaceReferencesWithIds(
|
||||
JsonNode node, Function<JsonNode, Optional<UUID>> function, String key) {
|
||||
JsonNode node, Function<JsonNode, Optional<UUID>> function, String key, boolean isRoot) {
|
||||
if (!node.isObject()) {
|
||||
return node;
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ public class DataStorageWriter {
|
|||
|
||||
var replacement = JsonNodeFactory.instance.objectNode();
|
||||
node.fields().forEachRemaining(stringJsonNodeEntry -> {
|
||||
var resolved = replaceReferencesWithIds(null, stringJsonNodeEntry.getValue());
|
||||
var resolved = replaceReferencesWithIds(stringJsonNodeEntry.getValue(), false);
|
||||
replacement.set(stringJsonNodeEntry.getKey(), resolved);
|
||||
});
|
||||
return replacement;
|
||||
|
|
|
@ -75,3 +75,7 @@
|
|||
-fx-padding: 0;
|
||||
-fx-focus-color: transparent;
|
||||
}
|
||||
|
||||
.store-list-comp .list-cell {
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
|
|
@ -1,27 +1,30 @@
|
|||
|
||||
.store-entry-comp .date, .store-entry-comp .summary {
|
||||
.store-entry-grid .date, .store-entry-grid .summary {
|
||||
-fx-text-fill: -xp-text-light;
|
||||
}
|
||||
|
||||
.store-entry-comp:failed .jfx-text-field {
|
||||
.store-entry-grid:failed .jfx-text-field {
|
||||
-fx-text-fill: red;
|
||||
}
|
||||
|
||||
.store-entry-comp:incomplete .jfx-text-field {
|
||||
.store-entry-grid:incomplete .jfx-text-field {
|
||||
-fx-text-fill: gray;
|
||||
}
|
||||
.store-entry-comp:incomplete .summary {
|
||||
.store-entry-grid:incomplete .summary {
|
||||
-fx-text-fill: gray;
|
||||
}
|
||||
.store-entry-comp:incomplete .information {
|
||||
.store-entry-grid:incomplete .information {
|
||||
-fx-text-fill: gray;
|
||||
}
|
||||
.store-entry-comp:incomplete .date {
|
||||
.store-entry-grid:incomplete .date {
|
||||
-fx-text-fill: gray;
|
||||
}
|
||||
.store-entry-comp:incomplete .icon {
|
||||
.store-entry-grid:incomplete .icon {
|
||||
-fx-opacity: 0.5;
|
||||
}
|
||||
.store-entry-comp {
|
||||
-fx-padding: 6px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -11,11 +11,9 @@ import java.util.function.Predicate;
|
|||
|
||||
public interface ShellProcessControl extends ProcessControl {
|
||||
|
||||
default String prepareTerminalOpen() throws Exception {
|
||||
return prepareTerminalOpen(null);
|
||||
}
|
||||
String prepareTerminalOpen() throws Exception;
|
||||
|
||||
String prepareTerminalOpen(String content) throws Exception;
|
||||
String prepareIntermediateTerminalOpen(String content) throws Exception;
|
||||
|
||||
default String executeStringSimpleCommand(String command) throws Exception {
|
||||
try (CommandProcessControl c = command(command).start()) {
|
||||
|
@ -35,7 +33,7 @@ public interface ShellProcessControl extends ProcessControl {
|
|||
}
|
||||
}
|
||||
|
||||
default void executeSimpleCommand(String command,String failMessage) throws Exception {
|
||||
default void executeSimpleCommand(String command, String failMessage) throws Exception {
|
||||
try (CommandProcessControl c = command(command).start()) {
|
||||
c.discardOrThrow();
|
||||
} catch (ProcessOutputException out) {
|
||||
|
@ -60,12 +58,14 @@ public interface ShellProcessControl extends ProcessControl {
|
|||
|
||||
ShellProcessControl elevation(SecretValue value);
|
||||
|
||||
ShellProcessControl initWith(List<String> cmds);
|
||||
|
||||
SecretValue getElevationPassword();
|
||||
|
||||
default ShellProcessControl subShell(@NonNull ShellType type) {
|
||||
return subShell(p -> type.getNormalOpenCommand(), (shellProcessControl, s) -> {
|
||||
return s == null ? type.getNormalOpenCommand() : type.executeCommandWithShell(s);
|
||||
})
|
||||
return s == null ? type.getNormalOpenCommand() : type.executeCommandWithShell(s);
|
||||
})
|
||||
.elevation(getElevationPassword());
|
||||
}
|
||||
|
||||
|
@ -80,10 +80,9 @@ public interface ShellProcessControl extends ProcessControl {
|
|||
|
||||
ShellProcessControl subShell(
|
||||
@NonNull Function<ShellProcessControl, String> command,
|
||||
BiFunction<ShellProcessControl, String, String> terminalCommand
|
||||
);
|
||||
BiFunction<ShellProcessControl, String, String> terminalCommand);
|
||||
|
||||
void executeCommand(String command) throws Exception;
|
||||
void executeLine(String command) throws Exception;
|
||||
|
||||
@Override
|
||||
ShellProcessControl start() throws Exception;
|
||||
|
@ -91,8 +90,7 @@ public interface ShellProcessControl extends ProcessControl {
|
|||
CommandProcessControl command(Function<ShellProcessControl, String> command);
|
||||
|
||||
CommandProcessControl command(
|
||||
Function<ShellProcessControl, String> command, Function<ShellProcessControl, String> terminalCommand
|
||||
);
|
||||
Function<ShellProcessControl, String> command, Function<ShellProcessControl, String> terminalCommand);
|
||||
|
||||
default CommandProcessControl command(String command) {
|
||||
return command(shellProcessControl -> command);
|
||||
|
|
|
@ -3,7 +3,6 @@ package io.xpipe.core.process;
|
|||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.xpipe.core.charsetter.NewLine;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -55,7 +54,7 @@ public interface ShellType {
|
|||
return getEchoCommand(s, false);
|
||||
}
|
||||
|
||||
String getSetVariableCommand(String variable, String value);
|
||||
String getSetEnvironmentVariableCommand(String variable, String value);
|
||||
|
||||
|
||||
String getEchoCommand(String s, boolean toErrorStream);
|
||||
|
@ -67,8 +66,14 @@ public interface ShellType {
|
|||
|
||||
String getPrintVariableCommand(String prefix, String name);
|
||||
|
||||
default String getPrintEnvironmentVariableCommand(String name) {
|
||||
return getPrintVariableCommand(name);
|
||||
}
|
||||
|
||||
String getNormalOpenCommand();
|
||||
|
||||
String getInitFileOpenCommand(String file);
|
||||
|
||||
String executeCommandWithShell(String cmd);
|
||||
|
||||
List<String> executeCommandListWithShell(String cmd);
|
||||
|
@ -79,7 +84,7 @@ public interface ShellType {
|
|||
|
||||
String getStreamFileWriteCommand(String file);
|
||||
|
||||
String getSimpleFileWriteCommand(String content, String file);
|
||||
String getTextFileWriteCommand(String content, String file);
|
||||
|
||||
String getFileDeleteCommand(String file);
|
||||
|
||||
|
|
|
@ -79,9 +79,9 @@ public class XPipeInstallation {
|
|||
public static Path getLocalDynamicLibraryDirectory() {
|
||||
Path path = getLocalInstallationBasePath();
|
||||
if (OsType.getLocal().equals(OsType.WINDOWS)) {
|
||||
return path.resolve("runtime").resolve("bin");
|
||||
return path.resolve("app").resolve("runtime").resolve("bin");
|
||||
} else if (OsType.getLocal().equals(OsType.LINUX)) {
|
||||
return path.resolve("lib").resolve("runtime").resolve("lib");
|
||||
return path.resolve("app").resolve("lib").resolve("runtime").resolve("lib");
|
||||
} else {
|
||||
return path.resolve("Contents")
|
||||
.resolve("runtime")
|
||||
|
@ -95,7 +95,7 @@ public class XPipeInstallation {
|
|||
Path path = getLocalInstallationBasePath();
|
||||
return OsType.getLocal().equals(OsType.MAC)
|
||||
? path.resolve("Contents").resolve("Resources").resolve("extensions")
|
||||
: path.resolve("extensions");
|
||||
: path.resolve("app").resolve("extensions");
|
||||
}
|
||||
|
||||
private static Path getLocalInstallationBasePathForJavaExecutable(Path executable) {
|
||||
|
@ -108,9 +108,9 @@ public class XPipeInstallation {
|
|||
.getParent()
|
||||
.getParent();
|
||||
} else if (OsType.getLocal().equals(OsType.LINUX)) {
|
||||
return executable.getParent().getParent().getParent().getParent();
|
||||
return executable.getParent().getParent().getParent().getParent().getParent();
|
||||
} else {
|
||||
return executable.getParent().getParent().getParent();
|
||||
return executable.getParent().getParent().getParent().getParent();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,9 +118,9 @@ public class XPipeInstallation {
|
|||
if (OsType.getLocal().equals(OsType.MAC)) {
|
||||
return executable.getParent().getParent().getParent();
|
||||
} else if (OsType.getLocal().equals(OsType.LINUX)) {
|
||||
return executable.getParent().getParent();
|
||||
return executable.getParent().getParent().getParent();
|
||||
} else {
|
||||
return executable.getParent();
|
||||
return executable.getParent().getParent();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
24
dist/cli.gradle
vendored
24
dist/cli.gradle
vendored
|
@ -3,8 +3,6 @@ import java.nio.file.Paths
|
|||
import java.nio.file.StandardCopyOption
|
||||
|
||||
def distDir = "$buildDir/dist"
|
||||
def windows = org.gradle.internal.os.OperatingSystem.current().isWindows();
|
||||
|
||||
|
||||
apply plugin: 'org.asciidoctor.jvm.convert'
|
||||
asciidoctor {
|
||||
|
@ -29,21 +27,37 @@ task copyCompletion(type: Copy) {
|
|||
into "$distDir/cli"
|
||||
}
|
||||
|
||||
task setupMusl(type: Exec) {
|
||||
commandLine "${project(':cli').projectDir.toString()}/musl-setup.sh", project(':cli').projectDir.toString()
|
||||
}
|
||||
|
||||
task buildCli(type: DefaultTask) {
|
||||
if (!org.gradle.internal.os.OperatingSystem.current().isWindows()) {
|
||||
if (org.gradle.internal.os.OperatingSystem.current().isLinux()) {
|
||||
dependsOn(setupMusl)
|
||||
project(':cli').getTasksByName('nativeCompile', true).forEach(v->v.mustRunAfter(setupMusl))
|
||||
}
|
||||
|
||||
if (org.gradle.internal.os.OperatingSystem.current().isMacOsX()) {
|
||||
dependsOn(project(':cli').getTasksByName('nativeCompile', true))
|
||||
}
|
||||
|
||||
doLast {
|
||||
if (windows) {
|
||||
if (rootProject.os.isWindows()) {
|
||||
exec {
|
||||
executable project(':cli').projectDir.toString() + '/native-build.bat'
|
||||
environment System.getenv()
|
||||
}
|
||||
}
|
||||
|
||||
if (rootProject.os.isLinux()) {
|
||||
exec {
|
||||
commandLine project(':cli').projectDir.toString() + '/native-build-musl.sh', project(':cli').projectDir.toString()
|
||||
environment System.getenv()
|
||||
}
|
||||
}
|
||||
|
||||
Files.createDirectories(Paths.get(distDir, 'cli'))
|
||||
def ending = windows ? ".exe" : ""
|
||||
def ending = rootProject.os.isWindows() ? ".exe" : ""
|
||||
var outputFile = Paths.get(project(':cli').buildDir.toString() + "/native/nativeCompile/xpipe$ending")
|
||||
if (!Files.exists(outputFile)) {
|
||||
throw new IOException("Cli output file does not exist")
|
||||
|
|
|
@ -39,6 +39,10 @@ public interface DataStoreProvider {
|
|||
throw new ExtensionException("Provider " + getId() + " has no set category");
|
||||
}
|
||||
|
||||
default DataStore getParent(DataStore store) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default GuiDialog guiDialog(Property<DataStore> store) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -72,6 +72,10 @@ public class TrackEvent {
|
|||
return builder().type("debug").message(message);
|
||||
}
|
||||
|
||||
public static TrackEventBuilder withDebug(String cat, String message) {
|
||||
return builder().category(cat).type("debug").message(message);
|
||||
}
|
||||
|
||||
public static void debug(String cat, String message) {
|
||||
builder().category(cat).type("debug").message(message).build().handle();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package io.xpipe.extension.fxcomps.util;
|
||||
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
|
@ -8,6 +10,7 @@ import java.lang.ref.WeakReference;
|
|||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class BindingsHelper {
|
||||
|
||||
|
@ -44,6 +47,45 @@ public class BindingsHelper {
|
|||
});
|
||||
}
|
||||
|
||||
public static <T, V> ObservableList<T> mappedContentBinding(ObservableList<V> l2, Function<V, T> map) {
|
||||
ObservableList<T> l1 = FXCollections.observableList(new ArrayList<>());
|
||||
Runnable runnable = () -> {
|
||||
setContent(l1, l2.stream().map(map).toList());
|
||||
};
|
||||
runnable.run();
|
||||
l2.addListener((ListChangeListener<? super V>) c -> {
|
||||
runnable.run();
|
||||
});
|
||||
return l1;
|
||||
}
|
||||
|
||||
public static <V> ObservableList<V> orderedContentBinding(ObservableList<V> l2, Comparator<V> comp) {
|
||||
ObservableList<V> l1 = FXCollections.observableList(new ArrayList<>());
|
||||
Runnable runnable = () -> {
|
||||
setContent(l1, l2.stream().sorted(comp).toList());
|
||||
};
|
||||
runnable.run();
|
||||
l2.addListener((ListChangeListener<? super V>) c -> {
|
||||
runnable.run();
|
||||
});
|
||||
return l1;
|
||||
}
|
||||
|
||||
public static <V> ObservableList<V> filteredContentBinding(ObservableList<V> l2, ObservableValue<Predicate<V>> predicate) {
|
||||
ObservableList<V> l1 = FXCollections.observableList(new ArrayList<>());
|
||||
Runnable runnable = () -> {
|
||||
setContent(l1, l2.stream().filter(predicate.getValue()).toList());
|
||||
};
|
||||
runnable.run();
|
||||
l2.addListener((ListChangeListener<? super V>) c -> {
|
||||
runnable.run();
|
||||
});
|
||||
predicate.addListener((c,o,n) -> {
|
||||
runnable.run();
|
||||
});
|
||||
return l1;
|
||||
}
|
||||
|
||||
public static <T> void setContent(ObservableList<T> toSet, List<? extends T> newList) {
|
||||
if (toSet.equals(newList)) {
|
||||
return;
|
||||
|
|
|
@ -40,8 +40,8 @@ public class DataStoreFormatter {
|
|||
return func.apply(length);
|
||||
}
|
||||
|
||||
var fileString = func.apply(length - atString.length() - 4);
|
||||
return String.format("%s -> %s", atString, fileString);
|
||||
var fileString = func.apply(length - atString.length() - 3);
|
||||
return String.format("%s > %s", atString, fileString);
|
||||
}
|
||||
|
||||
public static String toName(DataStore input) {
|
||||
|
@ -96,6 +96,17 @@ public class DataStoreFormatter {
|
|||
);
|
||||
}
|
||||
|
||||
if (input.endsWith(".compute.amazonaws.com")) {
|
||||
var split = input.split("\\.");
|
||||
var name = split[0];
|
||||
var region = split[1];
|
||||
var lengthShare = (length - 3) / 2;
|
||||
return String.format(
|
||||
"%s.%s",
|
||||
DataStoreFormatter.cut(name, lengthShare), DataStoreFormatter.cut(region, length - lengthShare)
|
||||
);
|
||||
}
|
||||
|
||||
return cut(input, length);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
package io.xpipe.extension.util;
|
||||
|
||||
import io.xpipe.core.impl.FileNames;
|
||||
import io.xpipe.core.process.OsType;
|
||||
import io.xpipe.core.process.ShellProcessControl;
|
||||
import io.xpipe.core.process.ShellType;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.core.util.SecretValue;
|
||||
import io.xpipe.core.util.XPipeSession;
|
||||
import io.xpipe.core.util.XPipeTempDirectory;
|
||||
import io.xpipe.extension.event.TrackEvent;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class ScriptHelper {
|
||||
|
||||
public static int getConnectionHash(String command) {
|
||||
return Math.abs(Objects.hash(command, XPipeSession.get().getSystemSessionId()));
|
||||
// This deterministic approach can cause permission problems when two different users execute the same command on a system
|
||||
return new Random().nextInt(Integer.MAX_VALUE);
|
||||
//return Math.abs(Objects.hash(command, XPipeSession.get().getSystemSessionId()));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
|
@ -25,6 +28,38 @@ public class ScriptHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public static String constructOpenWithInitScriptCommand(ShellProcessControl processControl, List<String> init, String toExecuteInShell) {
|
||||
ShellType t = processControl.getShellType();
|
||||
if (init.size() == 0 && toExecuteInShell == null) {
|
||||
return t.getNormalOpenCommand();
|
||||
}
|
||||
|
||||
String nl = t.getNewLine().getNewLineString();
|
||||
var content = String.join(nl, init)
|
||||
+ nl;
|
||||
|
||||
if (processControl.getOsType().equals(OsType.LINUX)
|
||||
|| processControl.getOsType().equals(OsType.MAC)) {
|
||||
content = "if [ -f ~/.bashrc ]; then . ~/.bashrc; fi\n" + content;
|
||||
}
|
||||
|
||||
if (toExecuteInShell != null) {
|
||||
content += toExecuteInShell + nl;
|
||||
content += t.getExitCommand() + nl;
|
||||
}
|
||||
|
||||
var initFile = createExecScript(processControl, content, true);
|
||||
return t.getInitFileOpenCommand(initFile);
|
||||
}
|
||||
|
||||
public static String prepend(ShellProcessControl processControl, List<String> init, String commands) {
|
||||
var prefix = init != null && init.size() > 0
|
||||
? String.join(processControl.getShellType().getNewLine().getNewLineString(), init)
|
||||
+ processControl.getShellType().getNewLine().getNewLineString()
|
||||
: "";
|
||||
return prefix + commands;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String createExecScript(ShellProcessControl processControl, String content, boolean restart) {
|
||||
var fileName = "exec-" + getConnectionHash(content);
|
||||
|
@ -35,7 +70,8 @@ public class ScriptHelper {
|
|||
}
|
||||
|
||||
@SneakyThrows
|
||||
private static String createExecScript(ShellProcessControl processControl, String file, String content, boolean restart) {
|
||||
private static String createExecScript(
|
||||
ShellProcessControl processControl, String file, String content, boolean restart) {
|
||||
ShellType type = processControl.getShellType();
|
||||
content = type.prepareScriptContent(content);
|
||||
|
||||
|
@ -49,32 +85,19 @@ public class ScriptHelper {
|
|||
.handle();
|
||||
|
||||
processControl.executeSimpleCommand(type.getFileTouchCommand(file), "Failed to create script " + file);
|
||||
processControl.executeSimpleCommand(type.getMakeExecutableCommand(file), "Failed to make script " + file + " executable");
|
||||
|
||||
if (!content.contains("\n")) {
|
||||
processControl.executeSimpleCommand(type.getSimpleFileWriteCommand(content, file));
|
||||
return file;
|
||||
}
|
||||
|
||||
try (var c = processControl.command(type.getStreamFileWriteCommand(file)).start()) {
|
||||
c.discardOut();
|
||||
c.discardErr();
|
||||
c.getStdin().write(content.getBytes(processControl.getCharset()));
|
||||
c.closeStdin();
|
||||
}
|
||||
|
||||
if (restart) {
|
||||
processControl.restart();
|
||||
}
|
||||
processControl.executeSimpleCommand(
|
||||
type.getMakeExecutableCommand(file), "Failed to make script " + file + " executable");
|
||||
|
||||
processControl.executeSimpleCommand(type.getTextFileWriteCommand(content, file));
|
||||
return file;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String createAskPassScript(SecretValue pass, ShellProcessControl parent, ShellType type, boolean restart) {
|
||||
public static String createAskPassScript(
|
||||
SecretValue pass, ShellProcessControl parent, ShellType type, boolean restart) {
|
||||
var content = type.getScriptEchoCommand(pass.getSecretValue());
|
||||
var temp = XPipeTempDirectory.get(parent);
|
||||
var file = FileNames.join(temp, "askpass-" + getConnectionHash(content) + "." + type.getScriptFileEnding());
|
||||
return createExecScript(parent,file, content, restart);
|
||||
return createExecScript(parent, file, content, restart);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue