This commit is contained in:
crschnick
2025-07-15 21:09:27 +00:00
parent 83c513d878
commit 692592d0ef
46 changed files with 194 additions and 146 deletions
@@ -1,19 +1,17 @@
package io.xpipe.app.action;
import io.xpipe.app.browser.action.BrowserAction;
import io.xpipe.app.browser.action.BrowserActionProvider;
import io.xpipe.app.ext.DataStore;
import io.xpipe.app.hub.action.*;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.core.JacksonMapper;
import io.xpipe.core.UuidHelper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.xpipe.core.UuidHelper;
import java.util.ArrayList;
@@ -98,14 +96,18 @@ public class ActionJacksonMapper {
"Store " + DataStorage.get().getStorePath(entry.get()) + " is incomplete"));
}
if (provider instanceof HubLeafProvider<?> l &&
(!l.getApplicableClass().isAssignableFrom(entry.get().getStore().getClass()) || !l.isApplicable(entry.get().ref()))) {
if (provider instanceof HubLeafProvider<?> l
&& (!l.getApplicableClass()
.isAssignableFrom(entry.get().getStore().getClass())
|| !l.isApplicable(entry.get().ref()))) {
throw ErrorEventFactory.expected(new IllegalArgumentException(
"Store " + DataStorage.get().getStorePath(entry.get()) + " is not applicable for action type"));
}
if (provider instanceof BatchHubProvider<?> h &&
(!h.getApplicableClass().isAssignableFrom(entry.get().getStore().getClass()) || !h.isApplicable(entry.get().ref()))) {
if (provider instanceof BatchHubProvider<?> h
&& (!h.getApplicableClass()
.isAssignableFrom(entry.get().getStore().getClass())
|| !h.isApplicable(entry.get().ref()))) {
throw ErrorEventFactory.expected(new IllegalArgumentException(
"Store " + DataStorage.get().getStorePath(entry.get()) + " is not applicable for action type"));
}
@@ -1,10 +1,7 @@
package io.xpipe.app.action;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.core.InPlaceSecretValue;
import io.xpipe.core.JacksonMapper;
import io.xpipe.core.SecretValue;
import io.xpipe.core.UuidHelper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
@@ -50,7 +47,8 @@ public class ActionUrls {
}
var json = sa.toNode();
var parsed = JacksonMapper.getDefault().treeToValue(json, new TypeReference<LinkedHashMap<String, JsonNode>>() {});
var parsed =
JacksonMapper.getDefault().treeToValue(json, new TypeReference<LinkedHashMap<String, JsonNode>>() {});
Map<String, List<String>> requestParams = new LinkedHashMap<>();
for (Map.Entry<String, JsonNode> e : parsed.entrySet()) {
@@ -3,10 +3,10 @@ package io.xpipe.app.action;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.DataStoreFormatter;
import io.xpipe.core.JacksonMapper;
import io.xpipe.core.UuidHelper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.xpipe.core.UuidHelper;
import lombok.experimental.SuperBuilder;
import java.util.*;
@@ -54,15 +54,22 @@ public abstract class SerializableAction extends AbstractAction {
}
var name = DataStoreFormatter.camelCaseToName(property.getKey());
name = Arrays.stream(name.split(" ")).filter(s -> !s.equals("Store")).collect(Collectors.joining(" "));
name = Arrays.stream(name.split(" "))
.filter(s -> !s.equals("Store"))
.collect(Collectors.joining(" "));
if (property.getValue().isTextual()) {
var value = property.getValue().textValue();
var uuid = UuidHelper.parse(value);
if (uuid.isPresent()) {
var refName = DataStorage.get().getStoreEntryIfPresent(uuid.get()).map(e -> e.getName()).or(() -> {
return DataStorage.get().getStoreCategoryIfPresent(uuid.get()).map(c -> c.getName());
});
var refName = DataStorage.get()
.getStoreEntryIfPresent(uuid.get())
.map(e -> e.getName())
.or(() -> {
return DataStorage.get()
.getStoreCategoryIfPresent(uuid.get())
.map(c -> c.getName());
});
map.put(name, refName.orElse(value));
} else {
map.put(name, value);
@@ -25,7 +25,6 @@ import javafx.collections.ListChangeListener;
import javafx.collections.ObservableMap;
import lombok.Getter;
import lombok.SneakyThrows;
import java.util.*;
@@ -62,12 +62,11 @@ public abstract class BrowserAction extends StoreAction<FileSystemStore> {
return true;
}
private void validateAutomatedAction() throws Exception {
var bap = (BrowserActionProvider) getProvider();
if (!bap.isApplicable(getModel(), getEntries())) {
throw ErrorEventFactory.expected(new IllegalArgumentException(
"Selection is not applicable for action type"));
throw ErrorEventFactory.expected(
new IllegalArgumentException("Selection is not applicable for action type"));
}
if (files != null) {
@@ -86,7 +85,8 @@ public abstract class BrowserAction extends StoreAction<FileSystemStore> {
} else {
var dir = files.getFirst();
if (!model.getFileSystem().directoryExists(dir)) {
throw ErrorEventFactory.expected(new IllegalArgumentException("File or directory does not exist: " + dir));
throw ErrorEventFactory.expected(
new IllegalArgumentException("File or directory does not exist: " + dir));
}
return dir;
}
@@ -30,7 +30,7 @@ public class RunCommandInBrowserActionProvider implements BrowserActionProvider
}
@Override
public void executeImpl() throws Exception {
public void executeImpl() {
var builder = CommandBuilder.of().add(command);
for (BrowserEntry entry : getEntries()) {
builder.addFile(entry.getRawFileEntry().getPath());
@@ -169,7 +169,9 @@ class BrowserFileListNameCell extends TableCell<BrowserEntry, String> {
var content = textField.getText();
if (content != null && !content.isEmpty()) {
var name = FilePath.of(content);
var baseNameEnd = item.getRawFileEntry().getKind() == FileKind.DIRECTORY ? content.length() : name.getBaseName().toString().length();
var baseNameEnd = item.getRawFileEntry().getKind() == FileKind.DIRECTORY
? content.length()
: name.getBaseName().toString().length();
textField.selectRange(0, baseNameEnd);
}
});
@@ -208,7 +208,7 @@ public class BrowserFileOpener {
}
@Override
public void onFinish() throws Exception {
public void onFinish() {
model.refreshFileEntriesSync(List.of(entry));
}
};
@@ -257,7 +257,7 @@ public class BrowserFileOpener {
}
@Override
public void onFinish() throws Exception {
public void onFinish() {
model.refreshFileEntriesSync(List.of(entry));
}
};
@@ -112,13 +112,7 @@ public class BrowserFileSystemTabComp extends SimpleComp {
rightBox.setFillHeight(true);
rightBox.getStyleClass().add("button-bar");
topBar.getChildren()
.setAll(
leftBox,
new Spacer(6),
navBar.get(),
new Spacer(6),
rightBox);
topBar.getChildren().setAll(leftBox, new Spacer(6), navBar.get(), new Spacer(6), rightBox);
topBar.setMinWidth(0);
if (model.getBrowserModel() instanceof BrowserFullSessionModel fullSessionModel) {
@@ -30,7 +30,6 @@ import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.SelectionMode;
import lombok.Getter;
import lombok.NonNull;
@@ -165,7 +165,8 @@ public class BrowserFileTransferOperation {
handleSingleOnSameFileSystem(file);
} else {
// Transfers might change the working directory
var currentDir = file.getFileSystem().getShell().orElseThrow().view().pwd();
var currentDir =
file.getFileSystem().getShell().orElseThrow().view().pwd();
handleSingleAcrossFileSystems(file);
file.getFileSystem().getShell().orElseThrow().view().cd(currentDir);
}
@@ -25,7 +25,7 @@ import java.util.List;
public interface BrowserMenuLeafProvider extends BrowserMenuItemProvider {
default void execute(BrowserFileSystemTabModel model, List<BrowserEntry> entries) throws Exception {
default void execute(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
createAction(model, entries).executeAsync();
}
@@ -7,8 +7,8 @@ import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.process.CommandBuilder;
import io.xpipe.app.process.ShellControl;
import io.xpipe.app.util.CommandDialog;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.value.ObservableValue;
import java.util.List;
@@ -25,7 +25,7 @@ public abstract class MultiExecuteMenuProvider implements BrowserMenuBranchProvi
new BrowserMenuLeafProvider() {
@Override
public void execute(BrowserFileSystemTabModel model, List<BrowserEntry> entries) throws Exception {
public void execute(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
var sc = model.getFileSystem().getShell().orElseThrow();
for (BrowserEntry entry : entries) {
var c = createCommand(sc, model, entry);
@@ -61,7 +61,7 @@ public abstract class MultiExecuteMenuProvider implements BrowserMenuBranchProvi
new BrowserMenuLeafProvider() {
@Override
public void execute(BrowserFileSystemTabModel model, List<BrowserEntry> entries) throws Exception {
public void execute(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
ThreadHelper.runAsync(() -> {
var sc = model.getFileSystem().getShell().orElseThrow();
for (BrowserEntry entry : entries) {
@@ -86,7 +86,7 @@ public abstract class MultiExecuteMenuProvider implements BrowserMenuBranchProvi
new BrowserMenuLeafProvider() {
@Override
public void execute(BrowserFileSystemTabModel model, List<BrowserEntry> entries) throws Exception {
public void execute(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
ThreadHelper.runFailableAsync(() -> {
var sc = model.getFileSystem().getShell().orElseThrow();
for (BrowserEntry entry : entries) {
@@ -95,7 +95,10 @@ public abstract class MultiExecuteMenuProvider implements BrowserMenuBranchProvi
continue;
}
sc.command(cmd).withWorkingDirectory(model.getCurrentDirectory().getPath()).execute();
sc.command(cmd)
.withWorkingDirectory(
model.getCurrentDirectory().getPath())
.execute();
}
model.refreshBrowserEntriesSync(entries);
});
@@ -5,8 +5,8 @@ import io.xpipe.app.browser.file.BrowserFileSystemTabModel;
import io.xpipe.app.browser.menu.BrowserMenuLeafProvider;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.util.LabelGraphic;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.value.ObservableValue;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
@@ -5,8 +5,8 @@ import io.xpipe.app.browser.file.BrowserFileSystemTabModel;
import io.xpipe.app.browser.menu.BrowserMenuLeafProvider;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.util.LabelGraphic;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.value.ObservableValue;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
@@ -10,8 +10,8 @@ import io.xpipe.app.browser.menu.FileTypeMenuProvider;
import io.xpipe.app.process.CommandBuilder;
import io.xpipe.app.process.ShellControl;
import io.xpipe.app.util.FileOpener;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
@@ -42,12 +42,16 @@ public class JavapMenuProvider
}
@Override
public void execute(BrowserFileSystemTabModel model, List<BrowserEntry> entries) throws Exception {
public void execute(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
ThreadHelper.runFailableAsync(() -> {
ShellControl sc = model.getFileSystem().getShell().orElseThrow();
for (BrowserEntry entry : entries) {
var command = CommandBuilder.of().add("javap", "-c", "-p").addFile(entry.getRawFileEntry().getPath());
var out = sc.command(command).withWorkingDirectory(model.getCurrentDirectory().getPath()).readStdoutOrThrow();
var command = CommandBuilder.of()
.add("javap", "-c", "-p")
.addFile(entry.getRawFileEntry().getPath());
var out = sc.command(command)
.withWorkingDirectory(model.getCurrentDirectory().getPath())
.readStdoutOrThrow();
FileOpener.openReadOnlyString(out);
}
});
@@ -5,8 +5,8 @@ import io.xpipe.app.browser.file.BrowserFileSystemTabModel;
import io.xpipe.app.browser.menu.BrowserMenuLeafProvider;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.util.LabelGraphic;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.value.ObservableValue;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
@@ -45,9 +45,13 @@ public class AppLayoutComp extends Comp<AppLayoutComp.Structure> {
multi.styleClass("background");
multi.apply(struc -> {
struc.get().opacityProperty().bind(Bindings.createDoubleBinding(() -> {
return AppPrefs.get().performanceMode().get() ? 1.0 : 0.95;
}, AppPrefs.get().performanceMode()));
struc.get()
.opacityProperty()
.bind(Bindings.createDoubleBinding(
() -> {
return AppPrefs.get().performanceMode().get() ? 1.0 : 0.95;
},
AppPrefs.get().performanceMode()));
});
var pane = new BorderPane();
@@ -6,6 +6,7 @@ import io.xpipe.app.core.AppFontSizes;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.core.AppLogs;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.LabelGraphic;
import io.xpipe.app.util.PlatformThread;
import io.xpipe.core.OsType;
@@ -17,7 +18,6 @@ import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ObservableDoubleValue;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
@@ -182,12 +182,16 @@ public class ModalOverlayComp extends SimpleComp {
content.setSpacing(20);
if (newValue.getTitle() != null) {
var l = new Label(
var l = new LabelComp(
newValue.getTitle().getValue(),
newValue.getGraphic() != null ? newValue.getGraphic().createGraphicNode() : null);
l.setGraphicTextGap(8);
AppFontSizes.xl(l);
content.getChildren().addFirst(l);
newValue.getGraphic() != null
? newValue.getGraphic()
: new LabelGraphic.IconGraphic("mdi2i-information-outline"));
l.apply(struc -> {
struc.get().setGraphicTextGap(8);
AppFontSizes.xl(struc.get());
});
content.getChildren().addFirst(l.createRegion());
} else {
content.getChildren().addFirst(Comp.vspacer(0).createRegion());
}
@@ -225,7 +225,6 @@ public class OptionsComp extends Comp<CompStructure<VBox>> {
.map(Region::getWidth)
.filter(aDouble -> aDouble > 0.0)
.max(Double::compareTo)
.map(d -> d)
.orElse(Region.USE_COMPUTED_SIZE);
},
nameRegions.stream().map(Region::widthProperty).toList().toArray(new Observable[0]));
@@ -2,7 +2,6 @@ package io.xpipe.app.core;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.core.OsType;
import javafx.scene.text.Font;
@@ -5,18 +5,15 @@ import io.xpipe.app.core.mode.OperationMode;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.beacon.BeaconAuthMethod;
import io.xpipe.beacon.BeaconClient;
import io.xpipe.beacon.BeaconClientInformation;
import io.xpipe.beacon.BeaconServer;
import io.xpipe.beacon.api.DaemonFocusExchange;
import io.xpipe.beacon.api.DaemonOpenExchange;
import io.xpipe.beacon.api.HandshakeExchange;
import io.xpipe.core.OsType;
import io.xpipe.core.XPipeInstallation;
import java.awt.*;
import java.nio.file.Files;
import java.util.List;
import java.util.Optional;
@@ -28,12 +25,14 @@ public class AppInstance {
public static Optional<BeaconClient> tryEstablishConnection(int port) {
try {
return Optional.of(BeaconClient.establishConnection(port, BeaconClientInformation.Daemon.builder().build()));
return Optional.of(BeaconClient.establishConnection(
port, BeaconClientInformation.Daemon.builder().build()));
} catch (Exception ex) {
ErrorEventFactory.fromThrowable(ex).omit().expected().handle();
return Optional.empty();
}
}
private static void checkStart(int attemptCounter) {
var port = AppBeaconServer.get().getPort();
var reachable = BeaconServer.isReachable(port);
@@ -65,7 +65,9 @@ public class AppTheme {
AppPrefs.get().theme().subscribe(t -> {
Theme.ALL.forEach(theme -> {
root.pseudoClassStateChanged(PseudoClass.getPseudoClass(theme.getCssId()), theme.getCssId().equals(t.getCssId()));
root.pseudoClassStateChanged(
PseudoClass.getPseudoClass(theme.getCssId()),
theme.getCssId().equals(t.getCssId()));
});
if (t == null) {
return;
@@ -31,12 +31,17 @@ public class AppGnomeScaleDialog {
return;
}
var content = AppDialog.dialogText("You are running XPipe on a Wayland system."
+ " If you are using a high-dpi display, eue to xwayland limitations, this might result in a blurry window. See the documentation for workarounds if you are affected.");
var content = AppDialog.dialogText(
"You are running XPipe on a Wayland system."
+ " If you are using a high-dpi display, eue to xwayland limitations, this might result in a blurry window. See the documentation for workarounds if you are affected.");
var modal = ModalOverlay.of("waylandScalingTitle", content);
modal.addButton(new ModalButton("docs", () -> {
DocumentationLink.GNOME_WAYLAND_SCALING.open();
}, false, false));
modal.addButton(new ModalButton(
"docs",
() -> {
DocumentationLink.GNOME_WAYLAND_SCALING.open();
},
false,
false));
modal.addButton(ModalButton.ok(() -> {
AppCache.update("gnomeScaleNoticeShown", true);
}));
@@ -44,7 +44,6 @@ public class DataStoreProviders {
return ALL.stream().filter(d -> d.getId().equalsIgnoreCase(id)).findAny();
}
@SuppressWarnings("unchecked")
public static <T extends DataStoreProvider> Optional<T> byStoreIfPresent(DataStore store) {
if (ALL == null) {
@@ -57,7 +56,8 @@ public class DataStoreProviders {
}
public static <T extends DataStoreProvider> T byStore(DataStore store) {
return DataStoreProviders.<T>byStoreIfPresent(store).orElseThrow(() -> new IllegalArgumentException("Unknown store class"));
return DataStoreProviders.<T>byStoreIfPresent(store)
.orElseThrow(() -> new IllegalArgumentException("Unknown store class"));
}
public static List<DataStoreProvider> getAll() {
@@ -29,7 +29,8 @@ public class ShellSession extends Session {
shellControl.start();
var shouldAliveCheck = !shellControl.isLocal();
var supportsAliveCheck = shellControl.getShellDialect().getDumbMode().supportsAnyPossibleInteraction();
var supportsAliveCheck =
shellControl.getShellDialect().getDumbMode().supportsAnyPossibleInteraction();
if (shouldAliveCheck && supportsAliveCheck) {
startAliveListener();
}
@@ -114,7 +114,8 @@ public class StoreCreationModel {
&& store.get().isComplete()
&& store.get() instanceof ValidatableStore) {
if (existingEntry != null) {
return !existingEntry.isFreeze() || !existingEntry.getName().equals(name.getValue());
return !existingEntry.isFreeze()
|| !existingEntry.getName().equals(name.getValue());
} else {
return true;
}
@@ -175,7 +176,8 @@ public class StoreCreationModel {
void connect() {
var temp = entry.getValue() != null ? entry.getValue() : DataStoreEntry.createTempWrapper(store.getValue());
var action = OpenTerminalHubMenuLeafProvider.Action.builder().ref(temp.ref()).build();
var action =
OpenTerminalHubMenuLeafProvider.Action.builder().ref(temp.ref()).build();
action.executeAsync();
}
@@ -267,7 +267,8 @@ public class StoreEntryWrapper {
} else {
return Optional.of(new EditHubLeafProvider());
}
}).orElse(null);
})
.orElse(null);
this.defaultActionProvider.setValue(defaultProvider);
var newMajorProviders = ActionProvider.ALL.stream()
@@ -207,8 +207,8 @@ public class AppPrefs {
final BooleanProperty disableSshPinCaching =
mapLocal(new SimpleBooleanProperty(false), "disableSshPinCaching", Boolean.class, false);
final ObjectProperty<SupportedLocale> language = mapLocal(
new SimpleObjectProperty<>(SupportedLocale.ENGLISH), "language", SupportedLocale.class, false);
final ObjectProperty<SupportedLocale> language =
mapLocal(new SimpleObjectProperty<>(SupportedLocale.ENGLISH), "language", SupportedLocale.class, false);
final BooleanProperty requireDoubleClickForConnections =
mapLocal(new SimpleBooleanProperty(false), "requireDoubleClickForConnections", Boolean.class, false);
@@ -7,7 +7,6 @@ import io.xpipe.core.JacksonMapper;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
@@ -23,9 +23,9 @@ public class LinksCategory extends AppPrefsCategory {
null)
.addComp(
new TileButtonComp("reddit", "redditDescription", "mdi2r-reddit", e -> {
Hyperlinks.open(Hyperlinks.REDDIT);
e.consume();
})
Hyperlinks.open(Hyperlinks.REDDIT);
e.consume();
})
.grow(true, false),
null)
.addComp(
@@ -25,7 +25,6 @@ import org.apache.commons.io.FileUtils;
import java.lang.management.ManagementFactory;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.management.MBeanServer;
public class TroubleshootCategory extends AppPrefsCategory {
@@ -159,17 +158,28 @@ public class TroubleshootCategory extends AppPrefsCategory {
if (OsType.getLocal() == OsType.MACOS && AppDistributionType.get() == AppDistributionType.NATIVE_INSTALLATION) {
b.addComp(
new TileButtonComp("uninstallApplication", "uninstallApplicationDescription", "mdi2d-dump-truck", e -> {
var file = XPipeInstallation.getCurrentInstallationBasePath().resolve("Contents").resolve("Resources").resolve("scripts").resolve("uninstall.sh");
OperationMode.executeAfterShutdown(() -> {
TerminalLauncher.openDirectFallback("Uninstall", sc -> ShellScript.lines(
"echo \"+ sudo " + file.toString() + "\"",
"sudo " + file.toString(),
ProcessControlProvider.get().getEffectiveLocalDialect().getPauseCommand())
);
});
e.consume();
})
new TileButtonComp(
"uninstallApplication",
"uninstallApplicationDescription",
"mdi2d-dump-truck",
e -> {
var file = XPipeInstallation.getCurrentInstallationBasePath()
.resolve("Contents")
.resolve("Resources")
.resolve("scripts")
.resolve("uninstall.sh");
OperationMode.executeAfterShutdown(() -> {
TerminalLauncher.openDirectFallback(
"Uninstall",
sc -> ShellScript.lines(
"echo \"+ sudo " + file + "\"",
"sudo " + file,
ProcessControlProvider.get()
.getEffectiveLocalDialect()
.getPauseCommand()));
});
e.consume();
})
.grow(true, false),
null);
}
@@ -154,7 +154,9 @@ public class ShellView {
}
public FilePath pwd() throws Exception {
return FilePath.of(shellControl.command(shellControl.getShellDialect().getPrintWorkingDirectoryCommand()).readStdoutOrThrow());
return FilePath.of(shellControl
.command(shellControl.getShellDialect().getPrintWorkingDirectoryCommand())
.readStdoutOrThrow());
}
public void cd(String directory) throws Exception {
@@ -71,7 +71,7 @@ public class DataStoreCategory extends StorageElement {
DataStoreCategoryConfig.empty());
}
public static Optional<DataStoreCategory> fromDirectory(Path dir) throws Exception {
public static Optional<DataStoreCategory> fromDirectory(Path dir) {
ObjectMapper mapper = JacksonMapper.getDefault();
var entryFile = dir.resolve("category.json");
@@ -112,7 +112,8 @@ public class DataStoreEntry extends StorageElement {
this.categoryUuid = categoryUuid;
this.store = store;
this.storeNode = storeNode;
this.provider = store != null ? DataStoreProviders.byStoreIfPresent(store).orElse(null) : null;
this.provider =
store != null ? DataStoreProviders.byStoreIfPresent(store).orElse(null) : null;
this.validity = this.provider != null ? validity : Validity.LOAD_FAILED;
this.storePersistentStateNode = storePersistentState;
this.notes = notes;
@@ -199,7 +200,7 @@ public class DataStoreEntry extends StorageElement {
return "icons/" + icon + ".svg";
}
public static Optional<DataStoreEntry> fromDirectory(Path dir) throws Exception {
public static Optional<DataStoreEntry> fromDirectory(Path dir) {
ObjectMapper mapper = JacksonMapper.getDefault();
var entryFile = dir.resolve("entry.json");
@@ -157,11 +157,8 @@ public class StandardStorage extends DataStorage {
}
addStoreCategory(c.get());
} catch (IOException ex) {
// IO exceptions are not expected
exception.set(new IOException("Unable to load data from " + path + ". Is it corrupted?", ex));
directoriesToKeep.add(path);
} catch (Exception ex) {
} // IO exceptions are not expected
catch (Exception ex) {
// Data corruption and schema changes are expected
ErrorEventFactory.fromThrowable(ex)
.expected()
@@ -214,11 +211,8 @@ public class StandardStorage extends DataStorage {
laterAddedEntries.add(entry.get());
storeEntries.put(entry.get(), entry.get());
} catch (IOException ex) {
// IO exceptions are not expected
exception.set(new IOException("Unable to load data from " + path + ". Is it corrupted?", ex));
directoriesToKeep.add(path);
} catch (Exception ex) {
} // IO exceptions are not expected
catch (Exception ex) {
// Data corruption and schema changes are expected
// We only keep invalid entries in developer mode as there's no point in keeping them in
@@ -1,19 +1,9 @@
package io.xpipe.app.terminal;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import io.xpipe.app.ext.ProcessControlProvider;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.prefs.ExternalApplicationType;
import io.xpipe.app.process.CommandBuilder;
import io.xpipe.app.process.ShellControl;
import io.xpipe.app.process.ShellDialects;
import io.xpipe.app.util.CommandSupport;
import io.xpipe.app.util.LocalShell;
import io.xpipe.app.util.ShellTemp;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.FilePath;
import io.xpipe.core.XPipeInstallation;
public interface GhosttyTerminalType extends ExternalTerminalType, TrackableTerminalType {
@@ -92,9 +92,7 @@ public class TerminalLaunchConfiguration {
Stop-Transcript > $Out-Null
echo 'Transcript stopped, output file is "sessions\\%s"'
"""
.formatted(logFile.getFileName(),
logFile,
launcherScript, logFile.getFileName());
.formatted(logFile.getFileName(), logFile, launcherScript, logFile.getFileName());
var config = new TerminalLaunchConfiguration(
entry != null ? color : null,
adjustedTitle,
@@ -104,20 +102,27 @@ public class TerminalLaunchConfiguration {
ShellDialects.POWERSHELL);
return config;
} else {
var found = sc.command(sc.getShellDialect().getWhichCommand("script"))
.executeAndCheck();
var found =
sc.command(sc.getShellDialect().getWhichCommand("script")).executeAndCheck();
if (!found) {
var suffix = sc.getOsType() == OsType.MACOS
? "This command is available in the util-linux package which can be installed via homebrew."
: "This command is available in the util-linux package.";
throw ErrorEventFactory.expected(new IllegalStateException(
"Logging requires the script command to be installed. " + suffix));
throw ErrorEventFactory.expected(
new IllegalStateException("Logging requires the script command to be installed. " + suffix));
}
var launcherScript = ScriptHelper.createExecScript(
LocalShell.getShell(), LocalShell.getShell().getShellDialect().terminalLauncherScript(request, adjustedTitle, alwaysPromptRestart));
var command = sc == LocalShell.getShell() ? launcherScript :
LocalShell.getShell().getShellDialect().getOpenScriptCommand(launcherScript.toString()).buildFull(LocalShell.getShell());
LocalShell.getShell(),
LocalShell.getShell()
.getShellDialect()
.terminalLauncherScript(request, adjustedTitle, alwaysPromptRestart));
var command = sc == LocalShell.getShell()
? launcherScript
: LocalShell.getShell()
.getShellDialect()
.getOpenScriptCommand(launcherScript.toString())
.buildFull(LocalShell.getShell());
var content = sc.getOsType() == OsType.MACOS || sc.getOsType() == OsType.BSD
? """
echo "Transcript started, output file is sessions/%s"
@@ -126,7 +131,15 @@ public class TerminalLaunchConfiguration {
cat "%s" | perl -pe 's/\\e([^\\[\\]]|\\[.*?[a-zA-Z]|\\].*?\\a)/\\n/g' | perl -0 -pe 's/\\n+/\\n/g' | col -b > "%s.new"
mv -f "%s.new" "%s"
"""
.formatted(logFile.getFileName(), logFile, command, logFile.getFileName(), logFile, logFile, logFile, logFile)
.formatted(
logFile.getFileName(),
logFile,
command,
logFile.getFileName(),
logFile,
logFile,
logFile,
logFile)
: """
echo "Transcript started, output file is sessions/%s"
script --quiet --command '%s' "%s"
@@ -134,14 +147,17 @@ public class TerminalLaunchConfiguration {
cat "%s" | perl -pe 's/\\e([^\\[\\]]|\\[.*?[a-zA-Z]|\\].*?\\a)/\\n/g' | perl -0 -pe 's/\\n+/\\n/g' | col -b > "%s.new"
mv -f "%s.new" "%s"
"""
.formatted(logFile.getFileName(), command, logFile, logFile.getFileName(), logFile, logFile, logFile, logFile);
.formatted(
logFile.getFileName(),
command,
logFile,
logFile.getFileName(),
logFile,
logFile,
logFile,
logFile);
var config = new TerminalLaunchConfiguration(
entry != null ? color : null,
adjustedTitle,
cleanTitle,
preferTabs,
content,
sc.getShellDialect());
entry != null ? color : null, adjustedTitle, cleanTitle, preferTabs, content, sc.getShellDialect());
config.scriptFile = ScriptHelper.createExecScript(sc.getShellDialect(), sc, content);
return config;
}
@@ -139,8 +139,12 @@ public class TerminalLauncher {
}
public static void open(
DataStoreEntry entry, String title, FilePath directory, ProcessControl cc, UUID request, boolean preferTabs)
throws Exception {
DataStoreEntry entry,
String title,
FilePath directory,
ProcessControl cc,
UUID request,
boolean preferTabs) {
var type = AppPrefs.get().terminalType().getValue();
if (type == null) {
throw ErrorEventFactory.expected(new IllegalStateException(AppI18n.get("noTerminalSet")));
@@ -79,7 +79,9 @@ public class AppDownloads {
req.put("first", first);
req.put("license", LicenseProvider.get().getLicenseId());
req.put("dist", AppDistributionType.get().getId());
req.put("lang", AppPrefs.get() != null ? AppPrefs.get().language().getValue().getId() : null);
req.put(
"lang",
AppPrefs.get() != null ? AppPrefs.get().language().getValue().getId() : null);
var url = URI.create("https://api.xpipe.io/version");
var builder = HttpRequest.newBuilder();
@@ -87,8 +87,10 @@ public class DesktopShortcuts {
</dict>
</plist>
""");
pc.command("cp \"" + icon + "\" \"" + base + "/Contents/Resources/xpipe.icns\"").execute();
pc.command("cp \"" + assets + "\" \"" + base + "/Contents/Resources/Assets.car\"").execute();
pc.command("cp \"" + icon + "\" \"" + base + "/Contents/Resources/xpipe.icns\"")
.execute();
pc.command("cp \"" + assets + "\" \"" + base + "/Contents/Resources/Assets.car\"")
.execute();
}
return base;
}
@@ -89,7 +89,8 @@ public enum DocumentationLink {
}
public static String getRoot() {
var ptbDocs = AppProperties.get().isDevelopmentEnvironment() || AppProperties.get().isStaging();
var ptbDocs = AppProperties.get().isDevelopmentEnvironment()
|| AppProperties.get().isStaging();
return ptbDocs ? "https://docs-ptb.xpipe.io" : "https://docs.xpipe.io";
}
}
@@ -1,7 +1,6 @@
package io.xpipe.app.util;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.core.FailableRunnable;
import java.time.Duration;
import java.util.Timer;
+4 -2
View File
@@ -121,7 +121,8 @@ open module io.xpipe.app {
uses ShellDialect;
provides ActionProvider with
XPipeUrlProvider, OpenTerminalHubMenuLeafProvider,
XPipeUrlProvider,
OpenTerminalHubMenuLeafProvider,
EditHubLeafProvider,
CloneHubLeafProvider,
DownloadMenuProvider,
@@ -142,7 +143,8 @@ open module io.xpipe.app {
ScanHubLeafProvider,
BrowseHubLeafProvider,
RefreshActionProvider,
ToggleActionProvider, OpenTerminalInDirectoryMenuProvider,
ToggleActionProvider,
OpenTerminalInDirectoryMenuProvider,
OpenNativeFileDetailsMenuProvider,
BrowseInNativeManagerActionProvider,
ApplyFileEditActionProvider,
@@ -5,6 +5,7 @@ import io.xpipe.core.FilePath;
import io.xpipe.core.OsType;
import io.xpipe.core.XPipeDaemonMode;
import io.xpipe.core.XPipeInstallation;
import lombok.SneakyThrows;
import java.io.BufferedReader;
@@ -22,7 +23,7 @@ public class BeaconServer {
@SneakyThrows
public static boolean isReachable(int port) {
var local = Inet4Address.getByAddress(new byte[]{0x7f, 0x00, 0x00, 0x01});
var local = Inet4Address.getByAddress(new byte[] {0x7f, 0x00, 0x00, 0x01});
try (var socket = new Socket()) {
InetSocketAddress adress = new InetSocketAddress(local, port);
@@ -36,7 +37,7 @@ public class BeaconServer {
// To be sure, check that the socket is indeed occupied
try (var ignored = new ServerSocket(port, 0, local)) {
return false;
} catch (Exception e) {
} catch (Exception e) {
return true;
}
}
+1 -1
View File
@@ -110,7 +110,7 @@ browserWelcomeEmptyContent=You can choose on the left which systems to open in t
browserWelcomeEmptyButton=Open local file browser
browserWelcomeSystems=You were recently connected to the following systems:
browserWelcomeDocsHeader=Documentation
browserWelcomeDocsContent=If you prefer a more guided approach to familiarizing yourself with XPipe, check out the documentation website.
browserWelcomeDocsContent=If you prefer a more guided approach to familiarizing yourself with XPipe, check out the documentation website.
browserWelcomeDocsButton=Open documentation
hostFeatureUnsupported=$FEATURE$ is not installed on the host
missingStore=$NAME$ does not exist