From c3beb6df6b122e2103581923640bff2ea00ebb8c Mon Sep 17 00:00:00 2001 From: Vantz Stockwell Date: Tue, 17 Mar 2026 14:14:52 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20revert=20CWD=20tracker=20from=20readLoop?= =?UTF-8?q?=20=E2=80=94=20corrupts=20ANSI=20escape=20sequences?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ProcessOutput() in the readLoop was processing every byte of SSH output, looking for OSC 7 sequences. When these sequences split across read boundaries (common with 32KB buffer), partial sequences leaked through and corrupted xterm.js parser state — producing red/green color blocks instead of text, and characters rendering at wrong widths. Reverted readLoop to direct passthrough (v0.7.3 behavior). Also removed shell integration injection (stty -echo + PROMPT_COMMAND) which caused terminal mode disruption on macOS. Also removed 4px xterm padding that could cause fitAddon cell width miscalculation. CWD tracking will be re-implemented via a separate SSH exec channel that polls pwd without touching the terminal data stream. Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/src/assets/css/terminal.css | 1 - internal/ssh/service.go | 44 ++++------------------------ 2 files changed, 6 insertions(+), 39 deletions(-) diff --git a/frontend/src/assets/css/terminal.css b/frontend/src/assets/css/terminal.css index 2e67ee6..c4ff062 100644 --- a/frontend/src/assets/css/terminal.css +++ b/frontend/src/assets/css/terminal.css @@ -10,7 +10,6 @@ .terminal-container .xterm { height: 100%; - padding: 4px; } .terminal-container .xterm-viewport { diff --git a/internal/ssh/service.go b/internal/ssh/service.go index db81178..723530e 100644 --- a/internal/ssh/service.go +++ b/internal/ssh/service.go @@ -142,23 +142,10 @@ 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) - // 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() - }() + // CWD tracking disabled — ProcessOutput() in the readLoop was corrupting + // ANSI escape sequences when OSC 7 sequences split across read boundaries. + // CWD following will be re-implemented via a separate SSH exec channel + // that doesn't touch the terminal data stream. return sessionID, nil } @@ -216,32 +203,13 @@ func (s *SSHService) buildHostKeyCallback(hostname string, port int) ssh.HostKey // (stripping OSC 7 sequences), and calls the output handler with cleaned data. // It stops when the reader returns an error (typically EOF when the session closes). func (s *SSHService) readLoop(sessionID string, reader io.Reader) { - // Grab the CWD tracker for this session (if any) - s.mu.RLock() - sess := s.sessions[sessionID] - s.mu.RUnlock() - buf := make([]byte, 32*1024) for { n, err := reader.Read(buf) - if n > 0 { + if n > 0 && s.outputHandler != nil { data := make([]byte, n) copy(data, buf[:n]) - - // Process CWD tracking — strips OSC 7 sequences from output - if sess != nil && sess.CWDTracker != nil { - cleaned, newCWD := sess.CWDTracker.ProcessOutput(data) - data = cleaned - - // Emit CWD change event if a new path was detected - if newCWD != "" && s.cwdHandler != nil { - s.cwdHandler(sessionID, newCWD) - } - } - - if len(data) > 0 && s.outputHandler != nil { - s.outputHandler(sessionID, data) - } + s.outputHandler(sessionID, data) } if err != nil { break