Add panel hierarchy styling

This commit is contained in:
slhx agent
2026-06-21 22:17:15 +02:00
parent de3b5d436f
commit f00eddcb2b
2 changed files with 56 additions and 2 deletions
+52 -1
View File
@@ -417,7 +417,7 @@ pub const Client = struct {
defer allocator.free(path);
const header = try std.fmt.allocPrint(allocator, "panel {s}", .{path});
defer allocator.free(header);
try appendThemedRow(allocator, &out, header, self.viewport.width, ansi_text);
try appendThemedRow(allocator, &out, header, self.viewport.width, ansi_panel_header);
try out.append(allocator, '\n');
body_lines_used += 1;
@@ -2589,6 +2589,8 @@ const ansi_syntax_number = "\x1b[38;2;231;168;78;48;2;25;22;33m";
const ansi_diag_error = "\x1b[38;2;238;84;88;48;2;25;22;33m";
const ansi_diag_warning = "\x1b[38;2;231;168;78;48;2;25;22;33m";
const ansi_diag_info = "\x1b[38;2;121;155;224;48;2;25;22;33m";
const ansi_panel_header = "\x1b[38;2;25;22;33;48;2;116;196;171m";
const ansi_panel_section = "\x1b[38;2;235;119;84;48;2;25;22;33m";
const ansi_diag_hint = "\x1b[38;2;116;196;171;48;2;25;22;33m";
const ansi_success = "\x1b[38;2;116;196;171;48;2;25;22;33m";
const ansi_stale = "\x1b[38;2;103;93;122;48;2;25;22;33m";
@@ -2786,6 +2788,7 @@ fn panelRowStyle(row: []const u8) []const u8 {
if (startsAny(row, &.{ "info:", "diagnostic:info", "stdout:", "job:status:running", "terminal:status:running" })) return ansi_diag_info;
if (startsAny(row, &.{ "hint:", "diagnostic:hint" })) return ansi_diag_hint;
if (startsAny(row, &.{ "provider:", "lsp-provider:", "language-provider:" })) return ansi_provider;
if (startsAny(row, &.{ "lsp:hover:", "lsp:signature:", "lsp:param:", "pi_ask:", "pi_edit:", "pi_review:" })) return ansi_panel_section;
if (startsAny(row, &.{ "missing:", "conflict:", "stale:", "job_status:idle", "terminal_status:idle" })) return ansi_stale;
return ansi_text;
}
@@ -5609,3 +5612,51 @@ test "regular: appended panel rows carry semantic color identity" {
try appendPanelRow(std.testing.allocator, &out, "provider:zig:zls:available", 72);
try std.testing.expect(std.mem.indexOf(u8, out.items, ansi_provider) != null);
}
test "regular: panel hierarchy uses header and section styles" {
var client = try Client.init(std.testing.allocator, .{ .width = 56, .height = 8 });
defer client.deinit();
try client.handleInput(" ");
try client.handleInput("P");
const frame = try client.render(std.testing.allocator);
defer std.testing.allocator.free(frame);
try std.testing.expect(std.mem.indexOf(u8, frame, ansi_panel_header) != null);
try std.testing.expectEqualStrings(ansi_panel_section, panelRowStyle("pi_ask:explain_current_buffer"));
try std.testing.expect(std.mem.indexOf(u8, frame, "panel [pi]") != null);
try std.testing.expect(std.mem.indexOf(u8, frame, "pi_ask:explain_current_buffer") != null);
}
test "regular: hover and signature rows have readable panel hierarchy styles" {
var client = try Client.init(std.testing.allocator, .{ .width = 64, .height = 8 });
defer client.deinit();
const hover_json = "{\"result\":{\"contents\":{\"kind\":\"markdown\",\"value\":\"```zig\\nfn foo() void\\n```\\nsummary\"}}}";
var hover_line: std.ArrayList(u8) = .empty;
defer hover_line.deinit(std.testing.allocator);
try hover_line.appendSlice(std.testing.allocator, "lsp_hover ");
try hover_line.appendSlice(std.testing.allocator, hover_json);
try client.handleTraceLine(hover_line.items);
{
const frame = try client.render(std.testing.allocator);
defer std.testing.allocator.free(frame);
try std.testing.expect(std.mem.indexOf(u8, frame, ansi_panel_header) != null);
try std.testing.expect(std.mem.indexOf(u8, frame, "panel [lsp-hover]") != null);
}
var signature_client = try Client.init(std.testing.allocator, .{ .width = 64, .height = 8 });
defer signature_client.deinit();
const signature_rows = [_][]const u8{
"lsp:signature:foo(a:i32,b:bool)",
"lsp:param:1:b_second",
};
try signature_client.openListRows("lsp-signature", signature_rows[0..], .none);
{
const frame = try signature_client.render(std.testing.allocator);
defer std.testing.allocator.free(frame);
try std.testing.expect(std.mem.indexOf(u8, frame, ansi_panel_header) != null);
try std.testing.expect(std.mem.indexOf(u8, frame, "panel [lsp-signature]") != null);
try std.testing.expectEqualStrings(ansi_panel_section, panelRowStyle("lsp:signature:foo(a:i32,b:bool)"));
}
}
+4 -1
View File
@@ -111,6 +111,8 @@ def classify_sgr(params: str) -> str:
return "current-line"
if "48;2;235;119;84" in joined:
return "status"
if "48;2;116;196;171" in joined:
return "panel-header"
return ""
@@ -555,7 +557,7 @@ SCENARIOS = [
Scenario("multi-file-coding-loop", 72, 16, "repo-multifile", "attached-keyboard", "multi-file", "truecolor", "file", setup_multifile, (b"A", b"?", b"\x1b", b" ", b"w", b" ", b"o", b"r", b"e", b"p", b"o", b"/", b"a", b"l", b"p", b"h", b"a", b".", b"z", b"i", b"g", b"\r", b"A", b"!", b"\x1b", b" ", b"w", b" ", b"q"), None, ("alpha.zig", "beta", "", "mode:normal"), max_key_events=30, expect_files=(("alpha.zig", "alpha!\n"), ("beta.zig", "beta?"))),
Scenario("lsp-assisted-coding", 60, 12, "lsp-fixture", "attached-keyboard", "lsp", "truecolor", "file", setup_lsp_assist, (b" ", b"l", b"h", b" ", b"l", b"s", b" ", b"d", b"q", b" ", b"d", b"n", b" ", b"l", b"f", b" ", b"w", b" ", b"q"), "ZLS", ("[zls] add(lhs, rhs)", "[zls] add(lhs: i32, rhs: i32) active=rhs", "diag:fresh:zls", "format:zls:applied", "ZLS", "mode:normal"), max_key_events=19, prelude=LSP_ASSIST_PRELUDE),
Scenario("project-search-panel", 60, 12, "repo-search", "attached-keyboard", "project-search", "truecolor", "file", setup_project_search, (b" ", b"s", b"p", b"f", b"i", b"n", b"d", b"m", b"e", b"\r", b"q", b" ", b"q"), "main\n", ("panel [search]", "target.zig", "findme", "mode:normal"), max_key_events=13),
Scenario("file-and-pi-panels", 60, 12, "repo-multifile", "attached-keyboard", "panels", "truecolor", "file", setup_multifile, (b" ", b"f", b"q", b" ", b"P", b"q", b" ", b"q"), "beta\n", ("panel [files]", "alpha.zig", "panel [pi]", "pi_ask:explain_current_buffer", "mode:normal"), max_key_events=8),
Scenario("file-and-pi-panels", 60, 12, "repo-multifile", "attached-keyboard", "panels", "truecolor", "file", setup_multifile, (b" ", b"f", b"q", b" ", b"P", b"q", b" ", b"q"), "beta\n", ("panel [files]", "alpha.zig", "panel [pi]", "pi_ask:explain_current_buffer", "mode:normal"), required_attrs=("panel-header", "current-line"), max_key_events=8),
Scenario("job-cancel-output-panel", 60, 12, "job", "attached-keyboard", "job-output", "truecolor", "file", setup_empty, (b" ", b"t", b"x", b"q", b" ", b"q"), "", ("panel [job-output]", "job:profile:build", "job:status:cancelled:user", "mode:normal"), max_key_events=6),
Scenario("dirty-discard-shift-q", 52, 12, "empty", "ios-default-qwertz-space-path", "dirty-discard", "truecolor", "file", setup_empty, tuple(bytes([b]) for b in b" ps Q"), "", ("1│", "")),
Scenario("directory-panel-narrow", 52, 12, "directory", "ios-default-qwertz-space-path", "panel", "mono", "directory", setup_directory, PANEL_QUIT, None, ("file", "one.zig"), required_attrs=()),
@@ -649,6 +651,7 @@ def run_one(mim: Path, root_out: Path, scenario: Scenario) -> tuple[str, Path]:
("48;2;55;65;92", "selection"),
("48;2;20;18;26", "current-line"),
("48;2;235;119;84", "status"),
("48;2;116;196;171", "panel-header"),
("38;2;235;119;84", "syntax-keyword"),
("38;2;121;155;224", "syntax-builtin"),
("38;2;116;196;171", "syntax-string"),