summaryrefslogtreecommitdiff
path: root/src/colors.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 06:04:28 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commit09ec515c5e94de741104038cd61f18b2b997e78c (patch)
tree4a7910c76c650df1695c4e3327538bfd8f68f493 /src/colors.zig
parent4452a8f467765cbfa0dd0444a9bf416ac8bd0bf5 (diff)
downloadpardes-09ec515c5e94de741104038cd61f18b2b997e78c.tar.gz
pardes-09ec515c5e94de741104038cd61f18b2b997e78c.zip
G5: lapis, 0x4200.cafe's manuscript, as a theme with its ornament
A native theme `lapis` (src/themes/lapis.zig) from web/theme/lapis.css: a lapis-deep page, lapis tags with gold names, vellum text, vermilion rubrics, gold rules and a vermilion tag rule, gold selection in lapis ink. Its ornament is the theme's new `decor` (colors.Decor), drawn by a pixel shell inside the chrome it already has (decision 1), all of it off every focus indicator: - page_dots: the site's gold dot grid (#e8c46a22, 22 px), drawn by the cell shader on page-coloured cells alone (flag 0x10000000), so it lies under the text and never under a selection, a tag or the cursor; fixed to the window, a page the panes lie on. - rail_checker: the dither frame's gold and lapis checker in the scroll track (a decor kind, flag 0x08000000), anchored to its rail; the thumb stays solid gold. - tag plaques (tag_border, tag_shadow, tag_stripe): every tag a raised plaque inside its own band, the site's trail: a 2px gold frame and a hard 5px vermilion shadow down and right, both within the band's rows and columns (down, what the slack under the text leaves), so no shadow reaches a body, a grip or the next pane. The focused pane's plaque is the code window's title bar, gold and lapis stripes of 2px, its words on plates of the focused ground; unfocused ones are plain; column and workspace tags take the object-label language (1px frame, 2px shadow toward the page). Drawn as decor under the cells (a group's under range), with blank tag cells see-through and the cells blended premultiplied (pipeline_over) on such frames; opaque windows only. - title_shadow: the titles' vermilion offset shadow under a file name's letters, sampled in the cell shader inside the letters' own cells (flag 0x08000000), none on a cursor. Chrome carries the decor (decor_*, name_ink, active_name_ink); a theme file folds a nested decor literal field by field. Left out, with why, in docs/effects.md: the fonts, the ornaments, a decor crossfade, and in the tty anything but the colours (a tag row has no spare cell for a shadow). An empty column is the page. The effect id is masked to 27 bits (ui.vert/ui.frag) for the fifth flag. Tests: the cell flags land only on page ground and file-name cells, never a cursor, nothing without decor; lapis's plaques stay inside their tag's band, clear of every grip, only the focused one striped; its checker is in the track; no other theme draws any; the separator and focus tests cover lapis. Goldens: theme and themesel (the ring gains lapis after acme). Gates as G4. Feel review: .scratch/render/lapis/ (lapis.png, lapis-2x.png, tags-focused-vs-unfocused-2x.png, a PanelSlide at 60 fps and a quarter).
Diffstat (limited to 'src/colors.zig')
-rw-r--r--src/colors.zig49
1 files changed, 48 insertions, 1 deletions
diff --git a/src/colors.zig b/src/colors.zig
index 4a18bba9..b44e84f9 100644
--- a/src/colors.zig
+++ b/src/colors.zig
@@ -123,6 +123,42 @@ pub fn mix(a: [3]u8, b: [3]u8) [3]u8 {
return out;
}
+/// A theme's ornament, drawn by a pixel shell inside the chrome it already
+/// has (docs/render-pipeline.md ยง10: no gaps between panes): the lapis
+/// theme's. Sizes are logical pixels. None of it touches a focus indicator:
+/// the dots lie on the page's own ground, the checker in the scroll track
+/// (never its thumb), a tag's plaque inside its band beside its grip (and
+/// the focused one only the more striking), and the shadow under a file
+/// name's own letters.
+pub const Decor = struct {
+ /// A dot grid on the page's ground, `page_dot_alpha` of 255 strong, a
+ /// dot one logical pixel across (fading out by a pixel and a half) every
+ /// `page_dot_px`, fixed to the window.
+ page_dots: ?[3]u8 = null,
+ page_dot_alpha: u8 = 0x22,
+ page_dot_px: u8 = 22,
+ /// A checker in the scroll track, squares of `rail_checker_px`, this
+ /// colour and the track's.
+ rail_checker: ?[3]u8 = null,
+ rail_checker_px: u8 = 3,
+ /// Every tag drawn as a raised plaque inside its own band (the site's
+ /// trail and title bars): a `tag_border` frame round the text, a hard
+ /// `tag_shadow` offset down and right by `tag_shadow_px`, both within
+ /// the band's rows and columns, never past them; the focused pane's
+ /// plaque striped `tag_stripe` and the tag's ground, `tag_stripe_px`
+ /// each, its words on plates of the ground. Column and workspace tags
+ /// take a quieter one (a one-pixel frame, a shadow of two, no stripes).
+ tag_border: ?[3]u8 = null,
+ tag_border_px: u8 = 2,
+ tag_shadow: ?[3]u8 = null,
+ tag_shadow_px: u8 = 5,
+ tag_stripe: ?[3]u8 = null,
+ tag_stripe_px: u8 = 2,
+ /// The file name's offset shadow, down and right, in a pane's tag.
+ title_shadow: ?[3]u8 = null,
+ title_shadow_px: u8 = 2,
+};
+
pub const Theme = struct {
name: []const u8,
bg: ?[3]u8,
@@ -188,6 +224,8 @@ pub const Theme = struct {
/// in pixels at a 17px tagline, the thumb one narrower (acme's 12px
/// Scrollwid, the default).
rail_px: ?u8 = null,
+ /// A pixel shell's ornament (Decor): lapis's.
+ decor: ?Decor = null,
};
const curated = struct {
@@ -209,6 +247,7 @@ const curated = struct {
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");
};
fn fold(comptime C: type) [@typeInfo(C).@"struct".decls.len]Theme {
@@ -219,7 +258,15 @@ fn fold(comptime C: type) [@typeInfo(C).@"struct".decls.len]Theme {
var t: Theme = undefined;
const source = @field(C, d.name).theme;
for (@typeInfo(Theme).@"struct".fields) |f| {
- @field(t, f.name) = if (@hasField(@TypeOf(source), f.name))
+ @field(t, f.name) = if (f.type == ?Decor and @hasField(@TypeOf(source), f.name)) decor: {
+ // A nested literal is folded field by field too.
+ var decor: Decor = .{};
+ const given = @field(source, f.name);
+ for (@typeInfo(Decor).@"struct".fields) |g| if (@hasField(@TypeOf(given), g.name)) {
+ @field(decor, g.name) = @field(given, g.name);
+ };
+ break :decor decor;
+ } else if (@hasField(@TypeOf(source), f.name))
@field(source, f.name)
else if (f.default_value_ptr) |ptr|
@as(*const f.type, @ptrCast(@alignCast(ptr))).*