diff options
| -rw-r--r-- | docs/config.md | 9 | ||||
| -rw-r--r-- | docs/themes.md | 3 | ||||
| -rw-r--r-- | src/pardes.zig | 73 | ||||
| -rw-r--r-- | src/themes/atelier.zig | 3 | ||||
| -rw-r--r-- | src/themes/clay.zig | 3 | ||||
| -rw-r--r-- | src/themes/daybreak.zig | 3 | ||||
| -rw-r--r-- | src/themes/dusk.zig | 3 | ||||
| -rw-r--r-- | src/themes/forge.zig | 3 | ||||
| -rw-r--r-- | src/themes/forge_black.zig | 3 | ||||
| -rw-r--r-- | src/themes/forge_soft.zig | 3 | ||||
| -rw-r--r-- | src/themes/harvest.zig | 3 | ||||
| -rw-r--r-- | src/themes/ink.zig | 3 | ||||
| -rw-r--r-- | src/themes/lagoon.zig | 3 | ||||
| -rw-r--r-- | src/themes/orchard.zig | 3 | ||||
| -rw-r--r-- | src/themes/orchard_black.zig | 3 | ||||
| -rw-r--r-- | src/themes/paper.zig | 3 | ||||
| -rw-r--r-- | src/themes/solarium.zig | 3 | ||||
| -rw-r--r-- | src/themes/spectrum.zig | 1 | ||||
| -rw-r--r-- | test/panes.zig | 1 |
19 files changed, 109 insertions, 20 deletions
diff --git a/docs/config.md b/docs/config.md index 53c76087..5a7a358f 100644 --- a/docs/config.md +++ b/docs/config.md @@ -268,7 +268,7 @@ name, thirteen required RGB roles (`sel_bg`, `sel_fg`, `tag_bg`, `tag_fg`, `box`, `box_dim`, `kw`, `str`, `num`, `comment`, `lineno`, `scroll_track`, `scroll_thumb`), nullable `bg`/`fg`, and either a 16-color RGB `palette` or `null`. Optional nullable RGB roles extend this format: -`tag_active_bg`, `tag_active_fg`, `tag_name_fg`, `border`, `lineno_active`, `search_bg`, `search_fg`, +`tag_active_bg`, `tag_active_fg`, `tag_name_fg`, `tag_active_name_fg`, `border`, `lineno_active`, `search_bg`, `search_fg`, `diagnostic_error`, `diagnostic_warning`, `diagnostic_info`, and `diagnostic_hint`. Missing new roles use backward-compatible defaults, so previously exported files remain valid. [Theme customization](themes.md) @@ -277,8 +277,11 @@ hex literals are accepted. The original fields remain required; there is no inheritance or partial override layer. `tag_name_fg` gives the filename at the end of a pane's path a separate -foreground on both normal and active tags. When omitted or `null`, it uses -the corresponding tag foreground. Directory text and tag commands retain +foreground. `tag_active_name_fg` can adjust that tint for active tags; it falls +back to `tag_name_fg`. When both are omitted or `null`, the filename uses +the corresponding tag foreground. All canonical themes use a different hue +at similar perceived brightness to the surrounding text in both states. +Directory text and tag commands retain their regular colors; selecting filename text uses the selection colors. Terminal tags use this color for `Tty`, which comes immediately after the path. diff --git a/docs/themes.md b/docs/themes.md index 5c7da44b..01965de5 100644 --- a/docs/themes.md +++ b/docs/themes.md @@ -102,7 +102,8 @@ so existing exported themes remain valid. | Optional role | Purpose | When absent | | --- | --- | --- | | `tag_active_bg`, `tag_active_fg` | Focused pane tag surface and text | `tag_bg`, `tag_fg` | -| `tag_name_fg` | Filename at the end of the pane's path, on normal and active tags | The corresponding normal or active tag foreground | +| `tag_name_fg` | Filename or terminal `Tty` command tint | The corresponding normal or active tag foreground | +| `tag_active_name_fg` | Filename or terminal `Tty` tint in active tags | `tag_name_fg`, then the active tag foreground | | `border` | Quiet separators | `scroll_track` | | `lineno_active` | Restrained current line number foreground | `lineno` | | `search_bg`, `search_fg` | Search matches, independent of selection | `sel_bg`, `sel_fg` | diff --git a/src/pardes.zig b/src/pardes.zig index b6d6ed8f..51c9c3db 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -2939,8 +2939,10 @@ 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. + /// Filename/output-name accent, also used by the Tty command. tag_name_fg: ?[3]u8 = null, + /// Active tint; absence preserves the shared name tint or active tag ink. + tag_active_name_fg: ?[3]u8 = null, border: ?[3]u8 = null, search_bg: ?[3]u8 = null, search_fg: ?[3]u8 = null, @@ -3049,6 +3051,51 @@ test "native themes keep text and UI roles readable on their own surfaces" { } } +test "native filename tints change hue without changing perceived lightness" { + const Perceptual = struct { + fn lab(rgb: [3]u8) [3]f64 { + var linear: [3]f64 = undefined; + for (rgb, 0..) |channel, i| { + const value = @as(f64, @floatFromInt(channel)) / 255; + linear[i] = if (value <= 0.04045) value / 12.92 else std.math.pow(f64, (value + 0.055) / 1.055, 2.4); + } + const r = linear[0]; + const g = linear[1]; + const b = linear[2]; + const l = std.math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b); + const m = std.math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b); + const n = std.math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b); + return .{ + 0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * n, + 1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * n, + 0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * n, + }; + } + }; + for (themes[0..native_theme_count]) |th| { + try std.testing.expect(th.tag_name_fg != null and th.tag_active_name_fg != null); + const chrome = ChromeTheme.fromTheme(&th); + const pairs = [2][2][3]u8{ + .{ chrome.tag_fg, chrome.tag_name_fg }, + .{ chrome.tag_active_fg, chrome.tag_active_name_fg }, + }; + for (pairs) |pair| { + const base = Perceptual.lab(pair[0]); + const name = Perceptual.lab(pair[1]); + // Half a lightness point allows 8-bit quantization without a + // basename becoming visibly brighter or darker than its tag. + try std.testing.expectApproxEqAbs(base[0], name[0], 0.005); + const base_chroma = @sqrt(base[1] * base[1] + base[2] * base[2]); + const name_chroma = @sqrt(name[1] * name[1] + name[2] * name[2]); + try std.testing.expect(base_chroma > 0.005 and name_chroma > 0.02); + // A normalized dot product <= cos(60 degrees) means a clear + // hue difference, independent of saturation or lightness. + const hue_cosine = (base[1] * name[1] + base[2] * name[2]) / (base_chroma * name_chroma); + try std.testing.expect(hue_cosine <= 0.5); + } + } +} + test "native dark palettes choose near-black or deliberately soft contrast" { for (themes[0..native_theme_count]) |th| { if (themeContrast(th.bg.?, .{ 0, 0, 0 }) > 10) continue; // light paper @@ -3122,6 +3169,7 @@ test "legacy ThemeFile documents inherit Pardes UI roles without new fields" { 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.tag_active_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); @@ -3131,6 +3179,27 @@ test "legacy ThemeFile documents inherit Pardes UI roles without new fields" { try std.testing.expectEqual(parsed.fg.?, chrome.diagnostic_error); } +test "custom filename tints preserve optional active fallback precedence" { + var theme = themes[0]; + theme.tag_name_fg = null; + theme.tag_active_name_fg = null; + var chrome = ChromeTheme.fromTheme(&theme); + try std.testing.expectEqual(theme.tag_fg, chrome.tag_name_fg); + try std.testing.expectEqual(theme.tag_active_fg.?, chrome.tag_active_name_fg); + theme.tag_name_fg = .{ 120, 130, 140 }; + chrome = ChromeTheme.fromTheme(&theme); + try std.testing.expectEqual(theme.tag_name_fg.?, chrome.tag_name_fg); + try std.testing.expectEqual(theme.tag_name_fg.?, chrome.tag_active_name_fg); + theme.tag_active_name_fg = .{ 150, 160, 170 }; + chrome = ChromeTheme.fromTheme(&theme); + try std.testing.expectEqual(theme.tag_name_fg.?, chrome.tag_name_fg); + try std.testing.expectEqual(theme.tag_active_name_fg.?, chrome.tag_active_name_fg); + theme.tag_name_fg = null; + chrome = ChromeTheme.fromTheme(&theme); + try std.testing.expectEqual(theme.tag_fg, chrome.tag_name_fg); + try std.testing.expectEqual(theme.tag_active_name_fg.?, chrome.tag_active_name_fg); +} + comptime { if (themes.len > std.math.maxInt(u16) + 1) @compileError("runtime config theme index no longer fits u16"); @@ -3209,7 +3278,7 @@ pub const ChromeTheme = struct { .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), + .tag_active_name_fg = th.tag_active_name_fg orelse (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, diff --git a/src/themes/atelier.zig b/src/themes/atelier.zig index 01e4ef4b..bd0e9639 100644 --- a/src/themes/atelier.zig +++ b/src/themes/atelier.zig @@ -7,7 +7,8 @@ pub const theme = .{ .fg = .{ 0x29, 0x3f, 0x35 }, .tag_bg = .{ 0xe4, 0xf0, 0xed }, .tag_fg = .{ 0x3d, 0x60, 0x55 }, - .tag_name_fg = .{ 0x72, 0x4b, 0x78 }, + .tag_name_fg = .{ 0x6e, 0x47, 0x74 }, + .tag_active_name_fg = .{ 0x65, 0x3f, 0x6b }, .tag_active_bg = .{ 0xd3, 0xe7, 0xdf }, .tag_active_fg = .{ 0x35, 0x58, 0x4d }, .box = .{ 0x3e, 0x7a, 0x68 }, diff --git a/src/themes/clay.zig b/src/themes/clay.zig index b57b6bfb..3c729dd8 100644 --- a/src/themes/clay.zig +++ b/src/themes/clay.zig @@ -8,7 +8,8 @@ pub const theme = .{ .fg = .{ 0xf3, 0xe5, 0xbc }, .tag_bg = .{ 0x34, 0x3f, 0x36 }, .tag_fg = .{ 0xd7, 0xd6, 0xb6 }, - .tag_name_fg = .{ 0xa9, 0xdf, 0xdf }, + .tag_name_fg = .{ 0xaa, 0xe0, 0xe0 }, + .tag_active_name_fg = .{ 0xb5, 0xec, 0xec }, .tag_active_bg = .{ 0x42, 0x4f, 0x40 }, .tag_active_fg = .{ 0xe3, 0xe2, 0xc2 }, .box = .{ 0x8e, 0xc0, 0x7c }, diff --git a/src/themes/daybreak.zig b/src/themes/daybreak.zig index 518d21b6..eca2e7fc 100644 --- a/src/themes/daybreak.zig +++ b/src/themes/daybreak.zig @@ -7,7 +7,8 @@ pub const theme = .{ .fg = .{ 0x14, 0x2d, 0x40 }, .tag_bg = .{ 0xed, 0xf2, 0xf7 }, .tag_fg = .{ 0x29, 0x46, 0x5e }, - .tag_name_fg = .{ 0x61, 0x33, 0x6e }, + .tag_name_fg = .{ 0x5a, 0x2d, 0x67 }, + .tag_active_name_fg = .{ 0x52, 0x25, 0x5f }, .tag_active_bg = .{ 0xdf, 0xea, 0xf3 }, .tag_active_fg = .{ 0x21, 0x3e, 0x56 }, .box = .{ 0x07, 0x5f, 0x91 }, diff --git a/src/themes/dusk.zig b/src/themes/dusk.zig index 1c579a40..ac3e4301 100644 --- a/src/themes/dusk.zig +++ b/src/themes/dusk.zig @@ -7,7 +7,8 @@ pub const theme = .{ .fg = .{ 0xc3, 0xb6, 0xc1 }, .tag_bg = .{ 0x2c, 0x26, 0x30 }, .tag_fg = .{ 0xc6, 0xb6, 0xc5 }, - .tag_name_fg = .{ 0x94, 0xc7, 0xb6 }, + .tag_name_fg = .{ 0x96, 0xc9, 0xb8 }, + .tag_active_name_fg = .{ 0xa1, 0xd5, 0xc4 }, .tag_active_bg = .{ 0x3b, 0x2f, 0x3b }, .tag_active_fg = .{ 0xd2, 0xc2, 0xd1 }, .box = .{ 0xe4, 0xb3, 0x9b }, diff --git a/src/themes/forge.zig b/src/themes/forge.zig index ea871274..83273329 100644 --- a/src/themes/forge.zig +++ b/src/themes/forge.zig @@ -8,7 +8,8 @@ pub const theme = .{ .fg = .{ 0xe5, 0xe5, 0xe5 }, .tag_bg = .{ 0x22, 0x25, 0x2b }, .tag_fg = .{ 0xc5, 0xc9, 0xd2 }, - .tag_name_fg = .{ 0xed, 0xc0, 0x9c }, + .tag_name_fg = .{ 0xec, 0xbf, 0x9b }, + .tag_active_name_fg = .{ 0xf8, 0xcb, 0xa7 }, .tag_active_bg = .{ 0x2d, 0x34, 0x40 }, .tag_active_fg = .{ 0xd1, 0xd5, 0xde }, .box = .{ 0xa5, 0xc4, 0xee }, diff --git a/src/themes/forge_black.zig b/src/themes/forge_black.zig index 1002b0d1..b9847472 100644 --- a/src/themes/forge_black.zig +++ b/src/themes/forge_black.zig @@ -8,7 +8,8 @@ pub const theme = .{ .fg = .{ 0xf6, 0xf6, 0xf6 }, .tag_bg = .{ 0x22, 0x25, 0x2b }, .tag_fg = .{ 0xc5, 0xc9, 0xd2 }, - .tag_name_fg = .{ 0xed, 0xc0, 0x9c }, + .tag_name_fg = .{ 0xec, 0xbf, 0x9b }, + .tag_active_name_fg = .{ 0xf8, 0xcb, 0xa7 }, .tag_active_bg = .{ 0x2d, 0x34, 0x40 }, .tag_active_fg = .{ 0xd1, 0xd5, 0xde }, .box = .{ 0xa5, 0xc4, 0xee }, diff --git a/src/themes/forge_soft.zig b/src/themes/forge_soft.zig index 055709a7..b003e559 100644 --- a/src/themes/forge_soft.zig +++ b/src/themes/forge_soft.zig @@ -8,7 +8,8 @@ pub const theme = .{ .fg = .{ 0xbf, 0xc3, 0xcc }, .tag_bg = .{ 0x2a, 0x2d, 0x34 }, .tag_fg = .{ 0xc5, 0xc9, 0xd2 }, - .tag_name_fg = .{ 0xed, 0xc0, 0x9c }, + .tag_name_fg = .{ 0xec, 0xbf, 0x9b }, + .tag_active_name_fg = .{ 0xf8, 0xcb, 0xa7 }, .tag_active_bg = .{ 0x37, 0x3e, 0x49 }, .tag_active_fg = .{ 0xd1, 0xd5, 0xde }, .box = .{ 0xa5, 0xc4, 0xee }, diff --git a/src/themes/harvest.zig b/src/themes/harvest.zig index 85f8e5b4..a2b4d619 100644 --- a/src/themes/harvest.zig +++ b/src/themes/harvest.zig @@ -8,7 +8,8 @@ pub const theme = .{ .fg = .{ 0xf3, 0xf2, 0xcc }, .tag_bg = .{ 0x34, 0x3b, 0x2e }, .tag_fg = .{ 0xd5, 0xd8, 0xb8 }, - .tag_name_fg = .{ 0xbb, 0xdc, 0xec }, + .tag_name_fg = .{ 0xba, 0xdb, 0xeb }, + .tag_active_name_fg = .{ 0xc5, 0xe7, 0xf7 }, .tag_active_bg = .{ 0x42, 0x4d, 0x38 }, .tag_active_fg = .{ 0xe1, 0xe4, 0xc4 }, .box = .{ 0xcf, 0xba, 0x8b }, diff --git a/src/themes/ink.zig b/src/themes/ink.zig index 03c3e41b..352fd095 100644 --- a/src/themes/ink.zig +++ b/src/themes/ink.zig @@ -7,7 +7,8 @@ pub const theme = .{ .fg = .{ 0xf3, 0xf7, 0xfc }, .tag_bg = .{ 0x15, 0x21, 0x2c }, .tag_fg = .{ 0xd5, 0xe4, 0xf1 }, - .tag_name_fg = .{ 0xf3, 0xde, 0xaa }, + .tag_name_fg = .{ 0xf5, 0xe0, 0xac }, + .tag_active_name_fg = .{ 0xff, 0xea, 0xb8 }, .tag_active_bg = .{ 0x22, 0x33, 0x42 }, .tag_active_fg = .{ 0xe1, 0xee, 0xf9 }, .box = .{ 0x86, 0xd8, 0xff }, diff --git a/src/themes/lagoon.zig b/src/themes/lagoon.zig index 49a5d28a..b19597e8 100644 --- a/src/themes/lagoon.zig +++ b/src/themes/lagoon.zig @@ -8,7 +8,8 @@ pub const theme = .{ .fg = .{ 0xba, 0xc9, 0xce }, .tag_bg = .{ 0x29, 0x44, 0x3f }, .tag_fg = .{ 0xbb, 0xd7, 0xcb }, - .tag_name_fg = .{ 0xe6, 0xc4, 0xee }, + .tag_name_fg = .{ 0xe4, 0xc2, 0xec }, + .tag_active_name_fg = .{ 0xf0, 0xce, 0xf8 }, .tag_active_bg = .{ 0x34, 0x57, 0x4d }, .tag_active_fg = .{ 0xc7, 0xe3, 0xd7 }, .box = .{ 0x89, 0xc7, 0xb4 }, diff --git a/src/themes/orchard.zig b/src/themes/orchard.zig index 389f9b4d..3e87948c 100644 --- a/src/themes/orchard.zig +++ b/src/themes/orchard.zig @@ -7,7 +7,8 @@ pub const theme = .{ .fg = .{ 0xe5, 0xee, 0xe5 }, .tag_bg = .{ 0x1d, 0x28, 0x24 }, .tag_fg = .{ 0xb2, 0xc3, 0xb8 }, - .tag_name_fg = .{ 0xd4, 0xb1, 0xda }, + .tag_name_fg = .{ 0xd3, 0xb0, 0xd9 }, + .tag_active_name_fg = .{ 0xdf, 0xbc, 0xe5 }, .tag_active_bg = .{ 0x26, 0x36, 0x2d }, .tag_active_fg = .{ 0xbe, 0xcf, 0xc4 }, .box = .{ 0xad, 0xcc, 0x91 }, diff --git a/src/themes/orchard_black.zig b/src/themes/orchard_black.zig index fb483e28..8440d9ac 100644 --- a/src/themes/orchard_black.zig +++ b/src/themes/orchard_black.zig @@ -7,7 +7,8 @@ pub const theme = .{ .fg = .{ 0xf2, 0xf8, 0xef }, .tag_bg = .{ 0x1d, 0x28, 0x24 }, .tag_fg = .{ 0xb2, 0xc3, 0xb8 }, - .tag_name_fg = .{ 0xd4, 0xb1, 0xda }, + .tag_name_fg = .{ 0xd3, 0xb0, 0xd9 }, + .tag_active_name_fg = .{ 0xdf, 0xbc, 0xe5 }, .tag_active_bg = .{ 0x26, 0x36, 0x2d }, .tag_active_fg = .{ 0xbe, 0xcf, 0xc4 }, .box = .{ 0xad, 0xcc, 0x91 }, diff --git a/src/themes/paper.zig b/src/themes/paper.zig index 1caf8e58..732d3d0a 100644 --- a/src/themes/paper.zig +++ b/src/themes/paper.zig @@ -7,7 +7,8 @@ pub const theme = .{ .fg = .{ 0x30, 0x3b, 0x3b }, .tag_bg = .{ 0xe8, 0xea, 0xe3 }, .tag_fg = .{ 0x52, 0x62, 0x5a }, - .tag_name_fg = .{ 0x76, 0x50, 0x87 }, + .tag_name_fg = .{ 0x72, 0x4c, 0x83 }, + .tag_active_name_fg = .{ 0x69, 0x44, 0x7a }, .tag_active_bg = .{ 0xdc, 0xe5, 0xdc }, .tag_active_fg = .{ 0x4a, 0x5a, 0x52 }, .box = .{ 0x3c, 0x6b, 0x56 }, diff --git a/src/themes/solarium.zig b/src/themes/solarium.zig index c3498880..c7ee2bdb 100644 --- a/src/themes/solarium.zig +++ b/src/themes/solarium.zig @@ -8,7 +8,8 @@ pub const theme = .{ .fg = .{ 0xa7, 0xb7, 0xae }, .tag_bg = .{ 0x12, 0x3f, 0x43 }, .tag_fg = .{ 0xc7, 0xd4, 0xbd }, - .tag_name_fg = .{ 0xed, 0xc5, 0xe4 }, + .tag_name_fg = .{ 0xe9, 0xc1, 0xe0 }, + .tag_active_name_fg = .{ 0xf5, 0xcd, 0xec }, .tag_active_bg = .{ 0x21, 0x4e, 0x50 }, .tag_active_fg = .{ 0xd3, 0xe0, 0xc9 }, .box = .{ 0xd5, 0xbc, 0x72 }, diff --git a/src/themes/spectrum.zig b/src/themes/spectrum.zig index 3b7511fa..664f4d0f 100644 --- a/src/themes/spectrum.zig +++ b/src/themes/spectrum.zig @@ -9,6 +9,7 @@ pub const theme = .{ .tag_bg = .{ 0x3b, 0x38, 0x30 }, .tag_fg = .{ 0xdd, 0xd8, 0xbb }, .tag_name_fg = .{ 0xc5, 0xd7, 0xff }, + .tag_active_name_fg = .{ 0xd7, 0xe3, 0xff }, .tag_active_bg = .{ 0x4d, 0x47, 0x36 }, .tag_active_fg = .{ 0xe9, 0xe4, 0xc7 }, .box = .{ 0xff, 0xd8, 0x66 }, diff --git a/test/panes.zig b/test/panes.zig index 4ed8c1e1..72d83ce9 100644 --- a/test/panes.zig +++ b/test/panes.zig @@ -757,6 +757,7 @@ const TagNameTintTests = struct { fn configure(p: *Pardes) void { var theme = p.theme().*; theme.tag_name_fg = name_fg; + theme.tag_active_name_fg = null; theme.tag_fg = tag_fg; p.custom_theme = theme; p.chrome_animation.snap(pardes.ChromeTheme.fromTheme(p.theme())); |
