feat: wire SFTP CWD following — listen for OSC 7 events + inject shell hook
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m2s
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m2s
Two missing halves of CWD tracking:
1. Frontend: useSftp now listens for ssh:cwd:{sessionId} Wails events
and calls navigateTo() when followTerminal is enabled (default: on).
2. Backend: re-added shell integration injection with stty -echo to
suppress visible command output. Leading space keeps it out of
shell history. Handles both bash (PROMPT_COMMAND) and zsh (precmd).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c953659852
commit
bce77e0932
@ -1,5 +1,5 @@
|
||||
import { ref, type Ref } from "vue";
|
||||
import { Call } from "@wailsio/runtime";
|
||||
import { ref, onBeforeUnmount, type Ref } from "vue";
|
||||
import { Call, Events } from "@wailsio/runtime";
|
||||
|
||||
const SFTP = "github.com/vstockwell/wraith/internal/sftp.SFTPService";
|
||||
|
||||
@ -30,7 +30,7 @@ export function useSftp(sessionId: string): UseSftpReturn {
|
||||
const currentPath = ref("/");
|
||||
const entries = ref<FileEntry[]>([]);
|
||||
const isLoading = ref(false);
|
||||
const followTerminal = ref(false);
|
||||
const followTerminal = ref(true);
|
||||
|
||||
async function listDirectory(path: string): Promise<FileEntry[]> {
|
||||
try {
|
||||
@ -66,6 +66,28 @@ export function useSftp(sessionId: string): UseSftpReturn {
|
||||
await navigateTo(currentPath.value);
|
||||
}
|
||||
|
||||
// Listen for CWD changes from the Go backend (OSC 7 tracking)
|
||||
const cleanupCwd = Events.On(`ssh:cwd:${sessionId}`, (event: any) => {
|
||||
if (!followTerminal.value) return;
|
||||
let newPath: string;
|
||||
if (typeof event === "string") {
|
||||
newPath = event;
|
||||
} else if (event?.data && typeof event.data === "string") {
|
||||
newPath = event.data;
|
||||
} else if (Array.isArray(event?.data)) {
|
||||
newPath = String(event.data[0] ?? "");
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (newPath && newPath !== currentPath.value) {
|
||||
navigateTo(newPath);
|
||||
}
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (cleanupCwd) cleanupCwd();
|
||||
});
|
||||
|
||||
// Load home directory on init
|
||||
navigateTo("/home");
|
||||
|
||||
|
||||
@ -142,10 +142,23 @@ func (s *SSHService) Connect(hostname string, port int, username string, authMet
|
||||
// Launch goroutine to read stdout and forward data via the output handler
|
||||
go s.readLoop(sessionID, stdout)
|
||||
|
||||
// CWD tracking via OSC 7 is handled passively — the CWDTracker in readLoop
|
||||
// parses OSC 7 sequences if the remote shell already emits them. Automatic
|
||||
// PROMPT_COMMAND injection is deferred until we have a non-echoing mechanism
|
||||
// (e.g., writing to a second SSH channel or modifying .bashrc).
|
||||
// Inject shell integration for CWD tracking (OSC 7).
|
||||
// Uses stty -echo to suppress the command from appearing in the terminal,
|
||||
// then restores echo. A leading space keeps it out of shell history.
|
||||
go func() {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
// Suppress echo, set PROMPT_COMMAND for bash (zsh uses precmd),
|
||||
// restore echo, then clear the current line so no visual artifact remains.
|
||||
injection := " stty -echo 2>/dev/null; " +
|
||||
ShellIntegrationCommand("bash") + "; " +
|
||||
"if [ -n \"$ZSH_VERSION\" ]; then " + ShellIntegrationCommand("zsh") + "; fi; " +
|
||||
"stty echo 2>/dev/null\n"
|
||||
sshSession.mu.Lock()
|
||||
if sshSession.Stdin != nil {
|
||||
_, _ = sshSession.Stdin.Write([]byte(injection))
|
||||
}
|
||||
sshSession.mu.Unlock()
|
||||
}()
|
||||
|
||||
return sessionID, nil
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user