mirror of
https://github.com/xpipe-io/xpipe.git
synced 2026-09-26 18:35:43 +00:00
Cherry pick fixes for 21.2 [release]
This commit is contained in:
@@ -44,6 +44,8 @@ import java.util.stream.Stream;
|
||||
@Getter
|
||||
public final class BrowserFileSystemTabModel extends BrowserStoreSessionTab<FileSystemStore> {
|
||||
|
||||
private static boolean wasTerminalDocked;
|
||||
|
||||
private final Property<String> filter = new SimpleStringProperty();
|
||||
private final BrowserFileListModel fileList;
|
||||
private final ReadOnlyObjectWrapper<FilePath> currentPath = new ReadOnlyObjectWrapper<>();
|
||||
@@ -580,19 +582,24 @@ public final class BrowserFileSystemTabModel extends BrowserStoreSessionTab<File
|
||||
throws Exception {
|
||||
var dock = shouldLaunchSplitTerminal() && dockIfPossible;
|
||||
var uuid = UUID.randomUUID();
|
||||
terminalRequests.add(uuid);
|
||||
if (dock
|
||||
&& browserModel instanceof BrowserFullSessionModel fullSessionModel
|
||||
&& !(fullSessionModel.getSplits().get(this) instanceof BrowserTerminalDockTabModel)) {
|
||||
terminalRequests.add(uuid);
|
||||
fullSessionModel.splitTab(this, new BrowserTerminalDockTabModel(browserModel, this, terminalRequests));
|
||||
}
|
||||
|
||||
// If we docked once, we don't want to break it by opening new tabs in maybe still docked tabs
|
||||
var preferTabs = !wasTerminalDocked && !dock;
|
||||
wasTerminalDocked = wasTerminalDocked || dock;
|
||||
|
||||
TerminalLaunch.builder()
|
||||
.entry(entry.get())
|
||||
.title(name)
|
||||
.directory(directory)
|
||||
.command(processControl)
|
||||
.request(uuid)
|
||||
.preferTabs(!dock)
|
||||
.preferTabs(preferTabs)
|
||||
.launch();
|
||||
|
||||
// Restart connection as we will have to start it anyway, so we speed it up by doing it preemptively
|
||||
|
||||
@@ -73,7 +73,7 @@ public class BrowserTransferComp extends SimpleRegionBuilder {
|
||||
},
|
||||
sourceItem.get().getProgress());
|
||||
}
|
||||
});
|
||||
}).vgrow();
|
||||
var dragNotice = new LabelComp(AppI18n.observable("dragLocalFiles"))
|
||||
.apply(struc -> struc.setGraphic(new FontIcon("mdi2h-hand-back-left-outline")))
|
||||
.apply(struc -> struc.setWrapText(true))
|
||||
|
||||
@@ -8,6 +8,11 @@ import javafx.beans.value.ObservableValue;
|
||||
|
||||
public interface CountGroupStoreProvider extends DataStoreProvider {
|
||||
|
||||
@Override
|
||||
default boolean includeInConnectionCount() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
default ObservableValue<String> informationString(StoreSection section) {
|
||||
return Bindings.createStringBinding(
|
||||
|
||||
@@ -74,9 +74,11 @@ public class SentryErrorHandler implements ErrorHandler {
|
||||
ObjectInputStream ois = new ObjectInputStream(bais);
|
||||
var copy = (Throwable) ois.readObject();
|
||||
|
||||
var msgField = Throwable.class.getDeclaredField("detailMessage");
|
||||
msgField.setAccessible(true);
|
||||
msgField.set(copy, null);
|
||||
if (!(copy instanceof NullPointerException) && !(copy instanceof IndexOutOfBoundsException)) {
|
||||
var msgField = Throwable.class.getDeclaredField("detailMessage");
|
||||
msgField.setAccessible(true);
|
||||
msgField.set(copy, null);
|
||||
}
|
||||
|
||||
if (copy instanceof FileSystemException) {
|
||||
var fileField = FileSystemException.class.getDeclaredField("file");
|
||||
|
||||
@@ -178,12 +178,6 @@ public class TerminalCategory extends AppPrefsCategory {
|
||||
return new OptionsBuilder()
|
||||
.addTitle("terminalConfiguration")
|
||||
.sub(terminalChoice(true))
|
||||
.hide(Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
return !TerminalDockHubManager.isSupported();
|
||||
},
|
||||
prefs.terminalType,
|
||||
prefs.terminalMultiplexer))
|
||||
.sub(terminalPrompt())
|
||||
.sub(terminalProxy())
|
||||
.sub(terminalMultiplexer())
|
||||
|
||||
@@ -61,7 +61,7 @@ public class SecretInPlaceStrategy implements SecretRetrievalStrategy {
|
||||
return new SecretQuery() {
|
||||
@Override
|
||||
public SecretQueryResult query(String prompt) {
|
||||
return new SecretQueryResult(value, SecretQueryState.NORMAL);
|
||||
return value != null ? new SecretQueryResult(value, SecretQueryState.NORMAL) : new SecretQueryResult(null, SecretQueryState.RETRIEVAL_FAILURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -9,4 +9,9 @@ public class SecretQueryResult {
|
||||
|
||||
SecretValue secret;
|
||||
SecretQueryState state;
|
||||
|
||||
public SecretQueryResult(SecretValue secret, SecretQueryState state) {
|
||||
this.secret = secret;
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.xpipe.app.comp.base.LoadingIconComp;
|
||||
import io.xpipe.app.core.AppFontSizes;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.core.window.AppMainWindow;
|
||||
import io.xpipe.app.platform.NativeWinWindowControl;
|
||||
import io.xpipe.app.platform.PlatformThread;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.util.GlobalTimer;
|
||||
@@ -153,14 +154,22 @@ public class TerminalDockBrowserComp extends SimpleRegionBuilder {
|
||||
return;
|
||||
}
|
||||
|
||||
var bounds = region.localToScreen(region.getBoundsInLocal());
|
||||
var bounds = region.localToScene(region.getBoundsInLocal());
|
||||
var p = region.getPadding();
|
||||
var sx = region.getScene().getWindow().getOutputScaleX();
|
||||
var sy = region.getScene().getWindow().getOutputScaleY();
|
||||
|
||||
var scene = region.getScene();
|
||||
var windowRect = NativeWinWindowControl.MAIN_WINDOW.getBounds();
|
||||
var x = windowRect.getX() + ((bounds.getMinX() + p.getLeft() + scene.getX()) * sx);
|
||||
var y = windowRect.getY() + ((bounds.getMinY() + p.getTop() + scene.getY()) * sy);
|
||||
var w = (bounds.getWidth() * sx) - p.getRight() - p.getLeft();
|
||||
var h = (bounds.getHeight() * sy) - p.getBottom() - p.getTop();
|
||||
|
||||
model.resizeView(
|
||||
(int) Math.ceil(bounds.getMinX() * sx + p.getLeft()),
|
||||
(int) Math.ceil(bounds.getMinY() * sy + p.getTop()),
|
||||
(int) Math.floor(bounds.getWidth() * sx - p.getRight() - p.getLeft()),
|
||||
(int) Math.floor(bounds.getHeight() * sy - p.getBottom() - p.getTop()));
|
||||
(int) Math.round(x),
|
||||
(int) Math.round(y),
|
||||
(int) Math.round(w),
|
||||
(int) Math.round(h));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package io.xpipe.app.terminal;
|
||||
import io.xpipe.app.comp.SimpleRegionBuilder;
|
||||
import io.xpipe.app.core.window.AppMainWindow;
|
||||
|
||||
import io.xpipe.app.platform.NativeWinWindowControl;
|
||||
import io.xpipe.app.util.GlobalTimer;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
@@ -13,6 +15,7 @@ import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.stage.WindowEvent;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class TerminalDockHubComp extends SimpleRegionBuilder {
|
||||
@@ -46,6 +49,16 @@ public class TerminalDockHubComp extends SimpleRegionBuilder {
|
||||
update(stack);
|
||||
}
|
||||
};
|
||||
var scale = new ChangeListener<Number>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
|
||||
GlobalTimer.delay(() -> {
|
||||
Platform.runLater(() -> {
|
||||
update(stack);
|
||||
});
|
||||
}, Duration.ofMillis(100));
|
||||
}
|
||||
};
|
||||
var update = new ChangeListener<Number>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
|
||||
@@ -87,6 +100,7 @@ public class TerminalDockHubComp extends SimpleRegionBuilder {
|
||||
s.iconifiedProperty().removeListener(iconified);
|
||||
s.removeEventFilter(WindowEvent.WINDOW_SHOWN, show);
|
||||
s.removeEventFilter(WindowEvent.WINDOW_HIDING, hide);
|
||||
s.outputScaleXProperty().addListener(scale);
|
||||
if (parent.get() != null) {
|
||||
parent.get().boundsInParentProperty().removeListener(bounds);
|
||||
parent.set(null);
|
||||
@@ -97,6 +111,7 @@ public class TerminalDockHubComp extends SimpleRegionBuilder {
|
||||
s.widthProperty().addListener(update);
|
||||
s.heightProperty().addListener(update);
|
||||
s.iconifiedProperty().addListener(iconified);
|
||||
s.outputScaleXProperty().removeListener(scale);
|
||||
s.addEventFilter(WindowEvent.WINDOW_SHOWN, show);
|
||||
s.addEventFilter(WindowEvent.WINDOW_HIDING, hide);
|
||||
// As in practice this node is wrapped in another stack pane
|
||||
@@ -113,14 +128,22 @@ public class TerminalDockHubComp extends SimpleRegionBuilder {
|
||||
return;
|
||||
}
|
||||
|
||||
var bounds = region.localToScreen(region.getBoundsInLocal());
|
||||
var bounds = region.localToScene(region.getBoundsInLocal());
|
||||
var p = region.getPadding();
|
||||
var sx = region.getScene().getWindow().getOutputScaleX();
|
||||
var sy = region.getScene().getWindow().getOutputScaleY();
|
||||
|
||||
var scene = region.getScene();
|
||||
var windowRect = NativeWinWindowControl.MAIN_WINDOW.getBounds();
|
||||
var x = windowRect.getX() + ((bounds.getMinX() + p.getLeft() + scene.getX()) * sx);
|
||||
var y = windowRect.getY() + ((bounds.getMinY() + p.getTop() + scene.getY()) * sy);
|
||||
var w = (bounds.getWidth() * sx) - p.getRight() - p.getLeft();
|
||||
var h = (bounds.getHeight() * sy) - p.getBottom() - p.getTop();
|
||||
|
||||
model.resizeView(
|
||||
(int) Math.round(bounds.getMinX() * sx + p.getLeft()),
|
||||
(int) Math.round(bounds.getMinY() * sy + p.getTop()),
|
||||
(int) Math.round(bounds.getWidth() * sx - p.getRight() - p.getLeft()),
|
||||
(int) Math.round(bounds.getHeight() * sy - p.getBottom() - p.getTop()));
|
||||
(int) Math.round(x),
|
||||
(int) Math.round(y),
|
||||
(int) Math.round(w),
|
||||
(int) Math.round(h));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,17 @@ import io.xpipe.app.util.Rect;
|
||||
import io.xpipe.core.OsType;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.collections.ListChangeListener;
|
||||
|
||||
import javafx.stage.Screen;
|
||||
import lombok.Getter;
|
||||
import org.kordamp.ikonli.Ikon;
|
||||
import org.kordamp.ikonli.Ikonli;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
import org.kordamp.ikonli.materialdesign2.MaterialDesignC;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashSet;
|
||||
@@ -59,12 +63,6 @@ public class TerminalDockHubManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
var primaryScreen = Screen.getPrimary();
|
||||
var uniformScale = Screen.getScreens().stream().allMatch(screen -> screen.getOutputScaleX() == primaryScreen.getOutputScaleX());
|
||||
if (!uniformScale) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -107,10 +105,13 @@ public class TerminalDockHubManager {
|
||||
});
|
||||
private final AppLayoutModel.QueueEntry queueEntry = new AppLayoutModel.QueueEntry(
|
||||
AppI18n.observable("toggleTerminalDock"), new LabelGraphic.NodeGraphic(() -> {
|
||||
var fi = new FontIcon("mdi2c-console");
|
||||
fi.getStyleClass().add("graphic");
|
||||
fi.getStyleClass().add("terminal-dock-button");
|
||||
return fi;
|
||||
var inner = new FontIcon();
|
||||
inner.iconCodeProperty().bind(PlatformThread.sync(Bindings.createObjectBinding(() -> {
|
||||
return detached.get() || minimized.get() || !showing.get() ? MaterialDesignC.CONSOLE_LINE : MaterialDesignC.CONSOLE;
|
||||
}, detached, minimized, showing)));
|
||||
inner.getStyleClass().add("graphic");
|
||||
inner.getStyleClass().add("terminal-dock-button");
|
||||
return inner;
|
||||
}), () -> {
|
||||
refreshDockStatus();
|
||||
|
||||
|
||||
@@ -47,7 +47,22 @@ public class TerminalDockView {
|
||||
|
||||
public synchronized void updateCustomBounds() {
|
||||
terminalInstances.forEach(terminal -> {
|
||||
var wasCustom = terminal.isCustomBounds();
|
||||
terminal.updateBoundsState();
|
||||
|
||||
if (wasCustom && viewBounds != null) {
|
||||
var currentBounds = terminal.getLastBounds();
|
||||
var targetBounds = windowBoundsFunction.apply(viewBounds);
|
||||
var sum = Math.abs(targetBounds.getX() - currentBounds.getX()) +
|
||||
Math.abs(targetBounds.getY() - currentBounds.getY()) +
|
||||
Math.abs(targetBounds.getW() - currentBounds.getW()) +
|
||||
Math.abs(targetBounds.getH() - currentBounds.getH());
|
||||
if (sum < 30) {
|
||||
trackTerminal(terminal, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (terminal.isCustomBounds()) {
|
||||
terminal.disown();
|
||||
}
|
||||
@@ -82,16 +97,16 @@ public class TerminalDockView {
|
||||
}
|
||||
|
||||
public synchronized boolean closeOtherTerminals(UUID request) {
|
||||
var sessions = TerminalView.get().getSessions();
|
||||
var tv = sessions.stream()
|
||||
.filter(s -> request.equals(s.getRequest()) && s.getTerminal().isRunning())
|
||||
.map(s -> s.getTerminal().controllable())
|
||||
.flatMap(Optional::stream)
|
||||
var others = terminalInstances.stream()
|
||||
.filter(terminal -> terminal.getTerminalProcess().isAlive())
|
||||
.filter(terminal -> TerminalView.get().getSessions().stream()
|
||||
.noneMatch(shellSession -> shellSession.getRequest().equals(request) &&
|
||||
shellSession.getTerminal().equals(terminal)))
|
||||
.toList();
|
||||
for (int i = 0; i < tv.size() - 1; i++) {
|
||||
closeTerminal(tv.get(i));
|
||||
for (ControllableTerminalSession other : others) {
|
||||
closeTerminal(other);
|
||||
}
|
||||
return tv.size() > 1;
|
||||
return others.size() > 0;
|
||||
}
|
||||
|
||||
public synchronized void closeTerminal(ControllableTerminalSession terminal) {
|
||||
|
||||
@@ -167,7 +167,7 @@ public class TerminalLauncher {
|
||||
|
||||
var effectivePreferTabs =
|
||||
preferTabs && AppPrefs.get().preferTerminalTabs().get();
|
||||
var launchConfig = new TerminalLaunchConfiguration(color, adjustedTitle, cleanTitle, preferTabs, paneList);
|
||||
var launchConfig = new TerminalLaunchConfiguration(color, adjustedTitle, cleanTitle, effectivePreferTabs, paneList);
|
||||
|
||||
if (effectivePreferTabs
|
||||
&& AppPrefs.get().enableConnectionHubTerminalDocking().get()
|
||||
|
||||
@@ -115,7 +115,7 @@ public interface WezTerminalType extends ExternalTerminalType, TrackableTerminal
|
||||
var base = getWeztermCommandBase();
|
||||
var activeSocket = waitForInstanceStart(1);
|
||||
// Always start a new window for split panes as we can't find the pane index to start with
|
||||
if (activeSocket.isEmpty() || configuration.getPanes().size() > 1) {
|
||||
if (activeSocket.isEmpty() || configuration.getPanes().size() > 1 || !configuration.isPreferTabs()) {
|
||||
var gui = CommandBuilder.of().add(base.buildSimple().replace("wezterm.exe", "wezterm-gui.exe"));
|
||||
var command = CommandBuilder.of()
|
||||
.add(gui)
|
||||
|
||||
@@ -63,7 +63,7 @@ public class DesktopHelper {
|
||||
|
||||
if (!Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) {
|
||||
if (OsType.ofLocal() == OsType.LINUX) {
|
||||
LocalExec.readStdoutIfPossible("xdg-open", file.toString());
|
||||
LocalExec.executeAsync("xdg-open", file.toString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@ public class DesktopHelper {
|
||||
// Windows does not support Action.BROWSE_FILE_DIR
|
||||
if (OsType.ofLocal() == OsType.WINDOWS) {
|
||||
// Explorer does not support single quotes, so use normal quotes
|
||||
LocalExec.readStdoutIfPossible("explorer", "/select,", "\"" + file + "\"");
|
||||
LocalExec.executeAsync("explorer", "/select,", "\"" + file + "\"");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ public enum DocumentationLink {
|
||||
LICENSE_ACTIVATION("troubleshoot/license-activation"),
|
||||
TLS_DECRYPTION("troubleshoot/license-activation#tls-decryption"),
|
||||
UPDATE_FAIL("troubleshoot/update-fail"),
|
||||
PRIVACY("legal/privacy"),
|
||||
EULA("legal/eula"),
|
||||
PRIVACY("legal/privacy-policy"),
|
||||
EULA("legal/end-user-license-agreement"),
|
||||
WEBTOP_UPDATE("guide/webtop#updating"),
|
||||
WEBTOP_TUN("guide/webtop#networking-tailscale-and-netbird"),
|
||||
SYNC("guide/sync"),
|
||||
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
- Fix various SSH agents not working if the socket path contained spaces
|
||||
- Fix terminal docking not working properly on multiple displays with different scale factor
|
||||
- Fix terminal docking sometimes not automatically reattaching windows that were moved back to the dock
|
||||
- The terminal dock indicator icon will now show a different icon to indicate when the terminal is detached
|
||||
- Automatically delete corrupted git index file when detected
|
||||
- Fix terminal test button not showing for the terminal selection in the settings menu
|
||||
- Fix network switch ports being included in total connection count
|
||||
- Fix various NullPointers
|
||||
- Fix some broken documentation links
|
||||
@@ -26,6 +26,10 @@ public class RunTerminalScriptActionProvider implements ActionProvider {
|
||||
public void executeImpl() throws Exception {
|
||||
var sc = ref.getStore().getOrStartSession();
|
||||
var script = scriptStore.getStore().assembleScriptChain(sc, false);
|
||||
if (script == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TerminalLaunch.builder()
|
||||
.entry(ref.get())
|
||||
.title(scriptStore.get().getName())
|
||||
|
||||
Reference in New Issue
Block a user