Skip to content

Commit a35635c

Browse files
authored
Handle terminal resizing (#30)
1 parent d177c91 commit a35635c

File tree

1 file changed

+39
-1
lines changed

1 file changed

+39
-1
lines changed

pkg/cmd/exec.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"os"
1212
"os/signal"
1313
"strings"
14+
"sync"
1415
"syscall"
1516

1617
"github.com/gorilla/websocket"
@@ -35,6 +36,8 @@ type execRequest struct {
3536
Env map[string]string `json:"env,omitempty"`
3637
Cwd string `json:"cwd,omitempty"`
3738
Timeout int32 `json:"timeout,omitempty"`
39+
Rows uint32 `json:"rows,omitempty"`
40+
Cols uint32 `json:"cols,omitempty"`
3841
}
3942

4043
var execCmd = cli.Command{
@@ -127,6 +130,17 @@ func handleExec(ctx context.Context, cmd *cli.Command) error {
127130
execReq.Timeout = int32(timeout)
128131
}
129132

133+
// Get terminal size for TTY mode (only if stdout is actually a terminal)
134+
if tty && term.IsTerminal(int(os.Stdout.Fd())) {
135+
cols, rows, _ := term.GetSize(int(os.Stdout.Fd()))
136+
if rows > 0 {
137+
execReq.Rows = uint32(rows)
138+
}
139+
if cols > 0 {
140+
execReq.Cols = uint32(cols)
141+
}
142+
}
143+
130144
reqBody, err := json.Marshal(execReq)
131145
if err != nil {
132146
return fmt.Errorf("failed to marshal request: %w", err)
@@ -213,9 +227,30 @@ func runExecInteractive(ws *websocket.Conn) (int, error) {
213227
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
214228
defer signal.Stop(sigCh)
215229

230+
// Handle SIGWINCH for terminal resize
231+
sigwinch := make(chan os.Signal, 1)
232+
signal.Notify(sigwinch, syscall.SIGWINCH)
233+
defer signal.Stop(sigwinch)
234+
235+
// Mutex to protect WebSocket writes from concurrent access
236+
var wsMu sync.Mutex
237+
216238
errCh := make(chan error, 2)
217239
exitCodeCh := make(chan int, 1)
218240

241+
// Handle terminal resize events
242+
go func() {
243+
for range sigwinch {
244+
cols, rows, _ := term.GetSize(int(os.Stdout.Fd()))
245+
if rows > 0 && cols > 0 {
246+
msg := fmt.Sprintf(`{"resize":{"rows":%d,"cols":%d}}`, rows, cols)
247+
wsMu.Lock()
248+
ws.WriteMessage(websocket.TextMessage, []byte(msg))
249+
wsMu.Unlock()
250+
}
251+
}
252+
}()
253+
219254
// Forward stdin to WebSocket
220255
go func() {
221256
buf := make([]byte, 32*1024)
@@ -228,7 +263,10 @@ func runExecInteractive(ws *websocket.Conn) (int, error) {
228263
return
229264
}
230265
if n > 0 {
231-
if err := ws.WriteMessage(websocket.BinaryMessage, buf[:n]); err != nil {
266+
wsMu.Lock()
267+
err := ws.WriteMessage(websocket.BinaryMessage, buf[:n])
268+
wsMu.Unlock()
269+
if err != nil {
232270
errCh <- fmt.Errorf("websocket write error: %w", err)
233271
return
234272
}

0 commit comments

Comments
 (0)