summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authort <t@tjp.lol>2026-07-01 13:08:42 -0600
committert <t@tjp.lol>2026-07-01 13:10:30 -0600
commitf8c67fe9f10e734dce91241bb5f19323ac6708d8 (patch)
tree2e557626c085d0605b74783127c5307cec1cb0b0
parent5e76e4aa90e70245404c10085e809880635572a0 (diff)
Refine TUI status and tool block styling
-rw-r--r--src/tui_components.zig69
-rw-r--r--src/tui_theme.zig20
2 files changed, 63 insertions, 26 deletions
diff --git a/src/tui_components.zig b/src/tui_components.zig
index 837db0d..b00098a 100644
--- a/src/tui_components.zig
+++ b/src/tui_components.zig
@@ -2198,10 +2198,24 @@ pub const Thinking = struct {
fn renderImpl(ptr: *anyopaque, width: usize, alloc: std.mem.Allocator) anyerror![]const []const u8 {
_ = alloc;
const self: *Thinking = @ptrCast(@alignCast(ptr));
- // Thinking blocks: dimmed text, no bg, with margin/indent spacing.
- const dim = theme.default.fg(.dim);
const plain = theme.default.fg(.assistant);
- return renderBlockCached(&self.cache, self.buffer.items, plain, dim, width, 1, 0, self.alloc);
+ const header = theme.default.fg(.thinking_header);
+ const body = theme.default.fg(.thinking);
+
+ var out: std.ArrayList([]const u8) = .empty;
+ defer {
+ for (out.items) |l| self.alloc.free(l);
+ out.deinit(self.alloc);
+ }
+
+ const styled_header = try std.fmt.allocPrint(self.alloc, "{s}thinking{s}", .{ header.open(), theme.reset });
+ defer self.alloc.free(styled_header);
+ try renderBlock(&out, styled_header, plain, plain, width, 1, 0, self.alloc);
+ if (self.buffer.items.len != 0) {
+ try renderBlock(&out, self.buffer.items, plain, body, width, 1, 0, self.alloc);
+ }
+ try self.cache.store(out.items);
+ return cacheLines(&self.cache);
}
fn firstLineChangedImpl(ptr: *anyopaque) ?usize {
@@ -2258,25 +2272,21 @@ pub const CompactionSummary = struct {
_ = alloc;
const self: *CompactionSummary = @ptrCast(@alignCast(ptr));
const a = self.alloc;
- const dim = theme.default.fg(.dim);
- const plain_bg = theme.default.fg(.assistant); // no-op bg
-
- // Build body: header + wrapped summary text.
- var body: std.ArrayList(u8) = .empty;
- defer body.deinit(a);
- try body.appendSlice(a, "[context compacted]");
- if (self.buffer.items.len != 0) {
- try body.append(a, '\n');
- try body.appendSlice(a, self.buffer.items);
- }
+ const header = theme.default.fg(.compaction_header);
+ const dim = theme.default.fg(.compaction);
- // Render as a dim block with margin/indent spacing.
var out: std.ArrayList([]const u8) = .empty;
defer {
for (out.items) |l| a.free(l);
out.deinit(a);
}
- try renderBlock(&out, body.items, plain_bg, dim, width, 1, 0, a);
+
+ const styled_header = try std.fmt.allocPrint(a, "{s}[context compacted]{s}", .{ header.open(), theme.reset });
+ defer a.free(styled_header);
+ try renderBlock(&out, styled_header, theme.default.fg(.assistant), theme.default.fg(.assistant), width, 1, 0, a);
+ if (self.buffer.items.len != 0) {
+ try renderBlock(&out, self.buffer.items, theme.default.fg(.assistant), dim, width, 1, 0, a);
+ }
try self.cache.store(out.items);
return cacheLines(&self.cache);
}
@@ -2470,10 +2480,14 @@ pub const ToolUse = struct {
// null -> pending (dark blue-gray)
// true -> success (dark green)
// false -> error (dark red)
- const bg = theme.default.fg(.tool_pending_bg);
+ const bg = if (self.result_ok) |ok| switch (ok) {
+ true => theme.default.fg(.tool_success_bg),
+ false => theme.default.fg(.tool_error_bg),
+ } else theme.default.fg(.tool_pending_bg);
const header_fg = theme.default.fg(.tool_header);
- const dim_fg = theme.default.fg(.dim);
- const plain_fg = theme.default.fg(.assistant);
+ const pending_fg = theme.default.fg(.tool_pending);
+ const success_fg = theme.default.fg(.tool_success);
+ const error_fg = theme.default.fg(.tool_error);
// Padding within the block (1 col each side matches pi).
const pad: usize = 1;
@@ -2513,8 +2527,8 @@ pub const ToolUse = struct {
// -- Stage 1: name not yet known -----------------------------------
if (self.name == null) {
try lines.append(a, try ctx.blank());
- const vis = truncateToCols("tool (?)\xe2\x80\xa6", inner_w);
- try lines.append(a, try ctx.line(header_fg, vis));
+ const vis = truncateToCols("tool (?)…", inner_w);
+ try lines.append(a, try ctx.line(pending_fg, vis));
try lines.append(a, try ctx.blank());
try lines.append(a, try a.dupe(u8, ""));
try self.cache.store(lines.items);
@@ -2541,10 +2555,15 @@ pub const ToolUse = struct {
// Separator blank row inside the block.
try lines.append(a, try ctx.blank());
+ const result_fg = if (self.result_ok) |ok| switch (ok) {
+ true => success_fg,
+ false => error_fg,
+ } else pending_fg;
+
// -- Result region: `(…)` placeholder or output text.
if (self.output == null) {
- const vis = truncateToCols("(\xe2\x80\xa6)", inner_w);
- try lines.append(a, try ctx.line(dim_fg, vis));
+ const vis = truncateToCols("(…)", inner_w);
+ try lines.append(a, try ctx.line(result_fg, vis));
} else {
var out_lines: std.ArrayList([]const u8) = .empty;
defer out_lines.deinit(a);
@@ -2558,11 +2577,11 @@ pub const ToolUse = struct {
}
if (truncated) {
const vis = truncateToCols("\xe2\x80\xa6", inner_w);
- try lines.append(a, try ctx.line(dim_fg, vis));
+ try lines.append(a, try ctx.line(result_fg, vis));
}
for (out_lines.items[start..]) |oline| {
const vis = truncateStyledToCols(oline, inner_w);
- try lines.append(a, try ctx.line(plain_fg, vis));
+ try lines.append(a, try ctx.line(result_fg, vis));
}
}
diff --git a/src/tui_theme.zig b/src/tui_theme.zig
index 038deb5..db0ee25 100644
--- a/src/tui_theme.zig
+++ b/src/tui_theme.zig
@@ -37,6 +37,18 @@ pub const StyleName = enum {
tool,
/// Footer / chrome line.
footer,
+ /// Neutral tool-body text shown inside tool blocks.
+ tool_text,
+ /// Pending tool placeholder / secondary chrome.
+ tool_pending,
+ /// Success accent for tool status / glyphs.
+ tool_success,
+ /// Error accent for tool status / glyphs.
+ tool_error,
+ /// Compaction-summary accent/header.
+ compaction_header,
+ /// Thinking-block accent/header.
+ thinking_header,
/// The virtual cursor: reverse video block.
cursor,
/// Error / retry text.
@@ -127,7 +139,13 @@ fn styleFor(name: StyleName) Style {
.assistant => .{ .open_seq = "", .is_plain = true },
.user => .{ .open_seq = "\x1b[39m" },
.tool => .{ .open_seq = "\x1b[36m" },
- .footer => .{ .open_seq = "\x1b[2m" },
+ .footer => .{ .open_seq = "\x1b[2m\x1b[38;2;120;130;150m" },
+ .tool_text => .{ .open_seq = "\x1b[38;2;210;214;220m" },
+ .tool_pending => .{ .open_seq = "\x1b[38;2;150;160;185m" },
+ .tool_success => .{ .open_seq = "\x1b[38;2;110;215;140m" },
+ .tool_error => .{ .open_seq = "\x1b[38;2;255;140;140m" },
+ .compaction_header => .{ .open_seq = "\x1b[38;2;255;210;120m" },
+ .thinking_header => .{ .open_seq = "\x1b[38;2;180;150;255m" },
// Reverse video — the virtual cursor block.
.cursor => .{ .open_seq = "\x1b[7m" },
.err => .{ .open_seq = "\x1b[31m" },