summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Output.zig5
-rw-r--r--src/animation.zig62
-rw-r--r--src/colors.zig159
-rw-r--r--src/config.zig50
-rw-r--r--src/detached/client.zig33
-rw-r--r--src/detached/server.zig36
-rw-r--r--src/detached/wire.zig77
-rw-r--r--src/draw.zig256
-rw-r--r--src/file_watch.zig13
-rw-r--r--src/gui/Post.zig234
-rw-r--r--src/gui/gui.zig653
-rw-r--r--src/look.zig33
-rw-r--r--src/main.zig1
-rw-r--r--src/pardes.zig7
-rw-r--r--src/shader_build.zig402
-rw-r--r--src/surface.zig12
-rw-r--r--src/themes/alucard.zig76
-rw-r--r--src/themes/ayu_dark.zig105
-rw-r--r--src/themes/ayu_light.zig104
-rw-r--r--src/themes/ayu_mirage.zig104
-rw-r--r--src/themes/catppuccin_frappe.zig105
-rw-r--r--src/themes/catppuccin_latte.zig105
-rw-r--r--src/themes/catppuccin_macchiato.zig105
-rw-r--r--src/themes/catppuccin_mocha.zig105
-rw-r--r--src/themes/dark_plus.zig85
-rw-r--r--src/themes/doom_one.zig104
-rw-r--r--src/themes/doom_one_light.zig106
-rw-r--r--src/themes/dracula.zig79
-rw-r--r--src/themes/dracula_soft.zig79
-rw-r--r--src/themes/duckbones.zig92
-rw-r--r--src/themes/forestbones_dark.zig92
-rw-r--r--src/themes/forestbones_light.zig94
-rw-r--r--src/themes/kanagawabones.zig93
-rw-r--r--src/themes/monokai.zig84
-rw-r--r--src/themes/monokai_pro.zig85
-rw-r--r--src/themes/monokai_pro_classic.zig85
-rw-r--r--src/themes/monokai_pro_light.zig87
-rw-r--r--src/themes/monokai_pro_light_sun.zig87
-rw-r--r--src/themes/monokai_pro_machine.zig86
-rw-r--r--src/themes/monokai_pro_octagon.zig86
-rw-r--r--src/themes/monokai_pro_ristretto.zig86
-rw-r--r--src/themes/monokai_pro_spectrum.zig85
-rw-r--r--src/themes/neobones_dark.zig92
-rw-r--r--src/themes/neobones_light.zig92
-rw-r--r--src/themes/nord.zig124
-rw-r--r--src/themes/nordbones.zig95
-rw-r--r--src/themes/rose_pine.zig104
-rw-r--r--src/themes/rose_pine_dawn.zig104
-rw-r--r--src/themes/rose_pine_moon.zig104
-rw-r--r--src/themes/rosebones_dark.zig93
-rw-r--r--src/themes/rosebones_light.zig94
-rw-r--r--src/themes/seoul256_dark_233.zig101
-rw-r--r--src/themes/seoul256_dark_234.zig101
-rw-r--r--src/themes/seoul256_dark_235.zig101
-rw-r--r--src/themes/seoul256_dark_236.zig101
-rw-r--r--src/themes/seoul256_dark_237.zig100
-rw-r--r--src/themes/seoul256_dark_238.zig102
-rw-r--r--src/themes/seoul256_dark_239.zig102
-rw-r--r--src/themes/seoul256_light_252.zig102
-rw-r--r--src/themes/seoul256_light_253.zig99
-rw-r--r--src/themes/seoul256_light_254.zig101
-rw-r--r--src/themes/seoul256_light_255.zig101
-rw-r--r--src/themes/seoul256_light_256.zig101
-rw-r--r--src/themes/seoulbones_dark.zig95
-rw-r--r--src/themes/seoulbones_light.zig95
-rw-r--r--src/themes/solarized_dark.zig81
-rw-r--r--src/themes/solarized_light.zig82
-rw-r--r--src/themes/tokyobones_dark.zig93
-rw-r--r--src/themes/tokyobones_light.zig94
-rw-r--r--src/themes/tokyonight_day.zig100
-rw-r--r--src/themes/tokyonight_moon.zig99
-rw-r--r--src/themes/tokyonight_night.zig97
-rw-r--r--src/themes/tokyonight_storm.zig97
-rw-r--r--src/themes/vimbones.zig95
-rw-r--r--src/themes/zenbones_dark.zig92
-rw-r--r--src/themes/zenbones_light.zig93
-rw-r--r--src/themes/zenburned.zig92
-rw-r--r--src/themes/zenwritten_dark.zig92
-rw-r--r--src/themes/zenwritten_light.zig93
-rw-r--r--src/tty/tty.zig334
80 files changed, 8047 insertions, 328 deletions
diff --git a/src/Output.zig b/src/Output.zig
index c6012d06..3b8e6a7a 100644
--- a/src/Output.zig
+++ b/src/Output.zig
@@ -614,10 +614,9 @@ pub fn openRecent(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/animation.zig b/src/animation.zig
index 713a8a30..19f7ce0b 100644
--- a/src/animation.zig
+++ b/src/animation.zig
@@ -313,11 +313,18 @@ pub fn cellNoise(serial: u32, col: u16, row: u16) f32 {
/// Whether a changed dissolve cell has crossed from the frozen old grid to
/// the new one. Exact endpoints are part of the presentation contract.
+/// T4 (§9.2): an ordered swap, each cell at its place in a 4x4 Bayer
+/// matrix (shifted by the pane's serial so two panes differ), so the new
+/// content comes in as an even, patterned screen rather than noise, and
+/// each cell swaps once.
pub fn dissolveRevealed(serial: u32, col: u16, row: u16, raw_progress: f32) bool {
const t = std.math.clamp(raw_progress, 0.0, 1.0);
if (t <= 0) return false;
if (t >= 1) return true;
- return cellNoise(serial, col, row) < t;
+ const bayer = [4][4]u8{ .{ 0, 8, 2, 10 }, .{ 12, 4, 14, 6 }, .{ 3, 11, 1, 9 }, .{ 15, 7, 13, 5 } };
+ const x = (@as(u32, col) +% serial) % 4;
+ const y = (@as(u32, row) +% serial / 4) % 4;
+ return (@as(f32, @floatFromInt(bayer[y][x])) + 0.5) / 16 < t;
}
pub const CellArea = struct {
@@ -352,6 +359,9 @@ pub const CharSource = union(enum) {
/// that cell holds — a caret marching over empty space is still a caret.
byte: u8,
churn: u8,
+ /// The destination cell with its colours still this far (0..1) toward
+ /// the frozen old cell's ground: a soft edge between the two grids.
+ edge: f32,
pub const Offset = struct { cols: i32 = 0, rows: i32 = 0 };
@@ -383,12 +393,16 @@ pub fn charSource(track: Track, col: u16, row: u16, area: CellArea) CharSource {
.wave => .{ .at = .{
.rows = cellsOf(remaining * @min(4.0, h) * @sin(c * 0.55 - t * 9.0)),
} },
- // A curtain of glyphs marches in from the right, column by column, left
- // to right; each column still has a short slide of its own.
+ // A wipe, left to right: the new grid behind the head, the old one
+ // ahead of it, and between them a soft edge two cells wide whose
+ // colours step from the old ground to the new. Only the head's
+ // columns change from one frame to the next.
.curtain => blk: {
- const lead = t * (w + 1.0) - c;
+ const lead = t * (w + 2.0) - c;
if (lead <= 0.0) break :blk .old;
- break :blk .{ .at = .{ .cols = -cellsOf(@max(0.0, 3.0 - lead)) } };
+ if (lead <= 1.0) break :blk .{ .edge = 2.0 / 3.0 };
+ if (lead <= 2.0) break :blk .{ .edge = 1.0 / 3.0 };
+ break :blk .settled;
},
// Every cell churns through printable ASCII and locks onto its final
// glyph at its own stable threshold: the pane resolves out of noise.
@@ -517,7 +531,7 @@ test "each character effect moves glyphs along its own axis" {
var col: u16 = 0;
while (col < area.cols) : (col += 1) switch (charSource(fall, col, 0, area)) {
.old => {},
- .byte, .churn => return error.FallShouldNotChurn,
+ .byte, .churn, .edge => return error.FallShouldNotChurn,
.at => |offset| {
try std.testing.expectEqual(@as(i32, 0), offset.cols);
try std.testing.expect(offset.rows >= 0);
@@ -539,20 +553,24 @@ test "each character effect moves glyphs along its own axis" {
}
try std.testing.expect(above and below);
- // The curtain has a head: columns behind it hold the old grid, columns the
- // head has passed are settled, and the head itself is still sliding.
+ // The curtain wipes: columns ahead of its head hold the old grid,
+ // columns it has passed are settled, and between them a soft edge of
+ // exactly two columns steps from the old ground to the new.
const curtain: Track = .{ .effect = .curtain, .frame = 5, .to = box };
try std.testing.expectEqual(CharSource.settled, charSource(curtain, 0, 0, area));
try std.testing.expectEqual(CharSource{ .old = {} }, charSource(curtain, 29, 0, area));
- var sliding = false;
+ var wiped: [2]f32 = undefined;
+ var at_edge: usize = 0;
col = 0;
while (col < area.cols) : (col += 1) switch (charSource(curtain, col, 0, area)) {
- .at => |offset| if (offset.cols < 0) {
- sliding = true;
+ .edge => |weight| {
+ wiped[at_edge] = weight;
+ at_edge += 1;
},
- .old, .byte, .churn => {},
+ .old, .at, .byte, .churn => {},
};
- try std.testing.expect(sliding);
+ try std.testing.expectEqual(@as(usize, 2), at_edge);
+ try std.testing.expect(wiped[0] < wiped[1]);
var scramble: Track = .{ .effect = .scramble, .frame = 3, .to = box };
var churning: usize = 0;
@@ -566,12 +584,12 @@ test "each character effect moves glyphs along its own axis" {
scramble.frame = 4;
switch (charSource(scramble, col, 0, area)) {
.churn => |next| changed = changed or next != byte,
- .old, .at, .byte => {},
+ .old, .at, .byte, .edge => {},
}
scramble.frame = 3;
},
.at => locked += 1,
- .old, .byte => return error.ScrambleShouldNotFreeze,
+ .old, .byte, .edge => return error.ScrambleShouldNotFreeze,
};
try std.testing.expect(churning > 0 and locked > 0 and changed);
@@ -591,7 +609,7 @@ test "each character effect moves glyphs along its own axis" {
try std.testing.expectEqual(@as(u8, '_'), byte);
carets += 1;
},
- .old, .at, .churn => {},
+ .old, .at, .churn, .edge => {},
};
}
try std.testing.expectEqual(@as(usize, 1), carets);
@@ -624,6 +642,16 @@ test "opening presets separate geometry and content transitions" {
try std.testing.expect(closing.amount() < progress(.vertical, closing.frame));
}
+test "the ordered dissolve reveals a 4x4 block evenly, each cell once" {
+ for ([_]f32{ 0.25, 0.5, 0.75 }) |t| {
+ var revealed: usize = 0;
+ for (0..4) |y| for (0..4) |x| {
+ if (dissolveRevealed(7, @intCast(x), @intCast(y), t)) revealed += 1;
+ };
+ try std.testing.expectEqual(@as(usize, @intFromFloat(t * 16)), revealed);
+ }
+}
+
test "dissolve has exact stable endpoints" {
for (0..64) |col| {
const x: u16 = @intCast(col);
@@ -974,7 +1002,7 @@ pub const CursorGlide = struct {
}
}
- fn centreAt(glide: *const CursorGlide, now_ns: u64) [2]f32 {
+ pub fn centreAt(glide: *const CursorGlide, now_ns: u64) [2]f32 {
var c: [2]f32 = .{ 0, 0 };
for (glide.corners) |corner| for (&c, corner) |*sum, axis| {
sum.* += axis.value(now_ns) / 4;
diff --git a/src/colors.zig b/src/colors.zig
index 5694998d..da074765 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,76 @@ 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/
+ // <name>.zig says where each value came from): `theme_groups`.
+ pub const dark_plus = @import("themes/dark_plus.zig");
+ pub const solarized_dark = @import("themes/solarized_dark.zig");
+ pub const solarized_light = @import("themes/solarized_light.zig");
+ pub const dracula = @import("themes/dracula.zig");
+ pub const dracula_soft = @import("themes/dracula_soft.zig");
+ pub const alucard = @import("themes/alucard.zig");
+ pub const monokai = @import("themes/monokai.zig");
+ pub const monokai_pro = @import("themes/monokai_pro.zig");
+ pub const monokai_pro_classic = @import("themes/monokai_pro_classic.zig");
+ pub const monokai_pro_machine = @import("themes/monokai_pro_machine.zig");
+ pub const monokai_pro_octagon = @import("themes/monokai_pro_octagon.zig");
+ pub const monokai_pro_ristretto = @import("themes/monokai_pro_ristretto.zig");
+ pub const monokai_pro_spectrum = @import("themes/monokai_pro_spectrum.zig");
+ pub const monokai_pro_light = @import("themes/monokai_pro_light.zig");
+ pub const monokai_pro_light_sun = @import("themes/monokai_pro_light_sun.zig");
+ pub const tokyonight_night = @import("themes/tokyonight_night.zig");
+ pub const tokyonight_storm = @import("themes/tokyonight_storm.zig");
+ pub const tokyonight_moon = @import("themes/tokyonight_moon.zig");
+ pub const tokyonight_day = @import("themes/tokyonight_day.zig");
+ pub const rose_pine = @import("themes/rose_pine.zig");
+ pub const rose_pine_moon = @import("themes/rose_pine_moon.zig");
+ pub const rose_pine_dawn = @import("themes/rose_pine_dawn.zig");
+ pub const catppuccin_latte = @import("themes/catppuccin_latte.zig");
+ pub const catppuccin_frappe = @import("themes/catppuccin_frappe.zig");
+ pub const catppuccin_macchiato = @import("themes/catppuccin_macchiato.zig");
+ pub const catppuccin_mocha = @import("themes/catppuccin_mocha.zig");
+ pub const seoul256_dark_233 = @import("themes/seoul256_dark_233.zig");
+ pub const seoul256_dark_234 = @import("themes/seoul256_dark_234.zig");
+ pub const seoul256_dark_235 = @import("themes/seoul256_dark_235.zig");
+ pub const seoul256_dark_236 = @import("themes/seoul256_dark_236.zig");
+ pub const seoul256_dark_237 = @import("themes/seoul256_dark_237.zig");
+ pub const seoul256_dark_238 = @import("themes/seoul256_dark_238.zig");
+ pub const seoul256_dark_239 = @import("themes/seoul256_dark_239.zig");
+ pub const seoul256_light_252 = @import("themes/seoul256_light_252.zig");
+ pub const seoul256_light_253 = @import("themes/seoul256_light_253.zig");
+ pub const seoul256_light_254 = @import("themes/seoul256_light_254.zig");
+ pub const seoul256_light_255 = @import("themes/seoul256_light_255.zig");
+ pub const seoul256_light_256 = @import("themes/seoul256_light_256.zig");
+ pub const zenbones_light = @import("themes/zenbones_light.zig");
+ pub const zenbones_dark = @import("themes/zenbones_dark.zig");
+ pub const neobones_light = @import("themes/neobones_light.zig");
+ pub const neobones_dark = @import("themes/neobones_dark.zig");
+ pub const vimbones = @import("themes/vimbones.zig");
+ pub const forestbones_light = @import("themes/forestbones_light.zig");
+ pub const forestbones_dark = @import("themes/forestbones_dark.zig");
+ pub const nordbones = @import("themes/nordbones.zig");
+ pub const rosebones_light = @import("themes/rosebones_light.zig");
+ pub const rosebones_dark = @import("themes/rosebones_dark.zig");
+ pub const tokyobones_light = @import("themes/tokyobones_light.zig");
+ pub const tokyobones_dark = @import("themes/tokyobones_dark.zig");
+ pub const seoulbones_light = @import("themes/seoulbones_light.zig");
+ pub const seoulbones_dark = @import("themes/seoulbones_dark.zig");
+ pub const duckbones = @import("themes/duckbones.zig");
+ pub const zenburned = @import("themes/zenburned.zig");
+ pub const zenwritten_light = @import("themes/zenwritten_light.zig");
+ pub const zenwritten_dark = @import("themes/zenwritten_dark.zig");
+ pub const kanagawabones = @import("themes/kanagawabones.zig");
+ pub const nord = @import("themes/nord.zig");
+ pub const ayu_dark = @import("themes/ayu_dark.zig");
+ pub const ayu_mirage = @import("themes/ayu_mirage.zig");
+ pub const ayu_light = @import("themes/ayu_light.zig");
+ pub const doom_one = @import("themes/doom_one.zig");
+ pub const doom_one_light = @import("themes/doom_one_light.zig");
+ // 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 +423,78 @@ 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 (`Themes`), 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 = "Visual Studio Code", .first = "dark_plus" },
+ .{ .title = "Solarized", .first = "solarized_dark" },
+ .{ .title = "Dracula", .first = "dracula" },
+ .{ .title = "Monokai", .first = "monokai" },
+ .{ .title = "Monokai Pro", .first = "monokai_pro" },
+ .{ .title = "Tokyo Night", .first = "tokyonight_night" },
+ .{ .title = "Rosé Pine", .first = "rose_pine" },
+ .{ .title = "Catppuccin", .first = "catppuccin_latte" },
+ .{ .title = "Seoul256", .first = "seoul256_dark_233" },
+ .{ .title = "Zenbones", .first = "zenbones_light" },
+ .{ .title = "Nord", .first = "nord" },
+ .{ .title = "Ayu", .first = "ayu_dark" },
+ .{ .title = "Doom One", .first = "doom_one" },
+ .{ .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;
diff --git a/src/config.zig b/src/config.zig
index e96d73be..a703f79e 100644
--- a/src/config.zig
+++ b/src/config.zig
@@ -124,6 +124,13 @@ pub const leader_path = paths: {
if (builtins.capabilities.ligatures) table.set(.Ligatures, null);
if (builtins.capabilities.lift) table.set(.CursorBlink, null);
if (builtins.capabilities.lift) table.set(.SelectionGlow, null);
+ if (builtins.capabilities.lift) table.set(.HoverGlow, null);
+ if (builtins.capabilities.lift) table.set(.Occlusion, null);
+ if (builtins.capabilities.lift) table.set(.Parallax, null);
+ if (builtins.capabilities.lift) table.set(.Afterglow, null);
+ table.set(.JumpTrail, null);
+ table.set(.ChipShadow, null);
+ table.set(.ThumbFlash, null);
if (builtins.capabilities.lift) table.set(.Lift, null);
table.set(.Motion, null);
table.set(.InactiveDim, null);
@@ -673,6 +680,27 @@ pub const Runtime = struct {
/// G9: a soft halo round the selection in the body, fading in over
/// 100 ms, a pixel shell's (docs/effects.md).
selection_glow: bool = false,
+ /// G11: a soft glow under the word a look-hover would open, fading in
+ /// over 80 ms, a pixel shell's.
+ hover_glow: bool = false,
+ /// G12: pane bodies darken faintly toward their edges (ambient
+ /// occlusion, at most 2%), a pixel shell's.
+ occlusion: bool = false,
+ /// G13: a theme's page pattern under the bodies (lapis's dots) moves
+ /// with the text at a quarter of its speed, a pixel shell's.
+ parallax: bool = false,
+ /// G14: text that changes glows for a moment in the theme's accent and
+ /// fades, a pixel shell's.
+ afterglow: bool = false,
+ /// T2: in a terminal, a jump of the cursor leaves a short trail that
+ /// fades in 120 ms (a truecolor terminal's; a pixel shell glides).
+ jump_trail: bool = false,
+ /// T1: in a terminal, a notice chip casts a cell's shadow right and
+ /// down (truecolor terminals).
+ chip_shadow: bool = false,
+ /// T7: in a terminal, a pane's scroll thumb brightens as it scrolls and
+ /// fades back (truecolor terminals).
+ thumb_flash: bool = false,
/// How long a message stays after the key or click that dismisses it.
message_linger_ms: u16 = 800,
/// How long its fall into the row takes, easing out (§8.1: arriving,
@@ -896,7 +924,7 @@ pub const Runtime = struct {
/// The message timings a setting word sets, each in milliseconds.
pub const MessageTime = enum { linger, fall, dissolve };
- pub const Toggle = enum { colors, wrap, tree_context_tag_style, tag_bottom, debug, focus_tint, syntax_bold, verbose, message_animation, ligatures, cursor_blink, selection_glow };
+ pub const Toggle = enum { colors, wrap, tree_context_tag_style, tag_bottom, debug, focus_tint, syntax_bold, verbose, message_animation, ligatures, cursor_blink, selection_glow, hover_glow, occlusion, parallax, afterglow, jump_trail, chip_shadow, thumb_flash };
/// A bundled post pass, a Shadertoy file in shaders/post/: the CRT, and
/// G4's bloom, vignette and grain (docs/effects.md).
pub const Scene = enum { crt, bloom, vignette, grain };
@@ -956,6 +984,13 @@ pub const Runtime = struct {
.{ .word = "Ligatures", .action = .{ .toggle = .ligatures }, .availability = .ligatures, .doc = "A font's programming ligatures draw across their cells, on or off." },
.{ .word = "CursorBlink", .action = .{ .toggle = .cursor_blink }, .availability = .lift, .doc = "The cursor blinks, solid while typing, on or off." },
.{ .word = "SelectionGlow", .action = .{ .toggle = .selection_glow }, .availability = .lift, .doc = "A soft halo of the selection's colour round it in the body, on or off." },
+ .{ .word = "HoverGlow", .action = .{ .toggle = .hover_glow }, .availability = .lift, .doc = "A soft underline of light under the word a look-hover would open, on or off." },
+ .{ .word = "Occlusion", .action = .{ .toggle = .occlusion }, .availability = .lift, .doc = "Pane bodies darken faintly toward their edges, on or off." },
+ .{ .word = "Parallax", .action = .{ .toggle = .parallax }, .availability = .lift, .doc = "A theme's page pattern moves with the text at a quarter of its speed, on or off." },
+ .{ .word = "Afterglow", .action = .{ .toggle = .afterglow }, .availability = .lift, .doc = "Text that changes glows for a moment and fades, on or off." },
+ .{ .word = "JumpTrail", .action = .{ .toggle = .jump_trail }, .doc = "In a terminal, a jump of the cursor leaves a short trail that fades, on or off." },
+ .{ .word = "ChipShadow", .action = .{ .toggle = .chip_shadow }, .doc = "In a terminal, a notice chip casts a cell's shadow right and down, on or off." },
+ .{ .word = "ThumbFlash", .action = .{ .toggle = .thumb_flash }, .doc = "In a terminal, a pane's scroll thumb brightens as it scrolls and fades back, on or off." },
.{ .word = "Lift", .action = .lift, .availability = .lift, .doc = "How the focused pane stands off the page: off, shadow, rim or auto." },
.{ .word = "Motion", .action = .motion, .doc = "How animations move: off, crisp, smooth, bouncy or playful." },
.{ .word = "InactiveDim", .action = .inactive_dim, .doc = "Fade the unfocused panes' text by a percentage, 0 through 100, 0 off." },
@@ -985,7 +1020,7 @@ pub const Runtime = struct {
.{ .word = "Bloom", .action = .{ .scene = .bloom }, .availability = .scene_shaders, .doc = "The brightest ink glows a little: a level, 0 (off) through 3." },
.{ .word = "Vignette", .action = .{ .scene = .vignette }, .availability = .scene_shaders, .doc = "The window's corners fall into shade: a level, 0 (off) through 3." },
.{ .word = "Grain", .action = .{ .scene = .grain }, .availability = .scene_shaders, .doc = "The page takes a fine, still grain, like paper: a level, 0 (off) through 3." },
- .{ .word = "Shader", .action = .shader, .availability = .scene_shaders, .doc = "A Shadertoy file into the post chain, or out of it; off empties the chain." },
+ .{ .word = "Shader", .action = .shader, .availability = .scene_shaders, .doc = "A Shadertoy file into the post chain, or out of it, compiled again when saved; off empties the chain." },
.{ .word = "ShaderAnimation", .action = .shader_animation, .availability = .scene_shaders, .doc = "When the post chain animates on its own: off, on (while focused) or always." },
.{ .word = "Placement", .action = .{ .choice = .placement }, .doc = "Where a new pane goes: acme, as makenewwindow does, or pardes, the older rules." },
.{ .word = "BootShell", .action = .{ .choice = .boot_shell }, .doc = "A document dropped where a lone untouched shell stands: keep the shell, or replace it." },
@@ -1032,9 +1067,9 @@ pub const Runtime = struct {
.toggle => |field| if (field == .ligatures) {
if (setting.availability != .ligatures)
@compileError("the ligatures toggle must use the ligatures capability");
- } else if (field == .cursor_blink or field == .selection_glow) {
+ } else if (field == .cursor_blink or field == .selection_glow or field == .hover_glow or field == .occlusion or field == .parallax or field == .afterglow) {
if (setting.availability != .lift)
- @compileError("CursorBlink and SelectionGlow must use the lift capability (a pixel shell's)");
+ @compileError("CursorBlink, SelectionGlow, HoverGlow, Occlusion, Parallax and Afterglow must use the lift capability (a pixel shell's)");
} else if (setting.availability != null)
@compileError("unconditional toggles cannot carry a backend capability"),
else => if (setting.availability != null)
@@ -1690,6 +1725,13 @@ pub const Runtime = struct {
.ligatures => "Ligatures off\n",
.cursor_blink => "CursorBlink off\n",
.selection_glow => "SelectionGlow on\n",
+ .hover_glow => "HoverGlow on\n",
+ .occlusion => "Occlusion on\n",
+ .parallax => "Parallax on\n",
+ .afterglow => "Afterglow on\n",
+ .jump_trail => "JumpTrail on\n",
+ .chip_shadow => "ChipShadow on\n",
+ .thumb_flash => "ThumbFlash on\n",
},
.shell => "Shell requested (new panes) fish\n",
.tagline_size => "TaglineSize 73\n",
diff --git a/src/detached/client.zig b/src/detached/client.zig
index 8c9a2560..69ab2fdd 100644
--- a/src/detached/client.zig
+++ b/src/detached/client.zig
@@ -109,6 +109,10 @@ pub const Client = struct {
regions: std.ArrayListUnmanaged(pardes.Region) = .empty,
/// The palette its chrome is drawn in, as its last frame gave it.
chrome: pardes.Chrome = .{},
+ /// The post chain its frames are drawn through, as the session last
+ /// sent it; its paths and SPIR-V owned here. `post_key` moves with it.
+ post: wire.Post = .{},
+ post_key: u32 = 0,
in: std.ArrayListUnmanaged(u8) = .empty,
out: std.ArrayListUnmanaged(u8) = .empty,
/// Bytes of `in` belonging to the message `next` returned last. Compacted at
@@ -187,6 +191,7 @@ pub const Client = struct {
}
for (&c.tag_layers) |*layer| c.gpa.free(layer.cells);
c.regions.deinit(c.gpa);
+ c.freePost();
c.in.deinit(c.gpa);
c.out.deinit(c.gpa);
}
@@ -356,10 +361,30 @@ pub const Client = struct {
// open a URI on this display or read this display's selection back
// over the socket. `Ungreeted` is the same refusal a frame gets.
.set_clipboard, .read_clipboard, .open_link => if (!c.attached()) return error.Ungreeted,
+ // Kept, like the chrome: the frames that follow are drawn
+ // through it.
+ .post => |chain| {
+ if (!c.attached()) return error.Ungreeted;
+ c.freePost();
+ c.post = chain;
+ for (c.post.passes[0..c.post.len]) |*pass| {
+ pass.path = c.gpa.dupe(u8, pass.path) catch return error.Closed;
+ pass.spirv = c.gpa.dupe(u8, pass.spirv) catch return error.Closed;
+ }
+ c.post_key +%= 1;
+ },
}
return msg;
}
+ fn freePost(c: *Client) void {
+ for (c.post.passes[0..c.post.len]) |pass| {
+ c.gpa.free(pass.path);
+ c.gpa.free(pass.spirv);
+ }
+ c.post = .{};
+ }
+
// ---- internals --------------------------------------------------------
fn reshape(c: *Client, cols: u16, rows: u16) (Error || wire.Error)!void {
@@ -1131,7 +1156,7 @@ test "detached session: the seam's own routing rules, per surviving effect" {
test "detached session: a frontend is never asked to fork, write, or watch" {
// THE INVARIANT OF THE WHOLE DETACHED DESIGN, pinned as a property of the
// protocol rather than of one code path: a frontend is input and screen, so
- // the set of messages that can reach it is exactly the eight below. If a
+ // the set of messages that can reach it is exactly the nine below. If a
// machine-local effect is ever put back on the wire, a frontend becomes the
// process that owns a pane's shell again — and a pane whose shell belongs to
// a frontend dies when that frontend leaves, which is the bug this replaced.
@@ -1149,6 +1174,10 @@ test "detached session: a frontend is never asked to fork, write, or watch" {
// The three that need THIS human's display and cannot be done by a
// daemon nobody is looking at.
"set_clipboard", "read_clipboard", "open_link",
+ // Display state, like a frame's chrome: the passes this display
+ // draws its frames through. A file's SPIR-V arrives compiled, so the
+ // frontend still reads no disk and runs no program.
+ "post",
};
// One: the union a frontend decodes into has no other variant. Named
@@ -1183,7 +1212,7 @@ test "detached session: a frontend is never asked to fork, write, or watch" {
if (@intFromEnum(want) == tag) break true;
} else false;
if (!named) {
- std.debug.print("tag 0x{x:0>2} is decodable by a frontend and is not one of the seven\n", .{tag});
+ std.debug.print("tag 0x{x:0>2} is decodable by a frontend and is not one of the nine\n", .{tag});
return error.MachineLocalEffectOnTheWire;
}
}
diff --git a/src/detached/server.zig b/src/detached/server.zig
index 4d2d3882..a08a0342 100644
--- a/src/detached/server.zig
+++ b/src/detached/server.zig
@@ -11,6 +11,7 @@ const host_io = @import("../host_io.zig");
const selection_pipe = @import("../selection_pipe.zig");
const file_watch = @import("../file_watch.zig");
+const shader_build = @import("../shader_build.zig");
const ninep_io = @import("../9p_io.zig");
@@ -63,6 +64,8 @@ const Client = struct {
mirror: std.ArrayListUnmanaged(pardes.Cell) = .empty,
need_full: bool = true,
accepted_ms: i64 = 0,
+ /// The post chain last sent (Session.sendPost); null: none yet.
+ post_sent: ?u64 = null,
};
const Pty = struct {
@@ -173,6 +176,8 @@ pub const Session = struct {
/// the next poll returns at once, to draw it.
ninep_wake: std.atomic.Value(bool) = .init(false),
watches: file_watch.Table = @splat(null),
+ /// The post chain's files, compiled here for the GUIs attached.
+ shaders: shader_build = .{},
check_files: bool = false,
in_loop: bool = false,
greet_deadline_ms: u32 = greet_deadline_default_ms,
@@ -254,6 +259,8 @@ pub const Session = struct {
for (0..pardes.MAX_PANES) |pane|
file_watch.watchPane(s.inotify_fd, &s.watches, @intCast(pane), null, 0, .{ .text = 0 });
_ = file_watch.applyThemeEffect(s.core, s.gpa, s.inotify_fd, &s.watches, 0, false, false);
+ // The new core's chain generations are its own.
+ s.shaders.generation = null;
s.check_files = false;
if (s.ninep) |listener| listener.reset(replacement);
s.ninep_pending = false;
@@ -267,6 +274,8 @@ pub const Session = struct {
}
pub fn deinit(s: *Session) void {
+ // First: its compile thread wakes the mailbox, closed below.
+ s.shaders.deinit(s.gpa, s.inotify_fd, &s.watches);
if (s.mailbox.wake[0] >= 0) pardes.lsp.setStatusSink(null, null);
if (s.mailbox.wake[0] >= 0) host_io.setExitWake(null, null);
s.cancelWorkers();
@@ -769,6 +778,7 @@ pub const Session = struct {
fn reloadWatched(s: *Session) void {
if (!s.check_files) return;
s.check_files = false;
+ s.shaders.recheck();
for (0..reload_retries) |_| {
if (!file_watch.reloadChanged(s.core, s.io, s.gpa, &s.watches)) return;
}
@@ -826,6 +836,8 @@ pub const Session = struct {
const completed = s.ninep_wake.swap(false, .acq_rel) or drained;
for (&s.clients) |*c| if (c.fd >= 0) s.flush(c);
const regridded = s.reconcile();
+ // After the greeting reconcile sends: a chain before it is refused.
+ const compiled = s.sendPost();
// Nobody attached, nobody to dismiss a message: the loop expires
// them itself, between the editor's steps (expireUnattended).
s.core.unattended = s.primary() == null;
@@ -894,7 +906,7 @@ pub const Session = struct {
}
var timeout: c_int = if (timeout_ms == 0) -1 else @intCast(@min(timeout_ms, std.math.maxInt(c_int)));
if (s.nextWake(now)) |due| timeout = if (timeout < 0) due else @min(timeout, due);
- if (completed or s.check_files or regridded or s.ninep_pending) timeout = 0;
+ if (completed or compiled or s.check_files or regridded or s.ninep_pending) timeout = 0;
pardes.turn.rest();
const ready = libc.poll(&fds, @intCast(n), timeout);
pardes.turn.wake();
@@ -1105,6 +1117,28 @@ pub const Session = struct {
return regridded;
}
+ /// Compiles the post chain's files (a finished compile wakes the loop
+ /// through the mailbox) and sends each attached frontend the chain it
+ /// has not seen: on attach, and when it or a file's SPIR-V changes. True
+ /// when a compile was taken in, so the loop draws what it said.
+ /// ponytail: a terminal frontend is sent it too and drops it; a hello
+ /// saying which frontend it is would spare those bytes.
+ fn sendPost(s: *Session) bool {
+ const files = for (s.core.settings.post.list()) |entry| {
+ if (entry.scene == null) break true;
+ } else false;
+ const compiled = s.shaders.sync(s.gpa, s.io, s.core, if (files) s.inotify() else s.inotify_fd, &s.watches, .{ .ctx = &s.mailbox, .call = wakeForExit });
+ const key = @as(u64, s.shaders.revision) << 8 | @intFromEnum(s.core.settings.shader_animation);
+ var chain: wire.Post = .{ .animation = s.core.settings.shader_animation, .len = s.core.settings.post.len };
+ _ = s.shaders.view(&s.core.settings.post, &chain.passes);
+ for (&s.clients) |*c| {
+ if (!c.attached or c.greet or c.post_sent == key) continue;
+ c.post_sent = key;
+ s.send(c, .{ .post = chain });
+ }
+ return compiled;
+ }
+
fn send(s: *Session, c: *Client, msg: wire.ServerMsg) void {
const want = wire.serverBound(msg);
s.scratch.ensureTotalCapacity(s.gpa, want) catch return s.close(c, .oom);
diff --git a/src/detached/wire.zig b/src/detached/wire.zig
index 3326277d..ee8c72fb 100644
--- a/src/detached/wire.zig
+++ b/src/detached/wire.zig
@@ -3,6 +3,9 @@
const std = @import("std");
const pardes = @import("../pardes.zig");
const limits = @import("../memory.zig").limits;
+const shader_build = @import("../shader_build.zig");
+const Scene = pardes.config.Runtime.Scene;
+const ShaderAnimation = pardes.config.Runtime.ShaderAnimation;
/// 7: tag layers gained the `.notice` kind, so a frame can carry a tag value
/// an older frontend would reject mid-session. The greeting is where that has
@@ -13,7 +16,7 @@ pub const version: u16 = 8;
/// The Chrome palette is carried field by field as its declaration lists
/// them, so any change to it changes the wire: bump `version`, then this.
-const chrome_layout: u64 = 0x7f982e44ef87de72;
+const chrome_layout: u64 = 0x5e758b47fb0cf8da;
comptime {
@setEvalBranchQuota(10_000);
var hash = std.hash.Fnv1a_64.init();
@@ -73,6 +76,9 @@ pub const ServerTag = enum(u8) {
set_clipboard = 0x10,
read_clipboard = 0x11,
open_link = 0x12,
+ /// The post chain this frontend draws its frames through: sent on
+ /// attach and whenever it or a file's SPIR-V changes.
+ post = 0x13,
};
/// Why the server hung up on a connect. Sent as a `refuse` and followed by a
@@ -270,6 +276,7 @@ pub const Frame = struct {
region.notice = try r.getByte();
region.clip = try r.getU16();
region.lift = @bitCast(try r.getU32());
+ region.line = @bitCast(try r.getU32());
// A springy flavour overshoots, and a wind-up dips below.
if (!(region.lift >= -1 and region.lift <= 2)) return error.BadValue;
if (region.rect.x > f.cols or region.rect.w > f.cols - region.rect.x or
@@ -352,6 +359,22 @@ pub const ServerMsg = union(enum) {
set_clipboard: []const u8,
read_clipboard,
open_link: []const u8,
+ post: Post,
+};
+
+/// The session's post chain, as a GUI attached to it draws it: its passes in
+/// order, each a bundled scene at its level or a Shadertoy file's SPIR-V
+/// (compiled by the session, shader_build.zig: a frontend reads no disk and
+/// runs no program), and when it animates on its own. Slices borrow the
+/// payload.
+pub const Post = struct {
+ animation: ShaderAnimation = .on,
+ passes: [shader_build.max]shader_build.Pass = @splat(.{}),
+ len: u8 = 0,
+
+ pub fn list(p: *const Post) []const shader_build.Pass {
+ return p.passes[0..p.len];
+ }
};
// ---------------------------------------------------------------------------
@@ -754,7 +777,7 @@ pub fn layersBound(layers: []const pardes.Layer, tags: []const pardes.Layer, reg
return n + regions.len * region_len + chrome_len;
}
-const region_len = 1 + 1 + 4 + 8 + 1 + 2 + 2 + 1 + 2 + 4;
+const region_len = 1 + 1 + 4 + 8 + 1 + 2 + 2 + 1 + 2 + 4 + 4;
/// The palette: each colour three bytes, a theme's optional ones a flag
/// before them.
const chrome_len = @typeInfo(pardes.Chrome).@"struct".fields.len * 4 + 8 * 4;
@@ -839,6 +862,7 @@ pub fn encodeFrameLayers(out: []u8, cols: u16, rows: u16, cursor: ?Cursor, point
try w.putByte(region.notice);
try w.putU16(region.clip);
try w.putU32(@bitCast(region.lift));
+ try w.putU32(@bitCast(region.line));
}
inline for (@typeInfo(pardes.Chrome).@"struct".fields) |field| {
const rgb = @field(chrome, field.name);
@@ -1147,6 +1171,18 @@ pub fn encodeServer(out: []u8, msg: ServerMsg) Error![]const u8 {
.set_clipboard => |t| try w.putSlice32(t),
.read_clipboard => {},
.open_link => |u| try w.putSlice16(u),
+ .post => |p| {
+ try w.putByte(@intFromEnum(p.animation));
+ try w.putByte(p.len);
+ for (p.list()) |pass| {
+ // 0 a file, else a bundled scene and one.
+ try w.putByte(if (pass.scene) |scene| @as(u8, @intFromEnum(scene)) + 1 else 0);
+ try w.putByte(pass.level);
+ try w.putU32(pass.revision);
+ try w.putSlice16(pass.path);
+ try w.putSlice32(pass.spirv);
+ }
+ },
}
try finishMessage(&w, at);
return w.written();
@@ -1168,6 +1204,11 @@ pub fn serverBound(msg: ServerMsg) usize {
.frame => |f| frameBound(f.cols, f.rows),
.set_clipboard => |t| t.len,
.open_link => |u| u.len,
+ .post => |p| blk: {
+ var n: usize = 0;
+ for (p.list()) |pass| n += 12 + pass.path.len + pass.spirv.len;
+ break :blk n;
+ },
};
}
@@ -1230,6 +1271,22 @@ pub fn decodeServer(tag: u8, payload: []const u8) Error!ServerMsg {
.set_clipboard => .{ .set_clipboard = try r.getSlice32() },
.read_clipboard => .read_clipboard,
.open_link => .{ .open_link = try r.getSlice16() },
+ .post => blk: {
+ var p: Post = .{ .animation = try r.getTag(ShaderAnimation) };
+ p.len = try r.getByte();
+ if (p.len > shader_build.max) return error.Overlong;
+ for (p.passes[0..p.len]) |*pass| {
+ const kind = try r.getByte();
+ pass.* = .{
+ .scene = if (kind == 0) null else std.enums.fromInt(Scene, kind - 1) orelse return error.BadValue,
+ .level = try r.getByte(),
+ .revision = try r.getU32(),
+ .path = try r.getSlice16(),
+ .spirv = try r.getSlice32(),
+ };
+ }
+ break :blk .{ .post = p };
+ },
};
try r.end();
return msg;
@@ -1347,6 +1404,18 @@ test "detached wire: every server message round-trips" {
try testing.expectEqualStrings("yank", (try roundServer(&buf, .{ .set_clipboard = "yank" })).set_clipboard);
try testing.expectEqual(ServerMsg.read_clipboard, try roundServer(&buf, .read_clipboard));
try testing.expectEqualStrings("https://x", (try roundServer(&buf, .{ .open_link = "https://x" })).open_link);
+ var chain: Post = .{ .animation = .always, .len = 2 };
+ chain.passes[0] = .{ .scene = .bloom, .level = 3 };
+ chain.passes[1] = .{ .path = "~/wave.glsl", .spirv = "\x03\x02\x23\x07spirv", .revision = 7 };
+ const back = (try roundServer(&buf, .{ .post = chain })).post;
+ try testing.expectEqual(ShaderAnimation.always, back.animation);
+ try testing.expectEqual(@as(u8, 2), back.len);
+ try testing.expectEqual(Scene.bloom, back.passes[0].scene.?);
+ try testing.expectEqual(@as(u8, 3), back.passes[0].level);
+ try testing.expect(back.passes[1].scene == null);
+ try testing.expectEqualStrings("~/wave.glsl", back.passes[1].path);
+ try testing.expectEqualStrings("\x03\x02\x23\x07spirv", back.passes[1].spirv);
+ try testing.expectEqual(@as(u32, 7), back.passes[1].revision);
}
test "detached wire: only the display's own effects are on the wire" {
@@ -1355,9 +1424,9 @@ test "detached wire: only the display's own effects are on the wire" {
// is the direction that matters: it would hand a frontend work that must
// outlive it. The count fails if one of the three is dropped, which would
// silently leave a clipboard or a link unanswered on every frontend.
- try testing.expectEqual(@as(usize, 8), std.enums.values(ServerTag).len);
+ try testing.expectEqual(@as(usize, 9), std.enums.values(ServerTag).len);
for (std.enums.values(ServerTag)) |t| switch (t) {
- .welcome, .refuse, .frame, .quit, .detach, .set_clipboard, .read_clipboard, .open_link => {},
+ .welcome, .refuse, .frame, .quit, .detach, .set_clipboard, .read_clipboard, .open_link, .post => {},
};
}
diff --git a/src/draw.zig b/src/draw.zig
index 6c0cc077..15411770 100644
--- a/src/draw.zig
+++ b/src/draw.zig
@@ -63,7 +63,8 @@ pub fn place(p: *Pardes, s: *Surface) void {
// A folded pane is its tag rows and nothing else.
if (pane.collapsed) continue;
const body_h = r.h -| pane.tag_rows;
- s.addRegion(.{ .kind = .body, .owner = owner, .serial = pane.serial, .active = active, .rect = .{ .x = r.x + config.GUTTER, .y = body_y, .w = r.w - config.GUTTER, .h = body_h } });
+ const line: i32 = if (pane.file) |f| @intCast(@min(f.scroll, std.math.maxInt(i32))) else if (pane.isTerminal()) panes.Terminal.gridOffset(pane) else 0;
+ s.addRegion(.{ .kind = .body, .owner = owner, .serial = pane.serial, .active = active, .line = line, .rect = .{ .x = r.x + config.GUTTER, .y = body_y, .w = r.w - config.GUTTER, .h = body_h } });
var rail: Region = .{ .kind = .rail, .owner = owner, .serial = pane.serial, .active = active, .rect = .{ .x = r.x, .y = body_y, .w = config.GUTTER, .h = body_h } };
// An image's rail has no thumb; a native PDF's is measured as its
// pages are placed (Pdf.draw); every other pane's is here.
@@ -211,6 +212,14 @@ fn focusedCursorBox(p: *const Pardes, s: *const Surface) ?animation.Box {
/// at the Motion flavour's pace, a jump gliding and a step or a keystroke in
/// insert mode landing at once; blinking unless typing. A pixel shell's: a
/// grid's cursor is the terminal's own.
+///
+/// The springs hold the cursor where the pane's final layout puts it, and
+/// the quad is drawn through the pane's frame (paneFrame): while the pane
+/// slides, zooms or is dragged, the cursor rides with its text. A pane that
+/// moved in the layout carries the springs with it (they keep their place
+/// in the pane), so with no transition the cursor lands with the text. A
+/// focus change glides from where the cursor is drawn to the new pane's
+/// cursor, wherever that pane is drawn now.
fn glideCursor(p: *Pardes, s: *Surface) void {
s.chrome.cursor_glide = 0;
s.chrome.cursor_alpha = 1;
@@ -219,26 +228,59 @@ fn glideCursor(p: *Pardes, s: *Surface) void {
const box = focusedCursorBox(p, s) orelse return;
const motion = animation.Motion.of(p.settings.motion);
var snap = if (s.cursor) |cursor| cursor.bar else false;
+ var frame: PaneFrame = .{};
+ // In a column's or the workspace's tag the cursor is not the pane's,
+ // and does not move with it.
+ var inside = false;
if (p.panes[p.active]) |pane| {
- // A focus change lands at once: a glide between panes would be
- // squashed against the new one's edge.
- if (p.cursor_view.serial != pane.serial) snap = true;
- // Typing never trails: insert mode lands every move.
- if (pane.body.mode == .insert) snap = true;
- // Scrolled (a file's view, a terminal's output): the quad moves with
- // the text and lands, never gliding against text that jumped (every
- // Enter at a prompt scrolls one and moves the other).
+ const rect = p.rects[p.active];
+ const cx = box.x + box.w / 2;
+ const cy = box.y + box.h / 2;
+ inside = cx >= @as(f32, @floatFromInt(rect.x)) and cx <= @as(f32, @floatFromInt(rect.x + rect.w)) and
+ cy >= @as(f32, @floatFromInt(rect.y)) and cy <= @as(f32, @floatFromInt(rect.y + rect.h));
+ if (inside) frame = paneFrame(s, pane.serial, rect);
+ const origin: [2]f32 = .{ @floatFromInt(rect.x), @floatFromInt(rect.y) };
+ const view = p.cursor_view;
const line: i64 = if (pane.file) |f| @intCast(f.scroll) else if (pane.isTerminal()) panes.Terminal.gridOffset(pane) else 0;
- if (p.cursor_view.serial == pane.serial and line != p.cursor_view.line) {
- p.cursor_glide.shift(0, @floatFromInt(p.cursor_view.line - line));
- snap = true;
+ if (view.serial == pane.serial and view.inside == inside) {
+ // The pane moved in the layout: the springs move with it (its
+ // own cursor's; one in a column's tag stays).
+ if (inside) p.cursor_glide.shift(origin[0] - view.origin[0], origin[1] - view.origin[1]);
+ // Typing never trails: insert mode lands every move.
+ if (pane.body.mode == .insert) snap = true;
+ // Scrolled (a file's view, a terminal's output): the quad moves
+ // with the text and lands, never gliding against text that
+ // jumped (every Enter at a prompt scrolls one and moves the
+ // other).
+ if (inside and line != view.line) {
+ p.cursor_glide.shift(0, @floatFromInt(view.line - line));
+ snap = true;
+ }
+ } else if (view.serial != 0 and p.cursor_glide.placed) {
+ // Focus moved (to another pane, or between a pane and a
+ // column's or the workspace's tag): the glide starts where the
+ // cursor is drawn, in the old place's frame, and heads for the
+ // new cursor through the new one's.
+ const old: PaneFrame = if (!view.inside) .{} else for (p.panes, 0..) |slot, id| {
+ const other = slot orelse continue;
+ if (other.serial != view.serial) continue;
+ const r = p.rects[id];
+ p.cursor_glide.shift(@as(f32, @floatFromInt(r.x)) - view.origin[0], @as(f32, @floatFromInt(r.y)) - view.origin[1]);
+ break paneFrame(s, view.serial, r);
+ } else .{};
+ const at = p.cursor_glide.centreAt(p.now_ns);
+ const seen = frame.unplace(old.place(at));
+ p.cursor_glide.shift(seen[0] - at[0], seen[1] - at[1]);
+ p.cursor_crossing = true;
}
- p.cursor_view = .{ .serial = pane.serial, .line = line };
+ p.cursor_view = .{ .serial = pane.serial, .line = line, .origin = origin, .inside = inside };
}
p.cursor_glide.aim(box, p.now_ns, motion, snap);
_ = p.cursor_glide.step(p.now_ns);
const moving = p.cursor_glide.moving();
- s.chrome.cursor_glide = 2 | @as(u8, @intFromBool(moving));
+ if (!moving) p.cursor_crossing = false;
+ // Drawn as the quad while it glides and while its pane moves.
+ s.chrome.cursor_glide = 2 | @as(u8, @intFromBool(moving or frame.moving()));
// The shell blinks it, from how long ago the last key or click was.
if (p.settings.cursor_blink)
s.chrome.cursor_idle_ms = @intCast(@min(std.math.maxInt(u32) - 1, (p.now_ns -| p.input_ns) / std.time.ns_per_ms));
@@ -248,13 +290,51 @@ fn glideCursor(p: *Pardes, s: *Surface) void {
const see_through: f32 = if (motion.zeta < 1) 0.7 else 0.85;
s.chrome.cursor_alpha = if (moving) see_through + (1 - see_through) * std.math.clamp(1 - remaining / 0.5, 0, 1) else 1;
const quad = p.cursor_glide.sample(p.now_ns, motion);
- // Never outside the focused pane, however far a flavour overshoots or
- // a scroll carried it: it squashes against the pane's edges.
- const r = p.rects[p.active];
- for (quad, 0..) |point, i| {
- s.chrome.cursor_quad[i * 2] = std.math.clamp(point[0], @as(f32, @floatFromInt(r.x)), @as(f32, @floatFromInt(r.x + r.w)));
- s.chrome.cursor_quad[i * 2 + 1] = std.math.clamp(point[1], @as(f32, @floatFromInt(r.y)), @as(f32, @floatFromInt(r.y + r.h)));
+ // Never outside the focused pane as drawn, however far a flavour
+ // overshoots or a scroll carried it: it squashes against the pane's
+ // edges. Crossing to another pane, it may pass over any of the grid.
+ const bounds: animation.Box = if (p.cursor_crossing or !inside)
+ .{ .w = @floatFromInt(s.cols), .h = @floatFromInt(s.rows) }
+ else
+ frame.at;
+ for (quad, 0..) |corner, i| {
+ const point = frame.place(corner);
+ s.chrome.cursor_quad[i * 2] = std.math.clamp(point[0], bounds.x, bounds.x + bounds.w);
+ s.chrome.cursor_quad[i * 2 + 1] = std.math.clamp(point[1], bounds.y, bounds.y + bounds.h);
+ }
+}
+
+/// Where a pane is drawn against where the layout puts it: its transition
+/// track's presented box against the track's target, or the layout's box
+/// both ways when it is still. Maps grid points between the two.
+const PaneFrame = struct {
+ to: animation.Box = .{ .w = 1, .h = 1 },
+ at: animation.Box = .{ .w = 1, .h = 1 },
+
+ fn moving(f: PaneFrame) bool {
+ return !std.meta.eql(f.to, f.at);
+ }
+
+ fn place(f: PaneFrame, point: [2]f32) [2]f32 {
+ return .{ f.at.x + (point[0] - f.to.x) * scale(f.at.w, f.to.w), f.at.y + (point[1] - f.to.y) * scale(f.at.h, f.to.h) };
}
+
+ fn unplace(f: PaneFrame, point: [2]f32) [2]f32 {
+ return .{ f.to.x + (point[0] - f.at.x) * scale(f.to.w, f.at.w), f.to.y + (point[1] - f.at.y) * scale(f.to.h, f.at.h) };
+ }
+
+ fn scale(num: f32, den: f32) f32 {
+ return if (den > 0.001) num / den else 1;
+ }
+};
+
+fn paneFrame(s: *const Surface, serial: u32, rect: Rect) PaneFrame {
+ const still = layout.panelBox(rect);
+ for (s.panelTracks()) |track| {
+ if (track.serial != serial or track.phase == .closing or !track.active()) continue;
+ return .{ .to = track.to, .at = track.visualBox() };
+ }
+ return .{ .to = still, .at = still };
}
/// The Lift style as drawn: `auto` is a shadow on a light page, and on a
@@ -472,7 +552,7 @@ pub fn render(p: *Pardes, arena: std.mem.Allocator) !*Surface {
.rail_px = @intCast(@min(255, @as(u32, p.theme().rail_px orelse 12) * p.settings.grip_width / 100)),
.name_ink = chrome.tag_name_fg,
.active_name_ink = if (p.settings.focus_tint) chrome.tag_active_name_fg else chrome.tag_name_fg,
- .selection_glow = @intFromBool(p.settings.selection_glow),
+ .fx = @as(u8, @intFromBool(p.settings.selection_glow)) | @as(u8, @intFromBool(p.settings.hover_glow)) << 1 | @as(u8, @intFromBool(p.settings.occlusion)) << 2 | @as(u8, @intFromBool(p.settings.parallax)) << 3 | @as(u8, @intFromBool(p.settings.afterglow)) << 4,
};
if (p.theme().decor) |decor| {
s.chrome.decor_dots = decor.page_dots;
@@ -666,8 +746,10 @@ pub fn render(p: *Pardes, arena: std.mem.Allocator) !*Surface {
}
}
};
- glideCursor(p, s);
+ // After the tracks are submitted: the cursor is drawn through its
+ // pane's.
try p.presentation.submit(p, s);
+ glideCursor(p, s);
return composeAsciiTransitions(p, arena, s);
}
@@ -722,6 +804,7 @@ fn composedCell(
return switch (animation.charSource(track, col - area.x0, row - area.y0, area)) {
.old => canonical.previous_cells[index],
.byte => |byte| withByte(canonical.cells[index], byte),
+ .edge => |weight| towardGround(canonical.cells[index], canonical.previous_cells[index].style.bg, weight),
// Churn belongs on the glyph, not on the pane's empty space, and it
// borrows whichever side of the diff actually has one.
.churn => |byte| blk: {
@@ -748,6 +831,25 @@ fn composedCell(
};
}
+/// A cell with its ink and ground drawn `weight` of the way toward another
+/// ground, in OKLab. A palette or default colour cannot be mixed, so the
+/// ground steps over at the halfway point instead.
+fn towardGround(cell: Cell, ground: Color, weight: f32) Cell {
+ var out = cell;
+ const to = switch (ground) {
+ .rgb => |rgb| rgb,
+ .default, .index => return if (weight >= 0.5) blk: {
+ out.style.bg = ground;
+ break :blk out;
+ } else out,
+ };
+ inline for (.{ &out.style.fg, &out.style.bg }) |color| switch (color.*) {
+ .rgb => |rgb| color.* = .{ .rgb = colors.mixOklab(rgb, to, weight) },
+ .default, .index => {},
+ };
+ return out;
+}
+
fn withByte(cell: Cell, byte: u8) Cell {
var out = cell;
out.text[0] = byte;
@@ -1685,11 +1787,13 @@ test "a dim leaves a pair already under 4.5 alone, and reads a theme's own sixte
try std.testing.expect(reverse.bg.rgb[0] < 255);
}
-test "with no lift and no dim a focus switch asks for no frames" {
+test "with no lift, no dim and no motion a focus switch asks for no frames" {
const p = try Pardes.init(std.testing.allocator, .{ .cols = 80, .rows = 24 });
defer p.deinit();
_ = try p.setTestFile("text\n");
p.newScratchBelow(0);
+ // With motion the cursor glides to the other pane (glideCursor).
+ p.settings.motion = .off;
var arena: std.heap.ArenaAllocator = .init(std.testing.allocator);
defer arena.deinit();
p.advance(std.time.ns_per_s);
@@ -2038,3 +2142,109 @@ test "a body with context rows is painted once, and the grid's rows are what a p
try std.testing.expect(layer.rows > body.rect.h);
}
}
+
+/// Two columns, the file in the left one and a shell in the right, the
+/// right one focused with its cursor settled.
+fn twoColumns(p: *Pardes, arena: *std.heap.ArenaAllocator) !void {
+ _ = try p.setTestFile("some text on a line\n" ** 6);
+ _ = try p.newShell(1, "");
+ try std.testing.expect(layout.splitColumn(p, 0, 1, false));
+ p.sync();
+ p.advance(std.time.ns_per_s);
+ const boot = try p.render(arena.allocator());
+ p.acknowledgePanelPresentation(boot.panelTracks());
+ p.advance(2 * std.time.ns_per_s);
+ _ = arena.reset(.retain_capacity);
+ const settled = try p.render(arena.allocator());
+ p.acknowledgePanelPresentation(settled.panelTracks());
+ try std.testing.expect(!p.cursor_glide.moving());
+}
+
+test "while its pane slides the cursor rides with it, at the same cell of the pane every frame" {
+ if (!builtins.capabilities.lift) return error.SkipZigTest;
+ const p = try Pardes.init(std.testing.allocator, .{ .cols = 120, .rows = 20 });
+ defer p.deinit();
+ var arena: std.heap.ArenaAllocator = .init(std.testing.allocator);
+ defer arena.deinit();
+ p.settings.motion = .bouncy;
+ try twoColumns(p, &arena);
+ p.active = 1;
+ p.advance(3 * std.time.ns_per_s);
+ _ = arena.reset(.retain_capacity);
+ p.acknowledgePanelPresentation((try p.render(arena.allocator())).panelTracks());
+ p.advance(4 * std.time.ns_per_s);
+ p.settings.panel_transition = .slide;
+ // The right column's pane moves right.
+ p.col_weight[0] = layout.column_weight_unit * 2;
+ p.col_weight[1] = layout.column_weight_unit;
+ p.sync();
+ var now: u64 = 4 * std.time.ns_per_s;
+ var riding: usize = 0;
+ for (0..40) |_| {
+ now += 16 * std.time.ns_per_ms;
+ p.advance(now);
+ _ = arena.reset(.retain_capacity);
+ const s = try p.render(arena.allocator());
+ p.acknowledgePanelPresentation(s.panelTracks());
+ const frame = paneFrame(s, p.panes[1].?.serial, p.rects[1]);
+ const box = focusedCursorBox(p, s).?;
+ const want = frame.place(.{ box.x, box.y });
+ try std.testing.expectApproxEqAbs(want[0], s.chrome.cursor_quad[0], 0.01);
+ try std.testing.expectApproxEqAbs(want[1], s.chrome.cursor_quad[1], 0.01);
+ if (frame.moving()) {
+ riding += 1;
+ // Drawn as the quad while the pane moves.
+ try std.testing.expect(s.chrome.cursor_glide & 1 != 0);
+ }
+ }
+ try std.testing.expect(riding >= 3);
+
+ // With no transition, the cursor lands with the text at once.
+ p.settings.panel_transition = .off;
+ p.col_weight[0] = layout.column_weight_unit;
+ p.col_weight[1] = layout.column_weight_unit * 2;
+ p.sync();
+ now += 16 * std.time.ns_per_ms;
+ p.advance(now);
+ _ = arena.reset(.retain_capacity);
+ const landed = try p.render(arena.allocator());
+ const box = focusedCursorBox(p, landed).?;
+ try std.testing.expectApproxEqAbs(box.x, landed.chrome.cursor_quad[0], 0.01);
+ try std.testing.expect(!p.cursor_glide.moving());
+}
+
+test "focus moved to another column glides from where the cursor was drawn" {
+ if (!builtins.capabilities.lift) return error.SkipZigTest;
+ const p = try Pardes.init(std.testing.allocator, .{ .cols = 120, .rows = 20 });
+ defer p.deinit();
+ var arena: std.heap.ArenaAllocator = .init(std.testing.allocator);
+ defer arena.deinit();
+ p.settings.motion = .smooth;
+ try twoColumns(p, &arena);
+ p.active = 0;
+ p.advance(3 * std.time.ns_per_s);
+ _ = arena.reset(.retain_capacity);
+ const before = try p.render(arena.allocator());
+ try std.testing.expect(!p.cursor_glide.moving());
+ const was: [2]f32 = .{ before.chrome.cursor_quad[0], before.chrome.cursor_quad[1] };
+
+ p.active = 1;
+ _ = arena.reset(.retain_capacity);
+ const start = try p.render(arena.allocator());
+ // It sets off from where it was drawn, in the left column...
+ try std.testing.expectApproxEqAbs(was[0], start.chrome.cursor_quad[0], 0.01);
+ try std.testing.expectApproxEqAbs(was[1], start.chrome.cursor_quad[1], 0.01);
+ try std.testing.expect(p.cursor_glide.moving() and start.chrome.cursor_glide & 1 != 0);
+ // ...crosses the grid, not squashed against the right one's edge...
+ p.advance(3 * std.time.ns_per_s + 40 * std.time.ns_per_ms);
+ _ = arena.reset(.retain_capacity);
+ const mid = try p.render(arena.allocator());
+ try std.testing.expect(mid.chrome.cursor_quad[0] > was[0] and mid.chrome.cursor_quad[0] < @as(f32, @floatFromInt(p.rects[1].x)) + 1);
+ // ...and lands on the right one's cursor.
+ p.advance(5 * std.time.ns_per_s);
+ _ = arena.reset(.retain_capacity);
+ const landed = try p.render(arena.allocator());
+ const box = focusedCursorBox(p, landed).?;
+ try std.testing.expectApproxEqAbs(box.x, landed.chrome.cursor_quad[0], 0.01);
+ try std.testing.expect(!p.cursor_glide.moving() and !p.cursor_crossing);
+}
diff --git a/src/file_watch.zig b/src/file_watch.zig
index d028488e..8bd9de8b 100644
--- a/src/file_watch.zig
+++ b/src/file_watch.zig
@@ -70,7 +70,11 @@ pub const Watch = struct {
/// Sharing the table also shares directory descriptors correctly when a user
/// happens to open that .zon as an ordinary document.
pub const theme_slot = pardes.MAX_PANES;
-pub const Table = [pardes.MAX_PANES + 1]?Watch;
+/// After it, one slot a post chain file (shader_build.zig): marked only so
+/// a save there wakes the host, which has the files read again; no
+/// generation of theirs is kept here.
+pub const shader_slot = theme_slot + 1;
+pub const Table = [shader_slot + pardes.config.Runtime.Post.max]?Watch;
/// Whether this OS has a watcher at all. Off it every entry point below
/// returns the inert answer: `watchPath` installs nothing, so `watches` stays
@@ -423,6 +427,13 @@ pub fn watchPane(
watchPath(fd, watches, id, path, serial, generation);
}
+/// Mark the post chain files' directories, one slot each, dropping the
+/// marks of files no longer in it.
+pub fn watchShaders(fd: c_int, watches: *Table, paths: []const []const u8) void {
+ for (0..pardes.config.Runtime.Post.max) |i|
+ watchPath(fd, watches, shader_slot + i, if (i < paths.len) paths[i] else null, 0, .{ .text = 0 });
+}
+
/// Reconcile one marked pane. A true result requests an immediate second pass:
/// the PDF pathname changed between the stat before MuPDF reopened it and the
/// stat after, so committing either identity would lose a generation.
diff --git a/src/gui/Post.zig b/src/gui/Post.zig
index 61ebc2cd..06310b7f 100644
--- a/src/gui/Post.zig
+++ b/src/gui/Post.zig
@@ -1,10 +1,10 @@
//! The post chain (docs/render-pipeline.md §6): Shadertoy passes over the
//! finished frame, each reading the one before it (the frame first) and the
//! last one writing the window. A pass is the bundled Crt (shaders/post/,
-//! compiled with the build) or a user's file (Shader <path>),
-//! compiled when it joins the chain by the build's own glslc invocation on a
-//! thread of its own, never on the frame path. A file whose compile fails
-//! keeps its last good pipeline, and glslc missing is said once.
+//! compiled with the build) or a user's file (Shader <path>), whose SPIR-V
+//! comes from shader_build.zig: compiled by the process that holds the core,
+//! this one or the detached session this GUI is attached to. A file whose
+//! compile fails keeps its last good pipeline.
//!
//! The uniforms are ghostty's (Uniforms), so a shader written for ghostty runs
//! here. Their clock is the shell's and moves only when the chain draws:
@@ -14,7 +14,6 @@ const std = @import("std");
const libc = std.c;
const ghostty_vt = @import("ghostty-vt");
const pardes = @import("../pardes.zig");
-const filesystem = @import("../fs.zig");
const gui = @import("gui.zig");
const c = gui.c;
@@ -25,6 +24,7 @@ const Pipeline = c.SDL_GPUGraphicsPipeline;
pub const max = Chain.max;
const prefix = @embedFile("post-prefix.glsl");
+const Built = @import("../shader_build.zig").Pass;
const vert_spv = @embedFile("post.vert.spv");
const bundled_spv = std.EnumArray(Scene, []const u8).init(.{
.crt = @embedFile("post-crt.frag.spv"),
@@ -92,28 +92,15 @@ pub const Pass = struct {
/// A user's file, owned; empty for a bundled pass.
path: []u8 = &.{},
pipeline: ?*Pipeline = null,
-};
-
-/// One compile of a chain's new files, on its own thread.
-const Job = struct {
- const Result = union(enum) { none, spirv: []u8, failed: []u8, missing };
- io: std.Io,
- gpa: std.mem.Allocator,
- paths: [max][]u8 = @splat(&.{}),
- results: [max]Result = @splat(.none),
- len: usize = 0,
- done: std.atomic.Value(bool) = .init(false),
- thread: std.Thread = undefined,
+ /// The SPIR-V revision `pipeline` was made from (shader_build.Pass).
+ revision: u32 = 0,
};
passes: [max]Pass = @splat(.{}),
len: usize = 0,
-/// The chain generation `passes` follow; null before the first.
-generation: ?u32 = null,
+/// The chain key `passes` follow (sync); null before the first.
+key: ?u32 = null,
bundled: std.EnumArray(Scene, ?*Pipeline) = .initFill(null),
-job: ?*Job = null,
-/// Said once: without glslc, files never compile.
-missing_said: bool = false,
ping: [2]?*c.SDL_GPUTexture = .{ null, null },
ping_w: u32 = 0,
ping_h: u32 = 0,
@@ -136,20 +123,17 @@ bloom_up: ?*Pipeline = null,
started_ns: ?u64 = null,
last_ns: u64 = 0,
-/// Brings the passes to the chain the core holds, and takes in a finished
-/// compile. Called each loop step, off the frame path.
-pub fn sync(post: *Post, gpa: std.mem.Allocator, io: std.Io, device: *c.SDL_GPUDevice, format: c.SDL_GPUTextureFormat, core: *pardes.Pardes) void {
- if (post.job) |job| {
- if (!job.done.load(.acquire)) return;
- job.thread.join();
- post.job = null;
- post.collect(gpa, device, format, core, job);
- }
- const chain = &core.settings.post;
- if (post.generation == chain.generation) return;
- post.generation = chain.generation;
+/// Brings the passes to the chain a shell was given (shader_build.zig: a
+/// local core's, or an attached session's over the wire), whose `key` moves
+/// whenever the chain or a file's SPIR-V does. A file whose SPIR-V has not
+/// moved keeps its pipeline; one the GPU refuses keeps its last, and is said
+/// to `core`, or logged where there is none. Each loop step, off the frame
+/// path.
+pub fn sync(post: *Post, gpa: std.mem.Allocator, device: *c.SDL_GPUDevice, format: c.SDL_GPUTextureFormat, chain: []const Built, key: u32, core: ?*pardes.Pardes) void {
+ if (post.key == key) return;
+ post.key = key;
var next: [max]Pass = @splat(.{});
- for (chain.list(), 0..) |entry, i| {
+ for (chain, 0..) |entry, i| {
if (entry.scene) |scene| {
if (post.bundled.get(scene) == null) post.bundled.set(scene, makePipeline(device, format, bundled_spv.get(scene), if (scene == .bloom) 2 else 1, 4) catch null);
if (scene == .bloom and post.bloom_down == null) {
@@ -161,11 +145,22 @@ pub fn sync(post: *Post, gpa: std.mem.Allocator, io: std.Io, device: *c.SDL_GPUD
}
// A file already in the chain keeps its pipeline and its path.
for (post.passes[0..post.len]) |*pass| {
- if (pass.scene != null or !std.mem.eql(u8, pass.path, entry.path.get())) continue;
+ if (pass.scene != null or !std.mem.eql(u8, pass.path, entry.path)) continue;
next[i] = pass.*;
pass.* = .{};
break;
- } else next[i] = .{ .path = gpa.dupe(u8, entry.path.get()) catch &.{} };
+ } else next[i] = .{ .path = gpa.dupe(u8, entry.path) catch &.{} };
+ const pass = &next[i];
+ if (pass.revision == entry.revision or entry.spirv.len == 0) continue;
+ pass.revision = entry.revision;
+ const made = makePipeline(device, format, entry.spirv, 1, 4) catch {
+ var buf: [512]u8 = undefined;
+ const text = std.fmt.bufPrint(&buf, "Shader {s}: the GPU refused it", .{entry.path}) catch "Shader: the GPU refused it";
+ if (core) |p| p.setMessage(p.active, text) else std.log.err("{s}", .{text});
+ continue;
+ };
+ if (pass.pipeline) |old| c.SDL_ReleaseGPUGraphicsPipeline(device, old);
+ pass.pipeline = made;
}
for (post.passes[0..post.len]) |pass| {
if (pass.scene == null) if (pass.pipeline) |pipeline| c.SDL_ReleaseGPUGraphicsPipeline(device, pipeline);
@@ -179,127 +174,6 @@ pub fn sync(post: *Post, gpa: std.mem.Allocator, io: std.Io, device: *c.SDL_GPUD
post.last_ns = 0;
post.uniforms.frame = 0;
}
- // The new files compile together, on a thread of their own.
- const job = gpa.create(Job) catch return;
- job.* = .{ .io = io, .gpa = gpa };
- for (post.passes[0..post.len]) |pass| {
- if (pass.scene != null or pass.pipeline != null or pass.path.len == 0) continue;
- job.paths[job.len] = gpa.dupe(u8, pass.path) catch continue;
- job.len += 1;
- }
- if (job.len == 0) return gpa.destroy(job);
- job.thread = std.Thread.spawn(.{}, compile, .{job}) catch {
- for (job.paths[0..job.len]) |path| gpa.free(path);
- return gpa.destroy(job);
- };
- post.job = job;
-}
-
-fn collect(post: *Post, gpa: std.mem.Allocator, device: *c.SDL_GPUDevice, format: c.SDL_GPUTextureFormat, core: *pardes.Pardes, job: *Job) void {
- defer gpa.destroy(job);
- for (job.paths[0..job.len], job.results[0..job.len]) |path, result| {
- defer gpa.free(path);
- const pass = for (post.passes[0..post.len]) |*pass| {
- if (pass.scene == null and std.mem.eql(u8, pass.path, path)) break pass;
- } else null;
- var buf: [512]u8 = undefined;
- switch (result) {
- .none => {},
- .missing => if (!post.missing_said) {
- post.missing_said = true;
- core.setMessage(core.active, "Shader: glslc is not on PATH, so Shadertoy files do not run (Crt does)");
- },
- .failed => |text| {
- defer gpa.free(text);
- // glslc's first line says where: in the file's own lines.
- const first = std.mem.sliceTo(text, '\n');
- core.setMessage(core.active, std.fmt.bufPrint(&buf, "Shader {s}: {s}", .{ path, first[0..@min(first.len, 400)] }) catch "Shader: compile failed");
- },
- .spirv => |spirv| {
- defer gpa.free(spirv);
- const target = pass orelse continue;
- const made = makePipeline(device, format, spirv, 1, 4) catch {
- core.setMessage(core.active, std.fmt.bufPrint(&buf, "Shader {s}: the GPU refused it", .{path}) catch "Shader: the GPU refused it");
- continue;
- };
- if (target.pipeline) |old| c.SDL_ReleaseGPUGraphicsPipeline(device, old);
- target.pipeline = made;
- },
- }
- }
-}
-
-/// The compile thread: each file behind the prefix, through glslc with the
-/// build's own flags (build.zig compileGlsl), source on stdin, SPIR-V on
-/// stdout.
-fn compile(job: *Job) void {
- defer job.done.store(true, .release);
- defer {
- var event = std.mem.zeroes(c.SDL_Event);
- event.type = c.SDL_EVENT_USER;
- _ = c.SDL_PushEvent(&event);
- }
- for (job.paths[0..job.len], 0..) |path, i| job.results[i] = compileOne(job.gpa, job.io, path);
-}
-
-fn compileOne(gpa: std.mem.Allocator, io: std.Io, path: []const u8) Job.Result {
- var home_buf: [4096]u8 = undefined;
- const expanded = if (std.mem.startsWith(u8, path, "~/")) if (libc.getenv("HOME")) |home|
- std.fmt.bufPrint(&home_buf, "{s}/{s}", .{ std.mem.span(home), path[2..] }) catch path
- else
- path else path;
- const body = filesystem.readFile(gpa, expanded) catch |err|
- return .{ .failed = std.fmt.allocPrint(gpa, "cannot read it ({t})", .{err}) catch return .none };
- defer gpa.free(body);
- // The prefix and the file go to glslc on its stdin: no file of ours is
- // written anywhere. std's spawn does every allocation before fork and
- // searches PATH on the stack after it (Io/Threaded.zig spawnPosix,
- // posixExecv), so a child forked from this thread cannot wedge on a
- // lock another thread held (the forkShell hazard).
- var child = std.process.spawn(io, .{
- .argv = &.{ "glslc", "-fshader-stage=fragment", "-o", "-", "-" },
- .stdin = .pipe,
- .stdout = .pipe,
- .stderr = .pipe,
- }) catch |err| return switch (err) {
- error.FileNotFound => .missing,
- else => .{ .failed = std.fmt.allocPrint(gpa, "glslc did not run ({t})", .{err}) catch return .none },
- };
- defer child.kill(io);
- // glslc reads all of its input before it writes a byte, so writing it
- // first cannot deadlock against a full output pipe.
- {
- var stdin = child.stdin.?;
- child.stdin = null;
- defer stdin.close(io);
- var buf: [4096]u8 = undefined;
- var writer = stdin.writer(io, &buf);
- writer.interface.writeAll(prefix) catch {};
- writer.interface.writeAll(body) catch {};
- writer.interface.flush() catch {};
- }
- var streams: std.Io.File.MultiReader.Buffer(2) = undefined;
- var reader: std.Io.File.MultiReader = undefined;
- reader.init(gpa, io, streams.toStreams(), &.{ child.stdout.?, child.stderr.? });
- defer reader.deinit();
- while (reader.fill(64, .none)) |_| {
- if (reader.reader(0).buffered().len > 16 << 20 or reader.reader(1).buffered().len > 64 << 10)
- return .{ .failed = gpa.dupe(u8, "glslc said too much") catch return .none };
- } else |err| switch (err) {
- error.EndOfStream => {},
- else => return .{ .failed = gpa.dupe(u8, "reading glslc failed") catch return .none },
- }
- const term = child.wait(io) catch return .{ .failed = gpa.dupe(u8, "glslc did not finish") catch return .none };
- const clean = switch (term) {
- .exited => |code| code == 0,
- else => false,
- };
- if (clean) return .{ .spirv = reader.toOwnedSlice(0) catch return .none };
- const said = reader.toOwnedSlice(1) catch return .none;
- // glslc calls its input <stdin>; the file is what a person knows.
- defer gpa.free(said);
- const named = std.mem.replaceOwned(u8, gpa, said, "<stdin>", path) catch return .{ .failed = gpa.dupe(u8, "glslc failed") catch return .none };
- return .{ .failed = named };
}
fn makePipeline(device: *c.SDL_GPUDevice, format: c.SDL_GPUTextureFormat, frag: []const u8, samplers: u32, uniform_buffers: u32) !*Pipeline {
@@ -686,17 +560,6 @@ fn bloomStep(cmd: *c.SDL_GPUCommandBuffer, pipeline: *Pipeline, sampler: *c.SDL_
pub fn deinit(post: *Post, gpa: std.mem.Allocator, device: *c.SDL_GPUDevice) void {
for (post.bloom_tex) |texture| if (texture) |t| c.SDL_ReleaseGPUTexture(device, t);
for ([_]?*Pipeline{ post.bloom_down, post.bloom_up }) |pipeline| if (pipeline) |p| c.SDL_ReleaseGPUGraphicsPipeline(device, p);
- if (post.job) |job| {
- job.thread.join();
- for (job.paths[0..job.len], job.results[0..job.len]) |path, result| {
- gpa.free(path);
- switch (result) {
- .spirv, .failed => |bytes| gpa.free(bytes),
- .none, .missing => {},
- }
- }
- gpa.destroy(job);
- }
for (post.passes[0..post.len]) |pass| {
if (pass.scene == null) if (pass.pipeline) |pipeline| c.SDL_ReleaseGPUGraphicsPipeline(device, pipeline);
gpa.free(pass.path);
@@ -801,36 +664,3 @@ test "a moved cursor becomes the previous one, stamped with the time of the move
try std.testing.expectEqual([4]f32{ 1.0 / 255.0, 2.0 / 255.0, 3.0 / 255.0, 1 }, post.uniforms.selection_foreground_color);
}
-test "ghostty's test shaders compile behind the prefix, and its invalid one fails with glslc's words" {
- const gpa = std.testing.allocator;
- const io = std.testing.io;
- // ghostty's own test shaders, from its package in zig-pkg.
- var pkg = std.Io.Dir.cwd().openDir(io, "zig-pkg", .{ .iterate = true }) catch return error.SkipZigTest;
- defer pkg.close(io);
- var it = pkg.iterate();
- const ghostty = while (it.next(io) catch null) |entry| {
- if (std.mem.startsWith(u8, entry.name, "ghostty-")) break entry.name;
- } else return error.SkipZigTest;
- var buf: [512]u8 = undefined;
- for ([_][]const u8{ "crt", "focus", "invalid" }) |name| {
- const path = try std.fmt.bufPrint(&buf, "zig-pkg/{s}/src/renderer/shaders/test_shadertoy_{s}.glsl", .{ ghostty, name });
- const result = compileOne(gpa, io, path);
- switch (result) {
- .missing => return error.SkipZigTest, // no glslc here
- .spirv => |spirv| {
- defer gpa.free(spirv);
- try std.testing.expect(!std.mem.eql(u8, name, "invalid"));
- // SPIR-V's magic number, little-endian.
- try std.testing.expectEqual(@as(u32, 0x07230203), std.mem.readInt(u32, spirv[0..4], .little));
- },
- .failed => |text| {
- defer gpa.free(text);
- try std.testing.expectEqualStrings("invalid", name);
- // Named by the file, not by glslc's <stdin>.
- try std.testing.expect(std.mem.indexOf(u8, text, path) != null);
- try std.testing.expect(std.mem.indexOf(u8, text, "<stdin>") == null);
- },
- .none => return error.TestUnexpectedResult,
- }
- }
-}
diff --git a/src/gui/gui.zig b/src/gui/gui.zig
index d36cf187..cb3bdd97 100644
--- a/src/gui/gui.zig
+++ b/src/gui/gui.zig
@@ -11,6 +11,7 @@ const config = @import("../config.zig");
const look = @import("../look.zig");
const message = pardes.Messages.Message;
const file_watch = @import("../file_watch.zig");
+const shader_build = @import("../shader_build.zig");
const deck = @import("deck.zig");
const Post = @import("Post.zig");
const pet = @import("pet.zig");
@@ -1870,8 +1871,12 @@ const Gui = struct {
/// picture follows on a spring (Glide), never the other way round.
scroll_notch: i32 = 0,
glide: Glide = .{},
- /// G9: the selection's glow blocks as last drawn, for their fade-in.
+ /// G9 and G11: the selection's and the hovered word's glow blocks as
+ /// last drawn, for their fade-in.
sel_glow: SelGlow = .{},
+ hover_glow: SelGlow = .{},
+ /// G14: the cells last drawn and when each last changed.
+ afterglow: Afterglow = .{},
/// The cursor's blink (blinkNow): when the frame drawn last arrived,
/// a redraw owed for an edge, and this frame's cursor mode and alpha.
@@ -2581,6 +2586,7 @@ fn runNative(init: std.process.Init, opts_in: pardes.Options, attach: ?[]const u
defer c.ui_font_free(g.font);
defer gpa.free(g.scroll_edge); // grown on demand by stepScroll
defer g.glide.deinit(gpa);
+ defer g.afterglow.deinit(gpa);
resetGlyphAtlas(&g);
if (attach) |requested| return attachRequested(gpa, &g, requested);
@@ -2688,6 +2694,7 @@ fn localSession(
.test_hz = if (std.c.getenv("PARDES_TEST_HZ")) |hz| @max(1, std.fmt.parseInt(u64, std.mem.span(hz), 10) catch 60) else 60,
};
defer shell.shutdownPtys();
+ defer shell.shaders.deinit(gpa, inotify_fd, &watches);
// A virtual clock never blinks: goldens and feel series hold still.
g.no_blink = shell.test_clock != null;
const host = shell.host();
@@ -2857,6 +2864,7 @@ fn attachedLoop(gpa: std.mem.Allocator, g: *Gui, client: *detached_client.Client
in.post(.{ .paste = text });
},
.open_link => |url| look.openLink(url),
+ .post => dirty = true,
}
}
try link;
@@ -2872,7 +2880,15 @@ fn attachedLoop(gpa: std.mem.Allocator, g: *Gui, client: *detached_client.Client
geom = now;
try attachedResize(client, g, now.cols, now.rows);
}
- if (!dirty) continue;
+ // The session's chain, its files compiled there.
+ g.post.sync(gpa, g.device, g.swapchain_format, client.post.list(), client.post_key, null);
+ if (!dirty) {
+ // As a local window does: a chain that moves on its own redraws
+ // alone, once a refresh.
+ if (g.post.animating(client.post.animation) and g.scene_tex != null and
+ c.SDL_GetTicksNS() -| g.post.last_ns + std.time.ns_per_ms >= refreshNs(g)) redrawPost(g, gpa);
+ continue;
+ }
dirty = false;
paintAttached(g, gpa, client);
}
@@ -3988,6 +4004,8 @@ const Shell = struct {
pipe_tasks: *PipeTasks,
inotify_fd: c_int,
watches: *file_watch.Table,
+ /// The post chain's files as SPIR-V.
+ shaders: shader_build = .{},
threads_ok: bool = false,
test_mode: bool = false,
feed: StdinFeed = .{},
@@ -4204,11 +4222,19 @@ const Shell = struct {
.files_changed => check_files = true,
.fs_ready => {},
};
+ if (check_files) s.shaders.recheck();
if (check_files and file_watch.reloadChanged(s.core, s.io, s.gpa, s.watches))
s.queue.push(.files_changed);
}
};
+/// A post chain compile is done (shader_build.zig): the loop's SDL wait ends.
+fn wakeSdl(_: ?*anyopaque) void {
+ var event = std.mem.zeroes(c.SDL_Event);
+ event.type = c.SDL_EVENT_USER;
+ _ = c.SDL_PushEvent(&event);
+}
+
fn wakeFs(ctx: ?*anyopaque) void {
const q: *Queue = @ptrCast(@alignCast(ctx.?));
q.push(.fs_ready);
@@ -4271,7 +4297,13 @@ fn waitInput(ctx: ?*anyopaque, timeout_ms: u32) void {
if (core.continuous()) {
const display = virtual.* + std.time.ns_per_s / s.test_hz;
virtual.* = if (core.nextWake()) |due| @max(virtual.*, @min(due, display)) else display;
- } else if (!got and timeout_ms != 0) virtual.* = @max(virtual.*, core.nextWake() orelse virtual.*);
+ } else if (timeout_ms != 0 and (!got or !core.needs_frame)) {
+ // A wait that ran out, or one a stray event cut short while
+ // nothing owes a frame: time moves to the core's wake all
+ // the same, so a steady trickle of events (a pty's, the
+ // test feed's) cannot hold a virtual clock still forever.
+ virtual.* = @max(virtual.*, core.nextWake() orelse virtual.*);
+ }
}
if (got) {
// A run of queued motions is one move to its last position (the
@@ -4345,7 +4377,12 @@ fn pollFrame(ctx: ?*anyopaque) void {
const geom = windowCells(g);
if (updateCoreResize(core, geom.cols, geom.rows, g.cell_w, g.cell_h, g.tagline_width, g.tagline_height)) resetScroll(g);
stepScroll(g, core, s.gpa);
- g.post.sync(s.gpa, s.io, g.device, g.swapchain_format, core);
+ _ = s.shaders.sync(s.gpa, s.io, core, s.inotify_fd, s.watches, .{ .call = wakeSdl });
+ {
+ var passes: [Post.max]shader_build.Pass = undefined;
+ g.post.sync(s.gpa, g.device, g.swapchain_format, s.shaders.view(&core.settings.post, &passes), s.shaders.revision, core);
+ }
+ if (s.test_mode) writeTestStatus(g, core, s.presented);
// Between the core's frames, a chain that moves on its own redraws
// alone, once a refresh of the display the window is on (a millisecond
// early is on time: the wait that paces it is in whole milliseconds).
@@ -4988,7 +5025,7 @@ fn stepGlide(g: *Gui, core: *pardes.Pardes, gpa: std.mem.Allocator) void {
const glide = &g.glide;
// The glide runs on the core's continuous path: frames every loop, and
// a virtual clock carried on.
- defer core.shell_continuous = glide.moving() or g.sel_glow.fading;
+ defer core.shell_continuous = glide.moving() or g.sel_glow.fading or g.hover_glow.fading or g.afterglow.glowing;
glide.now_ns = core.now_ns;
glide.motion = pardes.animation.Motion.of(core.settings.motion);
if (!glide.spring.settled) {
@@ -5793,8 +5830,9 @@ fn renderFrame(
const win_w: f32 = @floatFromInt(sw);
const win_h: f32 = @floatFromInt(sh);
- // The post chain runs over a local frame, from the scene texture.
- var scene_on = core != null and g.post.ready();
+ // The post chain runs from the scene texture, over a local frame or an
+ // attached session's alike.
+ var scene_on = g.post.ready();
if (scene_on) ensureSceneTexture(g, sw, sh) catch {
g.scene_target_failed = true;
scene_on = false;
@@ -5813,9 +5851,23 @@ fn renderFrame(
if (g.glide.pending) g.glide.measure(gpa, surface);
g.sel_glow.now_ns = if (core) |p| p.now_ns else c.SDL_GetTicksNS();
// Off, or while panes move, no glow and nothing fading.
- if (surface.chrome.selection_glow == 0 or surface.panelTracks().len != 0) g.sel_glow = .{ .now_ns = g.sel_glow.now_ns };
+ if (surface.chrome.fx & 1 == 0 or surface.panelTracks().len != 0) g.sel_glow = .{ .now_ns = g.sel_glow.now_ns };
+ if (surface.chrome.fx & 16 == 0) g.afterglow.glowing = false;
+ g.hover_glow.now_ns = g.sel_glow.now_ns;
+ if (surface.chrome.fx & 2 == 0 or surface.panelTracks().len != 0) g.hover_glow = .{ .now_ns = g.sel_glow.now_ns };
if (g.glide.moving()) g.glide.offset = g.glide.spring.value(g.glide.now_ns);
cell_decor = CellDecor.of(chrome);
+ // G13: a body's dots follow its text at a quarter of its speed: its
+ // first line, and a glide's picture offset, in rows.
+ if (chrome.fx & 8 != 0 and chrome.decor_dots != null) for (surface.regionList()) |region| {
+ if (region.kind != .body or cell_decor.nbodies == cell_decor.bodies.len) continue;
+ var rows: f32 = @floatFromInt(region.line);
+ if (g.glide.moving() and g.glide.pane == region.owner) rows += g.glide.offset;
+ const period = cell_decor.uniforms[4];
+ cell_decor.bodies[cell_decor.nbodies] = bodyPixels(region, layout);
+ cell_decor.shifts[cell_decor.nbodies] = @mod(-rows * layout.h * 0.25, period);
+ cell_decor.nbodies += 1;
+ };
// Plaques need an opaque window: with WindowOpacity under 100 the cells
// are drawn as they are, and the plaques not at all.
cell_decor.plaques_off = backgroundOpacity(g.applied_window_opacity) < 1;
@@ -5830,9 +5882,10 @@ fn renderFrame(
}
}
var groups = makeGroups(surface.panelTracks(), surface.hasPanelDiff());
- // Cursors stand still while panes move, as they always have; while the
- // focused one glides or blinks, it is the overlay's quad instead. One
- // or the other every frame (none only at a blink's off half).
+ // While panes move the cells' cursors are not drawn; the focused one is
+ // the overlay's quad, riding with its pane (draw.glideCursor), as it is
+ // while it glides or blinks. One or the other every frame (none only at
+ // a blink's off half).
const blink_alpha: f32 = if (blinkNow(g, chrome)) |b| b.alpha else 1;
const quad_mode = chrome.cursor_glide & 2 != 0 and (chrome.cursor_glide & 1 != 0 or blink_alpha < 1);
g.cursor_quad_alpha = if (quad_mode) chrome.cursor_alpha * blink_alpha else 0;
@@ -6153,19 +6206,17 @@ fn renderFrame(
c.SDL_EndGPURenderPass(rp);
if (scene_on) {
- if (core) |p| {
- var boxes: [max_cursors]CursorBox = undefined;
- const cursors = if (cursors_shown) cursorBoxes(g, surface, layout, win_h, &boxes) else 0;
- var spare: [Post.max_spare][4]f32 = undefined;
- g.post.describe(.{
- .cursor = if (cursors > 0) boxes[0] else null,
- .spare = spare[0..postSpare(surface, layout, &spare)],
- .background = chrome.page orelse bg_default,
- .foreground = chrome.fg orelse fg_default,
- .selection_foreground = p.theme().sel_fg,
- .selection_background = p.theme().sel_bg,
- });
- }
+ var boxes: [max_cursors]CursorBox = undefined;
+ const cursors = if (cursors_shown) cursorBoxes(g, surface, layout, win_h, &boxes) else 0;
+ var spare: [Post.max_spare][4]f32 = undefined;
+ g.post.describe(.{
+ .cursor = if (cursors > 0) boxes[0] else null,
+ .spare = spare[0..postSpare(surface, layout, &spare)],
+ .background = chrome.page orelse bg_default,
+ .foreground = chrome.fg orelse fg_default,
+ .selection_foreground = if (core) |p| p.theme().sel_fg else chrome.sel_fg,
+ .selection_background = if (core) |p| p.theme().sel_bg else chrome.sel_bg,
+ });
const chain_zone = tracy.zone(@src(), "post chain");
defer chain_zone.end();
g.post.scale = display_scale;
@@ -6656,9 +6707,12 @@ fn emitSurfaceCell(
// this underlay at tagline height leaves page-colored gutters between
// adjacent workspace, column and pane tags at TaglineSize < 100.
emitInstance(g, instances, next.*, col, row, body_layout, win_w, win_h, track, .body, (&band_base)[0..1], 0, null, old_layer, false, page);
- // Over a theme's plaque (not a grip's button) the underlay lets it
- // show (tagPlaques).
- if (cell_decor.nplaques != 0 and !here.grip and cell_decor.inPlaque((@as(f32, @floatFromInt(col)) + 0.5) * body_layout.w + body_layout.x_off, @as(f32, @floatFromInt(row)) * body_layout.h + body_layout.y_off + 0.5))
+ // Over a theme's plaque the underlay lets it show (tagPlaques),
+ // in a grip's cell too where the plaque reaches into it past the
+ // button (which is drawn over the cells).
+ const cell_x = @as(f32, @floatFromInt(col)) * body_layout.w + body_layout.x_off;
+ const cell_y = @as(f32, @floatFromInt(row)) * body_layout.h + body_layout.y_off + 0.5;
+ if (cell_decor.nplaques != 0 and cell_decor.inPlaque(if (here.grip) cell_x + body_layout.w - 0.5 else cell_x + 0.5, cell_y))
instances[next.*].effect |= clear_bg_bit;
next.* += 1;
// Tag glyphs keep their font size inside a full physical-width grip.
@@ -6993,7 +7047,13 @@ fn emitInstance(
(cell_decor.inPlaque(px0 + 0.5, py0 + 0.5) or cell_decor.inPlaque(px0 + layout.w - 0.5, py0 + 0.5)))
instances[idx].effect |= clear_bg_bit;
if (cell_decor.dots) |dotted| {
- if (!resolved.clear_bg and std.mem.eql(u8, &resolved.bg, &dotted)) instances[idx].effect |= dotted_bit;
+ if (!resolved.clear_bg and std.mem.eql(u8, &resolved.bg, &dotted)) {
+ instances[idx].effect |= dotted_bit;
+ // G13: the dots under a body move with its text, a quarter
+ // as far; the serial slot carries it (the shader reads it
+ // as the dots' offset alone).
+ instances[idx].serial = @bitCast(cell_decor.parallaxAt(px0 + 0.5, py0 + 0.5));
+ }
}
if (cell_decor.title) |inks| {
if (resolved.role == .tagline and (std.mem.eql(u8, &resolved.fg, &inks[0]) or std.mem.eql(u8, &resolved.fg, &inks[1]))) instances[idx].effect |= title_bit;
@@ -7082,16 +7142,20 @@ test "lapis plaques stay in their tag's band, clear of every grip; only the focu
for (under) |item| {
const r: [4]f32 = .{ (item.x0 + 1) / 2 * 1280, (1 - item.y0) / 2 * 768, (item.x1 + 1) / 2 * 1280, (1 - item.y1) / 2 * 768 };
// Inside one tag's band, whole: never on a body, a grip or the
- // next pane.
+ // next pane. A box stacked under another tag's lays its top frame
+ // over their shared edge, its own width up into the band above.
const home = for (s.regionList()) |region| {
if (region.kind != .tag and region.kind != .column_tag and region.kind != .workspace_tag) continue;
const band = plaqueBox(&g, &s.chrome, s.regionList(), region, layout, 1280, 768) orelse continue;
- if (r[0] >= band.band[0] - 0.01 and r[1] >= band.band[1] - 0.01 and r[2] <= band.band[2] + 0.01 and r[3] <= band.band[3] + 0.01) break region;
+ if (r[0] >= band.band[0] - 0.01 and r[1] >= band.band[1] - band.border - 0.01 and r[2] <= band.band[2] + 0.01 and r[3] <= band.band[3] + 0.01) break region;
} else return error.PlaqueOutsideItsBand;
+ // Clear of every grip's button: a plaque starts flush against it,
+ // in the gap between it and the text.
for (s.regionList()) |grip| {
if (grip.kind != .grip and grip.kind != .column_grip) continue;
- const gx0 = @as(f32, @floatFromInt(grip.rect.x)) * 16;
- const gx1 = @as(f32, @floatFromInt(grip.rect.x + grip.rect.w)) * 16;
+ const column = scrollColumn(&g, &s.chrome, grip.rect.x, layout);
+ const gx0 = column[0];
+ const gx1 = column[1];
const gy0 = @as(f32, @floatFromInt(grip.rect.y)) * 32;
const gy1 = @as(f32, @floatFromInt(grip.rect.y + grip.rect.h)) * 32;
try std.testing.expect(r[2] <= gx0 + 0.01 or r[0] >= gx1 - 0.01 or r[3] <= gy0 + 0.01 or r[1] >= gy1 - 0.01);
@@ -7105,6 +7169,31 @@ test "lapis plaques stay in their tag's band, clear of every grip; only the focu
}
try std.testing.expectEqual(@as(usize, 1), stripes);
try std.testing.expect(shadows >= 2);
+ // Stacked boxes share an edge and line up: a column's box spans its
+ // first pane's across, runs down to the edge that pane's frame lies
+ // over, and no shadow reaches the next column.
+ var stacks: usize = 0;
+ for (s.regionList()) |column| {
+ if (column.kind != .column_tag) continue;
+ const upper = plaqueBox(&g, &s.chrome, s.regionList(), column, layout, 1280, 768).?;
+ for (s.regionList()) |tag| {
+ if (tag.kind != .tag or tag.rect.y != column.rect.y + column.rect.h or tag.rect.x + tag.rect.w != column.rect.x + column.rect.w) continue;
+ const lower = plaqueBox(&g, &s.chrome, s.regionList(), tag, layout, 1280, 768).?;
+ try std.testing.expectEqual(upper.box[0], lower.box[0]);
+ // Flush against the grips' buttons, which line up too.
+ const grip = for (s.regionList()) |part| {
+ if (part.kind == .grip and part.owner == tag.owner) break part;
+ } else unreachable;
+ try std.testing.expectEqual(scrollColumn(&g, &s.chrome, grip.rect.x, layout)[1], lower.box[0]);
+ try std.testing.expectEqual(upper.box[2], lower.box[2]);
+ try std.testing.expectEqual(upper.band[3], upper.box[3]);
+ try std.testing.expectEqual(lower.band[1] - lower.border, lower.box[1]);
+ try std.testing.expectEqual(@as(f32, 0), upper.shadow[1]);
+ if (lower.band[2] < 1280) try std.testing.expect(lower.box[2] + lower.shadow[0] < lower.band[2]);
+ stacks += 1;
+ }
+ }
+ try std.testing.expectEqual(@as(usize, 2), stacks);
// The scroll track is chequered.
var checkers: usize = 0;
for (g.decor.items[groups.items[0].decor_start + groups.items[0].under_count ..]) |item| checkers += @intFromBool(item.effect & checker_bit != 0);
@@ -8231,6 +8320,32 @@ fn captureFrame(g: *Gui, gpa: std.mem.Allocator, cmd: *c.SDL_GPUCommandBuffer, t
g.captured +%= 1;
}
+/// A test window's state for a harness (test/gui_golden.py), in the
+/// capture directory's `status`: "<loop step> <frames captured> <idle>".
+/// Idle is 1 once the frame last captured is the one that stays: nothing
+/// owes a frame, nothing moves, nothing waits on a clock. A harness that
+/// sees a step past the one it acted on, idle, has its frame, however slow
+/// the machine is.
+var test_status_step: u64 = 0;
+fn writeTestStatus(g: *const Gui, core: *const pardes.Pardes, presented: bool) void {
+ test_status_step +%= 1;
+ if (g.capture_dir.len == 0) return;
+ const idle = presented and !core.needs_frame and !core.continuous() and core.nextWake() == null and
+ !g.glide.moving() and g.scroll_lag == 0 and g.scroll_delta == 0 and g.scroll_notch == 0;
+ var line_buf: [64]u8 = undefined;
+ // What keeps it busy, after: owes a frame, moves, waits, not shown.
+ const line = std.fmt.bufPrint(&line_buf, "{d} {d} {d} {d}{d}{d}{d}\n", .{ test_status_step, g.captured, @intFromBool(idle), @intFromBool(core.needs_frame), @intFromBool(core.continuous()), @intFromBool(core.nextWake() != null), @intFromBool(!presented) }) catch return;
+ var tmp_buf: [4096]u8 = undefined;
+ var final_buf: [4096]u8 = undefined;
+ const tmp_path = std.fmt.bufPrintSentinel(&tmp_buf, "{s}/status.tmp", .{g.capture_dir}, 0) catch return;
+ const final_path = std.fmt.bufPrintSentinel(&final_buf, "{s}/status", .{g.capture_dir}, 0) catch return;
+ const fd = libc.open(tmp_path, .{ .ACCMODE = .WRONLY, .CREAT = true, .TRUNC = true }, @as(libc.mode_t, 0o644));
+ if (fd < 0) return;
+ _ = libc.write(fd, line.ptr, line.len);
+ _ = libc.close(fd);
+ _ = libc.rename(tmp_path, final_path);
+}
+
fn writeCapturePpm(g: *Gui, gpa: std.mem.Allocator, pixels: []const u8, width: u32, height: u32, name: []const u8) !void {
// A latency trace times frames, and an 8 MB file write per frame would
// stall the loop it is timing.
@@ -8704,7 +8819,13 @@ fn buildDecor(g: *Gui, gpa: std.mem.Allocator, surface: *const pardes.Surface, g
const bodies = body_buf[0..nbodies];
try paneDecor(g, gpa, placed, groups, index, chrome, layout, surface.cols, surface.rows, win_w, win_h, track);
// G9, while nothing moves: a halo round the body's selection.
- if (index == 0 and groups.tracks == 0 and chrome.selection_glow != 0) try selectionGlow(g, gpa, surface, ceilings, layout, win_w, win_h);
+ if (index == 0 and groups.tracks == 0 and chrome.fx & 1 != 0) try glowBlocks(g, gpa, surface, .selection, layout, win_w, win_h);
+ // G11: a glow under the word a look-hover would open.
+ if (index == 0 and groups.tracks == 0 and chrome.fx & 2 != 0) try glowBlocks(g, gpa, surface, .hover, layout, win_w, win_h);
+ // G14: what changed glows, while nothing moves.
+ if (index == 0 and groups.tracks == 0 and chrome.fx & 16 != 0) try afterglowDecor(g, gpa, surface, layout, win_w, win_h);
+ // G12: the bodies' edges shaded, a pane's in its own group.
+ if (chrome.fx & 4 != 0) try occlusion(g, gpa, placed, groups, index, surface, ceilings, layout, win_w, win_h, track);
// A single spine runs down each column, through every tag and body.
if (index == 0) for (regions) |column| {
if (column.kind != .column) continue;
@@ -8913,6 +9034,17 @@ const CellDecor = struct {
grounds: [2][3]u8 = .{ .{ 0, 0, 0 }, .{ 0, 0, 0 } },
plaques_off: bool = false,
+ /// G13: each body's rectangle and how far its dots have moved.
+ bodies: [pardes.MAX_PANES][4]f32 = undefined,
+ shifts: [pardes.MAX_PANES]f32 = undefined,
+ nbodies: usize = 0,
+
+ fn parallaxAt(decor: *const CellDecor, x: f32, y: f32) f32 {
+ for (decor.bodies[0..decor.nbodies], decor.shifts[0..decor.nbodies]) |band, shift|
+ if (x >= band[0] and x < band[2] and y >= band[1] and y < band[3]) return shift;
+ return 0;
+ }
+
fn inPlaque(decor: *const CellDecor, x: f32, y: f32) bool {
for (decor.plaques[0..decor.nplaques]) |band|
if (x >= band[0] and x < band[2] and y >= band[1] and y < band[3]) return true;
@@ -8956,19 +9088,31 @@ const SelGlow = struct {
/// How long a glow takes to come up (§8.1's micro-feedback: 80 to 120 ms).
const sel_glow_ns: u64 = 100 * std.time.ns_per_ms;
+/// A glow's kind: G9's halo round the selection, G11's under a hovered word.
+const GlowKind = enum { selection, hover };
+
/// G9: a soft halo of the selection's own colour round each block of it in
/// a pane body (rows of selected cells that touch), fading in over 100 ms
/// with an ease-out. The selection itself is the halo's caster, never
/// touched; so is a cursor beside it; the halo stays on bodies (never a
/// tag or a grip) and at most 22%, less where the text round it would drop
/// below min(its contrast, 4.5).
-fn selectionGlow(g: *Gui, gpa: std.mem.Allocator, surface: *const pardes.Surface, ceilings: LiftCeilings, layout: CellLayout, win_w: f32, win_h: f32) !void {
- _ = ceilings;
+/// G11: under the word a look-hover would open (its cells in the
+/// affordance's tint), a soft line of the theme's accent along its foot,
+/// fading in over 80 ms, as strong at most, never under a cursor.
+fn glowBlocks(g: *Gui, gpa: std.mem.Allocator, surface: *const pardes.Surface, kind: GlowKind, layout: CellLayout, win_w: f32, win_h: f32) !void {
const chrome = &surface.chrome;
- const sel = chrome.sel_bg;
const page = chrome.page orelse bg_default;
const fg = chrome.fg orelse fg_default;
- const cap = @min(0.22, lightenCeiling(fg, page, sel));
+ // The selection's halo is its own colour; a hovered word's underline is
+ // the theme's accent (its focused grip), like a link's.
+ const glow = if (kind == .selection) chrome.sel_bg else chrome.accent;
+ // The cells a block is made of: the selection's, or the hover
+ // affordance's tint (draw.zig: halfway to a quarter of the selection).
+ const sel = if (kind == .selection) chrome.sel_bg else mixRgb(page, mixRgb(page, chrome.sel_bg));
+ const tracker = if (kind == .selection) &g.sel_glow else &g.hover_glow;
+ const rise_ns: u64 = if (kind == .selection) sel_glow_ns else 80 * std.time.ns_per_ms;
+ const cap = @min(0.22, lightenCeiling(fg, page, glow));
var blocks: [32][4]u16 = undefined;
var nblocks: usize = 0;
var bodies: [pardes.MAX_PANES][4]f32 = undefined;
@@ -9012,28 +9156,189 @@ fn selectionGlow(g: *Gui, gpa: std.mem.Allocator, surface: *const pardes.Surface
}
nblocks = @min(nblocks, blocks.len);
// Each block keeps the start of the one it overlaps from the last frame.
- var next: SelGlow = .{ .now_ns = g.sel_glow.now_ns };
+ var next: SelGlow = .{ .now_ns = tracker.now_ns };
for (blocks[0..nblocks]) |block| {
- const start = for (g.sel_glow.blocks[0..g.sel_glow.len]) |old| {
+ const start = for (tracker.blocks[0..tracker.len]) |old| {
if (old.box[0] < block[2] and block[0] < old.box[2] and old.box[1] < block[3] and block[1] < old.box[3]) break old.start_ns;
- } else g.sel_glow.now_ns;
+ } else tracker.now_ns;
next.blocks[next.len] = .{ .box = block, .start_ns = start };
next.len += 1;
- const t = @as(f32, @floatFromInt(g.sel_glow.now_ns -| start)) / @as(f32, @floatFromInt(sel_glow_ns));
+ const t = @as(f32, @floatFromInt(tracker.now_ns -| start)) / @as(f32, @floatFromInt(rise_ns));
if (t < 1) next.fading = true;
const u = std.math.clamp(t, 0, 1);
const fade = 1 - (1 - u) * (1 - u) * (1 - u);
var caster: [4]f32 = .{ @as(f32, @floatFromInt(block[0])) * layout.w, @as(f32, @floatFromInt(block[1])) * layout.h, @as(f32, @floatFromInt(block[2])) * layout.w, @as(f32, @floatFromInt(block[3])) * layout.h };
- // A cursor beside the selection is not glowed over: it joins the
- // caster.
- if (surface.cursor) |cu| if (cu.x + 1 >= block[0] and cu.x <= block[2] and cu.y + 1 >= block[1] and cu.y <= block[3]) {
- caster = .{ @min(caster[0], @as(f32, @floatFromInt(cu.x)) * layout.w), @min(caster[1], @as(f32, @floatFromInt(cu.y)) * layout.h), @max(caster[2], @as(f32, @floatFromInt(cu.x + 1)) * layout.w), @max(caster[3], @as(f32, @floatFromInt(cu.y + 1)) * layout.h) };
- };
+ const cursor_box: ?[4]f32 = if (surface.cursor) |cu| .{ @as(f32, @floatFromInt(cu.x)) * layout.w, @as(f32, @floatFromInt(cu.y)) * layout.h, @as(f32, @floatFromInt(cu.x + 1)) * layout.w, @as(f32, @floatFromInt(cu.y + 1)) * layout.h } else null;
const first = g.decor.items.len;
- try addSoft(g, gpa, caster, 0.3 * layout.h, 0, 0, cap * fade, sel, null, win_w, win_h);
- try clipToBodies(g, gpa, first, bodies[0..nbodies]);
+ switch (kind) {
+ .selection => {
+ // A cursor beside the selection is not glowed over: it
+ // joins the caster.
+ if (surface.cursor) |cu| if (cu.x + 1 >= block[0] and cu.x <= block[2] and cu.y + 1 >= block[1] and cu.y <= block[3]) {
+ const cb = cursor_box.?;
+ caster = .{ @min(caster[0], cb[0]), @min(caster[1], cb[1]), @max(caster[2], cb[2]), @max(caster[3], cb[3]) };
+ };
+ try addSoft(g, gpa, caster, 0.3 * layout.h, 0, 0, cap * fade, glow, null, win_w, win_h);
+ try clipToBodies(g, gpa, first, bodies[0..nbodies]);
+ },
+ .hover => {
+ // A strip three logical pixels tall along the word's foot,
+ // its edges blurred by one: an underline of light. It is
+ // cast from a row away (addSoft leaves its caster's own
+ // rectangle out), so all of it shows.
+ const foot = caster[3] - dp(1);
+ const away = layout.h;
+ try addSoft(g, gpa, .{ caster[0] + dp(1), foot - dp(3) - away, caster[2] - dp(1), foot - away }, dp(1), 0, away, cap * fade, glow, null, win_w, win_h);
+ // Kept to the bodies, and off a cursor: the parts either
+ // side of it.
+ var parts: [4 * pardes.MAX_PANES][4]f32 = undefined;
+ var nparts: usize = 0;
+ for (bodies[0..nbodies]) |b| {
+ const cb = cursor_box orelse {
+ parts[nparts] = b;
+ nparts += 1;
+ continue;
+ };
+ if (cb[0] >= b[2] or cb[2] <= b[0] or cb[1] >= b[3] or cb[3] <= b[1]) {
+ parts[nparts] = b;
+ nparts += 1;
+ continue;
+ }
+ // Left and right of it, and above and below it.
+ parts[nparts] = .{ b[0], b[1], cb[0], b[3] };
+ parts[nparts + 1] = .{ cb[2], b[1], b[2], b[3] };
+ parts[nparts + 2] = .{ cb[0], b[1], cb[2], cb[1] };
+ parts[nparts + 3] = .{ cb[0], cb[3], cb[2], b[3] };
+ nparts += 4;
+ }
+ try clipToBodies(g, gpa, first, parts[0..nparts]);
+ },
+ }
+ }
+ tracker.* = next;
+}
+
+/// G12: ambient occlusion, a pane body darkening faintly toward its edges
+/// (§9.1: an inner shadow of 1 to 2%): four soft strips cast from just
+/// outside the body inward, clipped to it and round a cursor, black in
+/// linear light, under the same contrast ceiling as a lift.
+fn occlusion(g: *Gui, gpa: std.mem.Allocator, regions: []const pardes.Region, groups: *const Groups, index: usize, surface: *const pardes.Surface, ceilings: LiftCeilings, layout: CellLayout, win_w: f32, win_h: f32, track: ?pardes.animation.Track) !void {
+ const strength = @min(0.02, ceilings.shadow);
+ if (strength <= 0.002) return;
+ const reach = 0.5 * layout.h;
+ for (regions) |body| {
+ if (body.kind != .body or body.rect.w == 0 or body.rect.h == 0 or groupOf(groups, body.serial) != index) continue;
+ const b = bodyPixels(body, layout);
+ // The body, less a cursor's cell in it: left, right, above, below.
+ var parts: [4][4]f32 = undefined;
+ var nparts: usize = 0;
+ const cursor: ?[4]f32 = if (surface.cursor) |cu| .{ @as(f32, @floatFromInt(cu.x)) * layout.w, @as(f32, @floatFromInt(cu.y)) * layout.h, @as(f32, @floatFromInt(cu.x + 1)) * layout.w, @as(f32, @floatFromInt(cu.y + 1)) * layout.h } else null;
+ if (cursor) |cb| if (!(cb[0] >= b[2] or cb[2] <= b[0] or cb[1] >= b[3] or cb[3] <= b[1])) {
+ parts = .{ .{ b[0], b[1], cb[0], b[3] }, .{ cb[2], b[1], b[2], b[3] }, .{ cb[0], b[1], cb[2], cb[1] }, .{ cb[0], cb[3], cb[2], b[3] } };
+ nparts = 4;
+ };
+ if (nparts == 0) {
+ parts[0] = b;
+ nparts = 1;
+ }
+ const t = reach / 3;
+ const strips = [_][4]f32{
+ .{ b[0], b[1] - t, b[2], b[1] },
+ .{ b[0], b[3], b[2], b[3] + t },
+ .{ b[0] - t, b[1], b[0], b[3] },
+ .{ b[2], b[1], b[2] + t, b[3] },
+ };
+ for (strips) |strip| {
+ const first = g.decor.items.len;
+ try addSoft(g, gpa, strip, reach / 3, 0, 0, strength, .{ 0, 0, 0 }, track, win_w, win_h);
+ try clipToBodies(g, gpa, first, parts[0..nparts]);
+ }
}
- g.sel_glow = next;
+}
+
+/// G14's state: the grid last drawn, when each cell last changed, and each
+/// body's line then (a scroll changes every cell and is not a change).
+const Afterglow = struct {
+ cells: []pardes.Cell = &.{},
+ changed: []u64 = &.{},
+ cols: u16 = 0,
+ rows: u16 = 0,
+ lines: [pardes.MAX_PANES]i32 = @splat(std.math.minInt(i32)),
+ /// A cell still glows: the core keeps frames coming.
+ glowing: bool = false,
+
+ fn deinit(glow: *Afterglow, gpa: std.mem.Allocator) void {
+ gpa.free(glow.cells);
+ gpa.free(glow.changed);
+ }
+};
+
+const afterglow_ns: u64 = 250 * std.time.ns_per_ms;
+
+/// G14: a cell of a pane body whose text changed since the last frame
+/// glows in the theme's accent at 18% and fades out over 250 ms with an
+/// ease-out, as a run of cells a row. Never on a tag or a grip (bodies
+/// only), the cursor or a selection; a body whose view moved (a scroll)
+/// starts over rather than lighting up whole.
+fn afterglowDecor(g: *Gui, gpa: std.mem.Allocator, surface: *const pardes.Surface, layout: CellLayout, win_w: f32, win_h: f32) !void {
+ const glow = &g.afterglow;
+ const now = g.sel_glow.now_ns;
+ const n = @as(usize, surface.cols) * surface.rows;
+ const fresh = glow.cols != surface.cols or glow.rows != surface.rows or glow.cells.len != n;
+ if (fresh) {
+ glow.cells = try gpa.realloc(glow.cells, n);
+ glow.changed = try gpa.realloc(glow.changed, n);
+ @memcpy(glow.cells, surface.cells);
+ @memset(glow.changed, 0);
+ glow.cols = surface.cols;
+ glow.rows = surface.rows;
+ }
+ const chrome = &surface.chrome;
+ const page = chrome.page orelse bg_default;
+ const fg = chrome.fg orelse fg_default;
+ const cap = @min(0.18, lightenCeiling(fg, page, chrome.accent));
+ var glowing = false;
+ for (surface.regionList()) |body| {
+ if (body.kind != .body or body.owner >= glow.lines.len) continue;
+ const scrolled = glow.lines[body.owner] != body.line;
+ glow.lines[body.owner] = body.line;
+ var row = body.rect.y;
+ while (row < body.rect.y + body.rect.h and row < surface.rows) : (row += 1) {
+ var run_start: ?u16 = null;
+ var run_age: u64 = 0;
+ var col = body.rect.x;
+ while (col <= body.rect.x + body.rect.w and col <= surface.cols) : (col += 1) {
+ var age: ?u64 = null;
+ if (col < body.rect.x + body.rect.w and col < surface.cols) {
+ const at = @as(usize, row) * surface.cols + col;
+ const cell = surface.cells[at];
+ const differs = !std.meta.eql(cell.style, glow.cells[at].style) or !std.mem.eql(u8, cell.grapheme(), glow.cells[at].grapheme()) or cell.default != glow.cells[at].default;
+ if (differs and !fresh) glow.changed[at] = if (scrolled) 0 else now;
+ const selected = !cell.default and switch (cell.style.bg) {
+ .rgb => |rgb| std.mem.eql(u8, &rgb, &chrome.sel_bg),
+ else => false,
+ };
+ const cursor_here = if (surface.cursor) |cu| cu.x == col and cu.y == row else false;
+ const since = now -| glow.changed[at];
+ if (glow.changed[at] != 0 and since < afterglow_ns and !selected and !cursor_here) age = since;
+ }
+ // A run is cells that changed together.
+ if (run_start) |start| if (age == null or age.? != run_age) {
+ const t = @as(f32, @floatFromInt(run_age)) / @as(f32, @floatFromInt(afterglow_ns));
+ const fade = (1 - t) * (1 - t) * (1 - t);
+ try addDecor(g, gpa, @as(f32, @floatFromInt(start)) * layout.w, @as(f32, @floatFromInt(row)) * layout.h, @as(f32, @floatFromInt(col)) * layout.w, @as(f32, @floatFromInt(row + 1)) * layout.h, chrome.accent, cap * fade, null, win_w, win_h);
+ glowing = true;
+ run_start = null;
+ };
+ if (age) |a| if (run_start == null) {
+ run_start = col;
+ run_age = a;
+ };
+ }
+ }
+ }
+ @memcpy(glow.cells, surface.cells);
+ glow.glowing = glowing;
}
/// A theme's tag plaques (Chrome.decor_box_*), drawn under the cells of
@@ -9048,8 +9353,11 @@ fn tagPlaques(g: *Gui, gpa: std.mem.Allocator, regions: []const pardes.Region, g
const border_rgb = chrome.decor_box_border orelse return;
if (cell_decor.plaques_off) return;
const page = chrome.page orelse bg_default;
- for (regions) |tag| {
+ // The workspace and column plaques first: a pane's plaque right under
+ // them shares their bottom edge (plaqueBox) and draws its frame last.
+ for ([_]bool{ false, true }) |panes| for (regions) |tag| {
const pane = tag.kind == .tag;
+ if (pane != panes) continue;
if (!pane and tag.kind != .column_tag and tag.kind != .workspace_tag) continue;
if (tag.rect.w == 0 or tag.rect.h == 0) continue;
if (pane and groupOf(groups, tag.serial) != index) continue;
@@ -9068,7 +9376,7 @@ fn tagPlaques(g: *Gui, gpa: std.mem.Allocator, regions: []const pardes.Region, g
try addPattern(g, gpa, inner[0], inner[1], inner[2], inner[3], chrome.decor_box_stripe.?, fill, dp(@floatFromInt(@max(1, chrome.decor_box_stripe_px))), true, track, win_w, win_h)
else
try addDecor(g, gpa, inner[0], inner[1], inner[2], inner[3], fill, 1, track, win_w, win_h);
- }
+ };
}
const Plaque = struct { band: [4]f32, box: [4]f32, border: f32, shadow: [2]f32 };
@@ -9077,16 +9385,29 @@ const Plaque = struct { band: [4]f32, box: [4]f32, border: f32, shadow: [2]f32 }
/// grip to the end), the framed box round the tagline's text, and the
/// frame's and the shadow's widths, the shadow cut to what the band's slack
/// below the text and its right end leave. Null where there is no room.
+/// Tags stacked on each other (the workspace's over the columns', a
+/// column's over its first pane's) share one edge, the ruled one between
+/// their bands: the upper box runs down to it and casts no shadow down, the
+/// lower one's frame lies over it. Every box in a column spans the same
+/// pixels across, right of the grips, its shadow short of the next column.
fn plaqueBox(g: *const Gui, chrome: *const pardes.Chrome, regions: []const pardes.Region, tag: pardes.Region, layout: CellLayout, win_w: f32, win_h: f32) ?Plaque {
const pane = tag.kind == .tag;
const first = tag.rect.y;
const last = tag.rect.y + tag.rect.h - 1;
- // From its start, right of its grip: the gap cell before its text is
- // the plaque's margin, so its frame is never under the first letter.
- _ = regions;
- const x0 = @as(f32, @floatFromInt(tag.rect.x)) * layout.w;
+ // Flush against its grip's button, so the two read as one piece: the
+ // gap between button and text (the scroll gap) is the plaque's, and so
+ // is the cell before its text, so its frame is never under the first
+ // letter. A column's plaque starts where the pane's under it does.
+ const grip_kind: pardes.Region.Kind = if (tag.kind == .column_tag) .column_grip else .grip;
+ const x0 = for (regions) |grip| {
+ if (tag.kind != .workspace_tag and grip.kind == grip_kind and grip.owner == tag.owner)
+ break scrollColumn(g, chrome, grip.rect.x, layout)[1];
+ } else @as(f32, @floatFromInt(tag.rect.x)) * layout.w;
const x1_cells = @as(f32, @floatFromInt(tag.rect.x + tag.rect.w)) * layout.w;
- const x1 = if (win_w - x1_cells < layout.w) @max(x1_cells, win_w) else x1_cells;
+ const window_edge = win_w - x1_cells < layout.w;
+ const x1 = if (window_edge) @max(x1_cells, win_w) else x1_cells;
+ const stacked_on = tagBandAt(regions, tag, .above);
+ const stacked_under = tagBandAt(regions, tag, .below);
const row_top = @as(f32, @floatFromInt(first)) * layout.h;
const row_bottom = @as(f32, @floatFromInt(last + 1)) * layout.h;
const text_top = row_top + @as(f32, @floatFromInt(taglineBandOffset(first, win_h, g.cell_h, g.tagline_height)));
@@ -9096,16 +9417,42 @@ fn plaqueBox(g: *const Gui, chrome: *const pardes.Chrome, regions: []const parde
// The shadow's full offset to the right; down, what the slack under the
// text leaves (a pixel or three at a tagline under the body's size).
const want: f32 = if (pane) dp(@floatFromInt(chrome.decor_box_shadow_px)) else dp(2);
- const drop = std.math.clamp(@floor(row_bottom - text_bottom - border), 0, want);
- if (x1 - x0 < 4 * border + want) return null;
+ const drop = if (stacked_under) 0 else std.math.clamp(@floor(row_bottom - text_bottom - border), 0, want);
+ // Every box ends where a pane's does, short of the next column's rule
+ // by a pane frame's width past its shadow, so no shadow meets the rule
+ // and a column's box lines up with its panes'; the window's edge needs
+ // no gap.
+ const pane_shadow = dp(@floatFromInt(chrome.decor_box_shadow_px));
+ const gap = @max(0, pane_shadow - want) + if (window_edge) 0 else dp(@floatFromInt(@max(1, chrome.decor_box_border_px)));
+ if (x1 - x0 < 4 * border + want + gap) return null;
return .{
.band = .{ x0, row_top, x1, row_bottom },
- .box = .{ x0, text_top - border, x1 - want, text_bottom + border },
+ .box = .{
+ x0,
+ if (stacked_on) row_top - border else text_top - border,
+ x1 - want - gap,
+ if (stacked_under) row_bottom else text_bottom + border,
+ },
.border = border,
.shadow = .{ want, drop },
};
}
+/// Whether another tag's band (the workspace's, a column's, a pane's) meets
+/// `tag`'s right above or below it, across some of its width.
+fn tagBandAt(regions: []const pardes.Region, tag: pardes.Region, side: enum { above, below }) bool {
+ for (regions) |other| {
+ if (other.kind != .tag and other.kind != .column_tag and other.kind != .workspace_tag) continue;
+ if (other.rect.h == 0 or std.meta.eql(other.rect, tag.rect)) continue;
+ const meets = switch (side) {
+ .above => other.rect.y + other.rect.h == tag.rect.y,
+ .below => tag.rect.y + tag.rect.h == other.rect.y,
+ };
+ if (meets and other.rect.x < tag.rect.x + tag.rect.w and tag.rect.x < other.rect.x + other.rect.w) return true;
+ }
+ return false;
+}
+
/// The two short bars on each row of a grip with no mode glyph there,
/// centered in its anchor. Only the panes of group `index`.
fn gripDecor(g: *Gui, gpa: std.mem.Allocator, regions: []const pardes.Region, cells: []const pardes.Cell, groups: *const Groups, index: usize, chrome: *const pardes.Chrome, layout: CellLayout, cols: u16, rows: u16, win_w: f32, win_h: f32, track: ?pardes.animation.Track) !void {
@@ -9120,7 +9467,13 @@ fn gripDecor(g: *Gui, gpa: std.mem.Allocator, regions: []const pardes.Region, ce
const left = @as(f32, @floatFromInt(grip.rect.x)) * layout.w;
const right = @as(f32, @floatFromInt(grip.rect.x + grip.rect.w)) * layout.w;
try addDecor(g, gpa, left, button[1], button[0], button[3], band, 1, track, win_w, win_h);
- try addDecor(g, gpa, button[2], button[1], right, button[3], band, 1, track, win_w, win_h);
+ // Right of it, a theme's plaque starts flush against it (plaqueBox).
+ const plaqued = chrome.decor_box_border != null and !cell_decor.plaques_off;
+ if (!plaqued) try addDecor(g, gpa, button[2], button[1], right, button[3], band, 1, track, win_w, win_h);
+ // Its cells over the plaque's reach are let through (emitInstance):
+ // under the button's row, the band goes on as far as the plaque.
+ if (plaqued and grip.rect.h > 1)
+ try addDecor(g, gpa, left, button[3], button[2], @as(f32, @floatFromInt(grip.rect.y + grip.rect.h)) * layout.h, band, 1, track, win_w, win_h);
if (grip.kind != .grip) continue;
// acme's button ring, two pixels (ButtonBorder) inside the button:
// the focused one's in its own fill. An unfocused dirty one keeps a
@@ -9480,7 +9833,7 @@ test "the selection glows round its block, on the body only, fading in over 100
core.settings.selection_glow = true;
pardes.test_api.sync(core);
const s = try core.render(core.scratch.allocator());
- try std.testing.expectEqual(@as(u8, 1), s.chrome.selection_glow);
+ try std.testing.expectEqual(@as(u8, 1), s.chrome.fx);
// Two rows of a selection in the body, as the core paints one.
const body = for (s.regionList()) |region| {
if (region.kind == .body) break region;
@@ -9522,6 +9875,183 @@ test "the selection glows round its block, on the body only, fading in over 100
try std.testing.expect(!g.sel_glow.fading);
}
+test "a hovered word glows along its foot, never over the cursor, fading in over 80 ms" {
+ const gpa = std.testing.allocator;
+ const core = try pardes.Pardes.init(gpa, .{ .cols = 80, .rows = 24 });
+ defer core.deinit();
+ _ = try core.setTestFile("some text on a line\n" ** 30);
+ core.settings.hover_glow = true;
+ pardes.test_api.sync(core);
+ const s = try core.render(core.scratch.allocator());
+ try std.testing.expectEqual(@as(u8, 2), s.chrome.fx);
+ const body = for (s.regionList()) |region| {
+ if (region.kind == .body) break region;
+ } else return error.NoBody;
+ const page = s.chrome.page orelse bg_default;
+ const tint = mixRgb(page, mixRgb(page, s.chrome.sel_bg));
+ const row = body.rect.y + 4;
+ for (5..12) |x| {
+ const cell = &s.cells[@as(usize, row) * s.cols + body.rect.x + x];
+ cell.default = false;
+ cell.style.bg = .{ .rgb = tint };
+ }
+ s.cursor = .{ .x = body.rect.x + 8, .y = row };
+ var g: Gui = undefined;
+ g.decor = .empty;
+ defer g.decor.deinit(gpa);
+ g.cell_w = 10;
+ g.cell_h = 20;
+ g.tagline_width = 6;
+ g.tagline_height = 12;
+ g.sel_glow = .{};
+ g.hover_glow = .{ .now_ns = std.time.ns_per_s };
+ var strongest: f32 = 0;
+ for ([_]u64{ 0, 90 * std.time.ns_per_ms }) |later| {
+ g.decor.clearRetainingCapacity();
+ g.hover_glow.now_ns = std.time.ns_per_s + later;
+ var groups = makeGroups(&.{}, false);
+ try buildDecor(&g, gpa, s, &groups, fixedCellLayout(&g), 800, 480, true);
+ for (g.decor.items) |item| {
+ if (item.effect & soft_shadow_bit == 0) continue;
+ if (later == 0) try std.testing.expect(false);
+ strongest = @max(strongest, item.fr);
+ // Never over the cursor's cell.
+ const cx0: f32 = @floatFromInt(@as(u32, s.cursor.?.x) * 10);
+ const cy0: f32 = @floatFromInt(@as(u32, s.cursor.?.y) * 20);
+ try std.testing.expect(item.clip_x1 <= cx0 + 0.01 or item.clip_x0 >= cx0 + 10 - 0.01 or item.clip_y1 <= cy0 + 0.01 or item.clip_y0 >= cy0 + 20 - 0.01);
+ }
+ }
+ try std.testing.expect(strongest > 0.05);
+ try std.testing.expect(!g.hover_glow.fading);
+}
+
+test "occlusion shades each body's edges, inside it, never a tag, a grip or the cursor" {
+ const gpa = std.testing.allocator;
+ const core = try pardes.Pardes.init(gpa, .{ .cols = 80, .rows = 24 });
+ defer core.deinit();
+ _ = try core.setTestFile("some text on a line\n" ** 30);
+ core.settings.occlusion = true;
+ pardes.test_api.sync(core);
+ const s = try core.render(core.scratch.allocator());
+ try std.testing.expectEqual(@as(u8, 4), s.chrome.fx);
+ const body = for (s.regionList()) |region| {
+ if (region.kind == .body) break region;
+ } else return error.NoBody;
+ // The cursor on the body's top-left cell, where the shade is.
+ s.cursor = .{ .x = body.rect.x, .y = body.rect.y };
+ var g: Gui = undefined;
+ g.decor = .empty;
+ defer g.decor.deinit(gpa);
+ g.cell_w = 10;
+ g.cell_h = 20;
+ g.tagline_width = 6;
+ g.tagline_height = 12;
+ g.sel_glow = .{};
+ g.hover_glow = .{};
+ var groups = makeGroups(&.{}, false);
+ try buildDecor(&g, gpa, s, &groups, fixedCellLayout(&g), 800, 480, true);
+ const r = bodyPixels(body, fixedCellLayout(&g));
+ var shades: usize = 0;
+ for (g.decor.items) |item| {
+ if (item.effect & soft_shadow_bit == 0) continue;
+ shades += 1;
+ try std.testing.expect(item.fr <= 0.02 + 1e-6);
+ try std.testing.expect(item.clip_x0 >= r[0] - 0.01 and item.clip_x1 <= r[2] + 0.01 and item.clip_y0 >= r[1] - 0.01 and item.clip_y1 <= r[3] + 0.01);
+ const cx: f32 = @floatFromInt(@as(u32, s.cursor.?.x) * 10);
+ const cy: f32 = @floatFromInt(@as(u32, s.cursor.?.y) * 20);
+ try std.testing.expect(item.clip_x1 <= cx + 0.01 or item.clip_x0 >= cx + 10 - 0.01 or item.clip_y1 <= cy + 0.01 or item.clip_y0 >= cy + 20 - 0.01);
+ }
+ try std.testing.expect(shades >= 4);
+}
+
+test "a body tells its first line, and lapis's dots under it move a quarter as far" {
+ const gpa = std.testing.allocator;
+ const core = try pardes.Pardes.init(gpa, .{ .cols = 80, .rows = 24 });
+ defer core.deinit();
+ const pane = try core.setTestFile("line\n" ** 80);
+ pane.scrollBy(8);
+ try std.testing.expect(core.executeBuiltinLine(0, "Theme lapis"));
+ core.settings.parallax = true;
+ for (0..40) |_| core.update(.tick);
+ pardes.test_api.sync(core);
+ const s = try core.render(core.scratch.allocator());
+ const body = for (s.regionList()) |region| {
+ if (region.kind == .body) break region;
+ } else return error.NoBody;
+ try std.testing.expectEqual(@as(i32, 8), body.line);
+ try std.testing.expect(s.chrome.fx & 8 != 0);
+ // The shift the cell shader is handed: a quarter of 8 rows of 20 px,
+ // upward, within the dots' period.
+ var decor = CellDecor.of(&s.chrome);
+ const period = decor.uniforms[4];
+ decor.bodies[0] = .{ 0, 0, 800, 480 };
+ decor.shifts[0] = @mod(-8.0 * 20.0 * 0.25, period);
+ decor.nbodies = 1;
+ try std.testing.expectApproxEqAbs(@mod(-40.0, period), decor.parallaxAt(10, 10), 1e-4);
+ try std.testing.expectEqual(@as(f32, 0), decor.parallaxAt(900, 10));
+}
+
+test "changed text glows and fades in 250 ms; the cursor and a scroll do not light up" {
+ const gpa = std.testing.allocator;
+ const core = try pardes.Pardes.init(gpa, .{ .cols = 80, .rows = 24 });
+ defer core.deinit();
+ const pane = try core.setTestFile("line\n" ** 60);
+ core.settings.afterglow = true;
+ pardes.test_api.sync(core);
+ const s = try core.render(core.scratch.allocator());
+ const body = for (s.regionList()) |region| {
+ if (region.kind == .body) break region;
+ } else return error.NoBody;
+ s.cursor = .{ .x = body.rect.x + 12, .y = body.rect.y + 2 };
+ var g: Gui = undefined;
+ g.decor = .empty;
+ defer g.decor.deinit(gpa);
+ g.cell_w = 10;
+ g.cell_h = 20;
+ g.afterglow = .{};
+ defer g.afterglow.deinit(gpa);
+ const layout = fixedCellLayout(&g);
+ const Count = struct {
+ fn glows(items: []const CellInstance, accent: [3]u8) usize {
+ var n: usize = 0;
+ for (items) |item| {
+ const rgb: [3]u8 = .{ @intFromFloat(@round(item.br * 255)), @intFromFloat(@round(item.bg * 255)), @intFromFloat(@round(item.bb * 255)) };
+ if (std.mem.eql(u8, &rgb, &accent) and item.fr > 0.001) n += 1;
+ }
+ return n;
+ }
+ };
+ g.sel_glow = .{ .now_ns = std.time.ns_per_s };
+ try afterglowDecor(&g, gpa, s, layout, 800, 480);
+ try std.testing.expectEqual(@as(usize, 0), g.decor.items.len);
+ // Three cells of a row change, the cursor's among them.
+ for (10..13) |x| {
+ const cell = &s.cells[@as(usize, body.rect.y + 2) * s.cols + body.rect.x + x];
+ cell.* = .{ .default = false, .style = .{ .fg = .{ .rgb = .{ 1, 2, 3 } } } };
+ cell.text[0] = 'X';
+ cell.len = 1;
+ }
+ g.sel_glow.now_ns += 16 * std.time.ns_per_ms;
+ try afterglowDecor(&g, gpa, s, layout, 800, 480);
+ try std.testing.expectEqual(@as(usize, 1), Count.glows(g.decor.items, s.chrome.accent));
+ const lit = g.decor.items[0];
+ // Two cells wide: the cursor's is left out.
+ try std.testing.expectApproxEqAbs(@as(f32, 2 * 10 * 2.0 / 800.0), lit.x1 - lit.x0, 1e-4);
+ try std.testing.expect(g.afterglow.glowing);
+ // Gone by 250 ms.
+ g.decor.clearRetainingCapacity();
+ g.sel_glow.now_ns += 260 * std.time.ns_per_ms;
+ try afterglowDecor(&g, gpa, s, layout, 800, 480);
+ try std.testing.expectEqual(@as(usize, 0), g.decor.items.len);
+ try std.testing.expect(!g.afterglow.glowing);
+ // A scroll changes every cell and lights none.
+ pane.scrollBy(5);
+ const scrolled = try core.render(core.scratch.allocator());
+ g.sel_glow.now_ns += 16 * std.time.ns_per_ms;
+ try afterglowDecor(&g, gpa, scrolled, layout, 800, 480);
+ try std.testing.expectEqual(@as(usize, 0), g.decor.items.len);
+}
+
/// What a bundled post pass leaves alone (Post.Frame.spare), in window
/// pixels: every tag with its grip, the column and workspace tags, and the
/// notices, the chrome the focus indicators live in. A grip, the gap after
@@ -9649,7 +10179,6 @@ fn buildOverlay(
fn appendCursorGlide(g: *Gui, surface: *const pardes.Surface, builder: *OverlayBuilder) void {
const chrome = &surface.chrome;
if (g.cursor_quad_alpha <= 0.001) return;
- if (surface.panelTracks().len != 0) return;
const layout = fixedCellLayout(g);
var px: [4][2]f32 = undefined;
var centre: [2]f32 = .{ 0, 0 };
diff --git a/src/look.zig b/src/look.zig
index 9a81a5c9..ca9aa80a 100644
--- a/src/look.zig
+++ b/src/look.zig
@@ -946,6 +946,12 @@ fn noteLookHover(p: *Pardes, col: u16, row: u16, body_hit: ?Mouse.BodyHit, tag_h
if (p.look_hover_wait) |waiting|
if (waiting.col == col and waiting.row == row and waiting.pane == id and waiting.serial == pane.serial and mouse.sameBodyCell(waiting.body_hit, body_hit) and tag_layer.sameCell(waiting.tag_hit, tag_hit)) return;
+ // Found nothing to open here a moment ago: the pointer resting on it
+ // does not start another wait (and another frame) every frame.
+ if (p.look_hover_refused) |refused|
+ if (refused.col == col and refused.row == row and refused.pane == id and refused.serial == pane.serial and mouse.sameBodyCell(refused.body_hit, body_hit) and tag_layer.sameCell(refused.tag_hit, tag_hit)) return;
+ p.look_hover_refused = null;
+
cancelLookHover(p);
p.look_hover_wait = .{ .col = col, .row = row, .pane = id, .serial = pane.serial, .body_hit = body_hit, .tag_hit = tag_hit };
}
@@ -970,8 +976,10 @@ pub fn advanceLookHover(p: *Pardes) void {
if (waiting.frames < delay) return;
const pane = p.panes[waiting.pane] orelse return cancelLookHover(p);
if (pane.serial != waiting.serial) return cancelLookHover(p);
- const pointed = edit.pointerTextSelection(p, waiting.pane, waiting.col, waiting.row, waiting.body_hit, waiting.tag_hit) orelse
+ const pointed = edit.pointerTextSelection(p, waiting.pane, waiting.col, waiting.row, waiting.body_hit, waiting.tag_hit) orelse {
+ p.look_hover_refused = waiting.*;
return cancelLookHover(p);
+ };
if (comptime pdf_enabled) if (!pointed.on_tag and panes.Pdf.paneNativeReady(p, pane)) {
const probe = panes.Pdf.probeAt(p, pane, waiting.col, waiting.row) orelse
return cancelLookHover(p);
@@ -988,7 +996,10 @@ pub fn advanceLookHover(p: *Pardes) void {
return;
};
const operand = exec.pointerOperand(p, pane, pointed.sel);
- if (operand.text == null or operand.text.?.len == 0) return cancelLookHover(p);
+ if (operand.text == null or operand.text.?.len == 0) {
+ p.look_hover_refused = waiting.*;
+ return cancelLookHover(p);
+ }
var preview = operand.preview;
if (preview) |*sel| sel.state = .done;
p.look_hover_preview = .{
@@ -1759,3 +1770,21 @@ test "Look ignores missing and out of bounds pane addresses" {
try std.testing.expect(pane.body.vsel.active); // a miss changes nothing, the selection included
}
}
+
+test "a pointer resting where a look-hover found nothing does not wake the core every frame" {
+ const p = try Pardes.init(std.testing.allocator, .{ .cols = 80, .rows = 24 });
+ defer p.deinit();
+ _ = try p.setTestFile(" \n" ** 30);
+ p.sync();
+ const r = p.rects[p.active];
+ // Resting on blanks in the body: a wait, then nothing to open.
+ p.update(.{ .mouse = .{ .button = .none, .kind = .motion, .col = r.x + 20, .row = r.y + 5 } });
+ var frames: usize = 0;
+ while (p.nextWake() != null and frames < 20) : (frames += 1) {
+ p.update(.tick);
+ // What a shell's present does after every frame.
+ p.acknowledgePanelPresentation(&.{});
+ }
+ try std.testing.expect(frames < 20);
+ try std.testing.expect(p.look_hover_wait == null and p.look_hover_preview == null);
+}
diff --git a/src/main.zig b/src/main.zig
index b784ec97..aaa237f6 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -411,6 +411,7 @@ test {
_ = @import("locations_cache.zig");
_ = @import("fs.zig");
_ = @import("detached/wire.zig");
+ _ = @import("shader_build.zig");
_ = @import("detached/server.zig");
_ = @import("detached/client.zig");
_ = @import("9p_io.zig");
diff --git a/src/pardes.zig b/src/pardes.zig
index 33aff840..0950d13a 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -4368,7 +4368,9 @@ pub const Pardes = struct {
/// scroll, G6): the core then keeps the continuous path running, so the
/// shell draws every loop and a virtual clock moves on, as for a lift.
shell_continuous: bool = false,
- cursor_view: struct { serial: u32 = 0, line: i64 = 0 } = .{},
+ cursor_view: struct { serial: u32 = 0, line: i64 = 0, origin: [2]f32 = .{ 0, 0 }, inside: bool = true } = .{},
+ /// The cursor glides from one pane to another: it may cross the grid.
+ cursor_crossing: bool = false,
input_ns: u64 = 0,
animate_theme_changes: bool = false,
native_images: bool = false,
@@ -4398,6 +4400,9 @@ pub const Pardes = struct {
/// The look just run found nothing (look.zig missed).
look_missed: bool = false,
look_hover_wait: ?look.LookHoverWait = null,
+ /// The place a look-hover last found nothing to open: the pointer
+ /// resting there does not ask again every frame (look.noteLookHover).
+ look_hover_refused: ?look.LookHoverWait = null,
look_hover_preview: ?look.LookHoverPreview = null,
pdf_hover_preview: if (pdf_enabled) ?look.PdfWordPreview else void = if (pdf_enabled) null else {},
wheel_guard: u8 = 0,
diff --git a/src/shader_build.zig b/src/shader_build.zig
new file mode 100644
index 00000000..8aa7eb5e
--- /dev/null
+++ b/src/shader_build.zig
@@ -0,0 +1,402 @@
+//! The post chain's Shadertoy files as SPIR-V (docs/render-pipeline.md §6),
+//! for whichever process holds the core: a local GUI makes its pipelines
+//! from them, a detached session sends them to the GUIs attached to it
+//! (wire `post`), which never read a disk or run a program themselves.
+//!
+//! Each file compiles behind the prefix by the build's own glslc invocation,
+//! on a thread of its own and never on the frame path, when it joins the
+//! chain and again whenever a save changes its bytes: the files' directories
+//! are watched (file_watch.watchShaders) and a change there has every file
+//! read and hashed, an unchanged one compiling nothing. A compile that fails
+//! keeps the last good SPIR-V and is said once, and glslc missing is said
+//! once for good.
+const std = @import("std");
+const libc = std.c;
+const pardes = @import("pardes.zig");
+const file_watch = @import("file_watch.zig");
+
+const Build = @This();
+const Chain = pardes.config.Runtime.Post;
+const Scene = pardes.config.Runtime.Scene;
+pub const max = Chain.max;
+
+const prefix = @embedFile("post-prefix.glsl");
+
+/// A pass as a shell draws it: a bundled scene at its level, or a file with
+/// its last good SPIR-V (empty until a compile succeeds), whose `revision`
+/// moves each time that SPIR-V does. Slices borrow the build's (or, in an
+/// attached GUI, the client's) storage until its next change.
+pub const Pass = struct {
+ scene: ?Scene = null,
+ level: u8 = 0,
+ path: []const u8 = &.{},
+ spirv: []const u8 = &.{},
+ revision: u32 = 0,
+};
+
+/// Who to wake when a compile is done: the host's loop.
+pub const Wake = struct {
+ ctx: ?*anyopaque = null,
+ call: ?*const fn (?*anyopaque) void = null,
+};
+
+const File = struct {
+ path: []u8 = &.{},
+ /// The hash of the source last compiled, good or not: the same bytes
+ /// saved again compile nothing and say nothing twice.
+ source: ?u64 = null,
+ spirv: []u8 = &.{},
+ revision: u32 = 0,
+ /// Wants a compile: new to the chain, or its directory changed.
+ stale: bool = true,
+};
+
+const Result = union(enum) {
+ none,
+ unchanged,
+ missing,
+ failed: struct { text: []u8, source: u64 },
+ spirv: struct { bytes: []u8, source: u64 },
+};
+
+/// One compile of the stale files, on its own thread.
+const Job = struct {
+ io: std.Io,
+ gpa: std.mem.Allocator,
+ wake: Wake,
+ paths: [max][]u8 = @splat(&.{}),
+ known: [max]?u64 = @splat(null),
+ results: [max]Result = @splat(.none),
+ len: usize = 0,
+ done: std.atomic.Value(bool) = .init(false),
+ thread: std.Thread = undefined,
+};
+
+files: [max]File = @splat(.{}),
+len: usize = 0,
+/// The chain generation `files` follow; null before the first.
+generation: ?u32 = null,
+/// Moves whenever what `view` gives does: the chain or a file's SPIR-V.
+revision: u32 = 0,
+job: ?*Job = null,
+missing_said: bool = false,
+
+/// Brings the files to the chain the core holds, watches their directories,
+/// takes in a finished compile and starts the next. Each loop step, off the
+/// frame path. True when a compile was taken in: what it said wants a frame.
+pub fn sync(b: *Build, gpa: std.mem.Allocator, io: std.Io, core: *pardes.Pardes, fd: c_int, watches: *file_watch.Table, wake: Wake) bool {
+ var collected = false;
+ if (b.job) |job| {
+ if (!job.done.load(.acquire)) return false;
+ job.thread.join();
+ b.job = null;
+ b.collect(gpa, core, job);
+ collected = true;
+ }
+ const chain = &core.settings.post;
+ if (b.generation != chain.generation) {
+ b.generation = chain.generation;
+ b.revision +%= 1;
+ var next: [max]File = @splat(.{});
+ var len: usize = 0;
+ var paths: [max][]const u8 = undefined;
+ for (chain.list()) |entry| {
+ if (entry.scene != null) continue;
+ next[len] = for (b.files[0..b.len]) |*file| {
+ if (!std.mem.eql(u8, file.path, entry.path.get())) continue;
+ const kept = file.*;
+ file.* = .{};
+ break kept;
+ } else .{ .path = gpa.dupe(u8, entry.path.get()) catch continue };
+ paths[len] = next[len].path;
+ len += 1;
+ }
+ for (b.files[0..b.len]) |file| free(gpa, file);
+ b.files = next;
+ b.len = len;
+ var bufs: [max][4096]u8 = undefined;
+ var expanded: [max][]const u8 = undefined;
+ for (paths[0..len], 0..) |path, i| expanded[i] = expand(&bufs[i], path);
+ file_watch.watchShaders(fd, watches, expanded[0..len]);
+ }
+ var stale = false;
+ for (b.files[0..b.len]) |file| stale = stale or file.stale;
+ if (!stale) return collected;
+ const job = gpa.create(Job) catch return collected;
+ job.* = .{ .io = io, .gpa = gpa, .wake = wake };
+ for (b.files[0..b.len]) |*file| {
+ if (!file.stale) continue;
+ job.paths[job.len] = gpa.dupe(u8, file.path) catch continue;
+ job.known[job.len] = file.source;
+ job.len += 1;
+ file.stale = false;
+ }
+ job.thread = std.Thread.spawn(.{}, compile, .{job}) catch {
+ for (job.paths[0..job.len]) |path| gpa.free(path);
+ gpa.destroy(job);
+ return collected;
+ };
+ b.job = job;
+ return collected;
+}
+
+/// A watched directory changed: every file is read again at the next sync.
+pub fn recheck(b: *Build) void {
+ for (b.files[0..b.len]) |*file| file.stale = true;
+}
+
+/// The chain as passes, in its order.
+pub fn view(b: *const Build, chain: *const Chain, out: *[max]Pass) []const Pass {
+ for (chain.list(), 0..) |entry, i| {
+ out[i] = if (entry.scene) |scene| .{ .scene = scene, .level = entry.level } else for (b.files[0..b.len]) |file| {
+ if (std.mem.eql(u8, file.path, entry.path.get())) break .{ .path = file.path, .spirv = file.spirv, .revision = file.revision };
+ } else .{ .path = entry.path.get() };
+ }
+ return out[0..chain.len];
+}
+
+pub fn deinit(b: *Build, gpa: std.mem.Allocator, fd: c_int, watches: *file_watch.Table) void {
+ if (b.job) |job| {
+ job.thread.join();
+ freeJob(gpa, job);
+ }
+ for (b.files[0..b.len]) |file| free(gpa, file);
+ b.len = 0;
+ file_watch.watchShaders(fd, watches, &.{});
+}
+
+fn free(gpa: std.mem.Allocator, file: File) void {
+ gpa.free(file.path);
+ gpa.free(file.spirv);
+}
+
+fn freeJob(gpa: std.mem.Allocator, job: *Job) void {
+ for (job.paths[0..job.len], job.results[0..job.len]) |path, result| {
+ gpa.free(path);
+ switch (result) {
+ .spirv => |r| gpa.free(r.bytes),
+ .failed => |r| gpa.free(r.text),
+ .none, .unchanged, .missing => {},
+ }
+ }
+ gpa.destroy(job);
+}
+
+fn collect(b: *Build, gpa: std.mem.Allocator, core: *pardes.Pardes, job: *Job) void {
+ defer gpa.destroy(job);
+ for (job.paths[0..job.len], job.results[0..job.len]) |path, result| {
+ defer gpa.free(path);
+ const file = for (b.files[0..b.len]) |*file| {
+ if (std.mem.eql(u8, file.path, path)) break file;
+ } else null;
+ var buf: [512]u8 = undefined;
+ switch (result) {
+ .none, .unchanged => {},
+ .missing => if (!b.missing_said) {
+ b.missing_said = true;
+ core.setMessage(core.active, "Shader: glslc is not on PATH, so Shadertoy files do not run (Crt does)");
+ },
+ .failed => |r| {
+ defer gpa.free(r.text);
+ if (file) |f| f.source = r.source;
+ // glslc's first line says where: in the file's own lines.
+ const first = std.mem.sliceTo(r.text, '\n');
+ core.setMessage(core.active, std.fmt.bufPrint(&buf, "Shader {s}: {s}", .{ path, first[0..@min(first.len, 400)] }) catch "Shader: compile failed");
+ },
+ .spirv => |r| {
+ const f = file orelse {
+ gpa.free(r.bytes);
+ continue;
+ };
+ gpa.free(f.spirv);
+ f.spirv = r.bytes;
+ f.source = r.source;
+ f.revision +%= 1;
+ b.revision +%= 1;
+ },
+ }
+ }
+}
+
+/// `~/` is the home directory.
+fn expand(buf: []u8, path: []const u8) []const u8 {
+ if (!std.mem.startsWith(u8, path, "~/")) return path;
+ const home = libc.getenv("HOME") orelse return path;
+ return std.fmt.bufPrint(buf, "{s}/{s}", .{ std.mem.span(home), path[2..] }) catch path;
+}
+
+/// The compile thread: each file behind the prefix, through glslc with the
+/// build's own flags (build.zig compileGlsl), source on stdin, SPIR-V on
+/// stdout.
+fn compile(job: *Job) void {
+ for (job.paths[0..job.len], 0..) |path, i| job.results[i] = compileOne(job.gpa, job.io, path, job.known[i]);
+ job.done.store(true, .release);
+ if (job.wake.call) |call| call(job.wake.ctx);
+}
+
+fn compileOne(gpa: std.mem.Allocator, io: std.Io, path: []const u8, known: ?u64) Result {
+ var home_buf: [4096]u8 = undefined;
+ // Not fs.zig's readFile: that hands the core's turn over for the read,
+ // a turn this thread never holds.
+ const body = std.Io.Dir.cwd().readFileAlloc(io, expand(&home_buf, path), gpa, .limited(1 << 20)) catch |err|
+ return .{ .failed = .{ .text = std.fmt.allocPrint(gpa, "cannot read it ({t})", .{err}) catch return .none, .source = 0 } };
+ defer gpa.free(body);
+ const source = std.hash.Wyhash.hash(0, body);
+ if (known == source) return .unchanged;
+ // The prefix and the file go to glslc on its stdin: no file of ours is
+ // written anywhere. std's spawn does every allocation before fork and
+ // searches PATH on the stack after it (Io/Threaded.zig spawnPosix,
+ // posixExecv), so a child forked from this thread cannot wedge on a
+ // lock another thread held (the forkShell hazard).
+ var child = std.process.spawn(io, .{
+ .argv = &.{ "glslc", "-fshader-stage=fragment", "-o", "-", "-" },
+ .stdin = .pipe,
+ .stdout = .pipe,
+ .stderr = .pipe,
+ }) catch |err| return switch (err) {
+ error.FileNotFound => .missing,
+ else => .{ .failed = .{ .text = std.fmt.allocPrint(gpa, "glslc did not run ({t})", .{err}) catch return .none, .source = source } },
+ };
+ defer child.kill(io);
+ // glslc reads all of its input before it writes a byte, so writing it
+ // first cannot deadlock against a full output pipe.
+ {
+ var stdin = child.stdin.?;
+ child.stdin = null;
+ defer stdin.close(io);
+ var buf: [4096]u8 = undefined;
+ var writer = stdin.writer(io, &buf);
+ writer.interface.writeAll(prefix) catch {};
+ writer.interface.writeAll(body) catch {};
+ writer.interface.flush() catch {};
+ }
+ var streams: std.Io.File.MultiReader.Buffer(2) = undefined;
+ var reader: std.Io.File.MultiReader = undefined;
+ reader.init(gpa, io, streams.toStreams(), &.{ child.stdout.?, child.stderr.? });
+ defer reader.deinit();
+ const failed = struct {
+ fn of(a: std.mem.Allocator, text: []const u8, hash: u64) Result {
+ return .{ .failed = .{ .text = a.dupe(u8, text) catch return .none, .source = hash } };
+ }
+ }.of;
+ while (reader.fill(64, .none)) |_| {
+ if (reader.reader(0).buffered().len > 16 << 20 or reader.reader(1).buffered().len > 64 << 10)
+ return failed(gpa, "glslc said too much", source);
+ } else |err| switch (err) {
+ error.EndOfStream => {},
+ else => return failed(gpa, "reading glslc failed", source),
+ }
+ const term = child.wait(io) catch return failed(gpa, "glslc did not finish", source);
+ const clean = switch (term) {
+ .exited => |code| code == 0,
+ else => false,
+ };
+ if (clean) return .{ .spirv = .{ .bytes = reader.toOwnedSlice(0) catch return .none, .source = source } };
+ const said = reader.toOwnedSlice(1) catch return .none;
+ // glslc calls its input <stdin>; the file is what a person knows.
+ defer gpa.free(said);
+ const named = std.mem.replaceOwned(u8, gpa, said, "<stdin>", path) catch return failed(gpa, "glslc failed", source);
+ return .{ .failed = .{ .text = named, .source = source } };
+}
+
+test "ghostty's test shaders compile behind the prefix, and its invalid one fails with glslc's words" {
+ const gpa = std.testing.allocator;
+ const io = std.testing.io;
+ // ghostty's own test shaders, from its package in zig-pkg.
+ var pkg = std.Io.Dir.cwd().openDir(io, "zig-pkg", .{ .iterate = true }) catch return error.SkipZigTest;
+ defer pkg.close(io);
+ var it = pkg.iterate();
+ const ghostty = while (it.next(io) catch null) |entry| {
+ if (std.mem.startsWith(u8, entry.name, "ghostty-")) break entry.name;
+ } else return error.SkipZigTest;
+ var buf: [512]u8 = undefined;
+ for ([_][]const u8{ "crt", "focus", "invalid" }) |name| {
+ const path = try std.fmt.bufPrint(&buf, "zig-pkg/{s}/src/renderer/shaders/test_shadertoy_{s}.glsl", .{ ghostty, name });
+ switch (compileOne(gpa, io, path, null)) {
+ .missing => return error.SkipZigTest, // no glslc here
+ .spirv => |r| {
+ defer gpa.free(r.bytes);
+ try std.testing.expect(!std.mem.eql(u8, name, "invalid"));
+ // SPIR-V's magic number, little-endian.
+ try std.testing.expectEqual(@as(u32, 0x07230203), std.mem.readInt(u32, r.bytes[0..4], .little));
+ // The same bytes again compile nothing.
+ try std.testing.expectEqual(Result.unchanged, compileOne(gpa, io, path, r.source));
+ },
+ .failed => |r| {
+ defer gpa.free(r.text);
+ try std.testing.expectEqualStrings("invalid", name);
+ // Named by the file, not by glslc's <stdin>.
+ try std.testing.expect(std.mem.indexOf(u8, r.text, path) != null);
+ try std.testing.expect(std.mem.indexOf(u8, r.text, "<stdin>") == null);
+ },
+ .none, .unchanged => return error.TestUnexpectedResult,
+ }
+ }
+}
+
+test "a save that changes a file compiles it again, a failed one keeps the last good SPIR-V and is said once" {
+ const gpa = std.testing.allocator;
+ const io = std.testing.io;
+ const core = try pardes.Pardes.init(gpa, .{ .cols = 80, .rows = 24 });
+ defer core.deinit();
+ _ = try core.newShell(1, "");
+ const pane = core.panes[core.active].?;
+ var tmp = std.testing.tmpDir(.{});
+ defer tmp.cleanup();
+ var dir_buf: [4096]u8 = undefined;
+ const dir = dir_buf[0..try tmp.dir.realPath(io, &dir_buf)];
+ var path_buf: [4200]u8 = undefined;
+ const path = try std.fmt.bufPrint(&path_buf, "{s}/wave.glsl", .{dir});
+ const good = "void mainImage(out vec4 o, in vec2 p) { o = texture(iChannel0, p / iResolution.xy); }\n";
+ try tmp.dir.writeFile(io, .{ .sub_path = "wave.glsl", .data = good });
+ try std.testing.expect(core.settings.post.set(null, path, true));
+
+ const fd = file_watch.init(true);
+ defer if (fd >= 0) {
+ _ = libc.close(fd);
+ };
+ var watches: file_watch.Table = @splat(null);
+ var b: Build = .{};
+ defer b.deinit(gpa, fd, &watches);
+ const settle = struct {
+ fn run(build: *Build, c: *pardes.Pardes, f: c_int, w: *file_watch.Table) void {
+ _ = build.sync(std.testing.allocator, std.testing.io, c, f, w, .{});
+ while (build.job) |job| {
+ while (!job.done.load(.acquire)) std.Thread.yield() catch {};
+ _ = build.sync(std.testing.allocator, std.testing.io, c, f, w, .{});
+ }
+ }
+ }.run;
+ settle(&b, core, fd, &watches);
+ if (b.missing_said) return error.SkipZigTest; // no glslc here
+ var out: [max]Pass = undefined;
+ const first = b.view(&core.settings.post, &out)[0];
+ try std.testing.expect(first.spirv.len != 0);
+ if (file_watch.supported) try std.testing.expect(watches[file_watch.shader_slot] != null);
+
+ // Unchanged bytes: nothing moves.
+ const at = b.revision;
+ b.recheck();
+ settle(&b, core, fd, &watches);
+ try std.testing.expectEqual(at, b.revision);
+
+ // A bad save: the last good SPIR-V stays, the error is said, and the
+ // same bad bytes again say nothing more.
+ try tmp.dir.writeFile(io, .{ .sub_path = "wave.glsl", .data = "void mainImage(" });
+ pane.msg_len = 0;
+ b.recheck();
+ settle(&b, core, fd, &watches);
+ try std.testing.expectEqual(at, b.revision);
+ try std.testing.expect(std.mem.startsWith(u8, pane.msg[0..pane.msg_len], "Shader "));
+ pane.msg_len = 0;
+ b.recheck();
+ settle(&b, core, fd, &watches);
+ try std.testing.expectEqual(@as(u16, 0), pane.msg_len);
+
+ // A good save again: new SPIR-V, a new revision.
+ try tmp.dir.writeFile(io, .{ .sub_path = "wave.glsl", .data = "// saved again\n" ++ good });
+ b.recheck();
+ settle(&b, core, fd, &watches);
+ const second = b.view(&core.settings.post, &out)[0];
+ try std.testing.expect(second.revision != first.revision and b.revision != at);
+}
diff --git a/src/surface.zig b/src/surface.zig
index 7256ecc1..22a71a27 100644
--- a/src/surface.zig
+++ b/src/surface.zig
@@ -370,6 +370,10 @@ pub const Region = struct {
/// focus (Lift, on a spring), a notice's while Lift is on. A pixel shell
/// casts a soft shadow from it; the tier it is drawn in never changes.
lift: f32 = 0,
+ /// A body's view: the first line it shows (a file's scroll, a
+ /// terminal's grid offset), for what a pixel shell moves with the text
+ /// (G13's parallax).
+ line: i32 = 0,
/// Cells the column-move rail takes off the right edge while it runs
/// there: layers and pixel shells stop short of them, the grid does not.
clip: u16 = 0,
@@ -448,8 +452,12 @@ pub const Chrome = struct {
decor_title_px: u8 = 2,
name_ink: [3]u8 = .{ 0, 0, 0 },
active_name_ink: [3]u8 = .{ 0, 0, 0 },
- /// G9: a pixel shell glows round the body's selection (SelectionGlow).
- selection_glow: u8 = 0,
+ /// A pixel shell's small effects: bit 0 a glow round the body's
+ /// selection (G9, SelectionGlow), bit 1 one under the word a look-hover
+ /// would open (G11, HoverGlow), bit 2 the bodies' edge shade (G12,
+ /// Occlusion), bit 3 the page pattern's parallax (G13, Parallax), bit 4
+ /// changed text's afterglow (G14, Afterglow).
+ fx: u8 = 0,
};
/// The canonical interface: what a frame of pardes IS. The tty shell writes
diff --git a/src/themes/alucard.zig b/src/themes/alucard.zig
new file mode 100644
index 00000000..1b5695cc
--- /dev/null
+++ b/src/themes/alucard.zig
@@ -0,0 +1,76 @@
+//! Alucard (Dracula light): Dracula, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! official palette (Dracula, Alucard); url https://github.com/dracula/dracula-theme; genizah ~/05-genizah/dracula-theme; commit 1e04a4b768302fa2de37d09cc7b8087eabcd3ed8
+//! official VS Code port: roles, UI, ANSI, Soft transform; url https://github.com/dracula/visual-studio-code; genizah ~/05-genizah/dracula-visual-studio-code; commit a08a206f2c8420ba3c05f0e8d01d43b2f933fdf8
+//! Tags: the tab (focused = the active tab, unfocused = the inactive one).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - unfocused tag text #6c664b -> #5d5841: 3.75:1 on #cfcfde, tag text needs 4.5:1 to be read and run
+//! Upstream notes: The README gives Alucard only as a palette; roles follow
+//! dracula.yml, UI surfaces it lacks are derived.
+pub const theme = .{
+ .name = "alucard",
+ // ~/05-genizah/dracula-theme/README.md:34 Alucard Background; role as dracula.yml editor.background *BG
+ .bg = .{ 0xff, 0xfb, 0xeb },
+ // ~/05-genizah/dracula-theme/README.md:37 Alucard Foreground; role as dracula.yml editor.foreground *FG
+ .fg = .{ 0x1f, 0x1f, 0x1f },
+ // ~/05-genizah/dracula-theme/README.md:36 Alucard Selection; role as dracula.yml editor.selectionBackground *SELECTION
+ .sel_bg = .{ 0xcf, 0xcf, 0xde },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x1f, 0x1f, 0x1f },
+ // ~/05-genizah/dracula-theme/README.md:36 Alucard Selection; role as dracula.yml tab.inactiveBackground (*BGDark: Alucard has no BG variants; Selection derived)
+ .tag_bg = .{ 0xcf, 0xcf, 0xde },
+ // ~/05-genizah/dracula-theme/README.md:38 Alucard Comment; role as dracula.yml tab.inactiveForeground *COMMENT #6c664b, adjusted
+ .tag_fg = .{ 0x5d, 0x58, 0x41 },
+ // ~/05-genizah/dracula-theme/README.md:34 Alucard Background; role as dracula.yml tab.activeBackground *BG
+ .tag_active_bg = .{ 0xff, 0xfb, 0xeb },
+ // ~/05-genizah/dracula-theme/README.md:37 Alucard Foreground; role as dracula.yml tab.activeForeground *FG
+ .tag_active_fg = .{ 0x1f, 0x1f, 0x1f },
+ // the focused grip: ~/05-genizah/dracula-theme/README.md:42 Alucard Pink; role as dracula.yml tab.activeBorderTop *PINK
+ .box = .{ 0xa3, 0x14, 0x4d },
+ // derived: the grip at rest, the accent #a3144d faded toward the unfocused tag #cfcfde as far as 1.6:1 off it (0.30 of the way)
+ .box_dim = .{ 0xc2, 0x97, 0xb3 },
+ // an unsaved grip: the original's warning colour, ~/05-genizah/dracula-theme/README.md:39 Alucard Cyan; role as dracula.yml editorWarning.foreground *CYAN
+ .box_dirty = .{ 0x03, 0x6a, 0x96 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/dracula-theme/README.md:39 Alucard Cyan; role as dracula.yml *CYAN
+ .column_box = .{ 0x03, 0x6a, 0x96 },
+ // ~/05-genizah/dracula-theme/README.md:42 Alucard Pink; role as dracula.yml Keywords *PINK
+ .kw = .{ 0xa3, 0x14, 0x4d },
+ // ~/05-genizah/dracula-theme/README.md:45 Alucard Yellow; role as dracula.yml Strings *YELLOW
+ .str = .{ 0x84, 0x6e, 0x15 },
+ // ~/05-genizah/dracula-theme/README.md:43 Alucard Purple; role as dracula.yml Constants *PURPLE
+ .num = .{ 0x64, 0x4a, 0xc9 },
+ // ~/05-genizah/dracula-theme/README.md:38 Alucard Comment; role as dracula.yml Comments *COMMENT
+ .comment = .{ 0x6c, 0x66, 0x4b },
+ // ~/05-genizah/dracula-theme/README.md:38 Alucard Comment; role as dracula.yml editorLineNumber.foreground *COMMENT
+ .lineno = .{ 0x6c, 0x66, 0x4b },
+ // derived: none in the original; the page
+ .scroll_track = .{ 0xff, 0xfb, 0xeb },
+ // derived: none in the original; line numbers, ~/05-genizah/dracula-theme/README.md:38 Alucard Comment; role as dracula.yml editorLineNumber.foreground *COMMENT
+ .scroll_thumb = .{ 0x6c, 0x66, 0x4b },
+ // ~/05-genizah/dracula-theme/README.md:43 Alucard Purple; role as dracula.yml editorGroup.border *PURPLE
+ .border = .{ 0x64, 0x4a, 0xc9 },
+ // ~/05-genizah/dracula-theme/README.md:44 Alucard Red; role as dracula.yml editorError.foreground *RED
+ .diagnostic_error = .{ 0xcb, 0x3a, 0x2a },
+ // ~/05-genizah/dracula-theme/README.md:39 Alucard Cyan; role as dracula.yml editorWarning.foreground *CYAN
+ .diagnostic_warning = .{ 0x03, 0x6a, 0x96 },
+ .palette = .{
+ .{ 0x1f, 0x1f, 0x1f }, // 0: ~/05-genizah/dracula-theme/README.md:37 Alucard Foreground (ANSI 0 derived: Dracula's ansi order with Alucard colours, dark ink as black)
+ .{ 0xcb, 0x3a, 0x2a }, // 1: ~/05-genizah/dracula-theme/README.md:44 Alucard Red (ANSI 1 derived: Dracula's ansi order with Alucard colours, dark ink as black)
+ .{ 0x14, 0x71, 0x0a }, // 2: ~/05-genizah/dracula-theme/README.md:40 Alucard Green (ANSI 2 derived: Dracula's ansi order with Alucard colours, dark ink as black)
+ .{ 0x84, 0x6e, 0x15 }, // 3: ~/05-genizah/dracula-theme/README.md:45 Alucard Yellow (ANSI 3 derived: Dracula's ansi order with Alucard colours, dark ink as black)
+ .{ 0x64, 0x4a, 0xc9 }, // 4: ~/05-genizah/dracula-theme/README.md:43 Alucard Purple (ANSI 4 derived: Dracula's ansi order with Alucard colours, dark ink as black)
+ .{ 0xa3, 0x14, 0x4d }, // 5: ~/05-genizah/dracula-theme/README.md:42 Alucard Pink (ANSI 5 derived: Dracula's ansi order with Alucard colours, dark ink as black)
+ .{ 0x03, 0x6a, 0x96 }, // 6: ~/05-genizah/dracula-theme/README.md:39 Alucard Cyan (ANSI 6 derived: Dracula's ansi order with Alucard colours, dark ink as black)
+ .{ 0xcf, 0xcf, 0xde }, // 7: ~/05-genizah/dracula-theme/README.md:36 Alucard Selection (ANSI 7 derived: Dracula's ansi order with Alucard colours, dark ink as black)
+ .{ 0x6c, 0x66, 0x4b }, // 8: ~/05-genizah/dracula-theme/README.md:38 Alucard Comment (ANSI 8 derived: Dracula's ansi order with Alucard colours, dark ink as black)
+ .{ 0xcb, 0x3a, 0x2a }, // 9: ~/05-genizah/dracula-theme/README.md:44 Alucard Red (ANSI 9 derived: Dracula's ansi order with Alucard colours, dark ink as black)
+ .{ 0x14, 0x71, 0x0a }, // 10: ~/05-genizah/dracula-theme/README.md:40 Alucard Green (ANSI 10 derived: Dracula's ansi order with Alucard colours, dark ink as black)
+ .{ 0x84, 0x6e, 0x15 }, // 11: ~/05-genizah/dracula-theme/README.md:45 Alucard Yellow (ANSI 11 derived: Dracula's ansi order with Alucard colours, dark ink as black)
+ .{ 0x64, 0x4a, 0xc9 }, // 12: ~/05-genizah/dracula-theme/README.md:43 Alucard Purple (ANSI 12 derived: Dracula's ansi order with Alucard colours, dark ink as black)
+ .{ 0xa3, 0x14, 0x4d }, // 13: ~/05-genizah/dracula-theme/README.md:42 Alucard Pink (ANSI 13 derived: Dracula's ansi order with Alucard colours, dark ink as black)
+ .{ 0x03, 0x6a, 0x96 }, // 14: ~/05-genizah/dracula-theme/README.md:39 Alucard Cyan (ANSI 14 derived: Dracula's ansi order with Alucard colours, dark ink as black)
+ .{ 0xff, 0xfb, 0xeb }, // 15: ~/05-genizah/dracula-theme/README.md:34 Alucard Background (ANSI 15 derived: Dracula's ansi order with Alucard colours, dark ink as black)
+ },
+};
diff --git a/src/themes/ayu_dark.zig b/src/themes/ayu_dark.zig
new file mode 100644
index 00000000..62fb29ed
--- /dev/null
+++ b/src/themes/ayu_dark.zig
@@ -0,0 +1,105 @@
+//! Ayu Dark: Ayu, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! palette (themes/*.yaml, generated src/generated/*.ts); url https://github.com/ayu-theme/ayu-colors; genizah ~/05-genizah/ayu-colors; commit b0fd979a1ddf050101b43311fa598a1a9c5f1bbc
+//! VS Code port (roles, terminal colours); url https://github.com/ayu-theme/vscode-ayu; genizah ~/05-genizah/vscode-ayu; commit d676974ebb245fa5a7ae4444027f72801017f1b6
+//! Tags: the tab (focused = the active tab, unfocused = the inactive one).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #10141c -> #33363b: 1.03:1 off the unfocused #0d1017, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #5a6378 -> #747c8e: 3.16:1 on #0d1017, tag text needs 4.5:1 to be read and run
+//! - rules between panes #1b1f29 -> #393b42: 1.12:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! - scroll thumb #2e3441 -> #313743: 1.48:1 on its track #10141c
+//! Upstream notes: Roles from vscode-ayu's generated ayu-dark.json (bordered
+//! variant, the one package.json ships as the default); syntax roles are its
+//! tokenColors and each is checked equal to ayu-colors syntax.* in
+//! src/generated/dark.ts (that file is ayu-colors' own generator output: `npx
+//! tsx scripts/generate-themes.ts` run in the genizah checkout; it resolves the
+//! OKLCH l1..l5 steps and -L/A modifiers of themes/dark.yaml). type =
+//! tokenColors "Entity name" (scope entity.name, covers entity.name.type; there
+//! is no entity.name.type rule). number and constant share syntax.constant.
+//! status_* from VS Code statusBar.* (one status bar: status_nc_* null).
+//! tabline_* from tab.*; tabline_fill = editorGroupHeader.tabsBackground. Alpha
+//! composited over editor.background (selection, line numbers, cursorline,
+//! split, scrollbar thumb). scroll_track null: VS Code has no track key
+//! (transparent over the editor). search_fg, selection_fg null: not set.
+//! info/hint null: neither vscode-ayu nor ayu-colors defines an info/hint
+//! colour (VS Code falls back to its own defaults). accent = focusBorder =
+//! common.accent.tint. ansi is vscode-ayu's terminal.ansi*
+//! (src/template.ts:458-473: normal = syntax.* darken(0.1) except green =
+//! vcs.added, black/white/brightBlack from a fixed table at template.ts:5),
+//! which differs from ayu-colors' own terminal.* section (listed in palette as
+//! terminal.*). palette = every colour of src/generated/dark.ts; alpha entries
+//! composited over editor.bg (editor.*) or ui.bg (others); skipped shadows:
+//! ui.panel.shadow, ui.popup.shadow.
+pub const theme = .{
+ .name = "ayu_dark",
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:106 editor.background; source ~/05-genizah/ayu-colors/src/generated/dark.ts:146 editor.bg #10141c
+ .bg = .{ 0x10, 0x14, 0x1c },
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:107 editor.foreground; source ~/05-genizah/ayu-colors/src/generated/dark.ts:128 editor.fg #bfbdb6
+ .fg = .{ 0xbf, 0xbd, 0xb6 },
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:112 editor.selectionBackground #3388ff40 composited over editor.background #10141c (~/05-genizah/vscode-ayu/ayu-dark.json:106 editor.background)
+ .sel_bg = .{ 0x19, 0x31, 0x55 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xbf, 0xbd, 0xb6 },
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:102 tab.inactiveBackground; source ~/05-genizah/ayu-colors/src/generated/dark.ts:163 ui.bg #0d1017
+ .tag_bg = .{ 0x0d, 0x10, 0x17 },
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:103 tab.inactiveForeground; source ~/05-genizah/ayu-colors/src/generated/dark.ts:149 ui.fg #5a6378 #5a6378, adjusted
+ .tag_fg = .{ 0x74, 0x7c, 0x8e },
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:96 tab.activeBackground; source ~/05-genizah/ayu-colors/src/generated/dark.ts:146 editor.bg #10141c #10141c, adjusted
+ .tag_active_bg = .{ 0x33, 0x36, 0x3b },
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:97 tab.activeForeground; source ~/05-genizah/ayu-colors/src/generated/dark.ts:128 editor.fg #bfbdb6
+ .tag_active_fg = .{ 0xbf, 0xbd, 0xb6 },
+ // the focused grip: ~/05-genizah/vscode-ayu/ayu-dark.json:4 focusBorder [also tab.activeBorderTop]; source ~/05-genizah/ayu-colors/src/generated/dark.ts:167 common.accent.tint #e6b450
+ .box = .{ 0xe6, 0xb4, 0x50 },
+ // derived: the grip at rest, the accent #e6b450 faded toward the unfocused tag #0d1017 as far as 1.6:1 off it (0.24 of the way)
+ .box_dim = .{ 0x41, 0x37, 0x25 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/vscode-ayu/ayu-dark.json:145 editorGutter.modifiedBackground [also gitDecoration.modifiedResourceForeground]; source ~/05-genizah/ayu-colors/src/generated/dark.ts:118 vcs.modified #73b8ff
+ .box_dirty = .{ 0x73, 0xb8, 0xff },
+ // derived: the column grip, the original's type colour, ~/05-genizah/vscode-ayu/ayu-dark.json:622 tokenColors "Entity name" foreground; = syntax.entity (~/05-genizah/ayu-colors/src/generated/dark.ts:88 syntax.entity)
+ .column_box = .{ 0x59, 0xc2, 0xff },
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:460 tokenColors "Keyword" foreground; = syntax.keyword (~/05-genizah/ayu-colors/src/generated/dark.ts:92 syntax.keyword)
+ .kw = .{ 0xff, 0x8f, 0x40 },
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:384 tokenColors "String" foreground; = syntax.string (~/05-genizah/ayu-colors/src/generated/dark.ts:89 syntax.string)
+ .str = .{ 0xaa, 0xd9, 0x4c },
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:404 tokenColors "Number" foreground; = syntax.constant (~/05-genizah/ayu-colors/src/generated/dark.ts:95 syntax.constant)
+ .num = .{ 0xd2, 0xa6, 0xff },
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:374 tokenColors "Comment" foreground; = syntax.comment (~/05-genizah/ayu-colors/src/generated/dark.ts:94 syntax.comment)
+ .comment = .{ 0x5a, 0x66, 0x73 },
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:108 editorLineNumber.foreground #5a6378a6 composited over editor.background #10141c (~/05-genizah/vscode-ayu/ayu-dark.json:106 editor.background) [gutter has no own bg, so editor bg]
+ .lineno = .{ 0x40, 0x47, 0x58 },
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:109 editorLineNumber.activeForeground [gutter has no own bg, so editor bg]
+ .lineno_active = .{ 0x5a, 0x63, 0x78 },
+ // derived: none in the original; the page
+ .scroll_track = .{ 0x10, 0x14, 0x1c },
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:44 scrollbarSlider.background #5a637866 composited over editor.background #10141c (~/05-genizah/vscode-ayu/ayu-dark.json:106 editor.background) #2e3441, adjusted
+ .scroll_thumb = .{ 0x31, 0x37, 0x43 },
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:92 editorGroup.border; source ~/05-genizah/ayu-colors/src/generated/dark.ts:150 ui.line #1b1f29 #1b1f29, adjusted
+ .border = .{ 0x39, 0x3b, 0x42 },
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:119 editor.findMatchBackground; source ~/05-genizah/ayu-colors/src/generated/dark.ts:135 editor.findMatch.active #4c4126
+ .search_bg = .{ 0x4c, 0x41, 0x26 },
+ // derived: the original keeps text colours over a match; the page's text, whichever reads better on it
+ .search_fg = .{ 0xbf, 0xbd, 0xb6 },
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:143 editorError.foreground; source ~/05-genizah/ayu-colors/src/generated/dark.ts:170 common.error #d95757
+ .diagnostic_error = .{ 0xd9, 0x57, 0x57 },
+ // ~/05-genizah/vscode-ayu/ayu-dark.json:144 editorWarning.foreground; source ~/05-genizah/ayu-colors/src/generated/dark.ts:167 common.accent.tint #e6b450
+ .diagnostic_warning = .{ 0xe6, 0xb4, 0x50 },
+ .palette = .{
+ .{ 0x1b, 0x1f, 0x29 }, // 0: ~/05-genizah/vscode-ayu/ayu-dark.json:264 terminal.ansiBlack
+ .{ 0xf0, 0x6b, 0x73 }, // 1: ~/05-genizah/vscode-ayu/ayu-dark.json:265 terminal.ansiRed
+ .{ 0x70, 0xbf, 0x56 }, // 2: ~/05-genizah/vscode-ayu/ayu-dark.json:266 terminal.ansiGreen
+ .{ 0xfd, 0xb0, 0x4c }, // 3: ~/05-genizah/vscode-ayu/ayu-dark.json:267 terminal.ansiYellow
+ .{ 0x4f, 0xbf, 0xff }, // 4: ~/05-genizah/vscode-ayu/ayu-dark.json:268 terminal.ansiBlue
+ .{ 0xd0, 0xa1, 0xff }, // 5: ~/05-genizah/vscode-ayu/ayu-dark.json:269 terminal.ansiMagenta
+ .{ 0x93, 0xe2, 0xc8 }, // 6: ~/05-genizah/vscode-ayu/ayu-dark.json:270 terminal.ansiCyan
+ .{ 0xc7, 0xc7, 0xc7 }, // 7: ~/05-genizah/vscode-ayu/ayu-dark.json:271 terminal.ansiWhite
+ .{ 0x68, 0x68, 0x68 }, // 8: ~/05-genizah/vscode-ayu/ayu-dark.json:272 terminal.ansiBrightBlack
+ .{ 0xf0, 0x71, 0x78 }, // 9: ~/05-genizah/vscode-ayu/ayu-dark.json:273 terminal.ansiBrightRed
+ .{ 0xaa, 0xd9, 0x4c }, // 10: ~/05-genizah/vscode-ayu/ayu-dark.json:274 terminal.ansiBrightGreen
+ .{ 0xff, 0xb4, 0x54 }, // 11: ~/05-genizah/vscode-ayu/ayu-dark.json:275 terminal.ansiBrightYellow
+ .{ 0x59, 0xc2, 0xff }, // 12: ~/05-genizah/vscode-ayu/ayu-dark.json:276 terminal.ansiBrightBlue
+ .{ 0xd2, 0xa6, 0xff }, // 13: ~/05-genizah/vscode-ayu/ayu-dark.json:277 terminal.ansiBrightMagenta
+ .{ 0x95, 0xe6, 0xcb }, // 14: ~/05-genizah/vscode-ayu/ayu-dark.json:278 terminal.ansiBrightCyan
+ .{ 0xff, 0xff, 0xff }, // 15: ~/05-genizah/vscode-ayu/ayu-dark.json:279 terminal.ansiBrightWhite
+ },
+};
diff --git a/src/themes/ayu_light.zig b/src/themes/ayu_light.zig
new file mode 100644
index 00000000..22436be7
--- /dev/null
+++ b/src/themes/ayu_light.zig
@@ -0,0 +1,104 @@
+//! Ayu Light: Ayu, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! palette (themes/*.yaml, generated src/generated/*.ts); url https://github.com/ayu-theme/ayu-colors; genizah ~/05-genizah/ayu-colors; commit b0fd979a1ddf050101b43311fa598a1a9c5f1bbc
+//! VS Code port (roles, terminal colours); url https://github.com/ayu-theme/vscode-ayu; genizah ~/05-genizah/vscode-ayu; commit d676974ebb245fa5a7ae4444027f72801017f1b6
+//! Tags: the tab (focused = the active tab, unfocused = the inactive one).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #fcfcfc -> #dcddde: 1.03:1 off the unfocused #f8f9fa, pardes needs 1.25:1 on a light page
+//! - unfocused tag text #828e9f -> #68727f: 3.15:1 on #f8f9fa, tag text needs 4.5:1 to be read and run
+//! - rules between panes #eaedef -> #c5c9cb: 1.15:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: Roles from vscode-ayu's generated ayu-light.json (bordered
+//! variant, the one package.json ships as the default); syntax roles are its
+//! tokenColors and each is checked equal to ayu-colors syntax.* in
+//! src/generated/light.ts (that file is ayu-colors' own generator output: `npx
+//! tsx scripts/generate-themes.ts` run in the genizah checkout; it resolves the
+//! OKLCH l1..l5 steps and -L/A modifiers of themes/light.yaml). type =
+//! tokenColors "Entity name" (scope entity.name, covers entity.name.type; there
+//! is no entity.name.type rule). number and constant share syntax.constant.
+//! status_* from VS Code statusBar.* (one status bar: status_nc_* null).
+//! tabline_* from tab.*; tabline_fill = editorGroupHeader.tabsBackground. Alpha
+//! composited over editor.background (selection, line numbers, cursorline,
+//! split, scrollbar thumb). scroll_track null: VS Code has no track key
+//! (transparent over the editor). search_fg, selection_fg null: not set.
+//! info/hint null: neither vscode-ayu nor ayu-colors defines an info/hint
+//! colour (VS Code falls back to its own defaults). accent = focusBorder =
+//! common.accent.tint. ansi is vscode-ayu's terminal.ansi*
+//! (src/template.ts:458-473: normal = syntax.* darken(0.1) except green =
+//! vcs.added, black/white/brightBlack from a fixed table at template.ts:5),
+//! which differs from ayu-colors' own terminal.* section (listed in palette as
+//! terminal.*). palette = every colour of src/generated/light.ts; alpha entries
+//! composited over editor.bg (editor.*) or ui.bg (others); skipped shadows:
+//! ui.panel.shadow, ui.popup.shadow.
+pub const theme = .{
+ .name = "ayu_light",
+ // ~/05-genizah/vscode-ayu/ayu-light.json:106 editor.background; source ~/05-genizah/ayu-colors/src/generated/light.ts:157 editor.bg #fcfcfc
+ .bg = .{ 0xfc, 0xfc, 0xfc },
+ // ~/05-genizah/vscode-ayu/ayu-light.json:107 editor.foreground; source ~/05-genizah/ayu-colors/src/generated/light.ts:140 editor.fg #5c6166
+ .fg = .{ 0x5c, 0x61, 0x66 },
+ // ~/05-genizah/vscode-ayu/ayu-light.json:112 editor.selectionBackground #035bd626 composited over editor.background #fcfcfc (~/05-genizah/vscode-ayu/ayu-light.json:106 editor.background)
+ .sel_bg = .{ 0xd7, 0xe4, 0xf6 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x5c, 0x61, 0x66 },
+ // ~/05-genizah/vscode-ayu/ayu-light.json:102 tab.inactiveBackground; source ~/05-genizah/ayu-colors/src/generated/light.ts:175 ui.bg #f8f9fa
+ .tag_bg = .{ 0xf8, 0xf9, 0xfa },
+ // ~/05-genizah/vscode-ayu/ayu-light.json:103 tab.inactiveForeground; source ~/05-genizah/ayu-colors/src/generated/light.ts:161 ui.fg #828e9f #828e9f, adjusted
+ .tag_fg = .{ 0x68, 0x72, 0x7f },
+ // ~/05-genizah/vscode-ayu/ayu-light.json:96 tab.activeBackground; source ~/05-genizah/ayu-colors/src/generated/light.ts:157 editor.bg #fcfcfc #fcfcfc, adjusted
+ .tag_active_bg = .{ 0xdc, 0xdd, 0xde },
+ // ~/05-genizah/vscode-ayu/ayu-light.json:97 tab.activeForeground; source ~/05-genizah/ayu-colors/src/generated/light.ts:140 editor.fg #5c6166
+ .tag_active_fg = .{ 0x5c, 0x61, 0x66 },
+ // the focused grip: ~/05-genizah/vscode-ayu/ayu-light.json:4 focusBorder [also tab.activeBorderTop]; source ~/05-genizah/ayu-colors/src/generated/light.ts:179 common.accent.tint #f29718
+ .box = .{ 0xf2, 0x97, 0x18 },
+ // derived: the grip at rest, the accent #f29718 faded toward the unfocused tag #f8f9fa as far as 1.6:1 off it (0.62 of the way)
+ .box_dim = .{ 0xf4, 0xbc, 0x6e },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/vscode-ayu/ayu-light.json:145 editorGutter.modifiedBackground [also gitDecoration.modifiedResourceForeground]; source ~/05-genizah/ayu-colors/src/generated/light.ts:130 vcs.modified #478acc
+ .box_dirty = .{ 0x47, 0x8a, 0xcc },
+ // derived: the column grip, the original's type colour, ~/05-genizah/vscode-ayu/ayu-light.json:622 tokenColors "Entity name" foreground; = syntax.entity (~/05-genizah/ayu-colors/src/generated/light.ts:92 syntax.entity)
+ .column_box = .{ 0x22, 0xa4, 0xe6 },
+ // ~/05-genizah/vscode-ayu/ayu-light.json:460 tokenColors "Keyword" foreground; = syntax.keyword (~/05-genizah/ayu-colors/src/generated/light.ts:100 syntax.keyword)
+ .kw = .{ 0xfa, 0x85, 0x32 },
+ // ~/05-genizah/vscode-ayu/ayu-light.json:384 tokenColors "String" foreground; = syntax.string (~/05-genizah/ayu-colors/src/generated/light.ts:94 syntax.string)
+ .str = .{ 0x86, 0xb3, 0x00 },
+ // ~/05-genizah/vscode-ayu/ayu-light.json:404 tokenColors "Number" foreground; = syntax.constant (~/05-genizah/ayu-colors/src/generated/light.ts:106 syntax.constant)
+ .num = .{ 0xa3, 0x7a, 0xcc },
+ // ~/05-genizah/vscode-ayu/ayu-light.json:374 tokenColors "Comment" foreground; = syntax.comment (~/05-genizah/ayu-colors/src/generated/light.ts:104 syntax.comment)
+ .comment = .{ 0xad, 0xae, 0xb1 },
+ // ~/05-genizah/vscode-ayu/ayu-light.json:108 editorLineNumber.foreground #828e9f66 composited over editor.background #fcfcfc (~/05-genizah/vscode-ayu/ayu-light.json:106 editor.background) [gutter has no own bg, so editor bg]
+ .lineno = .{ 0xcb, 0xd0, 0xd7 },
+ // ~/05-genizah/vscode-ayu/ayu-light.json:109 editorLineNumber.activeForeground #828e9fcc composited over editor.background #fcfcfc (~/05-genizah/vscode-ayu/ayu-light.json:106 editor.background) [gutter has no own bg, so editor bg]
+ .lineno_active = .{ 0x9a, 0xa4, 0xb2 },
+ // derived: none in the original; the page
+ .scroll_track = .{ 0xfc, 0xfc, 0xfc },
+ // ~/05-genizah/vscode-ayu/ayu-light.json:44 scrollbarSlider.background #828e9f66 composited over editor.background #fcfcfc (~/05-genizah/vscode-ayu/ayu-light.json:106 editor.background)
+ .scroll_thumb = .{ 0xcb, 0xd0, 0xd7 },
+ // ~/05-genizah/vscode-ayu/ayu-light.json:92 editorGroup.border #6b7d8f1f composited over editor.background #fcfcfc (~/05-genizah/vscode-ayu/ayu-light.json:106 editor.background); source ~/05-genizah/ayu-colors/src/generated/light.ts:162 ui.line #6b7d8f1f #eaedef, adjusted
+ .border = .{ 0xc5, 0xc9, 0xcb },
+ // ~/05-genizah/vscode-ayu/ayu-light.json:119 editor.findMatchBackground; source ~/05-genizah/ayu-colors/src/generated/light.ts:146 editor.findMatch.active #ffe294
+ .search_bg = .{ 0xff, 0xe2, 0x94 },
+ // derived: the original keeps text colours over a match; the page's text, whichever reads better on it
+ .search_fg = .{ 0x5c, 0x61, 0x66 },
+ // ~/05-genizah/vscode-ayu/ayu-light.json:143 editorError.foreground; source ~/05-genizah/ayu-colors/src/generated/light.ts:182 common.error #e65050
+ .diagnostic_error = .{ 0xe6, 0x50, 0x50 },
+ // ~/05-genizah/vscode-ayu/ayu-light.json:144 editorWarning.foreground; source ~/05-genizah/ayu-colors/src/generated/light.ts:179 common.accent.tint #f29718
+ .diagnostic_warning = .{ 0xf2, 0x97, 0x18 },
+ .palette = .{
+ .{ 0x00, 0x00, 0x00 }, // 0: ~/05-genizah/vscode-ayu/ayu-light.json:264 terminal.ansiBlack
+ .{ 0xf0, 0x6b, 0x6c }, // 1: ~/05-genizah/vscode-ayu/ayu-light.json:265 terminal.ansiRed
+ .{ 0x6c, 0xbf, 0x43 }, // 2: ~/05-genizah/vscode-ayu/ayu-light.json:266 terminal.ansiGreen
+ .{ 0xe7, 0xa1, 0x00 }, // 3: ~/05-genizah/vscode-ayu/ayu-light.json:267 terminal.ansiYellow
+ .{ 0x21, 0xa1, 0xe2 }, // 4: ~/05-genizah/vscode-ayu/ayu-light.json:268 terminal.ansiBlue
+ .{ 0xa1, 0x76, 0xcb }, // 5: ~/05-genizah/vscode-ayu/ayu-light.json:269 terminal.ansiMagenta
+ .{ 0x4a, 0xbc, 0x96 }, // 6: ~/05-genizah/vscode-ayu/ayu-light.json:270 terminal.ansiCyan
+ .{ 0xc7, 0xc7, 0xc7 }, // 7: ~/05-genizah/vscode-ayu/ayu-light.json:271 terminal.ansiWhite
+ .{ 0x68, 0x68, 0x68 }, // 8: ~/05-genizah/vscode-ayu/ayu-light.json:272 terminal.ansiBrightBlack
+ .{ 0xf0, 0x71, 0x71 }, // 9: ~/05-genizah/vscode-ayu/ayu-light.json:273 terminal.ansiBrightRed
+ .{ 0x86, 0xb3, 0x00 }, // 10: ~/05-genizah/vscode-ayu/ayu-light.json:274 terminal.ansiBrightGreen
+ .{ 0xeb, 0xa4, 0x00 }, // 11: ~/05-genizah/vscode-ayu/ayu-light.json:275 terminal.ansiBrightYellow
+ .{ 0x22, 0xa4, 0xe6 }, // 12: ~/05-genizah/vscode-ayu/ayu-light.json:276 terminal.ansiBrightBlue
+ .{ 0xa3, 0x7a, 0xcc }, // 13: ~/05-genizah/vscode-ayu/ayu-light.json:277 terminal.ansiBrightMagenta
+ .{ 0x4c, 0xbf, 0x99 }, // 14: ~/05-genizah/vscode-ayu/ayu-light.json:278 terminal.ansiBrightCyan
+ .{ 0xd1, 0xd1, 0xd1 }, // 15: ~/05-genizah/vscode-ayu/ayu-light.json:279 terminal.ansiBrightWhite
+ },
+};
diff --git a/src/themes/ayu_mirage.zig b/src/themes/ayu_mirage.zig
new file mode 100644
index 00000000..23aff67f
--- /dev/null
+++ b/src/themes/ayu_mirage.zig
@@ -0,0 +1,104 @@
+//! Ayu Mirage: Ayu, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! palette (themes/*.yaml, generated src/generated/*.ts); url https://github.com/ayu-theme/ayu-colors; genizah ~/05-genizah/ayu-colors; commit b0fd979a1ddf050101b43311fa598a1a9c5f1bbc
+//! VS Code port (roles, terminal colours); url https://github.com/ayu-theme/vscode-ayu; genizah ~/05-genizah/vscode-ayu; commit d676974ebb245fa5a7ae4444027f72801017f1b6
+//! Tags: the tab (focused = the active tab, unfocused = the inactive one).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #242936 -> #3f434c: 1.07:1 off the unfocused #1f2430, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #707a8c -> #848d9c: 3.58:1 on #1f2430, tag text needs 4.5:1 to be read and run
+//! - rules between panes #171b24 -> #46494d: 1.19:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: Roles from vscode-ayu's generated ayu-mirage.json (bordered
+//! variant, the one package.json ships as the default); syntax roles are its
+//! tokenColors and each is checked equal to ayu-colors syntax.* in
+//! src/generated/mirage.ts (that file is ayu-colors' own generator output: `npx
+//! tsx scripts/generate-themes.ts` run in the genizah checkout; it resolves the
+//! OKLCH l1..l5 steps and -L/A modifiers of themes/mirage.yaml). type =
+//! tokenColors "Entity name" (scope entity.name, covers entity.name.type; there
+//! is no entity.name.type rule). number and constant share syntax.constant.
+//! status_* from VS Code statusBar.* (one status bar: status_nc_* null).
+//! tabline_* from tab.*; tabline_fill = editorGroupHeader.tabsBackground. Alpha
+//! composited over editor.background (selection, line numbers, cursorline,
+//! split, scrollbar thumb). scroll_track null: VS Code has no track key
+//! (transparent over the editor). search_fg, selection_fg null: not set.
+//! info/hint null: neither vscode-ayu nor ayu-colors defines an info/hint
+//! colour (VS Code falls back to its own defaults). accent = focusBorder =
+//! common.accent.tint. ansi is vscode-ayu's terminal.ansi*
+//! (src/template.ts:458-473: normal = syntax.* darken(0.1) except green =
+//! vcs.added, black/white/brightBlack from a fixed table at template.ts:5),
+//! which differs from ayu-colors' own terminal.* section (listed in palette as
+//! terminal.*). palette = every colour of src/generated/mirage.ts; alpha
+//! entries composited over editor.bg (editor.*) or ui.bg (others); skipped
+//! shadows: ui.panel.shadow, ui.popup.shadow.
+pub const theme = .{
+ .name = "ayu_mirage",
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:106 editor.background; source ~/05-genizah/ayu-colors/src/generated/mirage.ts:146 editor.bg #242936
+ .bg = .{ 0x24, 0x29, 0x36 },
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:107 editor.foreground; source ~/05-genizah/ayu-colors/src/generated/mirage.ts:128 editor.fg #cccac2
+ .fg = .{ 0xcc, 0xca, 0xc2 },
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:112 editor.selectionBackground #409fff40 composited over editor.background #242936 (~/05-genizah/vscode-ayu/ayu-mirage.json:106 editor.background)
+ .sel_bg = .{ 0x2b, 0x47, 0x68 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xcc, 0xca, 0xc2 },
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:102 tab.inactiveBackground; source ~/05-genizah/ayu-colors/src/generated/mirage.ts:163 ui.bg #1f2430
+ .tag_bg = .{ 0x1f, 0x24, 0x30 },
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:103 tab.inactiveForeground; source ~/05-genizah/ayu-colors/src/generated/mirage.ts:149 ui.fg #707a8c #707a8c, adjusted
+ .tag_fg = .{ 0x84, 0x8d, 0x9c },
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:96 tab.activeBackground; source ~/05-genizah/ayu-colors/src/generated/mirage.ts:146 editor.bg #242936 #242936, adjusted
+ .tag_active_bg = .{ 0x3f, 0x43, 0x4c },
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:97 tab.activeForeground; source ~/05-genizah/ayu-colors/src/generated/mirage.ts:128 editor.fg #cccac2
+ .tag_active_fg = .{ 0xcc, 0xca, 0xc2 },
+ // the focused grip: ~/05-genizah/vscode-ayu/ayu-mirage.json:4 focusBorder [also tab.activeBorderTop]; source ~/05-genizah/ayu-colors/src/generated/mirage.ts:167 common.accent.tint #ffcc66
+ .box = .{ 0xff, 0xcc, 0x66 },
+ // derived: the grip at rest, the accent #ffcc66 faded toward the unfocused tag #1f2430 as far as 1.6:1 off it (0.20 of the way)
+ .box_dim = .{ 0x4c, 0x46, 0x3b },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/vscode-ayu/ayu-mirage.json:145 editorGutter.modifiedBackground [also gitDecoration.modifiedResourceForeground]; source ~/05-genizah/ayu-colors/src/generated/mirage.ts:118 vcs.modified #80bfff
+ .box_dirty = .{ 0x80, 0xbf, 0xff },
+ // derived: the column grip, the original's type colour, ~/05-genizah/vscode-ayu/ayu-mirage.json:622 tokenColors "Entity name" foreground; = syntax.entity (~/05-genizah/ayu-colors/src/generated/mirage.ts:88 syntax.entity)
+ .column_box = .{ 0x73, 0xd0, 0xff },
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:460 tokenColors "Keyword" foreground; = syntax.keyword (~/05-genizah/ayu-colors/src/generated/mirage.ts:92 syntax.keyword)
+ .kw = .{ 0xff, 0xa6, 0x59 },
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:384 tokenColors "String" foreground; = syntax.string (~/05-genizah/ayu-colors/src/generated/mirage.ts:89 syntax.string)
+ .str = .{ 0xd5, 0xff, 0x80 },
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:404 tokenColors "Number" foreground; = syntax.constant (~/05-genizah/ayu-colors/src/generated/mirage.ts:95 syntax.constant)
+ .num = .{ 0xdf, 0xbf, 0xff },
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:374 tokenColors "Comment" foreground; = syntax.comment (~/05-genizah/ayu-colors/src/generated/mirage.ts:94 syntax.comment)
+ .comment = .{ 0x6e, 0x7c, 0x8f },
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:108 editorLineNumber.foreground #707a8c80 composited over editor.background #242936 (~/05-genizah/vscode-ayu/ayu-mirage.json:106 editor.background) [gutter has no own bg, so editor bg]
+ .lineno = .{ 0x4a, 0x52, 0x61 },
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:109 editorLineNumber.activeForeground [gutter has no own bg, so editor bg]
+ .lineno_active = .{ 0x70, 0x7a, 0x8c },
+ // derived: none in the original; the page
+ .scroll_track = .{ 0x24, 0x29, 0x36 },
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:44 scrollbarSlider.background #707a8c66 composited over editor.background #242936 (~/05-genizah/vscode-ayu/ayu-mirage.json:106 editor.background)
+ .scroll_thumb = .{ 0x42, 0x49, 0x58 },
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:92 editorGroup.border; source ~/05-genizah/ayu-colors/src/generated/mirage.ts:150 ui.line #171b24 #171b24, adjusted
+ .border = .{ 0x46, 0x49, 0x4d },
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:119 editor.findMatchBackground; source ~/05-genizah/ayu-colors/src/generated/mirage.ts:135 editor.findMatch.active #736950
+ .search_bg = .{ 0x73, 0x69, 0x50 },
+ // derived: the original keeps text colours over a match; the page's text, whichever reads better on it
+ .search_fg = .{ 0xcc, 0xca, 0xc2 },
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:143 editorError.foreground; source ~/05-genizah/ayu-colors/src/generated/mirage.ts:170 common.error #ff6666
+ .diagnostic_error = .{ 0xff, 0x66, 0x66 },
+ // ~/05-genizah/vscode-ayu/ayu-mirage.json:144 editorWarning.foreground; source ~/05-genizah/ayu-colors/src/generated/mirage.ts:167 common.accent.tint #ffcc66
+ .diagnostic_warning = .{ 0xff, 0xcc, 0x66 },
+ .palette = .{
+ .{ 0x17, 0x1b, 0x24 }, // 0: ~/05-genizah/vscode-ayu/ayu-mirage.json:264 terminal.ansiBlack
+ .{ 0xf2, 0x82, 0x73 }, // 1: ~/05-genizah/vscode-ayu/ayu-mirage.json:265 terminal.ansiRed
+ .{ 0x87, 0xd9, 0x6c }, // 2: ~/05-genizah/vscode-ayu/ayu-mirage.json:266 terminal.ansiGreen
+ .{ 0xfc, 0xca, 0x60 }, // 3: ~/05-genizah/vscode-ayu/ayu-mirage.json:267 terminal.ansiYellow
+ .{ 0x6a, 0xcd, 0xff }, // 4: ~/05-genizah/vscode-ayu/ayu-mirage.json:268 terminal.ansiBlue
+ .{ 0xdd, 0xbb, 0xff }, // 5: ~/05-genizah/vscode-ayu/ayu-mirage.json:269 terminal.ansiMagenta
+ .{ 0x93, 0xe2, 0xc8 }, // 6: ~/05-genizah/vscode-ayu/ayu-mirage.json:270 terminal.ansiCyan
+ .{ 0xc7, 0xc7, 0xc7 }, // 7: ~/05-genizah/vscode-ayu/ayu-mirage.json:271 terminal.ansiWhite
+ .{ 0x68, 0x68, 0x68 }, // 8: ~/05-genizah/vscode-ayu/ayu-mirage.json:272 terminal.ansiBrightBlack
+ .{ 0xf2, 0x87, 0x79 }, // 9: ~/05-genizah/vscode-ayu/ayu-mirage.json:273 terminal.ansiBrightRed
+ .{ 0xd5, 0xff, 0x80 }, // 10: ~/05-genizah/vscode-ayu/ayu-mirage.json:274 terminal.ansiBrightGreen
+ .{ 0xff, 0xcd, 0x66 }, // 11: ~/05-genizah/vscode-ayu/ayu-mirage.json:275 terminal.ansiBrightYellow
+ .{ 0x73, 0xd0, 0xff }, // 12: ~/05-genizah/vscode-ayu/ayu-mirage.json:276 terminal.ansiBrightBlue
+ .{ 0xdf, 0xbf, 0xff }, // 13: ~/05-genizah/vscode-ayu/ayu-mirage.json:277 terminal.ansiBrightMagenta
+ .{ 0x95, 0xe6, 0xcb }, // 14: ~/05-genizah/vscode-ayu/ayu-mirage.json:278 terminal.ansiBrightCyan
+ .{ 0xff, 0xff, 0xff }, // 15: ~/05-genizah/vscode-ayu/ayu-mirage.json:279 terminal.ansiBrightWhite
+ },
+};
diff --git a/src/themes/catppuccin_frappe.zig b/src/themes/catppuccin_frappe.zig
new file mode 100644
index 00000000..7a97ee2c
--- /dev/null
+++ b/src/themes/catppuccin_frappe.zig
@@ -0,0 +1,105 @@
+//! Catppuccin Frappé: Catppuccin, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! palette; url https://github.com/catppuccin/palette; genizah ~/05-genizah/catppuccin-palette; commit 07d02aa110ef9eb7e7427afca5c73ba9cf7f8ebd
+//! neovim port (highlight mapping, terminal colours, lualine); url https://github.com/catppuccin/nvim; genizah ~/05-genizah/catppuccin-nvim; commit edefef779ab08ce1a4a404713e3012b0d202bd35
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #292c3c -> #454a5d: 1.00:1 off the unfocused #292c3c, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #51576d -> #9093a2: 1.93:1 on #292c3c, tag text needs 4.5:1 to be read and run
+//! - rules between panes #232634 -> #51566a: 1.22:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: Highlight mapping: catppuccin/nvim with default options
+//! (transparent_background false, float.solid/transparent false, dim_inactive
+//! off, color_overrides/highlight_overrides empty, kitty false, i.e. not
+//! running under kitty; under kitty the port nudges every palette blue channel
+//! by +/-1, palettes/init.lua:19-28). Runtime-computed values done exactly as
+//! utils/colors.lua blend (floor(clamp(a*fg+(1-a)*bg)+0.5)): cursorline and
+//! search_bg; inputs in the cites. U.darken(c, a, bg) = blend(c, bg, a);
+//! U.lighten(c, a, fg) = blend(c, fg, a). Terminal colours: groups/terminal.lua
+//! terminal_color_0..15, but NOTE the port only applies them when term_colors =
+//! true, and the default is false (init.lua:15, lib/compiler.lua:42); used
+//! anyway as the port's own terminal palette. Bright 1-7 equal normal; 0 =
+//! overlay0, 8 = overlay1, 5/13 = pink, 7/15 = text. palette.json also carries
+//! a separate ansiColors table (the terminal ports' palette), which differs;
+//! not used here. tabline_sel is TabLineSel -> link Normal. selection_fg:
+//! Visual has no fg (only bold). modified = Changed fg (blue); the gitsigns
+//! integration (auto-detected) instead uses GitSignsChange = C.yellow
+//! (groups/integrations/gitsigns.lua:11), and neo-tree uses NeoTreeModified =
+//! peach. Keyword = mauve directly (Statement also mauve). Diagnostic groups
+//! are in groups/lsp.lua (merged into editor groups by lib/mapper.lua:26).
+pub const theme = .{
+ .name = "catppuccin_frappe",
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:35 Normal bg = C.base (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:25)
+ .bg = .{ 0x30, 0x34, 0x46 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:35 Normal fg = C.text (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:16)
+ .fg = .{ 0xc6, 0xd0, 0xf5 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:107 Visual bg = C.surface1 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:23)
+ .sel_bg = .{ 0x51, 0x57, 0x6d },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xc6, 0xd0, 0xf5 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:100 StatusLineNC bg = C.mantle (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:26)
+ .tag_bg = .{ 0x29, 0x2c, 0x3c },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:100 StatusLineNC fg = C.surface1 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:23) #51576d, adjusted
+ .tag_fg = .{ 0x90, 0x93, 0xa2 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:99 StatusLine bg = C.mantle (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:26) #292c3c, adjusted
+ .tag_active_bg = .{ 0x45, 0x4a, 0x5d },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:99 StatusLine fg = C.text (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:16)
+ .tag_active_fg = .{ 0xc6, 0xd0, 0xf5 },
+ // the focused grip: ~/05-genizah/catppuccin-nvim/lua/catppuccin/utils/lualine.lua:20 lualine normal.a bg = C.blue (used by lua/lualine/themes/catppuccin-frappe.lua) -> C.blue (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:14)
+ .box = .{ 0x8c, 0xaa, 0xee },
+ // derived: the grip at rest, the accent #8caaee faded toward the unfocused tag #292c3c as far as 1.6:1 off it (0.26 of the way)
+ .box_dim = .{ 0x43, 0x4d, 0x6a },
+ // an unsaved grip: the original's warning colour, ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:51 DiagnosticWarn fg = warning (local warning = C.yellow at line 9) -> C.yellow (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:9)
+ .box_dirty = .{ 0xe5, 0xc8, 0x90 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:32 Type fg = C.yellow (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:9)
+ .column_box = .{ 0xe5, 0xc8, 0x90 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:20 Keyword fg = C.mauve (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:5)
+ .kw = .{ 0xca, 0x9e, 0xe6 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:8 String fg = C.green (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:10)
+ .str = .{ 0xa6, 0xd1, 0x89 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:10 Number fg = C.peach (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:8)
+ .num = .{ 0xef, 0x9f, 0x76 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:5 Comment fg = C.overlay2 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:19)
+ .comment = .{ 0x94, 0x9c, 0xbb },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:27 LineNr fg = C.surface1 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:23)
+ .lineno = .{ 0x51, 0x57, 0x6d },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:28 CursorLineNr fg = C.lavender (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:15)
+ .lineno_active = .{ 0xba, 0xbb, 0xf1 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:72 PmenuSbar bg = C.surface0 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:24)
+ .scroll_track = .{ 0x41, 0x45, 0x59 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:73 PmenuThumb bg = C.overlay0 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:21)
+ .scroll_thumb = .{ 0x73, 0x79, 0x94 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:114 WinSeparator fg = C.crust (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:27) #232634, adjusted
+ .border = .{ 0x51, 0x56, 0x6a },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:91 Search bg = U.darken(C.sky, 0.30, C.base) = blend(fg sky #99d1db, bg base #303446, alpha 0.3) per ~/05-genizah/catppuccin-nvim/lua/catppuccin/utils/colors.lua:29 -> #506373
+ .search_bg = .{ 0x50, 0x63, 0x73 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:91 Search fg = C.text (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:16)
+ .search_fg = .{ 0xc6, 0xd0, 0xf5 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:50 DiagnosticError fg = error (local error = C.red at line 8) -> C.red (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:6)
+ .diagnostic_error = .{ 0xe7, 0x82, 0x84 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:51 DiagnosticWarn fg = warning (local warning = C.yellow at line 9) -> C.yellow (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:9)
+ .diagnostic_warning = .{ 0xe5, 0xc8, 0x90 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:52 DiagnosticInfo fg = info (local info = C.sky at line 10) -> C.sky (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:12)
+ .diagnostic_info = .{ 0x99, 0xd1, 0xdb },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:53 DiagnosticHint fg = hint (local hint = C.teal at line 11) -> C.teal (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/frappe.lua:11)
+ .diagnostic_hint = .{ 0x81, 0xc8, 0xbe },
+ .palette = .{
+ .{ 0x51, 0x57, 0x6d }, // 0: ~/05-genizah/catppuccin-palette/palette.json:1470 frappe.ansiColors.black.normal
+ .{ 0xe7, 0x82, 0x84 }, // 1: ~/05-genizah/catppuccin-palette/palette.json:1514 frappe.ansiColors.red.normal
+ .{ 0xa6, 0xd1, 0x89 }, // 2: ~/05-genizah/catppuccin-palette/palette.json:1558 frappe.ansiColors.green.normal
+ .{ 0xe5, 0xc8, 0x90 }, // 3: ~/05-genizah/catppuccin-palette/palette.json:1602 frappe.ansiColors.yellow.normal
+ .{ 0x8c, 0xaa, 0xee }, // 4: ~/05-genizah/catppuccin-palette/palette.json:1646 frappe.ansiColors.blue.normal
+ .{ 0xf4, 0xb8, 0xe4 }, // 5: ~/05-genizah/catppuccin-palette/palette.json:1690 frappe.ansiColors.magenta.normal
+ .{ 0x81, 0xc8, 0xbe }, // 6: ~/05-genizah/catppuccin-palette/palette.json:1734 frappe.ansiColors.cyan.normal
+ .{ 0xa5, 0xad, 0xce }, // 7: ~/05-genizah/catppuccin-palette/palette.json:1778 frappe.ansiColors.white.normal
+ .{ 0x62, 0x68, 0x80 }, // 8: ~/05-genizah/catppuccin-palette/palette.json:1490 frappe.ansiColors.black.bright
+ .{ 0xe6, 0x71, 0x72 }, // 9: ~/05-genizah/catppuccin-palette/palette.json:1534 frappe.ansiColors.red.bright
+ .{ 0x8e, 0xc7, 0x72 }, // 10: ~/05-genizah/catppuccin-palette/palette.json:1578 frappe.ansiColors.green.bright
+ .{ 0xd9, 0xba, 0x73 }, // 11: ~/05-genizah/catppuccin-palette/palette.json:1622 frappe.ansiColors.yellow.bright
+ .{ 0x7b, 0x9e, 0xf0 }, // 12: ~/05-genizah/catppuccin-palette/palette.json:1666 frappe.ansiColors.blue.bright
+ .{ 0xf2, 0xa4, 0xdb }, // 13: ~/05-genizah/catppuccin-palette/palette.json:1710 frappe.ansiColors.magenta.bright
+ .{ 0x5a, 0xbf, 0xb5 }, // 14: ~/05-genizah/catppuccin-palette/palette.json:1754 frappe.ansiColors.cyan.bright
+ .{ 0xb5, 0xbf, 0xe2 }, // 15: ~/05-genizah/catppuccin-palette/palette.json:1798 frappe.ansiColors.white.bright
+ },
+};
diff --git a/src/themes/catppuccin_latte.zig b/src/themes/catppuccin_latte.zig
new file mode 100644
index 00000000..259198b4
--- /dev/null
+++ b/src/themes/catppuccin_latte.zig
@@ -0,0 +1,105 @@
+//! Catppuccin Latte: Catppuccin, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! palette; url https://github.com/catppuccin/palette; genizah ~/05-genizah/catppuccin-palette; commit 07d02aa110ef9eb7e7427afca5c73ba9cf7f8ebd
+//! neovim port (highlight mapping, terminal colours, lualine); url https://github.com/catppuccin/nvim; genizah ~/05-genizah/catppuccin-nvim; commit edefef779ab08ce1a4a404713e3012b0d202bd35
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #e6e9ef -> #cdd0da: 1.00:1 off the unfocused #e6e9ef, pardes needs 1.25:1 on a light page
+//! - unfocused tag text #bcc0cc -> #66686e: 1.49:1 on #e6e9ef, tag text needs 4.5:1 to be read and run
+//! - rules between panes #dce0e8 -> #bcc0cc: 1.17:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: Highlight mapping: catppuccin/nvim with default options
+//! (transparent_background false, float.solid/transparent false, dim_inactive
+//! off, color_overrides/highlight_overrides empty, kitty false, i.e. not
+//! running under kitty; under kitty the port nudges every palette blue channel
+//! by +/-1, palettes/init.lua:19-28). Runtime-computed values done exactly as
+//! utils/colors.lua blend (floor(clamp(a*fg+(1-a)*bg)+0.5)): cursorline and
+//! search_bg; inputs in the cites. U.darken(c, a, bg) = blend(c, bg, a);
+//! U.lighten(c, a, fg) = blend(c, fg, a). Terminal colours: groups/terminal.lua
+//! terminal_color_0..15, but NOTE the port only applies them when term_colors =
+//! true, and the default is false (init.lua:15, lib/compiler.lua:42); used
+//! anyway as the port's own terminal palette. Bright 1-7 equal normal; 0 =
+//! overlay0, 8 = overlay1, 5/13 = pink, 7/15 = text. palette.json also carries
+//! a separate ansiColors table (the terminal ports' palette), which differs;
+//! not used here. tabline_sel is TabLineSel -> link Normal. selection_fg:
+//! Visual has no fg (only bold). modified = Changed fg (blue); the gitsigns
+//! integration (auto-detected) instead uses GitSignsChange = C.yellow
+//! (groups/integrations/gitsigns.lua:11), and neo-tree uses NeoTreeModified =
+//! peach. Keyword = mauve directly (Statement also mauve). Diagnostic groups
+//! are in groups/lsp.lua (merged into editor groups by lib/mapper.lua:26).
+pub const theme = .{
+ .name = "catppuccin_latte",
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:35 Normal bg = C.base (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:25)
+ .bg = .{ 0xef, 0xf1, 0xf5 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:35 Normal fg = C.text (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:16)
+ .fg = .{ 0x4c, 0x4f, 0x69 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:107 Visual bg = C.surface1 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:23)
+ .sel_bg = .{ 0xbc, 0xc0, 0xcc },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x4c, 0x4f, 0x69 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:100 StatusLineNC bg = C.mantle (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:26)
+ .tag_bg = .{ 0xe6, 0xe9, 0xef },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:100 StatusLineNC fg = C.surface1 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:23) #bcc0cc, adjusted
+ .tag_fg = .{ 0x66, 0x68, 0x6e },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:99 StatusLine bg = C.mantle (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:26) #e6e9ef, adjusted
+ .tag_active_bg = .{ 0xcd, 0xd0, 0xda },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:99 StatusLine fg = C.text (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:16)
+ .tag_active_fg = .{ 0x4c, 0x4f, 0x69 },
+ // the focused grip: ~/05-genizah/catppuccin-nvim/lua/catppuccin/utils/lualine.lua:20 lualine normal.a bg = C.blue (used by lua/lualine/themes/catppuccin-latte.lua) -> C.blue (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:14)
+ .box = .{ 0x1e, 0x66, 0xf5 },
+ // derived: the grip at rest, the accent #1e66f5 faded toward the unfocused tag #e6e9ef as far as 1.6:1 off it (0.38 of the way)
+ .box_dim = .{ 0x9a, 0xb7, 0xf1 },
+ // an unsaved grip: the original's warning colour, ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:51 DiagnosticWarn fg = warning (local warning = C.yellow at line 9) -> C.yellow (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:9)
+ .box_dirty = .{ 0xdf, 0x8e, 0x1d },
+ // derived: the column grip, the original's type colour, ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:32 Type fg = C.yellow (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:9)
+ .column_box = .{ 0xdf, 0x8e, 0x1d },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:20 Keyword fg = C.mauve (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:5)
+ .kw = .{ 0x88, 0x39, 0xef },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:8 String fg = C.green (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:10)
+ .str = .{ 0x40, 0xa0, 0x2b },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:10 Number fg = C.peach (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:8)
+ .num = .{ 0xfe, 0x64, 0x0b },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:5 Comment fg = C.overlay2 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:19)
+ .comment = .{ 0x7c, 0x7f, 0x93 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:27 LineNr fg = C.surface1 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:23)
+ .lineno = .{ 0xbc, 0xc0, 0xcc },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:28 CursorLineNr fg = C.lavender (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:15)
+ .lineno_active = .{ 0x72, 0x87, 0xfd },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:72 PmenuSbar bg = C.surface0 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:24)
+ .scroll_track = .{ 0xcc, 0xd0, 0xda },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:73 PmenuThumb bg = C.overlay0 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:21)
+ .scroll_thumb = .{ 0x9c, 0xa0, 0xb0 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:114 WinSeparator fg = C.crust (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:27) #dce0e8, adjusted
+ .border = .{ 0xbc, 0xc0, 0xcc },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:91 Search bg = U.darken(C.sky, 0.30, C.base) = blend(fg sky #04a5e5, bg base #eff1f5, alpha 0.3) per ~/05-genizah/catppuccin-nvim/lua/catppuccin/utils/colors.lua:29 -> #a8daf0
+ .search_bg = .{ 0xa8, 0xda, 0xf0 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:91 Search fg = C.text (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:16)
+ .search_fg = .{ 0x4c, 0x4f, 0x69 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:50 DiagnosticError fg = error (local error = C.red at line 8) -> C.red (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:6)
+ .diagnostic_error = .{ 0xd2, 0x0f, 0x39 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:51 DiagnosticWarn fg = warning (local warning = C.yellow at line 9) -> C.yellow (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:9)
+ .diagnostic_warning = .{ 0xdf, 0x8e, 0x1d },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:52 DiagnosticInfo fg = info (local info = C.sky at line 10) -> C.sky (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:12)
+ .diagnostic_info = .{ 0x04, 0xa5, 0xe5 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:53 DiagnosticHint fg = hint (local hint = C.teal at line 11) -> C.teal (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/latte.lua:11)
+ .diagnostic_hint = .{ 0x17, 0x92, 0x99 },
+ .palette = .{
+ .{ 0x5c, 0x5f, 0x77 }, // 0: ~/05-genizah/catppuccin-palette/palette.json:562 latte.ansiColors.black.normal
+ .{ 0xd2, 0x0f, 0x39 }, // 1: ~/05-genizah/catppuccin-palette/palette.json:606 latte.ansiColors.red.normal
+ .{ 0x40, 0xa0, 0x2b }, // 2: ~/05-genizah/catppuccin-palette/palette.json:650 latte.ansiColors.green.normal
+ .{ 0xdf, 0x8e, 0x1d }, // 3: ~/05-genizah/catppuccin-palette/palette.json:694 latte.ansiColors.yellow.normal
+ .{ 0x1e, 0x66, 0xf5 }, // 4: ~/05-genizah/catppuccin-palette/palette.json:738 latte.ansiColors.blue.normal
+ .{ 0xea, 0x76, 0xcb }, // 5: ~/05-genizah/catppuccin-palette/palette.json:782 latte.ansiColors.magenta.normal
+ .{ 0x17, 0x92, 0x99 }, // 6: ~/05-genizah/catppuccin-palette/palette.json:826 latte.ansiColors.cyan.normal
+ .{ 0xac, 0xb0, 0xbe }, // 7: ~/05-genizah/catppuccin-palette/palette.json:870 latte.ansiColors.white.normal
+ .{ 0x6c, 0x6f, 0x85 }, // 8: ~/05-genizah/catppuccin-palette/palette.json:582 latte.ansiColors.black.bright
+ .{ 0xde, 0x29, 0x3e }, // 9: ~/05-genizah/catppuccin-palette/palette.json:626 latte.ansiColors.red.bright
+ .{ 0x49, 0xaf, 0x3d }, // 10: ~/05-genizah/catppuccin-palette/palette.json:670 latte.ansiColors.green.bright
+ .{ 0xee, 0xa0, 0x2d }, // 11: ~/05-genizah/catppuccin-palette/palette.json:714 latte.ansiColors.yellow.bright
+ .{ 0x45, 0x6e, 0xff }, // 12: ~/05-genizah/catppuccin-palette/palette.json:758 latte.ansiColors.blue.bright
+ .{ 0xfe, 0x85, 0xd8 }, // 13: ~/05-genizah/catppuccin-palette/palette.json:802 latte.ansiColors.magenta.bright
+ .{ 0x2d, 0x9f, 0xa8 }, // 14: ~/05-genizah/catppuccin-palette/palette.json:846 latte.ansiColors.cyan.bright
+ .{ 0xbc, 0xc0, 0xcc }, // 15: ~/05-genizah/catppuccin-palette/palette.json:890 latte.ansiColors.white.bright
+ },
+};
diff --git a/src/themes/catppuccin_macchiato.zig b/src/themes/catppuccin_macchiato.zig
new file mode 100644
index 00000000..918c3257
--- /dev/null
+++ b/src/themes/catppuccin_macchiato.zig
@@ -0,0 +1,105 @@
+//! Catppuccin Macchiato: Catppuccin, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! palette; url https://github.com/catppuccin/palette; genizah ~/05-genizah/catppuccin-palette; commit 07d02aa110ef9eb7e7427afca5c73ba9cf7f8ebd
+//! neovim port (highlight mapping, terminal colours, lualine); url https://github.com/catppuccin/nvim; genizah ~/05-genizah/catppuccin-nvim; commit edefef779ab08ce1a4a404713e3012b0d202bd35
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #1e2030 -> #3d4053: 1.00:1 off the unfocused #1e2030, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #494d64 -> #878a99: 1.94:1 on #1e2030, tag text needs 4.5:1 to be read and run
+//! - rules between panes #181926 -> #46495c: 1.18:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: Highlight mapping: catppuccin/nvim with default options
+//! (transparent_background false, float.solid/transparent false, dim_inactive
+//! off, color_overrides/highlight_overrides empty, kitty false, i.e. not
+//! running under kitty; under kitty the port nudges every palette blue channel
+//! by +/-1, palettes/init.lua:19-28). Runtime-computed values done exactly as
+//! utils/colors.lua blend (floor(clamp(a*fg+(1-a)*bg)+0.5)): cursorline and
+//! search_bg; inputs in the cites. U.darken(c, a, bg) = blend(c, bg, a);
+//! U.lighten(c, a, fg) = blend(c, fg, a). Terminal colours: groups/terminal.lua
+//! terminal_color_0..15, but NOTE the port only applies them when term_colors =
+//! true, and the default is false (init.lua:15, lib/compiler.lua:42); used
+//! anyway as the port's own terminal palette. Bright 1-7 equal normal; 0 =
+//! overlay0, 8 = overlay1, 5/13 = pink, 7/15 = text. palette.json also carries
+//! a separate ansiColors table (the terminal ports' palette), which differs;
+//! not used here. tabline_sel is TabLineSel -> link Normal. selection_fg:
+//! Visual has no fg (only bold). modified = Changed fg (blue); the gitsigns
+//! integration (auto-detected) instead uses GitSignsChange = C.yellow
+//! (groups/integrations/gitsigns.lua:11), and neo-tree uses NeoTreeModified =
+//! peach. Keyword = mauve directly (Statement also mauve). Diagnostic groups
+//! are in groups/lsp.lua (merged into editor groups by lib/mapper.lua:26).
+pub const theme = .{
+ .name = "catppuccin_macchiato",
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:35 Normal bg = C.base (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:25)
+ .bg = .{ 0x24, 0x27, 0x3a },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:35 Normal fg = C.text (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:16)
+ .fg = .{ 0xca, 0xd3, 0xf5 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:107 Visual bg = C.surface1 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:23)
+ .sel_bg = .{ 0x49, 0x4d, 0x64 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xca, 0xd3, 0xf5 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:100 StatusLineNC bg = C.mantle (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:26)
+ .tag_bg = .{ 0x1e, 0x20, 0x30 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:100 StatusLineNC fg = C.surface1 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:23) #494d64, adjusted
+ .tag_fg = .{ 0x87, 0x8a, 0x99 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:99 StatusLine bg = C.mantle (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:26) #1e2030, adjusted
+ .tag_active_bg = .{ 0x3d, 0x40, 0x53 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:99 StatusLine fg = C.text (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:16)
+ .tag_active_fg = .{ 0xca, 0xd3, 0xf5 },
+ // the focused grip: ~/05-genizah/catppuccin-nvim/lua/catppuccin/utils/lualine.lua:20 lualine normal.a bg = C.blue (used by lua/lualine/themes/catppuccin-macchiato.lua) -> C.blue (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:14)
+ .box = .{ 0x8a, 0xad, 0xf4 },
+ // derived: the grip at rest, the accent #8aadf4 faded toward the unfocused tag #1e2030 as far as 1.6:1 off it (0.24 of the way)
+ .box_dim = .{ 0x38, 0x42, 0x5f },
+ // an unsaved grip: the original's warning colour, ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:51 DiagnosticWarn fg = warning (local warning = C.yellow at line 9) -> C.yellow (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:9)
+ .box_dirty = .{ 0xee, 0xd4, 0x9f },
+ // derived: the column grip, the original's type colour, ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:32 Type fg = C.yellow (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:9)
+ .column_box = .{ 0xee, 0xd4, 0x9f },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:20 Keyword fg = C.mauve (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:5)
+ .kw = .{ 0xc6, 0xa0, 0xf6 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:8 String fg = C.green (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:10)
+ .str = .{ 0xa6, 0xda, 0x95 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:10 Number fg = C.peach (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:8)
+ .num = .{ 0xf5, 0xa9, 0x7f },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:5 Comment fg = C.overlay2 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:19)
+ .comment = .{ 0x93, 0x9a, 0xb7 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:27 LineNr fg = C.surface1 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:23)
+ .lineno = .{ 0x49, 0x4d, 0x64 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:28 CursorLineNr fg = C.lavender (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:15)
+ .lineno_active = .{ 0xb7, 0xbd, 0xf8 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:72 PmenuSbar bg = C.surface0 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:24)
+ .scroll_track = .{ 0x36, 0x3a, 0x4f },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:73 PmenuThumb bg = C.overlay0 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:21)
+ .scroll_thumb = .{ 0x6e, 0x73, 0x8d },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:114 WinSeparator fg = C.crust (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:27) #181926, adjusted
+ .border = .{ 0x46, 0x49, 0x5c },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:91 Search bg = U.darken(C.sky, 0.30, C.base) = blend(fg sky #91d7e3, bg base #24273a, alpha 0.3) per ~/05-genizah/catppuccin-nvim/lua/catppuccin/utils/colors.lua:29 -> #455c6d
+ .search_bg = .{ 0x45, 0x5c, 0x6d },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:91 Search fg = C.text (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:16)
+ .search_fg = .{ 0xca, 0xd3, 0xf5 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:50 DiagnosticError fg = error (local error = C.red at line 8) -> C.red (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:6)
+ .diagnostic_error = .{ 0xed, 0x87, 0x96 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:51 DiagnosticWarn fg = warning (local warning = C.yellow at line 9) -> C.yellow (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:9)
+ .diagnostic_warning = .{ 0xee, 0xd4, 0x9f },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:52 DiagnosticInfo fg = info (local info = C.sky at line 10) -> C.sky (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:12)
+ .diagnostic_info = .{ 0x91, 0xd7, 0xe3 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:53 DiagnosticHint fg = hint (local hint = C.teal at line 11) -> C.teal (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/macchiato.lua:11)
+ .diagnostic_hint = .{ 0x8b, 0xd5, 0xca },
+ .palette = .{
+ .{ 0x49, 0x4d, 0x64 }, // 0: ~/05-genizah/catppuccin-palette/palette.json:2378 macchiato.ansiColors.black.normal
+ .{ 0xed, 0x87, 0x96 }, // 1: ~/05-genizah/catppuccin-palette/palette.json:2422 macchiato.ansiColors.red.normal
+ .{ 0xa6, 0xda, 0x95 }, // 2: ~/05-genizah/catppuccin-palette/palette.json:2466 macchiato.ansiColors.green.normal
+ .{ 0xee, 0xd4, 0x9f }, // 3: ~/05-genizah/catppuccin-palette/palette.json:2510 macchiato.ansiColors.yellow.normal
+ .{ 0x8a, 0xad, 0xf4 }, // 4: ~/05-genizah/catppuccin-palette/palette.json:2554 macchiato.ansiColors.blue.normal
+ .{ 0xf5, 0xbd, 0xe6 }, // 5: ~/05-genizah/catppuccin-palette/palette.json:2598 macchiato.ansiColors.magenta.normal
+ .{ 0x8b, 0xd5, 0xca }, // 6: ~/05-genizah/catppuccin-palette/palette.json:2642 macchiato.ansiColors.cyan.normal
+ .{ 0xa5, 0xad, 0xcb }, // 7: ~/05-genizah/catppuccin-palette/palette.json:2686 macchiato.ansiColors.white.normal
+ .{ 0x5b, 0x60, 0x78 }, // 8: ~/05-genizah/catppuccin-palette/palette.json:2398 macchiato.ansiColors.black.bright
+ .{ 0xec, 0x74, 0x86 }, // 9: ~/05-genizah/catppuccin-palette/palette.json:2442 macchiato.ansiColors.red.bright
+ .{ 0x8c, 0xcf, 0x7f }, // 10: ~/05-genizah/catppuccin-palette/palette.json:2486 macchiato.ansiColors.green.bright
+ .{ 0xe1, 0xc6, 0x82 }, // 11: ~/05-genizah/catppuccin-palette/palette.json:2530 macchiato.ansiColors.yellow.bright
+ .{ 0x78, 0xa1, 0xf6 }, // 12: ~/05-genizah/catppuccin-palette/palette.json:2574 macchiato.ansiColors.blue.bright
+ .{ 0xf2, 0xa9, 0xdd }, // 13: ~/05-genizah/catppuccin-palette/palette.json:2618 macchiato.ansiColors.magenta.bright
+ .{ 0x63, 0xcb, 0xc0 }, // 14: ~/05-genizah/catppuccin-palette/palette.json:2662 macchiato.ansiColors.cyan.bright
+ .{ 0xb8, 0xc0, 0xe0 }, // 15: ~/05-genizah/catppuccin-palette/palette.json:2706 macchiato.ansiColors.white.bright
+ },
+};
diff --git a/src/themes/catppuccin_mocha.zig b/src/themes/catppuccin_mocha.zig
new file mode 100644
index 00000000..fb34a923
--- /dev/null
+++ b/src/themes/catppuccin_mocha.zig
@@ -0,0 +1,105 @@
+//! Catppuccin Mocha: Catppuccin, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! palette; url https://github.com/catppuccin/palette; genizah ~/05-genizah/catppuccin-palette; commit 07d02aa110ef9eb7e7427afca5c73ba9cf7f8ebd
+//! neovim port (highlight mapping, terminal colours, lualine); url https://github.com/catppuccin/nvim; genizah ~/05-genizah/catppuccin-nvim; commit edefef779ab08ce1a4a404713e3012b0d202bd35
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #181825 -> #393a4a: 1.00:1 off the unfocused #181825, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #45475a -> #81828f: 1.92:1 on #181825, tag text needs 4.5:1 to be read and run
+//! - rules between panes #11111b -> #3e404f: 1.14:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: Highlight mapping: catppuccin/nvim with default options
+//! (transparent_background false, float.solid/transparent false, dim_inactive
+//! off, color_overrides/highlight_overrides empty, kitty false, i.e. not
+//! running under kitty; under kitty the port nudges every palette blue channel
+//! by +/-1, palettes/init.lua:19-28). Runtime-computed values done exactly as
+//! utils/colors.lua blend (floor(clamp(a*fg+(1-a)*bg)+0.5)): cursorline and
+//! search_bg; inputs in the cites. U.darken(c, a, bg) = blend(c, bg, a);
+//! U.lighten(c, a, fg) = blend(c, fg, a). Terminal colours: groups/terminal.lua
+//! terminal_color_0..15, but NOTE the port only applies them when term_colors =
+//! true, and the default is false (init.lua:15, lib/compiler.lua:42); used
+//! anyway as the port's own terminal palette. Bright 1-7 equal normal; 0 =
+//! overlay0, 8 = overlay1, 5/13 = pink, 7/15 = text. palette.json also carries
+//! a separate ansiColors table (the terminal ports' palette), which differs;
+//! not used here. tabline_sel is TabLineSel -> link Normal. selection_fg:
+//! Visual has no fg (only bold). modified = Changed fg (blue); the gitsigns
+//! integration (auto-detected) instead uses GitSignsChange = C.yellow
+//! (groups/integrations/gitsigns.lua:11), and neo-tree uses NeoTreeModified =
+//! peach. Keyword = mauve directly (Statement also mauve). Diagnostic groups
+//! are in groups/lsp.lua (merged into editor groups by lib/mapper.lua:26).
+pub const theme = .{
+ .name = "catppuccin_mocha",
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:35 Normal bg = C.base (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:25)
+ .bg = .{ 0x1e, 0x1e, 0x2e },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:35 Normal fg = C.text (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:16)
+ .fg = .{ 0xcd, 0xd6, 0xf4 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:107 Visual bg = C.surface1 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:23)
+ .sel_bg = .{ 0x45, 0x47, 0x5a },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xcd, 0xd6, 0xf4 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:100 StatusLineNC bg = C.mantle (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:26)
+ .tag_bg = .{ 0x18, 0x18, 0x25 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:100 StatusLineNC fg = C.surface1 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:23) #45475a, adjusted
+ .tag_fg = .{ 0x81, 0x82, 0x8f },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:99 StatusLine bg = C.mantle (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:26) #181825, adjusted
+ .tag_active_bg = .{ 0x39, 0x3a, 0x4a },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:99 StatusLine fg = C.text (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:16)
+ .tag_active_fg = .{ 0xcd, 0xd6, 0xf4 },
+ // the focused grip: ~/05-genizah/catppuccin-nvim/lua/catppuccin/utils/lualine.lua:20 lualine normal.a bg = C.blue (used by lua/lualine/themes/catppuccin-mocha.lua) -> C.blue (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:14)
+ .box = .{ 0x89, 0xb4, 0xfa },
+ // derived: the grip at rest, the accent #89b4fa faded toward the unfocused tag #181825 as far as 1.6:1 off it (0.24 of the way)
+ .box_dim = .{ 0x33, 0x3d, 0x58 },
+ // an unsaved grip: the original's warning colour, ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:51 DiagnosticWarn fg = warning (local warning = C.yellow at line 9) -> C.yellow (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:9)
+ .box_dirty = .{ 0xf9, 0xe2, 0xaf },
+ // derived: the column grip, the original's type colour, ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:32 Type fg = C.yellow (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:9)
+ .column_box = .{ 0xf9, 0xe2, 0xaf },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:20 Keyword fg = C.mauve (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:5)
+ .kw = .{ 0xcb, 0xa6, 0xf7 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:8 String fg = C.green (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:10)
+ .str = .{ 0xa6, 0xe3, 0xa1 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:10 Number fg = C.peach (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:8)
+ .num = .{ 0xfa, 0xb3, 0x87 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/syntax.lua:5 Comment fg = C.overlay2 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:19)
+ .comment = .{ 0x93, 0x99, 0xb2 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:27 LineNr fg = C.surface1 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:23)
+ .lineno = .{ 0x45, 0x47, 0x5a },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:28 CursorLineNr fg = C.lavender (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:15)
+ .lineno_active = .{ 0xb4, 0xbe, 0xfe },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:72 PmenuSbar bg = C.surface0 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:24)
+ .scroll_track = .{ 0x31, 0x32, 0x44 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:73 PmenuThumb bg = C.overlay0 (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:21)
+ .scroll_thumb = .{ 0x6c, 0x70, 0x86 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:114 WinSeparator fg = C.crust (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:27) #11111b, adjusted
+ .border = .{ 0x3e, 0x40, 0x4f },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:91 Search bg = U.darken(C.sky, 0.30, C.base) = blend(fg sky #89dceb, bg base #1e1e2e, alpha 0.3) per ~/05-genizah/catppuccin-nvim/lua/catppuccin/utils/colors.lua:29 -> #3e5767
+ .search_bg = .{ 0x3e, 0x57, 0x67 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/editor.lua:91 Search fg = C.text (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:16)
+ .search_fg = .{ 0xcd, 0xd6, 0xf4 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:50 DiagnosticError fg = error (local error = C.red at line 8) -> C.red (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:6)
+ .diagnostic_error = .{ 0xf3, 0x8b, 0xa8 },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:51 DiagnosticWarn fg = warning (local warning = C.yellow at line 9) -> C.yellow (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:9)
+ .diagnostic_warning = .{ 0xf9, 0xe2, 0xaf },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:52 DiagnosticInfo fg = info (local info = C.sky at line 10) -> C.sky (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:12)
+ .diagnostic_info = .{ 0x89, 0xdc, 0xeb },
+ // ~/05-genizah/catppuccin-nvim/lua/catppuccin/groups/lsp.lua:53 DiagnosticHint fg = hint (local hint = C.teal at line 11) -> C.teal (~/05-genizah/catppuccin-nvim/lua/catppuccin/palettes/mocha.lua:11)
+ .diagnostic_hint = .{ 0x94, 0xe2, 0xd5 },
+ .palette = .{
+ .{ 0x45, 0x47, 0x5a }, // 0: ~/05-genizah/catppuccin-palette/palette.json:3286 mocha.ansiColors.black.normal
+ .{ 0xf3, 0x8b, 0xa8 }, // 1: ~/05-genizah/catppuccin-palette/palette.json:3330 mocha.ansiColors.red.normal
+ .{ 0xa6, 0xe3, 0xa1 }, // 2: ~/05-genizah/catppuccin-palette/palette.json:3374 mocha.ansiColors.green.normal
+ .{ 0xf9, 0xe2, 0xaf }, // 3: ~/05-genizah/catppuccin-palette/palette.json:3418 mocha.ansiColors.yellow.normal
+ .{ 0x89, 0xb4, 0xfa }, // 4: ~/05-genizah/catppuccin-palette/palette.json:3462 mocha.ansiColors.blue.normal
+ .{ 0xf5, 0xc2, 0xe7 }, // 5: ~/05-genizah/catppuccin-palette/palette.json:3506 mocha.ansiColors.magenta.normal
+ .{ 0x94, 0xe2, 0xd5 }, // 6: ~/05-genizah/catppuccin-palette/palette.json:3550 mocha.ansiColors.cyan.normal
+ .{ 0xa6, 0xad, 0xc8 }, // 7: ~/05-genizah/catppuccin-palette/palette.json:3594 mocha.ansiColors.white.normal
+ .{ 0x58, 0x5b, 0x70 }, // 8: ~/05-genizah/catppuccin-palette/palette.json:3306 mocha.ansiColors.black.bright
+ .{ 0xf3, 0x77, 0x99 }, // 9: ~/05-genizah/catppuccin-palette/palette.json:3350 mocha.ansiColors.red.bright
+ .{ 0x89, 0xd8, 0x8b }, // 10: ~/05-genizah/catppuccin-palette/palette.json:3394 mocha.ansiColors.green.bright
+ .{ 0xeb, 0xd3, 0x91 }, // 11: ~/05-genizah/catppuccin-palette/palette.json:3438 mocha.ansiColors.yellow.bright
+ .{ 0x74, 0xa8, 0xfc }, // 12: ~/05-genizah/catppuccin-palette/palette.json:3482 mocha.ansiColors.blue.bright
+ .{ 0xf2, 0xae, 0xde }, // 13: ~/05-genizah/catppuccin-palette/palette.json:3526 mocha.ansiColors.magenta.bright
+ .{ 0x6b, 0xd7, 0xca }, // 14: ~/05-genizah/catppuccin-palette/palette.json:3570 mocha.ansiColors.cyan.bright
+ .{ 0xba, 0xc2, 0xde }, // 15: ~/05-genizah/catppuccin-palette/palette.json:3614 mocha.ansiColors.white.bright
+ },
+};
diff --git a/src/themes/dark_plus.zig b/src/themes/dark_plus.zig
new file mode 100644
index 00000000..91092732
--- /dev/null
+++ b/src/themes/dark_plus.zig
@@ -0,0 +1,85 @@
+//! Dark+: Visual Studio Code, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! VS Code 1.130.0 built-in theme; path /usr/share/code/resources/app/extensions/theme-defaults/themes/dark_plus.json (includes ./dark_vs.json)
+//! VS Code source for workbench defaults; url https://github.com/microsoft/vscode; genizah ~/05-genizah/vscode; commit 1b6a188127eeaf9194f945eb6eb89a657e93c54c (tag 1.130.0)
+//! Tags: the tab (focused = the active tab, unfocused = the inactive one).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - unfocused tag ground #2d2d2d -> #3c3c3c: 1.21:1 off the focused one (the page, kept), pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #969696 -> #a7a7a7: 3.73:1 on #3c3c3c, tag text needs 4.5:1 to be read and run
+pub const theme = .{
+ .name = "dark_plus",
+ // /usr/share/code/resources/app/extensions/theme-defaults/themes/dark_vs.json:1 colors."editor.background"
+ .bg = .{ 0x1e, 0x1e, 0x1e },
+ // /usr/share/code/resources/app/extensions/theme-defaults/themes/dark_vs.json:1 colors."editor.foreground"
+ .fg = .{ 0xd4, 0xd4, 0xd4 },
+ // ~/05-genizah/vscode/src/vs/platform/theme/common/colors/editorColors.ts:122 editor.selectionBackground dark
+ .sel_bg = .{ 0x26, 0x4f, 0x78 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xd4, 0xd4, 0xd4 },
+ // ~/05-genizah/vscode/src/vs/workbench/common/theme.ts:36 tab.inactiveBackground dark #2d2d2d, adjusted
+ .tag_bg = .{ 0x3c, 0x3c, 0x3c },
+ // ~/05-genizah/vscode/src/vs/workbench/common/theme.ts:56 tab.inactiveForeground dark = transparent(tab.activeForeground #FFFFFF, 0.5) over #2d2d2d #969696, adjusted
+ .tag_fg = .{ 0xa7, 0xa7, 0xa7 },
+ // ~/05-genizah/vscode/src/vs/workbench/common/theme.ts:31 tab.activeBackground = editorBackground
+ .tag_active_bg = .{ 0x1e, 0x1e, 0x1e },
+ // ~/05-genizah/vscode/src/vs/workbench/common/theme.ts:49 tab.activeForeground dark Color.white
+ .tag_active_fg = .{ 0xff, 0xff, 0xff },
+ // the focused grip: ~/05-genizah/vscode/src/vs/platform/theme/common/colors/baseColors.ts:38 focusBorder dark
+ .box = .{ 0x00, 0x7f, 0xd4 },
+ // derived: the grip at rest, the accent #007fd4 faded toward the unfocused tag #3c3c3c as far as 1.6:1 off it (0.54 of the way)
+ .box_dim = .{ 0x1c, 0x60, 0x8e },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/vscode/src/vs/workbench/common/theme.ts:176 tab.activeModifiedBorder dark
+ .box_dirty = .{ 0x33, 0x99, 0xcc },
+ // derived: the column grip, the original's type colour, /usr/share/code/resources/app/extensions/theme-defaults/themes/dark_plus.json:1 tokenColors entity.name.type
+ .column_box = .{ 0x4e, 0xc9, 0xb0 },
+ // /usr/share/code/resources/app/extensions/theme-defaults/themes/dark_vs.json:1 tokenColors keyword (keyword.control is #C586C0 in dark_plus.json:1; pardes has one keyword colour)
+ .kw = .{ 0x56, 0x9c, 0xd6 },
+ // /usr/share/code/resources/app/extensions/theme-defaults/themes/dark_vs.json:1 tokenColors string
+ .str = .{ 0xce, 0x91, 0x78 },
+ // /usr/share/code/resources/app/extensions/theme-defaults/themes/dark_vs.json:1 tokenColors constant.numeric
+ .num = .{ 0xb5, 0xce, 0xa8 },
+ // /usr/share/code/resources/app/extensions/theme-defaults/themes/dark_vs.json:1 tokenColors comment
+ .comment = .{ 0x6a, 0x99, 0x55 },
+ // ~/05-genizah/vscode/src/vs/editor/common/core/editorColorRegistry.ts:29 editorLineNumber.foreground dark
+ .lineno = .{ 0x85, 0x85, 0x85 },
+ // ~/05-genizah/vscode/src/vs/editor/common/core/editorColorRegistry.ts:48 editorActiveLineNumber.foreground dark (editorLineNumber.activeForeground defaults to it, :49)
+ .lineno_active = .{ 0xc6, 0xc6, 0xc6 },
+ // ~/05-genizah/vscode/src/vs/platform/theme/common/colors/miscColors.ts:68 scrollbar.background null: the editor shows through
+ .scroll_track = .{ 0x1e, 0x1e, 0x1e },
+ // ~/05-genizah/vscode/src/vs/platform/theme/common/colors/miscColors.ts:57 scrollbarSlider.background dark #797979 at 0.4, over #1e1e1e
+ .scroll_thumb = .{ 0x42, 0x42, 0x42 },
+ // ~/05-genizah/vscode/src/vs/workbench/common/theme.ts:237 editorGroup.border dark
+ .border = .{ 0x44, 0x44, 0x44 },
+ // ~/05-genizah/vscode/src/vs/platform/theme/common/colors/editorColors.ts:149 editor.findMatchBackground dark
+ .search_bg = .{ 0x51, 0x5c, 0x6a },
+ // derived: the original keeps text colours over a match; the page's text, whichever reads better on it
+ .search_fg = .{ 0xd4, 0xd4, 0xd4 },
+ // ~/05-genizah/vscode/src/vs/platform/theme/common/colors/editorColors.ts:71 editorError.foreground dark
+ .diagnostic_error = .{ 0xf1, 0x4c, 0x4c },
+ // ~/05-genizah/vscode/src/vs/platform/theme/common/colors/editorColors.ts:84 editorWarning.foreground dark
+ .diagnostic_warning = .{ 0xcc, 0xa7, 0x00 },
+ // ~/05-genizah/vscode/src/vs/platform/theme/common/colors/editorColors.ts:97 editorInfo.foreground dark
+ .diagnostic_info = .{ 0x59, 0xa4, 0xf9 },
+ // ~/05-genizah/vscode/src/vs/platform/theme/common/colors/editorColors.ts:106 editorHint.foreground dark #eeeeee at 0.7, over #1e1e1e
+ .diagnostic_hint = .{ 0xb0, 0xb0, 0xb0 },
+ .palette = .{
+ .{ 0x00, 0x00, 0x00 }, // 0: ~/05-genizah/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts:107 terminal.ansiBlack dark
+ .{ 0xcd, 0x31, 0x31 }, // 1: ~/05-genizah/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts:116 terminal.ansiRed dark
+ .{ 0x0d, 0xbc, 0x79 }, // 2: ~/05-genizah/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts:125 terminal.ansiGreen dark
+ .{ 0xe5, 0xe5, 0x10 }, // 3: ~/05-genizah/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts:134 terminal.ansiYellow dark
+ .{ 0x24, 0x72, 0xc8 }, // 4: ~/05-genizah/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts:143 terminal.ansiBlue dark
+ .{ 0xbc, 0x3f, 0xbc }, // 5: ~/05-genizah/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts:152 terminal.ansiMagenta dark
+ .{ 0x11, 0xa8, 0xcd }, // 6: ~/05-genizah/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts:161 terminal.ansiCyan dark
+ .{ 0xe5, 0xe5, 0xe5 }, // 7: ~/05-genizah/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts:170 terminal.ansiWhite dark
+ .{ 0x66, 0x66, 0x66 }, // 8: ~/05-genizah/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts:179 terminal.ansiBrightBlack dark
+ .{ 0xf1, 0x4c, 0x4c }, // 9: ~/05-genizah/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts:188 terminal.ansiBrightRed dark
+ .{ 0x23, 0xd1, 0x8b }, // 10: ~/05-genizah/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts:197 terminal.ansiBrightGreen dark
+ .{ 0xf5, 0xf5, 0x43 }, // 11: ~/05-genizah/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts:206 terminal.ansiBrightYellow dark
+ .{ 0x3b, 0x8e, 0xea }, // 12: ~/05-genizah/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts:215 terminal.ansiBrightBlue dark
+ .{ 0xd6, 0x70, 0xd6 }, // 13: ~/05-genizah/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts:224 terminal.ansiBrightMagenta dark
+ .{ 0x29, 0xb8, 0xdb }, // 14: ~/05-genizah/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts:233 terminal.ansiBrightCyan dark
+ .{ 0xe5, 0xe5, 0xe5 }, // 15: ~/05-genizah/vscode/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts:242 terminal.ansiBrightWhite dark
+ },
+};
diff --git a/src/themes/doom_one.zig b/src/themes/doom_one.zig
new file mode 100644
index 00000000..9db8bdee
--- /dev/null
+++ b/src/themes/doom_one.zig
@@ -0,0 +1,104 @@
+//! Doom One: Doom One, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/doomemacs/themes; genizah ~/05-genizah/doomemacs-themes; commit a59202912ad55014e53a685eee6cd94130bdd4fd
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #1d2026 -> #40444b: 1.05:1 off the unfocused #21242b, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #5b6268 -> #868b8f: 2.51:1 on #21242b, tag text needs 4.5:1 to be read and run
+//! - rules between panes #191b20 -> #4a4d55: 1.23:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! - scroll thumb #3f444a -> #44494f: 1.42:1 on its track #282c34
+//! Upstream notes: doom-darken/doom-lighten/doom-blend computed in Python
+//! replicating ~/05-genizah/doomemacs-themes/doom-themes.el:293-343 exactly:
+//! each channel = int(hex,16)*65535/255 (tty-color-standard-values, integer) /
+//! 65535.0, blended as (alpha*c1)+(c2*(1-alpha)), then *255 and formatted with
+//! %02x, which truncates the float toward zero; darken c a = blend c #000000
+//! (1-a), lighten c a = blend c #FFFFFF (1-a). Inputs are shown in each cite.
+//! Default options: brighter-comments, brighter-modeline, comment-bg,
+//! padded-modeline all nil. GUI (first) column of each palette triple used.
+//! info = success face (green); hint = null (no hint face in
+//! Emacs/doom-themes); scroll_* = null (doom-themes defines no scroll-bar
+//! face); selection_fg = null (region sets only :distant-foreground). modified
+//! = diff-changed (violet) per brief; note the VC/diff-hl modified colour is
+//! vc-modified = orange (diff-hl-change, diff-indicator-changed). ansi =
+//! ansi-color-* faces (Emacs 28+; also ansi-color-names-vector uses bg..fg).
+//! The term-color-* faces
+//! (~/05-genizah/doomemacs-themes/doom-themes-base.el:1430-1437) differ: black
+//! = base0, white = base8, and there are no bright term-color faces. tabline_*
+//! from tab-bar faces (inherit tab-line-*). The doom-modeline package
+//! (separate) draws on the mode-line face; its accent bar is doom-modeline-bar
+//! = highlight.
+pub const theme = .{
+ .name = "doom_one",
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:13 default :background bg; bg = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:56 bg (GUI/default column #282c34)
+ .bg = .{ 0x28, 0x2c, 0x34 },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:13 default :foreground fg; fg = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:57 fg (GUI/default column #bbc2cf)
+ .fg = .{ 0xbb, 0xc2, 0xcf },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:15 region :background region; region = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:110 region = (doom-lighten (car bg-alt) #21242b 0.15) = (doom-blend #21242b #FFFFFF 0.85) = #42444a (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343 doom-blend/darken/lighten)
+ .sel_bg = .{ 0x42, 0x44, 0x4a },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xbb, 0xc2, 0xcf },
+ // ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:145 mode-line-inactive :background modeline-bg-inactive; modeline-bg-inactive = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:128 modeline-bg-inactive = bg-alt (car bg-alt) -> #21242b
+ .tag_bg = .{ 0x21, 0x24, 0x2b },
+ // ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:145 mode-line-inactive :foreground modeline-fg-alt; modeline-fg-alt = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:121 modeline-fg-alt = base5 -> #5b6268 #5b6268, adjusted
+ .tag_fg = .{ 0x86, 0x8b, 0x8f },
+ // ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:142 mode-line :background modeline-bg; modeline-bg = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:122 modeline-bg = (doom-darken bg-alt #21242b 0.1) (doom-one-brighter-modeline nil) = (doom-blend #21242b #000000 0.9) = #1d2026 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343 doom-blend/darken/lighten) #1d2026, adjusted
+ .tag_active_bg = .{ 0x40, 0x44, 0x4b },
+ // ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:142 mode-line :foreground modeline-fg; modeline-fg = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:120 modeline-fg = fg -> #bbc2cf
+ .tag_active_fg = .{ 0xbb, 0xc2, 0xcf },
+ // the focused grip: ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:154 doom-modeline-bar :background highlight (the modeline accent bar; also cursor/link/minibuffer-prompt, ~/05-genizah/doomemacs-themes/doom-themes-base.el:17); highlight = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:95 highlight = blue -> #51afef
+ .box = .{ 0x51, 0xaf, 0xef },
+ // derived: the grip at rest, the accent #51afef faded toward the unfocused tag #21242b as far as 1.6:1 off it (0.26 of the way)
+ .box_dim = .{ 0x2d, 0x48, 0x5e },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/doomemacs-themes/doom-themes-base.el:412 diff-changed :foreground violet (doom-modeline-buffer-modified not defined in doom-themes); violet = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:88 violet (GUI/default column #a9a1e1)
+ .box_dirty = .{ 0xa9, 0xa1, 0xe1 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/doomemacs-themes/doom-themes-base.el:57 font-lock-type-face :foreground type; type = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:106 type = yellow -> #ecbe7b
+ .column_box = .{ 0xec, 0xbe, 0x7b },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:44 font-lock-keyword-face :foreground keywords; keywords = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:103 keywords = blue -> #51afef
+ .kw = .{ 0x51, 0xaf, 0xef },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:56 font-lock-string-face :foreground strings; strings = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:107 strings = green -> #98be65
+ .str = .{ 0x98, 0xbe, 0x65 },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:769 highlight-numbers-number :foreground numbers; numbers = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:109 numbers = orange -> #da8548
+ .num = .{ 0xda, 0x85, 0x48 },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:37 font-lock-comment-face :foreground comments; comments = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:99 comments = base5 (doom-one-brighter-comments nil) -> #5b6268
+ .comment = .{ 0x5b, 0x62, 0x68 },
+ // ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:137 line-number &override :foreground base4; base4 = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:73 base4 (GUI/default column #3f444a)
+ .lineno = .{ 0x3f, 0x44, 0x4a },
+ // ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:138 line-number-current-line &override :foreground fg; fg = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:57 fg (GUI/default column #bbc2cf)
+ .lineno_active = .{ 0xbb, 0xc2, 0xcf },
+ // derived: none in the original; the page
+ .scroll_track = .{ 0x28, 0x2c, 0x34 },
+ // derived: none in the original; line numbers, ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:137 line-number &override :foreground base4; base4 = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:73 base4 (GUI/default column #3f444a) #3f444a, adjusted
+ .scroll_thumb = .{ 0x44, 0x49, 0x4f },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:28 vertical-border :foreground vertical-bar; vertical-bar = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:96 vertical-bar = (doom-darken base1 #1c1f24 0.1) = (doom-blend #1c1f24 #000000 0.9) = #191b20 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343 doom-blend/darken/lighten) #191b20, adjusted
+ .border = .{ 0x4a, 0x4d, 0x55 },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:23 lazy-highlight &dark :background (doom-darken highlight #51afef 0.3) = (doom-blend #51afef #000000 0.7) = #387aa7 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-331); isearch inherits lazy-highlight (~/05-genizah/doomemacs-themes/doom-themes-base.el:820)
+ .search_bg = .{ 0x38, 0x7a, 0xa7 },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:23 lazy-highlight &dark :foreground base8 (isearch :inherit lazy-highlight, ~/05-genizah/doomemacs-themes/doom-themes-base.el:820); base8 = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:77 base8 (GUI/default column #dfdfdf)
+ .search_fg = .{ 0xdf, 0xdf, 0xdf },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:30 error face :foreground error; error = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:111 error = red -> #ff6c6b
+ .diagnostic_error = .{ 0xff, 0x6c, 0x6b },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:31 warning face :foreground warning; warning = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:112 warning = yellow -> #ecbe7b
+ .diagnostic_warning = .{ 0xec, 0xbe, 0x7b },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:32 success face :foreground success; success = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:113 success = green -> #98be65
+ .diagnostic_info = .{ 0x98, 0xbe, 0x65 },
+ .palette = .{
+ .{ 0x28, 0x2c, 0x34 }, // 0: ~/05-genizah/doomemacs-themes/doom-themes-base.el:198 ansi-color-black :foreground bg; bg = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:56 bg (GUI/default column #282c34)
+ .{ 0xff, 0x6c, 0x6b }, // 1: ~/05-genizah/doomemacs-themes/doom-themes-base.el:199 ansi-color-red :foreground red; red = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:80 red (GUI/default column #ff6c6b)
+ .{ 0x98, 0xbe, 0x65 }, // 2: ~/05-genizah/doomemacs-themes/doom-themes-base.el:200 ansi-color-green :foreground green; green = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:82 green (GUI/default column #98be65)
+ .{ 0xec, 0xbe, 0x7b }, // 3: ~/05-genizah/doomemacs-themes/doom-themes-base.el:201 ansi-color-yellow :foreground yellow; yellow = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:84 yellow (GUI/default column #ecbe7b)
+ .{ 0x51, 0xaf, 0xef }, // 4: ~/05-genizah/doomemacs-themes/doom-themes-base.el:202 ansi-color-blue :foreground blue; blue = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:85 blue (GUI/default column #51afef)
+ .{ 0xc6, 0x78, 0xdd }, // 5: ~/05-genizah/doomemacs-themes/doom-themes-base.el:203 ansi-color-magenta :foreground magenta; magenta = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:87 magenta (GUI/default column #c678dd)
+ .{ 0x46, 0xd9, 0xff }, // 6: ~/05-genizah/doomemacs-themes/doom-themes-base.el:204 ansi-color-cyan :foreground cyan; cyan = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:89 cyan (GUI/default column #46d9ff)
+ .{ 0xbb, 0xc2, 0xcf }, // 7: ~/05-genizah/doomemacs-themes/doom-themes-base.el:205 ansi-color-white :foreground fg; fg = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:57 fg (GUI/default column #bbc2cf)
+ .{ 0x73, 0x79, 0x7e }, // 8: ~/05-genizah/doomemacs-themes/doom-themes-base.el:211 ansi-color-bright-black &dark :foreground base6; base6 = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:75 base6 (GUI/default column #73797e)
+ .{ 0xff, 0x82, 0x81 }, // 9: ~/05-genizah/doomemacs-themes/doom-themes-base.el:212 ansi-color-bright-red :foreground (doom-lighten red #ff6c6b 0.15) = (doom-blend #ff6c6b #FFFFFF 0.85) = #ff8281 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343)
+ .{ 0xa7, 0xc7, 0x7c }, // 10: ~/05-genizah/doomemacs-themes/doom-themes-base.el:213 ansi-color-bright-green :foreground (doom-lighten green #98be65 0.15) = (doom-blend #98be65 #FFFFFF 0.85) = #a7c77c (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343)
+ .{ 0xee, 0xc7, 0x8e }, // 11: ~/05-genizah/doomemacs-themes/doom-themes-base.el:214 ansi-color-bright-yellow :foreground (doom-lighten yellow #ecbe7b 0.15) = (doom-blend #ecbe7b #FFFFFF 0.85) = #eec78e (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343)
+ .{ 0x6b, 0xbb, 0xf1 }, // 12: ~/05-genizah/doomemacs-themes/doom-themes-base.el:215 ansi-color-bright-blue :foreground (doom-lighten blue #51afef 0.15) = (doom-blend #51afef #FFFFFF 0.85) = #6bbbf1 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343)
+ .{ 0xce, 0x8c, 0xe2 }, // 13: ~/05-genizah/doomemacs-themes/doom-themes-base.el:216 ansi-color-bright-magenta :foreground (doom-lighten magenta #c678dd 0.15) = (doom-blend #c678dd #FFFFFF 0.85) = #ce8ce2 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343)
+ .{ 0x61, 0xde, 0xff }, // 14: ~/05-genizah/doomemacs-themes/doom-themes-base.el:217 ansi-color-bright-cyan :foreground (doom-lighten cyan #46d9ff 0.15) = (doom-blend #46d9ff #FFFFFF 0.85) = #61deff (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343)
+ .{ 0xdf, 0xdf, 0xdf }, // 15: ~/05-genizah/doomemacs-themes/doom-themes-base.el:218 ansi-color-bright-white :foreground base8; base8 = ~/05-genizah/doomemacs-themes/themes/doom-one-theme.el:77 base8 (GUI/default column #dfdfdf)
+ },
+};
diff --git a/src/themes/doom_one_light.zig b/src/themes/doom_one_light.zig
new file mode 100644
index 00000000..c95b5c17
--- /dev/null
+++ b/src/themes/doom_one_light.zig
@@ -0,0 +1,106 @@
+//! Doom One Light: Doom One, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/doomemacs/themes; genizah ~/05-genizah/doomemacs-themes; commit a59202912ad55014e53a685eee6cd94130bdd4fd
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #e7e7e7 -> #c8c8c9: 1.06:1 off the unfocused #e1e1e1, pardes needs 1.25:1 on a light page
+//! - unfocused tag text #a190a7 -> #6a5f6e: 2.27:1 on #e1e1e1, tag text needs 4.5:1 to be read and run
+//! Upstream notes: doom-darken/doom-lighten/doom-blend computed in Python
+//! replicating ~/05-genizah/doomemacs-themes/doom-themes.el:293-343 exactly:
+//! each channel = int(hex,16)*65535/255 (tty-color-standard-values, integer) /
+//! 65535.0, blended as (alpha*c1)+(c2*(1-alpha)), then *255 and formatted with
+//! %02x, which truncates the float toward zero; darken c a = blend c #000000
+//! (1-a), lighten c a = blend c #FFFFFF (1-a). Inputs are shown in each cite.
+//! Default options: brighter-comments, brighter-modeline, comment-bg,
+//! padded-modeline all nil. GUI (first) column of each palette triple used.
+//! info = success face (green); hint = null (no hint face in
+//! Emacs/doom-themes); scroll_* = null (doom-themes defines no scroll-bar
+//! face); selection_fg = null (region sets only :distant-foreground). modified
+//! = diff-changed (violet) per brief; note the VC/diff-hl modified colour is
+//! vc-modified = orange (diff-hl-change, diff-indicator-changed). ansi =
+//! ansi-color-* faces (Emacs 28+; also ansi-color-names-vector uses bg..fg).
+//! The term-color-* faces
+//! (~/05-genizah/doomemacs-themes/doom-themes-base.el:1430-1437) differ: black
+//! = base0, white = base8, and there are no bright term-color faces. tabline_*
+//! from tab-bar faces (inherit tab-line-*). The doom-modeline package
+//! (separate) draws on the mode-line face; its accent bar is doom-modeline-bar
+//! = highlight. Light oddities kept verbatim: ansi 0 (black) = bg #fafafa and
+//! ansi 7 (white) = fg #383a42, i.e. inverted w.r.t. terminal convention
+//! because ansi-color-black/white map to bg/fg; tabline_fg = fg-alt #c6c7c7 is
+//! very low-contrast on bg-alt; the variable is named modeline-bg-alt-inactive
+//! in this theme.
+pub const theme = .{
+ .name = "doom_one_light",
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:13 default :background bg; bg = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:47 bg (GUI/default column #fafafa)
+ .bg = .{ 0xfa, 0xfa, 0xfa },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:13 default :foreground fg; fg = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:48 fg (GUI/default column #383a42)
+ .fg = .{ 0x38, 0x3a, 0x42 },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:15 region :background region; region = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:101 region = (doom-darken (car bg-alt) #f0f0f0 0.1) = (doom-blend #f0f0f0 #000000 0.9) = #d8d8d8 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343 doom-blend/darken/lighten)
+ .sel_bg = .{ 0xd8, 0xd8, 0xd8 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x38, 0x3a, 0x42 },
+ // ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:138 mode-line-inactive :background modeline-bg-inactive; modeline-bg-inactive = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:121 modeline-bg-inactive = (doom-darken bg #fafafa 0.1) = (doom-blend #fafafa #000000 0.9) = #e1e1e1 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343 doom-blend/darken/lighten)
+ .tag_bg = .{ 0xe1, 0xe1, 0xe1 },
+ // ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:138 mode-line-inactive :foreground modeline-fg-alt; modeline-fg-alt = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:112 modeline-fg-alt = (doom-blend violet #b751b6 base4 #9ca0a4 0.2) (doom-one-light-brighter-modeline nil) = #a190a7 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343 doom-blend/darken/lighten) #a190a7, adjusted
+ .tag_fg = .{ 0x6a, 0x5f, 0x6e },
+ // ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:135 mode-line :background modeline-bg; modeline-bg = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:115 modeline-bg = base1 (doom-one-light-brighter-modeline nil) -> #e7e7e7 #e7e7e7, adjusted
+ .tag_active_bg = .{ 0xc8, 0xc8, 0xc9 },
+ // ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:135 mode-line :foreground modeline-fg; modeline-fg = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:111 modeline-fg = fg -> #383a42
+ .tag_active_fg = .{ 0x38, 0x3a, 0x42 },
+ // the focused grip: ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:152 doom-modeline-bar :background highlight (the modeline accent bar; also cursor/link/minibuffer-prompt, ~/05-genizah/doomemacs-themes/doom-themes-base.el:17); highlight = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:86 highlight = blue -> #4078f2
+ .box = .{ 0x40, 0x78, 0xf2 },
+ // derived: the grip at rest, the accent #4078f2 faded toward the unfocused tag #e1e1e1 as far as 1.6:1 off it (0.46 of the way)
+ .box_dim = .{ 0x97, 0xb1, 0xe9 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/doomemacs-themes/doom-themes-base.el:412 diff-changed :foreground violet (doom-modeline-buffer-modified not defined in doom-themes); violet = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:79 violet (GUI/default column #b751b6)
+ .box_dirty = .{ 0xb7, 0x51, 0xb6 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/doomemacs-themes/doom-themes-base.el:57 font-lock-type-face :foreground type; type = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:97 type = yellow -> #986801
+ .column_box = .{ 0x98, 0x68, 0x01 },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:44 font-lock-keyword-face :foreground keywords; keywords = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:94 keywords = red -> #e45649
+ .kw = .{ 0xe4, 0x56, 0x49 },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:56 font-lock-string-face :foreground strings; strings = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:98 strings = green -> #50a14f
+ .str = .{ 0x50, 0xa1, 0x4f },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:769 highlight-numbers-number :foreground numbers; numbers = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:100 numbers = orange -> #da8548
+ .num = .{ 0xda, 0x85, 0x48 },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:37 font-lock-comment-face :foreground comments; comments = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:90 comments = base4 (doom-one-light-brighter-comments nil) -> #9ca0a4
+ .comment = .{ 0x9c, 0xa0, 0xa4 },
+ // ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:132 line-number &override :foreground (doom-lighten base4 #9ca0a4 0.15) = (doom-blend #9ca0a4 #FFFFFF 0.85) = #aaaeb1 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343)
+ .lineno = .{ 0xaa, 0xae, 0xb1 },
+ // ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:133 line-number-current-line &override :foreground base8; base8 = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:68 base8 (GUI/default column #1b2229)
+ .lineno_active = .{ 0x1b, 0x22, 0x29 },
+ // derived: none in the original; the page
+ .scroll_track = .{ 0xfa, 0xfa, 0xfa },
+ // derived: none in the original; line numbers, ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:132 line-number &override :foreground (doom-lighten base4 #9ca0a4 0.15) = (doom-blend #9ca0a4 #FFFFFF 0.85) = #aaaeb1 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343)
+ .scroll_thumb = .{ 0xaa, 0xae, 0xb1 },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:28 vertical-border :foreground vertical-bar; vertical-bar = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:87 vertical-bar = (doom-darken base2 #dfdfdf 0.1) = (doom-blend #dfdfdf #000000 0.9) = #c8c8c8 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343 doom-blend/darken/lighten)
+ .border = .{ 0xc8, 0xc8, 0xc8 },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:24 lazy-highlight &light :background (doom-blend bg #fafafa highlight #4078f2 0.7) = #c2d3f7 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-319); isearch inherits lazy-highlight (~/05-genizah/doomemacs-themes/doom-themes-base.el:820)
+ .search_bg = .{ 0xc2, 0xd3, 0xf7 },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:24 lazy-highlight &light :foreground base0 (isearch :inherit lazy-highlight, ~/05-genizah/doomemacs-themes/doom-themes-base.el:820); base0 = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:60 base0 (GUI/default column #f0f0f0)
+ .search_fg = .{ 0xf0, 0xf0, 0xf0 },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:30 error face :foreground error; error = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:102 error = red -> #e45649
+ .diagnostic_error = .{ 0xe4, 0x56, 0x49 },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:31 warning face :foreground warning; warning = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:103 warning = yellow -> #986801
+ .diagnostic_warning = .{ 0x98, 0x68, 0x01 },
+ // ~/05-genizah/doomemacs-themes/doom-themes-base.el:32 success face :foreground success; success = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:104 success = green -> #50a14f
+ .diagnostic_info = .{ 0x50, 0xa1, 0x4f },
+ .palette = .{
+ .{ 0xfa, 0xfa, 0xfa }, // 0: ~/05-genizah/doomemacs-themes/doom-themes-base.el:198 ansi-color-black :foreground bg; bg = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:47 bg (GUI/default column #fafafa)
+ .{ 0xe4, 0x56, 0x49 }, // 1: ~/05-genizah/doomemacs-themes/doom-themes-base.el:199 ansi-color-red :foreground red; red = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:71 red (GUI/default column #e45649)
+ .{ 0x50, 0xa1, 0x4f }, // 2: ~/05-genizah/doomemacs-themes/doom-themes-base.el:200 ansi-color-green :foreground green; green = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:73 green (GUI/default column #50a14f)
+ .{ 0x98, 0x68, 0x01 }, // 3: ~/05-genizah/doomemacs-themes/doom-themes-base.el:201 ansi-color-yellow :foreground yellow; yellow = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:75 yellow (GUI/default column #986801)
+ .{ 0x40, 0x78, 0xf2 }, // 4: ~/05-genizah/doomemacs-themes/doom-themes-base.el:202 ansi-color-blue :foreground blue; blue = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:76 blue (GUI/default column #4078f2)
+ .{ 0xa6, 0x26, 0xa4 }, // 5: ~/05-genizah/doomemacs-themes/doom-themes-base.el:203 ansi-color-magenta :foreground magenta; magenta = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:78 magenta (GUI/default column #a626a4)
+ .{ 0x01, 0x84, 0xbc }, // 6: ~/05-genizah/doomemacs-themes/doom-themes-base.el:204 ansi-color-cyan :foreground cyan; cyan = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:80 cyan (GUI/default column #0184bc)
+ .{ 0x38, 0x3a, 0x42 }, // 7: ~/05-genizah/doomemacs-themes/doom-themes-base.el:205 ansi-color-white :foreground fg; fg = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:48 fg (GUI/default column #383a42)
+ .{ 0x9c, 0xa0, 0xa4 }, // 8: ~/05-genizah/doomemacs-themes/doom-themes-base.el:210 ansi-color-bright-black &light :foreground base4; base4 = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:64 base4 (GUI/default column #9ca0a4)
+ .{ 0xe8, 0x6f, 0x64 }, // 9: ~/05-genizah/doomemacs-themes/doom-themes-base.el:212 ansi-color-bright-red :foreground (doom-lighten red #e45649 0.15) = (doom-blend #e45649 #FFFFFF 0.85) = #e86f64 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343)
+ .{ 0x6a, 0xaf, 0x69 }, // 10: ~/05-genizah/doomemacs-themes/doom-themes-base.el:213 ansi-color-bright-green :foreground (doom-lighten green #50a14f 0.15) = (doom-blend #50a14f #FFFFFF 0.85) = #6aaf69 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343)
+ .{ 0xa7, 0x7e, 0x27 }, // 11: ~/05-genizah/doomemacs-themes/doom-themes-base.el:214 ansi-color-bright-yellow :foreground (doom-lighten yellow #986801 0.15) = (doom-blend #986801 #FFFFFF 0.85) = #a77e27 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343)
+ .{ 0x5c, 0x8c, 0xf3 }, // 12: ~/05-genizah/doomemacs-themes/doom-themes-base.el:215 ansi-color-bright-blue :foreground (doom-lighten blue #4078f2 0.15) = (doom-blend #4078f2 #FFFFFF 0.85) = #5c8cf3 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343)
+ .{ 0xb3, 0x46, 0xb1 }, // 13: ~/05-genizah/doomemacs-themes/doom-themes-base.el:216 ansi-color-bright-magenta :foreground (doom-lighten magenta #a626a4 0.15) = (doom-blend #a626a4 #FFFFFF 0.85) = #b346b1 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343)
+ .{ 0x27, 0x96, 0xc6 }, // 14: ~/05-genizah/doomemacs-themes/doom-themes-base.el:217 ansi-color-bright-cyan :foreground (doom-lighten cyan #0184bc 0.15) = (doom-blend #0184bc #FFFFFF 0.85) = #2796c6 (computed per ~/05-genizah/doomemacs-themes/doom-themes.el:301-343)
+ .{ 0x1b, 0x22, 0x29 }, // 15: ~/05-genizah/doomemacs-themes/doom-themes-base.el:218 ansi-color-bright-white :foreground base8; base8 = ~/05-genizah/doomemacs-themes/themes/doom-one-light-theme.el:68 base8 (GUI/default column #1b2229)
+ },
+};
diff --git a/src/themes/dracula.zig b/src/themes/dracula.zig
new file mode 100644
index 00000000..16c6ba43
--- /dev/null
+++ b/src/themes/dracula.zig
@@ -0,0 +1,79 @@
+//! Dracula: Dracula, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! official palette (Dracula, Alucard); url https://github.com/dracula/dracula-theme; genizah ~/05-genizah/dracula-theme; commit 1e04a4b768302fa2de37d09cc7b8087eabcd3ed8
+//! official VS Code port: roles, UI, ANSI, Soft transform; url https://github.com/dracula/visual-studio-code; genizah ~/05-genizah/dracula-visual-studio-code; commit a08a206f2c8420ba3c05f0e8d01d43b2f933fdf8
+//! Tags: the tab (focused = the active tab, unfocused = the inactive one).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #282a36 -> #41434d: 1.11:1 off the unfocused #21222c, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #6272a4 -> #7b89b3: 3.36:1 on #21222c, tag text needs 4.5:1 to be read and run
+pub const theme = .{
+ .name = "dracula",
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:185 editor.background: *BG (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:10)
+ .bg = .{ 0x28, 0x2a, 0x36 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:184 editor.foreground: *FG (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:11)
+ .fg = .{ 0xf8, 0xf8, 0xf2 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:189 editor.selectionBackground: *SELECTION (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:12)
+ .sel_bg = .{ 0x44, 0x47, 0x5a },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xf8, 0xf8, 0xf2 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:178 tab.inactiveBackground: *BGDark (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:50)
+ .tag_bg = .{ 0x21, 0x22, 0x2c },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:179 tab.inactiveForeground: *COMMENT (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:13) #6272a4, adjusted
+ .tag_fg = .{ 0x7b, 0x89, 0xb3 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:172 tab.activeBackground: *BG (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:10) #282a36, adjusted
+ .tag_active_bg = .{ 0x41, 0x43, 0x4d },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:173 tab.activeForeground: *FG (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:11)
+ .tag_active_fg = .{ 0xf8, 0xf8, 0xf2 },
+ // the focused grip: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:175 tab.activeBorderTop: !alpha [*PINK, 80] over BG
+ .box = .{ 0x94, 0x52, 0x7e },
+ // derived: the grip at rest, the accent #94527e faded toward the unfocused tag #21222c as far as 1.6:1 off it (0.50 of the way)
+ .box_dim = .{ 0x5b, 0x3a, 0x55 },
+ // an unsaved grip: the original's warning colour, ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:250 editorWarning.foreground: *CYAN (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:14)
+ .box_dirty = .{ 0x8b, 0xe9, 0xfd },
+ // derived: the column grip, the original's function colour, ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:688 tokenColors "Function names" *GREEN
+ .column_box = .{ 0x50, 0xfa, 0x7b },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:725 tokenColors "Keywords" *PINK
+ .kw = .{ 0xff, 0x79, 0xc6 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:978 tokenColors "Strings" *YELLOW
+ .str = .{ 0xf1, 0xfa, 0x8c },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:640 tokenColors "Constants" *PURPLE
+ .num = .{ 0xbd, 0x93, 0xf9 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:601 tokenColors "Comments" *COMMENT
+ .comment = .{ 0x62, 0x72, 0xa4 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:186 editorLineNumber.foreground: *COMMENT (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:13)
+ .lineno = .{ 0x62, 0x72, 0xa4 },
+ // derived: none in the original; the page
+ .scroll_track = .{ 0x28, 0x2a, 0x36 },
+ // derived: none in the original; line numbers, ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:186 editorLineNumber.foreground: *COMMENT (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:13)
+ .scroll_thumb = .{ 0x62, 0x72, 0xa4 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:167 editorGroup.border: *PURPLE (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:18)
+ .border = .{ 0xbd, 0x93, 0xf9 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:197 editor.findMatchBackground: !alpha [*ORANGE, 80] over BG
+ .search_bg = .{ 0x94, 0x71, 0x51 },
+ // derived: the original keeps text colours over a match; the page's text, whichever reads better on it
+ .search_fg = .{ 0xf8, 0xf8, 0xf2 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:248 editorError.foreground: *RED (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:19)
+ .diagnostic_error = .{ 0xff, 0x55, 0x55 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:250 editorWarning.foreground: *CYAN (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:14)
+ .diagnostic_warning = .{ 0x8b, 0xe9, 0xfd },
+ .palette = .{
+ .{ 0x21, 0x22, 0x2c }, // 0: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:67 terminal.ansiBlack: *COLOR0 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:22)
+ .{ 0xff, 0x55, 0x55 }, // 1: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:68 terminal.ansiRed: *COLOR1 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:23)
+ .{ 0x50, 0xfa, 0x7b }, // 2: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:69 terminal.ansiGreen: *COLOR2 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:24)
+ .{ 0xf1, 0xfa, 0x8c }, // 3: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:70 terminal.ansiYellow: *COLOR3 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:25)
+ .{ 0xbd, 0x93, 0xf9 }, // 4: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:71 terminal.ansiBlue: *COLOR4 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:26)
+ .{ 0xff, 0x79, 0xc6 }, // 5: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:72 terminal.ansiMagenta: *COLOR5 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:27)
+ .{ 0x8b, 0xe9, 0xfd }, // 6: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:73 terminal.ansiCyan: *COLOR6 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:28)
+ .{ 0xf8, 0xf8, 0xf2 }, // 7: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:74 terminal.ansiWhite: *COLOR7 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:29)
+ .{ 0x62, 0x72, 0xa4 }, // 8: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:59 terminal.ansiBrightBlack: *COLOR8 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:30)
+ .{ 0xff, 0x6e, 0x6e }, // 9: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:60 terminal.ansiBrightRed: *COLOR9 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:31)
+ .{ 0x69, 0xff, 0x94 }, // 10: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:61 terminal.ansiBrightGreen: *COLOR10 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:32)
+ .{ 0xff, 0xff, 0xa5 }, // 11: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:62 terminal.ansiBrightYellow: *COLOR11 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:33)
+ .{ 0xd6, 0xac, 0xff }, // 12: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:63 terminal.ansiBrightBlue: *COLOR12 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:34)
+ .{ 0xff, 0x92, 0xdf }, // 13: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:64 terminal.ansiBrightMagenta: *COLOR13 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:35)
+ .{ 0xa4, 0xff, 0xff }, // 14: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:65 terminal.ansiBrightCyan: *COLOR14 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:36)
+ .{ 0xff, 0xff, 0xff }, // 15: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:66 terminal.ansiBrightWhite: *COLOR15 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:37)
+ },
+};
diff --git a/src/themes/dracula_soft.zig b/src/themes/dracula_soft.zig
new file mode 100644
index 00000000..9a8d5788
--- /dev/null
+++ b/src/themes/dracula_soft.zig
@@ -0,0 +1,79 @@
+//! Dracula Soft: Dracula, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! official palette (Dracula, Alucard); url https://github.com/dracula/dracula-theme; genizah ~/05-genizah/dracula-theme; commit 1e04a4b768302fa2de37d09cc7b8087eabcd3ed8
+//! official VS Code port: roles, UI, ANSI, Soft transform; url https://github.com/dracula/visual-studio-code; genizah ~/05-genizah/dracula-visual-studio-code; commit a08a206f2c8420ba3c05f0e8d01d43b2f933fdf8
+//! Tags: the tab (focused = the active tab, unfocused = the inactive one).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #282a36 -> #454751: 1.05:1 off the unfocused #272727, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #7b7f8b -> #8b8e99: 3.74:1 on #272727, tag text needs 4.5:1 to be read and run
+pub const theme = .{
+ .name = "dracula_soft",
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:185 editor.background: *BG (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:10)
+ .bg = .{ 0x28, 0x2a, 0x36 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:184 editor.foreground: *FG (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:11); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .fg = .{ 0xf6, 0xf6, 0xf4 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:189 editor.selectionBackground: *SELECTION (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:12)
+ .sel_bg = .{ 0x44, 0x47, 0x5a },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xf6, 0xf6, 0xf4 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:178 tab.inactiveBackground: *BGDark (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:50); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .tag_bg = .{ 0x27, 0x27, 0x27 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:179 tab.inactiveForeground: *COMMENT (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:13); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44) #7b7f8b, adjusted
+ .tag_fg = .{ 0x8b, 0x8e, 0x99 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:172 tab.activeBackground: *BG (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:10) #282a36, adjusted
+ .tag_active_bg = .{ 0x45, 0x47, 0x51 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:173 tab.activeForeground: *FG (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:11); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .tag_active_fg = .{ 0xf6, 0xf6, 0xf4 },
+ // the focused grip: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:175 tab.activeBorderTop: !alpha [*PINK, 80] over BG; Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .box = .{ 0x7d, 0x69, 0x76 },
+ // derived: the grip at rest, the accent #7d6976 faded toward the unfocused tag #272727 as far as 1.6:1 off it (0.46 of the way)
+ .box_dim = .{ 0x4f, 0x45, 0x4b },
+ // an unsaved grip: the original's warning colour, ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:250 editorWarning.foreground: *CYAN (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:14); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .box_dirty = .{ 0x97, 0xe1, 0xf1 },
+ // derived: the column grip, the original's function colour, ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:688 tokenColors "Function names" *GREEN; Soft: desaturate(20)
+ .column_box = .{ 0x62, 0xe8, 0x84 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:725 tokenColors "Keywords" *PINK; Soft: desaturate(20)
+ .kw = .{ 0xf2, 0x86, 0xc4 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:978 tokenColors "Strings" *YELLOW; Soft: desaturate(20)
+ .str = .{ 0xe7, 0xee, 0x98 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:640 tokenColors "Constants" *PURPLE; Soft: desaturate(20)
+ .num = .{ 0xbf, 0x9e, 0xee },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:601 tokenColors "Comments" *COMMENT; Soft: desaturate(20)
+ .comment = .{ 0x7b, 0x7f, 0x8b },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:186 editorLineNumber.foreground: *COMMENT (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:13); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .lineno = .{ 0x7b, 0x7f, 0x8b },
+ // derived: none in the original; the page
+ .scroll_track = .{ 0x28, 0x2a, 0x36 },
+ // derived: none in the original; line numbers, ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:186 editorLineNumber.foreground: *COMMENT (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:13); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .scroll_thumb = .{ 0x7b, 0x7f, 0x8b },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:167 editorGroup.border: *PURPLE (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:18); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .border = .{ 0xbf, 0x9e, 0xee },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:197 editor.findMatchBackground: !alpha [*ORANGE, 80] over BG
+ .search_bg = .{ 0x94, 0x71, 0x51 },
+ // derived: the original keeps text colours over a match; the page's text, whichever reads better on it
+ .search_fg = .{ 0xf6, 0xf6, 0xf4 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:248 editorError.foreground: *RED (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:19); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .diagnostic_error = .{ 0xee, 0x66, 0x66 },
+ // ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:250 editorWarning.foreground: *CYAN (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:14); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .diagnostic_warning = .{ 0x97, 0xe1, 0xf1 },
+ .palette = .{
+ .{ 0x27, 0x27, 0x27 }, // 0: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:67 terminal.ansiBlack: *COLOR0 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:22); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .{ 0xee, 0x66, 0x66 }, // 1: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:68 terminal.ansiRed: *COLOR1 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:23); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .{ 0x62, 0xe8, 0x84 }, // 2: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:69 terminal.ansiGreen: *COLOR2 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:24); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .{ 0xe7, 0xee, 0x98 }, // 3: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:70 terminal.ansiYellow: *COLOR3 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:25); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .{ 0xbf, 0x9e, 0xee }, // 4: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:71 terminal.ansiBlue: *COLOR4 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:26); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .{ 0xf2, 0x86, 0xc4 }, // 5: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:72 terminal.ansiMagenta: *COLOR5 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:27); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .{ 0x97, 0xe1, 0xf1 }, // 6: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:73 terminal.ansiCyan: *COLOR6 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:28); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .{ 0xf6, 0xf6, 0xf4 }, // 7: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:74 terminal.ansiWhite: *COLOR7 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:29); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .{ 0x7b, 0x7f, 0x8b }, // 8: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:59 terminal.ansiBrightBlack: *COLOR8 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:30); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .{ 0xf0, 0x7d, 0x7d }, // 9: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:60 terminal.ansiBrightRed: *COLOR9 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:31); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .{ 0x78, 0xf0, 0x9a }, // 10: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:61 terminal.ansiBrightGreen: *COLOR10 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:32); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .{ 0xf6, 0xf6, 0xae }, // 11: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:62 terminal.ansiBrightYellow: *COLOR11 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:33); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .{ 0xd6, 0xb4, 0xf7 }, // 12: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:63 terminal.ansiBrightBlue: *COLOR12 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:34); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .{ 0xf4, 0x9d, 0xda }, // 13: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:64 terminal.ansiBrightMagenta: *COLOR13 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:35); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .{ 0xad, 0xf6, 0xf6 }, // 14: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:65 terminal.ansiBrightCyan: *COLOR14 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:36); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ .{ 0xff, 0xff, 0xff }, // 15: ~/05-genizah/dracula-visual-studio-code/src/dracula.yml:66 terminal.ansiBrightWhite: *COLOR15 (~/05-genizah/dracula-visual-studio-code/src/dracula.yml:37); Soft: desaturate(20) (~/05-genizah/dracula-visual-studio-code/scripts/generate.js:44)
+ },
+};
diff --git a/src/themes/duckbones.zig b/src/themes/duckbones.zig
new file mode 100644
index 00000000..c4f86997
--- /dev/null
+++ b/src/themes/duckbones.zig
@@ -0,0 +1,92 @@
+//! Duckbones: Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #232738 -> #353b51: 1.13:1 off the unfocused #1a1d2b, pardes needs 1.5:1 on a dark page
+//! - focused grip #444a67 -> #4f5570: 1.28:1 on the focused tag #353b51
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/duckbones/palette.lua + lua/zenbones/specs/dark.lua); values are the
+//! default 'ness' baked into colors/duckbones.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours. Dark-only
+//! flavour (zenbones.json background='dark'). TabLineSel is guifg/guibg=NONE
+//! (bold only), so it inherits Normal. TabLine links to StatusLine, TabLineFill
+//! to StatusLineNC. Accent is the lualine normal-mode a bg, a muted neutral
+//! (the theme is contrast-based, not hue-accented). Modified = Changed fg.
+pub const theme = .{
+ .name = "duckbones",
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:34 Normal guibg
+ .bg = .{ 0x0e, 0x10, 0x1a },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:34 Normal guifg
+ .fg = .{ 0xeb, 0xef, 0xc0 },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:212 Visual guibg
+ .sel_bg = .{ 0x37, 0x38, 0x2d },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xeb, 0xef, 0xc0 },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:199 StatusLineNC guibg
+ .tag_bg = .{ 0x1a, 0x1d, 0x2b },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:199 StatusLineNC guifg
+ .tag_fg = .{ 0xf0, 0xf5, 0xc4 },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:196 StatusLine guibg #232738, adjusted
+ .tag_active_bg = .{ 0x35, 0x3b, 0x51 },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:196 StatusLine guifg
+ .tag_active_fg = .{ 0xeb, 0xef, 0xc0 },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/duckbones_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0x4f, 0x55, 0x70 },
+ // derived: the grip at rest, the accent #444a67 faded toward the unfocused tag #1a1d2b as far as 1.6:1 off it (0.76 of the way)
+ .box_dim = .{ 0x3a, 0x3f, 0x59 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/duckbones.vim:42 Changed guifg
+ .box_dirty = .{ 0x00, 0xa3, 0xcb },
+ // derived: the column grip, the original's type colour, ~/05-genizah/zenbones.nvim/colors/duckbones.vim:208 Type guifg
+ .column_box = .{ 0x89, 0x8f, 0xb1 },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:193 Statement guifg (no Keyword group defined; Vim default links Keyword to Statement)
+ .kw = .{ 0x79, 0x5c, 0xcc },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:202 String guifg
+ .str = .{ 0xae, 0xb1, 0x8d },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:165 Number guifg
+ .num = .{ 0xae, 0xb1, 0x8d },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:48 Comment guifg
+ .comment = .{ 0x5a, 0x5f, 0x7b },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:151 LineNr guifg
+ .lineno = .{ 0x51, 0x58, 0x7a },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:63 CursorLineNr guifg
+ .lineno_active = .{ 0xeb, 0xef, 0xc0 },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:168 PmenuSbar guibg
+ .scroll_track = .{ 0x4b, 0x51, 0x70 },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:170 PmenuThumb guibg
+ .scroll_thumb = .{ 0x75, 0x7d, 0xa3 },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:219 WinSeparator guifg
+ .border = .{ 0x51, 0x58, 0x7a },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:174 Search guibg
+ .search_bg = .{ 0x4d, 0x31, 0x91 },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:174 Search guifg
+ .search_fg = .{ 0xeb, 0xef, 0xc0 },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:114 Error guifg via DiagnosticError->Error (link :115)
+ .diagnostic_error = .{ 0xe0, 0x36, 0x00 },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:213 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :214)
+ .diagnostic_warning = .{ 0xe3, 0x95, 0x00 },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:73 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x00, 0xa3, 0xcb },
+ // ~/05-genizah/zenbones.nvim/colors/duckbones.vim:68 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0x79, 0x5c, 0xcc },
+ .palette = .{
+ .{ 0x0e, 0x10, 0x1a }, // 0: ~/05-genizah/zenbones.nvim/colors/duckbones.vim:18 g:terminal_color_0
+ .{ 0xe0, 0x36, 0x00 }, // 1: ~/05-genizah/zenbones.nvim/colors/duckbones.vim:19 g:terminal_color_1
+ .{ 0x5d, 0xcd, 0x97 }, // 2: ~/05-genizah/zenbones.nvim/colors/duckbones.vim:20 g:terminal_color_2
+ .{ 0xe3, 0x95, 0x00 }, // 3: ~/05-genizah/zenbones.nvim/colors/duckbones.vim:21 g:terminal_color_3
+ .{ 0x00, 0xa3, 0xcb }, // 4: ~/05-genizah/zenbones.nvim/colors/duckbones.vim:22 g:terminal_color_4
+ .{ 0x79, 0x5c, 0xcc }, // 5: ~/05-genizah/zenbones.nvim/colors/duckbones.vim:23 g:terminal_color_5
+ .{ 0x00, 0xa3, 0xcb }, // 6: ~/05-genizah/zenbones.nvim/colors/duckbones.vim:24 g:terminal_color_6
+ .{ 0xeb, 0xef, 0xc0 }, // 7: ~/05-genizah/zenbones.nvim/colors/duckbones.vim:25 g:terminal_color_7
+ .{ 0x2b, 0x2f, 0x46 }, // 8: ~/05-genizah/zenbones.nvim/colors/duckbones.vim:26 g:terminal_color_8
+ .{ 0xff, 0x48, 0x21 }, // 9: ~/05-genizah/zenbones.nvim/colors/duckbones.vim:27 g:terminal_color_9
+ .{ 0x58, 0xdb, 0x9e }, // 10: ~/05-genizah/zenbones.nvim/colors/duckbones.vim:28 g:terminal_color_10
+ .{ 0xf6, 0xa1, 0x00 }, // 11: ~/05-genizah/zenbones.nvim/colors/duckbones.vim:29 g:terminal_color_11
+ .{ 0x00, 0xb4, 0xe0 }, // 12: ~/05-genizah/zenbones.nvim/colors/duckbones.vim:30 g:terminal_color_12
+ .{ 0xb3, 0xa1, 0xe6 }, // 13: ~/05-genizah/zenbones.nvim/colors/duckbones.vim:31 g:terminal_color_13
+ .{ 0x00, 0xb4, 0xe0 }, // 14: ~/05-genizah/zenbones.nvim/colors/duckbones.vim:32 g:terminal_color_14
+ .{ 0xb3, 0xb6, 0x92 }, // 15: ~/05-genizah/zenbones.nvim/colors/duckbones.vim:33 g:terminal_color_15
+ },
+};
diff --git a/src/themes/forestbones_dark.zig b/src/themes/forestbones_dark.zig
new file mode 100644
index 00000000..11825525
--- /dev/null
+++ b/src/themes/forestbones_dark.zig
@@ -0,0 +1,92 @@
+//! Forestbones (dark): Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #3e4850 -> #505a64: 1.15:1 off the unfocused #353f46, pardes needs 1.5:1 on a dark page
+//! - focused grip #596873 -> #6a7781: 1.22:1 on the focused tag #505a64
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/forestbones/palette.lua + lua/zenbones/specs/dark.lua); values are the
+//! default 'ness' baked into colors/forestbones.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours. TabLineSel
+//! is guifg/guibg=NONE (bold only), so it inherits Normal. TabLine links to
+//! StatusLine, TabLineFill to StatusLineNC. Accent is the lualine normal-mode a
+//! bg, a muted neutral (the theme is contrast-based, not hue-accented).
+//! Modified = Changed fg.
+pub const theme = .{
+ .name = "forestbones_dark",
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:33 Normal guibg
+ .bg = .{ 0x2c, 0x34, 0x3a },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:33 Normal guifg
+ .fg = .{ 0xe7, 0xdc, 0xc4 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:211 Visual guibg
+ .sel_bg = .{ 0x61, 0x5b, 0x51 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xe7, 0xdc, 0xc4 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:198 StatusLineNC guibg
+ .tag_bg = .{ 0x35, 0x3f, 0x46 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:198 StatusLineNC guifg
+ .tag_fg = .{ 0xed, 0xe5, 0xd4 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:195 StatusLine guibg #3e4850, adjusted
+ .tag_active_bg = .{ 0x50, 0x5a, 0x64 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:195 StatusLine guifg
+ .tag_active_fg = .{ 0xe7, 0xdc, 0xc4 },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/forestbones_dark_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0x6a, 0x77, 0x81 },
+ // derived: the grip at rest, the accent #596873 faded toward the unfocused tag #353f46 as far as 1.6:1 off it (0.76 of the way)
+ .box_dim = .{ 0x50, 0x5e, 0x68 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/forestbones.vim:41 Changed guifg
+ .box_dirty = .{ 0x7f, 0xbc, 0xb4 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/zenbones.nvim/colors/forestbones.vim:207 Type guifg
+ .column_box = .{ 0x7f, 0xbc, 0xb4 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:192 Statement guifg (no Keyword group defined; Vim default links Keyword to Statement)
+ .kw = .{ 0xa9, 0xc1, 0x81 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:201 String guifg
+ .str = .{ 0xad, 0xa2, 0x8b },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:164 Number guifg
+ .num = .{ 0xad, 0xa2, 0x8b },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:47 Comment guifg
+ .comment = .{ 0x6e, 0x7b, 0x85 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:150 LineNr guifg
+ .lineno = .{ 0x66, 0x77, 0x83 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:62 CursorLineNr guifg
+ .lineno_active = .{ 0xe7, 0xdc, 0xc4 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:167 PmenuSbar guibg
+ .scroll_track = .{ 0x60, 0x6f, 0x7b },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:169 PmenuThumb guibg
+ .scroll_thumb = .{ 0x82, 0x96, 0xa5 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:218 WinSeparator guifg
+ .border = .{ 0x66, 0x77, 0x83 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:173 Search guibg
+ .search_bg = .{ 0x9e, 0x51, 0x79 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:173 Search guifg
+ .search_fg = .{ 0xe7, 0xdc, 0xc4 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:113 Error guifg via DiagnosticError->Error (link :114)
+ .diagnostic_error = .{ 0xe6, 0x7c, 0x7f },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:212 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :213)
+ .diagnostic_warning = .{ 0xdd, 0xbd, 0x7f },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:72 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x7f, 0xbc, 0xb4 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:67 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0xd6, 0x9a, 0xb7 },
+ .palette = .{
+ .{ 0x2c, 0x34, 0x3a }, // 0: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:17 g:terminal_color_0
+ .{ 0xe6, 0x7c, 0x7f }, // 1: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:18 g:terminal_color_1
+ .{ 0xa9, 0xc1, 0x81 }, // 2: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:19 g:terminal_color_2
+ .{ 0xdd, 0xbd, 0x7f }, // 3: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:20 g:terminal_color_3
+ .{ 0x7f, 0xbc, 0xb4 }, // 4: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:21 g:terminal_color_4
+ .{ 0xd6, 0x9a, 0xb7 }, // 5: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:22 g:terminal_color_5
+ .{ 0x83, 0xc1, 0x93 }, // 6: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:23 g:terminal_color_6
+ .{ 0xe7, 0xdc, 0xc4 }, // 7: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:24 g:terminal_color_7
+ .{ 0x45, 0x52, 0x5c }, // 8: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:25 g:terminal_color_8
+ .{ 0xed, 0x92, 0x94 }, // 9: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:26 g:terminal_color_9
+ .{ 0xb0, 0xce, 0x7b }, // 10: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:27 g:terminal_color_10
+ .{ 0xed, 0xc7, 0x7a }, // 11: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:28 g:terminal_color_11
+ .{ 0x7a, 0xc9, 0xc0 }, // 12: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:29 g:terminal_color_12
+ .{ 0xe5, 0xa7, 0xc4 }, // 13: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:30 g:terminal_color_13
+ .{ 0x7d, 0xd0, 0x93 }, // 14: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:31 g:terminal_color_14
+ .{ 0xb2, 0xa7, 0x90 }, // 15: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:32 g:terminal_color_15
+ },
+};
diff --git a/src/themes/forestbones_light.zig b/src/themes/forestbones_light.zig
new file mode 100644
index 00000000..e227d6a9
--- /dev/null
+++ b/src/themes/forestbones_light.zig
@@ -0,0 +1,94 @@
+//! Forestbones (light): Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #e3d191 -> #ddca87: 1.18:1 off the unfocused #f3e2aa, pardes needs 1.25:1 on a light page
+//! - unfocused tag text #758690 -> #59666d: 2.92:1 on #f3e2aa, tag text needs 4.5:1 to be read and run
+//! - focused tag text #4f5b62 -> #4c575e: 4.28:1 on #ddca87, tag text needs 4.5:1
+//! - focused grip #c0b079 -> #b1a26f: 1.32:1 on the focused tag #ddca87
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/forestbones/palette.lua + lua/zenbones/specs/light.lua); values are the
+//! default 'ness' baked into colors/forestbones.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours. TabLineSel
+//! is guifg/guibg=NONE (bold only), so it inherits Normal. TabLine links to
+//! StatusLine, TabLineFill to StatusLineNC. Accent is the lualine normal-mode a
+//! bg, a muted neutral (the theme is contrast-based, not hue-accented).
+//! Modified = Changed fg.
+pub const theme = .{
+ .name = "forestbones_light",
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:267 Normal guibg
+ .bg = .{ 0xfa, 0xf3, 0xe1 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:267 Normal guifg
+ .fg = .{ 0x4f, 0x5b, 0x62 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:445 Visual guibg
+ .sel_bg = .{ 0xd3, 0xdf, 0xe6 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x4f, 0x5b, 0x62 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:432 StatusLineNC guibg
+ .tag_bg = .{ 0xf3, 0xe2, 0xaa },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:432 StatusLineNC guifg #758690, adjusted
+ .tag_fg = .{ 0x59, 0x66, 0x6d },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:429 StatusLine guibg #e3d191, adjusted
+ .tag_active_bg = .{ 0xdd, 0xca, 0x87 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:429 StatusLine guifg #4f5b62, adjusted
+ .tag_active_fg = .{ 0x4c, 0x57, 0x5e },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/forestbones_light_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0xb1, 0xa2, 0x6f },
+ // derived: the grip at rest, the accent #c0b079 faded toward the unfocused tag #f3e2aa as far as 1.6:1 off it (0.94 of the way)
+ .box_dim = .{ 0xc3, 0xb3, 0x7c },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/forestbones.vim:275 Changed guifg
+ .box_dirty = .{ 0x3a, 0x94, 0xc4 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/zenbones.nvim/colors/forestbones.vim:441 Type guifg
+ .column_box = .{ 0x3a, 0x94, 0xc4 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:426 Statement guifg (no Keyword group defined; Vim default links Keyword to Statement)
+ .kw = .{ 0x8d, 0xa2, 0x00 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:435 String guifg
+ .str = .{ 0x73, 0x84, 0x8d },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:398 Number guifg
+ .num = .{ 0x73, 0x84, 0x8d },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:281 Comment guifg
+ .comment = .{ 0x9a, 0x90, 0x71 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:384 LineNr guifg
+ .lineno = .{ 0xa9, 0x9b, 0x6a },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:296 CursorLineNr guifg
+ .lineno_active = .{ 0x4f, 0x5b, 0x62 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:401 PmenuSbar guibg
+ .scroll_track = .{ 0xb7, 0xa8, 0x74 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:403 PmenuThumb guibg
+ .scroll_thumb = .{ 0xfc, 0xf9, 0xf1 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:452 WinSeparator guifg
+ .border = .{ 0xa9, 0x9b, 0x6a },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:407 Search guibg
+ .search_bg = .{ 0xee, 0xba, 0xdb },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:407 Search guifg
+ .search_fg = .{ 0x4f, 0x5b, 0x62 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:347 Error guifg via DiagnosticError->Error (link :348)
+ .diagnostic_error = .{ 0xf8, 0x55, 0x50 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:446 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :447)
+ .diagnostic_warning = .{ 0xde, 0xa0, 0x00 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:306 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x3a, 0x94, 0xc4 },
+ // ~/05-genizah/zenbones.nvim/colors/forestbones.vim:301 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0xdf, 0x69, 0xba },
+ .palette = .{
+ .{ 0xfa, 0xf3, 0xe1 }, // 0: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:251 g:terminal_color_0
+ .{ 0xf8, 0x55, 0x50 }, // 1: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:252 g:terminal_color_1
+ .{ 0x8d, 0xa2, 0x00 }, // 2: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:253 g:terminal_color_2
+ .{ 0xde, 0xa0, 0x00 }, // 3: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:254 g:terminal_color_3
+ .{ 0x3a, 0x94, 0xc4 }, // 4: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:255 g:terminal_color_4
+ .{ 0xdf, 0x69, 0xba }, // 5: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:256 g:terminal_color_5
+ .{ 0x36, 0xa8, 0x7e }, // 6: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:257 g:terminal_color_6
+ .{ 0x4f, 0x5b, 0x62 }, // 7: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:258 g:terminal_color_7
+ .{ 0xdb, 0xc9, 0x88 }, // 8: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:259 g:terminal_color_8
+ .{ 0xe6, 0x27, 0x1c }, // 9: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:260 g:terminal_color_9
+ .{ 0x75, 0x87, 0x00 }, // 10: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:261 g:terminal_color_10
+ .{ 0xb9, 0x85, 0x00 }, // 11: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:262 g:terminal_color_11
+ .{ 0x29, 0x7c, 0xa6 }, // 12: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:263 g:terminal_color_12
+ .{ 0xca, 0x43, 0xa3 }, // 13: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:264 g:terminal_color_13
+ .{ 0x25, 0x8c, 0x67 }, // 14: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:265 g:terminal_color_14
+ .{ 0x6e, 0x7f, 0x88 }, // 15: ~/05-genizah/zenbones.nvim/colors/forestbones.vim:266 g:terminal_color_15
+ },
+};
diff --git a/src/themes/kanagawabones.zig b/src/themes/kanagawabones.zig
new file mode 100644
index 00000000..94e5aab0
--- /dev/null
+++ b/src/themes/kanagawabones.zig
@@ -0,0 +1,93 @@
+//! Kanagawabones: Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #363644 -> #444456: 1.21:1 off the unfocused #292934, pardes needs 1.5:1 on a dark page
+//! - focused grip #565668 -> #606071: 1.33:1 on the focused tag #444456
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/kanagawabones/palette.lua + lua/zenbones/specs/dark.lua); values are
+//! the default 'ness' baked into colors/kanagawabones.vim. Palette = the 16
+//! names term.lua maps to ANSI, taken from the generated terminal colours.
+//! Dark-only flavour (zenbones.json background='dark'). TabLineSel is
+//! guifg/guibg=NONE (bold only), so it inherits Normal. TabLine links to
+//! StatusLine, TabLineFill to StatusLineNC. Accent is the lualine normal-mode a
+//! bg, a muted neutral (the theme is contrast-based, not hue-accented).
+//! Modified = Changed fg.
+pub const theme = .{
+ .name = "kanagawabones",
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:34 Normal guibg
+ .bg = .{ 0x1f, 0x1f, 0x28 },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:34 Normal guifg
+ .fg = .{ 0xdd, 0xd8, 0xbb },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:212 Visual guibg
+ .sel_bg = .{ 0x49, 0x47, 0x3e },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xdd, 0xd8, 0xbb },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:199 StatusLineNC guibg
+ .tag_bg = .{ 0x29, 0x29, 0x34 },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:199 StatusLineNC guifg
+ .tag_fg = .{ 0xe9, 0xe3, 0xc5 },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:196 StatusLine guibg #363644, adjusted
+ .tag_active_bg = .{ 0x44, 0x44, 0x56 },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:196 StatusLine guifg
+ .tag_active_fg = .{ 0xdd, 0xd8, 0xbb },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/kanagawabones_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0x60, 0x60, 0x71 },
+ // derived: the grip at rest, the accent #565668 faded toward the unfocused tag #292934 as far as 1.6:1 off it (0.70 of the way)
+ .box_dim = .{ 0x49, 0x49, 0x58 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:42 Changed guifg
+ .box_dirty = .{ 0x7e, 0xb3, 0xc9 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:208 Type guifg
+ .column_box = .{ 0x97, 0x97, 0xa5 },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:192 Statement guifg (no Keyword group defined; Vim default links Keyword to Statement)
+ .kw = .{ 0xdd, 0xd8, 0xbb },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:202 String guifg
+ .str = .{ 0xa2, 0x9e, 0x89 },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:165 Number guifg
+ .num = .{ 0xa2, 0x9e, 0x89 },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:48 Comment guifg
+ .comment = .{ 0x69, 0x69, 0x77 },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:151 LineNr guifg
+ .lineno = .{ 0x64, 0x64, 0x76 },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:63 CursorLineNr guifg
+ .lineno_active = .{ 0xdd, 0xd8, 0xbb },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:168 PmenuSbar guibg
+ .scroll_track = .{ 0x5d, 0x5d, 0x6f },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:170 PmenuThumb guibg
+ .scroll_thumb = .{ 0x85, 0x85, 0x94 },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:219 WinSeparator guifg
+ .border = .{ 0x64, 0x64, 0x76 },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:173 Search guibg
+ .search_bg = .{ 0x61, 0x4a, 0x82 },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:173 Search guifg
+ .search_fg = .{ 0xdd, 0xd8, 0xbb },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:114 Error guifg via DiagnosticError->Error (link :115)
+ .diagnostic_error = .{ 0xe4, 0x6a, 0x78 },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:213 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :214)
+ .diagnostic_warning = .{ 0xe5, 0xc2, 0x83 },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:73 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x7e, 0xb3, 0xc9 },
+ // ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:68 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0x95, 0x7f, 0xb8 },
+ .palette = .{
+ .{ 0x1f, 0x1f, 0x28 }, // 0: ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:18 g:terminal_color_0
+ .{ 0xe4, 0x6a, 0x78 }, // 1: ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:19 g:terminal_color_1
+ .{ 0x98, 0xbc, 0x6d }, // 2: ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:20 g:terminal_color_2
+ .{ 0xe5, 0xc2, 0x83 }, // 3: ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:21 g:terminal_color_3
+ .{ 0x7e, 0xb3, 0xc9 }, // 4: ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:22 g:terminal_color_4
+ .{ 0x95, 0x7f, 0xb8 }, // 5: ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:23 g:terminal_color_5
+ .{ 0x7e, 0xb3, 0xc9 }, // 6: ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:24 g:terminal_color_6
+ .{ 0xdd, 0xd8, 0xbb }, // 7: ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:25 g:terminal_color_7
+ .{ 0x3c, 0x3c, 0x51 }, // 8: ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:26 g:terminal_color_8
+ .{ 0xec, 0x81, 0x8c }, // 9: ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:27 g:terminal_color_9
+ .{ 0x9e, 0xc9, 0x67 }, // 10: ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:28 g:terminal_color_10
+ .{ 0xf1, 0xc9, 0x82 }, // 11: ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:29 g:terminal_color_11
+ .{ 0x7b, 0xc2, 0xdf }, // 12: ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:30 g:terminal_color_12
+ .{ 0xa9, 0x8f, 0xd2 }, // 13: ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:31 g:terminal_color_13
+ .{ 0x7b, 0xc2, 0xdf }, // 14: ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:32 g:terminal_color_14
+ .{ 0xa8, 0xa4, 0x8d }, // 15: ~/05-genizah/zenbones.nvim/colors/kanagawabones.vim:33 g:terminal_color_15
+ },
+};
diff --git a/src/themes/monokai.zig b/src/themes/monokai.zig
new file mode 100644
index 00000000..13e72336
--- /dev/null
+++ b/src/themes/monokai.zig
@@ -0,0 +1,84 @@
+//! Monokai: Monokai, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! Sublime Text 2.0.2's Monokai.tmTheme (Wimer Hazenberg), from 'Color Scheme - Default.sublime-package'; url https://download.sublimetext.com/Sublime%20Text%202.0.2%20x64.tar.bz2; genizah ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme
+//! Tags: the tab (focused = the active tab, unfocused = the inactive one).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #49483e -> #59584f: 1.19:1 off the unfocused #3e3d32, pardes needs 1.5:1 on a dark page
+//! - rules between panes #3b3a32 -> #4a4941: 1.30:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+pub const theme = .{
+ .name = "monokai",
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:13 background
+ .bg = .{ 0x27, 0x28, 0x22 },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:17 foreground
+ .fg = .{ 0xf8, 0xf8, 0xf2 },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:23 selection
+ .sel_bg = .{ 0x49, 0x48, 0x3e },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xf8, 0xf8, 0xf2 },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:21 lineHighlight — derived: no UI colours in a tmTheme; unfocused tag = lineHighlight
+ .tag_bg = .{ 0x3e, 0x3d, 0x32 },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:17 foreground — derived: foreground
+ .tag_fg = .{ 0xf8, 0xf8, 0xf2 },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:23 selection — derived: focused tag = selection #49483e, adjusted
+ .tag_active_bg = .{ 0x59, 0x58, 0x4f },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:17 foreground — derived: foreground
+ .tag_active_fg = .{ 0xf8, 0xf8, 0xf2 },
+ // the focused grip: ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:122 "Keyword" foreground — derived: Keyword pink
+ .box = .{ 0xf9, 0x26, 0x72 },
+ // derived: the grip at rest, the accent #f92672 faded toward the unfocused tag #3e3d32 as far as 1.6:1 off it (0.54 of the way)
+ .box_dim = .{ 0xa3, 0x31, 0x55 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:66 "String" foreground — derived: diff.changed #E6DB74
+ .box_dirty = .{ 0xe6, 0xdb, 0x74 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:148 "Storage type" foreground
+ .column_box = .{ 0x66, 0xd9, 0xef },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:122 "Keyword" foreground
+ .kw = .{ 0xf9, 0x26, 0x72 },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:66 "String" foreground
+ .str = .{ 0xe6, 0xdb, 0x74 },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:77 "Number" foreground
+ .num = .{ 0xae, 0x81, 0xff },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:55 "Comment" foreground
+ .comment = .{ 0x75, 0x71, 0x5e },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:55 "Comment" foreground — derived: the tmTheme has no gutter colour; Comment
+ .lineno = .{ 0x75, 0x71, 0x5e },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:17 foreground — derived: foreground
+ .lineno_active = .{ 0xf8, 0xf8, 0xf2 },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:13 background — derived: page
+ .scroll_track = .{ 0x27, 0x28, 0x22 },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:23 selection — derived: selection
+ .scroll_thumb = .{ 0x49, 0x48, 0x3e },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:19 invisibles — derived: invisibles #3b3a32, adjusted
+ .border = .{ 0x4a, 0x49, 0x41 },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:25 findHighlight
+ .search_bg = .{ 0xff, 0xe7, 0x92 },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:27 findHighlightForeground
+ .search_fg = .{ 0x00, 0x00, 0x00 },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:122 "Keyword" foreground — derived: Invalid is drawn on #F92672, the Keyword colour
+ .diagnostic_error = .{ 0xf9, 0x26, 0x72 },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:200 "Function argument" foreground — derived: Function argument orange
+ .diagnostic_warning = .{ 0xfd, 0x97, 0x1f },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:148 "Storage type" foreground — derived: Storage type cyan
+ .diagnostic_info = .{ 0x66, 0xd9, 0xef },
+ // ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:55 "Comment" foreground — derived: Comment
+ .diagnostic_hint = .{ 0x75, 0x71, 0x5e },
+ .palette = .{
+ .{ 0x27, 0x28, 0x22 }, // 0: ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:13 background — derived: ANSI 0: a tmTheme has no terminal colours; its own palette in ANSI order (black=page, red=Keyword, green=Function name, yellow=String, blue and cyan=Storage type, magenta=Number, white=foreground, bright black=Comment, brights repeat)
+ .{ 0xf9, 0x26, 0x72 }, // 1: ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:122 "Keyword" foreground — derived: ANSI 1: a tmTheme has no terminal colours; its own palette in ANSI order (black=page, red=Keyword, green=Function name, yellow=String, blue and cyan=Storage type, magenta=Number, white=foreground, bright black=Comment, brights repeat)
+ .{ 0xa6, 0xe2, 0x2e }, // 2: ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:187 "Function name" foreground — derived: ANSI 2: a tmTheme has no terminal colours; its own palette in ANSI order (black=page, red=Keyword, green=Function name, yellow=String, blue and cyan=Storage type, magenta=Number, white=foreground, bright black=Comment, brights repeat)
+ .{ 0xe6, 0xdb, 0x74 }, // 3: ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:66 "String" foreground — derived: ANSI 3: a tmTheme has no terminal colours; its own palette in ANSI order (black=page, red=Keyword, green=Function name, yellow=String, blue and cyan=Storage type, magenta=Number, white=foreground, bright black=Comment, brights repeat)
+ .{ 0x66, 0xd9, 0xef }, // 4: ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:148 "Storage type" foreground — derived: ANSI 4: a tmTheme has no terminal colours; its own palette in ANSI order (black=page, red=Keyword, green=Function name, yellow=String, blue and cyan=Storage type, magenta=Number, white=foreground, bright black=Comment, brights repeat)
+ .{ 0xae, 0x81, 0xff }, // 5: ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:77 "Number" foreground — derived: ANSI 5: a tmTheme has no terminal colours; its own palette in ANSI order (black=page, red=Keyword, green=Function name, yellow=String, blue and cyan=Storage type, magenta=Number, white=foreground, bright black=Comment, brights repeat)
+ .{ 0x66, 0xd9, 0xef }, // 6: ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:148 "Storage type" foreground — derived: ANSI 6: a tmTheme has no terminal colours; its own palette in ANSI order (black=page, red=Keyword, green=Function name, yellow=String, blue and cyan=Storage type, magenta=Number, white=foreground, bright black=Comment, brights repeat)
+ .{ 0xf8, 0xf8, 0xf2 }, // 7: ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:17 foreground — derived: ANSI 7: a tmTheme has no terminal colours; its own palette in ANSI order (black=page, red=Keyword, green=Function name, yellow=String, blue and cyan=Storage type, magenta=Number, white=foreground, bright black=Comment, brights repeat)
+ .{ 0x75, 0x71, 0x5e }, // 8: ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:55 "Comment" foreground — derived: ANSI 8: a tmTheme has no terminal colours; its own palette in ANSI order (black=page, red=Keyword, green=Function name, yellow=String, blue and cyan=Storage type, magenta=Number, white=foreground, bright black=Comment, brights repeat)
+ .{ 0xf9, 0x26, 0x72 }, // 9: ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:122 "Keyword" foreground — derived: ANSI 9: a tmTheme has no terminal colours; its own palette in ANSI order (black=page, red=Keyword, green=Function name, yellow=String, blue and cyan=Storage type, magenta=Number, white=foreground, bright black=Comment, brights repeat)
+ .{ 0xa6, 0xe2, 0x2e }, // 10: ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:187 "Function name" foreground — derived: ANSI 10: a tmTheme has no terminal colours; its own palette in ANSI order (black=page, red=Keyword, green=Function name, yellow=String, blue and cyan=Storage type, magenta=Number, white=foreground, bright black=Comment, brights repeat)
+ .{ 0xe6, 0xdb, 0x74 }, // 11: ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:66 "String" foreground — derived: ANSI 11: a tmTheme has no terminal colours; its own palette in ANSI order (black=page, red=Keyword, green=Function name, yellow=String, blue and cyan=Storage type, magenta=Number, white=foreground, bright black=Comment, brights repeat)
+ .{ 0x66, 0xd9, 0xef }, // 12: ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:148 "Storage type" foreground — derived: ANSI 12: a tmTheme has no terminal colours; its own palette in ANSI order (black=page, red=Keyword, green=Function name, yellow=String, blue and cyan=Storage type, magenta=Number, white=foreground, bright black=Comment, brights repeat)
+ .{ 0xae, 0x81, 0xff }, // 13: ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:77 "Number" foreground — derived: ANSI 13: a tmTheme has no terminal colours; its own palette in ANSI order (black=page, red=Keyword, green=Function name, yellow=String, blue and cyan=Storage type, magenta=Number, white=foreground, bright black=Comment, brights repeat)
+ .{ 0x66, 0xd9, 0xef }, // 14: ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:148 "Storage type" foreground — derived: ANSI 14: a tmTheme has no terminal colours; its own palette in ANSI order (black=page, red=Keyword, green=Function name, yellow=String, blue and cyan=Storage type, magenta=Number, white=foreground, bright black=Comment, brights repeat)
+ .{ 0xf8, 0xf8, 0xf2 }, // 15: ~/05-genizah/sublime-text-2-color-schemes/Monokai.tmTheme:17 foreground — derived: ANSI 15: a tmTheme has no terminal colours; its own palette in ANSI order (black=page, red=Keyword, green=Function name, yellow=String, blue and cyan=Storage type, magenta=Number, white=foreground, bright black=Comment, brights repeat)
+ },
+};
diff --git a/src/themes/monokai_pro.zig b/src/themes/monokai_pro.zig
new file mode 100644
index 00000000..46a5d58b
--- /dev/null
+++ b/src/themes/monokai_pro.zig
@@ -0,0 +1,85 @@
+//! Monokai Pro: Monokai Pro, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! Monokai Pro for VS Code 2.0.15, published by Wimer Hazenberg (Monokai), verified on Open VSX; url https://open-vsx.org/api/monokai/theme-monokai-pro-vscode/2.0.15/file/monokai.theme-monokai-pro-vscode-2.0.15.vsix; genizah ~/05-genizah/monokai-pro-vscode/vsix/extension/themes
+//! Tags: the tab (focused = the active tab, unfocused = the inactive one).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #2d2a2e -> #4f4637: 1.00:1 off the unfocused #2d2a2e, pardes needs 1.5:1 on a dark page
+//! - rules between panes #221f22 -> #4e4b4d: 1.15:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! - scroll thumb #434044 -> #4a484b: 1.39:1 on its track #2d2a2e
+pub const theme = .{
+ .name = "monokai_pro",
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."editor.background"
+ .bg = .{ 0x2d, 0x2a, 0x2e },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."editor.foreground"
+ .fg = .{ 0xfc, 0xfc, 0xfa },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."editor.selectionBackground" #c1c0c026 over #2d2a2e
+ .sel_bg = .{ 0x43, 0x40, 0x44 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xfc, 0xfc, 0xfa },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."tab.inactiveBackground"
+ .tag_bg = .{ 0x2d, 0x2a, 0x2e },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."tab.inactiveForeground"
+ .tag_fg = .{ 0x93, 0x92, 0x93 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."tab.activeBackground" #2d2a2e, adjusted
+ .tag_active_bg = .{ 0x4f, 0x46, 0x37 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."tab.activeForeground"
+ .tag_active_fg = .{ 0xff, 0xd8, 0x66 },
+ // the focused grip: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."tab.activeBorder"
+ .box = .{ 0xff, 0xd8, 0x66 },
+ // derived: the grip at rest, the accent #ffd866 faded toward the unfocused tag #2d2a2e as far as 1.6:1 off it (0.20 of the way)
+ .box_dim = .{ 0x57, 0x4d, 0x39 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."editorGutter.modifiedBackground"
+ .box_dirty = .{ 0xfc, 0x98, 0x67 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 tokenColors[42] "entity.name.type"
+ .column_box = .{ 0x78, 0xdc, 0xe8 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 tokenColors[57] "keyword"
+ .kw = .{ 0xff, 0x61, 0x88 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 tokenColors[132] "string"
+ .str = .{ 0xff, 0xd8, 0x66 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 tokenColors[22] "constant.numeric"
+ .num = .{ 0xab, 0x9d, 0xf2 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 tokenColors[0] "comment"
+ .comment = .{ 0x72, 0x70, 0x72 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."editorLineNumber.foreground"
+ .lineno = .{ 0x5b, 0x59, 0x5c },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."editorLineNumber.activeForeground"
+ .lineno_active = .{ 0xc1, 0xc0, 0xc0 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 no scrollbar.background: the editor shows through
+ .scroll_track = .{ 0x2d, 0x2a, 0x2e },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."scrollbarSlider.background" #c1c0c026 over #2d2a2e #434044, adjusted
+ .scroll_thumb = .{ 0x4a, 0x48, 0x4b },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."editorGroup.border" #221f22, adjusted
+ .border = .{ 0x4e, 0x4b, 0x4d },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."editor.findMatchBackground" #fcfcfa26 over #2d2a2e
+ .search_bg = .{ 0x4c, 0x49, 0x4c },
+ // derived: the original keeps text colours over a match; the page's text, whichever reads better on it
+ .search_fg = .{ 0xfc, 0xfc, 0xfa },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."editorError.foreground"
+ .diagnostic_error = .{ 0xff, 0x61, 0x88 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."editorWarning.foreground"
+ .diagnostic_warning = .{ 0xfc, 0x98, 0x67 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."editorInfo.foreground"
+ .diagnostic_info = .{ 0x78, 0xdc, 0xe8 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."editorHint.foreground"
+ .diagnostic_hint = .{ 0xab, 0x9d, 0xf2 },
+ .palette = .{
+ .{ 0x40, 0x3e, 0x41 }, // 0: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."terminal.ansiBlack"
+ .{ 0xff, 0x61, 0x88 }, // 1: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."terminal.ansiRed"
+ .{ 0xa9, 0xdc, 0x76 }, // 2: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."terminal.ansiGreen"
+ .{ 0xff, 0xd8, 0x66 }, // 3: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."terminal.ansiYellow"
+ .{ 0xfc, 0x98, 0x67 }, // 4: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."terminal.ansiBlue"
+ .{ 0xab, 0x9d, 0xf2 }, // 5: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."terminal.ansiMagenta"
+ .{ 0x78, 0xdc, 0xe8 }, // 6: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."terminal.ansiCyan"
+ .{ 0xfc, 0xfc, 0xfa }, // 7: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."terminal.ansiWhite"
+ .{ 0x72, 0x70, 0x72 }, // 8: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."terminal.ansiBrightBlack"
+ .{ 0xff, 0x61, 0x88 }, // 9: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."terminal.ansiBrightRed"
+ .{ 0xa9, 0xdc, 0x76 }, // 10: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."terminal.ansiBrightGreen"
+ .{ 0xff, 0xd8, 0x66 }, // 11: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."terminal.ansiBrightYellow"
+ .{ 0xfc, 0x98, 0x67 }, // 12: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."terminal.ansiBrightBlue"
+ .{ 0xab, 0x9d, 0xf2 }, // 13: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."terminal.ansiBrightMagenta"
+ .{ 0x78, 0xdc, 0xe8 }, // 14: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."terminal.ansiBrightCyan"
+ .{ 0xfc, 0xfc, 0xfa }, // 15: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro.json:1 colors."terminal.ansiBrightWhite"
+ },
+};
diff --git a/src/themes/monokai_pro_classic.zig b/src/themes/monokai_pro_classic.zig
new file mode 100644
index 00000000..69f26327
--- /dev/null
+++ b/src/themes/monokai_pro_classic.zig
@@ -0,0 +1,85 @@
+//! Monokai Classic: Monokai Pro, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! Monokai Pro for VS Code 2.0.15, published by Wimer Hazenberg (Monokai), verified on Open VSX; url https://open-vsx.org/api/monokai/theme-monokai-pro-vscode/2.0.15/file/monokai.theme-monokai-pro-vscode-2.0.15.vsix; genizah ~/05-genizah/monokai-pro-vscode/vsix/extension/themes
+//! Tags: the tab (focused = the active tab, unfocused = the inactive one).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #272822 -> #46452f: 1.00:1 off the unfocused #272822, pardes needs 1.5:1 on a dark page
+//! - rules between panes #1d1e19 -> #4a4b44: 1.13:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! - scroll thumb #3e3f38 -> #46473f: 1.40:1 on its track #272822
+pub const theme = .{
+ .name = "monokai_pro_classic",
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."editor.background"
+ .bg = .{ 0x27, 0x28, 0x22 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."editor.foreground"
+ .fg = .{ 0xfd, 0xff, 0xf1 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."editor.selectionBackground" #c0c1b526 over #272822
+ .sel_bg = .{ 0x3e, 0x3f, 0x38 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xfd, 0xff, 0xf1 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."tab.inactiveBackground"
+ .tag_bg = .{ 0x27, 0x28, 0x22 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."tab.inactiveForeground"
+ .tag_fg = .{ 0x91, 0x92, 0x88 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."tab.activeBackground" #272822, adjusted
+ .tag_active_bg = .{ 0x46, 0x45, 0x2f },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."tab.activeForeground"
+ .tag_active_fg = .{ 0xe6, 0xdb, 0x74 },
+ // the focused grip: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."tab.activeBorder"
+ .box = .{ 0xe6, 0xdb, 0x74 },
+ // derived: the grip at rest, the accent #e6db74 faded toward the unfocused tag #272822 as far as 1.6:1 off it (0.20 of the way)
+ .box_dim = .{ 0x4d, 0x4c, 0x32 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."editorGutter.modifiedBackground"
+ .box_dirty = .{ 0xfd, 0x97, 0x1f },
+ // derived: the column grip, the original's type colour, ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 tokenColors[42] "entity.name.type"
+ .column_box = .{ 0x66, 0xd9, 0xef },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 tokenColors[57] "keyword"
+ .kw = .{ 0xf9, 0x26, 0x72 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 tokenColors[132] "string"
+ .str = .{ 0xe6, 0xdb, 0x74 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 tokenColors[22] "constant.numeric"
+ .num = .{ 0xae, 0x81, 0xff },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 tokenColors[0] "comment"
+ .comment = .{ 0x6e, 0x70, 0x66 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."editorLineNumber.foreground"
+ .lineno = .{ 0x57, 0x58, 0x4f },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."editorLineNumber.activeForeground"
+ .lineno_active = .{ 0xc0, 0xc1, 0xb5 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 no scrollbar.background: the editor shows through
+ .scroll_track = .{ 0x27, 0x28, 0x22 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."scrollbarSlider.background" #c0c1b526 over #272822 #3e3f38, adjusted
+ .scroll_thumb = .{ 0x46, 0x47, 0x3f },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."editorGroup.border" #1d1e19, adjusted
+ .border = .{ 0x4a, 0x4b, 0x44 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."editor.findMatchBackground" #fdfff126 over #272822
+ .search_bg = .{ 0x47, 0x48, 0x41 },
+ // derived: the original keeps text colours over a match; the page's text, whichever reads better on it
+ .search_fg = .{ 0xfd, 0xff, 0xf1 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."editorError.foreground"
+ .diagnostic_error = .{ 0xf9, 0x26, 0x72 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."editorWarning.foreground"
+ .diagnostic_warning = .{ 0xfd, 0x97, 0x1f },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."editorInfo.foreground"
+ .diagnostic_info = .{ 0x66, 0xd9, 0xef },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."editorHint.foreground"
+ .diagnostic_hint = .{ 0xae, 0x81, 0xff },
+ .palette = .{
+ .{ 0x3b, 0x3c, 0x35 }, // 0: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."terminal.ansiBlack"
+ .{ 0xf9, 0x26, 0x72 }, // 1: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."terminal.ansiRed"
+ .{ 0xa6, 0xe2, 0x2e }, // 2: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."terminal.ansiGreen"
+ .{ 0xe6, 0xdb, 0x74 }, // 3: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."terminal.ansiYellow"
+ .{ 0xfd, 0x97, 0x1f }, // 4: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."terminal.ansiBlue"
+ .{ 0xae, 0x81, 0xff }, // 5: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."terminal.ansiMagenta"
+ .{ 0x66, 0xd9, 0xef }, // 6: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."terminal.ansiCyan"
+ .{ 0xfd, 0xff, 0xf1 }, // 7: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."terminal.ansiWhite"
+ .{ 0x6e, 0x70, 0x66 }, // 8: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."terminal.ansiBrightBlack"
+ .{ 0xf9, 0x26, 0x72 }, // 9: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."terminal.ansiBrightRed"
+ .{ 0xa6, 0xe2, 0x2e }, // 10: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."terminal.ansiBrightGreen"
+ .{ 0xe6, 0xdb, 0x74 }, // 11: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."terminal.ansiBrightYellow"
+ .{ 0xfd, 0x97, 0x1f }, // 12: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."terminal.ansiBrightBlue"
+ .{ 0xae, 0x81, 0xff }, // 13: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."terminal.ansiBrightMagenta"
+ .{ 0x66, 0xd9, 0xef }, // 14: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."terminal.ansiBrightCyan"
+ .{ 0xfd, 0xff, 0xf1 }, // 15: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Classic.json:1 colors."terminal.ansiBrightWhite"
+ },
+};
diff --git a/src/themes/monokai_pro_light.zig b/src/themes/monokai_pro_light.zig
new file mode 100644
index 00000000..ef638731
--- /dev/null
+++ b/src/themes/monokai_pro_light.zig
@@ -0,0 +1,87 @@
+//! Monokai Pro Light: Monokai Pro, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! Monokai Pro for VS Code 2.0.15, published by Wimer Hazenberg (Monokai), verified on Open VSX; url https://open-vsx.org/api/monokai/theme-monokai-pro-vscode/2.0.15/file/monokai.theme-monokai-pro-vscode-2.0.15.vsix; genizah ~/05-genizah/monokai-pro-vscode/vsix/extension/themes
+//! Tags: the tab (focused = the active tab, unfocused = the inactive one).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #faf4f2 -> #f5d1d9: 1.00:1 off the unfocused #faf4f2, pardes needs 1.25:1 on a light page
+//! - unfocused tag text #918c8e -> #716d6f: 3.04:1 on #faf4f2, tag text needs 4.5:1 to be read and run
+//! - focused tag text #e14775 -> #a73557: 2.80:1 on #f5d1d9, tag text needs 4.5:1
+//! - rules between panes #ede7e5 -> #c6c0c0: 1.12:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! - scroll thumb #e5e0de -> #cec9c8: 1.20:1 on its track #faf4f2
+pub const theme = .{
+ .name = "monokai_pro_light",
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."editor.background"
+ .bg = .{ 0xfa, 0xf4, 0xf2 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."editor.foreground"
+ .fg = .{ 0x29, 0x24, 0x2a },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."editor.selectionBackground" #706b6e26 over #faf4f2
+ .sel_bg = .{ 0xe5, 0xe0, 0xde },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x29, 0x24, 0x2a },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."tab.inactiveBackground"
+ .tag_bg = .{ 0xfa, 0xf4, 0xf2 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."tab.inactiveForeground" #918c8e, adjusted
+ .tag_fg = .{ 0x71, 0x6d, 0x6f },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."tab.activeBackground" #faf4f2, adjusted
+ .tag_active_bg = .{ 0xf5, 0xd1, 0xd9 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."tab.activeForeground" #e14775, adjusted
+ .tag_active_fg = .{ 0xa7, 0x35, 0x57 },
+ // the focused grip: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."tab.activeBorder"
+ .box = .{ 0xe1, 0x47, 0x75 },
+ // derived: the grip at rest, the accent #e14775 faded toward the unfocused tag #faf4f2 as far as 1.6:1 off it (0.38 of the way)
+ .box_dim = .{ 0xf1, 0xb2, 0xc3 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."editorGutter.modifiedBackground"
+ .box_dirty = .{ 0xe1, 0x60, 0x32 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 tokenColors[42] "entity.name.type"
+ .column_box = .{ 0x1c, 0x8c, 0xa8 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 tokenColors[57] "keyword"
+ .kw = .{ 0xe1, 0x47, 0x75 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 tokenColors[132] "string"
+ .str = .{ 0xcc, 0x7a, 0x0a },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 tokenColors[22] "constant.numeric"
+ .num = .{ 0x70, 0x58, 0xbe },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 tokenColors[0] "comment"
+ .comment = .{ 0xa5, 0x9f, 0xa0 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."editorLineNumber.foreground"
+ .lineno = .{ 0xbf, 0xb9, 0xba },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."editorLineNumber.activeForeground"
+ .lineno_active = .{ 0x70, 0x6b, 0x6e },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 no scrollbar.background: the editor shows through
+ .scroll_track = .{ 0xfa, 0xf4, 0xf2 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."scrollbarSlider.background" #706b6e26 over #faf4f2 #e5e0de, adjusted
+ .scroll_thumb = .{ 0xce, 0xc9, 0xc8 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."editorGroup.border" #ede7e5, adjusted
+ .border = .{ 0xc6, 0xc0, 0xc0 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."editor.findMatchBackground" #29242a26 over #faf4f2
+ .search_bg = .{ 0xdb, 0xd5, 0xd4 },
+ // derived: the original keeps text colours over a match; the page's text, whichever reads better on it
+ .search_fg = .{ 0x29, 0x24, 0x2a },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."editorError.foreground"
+ .diagnostic_error = .{ 0xe1, 0x47, 0x75 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."editorWarning.foreground"
+ .diagnostic_warning = .{ 0xe1, 0x60, 0x32 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."editorInfo.foreground"
+ .diagnostic_info = .{ 0x1c, 0x8c, 0xa8 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."editorHint.foreground"
+ .diagnostic_hint = .{ 0x70, 0x58, 0xbe },
+ .palette = .{
+ .{ 0xd3, 0xcd, 0xcc }, // 0: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."terminal.ansiBlack"
+ .{ 0xe1, 0x47, 0x75 }, // 1: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."terminal.ansiRed"
+ .{ 0x26, 0x9d, 0x69 }, // 2: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."terminal.ansiGreen"
+ .{ 0xcc, 0x7a, 0x0a }, // 3: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."terminal.ansiYellow"
+ .{ 0xe1, 0x60, 0x32 }, // 4: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."terminal.ansiBlue"
+ .{ 0x70, 0x58, 0xbe }, // 5: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."terminal.ansiMagenta"
+ .{ 0x1c, 0x8c, 0xa8 }, // 6: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."terminal.ansiCyan"
+ .{ 0x29, 0x24, 0x2a }, // 7: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."terminal.ansiWhite"
+ .{ 0xa5, 0x9f, 0xa0 }, // 8: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."terminal.ansiBrightBlack"
+ .{ 0xe1, 0x47, 0x75 }, // 9: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."terminal.ansiBrightRed"
+ .{ 0x26, 0x9d, 0x69 }, // 10: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."terminal.ansiBrightGreen"
+ .{ 0xcc, 0x7a, 0x0a }, // 11: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."terminal.ansiBrightYellow"
+ .{ 0xe1, 0x60, 0x32 }, // 12: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."terminal.ansiBrightBlue"
+ .{ 0x70, 0x58, 0xbe }, // 13: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."terminal.ansiBrightMagenta"
+ .{ 0x1c, 0x8c, 0xa8 }, // 14: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."terminal.ansiBrightCyan"
+ .{ 0x29, 0x24, 0x2a }, // 15: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light.json:1 colors."terminal.ansiBrightWhite"
+ },
+};
diff --git a/src/themes/monokai_pro_light_sun.zig b/src/themes/monokai_pro_light_sun.zig
new file mode 100644
index 00000000..2d023deb
--- /dev/null
+++ b/src/themes/monokai_pro_light_sun.zig
@@ -0,0 +1,87 @@
+//! Monokai Pro Light (Filter Sun): Monokai Pro, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! Monokai Pro for VS Code 2.0.15, published by Wimer Hazenberg (Monokai), verified on Open VSX; url https://open-vsx.org/api/monokai/theme-monokai-pro-vscode/2.0.15/file/monokai.theme-monokai-pro-vscode-2.0.15.vsix; genizah ~/05-genizah/monokai-pro-vscode/vsix/extension/themes
+//! Tags: the tab (focused = the active tab, unfocused = the inactive one).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #f8efe7 -> #f0cdcf: 1.00:1 off the unfocused #f8efe7, pardes needs 1.25:1 on a light page
+//! - unfocused tag text #92898a -> #726b6c: 3.00:1 on #f8efe7, tag text needs 4.5:1 to be read and run
+//! - focused tag text #ce4770 -> #9d3655: 3.01:1 on #f0cdcf, tag text needs 4.5:1
+//! - rules between panes #eee5de -> #c7bebb: 1.09:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! - scroll thumb #e4dbd5 -> #cac1be: 1.20:1 on its track #f8efe7
+pub const theme = .{
+ .name = "monokai_pro_light_sun",
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."editor.background"
+ .bg = .{ 0xf8, 0xef, 0xe7 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."editor.foreground"
+ .fg = .{ 0x2c, 0x23, 0x2e },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."editor.selectionBackground" #72696d26 over #f8efe7
+ .sel_bg = .{ 0xe4, 0xdb, 0xd5 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x2c, 0x23, 0x2e },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."tab.inactiveBackground"
+ .tag_bg = .{ 0xf8, 0xef, 0xe7 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."tab.inactiveForeground" #92898a, adjusted
+ .tag_fg = .{ 0x72, 0x6b, 0x6c },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."tab.activeBackground" #f8efe7, adjusted
+ .tag_active_bg = .{ 0xf0, 0xcd, 0xcf },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."tab.activeForeground" #ce4770, adjusted
+ .tag_active_fg = .{ 0x9d, 0x36, 0x55 },
+ // the focused grip: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."tab.activeBorder"
+ .box = .{ 0xce, 0x47, 0x70 },
+ // derived: the grip at rest, the accent #ce4770 faded toward the unfocused tag #f8efe7 as far as 1.6:1 off it (0.38 of the way)
+ .box_dim = .{ 0xe8, 0xaf, 0xba },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."editorGutter.modifiedBackground"
+ .box_dirty = .{ 0xd4, 0x57, 0x2b },
+ // derived: the column grip, the original's type colour, ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 tokenColors[42] "entity.name.type"
+ .column_box = .{ 0x24, 0x73, 0xb6 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 tokenColors[57] "keyword"
+ .kw = .{ 0xce, 0x47, 0x70 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 tokenColors[132] "string"
+ .str = .{ 0xb1, 0x68, 0x03 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 tokenColors[22] "constant.numeric"
+ .num = .{ 0x68, 0x51, 0xa2 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 tokenColors[0] "comment"
+ .comment = .{ 0xa5, 0x9c, 0x9c },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."editorLineNumber.foreground"
+ .lineno = .{ 0xbe, 0xb5, 0xb3 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."editorLineNumber.activeForeground"
+ .lineno_active = .{ 0x72, 0x69, 0x6d },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 no scrollbar.background: the editor shows through
+ .scroll_track = .{ 0xf8, 0xef, 0xe7 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."scrollbarSlider.background" #72696d26 over #f8efe7 #e4dbd5, adjusted
+ .scroll_thumb = .{ 0xca, 0xc1, 0xbe },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."editorGroup.border" #eee5de, adjusted
+ .border = .{ 0xc7, 0xbe, 0xbb },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."editor.findMatchBackground" #2c232e26 over #f8efe7
+ .search_bg = .{ 0xda, 0xd1, 0xcb },
+ // derived: the original keeps text colours over a match; the page's text, whichever reads better on it
+ .search_fg = .{ 0x2c, 0x23, 0x2e },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."editorError.foreground"
+ .diagnostic_error = .{ 0xce, 0x47, 0x70 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."editorWarning.foreground"
+ .diagnostic_warning = .{ 0xd4, 0x57, 0x2b },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."editorInfo.foreground"
+ .diagnostic_info = .{ 0x24, 0x73, 0xb6 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."editorHint.foreground"
+ .diagnostic_hint = .{ 0x68, 0x51, 0xa2 },
+ .palette = .{
+ .{ 0xd2, 0xc9, 0xc4 }, // 0: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."terminal.ansiBlack"
+ .{ 0xce, 0x47, 0x70 }, // 1: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."terminal.ansiRed"
+ .{ 0x21, 0x88, 0x71 }, // 2: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."terminal.ansiGreen"
+ .{ 0xb1, 0x68, 0x03 }, // 3: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."terminal.ansiYellow"
+ .{ 0xd4, 0x57, 0x2b }, // 4: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."terminal.ansiBlue"
+ .{ 0x68, 0x51, 0xa2 }, // 5: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."terminal.ansiMagenta"
+ .{ 0x24, 0x73, 0xb6 }, // 6: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."terminal.ansiCyan"
+ .{ 0x2c, 0x23, 0x2e }, // 7: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."terminal.ansiWhite"
+ .{ 0xa5, 0x9c, 0x9c }, // 8: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."terminal.ansiBrightBlack"
+ .{ 0xce, 0x47, 0x70 }, // 9: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."terminal.ansiBrightRed"
+ .{ 0x21, 0x88, 0x71 }, // 10: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."terminal.ansiBrightGreen"
+ .{ 0xb1, 0x68, 0x03 }, // 11: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."terminal.ansiBrightYellow"
+ .{ 0xd4, 0x57, 0x2b }, // 12: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."terminal.ansiBrightBlue"
+ .{ 0x68, 0x51, 0xa2 }, // 13: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."terminal.ansiBrightMagenta"
+ .{ 0x24, 0x73, 0xb6 }, // 14: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."terminal.ansiBrightCyan"
+ .{ 0x2c, 0x23, 0x2e }, // 15: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro Light (Filter Sun).json:1 colors."terminal.ansiBrightWhite"
+ },
+};
diff --git a/src/themes/monokai_pro_machine.zig b/src/themes/monokai_pro_machine.zig
new file mode 100644
index 00000000..5e4c6885
--- /dev/null
+++ b/src/themes/monokai_pro_machine.zig
@@ -0,0 +1,86 @@
+//! Monokai Pro (Filter Machine): Monokai Pro, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! Monokai Pro for VS Code 2.0.15, published by Wimer Hazenberg (Monokai), verified on Open VSX; url https://open-vsx.org/api/monokai/theme-monokai-pro-vscode/2.0.15/file/monokai.theme-monokai-pro-vscode-2.0.15.vsix; genizah ~/05-genizah/monokai-pro-vscode/vsix/extension/themes
+//! Tags: the tab (focused = the active tab, unfocused = the inactive one).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #273136 -> #4a4f40: 1.00:1 off the unfocused #273136, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #8b9798 -> #8d999a: 4.42:1 on #273136, tag text needs 4.5:1 to be read and run
+//! - rules between panes #1d2528 -> #485152: 1.17:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! - scroll thumb #3d474b -> #444e52: 1.39:1 on its track #273136
+pub const theme = .{
+ .name = "monokai_pro_machine",
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."editor.background"
+ .bg = .{ 0x27, 0x31, 0x36 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."editor.foreground"
+ .fg = .{ 0xf2, 0xff, 0xfc },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."editor.selectionBackground" #b8c4c326 over #273136
+ .sel_bg = .{ 0x3d, 0x47, 0x4b },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xf2, 0xff, 0xfc },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."tab.inactiveBackground"
+ .tag_bg = .{ 0x27, 0x31, 0x36 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."tab.inactiveForeground" #8b9798, adjusted
+ .tag_fg = .{ 0x8d, 0x99, 0x9a },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."tab.activeBackground" #273136, adjusted
+ .tag_active_bg = .{ 0x4a, 0x4f, 0x40 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."tab.activeForeground"
+ .tag_active_fg = .{ 0xff, 0xed, 0x72 },
+ // the focused grip: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."tab.activeBorder"
+ .box = .{ 0xff, 0xed, 0x72 },
+ // derived: the grip at rest, the accent #ffed72 faded toward the unfocused tag #273136 as far as 1.6:1 off it (0.18 of the way)
+ .box_dim = .{ 0x4e, 0x53, 0x41 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."editorGutter.modifiedBackground"
+ .box_dirty = .{ 0xff, 0xb2, 0x70 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 tokenColors[42] "entity.name.type"
+ .column_box = .{ 0x7c, 0xd5, 0xf1 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 tokenColors[57] "keyword"
+ .kw = .{ 0xff, 0x6d, 0x7e },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 tokenColors[132] "string"
+ .str = .{ 0xff, 0xed, 0x72 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 tokenColors[22] "constant.numeric"
+ .num = .{ 0xba, 0xa0, 0xf8 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 tokenColors[0] "comment"
+ .comment = .{ 0x6b, 0x76, 0x78 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."editorLineNumber.foreground"
+ .lineno = .{ 0x54, 0x5f, 0x62 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."editorLineNumber.activeForeground"
+ .lineno_active = .{ 0xb8, 0xc4, 0xc3 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 no scrollbar.background: the editor shows through
+ .scroll_track = .{ 0x27, 0x31, 0x36 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."scrollbarSlider.background" #b8c4c326 over #273136 #3d474b, adjusted
+ .scroll_thumb = .{ 0x44, 0x4e, 0x52 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."editorGroup.border" #1d2528, adjusted
+ .border = .{ 0x48, 0x51, 0x52 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."editor.findMatchBackground" #f2fffc26 over #273136
+ .search_bg = .{ 0x45, 0x50, 0x54 },
+ // derived: the original keeps text colours over a match; the page's text, whichever reads better on it
+ .search_fg = .{ 0xf2, 0xff, 0xfc },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."editorError.foreground"
+ .diagnostic_error = .{ 0xff, 0x6d, 0x7e },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."editorWarning.foreground"
+ .diagnostic_warning = .{ 0xff, 0xb2, 0x70 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."editorInfo.foreground"
+ .diagnostic_info = .{ 0x7c, 0xd5, 0xf1 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."editorHint.foreground"
+ .diagnostic_hint = .{ 0xba, 0xa0, 0xf8 },
+ .palette = .{
+ .{ 0x3a, 0x44, 0x49 }, // 0: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."terminal.ansiBlack"
+ .{ 0xff, 0x6d, 0x7e }, // 1: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."terminal.ansiRed"
+ .{ 0xa2, 0xe5, 0x7b }, // 2: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."terminal.ansiGreen"
+ .{ 0xff, 0xed, 0x72 }, // 3: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."terminal.ansiYellow"
+ .{ 0xff, 0xb2, 0x70 }, // 4: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."terminal.ansiBlue"
+ .{ 0xba, 0xa0, 0xf8 }, // 5: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."terminal.ansiMagenta"
+ .{ 0x7c, 0xd5, 0xf1 }, // 6: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."terminal.ansiCyan"
+ .{ 0xf2, 0xff, 0xfc }, // 7: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."terminal.ansiWhite"
+ .{ 0x6b, 0x76, 0x78 }, // 8: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."terminal.ansiBrightBlack"
+ .{ 0xff, 0x6d, 0x7e }, // 9: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."terminal.ansiBrightRed"
+ .{ 0xa2, 0xe5, 0x7b }, // 10: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."terminal.ansiBrightGreen"
+ .{ 0xff, 0xed, 0x72 }, // 11: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."terminal.ansiBrightYellow"
+ .{ 0xff, 0xb2, 0x70 }, // 12: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."terminal.ansiBrightBlue"
+ .{ 0xba, 0xa0, 0xf8 }, // 13: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."terminal.ansiBrightMagenta"
+ .{ 0x7c, 0xd5, 0xf1 }, // 14: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."terminal.ansiBrightCyan"
+ .{ 0xf2, 0xff, 0xfc }, // 15: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Machine).json:1 colors."terminal.ansiBrightWhite"
+ },
+};
diff --git a/src/themes/monokai_pro_octagon.zig b/src/themes/monokai_pro_octagon.zig
new file mode 100644
index 00000000..62bb8c15
--- /dev/null
+++ b/src/themes/monokai_pro_octagon.zig
@@ -0,0 +1,86 @@
+//! Monokai Pro (Filter Octagon): Monokai Pro, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! Monokai Pro for VS Code 2.0.15, published by Wimer Hazenberg (Monokai), verified on Open VSX; url https://open-vsx.org/api/monokai/theme-monokai-pro-vscode/2.0.15/file/monokai.theme-monokai-pro-vscode-2.0.15.vsix; genizah ~/05-genizah/monokai-pro-vscode/vsix/extension/themes
+//! Tags: the tab (focused = the active tab, unfocused = the inactive one).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #282a3a -> #4a4642: 1.00:1 off the unfocused #282a3a, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #888d94 -> #8d9298: 4.24:1 on #282a3a, tag text needs 4.5:1 to be read and run
+//! - rules between panes #1e1f2b -> #4b4d57: 1.15:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! - scroll thumb #3d3f4e -> #444655: 1.36:1 on its track #282a3a
+pub const theme = .{
+ .name = "monokai_pro_octagon",
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."editor.background"
+ .bg = .{ 0x28, 0x2a, 0x3a },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."editor.foreground"
+ .fg = .{ 0xea, 0xf2, 0xf1 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."editor.selectionBackground" #b2b9bd26 over #282a3a
+ .sel_bg = .{ 0x3d, 0x3f, 0x4e },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xea, 0xf2, 0xf1 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."tab.inactiveBackground"
+ .tag_bg = .{ 0x28, 0x2a, 0x3a },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."tab.inactiveForeground" #888d94, adjusted
+ .tag_fg = .{ 0x8d, 0x92, 0x98 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."tab.activeBackground" #282a3a, adjusted
+ .tag_active_bg = .{ 0x4a, 0x46, 0x42 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."tab.activeForeground"
+ .tag_active_fg = .{ 0xff, 0xd7, 0x6d },
+ // the focused grip: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."tab.activeBorder"
+ .box = .{ 0xff, 0xd7, 0x6d },
+ // derived: the grip at rest, the accent #ffd76d faded toward the unfocused tag #282a3a as far as 1.6:1 off it (0.20 of the way)
+ .box_dim = .{ 0x53, 0x4d, 0x44 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."editorGutter.modifiedBackground"
+ .box_dirty = .{ 0xff, 0x9b, 0x5e },
+ // derived: the column grip, the original's type colour, ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 tokenColors[42] "entity.name.type"
+ .column_box = .{ 0x9c, 0xd1, 0xbb },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 tokenColors[57] "keyword"
+ .kw = .{ 0xff, 0x65, 0x7a },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 tokenColors[132] "string"
+ .str = .{ 0xff, 0xd7, 0x6d },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 tokenColors[22] "constant.numeric"
+ .num = .{ 0xc3, 0x9a, 0xc9 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 tokenColors[0] "comment"
+ .comment = .{ 0x69, 0x6d, 0x77 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."editorLineNumber.foreground"
+ .lineno = .{ 0x53, 0x57, 0x63 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."editorLineNumber.activeForeground"
+ .lineno_active = .{ 0xb2, 0xb9, 0xbd },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 no scrollbar.background: the editor shows through
+ .scroll_track = .{ 0x28, 0x2a, 0x3a },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."scrollbarSlider.background" #b2b9bd26 over #282a3a #3d3f4e, adjusted
+ .scroll_thumb = .{ 0x44, 0x46, 0x55 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."editorGroup.border" #1e1f2b, adjusted
+ .border = .{ 0x4b, 0x4d, 0x57 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."editor.findMatchBackground" #eaf2f126 over #282a3a
+ .search_bg = .{ 0x45, 0x48, 0x55 },
+ // derived: the original keeps text colours over a match; the page's text, whichever reads better on it
+ .search_fg = .{ 0xea, 0xf2, 0xf1 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."editorError.foreground"
+ .diagnostic_error = .{ 0xff, 0x65, 0x7a },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."editorWarning.foreground"
+ .diagnostic_warning = .{ 0xff, 0x9b, 0x5e },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."editorInfo.foreground"
+ .diagnostic_info = .{ 0x9c, 0xd1, 0xbb },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."editorHint.foreground"
+ .diagnostic_hint = .{ 0xc3, 0x9a, 0xc9 },
+ .palette = .{
+ .{ 0x3a, 0x3d, 0x4b }, // 0: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."terminal.ansiBlack"
+ .{ 0xff, 0x65, 0x7a }, // 1: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."terminal.ansiRed"
+ .{ 0xba, 0xd7, 0x61 }, // 2: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."terminal.ansiGreen"
+ .{ 0xff, 0xd7, 0x6d }, // 3: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."terminal.ansiYellow"
+ .{ 0xff, 0x9b, 0x5e }, // 4: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."terminal.ansiBlue"
+ .{ 0xc3, 0x9a, 0xc9 }, // 5: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."terminal.ansiMagenta"
+ .{ 0x9c, 0xd1, 0xbb }, // 6: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."terminal.ansiCyan"
+ .{ 0xea, 0xf2, 0xf1 }, // 7: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."terminal.ansiWhite"
+ .{ 0x69, 0x6d, 0x77 }, // 8: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."terminal.ansiBrightBlack"
+ .{ 0xff, 0x65, 0x7a }, // 9: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."terminal.ansiBrightRed"
+ .{ 0xba, 0xd7, 0x61 }, // 10: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."terminal.ansiBrightGreen"
+ .{ 0xff, 0xd7, 0x6d }, // 11: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."terminal.ansiBrightYellow"
+ .{ 0xff, 0x9b, 0x5e }, // 12: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."terminal.ansiBrightBlue"
+ .{ 0xc3, 0x9a, 0xc9 }, // 13: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."terminal.ansiBrightMagenta"
+ .{ 0x9c, 0xd1, 0xbb }, // 14: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."terminal.ansiBrightCyan"
+ .{ 0xea, 0xf2, 0xf1 }, // 15: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Octagon).json:1 colors."terminal.ansiBrightWhite"
+ },
+};
diff --git a/src/themes/monokai_pro_ristretto.zig b/src/themes/monokai_pro_ristretto.zig
new file mode 100644
index 00000000..88b1788c
--- /dev/null
+++ b/src/themes/monokai_pro_ristretto.zig
@@ -0,0 +1,86 @@
+//! Monokai Pro (Filter Ristretto): Monokai Pro, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! Monokai Pro for VS Code 2.0.15, published by Wimer Hazenberg (Monokai), verified on Open VSX; url https://open-vsx.org/api/monokai/theme-monokai-pro-vscode/2.0.15/file/monokai.theme-monokai-pro-vscode-2.0.15.vsix; genizah ~/05-genizah/monokai-pro-vscode/vsix/extension/themes
+//! Tags: the tab (focused = the active tab, unfocused = the inactive one).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #2c2525 -> #514332: 1.00:1 off the unfocused #2c2525, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #948a8b -> #968c8d: 4.48:1 on #2c2525, tag text needs 4.5:1 to be read and run
+//! - rules between panes #211c1c -> #4d4747: 1.12:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! - scroll thumb #433b3b -> #4b4242: 1.38:1 on its track #2c2525
+pub const theme = .{
+ .name = "monokai_pro_ristretto",
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."editor.background"
+ .bg = .{ 0x2c, 0x25, 0x25 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."editor.foreground"
+ .fg = .{ 0xff, 0xf1, 0xf3 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."editor.selectionBackground" #c3b7b826 over #2c2525
+ .sel_bg = .{ 0x43, 0x3b, 0x3b },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xff, 0xf1, 0xf3 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."tab.inactiveBackground"
+ .tag_bg = .{ 0x2c, 0x25, 0x25 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."tab.inactiveForeground" #948a8b, adjusted
+ .tag_fg = .{ 0x96, 0x8c, 0x8d },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."tab.activeBackground" #2c2525, adjusted
+ .tag_active_bg = .{ 0x51, 0x43, 0x32 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."tab.activeForeground"
+ .tag_active_fg = .{ 0xf9, 0xcc, 0x6c },
+ // the focused grip: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."tab.activeBorder"
+ .box = .{ 0xf9, 0xcc, 0x6c },
+ // derived: the grip at rest, the accent #f9cc6c faded toward the unfocused tag #2c2525 as far as 1.6:1 off it (0.20 of the way)
+ .box_dim = .{ 0x55, 0x46, 0x33 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."editorGutter.modifiedBackground"
+ .box_dirty = .{ 0xf3, 0x8d, 0x70 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 tokenColors[42] "entity.name.type"
+ .column_box = .{ 0x85, 0xda, 0xcc },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 tokenColors[57] "keyword"
+ .kw = .{ 0xfd, 0x68, 0x83 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 tokenColors[132] "string"
+ .str = .{ 0xf9, 0xcc, 0x6c },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 tokenColors[22] "constant.numeric"
+ .num = .{ 0xa8, 0xa9, 0xeb },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 tokenColors[0] "comment"
+ .comment = .{ 0x72, 0x69, 0x6a },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."editorLineNumber.foreground"
+ .lineno = .{ 0x5b, 0x53, 0x53 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."editorLineNumber.activeForeground"
+ .lineno_active = .{ 0xc3, 0xb7, 0xb8 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 no scrollbar.background: the editor shows through
+ .scroll_track = .{ 0x2c, 0x25, 0x25 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."scrollbarSlider.background" #c3b7b826 over #2c2525 #433b3b, adjusted
+ .scroll_thumb = .{ 0x4b, 0x42, 0x42 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."editorGroup.border" #211c1c, adjusted
+ .border = .{ 0x4d, 0x47, 0x47 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."editor.findMatchBackground" #fff1f326 over #2c2525
+ .search_bg = .{ 0x4b, 0x43, 0x44 },
+ // derived: the original keeps text colours over a match; the page's text, whichever reads better on it
+ .search_fg = .{ 0xff, 0xf1, 0xf3 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."editorError.foreground"
+ .diagnostic_error = .{ 0xfd, 0x68, 0x83 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."editorWarning.foreground"
+ .diagnostic_warning = .{ 0xf3, 0x8d, 0x70 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."editorInfo.foreground"
+ .diagnostic_info = .{ 0x85, 0xda, 0xcc },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."editorHint.foreground"
+ .diagnostic_hint = .{ 0xa8, 0xa9, 0xeb },
+ .palette = .{
+ .{ 0x40, 0x38, 0x38 }, // 0: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."terminal.ansiBlack"
+ .{ 0xfd, 0x68, 0x83 }, // 1: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."terminal.ansiRed"
+ .{ 0xad, 0xda, 0x78 }, // 2: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."terminal.ansiGreen"
+ .{ 0xf9, 0xcc, 0x6c }, // 3: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."terminal.ansiYellow"
+ .{ 0xf3, 0x8d, 0x70 }, // 4: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."terminal.ansiBlue"
+ .{ 0xa8, 0xa9, 0xeb }, // 5: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."terminal.ansiMagenta"
+ .{ 0x85, 0xda, 0xcc }, // 6: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."terminal.ansiCyan"
+ .{ 0xff, 0xf1, 0xf3 }, // 7: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."terminal.ansiWhite"
+ .{ 0x72, 0x69, 0x6a }, // 8: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."terminal.ansiBrightBlack"
+ .{ 0xfd, 0x68, 0x83 }, // 9: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."terminal.ansiBrightRed"
+ .{ 0xad, 0xda, 0x78 }, // 10: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."terminal.ansiBrightGreen"
+ .{ 0xf9, 0xcc, 0x6c }, // 11: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."terminal.ansiBrightYellow"
+ .{ 0xf3, 0x8d, 0x70 }, // 12: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."terminal.ansiBrightBlue"
+ .{ 0xa8, 0xa9, 0xeb }, // 13: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."terminal.ansiBrightMagenta"
+ .{ 0x85, 0xda, 0xcc }, // 14: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."terminal.ansiBrightCyan"
+ .{ 0xff, 0xf1, 0xf3 }, // 15: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Ristretto).json:1 colors."terminal.ansiBrightWhite"
+ },
+};
diff --git a/src/themes/monokai_pro_spectrum.zig b/src/themes/monokai_pro_spectrum.zig
new file mode 100644
index 00000000..3abd67ee
--- /dev/null
+++ b/src/themes/monokai_pro_spectrum.zig
@@ -0,0 +1,85 @@
+//! Monokai Pro (Filter Spectrum): Monokai Pro, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! Monokai Pro for VS Code 2.0.15, published by Wimer Hazenberg (Monokai), verified on Open VSX; url https://open-vsx.org/api/monokai/theme-monokai-pro-vscode/2.0.15/file/monokai.theme-monokai-pro-vscode-2.0.15.vsix; genizah ~/05-genizah/monokai-pro-vscode/vsix/extension/themes
+//! Tags: the tab (focused = the active tab, unfocused = the inactive one).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #222222 -> #45412d: 1.00:1 off the unfocused #222222, pardes needs 1.5:1 on a dark page
+//! - rules between panes #191919 -> #454447: 1.11:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! - scroll thumb #39383a -> #413f42: 1.36:1 on its track #222222
+pub const theme = .{
+ .name = "monokai_pro_spectrum",
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."editor.background"
+ .bg = .{ 0x22, 0x22, 0x22 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."editor.foreground"
+ .fg = .{ 0xf7, 0xf1, 0xff },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."editor.selectionBackground" #bab6c026 over #222222
+ .sel_bg = .{ 0x39, 0x38, 0x3a },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xf7, 0xf1, 0xff },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."tab.inactiveBackground"
+ .tag_bg = .{ 0x22, 0x22, 0x22 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."tab.inactiveForeground"
+ .tag_fg = .{ 0x8b, 0x88, 0x8f },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."tab.activeBackground" #222222, adjusted
+ .tag_active_bg = .{ 0x45, 0x41, 0x2d },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."tab.activeForeground"
+ .tag_active_fg = .{ 0xfc, 0xe5, 0x66 },
+ // the focused grip: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."tab.activeBorder"
+ .box = .{ 0xfc, 0xe5, 0x66 },
+ // derived: the grip at rest, the accent #fce566 faded toward the unfocused tag #222222 as far as 1.6:1 off it (0.18 of the way)
+ .box_dim = .{ 0x49, 0x45, 0x2e },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."editorGutter.modifiedBackground"
+ .box_dirty = .{ 0xfd, 0x93, 0x53 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 tokenColors[42] "entity.name.type"
+ .column_box = .{ 0x5a, 0xd4, 0xe6 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 tokenColors[57] "keyword"
+ .kw = .{ 0xfc, 0x61, 0x8d },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 tokenColors[132] "string"
+ .str = .{ 0xfc, 0xe5, 0x66 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 tokenColors[22] "constant.numeric"
+ .num = .{ 0x94, 0x8a, 0xe3 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 tokenColors[0] "comment"
+ .comment = .{ 0x69, 0x67, 0x6c },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."editorLineNumber.foreground"
+ .lineno = .{ 0x52, 0x50, 0x53 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."editorLineNumber.activeForeground"
+ .lineno_active = .{ 0xba, 0xb6, 0xc0 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 no scrollbar.background: the editor shows through
+ .scroll_track = .{ 0x22, 0x22, 0x22 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."scrollbarSlider.background" #bab6c026 over #222222 #39383a, adjusted
+ .scroll_thumb = .{ 0x41, 0x3f, 0x42 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."editorGroup.border" #191919, adjusted
+ .border = .{ 0x45, 0x44, 0x47 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."editor.findMatchBackground" #f7f1ff26 over #222222
+ .search_bg = .{ 0x42, 0x41, 0x43 },
+ // derived: the original keeps text colours over a match; the page's text, whichever reads better on it
+ .search_fg = .{ 0xf7, 0xf1, 0xff },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."editorError.foreground"
+ .diagnostic_error = .{ 0xfc, 0x61, 0x8d },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."editorWarning.foreground"
+ .diagnostic_warning = .{ 0xfd, 0x93, 0x53 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."editorInfo.foreground"
+ .diagnostic_info = .{ 0x5a, 0xd4, 0xe6 },
+ // ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."editorHint.foreground"
+ .diagnostic_hint = .{ 0x94, 0x8a, 0xe3 },
+ .palette = .{
+ .{ 0x36, 0x35, 0x37 }, // 0: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."terminal.ansiBlack"
+ .{ 0xfc, 0x61, 0x8d }, // 1: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."terminal.ansiRed"
+ .{ 0x7b, 0xd8, 0x8f }, // 2: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."terminal.ansiGreen"
+ .{ 0xfc, 0xe5, 0x66 }, // 3: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."terminal.ansiYellow"
+ .{ 0xfd, 0x93, 0x53 }, // 4: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."terminal.ansiBlue"
+ .{ 0x94, 0x8a, 0xe3 }, // 5: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."terminal.ansiMagenta"
+ .{ 0x5a, 0xd4, 0xe6 }, // 6: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."terminal.ansiCyan"
+ .{ 0xf7, 0xf1, 0xff }, // 7: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."terminal.ansiWhite"
+ .{ 0x69, 0x67, 0x6c }, // 8: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."terminal.ansiBrightBlack"
+ .{ 0xfc, 0x61, 0x8d }, // 9: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."terminal.ansiBrightRed"
+ .{ 0x7b, 0xd8, 0x8f }, // 10: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."terminal.ansiBrightGreen"
+ .{ 0xfc, 0xe5, 0x66 }, // 11: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."terminal.ansiBrightYellow"
+ .{ 0xfd, 0x93, 0x53 }, // 12: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."terminal.ansiBrightBlue"
+ .{ 0x94, 0x8a, 0xe3 }, // 13: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."terminal.ansiBrightMagenta"
+ .{ 0x5a, 0xd4, 0xe6 }, // 14: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."terminal.ansiBrightCyan"
+ .{ 0xf7, 0xf1, 0xff }, // 15: ~/05-genizah/monokai-pro-vscode/vsix/extension/themes/Monokai Pro (Filter Spectrum).json:1 colors."terminal.ansiBrightWhite"
+ },
+};
diff --git a/src/themes/neobones_dark.zig b/src/themes/neobones_dark.zig
new file mode 100644
index 00000000..b0167da2
--- /dev/null
+++ b/src/themes/neobones_dark.zig
@@ -0,0 +1,92 @@
+//! Neobones (dark): Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #20303a -> #2e4350: 1.15:1 off the unfocused #18252d, pardes needs 1.5:1 on a dark page
+//! - focused grip #3b5362 -> #4b616f: 1.28:1 on the focused tag #2e4350
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/neobones/palette.lua + lua/zenbones/specs/dark.lua); values are the
+//! default 'ness' baked into colors/neobones.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours. TabLineSel
+//! is guifg/guibg=NONE (bold only), so it inherits Normal. TabLine links to
+//! StatusLine, TabLineFill to StatusLineNC. Accent is the lualine normal-mode a
+//! bg, a muted neutral (the theme is contrast-based, not hue-accented).
+//! Modified = Changed fg.
+pub const theme = .{
+ .name = "neobones_dark",
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:33 Normal guibg
+ .bg = .{ 0x0f, 0x19, 0x1f },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:33 Normal guifg
+ .fg = .{ 0xc6, 0xd5, 0xcf },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:209 Visual guibg
+ .sel_bg = .{ 0x3a, 0x3e, 0x3d },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xc6, 0xd5, 0xcf },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:196 StatusLineNC guibg
+ .tag_bg = .{ 0x18, 0x25, 0x2d },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:196 StatusLineNC guifg
+ .tag_fg = .{ 0xd1, 0xe0, 0xda },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:193 StatusLine guibg #20303a, adjusted
+ .tag_active_bg = .{ 0x2e, 0x43, 0x50 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:193 StatusLine guifg
+ .tag_active_fg = .{ 0xc6, 0xd5, 0xcf },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/neobones_dark_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0x4b, 0x61, 0x6f },
+ // derived: the grip at rest, the accent #3b5362 faded toward the unfocused tag #18252d as far as 1.6:1 off it (0.74 of the way)
+ .box_dim = .{ 0x32, 0x47, 0x54 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/neobones.vim:41 Changed guifg
+ .box_dirty = .{ 0x81, 0x90, 0xd4 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/zenbones.nvim/colors/neobones.vim:205 Type guifg
+ .column_box = .{ 0x6e, 0x99, 0xb2 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:189 Statement guifg (no Keyword group defined; Vim default links Keyword to Statement)
+ .kw = .{ 0xc6, 0xd5, 0xcf },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:199 String guifg
+ .str = .{ 0x93, 0x9e, 0x99 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:163 Number guifg
+ .num = .{ 0x93, 0x9e, 0x99 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:47 Comment guifg
+ .comment = .{ 0x53, 0x69, 0x77 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:149 LineNr guifg
+ .lineno = .{ 0x46, 0x62, 0x73 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:61 CursorLineNr guifg
+ .lineno_active = .{ 0xc6, 0xd5, 0xcf },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:166 PmenuSbar guibg
+ .scroll_track = .{ 0x40, 0x5a, 0x6b },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:168 PmenuThumb guibg
+ .scroll_thumb = .{ 0x60, 0x86, 0x9d },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:216 WinSeparator guifg
+ .border = .{ 0x46, 0x62, 0x73 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:171 Search guibg
+ .search_bg = .{ 0x62, 0x41, 0x5b },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:171 Search guifg
+ .search_fg = .{ 0xc6, 0xd5, 0xcf },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:112 Error guifg via DiagnosticError->Error (link :113)
+ .diagnostic_error = .{ 0xde, 0x6e, 0x7c },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:210 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :211)
+ .diagnostic_warning = .{ 0xb7, 0x7e, 0x64 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:71 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x81, 0x90, 0xd4 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:66 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0xb2, 0x79, 0xa7 },
+ .palette = .{
+ .{ 0x0f, 0x19, 0x1f }, // 0: ~/05-genizah/zenbones.nvim/colors/neobones.vim:17 g:terminal_color_0
+ .{ 0xde, 0x6e, 0x7c }, // 1: ~/05-genizah/zenbones.nvim/colors/neobones.vim:18 g:terminal_color_1
+ .{ 0x90, 0xff, 0x6b }, // 2: ~/05-genizah/zenbones.nvim/colors/neobones.vim:19 g:terminal_color_2
+ .{ 0xb7, 0x7e, 0x64 }, // 3: ~/05-genizah/zenbones.nvim/colors/neobones.vim:20 g:terminal_color_3
+ .{ 0x81, 0x90, 0xd4 }, // 4: ~/05-genizah/zenbones.nvim/colors/neobones.vim:21 g:terminal_color_4
+ .{ 0xb2, 0x79, 0xa7 }, // 5: ~/05-genizah/zenbones.nvim/colors/neobones.vim:22 g:terminal_color_5
+ .{ 0x66, 0xa5, 0xad }, // 6: ~/05-genizah/zenbones.nvim/colors/neobones.vim:23 g:terminal_color_6
+ .{ 0xc6, 0xd5, 0xcf }, // 7: ~/05-genizah/zenbones.nvim/colors/neobones.vim:24 g:terminal_color_7
+ .{ 0x26, 0x39, 0x45 }, // 8: ~/05-genizah/zenbones.nvim/colors/neobones.vim:25 g:terminal_color_8
+ .{ 0xe8, 0x83, 0x8f }, // 9: ~/05-genizah/zenbones.nvim/colors/neobones.vim:26 g:terminal_color_9
+ .{ 0xa0, 0xff, 0x85 }, // 10: ~/05-genizah/zenbones.nvim/colors/neobones.vim:27 g:terminal_color_10
+ .{ 0xd6, 0x8c, 0x67 }, // 11: ~/05-genizah/zenbones.nvim/colors/neobones.vim:28 g:terminal_color_11
+ .{ 0x92, 0xa0, 0xe2 }, // 12: ~/05-genizah/zenbones.nvim/colors/neobones.vim:29 g:terminal_color_12
+ .{ 0xcf, 0x86, 0xc1 }, // 13: ~/05-genizah/zenbones.nvim/colors/neobones.vim:30 g:terminal_color_13
+ .{ 0x65, 0xb8, 0xc1 }, // 14: ~/05-genizah/zenbones.nvim/colors/neobones.vim:31 g:terminal_color_14
+ .{ 0x98, 0xa3, 0x9e }, // 15: ~/05-genizah/zenbones.nvim/colors/neobones.vim:32 g:terminal_color_15
+ },
+};
diff --git a/src/themes/neobones_light.zig b/src/themes/neobones_light.zig
new file mode 100644
index 00000000..b24fcef5
--- /dev/null
+++ b/src/themes/neobones_light.zig
@@ -0,0 +1,92 @@
+//! Neobones (light): Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #c2cfc4 -> #bac6bc: 1.16:1 off the unfocused #d0ded2, pardes needs 1.25:1 on a light page
+//! - focused grip #a3aea4 -> #96a097: 1.30:1 on the focused tag #bac6bc
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/neobones/palette.lua + lua/zenbones/specs/light.lua); values are the
+//! default 'ness' baked into colors/neobones.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours. TabLineSel
+//! is guifg/guibg=NONE (bold only), so it inherits Normal. TabLine links to
+//! StatusLine, TabLineFill to StatusLineNC. Accent is the lualine normal-mode a
+//! bg, a muted neutral (the theme is contrast-based, not hue-accented).
+//! Modified = Changed fg.
+pub const theme = .{
+ .name = "neobones_light",
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:266 Normal guibg
+ .bg = .{ 0xe5, 0xed, 0xe6 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:266 Normal guifg
+ .fg = .{ 0x20, 0x2e, 0x18 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:442 Visual guibg
+ .sel_bg = .{ 0xad, 0xe4, 0x8c },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x20, 0x2e, 0x18 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:429 StatusLineNC guibg
+ .tag_bg = .{ 0xd0, 0xde, 0xd2 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:429 StatusLineNC guifg
+ .tag_fg = .{ 0x4b, 0x66, 0x3c },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:426 StatusLine guibg #c2cfc4, adjusted
+ .tag_active_bg = .{ 0xba, 0xc6, 0xbc },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:426 StatusLine guifg
+ .tag_active_fg = .{ 0x20, 0x2e, 0x18 },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/neobones_light_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0x96, 0xa0, 0x97 },
+ // derived: the grip at rest, the accent #a3aea4 faded toward the unfocused tag #d0ded2 as far as 1.6:1 off it (0.98 of the way)
+ .box_dim = .{ 0xa4, 0xaf, 0xa5 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/neobones.vim:274 Changed guifg
+ .box_dirty = .{ 0x28, 0x64, 0x86 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/zenbones.nvim/colors/neobones.vim:438 Type guifg
+ .column_box = .{ 0x49, 0x5c, 0x4c },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:422 Statement guifg (no Keyword group defined; Vim default links Keyword to Statement)
+ .kw = .{ 0x20, 0x2e, 0x18 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:432 String guifg
+ .str = .{ 0x47, 0x60, 0x38 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:396 Number guifg
+ .num = .{ 0x47, 0x60, 0x38 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:280 Comment guifg
+ .comment = .{ 0x87, 0x8d, 0x88 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:382 LineNr guifg
+ .lineno = .{ 0x8f, 0x98, 0x90 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:294 CursorLineNr guifg
+ .lineno_active = .{ 0x20, 0x2e, 0x18 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:399 PmenuSbar guibg
+ .scroll_track = .{ 0x9b, 0xa6, 0x9d },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:401 PmenuThumb guibg
+ .scroll_thumb = .{ 0xf4, 0xf7, 0xf5 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:449 WinSeparator guifg
+ .border = .{ 0x8f, 0x98, 0x90 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:404 Search guibg
+ .search_bg = .{ 0xdc, 0xb5, 0xd4 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:404 Search guifg
+ .search_fg = .{ 0x20, 0x2e, 0x18 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:345 Error guifg via DiagnosticError->Error (link :346)
+ .diagnostic_error = .{ 0xa8, 0x33, 0x4c },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:443 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :444)
+ .diagnostic_warning = .{ 0x94, 0x49, 0x27 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:304 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x28, 0x64, 0x86 },
+ // ~/05-genizah/zenbones.nvim/colors/neobones.vim:299 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0x88, 0x50, 0x7d },
+ .palette = .{
+ .{ 0xe5, 0xed, 0xe6 }, // 0: ~/05-genizah/zenbones.nvim/colors/neobones.vim:250 g:terminal_color_0
+ .{ 0xa8, 0x33, 0x4c }, // 1: ~/05-genizah/zenbones.nvim/colors/neobones.vim:251 g:terminal_color_1
+ .{ 0x56, 0x7a, 0x30 }, // 2: ~/05-genizah/zenbones.nvim/colors/neobones.vim:252 g:terminal_color_2
+ .{ 0x94, 0x49, 0x27 }, // 3: ~/05-genizah/zenbones.nvim/colors/neobones.vim:253 g:terminal_color_3
+ .{ 0x28, 0x64, 0x86 }, // 4: ~/05-genizah/zenbones.nvim/colors/neobones.vim:254 g:terminal_color_4
+ .{ 0x88, 0x50, 0x7d }, // 5: ~/05-genizah/zenbones.nvim/colors/neobones.vim:255 g:terminal_color_5
+ .{ 0x3b, 0x89, 0x92 }, // 6: ~/05-genizah/zenbones.nvim/colors/neobones.vim:256 g:terminal_color_6
+ .{ 0x20, 0x2e, 0x18 }, // 7: ~/05-genizah/zenbones.nvim/colors/neobones.vim:257 g:terminal_color_7
+ .{ 0xb3, 0xc6, 0xb6 }, // 8: ~/05-genizah/zenbones.nvim/colors/neobones.vim:258 g:terminal_color_8
+ .{ 0x94, 0x25, 0x3e }, // 9: ~/05-genizah/zenbones.nvim/colors/neobones.vim:259 g:terminal_color_9
+ .{ 0x3f, 0x5a, 0x22 }, // 10: ~/05-genizah/zenbones.nvim/colors/neobones.vim:260 g:terminal_color_10
+ .{ 0x80, 0x3d, 0x1c }, // 11: ~/05-genizah/zenbones.nvim/colors/neobones.vim:261 g:terminal_color_11
+ .{ 0x1d, 0x55, 0x73 }, // 12: ~/05-genizah/zenbones.nvim/colors/neobones.vim:262 g:terminal_color_12
+ .{ 0x7b, 0x3b, 0x70 }, // 13: ~/05-genizah/zenbones.nvim/colors/neobones.vim:263 g:terminal_color_13
+ .{ 0x2b, 0x74, 0x7c }, // 14: ~/05-genizah/zenbones.nvim/colors/neobones.vim:264 g:terminal_color_14
+ .{ 0x41, 0x59, 0x34 }, // 15: ~/05-genizah/zenbones.nvim/colors/neobones.vim:265 g:terminal_color_15
+ },
+};
diff --git a/src/themes/nord.zig b/src/themes/nord.zig
new file mode 100644
index 00000000..f74785b3
--- /dev/null
+++ b/src/themes/nord.zig
@@ -0,0 +1,124 @@
+//! Nord: Nord, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! palette; url https://github.com/nordtheme/nord; genizah ~/05-genizah/nord; commit 1cef71605416a222e57225b544540ce0fcec18d4
+//! vim port (roles, terminal colours); url https://github.com/nordtheme/vim; genizah ~/05-genizah/nord-vim; commit f13f5dfbb784deddbc1d8195f34dfd9ec73e2295
+//! VS Code port (reference only, see notes); url https://github.com/nordtheme/visual-studio-code; genizah ~/05-genizah/nord-visual-studio-code; commit 8ead09822c02d0d49d0f764104505e5a34d3689f
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #4c566a -> #525d72: 1.36:1 off the unfocused #3b4252, pardes needs 1.5:1 on a dark page
+//! - focused tag text #88c0d0 -> #bcdce5: 3.31:1 on #525d72, tag text needs 4.5:1
+//! - rules between panes #434c5e -> #4c5566: 1.45:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! - scroll thumb #4c566a -> #60697c: 1.17:1 on its track #434c5e
+//! Upstream notes: All roles come from nord-vim with default options (none of
+//! g:nord_* set). vim has every schema role, so VS Code was not needed; the
+//! official VS Code port differs, for reference: tabline_bg #2e3440
+//! (~/05-genizah/nord-visual-studio-code/themes/nord-color-theme.json:266
+//! tab.inactiveBackground), tabline_fg #727884 = #d8dee966 over
+//! tab.inactiveBackground #2e3440
+//! (~/05-genizah/nord-visual-studio-code/themes/nord-color-theme.json:267
+//! tab.inactiveForeground), tabline_sel_bg #3b4252
+//! (~/05-genizah/nord-visual-studio-code/themes/nord-color-theme.json:261
+//! tab.activeBackground), tabline_sel_fg #d8dee9
+//! (~/05-genizah/nord-visual-studio-code/themes/nord-color-theme.json:262
+//! tab.activeForeground), tabline_fill #2e3440
+//! (~/05-genizah/nord-visual-studio-code/themes/nord-color-theme.json:95
+//! editorGroupHeader.tabsBackground); scrollbar thumb #3b4252 = #434c5e99 over
+//! editor.background #2e3440
+//! (~/05-genizah/nord-visual-studio-code/themes/nord-color-theme.json:237
+//! scrollbarSlider.background;
+//! ~/05-genizah/nord-visual-studio-code/themes/nord-color-theme.json:58
+//! editor.background), no track colour; focusBorder #3b4252
+//! (~/05-genizah/nord-visual-studio-code/themes/nord-color-theme.json:6
+//! focusBorder); selection #3f4758 = #434c5ecc over #2e3440
+//! (~/05-genizah/nord-visual-studio-code/themes/nord-color-theme.json:69
+//! editor.selectionBackground); find match #526c7a = #88c0d066 over #2e3440
+//! (~/05-genizah/nord-visual-studio-code/themes/nord-color-theme.json:61
+//! editor.findMatchBackground); statusBar bg #3b4252 fg #d8dee9
+//! (~/05-genizah/nord-visual-studio-code/themes/nord-color-theme.json:246
+//! statusBar.background;
+//! ~/05-genizah/nord-visual-studio-code/themes/nord-color-theme.json:251
+//! statusBar.foreground). Comment uses s:nord3_gui_bright #616E88
+//! (nord.vim:19), not a canonical nord colour: nord.vim brightened comments by
+//! 10% (see the deprecation message at :86-93); listed in palette as
+//! nord3_bright. ansi is g:terminal_color_0..15 (nvim); g:terminal_ansi_colors
+//! at :154 (vim 8) is the same list. selection_fg null: Visual sets no guifg.
+//! Constant fg is nord4 (plain text), so constant uses Boolean (nord9).
+//! scroll_track/thumb are the popup-menu scrollbar (PmenuSbar/PmenuThumb); vim
+//! has no window scrollbar.
+pub const theme = .{
+ .name = "nord",
+ // ~/05-genizah/nord-vim/colors/nord.vim:139 Normal guibg (s:nord0_gui = #2E3440 at :15)
+ .bg = .{ 0x2e, 0x34, 0x40 },
+ // ~/05-genizah/nord-vim/colors/nord.vim:139 Normal guifg (s:nord4_gui = #D8DEE9 at :20)
+ .fg = .{ 0xd8, 0xde, 0xe9 },
+ // ~/05-genizah/nord-vim/colors/nord.vim:149 Visual guibg (s:nord2_gui = #434C5E at :17)
+ .sel_bg = .{ 0x43, 0x4c, 0x5e },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xd8, 0xde, 0xe9 },
+ // ~/05-genizah/nord-vim/colors/nord.vim:223 StatusLineNC guibg (s:nord1_gui = #3B4252 at :16) [default g:nord_uniform_status_lines=0, :83]
+ .tag_bg = .{ 0x3b, 0x42, 0x52 },
+ // ~/05-genizah/nord-vim/colors/nord.vim:223 StatusLineNC guifg (s:nord4_gui = #D8DEE9 at :20) [default g:nord_uniform_status_lines=0, :83]
+ .tag_fg = .{ 0xd8, 0xde, 0xe9 },
+ // ~/05-genizah/nord-vim/colors/nord.vim:222 StatusLine guibg (s:nord3_gui = #4C566A at :18) [default g:nord_uniform_status_lines=0, :83] #4c566a, adjusted
+ .tag_active_bg = .{ 0x52, 0x5d, 0x72 },
+ // ~/05-genizah/nord-vim/colors/nord.vim:222 StatusLine guifg (s:nord8_gui = #88C0D0 at :24) [default g:nord_uniform_status_lines=0, :83] #88c0d0, adjusted
+ .tag_active_fg = .{ 0xbc, 0xdc, 0xe5 },
+ // the focused grip: ~/05-genizah/nord-vim/autoload/lightline/colorscheme/nord.vim:24 s:p.normal.left section a bg s:nord8 (#88C0D0 at :15); same in ~/05-genizah/nord-vim/autoload/airline/themes/nord.vim:41 s:NMain bg s:nord8_gui
+ .box = .{ 0x88, 0xc0, 0xd0 },
+ // derived: the grip at rest, the accent #88c0d0 faded toward the unfocused tag #3b4252 as far as 1.6:1 off it (0.26 of the way)
+ .box_dim = .{ 0x4f, 0x63, 0x73 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/nord-vim/colors/nord.vim:589 GitGutterChange guifg (s:nord13_gui = #EBCB8B at :29); also DiffChange fg (inverse) at :371, SignifySignChange at :596, VS Code editorGutter.modifiedBackground #ebcb8b at ~/05-genizah/nord-visual-studio-code/themes/nord-color-theme.json:98
+ .box_dirty = .{ 0xeb, 0xcb, 0x8b },
+ // derived: the column grip, the original's string colour, ~/05-genizah/nord-vim/colors/nord.vim:281 String guifg (s:nord14_gui = #A3BE8C at :30)
+ .column_box = .{ 0xa3, 0xbe, 0x8c },
+ // ~/05-genizah/nord-vim/colors/nord.vim:270 Keyword guifg (s:nord9_gui = #81A1C1 at :25)
+ .kw = .{ 0x81, 0xa1, 0xc1 },
+ // ~/05-genizah/nord-vim/colors/nord.vim:281 String guifg (s:nord14_gui = #A3BE8C at :30)
+ .str = .{ 0xa3, 0xbe, 0x8c },
+ // ~/05-genizah/nord-vim/colors/nord.vim:272 Number guifg (s:nord15_gui = #B48EAD at :31)
+ .num = .{ 0xb4, 0x8e, 0xad },
+ // ~/05-genizah/nord-vim/colors/nord.vim:258 Comment guifg (s:nord3_gui_bright = #616E88 at :19)
+ .comment = .{ 0x61, 0x6e, 0x88 },
+ // ~/05-genizah/nord-vim/colors/nord.vim:136 LineNr guifg (s:nord3_gui = #4C566A at :18)
+ .lineno = .{ 0x4c, 0x56, 0x6a },
+ // ~/05-genizah/nord-vim/colors/nord.vim:204 CursorLineNr guifg (s:nord4_gui = #D8DEE9 at :20) [default g:nord_cursor_line_number_background=0, :94]
+ .lineno_active = .{ 0xd8, 0xde, 0xe9 },
+ // ~/05-genizah/nord-vim/colors/nord.vim:141 PmenuSbar guibg (s:nord2_gui = #434C5E at :17)
+ .scroll_track = .{ 0x43, 0x4c, 0x5e },
+ // ~/05-genizah/nord-vim/colors/nord.vim:143 PmenuThumb guibg (s:nord3_gui = #4C566A at :18) #4c566a, adjusted
+ .scroll_thumb = .{ 0x60, 0x69, 0x7c },
+ // ~/05-genizah/nord-vim/colors/nord.vim:248 VertSplit guifg (s:nord2_gui = #434C5E at :17) [no WinSeparator group; default g:nord_bold_vertical_split_line=0, :85] #434c5e, adjusted
+ .border = .{ 0x4c, 0x55, 0x66 },
+ // ~/05-genizah/nord-vim/colors/nord.vim:237 Search guibg (s:nord8_gui = #88C0D0 at :24)
+ .search_bg = .{ 0x88, 0xc0, 0xd0 },
+ // ~/05-genizah/nord-vim/colors/nord.vim:237 Search guifg (s:nord1_gui = #3B4252 at :16)
+ .search_fg = .{ 0x3b, 0x42, 0x52 },
+ // ~/05-genizah/nord-vim/colors/nord.vim:184 DiagnosticError guifg (s:nord11_gui = #BF616A at :27)
+ .diagnostic_error = .{ 0xbf, 0x61, 0x6a },
+ // ~/05-genizah/nord-vim/colors/nord.vim:183 DiagnosticWarn guifg (s:nord13_gui = #EBCB8B at :29)
+ .diagnostic_warning = .{ 0xeb, 0xcb, 0x8b },
+ // ~/05-genizah/nord-vim/colors/nord.vim:185 DiagnosticInfo guifg (s:nord8_gui = #88C0D0 at :24)
+ .diagnostic_info = .{ 0x88, 0xc0, 0xd0 },
+ // ~/05-genizah/nord-vim/colors/nord.vim:186 DiagnosticHint guifg (s:nord10_gui = #5E81AC at :26)
+ .diagnostic_hint = .{ 0x5e, 0x81, 0xac },
+ .palette = .{
+ .{ 0x3b, 0x42, 0x52 }, // 0: ~/05-genizah/nord-vim/colors/nord.vim:159 g:terminal_color_0 (s:nord1_gui = #3B4252 at :16)
+ .{ 0xbf, 0x61, 0x6a }, // 1: ~/05-genizah/nord-vim/colors/nord.vim:160 g:terminal_color_1 (s:nord11_gui = #BF616A at :27)
+ .{ 0xa3, 0xbe, 0x8c }, // 2: ~/05-genizah/nord-vim/colors/nord.vim:161 g:terminal_color_2 (s:nord14_gui = #A3BE8C at :30)
+ .{ 0xeb, 0xcb, 0x8b }, // 3: ~/05-genizah/nord-vim/colors/nord.vim:162 g:terminal_color_3 (s:nord13_gui = #EBCB8B at :29)
+ .{ 0x81, 0xa1, 0xc1 }, // 4: ~/05-genizah/nord-vim/colors/nord.vim:163 g:terminal_color_4 (s:nord9_gui = #81A1C1 at :25)
+ .{ 0xb4, 0x8e, 0xad }, // 5: ~/05-genizah/nord-vim/colors/nord.vim:164 g:terminal_color_5 (s:nord15_gui = #B48EAD at :31)
+ .{ 0x88, 0xc0, 0xd0 }, // 6: ~/05-genizah/nord-vim/colors/nord.vim:165 g:terminal_color_6 (s:nord8_gui = #88C0D0 at :24)
+ .{ 0xe5, 0xe9, 0xf0 }, // 7: ~/05-genizah/nord-vim/colors/nord.vim:166 g:terminal_color_7 (s:nord5_gui = #E5E9F0 at :21)
+ .{ 0x4c, 0x56, 0x6a }, // 8: ~/05-genizah/nord-vim/colors/nord.vim:167 g:terminal_color_8 (s:nord3_gui = #4C566A at :18)
+ .{ 0xbf, 0x61, 0x6a }, // 9: ~/05-genizah/nord-vim/colors/nord.vim:168 g:terminal_color_9 (s:nord11_gui = #BF616A at :27)
+ .{ 0xa3, 0xbe, 0x8c }, // 10: ~/05-genizah/nord-vim/colors/nord.vim:169 g:terminal_color_10 (s:nord14_gui = #A3BE8C at :30)
+ .{ 0xeb, 0xcb, 0x8b }, // 11: ~/05-genizah/nord-vim/colors/nord.vim:170 g:terminal_color_11 (s:nord13_gui = #EBCB8B at :29)
+ .{ 0x81, 0xa1, 0xc1 }, // 12: ~/05-genizah/nord-vim/colors/nord.vim:171 g:terminal_color_12 (s:nord9_gui = #81A1C1 at :25)
+ .{ 0xb4, 0x8e, 0xad }, // 13: ~/05-genizah/nord-vim/colors/nord.vim:172 g:terminal_color_13 (s:nord15_gui = #B48EAD at :31)
+ .{ 0x8f, 0xbc, 0xbb }, // 14: ~/05-genizah/nord-vim/colors/nord.vim:173 g:terminal_color_14 (s:nord7_gui = #8FBCBB at :23)
+ .{ 0xec, 0xef, 0xf4 }, // 15: ~/05-genizah/nord-vim/colors/nord.vim:174 g:terminal_color_15 (s:nord6_gui = #ECEFF4 at :22)
+ },
+};
diff --git a/src/themes/nordbones.zig b/src/themes/nordbones.zig
new file mode 100644
index 00000000..e7ec64b3
--- /dev/null
+++ b/src/themes/nordbones.zig
@@ -0,0 +1,95 @@
+//! Nordbones: Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #414959 -> #515b6e: 1.15:1 off the unfocused #39404e, pardes needs 1.5:1 on a dark page
+//! - focused grip #5e687e -> #6e778b: 1.22:1 on the focused tag #515b6e
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/nordbones/palette.lua + lua/zenbones/specs/dark.lua); values are the
+//! default 'ness' baked into colors/nordbones.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours. Dark-only
+//! flavour (zenbones.json background='dark'). palette.lua also names nord0,
+//! nord1, nord10, nord11, nord12, nord13, nord14, nord15, nord2, nord3, nord4,
+//! nord5, nord6, nord7, nord8, nord9 (in one or both backgrounds) with no
+//! generated literal-hex output; omitted. TabLineSel is guifg/guibg=NONE (bold
+//! only), so it inherits Normal. TabLine links to StatusLine, TabLineFill to
+//! StatusLineNC. Accent is the lualine normal-mode a bg, a muted neutral (the
+//! theme is contrast-based, not hue-accented). Modified = Changed fg.
+pub const theme = .{
+ .name = "nordbones",
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:34 Normal guibg
+ .bg = .{ 0x2f, 0x35, 0x41 },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:34 Normal guifg
+ .fg = .{ 0xeb, 0xee, 0xf3 },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:212 Visual guibg
+ .sel_bg = .{ 0x54, 0x5f, 0x70 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xeb, 0xee, 0xf3 },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:199 StatusLineNC guibg
+ .tag_bg = .{ 0x39, 0x40, 0x4e },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:199 StatusLineNC guifg
+ .tag_fg = .{ 0xf2, 0xf4, 0xf7 },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:196 StatusLine guibg #414959, adjusted
+ .tag_active_bg = .{ 0x51, 0x5b, 0x6e },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:196 StatusLine guifg
+ .tag_active_fg = .{ 0xeb, 0xee, 0xf3 },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/nordbones_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0x6e, 0x77, 0x8b },
+ // derived: the grip at rest, the accent #5e687e faded toward the unfocused tag #39404e as far as 1.6:1 off it (0.78 of the way)
+ .box_dim = .{ 0x56, 0x5f, 0x73 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/nordbones.vim:42 Changed guifg
+ .box_dirty = .{ 0x8f, 0xbc, 0xba },
+ // derived: the column grip, the original's function colour, ~/05-genizah/zenbones.nvim/colors/nordbones.vim:123 Function guifg
+ .column_box = .{ 0x87, 0xbf, 0xce },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:192 Statement guifg (no Keyword group defined; Vim default links Keyword to Statement)
+ .kw = .{ 0x81, 0xa1, 0xc1 },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:202 String guifg
+ .str = .{ 0x9e, 0xaf, 0xc9 },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:165 Number guifg
+ .num = .{ 0x8f, 0xbc, 0xba },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:48 Comment guifg
+ .comment = .{ 0x73, 0x7c, 0x90 },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:151 LineNr guifg
+ .lineno = .{ 0x69, 0x75, 0x8c },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:63 CursorLineNr guifg
+ .lineno_active = .{ 0xeb, 0xee, 0xf3 },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:168 PmenuSbar guibg
+ .scroll_track = .{ 0x65, 0x70, 0x87 },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:170 PmenuThumb guibg
+ .scroll_thumb = .{ 0x86, 0x94, 0xb0 },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:219 WinSeparator guifg
+ .border = .{ 0x69, 0x75, 0x8c },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:173 Search guibg
+ .search_bg = .{ 0x84, 0x63, 0x7e },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:173 Search guifg
+ .search_fg = .{ 0xeb, 0xee, 0xf3 },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:114 Error guifg via DiagnosticError->Error (link :115)
+ .diagnostic_error = .{ 0xc1, 0x61, 0x6a },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:213 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :214)
+ .diagnostic_warning = .{ 0xcf, 0x86, 0x6f },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:73 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x8f, 0xbc, 0xba },
+ // ~/05-genizah/zenbones.nvim/colors/nordbones.vim:68 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0xb3, 0x8d, 0xac },
+ .palette = .{
+ .{ 0x2f, 0x35, 0x41 }, // 0: ~/05-genizah/zenbones.nvim/colors/nordbones.vim:18 g:terminal_color_0
+ .{ 0xc1, 0x61, 0x6a }, // 1: ~/05-genizah/zenbones.nvim/colors/nordbones.vim:19 g:terminal_color_1
+ .{ 0xa4, 0xbe, 0x8d }, // 2: ~/05-genizah/zenbones.nvim/colors/nordbones.vim:20 g:terminal_color_2
+ .{ 0xcf, 0x86, 0x6f }, // 3: ~/05-genizah/zenbones.nvim/colors/nordbones.vim:21 g:terminal_color_3
+ .{ 0x8f, 0xbc, 0xba }, // 4: ~/05-genizah/zenbones.nvim/colors/nordbones.vim:22 g:terminal_color_4
+ .{ 0xb3, 0x8d, 0xac }, // 5: ~/05-genizah/zenbones.nvim/colors/nordbones.vim:23 g:terminal_color_5
+ .{ 0x87, 0xbf, 0xce }, // 6: ~/05-genizah/zenbones.nvim/colors/nordbones.vim:24 g:terminal_color_6
+ .{ 0xeb, 0xee, 0xf3 }, // 7: ~/05-genizah/zenbones.nvim/colors/nordbones.vim:25 g:terminal_color_7
+ .{ 0x47, 0x50, 0x63 }, // 8: ~/05-genizah/zenbones.nvim/colors/nordbones.vim:26 g:terminal_color_8
+ .{ 0xd6, 0x78, 0x7f }, // 9: ~/05-genizah/zenbones.nvim/colors/nordbones.vim:27 g:terminal_color_9
+ .{ 0xa8, 0xcc, 0x86 }, // 10: ~/05-genizah/zenbones.nvim/colors/nordbones.vim:28 g:terminal_color_10
+ .{ 0xe0, 0x96, 0x80 }, // 11: ~/05-genizah/zenbones.nvim/colors/nordbones.vim:29 g:terminal_color_11
+ .{ 0x89, 0xca, 0xc8 }, // 12: ~/05-genizah/zenbones.nvim/colors/nordbones.vim:30 g:terminal_color_12
+ .{ 0xcf, 0x97, 0xc5 }, // 13: ~/05-genizah/zenbones.nvim/colors/nordbones.vim:31 g:terminal_color_13
+ .{ 0x82, 0xcc, 0xe0 }, // 14: ~/05-genizah/zenbones.nvim/colors/nordbones.vim:32 g:terminal_color_14
+ .{ 0xa5, 0xb4, 0xcd }, // 15: ~/05-genizah/zenbones.nvim/colors/nordbones.vim:33 g:terminal_color_15
+ },
+};
diff --git a/src/themes/rose_pine.zig b/src/themes/rose_pine.zig
new file mode 100644
index 00000000..8409af9b
--- /dev/null
+++ b/src/themes/rose_pine.zig
@@ -0,0 +1,104 @@
+//! Rosé Pine: Rosé Pine, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! palette; url https://github.com/rose-pine/palette; genizah ~/05-genizah/rose-pine-palette; commit 92af52b465ab6e47437aca223c9b8d3009a2023b
+//! neovim port (highlight mapping, terminal colours, lualine); url https://github.com/rose-pine/neovim; genizah ~/05-genizah/rose-pine-neovim; commit ff483051a47e27d84bdef47703538df1ed9f4a47
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #1f1d2e -> #3c3a4e: 1.02:1 off the unfocused #1d1b2a, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #6e6a86 -> #858299: 3.27:1 on #1d1b2a, tag text needs 4.5:1 to be read and run
+//! - focused tag text #908caa -> #a8a5bd: 3.42:1 on #3c3a4e, tag text needs 4.5:1
+//! Upstream notes: Highlight mapping: rose-pine/neovim with default options
+//! (variant auto, extend_background_behind_borders true, transparency false,
+//! dim_inactive_windows false, groups defaults from lua/rose-pine/config.lua).
+//! blend = N groups are composited at runtime by utilities.blend(bg,
+//! palette.base, N/100): each channel floor(clamp(a*fg+(1-a)*bg)+0.5); I
+//! computed selection_bg (Visual iris@15), search_bg (Search gold@20) and
+//! status_nc_bg (StatusLineNC surface@60 over base) that way; inputs in the
+//! cites. Palette: palette.json (the build input, build.js:3) has only the 12
+//! base roles. highlight_low/med/high come from the older source/index.ts
+//! (which agrees with the neovim port for these, but whose dawn text 575279 is
+//! stale vs palette.json and the port's 464261). leaf and _nc exist only in the
+//! neovim port. Terminal colours: the neovim port's own
+//! vim.g.terminal_color_0..15 (enable.terminal defaults true); bright = normal
+//! for 1-7, 0 = overlay, 8 = subtle, 7/15 = text. Note cyan is rose, not foam,
+//! in the port. modified = Changed fg (groups.git_change = rose), the port's
+//! git 'changed' colour (also GitSignsChange/SignChange). keyword: Keyword =
+//! pine exists directly (Statement is also pine). constant: Constant gold;
+//! Boolean differs (rose).
+pub const theme = .{
+ .name = "rose_pine",
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:111 Normal bg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:5 main.base
+ .bg = .{ 0x19, 0x17, 0x24 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:111 Normal fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:10 main.text
+ .fg = .{ 0xe0, 0xde, 0xf4 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:146 Visual bg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:16 main.iris, blend = 15 -> composited over palette.base #191724 at alpha 0.15 by utilities.blend (~/05-genizah/rose-pine-neovim/lua/rose-pine/utilities.lua:55, called at ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1176) = #332d41
+ .sel_bg = .{ 0x33, 0x2d, 0x41 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xe0, 0xde, 0xf4 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:137 StatusLineNC bg = groups.panel = "surface" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:45) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:6 main.surface, blend = 60 -> composited over palette.base #191724 at alpha 0.60 by utilities.blend (~/05-genizah/rose-pine-neovim/lua/rose-pine/utilities.lua:55, called at ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1176) = #1d1b2a
+ .tag_bg = .{ 0x1d, 0x1b, 0x2a },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:137 StatusLineNC fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:8 main.muted #6e6a86, adjusted
+ .tag_fg = .{ 0x85, 0x82, 0x99 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:136 StatusLine bg = groups.panel = "surface" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:45) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:6 main.surface #1f1d2e, adjusted
+ .tag_active_bg = .{ 0x3c, 0x3a, 0x4e },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:136 StatusLine fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:9 main.subtle #908caa, adjusted
+ .tag_active_fg = .{ 0xa8, 0xa5, 0xbd },
+ // the focused grip: ~/05-genizah/rose-pine-neovim/lua/lualine/themes/rose-pine.lua:11 lualine theme normal.a bg = p.rose -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:13 main.rose
+ .box = .{ 0xeb, 0xbc, 0xba },
+ // derived: the grip at rest, the accent #ebbcba faded toward the unfocused tag #1d1b2a as far as 1.6:1 off it (0.22 of the way)
+ .box_dim = .{ 0x4a, 0x3e, 0x4a },
+ // an unsaved grip: the original's warning colour, ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:159 DiagnosticWarn fg = groups.warn = "gold" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:51) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:12 main.gold
+ .box_dirty = .{ 0xf6, 0xc1, 0x77 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:223 Type fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:15 main.foam
+ .column_box = .{ 0x9c, 0xcf, 0xd8 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:200 Keyword fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:14 main.pine
+ .kw = .{ 0x31, 0x74, 0x8f },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:219 String fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:12 main.gold
+ .str = .{ 0xf6, 0xc1, 0x77 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:209 Number fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:12 main.gold
+ .num = .{ 0xf6, 0xc1, 0x77 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:188 Comment fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:9 main.subtle
+ .comment = .{ 0x90, 0x8c, 0xaa },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:106 LineNr fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:8 main.muted
+ .lineno = .{ 0x6e, 0x6a, 0x86 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:88 CursorLineNr fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:10 main.text
+ .lineno_active = .{ 0xe0, 0xde, 0xf4 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:120 PmenuSbar bg = groups.panel = "surface" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:45) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:6 main.surface
+ .scroll_track = .{ 0x1f, 0x1d, 0x2e },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:122 PmenuThumb bg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:8 main.muted
+ .scroll_thumb = .{ 0x6e, 0x6a, 0x86 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:153 WinSeparator fg = groups.border = "muted" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:43) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:8 main.muted
+ .border = .{ 0x6e, 0x6a, 0x86 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:129 Search bg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:12 main.gold, blend = 20 -> composited over palette.base #191724 at alpha 0.20 by utilities.blend (~/05-genizah/rose-pine-neovim/lua/rose-pine/utilities.lua:55, called at ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1176) = #453935
+ .search_bg = .{ 0x45, 0x39, 0x35 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:129 Search fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:10 main.text
+ .search_fg = .{ 0xe0, 0xde, 0xf4 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:155 DiagnosticError fg = groups.error = "love" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:47) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:11 main.love
+ .diagnostic_error = .{ 0xeb, 0x6f, 0x92 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:159 DiagnosticWarn fg = groups.warn = "gold" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:51) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:12 main.gold
+ .diagnostic_warning = .{ 0xf6, 0xc1, 0x77 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:157 DiagnosticInfo fg = groups.info = "foam" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:49) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:15 main.foam
+ .diagnostic_info = .{ 0x9c, 0xcf, 0xd8 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:156 DiagnosticHint fg = groups.hint = "iris" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:48) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:16 main.iris
+ .diagnostic_hint = .{ 0xc4, 0xa7, 0xe7 },
+ .palette = .{
+ .{ 0x26, 0x23, 0x3a }, // 0: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1191 vim.g.terminal_color_0 = palette.overlay -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:7 main.overlay
+ .{ 0xeb, 0x6f, 0x92 }, // 1: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1193 vim.g.terminal_color_1 = palette.love -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:11 main.love
+ .{ 0x31, 0x74, 0x8f }, // 2: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1195 vim.g.terminal_color_2 = palette.pine -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:14 main.pine
+ .{ 0xf6, 0xc1, 0x77 }, // 3: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1197 vim.g.terminal_color_3 = palette.gold -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:12 main.gold
+ .{ 0x9c, 0xcf, 0xd8 }, // 4: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1199 vim.g.terminal_color_4 = palette.foam -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:15 main.foam
+ .{ 0xc4, 0xa7, 0xe7 }, // 5: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1201 vim.g.terminal_color_5 = palette.iris -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:16 main.iris
+ .{ 0xeb, 0xbc, 0xba }, // 6: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1203 vim.g.terminal_color_6 = palette.rose -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:13 main.rose
+ .{ 0xe0, 0xde, 0xf4 }, // 7: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1205 vim.g.terminal_color_7 = palette.text -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:10 main.text
+ .{ 0x90, 0x8c, 0xaa }, // 8: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1192 vim.g.terminal_color_8 = palette.subtle -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:9 main.subtle
+ .{ 0xeb, 0x6f, 0x92 }, // 9: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1194 vim.g.terminal_color_9 = palette.love -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:11 main.love
+ .{ 0x31, 0x74, 0x8f }, // 10: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1196 vim.g.terminal_color_10 = palette.pine -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:14 main.pine
+ .{ 0xf6, 0xc1, 0x77 }, // 11: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1198 vim.g.terminal_color_11 = palette.gold -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:12 main.gold
+ .{ 0x9c, 0xcf, 0xd8 }, // 12: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1200 vim.g.terminal_color_12 = palette.foam -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:15 main.foam
+ .{ 0xc4, 0xa7, 0xe7 }, // 13: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1202 vim.g.terminal_color_13 = palette.iris -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:16 main.iris
+ .{ 0xeb, 0xbc, 0xba }, // 14: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1204 vim.g.terminal_color_14 = palette.rose -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:13 main.rose
+ .{ 0xe0, 0xde, 0xf4 }, // 15: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1206 vim.g.terminal_color_15 = palette.text -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:10 main.text
+ },
+};
diff --git a/src/themes/rose_pine_dawn.zig b/src/themes/rose_pine_dawn.zig
new file mode 100644
index 00000000..304df52b
--- /dev/null
+++ b/src/themes/rose_pine_dawn.zig
@@ -0,0 +1,104 @@
+//! Rosé Pine Dawn: Rosé Pine, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! palette; url https://github.com/rose-pine/palette; genizah ~/05-genizah/rose-pine-palette; commit 92af52b465ab6e47437aca223c9b8d3009a2023b
+//! neovim port (highlight mapping, terminal colours, lualine); url https://github.com/rose-pine/neovim; genizah ~/05-genizah/rose-pine-neovim; commit ff483051a47e27d84bdef47703538df1ed9f4a47
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #fffaf3 -> #e2ddde: 1.02:1 off the unfocused #fdf8f1, pardes needs 1.25:1 on a light page
+//! - unfocused tag text #9893a5 -> #74707d: 2.82:1 on #fdf8f1, tag text needs 4.5:1 to be read and run
+//! - focused tag text #797593 -> #615e76: 3.27:1 on #e2ddde, tag text needs 4.5:1
+//! Upstream notes: Highlight mapping: rose-pine/neovim with default options
+//! (variant auto, extend_background_behind_borders true, transparency false,
+//! dim_inactive_windows false, groups defaults from lua/rose-pine/config.lua).
+//! blend = N groups are composited at runtime by utilities.blend(bg,
+//! palette.base, N/100): each channel floor(clamp(a*fg+(1-a)*bg)+0.5); I
+//! computed selection_bg (Visual iris@15), search_bg (Search gold@20) and
+//! status_nc_bg (StatusLineNC surface@60 over base) that way; inputs in the
+//! cites. Palette: palette.json (the build input, build.js:3) has only the 12
+//! base roles. highlight_low/med/high come from the older source/index.ts
+//! (which agrees with the neovim port for these, but whose dawn text 575279 is
+//! stale vs palette.json and the port's 464261). leaf and _nc exist only in the
+//! neovim port. Terminal colours: the neovim port's own
+//! vim.g.terminal_color_0..15 (enable.terminal defaults true); bright = normal
+//! for 1-7, 0 = overlay, 8 = subtle, 7/15 = text. Note cyan is rose, not foam,
+//! in the port. modified = Changed fg (groups.git_change = rose), the port's
+//! git 'changed' colour (also GitSignsChange/SignChange). keyword: Keyword =
+//! pine exists directly (Statement is also pine). constant: Constant gold;
+//! Boolean differs (rose).
+pub const theme = .{
+ .name = "rose_pine_dawn",
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:111 Normal bg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:45 dawn.base
+ .bg = .{ 0xfa, 0xf4, 0xed },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:111 Normal fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:50 dawn.text
+ .fg = .{ 0x46, 0x42, 0x61 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:146 Visual bg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:56 dawn.iris, blend = 15 -> composited over palette.base #faf4ed at alpha 0.15 by utilities.blend (~/05-genizah/rose-pine-neovim/lua/rose-pine/utilities.lua:55, called at ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1176) = #eae2e3
+ .sel_bg = .{ 0xea, 0xe2, 0xe3 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x46, 0x42, 0x61 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:137 StatusLineNC bg = groups.panel = "surface" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:45) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:46 dawn.surface, blend = 60 -> composited over palette.base #faf4ed at alpha 0.60 by utilities.blend (~/05-genizah/rose-pine-neovim/lua/rose-pine/utilities.lua:55, called at ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1176) = #fdf8f1
+ .tag_bg = .{ 0xfd, 0xf8, 0xf1 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:137 StatusLineNC fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:48 dawn.muted #9893a5, adjusted
+ .tag_fg = .{ 0x74, 0x70, 0x7d },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:136 StatusLine bg = groups.panel = "surface" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:45) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:46 dawn.surface #fffaf3, adjusted
+ .tag_active_bg = .{ 0xe2, 0xdd, 0xde },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:136 StatusLine fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:49 dawn.subtle #797593, adjusted
+ .tag_active_fg = .{ 0x61, 0x5e, 0x76 },
+ // the focused grip: ~/05-genizah/rose-pine-neovim/lua/lualine/themes/rose-pine.lua:11 lualine theme normal.a bg = p.rose -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:53 dawn.rose
+ .box = .{ 0xd7, 0x82, 0x7e },
+ // derived: the grip at rest, the accent #d7827e faded toward the unfocused tag #fdf8f1 as far as 1.6:1 off it (0.52 of the way)
+ .box_dim = .{ 0xe9, 0xbb, 0xb5 },
+ // an unsaved grip: the original's warning colour, ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:159 DiagnosticWarn fg = groups.warn = "gold" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:51) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:52 dawn.gold
+ .box_dirty = .{ 0xea, 0x9d, 0x34 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:223 Type fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:55 dawn.foam
+ .column_box = .{ 0x56, 0x94, 0x9f },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:200 Keyword fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:54 dawn.pine
+ .kw = .{ 0x28, 0x69, 0x83 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:219 String fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:52 dawn.gold
+ .str = .{ 0xea, 0x9d, 0x34 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:209 Number fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:52 dawn.gold
+ .num = .{ 0xea, 0x9d, 0x34 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:188 Comment fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:49 dawn.subtle
+ .comment = .{ 0x79, 0x75, 0x93 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:106 LineNr fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:48 dawn.muted
+ .lineno = .{ 0x98, 0x93, 0xa5 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:88 CursorLineNr fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:50 dawn.text
+ .lineno_active = .{ 0x46, 0x42, 0x61 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:120 PmenuSbar bg = groups.panel = "surface" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:45) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:46 dawn.surface
+ .scroll_track = .{ 0xff, 0xfa, 0xf3 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:122 PmenuThumb bg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:48 dawn.muted
+ .scroll_thumb = .{ 0x98, 0x93, 0xa5 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:153 WinSeparator fg = groups.border = "muted" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:43) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:48 dawn.muted
+ .border = .{ 0x98, 0x93, 0xa5 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:129 Search bg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:52 dawn.gold, blend = 20 -> composited over palette.base #faf4ed at alpha 0.20 by utilities.blend (~/05-genizah/rose-pine-neovim/lua/rose-pine/utilities.lua:55, called at ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1176) = #f7e3c8
+ .search_bg = .{ 0xf7, 0xe3, 0xc8 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:129 Search fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:50 dawn.text
+ .search_fg = .{ 0x46, 0x42, 0x61 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:155 DiagnosticError fg = groups.error = "love" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:47) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:51 dawn.love
+ .diagnostic_error = .{ 0xb4, 0x63, 0x7a },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:159 DiagnosticWarn fg = groups.warn = "gold" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:51) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:52 dawn.gold
+ .diagnostic_warning = .{ 0xea, 0x9d, 0x34 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:157 DiagnosticInfo fg = groups.info = "foam" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:49) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:55 dawn.foam
+ .diagnostic_info = .{ 0x56, 0x94, 0x9f },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:156 DiagnosticHint fg = groups.hint = "iris" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:48) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:56 dawn.iris
+ .diagnostic_hint = .{ 0x90, 0x7a, 0xa9 },
+ .palette = .{
+ .{ 0xf2, 0xe9, 0xe1 }, // 0: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1191 vim.g.terminal_color_0 = palette.overlay -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:47 dawn.overlay
+ .{ 0xb4, 0x63, 0x7a }, // 1: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1193 vim.g.terminal_color_1 = palette.love -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:51 dawn.love
+ .{ 0x28, 0x69, 0x83 }, // 2: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1195 vim.g.terminal_color_2 = palette.pine -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:54 dawn.pine
+ .{ 0xea, 0x9d, 0x34 }, // 3: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1197 vim.g.terminal_color_3 = palette.gold -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:52 dawn.gold
+ .{ 0x56, 0x94, 0x9f }, // 4: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1199 vim.g.terminal_color_4 = palette.foam -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:55 dawn.foam
+ .{ 0x90, 0x7a, 0xa9 }, // 5: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1201 vim.g.terminal_color_5 = palette.iris -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:56 dawn.iris
+ .{ 0xd7, 0x82, 0x7e }, // 6: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1203 vim.g.terminal_color_6 = palette.rose -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:53 dawn.rose
+ .{ 0x46, 0x42, 0x61 }, // 7: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1205 vim.g.terminal_color_7 = palette.text -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:50 dawn.text
+ .{ 0x79, 0x75, 0x93 }, // 8: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1192 vim.g.terminal_color_8 = palette.subtle -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:49 dawn.subtle
+ .{ 0xb4, 0x63, 0x7a }, // 9: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1194 vim.g.terminal_color_9 = palette.love -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:51 dawn.love
+ .{ 0x28, 0x69, 0x83 }, // 10: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1196 vim.g.terminal_color_10 = palette.pine -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:54 dawn.pine
+ .{ 0xea, 0x9d, 0x34 }, // 11: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1198 vim.g.terminal_color_11 = palette.gold -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:52 dawn.gold
+ .{ 0x56, 0x94, 0x9f }, // 12: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1200 vim.g.terminal_color_12 = palette.foam -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:55 dawn.foam
+ .{ 0x90, 0x7a, 0xa9 }, // 13: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1202 vim.g.terminal_color_13 = palette.iris -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:56 dawn.iris
+ .{ 0xd7, 0x82, 0x7e }, // 14: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1204 vim.g.terminal_color_14 = palette.rose -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:53 dawn.rose
+ .{ 0x46, 0x42, 0x61 }, // 15: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1206 vim.g.terminal_color_15 = palette.text -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:50 dawn.text
+ },
+};
diff --git a/src/themes/rose_pine_moon.zig b/src/themes/rose_pine_moon.zig
new file mode 100644
index 00000000..f90975f1
--- /dev/null
+++ b/src/themes/rose_pine_moon.zig
@@ -0,0 +1,104 @@
+//! Rosé Pine Moon: Rosé Pine, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! palette; url https://github.com/rose-pine/palette; genizah ~/05-genizah/rose-pine-palette; commit 92af52b465ab6e47437aca223c9b8d3009a2023b
+//! neovim port (highlight mapping, terminal colours, lualine); url https://github.com/rose-pine/neovim; genizah ~/05-genizah/rose-pine-neovim; commit ff483051a47e27d84bdef47703538df1ed9f4a47
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #2a273f -> #45415b: 1.03:1 off the unfocused #27253b, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #6e6a86 -> #918ea3: 2.88:1 on #27253b, tag text needs 4.5:1 to be read and run
+//! - focused tag text #908caa -> #b1afc4: 3.02:1 on #45415b, tag text needs 4.5:1
+//! Upstream notes: Highlight mapping: rose-pine/neovim with default options
+//! (variant auto, extend_background_behind_borders true, transparency false,
+//! dim_inactive_windows false, groups defaults from lua/rose-pine/config.lua).
+//! blend = N groups are composited at runtime by utilities.blend(bg,
+//! palette.base, N/100): each channel floor(clamp(a*fg+(1-a)*bg)+0.5); I
+//! computed selection_bg (Visual iris@15), search_bg (Search gold@20) and
+//! status_nc_bg (StatusLineNC surface@60 over base) that way; inputs in the
+//! cites. Palette: palette.json (the build input, build.js:3) has only the 12
+//! base roles. highlight_low/med/high come from the older source/index.ts
+//! (which agrees with the neovim port for these, but whose dawn text 575279 is
+//! stale vs palette.json and the port's 464261). leaf and _nc exist only in the
+//! neovim port. Terminal colours: the neovim port's own
+//! vim.g.terminal_color_0..15 (enable.terminal defaults true); bright = normal
+//! for 1-7, 0 = overlay, 8 = subtle, 7/15 = text. Note cyan is rose, not foam,
+//! in the port. modified = Changed fg (groups.git_change = rose), the port's
+//! git 'changed' colour (also GitSignsChange/SignChange). keyword: Keyword =
+//! pine exists directly (Statement is also pine). constant: Constant gold;
+//! Boolean differs (rose).
+pub const theme = .{
+ .name = "rose_pine_moon",
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:111 Normal bg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:25 moon.base
+ .bg = .{ 0x23, 0x21, 0x36 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:111 Normal fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:30 moon.text
+ .fg = .{ 0xe0, 0xde, 0xf4 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:146 Visual bg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:36 moon.iris, blend = 15 -> composited over palette.base #232136 at alpha 0.15 by utilities.blend (~/05-genizah/rose-pine-neovim/lua/rose-pine/utilities.lua:55, called at ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1176) = #3b3551
+ .sel_bg = .{ 0x3b, 0x35, 0x51 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xe0, 0xde, 0xf4 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:137 StatusLineNC bg = groups.panel = "surface" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:45) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:26 moon.surface, blend = 60 -> composited over palette.base #232136 at alpha 0.60 by utilities.blend (~/05-genizah/rose-pine-neovim/lua/rose-pine/utilities.lua:55, called at ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1176) = #27253b
+ .tag_bg = .{ 0x27, 0x25, 0x3b },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:137 StatusLineNC fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:28 moon.muted #6e6a86, adjusted
+ .tag_fg = .{ 0x91, 0x8e, 0xa3 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:136 StatusLine bg = groups.panel = "surface" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:45) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:26 moon.surface #2a273f, adjusted
+ .tag_active_bg = .{ 0x45, 0x41, 0x5b },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:136 StatusLine fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:29 moon.subtle #908caa, adjusted
+ .tag_active_fg = .{ 0xb1, 0xaf, 0xc4 },
+ // the focused grip: ~/05-genizah/rose-pine-neovim/lua/lualine/themes/rose-pine.lua:11 lualine theme normal.a bg = p.rose -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:33 moon.rose
+ .box = .{ 0xea, 0x9a, 0x97 },
+ // derived: the grip at rest, the accent #ea9a97 faded toward the unfocused tag #27253b as far as 1.6:1 off it (0.26 of the way)
+ .box_dim = .{ 0x5a, 0x43, 0x53 },
+ // an unsaved grip: the original's warning colour, ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:159 DiagnosticWarn fg = groups.warn = "gold" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:51) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:32 moon.gold
+ .box_dirty = .{ 0xf6, 0xc1, 0x77 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:223 Type fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:35 moon.foam
+ .column_box = .{ 0x9c, 0xcf, 0xd8 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:200 Keyword fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:34 moon.pine
+ .kw = .{ 0x3e, 0x8f, 0xb0 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:219 String fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:32 moon.gold
+ .str = .{ 0xf6, 0xc1, 0x77 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:209 Number fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:32 moon.gold
+ .num = .{ 0xf6, 0xc1, 0x77 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:188 Comment fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:29 moon.subtle
+ .comment = .{ 0x90, 0x8c, 0xaa },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:106 LineNr fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:28 moon.muted
+ .lineno = .{ 0x6e, 0x6a, 0x86 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:88 CursorLineNr fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:30 moon.text
+ .lineno_active = .{ 0xe0, 0xde, 0xf4 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:120 PmenuSbar bg = groups.panel = "surface" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:45) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:26 moon.surface
+ .scroll_track = .{ 0x2a, 0x27, 0x3f },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:122 PmenuThumb bg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:28 moon.muted
+ .scroll_thumb = .{ 0x6e, 0x6a, 0x86 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:153 WinSeparator fg = groups.border = "muted" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:43) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:28 moon.muted
+ .border = .{ 0x6e, 0x6a, 0x86 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:129 Search bg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:32 moon.gold, blend = 20 -> composited over palette.base #232136 at alpha 0.20 by utilities.blend (~/05-genizah/rose-pine-neovim/lua/rose-pine/utilities.lua:55, called at ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1176) = #4d4143
+ .search_bg = .{ 0x4d, 0x41, 0x43 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:129 Search fg = ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:30 moon.text
+ .search_fg = .{ 0xe0, 0xde, 0xf4 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:155 DiagnosticError fg = groups.error = "love" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:47) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:31 moon.love
+ .diagnostic_error = .{ 0xeb, 0x6f, 0x92 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:159 DiagnosticWarn fg = groups.warn = "gold" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:51) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:32 moon.gold
+ .diagnostic_warning = .{ 0xf6, 0xc1, 0x77 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:157 DiagnosticInfo fg = groups.info = "foam" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:49) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:35 moon.foam
+ .diagnostic_info = .{ 0x9c, 0xcf, 0xd8 },
+ // ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:156 DiagnosticHint fg = groups.hint = "iris" (~/05-genizah/rose-pine-neovim/lua/rose-pine/config.lua:48) -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:36 moon.iris
+ .diagnostic_hint = .{ 0xc4, 0xa7, 0xe7 },
+ .palette = .{
+ .{ 0x39, 0x35, 0x52 }, // 0: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1191 vim.g.terminal_color_0 = palette.overlay -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:27 moon.overlay
+ .{ 0xeb, 0x6f, 0x92 }, // 1: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1193 vim.g.terminal_color_1 = palette.love -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:31 moon.love
+ .{ 0x3e, 0x8f, 0xb0 }, // 2: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1195 vim.g.terminal_color_2 = palette.pine -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:34 moon.pine
+ .{ 0xf6, 0xc1, 0x77 }, // 3: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1197 vim.g.terminal_color_3 = palette.gold -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:32 moon.gold
+ .{ 0x9c, 0xcf, 0xd8 }, // 4: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1199 vim.g.terminal_color_4 = palette.foam -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:35 moon.foam
+ .{ 0xc4, 0xa7, 0xe7 }, // 5: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1201 vim.g.terminal_color_5 = palette.iris -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:36 moon.iris
+ .{ 0xea, 0x9a, 0x97 }, // 6: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1203 vim.g.terminal_color_6 = palette.rose -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:33 moon.rose
+ .{ 0xe0, 0xde, 0xf4 }, // 7: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1205 vim.g.terminal_color_7 = palette.text -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:30 moon.text
+ .{ 0x90, 0x8c, 0xaa }, // 8: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1192 vim.g.terminal_color_8 = palette.subtle -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:29 moon.subtle
+ .{ 0xeb, 0x6f, 0x92 }, // 9: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1194 vim.g.terminal_color_9 = palette.love -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:31 moon.love
+ .{ 0x3e, 0x8f, 0xb0 }, // 10: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1196 vim.g.terminal_color_10 = palette.pine -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:34 moon.pine
+ .{ 0xf6, 0xc1, 0x77 }, // 11: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1198 vim.g.terminal_color_11 = palette.gold -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:32 moon.gold
+ .{ 0x9c, 0xcf, 0xd8 }, // 12: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1200 vim.g.terminal_color_12 = palette.foam -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:35 moon.foam
+ .{ 0xc4, 0xa7, 0xe7 }, // 13: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1202 vim.g.terminal_color_13 = palette.iris -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:36 moon.iris
+ .{ 0xea, 0x9a, 0x97 }, // 14: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1204 vim.g.terminal_color_14 = palette.rose -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:33 moon.rose
+ .{ 0xe0, 0xde, 0xf4 }, // 15: ~/05-genizah/rose-pine-neovim/lua/rose-pine.lua:1206 vim.g.terminal_color_15 = palette.text -> ~/05-genizah/rose-pine-neovim/lua/rose-pine/palette.lua:30 moon.text
+ },
+};
diff --git a/src/themes/rosebones_dark.zig b/src/themes/rosebones_dark.zig
new file mode 100644
index 00000000..bd3ae169
--- /dev/null
+++ b/src/themes/rosebones_dark.zig
@@ -0,0 +1,93 @@
+//! Rosebones (dark): Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #312e43 -> #423e59: 1.19:1 off the unfocused #242232, pardes needs 1.5:1 on a dark page
+//! - focused grip #534e6f -> #5d5978: 1.30:1 on the focused tag #423e59
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/rosebones/palette.lua + lua/zenbones/specs/dark.lua); values are the
+//! default 'ness' baked into colors/rosebones.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours.
+//! palette.lua also names bg_warm, text (in one or both backgrounds) with no
+//! generated literal-hex output; omitted. TabLineSel is guifg/guibg=NONE (bold
+//! only), so it inherits Normal. TabLine links to StatusLine, TabLineFill to
+//! StatusLineNC. Accent is the lualine normal-mode a bg, a muted neutral (the
+//! theme is contrast-based, not hue-accented). Modified = Changed fg.
+pub const theme = .{
+ .name = "rosebones_dark",
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:33 Normal guibg
+ .bg = .{ 0x1a, 0x18, 0x25 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:33 Normal guifg
+ .fg = .{ 0xe1, 0xd4, 0xd4 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:211 Visual guibg
+ .sel_bg = .{ 0x52, 0x3a, 0x39 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xe1, 0xd4, 0xd4 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:198 StatusLineNC guibg
+ .tag_bg = .{ 0x24, 0x22, 0x32 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:198 StatusLineNC guifg
+ .tag_fg = .{ 0xe9, 0xe0, 0xe0 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:195 StatusLine guibg #312e43, adjusted
+ .tag_active_bg = .{ 0x42, 0x3e, 0x59 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:195 StatusLine guifg
+ .tag_active_fg = .{ 0xe1, 0xd4, 0xd4 },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/rosebones_dark_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0x5d, 0x59, 0x78 },
+ // derived: the grip at rest, the accent #534e6f faded toward the unfocused tag #242232 as far as 1.6:1 off it (0.72 of the way)
+ .box_dim = .{ 0x46, 0x42, 0x5e },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/rosebones.vim:41 Changed guifg
+ .box_dirty = .{ 0x9c, 0xcf, 0xd8 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/zenbones.nvim/colors/rosebones.vim:207 Type guifg
+ .column_box = .{ 0xdf, 0xde, 0xf1 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:191 Statement guifg (no Keyword group defined; Vim default links Keyword to Statement)
+ .kw = .{ 0x31, 0x74, 0x90 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:201 String guifg
+ .str = .{ 0xbc, 0x94, 0x93 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:164 Number guifg
+ .num = .{ 0xbc, 0x94, 0x93 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:47 Comment guifg
+ .comment = .{ 0x69, 0x65, 0x7e },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:150 LineNr guifg
+ .lineno = .{ 0x62, 0x5d, 0x7f },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:62 CursorLineNr guifg
+ .lineno_active = .{ 0xe1, 0xd4, 0xd4 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:167 PmenuSbar guibg
+ .scroll_track = .{ 0x5a, 0x55, 0x78 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:169 PmenuThumb guibg
+ .scroll_thumb = .{ 0x84, 0x80, 0x9d },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:218 WinSeparator guifg
+ .border = .{ 0x62, 0x5d, 0x7f },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:172 Search guibg
+ .search_bg = .{ 0x67, 0x35, 0x92 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:172 Search guifg
+ .search_fg = .{ 0xe1, 0xd4, 0xd4 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:113 Error guifg via DiagnosticError->Error (link :114)
+ .diagnostic_error = .{ 0xeb, 0x71, 0x93 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:212 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :213)
+ .diagnostic_warning = .{ 0xf6, 0xc0, 0x74 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:72 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x9c, 0xcf, 0xd8 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:67 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0xc4, 0xa7, 0xe7 },
+ .palette = .{
+ .{ 0x1a, 0x18, 0x25 }, // 0: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:17 g:terminal_color_0
+ .{ 0xeb, 0x71, 0x93 }, // 1: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:18 g:terminal_color_1
+ .{ 0x31, 0x74, 0x90 }, // 2: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:19 g:terminal_color_2
+ .{ 0xf6, 0xc0, 0x74 }, // 3: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:20 g:terminal_color_3
+ .{ 0x9c, 0xcf, 0xd8 }, // 4: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:21 g:terminal_color_4
+ .{ 0xc4, 0xa7, 0xe7 }, // 5: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:22 g:terminal_color_5
+ .{ 0x9c, 0xcf, 0xd8 }, // 6: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:23 g:terminal_color_6
+ .{ 0xe1, 0xd4, 0xd4 }, // 7: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:24 g:terminal_color_7
+ .{ 0x3a, 0x36, 0x51 }, // 8: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:25 g:terminal_color_8
+ .{ 0xf2, 0x89, 0xa4 }, // 9: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:26 g:terminal_color_9
+ .{ 0x35, 0x8d, 0xaf }, // 10: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:27 g:terminal_color_10
+ .{ 0xf9, 0xca, 0x8e }, // 11: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:28 g:terminal_color_11
+ .{ 0x94, 0xda, 0xe6 }, // 12: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:29 g:terminal_color_12
+ .{ 0xce, 0xb3, 0xef }, // 13: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:30 g:terminal_color_13
+ .{ 0x94, 0xda, 0xe6 }, // 14: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:31 g:terminal_color_14
+ .{ 0xbf, 0x9b, 0x99 }, // 15: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:32 g:terminal_color_15
+ },
+};
diff --git a/src/themes/rosebones_light.zig b/src/themes/rosebones_light.zig
new file mode 100644
index 00000000..314424a6
--- /dev/null
+++ b/src/themes/rosebones_light.zig
@@ -0,0 +1,94 @@
+//! Rosebones (light): Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #ecd0a9 -> #e9c89a: 1.18:1 off the unfocused #f3e3cf, pardes needs 1.25:1 on a light page
+//! - unfocused tag text #ae6966 -> #8f5654: 3.33:1 on #f3e3cf, tag text needs 4.5:1 to be read and run
+//! - focused grip #ceaf7e -> #bea174: 1.31:1 on the focused tag #e9c89a
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/rosebones/palette.lua + lua/zenbones/specs/light.lua); values are the
+//! default 'ness' baked into colors/rosebones.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours.
+//! palette.lua also names bg_warm, text (in one or both backgrounds) with no
+//! generated literal-hex output; omitted. TabLineSel is guifg/guibg=NONE (bold
+//! only), so it inherits Normal. TabLine links to StatusLine, TabLineFill to
+//! StatusLineNC. Accent is the lualine normal-mode a bg, a muted neutral (the
+//! theme is contrast-based, not hue-accented). Modified = Changed fg.
+pub const theme = .{
+ .name = "rosebones_light",
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:267 Normal guibg
+ .bg = .{ 0xfb, 0xf6, 0xf0 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:267 Normal guifg
+ .fg = .{ 0x72, 0x43, 0x41 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:445 Visual guibg
+ .sel_bg = .{ 0xea, 0xdd, 0xdc },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x72, 0x43, 0x41 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:432 StatusLineNC guibg
+ .tag_bg = .{ 0xf3, 0xe3, 0xcf },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:432 StatusLineNC guifg #ae6966, adjusted
+ .tag_fg = .{ 0x8f, 0x56, 0x54 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:429 StatusLine guibg #ecd0a9, adjusted
+ .tag_active_bg = .{ 0xe9, 0xc8, 0x9a },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:429 StatusLine guifg
+ .tag_active_fg = .{ 0x72, 0x43, 0x41 },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/rosebones_light_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0xbe, 0xa1, 0x74 },
+ // derived: the grip at rest, the accent #ceaf7e faded toward the unfocused tag #f3e3cf as far as 1.6:1 off it (0.96 of the way)
+ .box_dim = .{ 0xcf, 0xb1, 0x81 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/rosebones.vim:275 Changed guifg
+ .box_dirty = .{ 0x57, 0x95, 0xa0 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/zenbones.nvim/colors/rosebones.vim:441 Type guifg
+ .column_box = .{ 0x57, 0x52, 0x7a },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:425 Statement guifg (no Keyword group defined; Vim default links Keyword to Statement)
+ .kw = .{ 0x28, 0x6a, 0x84 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:435 String guifg
+ .str = .{ 0xab, 0x67, 0x63 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:398 Number guifg
+ .num = .{ 0xab, 0x67, 0x63 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:281 Comment guifg
+ .comment = .{ 0xa1, 0x8e, 0x72 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:384 LineNr guifg
+ .lineno = .{ 0xb6, 0x9a, 0x6e },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:296 CursorLineNr guifg
+ .lineno_active = .{ 0x72, 0x43, 0x41 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:401 PmenuSbar guibg
+ .scroll_track = .{ 0xc5, 0xa7, 0x78 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:403 PmenuThumb guibg
+ .scroll_thumb = .{ 0xfe, 0xfc, 0xfa },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:452 WinSeparator guifg
+ .border = .{ 0xb6, 0x9a, 0x6e },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:406 Search guibg
+ .search_bg = .{ 0xd1, 0xc9, 0xdc },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:406 Search guifg
+ .search_fg = .{ 0x72, 0x43, 0x41 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:347 Error guifg via DiagnosticError->Error (link :348)
+ .diagnostic_error = .{ 0xb5, 0x63, 0x7a },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:446 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :447)
+ .diagnostic_warning = .{ 0xec, 0x9d, 0x33 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:306 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x57, 0x95, 0xa0 },
+ // ~/05-genizah/zenbones.nvim/colors/rosebones.vim:301 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0x91, 0x7b, 0xa9 },
+ .palette = .{
+ .{ 0xfb, 0xf6, 0xf0 }, // 0: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:251 g:terminal_color_0
+ .{ 0xb5, 0x63, 0x7a }, // 1: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:252 g:terminal_color_1
+ .{ 0x28, 0x6a, 0x84 }, // 2: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:253 g:terminal_color_2
+ .{ 0xec, 0x9d, 0x33 }, // 3: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:254 g:terminal_color_3
+ .{ 0x57, 0x95, 0xa0 }, // 4: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:255 g:terminal_color_4
+ .{ 0x91, 0x7b, 0xa9 }, // 5: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:256 g:terminal_color_5
+ .{ 0x57, 0x95, 0xa0 }, // 6: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:257 g:terminal_color_6
+ .{ 0x72, 0x43, 0x41 }, // 7: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:258 g:terminal_color_7
+ .{ 0xe8, 0xc4, 0x8b }, // 8: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:259 g:terminal_color_8
+ .{ 0xa5, 0x4a, 0x66 }, // 9: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:260 g:terminal_color_9
+ .{ 0x1c, 0x59, 0x70 }, // 10: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:261 g:terminal_color_10
+ .{ 0xc6, 0x82, 0x23 }, // 11: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:262 g:terminal_color_11
+ .{ 0x40, 0x7d, 0x88 }, // 12: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:263 g:terminal_color_12
+ .{ 0x85, 0x5a, 0xac }, // 13: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:264 g:terminal_color_13
+ .{ 0x40, 0x7d, 0x88 }, // 14: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:265 g:terminal_color_14
+ .{ 0xa4, 0x63, 0x5f }, // 15: ~/05-genizah/zenbones.nvim/colors/rosebones.vim:266 g:terminal_color_15
+ },
+};
diff --git a/src/themes/seoul256_dark_233.zig b/src/themes/seoul256_dark_233.zig
new file mode 100644
index 00000000..12e36a5e
--- /dev/null
+++ b/src/themes/seoul256_dark_233.zig
@@ -0,0 +1,101 @@
+//! Seoul256 (233): Seoul256, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/junegunn/seoul256.vim; genizah ~/05-genizah/seoul256.vim; commit 0357ff3e44faab66c98bc98dfc89c834e37012da
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag text #dfdebd -> #12120f: 3.02:1 on #9a7372, tag text needs 4.5:1
+//! - focused grip #007173 -> #006a6c: 1.40:1 on the focused tag #9a7372
+//! - rules between panes #000000 -> #3d3d3d: 1.17:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: g:seoul256_background = 233 (dark). Resolved exactly as
+//! ~/05-genizah/seoul256.vim/colors/seoul256.vim:145-177: dark_bg=233,
+//! light_bg=253, dark_bg_2=16 (line 175: dark_bg>233 ? dark_bg-2 : 16),
+//! light_bg_1=254, light_bg_2=255 (lines 176-177, min(..,256)); s:hi uses the
+//! first (dark) list element (s:style_idx, line 165) and maps any index >255 to
+//! 231 (lines 184-185). GUI hex from the file's default s:rgb_map
+//! (~/05-genizah/seoul256.vim/colors/seoul256.vim:120-135, iTerm2-measured
+//! values; NOT the xterm table, which is only used when g:seoul256_srgb=1,
+//! lines 37-118). The background knob is a fixed integer set: dark 233..239
+//! (default 237), light 252..256 (default 253) (lines 147-150, 169/173;
+//! README.md:52-60); any other value is discarded (line 152) and the style
+//! falls back to &background/colors_name; non-integers are not meaningful
+//! because the offsets index a Dict by integer. StatusLine/StatusLineNC are
+//! gui=bold,reverse so status roles are the rendered (swapped) colours. No
+//! Diagnostic* groups (error/warning from ALE signs, info/hint null). ansi: the
+//! colorscheme sets no g:terminal_ansi_colors/terminal_color_*; ansi is the
+//! repo's own urxvt port, which exists for one background only (237) and is
+//! reused unchanged for every level. Index gaps: for this level the roles
+//! tabline_fg land on an xterm index missing from the default s:rgb_map, so Vim
+//! emits gui*=NONE and they inherit Normal (given as such).
+pub const theme = .{
+ .name = "seoul256_dark_233",
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guibg = s:hi arg s:dark_bg = 233 -> s:rgb_map[233] ~/05-genizah/seoul256.vim/colors/seoul256.vim:132
+ .bg = .{ 0x17, 0x17, 0x17 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guifg = s:hi arg s:dark_fg = 252 -> s:rgb_map[252] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134
+ .fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:209 Visual guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .sel_bg = .{ 0x00, 0x71, 0x73 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guifg = s:hi arg s:dark_bg + 2 = 235 -> s:rgb_map[235] ~/05-genizah/seoul256.vim/colors/seoul256.vim:132; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_bg = .{ 0x33, 0x32, 0x33 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_fg = .{ 0xdf, 0xde, 0xbd },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guifg = s:hi arg 95 = 95 -> s:rgb_map[95] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse
+ .tag_active_bg = .{ 0x9a, 0x73, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse #dfdebd, adjusted
+ .tag_active_fg = .{ 0x12, 0x12, 0x0f },
+ // the focused grip: ~/05-genizah/seoul256.vim/colors/seoul256.vim:359 TabLineSel guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121 (accent: the selected-tab background, TabLineSel, the file's only coloured focus marker; the airline/lightline seoul256 themes live outside this repo)
+ .box = .{ 0x00, 0x6a, 0x6c },
+ // derived: the grip at rest, the accent #007173 faded toward the unfocused tag #333233 as far as 1.6:1 off it (0.66 of the way)
+ .box_dim = .{ 0x11, 0x5c, 0x5d },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:396 GitGutterChange guifg = s:hi arg 68 = 68 -> s:rgb_map[68] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123 (modified: vim-gitgutter Changed sign)
+ .box_dirty = .{ 0x71, 0x9c, 0xdf },
+ // derived: the column grip, the original's type colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:247 Type guifg = s:hi arg 179 = 179 -> s:rgb_map[179] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129
+ .column_box = .{ 0xdf, 0xbc, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:253 Keyword guifg = s:hi arg 168 = 168 -> s:rgb_map[168] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128
+ .kw = .{ 0xe1, 0x78, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:216 String guifg = s:hi arg 109 = 109 -> s:rgb_map[109] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .str = .{ 0x98, 0xbc, 0xbd },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:213 Number guifg = s:hi arg 222 = 222 -> s:rgb_map[222] ~/05-genizah/seoul256.vim/colors/seoul256.vim:131
+ .num = .{ 0xff, 0xde, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:212 Comment guifg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .comment = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:208 LineNr guifg = s:hi arg 101 = 101 -> s:rgb_map[101] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .lineno = .{ 0x99, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:276 CursorLineNr guifg = s:hi arg 131 = 131 -> s:rgb_map[131] ~/05-genizah/seoul256.vim/colors/seoul256.vim:126
+ .lineno_active = .{ 0xbe, 0x75, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:306 PmenuSbar guibg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .scroll_track = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:307 PmenuThumb guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .scroll_thumb = .{ 0x00, 0x71, 0x73 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:287 VertSplit guifg = s:hi arg s:dark_bg_2 = 16 -> s:rgb_map[16] ~/05-genizah/seoul256.vim/colors/seoul256.vim:120 #000000, adjusted
+ .border = .{ 0x3d, 0x3d, 0x3d },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guibg = s:hi arg 24 = 24 -> s:rgb_map[24] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .search_bg = .{ 0x00, 0x72, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guifg = s:hi arg s:dark_fg = 252 -> s:rgb_map[252] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134
+ .search_fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:402 ALEErrorSign guifg = s:hi arg 161 = 161 -> s:rgb_map[161] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE error sign used)
+ .diagnostic_error = .{ 0xe1, 0x26, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:403 ALEWarningSign guifg = s:hi arg 174 = 174 -> s:rgb_map[174] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE warning sign used)
+ .diagnostic_warning = .{ 0xe0, 0x9b, 0x99 },
+ .palette = .{
+ .{ 0x4e, 0x4e, 0x4e }, // 0: ~/05-genizah/seoul256.vim/urxvt/seoul256:5 URxvt*color0 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd6, 0x87, 0x87 }, // 1: ~/05-genizah/seoul256.vim/urxvt/seoul256:6 URxvt*color1 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x86, 0x5f }, // 2: ~/05-genizah/seoul256.vim/urxvt/seoul256:7 URxvt*color2 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd8, 0xaf, 0x5f }, // 3: ~/05-genizah/seoul256.vim/urxvt/seoul256:8 URxvt*color3 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x85, 0xad, 0xd4 }, // 4: ~/05-genizah/seoul256.vim/urxvt/seoul256:9 URxvt*color4 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd7, 0xaf, 0xaf }, // 5: ~/05-genizah/seoul256.vim/urxvt/seoul256:10 URxvt*color5 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xaf, 0xaf }, // 6: ~/05-genizah/seoul256.vim/urxvt/seoul256:11 URxvt*color6 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd0, 0xd0, 0xd0 }, // 7: ~/05-genizah/seoul256.vim/urxvt/seoul256:12 URxvt*color7 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x62, 0x62, 0x62 }, // 8: ~/05-genizah/seoul256.vim/urxvt/seoul256:13 URxvt*color8 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd7, 0x5f, 0x87 }, // 9: ~/05-genizah/seoul256.vim/urxvt/seoul256:14 URxvt*color9 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xaf, 0x87 }, // 10: ~/05-genizah/seoul256.vim/urxvt/seoul256:15 URxvt*color10 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xff, 0xd7, 0x87 }, // 11: ~/05-genizah/seoul256.vim/urxvt/seoul256:16 URxvt*color11 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xad, 0xd4, 0xfb }, // 12: ~/05-genizah/seoul256.vim/urxvt/seoul256:17 URxvt*color12 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xff, 0xaf, 0xaf }, // 13: ~/05-genizah/seoul256.vim/urxvt/seoul256:18 URxvt*color13 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xd7, 0xd7 }, // 14: ~/05-genizah/seoul256.vim/urxvt/seoul256:19 URxvt*color14 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xe4, 0xe4, 0xe4 }, // 15: ~/05-genizah/seoul256.vim/urxvt/seoul256:20 URxvt*color15 (official urxvt port; the vim colorscheme sets no terminal colours)
+ },
+};
diff --git a/src/themes/seoul256_dark_234.zig b/src/themes/seoul256_dark_234.zig
new file mode 100644
index 00000000..e017e985
--- /dev/null
+++ b/src/themes/seoul256_dark_234.zig
@@ -0,0 +1,101 @@
+//! Seoul256 (234): Seoul256, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/junegunn/seoul256.vim; genizah ~/05-genizah/seoul256.vim; commit 0357ff3e44faab66c98bc98dfc89c834e37012da
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag text #dfdebd -> #12120f: 3.02:1 on #9a7372, tag text needs 4.5:1
+//! - focused grip #007173 -> #006a6c: 1.40:1 on the focused tag #9a7372
+//! - rules between panes #060606 -> #4a4a4a: 1.32:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: g:seoul256_background = 234 (dark). Resolved exactly as
+//! ~/05-genizah/seoul256.vim/colors/seoul256.vim:145-177: dark_bg=234,
+//! light_bg=253, dark_bg_2=232 (line 175: dark_bg>233 ? dark_bg-2 : 16),
+//! light_bg_1=254, light_bg_2=255 (lines 176-177, min(..,256)); s:hi uses the
+//! first (dark) list element (s:style_idx, line 165) and maps any index >255 to
+//! 231 (lines 184-185). GUI hex from the file's default s:rgb_map
+//! (~/05-genizah/seoul256.vim/colors/seoul256.vim:120-135, iTerm2-measured
+//! values; NOT the xterm table, which is only used when g:seoul256_srgb=1,
+//! lines 37-118). The background knob is a fixed integer set: dark 233..239
+//! (default 237), light 252..256 (default 253) (lines 147-150, 169/173;
+//! README.md:52-60); any other value is discarded (line 152) and the style
+//! falls back to &background/colors_name; non-integers are not meaningful
+//! because the offsets index a Dict by integer. StatusLine/StatusLineNC are
+//! gui=bold,reverse so status roles are the rendered (swapped) colours. No
+//! Diagnostic* groups (error/warning from ALE signs, info/hint null). ansi: the
+//! colorscheme sets no g:terminal_ansi_colors/terminal_color_*; ansi is the
+//! repo's own urxvt port, which exists for one background only (237) and is
+//! reused unchanged for every level. Index gaps: for this level the roles
+//! tabline_fg land on an xterm index missing from the default s:rgb_map, so Vim
+//! emits gui*=NONE and they inherit Normal (given as such).
+pub const theme = .{
+ .name = "seoul256_dark_234",
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guibg = s:hi arg s:dark_bg = 234 -> s:rgb_map[234] ~/05-genizah/seoul256.vim/colors/seoul256.vim:132
+ .bg = .{ 0x25, 0x25, 0x25 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guifg = s:hi arg s:dark_fg = 252 -> s:rgb_map[252] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134
+ .fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:209 Visual guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .sel_bg = .{ 0x00, 0x71, 0x73 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guifg = s:hi arg s:dark_bg + 2 = 236 -> s:rgb_map[236] ~/05-genizah/seoul256.vim/colors/seoul256.vim:132; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_bg = .{ 0x3f, 0x3f, 0x3f },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_fg = .{ 0xdf, 0xde, 0xbd },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guifg = s:hi arg 95 = 95 -> s:rgb_map[95] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse
+ .tag_active_bg = .{ 0x9a, 0x73, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse #dfdebd, adjusted
+ .tag_active_fg = .{ 0x12, 0x12, 0x0f },
+ // the focused grip: ~/05-genizah/seoul256.vim/colors/seoul256.vim:359 TabLineSel guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121 (accent: the selected-tab background, TabLineSel, the file's only coloured focus marker; the airline/lightline seoul256 themes live outside this repo)
+ .box = .{ 0x00, 0x6a, 0x6c },
+ // derived: the grip at rest, the accent #007173 faded toward the unfocused tag #3f3f3f as far as 1.6:1 off it (0.84 of the way)
+ .box_dim = .{ 0x0a, 0x69, 0x6b },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:396 GitGutterChange guifg = s:hi arg 68 = 68 -> s:rgb_map[68] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123 (modified: vim-gitgutter Changed sign)
+ .box_dirty = .{ 0x71, 0x9c, 0xdf },
+ // derived: the column grip, the original's type colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:247 Type guifg = s:hi arg 179 = 179 -> s:rgb_map[179] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129
+ .column_box = .{ 0xdf, 0xbc, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:253 Keyword guifg = s:hi arg 168 = 168 -> s:rgb_map[168] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128
+ .kw = .{ 0xe1, 0x78, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:216 String guifg = s:hi arg 109 = 109 -> s:rgb_map[109] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .str = .{ 0x98, 0xbc, 0xbd },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:213 Number guifg = s:hi arg 222 = 222 -> s:rgb_map[222] ~/05-genizah/seoul256.vim/colors/seoul256.vim:131
+ .num = .{ 0xff, 0xde, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:212 Comment guifg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .comment = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:208 LineNr guifg = s:hi arg 101 = 101 -> s:rgb_map[101] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .lineno = .{ 0x99, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:276 CursorLineNr guifg = s:hi arg 131 = 131 -> s:rgb_map[131] ~/05-genizah/seoul256.vim/colors/seoul256.vim:126
+ .lineno_active = .{ 0xbe, 0x75, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:306 PmenuSbar guibg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .scroll_track = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:307 PmenuThumb guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .scroll_thumb = .{ 0x00, 0x71, 0x73 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:287 VertSplit guifg = s:hi arg s:dark_bg_2 = 232 -> s:rgb_map[232] ~/05-genizah/seoul256.vim/colors/seoul256.vim:131 #060606, adjusted
+ .border = .{ 0x4a, 0x4a, 0x4a },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guibg = s:hi arg 24 = 24 -> s:rgb_map[24] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .search_bg = .{ 0x00, 0x72, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guifg = s:hi arg s:dark_fg = 252 -> s:rgb_map[252] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134
+ .search_fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:402 ALEErrorSign guifg = s:hi arg 161 = 161 -> s:rgb_map[161] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE error sign used)
+ .diagnostic_error = .{ 0xe1, 0x26, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:403 ALEWarningSign guifg = s:hi arg 174 = 174 -> s:rgb_map[174] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE warning sign used)
+ .diagnostic_warning = .{ 0xe0, 0x9b, 0x99 },
+ .palette = .{
+ .{ 0x4e, 0x4e, 0x4e }, // 0: ~/05-genizah/seoul256.vim/urxvt/seoul256:5 URxvt*color0 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd6, 0x87, 0x87 }, // 1: ~/05-genizah/seoul256.vim/urxvt/seoul256:6 URxvt*color1 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x86, 0x5f }, // 2: ~/05-genizah/seoul256.vim/urxvt/seoul256:7 URxvt*color2 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd8, 0xaf, 0x5f }, // 3: ~/05-genizah/seoul256.vim/urxvt/seoul256:8 URxvt*color3 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x85, 0xad, 0xd4 }, // 4: ~/05-genizah/seoul256.vim/urxvt/seoul256:9 URxvt*color4 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd7, 0xaf, 0xaf }, // 5: ~/05-genizah/seoul256.vim/urxvt/seoul256:10 URxvt*color5 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xaf, 0xaf }, // 6: ~/05-genizah/seoul256.vim/urxvt/seoul256:11 URxvt*color6 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd0, 0xd0, 0xd0 }, // 7: ~/05-genizah/seoul256.vim/urxvt/seoul256:12 URxvt*color7 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x62, 0x62, 0x62 }, // 8: ~/05-genizah/seoul256.vim/urxvt/seoul256:13 URxvt*color8 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd7, 0x5f, 0x87 }, // 9: ~/05-genizah/seoul256.vim/urxvt/seoul256:14 URxvt*color9 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xaf, 0x87 }, // 10: ~/05-genizah/seoul256.vim/urxvt/seoul256:15 URxvt*color10 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xff, 0xd7, 0x87 }, // 11: ~/05-genizah/seoul256.vim/urxvt/seoul256:16 URxvt*color11 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xad, 0xd4, 0xfb }, // 12: ~/05-genizah/seoul256.vim/urxvt/seoul256:17 URxvt*color12 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xff, 0xaf, 0xaf }, // 13: ~/05-genizah/seoul256.vim/urxvt/seoul256:18 URxvt*color13 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xd7, 0xd7 }, // 14: ~/05-genizah/seoul256.vim/urxvt/seoul256:19 URxvt*color14 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xe4, 0xe4, 0xe4 }, // 15: ~/05-genizah/seoul256.vim/urxvt/seoul256:20 URxvt*color15 (official urxvt port; the vim colorscheme sets no terminal colours)
+ },
+};
diff --git a/src/themes/seoul256_dark_235.zig b/src/themes/seoul256_dark_235.zig
new file mode 100644
index 00000000..125d86df
--- /dev/null
+++ b/src/themes/seoul256_dark_235.zig
@@ -0,0 +1,101 @@
+//! Seoul256 (235): Seoul256, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/junegunn/seoul256.vim; genizah ~/05-genizah/seoul256.vim; commit 0357ff3e44faab66c98bc98dfc89c834e37012da
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag text #dfdebd -> #12120f: 3.02:1 on #9a7372, tag text needs 4.5:1
+//! - focused grip #007173 -> #006a6c: 1.40:1 on the focused tag #9a7372
+//! - rules between panes #171717 -> #515151: 1.40:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: g:seoul256_background = 235 (dark). Resolved exactly as
+//! ~/05-genizah/seoul256.vim/colors/seoul256.vim:145-177: dark_bg=235,
+//! light_bg=253, dark_bg_2=233 (line 175: dark_bg>233 ? dark_bg-2 : 16),
+//! light_bg_1=254, light_bg_2=255 (lines 176-177, min(..,256)); s:hi uses the
+//! first (dark) list element (s:style_idx, line 165) and maps any index >255 to
+//! 231 (lines 184-185). GUI hex from the file's default s:rgb_map
+//! (~/05-genizah/seoul256.vim/colors/seoul256.vim:120-135, iTerm2-measured
+//! values; NOT the xterm table, which is only used when g:seoul256_srgb=1,
+//! lines 37-118). The background knob is a fixed integer set: dark 233..239
+//! (default 237), light 252..256 (default 253) (lines 147-150, 169/173;
+//! README.md:52-60); any other value is discarded (line 152) and the style
+//! falls back to &background/colors_name; non-integers are not meaningful
+//! because the offsets index a Dict by integer. StatusLine/StatusLineNC are
+//! gui=bold,reverse so status roles are the rendered (swapped) colours. No
+//! Diagnostic* groups (error/warning from ALE signs, info/hint null). ansi: the
+//! colorscheme sets no g:terminal_ansi_colors/terminal_color_*; ansi is the
+//! repo's own urxvt port, which exists for one background only (237) and is
+//! reused unchanged for every level. Index gaps: for this level the roles
+//! tabline_fg land on an xterm index missing from the default s:rgb_map, so Vim
+//! emits gui*=NONE and they inherit Normal (given as such).
+pub const theme = .{
+ .name = "seoul256_dark_235",
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guibg = s:hi arg s:dark_bg = 235 -> s:rgb_map[235] ~/05-genizah/seoul256.vim/colors/seoul256.vim:132
+ .bg = .{ 0x33, 0x32, 0x33 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guifg = s:hi arg s:dark_fg = 252 -> s:rgb_map[252] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134
+ .fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:209 Visual guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .sel_bg = .{ 0x00, 0x71, 0x73 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guifg = s:hi arg s:dark_bg + 2 = 237 -> s:rgb_map[237] ~/05-genizah/seoul256.vim/colors/seoul256.vim:132; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_bg = .{ 0x4b, 0x4b, 0x4b },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_fg = .{ 0xdf, 0xde, 0xbd },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guifg = s:hi arg 95 = 95 -> s:rgb_map[95] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse
+ .tag_active_bg = .{ 0x9a, 0x73, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse #dfdebd, adjusted
+ .tag_active_fg = .{ 0x12, 0x12, 0x0f },
+ // the focused grip: ~/05-genizah/seoul256.vim/colors/seoul256.vim:359 TabLineSel guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121 (accent: the selected-tab background, TabLineSel, the file's only coloured focus marker; the airline/lightline seoul256 themes live outside this repo)
+ .box = .{ 0x00, 0x6a, 0x6c },
+ // derived: the grip at rest, the accent #007173 faded toward the unfocused tag #4b4b4b as far as 1.6:1 off it (1.00 of the way)
+ .box_dim = .{ 0x0a, 0x77, 0x79 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:396 GitGutterChange guifg = s:hi arg 68 = 68 -> s:rgb_map[68] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123 (modified: vim-gitgutter Changed sign)
+ .box_dirty = .{ 0x71, 0x9c, 0xdf },
+ // derived: the column grip, the original's type colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:247 Type guifg = s:hi arg 179 = 179 -> s:rgb_map[179] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129
+ .column_box = .{ 0xdf, 0xbc, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:253 Keyword guifg = s:hi arg 168 = 168 -> s:rgb_map[168] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128
+ .kw = .{ 0xe1, 0x78, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:216 String guifg = s:hi arg 109 = 109 -> s:rgb_map[109] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .str = .{ 0x98, 0xbc, 0xbd },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:213 Number guifg = s:hi arg 222 = 222 -> s:rgb_map[222] ~/05-genizah/seoul256.vim/colors/seoul256.vim:131
+ .num = .{ 0xff, 0xde, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:212 Comment guifg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .comment = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:208 LineNr guifg = s:hi arg 101 = 101 -> s:rgb_map[101] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .lineno = .{ 0x99, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:276 CursorLineNr guifg = s:hi arg 131 = 131 -> s:rgb_map[131] ~/05-genizah/seoul256.vim/colors/seoul256.vim:126
+ .lineno_active = .{ 0xbe, 0x75, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:306 PmenuSbar guibg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .scroll_track = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:307 PmenuThumb guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .scroll_thumb = .{ 0x00, 0x71, 0x73 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:287 VertSplit guifg = s:hi arg s:dark_bg_2 = 233 -> s:rgb_map[233] ~/05-genizah/seoul256.vim/colors/seoul256.vim:132 #171717, adjusted
+ .border = .{ 0x51, 0x51, 0x51 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guibg = s:hi arg 24 = 24 -> s:rgb_map[24] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .search_bg = .{ 0x00, 0x72, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guifg = s:hi arg s:dark_fg = 252 -> s:rgb_map[252] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134
+ .search_fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:402 ALEErrorSign guifg = s:hi arg 161 = 161 -> s:rgb_map[161] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE error sign used)
+ .diagnostic_error = .{ 0xe1, 0x26, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:403 ALEWarningSign guifg = s:hi arg 174 = 174 -> s:rgb_map[174] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE warning sign used)
+ .diagnostic_warning = .{ 0xe0, 0x9b, 0x99 },
+ .palette = .{
+ .{ 0x4e, 0x4e, 0x4e }, // 0: ~/05-genizah/seoul256.vim/urxvt/seoul256:5 URxvt*color0 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd6, 0x87, 0x87 }, // 1: ~/05-genizah/seoul256.vim/urxvt/seoul256:6 URxvt*color1 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x86, 0x5f }, // 2: ~/05-genizah/seoul256.vim/urxvt/seoul256:7 URxvt*color2 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd8, 0xaf, 0x5f }, // 3: ~/05-genizah/seoul256.vim/urxvt/seoul256:8 URxvt*color3 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x85, 0xad, 0xd4 }, // 4: ~/05-genizah/seoul256.vim/urxvt/seoul256:9 URxvt*color4 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd7, 0xaf, 0xaf }, // 5: ~/05-genizah/seoul256.vim/urxvt/seoul256:10 URxvt*color5 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xaf, 0xaf }, // 6: ~/05-genizah/seoul256.vim/urxvt/seoul256:11 URxvt*color6 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd0, 0xd0, 0xd0 }, // 7: ~/05-genizah/seoul256.vim/urxvt/seoul256:12 URxvt*color7 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x62, 0x62, 0x62 }, // 8: ~/05-genizah/seoul256.vim/urxvt/seoul256:13 URxvt*color8 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd7, 0x5f, 0x87 }, // 9: ~/05-genizah/seoul256.vim/urxvt/seoul256:14 URxvt*color9 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xaf, 0x87 }, // 10: ~/05-genizah/seoul256.vim/urxvt/seoul256:15 URxvt*color10 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xff, 0xd7, 0x87 }, // 11: ~/05-genizah/seoul256.vim/urxvt/seoul256:16 URxvt*color11 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xad, 0xd4, 0xfb }, // 12: ~/05-genizah/seoul256.vim/urxvt/seoul256:17 URxvt*color12 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xff, 0xaf, 0xaf }, // 13: ~/05-genizah/seoul256.vim/urxvt/seoul256:18 URxvt*color13 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xd7, 0xd7 }, // 14: ~/05-genizah/seoul256.vim/urxvt/seoul256:19 URxvt*color14 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xe4, 0xe4, 0xe4 }, // 15: ~/05-genizah/seoul256.vim/urxvt/seoul256:20 URxvt*color15 (official urxvt port; the vim colorscheme sets no terminal colours)
+ },
+};
diff --git a/src/themes/seoul256_dark_236.zig b/src/themes/seoul256_dark_236.zig
new file mode 100644
index 00000000..0cfec5ab
--- /dev/null
+++ b/src/themes/seoul256_dark_236.zig
@@ -0,0 +1,101 @@
+//! Seoul256 (236): Seoul256, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/junegunn/seoul256.vim; genizah ~/05-genizah/seoul256.vim; commit 0357ff3e44faab66c98bc98dfc89c834e37012da
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag text #dfdebd -> #12120f: 3.02:1 on #9a7372, tag text needs 4.5:1
+//! - focused grip #007173 -> #006a6c: 1.40:1 on the focused tag #9a7372
+//! - rules between panes #252525 -> #5f5f5f: 1.46:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: g:seoul256_background = 236 (dark). Resolved exactly as
+//! ~/05-genizah/seoul256.vim/colors/seoul256.vim:145-177: dark_bg=236,
+//! light_bg=253, dark_bg_2=234 (line 175: dark_bg>233 ? dark_bg-2 : 16),
+//! light_bg_1=254, light_bg_2=255 (lines 176-177, min(..,256)); s:hi uses the
+//! first (dark) list element (s:style_idx, line 165) and maps any index >255 to
+//! 231 (lines 184-185). GUI hex from the file's default s:rgb_map
+//! (~/05-genizah/seoul256.vim/colors/seoul256.vim:120-135, iTerm2-measured
+//! values; NOT the xterm table, which is only used when g:seoul256_srgb=1,
+//! lines 37-118). The background knob is a fixed integer set: dark 233..239
+//! (default 237), light 252..256 (default 253) (lines 147-150, 169/173;
+//! README.md:52-60); any other value is discarded (line 152) and the style
+//! falls back to &background/colors_name; non-integers are not meaningful
+//! because the offsets index a Dict by integer. StatusLine/StatusLineNC are
+//! gui=bold,reverse so status roles are the rendered (swapped) colours. No
+//! Diagnostic* groups (error/warning from ALE signs, info/hint null). ansi: the
+//! colorscheme sets no g:terminal_ansi_colors/terminal_color_*; ansi is the
+//! repo's own urxvt port, which exists for one background only (237) and is
+//! reused unchanged for every level. Index gaps: for this level the roles
+//! tabline_fg land on an xterm index missing from the default s:rgb_map, so Vim
+//! emits gui*=NONE and they inherit Normal (given as such).
+pub const theme = .{
+ .name = "seoul256_dark_236",
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guibg = s:hi arg s:dark_bg = 236 -> s:rgb_map[236] ~/05-genizah/seoul256.vim/colors/seoul256.vim:132
+ .bg = .{ 0x3f, 0x3f, 0x3f },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guifg = s:hi arg s:dark_fg = 252 -> s:rgb_map[252] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134
+ .fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:209 Visual guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .sel_bg = .{ 0x00, 0x71, 0x73 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guifg = s:hi arg s:dark_bg + 2 = 238 -> s:rgb_map[238] ~/05-genizah/seoul256.vim/colors/seoul256.vim:133; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_bg = .{ 0x56, 0x56, 0x56 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_fg = .{ 0xdf, 0xde, 0xbd },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guifg = s:hi arg 95 = 95 -> s:rgb_map[95] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse
+ .tag_active_bg = .{ 0x9a, 0x73, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse #dfdebd, adjusted
+ .tag_active_fg = .{ 0x12, 0x12, 0x0f },
+ // the focused grip: ~/05-genizah/seoul256.vim/colors/seoul256.vim:359 TabLineSel guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121 (accent: the selected-tab background, TabLineSel, the file's only coloured focus marker; the airline/lightline seoul256 themes live outside this repo)
+ .box = .{ 0x00, 0x6a, 0x6c },
+ // derived: the grip at rest, the accent #007173 faded toward the unfocused tag #565656 as far as 1.6:1 off it (1.00 of the way)
+ .box_dim = .{ 0x24, 0x85, 0x87 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:396 GitGutterChange guifg = s:hi arg 68 = 68 -> s:rgb_map[68] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123 (modified: vim-gitgutter Changed sign)
+ .box_dirty = .{ 0x71, 0x9c, 0xdf },
+ // derived: the column grip, the original's type colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:247 Type guifg = s:hi arg 179 = 179 -> s:rgb_map[179] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129
+ .column_box = .{ 0xdf, 0xbc, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:253 Keyword guifg = s:hi arg 168 = 168 -> s:rgb_map[168] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128
+ .kw = .{ 0xe1, 0x78, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:216 String guifg = s:hi arg 109 = 109 -> s:rgb_map[109] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .str = .{ 0x98, 0xbc, 0xbd },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:213 Number guifg = s:hi arg 222 = 222 -> s:rgb_map[222] ~/05-genizah/seoul256.vim/colors/seoul256.vim:131
+ .num = .{ 0xff, 0xde, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:212 Comment guifg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .comment = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:208 LineNr guifg = s:hi arg 101 = 101 -> s:rgb_map[101] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .lineno = .{ 0x99, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:276 CursorLineNr guifg = s:hi arg 131 = 131 -> s:rgb_map[131] ~/05-genizah/seoul256.vim/colors/seoul256.vim:126
+ .lineno_active = .{ 0xbe, 0x75, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:306 PmenuSbar guibg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .scroll_track = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:307 PmenuThumb guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .scroll_thumb = .{ 0x00, 0x71, 0x73 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:287 VertSplit guifg = s:hi arg s:dark_bg_2 = 234 -> s:rgb_map[234] ~/05-genizah/seoul256.vim/colors/seoul256.vim:132 #252525, adjusted
+ .border = .{ 0x5f, 0x5f, 0x5f },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guibg = s:hi arg 24 = 24 -> s:rgb_map[24] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .search_bg = .{ 0x00, 0x72, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guifg = s:hi arg s:dark_fg = 252 -> s:rgb_map[252] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134
+ .search_fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:402 ALEErrorSign guifg = s:hi arg 161 = 161 -> s:rgb_map[161] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE error sign used)
+ .diagnostic_error = .{ 0xe1, 0x26, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:403 ALEWarningSign guifg = s:hi arg 174 = 174 -> s:rgb_map[174] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE warning sign used)
+ .diagnostic_warning = .{ 0xe0, 0x9b, 0x99 },
+ .palette = .{
+ .{ 0x4e, 0x4e, 0x4e }, // 0: ~/05-genizah/seoul256.vim/urxvt/seoul256:5 URxvt*color0 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd6, 0x87, 0x87 }, // 1: ~/05-genizah/seoul256.vim/urxvt/seoul256:6 URxvt*color1 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x86, 0x5f }, // 2: ~/05-genizah/seoul256.vim/urxvt/seoul256:7 URxvt*color2 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd8, 0xaf, 0x5f }, // 3: ~/05-genizah/seoul256.vim/urxvt/seoul256:8 URxvt*color3 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x85, 0xad, 0xd4 }, // 4: ~/05-genizah/seoul256.vim/urxvt/seoul256:9 URxvt*color4 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd7, 0xaf, 0xaf }, // 5: ~/05-genizah/seoul256.vim/urxvt/seoul256:10 URxvt*color5 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xaf, 0xaf }, // 6: ~/05-genizah/seoul256.vim/urxvt/seoul256:11 URxvt*color6 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd0, 0xd0, 0xd0 }, // 7: ~/05-genizah/seoul256.vim/urxvt/seoul256:12 URxvt*color7 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x62, 0x62, 0x62 }, // 8: ~/05-genizah/seoul256.vim/urxvt/seoul256:13 URxvt*color8 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd7, 0x5f, 0x87 }, // 9: ~/05-genizah/seoul256.vim/urxvt/seoul256:14 URxvt*color9 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xaf, 0x87 }, // 10: ~/05-genizah/seoul256.vim/urxvt/seoul256:15 URxvt*color10 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xff, 0xd7, 0x87 }, // 11: ~/05-genizah/seoul256.vim/urxvt/seoul256:16 URxvt*color11 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xad, 0xd4, 0xfb }, // 12: ~/05-genizah/seoul256.vim/urxvt/seoul256:17 URxvt*color12 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xff, 0xaf, 0xaf }, // 13: ~/05-genizah/seoul256.vim/urxvt/seoul256:18 URxvt*color13 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xd7, 0xd7 }, // 14: ~/05-genizah/seoul256.vim/urxvt/seoul256:19 URxvt*color14 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xe4, 0xe4, 0xe4 }, // 15: ~/05-genizah/seoul256.vim/urxvt/seoul256:20 URxvt*color15 (official urxvt port; the vim colorscheme sets no terminal colours)
+ },
+};
diff --git a/src/themes/seoul256_dark_237.zig b/src/themes/seoul256_dark_237.zig
new file mode 100644
index 00000000..8319dec4
--- /dev/null
+++ b/src/themes/seoul256_dark_237.zig
@@ -0,0 +1,100 @@
+//! Seoul256 (237): Seoul256, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/junegunn/seoul256.vim; genizah ~/05-genizah/seoul256.vim; commit 0357ff3e44faab66c98bc98dfc89c834e37012da
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #9a7372 -> #9d7473: 1.50:1 off the unfocused #616161, pardes needs 1.5:1 on a dark page
+//! - focused tag text #dfdebd -> #12120f: 2.96:1 on #9d7473, tag text needs 4.5:1
+//! - focused grip #007173 -> #006c6e: 1.43:1 on the focused tag #9d7473
+//! - rules between panes #333233 -> #6b6b6b: 1.46:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: g:seoul256_background = 237 (dark). Resolved exactly as
+//! ~/05-genizah/seoul256.vim/colors/seoul256.vim:145-177: dark_bg=237,
+//! light_bg=253, dark_bg_2=235 (line 175: dark_bg>233 ? dark_bg-2 : 16),
+//! light_bg_1=254, light_bg_2=255 (lines 176-177, min(..,256)); s:hi uses the
+//! first (dark) list element (s:style_idx, line 165) and maps any index >255 to
+//! 231 (lines 184-185). GUI hex from the file's default s:rgb_map
+//! (~/05-genizah/seoul256.vim/colors/seoul256.vim:120-135, iTerm2-measured
+//! values; NOT the xterm table, which is only used when g:seoul256_srgb=1,
+//! lines 37-118). The background knob is a fixed integer set: dark 233..239
+//! (default 237), light 252..256 (default 253) (lines 147-150, 169/173;
+//! README.md:52-60); any other value is discarded (line 152) and the style
+//! falls back to &background/colors_name; non-integers are not meaningful
+//! because the offsets index a Dict by integer. StatusLine/StatusLineNC are
+//! gui=bold,reverse so status roles are the rendered (swapped) colours. No
+//! Diagnostic* groups (error/warning from ALE signs, info/hint null). ansi: the
+//! colorscheme sets no g:terminal_ansi_colors/terminal_color_*; ansi is the
+//! repo's own urxvt port, which exists for one background only (237) and is
+//! reused unchanged for every level.
+pub const theme = .{
+ .name = "seoul256_dark_237",
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guibg = s:hi arg s:dark_bg = 237 -> s:rgb_map[237] ~/05-genizah/seoul256.vim/colors/seoul256.vim:132
+ .bg = .{ 0x4b, 0x4b, 0x4b },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guifg = s:hi arg s:dark_fg = 252 -> s:rgb_map[252] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134
+ .fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:209 Visual guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .sel_bg = .{ 0x00, 0x71, 0x73 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guifg = s:hi arg s:dark_bg + 2 = 239 -> s:rgb_map[239] ~/05-genizah/seoul256.vim/colors/seoul256.vim:133; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_bg = .{ 0x61, 0x61, 0x61 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_fg = .{ 0xdf, 0xde, 0xbd },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guifg = s:hi arg 95 = 95 -> s:rgb_map[95] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse #9a7372, adjusted
+ .tag_active_bg = .{ 0x9d, 0x74, 0x73 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse #dfdebd, adjusted
+ .tag_active_fg = .{ 0x12, 0x12, 0x0f },
+ // the focused grip: ~/05-genizah/seoul256.vim/colors/seoul256.vim:359 TabLineSel guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121 (accent: the selected-tab background, TabLineSel, the file's only coloured focus marker; the airline/lightline seoul256 themes live outside this repo)
+ .box = .{ 0x00, 0x6c, 0x6e },
+ // derived: the grip at rest, the accent #007173 faded toward the unfocused tag #616161 as far as 1.6:1 off it (1.00 of the way)
+ .box_dim = .{ 0x38, 0x90, 0x92 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:396 GitGutterChange guifg = s:hi arg 68 = 68 -> s:rgb_map[68] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123 (modified: vim-gitgutter Changed sign)
+ .box_dirty = .{ 0x71, 0x9c, 0xdf },
+ // derived: the column grip, the original's type colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:247 Type guifg = s:hi arg 179 = 179 -> s:rgb_map[179] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129
+ .column_box = .{ 0xdf, 0xbc, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:253 Keyword guifg = s:hi arg 168 = 168 -> s:rgb_map[168] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128
+ .kw = .{ 0xe1, 0x78, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:216 String guifg = s:hi arg 109 = 109 -> s:rgb_map[109] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .str = .{ 0x98, 0xbc, 0xbd },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:213 Number guifg = s:hi arg 222 = 222 -> s:rgb_map[222] ~/05-genizah/seoul256.vim/colors/seoul256.vim:131
+ .num = .{ 0xff, 0xde, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:212 Comment guifg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .comment = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:208 LineNr guifg = s:hi arg 101 = 101 -> s:rgb_map[101] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .lineno = .{ 0x99, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:276 CursorLineNr guifg = s:hi arg 131 = 131 -> s:rgb_map[131] ~/05-genizah/seoul256.vim/colors/seoul256.vim:126
+ .lineno_active = .{ 0xbe, 0x75, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:306 PmenuSbar guibg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .scroll_track = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:307 PmenuThumb guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .scroll_thumb = .{ 0x00, 0x71, 0x73 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:287 VertSplit guifg = s:hi arg s:dark_bg_2 = 235 -> s:rgb_map[235] ~/05-genizah/seoul256.vim/colors/seoul256.vim:132 #333233, adjusted
+ .border = .{ 0x6b, 0x6b, 0x6b },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guibg = s:hi arg 24 = 24 -> s:rgb_map[24] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .search_bg = .{ 0x00, 0x72, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guifg = s:hi arg s:dark_fg = 252 -> s:rgb_map[252] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134
+ .search_fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:402 ALEErrorSign guifg = s:hi arg 161 = 161 -> s:rgb_map[161] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE error sign used)
+ .diagnostic_error = .{ 0xe1, 0x26, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:403 ALEWarningSign guifg = s:hi arg 174 = 174 -> s:rgb_map[174] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE warning sign used)
+ .diagnostic_warning = .{ 0xe0, 0x9b, 0x99 },
+ .palette = .{
+ .{ 0x4e, 0x4e, 0x4e }, // 0: ~/05-genizah/seoul256.vim/urxvt/seoul256:5 URxvt*color0 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd6, 0x87, 0x87 }, // 1: ~/05-genizah/seoul256.vim/urxvt/seoul256:6 URxvt*color1 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x86, 0x5f }, // 2: ~/05-genizah/seoul256.vim/urxvt/seoul256:7 URxvt*color2 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd8, 0xaf, 0x5f }, // 3: ~/05-genizah/seoul256.vim/urxvt/seoul256:8 URxvt*color3 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x85, 0xad, 0xd4 }, // 4: ~/05-genizah/seoul256.vim/urxvt/seoul256:9 URxvt*color4 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd7, 0xaf, 0xaf }, // 5: ~/05-genizah/seoul256.vim/urxvt/seoul256:10 URxvt*color5 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xaf, 0xaf }, // 6: ~/05-genizah/seoul256.vim/urxvt/seoul256:11 URxvt*color6 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd0, 0xd0, 0xd0 }, // 7: ~/05-genizah/seoul256.vim/urxvt/seoul256:12 URxvt*color7 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x62, 0x62, 0x62 }, // 8: ~/05-genizah/seoul256.vim/urxvt/seoul256:13 URxvt*color8 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd7, 0x5f, 0x87 }, // 9: ~/05-genizah/seoul256.vim/urxvt/seoul256:14 URxvt*color9 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xaf, 0x87 }, // 10: ~/05-genizah/seoul256.vim/urxvt/seoul256:15 URxvt*color10 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xff, 0xd7, 0x87 }, // 11: ~/05-genizah/seoul256.vim/urxvt/seoul256:16 URxvt*color11 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xad, 0xd4, 0xfb }, // 12: ~/05-genizah/seoul256.vim/urxvt/seoul256:17 URxvt*color12 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xff, 0xaf, 0xaf }, // 13: ~/05-genizah/seoul256.vim/urxvt/seoul256:18 URxvt*color13 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xd7, 0xd7 }, // 14: ~/05-genizah/seoul256.vim/urxvt/seoul256:19 URxvt*color14 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xe4, 0xe4, 0xe4 }, // 15: ~/05-genizah/seoul256.vim/urxvt/seoul256:20 URxvt*color15 (official urxvt port; the vim colorscheme sets no terminal colours)
+ },
+};
diff --git a/src/themes/seoul256_dark_238.zig b/src/themes/seoul256_dark_238.zig
new file mode 100644
index 00000000..2a821144
--- /dev/null
+++ b/src/themes/seoul256_dark_238.zig
@@ -0,0 +1,102 @@
+//! Seoul256 (238): Seoul256, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/junegunn/seoul256.vim; genizah ~/05-genizah/seoul256.vim; commit 0357ff3e44faab66c98bc98dfc89c834e37012da
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #9a7372 -> #b97877: 1.29:1 off the unfocused #6b6b6b, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #dfdebd -> #eeeddb: 3.89:1 on #6b6b6b, tag text needs 4.5:1 to be read and run
+//! - focused tag text #dfdebd -> #1f1f1a: 2.55:1 on #b97877, tag text needs 4.5:1
+//! - rules between panes #3f3f3f -> #767676: 1.43:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: g:seoul256_background = 238 (dark). Resolved exactly as
+//! ~/05-genizah/seoul256.vim/colors/seoul256.vim:145-177: dark_bg=238,
+//! light_bg=253, dark_bg_2=236 (line 175: dark_bg>233 ? dark_bg-2 : 16),
+//! light_bg_1=254, light_bg_2=255 (lines 176-177, min(..,256)); s:hi uses the
+//! first (dark) list element (s:style_idx, line 165) and maps any index >255 to
+//! 231 (lines 184-185). GUI hex from the file's default s:rgb_map
+//! (~/05-genizah/seoul256.vim/colors/seoul256.vim:120-135, iTerm2-measured
+//! values; NOT the xterm table, which is only used when g:seoul256_srgb=1,
+//! lines 37-118). The background knob is a fixed integer set: dark 233..239
+//! (default 237), light 252..256 (default 253) (lines 147-150, 169/173;
+//! README.md:52-60); any other value is discarded (line 152) and the style
+//! falls back to &background/colors_name; non-integers are not meaningful
+//! because the offsets index a Dict by integer. StatusLine/StatusLineNC are
+//! gui=bold,reverse so status roles are the rendered (swapped) colours. No
+//! Diagnostic* groups (error/warning from ALE signs, info/hint null). ansi: the
+//! colorscheme sets no g:terminal_ansi_colors/terminal_color_*; ansi is the
+//! repo's own urxvt port, which exists for one background only (237) and is
+//! reused unchanged for every level. Index gaps: for this level the roles
+//! tabline_bg land on an xterm index missing from the default s:rgb_map, so Vim
+//! emits gui*=NONE and they inherit Normal (given as such).
+pub const theme = .{
+ .name = "seoul256_dark_238",
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guibg = s:hi arg s:dark_bg = 238 -> s:rgb_map[238] ~/05-genizah/seoul256.vim/colors/seoul256.vim:133
+ .bg = .{ 0x56, 0x56, 0x56 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guifg = s:hi arg s:dark_fg = 252 -> s:rgb_map[252] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134
+ .fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:209 Visual guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .sel_bg = .{ 0x00, 0x71, 0x73 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guifg = s:hi arg s:dark_bg + 2 = 240 -> s:rgb_map[240] ~/05-genizah/seoul256.vim/colors/seoul256.vim:133; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_bg = .{ 0x6b, 0x6b, 0x6b },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse #dfdebd, adjusted
+ .tag_fg = .{ 0xee, 0xed, 0xdb },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guifg = s:hi arg 95 = 95 -> s:rgb_map[95] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse #9a7372, adjusted
+ .tag_active_bg = .{ 0xb9, 0x78, 0x77 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse #dfdebd, adjusted
+ .tag_active_fg = .{ 0x1f, 0x1f, 0x1a },
+ // the focused grip: ~/05-genizah/seoul256.vim/colors/seoul256.vim:359 TabLineSel guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121 (accent: the selected-tab background, TabLineSel, the file's only coloured focus marker; the airline/lightline seoul256 themes live outside this repo)
+ .box = .{ 0x00, 0x71, 0x73 },
+ // derived: the grip at rest, the accent #007173 faded toward the unfocused tag #6b6b6b as far as 1.6:1 off it (1.00 of the way)
+ .box_dim = .{ 0x4d, 0x9c, 0x9d },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:396 GitGutterChange guifg = s:hi arg 68 = 68 -> s:rgb_map[68] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123 (modified: vim-gitgutter Changed sign)
+ .box_dirty = .{ 0x71, 0x9c, 0xdf },
+ // derived: the column grip, the original's type colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:247 Type guifg = s:hi arg 179 = 179 -> s:rgb_map[179] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129
+ .column_box = .{ 0xdf, 0xbc, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:253 Keyword guifg = s:hi arg 168 = 168 -> s:rgb_map[168] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128
+ .kw = .{ 0xe1, 0x78, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:216 String guifg = s:hi arg 109 = 109 -> s:rgb_map[109] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .str = .{ 0x98, 0xbc, 0xbd },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:213 Number guifg = s:hi arg 222 = 222 -> s:rgb_map[222] ~/05-genizah/seoul256.vim/colors/seoul256.vim:131
+ .num = .{ 0xff, 0xde, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:212 Comment guifg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .comment = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:208 LineNr guifg = s:hi arg 101 = 101 -> s:rgb_map[101] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .lineno = .{ 0x99, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:276 CursorLineNr guifg = s:hi arg 131 = 131 -> s:rgb_map[131] ~/05-genizah/seoul256.vim/colors/seoul256.vim:126
+ .lineno_active = .{ 0xbe, 0x75, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:306 PmenuSbar guibg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .scroll_track = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:307 PmenuThumb guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .scroll_thumb = .{ 0x00, 0x71, 0x73 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:287 VertSplit guifg = s:hi arg s:dark_bg_2 = 236 -> s:rgb_map[236] ~/05-genizah/seoul256.vim/colors/seoul256.vim:132 #3f3f3f, adjusted
+ .border = .{ 0x76, 0x76, 0x76 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guibg = s:hi arg 24 = 24 -> s:rgb_map[24] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .search_bg = .{ 0x00, 0x72, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guifg = s:hi arg s:dark_fg = 252 -> s:rgb_map[252] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134
+ .search_fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:402 ALEErrorSign guifg = s:hi arg 161 = 161 -> s:rgb_map[161] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE error sign used)
+ .diagnostic_error = .{ 0xe1, 0x26, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:403 ALEWarningSign guifg = s:hi arg 174 = 174 -> s:rgb_map[174] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE warning sign used)
+ .diagnostic_warning = .{ 0xe0, 0x9b, 0x99 },
+ .palette = .{
+ .{ 0x4e, 0x4e, 0x4e }, // 0: ~/05-genizah/seoul256.vim/urxvt/seoul256:5 URxvt*color0 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd6, 0x87, 0x87 }, // 1: ~/05-genizah/seoul256.vim/urxvt/seoul256:6 URxvt*color1 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x86, 0x5f }, // 2: ~/05-genizah/seoul256.vim/urxvt/seoul256:7 URxvt*color2 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd8, 0xaf, 0x5f }, // 3: ~/05-genizah/seoul256.vim/urxvt/seoul256:8 URxvt*color3 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x85, 0xad, 0xd4 }, // 4: ~/05-genizah/seoul256.vim/urxvt/seoul256:9 URxvt*color4 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd7, 0xaf, 0xaf }, // 5: ~/05-genizah/seoul256.vim/urxvt/seoul256:10 URxvt*color5 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xaf, 0xaf }, // 6: ~/05-genizah/seoul256.vim/urxvt/seoul256:11 URxvt*color6 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd0, 0xd0, 0xd0 }, // 7: ~/05-genizah/seoul256.vim/urxvt/seoul256:12 URxvt*color7 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x62, 0x62, 0x62 }, // 8: ~/05-genizah/seoul256.vim/urxvt/seoul256:13 URxvt*color8 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd7, 0x5f, 0x87 }, // 9: ~/05-genizah/seoul256.vim/urxvt/seoul256:14 URxvt*color9 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xaf, 0x87 }, // 10: ~/05-genizah/seoul256.vim/urxvt/seoul256:15 URxvt*color10 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xff, 0xd7, 0x87 }, // 11: ~/05-genizah/seoul256.vim/urxvt/seoul256:16 URxvt*color11 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xad, 0xd4, 0xfb }, // 12: ~/05-genizah/seoul256.vim/urxvt/seoul256:17 URxvt*color12 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xff, 0xaf, 0xaf }, // 13: ~/05-genizah/seoul256.vim/urxvt/seoul256:18 URxvt*color13 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xd7, 0xd7 }, // 14: ~/05-genizah/seoul256.vim/urxvt/seoul256:19 URxvt*color14 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xe4, 0xe4, 0xe4 }, // 15: ~/05-genizah/seoul256.vim/urxvt/seoul256:20 URxvt*color15 (official urxvt port; the vim colorscheme sets no terminal colours)
+ },
+};
diff --git a/src/themes/seoul256_dark_239.zig b/src/themes/seoul256_dark_239.zig
new file mode 100644
index 00000000..e63dc91f
--- /dev/null
+++ b/src/themes/seoul256_dark_239.zig
@@ -0,0 +1,102 @@
+//! Seoul256 (239): Seoul256, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/junegunn/seoul256.vim; genizah ~/05-genizah/seoul256.vim; commit 0357ff3e44faab66c98bc98dfc89c834e37012da
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #9a7372 -> #ac8f86: 1.11:1 off the unfocused #757575, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #dfdebd -> #fdfdfb: 3.36:1 on #757575, tag text needs 4.5:1 to be read and run
+//! - focused tag text #dfdebd -> #2d2c26: 2.18:1 on #ac8f86, tag text needs 4.5:1
+//! - rules between panes #4b4b4b -> #848484: 1.41:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: g:seoul256_background = 239 (dark). Resolved exactly as
+//! ~/05-genizah/seoul256.vim/colors/seoul256.vim:145-177: dark_bg=239,
+//! light_bg=253, dark_bg_2=237 (line 175: dark_bg>233 ? dark_bg-2 : 16),
+//! light_bg_1=254, light_bg_2=255 (lines 176-177, min(..,256)); s:hi uses the
+//! first (dark) list element (s:style_idx, line 165) and maps any index >255 to
+//! 231 (lines 184-185). GUI hex from the file's default s:rgb_map
+//! (~/05-genizah/seoul256.vim/colors/seoul256.vim:120-135, iTerm2-measured
+//! values; NOT the xterm table, which is only used when g:seoul256_srgb=1,
+//! lines 37-118). The background knob is a fixed integer set: dark 233..239
+//! (default 237), light 252..256 (default 253) (lines 147-150, 169/173;
+//! README.md:52-60); any other value is discarded (line 152) and the style
+//! falls back to &background/colors_name; non-integers are not meaningful
+//! because the offsets index a Dict by integer. StatusLine/StatusLineNC are
+//! gui=bold,reverse so status roles are the rendered (swapped) colours. No
+//! Diagnostic* groups (error/warning from ALE signs, info/hint null). ansi: the
+//! colorscheme sets no g:terminal_ansi_colors/terminal_color_*; ansi is the
+//! repo's own urxvt port, which exists for one background only (237) and is
+//! reused unchanged for every level. Index gaps: for this level the roles
+//! tabline_bg land on an xterm index missing from the default s:rgb_map, so Vim
+//! emits gui*=NONE and they inherit Normal (given as such).
+pub const theme = .{
+ .name = "seoul256_dark_239",
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guibg = s:hi arg s:dark_bg = 239 -> s:rgb_map[239] ~/05-genizah/seoul256.vim/colors/seoul256.vim:133
+ .bg = .{ 0x61, 0x61, 0x61 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guifg = s:hi arg s:dark_fg = 252 -> s:rgb_map[252] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134
+ .fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:209 Visual guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .sel_bg = .{ 0x00, 0x71, 0x73 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guifg = s:hi arg s:dark_bg + 2 = 241 -> s:rgb_map[241] ~/05-genizah/seoul256.vim/colors/seoul256.vim:133; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_bg = .{ 0x75, 0x75, 0x75 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse #dfdebd, adjusted
+ .tag_fg = .{ 0xfd, 0xfd, 0xfb },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guifg = s:hi arg 95 = 95 -> s:rgb_map[95] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse #9a7372, adjusted
+ .tag_active_bg = .{ 0xac, 0x8f, 0x86 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse #dfdebd, adjusted
+ .tag_active_fg = .{ 0x2d, 0x2c, 0x26 },
+ // the focused grip: ~/05-genizah/seoul256.vim/colors/seoul256.vim:359 TabLineSel guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121 (accent: the selected-tab background, TabLineSel, the file's only coloured focus marker; the airline/lightline seoul256 themes live outside this repo)
+ .box = .{ 0x00, 0x71, 0x73 },
+ // derived: the grip at rest, the accent #007173 faded toward the unfocused tag #757575 as far as 1.6:1 off it (1.00 of the way)
+ .box_dim = .{ 0x61, 0xa7, 0xa8 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:396 GitGutterChange guifg = s:hi arg 68 = 68 -> s:rgb_map[68] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123 (modified: vim-gitgutter Changed sign)
+ .box_dirty = .{ 0x71, 0x9c, 0xdf },
+ // derived: the column grip, the original's type colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:247 Type guifg = s:hi arg 179 = 179 -> s:rgb_map[179] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129
+ .column_box = .{ 0xdf, 0xbc, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:253 Keyword guifg = s:hi arg 168 = 168 -> s:rgb_map[168] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128
+ .kw = .{ 0xe1, 0x78, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:216 String guifg = s:hi arg 109 = 109 -> s:rgb_map[109] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .str = .{ 0x98, 0xbc, 0xbd },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:213 Number guifg = s:hi arg 222 = 222 -> s:rgb_map[222] ~/05-genizah/seoul256.vim/colors/seoul256.vim:131
+ .num = .{ 0xff, 0xde, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:212 Comment guifg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .comment = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:208 LineNr guifg = s:hi arg 101 = 101 -> s:rgb_map[101] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .lineno = .{ 0x99, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:276 CursorLineNr guifg = s:hi arg 131 = 131 -> s:rgb_map[131] ~/05-genizah/seoul256.vim/colors/seoul256.vim:126
+ .lineno_active = .{ 0xbe, 0x75, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:306 PmenuSbar guibg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .scroll_track = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:307 PmenuThumb guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .scroll_thumb = .{ 0x00, 0x71, 0x73 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:287 VertSplit guifg = s:hi arg s:dark_bg_2 = 237 -> s:rgb_map[237] ~/05-genizah/seoul256.vim/colors/seoul256.vim:132 #4b4b4b, adjusted
+ .border = .{ 0x84, 0x84, 0x84 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guibg = s:hi arg 24 = 24 -> s:rgb_map[24] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .search_bg = .{ 0x00, 0x72, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guifg = s:hi arg s:dark_fg = 252 -> s:rgb_map[252] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134
+ .search_fg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:402 ALEErrorSign guifg = s:hi arg 161 = 161 -> s:rgb_map[161] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE error sign used)
+ .diagnostic_error = .{ 0xe1, 0x26, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:403 ALEWarningSign guifg = s:hi arg 174 = 174 -> s:rgb_map[174] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE warning sign used)
+ .diagnostic_warning = .{ 0xe0, 0x9b, 0x99 },
+ .palette = .{
+ .{ 0x4e, 0x4e, 0x4e }, // 0: ~/05-genizah/seoul256.vim/urxvt/seoul256:5 URxvt*color0 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd6, 0x87, 0x87 }, // 1: ~/05-genizah/seoul256.vim/urxvt/seoul256:6 URxvt*color1 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x86, 0x5f }, // 2: ~/05-genizah/seoul256.vim/urxvt/seoul256:7 URxvt*color2 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd8, 0xaf, 0x5f }, // 3: ~/05-genizah/seoul256.vim/urxvt/seoul256:8 URxvt*color3 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x85, 0xad, 0xd4 }, // 4: ~/05-genizah/seoul256.vim/urxvt/seoul256:9 URxvt*color4 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd7, 0xaf, 0xaf }, // 5: ~/05-genizah/seoul256.vim/urxvt/seoul256:10 URxvt*color5 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xaf, 0xaf }, // 6: ~/05-genizah/seoul256.vim/urxvt/seoul256:11 URxvt*color6 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd0, 0xd0, 0xd0 }, // 7: ~/05-genizah/seoul256.vim/urxvt/seoul256:12 URxvt*color7 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x62, 0x62, 0x62 }, // 8: ~/05-genizah/seoul256.vim/urxvt/seoul256:13 URxvt*color8 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd7, 0x5f, 0x87 }, // 9: ~/05-genizah/seoul256.vim/urxvt/seoul256:14 URxvt*color9 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xaf, 0x87 }, // 10: ~/05-genizah/seoul256.vim/urxvt/seoul256:15 URxvt*color10 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xff, 0xd7, 0x87 }, // 11: ~/05-genizah/seoul256.vim/urxvt/seoul256:16 URxvt*color11 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xad, 0xd4, 0xfb }, // 12: ~/05-genizah/seoul256.vim/urxvt/seoul256:17 URxvt*color12 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xff, 0xaf, 0xaf }, // 13: ~/05-genizah/seoul256.vim/urxvt/seoul256:18 URxvt*color13 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0xd7, 0xd7 }, // 14: ~/05-genizah/seoul256.vim/urxvt/seoul256:19 URxvt*color14 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xe4, 0xe4, 0xe4 }, // 15: ~/05-genizah/seoul256.vim/urxvt/seoul256:20 URxvt*color15 (official urxvt port; the vim colorscheme sets no terminal colours)
+ },
+};
diff --git a/src/themes/seoul256_light_252.zig b/src/themes/seoul256_light_252.zig
new file mode 100644
index 00000000..bfd43af0
--- /dev/null
+++ b/src/themes/seoul256_light_252.zig
@@ -0,0 +1,102 @@
+//! Seoul256 Light (252): Seoul256, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/junegunn/seoul256.vim; genizah ~/05-genizah/seoul256.vim; commit 0357ff3e44faab66c98bc98dfc89c834e37012da
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - unfocused tag text #565656 -> #545454: 4.39:1 on #c8c8c8, tag text needs 4.5:1 to be read and run
+//! - focused tag text #dfdebd -> #12120f: 3.02:1 on #9a7372, tag text needs 4.5:1
+//! - focused grip #719899 -> #4b6465: 1.31:1 on the focused tag #9a7372
+//! - rules between panes #bfbfbf -> #acacac: 1.30:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: g:seoul256_background = 252 (light). Resolved exactly as
+//! ~/05-genizah/seoul256.vim/colors/seoul256.vim:145-177: dark_bg=237,
+//! light_bg=252, dark_bg_2=235 (line 175: dark_bg>233 ? dark_bg-2 : 16),
+//! light_bg_1=253, light_bg_2=254 (lines 176-177, min(..,256)); s:hi uses the
+//! second (light) list element (s:style_idx, line 165) and maps any index >255
+//! to 231 (lines 184-185). GUI hex from the file's default s:rgb_map
+//! (~/05-genizah/seoul256.vim/colors/seoul256.vim:120-135, iTerm2-measured
+//! values; NOT the xterm table, which is only used when g:seoul256_srgb=1,
+//! lines 37-118). The background knob is a fixed integer set: dark 233..239
+//! (default 237), light 252..256 (default 253) (lines 147-150, 169/173;
+//! README.md:52-60); any other value is discarded (line 152) and the style
+//! falls back to &background/colors_name; non-integers are not meaningful
+//! because the offsets index a Dict by integer. StatusLine/StatusLineNC are
+//! gui=bold,reverse so status roles are the rendered (swapped) colours. No
+//! Diagnostic* groups (error/warning from ALE signs, info/hint null). ansi: the
+//! colorscheme sets no g:terminal_ansi_colors/terminal_color_*; ansi is the
+//! repo's own urxvt port, which exists for one background only (253) and is
+//! reused unchanged for every level. Index gaps: for this level the roles
+//! tabline_bg land on an xterm index missing from the default s:rgb_map, so Vim
+//! emits gui*=NONE and they inherit Normal (given as such).
+pub const theme = .{
+ .name = "seoul256_light_252",
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guibg = s:hi arg s:light_bg = 252 -> s:rgb_map[252] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134
+ .bg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guifg = s:hi arg s:light_fg = 239 -> s:rgb_map[239] ~/05-genizah/seoul256.vim/colors/seoul256.vim:133
+ .fg = .{ 0x61, 0x61, 0x61 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:209 Visual guibg = s:hi arg 152 = 152 -> s:rgb_map[152] ~/05-genizah/seoul256.vim/colors/seoul256.vim:127
+ .sel_bg = .{ 0xbc, 0xde, 0xde },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x61, 0x61, 0x61 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guifg = s:hi arg s:light_bg - 2 = 250 -> s:rgb_map[250] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_bg = .{ 0xc8, 0xc8, 0xc8 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guibg = s:hi arg 238 = 238 -> s:rgb_map[238] ~/05-genizah/seoul256.vim/colors/seoul256.vim:133; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse #565656, adjusted
+ .tag_fg = .{ 0x54, 0x54, 0x54 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guifg = s:hi arg 95 = 95 -> s:rgb_map[95] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse
+ .tag_active_bg = .{ 0x9a, 0x73, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse #dfdebd, adjusted
+ .tag_active_fg = .{ 0x12, 0x12, 0x0f },
+ // the focused grip: ~/05-genizah/seoul256.vim/colors/seoul256.vim:359 TabLineSel guibg = s:hi arg 66 = 66 -> s:rgb_map[66] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123 (accent: the selected-tab background, TabLineSel, the file's only coloured focus marker; the airline/lightline seoul256 themes live outside this repo)
+ .box = .{ 0x4b, 0x64, 0x65 },
+ // derived: the grip at rest, the accent #719899 faded toward the unfocused tag #c8c8c8 as far as 1.6:1 off it (0.78 of the way)
+ .box_dim = .{ 0x84, 0xa3, 0xa3 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:396 GitGutterChange guifg = s:hi arg 68 = 68 -> s:rgb_map[68] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123 (modified: vim-gitgutter Changed sign)
+ .box_dirty = .{ 0x71, 0x9c, 0xdf },
+ // derived: the column grip, the original's type colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:247 Type guifg = s:hi arg 94 = 94 -> s:rgb_map[94] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124
+ .column_box = .{ 0x9a, 0x72, 0x00 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:253 Keyword guifg = s:hi arg 168 = 168 -> s:rgb_map[168] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128
+ .kw = .{ 0xe1, 0x78, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:216 String guifg = s:hi arg 30 = 30 -> s:rgb_map[30] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .str = .{ 0x00, 0x97, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:213 Number guifg = s:hi arg 95 = 95 -> s:rgb_map[95] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124
+ .num = .{ 0x9a, 0x73, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:212 Comment guifg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .comment = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:208 LineNr guifg = s:hi arg 101 = 101 -> s:rgb_map[101] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .lineno = .{ 0x99, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:276 CursorLineNr guifg = s:hi arg 131 = 131 -> s:rgb_map[131] ~/05-genizah/seoul256.vim/colors/seoul256.vim:126
+ .lineno_active = .{ 0xbe, 0x75, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:306 PmenuSbar guibg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .scroll_track = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:307 PmenuThumb guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .scroll_thumb = .{ 0x00, 0x71, 0x73 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:287 VertSplit guifg = s:hi arg s:light_bg - 3 = 249 -> s:rgb_map[249] ~/05-genizah/seoul256.vim/colors/seoul256.vim:133 #bfbfbf, adjusted
+ .border = .{ 0xac, 0xac, 0xac },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guibg = s:hi arg 74 = 74 -> s:rgb_map[74] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124
+ .search_bg = .{ 0x70, 0xbd, 0xdf },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guifg = s:hi arg 255 = 255 -> s:rgb_map[255] ~/05-genizah/seoul256.vim/colors/seoul256.vim:135
+ .search_fg = .{ 0xf1, 0xf1, 0xf1 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:402 ALEErrorSign guifg = s:hi arg 161 = 161 -> s:rgb_map[161] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE error sign used)
+ .diagnostic_error = .{ 0xe1, 0x26, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:403 ALEWarningSign guifg = s:hi arg 131 = 131 -> s:rgb_map[131] ~/05-genizah/seoul256.vim/colors/seoul256.vim:126 (no Diagnostic* groups; ALE warning sign used)
+ .diagnostic_warning = .{ 0xbe, 0x75, 0x72 },
+ .palette = .{
+ .{ 0x4e, 0x4e, 0x4e }, // 0: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:5 URxvt*color0 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xaf, 0x5f, 0x5f }, // 1: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:6 URxvt*color1 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x88, 0x5f }, // 2: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:7 URxvt*color2 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xaf, 0x87, 0x60 }, // 3: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:8 URxvt*color3 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x87, 0xae }, // 4: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:9 URxvt*color4 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0x5f, 0x87 }, // 5: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:10 URxvt*color5 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x87, 0x87 }, // 6: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:11 URxvt*color6 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xe4, 0xe4, 0xe4 }, // 7: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:12 URxvt*color7 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x3a, 0x3a, 0x3a }, // 8: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:13 URxvt*color8 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0x01, 0x00 }, // 9: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:14 URxvt*color9 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x00, 0x5f, 0x00 }, // 10: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:15 URxvt*color10 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd8, 0x86, 0x5f }, // 11: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:16 URxvt*color11 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x00, 0x87, 0xaf }, // 12: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:17 URxvt*color12 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0x02, 0x5f }, // 13: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:18 URxvt*color13 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x00, 0x87, 0x87 }, // 14: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:19 URxvt*color14 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xee, 0xee, 0xee }, // 15: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:20 URxvt*color15 (official urxvt port; the vim colorscheme sets no terminal colours)
+ },
+};
diff --git a/src/themes/seoul256_light_253.zig b/src/themes/seoul256_light_253.zig
new file mode 100644
index 00000000..cf7518d4
--- /dev/null
+++ b/src/themes/seoul256_light_253.zig
@@ -0,0 +1,99 @@
+//! Seoul256 Light (253): Seoul256, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/junegunn/seoul256.vim; genizah ~/05-genizah/seoul256.vim; commit 0357ff3e44faab66c98bc98dfc89c834e37012da
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag text #dfdebd -> #12120f: 3.02:1 on #9a7372, tag text needs 4.5:1
+//! - focused grip #719899 -> #4b6465: 1.31:1 on the focused tag #9a7372
+//! - rules between panes #c8c8c8 -> #b3b3b3: 1.28:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: g:seoul256_background = 253 (light). Resolved exactly as
+//! ~/05-genizah/seoul256.vim/colors/seoul256.vim:145-177: dark_bg=237,
+//! light_bg=253, dark_bg_2=235 (line 175: dark_bg>233 ? dark_bg-2 : 16),
+//! light_bg_1=254, light_bg_2=255 (lines 176-177, min(..,256)); s:hi uses the
+//! second (light) list element (s:style_idx, line 165) and maps any index >255
+//! to 231 (lines 184-185). GUI hex from the file's default s:rgb_map
+//! (~/05-genizah/seoul256.vim/colors/seoul256.vim:120-135, iTerm2-measured
+//! values; NOT the xterm table, which is only used when g:seoul256_srgb=1,
+//! lines 37-118). The background knob is a fixed integer set: dark 233..239
+//! (default 237), light 252..256 (default 253) (lines 147-150, 169/173;
+//! README.md:52-60); any other value is discarded (line 152) and the style
+//! falls back to &background/colors_name; non-integers are not meaningful
+//! because the offsets index a Dict by integer. StatusLine/StatusLineNC are
+//! gui=bold,reverse so status roles are the rendered (swapped) colours. No
+//! Diagnostic* groups (error/warning from ALE signs, info/hint null). ansi: the
+//! colorscheme sets no g:terminal_ansi_colors/terminal_color_*; ansi is the
+//! repo's own urxvt port, which exists for one background only (253) and is
+//! reused unchanged for every level.
+pub const theme = .{
+ .name = "seoul256_light_253",
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guibg = s:hi arg s:light_bg = 253 -> s:rgb_map[253] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134
+ .bg = .{ 0xe1, 0xe1, 0xe1 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guifg = s:hi arg s:light_fg = 239 -> s:rgb_map[239] ~/05-genizah/seoul256.vim/colors/seoul256.vim:133
+ .fg = .{ 0x61, 0x61, 0x61 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:209 Visual guibg = s:hi arg 152 = 152 -> s:rgb_map[152] ~/05-genizah/seoul256.vim/colors/seoul256.vim:127
+ .sel_bg = .{ 0xbc, 0xde, 0xde },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x61, 0x61, 0x61 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guifg = s:hi arg s:light_bg - 2 = 251 -> s:rgb_map[251] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_bg = .{ 0xd1, 0xd0, 0xd1 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guibg = s:hi arg 238 = 238 -> s:rgb_map[238] ~/05-genizah/seoul256.vim/colors/seoul256.vim:133; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_fg = .{ 0x56, 0x56, 0x56 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guifg = s:hi arg 95 = 95 -> s:rgb_map[95] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse
+ .tag_active_bg = .{ 0x9a, 0x73, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse #dfdebd, adjusted
+ .tag_active_fg = .{ 0x12, 0x12, 0x0f },
+ // the focused grip: ~/05-genizah/seoul256.vim/colors/seoul256.vim:359 TabLineSel guibg = s:hi arg 66 = 66 -> s:rgb_map[66] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123 (accent: the selected-tab background, TabLineSel, the file's only coloured focus marker; the airline/lightline seoul256 themes live outside this repo)
+ .box = .{ 0x4b, 0x64, 0x65 },
+ // derived: the grip at rest, the accent #719899 faded toward the unfocused tag #d1d0d1 as far as 1.6:1 off it (0.70 of the way)
+ .box_dim = .{ 0x8e, 0xa9, 0xaa },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:396 GitGutterChange guifg = s:hi arg 68 = 68 -> s:rgb_map[68] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123 (modified: vim-gitgutter Changed sign)
+ .box_dirty = .{ 0x71, 0x9c, 0xdf },
+ // derived: the column grip, the original's type colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:247 Type guifg = s:hi arg 94 = 94 -> s:rgb_map[94] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124
+ .column_box = .{ 0x9a, 0x72, 0x00 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:253 Keyword guifg = s:hi arg 168 = 168 -> s:rgb_map[168] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128
+ .kw = .{ 0xe1, 0x78, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:216 String guifg = s:hi arg 30 = 30 -> s:rgb_map[30] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .str = .{ 0x00, 0x97, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:213 Number guifg = s:hi arg 95 = 95 -> s:rgb_map[95] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124
+ .num = .{ 0x9a, 0x73, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:212 Comment guifg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .comment = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:208 LineNr guifg = s:hi arg 101 = 101 -> s:rgb_map[101] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .lineno = .{ 0x99, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:276 CursorLineNr guifg = s:hi arg 131 = 131 -> s:rgb_map[131] ~/05-genizah/seoul256.vim/colors/seoul256.vim:126
+ .lineno_active = .{ 0xbe, 0x75, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:306 PmenuSbar guibg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .scroll_track = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:307 PmenuThumb guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .scroll_thumb = .{ 0x00, 0x71, 0x73 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:287 VertSplit guifg = s:hi arg s:light_bg - 3 = 250 -> s:rgb_map[250] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134 #c8c8c8, adjusted
+ .border = .{ 0xb3, 0xb3, 0xb3 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guibg = s:hi arg 74 = 74 -> s:rgb_map[74] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124
+ .search_bg = .{ 0x70, 0xbd, 0xdf },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guifg = s:hi arg 255 = 255 -> s:rgb_map[255] ~/05-genizah/seoul256.vim/colors/seoul256.vim:135
+ .search_fg = .{ 0xf1, 0xf1, 0xf1 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:402 ALEErrorSign guifg = s:hi arg 161 = 161 -> s:rgb_map[161] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE error sign used)
+ .diagnostic_error = .{ 0xe1, 0x26, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:403 ALEWarningSign guifg = s:hi arg 131 = 131 -> s:rgb_map[131] ~/05-genizah/seoul256.vim/colors/seoul256.vim:126 (no Diagnostic* groups; ALE warning sign used)
+ .diagnostic_warning = .{ 0xbe, 0x75, 0x72 },
+ .palette = .{
+ .{ 0x4e, 0x4e, 0x4e }, // 0: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:5 URxvt*color0 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xaf, 0x5f, 0x5f }, // 1: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:6 URxvt*color1 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x88, 0x5f }, // 2: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:7 URxvt*color2 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xaf, 0x87, 0x60 }, // 3: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:8 URxvt*color3 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x87, 0xae }, // 4: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:9 URxvt*color4 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0x5f, 0x87 }, // 5: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:10 URxvt*color5 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x87, 0x87 }, // 6: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:11 URxvt*color6 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xe4, 0xe4, 0xe4 }, // 7: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:12 URxvt*color7 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x3a, 0x3a, 0x3a }, // 8: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:13 URxvt*color8 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0x01, 0x00 }, // 9: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:14 URxvt*color9 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x00, 0x5f, 0x00 }, // 10: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:15 URxvt*color10 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd8, 0x86, 0x5f }, // 11: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:16 URxvt*color11 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x00, 0x87, 0xaf }, // 12: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:17 URxvt*color12 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0x02, 0x5f }, // 13: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:18 URxvt*color13 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x00, 0x87, 0x87 }, // 14: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:19 URxvt*color14 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xee, 0xee, 0xee }, // 15: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:20 URxvt*color15 (official urxvt port; the vim colorscheme sets no terminal colours)
+ },
+};
diff --git a/src/themes/seoul256_light_254.zig b/src/themes/seoul256_light_254.zig
new file mode 100644
index 00000000..db5ce09c
--- /dev/null
+++ b/src/themes/seoul256_light_254.zig
@@ -0,0 +1,101 @@
+//! Seoul256 Light (254): Seoul256, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/junegunn/seoul256.vim; genizah ~/05-genizah/seoul256.vim; commit 0357ff3e44faab66c98bc98dfc89c834e37012da
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag text #dfdebd -> #12120f: 3.02:1 on #9a7372, tag text needs 4.5:1
+//! - focused grip #719899 -> #4b6465: 1.31:1 on the focused tag #9a7372
+//! - rules between panes #d1d0d1 -> #b8b8b8: 1.27:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: g:seoul256_background = 254 (light). Resolved exactly as
+//! ~/05-genizah/seoul256.vim/colors/seoul256.vim:145-177: dark_bg=237,
+//! light_bg=254, dark_bg_2=235 (line 175: dark_bg>233 ? dark_bg-2 : 16),
+//! light_bg_1=255, light_bg_2=256 (lines 176-177, min(..,256)); s:hi uses the
+//! second (light) list element (s:style_idx, line 165) and maps any index >255
+//! to 231 (lines 184-185). GUI hex from the file's default s:rgb_map
+//! (~/05-genizah/seoul256.vim/colors/seoul256.vim:120-135, iTerm2-measured
+//! values; NOT the xterm table, which is only used when g:seoul256_srgb=1,
+//! lines 37-118). The background knob is a fixed integer set: dark 233..239
+//! (default 237), light 252..256 (default 253) (lines 147-150, 169/173;
+//! README.md:52-60); any other value is discarded (line 152) and the style
+//! falls back to &background/colors_name; non-integers are not meaningful
+//! because the offsets index a Dict by integer. StatusLine/StatusLineNC are
+//! gui=bold,reverse so status roles are the rendered (swapped) colours. No
+//! Diagnostic* groups (error/warning from ALE signs, info/hint null). ansi: the
+//! colorscheme sets no g:terminal_ansi_colors/terminal_color_*; ansi is the
+//! repo's own urxvt port, which exists for one background only (253) and is
+//! reused unchanged for every level. Index gaps: for this level the roles
+//! tabline_fg land on an xterm index missing from the default s:rgb_map, so Vim
+//! emits gui*=NONE and they inherit Normal (given as such).
+pub const theme = .{
+ .name = "seoul256_light_254",
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guibg = s:hi arg s:light_bg = 254 -> s:rgb_map[254] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134
+ .bg = .{ 0xe9, 0xe9, 0xe9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guifg = s:hi arg s:light_fg = 239 -> s:rgb_map[239] ~/05-genizah/seoul256.vim/colors/seoul256.vim:133
+ .fg = .{ 0x61, 0x61, 0x61 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:209 Visual guibg = s:hi arg 152 = 152 -> s:rgb_map[152] ~/05-genizah/seoul256.vim/colors/seoul256.vim:127
+ .sel_bg = .{ 0xbc, 0xde, 0xde },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x61, 0x61, 0x61 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guifg = s:hi arg s:light_bg - 2 = 252 -> s:rgb_map[252] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_bg = .{ 0xd9, 0xd9, 0xd9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guibg = s:hi arg 238 = 238 -> s:rgb_map[238] ~/05-genizah/seoul256.vim/colors/seoul256.vim:133; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_fg = .{ 0x56, 0x56, 0x56 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guifg = s:hi arg 95 = 95 -> s:rgb_map[95] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse
+ .tag_active_bg = .{ 0x9a, 0x73, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse #dfdebd, adjusted
+ .tag_active_fg = .{ 0x12, 0x12, 0x0f },
+ // the focused grip: ~/05-genizah/seoul256.vim/colors/seoul256.vim:359 TabLineSel guibg = s:hi arg 66 = 66 -> s:rgb_map[66] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123 (accent: the selected-tab background, TabLineSel, the file's only coloured focus marker; the airline/lightline seoul256 themes live outside this repo)
+ .box = .{ 0x4b, 0x64, 0x65 },
+ // derived: the grip at rest, the accent #719899 faded toward the unfocused tag #d9d9d9 as far as 1.6:1 off it (0.64 of the way)
+ .box_dim = .{ 0x96, 0xaf, 0xb0 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:396 GitGutterChange guifg = s:hi arg 68 = 68 -> s:rgb_map[68] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123 (modified: vim-gitgutter Changed sign)
+ .box_dirty = .{ 0x71, 0x9c, 0xdf },
+ // derived: the column grip, the original's type colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:247 Type guifg = s:hi arg 94 = 94 -> s:rgb_map[94] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124
+ .column_box = .{ 0x9a, 0x72, 0x00 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:253 Keyword guifg = s:hi arg 168 = 168 -> s:rgb_map[168] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128
+ .kw = .{ 0xe1, 0x78, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:216 String guifg = s:hi arg 30 = 30 -> s:rgb_map[30] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .str = .{ 0x00, 0x97, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:213 Number guifg = s:hi arg 95 = 95 -> s:rgb_map[95] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124
+ .num = .{ 0x9a, 0x73, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:212 Comment guifg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .comment = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:208 LineNr guifg = s:hi arg 101 = 101 -> s:rgb_map[101] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .lineno = .{ 0x99, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:276 CursorLineNr guifg = s:hi arg 131 = 131 -> s:rgb_map[131] ~/05-genizah/seoul256.vim/colors/seoul256.vim:126
+ .lineno_active = .{ 0xbe, 0x75, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:306 PmenuSbar guibg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .scroll_track = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:307 PmenuThumb guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .scroll_thumb = .{ 0x00, 0x71, 0x73 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:287 VertSplit guifg = s:hi arg s:light_bg - 3 = 251 -> s:rgb_map[251] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134 #d1d0d1, adjusted
+ .border = .{ 0xb8, 0xb8, 0xb8 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guibg = s:hi arg 74 = 74 -> s:rgb_map[74] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124
+ .search_bg = .{ 0x70, 0xbd, 0xdf },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guifg = s:hi arg 255 = 255 -> s:rgb_map[255] ~/05-genizah/seoul256.vim/colors/seoul256.vim:135
+ .search_fg = .{ 0xf1, 0xf1, 0xf1 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:402 ALEErrorSign guifg = s:hi arg 161 = 161 -> s:rgb_map[161] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE error sign used)
+ .diagnostic_error = .{ 0xe1, 0x26, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:403 ALEWarningSign guifg = s:hi arg 131 = 131 -> s:rgb_map[131] ~/05-genizah/seoul256.vim/colors/seoul256.vim:126 (no Diagnostic* groups; ALE warning sign used)
+ .diagnostic_warning = .{ 0xbe, 0x75, 0x72 },
+ .palette = .{
+ .{ 0x4e, 0x4e, 0x4e }, // 0: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:5 URxvt*color0 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xaf, 0x5f, 0x5f }, // 1: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:6 URxvt*color1 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x88, 0x5f }, // 2: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:7 URxvt*color2 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xaf, 0x87, 0x60 }, // 3: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:8 URxvt*color3 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x87, 0xae }, // 4: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:9 URxvt*color4 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0x5f, 0x87 }, // 5: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:10 URxvt*color5 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x87, 0x87 }, // 6: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:11 URxvt*color6 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xe4, 0xe4, 0xe4 }, // 7: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:12 URxvt*color7 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x3a, 0x3a, 0x3a }, // 8: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:13 URxvt*color8 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0x01, 0x00 }, // 9: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:14 URxvt*color9 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x00, 0x5f, 0x00 }, // 10: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:15 URxvt*color10 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd8, 0x86, 0x5f }, // 11: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:16 URxvt*color11 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x00, 0x87, 0xaf }, // 12: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:17 URxvt*color12 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0x02, 0x5f }, // 13: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:18 URxvt*color13 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x00, 0x87, 0x87 }, // 14: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:19 URxvt*color14 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xee, 0xee, 0xee }, // 15: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:20 URxvt*color15 (official urxvt port; the vim colorscheme sets no terminal colours)
+ },
+};
diff --git a/src/themes/seoul256_light_255.zig b/src/themes/seoul256_light_255.zig
new file mode 100644
index 00000000..db25fd20
--- /dev/null
+++ b/src/themes/seoul256_light_255.zig
@@ -0,0 +1,101 @@
+//! Seoul256 Light (255): Seoul256, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/junegunn/seoul256.vim; genizah ~/05-genizah/seoul256.vim; commit 0357ff3e44faab66c98bc98dfc89c834e37012da
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag text #dfdebd -> #12120f: 3.02:1 on #9a7372, tag text needs 4.5:1
+//! - focused grip #719899 -> #4b6465: 1.31:1 on the focused tag #9a7372
+//! - rules between panes #d9d9d9 -> #bfbfbf: 1.25:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: g:seoul256_background = 255 (light). Resolved exactly as
+//! ~/05-genizah/seoul256.vim/colors/seoul256.vim:145-177: dark_bg=237,
+//! light_bg=255, dark_bg_2=235 (line 175: dark_bg>233 ? dark_bg-2 : 16),
+//! light_bg_1=256, light_bg_2=256 (lines 176-177, min(..,256)); s:hi uses the
+//! second (light) list element (s:style_idx, line 165) and maps any index >255
+//! to 231 (lines 184-185). GUI hex from the file's default s:rgb_map
+//! (~/05-genizah/seoul256.vim/colors/seoul256.vim:120-135, iTerm2-measured
+//! values; NOT the xterm table, which is only used when g:seoul256_srgb=1,
+//! lines 37-118). The background knob is a fixed integer set: dark 233..239
+//! (default 237), light 252..256 (default 253) (lines 147-150, 169/173;
+//! README.md:52-60); any other value is discarded (line 152) and the style
+//! falls back to &background/colors_name; non-integers are not meaningful
+//! because the offsets index a Dict by integer. StatusLine/StatusLineNC are
+//! gui=bold,reverse so status roles are the rendered (swapped) colours. No
+//! Diagnostic* groups (error/warning from ALE signs, info/hint null). ansi: the
+//! colorscheme sets no g:terminal_ansi_colors/terminal_color_*; ansi is the
+//! repo's own urxvt port, which exists for one background only (253) and is
+//! reused unchanged for every level. Index gaps: for this level the roles
+//! tabline_fg land on an xterm index missing from the default s:rgb_map, so Vim
+//! emits gui*=NONE and they inherit Normal (given as such).
+pub const theme = .{
+ .name = "seoul256_light_255",
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guibg = s:hi arg s:light_bg = 255 -> s:rgb_map[255] ~/05-genizah/seoul256.vim/colors/seoul256.vim:135
+ .bg = .{ 0xf1, 0xf1, 0xf1 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guifg = s:hi arg s:light_fg = 239 -> s:rgb_map[239] ~/05-genizah/seoul256.vim/colors/seoul256.vim:133
+ .fg = .{ 0x61, 0x61, 0x61 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:209 Visual guibg = s:hi arg 152 = 152 -> s:rgb_map[152] ~/05-genizah/seoul256.vim/colors/seoul256.vim:127
+ .sel_bg = .{ 0xbc, 0xde, 0xde },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x61, 0x61, 0x61 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guifg = s:hi arg s:light_bg - 2 = 253 -> s:rgb_map[253] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_bg = .{ 0xe1, 0xe1, 0xe1 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guibg = s:hi arg 238 = 238 -> s:rgb_map[238] ~/05-genizah/seoul256.vim/colors/seoul256.vim:133; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_fg = .{ 0x56, 0x56, 0x56 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guifg = s:hi arg 95 = 95 -> s:rgb_map[95] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse
+ .tag_active_bg = .{ 0x9a, 0x73, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse #dfdebd, adjusted
+ .tag_active_fg = .{ 0x12, 0x12, 0x0f },
+ // the focused grip: ~/05-genizah/seoul256.vim/colors/seoul256.vim:359 TabLineSel guibg = s:hi arg 66 = 66 -> s:rgb_map[66] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123 (accent: the selected-tab background, TabLineSel, the file's only coloured focus marker; the airline/lightline seoul256 themes live outside this repo)
+ .box = .{ 0x4b, 0x64, 0x65 },
+ // derived: the grip at rest, the accent #719899 faded toward the unfocused tag #e1e1e1 as far as 1.6:1 off it (0.58 of the way)
+ .box_dim = .{ 0xa0, 0xb7, 0xb7 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:396 GitGutterChange guifg = s:hi arg 68 = 68 -> s:rgb_map[68] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123 (modified: vim-gitgutter Changed sign)
+ .box_dirty = .{ 0x71, 0x9c, 0xdf },
+ // derived: the column grip, the original's type colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:247 Type guifg = s:hi arg 94 = 94 -> s:rgb_map[94] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124
+ .column_box = .{ 0x9a, 0x72, 0x00 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:253 Keyword guifg = s:hi arg 168 = 168 -> s:rgb_map[168] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128
+ .kw = .{ 0xe1, 0x78, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:216 String guifg = s:hi arg 30 = 30 -> s:rgb_map[30] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .str = .{ 0x00, 0x97, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:213 Number guifg = s:hi arg 95 = 95 -> s:rgb_map[95] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124
+ .num = .{ 0x9a, 0x73, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:212 Comment guifg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .comment = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:208 LineNr guifg = s:hi arg 101 = 101 -> s:rgb_map[101] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .lineno = .{ 0x99, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:276 CursorLineNr guifg = s:hi arg 131 = 131 -> s:rgb_map[131] ~/05-genizah/seoul256.vim/colors/seoul256.vim:126
+ .lineno_active = .{ 0xbe, 0x75, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:306 PmenuSbar guibg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .scroll_track = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:307 PmenuThumb guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .scroll_thumb = .{ 0x00, 0x71, 0x73 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:287 VertSplit guifg = s:hi arg s:light_bg - 3 = 252 -> s:rgb_map[252] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134 #d9d9d9, adjusted
+ .border = .{ 0xbf, 0xbf, 0xbf },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guibg = s:hi arg 74 = 74 -> s:rgb_map[74] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124
+ .search_bg = .{ 0x70, 0xbd, 0xdf },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guifg = s:hi arg 255 = 255 -> s:rgb_map[255] ~/05-genizah/seoul256.vim/colors/seoul256.vim:135
+ .search_fg = .{ 0xf1, 0xf1, 0xf1 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:402 ALEErrorSign guifg = s:hi arg 161 = 161 -> s:rgb_map[161] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE error sign used)
+ .diagnostic_error = .{ 0xe1, 0x26, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:403 ALEWarningSign guifg = s:hi arg 131 = 131 -> s:rgb_map[131] ~/05-genizah/seoul256.vim/colors/seoul256.vim:126 (no Diagnostic* groups; ALE warning sign used)
+ .diagnostic_warning = .{ 0xbe, 0x75, 0x72 },
+ .palette = .{
+ .{ 0x4e, 0x4e, 0x4e }, // 0: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:5 URxvt*color0 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xaf, 0x5f, 0x5f }, // 1: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:6 URxvt*color1 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x88, 0x5f }, // 2: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:7 URxvt*color2 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xaf, 0x87, 0x60 }, // 3: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:8 URxvt*color3 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x87, 0xae }, // 4: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:9 URxvt*color4 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0x5f, 0x87 }, // 5: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:10 URxvt*color5 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x87, 0x87 }, // 6: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:11 URxvt*color6 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xe4, 0xe4, 0xe4 }, // 7: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:12 URxvt*color7 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x3a, 0x3a, 0x3a }, // 8: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:13 URxvt*color8 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0x01, 0x00 }, // 9: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:14 URxvt*color9 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x00, 0x5f, 0x00 }, // 10: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:15 URxvt*color10 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd8, 0x86, 0x5f }, // 11: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:16 URxvt*color11 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x00, 0x87, 0xaf }, // 12: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:17 URxvt*color12 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0x02, 0x5f }, // 13: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:18 URxvt*color13 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x00, 0x87, 0x87 }, // 14: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:19 URxvt*color14 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xee, 0xee, 0xee }, // 15: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:20 URxvt*color15 (official urxvt port; the vim colorscheme sets no terminal colours)
+ },
+};
diff --git a/src/themes/seoul256_light_256.zig b/src/themes/seoul256_light_256.zig
new file mode 100644
index 00000000..32dfb211
--- /dev/null
+++ b/src/themes/seoul256_light_256.zig
@@ -0,0 +1,101 @@
+//! Seoul256 Light (256): Seoul256, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/junegunn/seoul256.vim; genizah ~/05-genizah/seoul256.vim; commit 0357ff3e44faab66c98bc98dfc89c834e37012da
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag text #dfdebd -> #12120f: 3.02:1 on #9a7372, tag text needs 4.5:1
+//! - focused grip #719899 -> #4b6465: 1.31:1 on the focused tag #9a7372
+//! - rules between panes #e1e1e1 -> #cacaca: 1.31:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: g:seoul256_background = 256 (light). Resolved exactly as
+//! ~/05-genizah/seoul256.vim/colors/seoul256.vim:145-177: dark_bg=237,
+//! light_bg=256, dark_bg_2=235 (line 175: dark_bg>233 ? dark_bg-2 : 16),
+//! light_bg_1=256, light_bg_2=256 (lines 176-177, min(..,256)); s:hi uses the
+//! second (light) list element (s:style_idx, line 165) and maps any index >255
+//! to 231 (lines 184-185). GUI hex from the file's default s:rgb_map
+//! (~/05-genizah/seoul256.vim/colors/seoul256.vim:120-135, iTerm2-measured
+//! values; NOT the xterm table, which is only used when g:seoul256_srgb=1,
+//! lines 37-118). The background knob is a fixed integer set: dark 233..239
+//! (default 237), light 252..256 (default 253) (lines 147-150, 169/173;
+//! README.md:52-60); any other value is discarded (line 152) and the style
+//! falls back to &background/colors_name; non-integers are not meaningful
+//! because the offsets index a Dict by integer. StatusLine/StatusLineNC are
+//! gui=bold,reverse so status roles are the rendered (swapped) colours. No
+//! Diagnostic* groups (error/warning from ALE signs, info/hint null). ansi: the
+//! colorscheme sets no g:terminal_ansi_colors/terminal_color_*; ansi is the
+//! repo's own urxvt port, which exists for one background only (253) and is
+//! reused unchanged for every level. Index gaps: for this level the roles
+//! tabline_fg land on an xterm index missing from the default s:rgb_map, so Vim
+//! emits gui*=NONE and they inherit Normal (given as such).
+pub const theme = .{
+ .name = "seoul256_light_256",
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guibg = s:hi arg s:light_bg = 256 (256>255 -> 231 per ~/05-genizah/seoul256.vim/colors/seoul256.vim:184-185) -> s:rgb_map[231] ~/05-genizah/seoul256.vim/colors/seoul256.vim:131
+ .bg = .{ 0xff, 0xff, 0xff },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:206 Normal guifg = s:hi arg s:light_fg = 239 -> s:rgb_map[239] ~/05-genizah/seoul256.vim/colors/seoul256.vim:133
+ .fg = .{ 0x61, 0x61, 0x61 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:209 Visual guibg = s:hi arg 152 = 152 -> s:rgb_map[152] ~/05-genizah/seoul256.vim/colors/seoul256.vim:127
+ .sel_bg = .{ 0xbc, 0xde, 0xde },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x61, 0x61, 0x61 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guifg = s:hi arg s:light_bg - 2 = 254 -> s:rgb_map[254] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_bg = .{ 0xe9, 0xe9, 0xe9 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:351 StatusLineNC guibg = s:hi arg 238 = 238 -> s:rgb_map[238] ~/05-genizah/seoul256.vim/colors/seoul256.vim:133; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:355 sets gui=bold,reverse
+ .tag_fg = .{ 0x56, 0x56, 0x56 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guifg = s:hi arg 95 = 95 -> s:rgb_map[95] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124; rendered as the bar background because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse
+ .tag_active_bg = .{ 0x9a, 0x73, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:350 StatusLine guibg = s:hi arg 187 = 187 -> s:rgb_map[187] ~/05-genizah/seoul256.vim/colors/seoul256.vim:129; rendered as the bar text because ~/05-genizah/seoul256.vim/colors/seoul256.vim:354 sets gui=bold,reverse #dfdebd, adjusted
+ .tag_active_fg = .{ 0x12, 0x12, 0x0f },
+ // the focused grip: ~/05-genizah/seoul256.vim/colors/seoul256.vim:359 TabLineSel guibg = s:hi arg 66 = 66 -> s:rgb_map[66] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123 (accent: the selected-tab background, TabLineSel, the file's only coloured focus marker; the airline/lightline seoul256 themes live outside this repo)
+ .box = .{ 0x4b, 0x64, 0x65 },
+ // derived: the grip at rest, the accent #719899 faded toward the unfocused tag #e9e9e9 as far as 1.6:1 off it (0.54 of the way)
+ .box_dim = .{ 0xa8, 0xbd, 0xbe },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:396 GitGutterChange guifg = s:hi arg 68 = 68 -> s:rgb_map[68] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123 (modified: vim-gitgutter Changed sign)
+ .box_dirty = .{ 0x71, 0x9c, 0xdf },
+ // derived: the column grip, the original's type colour, ~/05-genizah/seoul256.vim/colors/seoul256.vim:247 Type guifg = s:hi arg 94 = 94 -> s:rgb_map[94] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124
+ .column_box = .{ 0x9a, 0x72, 0x00 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:253 Keyword guifg = s:hi arg 168 = 168 -> s:rgb_map[168] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128
+ .kw = .{ 0xe1, 0x78, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:216 String guifg = s:hi arg 30 = 30 -> s:rgb_map[30] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .str = .{ 0x00, 0x97, 0x99 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:213 Number guifg = s:hi arg 95 = 95 -> s:rgb_map[95] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124
+ .num = .{ 0x9a, 0x73, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:212 Comment guifg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .comment = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:208 LineNr guifg = s:hi arg 101 = 101 -> s:rgb_map[101] ~/05-genizah/seoul256.vim/colors/seoul256.vim:125
+ .lineno = .{ 0x99, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:276 CursorLineNr guifg = s:hi arg 131 = 131 -> s:rgb_map[131] ~/05-genizah/seoul256.vim/colors/seoul256.vim:126
+ .lineno_active = .{ 0xbe, 0x75, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:306 PmenuSbar guibg = s:hi arg 65 = 65 -> s:rgb_map[65] ~/05-genizah/seoul256.vim/colors/seoul256.vim:123
+ .scroll_track = .{ 0x71, 0x98, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:307 PmenuThumb guibg = s:hi arg 23 = 23 -> s:rgb_map[23] ~/05-genizah/seoul256.vim/colors/seoul256.vim:121
+ .scroll_thumb = .{ 0x00, 0x71, 0x73 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:287 VertSplit guifg = s:hi arg s:light_bg - 3 = 253 -> s:rgb_map[253] ~/05-genizah/seoul256.vim/colors/seoul256.vim:134 #e1e1e1, adjusted
+ .border = .{ 0xca, 0xca, 0xca },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guibg = s:hi arg 74 = 74 -> s:rgb_map[74] ~/05-genizah/seoul256.vim/colors/seoul256.vim:124
+ .search_bg = .{ 0x70, 0xbd, 0xdf },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:309 Search guifg = s:hi arg 255 = 255 -> s:rgb_map[255] ~/05-genizah/seoul256.vim/colors/seoul256.vim:135
+ .search_fg = .{ 0xf1, 0xf1, 0xf1 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:402 ALEErrorSign guifg = s:hi arg 161 = 161 -> s:rgb_map[161] ~/05-genizah/seoul256.vim/colors/seoul256.vim:128 (no Diagnostic* groups; ALE error sign used)
+ .diagnostic_error = .{ 0xe1, 0x26, 0x72 },
+ // ~/05-genizah/seoul256.vim/colors/seoul256.vim:403 ALEWarningSign guifg = s:hi arg 131 = 131 -> s:rgb_map[131] ~/05-genizah/seoul256.vim/colors/seoul256.vim:126 (no Diagnostic* groups; ALE warning sign used)
+ .diagnostic_warning = .{ 0xbe, 0x75, 0x72 },
+ .palette = .{
+ .{ 0x4e, 0x4e, 0x4e }, // 0: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:5 URxvt*color0 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xaf, 0x5f, 0x5f }, // 1: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:6 URxvt*color1 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x88, 0x5f }, // 2: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:7 URxvt*color2 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xaf, 0x87, 0x60 }, // 3: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:8 URxvt*color3 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x87, 0xae }, // 4: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:9 URxvt*color4 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0x5f, 0x87 }, // 5: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:10 URxvt*color5 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x5f, 0x87, 0x87 }, // 6: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:11 URxvt*color6 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xe4, 0xe4, 0xe4 }, // 7: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:12 URxvt*color7 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x3a, 0x3a, 0x3a }, // 8: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:13 URxvt*color8 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0x01, 0x00 }, // 9: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:14 URxvt*color9 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x00, 0x5f, 0x00 }, // 10: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:15 URxvt*color10 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xd8, 0x86, 0x5f }, // 11: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:16 URxvt*color11 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x00, 0x87, 0xaf }, // 12: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:17 URxvt*color12 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x87, 0x02, 0x5f }, // 13: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:18 URxvt*color13 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0x00, 0x87, 0x87 }, // 14: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:19 URxvt*color14 (official urxvt port; the vim colorscheme sets no terminal colours)
+ .{ 0xee, 0xee, 0xee }, // 15: ~/05-genizah/seoul256.vim/urxvt/seoul256-light:20 URxvt*color15 (official urxvt port; the vim colorscheme sets no terminal colours)
+ },
+};
diff --git a/src/themes/seoulbones_dark.zig b/src/themes/seoulbones_dark.zig
new file mode 100644
index 00000000..74cdf1ff
--- /dev/null
+++ b/src/themes/seoulbones_dark.zig
@@ -0,0 +1,95 @@
+//! Seoulbones (dark): Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #5e5e5e -> #727272: 1.15:1 off the unfocused #555555, pardes needs 1.5:1 on a dark page
+//! - focused tag text #dddddd -> #f8f8f8: 3.54:1 on #727272, tag text needs 4.5:1
+//! - focused grip #797979 -> #919191: 1.11:1 on the focused tag #727272
+//! - scroll thumb #a0a0a0 -> #a1a1a1: 1.49:1 on its track #818181
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/seoulbones/palette.lua + lua/zenbones/specs/dark.lua); values are the
+//! default 'ness' baked into colors/seoulbones.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours.
+//! palette.lua also names orange (in one or both backgrounds) with no generated
+//! literal-hex output; omitted. TabLineSel is guifg/guibg=NONE (bold only), so
+//! it inherits Normal. TabLine links to StatusLine, TabLineFill to
+//! StatusLineNC. Accent is the lualine normal-mode a bg, a muted neutral (the
+//! theme is contrast-based, not hue-accented). Modified = Changed fg.
+pub const theme = .{
+ .name = "seoulbones_dark",
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:33 Normal guibg
+ .bg = .{ 0x4b, 0x4b, 0x4b },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:33 Normal guifg
+ .fg = .{ 0xdd, 0xdd, 0xdd },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:213 Visual guibg
+ .sel_bg = .{ 0x77, 0x77, 0x77 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xdd, 0xdd, 0xdd },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:200 StatusLineNC guibg
+ .tag_bg = .{ 0x55, 0x55, 0x55 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:200 StatusLineNC guifg
+ .tag_fg = .{ 0xe5, 0xe5, 0xe5 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:197 StatusLine guibg #5e5e5e, adjusted
+ .tag_active_bg = .{ 0x72, 0x72, 0x72 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:197 StatusLine guifg #dddddd, adjusted
+ .tag_active_fg = .{ 0xf8, 0xf8, 0xf8 },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/seoulbones_dark_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0x91, 0x91, 0x91 },
+ // derived: the grip at rest, the accent #797979 faded toward the unfocused tag #555555 as far as 1.6:1 off it (0.88 of the way)
+ .box_dim = .{ 0x75, 0x75, 0x75 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:41 Changed guifg
+ .box_dirty = .{ 0x97, 0xbd, 0xde },
+ // derived: the column grip, the original's function colour, ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:122 Function guifg
+ .column_box = .{ 0xdf, 0xdf, 0xc1 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:150 Keyword guifg
+ .kw = .{ 0xdc, 0x8c, 0xa3 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:203 String guifg
+ .str = .{ 0xab, 0xc4, 0xdb },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:166 Number guifg
+ .num = .{ 0xf7, 0xe0, 0xb3 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:47 Comment guifg
+ .comment = .{ 0x71, 0x98, 0x71 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:152 LineNr guifg
+ .lineno = .{ 0x86, 0x86, 0x86 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:62 CursorLineNr guifg
+ .lineno_active = .{ 0xdd, 0xdd, 0xdd },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:169 PmenuSbar guibg
+ .scroll_track = .{ 0x81, 0x81, 0x81 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:171 PmenuThumb guibg #a0a0a0, adjusted
+ .scroll_thumb = .{ 0xa1, 0xa1, 0xa1 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:220 WinSeparator guifg
+ .border = .{ 0x86, 0x86, 0x86 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:175 Search guibg
+ .search_bg = .{ 0x82, 0x83, 0xad },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:175 Search guifg
+ .search_fg = .{ 0xdd, 0xdd, 0xdd },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:113 Error guifg via DiagnosticError->Error (link :114)
+ .diagnostic_error = .{ 0xe3, 0x88, 0xa3 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:214 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :215)
+ .diagnostic_warning = .{ 0xff, 0xdf, 0x9b },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:72 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x97, 0xbd, 0xde },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:67 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0xa5, 0xa6, 0xc5 },
+ .palette = .{
+ .{ 0x4b, 0x4b, 0x4b }, // 0: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:17 g:terminal_color_0
+ .{ 0xe3, 0x88, 0xa3 }, // 1: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:18 g:terminal_color_1
+ .{ 0x98, 0xbd, 0x99 }, // 2: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:19 g:terminal_color_2
+ .{ 0xff, 0xdf, 0x9b }, // 3: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:20 g:terminal_color_3
+ .{ 0x97, 0xbd, 0xde }, // 4: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:21 g:terminal_color_4
+ .{ 0xa5, 0xa6, 0xc5 }, // 5: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:22 g:terminal_color_5
+ .{ 0x6f, 0xbd, 0xbe }, // 6: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:23 g:terminal_color_6
+ .{ 0xdd, 0xdd, 0xdd }, // 7: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:24 g:terminal_color_7
+ .{ 0x6c, 0x64, 0x65 }, // 8: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:25 g:terminal_color_8
+ .{ 0xeb, 0x99, 0xb1 }, // 9: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:26 g:terminal_color_9
+ .{ 0x8f, 0xcd, 0x92 }, // 10: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:27 g:terminal_color_10
+ .{ 0xff, 0xe5, 0xb3 }, // 11: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:28 g:terminal_color_11
+ .{ 0xa2, 0xc8, 0xe9 }, // 12: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:29 g:terminal_color_12
+ .{ 0xb2, 0xb3, 0xda }, // 13: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:30 g:terminal_color_13
+ .{ 0x6b, 0xca, 0xcb }, // 14: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:31 g:terminal_color_14
+ .{ 0xa8, 0xa8, 0xa8 }, // 15: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:32 g:terminal_color_15
+ },
+};
diff --git a/src/themes/seoulbones_light.zig b/src/themes/seoulbones_light.zig
new file mode 100644
index 00000000..7b897be5
--- /dev/null
+++ b/src/themes/seoulbones_light.zig
@@ -0,0 +1,95 @@
+//! Seoulbones (light): Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #c4c4c4 -> #bdbdbd: 1.18:1 off the unfocused #d4d4d4, pardes needs 1.25:1 on a light page
+//! - unfocused tag text #818181 -> #5a5a5a: 2.63:1 on #d4d4d4, tag text needs 4.5:1 to be read and run
+//! - focused tag text #555555 -> #4b4b4b: 3.97:1 on #bdbdbd, tag text needs 4.5:1
+//! - focused grip #a6a6a6 -> #999999: 1.30:1 on the focused tag #bdbdbd
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/seoulbones/palette.lua + lua/zenbones/specs/light.lua); values are the
+//! default 'ness' baked into colors/seoulbones.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours.
+//! palette.lua also names orange (in one or both backgrounds) with no generated
+//! literal-hex output; omitted. TabLineSel is guifg/guibg=NONE (bold only), so
+//! it inherits Normal. TabLine links to StatusLine, TabLineFill to
+//! StatusLineNC. Accent is the lualine normal-mode a bg, a muted neutral (the
+//! theme is contrast-based, not hue-accented). Modified = Changed fg.
+pub const theme = .{
+ .name = "seoulbones_light",
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:269 Normal guibg
+ .bg = .{ 0xe2, 0xe2, 0xe2 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:269 Normal guifg
+ .fg = .{ 0x55, 0x55, 0x55 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:449 Visual guibg
+ .sel_bg = .{ 0xcc, 0xcc, 0xcc },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x55, 0x55, 0x55 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:436 StatusLineNC guibg
+ .tag_bg = .{ 0xd4, 0xd4, 0xd4 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:436 StatusLineNC guifg #818181, adjusted
+ .tag_fg = .{ 0x5a, 0x5a, 0x5a },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:433 StatusLine guibg #c4c4c4, adjusted
+ .tag_active_bg = .{ 0xbd, 0xbd, 0xbd },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:433 StatusLine guifg #555555, adjusted
+ .tag_active_fg = .{ 0x4b, 0x4b, 0x4b },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/seoulbones_light_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0x99, 0x99, 0x99 },
+ // derived: the grip at rest, the accent #a6a6a6 faded toward the unfocused tag #d4d4d4 as far as 1.6:1 off it (0.98 of the way)
+ .box_dim = .{ 0xa7, 0xa7, 0xa7 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:277 Changed guifg
+ .box_dirty = .{ 0x00, 0x84, 0xa3 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:445 Type guifg
+ .column_box = .{ 0x6d, 0x4c, 0x52 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:386 Keyword guifg
+ .kw = .{ 0xca, 0x62, 0x84 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:439 String guifg
+ .str = .{ 0x4a, 0x75, 0x87 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:402 Number guifg
+ .num = .{ 0x89, 0x65, 0x00 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:283 Comment guifg
+ .comment = .{ 0x62, 0x85, 0x62 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:388 LineNr guifg
+ .lineno = .{ 0x91, 0x91, 0x91 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:298 CursorLineNr guifg
+ .lineno_active = .{ 0x55, 0x55, 0x55 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:405 PmenuSbar guibg
+ .scroll_track = .{ 0x9e, 0x9e, 0x9e },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:407 PmenuThumb guibg
+ .scroll_thumb = .{ 0xf3, 0xf3, 0xf3 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:456 WinSeparator guifg
+ .border = .{ 0x91, 0x91, 0x91 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:411 Search guibg
+ .search_bg = .{ 0xcb, 0xb1, 0xca },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:411 Search guifg
+ .search_fg = .{ 0x55, 0x55, 0x55 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:349 Error guifg via DiagnosticError->Error (link :350)
+ .diagnostic_error = .{ 0xdc, 0x52, 0x84 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:450 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :451)
+ .diagnostic_warning = .{ 0xc4, 0x85, 0x62 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:308 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x00, 0x84, 0xa3 },
+ // ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:303 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0x89, 0x67, 0x88 },
+ .palette = .{
+ .{ 0xe2, 0xe2, 0xe2 }, // 0: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:253 g:terminal_color_0
+ .{ 0xdc, 0x52, 0x84 }, // 1: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:254 g:terminal_color_1
+ .{ 0x62, 0x85, 0x62 }, // 2: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:255 g:terminal_color_2
+ .{ 0xc4, 0x85, 0x62 }, // 3: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:256 g:terminal_color_3
+ .{ 0x00, 0x84, 0xa3 }, // 4: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:257 g:terminal_color_4
+ .{ 0x89, 0x67, 0x88 }, // 5: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:258 g:terminal_color_5
+ .{ 0x00, 0x85, 0x86 }, // 6: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:259 g:terminal_color_6
+ .{ 0x55, 0x55, 0x55 }, // 7: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:260 g:terminal_color_7
+ .{ 0xbf, 0xba, 0xbb }, // 8: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:261 g:terminal_color_8
+ .{ 0xbe, 0x3c, 0x6d }, // 9: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:262 g:terminal_color_9
+ .{ 0x48, 0x72, 0x49 }, // 10: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:263 g:terminal_color_10
+ .{ 0xa7, 0x6b, 0x48 }, // 11: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:264 g:terminal_color_11
+ .{ 0x00, 0x6f, 0x89 }, // 12: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:265 g:terminal_color_12
+ .{ 0x7f, 0x4c, 0x7e }, // 13: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:266 g:terminal_color_13
+ .{ 0x00, 0x6f, 0x70 }, // 14: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:267 g:terminal_color_14
+ .{ 0x77, 0x77, 0x77 }, // 15: ~/05-genizah/zenbones.nvim/colors/seoulbones.vim:268 g:terminal_color_15
+ },
+};
diff --git a/src/themes/solarized_dark.zig b/src/themes/solarized_dark.zig
new file mode 100644
index 00000000..c7c43892
--- /dev/null
+++ b/src/themes/solarized_dark.zig
@@ -0,0 +1,81 @@
+//! Solarized Dark: Solarized, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! canonical palette and ANSI map; url https://github.com/altercation/solarized; genizah ~/05-genizah/solarized; commit 62f656a02f93c5190a8753159e34b385588d5ff3
+//! the author's vim port for roles; path ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - unfocused tag text #073642 -> #01090b: 2.92:1 on #657b83, tag text needs 4.5:1 to be read and run
+//! - focused grip #268bd2 -> #2483c5: 1.38:1 on the focused tag #93a1a1
+//! Upstream notes: green is the canonical #859900 (README), not
+//! solarized.vim:261's "experimental" #719e07.
+pub const theme = .{
+ .name = "solarized_dark",
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:539 Normal bg_back: base03 = ~/05-genizah/solarized/README.md:354 base03
+ .bg = .{ 0x00, 0x2b, 0x36 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:539 Normal fg: base0 = ~/05-genizah/solarized/README.md:358 base0
+ .fg = .{ 0x83, 0x94, 0x96 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:614 Visual fg_base01 reversed: base01 = ~/05-genizah/solarized/README.md:356 base01
+ .sel_bg = .{ 0x58, 0x6e, 0x75 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:614 Visual bg_base03 reversed: base03 = ~/05-genizah/solarized/README.md:354 base03
+ .sel_fg = .{ 0x00, 0x2b, 0x36 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:613 StatusLineNC fg_base00 reversed: base00 = ~/05-genizah/solarized/README.md:357 base00
+ .tag_bg = .{ 0x65, 0x7b, 0x83 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:613 StatusLineNC bg_base02 reversed: base02 = ~/05-genizah/solarized/README.md:355 base02 #073642, adjusted
+ .tag_fg = .{ 0x01, 0x09, 0x0b },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:612 StatusLine fg_base1 reversed: base1 = ~/05-genizah/solarized/README.md:359 base1
+ .tag_active_bg = .{ 0x93, 0xa1, 0xa1 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:612 StatusLine bg_base02 reversed: base02 = ~/05-genizah/solarized/README.md:355 base02
+ .tag_active_fg = .{ 0x07, 0x36, 0x42 },
+ // the focused grip: no UI accent in solarized.vim; Identifier blue, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:552 Identifier: blue = ~/05-genizah/solarized/README.md:367 blue
+ .box = .{ 0x24, 0x83, 0xc5 },
+ // derived: the grip at rest, the accent #268bd2 faded toward the unfocused tag #657b83 as far as 1.6:1 off it (1.00 of the way)
+ .box_dim = .{ 0x18, 0x59, 0x86 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:636 DiffChange fg_yellow: yellow = ~/05-genizah/solarized/README.md:362 yellow
+ .box_dirty = .{ 0xb5, 0x89, 0x00 },
+ // derived: the column grip, the original's keyword colour, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:556 Statement fg (Keyword links to Statement): green = ~/05-genizah/solarized/README.md:369 green
+ .column_box = .{ 0x85, 0x99, 0x00 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:556 Statement fg (Keyword links to Statement): green = ~/05-genizah/solarized/README.md:369 green
+ .kw = .{ 0x85, 0x99, 0x00 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:544 Constant fg (String links to Constant): cyan = ~/05-genizah/solarized/README.md:368 cyan
+ .str = .{ 0x2a, 0xa1, 0x98 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:544 Constant fg (Number links to Constant): cyan = ~/05-genizah/solarized/README.md:368 cyan
+ .num = .{ 0x2a, 0xa1, 0x98 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:541 Comment fg: base01 = ~/05-genizah/solarized/README.md:356 base01
+ .comment = .{ 0x58, 0x6e, 0x75 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:621 LineNr fg: base01 = ~/05-genizah/solarized/README.md:356 base01
+ .lineno = .{ 0x58, 0x6e, 0x75 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:665 PmenuSbar fg_base2 reversed: base2 = ~/05-genizah/solarized/README.md:360 base2
+ .scroll_track = .{ 0xee, 0xe8, 0xd5 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:666 PmenuThumb fg_base0 reversed: base0 = ~/05-genizah/solarized/README.md:358 base0
+ .scroll_thumb = .{ 0x83, 0x94, 0x96 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:624 VertSplit fg_base00 bg_base00: base00 = ~/05-genizah/solarized/README.md:357 base00
+ .border = .{ 0x65, 0x7b, 0x83 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:618 Search fg_yellow reversed: yellow = ~/05-genizah/solarized/README.md:362 yellow
+ .search_bg = .{ 0xb5, 0x89, 0x00 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:618 Search bg_none reversed: the page: base03 = ~/05-genizah/solarized/README.md:354 base03
+ .search_fg = .{ 0x00, 0x2b, 0x36 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:592 Error fg: red = ~/05-genizah/solarized/README.md:364 red
+ .diagnostic_error = .{ 0xdc, 0x32, 0x2f },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:630 WarningMsg fg: red = ~/05-genizah/solarized/README.md:364 red
+ .diagnostic_warning = .{ 0xdc, 0x32, 0x2f },
+ .palette = .{
+ .{ 0x07, 0x36, 0x42 }, // 0: ~/05-genizah/solarized/README.md:355 base02 ANSI 0 (README TERMCOL column)
+ .{ 0xdc, 0x32, 0x2f }, // 1: ~/05-genizah/solarized/README.md:364 red ANSI 1 (README TERMCOL column)
+ .{ 0x85, 0x99, 0x00 }, // 2: ~/05-genizah/solarized/README.md:369 green ANSI 2 (README TERMCOL column)
+ .{ 0xb5, 0x89, 0x00 }, // 3: ~/05-genizah/solarized/README.md:362 yellow ANSI 3 (README TERMCOL column)
+ .{ 0x26, 0x8b, 0xd2 }, // 4: ~/05-genizah/solarized/README.md:367 blue ANSI 4 (README TERMCOL column)
+ .{ 0xd3, 0x36, 0x82 }, // 5: ~/05-genizah/solarized/README.md:365 magenta ANSI 5 (README TERMCOL column)
+ .{ 0x2a, 0xa1, 0x98 }, // 6: ~/05-genizah/solarized/README.md:368 cyan ANSI 6 (README TERMCOL column)
+ .{ 0xee, 0xe8, 0xd5 }, // 7: ~/05-genizah/solarized/README.md:360 base2 ANSI 7 (README TERMCOL column)
+ .{ 0x00, 0x2b, 0x36 }, // 8: ~/05-genizah/solarized/README.md:354 base03 ANSI 8 (README TERMCOL column)
+ .{ 0xcb, 0x4b, 0x16 }, // 9: ~/05-genizah/solarized/README.md:363 orange ANSI 9 (README TERMCOL column)
+ .{ 0x58, 0x6e, 0x75 }, // 10: ~/05-genizah/solarized/README.md:356 base01 ANSI 10 (README TERMCOL column)
+ .{ 0x65, 0x7b, 0x83 }, // 11: ~/05-genizah/solarized/README.md:357 base00 ANSI 11 (README TERMCOL column)
+ .{ 0x83, 0x94, 0x96 }, // 12: ~/05-genizah/solarized/README.md:358 base0 ANSI 12 (README TERMCOL column)
+ .{ 0x6c, 0x71, 0xc4 }, // 13: ~/05-genizah/solarized/README.md:366 violet ANSI 13 (README TERMCOL column)
+ .{ 0x93, 0xa1, 0xa1 }, // 14: ~/05-genizah/solarized/README.md:359 base1 ANSI 14 (README TERMCOL column)
+ .{ 0xfd, 0xf6, 0xe3 }, // 15: ~/05-genizah/solarized/README.md:361 base3 ANSI 15 (README TERMCOL column)
+ },
+};
diff --git a/src/themes/solarized_light.zig b/src/themes/solarized_light.zig
new file mode 100644
index 00000000..124d6d59
--- /dev/null
+++ b/src/themes/solarized_light.zig
@@ -0,0 +1,82 @@
+//! Solarized Light: Solarized, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! canonical palette and ANSI map; url https://github.com/altercation/solarized; genizah ~/05-genizah/solarized; commit 62f656a02f93c5190a8753159e34b385588d5ff3
+//! the author's vim port for roles; path ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - unfocused tag text #eee8d5 -> #2b2a26: 2.58:1 on #839496, tag text needs 4.5:1 to be read and run
+//! - focused tag text #eee8d5 -> #f0ebda: 4.39:1 on #586e75, tag text needs 4.5:1
+//! - focused grip #268bd2 -> #2f90d4: 1.46:1 on the focused tag #586e75
+//! Upstream notes: green is the canonical #859900 (README), not
+//! solarized.vim:261's "experimental" #719e07.
+pub const theme = .{
+ .name = "solarized_light",
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:539 Normal bg_back: base03 = ~/05-genizah/solarized/README.md:361 base3 (swapped for background=light, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:379-391)
+ .bg = .{ 0xfd, 0xf6, 0xe3 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:539 Normal fg: base0 = ~/05-genizah/solarized/README.md:357 base00 (swapped for background=light, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:379-391)
+ .fg = .{ 0x65, 0x7b, 0x83 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:614 Visual fg_base01 reversed: base01 = ~/05-genizah/solarized/README.md:359 base1 (swapped for background=light, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:379-391)
+ .sel_bg = .{ 0x93, 0xa1, 0xa1 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:614 Visual bg_base03 reversed: base03 = ~/05-genizah/solarized/README.md:361 base3 (swapped for background=light, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:379-391)
+ .sel_fg = .{ 0xfd, 0xf6, 0xe3 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:613 StatusLineNC fg_base00 reversed: base00 = ~/05-genizah/solarized/README.md:358 base0 (swapped for background=light, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:379-391)
+ .tag_bg = .{ 0x83, 0x94, 0x96 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:613 StatusLineNC bg_base02 reversed: base02 = ~/05-genizah/solarized/README.md:360 base2 (swapped for background=light, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:379-391) #eee8d5, adjusted
+ .tag_fg = .{ 0x2b, 0x2a, 0x26 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:612 StatusLine fg_base1 reversed: base1 = ~/05-genizah/solarized/README.md:356 base01 (swapped for background=light, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:379-391)
+ .tag_active_bg = .{ 0x58, 0x6e, 0x75 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:612 StatusLine bg_base02 reversed: base02 = ~/05-genizah/solarized/README.md:360 base2 (swapped for background=light, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:379-391) #eee8d5, adjusted
+ .tag_active_fg = .{ 0xf0, 0xeb, 0xda },
+ // the focused grip: no UI accent in solarized.vim; Identifier blue, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:552 Identifier: blue = ~/05-genizah/solarized/README.md:367 blue
+ .box = .{ 0x2f, 0x90, 0xd4 },
+ // derived: the grip at rest, the accent #268bd2 faded toward the unfocused tag #839496 as far as 1.6:1 off it (1.00 of the way)
+ .box_dim = .{ 0x1f, 0x72, 0xac },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:636 DiffChange fg_yellow: yellow = ~/05-genizah/solarized/README.md:362 yellow
+ .box_dirty = .{ 0xb5, 0x89, 0x00 },
+ // derived: the column grip, the original's keyword colour, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:556 Statement fg (Keyword links to Statement): green = ~/05-genizah/solarized/README.md:369 green
+ .column_box = .{ 0x85, 0x99, 0x00 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:556 Statement fg (Keyword links to Statement): green = ~/05-genizah/solarized/README.md:369 green
+ .kw = .{ 0x85, 0x99, 0x00 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:544 Constant fg (String links to Constant): cyan = ~/05-genizah/solarized/README.md:368 cyan
+ .str = .{ 0x2a, 0xa1, 0x98 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:544 Constant fg (Number links to Constant): cyan = ~/05-genizah/solarized/README.md:368 cyan
+ .num = .{ 0x2a, 0xa1, 0x98 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:541 Comment fg: base01 = ~/05-genizah/solarized/README.md:359 base1 (swapped for background=light, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:379-391)
+ .comment = .{ 0x93, 0xa1, 0xa1 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:621 LineNr fg: base01 = ~/05-genizah/solarized/README.md:359 base1 (swapped for background=light, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:379-391)
+ .lineno = .{ 0x93, 0xa1, 0xa1 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:665 PmenuSbar fg_base2 reversed: base2 = ~/05-genizah/solarized/README.md:355 base02 (swapped for background=light, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:379-391)
+ .scroll_track = .{ 0x07, 0x36, 0x42 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:666 PmenuThumb fg_base0 reversed: base0 = ~/05-genizah/solarized/README.md:357 base00 (swapped for background=light, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:379-391)
+ .scroll_thumb = .{ 0x65, 0x7b, 0x83 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:624 VertSplit fg_base00 bg_base00: base00 = ~/05-genizah/solarized/README.md:358 base0 (swapped for background=light, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:379-391)
+ .border = .{ 0x83, 0x94, 0x96 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:618 Search fg_yellow reversed: yellow = ~/05-genizah/solarized/README.md:362 yellow
+ .search_bg = .{ 0xb5, 0x89, 0x00 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:618 Search bg_none reversed: the page: base03 = ~/05-genizah/solarized/README.md:361 base3 (swapped for background=light, ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:379-391)
+ .search_fg = .{ 0xfd, 0xf6, 0xe3 },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:592 Error fg: red = ~/05-genizah/solarized/README.md:364 red
+ .diagnostic_error = .{ 0xdc, 0x32, 0x2f },
+ // ~/05-genizah/solarized/vim-colors-solarized/colors/solarized.vim:630 WarningMsg fg: red = ~/05-genizah/solarized/README.md:364 red
+ .diagnostic_warning = .{ 0xdc, 0x32, 0x2f },
+ .palette = .{
+ .{ 0x07, 0x36, 0x42 }, // 0: ~/05-genizah/solarized/README.md:355 base02 ANSI 0 (README TERMCOL column)
+ .{ 0xdc, 0x32, 0x2f }, // 1: ~/05-genizah/solarized/README.md:364 red ANSI 1 (README TERMCOL column)
+ .{ 0x85, 0x99, 0x00 }, // 2: ~/05-genizah/solarized/README.md:369 green ANSI 2 (README TERMCOL column)
+ .{ 0xb5, 0x89, 0x00 }, // 3: ~/05-genizah/solarized/README.md:362 yellow ANSI 3 (README TERMCOL column)
+ .{ 0x26, 0x8b, 0xd2 }, // 4: ~/05-genizah/solarized/README.md:367 blue ANSI 4 (README TERMCOL column)
+ .{ 0xd3, 0x36, 0x82 }, // 5: ~/05-genizah/solarized/README.md:365 magenta ANSI 5 (README TERMCOL column)
+ .{ 0x2a, 0xa1, 0x98 }, // 6: ~/05-genizah/solarized/README.md:368 cyan ANSI 6 (README TERMCOL column)
+ .{ 0xee, 0xe8, 0xd5 }, // 7: ~/05-genizah/solarized/README.md:360 base2 ANSI 7 (README TERMCOL column)
+ .{ 0x00, 0x2b, 0x36 }, // 8: ~/05-genizah/solarized/README.md:354 base03 ANSI 8 (README TERMCOL column)
+ .{ 0xcb, 0x4b, 0x16 }, // 9: ~/05-genizah/solarized/README.md:363 orange ANSI 9 (README TERMCOL column)
+ .{ 0x58, 0x6e, 0x75 }, // 10: ~/05-genizah/solarized/README.md:356 base01 ANSI 10 (README TERMCOL column)
+ .{ 0x65, 0x7b, 0x83 }, // 11: ~/05-genizah/solarized/README.md:357 base00 ANSI 11 (README TERMCOL column)
+ .{ 0x83, 0x94, 0x96 }, // 12: ~/05-genizah/solarized/README.md:358 base0 ANSI 12 (README TERMCOL column)
+ .{ 0x6c, 0x71, 0xc4 }, // 13: ~/05-genizah/solarized/README.md:366 violet ANSI 13 (README TERMCOL column)
+ .{ 0x93, 0xa1, 0xa1 }, // 14: ~/05-genizah/solarized/README.md:359 base1 ANSI 14 (README TERMCOL column)
+ .{ 0xfd, 0xf6, 0xe3 }, // 15: ~/05-genizah/solarized/README.md:361 base3 ANSI 15 (README TERMCOL column)
+ },
+};
diff --git a/src/themes/tokyobones_dark.zig b/src/themes/tokyobones_dark.zig
new file mode 100644
index 00000000..6a05e456
--- /dev/null
+++ b/src/themes/tokyobones_dark.zig
@@ -0,0 +1,93 @@
+//! Tokyobones (dark): Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #303142 -> #404157: 1.19:1 off the unfocused #242532, pardes needs 1.5:1 on a dark page
+//! - focused grip #51536d -> #5b5d76: 1.33:1 on the focused tag #404157
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/tokyobones/palette.lua + lua/zenbones/specs/dark.lua); values are the
+//! default 'ness' baked into colors/tokyobones.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours.
+//! palette.lua also names bg_warm, orange (in one or both backgrounds) with no
+//! generated literal-hex output; omitted. TabLineSel is guifg/guibg=NONE (bold
+//! only), so it inherits Normal. TabLine links to StatusLine, TabLineFill to
+//! StatusLineNC. Accent is the lualine normal-mode a bg, a muted neutral (the
+//! theme is contrast-based, not hue-accented). Modified = Changed fg.
+pub const theme = .{
+ .name = "tokyobones_dark",
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:33 Normal guibg
+ .bg = .{ 0x1a, 0x1b, 0x26 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:33 Normal guifg
+ .fg = .{ 0xc0, 0xca, 0xf5 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:214 Visual guibg
+ .sel_bg = .{ 0x2c, 0x40, 0x75 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xc0, 0xca, 0xf5 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:198 StatusLineNC guibg
+ .tag_bg = .{ 0x24, 0x25, 0x32 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:198 StatusLineNC guifg
+ .tag_fg = .{ 0xd2, 0xd9, 0xf8 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:195 StatusLine guibg #303142, adjusted
+ .tag_active_bg = .{ 0x40, 0x41, 0x57 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:195 StatusLine guifg
+ .tag_active_fg = .{ 0xc0, 0xca, 0xf5 },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/tokyobones_dark_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0x5b, 0x5d, 0x76 },
+ // derived: the grip at rest, the accent #51536d faded toward the unfocused tag #242532 as far as 1.6:1 off it (0.70 of the way)
+ .box_dim = .{ 0x44, 0x45, 0x5b },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:41 Changed guifg
+ .box_dirty = .{ 0x7b, 0xa2, 0xf7 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:210 Type guifg
+ .column_box = .{ 0x93, 0x94, 0xaa },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:192 Statement guifg (no Keyword group defined; Vim default links Keyword to Statement)
+ .kw = .{ 0xbb, 0x9b, 0xf7 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:201 String guifg
+ .str = .{ 0x75, 0x92, 0xea },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:164 Number guifg
+ .num = .{ 0x2b, 0xc4, 0xde },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:47 Comment guifg
+ .comment = .{ 0x65, 0x67, 0x7d },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:150 LineNr guifg
+ .lineno = .{ 0x5f, 0x61, 0x7e },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:62 CursorLineNr guifg
+ .lineno_active = .{ 0xc0, 0xca, 0xf5 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:167 PmenuSbar guibg
+ .scroll_track = .{ 0x58, 0x5a, 0x76 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:169 PmenuThumb guibg
+ .scroll_thumb = .{ 0x80, 0x82, 0x9b },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:221 WinSeparator guifg
+ .border = .{ 0x5f, 0x61, 0x7e },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:173 Search guibg
+ .search_bg = .{ 0x6e, 0x20, 0xbd },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:173 Search guifg
+ .search_fg = .{ 0xc0, 0xca, 0xf5 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:113 Error guifg via DiagnosticError->Error (link :114)
+ .diagnostic_error = .{ 0xf7, 0x78, 0x90 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:215 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :216)
+ .diagnostic_warning = .{ 0xe1, 0xb0, 0x68 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:72 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x7b, 0xa2, 0xf7 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:67 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0xbb, 0x9b, 0xf7 },
+ .palette = .{
+ .{ 0x1a, 0x1b, 0x26 }, // 0: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:17 g:terminal_color_0
+ .{ 0xf7, 0x78, 0x90 }, // 1: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:18 g:terminal_color_1
+ .{ 0x74, 0xdb, 0xcb }, // 2: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:19 g:terminal_color_2
+ .{ 0xe1, 0xb0, 0x68 }, // 3: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:20 g:terminal_color_3
+ .{ 0x7b, 0xa2, 0xf7 }, // 4: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:21 g:terminal_color_4
+ .{ 0xbb, 0x9b, 0xf7 }, // 5: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:22 g:terminal_color_5
+ .{ 0x2b, 0xc4, 0xde }, // 6: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:23 g:terminal_color_6
+ .{ 0xc0, 0xca, 0xf5 }, // 7: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:24 g:terminal_color_7
+ .{ 0x36, 0x38, 0x4d }, // 8: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:25 g:terminal_color_8
+ .{ 0xf9, 0x8e, 0xa0 }, // 9: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:26 g:terminal_color_9
+ .{ 0x6d, 0xe5, 0xd3 }, // 10: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:27 g:terminal_color_10
+ .{ 0xf2, 0xba, 0x64 }, // 11: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:28 g:terminal_color_11
+ .{ 0x90, 0xaf, 0xfa }, // 12: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:29 g:terminal_color_12
+ .{ 0xc6, 0xac, 0xfa }, // 13: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:30 g:terminal_color_13
+ .{ 0x74, 0xdb, 0xcb }, // 14: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:31 g:terminal_color_14
+ .{ 0x7e, 0x98, 0xeb }, // 15: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:32 g:terminal_color_15
+ },
+};
diff --git a/src/themes/tokyobones_light.zig b/src/themes/tokyobones_light.zig
new file mode 100644
index 00000000..f57287ce
--- /dev/null
+++ b/src/themes/tokyobones_light.zig
@@ -0,0 +1,94 @@
+//! Tokyobones (light): Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #b9bbc3 -> #b1b3bc: 1.16:1 off the unfocused #c8c9cf, pardes needs 1.25:1 on a light page
+//! - unfocused tag text #5f6b9b -> #4a5379: 3.13:1 on #c8c9cf, tag text needs 4.5:1 to be read and run
+//! - focused grip #9b9da9 -> #8f909b: 1.29:1 on the focused tag #b1b3bc
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/tokyobones/palette.lua + lua/zenbones/specs/light.lua); values are the
+//! default 'ness' baked into colors/tokyobones.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours.
+//! palette.lua also names bg_warm, orange (in one or both backgrounds) with no
+//! generated literal-hex output; omitted. TabLineSel is guifg/guibg=NONE (bold
+//! only), so it inherits Normal. TabLine links to StatusLine, TabLineFill to
+//! StatusLineNC. Accent is the lualine normal-mode a bg, a muted neutral (the
+//! theme is contrast-based, not hue-accented). Modified = Changed fg.
+pub const theme = .{
+ .name = "tokyobones_light",
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:271 Normal guibg
+ .bg = .{ 0xd6, 0xd7, 0xdc },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:271 Normal guifg
+ .fg = .{ 0x33, 0x3a, 0x57 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:452 Visual guibg
+ .sel_bg = .{ 0xbb, 0xc0, 0xd8 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x33, 0x3a, 0x57 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:436 StatusLineNC guibg
+ .tag_bg = .{ 0xc8, 0xc9, 0xcf },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:436 StatusLineNC guifg #5f6b9b, adjusted
+ .tag_fg = .{ 0x4a, 0x53, 0x79 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:433 StatusLine guibg #b9bbc3, adjusted
+ .tag_active_bg = .{ 0xb1, 0xb3, 0xbc },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:433 StatusLine guifg
+ .tag_active_fg = .{ 0x33, 0x3a, 0x57 },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/tokyobones_light_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0x8f, 0x90, 0x9b },
+ // derived: the grip at rest, the accent #9b9da9 faded toward the unfocused tag #c8c9cf as far as 1.6:1 off it (0.98 of the way)
+ .box_dim = .{ 0x9c, 0x9e, 0xaa },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:279 Changed guifg
+ .box_dirty = .{ 0x34, 0x54, 0x8c },
+ // derived: the column grip, the original's type colour, ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:448 Type guifg
+ .column_box = .{ 0x48, 0x4f, 0x6b },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:430 Statement guifg (no Keyword group defined; Vim default links Keyword to Statement)
+ .kw = .{ 0x5a, 0x4a, 0x79 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:439 String guifg
+ .str = .{ 0x5b, 0x66, 0x94 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:402 Number guifg
+ .num = .{ 0x17, 0x67, 0x75 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:285 Comment guifg
+ .comment = .{ 0x7c, 0x7e, 0x89 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:388 LineNr guifg
+ .lineno = .{ 0x88, 0x8b, 0x99 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:300 CursorLineNr guifg
+ .lineno_active = .{ 0x33, 0x3a, 0x57 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:405 PmenuSbar guibg
+ .scroll_track = .{ 0x93, 0x95, 0xa2 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:407 PmenuThumb guibg
+ .scroll_thumb = .{ 0xed, 0xee, 0xf0 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:459 WinSeparator guifg
+ .border = .{ 0x88, 0x8b, 0x99 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:411 Search guibg
+ .search_bg = .{ 0xb3, 0xa9, 0xc9 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:411 Search guifg
+ .search_fg = .{ 0x33, 0x3a, 0x57 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:351 Error guifg via DiagnosticError->Error (link :352)
+ .diagnostic_error = .{ 0x8b, 0x43, 0x51 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:453 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :454)
+ .diagnostic_warning = .{ 0x8f, 0x5e, 0x14 },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:310 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x34, 0x54, 0x8c },
+ // ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:305 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0x5a, 0x4a, 0x79 },
+ .palette = .{
+ .{ 0xd6, 0xd7, 0xdc }, // 0: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:255 g:terminal_color_0
+ .{ 0x8b, 0x43, 0x51 }, // 1: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:256 g:terminal_color_1
+ .{ 0x34, 0x64, 0x5d }, // 2: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:257 g:terminal_color_2
+ .{ 0x8f, 0x5e, 0x14 }, // 3: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:258 g:terminal_color_3
+ .{ 0x34, 0x54, 0x8c }, // 4: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:259 g:terminal_color_4
+ .{ 0x5a, 0x4a, 0x79 }, // 5: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:260 g:terminal_color_5
+ .{ 0x17, 0x67, 0x75 }, // 6: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:261 g:terminal_color_6
+ .{ 0x33, 0x3a, 0x57 }, // 7: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:262 g:terminal_color_7
+ .{ 0xad, 0xb0, 0xbd }, // 8: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:263 g:terminal_color_8
+ .{ 0x7e, 0x32, 0x42 }, // 9: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:264 g:terminal_color_9
+ .{ 0x26, 0x55, 0x4f }, // 10: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:265 g:terminal_color_10
+ .{ 0x79, 0x4e, 0x0d }, // 11: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:266 g:terminal_color_11
+ .{ 0x26, 0x46, 0x7a }, // 12: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:267 g:terminal_color_12
+ .{ 0x50, 0x38, 0x75 }, // 13: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:268 g:terminal_color_13
+ .{ 0x34, 0x64, 0x5d }, // 14: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:269 g:terminal_color_14
+ .{ 0x56, 0x61, 0x8d }, // 15: ~/05-genizah/zenbones.nvim/colors/tokyobones.vim:270 g:terminal_color_15
+ },
+};
diff --git a/src/themes/tokyonight_day.zig b/src/themes/tokyonight_day.zig
new file mode 100644
index 00000000..3d188454
--- /dev/null
+++ b/src/themes/tokyonight_day.zig
@@ -0,0 +1,100 @@
+//! Tokyo Night Day: Tokyo Night, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/folke/tokyonight.nvim; genizah ~/05-genizah/tokyonight.nvim; commit cdc07ac78467a233fd62c493de29a17e0cf2b2b6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #d0d5e3 -> #b5bdd7: 1.00:1 off the unfocused #d0d5e3, pardes needs 1.25:1 on a light page
+//! - unfocused tag text #a8aecb -> #575a6a: 1.49:1 on #d0d5e3, tag text needs 4.5:1 to be read and run
+//! - focused tag text #6172b0 -> #404b74: 2.47:1 on #b5bdd7, tag text needs 4.5:1
+//! - scroll thumb #a8aecb -> #9fa8ca: 1.41:1 on its track #c8cfe1
+//! Upstream notes: Values from upstream's generated extras (literal hex):
+//! highlight groups from extras/vim/colors, palette and terminal colours from
+//! extras/lua (the resolved colors table, which includes computed entries such
+//! as bg_statusline, bg_visual, diff.*; these match lua/tokyonight/colors/*.lua
+//! for the base names). extras/vim omits linked groups: Number and Boolean are
+//! not defined by the theme at all, so Neovim's built-in links to Constant
+//! apply. Visual has no fg (selection_fg null). WinSeparator fg is used for
+//! split. modified = diffChanged fg (git.change). accent = lualine normal a bg
+//! (colors.blue). Palette skips 'none' and the 'rainbow' list (duplicates of
+//! named colours); terminal.* omitted from palette since it is the ansi list.
+//! ansi matches extras/kitty/tokyonight_day.conf color0-15. Day palette is
+//! computed at run time by Util.invert() of the night palette plus blended
+//! bg_dark/bg_dark1 (lua/tokyonight/colors/day.lua); all values here come from
+//! the generated extras for day.
+pub const theme = .{
+ .name = "tokyonight_day",
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:193 Normal guibg
+ .bg = .{ 0xe1, 0xe2, 0xe7 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:193 Normal guifg
+ .fg = .{ 0x37, 0x60, 0xbf },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:245 Visual guibg
+ .sel_bg = .{ 0xb7, 0xc1, 0xe3 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x37, 0x60, 0xbf },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:220 StatusLineNC guibg
+ .tag_bg = .{ 0xd0, 0xd5, 0xe3 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:220 StatusLineNC guifg #a8aecb, adjusted
+ .tag_fg = .{ 0x57, 0x5a, 0x6a },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:219 StatusLine guibg #d0d5e3, adjusted
+ .tag_active_bg = .{ 0xb5, 0xbd, 0xd7 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:219 StatusLine guifg #6172b0, adjusted
+ .tag_active_fg = .{ 0x40, 0x4b, 0x74 },
+ // the focused grip: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:15 blue; lualine normal-mode section a bg = colors.blue per ~/05-genizah/tokyonight.nvim/lua/lualine/themes/_tokyonight.lua:12 (tokyonight-day.lua calls it with style "day")
+ .box = .{ 0x2e, 0x7d, 0xe9 },
+ // derived: the grip at rest, the accent #2e7de9 faded toward the unfocused tag #d0d5e3 as far as 1.6:1 off it (0.50 of the way)
+ .box_dim = .{ 0x7f, 0xa9, 0xe6 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:254 diffChanged guifg (= palette git.change)
+ .box_dirty = .{ 0x50, 0x6d, 0x9c },
+ // derived: the column grip, the original's type colour, ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:228 Type guifg
+ .column_box = .{ 0x18, 0x80, 0x92 },
+ // ~/05-genizah/tokyonight.nvim/lua/tokyonight/groups/treesitter.lua:36 ["@keyword"] = c.purple, ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:55 purple (vim's regex Keyword is #007197)
+ .kw = .{ 0x78, 0x47, 0xbd },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:221 String guifg
+ .str = .{ 0x58, 0x75, 0x39 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:27 Constant guifg (Number is not defined by the theme, so Neovim's default link Number->Constant applies)
+ .num = .{ 0xb1, 0x5c, 0x00 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:24 Comment guifg
+ .comment = .{ 0x84, 0x8c, 0xb5 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:91 LineNr guifg
+ .lineno = .{ 0xa8, 0xae, 0xcb },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:34 CursorLineNr guifg
+ .lineno_active = .{ 0xb1, 0x5c, 0x00 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:201 PmenuSbar guibg
+ .scroll_track = .{ 0xc8, 0xcf, 0xe1 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:203 PmenuThumb guibg #a8aecb, adjusted
+ .scroll_thumb = .{ 0x9f, 0xa8, 0xca },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:250 WinSeparator guifg
+ .border = .{ 0xb4, 0xb5, 0xb9 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:207 Search guibg
+ .search_bg = .{ 0x78, 0x90, 0xdd },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:207 Search guifg
+ .search_fg = .{ 0x37, 0x60, 0xbf },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:36 DiagnosticError guifg
+ .diagnostic_error = .{ 0xc6, 0x43, 0x43 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:48 DiagnosticWarn guifg
+ .diagnostic_warning = .{ 0x8c, 0x6c, 0x3e },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:38 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x07, 0x87, 0x9d },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-day.vim:37 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0x11, 0x8c, 0x74 },
+ .palette = .{
+ .{ 0xb4, 0xb5, 0xb9 }, // 0: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:61 terminal.black (vim.g.terminal_color_0 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xf5, 0x2a, 0x65 }, // 1: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:71 terminal.red (vim.g.terminal_color_1 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x58, 0x75, 0x39 }, // 2: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:67 terminal.green (vim.g.terminal_color_2 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x8c, 0x6c, 0x3e }, // 3: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:75 terminal.yellow (vim.g.terminal_color_3 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x2e, 0x7d, 0xe9 }, // 4: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:63 terminal.blue (vim.g.terminal_color_4 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x98, 0x54, 0xf1 }, // 5: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:69 terminal.magenta (vim.g.terminal_color_5 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x00, 0x71, 0x97 }, // 6: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:65 terminal.cyan (vim.g.terminal_color_6 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x61, 0x72, 0xb0 }, // 7: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:73 terminal.white (vim.g.terminal_color_7 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xa1, 0xa6, 0xc5 }, // 8: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:62 terminal.black_bright (vim.g.terminal_color_8 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xff, 0x47, 0x74 }, // 9: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:72 terminal.red_bright (vim.g.terminal_color_9 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x5c, 0x85, 0x24 }, // 10: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:68 terminal.green_bright (vim.g.terminal_color_10 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xa2, 0x76, 0x29 }, // 11: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:76 terminal.yellow_bright (vim.g.terminal_color_11 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x35, 0x8a, 0xff }, // 12: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:64 terminal.blue_bright (vim.g.terminal_color_12 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xa4, 0x63, 0xff }, // 13: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:70 terminal.magenta_bright (vim.g.terminal_color_13 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x00, 0x7e, 0xa8 }, // 14: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:66 terminal.cyan_bright (vim.g.terminal_color_14 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x37, 0x60, 0xbf }, // 15: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_day.lua:74 terminal.white_bright (vim.g.terminal_color_15 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ },
+};
diff --git a/src/themes/tokyonight_moon.zig b/src/themes/tokyonight_moon.zig
new file mode 100644
index 00000000..04c1bccc
--- /dev/null
+++ b/src/themes/tokyonight_moon.zig
@@ -0,0 +1,99 @@
+//! Tokyo Night Moon: Tokyo Night, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/folke/tokyonight.nvim; genizah ~/05-genizah/tokyonight.nvim; commit cdc07ac78467a233fd62c493de29a17e0cf2b2b6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #1e2030 -> #3a3e56: 1.00:1 off the unfocused #1e2030, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #3b4261 -> #858a9d: 1.64:1 on #1e2030, tag text needs 4.5:1 to be read and run
+//! - focused tag text #828bb8 -> #a3a9ca: 3.16:1 on #3a3e56, tag text needs 4.5:1
+//! - rules between panes #1b1d2b -> #414557: 1.09:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! - scroll thumb #3b4261 -> #3e4564: 1.46:1 on its track #27293a
+//! Upstream notes: Values from upstream's generated extras (literal hex):
+//! highlight groups from extras/vim/colors, palette and terminal colours from
+//! extras/lua (the resolved colors table, which includes computed entries such
+//! as bg_statusline, bg_visual, diff.*; these match lua/tokyonight/colors/*.lua
+//! for the base names). extras/vim omits linked groups: Number and Boolean are
+//! not defined by the theme at all, so Neovim's built-in links to Constant
+//! apply. Visual has no fg (selection_fg null). WinSeparator fg is used for
+//! split. modified = diffChanged fg (git.change). accent = lualine normal a bg
+//! (colors.blue). Palette skips 'none' and the 'rainbow' list (duplicates of
+//! named colours); terminal.* omitted from palette since it is the ansi list.
+//! ansi matches extras/kitty/tokyonight_moon.conf color0-15. Lowercased from
+//! upstream uppercase hex: bg_dark1.
+pub const theme = .{
+ .name = "tokyonight_moon",
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:193 Normal guibg
+ .bg = .{ 0x22, 0x24, 0x36 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:193 Normal guifg
+ .fg = .{ 0xc8, 0xd3, 0xf5 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:245 Visual guibg
+ .sel_bg = .{ 0x2d, 0x3f, 0x76 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xc8, 0xd3, 0xf5 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:220 StatusLineNC guibg
+ .tag_bg = .{ 0x1e, 0x20, 0x30 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:220 StatusLineNC guifg #3b4261, adjusted
+ .tag_fg = .{ 0x85, 0x8a, 0x9d },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:219 StatusLine guibg #1e2030, adjusted
+ .tag_active_bg = .{ 0x3a, 0x3e, 0x56 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:219 StatusLine guifg #828bb8, adjusted
+ .tag_active_fg = .{ 0xa3, 0xa9, 0xca },
+ // the focused grip: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:15 blue; lualine normal-mode section a bg = colors.blue per ~/05-genizah/tokyonight.nvim/lua/lualine/themes/_tokyonight.lua:12 (tokyonight-moon.lua calls it with style "moon")
+ .box = .{ 0x82, 0xaa, 0xff },
+ // derived: the grip at rest, the accent #82aaff faded toward the unfocused tag #1e2030 as far as 1.6:1 off it (0.26 of the way)
+ .box_dim = .{ 0x38, 0x44, 0x66 },
+ // an unsaved grip: the original's warning colour, ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:48 DiagnosticWarn guifg
+ .box_dirty = .{ 0xff, 0xc7, 0x77 },
+ // derived: the column grip, the original's string colour, ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:221 String guifg
+ .column_box = .{ 0xc3, 0xe8, 0x8d },
+ // ~/05-genizah/tokyonight.nvim/lua/tokyonight/groups/treesitter.lua:36 ["@keyword"] = c.purple, ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:55 purple (vim's regex Keyword is #86e1fc)
+ .kw = .{ 0xfc, 0xa7, 0xea },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:221 String guifg
+ .str = .{ 0xc3, 0xe8, 0x8d },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:27 Constant guifg (Number is not defined by the theme, so Neovim's default link Number->Constant applies)
+ .num = .{ 0xff, 0x96, 0x6c },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:24 Comment guifg
+ .comment = .{ 0x63, 0x6d, 0xa6 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:91 LineNr guifg
+ .lineno = .{ 0x3b, 0x42, 0x61 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:34 CursorLineNr guifg
+ .lineno_active = .{ 0xff, 0x96, 0x6c },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:201 PmenuSbar guibg
+ .scroll_track = .{ 0x27, 0x29, 0x3a },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:203 PmenuThumb guibg #3b4261, adjusted
+ .scroll_thumb = .{ 0x3e, 0x45, 0x64 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:250 WinSeparator guifg #1b1d2b, adjusted
+ .border = .{ 0x41, 0x45, 0x57 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:207 Search guibg
+ .search_bg = .{ 0x3e, 0x68, 0xd7 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:207 Search guifg
+ .search_fg = .{ 0xc8, 0xd3, 0xf5 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:36 DiagnosticError guifg
+ .diagnostic_error = .{ 0xc5, 0x3b, 0x53 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:48 DiagnosticWarn guifg
+ .diagnostic_warning = .{ 0xff, 0xc7, 0x77 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:38 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x0d, 0xb9, 0xd7 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-moon.vim:37 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0x4f, 0xd6, 0xbe },
+ .palette = .{
+ .{ 0x1b, 0x1d, 0x2b }, // 0: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:61 terminal.black (vim.g.terminal_color_0 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xff, 0x75, 0x7f }, // 1: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:71 terminal.red (vim.g.terminal_color_1 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xc3, 0xe8, 0x8d }, // 2: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:67 terminal.green (vim.g.terminal_color_2 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xff, 0xc7, 0x77 }, // 3: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:75 terminal.yellow (vim.g.terminal_color_3 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x82, 0xaa, 0xff }, // 4: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:63 terminal.blue (vim.g.terminal_color_4 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xc0, 0x99, 0xff }, // 5: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:69 terminal.magenta (vim.g.terminal_color_5 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x86, 0xe1, 0xfc }, // 6: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:65 terminal.cyan (vim.g.terminal_color_6 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x82, 0x8b, 0xb8 }, // 7: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:73 terminal.white (vim.g.terminal_color_7 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x44, 0x4a, 0x73 }, // 8: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:62 terminal.black_bright (vim.g.terminal_color_8 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xff, 0x8d, 0x94 }, // 9: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:72 terminal.red_bright (vim.g.terminal_color_9 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xc7, 0xfb, 0x6d }, // 10: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:68 terminal.green_bright (vim.g.terminal_color_10 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xff, 0xd8, 0xab }, // 11: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:76 terminal.yellow_bright (vim.g.terminal_color_11 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x9a, 0xb8, 0xff }, // 12: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:64 terminal.blue_bright (vim.g.terminal_color_12 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xca, 0xab, 0xff }, // 13: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:70 terminal.magenta_bright (vim.g.terminal_color_13 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xb2, 0xeb, 0xff }, // 14: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:66 terminal.cyan_bright (vim.g.terminal_color_14 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xc8, 0xd3, 0xf5 }, // 15: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_moon.lua:74 terminal.white_bright (vim.g.terminal_color_15 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ },
+};
diff --git a/src/themes/tokyonight_night.zig b/src/themes/tokyonight_night.zig
new file mode 100644
index 00000000..eeb7e5a5
--- /dev/null
+++ b/src/themes/tokyonight_night.zig
@@ -0,0 +1,97 @@
+//! Tokyo Night: Tokyo Night, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/folke/tokyonight.nvim; genizah ~/05-genizah/tokyonight.nvim; commit cdc07ac78467a233fd62c493de29a17e0cf2b2b6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #16161e -> #363846: 1.00:1 off the unfocused #16161e, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #3b4261 -> #7e8297: 1.83:1 on #16161e, tag text needs 4.5:1 to be read and run
+//! - rules between panes #15161e -> #3b3e4d: 1.05:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! Upstream notes: Values from upstream's generated extras (literal hex):
+//! highlight groups from extras/vim/colors, palette and terminal colours from
+//! extras/lua (the resolved colors table, which includes computed entries such
+//! as bg_statusline, bg_visual, diff.*; these match lua/tokyonight/colors/*.lua
+//! for the base names). extras/vim omits linked groups: Number and Boolean are
+//! not defined by the theme at all, so Neovim's built-in links to Constant
+//! apply. Visual has no fg (selection_fg null). WinSeparator fg is used for
+//! split. modified = diffChanged fg (git.change). accent = lualine normal a bg
+//! (colors.blue). Palette skips 'none' and the 'rainbow' list (duplicates of
+//! named colours); terminal.* omitted from palette since it is the ansi list.
+//! ansi matches extras/kitty/tokyonight_night.conf color0-15. Lowercased from
+//! upstream uppercase hex: bg_dark1.
+pub const theme = .{
+ .name = "tokyonight_night",
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:193 Normal guibg
+ .bg = .{ 0x1a, 0x1b, 0x26 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:193 Normal guifg
+ .fg = .{ 0xc0, 0xca, 0xf5 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:245 Visual guibg
+ .sel_bg = .{ 0x28, 0x34, 0x57 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xc0, 0xca, 0xf5 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:220 StatusLineNC guibg
+ .tag_bg = .{ 0x16, 0x16, 0x1e },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:220 StatusLineNC guifg #3b4261, adjusted
+ .tag_fg = .{ 0x7e, 0x82, 0x97 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:219 StatusLine guibg #16161e, adjusted
+ .tag_active_bg = .{ 0x36, 0x38, 0x46 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:219 StatusLine guifg
+ .tag_active_fg = .{ 0xa9, 0xb1, 0xd6 },
+ // the focused grip: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:15 blue; lualine normal-mode section a bg = colors.blue per ~/05-genizah/tokyonight.nvim/lua/lualine/themes/_tokyonight.lua:12 (tokyonight-night.lua calls it with style "night")
+ .box = .{ 0x7a, 0xa2, 0xf7 },
+ // derived: the grip at rest, the accent #7aa2f7 faded toward the unfocused tag #16161e as far as 1.6:1 off it (0.28 of the way)
+ .box_dim = .{ 0x32, 0x3d, 0x5b },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:254 diffChanged guifg (= palette git.change)
+ .box_dirty = .{ 0x61, 0x83, 0xbb },
+ // derived: the column grip, the original's type colour, ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:228 Type guifg
+ .column_box = .{ 0x2a, 0xc3, 0xde },
+ // ~/05-genizah/tokyonight.nvim/lua/tokyonight/groups/treesitter.lua:36 ["@keyword"] = c.purple, ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:55 purple (vim's regex Keyword is #7dcfff)
+ .kw = .{ 0x9d, 0x7c, 0xd8 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:221 String guifg
+ .str = .{ 0x9e, 0xce, 0x6a },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:27 Constant guifg (Number is not defined by the theme, so Neovim's default link Number->Constant applies)
+ .num = .{ 0xff, 0x9e, 0x64 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:24 Comment guifg
+ .comment = .{ 0x56, 0x5f, 0x89 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:91 LineNr guifg
+ .lineno = .{ 0x3b, 0x42, 0x61 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:34 CursorLineNr guifg
+ .lineno_active = .{ 0xff, 0x9e, 0x64 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:201 PmenuSbar guibg
+ .scroll_track = .{ 0x1f, 0x1f, 0x29 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:203 PmenuThumb guibg
+ .scroll_thumb = .{ 0x3b, 0x42, 0x61 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:250 WinSeparator guifg #15161e, adjusted
+ .border = .{ 0x3b, 0x3e, 0x4d },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:207 Search guibg
+ .search_bg = .{ 0x3d, 0x59, 0xa1 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:207 Search guifg
+ .search_fg = .{ 0xc0, 0xca, 0xf5 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:36 DiagnosticError guifg
+ .diagnostic_error = .{ 0xdb, 0x4b, 0x4b },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:48 DiagnosticWarn guifg
+ .diagnostic_warning = .{ 0xe0, 0xaf, 0x68 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:38 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x0d, 0xb9, 0xd7 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-night.vim:37 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0x1a, 0xbc, 0x9c },
+ .palette = .{
+ .{ 0x15, 0x16, 0x1e }, // 0: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:61 terminal.black (vim.g.terminal_color_0 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xf7, 0x76, 0x8e }, // 1: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:71 terminal.red (vim.g.terminal_color_1 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x9e, 0xce, 0x6a }, // 2: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:67 terminal.green (vim.g.terminal_color_2 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xe0, 0xaf, 0x68 }, // 3: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:75 terminal.yellow (vim.g.terminal_color_3 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x7a, 0xa2, 0xf7 }, // 4: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:63 terminal.blue (vim.g.terminal_color_4 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xbb, 0x9a, 0xf7 }, // 5: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:69 terminal.magenta (vim.g.terminal_color_5 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x7d, 0xcf, 0xff }, // 6: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:65 terminal.cyan (vim.g.terminal_color_6 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xa9, 0xb1, 0xd6 }, // 7: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:73 terminal.white (vim.g.terminal_color_7 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x41, 0x48, 0x68 }, // 8: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:62 terminal.black_bright (vim.g.terminal_color_8 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xff, 0x89, 0x9d }, // 9: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:72 terminal.red_bright (vim.g.terminal_color_9 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x9f, 0xe0, 0x44 }, // 10: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:68 terminal.green_bright (vim.g.terminal_color_10 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xfa, 0xba, 0x4a }, // 11: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:76 terminal.yellow_bright (vim.g.terminal_color_11 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x8d, 0xb0, 0xff }, // 12: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:64 terminal.blue_bright (vim.g.terminal_color_12 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xc7, 0xa9, 0xff }, // 13: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:70 terminal.magenta_bright (vim.g.terminal_color_13 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xa4, 0xda, 0xff }, // 14: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:66 terminal.cyan_bright (vim.g.terminal_color_14 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xc0, 0xca, 0xf5 }, // 15: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_night.lua:74 terminal.white_bright (vim.g.terminal_color_15 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ },
+};
diff --git a/src/themes/tokyonight_storm.zig b/src/themes/tokyonight_storm.zig
new file mode 100644
index 00000000..5fadf177
--- /dev/null
+++ b/src/themes/tokyonight_storm.zig
@@ -0,0 +1,97 @@
+//! Tokyo Night Storm: Tokyo Night, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/folke/tokyonight.nvim; genizah ~/05-genizah/tokyonight.nvim; commit cdc07ac78467a233fd62c493de29a17e0cf2b2b6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #1f2335 -> #3d4258: 1.00:1 off the unfocused #1f2335, pardes needs 1.5:1 on a dark page
+//! - unfocused tag text #3b4261 -> #858a9d: 1.58:1 on #1f2335, tag text needs 4.5:1 to be read and run
+//! - rules between panes #1d202f -> #44495f: 1.11:1 off the page, lifted toward the text to 1.6:1 (as colors.zig separatorOf does)
+//! - scroll thumb #3b4261 -> #404767: 1.42:1 on its track #272b3f
+//! Upstream notes: Values from upstream's generated extras (literal hex):
+//! highlight groups from extras/vim/colors, palette and terminal colours from
+//! extras/lua (the resolved colors table, which includes computed entries such
+//! as bg_statusline, bg_visual, diff.*; these match lua/tokyonight/colors/*.lua
+//! for the base names). extras/vim omits linked groups: Number and Boolean are
+//! not defined by the theme at all, so Neovim's built-in links to Constant
+//! apply. Visual has no fg (selection_fg null). WinSeparator fg is used for
+//! split. modified = diffChanged fg (git.change). accent = lualine normal a bg
+//! (colors.blue). Palette skips 'none' and the 'rainbow' list (duplicates of
+//! named colours); terminal.* omitted from palette since it is the ansi list.
+//! ansi matches extras/kitty/tokyonight_storm.conf color0-15.
+pub const theme = .{
+ .name = "tokyonight_storm",
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:193 Normal guibg
+ .bg = .{ 0x24, 0x28, 0x3b },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:193 Normal guifg
+ .fg = .{ 0xc0, 0xca, 0xf5 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:245 Visual guibg
+ .sel_bg = .{ 0x2e, 0x3c, 0x64 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xc0, 0xca, 0xf5 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:220 StatusLineNC guibg
+ .tag_bg = .{ 0x1f, 0x23, 0x35 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:220 StatusLineNC guifg #3b4261, adjusted
+ .tag_fg = .{ 0x85, 0x8a, 0x9d },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:219 StatusLine guibg #1f2335, adjusted
+ .tag_active_bg = .{ 0x3d, 0x42, 0x58 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:219 StatusLine guifg
+ .tag_active_fg = .{ 0xa9, 0xb1, 0xd6 },
+ // the focused grip: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:15 blue; lualine normal-mode section a bg = colors.blue per ~/05-genizah/tokyonight.nvim/lua/lualine/themes/_tokyonight.lua:12 (tokyonight-storm.lua calls it with style "storm")
+ .box = .{ 0x7a, 0xa2, 0xf7 },
+ // derived: the grip at rest, the accent #7aa2f7 faded toward the unfocused tag #1f2335 as far as 1.6:1 off it (0.26 of the way)
+ .box_dim = .{ 0x37, 0x44, 0x67 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:254 diffChanged guifg (= palette git.change)
+ .box_dirty = .{ 0x61, 0x83, 0xbb },
+ // derived: the column grip, the original's type colour, ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:228 Type guifg
+ .column_box = .{ 0x2a, 0xc3, 0xde },
+ // ~/05-genizah/tokyonight.nvim/lua/tokyonight/groups/treesitter.lua:36 ["@keyword"] = c.purple, ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:55 purple (vim's regex Keyword is #7dcfff)
+ .kw = .{ 0x9d, 0x7c, 0xd8 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:221 String guifg
+ .str = .{ 0x9e, 0xce, 0x6a },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:27 Constant guifg (Number is not defined by the theme, so Neovim's default link Number->Constant applies)
+ .num = .{ 0xff, 0x9e, 0x64 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:24 Comment guifg
+ .comment = .{ 0x56, 0x5f, 0x89 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:91 LineNr guifg
+ .lineno = .{ 0x3b, 0x42, 0x61 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:34 CursorLineNr guifg
+ .lineno_active = .{ 0xff, 0x9e, 0x64 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:201 PmenuSbar guibg
+ .scroll_track = .{ 0x27, 0x2b, 0x3f },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:203 PmenuThumb guibg #3b4261, adjusted
+ .scroll_thumb = .{ 0x40, 0x47, 0x67 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:250 WinSeparator guifg #1d202f, adjusted
+ .border = .{ 0x44, 0x49, 0x5f },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:207 Search guibg
+ .search_bg = .{ 0x3d, 0x59, 0xa1 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:207 Search guifg
+ .search_fg = .{ 0xc0, 0xca, 0xf5 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:36 DiagnosticError guifg
+ .diagnostic_error = .{ 0xdb, 0x4b, 0x4b },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:48 DiagnosticWarn guifg
+ .diagnostic_warning = .{ 0xe0, 0xaf, 0x68 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:38 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x0d, 0xb9, 0xd7 },
+ // ~/05-genizah/tokyonight.nvim/extras/vim/colors/tokyonight-storm.vim:37 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0x1a, 0xbc, 0x9c },
+ .palette = .{
+ .{ 0x1d, 0x20, 0x2f }, // 0: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:61 terminal.black (vim.g.terminal_color_0 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xf7, 0x76, 0x8e }, // 1: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:71 terminal.red (vim.g.terminal_color_1 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x9e, 0xce, 0x6a }, // 2: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:67 terminal.green (vim.g.terminal_color_2 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xe0, 0xaf, 0x68 }, // 3: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:75 terminal.yellow (vim.g.terminal_color_3 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x7a, 0xa2, 0xf7 }, // 4: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:63 terminal.blue (vim.g.terminal_color_4 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xbb, 0x9a, 0xf7 }, // 5: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:69 terminal.magenta (vim.g.terminal_color_5 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x7d, 0xcf, 0xff }, // 6: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:65 terminal.cyan (vim.g.terminal_color_6 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xa9, 0xb1, 0xd6 }, // 7: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:73 terminal.white (vim.g.terminal_color_7 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x41, 0x48, 0x68 }, // 8: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:62 terminal.black_bright (vim.g.terminal_color_8 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xff, 0x89, 0x9d }, // 9: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:72 terminal.red_bright (vim.g.terminal_color_9 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x9f, 0xe0, 0x44 }, // 10: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:68 terminal.green_bright (vim.g.terminal_color_10 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xfa, 0xba, 0x4a }, // 11: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:76 terminal.yellow_bright (vim.g.terminal_color_11 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0x8d, 0xb0, 0xff }, // 12: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:64 terminal.blue_bright (vim.g.terminal_color_12 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xc7, 0xa9, 0xff }, // 13: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:70 terminal.magenta_bright (vim.g.terminal_color_13 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xa4, 0xda, 0xff }, // 14: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:66 terminal.cyan_bright (vim.g.terminal_color_14 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ .{ 0xc0, 0xca, 0xf5 }, // 15: ~/05-genizah/tokyonight.nvim/extras/lua/tokyonight_storm.lua:74 terminal.white_bright (vim.g.terminal_color_15 per ~/05-genizah/tokyonight.nvim/lua/tokyonight/theme.lua:33-57)
+ },
+};
diff --git a/src/themes/vimbones.zig b/src/themes/vimbones.zig
new file mode 100644
index 00000000..1823c40d
--- /dev/null
+++ b/src/themes/vimbones.zig
@@ -0,0 +1,95 @@
+//! Vimbones: Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #d1d1b0 -> #c7c7a8: 1.15:1 off the unfocused #dfdfbc, pardes needs 1.25:1 on a light page
+//! - unfocused tag text #686868 -> #606060: 4.09:1 on #dfdfbc, tag text needs 4.5:1 to be read and run
+//! - focused grip #b0b093 -> #a2a287: 1.28:1 on the focused tag #c7c7a8
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/vimbones/palette.lua + lua/zenbones/specs/light.lua); values are the
+//! default 'ness' baked into colors/vimbones.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours. Light-only
+//! flavour (zenbones.json background='light'). palette.lua also names vim (in
+//! one or both backgrounds) with no generated literal-hex output; omitted.
+//! TabLineSel is guifg/guibg=NONE (bold only), so it inherits Normal. TabLine
+//! links to StatusLine, TabLineFill to StatusLineNC. Accent is the lualine
+//! normal-mode a bg, a muted neutral (the theme is contrast-based, not
+//! hue-accented). Modified = Changed fg.
+pub const theme = .{
+ .name = "vimbones",
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:34 Normal guibg
+ .bg = .{ 0xf0, 0xf0, 0xca },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:34 Normal guifg
+ .fg = .{ 0x35, 0x35, 0x35 },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:212 Visual guibg
+ .sel_bg = .{ 0xd7, 0xd7, 0xd7 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x35, 0x35, 0x35 },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:199 StatusLineNC guibg
+ .tag_bg = .{ 0xdf, 0xdf, 0xbc },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:199 StatusLineNC guifg #686868, adjusted
+ .tag_fg = .{ 0x60, 0x60, 0x60 },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:196 StatusLine guibg #d1d1b0, adjusted
+ .tag_active_bg = .{ 0xc7, 0xc7, 0xa8 },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:196 StatusLine guifg
+ .tag_active_fg = .{ 0x35, 0x35, 0x35 },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/vimbones_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0xa2, 0xa2, 0x87 },
+ // derived: the grip at rest, the accent #b0b093 faded toward the unfocused tag #dfdfbc as far as 1.6:1 off it (1.00 of the way)
+ .box_dim = .{ 0xb0, 0xb0, 0x93 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/vimbones.vim:42 Changed guifg
+ .box_dirty = .{ 0x28, 0x64, 0x86 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/zenbones.nvim/colors/vimbones.vim:208 Type guifg
+ .column_box = .{ 0x5b, 0x5b, 0x42 },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:193 Statement guifg (no Keyword group defined; Vim default links Keyword to Statement)
+ .kw = .{ 0x15, 0x6a, 0x29 },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:202 String guifg
+ .str = .{ 0x63, 0x63, 0x63 },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:165 Number guifg
+ .num = .{ 0x2a, 0x65, 0x35 },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:48 Comment guifg
+ .comment = .{ 0x8c, 0x8c, 0x7c },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:151 LineNr guifg
+ .lineno = .{ 0x9a, 0x9a, 0x81 },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:63 CursorLineNr guifg
+ .lineno_active = .{ 0x35, 0x35, 0x35 },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:168 PmenuSbar guibg
+ .scroll_track = .{ 0xa7, 0xa7, 0x8d },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:170 PmenuThumb guibg
+ .scroll_thumb = .{ 0xf9, 0xf9, 0xd2 },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:219 WinSeparator guifg
+ .border = .{ 0x9a, 0x9a, 0x81 },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:174 Search guibg
+ .search_bg = .{ 0xde, 0xb9, 0xd6 },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:174 Search guifg
+ .search_fg = .{ 0x35, 0x35, 0x35 },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:114 Error guifg via DiagnosticError->Error (link :115)
+ .diagnostic_error = .{ 0xa8, 0x33, 0x4c },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:213 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :214)
+ .diagnostic_warning = .{ 0x94, 0x49, 0x27 },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:73 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x28, 0x64, 0x86 },
+ // ~/05-genizah/zenbones.nvim/colors/vimbones.vim:68 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0x88, 0x50, 0x7d },
+ .palette = .{
+ .{ 0xf0, 0xf0, 0xca }, // 0: ~/05-genizah/zenbones.nvim/colors/vimbones.vim:18 g:terminal_color_0
+ .{ 0xa8, 0x33, 0x4c }, // 1: ~/05-genizah/zenbones.nvim/colors/vimbones.vim:19 g:terminal_color_1
+ .{ 0x4f, 0x6c, 0x31 }, // 2: ~/05-genizah/zenbones.nvim/colors/vimbones.vim:20 g:terminal_color_2
+ .{ 0x94, 0x49, 0x27 }, // 3: ~/05-genizah/zenbones.nvim/colors/vimbones.vim:21 g:terminal_color_3
+ .{ 0x28, 0x64, 0x86 }, // 4: ~/05-genizah/zenbones.nvim/colors/vimbones.vim:22 g:terminal_color_4
+ .{ 0x88, 0x50, 0x7d }, // 5: ~/05-genizah/zenbones.nvim/colors/vimbones.vim:23 g:terminal_color_5
+ .{ 0x3b, 0x89, 0x92 }, // 6: ~/05-genizah/zenbones.nvim/colors/vimbones.vim:24 g:terminal_color_6
+ .{ 0x35, 0x35, 0x35 }, // 7: ~/05-genizah/zenbones.nvim/colors/vimbones.vim:25 g:terminal_color_7
+ .{ 0xc6, 0xc6, 0xa3 }, // 8: ~/05-genizah/zenbones.nvim/colors/vimbones.vim:26 g:terminal_color_8
+ .{ 0x94, 0x25, 0x3e }, // 9: ~/05-genizah/zenbones.nvim/colors/vimbones.vim:27 g:terminal_color_9
+ .{ 0x3f, 0x5a, 0x22 }, // 10: ~/05-genizah/zenbones.nvim/colors/vimbones.vim:28 g:terminal_color_10
+ .{ 0x80, 0x3d, 0x1c }, // 11: ~/05-genizah/zenbones.nvim/colors/vimbones.vim:29 g:terminal_color_11
+ .{ 0x1d, 0x55, 0x73 }, // 12: ~/05-genizah/zenbones.nvim/colors/vimbones.vim:30 g:terminal_color_12
+ .{ 0x7b, 0x3b, 0x70 }, // 13: ~/05-genizah/zenbones.nvim/colors/vimbones.vim:31 g:terminal_color_13
+ .{ 0x2b, 0x74, 0x7c }, // 14: ~/05-genizah/zenbones.nvim/colors/vimbones.vim:32 g:terminal_color_14
+ .{ 0x5c, 0x5c, 0x5c }, // 15: ~/05-genizah/zenbones.nvim/colors/vimbones.vim:33 g:terminal_color_15
+ },
+};
diff --git a/src/themes/zenbones_dark.zig b/src/themes/zenbones_dark.zig
new file mode 100644
index 00000000..89f5a934
--- /dev/null
+++ b/src/themes/zenbones_dark.zig
@@ -0,0 +1,92 @@
+//! Zenbones (dark): Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #352f2d -> #473f3d: 1.18:1 off the unfocused #272321, pardes needs 1.5:1 on a dark page
+//! - focused grip #59514d -> #635b58: 1.32:1 on the focused tag #473f3d
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/zenbones/palette.lua + lua/zenbones/specs/dark.lua); values are the
+//! default 'ness' baked into colors/zenbones.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours. TabLineSel
+//! is guifg/guibg=NONE (bold only), so it inherits Normal. TabLine links to
+//! StatusLine, TabLineFill to StatusLineNC. Accent is the lualine normal-mode a
+//! bg, a muted neutral (the theme is contrast-based, not hue-accented).
+//! Modified = Changed fg.
+pub const theme = .{
+ .name = "zenbones_dark",
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:33 Normal guibg
+ .bg = .{ 0x1c, 0x19, 0x17 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:33 Normal guifg
+ .fg = .{ 0xb4, 0xbd, 0xc3 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:211 Visual guibg
+ .sel_bg = .{ 0x3d, 0x40, 0x42 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xb4, 0xbd, 0xc3 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:198 StatusLineNC guibg
+ .tag_bg = .{ 0x27, 0x23, 0x21 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:198 StatusLineNC guifg
+ .tag_fg = .{ 0xca, 0xd0, 0xd4 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:195 StatusLine guibg #352f2d, adjusted
+ .tag_active_bg = .{ 0x47, 0x3f, 0x3d },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:195 StatusLine guifg
+ .tag_active_fg = .{ 0xb4, 0xbd, 0xc3 },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/zenbones_dark_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0x63, 0x5b, 0x58 },
+ // derived: the grip at rest, the accent #59514d faded toward the unfocused tag #272321 as far as 1.6:1 off it (0.72 of the way)
+ .box_dim = .{ 0x4b, 0x44, 0x41 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/zenbones.vim:41 Changed guifg
+ .box_dirty = .{ 0x60, 0x99, 0xc0 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/zenbones.nvim/colors/zenbones.vim:207 Type guifg
+ .column_box = .{ 0xa1, 0x93, 0x8c },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:191 Statement guifg (no Keyword group defined; Vim default links Keyword to Statement)
+ .kw = .{ 0xb4, 0xbd, 0xc3 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:201 String guifg
+ .str = .{ 0x86, 0x8c, 0x91 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:164 Number guifg
+ .num = .{ 0x86, 0x8c, 0x91 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:47 Comment guifg
+ .comment = .{ 0x6e, 0x67, 0x63 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:150 LineNr guifg
+ .lineno = .{ 0x68, 0x5f, 0x5a },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:62 CursorLineNr guifg
+ .lineno_active = .{ 0xb4, 0xbd, 0xc3 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:167 PmenuSbar guibg
+ .scroll_track = .{ 0x61, 0x58, 0x53 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:169 PmenuThumb guibg
+ .scroll_thumb = .{ 0x8e, 0x81, 0x7b },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:218 WinSeparator guifg
+ .border = .{ 0x68, 0x5f, 0x5a },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:172 Search guibg
+ .search_bg = .{ 0x65, 0x43, 0x5e },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:172 Search guifg
+ .search_fg = .{ 0xb4, 0xbd, 0xc3 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:113 Error guifg via DiagnosticError->Error (link :114)
+ .diagnostic_error = .{ 0xde, 0x6e, 0x7c },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:212 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :213)
+ .diagnostic_warning = .{ 0xb7, 0x7e, 0x64 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:72 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x60, 0x99, 0xc0 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:67 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0xb2, 0x79, 0xa7 },
+ .palette = .{
+ .{ 0x1c, 0x19, 0x17 }, // 0: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:17 g:terminal_color_0
+ .{ 0xde, 0x6e, 0x7c }, // 1: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:18 g:terminal_color_1
+ .{ 0x81, 0x9b, 0x69 }, // 2: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:19 g:terminal_color_2
+ .{ 0xb7, 0x7e, 0x64 }, // 3: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:20 g:terminal_color_3
+ .{ 0x60, 0x99, 0xc0 }, // 4: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:21 g:terminal_color_4
+ .{ 0xb2, 0x79, 0xa7 }, // 5: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:22 g:terminal_color_5
+ .{ 0x66, 0xa5, 0xad }, // 6: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:23 g:terminal_color_6
+ .{ 0xb4, 0xbd, 0xc3 }, // 7: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:24 g:terminal_color_7
+ .{ 0x40, 0x38, 0x33 }, // 8: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:25 g:terminal_color_8
+ .{ 0xe8, 0x83, 0x8f }, // 9: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:26 g:terminal_color_9
+ .{ 0x8b, 0xae, 0x68 }, // 10: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:27 g:terminal_color_10
+ .{ 0xd6, 0x8c, 0x67 }, // 11: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:28 g:terminal_color_11
+ .{ 0x61, 0xab, 0xda }, // 12: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:29 g:terminal_color_12
+ .{ 0xcf, 0x86, 0xc1 }, // 13: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:30 g:terminal_color_13
+ .{ 0x65, 0xb8, 0xc1 }, // 14: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:31 g:terminal_color_14
+ .{ 0x88, 0x8f, 0x94 }, // 15: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:32 g:terminal_color_15
+ },
+};
diff --git a/src/themes/zenbones_light.zig b/src/themes/zenbones_light.zig
new file mode 100644
index 00000000..8d5cc2b8
--- /dev/null
+++ b/src/themes/zenbones_light.zig
@@ -0,0 +1,93 @@
+//! Zenbones (light): Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #d6cdc9 -> #cfc3bf: 1.15:1 off the unfocused #e1dcd9, pardes needs 1.25:1 on a light page
+//! - unfocused tag text #596a76 -> #52626d: 4.12:1 on #e1dcd9, tag text needs 4.5:1 to be read and run
+//! - focused grip #bbaba3 -> #ac9d96: 1.29:1 on the focused tag #cfc3bf
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/zenbones/palette.lua + lua/zenbones/specs/light.lua); values are the
+//! default 'ness' baked into colors/zenbones.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours. TabLineSel
+//! is guifg/guibg=NONE (bold only), so it inherits Normal. TabLine links to
+//! StatusLine, TabLineFill to StatusLineNC. Accent is the lualine normal-mode a
+//! bg, a muted neutral (the theme is contrast-based, not hue-accented).
+//! Modified = Changed fg.
+pub const theme = .{
+ .name = "zenbones_light",
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:267 Normal guibg
+ .bg = .{ 0xf0, 0xed, 0xec },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:267 Normal guifg
+ .fg = .{ 0x2c, 0x36, 0x3c },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:445 Visual guibg
+ .sel_bg = .{ 0xcb, 0xd9, 0xe3 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x2c, 0x36, 0x3c },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:432 StatusLineNC guibg
+ .tag_bg = .{ 0xe1, 0xdc, 0xd9 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:432 StatusLineNC guifg #596a76, adjusted
+ .tag_fg = .{ 0x52, 0x62, 0x6d },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:429 StatusLine guibg #d6cdc9, adjusted
+ .tag_active_bg = .{ 0xcf, 0xc3, 0xbf },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:429 StatusLine guifg
+ .tag_active_fg = .{ 0x2c, 0x36, 0x3c },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/zenbones_light_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0xac, 0x9d, 0x96 },
+ // derived: the grip at rest, the accent #bbaba3 faded toward the unfocused tag #e1dcd9 as far as 1.6:1 off it (0.98 of the way)
+ .box_dim = .{ 0xbc, 0xac, 0xa4 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/zenbones.vim:275 Changed guifg
+ .box_dirty = .{ 0x28, 0x64, 0x86 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/zenbones.nvim/colors/zenbones.vim:441 Type guifg
+ .column_box = .{ 0x6a, 0x55, 0x49 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:425 Statement guifg (no Keyword group defined; Vim default links Keyword to Statement)
+ .kw = .{ 0x2c, 0x36, 0x3c },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:435 String guifg
+ .str = .{ 0x55, 0x65, 0x70 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:398 Number guifg
+ .num = .{ 0x55, 0x65, 0x70 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:281 Comment guifg
+ .comment = .{ 0x94, 0x89, 0x85 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:384 LineNr guifg
+ .lineno = .{ 0xa4, 0x96, 0x8f },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:296 CursorLineNr guifg
+ .lineno_active = .{ 0x2c, 0x36, 0x3c },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:401 PmenuSbar guibg
+ .scroll_track = .{ 0xb2, 0xa3, 0x9b },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:403 PmenuThumb guibg
+ .scroll_thumb = .{ 0xf7, 0xf6, 0xf5 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:452 WinSeparator guifg
+ .border = .{ 0xa4, 0x96, 0x8f },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:406 Search guibg
+ .search_bg = .{ 0xde, 0xb9, 0xd6 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:406 Search guifg
+ .search_fg = .{ 0x2c, 0x36, 0x3c },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:347 Error guifg via DiagnosticError->Error (link :348)
+ .diagnostic_error = .{ 0xa8, 0x33, 0x4c },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:446 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :447)
+ .diagnostic_warning = .{ 0x94, 0x49, 0x27 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:306 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x28, 0x64, 0x86 },
+ // ~/05-genizah/zenbones.nvim/colors/zenbones.vim:301 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0x88, 0x50, 0x7d },
+ .palette = .{
+ .{ 0xf0, 0xed, 0xec }, // 0: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:251 g:terminal_color_0
+ .{ 0xa8, 0x33, 0x4c }, // 1: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:252 g:terminal_color_1
+ .{ 0x4f, 0x6c, 0x31 }, // 2: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:253 g:terminal_color_2
+ .{ 0x94, 0x49, 0x27 }, // 3: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:254 g:terminal_color_3
+ .{ 0x28, 0x64, 0x86 }, // 4: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:255 g:terminal_color_4
+ .{ 0x88, 0x50, 0x7d }, // 5: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:256 g:terminal_color_5
+ .{ 0x3b, 0x89, 0x92 }, // 6: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:257 g:terminal_color_6
+ .{ 0x2c, 0x36, 0x3c }, // 7: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:258 g:terminal_color_7
+ .{ 0xcf, 0xc1, 0xba }, // 8: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:259 g:terminal_color_8
+ .{ 0x94, 0x25, 0x3e }, // 9: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:260 g:terminal_color_9
+ .{ 0x3f, 0x5a, 0x22 }, // 10: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:261 g:terminal_color_10
+ .{ 0x80, 0x3d, 0x1c }, // 11: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:262 g:terminal_color_11
+ .{ 0x1d, 0x55, 0x73 }, // 12: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:263 g:terminal_color_12
+ .{ 0x7b, 0x3b, 0x70 }, // 13: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:264 g:terminal_color_13
+ .{ 0x2b, 0x74, 0x7c }, // 14: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:265 g:terminal_color_14
+ .{ 0x4f, 0x5e, 0x68 }, // 15: ~/05-genizah/zenbones.nvim/colors/zenbones.vim:266 g:terminal_color_15
+ },
+};
diff --git a/src/themes/zenburned.zig b/src/themes/zenburned.zig
new file mode 100644
index 00000000..3c8baa82
--- /dev/null
+++ b/src/themes/zenburned.zig
@@ -0,0 +1,92 @@
+//! Zenburned: Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #555555 -> #646464: 1.21:1 off the unfocused #494949, pardes needs 1.5:1 on a dark page
+//! - focused grip #6f6f6f -> #838383: 1.18:1 on the focused tag #646464
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/zenburned/palette.lua + lua/zenbones/specs/dark.lua); values are the
+//! default 'ness' baked into colors/zenburned.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours. Dark-only
+//! flavour (zenbones.json background='dark'). TabLineSel is guifg/guibg=NONE
+//! (bold only), so it inherits Normal. TabLine links to StatusLine, TabLineFill
+//! to StatusLineNC. Accent is the lualine normal-mode a bg, a muted neutral
+//! (the theme is contrast-based, not hue-accented). Modified = Changed fg.
+pub const theme = .{
+ .name = "zenburned",
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:34 Normal guibg
+ .bg = .{ 0x40, 0x40, 0x40 },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:34 Normal guifg
+ .fg = .{ 0xf0, 0xe4, 0xcf },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:212 Visual guibg
+ .sel_bg = .{ 0x74, 0x69, 0x56 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xf0, 0xe4, 0xcf },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:199 StatusLineNC guibg
+ .tag_bg = .{ 0x49, 0x49, 0x49 },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:199 StatusLineNC guifg
+ .tag_fg = .{ 0xf5, 0xed, 0xe0 },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:196 StatusLine guibg #555555, adjusted
+ .tag_active_bg = .{ 0x64, 0x64, 0x64 },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:196 StatusLine guifg
+ .tag_active_fg = .{ 0xf0, 0xe4, 0xcf },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/zenburned_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0x83, 0x83, 0x83 },
+ // derived: the grip at rest, the accent #6f6f6f faded toward the unfocused tag #494949 as far as 1.6:1 off it (0.82 of the way)
+ .box_dim = .{ 0x68, 0x68, 0x68 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/zenburned.vim:42 Changed guifg
+ .box_dirty = .{ 0x60, 0x99, 0xc0 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/zenbones.nvim/colors/zenburned.vim:208 Type guifg
+ .column_box = .{ 0xa8, 0xa8, 0xa8 },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:193 Statement guifg (no Keyword group defined; Vim default links Keyword to Statement)
+ .kw = .{ 0xdc, 0xa2, 0xa2 },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:202 String guifg
+ .str = .{ 0xba, 0xa6, 0x81 },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:165 Number guifg
+ .num = .{ 0xba, 0xa6, 0x81 },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:48 Comment guifg
+ .comment = .{ 0x84, 0x84, 0x84 },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:151 LineNr guifg
+ .lineno = .{ 0x7f, 0x7f, 0x7f },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:63 CursorLineNr guifg
+ .lineno_active = .{ 0xf0, 0xe4, 0xcf },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:168 PmenuSbar guibg
+ .scroll_track = .{ 0x77, 0x77, 0x77 },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:170 PmenuThumb guibg
+ .scroll_thumb = .{ 0x9b, 0x9b, 0x9b },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:219 WinSeparator guifg
+ .border = .{ 0x7f, 0x7f, 0x7f },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:174 Search guibg
+ .search_bg = .{ 0x9c, 0x69, 0x92 },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:174 Search guifg
+ .search_fg = .{ 0xf0, 0xe4, 0xcf },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:114 Error guifg via DiagnosticError->Error (link :115)
+ .diagnostic_error = .{ 0xe3, 0x71, 0x6e },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:213 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :214)
+ .diagnostic_warning = .{ 0xb7, 0x7e, 0x64 },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:73 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x60, 0x99, 0xc0 },
+ // ~/05-genizah/zenbones.nvim/colors/zenburned.vim:68 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0xb2, 0x79, 0xa7 },
+ .palette = .{
+ .{ 0x40, 0x40, 0x40 }, // 0: ~/05-genizah/zenbones.nvim/colors/zenburned.vim:18 g:terminal_color_0
+ .{ 0xe3, 0x71, 0x6e }, // 1: ~/05-genizah/zenbones.nvim/colors/zenburned.vim:19 g:terminal_color_1
+ .{ 0x81, 0x9b, 0x69 }, // 2: ~/05-genizah/zenbones.nvim/colors/zenburned.vim:20 g:terminal_color_2
+ .{ 0xb7, 0x7e, 0x64 }, // 3: ~/05-genizah/zenbones.nvim/colors/zenburned.vim:21 g:terminal_color_3
+ .{ 0x60, 0x99, 0xc0 }, // 4: ~/05-genizah/zenbones.nvim/colors/zenburned.vim:22 g:terminal_color_4
+ .{ 0xb2, 0x79, 0xa7 }, // 5: ~/05-genizah/zenbones.nvim/colors/zenburned.vim:23 g:terminal_color_5
+ .{ 0x66, 0xa5, 0xad }, // 6: ~/05-genizah/zenbones.nvim/colors/zenburned.vim:24 g:terminal_color_6
+ .{ 0xf0, 0xe4, 0xcf }, // 7: ~/05-genizah/zenbones.nvim/colors/zenburned.vim:25 g:terminal_color_7
+ .{ 0x62, 0x5a, 0x5b }, // 8: ~/05-genizah/zenbones.nvim/colors/zenburned.vim:26 g:terminal_color_8
+ .{ 0xec, 0x86, 0x85 }, // 9: ~/05-genizah/zenbones.nvim/colors/zenburned.vim:27 g:terminal_color_9
+ .{ 0x8b, 0xae, 0x68 }, // 10: ~/05-genizah/zenbones.nvim/colors/zenburned.vim:28 g:terminal_color_10
+ .{ 0xd6, 0x8c, 0x67 }, // 11: ~/05-genizah/zenbones.nvim/colors/zenburned.vim:29 g:terminal_color_11
+ .{ 0x61, 0xab, 0xda }, // 12: ~/05-genizah/zenbones.nvim/colors/zenburned.vim:30 g:terminal_color_12
+ .{ 0xcf, 0x86, 0xc1 }, // 13: ~/05-genizah/zenbones.nvim/colors/zenburned.vim:31 g:terminal_color_13
+ .{ 0x65, 0xb8, 0xc1 }, // 14: ~/05-genizah/zenbones.nvim/colors/zenburned.vim:32 g:terminal_color_14
+ .{ 0xc0, 0xab, 0x86 }, // 15: ~/05-genizah/zenbones.nvim/colors/zenburned.vim:33 g:terminal_color_15
+ },
+};
diff --git a/src/themes/zenwritten_dark.zig b/src/themes/zenwritten_dark.zig
new file mode 100644
index 00000000..76b7047e
--- /dev/null
+++ b/src/themes/zenwritten_dark.zig
@@ -0,0 +1,92 @@
+//! Zenwritten (dark): Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #303030 -> #414141: 1.18:1 off the unfocused #242424, pardes needs 1.5:1 on a dark page
+//! - focused grip #525252 -> #5c5c5c: 1.31:1 on the focused tag #414141
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/zenwritten/palette.lua + lua/zenbones/specs/dark.lua); values are the
+//! default 'ness' baked into colors/zenwritten.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours. TabLineSel
+//! is guifg/guibg=NONE (bold only), so it inherits Normal. TabLine links to
+//! StatusLine, TabLineFill to StatusLineNC. Accent is the lualine normal-mode a
+//! bg, a muted neutral (the theme is contrast-based, not hue-accented).
+//! Modified = Changed fg.
+pub const theme = .{
+ .name = "zenwritten_dark",
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:33 Normal guibg
+ .bg = .{ 0x19, 0x19, 0x19 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:33 Normal guifg
+ .fg = .{ 0xbb, 0xbb, 0xbb },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:211 Visual guibg
+ .sel_bg = .{ 0x40, 0x40, 0x40 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0xbb, 0xbb, 0xbb },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:198 StatusLineNC guibg
+ .tag_bg = .{ 0x24, 0x24, 0x24 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:198 StatusLineNC guifg
+ .tag_fg = .{ 0xcf, 0xcf, 0xcf },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:195 StatusLine guibg #303030, adjusted
+ .tag_active_bg = .{ 0x41, 0x41, 0x41 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:195 StatusLine guifg
+ .tag_active_fg = .{ 0xbb, 0xbb, 0xbb },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/zenwritten_dark_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0x5c, 0x5c, 0x5c },
+ // derived: the grip at rest, the accent #525252 faded toward the unfocused tag #242424 as far as 1.6:1 off it (0.72 of the way)
+ .box_dim = .{ 0x45, 0x45, 0x45 },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:41 Changed guifg
+ .box_dirty = .{ 0x60, 0x99, 0xc0 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:207 Type guifg
+ .column_box = .{ 0x96, 0x96, 0x96 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:191 Statement guifg (no Keyword group defined; Vim default links Keyword to Statement)
+ .kw = .{ 0xbb, 0xbb, 0xbb },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:201 String guifg
+ .str = .{ 0x8b, 0x8b, 0x8b },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:164 Number guifg
+ .num = .{ 0x8b, 0x8b, 0x8b },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:47 Comment guifg
+ .comment = .{ 0x68, 0x68, 0x68 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:150 LineNr guifg
+ .lineno = .{ 0x61, 0x61, 0x61 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:62 CursorLineNr guifg
+ .lineno_active = .{ 0xbb, 0xbb, 0xbb },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:167 PmenuSbar guibg
+ .scroll_track = .{ 0x59, 0x59, 0x59 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:169 PmenuThumb guibg
+ .scroll_thumb = .{ 0x84, 0x84, 0x84 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:218 WinSeparator guifg
+ .border = .{ 0x61, 0x61, 0x61 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:172 Search guibg
+ .search_bg = .{ 0x65, 0x43, 0x5e },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:172 Search guifg
+ .search_fg = .{ 0xbb, 0xbb, 0xbb },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:113 Error guifg via DiagnosticError->Error (link :114)
+ .diagnostic_error = .{ 0xde, 0x6e, 0x7c },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:212 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :213)
+ .diagnostic_warning = .{ 0xb7, 0x7e, 0x64 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:72 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x60, 0x99, 0xc0 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:67 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0xb2, 0x79, 0xa7 },
+ .palette = .{
+ .{ 0x19, 0x19, 0x19 }, // 0: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:17 g:terminal_color_0
+ .{ 0xde, 0x6e, 0x7c }, // 1: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:18 g:terminal_color_1
+ .{ 0x81, 0x9b, 0x69 }, // 2: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:19 g:terminal_color_2
+ .{ 0xb7, 0x7e, 0x64 }, // 3: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:20 g:terminal_color_3
+ .{ 0x60, 0x99, 0xc0 }, // 4: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:21 g:terminal_color_4
+ .{ 0xb2, 0x79, 0xa7 }, // 5: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:22 g:terminal_color_5
+ .{ 0x66, 0xa5, 0xad }, // 6: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:23 g:terminal_color_6
+ .{ 0xbb, 0xbb, 0xbb }, // 7: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:24 g:terminal_color_7
+ .{ 0x3d, 0x38, 0x39 }, // 8: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:25 g:terminal_color_8
+ .{ 0xe8, 0x83, 0x8f }, // 9: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:26 g:terminal_color_9
+ .{ 0x8b, 0xae, 0x68 }, // 10: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:27 g:terminal_color_10
+ .{ 0xd6, 0x8c, 0x67 }, // 11: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:28 g:terminal_color_11
+ .{ 0x61, 0xab, 0xda }, // 12: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:29 g:terminal_color_12
+ .{ 0xcf, 0x86, 0xc1 }, // 13: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:30 g:terminal_color_13
+ .{ 0x65, 0xb8, 0xc1 }, // 14: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:31 g:terminal_color_14
+ .{ 0x8e, 0x8e, 0x8e }, // 15: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:32 g:terminal_color_15
+ },
+};
diff --git a/src/themes/zenwritten_light.zig b/src/themes/zenwritten_light.zig
new file mode 100644
index 00000000..42e1623a
--- /dev/null
+++ b/src/themes/zenwritten_light.zig
@@ -0,0 +1,93 @@
+//! Zenwritten (light): Zenbones, as close to the original as pardes can draw it.
+//! Every value is the original's own, cited on its line (path:line key); the
+//! chrome pardes needs and the original lacks is derived from its palette.
+//! Sources:
+//! repo; url https://github.com/zenbones-theme/zenbones.nvim; genizah ~/05-genizah/zenbones.nvim; commit 8304d8df9b823ff11e103afa62f38c39f534abe6
+//! Tags: the window bar (StatusLine / StatusLineNC, or mode-line / mode-line-inactive).
+//! Adjusted to meet pardes's floors (chrome only; text, syntax and ANSI are untouched):
+//! - focused tag ground #cfcfcf -> #c5c5c5: 1.15:1 off the unfocused #dddddd, pardes needs 1.25:1 on a light page
+//! - unfocused tag text #686868 -> #606060: 4.10:1 on #dddddd, tag text needs 4.5:1 to be read and run
+//! - focused grip #aeaeae -> #a0a0a0: 1.29:1 on the focused tag #c5c5c5
+//! Upstream notes: Generated by shipwright from lush/HSLuv
+//! (lua/zenwritten/palette.lua + lua/zenbones/specs/light.lua); values are the
+//! default 'ness' baked into colors/zenwritten.vim. Palette = the 16 names
+//! term.lua maps to ANSI, taken from the generated terminal colours. TabLineSel
+//! is guifg/guibg=NONE (bold only), so it inherits Normal. TabLine links to
+//! StatusLine, TabLineFill to StatusLineNC. Accent is the lualine normal-mode a
+//! bg, a muted neutral (the theme is contrast-based, not hue-accented).
+//! Modified = Changed fg.
+pub const theme = .{
+ .name = "zenwritten_light",
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:267 Normal guibg
+ .bg = .{ 0xee, 0xee, 0xee },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:267 Normal guifg
+ .fg = .{ 0x35, 0x35, 0x35 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:445 Visual guibg
+ .sel_bg = .{ 0xd7, 0xd7, 0xd7 },
+ // the original keeps text colours over its selection; its text colour
+ .sel_fg = .{ 0x35, 0x35, 0x35 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:432 StatusLineNC guibg
+ .tag_bg = .{ 0xdd, 0xdd, 0xdd },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:432 StatusLineNC guifg #686868, adjusted
+ .tag_fg = .{ 0x60, 0x60, 0x60 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:429 StatusLine guibg #cfcfcf, adjusted
+ .tag_active_bg = .{ 0xc5, 0xc5, 0xc5 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:429 StatusLine guifg
+ .tag_active_fg = .{ 0x35, 0x35, 0x35 },
+ // the focused grip: ~/05-genizah/zenbones.nvim/lua/lualine/themes/zenwritten_light_default.lua:8 normal.a bg (lualine theme, default ness; generator derives it as PmenuSbar.bg.abs_da(3), lua/zenbones/shipwright/runners/lualine.lua:66)
+ .box = .{ 0xa0, 0xa0, 0xa0 },
+ // derived: the grip at rest, the accent #aeaeae faded toward the unfocused tag #dddddd as far as 1.6:1 off it (0.98 of the way)
+ .box_dim = .{ 0xaf, 0xaf, 0xaf },
+ // an unsaved grip: the original's modified colour, ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:275 Changed guifg
+ .box_dirty = .{ 0x28, 0x64, 0x86 },
+ // derived: the column grip, the original's type colour, ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:441 Type guifg
+ .column_box = .{ 0x73, 0x50, 0x57 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:425 Statement guifg (no Keyword group defined; Vim default links Keyword to Statement)
+ .kw = .{ 0x35, 0x35, 0x35 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:435 String guifg
+ .str = .{ 0x63, 0x63, 0x63 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:398 Number guifg
+ .num = .{ 0x63, 0x63, 0x63 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:281 Comment guifg
+ .comment = .{ 0x8b, 0x8b, 0x8b },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:384 LineNr guifg
+ .lineno = .{ 0x98, 0x98, 0x98 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:296 CursorLineNr guifg
+ .lineno_active = .{ 0x35, 0x35, 0x35 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:401 PmenuSbar guibg
+ .scroll_track = .{ 0xa6, 0xa6, 0xa6 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:403 PmenuThumb guibg
+ .scroll_thumb = .{ 0xf6, 0xf6, 0xf6 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:452 WinSeparator guifg
+ .border = .{ 0x98, 0x98, 0x98 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:406 Search guibg
+ .search_bg = .{ 0xde, 0xb9, 0xd6 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:406 Search guifg
+ .search_fg = .{ 0x35, 0x35, 0x35 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:347 Error guifg via DiagnosticError->Error (link :348)
+ .diagnostic_error = .{ 0xa8, 0x33, 0x4c },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:446 WarningMsg guifg via DiagnosticWarn->WarningMsg (link :447)
+ .diagnostic_warning = .{ 0x94, 0x49, 0x27 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:306 DiagnosticInfo guifg
+ .diagnostic_info = .{ 0x28, 0x64, 0x86 },
+ // ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:301 DiagnosticHint guifg
+ .diagnostic_hint = .{ 0x88, 0x50, 0x7d },
+ .palette = .{
+ .{ 0xee, 0xee, 0xee }, // 0: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:251 g:terminal_color_0
+ .{ 0xa8, 0x33, 0x4c }, // 1: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:252 g:terminal_color_1
+ .{ 0x4f, 0x6c, 0x31 }, // 2: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:253 g:terminal_color_2
+ .{ 0x94, 0x49, 0x27 }, // 3: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:254 g:terminal_color_3
+ .{ 0x28, 0x64, 0x86 }, // 4: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:255 g:terminal_color_4
+ .{ 0x88, 0x50, 0x7d }, // 5: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:256 g:terminal_color_5
+ .{ 0x3b, 0x89, 0x92 }, // 6: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:257 g:terminal_color_6
+ .{ 0x35, 0x35, 0x35 }, // 7: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:258 g:terminal_color_7
+ .{ 0xc6, 0xc3, 0xc3 }, // 8: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:259 g:terminal_color_8
+ .{ 0x94, 0x25, 0x3e }, // 9: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:260 g:terminal_color_9
+ .{ 0x3f, 0x5a, 0x22 }, // 10: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:261 g:terminal_color_10
+ .{ 0x80, 0x3d, 0x1c }, // 11: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:262 g:terminal_color_11
+ .{ 0x1d, 0x55, 0x73 }, // 12: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:263 g:terminal_color_12
+ .{ 0x7b, 0x3b, 0x70 }, // 13: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:264 g:terminal_color_13
+ .{ 0x2b, 0x74, 0x7c }, // 14: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:265 g:terminal_color_14
+ .{ 0x5c, 0x5c, 0x5c }, // 15: ~/05-genizah/zenbones.nvim/colors/zenwritten.vim:266 g:terminal_color_15
+ },
+};
diff --git a/src/tty/tty.zig b/src/tty/tty.zig
index 762616f5..3c8c90bc 100644
--- a/src/tty/tty.zig
+++ b/src/tty/tty.zig
@@ -914,6 +914,10 @@ const Shell = struct {
check_files: bool = false,
in_paste: bool = false,
tracks: []const pardes.animation.Track = &.{},
+ /// T2: the cursor's jump trail (JumpTrail).
+ trail: JumpTrail = .{},
+ /// T7: when each pane's view last moved (ThumbFlash).
+ flash: ThumbFlash = .{},
/// PARDES_TEST_CLOCK's virtual time (host_io.testClock), and where the
/// running-out wait moves it: the core's next wake.
test_clock: ?u64 = null,
@@ -1161,7 +1165,13 @@ const Shell = struct {
) catch return;
const tz_cells = tracy.zone(@src(), "surface->vaxis");
const win = vx.window();
- paintCells(win, surface.cells, surface.cols, surface.rows);
+ const truecolor = vx.caps.rgb or colortermTruecolor();
+ // Each of the shell's own motions below asks for frames while it runs.
+ s.core.shell_continuous = false;
+ const shadowed = chipShadows(s.frame.allocator(), surface, s.core, truecolor) catch surface;
+ const flashed = s.flash.apply(s.frame.allocator(), shadowed, s.core, truecolor) catch shadowed;
+ const trailed = s.trail.apply(s.frame.allocator(), flashed, s.core, truecolor) catch flashed;
+ paintCells(win, trailed.cells, trailed.cols, trailed.rows);
tz_cells.end();
for (surface.images[0..surface.nimages]) |maybe| {
const place = maybe orelse continue;
@@ -1807,6 +1817,213 @@ fn paintCells(win: vaxis.Window, cells: []const pardes.Cell, cols: u16, rows: u1
}
}
+/// T1 (§9.2): each notice chip casts a shadow one cell right and one down,
+/// inside its own pane's body: its ground 35% toward black in OKLab on a
+/// cell with text; on a blank one a half block (▌ on the right, ▀ below,
+/// ▘ at the corner) in the darker colour, so the shadow is half a cell
+/// thick and reads as one. Never the cursor's cell, a selection, a tag or
+/// another chip. Truecolor terminals only.
+fn chipShadows(arena: std.mem.Allocator, surface: *pardes.Surface, core: *pardes.Pardes, truecolor: bool) !*pardes.Surface {
+ if (!core.settings.chip_shadow or !truecolor) return surface;
+ const regions = surface.regionList();
+ const any = for (regions) |region| {
+ if (region.kind == .notice) break true;
+ } else false;
+ if (!any) return surface;
+ const out = try arena.create(pardes.Surface);
+ out.* = surface.*;
+ out.cells = try arena.dupe(pardes.Cell, surface.cells);
+ const theme = core.theme();
+ const page = theme.bg orelse pardes.colors.null_page;
+ for (regions) |chip| {
+ if (chip.kind != .notice or chip.rect.w == 0) continue;
+ const body = for (regions) |region| {
+ if (region.kind == .body and region.owner == chip.owner) break region.rect;
+ } else continue;
+ const right = chip.rect.x + chip.rect.w;
+ const below = chip.rect.y + chip.rect.h;
+ // The column right of it (from its second row), the row below (from
+ // its second column), and the corner.
+ var y = chip.rect.y;
+ while (y <= below) : (y += 1) {
+ var x = chip.rect.x;
+ while (x <= right) : (x += 1) {
+ const side = x == right and y > chip.rect.y and y < below;
+ const foot = y == below and x > chip.rect.x and x < right;
+ const corner = x == right and y == below;
+ if (!side and !foot and !corner) continue;
+ if (x < body.x or y < body.y or x >= body.x + body.w or y >= body.y + body.h or x >= out.cols or y >= out.rows) continue;
+ if (surface.cursor) |cu| if (cu.x == x and cu.y == y) continue;
+ const covered = for (regions) |region| {
+ if ((region.kind == .notice or region.kind == .tag or region.kind == .grip) and x >= region.rect.x and x < region.rect.x + region.rect.w and y >= region.rect.y and y < region.rect.y + region.rect.h) break true;
+ } else false;
+ if (covered) continue;
+ const cell = &out.cells[@as(usize, y) * out.cols + x];
+ const ground: [3]u8 = if (cell.default) page else switch (cell.style.bg) {
+ .rgb => |rgb| rgb,
+ else => page,
+ };
+ if (std.mem.eql(u8, &ground, &theme.sel_bg)) continue;
+ const dark = pardes.colors.mixOklab(ground, .{ 0, 0, 0 }, 0.35);
+ const blank = cell.default or cell.len == 0 or (cell.len == 1 and cell.text[0] == ' ');
+ if (blank) {
+ const glyph: []const u8 = if (corner) "▘" else if (side) "▌" else "▀";
+ cell.* = .{ .default = false, .style = .{ .fg = .{ .rgb = dark }, .bg = .{ .rgb = ground } } };
+ @memcpy(cell.text[0..glyph.len], glyph);
+ cell.len = @intCast(glyph.len);
+ } else {
+ cell.default = false;
+ cell.style.bg = .{ .rgb = dark };
+ }
+ }
+ }
+ }
+ return out;
+}
+
+/// T7 (§9.2): a pane whose view moved (Region.line) has its scroll thumb
+/// brighten, drawn most of the way to the text's colour, and fade back
+/// over 250 ms with an ease-out: where you are in the file, as you move.
+/// The rail is chrome, not a focus indicator. Truecolor terminals only.
+const ThumbFlash = struct {
+ lines: [pardes.MAX_PANES]i32 = @splat(std.math.minInt(i32)),
+ since: [pardes.MAX_PANES]u64 = @splat(0),
+
+ const life_ns: u64 = 250 * std.time.ns_per_ms;
+
+ fn apply(flash: *ThumbFlash, arena: std.mem.Allocator, surface: *pardes.Surface, core: *pardes.Pardes, truecolor: bool) !*pardes.Surface {
+ const on = core.settings.thumb_flash and truecolor and core.settings.motion != .off;
+ var out: ?*pardes.Surface = null;
+ const theme = core.theme();
+ const ink = theme.fg orelse .{ 0xcc, 0xcc, 0xcc };
+ for (surface.regionList()) |body| {
+ if (body.kind != .body or body.owner >= flash.lines.len) continue;
+ const id = body.owner;
+ if (flash.lines[id] != std.math.minInt(i32) and flash.lines[id] != body.line) flash.since[id] = core.now_ns;
+ flash.lines[id] = body.line;
+ const age = core.now_ns -| flash.since[id];
+ if (!on or flash.since[id] == 0 or age >= life_ns) continue;
+ const rail = for (surface.regionList()) |region| {
+ if (region.kind == .rail and region.owner == id) break region;
+ } else continue;
+ if (rail.thumb_h == 0) continue;
+ const t = @as(f32, @floatFromInt(age)) / @as(f32, @floatFromInt(life_ns));
+ const strength = 0.6 * (1 - t) * (1 - t);
+ if (out == null) {
+ const copy = try arena.create(pardes.Surface);
+ copy.* = surface.*;
+ copy.cells = try arena.dupe(pardes.Cell, surface.cells);
+ out = copy;
+ }
+ const o = out.?;
+ var y = rail.rect.y + rail.thumb_y;
+ while (y < rail.rect.y + rail.thumb_y + rail.thumb_h and y < o.rows) : (y += 1) {
+ var x = rail.rect.x;
+ while (x < rail.rect.x + rail.rect.w and x < o.cols) : (x += 1) {
+ const cell = &o.cells[@as(usize, y) * o.cols + x];
+ const ground: [3]u8 = switch (cell.style.bg) {
+ .rgb => |rgb| rgb,
+ else => continue,
+ };
+ if (!std.mem.eql(u8, &ground, &core.chromeTheme().scroll_thumb)) continue;
+ cell.style.bg = .{ .rgb = pardes.colors.mixOklab(ground, ink, strength) };
+ }
+ }
+ core.shell_continuous = true;
+ }
+ return out orelse surface;
+ }
+};
+
+/// Truecolor by COLORTERM (truecolor or 24bit), for a terminal that does
+/// not answer vaxis's query (§9.2: vaxis caps or COLORTERM).
+fn colortermTruecolor() bool {
+ const value = std.mem.span(std.c.getenv("COLORTERM") orelse return false);
+ return std.mem.eql(u8, value, "truecolor") or std.mem.eql(u8, value, "24bit");
+}
+
+/// T2 (docs/render-pipeline.md §9.2): the cursor jumping three cells or
+/// more leaves a trail of three to five cells along its path, just behind
+/// it, their ground drawn toward the text's colour, most beside the
+/// cursor, all fading out over 120 ms. Truecolor terminals only; a cell a
+/// frame changes, a few bytes. Never the cursor's own cell, a selection or
+/// anything outside the focused pane's body; off with Motion off.
+const JumpTrail = struct {
+ last: ?[2]u16 = null,
+ from: [2]u16 = .{ 0, 0 },
+ to: [2]u16 = .{ 0, 0 },
+ start_ns: u64 = 0,
+ active: bool = false,
+
+ const life_ns: u64 = 120 * std.time.ns_per_ms;
+
+ fn apply(trail: *JumpTrail, arena: std.mem.Allocator, surface: *pardes.Surface, core: *pardes.Pardes, truecolor: bool) !*pardes.Surface {
+ defer core.shell_continuous = trail.active or core.shell_continuous;
+ const on = core.settings.jump_trail and truecolor and core.settings.motion != .off;
+ const cursor = surface.cursor orelse {
+ trail.active = false;
+ return surface;
+ };
+ const here: [2]u16 = .{ cursor.x, cursor.y };
+ if (trail.last) |was| {
+ const dx = @as(i32, here[0]) - was[0];
+ const dy = @as(i32, here[1]) - was[1];
+ if (on and @max(@abs(dx), @abs(dy)) >= 3) {
+ trail.* = .{ .last = here, .from = was, .to = here, .start_ns = core.now_ns, .active = true };
+ }
+ }
+ trail.last = here;
+ if (!on or !trail.active) {
+ trail.active = false;
+ return surface;
+ }
+ const age = core.now_ns -| trail.start_ns;
+ if (age >= life_ns or !std.meta.eql(trail.to, here)) {
+ trail.active = false;
+ return surface;
+ }
+ const out = try arena.create(pardes.Surface);
+ out.* = surface.*;
+ out.cells = try arena.dupe(pardes.Cell, surface.cells);
+ const theme = core.theme();
+ const ink = theme.fg orelse .{ 0xcc, 0xcc, 0xcc };
+ const page = theme.bg orelse pardes.colors.null_page;
+ const body = for (surface.regionList()) |region| {
+ if (region.kind == .body and region.owner == core.active) break region.rect;
+ } else return out;
+ const t = @as(f32, @floatFromInt(age)) / @as(f32, @floatFromInt(life_ns));
+ const fade = (1 - t) * (1 - t);
+ const dx = @as(f32, @floatFromInt(@as(i32, trail.from[0]) - trail.to[0]));
+ const dy = @as(f32, @floatFromInt(@as(i32, trail.from[1]) - trail.to[1]));
+ const span = @max(@abs(dx), @abs(dy));
+ const n: usize = @intFromFloat(std.math.clamp(@round(span / 4), 3, 5));
+ var k: usize = 1;
+ var last_cell: ?[2]i32 = null;
+ var step: f32 = 1;
+ while (k <= n and step <= span) : (step += 1) {
+ const x: i32 = @intFromFloat(@round(@as(f32, @floatFromInt(trail.to[0])) + dx * step / span));
+ const y: i32 = @intFromFloat(@round(@as(f32, @floatFromInt(trail.to[1])) + dy * step / span));
+ if (last_cell) |seen| if (seen[0] == x and seen[1] == y) continue;
+ last_cell = .{ x, y };
+ defer k += 1;
+ if (x < body.x or y < body.y or x >= @as(i32, body.x) + body.w or y >= @as(i32, body.y) + body.h) continue;
+ const cell = &out.cells[@as(usize, @intCast(y)) * out.cols + @as(usize, @intCast(x))];
+ const ground: [3]u8 = if (cell.default) page else switch (cell.style.bg) {
+ .rgb => |rgb| rgb,
+ else => page,
+ };
+ if (std.mem.eql(u8, &ground, &theme.sel_bg)) continue;
+ const strength = 0.55 * fade * (1 - @as(f32, @floatFromInt(k - 1)) / @as(f32, @floatFromInt(n)));
+ if (strength < 0.02) continue;
+ const was_default = cell.default;
+ cell.default = false;
+ if (was_default) cell.style = .{};
+ cell.style.bg = .{ .rgb = pardes.colors.mixOklab(ground, ink, strength) };
+ }
+ return out;
+ }
+};
+
fn paintCursor(win: vaxis.Window, x: u16, y: u16, bar: bool) void {
win.showCursor(x, y);
win.setCursorShape(if (bar) .beam else .default);
@@ -1943,6 +2160,8 @@ const Attach = struct {
.set_clipboard => |text| copyToClipboard(a.vx, a.tty, a.gpa, text),
.read_clipboard => requestClipboard(a.vx, a.tty),
.open_link => |url| look.openLink(url),
+ // A terminal has no post chain to draw through.
+ .post => {},
}
return null;
}
@@ -2067,3 +2286,116 @@ fn attachSession(init: std.process.Init, name: []const u8, tty: *vaxis.Tty, vx:
return attachLoop(&a);
}
+
+test "a jump leaves a trail of a few cells behind the cursor that fades in 120 ms" {
+ const gpa = std.testing.allocator;
+ const core = try pardes.Pardes.init(gpa, .{ .tty_only = true, .cols = 80, .rows = 24 });
+ defer core.deinit();
+ _ = try core.setTestFile("some text on a line\n" ** 20);
+ try std.testing.expect(core.executeBuiltinLine(0, "Theme forge"));
+ core.settings.jump_trail = true;
+ for (0..40) |_| core.update(.tick);
+ core.sync();
+ var arena: std.heap.ArenaAllocator = .init(gpa);
+ defer arena.deinit();
+ const s = try core.render(arena.allocator());
+ const body = for (s.regionList()) |region| {
+ if (region.kind == .body) break region.rect;
+ } else return error.NoBody;
+ var trail: JumpTrail = .{};
+ core.now_ns = std.time.ns_per_s;
+ s.cursor = .{ .x = body.x + 2, .y = body.y + 1 };
+ _ = try trail.apply(arena.allocator(), s, core, true);
+ // Ten cells right: a trail back toward where it was.
+ s.cursor = .{ .x = body.x + 12, .y = body.y + 1 };
+ core.now_ns += 16 * std.time.ns_per_ms;
+ const lit = try trail.apply(arena.allocator(), s, core, true);
+ try std.testing.expect(trail.active and core.shell_continuous);
+ var changed: usize = 0;
+ for (lit.cells, s.cells, 0..) |a, b, i| {
+ if (std.meta.eql(a.style.bg, b.style.bg)) continue;
+ changed += 1;
+ // On the cursor's row, behind it, never its own cell.
+ try std.testing.expectEqual(@as(usize, body.y + 1), i / s.cols);
+ try std.testing.expect(i % s.cols < body.x + 12 and i % s.cols > body.x + 2);
+ }
+ try std.testing.expect(changed >= 3 and changed <= 5);
+ // Gone by 120 ms; a step of a cell leaves none; nor a terminal without
+ // truecolor.
+ core.now_ns += 130 * std.time.ns_per_ms;
+ core.shell_continuous = false;
+ const faded = try trail.apply(arena.allocator(), s, core, true);
+ try std.testing.expect(faded == s and !trail.active and !core.shell_continuous);
+ s.cursor = .{ .x = body.x + 13, .y = body.y + 1 };
+ try std.testing.expect(try trail.apply(arena.allocator(), s, core, true) == s);
+ s.cursor = .{ .x = body.x + 2, .y = body.y + 1 };
+ try std.testing.expect(try trail.apply(arena.allocator(), s, core, false) == s);
+}
+
+test "a notice chip casts a cell's shadow right and down, inside its body, off the cursor" {
+ const gpa = std.testing.allocator;
+ const core = try pardes.Pardes.init(gpa, .{ .tty_only = true, .cols = 80, .rows = 24 });
+ defer core.deinit();
+ _ = try core.setTestFile("some text on a line\n" ** 20);
+ try std.testing.expect(core.executeBuiltinLine(0, "Theme forge"));
+ core.settings.chip_shadow = true;
+ try std.testing.expect(core.executeBuiltinLine(0, "Msg a notice"));
+ for (0..60) |_| core.update(.tick);
+ core.sync();
+ var arena: std.heap.ArenaAllocator = .init(gpa);
+ defer arena.deinit();
+ const s = try core.render(arena.allocator());
+ const chip = for (s.regionList()) |region| {
+ if (region.kind == .notice) break region.rect;
+ } else return error.NoNotice;
+ s.cursor = null;
+ const out = try chipShadows(arena.allocator(), s, core, true);
+ try std.testing.expect(out != s);
+ var changed: usize = 0;
+ for (out.cells, s.cells, 0..) |a, b, i| {
+ if (std.meta.eql(a, b)) continue;
+ changed += 1;
+ const x = i % s.cols;
+ const y = i / s.cols;
+ // Right of the chip or below it, never inside it.
+ try std.testing.expect(x == chip.x + chip.w or y == chip.y + chip.h);
+ try std.testing.expect(!(x >= chip.x and x < chip.x + chip.w and y >= chip.y and y < chip.y + chip.h));
+ }
+ try std.testing.expect(changed >= chip.w - 1);
+ // Off, or without truecolor: the frame as it was.
+ try std.testing.expect(try chipShadows(arena.allocator(), s, core, false) == s);
+ core.settings.chip_shadow = false;
+ try std.testing.expect(try chipShadows(arena.allocator(), s, core, true) == s);
+}
+
+test "a scrolled pane's thumb brightens and fades back in 250 ms" {
+ const gpa = std.testing.allocator;
+ const core = try pardes.Pardes.init(gpa, .{ .tty_only = true, .cols = 80, .rows = 24 });
+ defer core.deinit();
+ const pane = try core.setTestFile("some text on a line\n" ** 200);
+ try std.testing.expect(core.executeBuiltinLine(0, "Theme forge"));
+ core.settings.thumb_flash = true;
+ for (0..40) |_| core.update(.tick);
+ core.sync();
+ var arena: std.heap.ArenaAllocator = .init(gpa);
+ defer arena.deinit();
+ var flash: ThumbFlash = .{};
+ core.now_ns = std.time.ns_per_s;
+ const first = try core.render(arena.allocator());
+ try std.testing.expect(try flash.apply(arena.allocator(), first, core, true) == first);
+ pane.scrollBy(20);
+ core.now_ns += 16 * std.time.ns_per_ms;
+ const moved = try core.render(arena.allocator());
+ const lit = try flash.apply(arena.allocator(), moved, core, true);
+ try std.testing.expect(lit != moved and core.shell_continuous);
+ var brighter: usize = 0;
+ for (lit.cells, moved.cells) |a, b| {
+ if (!std.meta.eql(a.style.bg, b.style.bg)) brighter += 1;
+ }
+ try std.testing.expect(brighter >= 1);
+ core.now_ns += 300 * std.time.ns_per_ms;
+ core.shell_continuous = false;
+ const later = try core.render(arena.allocator());
+ try std.testing.expect(try flash.apply(arena.allocator(), later, core, true) == later);
+ try std.testing.expect(!core.shell_continuous);
+}