mirror of
https://github.com/xpipe-io/xpipe.git
synced 2025-04-19 02:33:39 +00:00
Rework
This commit is contained in:
parent
120a463d88
commit
7d252c1582
34 changed files with 225 additions and 106 deletions
|
@ -112,9 +112,7 @@ public class BeaconRequestHandler<T> implements HttpHandler {
|
|||
} catch (BeaconServerException serverException) {
|
||||
var cause = serverException.getCause() != null ? serverException.getCause() : serverException;
|
||||
var event = ErrorEvent.fromThrowable(cause).omit().handle();
|
||||
var link = event.getDocumentationLink() != null
|
||||
? event.getDocumentationLink().getLink()
|
||||
: null;
|
||||
var link = event.getLink();
|
||||
writeError(exchange, new BeaconServerErrorResponse(cause, link), 500);
|
||||
return;
|
||||
} catch (IOException ex) {
|
||||
|
@ -136,9 +134,7 @@ public class BeaconRequestHandler<T> implements HttpHandler {
|
|||
return;
|
||||
} catch (Throwable other) {
|
||||
var event = ErrorEvent.fromThrowable(other).omit().expected().handle();
|
||||
var link = event.getDocumentationLink() != null
|
||||
? event.getDocumentationLink().getLink()
|
||||
: null;
|
||||
var link = event.getLink();
|
||||
writeError(exchange, new BeaconServerErrorResponse(other, link), 500);
|
||||
return;
|
||||
}
|
||||
|
@ -167,9 +163,7 @@ public class BeaconRequestHandler<T> implements HttpHandler {
|
|||
}
|
||||
} catch (Throwable other) {
|
||||
var event = ErrorEvent.fromThrowable(other).handle();
|
||||
var link = event.getDocumentationLink() != null
|
||||
? event.getDocumentationLink().getLink()
|
||||
: null;
|
||||
var link = event.getLink();
|
||||
writeError(exchange, new BeaconServerErrorResponse(other, link), 500);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package io.xpipe.app.browser;
|
||||
|
||||
import io.xpipe.app.comp.Comp;
|
||||
import io.xpipe.app.storage.DataColor;
|
||||
import io.xpipe.app.storage.DataStoreColor;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.Property;
|
||||
|
@ -34,7 +34,7 @@ public abstract class BrowserSessionTab {
|
|||
|
||||
public abstract String getIcon();
|
||||
|
||||
public abstract DataColor getColor();
|
||||
public abstract DataStoreColor getColor();
|
||||
|
||||
public boolean isCloseable() {
|
||||
return true;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package io.xpipe.app.browser;
|
||||
|
||||
import io.xpipe.app.comp.Comp;
|
||||
import io.xpipe.app.storage.DataColor;
|
||||
import io.xpipe.app.storage.DataStoreColor;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
|
@ -42,7 +42,7 @@ public abstract class BrowserStoreSessionTab<T extends DataStore> extends Browse
|
|||
}
|
||||
|
||||
@Override
|
||||
public DataColor getColor() {
|
||||
public DataStoreColor getColor() {
|
||||
return DataStorage.get().getEffectiveColor(entry.get());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import io.xpipe.app.browser.BrowserFullSessionModel;
|
|||
import io.xpipe.app.browser.BrowserSessionTab;
|
||||
import io.xpipe.app.comp.Comp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.storage.DataColor;
|
||||
import io.xpipe.app.storage.DataStoreColor;
|
||||
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
|
@ -42,7 +42,7 @@ public final class BrowserHistoryTabModel extends BrowserSessionTab {
|
|||
}
|
||||
|
||||
@Override
|
||||
public DataColor getColor() {
|
||||
public DataStoreColor getColor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import io.xpipe.app.core.AppI18n;
|
|||
import io.xpipe.app.core.AppLayoutModel;
|
||||
import io.xpipe.app.core.window.AppDialog;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.storage.DataColor;
|
||||
import io.xpipe.app.storage.DataStoreColor;
|
||||
import io.xpipe.app.terminal.TerminalDockComp;
|
||||
import io.xpipe.app.terminal.TerminalDockModel;
|
||||
import io.xpipe.app.terminal.TerminalView;
|
||||
|
@ -176,7 +176,7 @@ public final class BrowserTerminalDockTabModel extends BrowserSessionTab {
|
|||
}
|
||||
|
||||
@Override
|
||||
public DataColor getColor() {
|
||||
public DataStoreColor getColor() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,8 +108,14 @@ public class ContextualFileReferenceChoiceComp extends Comp<CompStructure<HBox>>
|
|||
}
|
||||
|
||||
var handler = DataStorageSyncHandler.getInstance();
|
||||
var syncedTarget = handler.addDataFile(
|
||||
source, target, sync.getPerUser().test(source));
|
||||
var syncedTarget = handler.addDataFile(source, target, sync.getPerUser().test(source));
|
||||
|
||||
var pubSource = Path.of(source + ".pub");
|
||||
if (Files.exists(pubSource)) {
|
||||
var pubTarget = sync.getTargetLocation().apply(pubSource);
|
||||
DataStorageSyncHandler.getInstance().addDataFile(pubSource, pubTarget, sync.getPerUser().test(pubSource));
|
||||
}
|
||||
|
||||
Platform.runLater(() -> {
|
||||
filePath.setValue(FilePath.of(syncedTarget));
|
||||
});
|
||||
|
|
|
@ -83,6 +83,10 @@ public class ModalOverlay {
|
|||
AppDialog.show(this, false);
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
AppDialog.hide(this);
|
||||
}
|
||||
|
||||
public boolean isShowing() {
|
||||
return AppDialog.getModalOverlays().contains(this);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import io.xpipe.app.comp.base.*;
|
|||
import io.xpipe.app.core.AppFontSizes;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.storage.DataColor;
|
||||
import io.xpipe.app.storage.DataStoreColor;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreCategory;
|
||||
import io.xpipe.app.util.ClipboardHelper;
|
||||
|
@ -187,7 +187,7 @@ public class StoreCategoryComp extends SimpleComp {
|
|||
});
|
||||
|
||||
category.getColor().subscribe((c) -> {
|
||||
DataColor.applyStyleClasses(c, struc.get());
|
||||
DataStoreColor.applyStyleClasses(c, struc.get());
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -222,7 +222,7 @@ public class StoreCategoryComp extends SimpleComp {
|
|||
event.consume();
|
||||
});
|
||||
color.getItems().add(none);
|
||||
Arrays.stream(DataColor.values()).forEach(dataStoreColor -> {
|
||||
Arrays.stream(DataStoreColor.values()).forEach(dataStoreColor -> {
|
||||
MenuItem m = new MenuItem();
|
||||
m.textProperty().bind(AppI18n.observable(dataStoreColor.getId()));
|
||||
m.setOnAction(event -> {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
package io.xpipe.app.comp.store;
|
||||
|
||||
public class StoreCategoryConfigComp {}
|
|
@ -2,7 +2,7 @@ package io.xpipe.app.comp.store;
|
|||
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.storage.DataColor;
|
||||
import io.xpipe.app.storage.DataStoreColor;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreCategory;
|
||||
import io.xpipe.app.util.DerivedObservableList;
|
||||
|
@ -35,7 +35,7 @@ public class StoreCategoryWrapper {
|
|||
private final IntegerProperty shownContainedEntriesCount = new SimpleIntegerProperty();
|
||||
private final IntegerProperty allContainedEntriesCount = new SimpleIntegerProperty();
|
||||
private final BooleanProperty expanded = new SimpleBooleanProperty();
|
||||
private final Property<DataColor> color = new SimpleObjectProperty<>();
|
||||
private final Property<DataStoreColor> color = new SimpleObjectProperty<>();
|
||||
private final BooleanProperty largeCategoryOptimizations = new SimpleBooleanProperty();
|
||||
private StoreCategoryWrapper cachedParent;
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package io.xpipe.app.comp.store;
|
|||
import io.xpipe.app.comp.base.ModalButton;
|
||||
import io.xpipe.app.comp.base.ModalOverlay;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.core.AppLayoutModel;
|
||||
import io.xpipe.app.core.window.AppDialog;
|
||||
import io.xpipe.app.ext.DataStoreCreationCategory;
|
||||
import io.xpipe.app.ext.DataStoreProvider;
|
||||
|
@ -143,6 +144,16 @@ public class StoreCreationDialog {
|
|||
modal.hideable(AppI18n.observable(model.storeTypeNameKey() + "Add"), graphic, () -> {
|
||||
modal.show();
|
||||
});
|
||||
AppLayoutModel.get().getSelected().addListener((observable, oldValue, newValue) -> {
|
||||
if (model.getFinished().get() || !modal.isShowing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
modal.hide();
|
||||
AppLayoutModel.get().getQueueEntries().add(new AppLayoutModel.QueueEntry(AppI18n.observable(model.storeTypeNameKey() + "Add"), graphic, () -> {
|
||||
modal.show();
|
||||
}));
|
||||
});
|
||||
modal.setRequireCloseButtonForClose(true);
|
||||
modal.addButton(new ModalButton(
|
||||
"docs",
|
||||
|
|
|
@ -12,7 +12,7 @@ import io.xpipe.app.core.*;
|
|||
import io.xpipe.app.ext.ActionProvider;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.resources.AppResources;
|
||||
import io.xpipe.app.storage.DataColor;
|
||||
import io.xpipe.app.storage.DataStoreColor;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.util.*;
|
||||
|
@ -340,7 +340,7 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
event.consume();
|
||||
});
|
||||
color.getItems().add(none);
|
||||
Arrays.stream(DataColor.values()).forEach(dataStoreColor -> {
|
||||
Arrays.stream(DataStoreColor.values()).forEach(dataStoreColor -> {
|
||||
MenuItem m = new MenuItem();
|
||||
m.textProperty().bind(AppI18n.observable(dataStoreColor.getId()));
|
||||
m.setOnAction(event -> {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package io.xpipe.app.comp.store;
|
||||
|
||||
import io.xpipe.app.ext.ActionProvider;
|
||||
import io.xpipe.app.ext.LocalStore;
|
||||
import io.xpipe.app.ext.ShellStore;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.storage.DataColor;
|
||||
import io.xpipe.app.storage.DataStoreColor;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreCategory;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
|
@ -42,7 +43,7 @@ public class StoreEntryWrapper {
|
|||
private final BooleanProperty expanded = new SimpleBooleanProperty();
|
||||
private final Property<Object> persistentState = new SimpleObjectProperty<>();
|
||||
private final Property<Map<String, Object>> cache = new SimpleObjectProperty<>(Map.of());
|
||||
private final Property<DataColor> color = new SimpleObjectProperty<>();
|
||||
private final Property<DataStoreColor> color = new SimpleObjectProperty<>();
|
||||
private final Property<StoreCategoryWrapper> category = new SimpleObjectProperty<>();
|
||||
private final Property<String> summary = new SimpleObjectProperty<>();
|
||||
private final Property<StoreNotes> notes;
|
||||
|
@ -201,7 +202,7 @@ public class StoreEntryWrapper {
|
|||
customIcon.setValue(entry.getIcon());
|
||||
iconFile.setValue(entry.getEffectiveIconFile());
|
||||
busy.setValue(entry.getBusyCounter().get() != 0);
|
||||
deletable.setValue(entry.getConfiguration().isDeletable());
|
||||
deletable.setValue(!(entry.getStore() instanceof LocalStore));
|
||||
sessionActive.setValue(entry.getStore() instanceof SingletonSessionStore<?> ss
|
||||
&& entry.getStore() instanceof ShellStore
|
||||
&& ss.isSessionRunning());
|
||||
|
|
|
@ -4,7 +4,7 @@ import io.xpipe.app.comp.Comp;
|
|||
import io.xpipe.app.comp.CompStructure;
|
||||
import io.xpipe.app.comp.base.IconButtonComp;
|
||||
import io.xpipe.app.comp.base.ListBoxViewComp;
|
||||
import io.xpipe.app.storage.DataColor;
|
||||
import io.xpipe.app.storage.DataStoreColor;
|
||||
import io.xpipe.app.util.BindingsHelper;
|
||||
import io.xpipe.app.util.LabelGraphic;
|
||||
|
||||
|
@ -65,7 +65,7 @@ public abstract class StoreSectionBaseComp extends Comp<CompStructure<VBox>> {
|
|||
if (section.getDepth() == 1) {
|
||||
section.getWrapper().getColor().subscribe(val -> {
|
||||
var newList = new ArrayList<>(vbox.getStyleClass());
|
||||
newList.removeIf(s -> Arrays.stream(DataColor.values())
|
||||
newList.removeIf(s -> Arrays.stream(DataStoreColor.values())
|
||||
.anyMatch(dataStoreColor -> dataStoreColor.getId().equals(s)));
|
||||
newList.remove("gray");
|
||||
newList.add("color-box");
|
||||
|
|
|
@ -56,6 +56,12 @@ public class AppDialog {
|
|||
show(o, false);
|
||||
}
|
||||
|
||||
public static void hide(ModalOverlay o) {
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
modalOverlays.remove(o);
|
||||
});
|
||||
}
|
||||
|
||||
public static void showAndWait(ModalOverlay o) {
|
||||
show(o, true);
|
||||
}
|
||||
|
|
|
@ -2,10 +2,11 @@ package io.xpipe.app.issue;
|
|||
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.util.DocumentationLink;
|
||||
import io.xpipe.app.util.Hyperlinks;
|
||||
|
||||
public interface ErrorAction {
|
||||
|
||||
static ErrorAction openDocumentation(DocumentationLink link) {
|
||||
static ErrorAction openDocumentation(String link) {
|
||||
return new ErrorAction() {
|
||||
@Override
|
||||
public String getName() {
|
||||
|
@ -19,7 +20,7 @@ public interface ErrorAction {
|
|||
|
||||
@Override
|
||||
public boolean handle(ErrorEvent event) {
|
||||
link.open();
|
||||
Hyperlinks.open(link);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -42,7 +42,7 @@ public class ErrorEvent {
|
|||
@Singular
|
||||
private List<Path> attachments;
|
||||
|
||||
private DocumentationLink documentationLink;
|
||||
private String link;
|
||||
|
||||
private String email;
|
||||
private String userReport;
|
||||
|
@ -147,6 +147,10 @@ public class ErrorEvent {
|
|||
|
||||
public static class ErrorEventBuilder {
|
||||
|
||||
public ErrorEventBuilder documentationLink(DocumentationLink documentationLink) {
|
||||
return link(documentationLink.getLink());
|
||||
}
|
||||
|
||||
public ErrorEventBuilder term() {
|
||||
return terminal(true);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import io.xpipe.app.core.AppI18n;
|
|||
import io.xpipe.app.core.AppLayoutModel;
|
||||
import io.xpipe.app.util.LicenseRequiredException;
|
||||
|
||||
import io.xpipe.app.util.PlatformThread;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
@ -22,8 +21,6 @@ import javafx.scene.layout.VBox;
|
|||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import static atlantafx.base.theme.Styles.ACCENT;
|
||||
import static atlantafx.base.theme.Styles.BUTTON_OUTLINED;
|
||||
|
||||
|
@ -134,8 +131,8 @@ public class ErrorHandlerComp extends SimpleComp {
|
|||
actionBox.getChildren().add(ac);
|
||||
}
|
||||
|
||||
if (event.getDocumentationLink() != null) {
|
||||
actionBox.getChildren().add(createActionComp(ErrorAction.openDocumentation(event.getDocumentationLink())));
|
||||
if (event.getLink() != null) {
|
||||
actionBox.getChildren().add(createActionComp(ErrorAction.openDocumentation(event.getLink())));
|
||||
}
|
||||
|
||||
if (actionBox.getChildren().size() > 0) {
|
||||
|
|
|
@ -1,25 +1,86 @@
|
|||
package io.xpipe.app.password;
|
||||
|
||||
import io.xpipe.app.ext.ProcessControlProvider;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.terminal.TerminalLauncher;
|
||||
import io.xpipe.app.util.*;
|
||||
import io.xpipe.core.process.CommandBuilder;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.process.ShellScript;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@JsonTypeName("bitwarden")
|
||||
public class BitwardenPasswordManager extends PasswordManagerFixedCommand {
|
||||
public class BitwardenPasswordManager implements PasswordManager {
|
||||
|
||||
private static final UUID MASTER_PASSWORD_ID = UUID.randomUUID();
|
||||
private static ShellControl SHELL;
|
||||
|
||||
private static synchronized ShellControl getOrStartShell() throws Exception {
|
||||
if (SHELL == null) {
|
||||
SHELL = ProcessControlProvider.get().createLocalProcessControl(true);
|
||||
}
|
||||
SHELL.start();
|
||||
return SHELL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ShellScript getScript() {
|
||||
var s = "bw get password $KEY --nointeraction --raw";
|
||||
return new ShellScript(s);
|
||||
public synchronized String retrievePassword(String key) {
|
||||
try {
|
||||
CommandSupport.isInLocalPathOrThrow("BitwardenCLI", "bw");
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e).link("https://bitwarden.com/help/cli/#download-and-install").handle();
|
||||
return null;
|
||||
}
|
||||
|
||||
try (var sc = getOrStartShell().start()) {
|
||||
var command = sc.command(CommandBuilder.of().add("bw", "get", "item", "xpipe-test", "--nointeraction"));
|
||||
var r = command.readStdoutAndStderr();
|
||||
if (r[1].contains("You are not logged in")) {
|
||||
var script = ShellScript.lines(
|
||||
sc.getShellDialect().getEchoCommand("Log in into your Bitwarden account from the CLI:", false),
|
||||
"bw login"
|
||||
);
|
||||
TerminalLauncher.openDirect("Bitwarden login", script);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (r[1].contains("Vault is locked")) {
|
||||
var pw = SecretManager.retrieve(new SecretRetrievalStrategy.Prompt(), "Unlock vault with your Bitwarden master password", MASTER_PASSWORD_ID, 0, true);
|
||||
if (pw == null) {
|
||||
return null;
|
||||
}
|
||||
var cmd = sc.command(CommandBuilder.of().add("bw", "unlock", "--passwordenv", "BW_PASSWORD")
|
||||
.fixedEnvironment("BW_PASSWORD", pw.getSecretValue()));
|
||||
cmd.setSensitive();
|
||||
var out = cmd.readStdoutOrThrow();
|
||||
var matcher = Pattern.compile("export BW_SESSION=\"(.+)\"").matcher(out);
|
||||
if (matcher.find()) {
|
||||
var sessionKey = matcher.group(1);
|
||||
sc.view().setSensitiveEnvironmentVariable("BW_SESSION", sessionKey);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
var b = CommandBuilder.of().add("bw", "get", "password").addLiteral(key).add("--nointeraction", "--raw");
|
||||
return sc.command(b).readStdoutOrThrow();
|
||||
} catch (Exception ex) {
|
||||
ErrorEvent.fromThrowable(ex).handle();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDocsLink() {
|
||||
return "https://bitwarden.com/help/cli/#get";
|
||||
return "https://bitwarden.com/help/cli/#download-and-install";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeyPlaceholder() {
|
||||
return "Item ID";
|
||||
return "Item name";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -883,7 +883,7 @@ public abstract class DataStorage {
|
|||
return false;
|
||||
}
|
||||
|
||||
public DataColor getEffectiveColor(DataStoreEntry entry) {
|
||||
public DataStoreColor getEffectiveColor(DataStoreEntry entry) {
|
||||
var cat = getStoreCategoryIfPresent(entry.getCategoryUuid()).orElseThrow();
|
||||
var root = getRootForEntry(entry, cat);
|
||||
if (root.getColor() != null) {
|
||||
|
|
|
@ -39,7 +39,7 @@ public class DataStoreCategory extends StorageElement {
|
|||
String name,
|
||||
Instant lastUsed,
|
||||
Instant lastModified,
|
||||
DataColor color,
|
||||
DataStoreColor color,
|
||||
boolean dirty,
|
||||
UUID parentCategory,
|
||||
StoreSortMode sortMode,
|
||||
|
@ -102,7 +102,7 @@ public class DataStoreCategory extends StorageElement {
|
|||
var color = Optional.ofNullable(json.get("color"))
|
||||
.map(node -> {
|
||||
try {
|
||||
return mapper.treeToValue(node, DataColor.class);
|
||||
return mapper.treeToValue(node, DataStoreColor.class);
|
||||
} catch (JsonProcessingException e) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package io.xpipe.app.storage;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
@Value
|
||||
@Builder
|
||||
@Jacksonized
|
||||
public class DataStoreCategoryConfig {
|
||||
|
||||
DataStoreColor color;
|
||||
|
||||
Boolean allowInitScripts;
|
||||
|
||||
Boolean allowTerminalPromptScripts;
|
||||
}
|
|
@ -10,7 +10,7 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
|
||||
@Getter
|
||||
public enum DataColor {
|
||||
public enum DataStoreColor {
|
||||
@JsonProperty("red")
|
||||
RED("red", "\uD83D\uDD34", Color.DARKRED),
|
||||
|
||||
|
@ -27,7 +27,7 @@ public enum DataColor {
|
|||
private final String emoji;
|
||||
private final Color terminalColor;
|
||||
|
||||
DataColor(String id, String emoji, Color terminalColor) {
|
||||
DataStoreColor(String id, String emoji, Color terminalColor) {
|
||||
this.id = id;
|
||||
this.emoji = emoji;
|
||||
this.terminalColor = terminalColor;
|
||||
|
@ -43,9 +43,9 @@ public enum DataColor {
|
|||
return "#" + (format(value.getRed()) + format(value.getGreen()) + format(value.getBlue())).toUpperCase();
|
||||
}
|
||||
|
||||
public static void applyStyleClasses(DataColor color, Node node) {
|
||||
public static void applyStyleClasses(DataStoreColor color, Node node) {
|
||||
var newList = new ArrayList<>(node.getStyleClass());
|
||||
newList.removeIf(s -> Arrays.stream(DataColor.values())
|
||||
newList.removeIf(s -> Arrays.stream(DataStoreColor.values())
|
||||
.anyMatch(dataStoreColor -> dataStoreColor.getId().equals(s)));
|
||||
newList.remove("gray");
|
||||
if (color != null) {
|
|
@ -43,9 +43,6 @@ public class DataStoreEntry extends StorageElement {
|
|||
@NonFinal
|
||||
DataStore store;
|
||||
|
||||
@NonFinal
|
||||
Configuration configuration;
|
||||
|
||||
AtomicInteger busyCounter = new AtomicInteger();
|
||||
|
||||
@Getter
|
||||
|
@ -88,10 +85,9 @@ public class DataStoreEntry extends StorageElement {
|
|||
DataStorageNode storeNode,
|
||||
boolean dirty,
|
||||
Validity validity,
|
||||
Configuration configuration,
|
||||
JsonNode storePersistentState,
|
||||
boolean expanded,
|
||||
DataColor color,
|
||||
DataStoreColor color,
|
||||
String notes,
|
||||
Order explicitOrder,
|
||||
String icon) {
|
||||
|
@ -100,7 +96,6 @@ public class DataStoreEntry extends StorageElement {
|
|||
this.store = store;
|
||||
this.storeNode = storeNode;
|
||||
this.validity = validity;
|
||||
this.configuration = configuration;
|
||||
this.explicitOrder = explicitOrder;
|
||||
this.provider = store != null ? DataStoreProviders.byStore(store) : null;
|
||||
this.storePersistentStateNode = storePersistentState;
|
||||
|
@ -125,7 +120,6 @@ public class DataStoreEntry extends StorageElement {
|
|||
this.icon = icon;
|
||||
this.storeNode = DataStorageNode.fail();
|
||||
this.validity = Validity.INCOMPLETE;
|
||||
this.configuration = Configuration.defaultConfiguration();
|
||||
this.expanded = false;
|
||||
this.provider = null;
|
||||
this.storePersistentStateNode = null;
|
||||
|
@ -173,7 +167,6 @@ public class DataStoreEntry extends StorageElement {
|
|||
storeNode,
|
||||
true,
|
||||
validity,
|
||||
Configuration.defaultConfiguration(),
|
||||
null,
|
||||
false,
|
||||
null,
|
||||
|
@ -221,7 +214,7 @@ public class DataStoreEntry extends StorageElement {
|
|||
var color = Optional.ofNullable(json.get("color"))
|
||||
.map(node -> {
|
||||
try {
|
||||
return mapper.treeToValue(node, DataColor.class);
|
||||
return mapper.treeToValue(node, DataStoreColor.class);
|
||||
} catch (JsonProcessingException e) {
|
||||
return null;
|
||||
}
|
||||
|
@ -254,15 +247,6 @@ public class DataStoreEntry extends StorageElement {
|
|||
}
|
||||
})
|
||||
.orElse(null);
|
||||
var configuration = Optional.ofNullable(json.get("configuration"))
|
||||
.map(node -> {
|
||||
try {
|
||||
return mapper.treeToValue(node, Configuration.class);
|
||||
} catch (JsonProcessingException e) {
|
||||
return Configuration.defaultConfiguration();
|
||||
}
|
||||
})
|
||||
.orElse(Configuration.defaultConfiguration());
|
||||
var expanded = Optional.ofNullable(stateJson.get("expanded"))
|
||||
.map(jsonNode -> jsonNode.booleanValue())
|
||||
.orElse(true);
|
||||
|
@ -271,7 +255,7 @@ public class DataStoreEntry extends StorageElement {
|
|||
color = Optional.ofNullable(stateJson.get("color"))
|
||||
.map(node -> {
|
||||
try {
|
||||
return mapper.treeToValue(node, DataColor.class);
|
||||
return mapper.treeToValue(node, DataStoreColor.class);
|
||||
} catch (JsonProcessingException e) {
|
||||
return null;
|
||||
}
|
||||
|
@ -313,7 +297,6 @@ public class DataStoreEntry extends StorageElement {
|
|||
node,
|
||||
false,
|
||||
store == null ? Validity.LOAD_FAILED : Validity.INCOMPLETE,
|
||||
configuration,
|
||||
persistentState,
|
||||
expanded,
|
||||
color,
|
||||
|
@ -425,11 +408,6 @@ public class DataStoreEntry extends StorageElement {
|
|||
}
|
||||
}
|
||||
|
||||
public void setConfiguration(Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
notifyUpdate(false, true);
|
||||
}
|
||||
|
||||
public void setCategoryUuid(UUID categoryUuid) {
|
||||
var changed = !Objects.equals(this.categoryUuid, categoryUuid);
|
||||
this.categoryUuid = categoryUuid;
|
||||
|
@ -464,7 +442,6 @@ public class DataStoreEntry extends StorageElement {
|
|||
obj.put("categoryUuid", categoryUuid.toString());
|
||||
obj.set("color", mapper.valueToTree(color));
|
||||
obj.set("icon", mapper.valueToTree(icon));
|
||||
obj.set("configuration", mapper.valueToTree(configuration));
|
||||
|
||||
ObjectNode stateObj = JsonNodeFactory.instance.objectNode();
|
||||
stateObj.put("lastUsed", lastUsed.toString());
|
||||
|
|
|
@ -48,8 +48,6 @@ public class ImpersistentStorage extends DataStorage {
|
|||
|
||||
var e = DataStoreEntry.createNew(
|
||||
LOCAL_ID, DataStorage.DEFAULT_CATEGORY_UUID, "Local Machine", new LocalStore());
|
||||
e.setConfiguration(
|
||||
StorageElement.Configuration.builder().deletable(false).build());
|
||||
storeEntries.put(e, e);
|
||||
e.validate();
|
||||
}
|
||||
|
|
|
@ -221,15 +221,13 @@ public class StandardStorage extends DataStorage {
|
|||
var e = DataStoreEntry.createNew(
|
||||
LOCAL_ID, DataStorage.DEFAULT_CATEGORY_UUID, "Local Machine", new LocalStore());
|
||||
e.setDirectory(getStoresDir().resolve(LOCAL_ID.toString()));
|
||||
e.setConfiguration(
|
||||
StorageElement.Configuration.builder().deletable(false).build());
|
||||
storeEntries.put(e, e);
|
||||
e.validate();
|
||||
}
|
||||
|
||||
var local = DataStorage.get().getStoreEntry(LOCAL_ID);
|
||||
if (storeEntriesSet.stream().noneMatch(entry -> entry.getColor() != null)) {
|
||||
local.setColor(DataColor.BLUE);
|
||||
local.setColor(DataStoreColor.BLUE);
|
||||
}
|
||||
|
||||
// Reload stores, this time with all entry refs present
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
package io.xpipe.app.storage;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.Value;
|
||||
import lombok.experimental.NonFinal;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -43,7 +40,7 @@ public abstract class StorageElement {
|
|||
@Getter
|
||||
protected boolean expanded;
|
||||
|
||||
protected @NonFinal @Getter DataColor color;
|
||||
protected @NonFinal @Getter DataStoreColor color;
|
||||
|
||||
public StorageElement(
|
||||
Path directory,
|
||||
|
@ -51,7 +48,7 @@ public abstract class StorageElement {
|
|||
String name,
|
||||
Instant lastUsed,
|
||||
Instant lastModified,
|
||||
DataColor color,
|
||||
DataStoreColor color,
|
||||
boolean expanded,
|
||||
boolean dirty) {
|
||||
this.directory = directory;
|
||||
|
@ -98,7 +95,7 @@ public abstract class StorageElement {
|
|||
FileUtils.deleteDirectory(directory.toFile());
|
||||
}
|
||||
|
||||
public void setColor(DataColor newColor) {
|
||||
public void setColor(DataStoreColor newColor) {
|
||||
var changed = !Objects.equals(color, newColor);
|
||||
this.color = newColor;
|
||||
if (changed) {
|
||||
|
@ -146,15 +143,4 @@ public abstract class StorageElement {
|
|||
public interface Listener {
|
||||
void onUpdate();
|
||||
}
|
||||
|
||||
@Builder
|
||||
@Jacksonized
|
||||
@Value
|
||||
public static class Configuration {
|
||||
boolean deletable;
|
||||
|
||||
public static Configuration defaultConfiguration() {
|
||||
return new Configuration(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import io.xpipe.app.core.AppProperties;
|
|||
import io.xpipe.app.ext.ProcessControlProvider;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.storage.DataColor;
|
||||
import io.xpipe.app.storage.DataStoreColor;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.util.LicenseProvider;
|
||||
|
@ -29,7 +29,7 @@ import java.util.UUID;
|
|||
@Value
|
||||
@With
|
||||
public class TerminalLaunchConfiguration {
|
||||
DataColor color;
|
||||
DataStoreColor color;
|
||||
String coloredTitle;
|
||||
String cleanTitle;
|
||||
boolean preferTabs;
|
||||
|
|
|
@ -70,6 +70,26 @@ public class TerminalLauncher {
|
|||
return ScriptHelper.createExecScriptRaw(processControl, file, content);
|
||||
}
|
||||
|
||||
public static void openDirect(
|
||||
String title, CommandBuilder command)
|
||||
throws Exception {
|
||||
openDirect(title, sc -> command.buildFull(sc), AppPrefs.get().terminalType().getValue());
|
||||
}
|
||||
|
||||
public static void openDirect(
|
||||
String title,
|
||||
ShellScript command)
|
||||
throws Exception {
|
||||
openDirect(title, sc -> command.toString(), AppPrefs.get().terminalType().getValue());
|
||||
}
|
||||
|
||||
public static void openDirect(
|
||||
String title,
|
||||
FailableFunction<ShellControl, ShellScript, Exception> command)
|
||||
throws Exception {
|
||||
openDirect(title, sc -> command.apply(sc).toString(), AppPrefs.get().terminalType().getValue());
|
||||
}
|
||||
|
||||
public static void openDirect(
|
||||
String title, FailableFunction<ShellControl, String, Exception> command, ExternalTerminalType type)
|
||||
throws Exception {
|
||||
|
|
|
@ -3,6 +3,7 @@ package io.xpipe.app.util;
|
|||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.core.process.OsType;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.store.FilePath;
|
||||
import io.xpipe.core.util.FailableSupplier;
|
||||
|
@ -58,4 +59,20 @@ public class CommandSupport {
|
|||
+ (connection != null ? " on system " + connection.getName() : "")));
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isInLocalPath(String executable) throws Exception {
|
||||
var cmd = OsType.getLocal() == OsType.WINDOWS ? "where" : "which";
|
||||
var r = LocalExec.readStdoutIfPossible(cmd, executable);
|
||||
return r.isPresent();
|
||||
}
|
||||
|
||||
public static void isInLocalPathOrThrow(String displayName, String executable) throws Exception {
|
||||
var present = isInLocalPath(executable);
|
||||
var prefix = displayName != null ? displayName + " executable \"" + executable + "\"" : "\"" + executable + "\" executable";
|
||||
if (present) {
|
||||
return;
|
||||
}
|
||||
throw ErrorEvent.expected(new IOException(
|
||||
prefix + " not found in PATH. Install the executable, add it to the PATH, and refresh the environment by restarting XPipe to fix this."));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.xpipe.app.util;
|
||||
|
||||
public enum DocumentationLink {
|
||||
|
||||
INDEX(""),
|
||||
TTY("troubleshoot/tty"),
|
||||
WINDOWS_SSH("troubleshoot/windows-ssh"),
|
||||
|
|
|
@ -4,7 +4,6 @@ import io.xpipe.core.store.FileEntry;
|
|||
import io.xpipe.core.store.FilePath;
|
||||
import io.xpipe.core.store.FileSystem;
|
||||
import io.xpipe.core.util.NewLine;
|
||||
import io.xpipe.core.util.SecretValue;
|
||||
import io.xpipe.core.util.StreamCharset;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
@ -112,8 +111,6 @@ public interface ShellDialect {
|
|||
|
||||
String getSetEnvironmentVariableCommand(String variable, String value);
|
||||
|
||||
String setSecretEnvironmentVariableCommand(ShellControl sc, String variable, SecretValue value) throws Exception;
|
||||
|
||||
String getEchoCommand(String s, boolean toErrorStream);
|
||||
|
||||
String getPrintVariableCommand(String name);
|
||||
|
|
|
@ -147,9 +147,22 @@ public class ShellView {
|
|||
cmd.executeAndCheck();
|
||||
}
|
||||
|
||||
public String environmentVariable(String name) throws Exception {
|
||||
public String getEnvironmentVariable(String name) throws Exception {
|
||||
return shellControl
|
||||
.command(shellControl.getShellDialect().getPrintEnvironmentVariableCommand(name))
|
||||
.readStdoutOrThrow();
|
||||
}
|
||||
|
||||
public void setEnvironmentVariable(String name, String value) throws Exception {
|
||||
shellControl
|
||||
.command(shellControl.getShellDialect().getSetEnvironmentVariableCommand(name, value))
|
||||
.execute();
|
||||
}
|
||||
|
||||
public void setSensitiveEnvironmentVariable(String name, String value) throws Exception {
|
||||
var command = shellControl.command(shellControl.getShellDialect().getSetEnvironmentVariableCommand(name, value));
|
||||
command.setSensitive();
|
||||
command
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import javafx.beans.property.SimpleBooleanProperty;
|
|||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
@ -51,6 +52,12 @@ public class SyncedIdentityStoreProvider extends IdentityStoreProvider {
|
|||
var source = Path.of(f.getFile().toAbsoluteFilePath(null).toString());
|
||||
var target = Path.of("keys", f.getFile().toAbsoluteFilePath(null).getFileName());
|
||||
DataStorageSyncHandler.getInstance().addDataFile(source, target, newValue);
|
||||
|
||||
var pub = Path.of(source + ".pub");
|
||||
var pubTarget = Path.of("keys", f.getFile().toAbsoluteFilePath(null).getFileName() + ".pub");
|
||||
if (Files.exists(pub)) {
|
||||
DataStorageSyncHandler.getInstance().addDataFile(pub, pubTarget, newValue);
|
||||
}
|
||||
});
|
||||
|
||||
return new OptionsBuilder()
|
||||
|
|
Loading…
Add table
Reference in a new issue