This commit is contained in:
crschnick 2025-04-12 14:54:53 +00:00
parent ae57c794e9
commit a419d623a1
13 changed files with 47 additions and 5085 deletions

View file

@ -75,7 +75,7 @@ Especially when starting out, it might be a good idea to start with easy tasks f
### Interacting via the HTTP API
You can create clients that communicate with the XPipe daemon via its HTTP API.
To get started, see the [OpenAPI spec](/openapi.yaml).
To get started, see the [OpenAPI spec](https://docs.xpipe.io/api).
### Implementing support for a new editor

View file

@ -156,13 +156,6 @@ processResources {
into resourcesDir
}
}
doLast {
copy {
from file("$rootDir/openapi.yaml")
into file("${sourceSets.main.output.resourcesDir}/io/xpipe/app/resources/misc");
}
}
}
distTar {

View file

@ -31,7 +31,7 @@ public class AboutCategory extends AppPrefsCategory {
@Override
protected Comp<?> create() {
var props = createProperties().padding(new Insets(0, 0, 0, 5));
var update = new UpdateCheckComp().grow(true, false);
var update = new UpdateCheckComp().grow(true, false).prefWidth(600);
return new VerticalComp(List.of(props, Comp.hspacer(8), update, Comp.hspacer(13), Comp.hseparator().padding(Insets.EMPTY)))
.apply(s -> s.get().setFillWidth(true))
.apply(struc -> struc.get().setSpacing(15))

View file

@ -175,6 +175,7 @@ public class TerminalLauncher {
if (preferTabs) {
var multiplexerConfig = launchMultiplexerTabInNewTerminal(request, terminalConfig, config);
if (multiplexerConfig.isPresent()) {
TerminalMultiplexerManager.registerMultiplexerLaunch(request);
launch(type, multiplexerConfig.get(), latch);
return;
}

View file

@ -1,19 +1,49 @@
package io.xpipe.app.terminal;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.ThreadHelper;
import java.util.*;
public class TerminalMultiplexerManager {
private static UUID pendingMultiplexerLaunch;
private static final Map<UUID, TerminalMultiplexer> connectionHubRequests = new HashMap<>();
public static void registerMultiplexerLaunch(UUID uuid) {
pendingMultiplexerLaunch = uuid;
var listener = new TerminalView.Listener() {
@Override
public void onSessionOpened(TerminalView.ShellSession session) {
if (session.getRequest().equals(pendingMultiplexerLaunch)) {
pendingMultiplexerLaunch = null;
TerminalView.get().removeListener(this);
}
}
};
TerminalView.get().addListener(listener);
}
public static Optional<TerminalMultiplexer> getEffectiveMultiplexer() {
var multiplexer = AppPrefs.get().terminalMultiplexer().getValue();
return Optional.ofNullable(multiplexer);
}
public static boolean requiresNewTerminalSession(UUID requestUuid) {
// Wait if we are currently opening a new multiplexer
if (pendingMultiplexerLaunch != null) {
// Wait for max 10s
for (int i = 0; i < 100; i++) {
if (pendingMultiplexerLaunch == null) {
break;
}
ThreadHelper.sleep(100);
}
// Give multiplexer a second to start in terminal
ThreadHelper.sleep(1000);
}
var mult = getEffectiveMultiplexer();
if (mult.isEmpty()) {
connectionHubRequests.put(requestUuid, null);

View file

@ -13,6 +13,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Consumer;
public class TerminalView {
@ -106,18 +107,23 @@ public class TerminalView {
if (!terminalInstances.contains(tv.get())) {
terminalInstances.add(tv.get());
listeners.forEach(listener -> listener.onTerminalOpened(tv.get()));
forListeners(listener -> listener.onTerminalOpened(tv.get()));
}
var session = new ShellSession(request, shell.get(), tv.get());
sessions.add(session);
listeners.forEach(listener -> listener.onSessionOpened(session));
forListeners(listener -> listener.onSessionOpened(session));
TrackEvent.withTrace("Terminal instance opened")
.tag("terminalPid", terminal.get().pid())
.handle();
}
private void forListeners(Consumer<Listener> consumer) {
var copy = new ArrayList<>(listeners);
copy.forEach(consumer);
}
private Optional<TerminalSession> createTerminalSession(ProcessHandle terminalProcess) {
return switch (OsType.getLocal()) {
case OsType.Linux linux -> Optional.of(new TerminalSession(terminalProcess));
@ -179,7 +185,7 @@ public class TerminalView {
var alive = session.shell.isAlive() && session.getTerminal().isRunning();
if (!alive) {
sessions.remove(session);
listeners.forEach(listener -> listener.onSessionClosed(session));
forListeners(listener -> listener.onSessionClosed(session));
}
}
@ -190,7 +196,7 @@ public class TerminalView {
TrackEvent.withTrace("Terminal session is dead")
.tag("pid", terminalInstance.getTerminalProcess().pid())
.handle();
listeners.forEach(listener -> listener.onTerminalClosed(terminalInstance));
forListeners(listener -> listener.onTerminalClosed(terminalInstance));
}
}
}

View file

@ -38,7 +38,7 @@ public class TmuxTerminalMultiplexer implements TerminalMultiplexer {
"tmux kill-session -t xpipe",
"tmux new-session -d -s xpipe",
"tmux rename-window \"" + escape(config.getDisplayName(), true) + "\"",
"tmux send-keys -t xpipe '" + escape(command, false) + ";exit' Enter",
"tmux send-keys -t xpipe ' " + escape(command, false) + "; exit' Enter",
"tmux attach -d -t xpipe");
}

View file

@ -30,7 +30,7 @@ public class ZellijTerminalMultiplexer implements TerminalMultiplexer {
return ShellScript.lines(
"zellij attach --create-background xpipe",
"zellij -s xpipe action new-tab --name \"" + escape(config.getDisplayName(), false, true) + "\"",
"zellij -s xpipe action write-chars -- " + escape(command, true, true) + "\\;exit",
"zellij -s xpipe action write-chars -- " + escape(" " + command, true, true) + "\\;exit",
"zellij -s xpipe action write 10",
"zellij -s xpipe action clear"
);
@ -43,7 +43,7 @@ public class ZellijTerminalMultiplexer implements TerminalMultiplexer {
"zellij delete-session -f xpipe > /dev/null 2>&1",
"zellij attach --create-background xpipe",
"zellij -s xpipe run -c --name \"" + escape(config.getDisplayName(), false, true) + "\" -- "
+ escape(command, false, false),
+ escape(" " + command, false, false),
"zellij attach xpipe");
}

View file

@ -6,12 +6,9 @@ public class Hyperlinks {
public static final String GITHUB = "https://github.com/xpipe-io/xpipe";
public static final String GITHUB_PTB = "https://github.com/xpipe-io/xpipe-ptb";
public static final String GITHUB_LATEST = "https://github.com/xpipe-io/xpipe/releases/latest";
public static final String GITHUB_PYTHON_API = "https://github.com/xpipe-io/xpipe-python-api";
public static final String TRANSLATE = "https://github.com/xpipe-io/xpipe/tree/master/lang";
public static final String DISCORD = "https://discord.gg/8y89vS8cRb";
public static final String GITHUB_WEBTOP = "https://github.com/xpipe-io/xpipe-webtop";
public static final String SLACK =
"https://join.slack.com/t/XPipe/shared_invite/zt-1awjq0t5j-5i4UjNJfNe1VN4b_auu6Cg";
public static void open(String uri) {
DesktopHelper.openUrl(uri);

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@ The XPipe beacon component is responsible for handling all communications betwee
and the APIs and the CLI. It provides an API that supports all kinds
of different operations.
For a full documentation, see the [OpenAPI spec](/../openapi.yaml)
For a full documentation, see the [OpenAPI spec](https://docs.xpipe.io/api)
### Inner Workings

View file

@ -102,11 +102,6 @@ public class RunScriptActionMenu implements ActionProvider {
public ActionProvider.Action createAction(DataStoreEntryRef<ShellStore> store) {
return new Action(store);
}
@Override
public List<? extends ActionProvider> getChildren(List<DataStoreEntryRef<ShellStore>> batch) {
return List.of();
}
};
}
}

File diff suppressed because it is too large Load diff