- commit
- 0639e71
- parent
- abfa487
- author
- Eric Bower
- date
- 2025-10-10 21:21:20 -0400 EDT
feat: terminal restore works!
2 files changed,
+55,
-17
+1,
-1
1@@ -123,7 +123,7 @@ fn writeCallback(
2
3 const ReadContext = struct {
4 ctx: *Context,
5- buffer: [4096]u8,
6+ buffer: [128 * 1024]u8, // 128KB to handle large scrollback messages
7 };
8
9 fn readCallback(
+54,
-16
1@@ -529,30 +529,50 @@ fn handleAttachSession(ctx: *ServerContext, client: *Client, session_name: []con
2 _ = try posix.write(client.fd, response);
3 }
4
5- // If reattaching, send the current terminal state after attach response
6- if (is_reattach) {
7- const snapshot = try renderTerminalSnapshot(session, ctx.allocator);
8- defer ctx.allocator.free(snapshot);
9+ // If reattaching, send the scrollback buffer (raw PTY output with colors)
10+ // Limit to last 64KB to avoid huge JSON messages
11+ if (is_reattach and session.buffer.items.len > 0) {
12+ std.debug.print("Sending scrollback buffer: {d} bytes total\n", .{session.buffer.items.len});
13
14- // Use Zig's JSON formatting to properly escape the snapshot
15+ const max_buffer_size = 64 * 1024;
16+ const buffer_start = if (session.buffer.items.len > max_buffer_size)
17+ session.buffer.items.len - max_buffer_size
18+ else
19+ 0;
20+ const buffer_slice = session.buffer.items[buffer_start..];
21+
22+ std.debug.print("Sending slice: {d} bytes (from offset {d})\n", .{ buffer_slice.len, buffer_start });
23+
24+ // Use Zig's JSON formatting to properly escape the buffer
25 var out: std.io.Writer.Allocating = .init(ctx.allocator);
26 defer out.deinit();
27
28 var json_writer: std.json.Stringify = .{ .writer = &out.writer };
29
30- try json_writer.beginObject();
31- try json_writer.objectField("type");
32- try json_writer.write("pty_out");
33- try json_writer.objectField("payload");
34- try json_writer.beginObject();
35- try json_writer.objectField("text");
36- try json_writer.write(snapshot);
37- try json_writer.endObject();
38- try json_writer.endObject();
39-
40- const response = try std.fmt.allocPrint(ctx.allocator, "{s}\n", .{out.written()});
41+ json_writer.beginObject() catch |err| {
42+ std.debug.print("JSON beginObject error: {s}\n", .{@errorName(err)});
43+ return;
44+ };
45+ json_writer.objectField("type") catch return;
46+ json_writer.write("pty_out") catch return;
47+ json_writer.objectField("payload") catch return;
48+ json_writer.beginObject() catch return;
49+ json_writer.objectField("text") catch return;
50+ json_writer.write(buffer_slice) catch |err| {
51+ std.debug.print("JSON write buffer error: {s}\n", .{@errorName(err)});
52+ return;
53+ };
54+ json_writer.endObject() catch return;
55+ json_writer.endObject() catch return;
56+
57+ const json_output = out.written();
58+ std.debug.print("JSON output length: {d} bytes\n", .{json_output.len});
59+ std.debug.print("JSON first 100 bytes: {s}\n", .{json_output[0..@min(100, json_output.len)]});
60+
61+ const response = try std.fmt.allocPrint(ctx.allocator, "{s}\n", .{json_output});
62 defer ctx.allocator.free(response);
63 _ = try posix.write(client.fd, response);
64+ std.debug.print("Sent scrollback buffer to client fd={d}\n", .{client.fd});
65 }
66 }
67
68@@ -718,6 +738,11 @@ fn readPtyCallback(
69 const data = read_buffer.slice[0..bytes_read];
70 std.debug.print("PTY output ({d} bytes)\n", .{bytes_read});
71
72+ // Store PTY output in buffer for session restore
73+ session.buffer.appendSlice(session.allocator, data) catch |err| {
74+ std.debug.print("Buffer append error: {s}\n", .{@errorName(err)});
75+ };
76+
77 // ALWAYS parse through libghostty-vt to maintain state
78 session.vt_stream.nextSlice(data) catch |err| {
79 std.debug.print("VT parse error: {s}\n", .{@errorName(err)});
80@@ -775,6 +800,19 @@ fn readPtyCallback(
81 );
82 return .disarm;
83 } else |err| {
84+ // WouldBlock is expected for non-blocking I/O
85+ if (err == error.WouldBlock) {
86+ stream.read(
87+ loop,
88+ completion,
89+ .{ .slice = &session.pty_read_buffer },
90+ PtyReadContext,
91+ pty_ctx,
92+ readPtyCallback,
93+ );
94+ return .disarm;
95+ }
96+
97 std.debug.print("PTY read error: {s}\n", .{@errorName(err)});
98 return .disarm;
99 }