diff --git a/src/agent/Spinner.zig b/src/agent/Spinner.zig index dfd789e85..5cf13cc30 100644 --- a/src/agent/Spinner.zig +++ b/src/agent/Spinner.zig @@ -19,7 +19,6 @@ const std = @import("std"); const lp = @import("lightpanda"); const log = lp.log; -const Terminal = @import("Terminal.zig"); const ansi = @import("ansi.zig"); const truncateUtf8 = @import("../string.zig").truncateUtf8; @@ -291,7 +290,7 @@ fn renderLocked(self: *Spinner) void { // (glyph, two spaces, `[`, `]`) around prefix+name+args. `\r` and // ANSI escapes are zero-width, so they don't count toward wrap. const decoration_cells: usize = 5 + prefix.len + name.len; - const cols: usize = Terminal.columns() orelse 80; + const cols: usize = columns() orelse 80; // Reserve one extra cell so the line is strictly less than `cols`: // auto-wrap (DECAWM) terminals advance past a row that exactly fills // the width. @@ -316,6 +315,21 @@ fn renderLocked(self: *Spinner) void { /// Returns the byte length of `bytes` that fits in `max_cells` cells, rounded /// down to a whole UTF-8 codepoint. Multi-cell glyphs (CJK, wide emoji) count /// as 1 — args are typically ASCII, so the approximation is good enough. +/// Current terminal width in columns, queried via TIOCGWINSZ on stderr. +/// Null when stderr isn't a tty, the ioctl fails, or the kernel reports 0 +/// (some pseudo-ttys leave the field unset). Cheap enough to call per render +/// frame; picks up resizes without SIGWINCH plumbing. +fn columns() ?u16 { + var ws: std.posix.winsize = undefined; + // bitcast via c_uint: on archs where `_IOR` sets the direction bit + // (MIPS/PPC/SPARC), `IOCGWINSZ` exceeds i32 range, so a plain @intCast + // panics; the bitcast preserves the bit pattern. + const req: c_int = @bitCast(@as(c_uint, std.posix.T.IOCGWINSZ)); + const rc = std.c.ioctl(std.posix.STDERR_FILENO, req, &ws); + if (rc != 0 or ws.col == 0) return null; + return ws.col; +} + fn truncToCells(bytes: []const u8, max_cells: usize) usize { var cells: usize = 0; var i: usize = 0; diff --git a/src/agent/Terminal.zig b/src/agent/Terminal.zig index f39c3802d..36ca4c76f 100644 --- a/src/agent/Terminal.zig +++ b/src/agent/Terminal.zig @@ -948,21 +948,6 @@ fn logSink(bytes: []const u8) void { _ = std.posix.write(std.posix.STDERR_FILENO, bytes) catch {}; } -/// Current terminal width in columns, queried via TIOCGWINSZ on stderr. -/// Null when stderr isn't a tty, the ioctl fails, or the kernel reports 0 -/// (some pseudo-ttys leave the field unset). Cheap enough to call per render -/// frame; picks up resizes without SIGWINCH plumbing. -pub fn columns() ?u16 { - var ws: std.posix.winsize = undefined; - // bitcast via c_uint: on archs where `_IOR` sets the direction bit - // (MIPS/PPC/SPARC), `IOCGWINSZ` exceeds i32 range, so a plain @intCast - // panics; the bitcast preserves the bit pattern. - const req: c_int = @bitCast(@as(c_uint, std.posix.T.IOCGWINSZ)); - const rc = std.c.ioctl(std.posix.STDERR_FILENO, req, &ws); - if (rc != 0 or ws.col == 0) return null; - return ws.col; -} - /// Erase the frame after an empty submit. The bars collapse on submit, leaving /// the spacing and prompt lines with the cursor one line below; move up two, /// clear to end of screen.