mirror of
https://github.com/xpipe-io/xpipe.git
synced 2025-04-17 09:43:37 +00:00
Rework prompts
This commit is contained in:
parent
953d91ca42
commit
a5027fa6a7
10 changed files with 220 additions and 19 deletions
|
@ -11,6 +11,8 @@ import io.xpipe.app.password.PasswordManagerCommand;
|
|||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.terminal.ExternalTerminalType;
|
||||
import io.xpipe.app.terminal.TerminalMultiplexer;
|
||||
import io.xpipe.app.terminal.TerminalPrompt;
|
||||
import io.xpipe.app.terminal.TerminalPromptManager;
|
||||
import io.xpipe.app.util.PlatformState;
|
||||
import io.xpipe.app.util.PlatformThread;
|
||||
import io.xpipe.core.process.ShellScript;
|
||||
|
@ -109,6 +111,12 @@ public class AppPrefs {
|
|||
mapLocal(new SimpleObjectProperty<>(null), "terminalMultiplexer", TerminalMultiplexer.class, false);
|
||||
final Property<Boolean> terminalPromptForRestart =
|
||||
mapLocal(new SimpleBooleanProperty(true), "terminalPromptForRestart", Boolean.class, false);
|
||||
final Property<TerminalPrompt> terminalPrompt =
|
||||
mapLocal(new SimpleObjectProperty<>(null), "terminalPrompt", TerminalPrompt.class, false);
|
||||
|
||||
public ObservableValue<TerminalPrompt> terminalPrompt() {
|
||||
return terminalPrompt;
|
||||
}
|
||||
|
||||
public ObservableValue<UUID> terminalProxy() {
|
||||
return terminalProxy;
|
||||
|
@ -268,6 +276,7 @@ public class AppPrefs {
|
|||
new VaultCategory(),
|
||||
new SyncCategory(),
|
||||
new TerminalCategory(),
|
||||
new TerminalPromptCategory(),
|
||||
new LoggingCategory(),
|
||||
new EditorCategory(),
|
||||
new RdpCategory(),
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package io.xpipe.app.prefs;
|
||||
|
||||
import io.xpipe.app.comp.Comp;
|
||||
import io.xpipe.app.comp.base.*;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.terminal.ExternalTerminalType;
|
||||
import io.xpipe.app.terminal.TerminalMultiplexer;
|
||||
import io.xpipe.app.terminal.TerminalPrompt;
|
||||
import io.xpipe.app.util.*;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
public class TerminalPromptCategory extends AppPrefsCategory {
|
||||
|
||||
@Override
|
||||
protected String getId() {
|
||||
return "terminalPrompt";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Comp<?> create() {
|
||||
return new OptionsBuilder()
|
||||
.addTitle("terminalPromptConfiguration")
|
||||
.sub(terminalPrompt())
|
||||
.buildComp();
|
||||
}
|
||||
|
||||
private OptionsBuilder terminalPrompt() {
|
||||
var prefs = AppPrefs.get();
|
||||
var choiceBuilder = OptionsChoiceBuilder.builder()
|
||||
.property(prefs.terminalPrompt)
|
||||
.allowNull(true)
|
||||
.subclasses(TerminalPrompt.getClasses())
|
||||
.transformer(entryComboBox -> {
|
||||
var websiteLinkButton =
|
||||
new ButtonComp(AppI18n.observable("website"), new FontIcon("mdi2w-web"), () -> {
|
||||
var l = prefs.terminalPrompt().getValue().getDocsLink();
|
||||
if (l != null) {
|
||||
Hyperlinks.open(l);
|
||||
}
|
||||
});
|
||||
websiteLinkButton.minWidth(Region.USE_PREF_SIZE);
|
||||
websiteLinkButton.disable(Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
return prefs.terminalPrompt.getValue() == null
|
||||
|| prefs.terminalPrompt.getValue().getDocsLink() == null;
|
||||
},
|
||||
prefs.terminalPrompt));
|
||||
|
||||
var hbox = new HBox(entryComboBox, websiteLinkButton.createRegion());
|
||||
HBox.setHgrow(entryComboBox, Priority.ALWAYS);
|
||||
hbox.setSpacing(10);
|
||||
return hbox;
|
||||
})
|
||||
.build();
|
||||
var choice = choiceBuilder.build().buildComp();
|
||||
choice.maxWidth(getCompWidth());
|
||||
return new OptionsBuilder().nameAndDescription("terminalPrompt").addComp(choice, prefs.terminalPrompt);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import io.xpipe.app.util.OptionsBuilder;
|
|||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.process.ShellTerminalInitCommand;
|
||||
import io.xpipe.core.store.FilePath;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
|
@ -19,8 +20,8 @@ import java.util.function.Function;
|
|||
@SuperBuilder
|
||||
public abstract class ConfigFileTerminalPrompt implements TerminalPrompt {
|
||||
|
||||
protected static OptionsBuilder createOptions(Property<ConfigFileTerminalPrompt> p, String extension, Function<String, ConfigFileTerminalPrompt> creator) {
|
||||
var prop = new SimpleObjectProperty<String>();
|
||||
protected static <T extends ConfigFileTerminalPrompt> OptionsBuilder createOptions(Property<T> p, String extension, Function<String, T> creator) {
|
||||
var prop = new SimpleObjectProperty<>(p.getValue() != null ? p.getValue().configuration : null);
|
||||
return new OptionsBuilder()
|
||||
.nameAndDescription("configuration")
|
||||
.addComp(new IntegratedTextAreaComp(prop, false, "config", new SimpleStringProperty(extension)), prop)
|
||||
|
@ -32,4 +33,21 @@ public abstract class ConfigFileTerminalPrompt implements TerminalPrompt {
|
|||
}
|
||||
|
||||
protected String configuration;
|
||||
|
||||
protected abstract FilePath prepareCustomConfigFile(ShellControl sc) throws Exception;
|
||||
|
||||
protected abstract FilePath getDefaultConfigFile(ShellControl sc) throws Exception;
|
||||
|
||||
@Override
|
||||
public ShellTerminalInitCommand terminalCommand(ShellControl sc) throws Exception {
|
||||
FilePath configFile;
|
||||
if (configuration == null || configuration.isBlank()) {
|
||||
configFile = getDefaultConfigFile(sc);
|
||||
} else {
|
||||
configFile = prepareCustomConfigFile(sc);
|
||||
}
|
||||
return terminalCommand(sc, configFile);
|
||||
}
|
||||
|
||||
protected abstract ShellTerminalInitCommand terminalCommand(ShellControl shellControl, FilePath config) throws Exception;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,29 @@
|
|||
package io.xpipe.app.terminal;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.password.KeePassXcAssociationKey;
|
||||
import io.xpipe.app.password.KeePassXcManager;
|
||||
import io.xpipe.app.util.CommandSupport;
|
||||
import io.xpipe.app.util.OptionsBuilder;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.process.ShellDialect;
|
||||
import io.xpipe.core.process.ShellDialects;
|
||||
import io.xpipe.core.process.ShellTerminalInitCommand;
|
||||
import io.xpipe.core.store.FilePath;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Getter
|
||||
@SuperBuilder
|
||||
@ToString
|
||||
|
@ -16,18 +31,67 @@ import lombok.extern.jackson.Jacksonized;
|
|||
@JsonTypeName("starship")
|
||||
public class StarshipTerminalPrompt extends ConfigFileTerminalPrompt {
|
||||
|
||||
public static OptionsBuilder createOptions(Property<StarshipTerminalPrompt> p) {
|
||||
return createOptions(p, "toml", s -> StarshipTerminalPrompt.builder().configuration(s).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDocsLink() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkSupported(ShellControl sc) throws Exception {
|
||||
|
||||
public void checkCanInstall(ShellControl sc) throws Exception {
|
||||
CommandSupport.isInPathOrThrow(sc, "curl");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShellTerminalInitCommand setup(ShellControl shellControl) throws Exception {
|
||||
return null;
|
||||
public boolean checkIfInstalled(ShellControl sc) throws Exception {
|
||||
if (sc.view().findProgram("starship").isPresent()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void install(ShellControl sc) throws Exception {
|
||||
var dir = getBinaryDirectory(sc).join("starship");
|
||||
sc.command("curl -sS https://starship.rs/install.sh | sh /dev/stdin -y --bin-dir \"" + dir + "\" > /dev/null").execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePath prepareCustomConfigFile(ShellControl sc) throws Exception {
|
||||
var file = getConfigurationDirectory(sc).join("starship").join("starship.toml");
|
||||
sc.view().writeTextFile(file, configuration);
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePath getDefaultConfigFile(ShellControl sc) throws Exception {
|
||||
return sc.view().userHome().join(".config").join("starship.toml");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShellTerminalInitCommand terminalCommand(ShellControl shellControl, FilePath configFile) throws Exception {
|
||||
return new ShellTerminalInitCommand() {
|
||||
@Override
|
||||
public Optional<String> terminalContent(ShellControl shellControl) throws Exception {
|
||||
var s = shellControl.getShellDialect().getSetEnvironmentVariableCommand("STARSHIP_CONFIG", "") + "\n" + "eval \"$(starship init bash)\"";
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPotentiallyRunInDialect(ShellDialect dialect) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ShellDialect> getSupportedDialects() {
|
||||
return List.of(ShellDialects.BASH);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package io.xpipe.app.terminal;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.xpipe.app.util.ShellTemp;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.process.ShellScript;
|
||||
import io.xpipe.core.process.ShellDialect;
|
||||
import io.xpipe.core.process.ShellTerminalInitCommand;
|
||||
import io.xpipe.core.process.TerminalInitScriptConfig;
|
||||
import io.xpipe.core.util.ValidationException;
|
||||
import io.xpipe.core.store.FilePath;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -15,17 +15,34 @@ public interface TerminalPrompt {
|
|||
|
||||
static List<Class<?>> getClasses() {
|
||||
var l = new ArrayList<Class<?>>();
|
||||
l.add(TmuxTerminalMultiplexer.class);
|
||||
l.add(ZellijTerminalMultiplexer.class);
|
||||
l.add(ScreenTerminalMultiplexer.class);
|
||||
l.add(StarshipTerminalPrompt.class);
|
||||
return l;
|
||||
}
|
||||
|
||||
default void checkComplete() throws ValidationException {}
|
||||
|
||||
String getDocsLink();
|
||||
|
||||
void checkSupported(ShellControl sc) throws Exception;
|
||||
default FilePath getConfigurationDirectory(ShellControl sc) throws Exception {
|
||||
return ShellTemp.createUserSpecificTempDataDirectory(sc, "prompt");
|
||||
}
|
||||
|
||||
ShellTerminalInitCommand setup(ShellControl shellControl) throws Exception;
|
||||
default FilePath getBinaryDirectory(ShellControl sc) throws Exception {
|
||||
return ShellTemp.createUserSpecificTempDataDirectory(sc, "bin");
|
||||
}
|
||||
|
||||
default void installIfNeeded(ShellControl sc) throws Exception {
|
||||
if (checkIfInstalled(sc)) {
|
||||
checkCanInstall(sc);
|
||||
install(sc);
|
||||
}
|
||||
}
|
||||
|
||||
void checkCanInstall(ShellControl sc) throws Exception;
|
||||
|
||||
boolean checkIfInstalled(ShellControl sc) throws Exception;
|
||||
|
||||
void install(ShellControl sc) throws Exception;
|
||||
|
||||
ShellTerminalInitCommand terminalCommand(ShellControl shellControl) throws Exception;
|
||||
|
||||
List<ShellDialect> getSupportedDialects();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package io.xpipe.app.terminal;
|
||||
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
|
||||
public class TerminalPromptManager {
|
||||
|
||||
public static void configurePromptScript(ShellControl sc) {
|
||||
var p = AppPrefs.get().terminalPrompt().getValue();
|
||||
if (p == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var d = p.getSupportedDialects();
|
||||
if (!d.contains(sc.getShellDialect())) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
p.installIfNeeded(sc);
|
||||
sc.withInitSnippet(p.terminalCommand(sc));
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e).handle();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import io.xpipe.app.password.PasswordManager;
|
|||
import io.xpipe.app.storage.*;
|
||||
import io.xpipe.app.terminal.ExternalTerminalType;
|
||||
import io.xpipe.app.terminal.TerminalMultiplexer;
|
||||
import io.xpipe.app.terminal.TerminalPrompt;
|
||||
import io.xpipe.core.util.InPlaceSecretValue;
|
||||
import io.xpipe.core.util.JacksonMapper;
|
||||
|
||||
|
@ -45,6 +46,7 @@ public class AppJacksonModule extends SimpleModule {
|
|||
|
||||
context.registerSubtypes(PasswordManager.getClasses());
|
||||
context.registerSubtypes(TerminalMultiplexer.getClasses());
|
||||
context.registerSubtypes(TerminalPrompt.getClasses());
|
||||
|
||||
context.addSerializers(_serializers);
|
||||
context.addDeserializers(_deserializers);
|
||||
|
|
|
@ -6,9 +6,7 @@ import java.util.Optional;
|
|||
|
||||
public interface ShellTerminalInitCommand {
|
||||
|
||||
default Optional<String> terminalContent(ShellControl shellControl) throws Exception {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
Optional<String> terminalContent(ShellControl shellControl) throws Exception;
|
||||
|
||||
boolean canPotentiallyRunInDialect(ShellDialect dialect);
|
||||
}
|
||||
|
|
1
lang/strings/fixed_en.properties
generated
1
lang/strings/fixed_en.properties
generated
|
@ -120,3 +120,4 @@ keePassXc=KeePassXC
|
|||
zellij=zellij
|
||||
tmux=tmux
|
||||
onePassword=1Password
|
||||
starship=Starship
|
||||
|
|
2
lang/strings/translations_en.properties
generated
2
lang/strings/translations_en.properties
generated
|
@ -1380,3 +1380,5 @@ retrievedPassword=Obtained: $PASSWORD$
|
|||
refreshOpenpubkey=Refresh openpubkey identity
|
||||
refreshOpenpubkeyDescription=Run opkssh refresh to make the openpubkey identity valid again
|
||||
all=All
|
||||
terminalPrompt=Terminal prompt
|
||||
terminalPromptConfiguration=Terminal prompt configuration
|
||||
|
|
Loading…
Add table
Reference in a new issue