mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-21 23:20:23 +00:00
Merge branch no-stderr into master
This commit is contained in:
parent
65b2be5709
commit
9860b0c10f
34 changed files with 305 additions and 200 deletions
|
@ -33,26 +33,49 @@ public class SystemStateComp extends SimpleComp {
|
||||||
PlatformThread.runLaterIfNeeded(() -> fi.setIconLiteral(i));
|
PlatformThread.runLaterIfNeeded(() -> fi.setIconLiteral(i));
|
||||||
});
|
});
|
||||||
|
|
||||||
var border = new FontIcon("mdi2c-circle-outline");
|
var bg = new FontIcon("mdi2s-square-rounded");
|
||||||
|
bg.getStyleClass().add("background-icon");
|
||||||
|
|
||||||
|
var border = new FontIcon("mdi2s-square-rounded-outline");
|
||||||
border.getStyleClass().add("outer-icon");
|
border.getStyleClass().add("outer-icon");
|
||||||
border.setOpacity(0.5);
|
border.setOpacity(0.3);
|
||||||
|
|
||||||
var success = Styles.toDataURI(
|
var success = Styles.toDataURI(
|
||||||
".stacked-ikonli-font-icon > .outer-icon { -fx-icon-color: -color-success-emphasis; }");
|
"""
|
||||||
|
.stacked-ikonli-font-icon > .outer-icon { -fx-icon-color: -color-success-emphasis; }
|
||||||
|
|
||||||
|
.stacked-ikonli-font-icon > .background-icon { -fx-icon-color: -color-success-9; }
|
||||||
|
"""
|
||||||
|
);
|
||||||
var failure =
|
var failure =
|
||||||
Styles.toDataURI(".stacked-ikonli-font-icon > .outer-icon { -fx-icon-color: -color-danger-emphasis; }");
|
Styles.toDataURI(
|
||||||
|
"""
|
||||||
|
.stacked-ikonli-font-icon > .outer-icon { -fx-icon-color: -color-danger-emphasis; }
|
||||||
|
|
||||||
|
.stacked-ikonli-font-icon > .background-icon { -fx-icon-color: -color-danger-9; }
|
||||||
|
"""
|
||||||
|
);
|
||||||
var other =
|
var other =
|
||||||
Styles.toDataURI(".stacked-ikonli-font-icon > .outer-icon { -fx-icon-color: -color-accent-emphasis; }");
|
Styles.toDataURI(
|
||||||
|
"""
|
||||||
|
.stacked-ikonli-font-icon > .outer-icon { -fx-icon-color: -color-accent-emphasis; }
|
||||||
|
|
||||||
|
.stacked-ikonli-font-icon > .background-icon { -fx-icon-color: -color-accent-9; }
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
|
||||||
var pane = new StackedFontIcon();
|
var pane = new StackedFontIcon();
|
||||||
pane.getChildren().addAll(fi, border);
|
pane.getChildren().addAll(bg, fi, border);
|
||||||
pane.setAlignment(Pos.CENTER);
|
pane.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
var dataClass1 =
|
var dataClass1 =
|
||||||
"""
|
"""
|
||||||
.stacked-ikonli-font-icon > .outer-icon {
|
.stacked-ikonli-font-icon > .outer-icon {
|
||||||
-fx-icon-size: 22px;
|
-fx-icon-size: 26px;
|
||||||
}
|
}
|
||||||
|
.stacked-ikonli-font-icon > .background-icon {
|
||||||
|
-fx-icon-size: 26px;
|
||||||
|
}
|
||||||
.stacked-ikonli-font-icon > .inner-icon {
|
.stacked-ikonli-font-icon > .inner-icon {
|
||||||
-fx-icon-size: 12px;
|
-fx-icon-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
50
app/src/main/java/io/xpipe/app/comp/base/TtyWarningComp.java
Normal file
50
app/src/main/java/io/xpipe/app/comp/base/TtyWarningComp.java
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package io.xpipe.app.comp.base;
|
||||||
|
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
|
import io.xpipe.app.fxcomps.SimpleComp;
|
||||||
|
import io.xpipe.app.fxcomps.impl.TooltipAugment;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
import org.kordamp.ikonli.javafx.StackedFontIcon;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class TtyWarningComp extends SimpleComp {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Region createSimple() {
|
||||||
|
var fi = new FontIcon("mdi2l-lightning-bolt");
|
||||||
|
fi.getStyleClass().add("inner-icon");
|
||||||
|
|
||||||
|
var border = new FontIcon("mdi2s-square-rounded-outline");
|
||||||
|
border.getStyleClass().add("outer-icon");
|
||||||
|
border.setOpacity(0.5);
|
||||||
|
|
||||||
|
var bg = new FontIcon("mdi2s-square-rounded");
|
||||||
|
bg.getStyleClass().add("background-icon");
|
||||||
|
|
||||||
|
var pane = new StackedFontIcon();
|
||||||
|
pane.getChildren().addAll(bg, fi, border);
|
||||||
|
pane.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
var style =
|
||||||
|
"""
|
||||||
|
.stacked-ikonli-font-icon > .outer-icon { -fx-icon-color: -color-danger-emphasis; }
|
||||||
|
|
||||||
|
.stacked-ikonli-font-icon > .outer-icon {
|
||||||
|
-fx-icon-size: 26px;
|
||||||
|
}
|
||||||
|
.stacked-ikonli-font-icon > .background-icon {
|
||||||
|
-fx-icon-size: 26px;
|
||||||
|
-fx-icon-color: -color-danger-9;
|
||||||
|
}
|
||||||
|
.stacked-ikonli-font-icon > .inner-icon {
|
||||||
|
-fx-icon-size: 12px;
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
pane.getStylesheets().add(Styles.toDataURI(style));
|
||||||
|
new TooltipAugment<>("ttyWarning", null).augment(pane);
|
||||||
|
return pane;
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import javafx.beans.value.ObservableValue;
|
||||||
import javafx.scene.control.Tooltip;
|
import javafx.scene.control.Tooltip;
|
||||||
import javafx.scene.input.KeyCombination;
|
import javafx.scene.input.KeyCombination;
|
||||||
import javafx.stage.Window;
|
import javafx.stage.Window;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
|
||||||
public class TooltipAugment<S extends CompStructure<?>> implements Augment<S> {
|
public class TooltipAugment<S extends CompStructure<?>> implements Augment<S> {
|
||||||
|
|
||||||
|
@ -45,6 +46,7 @@ public class TooltipAugment<S extends CompStructure<?>> implements Augment<S> {
|
||||||
tt.setWrapText(true);
|
tt.setWrapText(true);
|
||||||
tt.setMaxWidth(400);
|
tt.setMaxWidth(400);
|
||||||
tt.getStyleClass().add("fancy-tooltip");
|
tt.getStyleClass().add("fancy-tooltip");
|
||||||
|
tt.setHideDelay(Duration.INDEFINITE);
|
||||||
Tooltip.install(struc.get(), tt);
|
Tooltip.install(struc.get(), tt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,9 @@ public class AppPrefs {
|
||||||
map(new SimpleBooleanProperty(false), "developerDisableGuiRestrictions", Boolean.class);
|
map(new SimpleBooleanProperty(false), "developerDisableGuiRestrictions", Boolean.class);
|
||||||
private final ObservableBooleanValue developerDisableGuiRestrictionsEffective =
|
private final ObservableBooleanValue developerDisableGuiRestrictionsEffective =
|
||||||
bindDeveloperTrue(developerDisableGuiRestrictions);
|
bindDeveloperTrue(developerDisableGuiRestrictions);
|
||||||
|
final BooleanProperty developerForceSshTty =
|
||||||
|
map(new SimpleBooleanProperty(false), "developerForceSshTty", Boolean.class);
|
||||||
|
|
||||||
final ObjectProperty<SupportedLocale> language =
|
final ObjectProperty<SupportedLocale> language =
|
||||||
map(new SimpleObjectProperty<>(SupportedLocale.getEnglish()), "language", SupportedLocale.class);
|
map(new SimpleObjectProperty<>(SupportedLocale.getEnglish()), "language", SupportedLocale.class);
|
||||||
|
|
||||||
|
@ -435,6 +438,10 @@ public class AppPrefs {
|
||||||
return developerDisableGuiRestrictionsEffective;
|
return developerDisableGuiRestrictionsEffective;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ObservableBooleanValue developerForceSshTty() {
|
||||||
|
return bindDeveloperTrue(developerForceSshTty);
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <T> T map(T o, String name, Class<?> clazz) {
|
private <T> T map(T o, String name, Class<?> clazz) {
|
||||||
mapping.add(new Mapping<>(name, (Property<T>) o, (Class<T>) clazz));
|
mapping.add(new Mapping<>(name, (Property<T>) o, (Class<T>) clazz));
|
||||||
|
|
|
@ -61,6 +61,8 @@ public class DeveloperCategory extends AppPrefsCategory {
|
||||||
.sub(new OptionsBuilder()
|
.sub(new OptionsBuilder()
|
||||||
.nameAndDescription("developerDisableUpdateVersionCheck")
|
.nameAndDescription("developerDisableUpdateVersionCheck")
|
||||||
.addToggle(prefs.developerDisableUpdateVersionCheck)
|
.addToggle(prefs.developerDisableUpdateVersionCheck)
|
||||||
|
.nameAndDescription("developerForceSshTty")
|
||||||
|
.addToggle(prefs.developerForceSshTty)
|
||||||
.nameAndDescription("developerDisableGuiRestrictions")
|
.nameAndDescription("developerDisableGuiRestrictions")
|
||||||
.addToggle(prefs.developerDisableGuiRestrictions)
|
.addToggle(prefs.developerDisableGuiRestrictions)
|
||||||
.nameAndDescription("shellCommandTest")
|
.nameAndDescription("shellCommandTest")
|
||||||
|
|
|
@ -114,14 +114,12 @@ public abstract class ExternalApplicationType implements PrefsChoiceValue {
|
||||||
|
|
||||||
protected Optional<Path> determineFromPath() {
|
protected Optional<Path> determineFromPath() {
|
||||||
// Try to locate if it is in the Path
|
// Try to locate if it is in the Path
|
||||||
try (var cc = LocalShell.getShell()
|
try (var sc = LocalShell.getShell()
|
||||||
.command(CommandBuilder.ofFunction(
|
|
||||||
var1 -> var1.getShellDialect().getWhichCommand(executable)))
|
|
||||||
.start()) {
|
.start()) {
|
||||||
var out = cc.readStdoutDiscardErr();
|
var out = sc.command(CommandBuilder.ofFunction(
|
||||||
var exit = cc.getExitCode();
|
var1 -> var1.getShellDialect().getWhichCommand(executable))).readStdoutIfPossible();
|
||||||
if (exit == 0) {
|
if (out.isPresent()) {
|
||||||
var first = out.lines().findFirst();
|
var first = out.get().lines().findFirst();
|
||||||
if (first.isPresent()) {
|
if (first.isPresent()) {
|
||||||
return first.map(String::trim).map(Path::of);
|
return first.map(String::trim).map(Path::of);
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,16 +99,13 @@ public enum XPipeDistributionType {
|
||||||
// In theory, we can also add && !AppProperties.get().isStaging() here, but we want to replicate the
|
// In theory, we can also add && !AppProperties.get().isStaging() here, but we want to replicate the
|
||||||
// production behavior
|
// production behavior
|
||||||
if (OsType.getLocal().equals(OsType.WINDOWS)) {
|
if (OsType.getLocal().equals(OsType.WINDOWS)) {
|
||||||
try (var chocoOut =
|
var out = sc.command("choco search --local-only -r xpipe").readStdoutIfPossible();
|
||||||
sc.command("choco search --local-only -r xpipe").start()) {
|
if (out.isPresent()) {
|
||||||
var out = chocoOut.readStdoutDiscardErr();
|
var split = out.get().split("\\|");
|
||||||
if (chocoOut.getExitCode() == 0) {
|
if (split.length == 2) {
|
||||||
var split = out.split("\\|");
|
var version = split[1];
|
||||||
if (split.length == 2) {
|
if (AppProperties.get().getVersion().equals(version)) {
|
||||||
var version = split[1];
|
return CHOCO;
|
||||||
if (AppProperties.get().getVersion().equals(version)) {
|
|
||||||
return CHOCO;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,17 +114,15 @@ public enum XPipeDistributionType {
|
||||||
// In theory, we can also add && !AppProperties.get().isStaging() here, but we want to replicate the
|
// In theory, we can also add && !AppProperties.get().isStaging() here, but we want to replicate the
|
||||||
// production behavior
|
// production behavior
|
||||||
if (OsType.getLocal().equals(OsType.MACOS)) {
|
if (OsType.getLocal().equals(OsType.MACOS)) {
|
||||||
try (var brewOut = sc.command("brew list --casks --versions").start()) {
|
var out = sc.command("brew list --casks --versions").readStdoutIfPossible();
|
||||||
var out = brewOut.readStdoutDiscardErr();
|
if (out.isPresent()) {
|
||||||
if (brewOut.getExitCode() == 0) {
|
if (out.get().lines().anyMatch(s -> {
|
||||||
if (out.lines().anyMatch(s -> {
|
var split = s.split(" ");
|
||||||
var split = s.split(" ");
|
return split.length == 2
|
||||||
return split.length == 2
|
&& split[0].equals("xpipe")
|
||||||
&& split[0].equals("xpipe")
|
&& split[1].equals(AppProperties.get().getVersion());
|
||||||
&& split[1].equals(AppProperties.get().getVersion());
|
})) {
|
||||||
})) {
|
return HOMEBREW;
|
||||||
return HOMEBREW;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,10 @@ public class DesktopHelper {
|
||||||
return Path.of(LocalShell.getLocalPowershell()
|
return Path.of(LocalShell.getLocalPowershell()
|
||||||
.executeSimpleStringCommand("[Environment]::GetFolderPath([Environment+SpecialFolder]::Desktop)"));
|
.executeSimpleStringCommand("[Environment]::GetFolderPath([Environment+SpecialFolder]::Desktop)"));
|
||||||
} else if (OsType.getLocal() == OsType.LINUX) {
|
} else if (OsType.getLocal() == OsType.LINUX) {
|
||||||
try (var cmd = LocalShell.getShell().command("xdg-user-dir DESKTOP").start()) {
|
try (var sc = LocalShell.getShell().start()) {
|
||||||
var read = cmd.readStdoutDiscardErr();
|
var out = sc.command("xdg-user-dir DESKTOP").readStdoutIfPossible();
|
||||||
var exit = cmd.getExitCode();
|
if (out.isPresent()) {
|
||||||
if (exit == 0) {
|
return Path.of(out.get());
|
||||||
return Path.of(read);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,12 +33,10 @@ public class DesktopHelper {
|
||||||
.executeSimpleStringCommand(
|
.executeSimpleStringCommand(
|
||||||
"(New-Object -ComObject Shell.Application).NameSpace('shell:Downloads').Self.Path"));
|
"(New-Object -ComObject Shell.Application).NameSpace('shell:Downloads').Self.Path"));
|
||||||
} else if (OsType.getLocal() == OsType.LINUX) {
|
} else if (OsType.getLocal() == OsType.LINUX) {
|
||||||
try (var cmd =
|
try (var sc = LocalShell.getShell().start()) {
|
||||||
LocalShell.getShell().command("xdg-user-dir DOWNLOAD").start()) {
|
var out = sc.command("xdg-user-dir DOWNLOAD").readStdoutIfPossible();
|
||||||
var read = cmd.readStdoutDiscardErr();
|
if (out.isPresent()) {
|
||||||
var exit = cmd.getExitCode();
|
return Path.of(out.get());
|
||||||
if (exit == 0) {
|
|
||||||
return Path.of(read);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,23 +145,20 @@ public abstract class WindowsRegistry {
|
||||||
.add("/v")
|
.add("/v")
|
||||||
.addQuoted(valueName);
|
.addQuoted(valueName);
|
||||||
|
|
||||||
String output;
|
var output = shellControl.command(command).readStdoutIfPossible();
|
||||||
try (var c = shellControl.command(command).start()) {
|
if (output.isEmpty()) {
|
||||||
output = c.readStdoutDiscardErr();
|
return Optional.empty();
|
||||||
if (c.getExitCode() != 0) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output has the following format:
|
// Output has the following format:
|
||||||
// \n<Version information>\n\n<key>\t<registry type>\t<value>
|
// \n<Version information>\n\n<key>\t<registry type>\t<value>
|
||||||
if (output.contains("\t")) {
|
if (output.get().contains("\t")) {
|
||||||
String[] parsed = output.split("\t");
|
String[] parsed = output.get().split("\t");
|
||||||
return Optional.of(parsed[parsed.length - 1]);
|
return Optional.of(parsed[parsed.length - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output.contains(" ")) {
|
if (output.get().contains(" ")) {
|
||||||
String[] parsed = output.split(" ");
|
String[] parsed = output.get().split(" ");
|
||||||
return Optional.of(parsed[parsed.length - 1]);
|
return Optional.of(parsed[parsed.length - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,14 +173,7 @@ public abstract class WindowsRegistry {
|
||||||
.add("/v")
|
.add("/v")
|
||||||
.addQuoted(valueName)
|
.addQuoted(valueName)
|
||||||
.add("/s");
|
.add("/s");
|
||||||
try (var c = shellControl.command(command).start()) {
|
return shellControl.command(command).readStdoutIfPossible();
|
||||||
var output = c.readStdoutDiscardErr();
|
|
||||||
if (c.getExitCode() != 0) {
|
|
||||||
return Optional.empty();
|
|
||||||
} else {
|
|
||||||
return Optional.of(output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -196,22 +186,17 @@ public abstract class WindowsRegistry {
|
||||||
.add("/s")
|
.add("/s")
|
||||||
.add("/e")
|
.add("/e")
|
||||||
.add("/d");
|
.add("/d");
|
||||||
try (var c = shellControl.command(command).start()) {
|
return shellControl.command(command).readStdoutIfPossible().flatMap(output -> {
|
||||||
var output = c.readStdoutDiscardErr();
|
return output.lines().findFirst().flatMap(s -> {
|
||||||
if (c.getExitCode() != 0) {
|
if (s.startsWith("HKEY_CURRENT_USER\\")) {
|
||||||
|
return Optional.of(new Key(HKEY_CURRENT_USER, s.replace("HKEY_CURRENT_USER\\", "")));
|
||||||
|
}
|
||||||
|
if (s.startsWith("HKEY_LOCAL_MACHINE\\")) {
|
||||||
|
return Optional.of(new Key(HKEY_LOCAL_MACHINE, s.replace("HKEY_LOCAL_MACHINE\\", "")));
|
||||||
|
}
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
} else {
|
});
|
||||||
return output.lines().findFirst().flatMap(s -> {
|
});
|
||||||
if (s.startsWith("HKEY_CURRENT_USER\\")) {
|
|
||||||
return Optional.of(new Key(HKEY_CURRENT_USER, s.replace("HKEY_CURRENT_USER\\", "")));
|
|
||||||
}
|
|
||||||
if (s.startsWith("HKEY_LOCAL_MACHINE\\")) {
|
|
||||||
return Optional.of(new Key(HKEY_LOCAL_MACHINE, s.replace("HKEY_LOCAL_MACHINE\\", "")));
|
|
||||||
}
|
|
||||||
return Optional.empty();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
## Welcome
|
## Welcome
|
||||||
|
|
||||||
Thank you for using XPipe!
|
Welcome to XPipe!
|
||||||
|
|
||||||
You can view the development status, report issues, and more at the following places:
|
You can view the development status, report issues, and more at the following places:
|
||||||
|
|
||||||
- [GitHub Repository](https://github.com/xpipe-io/xpipe/)
|
- [GitHub Repository](https://github.com/xpipe-io/xpipe/)
|
||||||
- [Discord Server](https://discord.gg/8y89vS8cRb)
|
- [Discord Server](https://discord.gg/8y89vS8cRb)
|
||||||
- [Slack Server](https://join.slack.com/t/XPipe/shared_invite/zt-1awjq0t5j-5i4UjNJfNe1VN4b_auu6Cg)
|
- [Slack Server](https://join.slack.com/t/XPipe/shared_invite/zt-1awjq0t5j-5i4UjNJfNe1VN4b_auu6Cg)
|
||||||
- [Email me](mailto://crschnick@xpipe.io)
|
- [Email us](mailto://hello@xpipe.io)
|
||||||
|
|
||||||
Note that the XPipe project currently is a one-man show, but I still try to respond to everything in time.
|
|
||||||
|
|
|
@ -128,3 +128,7 @@
|
||||||
.root:dark .loading-comp {
|
.root:dark .loading-comp {
|
||||||
-fx-background-color: rgba(0, 0, 0, 0.5);
|
-fx-background-color: rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.root:light .stacked-ikonli-font-icon > .background-icon { -fx-opacity: 0; }
|
||||||
|
|
||||||
|
.stacked-ikonli-font-icon > .background-icon { -fx-opacity: 0.2; }
|
||||||
|
|
|
@ -1,16 +1,10 @@
|
||||||
package io.xpipe.core.process;
|
package io.xpipe.core.process;
|
||||||
|
|
||||||
import io.xpipe.core.util.FailableConsumer;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public interface CommandControl extends ProcessControl {
|
public interface CommandControl extends ProcessControl {
|
||||||
|
@ -70,32 +64,14 @@ public interface CommandControl extends ProcessControl {
|
||||||
|
|
||||||
CommandControl elevated(ElevationFunction function);
|
CommandControl elevated(ElevationFunction function);
|
||||||
|
|
||||||
void withStdoutOrThrow(FailableConsumer<InputStreamReader, Exception> c);
|
|
||||||
|
|
||||||
String[] readStdoutAndStderr() throws Exception;
|
String[] readStdoutAndStderr() throws Exception;
|
||||||
|
|
||||||
String readStdoutDiscardErr() throws Exception;
|
|
||||||
|
|
||||||
String readStderrDiscardStdout() throws Exception;
|
|
||||||
|
|
||||||
void discardOrThrow() throws Exception;
|
void discardOrThrow() throws Exception;
|
||||||
|
|
||||||
void accumulateStdout(Consumer<String> con);
|
|
||||||
|
|
||||||
void accumulateStderr(Consumer<String> con);
|
|
||||||
|
|
||||||
byte[] readRawBytesOrThrow() throws Exception;
|
byte[] readRawBytesOrThrow() throws Exception;
|
||||||
|
|
||||||
String readStdoutOrThrow() throws Exception;
|
String readStdoutOrThrow() throws Exception;
|
||||||
|
|
||||||
JsonNode readStdoutJsonOrThrow() throws Exception;
|
|
||||||
|
|
||||||
String readStderrOrThrow() throws Exception;
|
|
||||||
|
|
||||||
String readStdoutAndWait() throws Exception;
|
|
||||||
|
|
||||||
String readStderrAndWait() throws Exception;
|
|
||||||
|
|
||||||
Optional<String> readStdoutIfPossible() throws Exception;
|
Optional<String> readStdoutIfPossible() throws Exception;
|
||||||
|
|
||||||
default boolean discardAndCheckExit() throws ProcessOutputException {
|
default boolean discardAndCheckExit() throws ProcessOutputException {
|
||||||
|
@ -113,10 +89,6 @@ public interface CommandControl extends ProcessControl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void discardOut();
|
|
||||||
|
|
||||||
void discardErr();
|
|
||||||
|
|
||||||
enum TerminalExitMode {
|
enum TerminalExitMode {
|
||||||
KEEP_OPEN,
|
KEEP_OPEN,
|
||||||
CLOSE
|
CLOSE
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
package io.xpipe.core.process;
|
|
||||||
|
|
||||||
public interface CommandFeedbackPredicate {
|
|
||||||
|
|
||||||
boolean test(CommandBuilder command);
|
|
||||||
}
|
|
|
@ -181,19 +181,15 @@ public interface OsType {
|
||||||
@Override
|
@Override
|
||||||
public String determineOperatingSystemName(ShellControl pc) throws Exception {
|
public String determineOperatingSystemName(ShellControl pc) throws Exception {
|
||||||
String type = "Unknown";
|
String type = "Unknown";
|
||||||
try (CommandControl c = pc.command("uname -o").start()) {
|
var uname = pc.command("uname -o").readStdoutIfPossible();
|
||||||
var text = c.readStdoutDiscardErr();
|
if (uname.isPresent()) {
|
||||||
if (c.getExitCode() == 0) {
|
type = uname.get();
|
||||||
type = text.strip();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String version = "?";
|
String version = "?";
|
||||||
try (CommandControl c = pc.command("uname -r").start()) {
|
var unameR = pc.command("uname -r").readStdoutIfPossible();
|
||||||
var text = c.readStdoutDiscardErr();
|
if (unameR.isPresent()) {
|
||||||
if (c.getExitCode() == 0) {
|
version = unameR.get();
|
||||||
version = text.strip();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return type + " " + version;
|
return type + " " + version;
|
||||||
|
@ -209,18 +205,14 @@ public interface OsType {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String determineOperatingSystemName(ShellControl pc) throws Exception {
|
public String determineOperatingSystemName(ShellControl pc) throws Exception {
|
||||||
try (CommandControl c = pc.command("lsb_release -a").start()) {
|
var rel = pc.command("lsb_release -a").readStdoutIfPossible();
|
||||||
var text = c.readStdoutDiscardErr();
|
if (rel.isPresent()) {
|
||||||
if (c.getExitCode() == 0) {
|
return PropertiesFormatsParser.parse(rel.get(), ":").getOrDefault("Description", "Unknown");
|
||||||
return PropertiesFormatsParser.parse(text, ":").getOrDefault("Description", "Unknown");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try (CommandControl c = pc.command("cat /etc/*release").start()) {
|
var cat = pc.command("cat /etc/*release").readStdoutIfPossible();
|
||||||
var text = c.readStdoutDiscardErr();
|
if (cat.isPresent()) {
|
||||||
if (c.getExitCode() == 0) {
|
return PropertiesFormatsParser.parse(cat.get(), "=").getOrDefault("PRETTY_NAME", "Unknown");
|
||||||
return PropertiesFormatsParser.parse(text, "=").getOrDefault("PRETTY_NAME", "Unknown");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.determineOperatingSystemName(pc);
|
return super.determineOperatingSystemName(pc);
|
||||||
|
|
|
@ -18,6 +18,8 @@ import java.util.function.Function;
|
||||||
|
|
||||||
public interface ShellControl extends ProcessControl {
|
public interface ShellControl extends ProcessControl {
|
||||||
|
|
||||||
|
ShellTtyState getTtyState();
|
||||||
|
|
||||||
void setNonInteractive();
|
void setNonInteractive();
|
||||||
|
|
||||||
boolean isInteractive();
|
boolean isInteractive();
|
||||||
|
@ -65,6 +67,7 @@ public interface ShellControl extends ProcessControl {
|
||||||
var s = store.getState().toBuilder()
|
var s = store.getState().toBuilder()
|
||||||
.osType(shellControl.getOsType())
|
.osType(shellControl.getOsType())
|
||||||
.shellDialect(shellControl.getOriginalShellDialect())
|
.shellDialect(shellControl.getOriginalShellDialect())
|
||||||
|
.ttyState(shellControl.getTtyState())
|
||||||
.running(true)
|
.running(true)
|
||||||
.osName(shellControl.getOsName())
|
.osName(shellControl.getOsName())
|
||||||
.build();
|
.build();
|
||||||
|
@ -113,25 +116,12 @@ public interface ShellControl extends ProcessControl {
|
||||||
script));
|
script));
|
||||||
}
|
}
|
||||||
|
|
||||||
default byte[] executeSimpleRawBytesCommand(String command) throws Exception {
|
|
||||||
try (CommandControl c = command(command).start()) {
|
|
||||||
return c.readRawBytesOrThrow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default String executeSimpleStringCommand(String command) throws Exception {
|
default String executeSimpleStringCommand(String command) throws Exception {
|
||||||
try (CommandControl c = command(command).start()) {
|
try (CommandControl c = command(command).start()) {
|
||||||
return c.readStdoutOrThrow();
|
return c.readStdoutOrThrow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default Optional<String> executeSimpleStringCommandAndCheck(String command) throws Exception {
|
|
||||||
try (CommandControl c = command(command).start()) {
|
|
||||||
var out = c.readStdoutDiscardErr();
|
|
||||||
return c.getExitCode() == 0 ? Optional.of(out) : Optional.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default boolean executeSimpleBooleanCommand(String command) throws Exception {
|
default boolean executeSimpleBooleanCommand(String command) throws Exception {
|
||||||
try (CommandControl c = command(command).start()) {
|
try (CommandControl c = command(command).start()) {
|
||||||
return c.discardAndCheckExit();
|
return c.discardAndCheckExit();
|
||||||
|
@ -150,20 +140,6 @@ public interface ShellControl extends ProcessControl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default void executeSimpleCommand(String command, String failMessage) throws Exception {
|
|
||||||
try (CommandControl c = command(command).start()) {
|
|
||||||
c.discardOrThrow();
|
|
||||||
} catch (ProcessOutputException out) {
|
|
||||||
throw ProcessOutputException.withPrefix(failMessage, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default String executeSimpleStringCommand(ShellDialect type, String command) throws Exception {
|
|
||||||
try (var sub = subShell(type).start()) {
|
|
||||||
return sub.executeSimpleStringCommand(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShellControl withSecurityPolicy(ShellSecurityPolicy policy);
|
ShellControl withSecurityPolicy(ShellSecurityPolicy policy);
|
||||||
|
|
||||||
ShellSecurityPolicy getEffectiveSecurityPolicy();
|
ShellSecurityPolicy getEffectiveSecurityPolicy();
|
||||||
|
|
|
@ -184,5 +184,5 @@ public interface ShellDialect {
|
||||||
|
|
||||||
String getDisplayName();
|
String getDisplayName();
|
||||||
|
|
||||||
boolean doesEchoInput();
|
boolean doesEchoInputByDefault();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
package io.xpipe.core.process;
|
|
||||||
|
|
||||||
import lombok.Value;
|
|
||||||
|
|
||||||
@Value
|
|
||||||
public class ShellProperties {
|
|
||||||
|
|
||||||
ShellDialect dialect;
|
|
||||||
boolean ansiEscapes;
|
|
||||||
}
|
|
|
@ -19,6 +19,7 @@ public class ShellStoreState extends DataStoreState implements OsNameState {
|
||||||
OsType.Any osType;
|
OsType.Any osType;
|
||||||
String osName;
|
String osName;
|
||||||
ShellDialect shellDialect;
|
ShellDialect shellDialect;
|
||||||
|
ShellTtyState ttyState;
|
||||||
Boolean running;
|
Boolean running;
|
||||||
|
|
||||||
public boolean isRunning() {
|
public boolean isRunning() {
|
||||||
|
@ -39,6 +40,7 @@ public class ShellStoreState extends DataStoreState implements OsNameState {
|
||||||
b.osType(useNewer(osType, shellStoreState.getOsType()))
|
b.osType(useNewer(osType, shellStoreState.getOsType()))
|
||||||
.osName(useNewer(osName, shellStoreState.getOsName()))
|
.osName(useNewer(osName, shellStoreState.getOsName()))
|
||||||
.shellDialect(useNewer(shellDialect, shellStoreState.getShellDialect()))
|
.shellDialect(useNewer(shellDialect, shellStoreState.getShellDialect()))
|
||||||
|
.ttyState(useNewer(ttyState, shellStoreState.getTtyState()))
|
||||||
.running(useNewer(running, shellStoreState.getRunning()));
|
.running(useNewer(running, shellStoreState.getRunning()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
25
core/src/main/java/io/xpipe/core/process/ShellTtyState.java
Normal file
25
core/src/main/java/io/xpipe/core/process/ShellTtyState.java
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package io.xpipe.core.process;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum ShellTtyState {
|
||||||
|
|
||||||
|
@JsonProperty("none")
|
||||||
|
NONE(true, false, false),
|
||||||
|
@JsonProperty("merged")
|
||||||
|
MERGED_STDERR(false, false, false),
|
||||||
|
@JsonProperty("pty")
|
||||||
|
PTY_ALLOCATED(false, true, true);
|
||||||
|
|
||||||
|
private final boolean hasSeparateStreams;
|
||||||
|
private final boolean hasAnsiEscapes;
|
||||||
|
private final boolean echoesAllInput;
|
||||||
|
|
||||||
|
ShellTtyState(boolean hasSeparateStreams, boolean hasAnsiEscapes, boolean echoesAllInput) {
|
||||||
|
this.hasSeparateStreams = hasSeparateStreams;
|
||||||
|
this.hasAnsiEscapes = hasAnsiEscapes;
|
||||||
|
this.echoesAllInput = echoesAllInput;
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,13 +10,11 @@ import io.xpipe.core.process.ShellControl;
|
||||||
import io.xpipe.core.process.ShellDialects;
|
import io.xpipe.core.process.ShellDialects;
|
||||||
import io.xpipe.core.store.LocalStore;
|
import io.xpipe.core.store.LocalStore;
|
||||||
import io.xpipe.core.store.ShellStore;
|
import io.xpipe.core.store.ShellStore;
|
||||||
|
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
|
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.InputStreamReader;
|
import java.io.StringReader;
|
||||||
|
|
||||||
public class SampleStoreAction implements ActionProvider {
|
public class SampleStoreAction implements ActionProvider {
|
||||||
|
|
||||||
|
@ -83,23 +81,14 @@ public class SampleStoreAction implements ActionProvider {
|
||||||
sc.executeSimpleStringCommand(sc.getShellDialect().getEchoCommand("hello!", false));
|
sc.executeSimpleStringCommand(sc.getShellDialect().getEchoCommand("hello!", false));
|
||||||
|
|
||||||
// You can also implement custom handling for more complex commands
|
// You can also implement custom handling for more complex commands
|
||||||
try (CommandControl cc = sc.command("ls").start()) {
|
var lsOut = sc.command("ls").readStdoutOrThrow();
|
||||||
// Discard stderr
|
// Read the stdout lines as a stream
|
||||||
cc.discardErr();
|
BufferedReader reader = new BufferedReader(new StringReader(lsOut));
|
||||||
|
// We don't have to close this stream here, that will be automatically done by the command control
|
||||||
// Read the stdout lines as a stream
|
// after the try-with block
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(cc.getStdout(), cc.getCharset()));
|
reader.lines().filter(s -> !s.isBlank()).forEach(s -> {
|
||||||
// We don't have to close this stream here, that will be automatically done by the command control
|
System.out.println(s);
|
||||||
// after the try-with block
|
});
|
||||||
reader.lines().filter(s -> !s.isBlank()).forEach(s -> {
|
|
||||||
System.out.println(s);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Waits for command completion and returns exit code
|
|
||||||
if (cc.getExitCode() != 0) {
|
|
||||||
// Handle failure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commands can also be more complex and span multiple lines.
|
// Commands can also be more complex and span multiple lines.
|
||||||
// In this case, XPipe will internally write a command to a script file and then execute the script
|
// In this case, XPipe will internally write a command to a script file and then execute the script
|
||||||
|
|
|
@ -16,12 +16,10 @@ public abstract class ToFileCommandAction implements LeafAction, ApplicationPath
|
||||||
ShellControl sc = model.getFileSystem().getShell().orElseThrow();
|
ShellControl sc = model.getFileSystem().getShell().orElseThrow();
|
||||||
for (BrowserEntry entry : entries) {
|
for (BrowserEntry entry : entries) {
|
||||||
var command = createCommand(model, entry);
|
var command = createCommand(model, entry);
|
||||||
try (var cc = sc.command(command)
|
var out = sc.command(command)
|
||||||
.withWorkingDirectory(model.getCurrentDirectory().getPath())
|
.withWorkingDirectory(model.getCurrentDirectory().getPath())
|
||||||
.start()) {
|
.readStdoutOrThrow();
|
||||||
cc.discardErr();
|
FileOpener.openReadOnlyString(out);
|
||||||
FileOpener.openCommandOutput(entry.getFileName(), entry, cc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
package io.xpipe.ext.base.store;
|
||||||
|
|
||||||
|
import io.xpipe.app.browser.session.BrowserSessionModel;
|
||||||
|
import io.xpipe.app.comp.base.OsLogoComp;
|
||||||
|
import io.xpipe.app.comp.base.SystemStateComp;
|
||||||
|
import io.xpipe.app.comp.base.TtyWarningComp;
|
||||||
|
import io.xpipe.app.comp.store.StoreEntryComp;
|
||||||
|
import io.xpipe.app.comp.store.StoreEntryWrapper;
|
||||||
|
import io.xpipe.app.comp.store.StoreSection;
|
||||||
|
import io.xpipe.app.ext.ActionProvider;
|
||||||
|
import io.xpipe.app.ext.DataStoreProvider;
|
||||||
|
import io.xpipe.app.ext.DataStoreUsageCategory;
|
||||||
|
import io.xpipe.app.fxcomps.Comp;
|
||||||
|
import io.xpipe.app.storage.DataStorage;
|
||||||
|
import io.xpipe.app.storage.DataStoreEntry;
|
||||||
|
import io.xpipe.app.util.TerminalLauncher;
|
||||||
|
import io.xpipe.core.process.ShellStoreState;
|
||||||
|
import io.xpipe.core.process.ShellTtyState;
|
||||||
|
import io.xpipe.core.store.ShellStore;
|
||||||
|
import io.xpipe.ext.base.script.ScriptStore;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
|
||||||
|
public interface ShellStoreProvider extends DataStoreProvider {
|
||||||
|
|
||||||
|
default Comp<?> createTtyWarning(StoreEntryWrapper w) {
|
||||||
|
return new TtyWarningComp().hide(Bindings.createObjectBinding(
|
||||||
|
() -> {
|
||||||
|
ShellStoreState state = (ShellStoreState) w.getPersistentState().getValue();
|
||||||
|
return state.getTtyState() == ShellTtyState.NONE;
|
||||||
|
},
|
||||||
|
w.getPersistentState()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default StoreEntryComp customEntryComp(StoreSection s, boolean preferLarge) {
|
||||||
|
return StoreEntryComp.create(s, createTtyWarning(s.getWrapper()), preferLarge);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default ActionProvider.Action launchAction(DataStoreEntry entry) {
|
||||||
|
return new ActionProvider.Action() {
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
ShellStore store = entry.getStore().asNeeded();
|
||||||
|
TerminalLauncher.open(entry, DataStorage.get().getStoreEntryDisplayName(entry), null, ScriptStore.controlWithDefaultScripts(store.control()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default ActionProvider.Action browserAction(BrowserSessionModel sessionModel, DataStoreEntry store, BooleanProperty busy) {
|
||||||
|
return new ActionProvider.Action() {
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
sessionModel.openFileSystemAsync(store.ref(), null, busy);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
default Comp<?> stateDisplay(StoreEntryWrapper w) {
|
||||||
|
return new OsLogoComp(w, SystemStateComp.State.shellState(w));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default DataStoreUsageCategory getUsageCategory() {
|
||||||
|
return DataStoreUsageCategory.SHELL;
|
||||||
|
}
|
||||||
|
}
|
|
@ -500,3 +500,6 @@ workspaceNameDescription=Visningsnavnet på arbejdsområdet
|
||||||
workspacePath=Sti til arbejdsområde
|
workspacePath=Sti til arbejdsområde
|
||||||
workspacePathDescription=Placeringen af arbejdsområdets datakatalog
|
workspacePathDescription=Placeringen af arbejdsområdets datakatalog
|
||||||
workspaceCreationAlertTitle=Oprettelse af arbejdsområde
|
workspaceCreationAlertTitle=Oprettelse af arbejdsområde
|
||||||
|
developerForceSshTty=Fremtving SSH TTY
|
||||||
|
developerForceSshTtyDescription=Få alle SSH-forbindelser til at tildele en pty for at teste understøttelsen af en manglende stderr og en pty.
|
||||||
|
ttyWarning=Forbindelsen har tvangstildelt en pty/tty og giver ikke en separat stderr-strøm.\n\nDet kan føre til et par problemer.\n\nHvis du kan, så prøv at få forbindelseskommandoen til ikke at tildele en pty.
|
||||||
|
|
|
@ -494,3 +494,6 @@ workspaceNameDescription=Der Anzeigename des Arbeitsbereichs
|
||||||
workspacePath=Pfad zum Arbeitsbereich
|
workspacePath=Pfad zum Arbeitsbereich
|
||||||
workspacePathDescription=Der Ort des Datenverzeichnisses des Arbeitsbereichs
|
workspacePathDescription=Der Ort des Datenverzeichnisses des Arbeitsbereichs
|
||||||
workspaceCreationAlertTitle=Arbeitsbereich erstellen
|
workspaceCreationAlertTitle=Arbeitsbereich erstellen
|
||||||
|
developerForceSshTty=SSH TTY erzwingen
|
||||||
|
developerForceSshTtyDescription=Lass alle SSH-Verbindungen ein pty zuweisen, um die Unterstützung für einen fehlenden stderr und ein pty zu testen.
|
||||||
|
ttyWarning=Die Verbindung hat zwangsweise ein pty/tty zugewiesen und stellt keinen separaten stderr-Stream zur Verfügung.\n\nDas kann zu einigen Problemen führen.\n\nWenn du kannst, solltest du dafür sorgen, dass der Verbindungsbefehl kein pty zuweist.
|
||||||
|
|
|
@ -498,3 +498,6 @@ workspaceNameDescription=The display name of the workspace
|
||||||
workspacePath=Workspace path
|
workspacePath=Workspace path
|
||||||
workspacePathDescription=The location of the workspace data directory
|
workspacePathDescription=The location of the workspace data directory
|
||||||
workspaceCreationAlertTitle=Workspace creation
|
workspaceCreationAlertTitle=Workspace creation
|
||||||
|
developerForceSshTty=Force SSH TTY
|
||||||
|
developerForceSshTtyDescription=Make all SSH connections allocate a pty to test the support for a missing stderr and a pty.
|
||||||
|
ttyWarning=The connection has forcefully allocated a pty/tty and does not provide a separate stderr stream.\n\nThis might lead to a few problems.\n\nIf you can, look into making the connection command not allocate a pty.
|
||||||
|
|
|
@ -481,3 +481,6 @@ workspaceNameDescription=El nombre para mostrar del espacio de trabajo
|
||||||
workspacePath=Ruta del espacio de trabajo
|
workspacePath=Ruta del espacio de trabajo
|
||||||
workspacePathDescription=La ubicación del directorio de datos del espacio de trabajo
|
workspacePathDescription=La ubicación del directorio de datos del espacio de trabajo
|
||||||
workspaceCreationAlertTitle=Creación de espacios de trabajo
|
workspaceCreationAlertTitle=Creación de espacios de trabajo
|
||||||
|
developerForceSshTty=Forzar SSH TTY
|
||||||
|
developerForceSshTtyDescription=Haz que todas las conexiones SSH asignen una pty para probar la compatibilidad con una stderr y una pty ausentes.
|
||||||
|
ttyWarning=La conexión ha asignado forzosamente un pty/tty y no proporciona un flujo stderr separado.\n\nEsto puede provocar algunos problemas.\n\nSi puedes, intenta que el comando de conexión no asigne una pty.
|
||||||
|
|
|
@ -481,3 +481,6 @@ workspaceNameDescription=Le nom d'affichage de l'espace de travail
|
||||||
workspacePath=Chemin d'accès à l'espace de travail
|
workspacePath=Chemin d'accès à l'espace de travail
|
||||||
workspacePathDescription=L'emplacement du répertoire de données de l'espace de travail
|
workspacePathDescription=L'emplacement du répertoire de données de l'espace de travail
|
||||||
workspaceCreationAlertTitle=Création d'un espace de travail
|
workspaceCreationAlertTitle=Création d'un espace de travail
|
||||||
|
developerForceSshTty=Force SSH TTY
|
||||||
|
developerForceSshTtyDescription=Fais en sorte que toutes les connexions SSH allouent un pty pour tester la prise en charge d'un stderr et d'un pty manquants.
|
||||||
|
ttyWarning=La connexion a alloué de force un pty/tty et ne fournit pas de flux stderr séparé.\n\nCela peut entraîner quelques problèmes.\n\nSi tu le peux, essaie de faire en sorte que la commande de connexion n'alloue pas de pty.
|
||||||
|
|
|
@ -481,3 +481,6 @@ workspaceNameDescription=Il nome di visualizzazione dell'area di lavoro
|
||||||
workspacePath=Percorso dello spazio di lavoro
|
workspacePath=Percorso dello spazio di lavoro
|
||||||
workspacePathDescription=La posizione della directory dei dati dell'area di lavoro
|
workspacePathDescription=La posizione della directory dei dati dell'area di lavoro
|
||||||
workspaceCreationAlertTitle=Creazione di uno spazio di lavoro
|
workspaceCreationAlertTitle=Creazione di uno spazio di lavoro
|
||||||
|
developerForceSshTty=Forza SSH TTY
|
||||||
|
developerForceSshTtyDescription=Fai in modo che tutte le connessioni SSH allocino una pty per testare il supporto di una stderr e di una pty mancanti.
|
||||||
|
ttyWarning=La connessione ha allocato forzatamente una pty/tty e non fornisce un flusso stderr separato.\n\nQuesto potrebbe causare alcuni problemi.\n\nSe puoi, cerca di fare in modo che il comando di connessione non allarghi una pty.
|
||||||
|
|
|
@ -481,3 +481,6 @@ workspaceNameDescription=ワークスペースの表示名
|
||||||
workspacePath=ワークスペースのパス
|
workspacePath=ワークスペースのパス
|
||||||
workspacePathDescription=ワークスペースのデータディレクトリの場所
|
workspacePathDescription=ワークスペースのデータディレクトリの場所
|
||||||
workspaceCreationAlertTitle=ワークスペースの作成
|
workspaceCreationAlertTitle=ワークスペースの作成
|
||||||
|
developerForceSshTty=強制SSH TTY
|
||||||
|
developerForceSshTtyDescription=すべてのSSHコネクションにptyを割り当て、stderrとptyがない場合のサポートをテストする。
|
||||||
|
ttyWarning=接続が強制的にpty/ttyを割り当て、個別のstderrストリームを提供しない。\n\nこれはいくつかの問題を引き起こす可能性がある。\n\n可能であれば、接続コマンドで pty を割り当てないようにすることを検討してほしい。
|
||||||
|
|
|
@ -481,3 +481,6 @@ workspaceNameDescription=De weergavenaam van de werkruimte
|
||||||
workspacePath=Werkruimte pad
|
workspacePath=Werkruimte pad
|
||||||
workspacePathDescription=De locatie van de gegevensmap van de werkruimte
|
workspacePathDescription=De locatie van de gegevensmap van de werkruimte
|
||||||
workspaceCreationAlertTitle=Werkruimte maken
|
workspaceCreationAlertTitle=Werkruimte maken
|
||||||
|
developerForceSshTty=SSH TTY afdwingen
|
||||||
|
developerForceSshTtyDescription=Laat alle SSH-verbindingen een pty toewijzen om de ondersteuning voor een ontbrekende stderr en een pty te testen.
|
||||||
|
ttyWarning=De verbinding heeft geforceerd een pty/tty toegewezen en biedt geen aparte stderr stream.\n\nDit kan tot een paar problemen leiden.\n\nAls je kunt, kijk dan of je het connection commando geen pty kunt laten toewijzen.
|
||||||
|
|
|
@ -481,3 +481,6 @@ workspaceNameDescription=O nome de apresentação do espaço de trabalho
|
||||||
workspacePath=Caminho do espaço de trabalho
|
workspacePath=Caminho do espaço de trabalho
|
||||||
workspacePathDescription=A localização do diretório de dados do espaço de trabalho
|
workspacePathDescription=A localização do diretório de dados do espaço de trabalho
|
||||||
workspaceCreationAlertTitle=Criação de espaço de trabalho
|
workspaceCreationAlertTitle=Criação de espaço de trabalho
|
||||||
|
developerForceSshTty=Força o SSH TTY
|
||||||
|
developerForceSshTtyDescription=Faz com que todas as ligações SSH atribuam um pty para testar o suporte para um stderr e um pty em falta.
|
||||||
|
ttyWarning=A ligação atribuiu à força um pty/tty e não fornece um fluxo stderr separado.\n\nIsto pode levar a alguns problemas.\n\nSe puderes, tenta fazer com que o comando de ligação não atribua um pty.
|
||||||
|
|
|
@ -481,3 +481,6 @@ workspaceNameDescription=Отображаемое имя рабочей обла
|
||||||
workspacePath=Путь к рабочему пространству
|
workspacePath=Путь к рабочему пространству
|
||||||
workspacePathDescription=Расположение каталога данных рабочей области
|
workspacePathDescription=Расположение каталога данных рабочей области
|
||||||
workspaceCreationAlertTitle=Создание рабочего пространства
|
workspaceCreationAlertTitle=Создание рабочего пространства
|
||||||
|
developerForceSshTty=Принудительный SSH TTY
|
||||||
|
developerForceSshTtyDescription=Заставь все SSH-соединения выделять pty, чтобы проверить поддержку отсутствующего stderr и pty.
|
||||||
|
ttyWarning=Соединение принудительно выделило pty/tty и не предоставляет отдельный поток stderr.\n\nЭто может привести к нескольким проблемам.\n\nЕсли можешь, попробуй сделать так, чтобы команда подключения не выделяла pty.
|
||||||
|
|
|
@ -482,3 +482,6 @@ workspaceNameDescription=Çalışma alanının görünen adı
|
||||||
workspacePath=Çalışma alanı yolu
|
workspacePath=Çalışma alanı yolu
|
||||||
workspacePathDescription=Çalışma alanı veri dizininin konumu
|
workspacePathDescription=Çalışma alanı veri dizininin konumu
|
||||||
workspaceCreationAlertTitle=Çalışma alanı oluşturma
|
workspaceCreationAlertTitle=Çalışma alanı oluşturma
|
||||||
|
developerForceSshTty=SSH TTY'yi Zorla
|
||||||
|
developerForceSshTtyDescription=Eksik bir stderr ve bir pty desteğini test etmek için tüm SSH bağlantılarının bir pty ayırmasını sağlayın.
|
||||||
|
ttyWarning=Bağlantı zorla bir pty/tty ayırmış ve ayrı bir stderr akışı sağlamıyor.\n\nBu durum birkaç soruna yol açabilir.\n\nEğer yapabiliyorsanız, bağlantı komutunun bir pty tahsis etmemesini sağlayın.
|
||||||
|
|
|
@ -481,3 +481,6 @@ workspaceNameDescription=工作区的显示名称
|
||||||
workspacePath=工作区路径
|
workspacePath=工作区路径
|
||||||
workspacePathDescription=工作区数据目录的位置
|
workspacePathDescription=工作区数据目录的位置
|
||||||
workspaceCreationAlertTitle=创建工作区
|
workspaceCreationAlertTitle=创建工作区
|
||||||
|
developerForceSshTty=强制 SSH TTY
|
||||||
|
developerForceSshTtyDescription=让所有 SSH 连接都分配一个 pty,以测试对缺失的 stderr 和 pty 的支持。
|
||||||
|
ttyWarning=连接强行分配了 pty/tty,且未提供单独的 stderr 流。\n\n这可能会导致一些问题。\n\n如果可以,请考虑让连接命令不分配 pty。
|
||||||
|
|
Loading…
Reference in a new issue