From c8bcda00ad54e4f81cc83ffe601d49703f4a7135 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Tue, 29 Sep 2026 10:18:23 -0300 Subject: ThemeSel in sections: acme and lapis with the current themes, helix and dark as legacy, a section per ported family The picker's headings come from colors.theme_groups instead of one split at native_theme_count, so acme (current, matched to plan9port acme's source) no longer sits under a legacy heading. Ring order follows the sections: the fifteen pardes palettes, acme, lapis, the faithful ports a family at a time, then helix and dark, then the imports. An imported theme whose name a port takes gets _helix, as zed's carry _zed. --- src/Output.zig | 5 ++-- src/colors.zig | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 82 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/Output.zig b/src/Output.zig index b32a40a2..553dcc95 100644 --- a/src/Output.zig +++ b/src/Output.zig @@ -522,10 +522,9 @@ pub fn openJumps(p: *Pardes, id: usize) !void { pub fn openThemes(p: *Pardes, id: usize) !void { var out: std.Io.Writer.Allocating = .init(p.gpa); errdefer out.deinit(); - try out.writer.writeAll("# Pardes themes\n"); for (pardes.themes, 0..) |t, i| { - if (i == pardes.native_theme_count) - try out.writer.writeAll("\n# Legacy and imported themes\n"); + if (pardes.colors.groupAt(i)) |title| + try out.writer.print("{s}# {s}\n", .{ if (i == 0) "" else "\n", title }); try out.writer.print(comptime config.Runtime.findAction(.theme).?.word ++ " {s}\n", .{t.name}); } const content = try out.toOwnedSlice(); diff --git a/src/colors.zig b/src/colors.zig index 8319a112..743a74d7 100644 --- a/src/colors.zig +++ b/src/colors.zig @@ -105,6 +105,25 @@ test "every native theme's separators stand off its page, and its focus tint off } } +test "every faithful port meets pardes's chrome floors in its own colours, with nothing left to derive" { + const first = comptime indexOfTheme("lapis") + 1; + const last = comptime indexOfTheme("helix"); + for (themes[first..last]) |*th| { + const chrome = ChromeTheme.fromTheme(th); + const page = th.bg.?; + // Written out already adjusted: the run-time lift and push leave + // the file's own values, so what the file says is what is drawn. + try std.testing.expectEqual(th.border.?, chrome.border); + try std.testing.expectEqual(th.tag_active_bg.?, chrome.tag_active_bg); + try std.testing.expect(contrast(th.border.?, page) >= separator_floor); + try std.testing.expect(contrast(th.tag_active_bg.?, th.tag_bg) >= focusTintFloor(th)); + try std.testing.expect(contrast(th.tag_fg, th.tag_bg) >= 4.5); + try std.testing.expect(contrast(th.tag_active_fg.?, th.tag_active_bg.?) >= 4.5); + try std.testing.expect(contrast(th.box_dim, th.tag_bg) >= 1.5); + try std.testing.expect(th.palette != null); + } +} + test "a derived separator or tint keeps the theme's own when it already stands off" { const forge = &themes[for (themes[0..native_count], 0..) |t, i| { if (std.mem.eql(u8, t.name, "forge")) break i; @@ -303,10 +322,13 @@ const curated = struct { pub const forge_black = @import("themes/forge_black.zig"); pub const forge_soft = @import("themes/forge_soft.zig"); pub const orchard_black = @import("themes/orchard_black.zig"); - pub const helix = @import("themes/helix.zig"); - pub const dark = @import("themes/dark.zig"); pub const acme = @import("themes/acme.zig"); pub const lapis = @import("themes/lapis.zig"); + // Faithful ports of well-known themes, a family at a time (src/themes/ + // .zig says where each value came from): `theme_groups`. + // The legacy pair last. + pub const helix = @import("themes/helix.zig"); + pub const dark = @import("themes/dark.zig"); }; fn fold(comptime C: type) [@typeInfo(C).@"struct".decls.len]Theme { @@ -338,10 +360,65 @@ fn fold(comptime C: type) [@typeInfo(C).@"struct".decls.len]Theme { } } -pub const themes = fold(curated) ++ fold(@import("generated_themes")); +/// The imported themes (tools/gen_themes.zig), less any name one of ours +/// takes: a faithful port (`dracula`) keeps the plain name, and helix's +/// looser copy of it is still there as `dracula_helix` (the zed ones already +/// carry `_zed`). +fn foldImports(comptime ours: anytype, comptime C: type) [@typeInfo(C).@"struct".decls.len]Theme { + comptime { + @setEvalBranchQuota(400000); + var out = fold(C); + for (&out) |*g| for (ours) |c| if (std.mem.eql(u8, c.name, g.name)) { + g.name = g.name ++ "_helix"; + }; + return out; + } +} + +pub const themes = fold(curated) ++ foldImports(fold(curated), @import("generated_themes")); /// The first `native_count` of `themes` are pardes's own; the rest imported. pub const native_count = @typeInfo(curated).@"struct".decls.len; +/// The picker's sections (`ThemeSel`), in ring order, each running from +/// the theme it names to the next section's; `NextColor` and `Theme` still +/// see one flat ring. The current pardes themes, then the faithful ports a +/// family at a time, then the legacy pair (`helix`, captured from an old +/// helix setup and the boot default before orchard; `dark`, the host +/// terminal's own ground), then everything imported. +pub const ThemeGroup = struct { title: []const u8, first: []const u8 }; +const theme_groups = [_]ThemeGroup{ + .{ .title = "Pardes themes", .first = "orchard" }, + .{ .title = "Legacy themes", .first = "helix" }, +}; + +fn indexOfTheme(comptime name: []const u8) usize { + return comptime blk: { + @setEvalBranchQuota(100000); + for (themes[0..native_count], 0..) |t, i| if (std.mem.eql(u8, t.name, name)) break :blk i; + @compileError("theme_groups names " ++ name ++ ", which is not one of ours"); + }; +} + +/// The heading of the picker section that starts at ring index `index`. +pub fn groupAt(index: usize) ?[]const u8 { + inline for (theme_groups) |g| if (index == comptime indexOfTheme(g.first)) return g.title; + if (index == native_count) return "Imported themes (helix, zed)"; + return null; +} + +test "the picker's sections run in ring order and cover it" { + var last: usize = 0; + inline for (theme_groups, 0..) |g, n| { + const at = comptime indexOfTheme(g.first); + if (n == 0) try std.testing.expectEqual(@as(usize, 0), at) else try std.testing.expect(at > last); + last = at; + } + try std.testing.expectEqualStrings("Pardes themes", groupAt(0).?); + // acme is a current theme, not a legacy one. + try std.testing.expect(indexOfTheme("acme") < indexOfTheme("helix")); + try std.testing.expect(groupAt(native_count) != null); +} + /// Original and classic-inspired Pardes palettes precede retained imports. pub const native_theme_count = 15; -- cgit v1.3