mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-23 08:00:23 +00:00
Reformat
This commit is contained in:
parent
6a946dbc5a
commit
1ebc60cb71
78 changed files with 561 additions and 387 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -18,7 +18,6 @@ bin
|
|||
ComponentsGenerated.wxs
|
||||
!dist/javafx/**/lib
|
||||
!dist/javafx/**/bin
|
||||
dev.properties
|
||||
xcuserdata/
|
||||
*.dylib
|
||||
project.xcworkspace
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package io.xpipe.app.beacon;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import io.xpipe.app.core.AppResources;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
|
@ -10,6 +8,9 @@ import io.xpipe.beacon.BeaconConfig;
|
|||
import io.xpipe.beacon.BeaconInterface;
|
||||
import io.xpipe.core.process.OsType;
|
||||
import io.xpipe.core.util.XPipeInstallation;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -126,7 +127,8 @@ public class AppBeaconServer {
|
|||
}
|
||||
|
||||
private void start() throws IOException {
|
||||
server = HttpServer.create(new InetSocketAddress(Inet4Address.getByAddress(new byte[]{ 0x7f,0x00,0x00,0x01 }), port), 10);
|
||||
server = HttpServer.create(
|
||||
new InetSocketAddress(Inet4Address.getByAddress(new byte[] {0x7f, 0x00, 0x00, 0x01}), port), 10);
|
||||
BeaconInterface.getAll().forEach(beaconInterface -> {
|
||||
server.createContext(beaconInterface.getPath(), new BeaconRequestHandler<>(beaconInterface));
|
||||
});
|
||||
|
|
|
@ -186,7 +186,7 @@ public class BeaconRequestHandler<T> implements HttpHandler {
|
|||
&& method.getParameters()[0].getType().equals(byte[].class))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
setMethod.invoke(b, (Object) s);
|
||||
setMethod.invoke(b, s);
|
||||
|
||||
var m = b.getClass().getDeclaredMethod("build");
|
||||
m.setAccessible(true);
|
||||
|
|
|
@ -1,23 +1,31 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.util.TerminalLauncherManager;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.api.SshLaunchExchange;
|
||||
import io.xpipe.core.process.ProcessControlProvider;
|
||||
import io.xpipe.core.process.ShellDialects;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
public class SshLaunchExchangeImpl extends SshLaunchExchange {
|
||||
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request msg) throws Exception {
|
||||
var usedDialect = ShellDialects.ALL.stream().filter(dialect -> dialect.getExecutableName().equalsIgnoreCase(msg.getArguments())).findFirst();
|
||||
if (msg.getArguments() != null && usedDialect.isEmpty() && !msg.getArguments().contains("SSH_ORIGINAL_COMMAND")) {
|
||||
var usedDialect = ShellDialects.ALL.stream()
|
||||
.filter(dialect -> dialect.getExecutableName().equalsIgnoreCase(msg.getArguments()))
|
||||
.findFirst();
|
||||
if (msg.getArguments() != null
|
||||
&& usedDialect.isEmpty()
|
||||
&& !msg.getArguments().contains("SSH_ORIGINAL_COMMAND")) {
|
||||
throw new BeaconClientException("Unexpected argument: " + msg.getArguments());
|
||||
}
|
||||
|
||||
var r = TerminalLauncherManager.waitForNextLaunch();
|
||||
var c = ProcessControlProvider.get().getEffectiveLocalDialect().getOpenScriptCommand(r.toString()).buildBaseParts(null);
|
||||
var c = ProcessControlProvider.get()
|
||||
.getEffectiveLocalDialect()
|
||||
.getOpenScriptCommand(r.toString())
|
||||
.buildBaseParts(null);
|
||||
return Response.builder().command(c).build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ import io.xpipe.app.core.AppI18n;
|
|||
import io.xpipe.app.core.AppLayoutModel;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
|
||||
import io.xpipe.core.process.OsType;
|
||||
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
|
|
|
@ -191,9 +191,12 @@ public class BrowserTransferModel {
|
|||
|
||||
public ObservableBooleanValue downloadFinished() {
|
||||
synchronized (progress) {
|
||||
return Bindings.createBooleanBinding(() -> {
|
||||
return progress.getValue() != null && progress.getValue().done();
|
||||
}, progress);
|
||||
return Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
return progress.getValue() != null
|
||||
&& progress.getValue().done();
|
||||
},
|
||||
progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ public interface ApplicationPathAction extends BrowserAction {
|
|||
String getExecutable();
|
||||
|
||||
@Override
|
||||
default void init(OpenFileSystemModel model) throws Exception {
|
||||
default void init(OpenFileSystemModel model) {
|
||||
// Cache result for later calls
|
||||
model.getCache().isApplicationInPath(getExecutable());
|
||||
}
|
||||
|
|
|
@ -3,8 +3,10 @@ package io.xpipe.app.browser.action;
|
|||
import io.xpipe.app.browser.file.BrowserEntry;
|
||||
import io.xpipe.app.browser.fs.OpenFileSystemModel;
|
||||
import io.xpipe.app.util.LicenseProvider;
|
||||
|
||||
import javafx.scene.control.Menu;
|
||||
import javafx.scene.control.MenuItem;
|
||||
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -27,15 +29,13 @@ public interface BranchAction extends BrowserAction {
|
|||
m.setDisable(!isActive(model, selected));
|
||||
|
||||
if (getProFeatureId() != null
|
||||
&& !LicenseProvider.get()
|
||||
.getFeature(getProFeatureId())
|
||||
.isSupported()) {
|
||||
&& !LicenseProvider.get().getFeature(getProFeatureId()).isSupported()) {
|
||||
m.setDisable(true);
|
||||
m.setGraphic(new FontIcon("mdi2p-professional-hexagon"));
|
||||
}
|
||||
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
List<? extends BrowserAction> getBranchingActions(OpenFileSystemModel model, List<BrowserEntry> entries);
|
||||
}
|
||||
|
|
|
@ -25,10 +25,15 @@ public interface BrowserAction {
|
|||
.toList();
|
||||
}
|
||||
|
||||
static List<LeafAction> getFlattened(BrowserAction browserAction, OpenFileSystemModel model, List<BrowserEntry> entries) {
|
||||
static List<LeafAction> getFlattened(
|
||||
BrowserAction browserAction, OpenFileSystemModel model, List<BrowserEntry> entries) {
|
||||
return browserAction instanceof LeafAction
|
||||
? List.of((LeafAction) browserAction)
|
||||
: ((BranchAction) browserAction).getBranchingActions(model, entries).stream().map(action -> getFlattened(action, model, entries)).flatMap(List::stream).toList();
|
||||
? List.of((LeafAction) browserAction)
|
||||
: ((BranchAction) browserAction)
|
||||
.getBranchingActions(model, entries).stream()
|
||||
.map(action -> getFlattened(action, model, entries))
|
||||
.flatMap(List::stream)
|
||||
.toList();
|
||||
}
|
||||
|
||||
static LeafAction byId(String id, OpenFileSystemModel model, List<BrowserEntry> entries) {
|
||||
|
@ -41,9 +46,9 @@ public interface BrowserAction {
|
|||
default List<BrowserEntry> resolveFilesIfNeeded(List<BrowserEntry> selected) {
|
||||
return automaticallyResolveLinks()
|
||||
? selected.stream()
|
||||
.map(browserEntry ->
|
||||
new BrowserEntry(browserEntry.getRawFileEntry().resolved(), browserEntry.getModel()))
|
||||
.toList()
|
||||
.map(browserEntry ->
|
||||
new BrowserEntry(browserEntry.getRawFileEntry().resolved(), browserEntry.getModel()))
|
||||
.toList()
|
||||
: selected;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import io.xpipe.app.browser.action.BrowserAction;
|
|||
import io.xpipe.app.browser.fs.OpenFileSystemModel;
|
||||
import io.xpipe.app.core.AppFont;
|
||||
import io.xpipe.app.util.InputHelper;
|
||||
|
||||
import javafx.scene.control.ContextMenu;
|
||||
import javafx.scene.control.SeparatorMenuItem;
|
||||
|
||||
|
|
|
@ -157,7 +157,8 @@ public final class BrowserFileListComp extends SimpleComp {
|
|||
});
|
||||
}
|
||||
|
||||
private void updateTypedSelection(TableView<BrowserEntry> table, AtomicReference<Instant> lastType, KeyEvent event, boolean recursive) {
|
||||
private void updateTypedSelection(
|
||||
TableView<BrowserEntry> table, AtomicReference<Instant> lastType, KeyEvent event, boolean recursive) {
|
||||
var typed = event.getText();
|
||||
if (typed.isEmpty()) {
|
||||
return;
|
||||
|
@ -165,15 +166,15 @@ public final class BrowserFileListComp extends SimpleComp {
|
|||
|
||||
var updated = typedSelection.get() + typed;
|
||||
var found = fileList.getShown().getValue().stream()
|
||||
.filter(browserEntry ->
|
||||
browserEntry.getFileName().toLowerCase().startsWith(updated.toLowerCase()))
|
||||
.filter(browserEntry -> browserEntry.getFileName().toLowerCase().startsWith(updated.toLowerCase()))
|
||||
.findFirst();
|
||||
if (found.isEmpty()) {
|
||||
if (typedSelection.get().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var inCooldown = lastType.get() != null && Duration.between(lastType.get(), Instant.now()).toMillis() < 1000;
|
||||
var inCooldown = lastType.get() != null
|
||||
&& Duration.between(lastType.get(), Instant.now()).toMillis() < 1000;
|
||||
if (inCooldown) {
|
||||
lastType.set(Instant.now());
|
||||
event.consume();
|
||||
|
@ -599,7 +600,8 @@ public final class BrowserFileListComp extends SimpleComp {
|
|||
browserEntry.getFileName().toLowerCase().startsWith(selection))
|
||||
.findFirst();
|
||||
// Ugly fix to prevent space from showing the menu when there is a file matching
|
||||
// Due to the table view input map, these events always get sent and consumed, not allowing us to differentiate between these cases
|
||||
// Due to the table view input map, these events always get sent and consumed, not allowing us to
|
||||
// differentiate between these cases
|
||||
if (found.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -99,7 +99,9 @@ public final class BrowserFileListModel {
|
|||
}
|
||||
|
||||
public BrowserEntry rename(BrowserEntry old, String newName) {
|
||||
if (fileSystemModel == null || fileSystemModel.isClosed() || fileSystemModel.getCurrentPath().get() == null) {
|
||||
if (fileSystemModel == null
|
||||
|| fileSystemModel.isClosed()
|
||||
|| fileSystemModel.getCurrentPath().get() == null) {
|
||||
return old;
|
||||
}
|
||||
|
||||
|
|
|
@ -142,14 +142,13 @@ public class OpenFileSystemComp extends SimpleComp {
|
|||
}
|
||||
keyEvent.consume();
|
||||
});
|
||||
InputHelper.onKeyCombination(
|
||||
root, new KeyCodeCombination(KeyCode.BACK_SPACE), true, keyEvent -> {
|
||||
var p = model.getCurrentParentDirectory();
|
||||
if (p != null) {
|
||||
model.cdAsync(p.getPath());
|
||||
}
|
||||
keyEvent.consume();
|
||||
});
|
||||
InputHelper.onKeyCombination(root, new KeyCodeCombination(KeyCode.BACK_SPACE), true, keyEvent -> {
|
||||
var p = model.getCurrentParentDirectory();
|
||||
if (p != null) {
|
||||
model.cdAsync(p.getPath());
|
||||
}
|
||||
keyEvent.consume();
|
||||
});
|
||||
return root;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package io.xpipe.app.browser.session;
|
||||
|
||||
import atlantafx.base.controls.RingProgressIndicator;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.browser.BrowserWelcomeComp;
|
||||
import io.xpipe.app.comp.base.MultiContentComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
|
@ -14,6 +12,7 @@ import io.xpipe.app.fxcomps.util.PlatformThread;
|
|||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.util.BooleanScope;
|
||||
import io.xpipe.app.util.ContextMenuHelper;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
|
@ -28,6 +27,9 @@ import javafx.scene.input.*;
|
|||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
import atlantafx.base.controls.RingProgressIndicator;
|
||||
import atlantafx.base.theme.Styles;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static atlantafx.base.theme.Styles.DENSE;
|
||||
|
@ -205,7 +207,8 @@ public class BrowserSessionTabsComp extends SimpleComp {
|
|||
return;
|
||||
}
|
||||
|
||||
if (new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN, KeyCombination.SHIFT_DOWN).match(keyEvent)) {
|
||||
if (new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN, KeyCombination.SHIFT_DOWN)
|
||||
.match(keyEvent)) {
|
||||
tabs.getTabs().clear();
|
||||
keyEvent.consume();
|
||||
}
|
||||
|
|
|
@ -60,8 +60,7 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
|
|||
Platform.getPreferences().accentColorProperty());
|
||||
|
||||
var selected = PseudoClass.getPseudoClass("selected");
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
var e = entries.get(i);
|
||||
for (AppLayoutModel.Entry e : entries) {
|
||||
var b = new IconButtonComp(e.icon(), () -> {
|
||||
if (e.action() != null) {
|
||||
e.action().run();
|
||||
|
@ -84,30 +83,21 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
|
|||
b.accessibleText(e.name());
|
||||
|
||||
var indicator = Comp.empty().styleClass("indicator");
|
||||
var stack = new StackComp(List.of(indicator, b))
|
||||
.apply(struc -> struc.get().setAlignment(Pos.CENTER_RIGHT));
|
||||
var stack = new StackComp(List.of(indicator, b)).apply(struc -> struc.get().setAlignment(Pos.CENTER_RIGHT));
|
||||
stack.apply(struc -> {
|
||||
var indicatorRegion = (Region) struc.get().getChildren().getFirst();
|
||||
indicatorRegion.setMaxWidth(7);
|
||||
indicatorRegion
|
||||
.backgroundProperty()
|
||||
.bind(Bindings.createObjectBinding(
|
||||
() -> {
|
||||
if (value.getValue().equals(e)) {
|
||||
return selectedBorder.get();
|
||||
}
|
||||
indicatorRegion.backgroundProperty().bind(Bindings.createObjectBinding(() -> {
|
||||
if (value.getValue().equals(e)) {
|
||||
return selectedBorder.get();
|
||||
}
|
||||
|
||||
if (struc.get().isHover()) {
|
||||
return hoverBorder.get();
|
||||
}
|
||||
if (struc.get().isHover()) {
|
||||
return hoverBorder.get();
|
||||
}
|
||||
|
||||
return noneBorder.get();
|
||||
},
|
||||
struc.get().hoverProperty(),
|
||||
value,
|
||||
hoverBorder,
|
||||
selectedBorder,
|
||||
noneBorder));
|
||||
return noneBorder.get();
|
||||
}, struc.get().hoverProperty(), value, hoverBorder, selectedBorder, noneBorder));
|
||||
});
|
||||
if (shortcut != null) {
|
||||
stack.apply(struc -> struc.get().getProperties().put("shortcut", shortcut));
|
||||
|
|
|
@ -40,20 +40,15 @@ public class SystemStateComp extends SimpleComp {
|
|||
var success = Styles.toDataURI(
|
||||
"""
|
||||
.stacked-ikonli-font-icon > .outer-icon { -fx-icon-color: -color-success-emphasis; }
|
||||
""");
|
||||
var failure = Styles.toDataURI(
|
||||
"""
|
||||
);
|
||||
var failure =
|
||||
Styles.toDataURI(
|
||||
"""
|
||||
.stacked-ikonli-font-icon > .outer-icon { -fx-icon-color: -color-danger-emphasis; }
|
||||
"""
|
||||
);
|
||||
var other =
|
||||
Styles.toDataURI(
|
||||
"""
|
||||
""");
|
||||
var other = Styles.toDataURI(
|
||||
"""
|
||||
.stacked-ikonli-font-icon > .outer-icon { -fx-icon-color: -color-accent-emphasis; }
|
||||
"""
|
||||
);
|
||||
""");
|
||||
|
||||
var pane = new StackedFontIcon();
|
||||
pane.getChildren().addAll(fi, border);
|
||||
|
|
|
@ -3,6 +3,7 @@ package io.xpipe.app.comp.store;
|
|||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.augment.GrowAugment;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.geometry.HPos;
|
||||
|
@ -41,10 +42,9 @@ public class DenseStoreEntryComp extends StoreEntryComp {
|
|||
() -> {
|
||||
var val = summary.getValue();
|
||||
var p = getWrapper().getEntry().getProvider();
|
||||
if (val != null && grid.isHover()
|
||||
&& p.alwaysShowSummary()) {
|
||||
if (val != null && grid.isHover() && p.alwaysShowSummary()) {
|
||||
return val;
|
||||
} else if (info.getValue() == null && p.alwaysShowSummary()){
|
||||
} else if (info.getValue() == null && p.alwaysShowSummary()) {
|
||||
return val;
|
||||
} else {
|
||||
return info.getValue();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.xpipe.app.comp.store;
|
||||
|
||||
import atlantafx.base.controls.Spacer;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.comp.base.DialogComp;
|
||||
import io.xpipe.app.comp.base.ErrorOverlayComp;
|
||||
|
@ -22,6 +21,7 @@ import io.xpipe.app.storage.DataStoreEntry;
|
|||
import io.xpipe.app.util.*;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.util.ValidationException;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
|
@ -33,6 +33,8 @@ import javafx.scene.layout.BorderPane;
|
|||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import atlantafx.base.controls.Spacer;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
import net.synedra.validatorfx.GraphicDecorationStackPane;
|
||||
|
@ -242,23 +244,30 @@ public class StoreCreationComp extends DialogComp {
|
|||
|
||||
@Override
|
||||
protected List<Comp<?>> customButtons() {
|
||||
return List.of(new ButtonComp(AppI18n.observable("skip"), null, () -> {
|
||||
if (showInvalidConfirmAlert()) {
|
||||
commit(false);
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.visible(skippable),
|
||||
return List.of(
|
||||
new ButtonComp(AppI18n.observable("skip"), null, () -> {
|
||||
if (showInvalidConfirmAlert()) {
|
||||
commit(false);
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.visible(skippable),
|
||||
new ButtonComp(AppI18n.observable("connect"), null, () -> {
|
||||
var temp = DataStoreEntry.createTempWrapper(store.getValue());
|
||||
var action = provider.getValue().launchAction(temp);
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
action.execute();
|
||||
});
|
||||
}).hide(connectable.not().or(Bindings.createBooleanBinding(() -> {
|
||||
return store.getValue() == null || !store.getValue().isComplete();
|
||||
}, store))));
|
||||
var temp = DataStoreEntry.createTempWrapper(store.getValue());
|
||||
var action = provider.getValue().launchAction(temp);
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
action.execute();
|
||||
});
|
||||
})
|
||||
.hide(connectable
|
||||
.not()
|
||||
.or(Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
return store.getValue() == null
|
||||
|| !store.getValue().isComplete();
|
||||
},
|
||||
store))));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -413,8 +422,10 @@ public class StoreCreationComp extends DialogComp {
|
|||
var layout = new BorderPane();
|
||||
layout.getStyleClass().add("store-creator");
|
||||
var providerChoice = new StoreProviderChoiceComp(filter, provider, staticDisplay);
|
||||
var showProviders = (!staticDisplay && (providerChoice.getProviders().size() > 1 || providerChoice.getProviders().getFirst().showProviderChoice())) ||
|
||||
(staticDisplay && provider.getValue().showProviderChoice());
|
||||
var showProviders = (!staticDisplay
|
||||
&& (providerChoice.getProviders().size() > 1
|
||||
|| providerChoice.getProviders().getFirst().showProviderChoice()))
|
||||
|| (staticDisplay && provider.getValue().showProviderChoice());
|
||||
if (showProviders) {
|
||||
providerChoice.onSceneAssign(struc -> struc.get().requestFocus());
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ import io.xpipe.app.fxcomps.impl.PrettySvgComp;
|
|||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.util.ScanAlert;
|
||||
|
||||
import io.xpipe.core.process.OsType;
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
|
|
|
@ -163,10 +163,10 @@ public class StoreSection {
|
|||
var allChildren = all.filtered(
|
||||
other -> {
|
||||
// Legacy implementation that does not use children caches. Use for testing
|
||||
// if (true) return DataStorage.get()
|
||||
// .getDefaultDisplayParent(other.getEntry())
|
||||
// .map(found -> found.equals(e.getEntry()))
|
||||
// .orElse(false);
|
||||
// if (true) return DataStorage.get()
|
||||
// .getDefaultDisplayParent(other.getEntry())
|
||||
// .map(found -> found.equals(e.getEntry()))
|
||||
// .orElse(false);
|
||||
|
||||
// is children. This check is fast as the children are cached in the storage
|
||||
return DataStorage.get().getStoreChildren(e.getEntry()).contains(other.getEntry())
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.xpipe.app.core;
|
||||
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.RandomAccessFile;
|
||||
|
|
|
@ -2,12 +2,9 @@ package io.xpipe.app.core;
|
|||
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.WritableImage;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
|
@ -131,7 +128,7 @@ public class AppImages {
|
|||
}
|
||||
|
||||
if (!Files.isRegularFile(p)) {
|
||||
LoggerFactory.getLogger(AppImages.class).error("Image file " + p + " not found.");
|
||||
TrackEvent.error("Image file " + p + " not found.");
|
||||
return DEFAULT_IMAGE;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,9 @@ public class AppRosettaCheck {
|
|||
return;
|
||||
}
|
||||
|
||||
var ret = LocalShell.getShell().command("sysctl -n sysctl.proc_translated").readStdoutIfPossible();
|
||||
var ret = LocalShell.getShell()
|
||||
.command("sysctl -n sysctl.proc_translated")
|
||||
.readStdoutIfPossible();
|
||||
if (ret.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.xpipe.app.core.check;
|
|||
|
||||
import io.xpipe.app.core.AppProperties;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -22,9 +23,11 @@ public class AppUserDirectoryCheck {
|
|||
Files.delete(testDirectory);
|
||||
// if (true) throw new IOException();
|
||||
} catch (IOException e) {
|
||||
ErrorEvent.fromThrowable("Unable to access directory " + dataDirectory
|
||||
+ ". Please make sure that you have the appropriate permissions and no Antivirus program is blocking the access. "
|
||||
+ "In case you use cloud storage, verify that your cloud storage is working and you are logged in.", e)
|
||||
ErrorEvent.fromThrowable(
|
||||
"Unable to access directory " + dataDirectory
|
||||
+ ". Please make sure that you have the appropriate permissions and no Antivirus program is blocking the access. "
|
||||
+ "In case you use cloud storage, verify that your cloud storage is working and you are logged in.",
|
||||
e)
|
||||
.term()
|
||||
.expected()
|
||||
.handle();
|
||||
|
|
|
@ -11,6 +11,7 @@ import io.xpipe.app.prefs.AppPrefs;
|
|||
import io.xpipe.app.prefs.CloseBehaviourAlert;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.process.OsType;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.geometry.Rectangle2D;
|
||||
|
@ -23,17 +24,18 @@ import javafx.scene.layout.Region;
|
|||
import javafx.scene.paint.Color;
|
||||
import javafx.stage.Screen;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
public class AppMainWindow {
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ 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;
|
||||
|
|
|
@ -121,7 +121,7 @@ public interface ActionProvider {
|
|||
|
||||
interface BranchDataStoreCallSite<T extends DataStore> extends DataStoreCallSite<T> {
|
||||
|
||||
default boolean isDynamicallyGenerated(){
|
||||
default boolean isDynamicallyGenerated() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -150,8 +150,10 @@ public interface ActionProvider {
|
|||
.toList());
|
||||
|
||||
var menuProviders = ALL.stream()
|
||||
.map(actionProvider -> actionProvider.getBranchDataStoreCallSite() != null &&
|
||||
!actionProvider.getBranchDataStoreCallSite().isDynamicallyGenerated()
|
||||
.map(actionProvider -> actionProvider.getBranchDataStoreCallSite() != null
|
||||
&& !actionProvider
|
||||
.getBranchDataStoreCallSite()
|
||||
.isDynamicallyGenerated()
|
||||
? actionProvider.getBranchDataStoreCallSite().getChildren(null)
|
||||
: List.of())
|
||||
.flatMap(List::stream)
|
||||
|
|
|
@ -18,5 +18,5 @@ public enum DataStoreUsageCategory {
|
|||
@JsonProperty("group")
|
||||
GROUP,
|
||||
@JsonProperty("serial")
|
||||
SERIAL;
|
||||
SERIAL
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import io.xpipe.app.storage.DataStoreEntry;
|
|||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.util.FailableRunnable;
|
||||
import io.xpipe.core.util.ModuleLayerLoader;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.xpipe.app.fxcomps.impl;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.browser.session.BrowserChooserComp;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.core.AppLayoutModel;
|
||||
|
@ -15,11 +14,14 @@ import io.xpipe.app.storage.DataStorage;
|
|||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.core.store.FileNames;
|
||||
import io.xpipe.core.store.FileSystemStore;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.nio.file.Files;
|
||||
|
@ -34,8 +36,7 @@ public class ContextualFileReferenceChoiceComp extends Comp<CompStructure<HBox>>
|
|||
private final boolean allowSync;
|
||||
|
||||
public <T extends FileSystemStore> ContextualFileReferenceChoiceComp(
|
||||
Property<DataStoreEntryRef<T>> fileSystem, Property<String> filePath, boolean allowSync
|
||||
) {
|
||||
Property<DataStoreEntryRef<T>> fileSystem, Property<String> filePath, boolean allowSync) {
|
||||
this.allowSync = allowSync;
|
||||
this.fileSystem = new SimpleObjectProperty<>();
|
||||
fileSystem.subscribe(val -> {
|
||||
|
@ -92,7 +93,8 @@ public class ContextualFileReferenceChoiceComp extends Comp<CompStructure<HBox>>
|
|||
var f = data.resolve(FileNames.getFileName(currentPath.trim()));
|
||||
var source = Path.of(currentPath.trim());
|
||||
if (Files.exists(source)) {
|
||||
var shouldCopy = AppWindowHelper.showConfirmationAlert("confirmGitShareTitle","confirmGitShareHeader", "confirmGitShareContent");
|
||||
var shouldCopy = AppWindowHelper.showConfirmationAlert(
|
||||
"confirmGitShareTitle", "confirmGitShareHeader", "confirmGitShareContent");
|
||||
if (!shouldCopy) {
|
||||
return;
|
||||
}
|
||||
|
@ -115,8 +117,7 @@ public class ContextualFileReferenceChoiceComp extends Comp<CompStructure<HBox>>
|
|||
if (allowSync) {
|
||||
nodes.add(gitShareButton);
|
||||
}
|
||||
var layout = new HorizontalComp(nodes)
|
||||
.apply(struc -> struc.get().setFillHeight(true));
|
||||
var layout = new HorizontalComp(nodes).apply(struc -> struc.get().setFillHeight(true));
|
||||
|
||||
layout.apply(struc -> {
|
||||
struc.get().focusedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
|
|
|
@ -114,7 +114,8 @@ public class DataStoreChoiceComp<T extends DataStore> extends SimpleComp {
|
|||
StoreViewState.get().getActiveCategory(),
|
||||
selectedCategory)
|
||||
.styleClass(Styles.LEFT_PILL);
|
||||
var filter = new FilterComp(filterText).styleClass(Styles.CENTER_PILL).hgrow();
|
||||
var filter =
|
||||
new FilterComp(filterText).styleClass(Styles.CENTER_PILL).hgrow();
|
||||
|
||||
var addButton = Comp.of(() -> {
|
||||
MenuButton m = new MenuButton(null, new FontIcon("mdi2p-plus-box-outline"));
|
||||
|
|
|
@ -56,7 +56,7 @@ public class FilterComp extends Comp<CompStructure<CustomTextField>> {
|
|||
filter.focusedProperty()));
|
||||
filter.setAccessibleText("Filter");
|
||||
|
||||
filter.addEventFilter(KeyEvent.KEY_PRESSED,event -> {
|
||||
filter.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
|
||||
if (new KeyCodeCombination(KeyCode.ESCAPE).match(event)) {
|
||||
filter.getScene().getRoot().requestFocus();
|
||||
event.consume();
|
||||
|
|
|
@ -4,12 +4,14 @@ import io.xpipe.app.fxcomps.Comp;
|
|||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.skin.ComboBoxListViewSkin;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
|
@ -33,7 +35,8 @@ public class IntComboFieldComp extends Comp<CompStructure<ComboBox<String>>> {
|
|||
var text = new ComboBox<String>();
|
||||
text.setEditable(true);
|
||||
text.setValue(value.getValue() != null ? value.getValue().toString() : null);
|
||||
text.setItems(FXCollections.observableList(predefined.stream().map(integer -> "" + integer).toList()));
|
||||
text.setItems(FXCollections.observableList(
|
||||
predefined.stream().map(integer -> "" + integer).toList()));
|
||||
text.setMaxWidth(2000);
|
||||
text.getStyleClass().add("int-combo-field-comp");
|
||||
text.setSkin(new ComboBoxListViewSkin<>(text));
|
||||
|
|
|
@ -4,10 +4,12 @@ import io.xpipe.app.fxcomps.Comp;
|
|||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
public class PlatformThread {
|
||||
|
||||
public static <T> ObservableValue<T> syncHighFrequency(ObservableValue<T> observable) {
|
||||
var prop = new SimpleObjectProperty<T>(observable.getValue());
|
||||
var prop = new SimpleObjectProperty<>(observable.getValue());
|
||||
var applied = new AtomicBoolean(true);
|
||||
observable.addListener((observable1, oldValue, newValue) -> {
|
||||
if (Platform.isFxApplicationThread()) {
|
||||
|
|
|
@ -57,7 +57,10 @@ public class ErrorEvent {
|
|||
return EVENT_BASES.remove(t).description(msg);
|
||||
}
|
||||
|
||||
return builder().throwable(t).description(msg + (t.getMessage() != null ? "\n\n" + t.getMessage().trim() : ""));
|
||||
return builder()
|
||||
.throwable(t)
|
||||
.description(
|
||||
msg + (t.getMessage() != null ? "\n\n" + t.getMessage().trim() : ""));
|
||||
}
|
||||
|
||||
public static ErrorEventBuilder fromMessage(String msg) {
|
||||
|
|
|
@ -89,8 +89,9 @@ public class LauncherCommand implements Callable<Integer> {
|
|||
// there might be another instance running, for example
|
||||
// starting up or listening on another port
|
||||
if (!AppDataLock.lock()) {
|
||||
TrackEvent.info("Data directory " + AppProperties.get().getDataDir().toString()
|
||||
+ " is already locked. Is another instance running?");
|
||||
TrackEvent.info(
|
||||
"Data directory " + AppProperties.get().getDataDir().toString()
|
||||
+ " is already locked. Is another instance running?");
|
||||
OperationMode.halt(1);
|
||||
}
|
||||
|
||||
|
@ -104,28 +105,43 @@ public class LauncherCommand implements Callable<Integer> {
|
|||
// If an instance is running as another user, we cannot connect to it as the xpipe_auth file is inaccessible
|
||||
// Therefore the beacon client is not present.
|
||||
// We still should check whether it is somehow occupied, otherwise beacon server startup will fail
|
||||
TrackEvent.info("Another instance is already running on this port as another user or is not reachable. Quitting ...");
|
||||
TrackEvent.info(
|
||||
"Another instance is already running on this port as another user or is not reachable. Quitting ...");
|
||||
OperationMode.halt(1);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
client.get().performRequest(DaemonFocusExchange.Request.builder().mode(getEffectiveMode()).build());
|
||||
client.get()
|
||||
.performRequest(DaemonFocusExchange.Request.builder()
|
||||
.mode(getEffectiveMode())
|
||||
.build());
|
||||
if (!inputs.isEmpty()) {
|
||||
client.get().performRequest(DaemonOpenExchange.Request.builder().arguments(inputs).build());
|
||||
client.get()
|
||||
.performRequest(DaemonOpenExchange.Request.builder()
|
||||
.arguments(inputs)
|
||||
.build());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
var cli = XPipeInstallation.getLocalDefaultCliExecutable();
|
||||
ErrorEvent.fromThrowable("Unable to connect to existing running daemon instance as it did not respond." +
|
||||
" Either try to kill the process xpiped manually or use the command \"" +
|
||||
cli +
|
||||
"\" daemon stop --force.", ex).term().expected().handle();
|
||||
ErrorEvent.fromThrowable(
|
||||
"Unable to connect to existing running daemon instance as it did not respond."
|
||||
+ " Either try to kill the process xpiped manually or use the command \""
|
||||
+ cli
|
||||
+ "\" daemon stop --force.",
|
||||
ex)
|
||||
.term()
|
||||
.expected()
|
||||
.handle();
|
||||
}
|
||||
|
||||
if (OsType.getLocal().equals(OsType.MACOS)) {
|
||||
Desktop.getDesktop().setOpenURIHandler(e -> {
|
||||
try {
|
||||
client.get().performRequest(DaemonOpenExchange.Request.builder().arguments(List.of(e.getURI().toString())).build());
|
||||
client.get()
|
||||
.performRequest(DaemonOpenExchange.Request.builder()
|
||||
.arguments(List.of(e.getURI().toString()))
|
||||
.build());
|
||||
} catch (Exception ex) {
|
||||
ErrorEvent.fromThrowable(ex).expected().omit().handle();
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ import io.xpipe.app.fxcomps.impl.VerticalComp;
|
|||
import io.xpipe.app.util.Hyperlinks;
|
||||
import io.xpipe.app.util.JfxHelper;
|
||||
import io.xpipe.app.util.OptionsBuilder;
|
||||
|
||||
import io.xpipe.core.process.OsType;
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
|
@ -41,9 +41,9 @@ public class AboutCategory extends AppPrefsCategory {
|
|||
null)
|
||||
.addComp(
|
||||
new TileButtonComp("tryPtb", "tryPtbDescription", "mdi2t-test-tube", e -> {
|
||||
Hyperlinks.open(Hyperlinks.GITHUB_PTB);
|
||||
e.consume();
|
||||
})
|
||||
Hyperlinks.open(Hyperlinks.GITHUB_PTB);
|
||||
e.consume();
|
||||
})
|
||||
.grow(true, false),
|
||||
null)
|
||||
.addComp(
|
||||
|
@ -111,16 +111,15 @@ public class AboutCategory extends AppPrefsCategory {
|
|||
|
||||
private Comp<?> createProperties() {
|
||||
var title = Comp.of(() -> {
|
||||
return JfxHelper.createNamedEntry(
|
||||
AppI18n.observable("xPipeClient"),
|
||||
new SimpleStringProperty(
|
||||
"Version " + AppProperties.get().getVersion() + " ("
|
||||
+ AppProperties.get().getArch() + ")"),
|
||||
"logo.png");
|
||||
});
|
||||
return JfxHelper.createNamedEntry(
|
||||
AppI18n.observable("xPipeClient"),
|
||||
new SimpleStringProperty("Version " + AppProperties.get().getVersion() + " ("
|
||||
+ AppProperties.get().getArch() + ")"),
|
||||
"logo.png");
|
||||
});
|
||||
|
||||
if (OsType.getLocal() != OsType.MACOS) {
|
||||
title.styleClass(Styles.TEXT_BOLD);
|
||||
title.styleClass(Styles.TEXT_BOLD);
|
||||
}
|
||||
|
||||
var section = new OptionsBuilder()
|
||||
|
|
|
@ -14,12 +14,14 @@ import io.xpipe.app.terminal.ExternalTerminalType;
|
|||
import io.xpipe.app.util.PasswordLockSecretValue;
|
||||
import io.xpipe.core.util.InPlaceSecretValue;
|
||||
import io.xpipe.core.util.ModuleHelper;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.beans.value.ObservableBooleanValue;
|
||||
import javafx.beans.value.ObservableDoubleValue;
|
||||
import javafx.beans.value.ObservableStringValue;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
|
|
@ -114,10 +114,10 @@ public abstract class ExternalApplicationType implements PrefsChoiceValue {
|
|||
|
||||
protected Optional<Path> determineFromPath() {
|
||||
// Try to locate if it is in the Path
|
||||
try (var sc = LocalShell.getShell()
|
||||
.start()) {
|
||||
try (var sc = LocalShell.getShell().start()) {
|
||||
var out = sc.command(CommandBuilder.ofFunction(
|
||||
var1 -> var1.getShellDialect().getWhichCommand(executable))).readStdoutIfPossible();
|
||||
var1 -> var1.getShellDialect().getWhichCommand(executable)))
|
||||
.readStdoutIfPossible();
|
||||
if (out.isPresent()) {
|
||||
var first = out.get().lines().findFirst();
|
||||
if (first.isPresent()) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import io.xpipe.app.util.*;
|
|||
import io.xpipe.core.process.CommandBuilder;
|
||||
import io.xpipe.core.process.OsType;
|
||||
import io.xpipe.core.util.SecretValue;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -16,7 +17,7 @@ import java.util.function.Supplier;
|
|||
|
||||
public interface ExternalRdpClientType extends PrefsChoiceValue {
|
||||
|
||||
public static ExternalRdpClientType getApplicationLauncher() {
|
||||
static ExternalRdpClientType getApplicationLauncher() {
|
||||
if (OsType.getLocal() == OsType.WINDOWS) {
|
||||
return MSTSC;
|
||||
} else {
|
||||
|
@ -76,9 +77,10 @@ public interface ExternalRdpClientType extends PrefsChoiceValue {
|
|||
@Override
|
||||
protected Optional<Path> determineInstallation() {
|
||||
try {
|
||||
var r = WindowsRegistry.local().readValue(WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\Classes\\rdm\\DefaultIcon");
|
||||
var r = WindowsRegistry.local()
|
||||
.readValue(WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\Classes\\rdm\\DefaultIcon");
|
||||
return r.map(Path::of);
|
||||
} catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e).omit().handle();
|
||||
return Optional.empty();
|
||||
}
|
||||
|
@ -87,7 +89,11 @@ public interface ExternalRdpClientType extends PrefsChoiceValue {
|
|||
@Override
|
||||
protected void execute(Path file, LaunchConfiguration configuration) throws Exception {
|
||||
var config = writeConfig(configuration.getConfig());
|
||||
LocalShell.getShell().executeSimpleCommand(CommandBuilder.of().addFile(file.toString()).addFile(config.toString()).discardOutput());
|
||||
LocalShell.getShell()
|
||||
.executeSimpleCommand(CommandBuilder.of()
|
||||
.addFile(file.toString())
|
||||
.addFile(config.toString())
|
||||
.discardOutput());
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
// Startup is slow
|
||||
ThreadHelper.sleep(10000);
|
||||
|
|
|
@ -12,9 +12,11 @@ import io.xpipe.app.util.OptionsBuilder;
|
|||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.process.OsType;
|
||||
import io.xpipe.core.util.XPipeInstallation;
|
||||
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.ButtonType;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.nio.file.Files;
|
||||
|
@ -54,21 +56,33 @@ public class WorkspaceCreationAlert {
|
|||
}
|
||||
|
||||
if (Files.exists(path.get()) && !FileUtils.isEmptyDirectory(path.get().toFile())) {
|
||||
ErrorEvent.fromMessage("New workspace directory is not empty").expected().handle();
|
||||
ErrorEvent.fromMessage("New workspace directory is not empty")
|
||||
.expected()
|
||||
.handle();
|
||||
return;
|
||||
}
|
||||
|
||||
var shortcutName = (AppProperties.get().isStaging() ? "XPipe PTB" : "XPipe") + " (" + name.get() + ")";
|
||||
var file = switch (OsType.getLocal()) {
|
||||
case OsType.Windows w -> {
|
||||
var exec = XPipeInstallation.getCurrentInstallationBasePath().resolve(XPipeInstallation.getDaemonExecutablePath(w)).toString();
|
||||
yield DesktopShortcuts.create(exec, "-Dio.xpipe.app.dataDir=\"" + path.get().toString() + "\" -Dio.xpipe.app.acceptEula=true", shortcutName);
|
||||
}
|
||||
default -> {
|
||||
var exec = XPipeInstallation.getCurrentInstallationBasePath().resolve(XPipeInstallation.getRelativeCliExecutablePath(OsType.getLocal())).toString();
|
||||
yield DesktopShortcuts.create(exec, "-d \"" + path.get().toString() + "\" --accept-eula", shortcutName);
|
||||
}
|
||||
};
|
||||
var file =
|
||||
switch (OsType.getLocal()) {
|
||||
case OsType.Windows w -> {
|
||||
var exec = XPipeInstallation.getCurrentInstallationBasePath()
|
||||
.resolve(XPipeInstallation.getDaemonExecutablePath(w))
|
||||
.toString();
|
||||
yield DesktopShortcuts.create(
|
||||
exec,
|
||||
"-Dio.xpipe.app.dataDir=\"" + path.get().toString()
|
||||
+ "\" -Dio.xpipe.app.acceptEula=true",
|
||||
shortcutName);
|
||||
}
|
||||
default -> {
|
||||
var exec = XPipeInstallation.getCurrentInstallationBasePath()
|
||||
.resolve(XPipeInstallation.getRelativeCliExecutablePath(OsType.getLocal()))
|
||||
.toString();
|
||||
yield DesktopShortcuts.create(
|
||||
exec, "-d \"" + path.get().toString() + "\" --accept-eula", shortcutName);
|
||||
}
|
||||
};
|
||||
DesktopHelper.browseFileInDirectory(file);
|
||||
OperationMode.close();
|
||||
}
|
||||
|
|
|
@ -18,9 +18,7 @@ public class WorkspacesCategory extends AppPrefsCategory {
|
|||
.addTitle("manageWorkspaces")
|
||||
.sub(new OptionsBuilder()
|
||||
.nameAndDescription("workspaceAdd")
|
||||
.addComp(
|
||||
new ButtonComp(AppI18n.observable("addWorkspace"),
|
||||
WorkspaceCreationAlert::showAsync)))
|
||||
.addComp(new ButtonComp(AppI18n.observable("addWorkspace"), WorkspaceCreationAlert::showAsync)))
|
||||
.buildComp();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -584,6 +584,6 @@ public class DataStoreEntry extends StorageElement {
|
|||
@JsonProperty("top")
|
||||
TOP,
|
||||
@JsonProperty("bottom")
|
||||
BOTTOM;
|
||||
BOTTOM
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,9 +12,11 @@ import io.xpipe.app.util.*;
|
|||
import io.xpipe.core.process.*;
|
||||
import io.xpipe.core.store.FilePath;
|
||||
import io.xpipe.core.util.FailableFunction;
|
||||
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.ButtonBar;
|
||||
import javafx.scene.control.ButtonType;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
|
@ -27,56 +29,60 @@ import java.util.*;
|
|||
|
||||
public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||
|
||||
// ExternalTerminalType PUTTY = new WindowsType("app.putty","putty") {
|
||||
//
|
||||
// @Override
|
||||
// protected Optional<Path> determineInstallation() {
|
||||
// try {
|
||||
// var r = WindowsRegistry.local().readValue(WindowsRegistry.HKEY_LOCAL_MACHINE,
|
||||
// "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Xshell.exe");
|
||||
// return r.map(Path::of);
|
||||
// } catch (Exception e) {
|
||||
// ErrorEvent.fromThrowable(e).omit().handle();
|
||||
// return Optional.empty();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean supportsTabs() {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean isRecommended() {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean supportsColoredTitle() {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected void execute(Path file, LaunchConfiguration configuration) throws Exception {
|
||||
// try (var sc = LocalShell.getShell()) {
|
||||
// SshLocalBridge.init();
|
||||
// var b = SshLocalBridge.get();
|
||||
// var command = CommandBuilder.of().addFile(file.toString()).add("-ssh", "localhost", "-l").addQuoted(b.getUser())
|
||||
// .add("-i").addFile(b.getIdentityKey().toString()).add("-P", "" + b.getPort()).add("-hostkey").addFile(b.getPubHostKey().toString());
|
||||
// sc.executeSimpleCommand(command);
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// ExternalTerminalType PUTTY = new WindowsType("app.putty","putty") {
|
||||
//
|
||||
// @Override
|
||||
// protected Optional<Path> determineInstallation() {
|
||||
// try {
|
||||
// var r = WindowsRegistry.local().readValue(WindowsRegistry.HKEY_LOCAL_MACHINE,
|
||||
// "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Xshell.exe");
|
||||
// return r.map(Path::of);
|
||||
// } catch (Exception e) {
|
||||
// ErrorEvent.fromThrowable(e).omit().handle();
|
||||
// return Optional.empty();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean supportsTabs() {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean isRecommended() {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean supportsColoredTitle() {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected void execute(Path file, LaunchConfiguration configuration) throws Exception {
|
||||
// try (var sc = LocalShell.getShell()) {
|
||||
// SshLocalBridge.init();
|
||||
// var b = SshLocalBridge.get();
|
||||
// var command = CommandBuilder.of().addFile(file.toString()).add("-ssh", "localhost",
|
||||
// "-l").addQuoted(b.getUser())
|
||||
// .add("-i").addFile(b.getIdentityKey().toString()).add("-P", "" +
|
||||
// b.getPort()).add("-hostkey").addFile(b.getPubHostKey().toString());
|
||||
// sc.executeSimpleCommand(command);
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
ExternalTerminalType XSHELL = new WindowsType("app.xShell","Xshell") {
|
||||
ExternalTerminalType XSHELL = new WindowsType("app.xShell", "Xshell") {
|
||||
|
||||
@Override
|
||||
protected Optional<Path> determineInstallation() {
|
||||
try {
|
||||
var r = WindowsRegistry.local().readValue(WindowsRegistry.HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Xshell.exe");
|
||||
var r = WindowsRegistry.local()
|
||||
.readValue(
|
||||
WindowsRegistry.HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Xshell.exe");
|
||||
return r.map(Path::of);
|
||||
} catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e).omit().handle();
|
||||
return Optional.empty();
|
||||
}
|
||||
|
@ -107,7 +113,10 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
|||
try (var sc = LocalShell.getShell()) {
|
||||
var b = SshLocalBridge.get();
|
||||
var keyName = b.getIdentityKey().getFileName().toString();
|
||||
var command = CommandBuilder.of().addFile(file.toString()).add("-url").addQuoted("ssh://" + b.getUser() + "@localhost:" + b.getPort())
|
||||
var command = CommandBuilder.of()
|
||||
.addFile(file.toString())
|
||||
.add("-url")
|
||||
.addQuoted("ssh://" + b.getUser() + "@localhost:" + b.getPort())
|
||||
.add("-i", keyName);
|
||||
sc.executeSimpleCommand(command);
|
||||
}
|
||||
|
@ -121,29 +130,30 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
|||
|
||||
var b = SshLocalBridge.get();
|
||||
var keyName = b.getIdentityKey().getFileName().toString();
|
||||
var r = AppWindowHelper.showBlockingAlert(
|
||||
alert -> {
|
||||
alert.setTitle(AppI18n.get("xshellSetup"));
|
||||
alert.setAlertType(Alert.AlertType.NONE);
|
||||
var r = AppWindowHelper.showBlockingAlert(alert -> {
|
||||
alert.setTitle(AppI18n.get("xshellSetup"));
|
||||
alert.setAlertType(Alert.AlertType.NONE);
|
||||
|
||||
var activated = AppI18n.get().getMarkdownDocumentation("app:xshellSetup").formatted(b.getIdentityKey(), keyName);
|
||||
var markdown = new MarkdownComp(activated, s -> s)
|
||||
.prefWidth(450)
|
||||
.prefHeight(400)
|
||||
.createRegion();
|
||||
alert.getDialogPane().setContent(markdown);
|
||||
var activated = AppI18n.get()
|
||||
.getMarkdownDocumentation("app:xshellSetup")
|
||||
.formatted(b.getIdentityKey(), keyName);
|
||||
var markdown = new MarkdownComp(activated, s -> s)
|
||||
.prefWidth(450)
|
||||
.prefHeight(400)
|
||||
.createRegion();
|
||||
alert.getDialogPane().setContent(markdown);
|
||||
|
||||
alert.getButtonTypes().add(new ButtonType(AppI18n.get("ok"), ButtonBar.ButtonData.OK_DONE));
|
||||
});
|
||||
alert.getButtonTypes().add(new ButtonType(AppI18n.get("ok"), ButtonBar.ButtonData.OK_DONE));
|
||||
});
|
||||
r.filter(buttonType -> buttonType.getButtonData().isDefaultButton());
|
||||
r.ifPresent(buttonType -> {
|
||||
AppCache.update("xshellSetup", true);
|
||||
AppCache.update("xshellSetup", true);
|
||||
});
|
||||
return r.isPresent();
|
||||
}
|
||||
};
|
||||
|
||||
ExternalTerminalType SECURECRT = new WindowsType("app.secureCrt","SecureCRT") {
|
||||
ExternalTerminalType SECURECRT = new WindowsType("app.secureCrt", "SecureCRT") {
|
||||
|
||||
@Override
|
||||
protected Optional<Path> determineInstallation() {
|
||||
|
@ -156,7 +166,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
|||
}
|
||||
|
||||
return Optional.of(file);
|
||||
} catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e).omit().handle();
|
||||
return Optional.empty();
|
||||
}
|
||||
|
@ -182,21 +192,29 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
|||
try (var sc = LocalShell.getShell()) {
|
||||
SshLocalBridge.init();
|
||||
var b = SshLocalBridge.get();
|
||||
var command = CommandBuilder.of().addFile(file.toString()).add("/T").add("/SSH2", "/ACCEPTHOSTKEYS", "/I").addFile(
|
||||
b.getIdentityKey().toString()).add("/P", "" + b.getPort()).add("/L").addQuoted(b.getUser()).add("localhost");
|
||||
var command = CommandBuilder.of()
|
||||
.addFile(file.toString())
|
||||
.add("/T")
|
||||
.add("/SSH2", "/ACCEPTHOSTKEYS", "/I")
|
||||
.addFile(b.getIdentityKey().toString())
|
||||
.add("/P", "" + b.getPort())
|
||||
.add("/L")
|
||||
.addQuoted(b.getUser())
|
||||
.add("localhost");
|
||||
sc.executeSimpleCommand(command);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ExternalTerminalType MOBAXTERM = new WindowsType("app.mobaXterm","MobaXterm") {
|
||||
ExternalTerminalType MOBAXTERM = new WindowsType("app.mobaXterm", "MobaXterm") {
|
||||
|
||||
@Override
|
||||
protected Optional<Path> determineInstallation() {
|
||||
try {
|
||||
var r = WindowsRegistry.local().readValue(WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\Classes\\mobaxterm\\DefaultIcon");
|
||||
return r.map(Path::of);
|
||||
} catch (Exception e) {
|
||||
var r = WindowsRegistry.local()
|
||||
.readValue(WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\Classes\\mobaxterm\\DefaultIcon");
|
||||
return r.map(Path::of);
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e).omit().handle();
|
||||
return Optional.empty();
|
||||
}
|
||||
|
@ -220,13 +238,20 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
|||
@Override
|
||||
protected void execute(Path file, LaunchConfiguration configuration) throws Exception {
|
||||
try (var sc = LocalShell.getShell()) {
|
||||
var fixedFile = configuration.getScriptFile().toString()
|
||||
var fixedFile = configuration
|
||||
.getScriptFile()
|
||||
.toString()
|
||||
.replaceAll("\\\\", "/")
|
||||
.replaceAll("\\s","\\$0");
|
||||
var command = sc.getShellDialect() == ShellDialects.CMD ?
|
||||
CommandBuilder.of().addQuoted("cmd /c " + fixedFile) :
|
||||
CommandBuilder.of().addQuoted("powershell -NoProfile -ExecutionPolicy Bypass -File " + fixedFile);
|
||||
sc.command(CommandBuilder.of().addFile(file.toString()).add("-newtab").add(command)).execute();
|
||||
.replaceAll("\\s", "\\$0");
|
||||
var command = sc.getShellDialect() == ShellDialects.CMD
|
||||
? CommandBuilder.of().addQuoted("cmd /c " + fixedFile)
|
||||
: CommandBuilder.of()
|
||||
.addQuoted("powershell -NoProfile -ExecutionPolicy Bypass -File " + fixedFile);
|
||||
sc.command(CommandBuilder.of()
|
||||
.addFile(file.toString())
|
||||
.add("-newtab")
|
||||
.add(command))
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -249,11 +274,12 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
|||
yield CommandSupport.isInPathSilent(sc, "termius");
|
||||
}
|
||||
case OsType.Windows windows -> {
|
||||
var r = WindowsRegistry.local().readValue(WindowsRegistry.HKEY_CURRENT_USER, "SOFTWARE\\Classes\\termius");
|
||||
var r = WindowsRegistry.local()
|
||||
.readValue(WindowsRegistry.HKEY_CURRENT_USER, "SOFTWARE\\Classes\\termius");
|
||||
yield r.isPresent();
|
||||
}
|
||||
};
|
||||
} catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e).omit().handle();
|
||||
return false;
|
||||
}
|
||||
|
@ -298,20 +324,21 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
|||
|
||||
var b = SshLocalBridge.get();
|
||||
var keyName = b.getIdentityKey().getFileName().toString();
|
||||
var r = AppWindowHelper.showBlockingAlert(
|
||||
alert -> {
|
||||
alert.setTitle(AppI18n.get("termiusSetup"));
|
||||
alert.setAlertType(Alert.AlertType.NONE);
|
||||
var r = AppWindowHelper.showBlockingAlert(alert -> {
|
||||
alert.setTitle(AppI18n.get("termiusSetup"));
|
||||
alert.setAlertType(Alert.AlertType.NONE);
|
||||
|
||||
var activated = AppI18n.get().getMarkdownDocumentation("app:termiusSetup").formatted(b.getIdentityKey(), keyName);
|
||||
var markdown = new MarkdownComp(activated, s -> s)
|
||||
.prefWidth(450)
|
||||
.prefHeight(400)
|
||||
.createRegion();
|
||||
alert.getDialogPane().setContent(markdown);
|
||||
var activated = AppI18n.get()
|
||||
.getMarkdownDocumentation("app:termiusSetup")
|
||||
.formatted(b.getIdentityKey(), keyName);
|
||||
var markdown = new MarkdownComp(activated, s -> s)
|
||||
.prefWidth(450)
|
||||
.prefHeight(400)
|
||||
.createRegion();
|
||||
alert.getDialogPane().setContent(markdown);
|
||||
|
||||
alert.getButtonTypes().add(new ButtonType(AppI18n.get("ok"), ButtonBar.ButtonData.OK_DONE));
|
||||
});
|
||||
alert.getButtonTypes().add(new ButtonType(AppI18n.get("ok"), ButtonBar.ButtonData.OK_DONE));
|
||||
});
|
||||
r.filter(buttonType -> buttonType.getButtonData().isDefaultButton());
|
||||
r.ifPresent(buttonType -> {
|
||||
AppCache.update("termiusSetup", true);
|
||||
|
@ -320,7 +347,6 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
ExternalTerminalType CMD = new SimplePathType("app.cmd", "cmd.exe", true) {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -139,7 +139,7 @@ public class AppInstaller {
|
|||
public static final class Debian extends InstallerAssetType {
|
||||
|
||||
@Override
|
||||
public void installLocal(Path file) throws Exception {
|
||||
public void installLocal(Path file) {
|
||||
var start = AppPrefs.get() != null
|
||||
&& AppPrefs.get().terminalType().getValue() != null
|
||||
&& AppPrefs.get().terminalType().getValue().isAvailable();
|
||||
|
@ -177,7 +177,7 @@ public class AppInstaller {
|
|||
public static final class Rpm extends InstallerAssetType {
|
||||
|
||||
@Override
|
||||
public void installLocal(Path file) throws Exception {
|
||||
public void installLocal(Path file) {
|
||||
var start = AppPrefs.get() != null
|
||||
&& AppPrefs.get().terminalType().getValue() != null
|
||||
&& AppPrefs.get().terminalType().getValue().isAvailable();
|
||||
|
@ -215,7 +215,7 @@ public class AppInstaller {
|
|||
public static final class Pkg extends InstallerAssetType {
|
||||
|
||||
@Override
|
||||
public void installLocal(Path file) throws Exception {
|
||||
public void installLocal(Path file) {
|
||||
var start = AppPrefs.get() != null
|
||||
&& AppPrefs.get().terminalType().getValue() != null
|
||||
&& AppPrefs.get().terminalType().getValue().isAvailable();
|
||||
|
|
|
@ -5,8 +5,8 @@ import io.xpipe.app.fxcomps.util.BindingsHelper;
|
|||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.core.process.ShellDialects;
|
||||
import io.xpipe.core.process.ShellStoreState;
|
||||
|
||||
import io.xpipe.core.process.ShellTtyState;
|
||||
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
public class DataStoreFormatter {
|
||||
|
|
|
@ -3,14 +3,10 @@ package io.xpipe.app.util;
|
|||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.core.process.CommandBuilder;
|
||||
import io.xpipe.core.process.CommandControl;
|
||||
import io.xpipe.core.process.OsType;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
|
@ -109,21 +105,4 @@ public class FileOpener {
|
|||
},
|
||||
file -> openInTextEditor(file));
|
||||
}
|
||||
|
||||
public static void openCommandOutput(String keyName, Object key, CommandControl cc) {
|
||||
FileBridge.get()
|
||||
.openIO(
|
||||
keyName,
|
||||
key,
|
||||
null,
|
||||
() -> new FilterInputStream(cc.getStdout()) {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void close() {
|
||||
cc.close();
|
||||
}
|
||||
},
|
||||
null,
|
||||
file -> openInTextEditor(file));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,8 @@ public class InputHelper {
|
|||
|
||||
public static void onLeft(EventTarget target, boolean filter, Consumer<KeyEvent> r) {
|
||||
EventHandler<KeyEvent> e = event -> {
|
||||
if (new KeyCodeCombination(KeyCode.LEFT).match(event) || new KeyCodeCombination(KeyCode.NUMPAD4).match(event)) {
|
||||
if (new KeyCodeCombination(KeyCode.LEFT).match(event)
|
||||
|| new KeyCodeCombination(KeyCode.NUMPAD4).match(event)) {
|
||||
r.accept(event);
|
||||
}
|
||||
};
|
||||
|
@ -50,7 +51,8 @@ public class InputHelper {
|
|||
|
||||
public static void onRight(EventTarget target, boolean filter, Consumer<KeyEvent> r) {
|
||||
EventHandler<KeyEvent> e = event -> {
|
||||
if (new KeyCodeCombination(KeyCode.RIGHT).match(event) || new KeyCodeCombination(KeyCode.NUMPAD6).match(event)) {
|
||||
if (new KeyCodeCombination(KeyCode.RIGHT).match(event)
|
||||
|| new KeyCodeCombination(KeyCode.NUMPAD6).match(event)) {
|
||||
r.accept(event);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -37,8 +37,8 @@ public class NativeBridge {
|
|||
return Optional.ofNullable(macOsLibrary);
|
||||
}
|
||||
|
||||
public static interface MacOsLibrary extends Library {
|
||||
public interface MacOsLibrary extends Library {
|
||||
|
||||
public abstract void setAppearance(NativeLong window, boolean seamlessFrame, boolean dark);
|
||||
void setAppearance(NativeLong window, boolean seamlessFrame, boolean dark);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,9 +36,11 @@ public class ScanAlert {
|
|||
|
||||
public static void showAsync(DataStoreEntry entry) {
|
||||
ThreadHelper.runAsync(() -> {
|
||||
var showForCon = entry == null || (entry.getStore() instanceof ShellStore && (
|
||||
!(entry.getStorePersistentState() instanceof ShellStoreState shellStoreState) ||
|
||||
shellStoreState.getTtyState() == null || shellStoreState.getTtyState() == ShellTtyState.NONE));
|
||||
var showForCon = entry == null
|
||||
|| (entry.getStore() instanceof ShellStore
|
||||
&& (!(entry.getStorePersistentState() instanceof ShellStoreState shellStoreState)
|
||||
|| shellStoreState.getTtyState() == null
|
||||
|| shellStoreState.getTtyState() == ShellTtyState.NONE));
|
||||
if (showForCon) {
|
||||
showForShellStore(entry);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import io.xpipe.core.process.OsType;
|
|||
import io.xpipe.core.process.ProcessControlProvider;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.util.XPipeInstallation;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
|
@ -27,6 +28,7 @@ public class SshLocalBridge {
|
|||
private final Path directory;
|
||||
private final int port;
|
||||
private final String user;
|
||||
|
||||
@Setter
|
||||
private ShellControl runningShell;
|
||||
|
||||
|
@ -74,22 +76,39 @@ public class SshLocalBridge {
|
|||
INSTANCE = new SshLocalBridge(bridgeDir, port, user);
|
||||
|
||||
var hostKey = INSTANCE.getHostKey();
|
||||
if (!sc.getShellDialect().createFileExistsCommand(sc, hostKey.toString()).executeAndCheck()) {
|
||||
sc.command(CommandBuilder.of().add("ssh-keygen", "-q", "-N")
|
||||
.addQuoted("").add("-C").addQuoted("XPipe SSH bridge host key")
|
||||
.add("-t", "ed25519", "-f").addFile(hostKey.toString())).execute();
|
||||
if (!sc.getShellDialect()
|
||||
.createFileExistsCommand(sc, hostKey.toString())
|
||||
.executeAndCheck()) {
|
||||
sc.command(CommandBuilder.of()
|
||||
.add("ssh-keygen", "-q", "-N")
|
||||
.addQuoted("")
|
||||
.add("-C")
|
||||
.addQuoted("XPipe SSH bridge host key")
|
||||
.add("-t", "ed25519", "-f")
|
||||
.addFile(hostKey.toString()))
|
||||
.execute();
|
||||
}
|
||||
|
||||
var idKey = INSTANCE.getIdentityKey();
|
||||
if (!sc.getShellDialect().createFileExistsCommand(sc, idKey.toString()).executeAndCheck()) {
|
||||
sc.command(CommandBuilder.of().add("ssh-keygen", "-q", "-N")
|
||||
.addQuoted("").add("-C").addQuoted("XPipe SSH bridge identity").add("-t", "ed25519", "-f").addFile(idKey.toString())).execute();
|
||||
if (!sc.getShellDialect()
|
||||
.createFileExistsCommand(sc, idKey.toString())
|
||||
.executeAndCheck()) {
|
||||
sc.command(CommandBuilder.of()
|
||||
.add("ssh-keygen", "-q", "-N")
|
||||
.addQuoted("")
|
||||
.add("-C")
|
||||
.addQuoted("XPipe SSH bridge identity")
|
||||
.add("-t", "ed25519", "-f")
|
||||
.addFile(idKey.toString()))
|
||||
.execute();
|
||||
}
|
||||
|
||||
var config = INSTANCE.getConfig();
|
||||
var command = "\"" + XPipeInstallation.getLocalDefaultCliExecutable() + "\" ssh-launch " + sc.getShellDialect().environmentVariable("SSH_ORIGINAL_COMMAND");
|
||||
var command = "\"" + XPipeInstallation.getLocalDefaultCliExecutable() + "\" ssh-launch "
|
||||
+ sc.getShellDialect().environmentVariable("SSH_ORIGINAL_COMMAND");
|
||||
var pidFile = bridgeDir.resolve("sshd.pid");
|
||||
var content = """
|
||||
var content =
|
||||
"""
|
||||
ForceCommand %s
|
||||
PidFile "%s"
|
||||
StrictModes no
|
||||
|
@ -101,14 +120,24 @@ public class SshLocalBridge {
|
|||
PubkeyAuthentication yes
|
||||
AuthorizedKeysFile "%s"
|
||||
"""
|
||||
.formatted(command, pidFile.toString(), "" + port, INSTANCE.getHostKey().toString(), INSTANCE.getPubIdentityKey());;
|
||||
.formatted(
|
||||
command,
|
||||
pidFile.toString(),
|
||||
"" + port,
|
||||
INSTANCE.getHostKey().toString(),
|
||||
INSTANCE.getPubIdentityKey());
|
||||
Files.writeString(config, content);
|
||||
|
||||
// INSTANCE.updateConfig();
|
||||
|
||||
var exec = getSshd(sc);
|
||||
var launchCommand = CommandBuilder.of().addFile(exec).add("-f").addFile(INSTANCE.getConfig().toString()).add("-p", "" + port);
|
||||
var control = ProcessControlProvider.get().createLocalProcessControl(true).start();
|
||||
var exec = getSshd(sc);
|
||||
var launchCommand = CommandBuilder.of()
|
||||
.addFile(exec)
|
||||
.add("-f")
|
||||
.addFile(INSTANCE.getConfig().toString())
|
||||
.add("-p", "" + port);
|
||||
var control =
|
||||
ProcessControlProvider.get().createLocalProcessControl(true).start();
|
||||
control.writeLine(launchCommand.buildFull(control));
|
||||
INSTANCE.setRunningShell(control);
|
||||
}
|
||||
|
@ -125,19 +154,24 @@ public class SshLocalBridge {
|
|||
return;
|
||||
}
|
||||
|
||||
var updated = content + "\n\n" + """
|
||||
var updated = content + "\n\n"
|
||||
+ """
|
||||
Host %s
|
||||
HostName localhost
|
||||
User "%s"
|
||||
Port %s
|
||||
IdentityFile "%s"
|
||||
""".formatted(getName(), port, user, getIdentityKey());
|
||||
"""
|
||||
.formatted(getName(), port, user, getIdentityKey());
|
||||
Files.writeString(file, updated);
|
||||
}
|
||||
|
||||
private static String getSshd(ShellControl sc) throws Exception {
|
||||
if (OsType.getLocal() == OsType.WINDOWS) {
|
||||
return XPipeInstallation.getLocalBundledToolsDirectory().resolve("openssh").resolve("sshd").toString();
|
||||
return XPipeInstallation.getLocalBundledToolsDirectory()
|
||||
.resolve("openssh")
|
||||
.resolve("sshd")
|
||||
.toString();
|
||||
} else {
|
||||
var exec = sc.executeSimpleStringCommand(sc.getShellDialect().getWhichCommand("sshd"));
|
||||
return exec;
|
||||
|
|
|
@ -7,6 +7,7 @@ import io.xpipe.core.process.ShellControl;
|
|||
import io.xpipe.core.process.TerminalInitScriptConfig;
|
||||
import io.xpipe.core.process.WorkingDirectoryFunction;
|
||||
import io.xpipe.core.store.FilePath;
|
||||
|
||||
import lombok.Setter;
|
||||
import lombok.Value;
|
||||
import lombok.experimental.NonFinal;
|
||||
|
@ -90,7 +91,7 @@ public class TerminalLauncherManager {
|
|||
return waitForCompletion(e);
|
||||
}
|
||||
|
||||
public static Path waitForCompletion(Entry e) throws BeaconClientException, BeaconServerException {
|
||||
public static Path waitForCompletion(Entry e) throws BeaconServerException {
|
||||
while (true) {
|
||||
if (e.result == null) {
|
||||
ThreadHelper.sleep(10);
|
||||
|
|
|
@ -6,7 +6,7 @@ import io.xpipe.app.prefs.AppPrefs;
|
|||
|
||||
public class UnlockAlert {
|
||||
|
||||
public static void showIfNeeded() throws Throwable {
|
||||
public static void showIfNeeded() {
|
||||
if (AppPrefs.get().getLockCrypt().getValue() == null
|
||||
|| AppPrefs.get().getLockCrypt().getValue().isEmpty()) {
|
||||
return;
|
||||
|
|
|
@ -20,7 +20,8 @@ public class BeaconServer {
|
|||
|
||||
public static boolean isReachable(int port) {
|
||||
try (var socket = new Socket()) {
|
||||
socket.connect(new InetSocketAddress(Inet4Address.getByAddress(new byte[]{ 0x7f,0x00,0x00,0x01 }), port), 5000);
|
||||
socket.connect(
|
||||
new InetSocketAddress(Inet4Address.getByAddress(new byte[] {0x7f, 0x00, 0x00, 0x01}), port), 5000);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.xpipe.beacon.api;
|
||||
|
||||
import io.xpipe.beacon.BeaconInterface;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
|
|
|
@ -54,7 +54,7 @@ public interface CommandControl extends ProcessControl {
|
|||
|
||||
OutputStream startExternalStdin() throws Exception;
|
||||
|
||||
public void setExitTimeout(Duration duration);
|
||||
void setExitTimeout(Duration duration);
|
||||
|
||||
boolean waitFor();
|
||||
|
||||
|
|
|
@ -97,7 +97,13 @@ public interface OsType {
|
|||
public List<String> determineInterestingPaths(ShellControl pc) throws Exception {
|
||||
var home = getHomeDirectory(pc);
|
||||
return List.of(
|
||||
home, "/home", FileNames.join(home, "Downloads"), FileNames.join(home, "Documents"), "/etc", "/tmp", "/var");
|
||||
home,
|
||||
"/home",
|
||||
FileNames.join(home, "Downloads"),
|
||||
FileNames.join(home, "Documents"),
|
||||
"/etc",
|
||||
"/tmp",
|
||||
"/var");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -114,7 +120,6 @@ public interface OsType {
|
|||
public String getName() {
|
||||
return "Linux";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final class Linux extends Unix implements OsType, Local, Any {
|
||||
|
@ -123,7 +128,6 @@ public interface OsType {
|
|||
public String getId() {
|
||||
return "linux";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final class Solaris extends Unix implements Any {}
|
||||
|
@ -171,6 +175,5 @@ public interface OsType {
|
|||
public String getName() {
|
||||
return "Mac";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,9 @@ public interface ShellDumbMode {
|
|||
|
||||
private final String message;
|
||||
|
||||
public Unsupported(String message) {this.message = message;}
|
||||
public Unsupported(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public void throwIfUnsupported() {
|
||||
throw new UnsupportedOperationException(message);
|
||||
|
|
|
@ -5,7 +5,6 @@ import lombok.Getter;
|
|||
|
||||
@Getter
|
||||
public enum ShellTtyState {
|
||||
|
||||
@JsonProperty("none")
|
||||
NONE(true, false, false, true, true),
|
||||
@JsonProperty("merged")
|
||||
|
@ -19,7 +18,12 @@ public enum ShellTtyState {
|
|||
private final boolean supportsInput;
|
||||
private final boolean preservesOutput;
|
||||
|
||||
ShellTtyState(boolean hasSeparateStreams, boolean hasAnsiEscapes, boolean echoesAllInput, boolean supportsInput, boolean preservesOutput) {
|
||||
ShellTtyState(
|
||||
boolean hasSeparateStreams,
|
||||
boolean hasAnsiEscapes,
|
||||
boolean echoesAllInput,
|
||||
boolean supportsInput,
|
||||
boolean preservesOutput) {
|
||||
this.hasSeparateStreams = hasSeparateStreams;
|
||||
this.hasAnsiEscapes = hasAnsiEscapes;
|
||||
this.echoesAllInput = echoesAllInput;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.xpipe.core.process.CommandBuilder;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
@ -43,8 +44,10 @@ public class ConnectionFileSystem implements FileSystem {
|
|||
d.throwIfUnsupported();
|
||||
}
|
||||
|
||||
if (!shellControl.getTtyState().isPreservesOutput() || !shellControl.getTtyState().isSupportsInput()) {
|
||||
throw new UnsupportedOperationException("Shell has a PTY allocated and does not support file system operations");
|
||||
if (!shellControl.getTtyState().isPreservesOutput()
|
||||
|| !shellControl.getTtyState().isSupportsInput()) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Shell has a PTY allocated and does not support file system operations");
|
||||
}
|
||||
|
||||
return this;
|
||||
|
|
|
@ -3,7 +3,6 @@ package io.xpipe.core.store;
|
|||
import io.xpipe.core.process.ShellControl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public interface NetworkTunnelStore extends DataStore {
|
||||
|
@ -64,8 +63,6 @@ public interface NetworkTunnelStore extends DataStore {
|
|||
"Unable to create tunnel chain as one intermediate system does not support tunneling");
|
||||
}
|
||||
|
||||
var running = new AtomicBoolean();
|
||||
var runningCounter = new AtomicInteger();
|
||||
var counter = new AtomicInteger();
|
||||
var sessions = new ArrayList<NetworkTunnelSession>();
|
||||
NetworkTunnelStore current = this;
|
||||
|
@ -79,14 +76,6 @@ public interface NetworkTunnelStore extends DataStore {
|
|||
var currentRemotePort =
|
||||
sessions.isEmpty() ? remotePort : sessions.getLast().getLocalPort();
|
||||
var t = func.create(currentLocalPort, currentRemotePort);
|
||||
t.addListener(r -> {
|
||||
if (r) {
|
||||
runningCounter.incrementAndGet();
|
||||
} else {
|
||||
runningCounter.decrementAndGet();
|
||||
}
|
||||
running.set(runningCounter.get() == counter.get());
|
||||
});
|
||||
t.start();
|
||||
sessions.add(t);
|
||||
counter.incrementAndGet();
|
||||
|
|
|
@ -4,12 +4,6 @@ import lombok.EqualsAndHashCode;
|
|||
import lombok.SneakyThrows;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.GCMParameterSpec;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
@ -17,6 +11,12 @@ import java.security.SecureRandom;
|
|||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.KeySpec;
|
||||
import java.util.Random;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.GCMParameterSpec;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
@SuperBuilder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
|
|
|
@ -5,9 +5,9 @@ import lombok.EqualsAndHashCode;
|
|||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Random;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
@JsonTypeName("default")
|
||||
@SuperBuilder
|
||||
|
|
|
@ -7,8 +7,10 @@ import io.xpipe.app.ext.ActionProvider;
|
|||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
public class BrowseStoreAction implements ActionProvider {
|
||||
|
|
|
@ -4,6 +4,7 @@ import io.xpipe.app.core.AppI18n;
|
|||
import io.xpipe.app.ext.ActionProvider;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
public class LaunchStoreAction implements ActionProvider {
|
||||
|
|
|
@ -10,8 +10,10 @@ import io.xpipe.core.process.ShellStoreState;
|
|||
import io.xpipe.core.process.ShellTtyState;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.ext.base.script.ScriptHierarchy;
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -34,7 +36,8 @@ public class RunScriptActionMenu implements ActionProvider {
|
|||
var script = hierarchy.getLeafBase().getStore().assembleScriptChain(sc);
|
||||
TerminalLauncher.open(
|
||||
shellStore.getEntry(),
|
||||
hierarchy.getLeafBase().get().getName() + " - " + shellStore.get().getName(),
|
||||
hierarchy.getLeafBase().get().getName() + " - "
|
||||
+ shellStore.get().getName(),
|
||||
null,
|
||||
sc.command(script));
|
||||
}
|
||||
|
@ -145,7 +148,8 @@ public class RunScriptActionMenu implements ActionProvider {
|
|||
|
||||
@Override
|
||||
public List<? extends ActionProvider> getChildren(DataStoreEntryRef<ShellStore> store) {
|
||||
return List.of(new TerminalRunActionProvider(hierarchy), new BackgroundRunActionProvider(hierarchy));
|
||||
return List.of(
|
||||
new TerminalRunActionProvider(hierarchy), new BackgroundRunActionProvider(hierarchy));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -179,7 +183,9 @@ public class RunScriptActionMenu implements ActionProvider {
|
|||
|
||||
@Override
|
||||
public List<? extends ActionProvider> getChildren(DataStoreEntryRef<ShellStore> store) {
|
||||
return hierarchy.getChildren().stream().map(c -> new ScriptActionProvider(c)).toList();
|
||||
return hierarchy.getChildren().stream()
|
||||
.map(c -> new ScriptActionProvider(c))
|
||||
.toList();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -190,7 +196,7 @@ public class RunScriptActionMenu implements ActionProvider {
|
|||
private static class Action implements ActionProvider.Action {
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
public void execute() {
|
||||
StoreViewState.get().getAllScriptsCategory().select();
|
||||
}
|
||||
}
|
||||
|
@ -255,8 +261,12 @@ public class RunScriptActionMenu implements ActionProvider {
|
|||
var state = o.get().getStorePersistentState();
|
||||
if (state instanceof ShellStoreState shellStoreState) {
|
||||
return (shellStoreState.getShellDialect() == null
|
||||
|| shellStoreState.getShellDialect().getDumbMode().supportsAnyPossibleInteraction()) &&
|
||||
(shellStoreState.getTtyState() == null || shellStoreState.getTtyState() == ShellTtyState.NONE);
|
||||
|| shellStoreState
|
||||
.getShellDialect()
|
||||
.getDumbMode()
|
||||
.supportsAnyPossibleInteraction())
|
||||
&& (shellStoreState.getTtyState() == null
|
||||
|| shellStoreState.getTtyState() == ShellTtyState.NONE);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -280,7 +290,9 @@ public class RunScriptActionMenu implements ActionProvider {
|
|||
|
||||
return true;
|
||||
});
|
||||
var list = hierarchy.getChildren().stream().map(c -> new ScriptActionProvider(c)).toList();
|
||||
var list = hierarchy.getChildren().stream()
|
||||
.map(c -> new ScriptActionProvider(c))
|
||||
.toList();
|
||||
if (list.isEmpty()) {
|
||||
return List.of(new NoScriptsActionProvider());
|
||||
} else {
|
||||
|
|
|
@ -10,7 +10,9 @@ import io.xpipe.core.process.ShellControl;
|
|||
import io.xpipe.core.process.ShellDialects;
|
||||
import io.xpipe.core.store.LocalStore;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
|
|
|
@ -8,7 +8,9 @@ import io.xpipe.app.util.ScanAlert;
|
|||
import io.xpipe.core.process.ShellStoreState;
|
||||
import io.xpipe.core.process.ShellTtyState;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
public class ScanStoreAction implements ActionProvider {
|
||||
|
@ -41,8 +43,12 @@ public class ScanStoreAction implements ActionProvider {
|
|||
var state = o.get().getStorePersistentState();
|
||||
if (state instanceof ShellStoreState shellStoreState) {
|
||||
return (shellStoreState.getShellDialect() == null
|
||||
|| shellStoreState.getShellDialect().getDumbMode().supportsAnyPossibleInteraction()) &&
|
||||
(shellStoreState.getTtyState() == null || shellStoreState.getTtyState() == ShellTtyState.NONE);
|
||||
|| shellStoreState
|
||||
.getShellDialect()
|
||||
.getDumbMode()
|
||||
.supportsAnyPossibleInteraction())
|
||||
&& (shellStoreState.getTtyState() == null
|
||||
|| shellStoreState.getTtyState() == ShellTtyState.NONE);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import io.xpipe.app.prefs.AppPrefs;
|
|||
import io.xpipe.app.util.TerminalLauncher;
|
||||
import io.xpipe.core.process.CommandBuilder;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -38,7 +39,8 @@ public abstract class MultiExecuteAction implements BranchAction {
|
|||
model.getCurrentDirectory() != null
|
||||
? model.getCurrentDirectory()
|
||||
.getPath()
|
||||
: null, cmd);
|
||||
: null,
|
||||
cmd);
|
||||
}
|
||||
},
|
||||
false);
|
||||
|
|
|
@ -9,13 +9,15 @@ import io.xpipe.app.prefs.AppPrefs;
|
|||
import io.xpipe.app.util.TerminalLauncher;
|
||||
import io.xpipe.core.process.CommandBuilder;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class MultiExecuteSelectionAction implements BranchAction {
|
||||
|
||||
protected abstract CommandBuilder createCommand(ShellControl sc, OpenFileSystemModel model, List<BrowserEntry> entries);
|
||||
protected abstract CommandBuilder createCommand(
|
||||
ShellControl sc, OpenFileSystemModel model, List<BrowserEntry> entries);
|
||||
|
||||
protected abstract String getTerminalTitle();
|
||||
|
||||
|
@ -28,14 +30,15 @@ public abstract class MultiExecuteSelectionAction implements BranchAction {
|
|||
public void execute(OpenFileSystemModel model, List<BrowserEntry> entries) {
|
||||
model.withShell(
|
||||
pc -> {
|
||||
var cmd = pc.command(createCommand(pc, model, entries));
|
||||
TerminalLauncher.open(
|
||||
model.getEntry().getEntry(),
|
||||
getTerminalTitle(),
|
||||
model.getCurrentDirectory() != null
|
||||
? model.getCurrentDirectory()
|
||||
.getPath()
|
||||
: null, cmd);
|
||||
var cmd = pc.command(createCommand(pc, model, entries));
|
||||
TerminalLauncher.open(
|
||||
model.getEntry().getEntry(),
|
||||
getTerminalTitle(),
|
||||
model.getCurrentDirectory() != null
|
||||
? model.getCurrentDirectory()
|
||||
.getPath()
|
||||
: null,
|
||||
cmd);
|
||||
},
|
||||
false);
|
||||
}
|
||||
|
@ -61,8 +64,8 @@ public abstract class MultiExecuteSelectionAction implements BranchAction {
|
|||
pc -> {
|
||||
var cmd = createCommand(pc, model, entries);
|
||||
pc.command(cmd)
|
||||
.withWorkingDirectory(model.getCurrentDirectory()
|
||||
.getPath())
|
||||
.withWorkingDirectory(
|
||||
model.getCurrentDirectory().getPath())
|
||||
.execute();
|
||||
},
|
||||
false);
|
||||
|
|
|
@ -97,7 +97,8 @@ public class DesktopEnvironmentStore extends JacksonizedValue
|
|||
var scriptFile = base.getStore().createScript(dialect, toExecute);
|
||||
var launchScriptFile = base.getStore()
|
||||
.createScript(
|
||||
dialect, dialect.prepareTerminalInitFileOpenCommand(dialect, null, scriptFile.toString(), false));
|
||||
dialect,
|
||||
dialect.prepareTerminalInitFileOpenCommand(dialect, null, scriptFile.toString(), false));
|
||||
var launchConfig = new ExternalTerminalType.LaunchConfiguration(null, name, name, launchScriptFile, dialect);
|
||||
base.getStore().runDesktopScript(name, launchCommand.apply(launchConfig));
|
||||
}
|
||||
|
|
|
@ -11,9 +11,11 @@ import io.xpipe.app.util.ScriptHelper;
|
|||
import io.xpipe.core.process.CommandBuilder;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.ext.base.browser.MultiExecuteSelectionAction;
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.Node;
|
||||
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -47,7 +49,8 @@ public class RunScriptAction implements BrowserAction, BranchAction {
|
|||
return actions;
|
||||
}
|
||||
|
||||
private List<? extends BrowserAction> createActionForScriptHierarchy(OpenFileSystemModel model, List<BrowserEntry> selected) {
|
||||
private List<? extends BrowserAction> createActionForScriptHierarchy(
|
||||
OpenFileSystemModel model, List<BrowserEntry> selected) {
|
||||
var sc = model.getFileSystem().getShell().orElseThrow();
|
||||
var hierarchy = ScriptHierarchy.buildEnabledHierarchy(ref -> {
|
||||
if (!ref.getStore().isFileScript()) {
|
||||
|
@ -67,10 +70,13 @@ public class RunScriptAction implements BrowserAction, BranchAction {
|
|||
return createActionForScript(model, hierarchy.getLeafBase());
|
||||
}
|
||||
|
||||
var list = hierarchy.getChildren().stream().map(c -> createActionForScriptHierarchy(model, c)).toList();
|
||||
var list = hierarchy.getChildren().stream()
|
||||
.map(c -> createActionForScriptHierarchy(model, c))
|
||||
.toList();
|
||||
return new BranchAction() {
|
||||
@Override
|
||||
public List<? extends BrowserAction> getBranchingActions(OpenFileSystemModel model, List<BrowserEntry> entries) {
|
||||
public List<? extends BrowserAction> getBranchingActions(
|
||||
OpenFileSystemModel model, List<BrowserEntry> entries) {
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -91,7 +97,8 @@ public class RunScriptAction implements BrowserAction, BranchAction {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected CommandBuilder createCommand(ShellControl sc, OpenFileSystemModel model, List<BrowserEntry> selected) {
|
||||
protected CommandBuilder createCommand(
|
||||
ShellControl sc, OpenFileSystemModel model, List<BrowserEntry> selected) {
|
||||
if (!(model.getBrowserModel() instanceof BrowserSessionModel)) {
|
||||
return null;
|
||||
}
|
||||
|
@ -100,9 +107,7 @@ public class RunScriptAction implements BrowserAction, BranchAction {
|
|||
var script = ScriptHelper.createExecScript(sc, content);
|
||||
var builder = CommandBuilder.of().add(sc.getShellDialect().runScriptCommand(sc, script.toString()));
|
||||
selected.stream()
|
||||
.map(browserEntry -> browserEntry
|
||||
.getRawFileEntry()
|
||||
.getPath())
|
||||
.map(browserEntry -> browserEntry.getRawFileEntry().getPath())
|
||||
.forEach(s -> {
|
||||
builder.addFile(s);
|
||||
});
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.xpipe.ext.base.script;
|
|||
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
@ -37,10 +38,12 @@ public class ScriptHierarchy {
|
|||
}
|
||||
}
|
||||
|
||||
var top = all.stream().filter(ref -> {
|
||||
var parent = DataStorage.get().getDefaultDisplayParent(ref.get());
|
||||
return parent.isEmpty();
|
||||
}).toList();
|
||||
var top = all.stream()
|
||||
.filter(ref -> {
|
||||
var parent = DataStorage.get().getDefaultDisplayParent(ref.get());
|
||||
return parent.isEmpty();
|
||||
})
|
||||
.toList();
|
||||
|
||||
var mapped = top.stream()
|
||||
.map(ref -> buildHierarchy(ref, check -> {
|
||||
|
@ -56,17 +59,21 @@ public class ScriptHierarchy {
|
|||
}))
|
||||
.map(hierarchy -> condenseHierarchy(hierarchy))
|
||||
.filter(hierarchy -> hierarchy.show())
|
||||
.sorted(Comparator.comparing(scriptHierarchy -> scriptHierarchy.getBase().get().getName().toLowerCase()))
|
||||
.sorted(Comparator.comparing(scriptHierarchy ->
|
||||
scriptHierarchy.getBase().get().getName().toLowerCase()))
|
||||
.toList();
|
||||
return condenseHierarchy(new ScriptHierarchy(null, mapped));
|
||||
}
|
||||
|
||||
private static ScriptHierarchy buildHierarchy(DataStoreEntryRef<ScriptStore> ref, Predicate<DataStoreEntryRef<ScriptStore>> include) {
|
||||
private static ScriptHierarchy buildHierarchy(
|
||||
DataStoreEntryRef<ScriptStore> ref, Predicate<DataStoreEntryRef<ScriptStore>> include) {
|
||||
if (ref.getStore() instanceof ScriptGroupStore groupStore) {
|
||||
var children = groupStore.getEffectiveScripts().stream().filter(include)
|
||||
var children = groupStore.getEffectiveScripts().stream()
|
||||
.filter(include)
|
||||
.map(c -> buildHierarchy(c, include))
|
||||
.filter(hierarchy -> hierarchy.show())
|
||||
.sorted(Comparator.comparing(scriptHierarchy -> scriptHierarchy.getBase().get().getName().toLowerCase()))
|
||||
.sorted(Comparator.comparing(scriptHierarchy ->
|
||||
scriptHierarchy.getBase().get().getName().toLowerCase()))
|
||||
.toList();
|
||||
return new ScriptHierarchy(ref, children);
|
||||
} else {
|
||||
|
@ -74,11 +81,9 @@ public class ScriptHierarchy {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static ScriptHierarchy condenseHierarchy(ScriptHierarchy hierarchy) {
|
||||
var children = hierarchy.getChildren().stream()
|
||||
.map(c -> condenseHierarchy(c))
|
||||
.toList();
|
||||
var children =
|
||||
hierarchy.getChildren().stream().map(c -> condenseHierarchy(c)).toList();
|
||||
if (children.size() == 1 && !children.getFirst().isLeaf()) {
|
||||
var nestedChildren = children.getFirst().getChildren();
|
||||
return new ScriptHierarchy(hierarchy.getBase(), nestedChildren);
|
||||
|
|
|
@ -149,8 +149,8 @@ public abstract class ScriptStore extends JacksonizedValue implements DataStore,
|
|||
|
||||
public static List<DataStoreEntryRef<ScriptStore>> getEnabledScripts() {
|
||||
return DataStorage.get().getStoreEntries().stream()
|
||||
.filter(dataStoreEntry -> dataStoreEntry.getValidity().isUsable() &&
|
||||
dataStoreEntry.getStore() instanceof ScriptStore scriptStore
|
||||
.filter(dataStoreEntry -> dataStoreEntry.getValidity().isUsable()
|
||||
&& dataStoreEntry.getStore() instanceof ScriptStore scriptStore
|
||||
&& scriptStore.getState().isEnabled())
|
||||
.map(DataStoreEntry::<ScriptStore>ref)
|
||||
.toList();
|
||||
|
@ -161,7 +161,8 @@ public abstract class ScriptStore extends JacksonizedValue implements DataStore,
|
|||
scripts.forEach(scriptStoreDataStoreEntryRef ->
|
||||
scriptStoreDataStoreEntryRef.getStore().queryFlattenedScripts(seen));
|
||||
|
||||
var dependencies = new HashMap<DataStoreEntryRef<? extends ScriptStore>, Set<DataStoreEntryRef<SimpleScriptStore>>>();
|
||||
var dependencies =
|
||||
new HashMap<DataStoreEntryRef<? extends ScriptStore>, Set<DataStoreEntryRef<SimpleScriptStore>>>();
|
||||
seen.forEach(ref -> {
|
||||
var f = new HashSet<>(ref.getStore().queryFlattenedScripts());
|
||||
f.remove(ref);
|
||||
|
|
|
@ -8,9 +8,9 @@ import io.xpipe.core.process.ShellControl;
|
|||
import io.xpipe.core.process.ShellDialect;
|
||||
import io.xpipe.core.process.ShellInitCommand;
|
||||
import io.xpipe.core.util.ValidationException;
|
||||
import io.xpipe.ext.base.SelfReferentialStore;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.xpipe.ext.base.SelfReferentialStore;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
@ -62,7 +62,10 @@ public class SimpleScriptStore extends ScriptStore implements ShellInitCommand.T
|
|||
public String assembleScriptChain(ShellControl shellControl) {
|
||||
var nl = shellControl.getShellDialect().getNewLine().getNewLineString();
|
||||
var all = queryFlattenedScripts();
|
||||
var r = all.stream().map(ref -> ref.getStore().assembleScript(shellControl)).filter(s -> s != null).toList();
|
||||
var r = all.stream()
|
||||
.map(ref -> ref.getStore().assembleScript(shellControl))
|
||||
.filter(s -> s != null)
|
||||
.toList();
|
||||
if (r.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
@ -94,7 +97,12 @@ public class SimpleScriptStore extends ScriptStore implements ShellInitCommand.T
|
|||
|
||||
@Override
|
||||
public List<DataStoreEntryRef<ScriptStore>> getEffectiveScripts() {
|
||||
return scripts != null ? scripts.stream().filter(Objects::nonNull).filter(ref -> ref.get().getValidity().isUsable()).toList() : List.of();
|
||||
return scripts != null
|
||||
? scripts.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(ref -> ref.get().getValidity().isUsable())
|
||||
.toList()
|
||||
: List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -13,7 +13,7 @@ public class ServiceOpenAction implements ActionProvider {
|
|||
|
||||
@Override
|
||||
public BranchDataStoreCallSite<?> getBranchDataStoreCallSite() {
|
||||
return new BranchDataStoreCallSite<DataStore>() {
|
||||
return new BranchDataStoreCallSite<>() {
|
||||
@Override
|
||||
public boolean isMajor(DataStoreEntryRef<DataStore> o) {
|
||||
return true;
|
||||
|
|
|
@ -15,6 +15,7 @@ import io.xpipe.app.util.DataStoreFormatter;
|
|||
import io.xpipe.app.util.TerminalLauncher;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.ext.base.script.ScriptStore;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
|
@ -26,17 +27,21 @@ public interface ShellStoreProvider extends DataStoreProvider {
|
|||
@Override
|
||||
public void execute() throws Exception {
|
||||
ShellStore store = entry.getStore().asNeeded();
|
||||
TerminalLauncher.open(entry, DataStorage.get().getStoreEntryDisplayName(entry), null,
|
||||
TerminalLauncher.open(
|
||||
entry,
|
||||
DataStorage.get().getStoreEntryDisplayName(entry),
|
||||
null,
|
||||
ScriptStore.controlWithDefaultScripts(store.control()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
default ActionProvider.Action browserAction(BrowserSessionModel sessionModel, DataStoreEntry store, BooleanProperty busy) {
|
||||
default ActionProvider.Action browserAction(
|
||||
BrowserSessionModel sessionModel, DataStoreEntry store, BooleanProperty busy) {
|
||||
return new ActionProvider.Action() {
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
public void execute() {
|
||||
sessionModel.openFileSystemAsync(store.ref(), null, busy);
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue