diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-15 13:33:54 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-09-15 17:24:42 -0300 |
| commit | 1d19d0ec24b924d1d2ccf2cab25199c6e6865fd7 (patch) | |
| tree | 27ba58d9d0cd8eed3adf808b35242aed7ac28fa3 /src/pardes.zig | |
| parent | 2bdde8c5ec1049ea0f575c6f9165ed4cef00ea50 (diff) | |
| download | pardes-1d19d0ec24b924d1d2ccf2cab25199c6e6865fd7.tar.gz pardes-1d19d0ec24b924d1d2ccf2cab25199c6e6865fd7.zip | |
Tint pane tag filenames with an optional theme color
Diffstat (limited to 'src/pardes.zig')
| -rw-r--r-- | src/pardes.zig | 39 |
1 files changed, 39 insertions, 0 deletions
diff --git a/src/pardes.zig b/src/pardes.zig index 537b212a..3c0205d2 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -2894,6 +2894,8 @@ pub const Theme = struct { // editor palettes keep working; ChromeTheme resolves their legacy defaults. tag_active_bg: ?[3]u8 = null, tag_active_fg: ?[3]u8 = null, + /// Quiet filename/output-name accent; directories and commands keep tag ink. + tag_name_fg: ?[3]u8 = null, border: ?[3]u8 = null, search_bg: ?[3]u8 = null, search_fg: ?[3]u8 = null, @@ -2987,6 +2989,9 @@ test "native themes keep text and UI roles readable on their own surfaces" { try std.testing.expect(themeContrast(th.comment, th.bg.?) >= themeContrast(th.lineno, th.bg.?) + 2); try std.testing.expect(themeContrast(th.tag_fg, th.tag_bg) >= minimum); try std.testing.expect(themeContrast(chrome.tag_active_fg, chrome.tag_active_bg) >= minimum); + try std.testing.expect(th.tag_name_fg != null); + try std.testing.expect(themeContrast(chrome.tag_name_fg, th.tag_bg) >= minimum); + try std.testing.expect(themeContrast(chrome.tag_active_name_fg, chrome.tag_active_bg) >= minimum); // Focus stays on the same tonal surface, rather than inverting a // whole command strip. Text remains readable in both states. try std.testing.expect(themeContrast(chrome.tag_active_bg, th.tag_bg) <= 1.6); @@ -3071,9 +3076,12 @@ test "legacy ThemeFile documents inherit Pardes UI roles without new fields" { const parsed = try std.zon.parse.fromSliceAlloc(Theme, std.testing.allocator, source, null, .{}); defer std.zon.parse.free(std.testing.allocator, parsed); try std.testing.expectEqual(@as(?[3]u8, null), parsed.tag_active_bg); + try std.testing.expectEqual(@as(?[3]u8, null), parsed.tag_name_fg); try std.testing.expectEqual(@as(?[3]u8, null), parsed.search_bg); const chrome = ChromeTheme.fromTheme(&parsed); try std.testing.expectEqual(parsed.tag_bg, chrome.tag_active_bg); + try std.testing.expectEqual(parsed.tag_fg, chrome.tag_name_fg); + try std.testing.expectEqual(chrome.tag_active_fg, chrome.tag_active_name_fg); try std.testing.expectEqual(parsed.sel_bg, chrome.search_bg); try std.testing.expectEqual(parsed.fg.?, chrome.diagnostic_error); } @@ -3134,6 +3142,8 @@ pub const ChromeTheme = struct { tag_fg: [3]u8, tag_active_bg: [3]u8, tag_active_fg: [3]u8, + tag_name_fg: [3]u8, + tag_active_name_fg: [3]u8, border: [3]u8, search_bg: [3]u8, search_fg: [3]u8, @@ -3153,6 +3163,8 @@ pub const ChromeTheme = struct { .tag_fg = th.tag_fg, .tag_active_bg = th.tag_active_bg orelse th.tag_bg, .tag_active_fg = th.tag_active_fg orelse th.tag_fg, + .tag_name_fg = th.tag_name_fg orelse th.tag_fg, + .tag_active_name_fg = th.tag_name_fg orelse (th.tag_active_fg orelse th.tag_fg), .border = th.border orelse th.scroll_track, .search_bg = th.search_bg orelse th.sel_bg, .search_fg = th.search_fg orelse th.sel_fg, @@ -7090,6 +7102,22 @@ pub const Pardes = struct { return out; } + fn tagNameRange(pane: *const Pane, prefix_len: usize) ?struct { lo: usize, hi: usize } { + var start: usize = 0; + const name = if (pane.file) |file| + if (pane.tag_name_len) |len| pane.tag_name[0..len] else file.path + else if (pane.image) |state| blk: { + // Image tags put renderer controls before the path. + start = prefix_len -| state.path.len; + break :blk state.path; + } else if (comptime pdf_enabled) blk: { + break :blk if (pane.pdf) |state| state.path else return null; + } else return null; + const basename = if (std.mem.lastIndexOfScalar(u8, name, '/')) |slash| slash + 1 else 0; + if (basename == name.len) return null; + return .{ .lo = start + basename, .hi = start + name.len }; + } + pub fn seedTail(_: *Pardes, pane: *Pane) void { if (pane.tag_init) return; const tail = curTail(pane); @@ -13094,6 +13122,17 @@ pub const Pardes = struct { tag.len; const tag_start = @min(tag_cut, panes.File.rawAtDisplay(tag, pane.tag_scroll)); _ = s.print(tx, tag_y, tw, tag[tag_start..tag_cut], tag_style); + if (tagNameRange(pane, tag.len - curTail(pane).len)) |name| { + const lo = @max(name.lo, tag_start); + const hi = @min(name.hi, tag_cut); + if (lo < hi) { + var col = panes.File.rawDisplayCol(tag, lo) -| pane.tag_scroll; + const end = panes.File.rawDisplayCol(tag, hi) -| pane.tag_scroll; + const name_fg = if (active and p.settings.focus_tint) chrome.tag_active_name_fg else chrome.tag_name_fg; + while (col < end and col < tw) : (col += 1) + s.at(tx + @as(u16, @intCast(col)), tag_y).style.fg = .{ .rgb = name_fg }; + } + } if (p.look_hover_preview) |preview| { if (preview.pane == id and preview.serial == pane.serial) if (preview.sel) |sel| if (@min(sel.r0, sel.r1) < BOX_H) { const preview_bg = mix(page_rgb, mix(page_rgb, th.sel_bg)); |
