repos / zmx

session persistence for terminal processes
git clone https://github.com/neurosnap/zmx.git

commit
fcf31d6
parent
53dacdc
author
Eric Bower
date
2025-12-02 20:05:56 -0500 EST
fix: dont send ghostty snapshot on first attach

chore: clear main screen and set cursor to home on first attach
1 files changed,  +22, -14
M src/main.zig
+22, -14
 1@@ -83,6 +83,7 @@ const Daemon = struct {
 2     running: bool,
 3     pid: i32,
 4     command: ?[]const []const u8 = null,
 5+    has_pty_output: bool = false,
 6 
 7     pub fn deinit(self: *Daemon) void {
 8         self.clients.deinit(self.alloc);
 9@@ -410,6 +411,10 @@ fn attach(daemon: *Daemon) !void {
10 
11     _ = c.tcsetattr(posix.STDIN_FILENO, c.TCSANOW, &raw_termios);
12 
13+    // Clear screen and move cursor to home before attaching
14+    const clear_seq = "\x1b[2J\x1b[H";
15+    _ = try posix.write(posix.STDOUT_FILENO, clear_seq);
16+
17     try clientLoop(daemon.cfg, client_sock);
18 }
19 
20@@ -640,6 +645,7 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void {
21                 } else {
22                     // Feed PTY output to terminal emulator for state tracking
23                     try vt_stream.nextSlice(buf[0..n]);
24+                    daemon.has_pty_output = true;
25 
26                     // Broadcast data to all clients
27                     for (daemon.clients.items) |client| {
28@@ -704,21 +710,23 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void {
29                                 try term.resize(daemon.alloc, resize.cols, resize.rows);
30                                 std.log.debug("init resize rows={d} cols={d}", .{ resize.rows, resize.cols });
31 
32-                                // Send terminal state after resize
33-                                var builder: std.Io.Writer.Allocating = .init(daemon.alloc);
34-                                defer builder.deinit();
35-                                var term_formatter = ghostty_vt.formatter.TerminalFormatter.init(&term, .vt);
36-                                term_formatter.content = .{ .selection = null };
37-                                term_formatter.extra = .all;
38-                                term_formatter.format(&builder.writer) catch |err| {
39-                                    std.log.warn("failed to format terminal state err={s}", .{@errorName(err)});
40-                                };
41-                                const term_output = builder.writer.buffered();
42-                                if (term_output.len > 0) {
43-                                    ipc.appendMessage(daemon.alloc, &client.write_buf, .Output, term_output) catch |err| {
44-                                        std.log.warn("failed to buffer terminal state for client err={s}", .{@errorName(err)});
45+                                // Only send terminal state if there's been PTY output (skip on first attach)
46+                                if (daemon.has_pty_output) {
47+                                    var builder: std.Io.Writer.Allocating = .init(daemon.alloc);
48+                                    defer builder.deinit();
49+                                    var term_formatter = ghostty_vt.formatter.TerminalFormatter.init(&term, .vt);
50+                                    term_formatter.content = .{ .selection = null };
51+                                    term_formatter.extra = .all;
52+                                    term_formatter.format(&builder.writer) catch |err| {
53+                                        std.log.warn("failed to format terminal state err={s}", .{@errorName(err)});
54                                     };
55-                                    client.has_pending_output = true;
56+                                    const term_output = builder.writer.buffered();
57+                                    if (term_output.len > 0) {
58+                                        ipc.appendMessage(daemon.alloc, &client.write_buf, .Output, term_output) catch |err| {
59+                                            std.log.warn("failed to buffer terminal state for client err={s}", .{@errorName(err)});
60+                                        };
61+                                        client.has_pending_output = true;
62+                                    }
63                                 }
64                             }
65                         },