summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-30 19:26:12 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:17 -0300
commit939c369aee270067d9f030e2b234d93bae549988 (patch)
tree6e3541479cbc228bdd1566f4805496c538ec2f3e
parent109a558a0488f3269f1864985e9b3db5f58b9552 (diff)
downloadpardes-939c369aee270067d9f030e2b234d93bae549988.tar.gz
pardes-939c369aee270067d9f030e2b234d93bae549988.zip
After a ThemeFile, the root ctl and DumpConfig say ThemeFile and its path, so what they report writes back
Both said Theme and the name inside the .zon, which no Theme knows, so writing the report back to /ctl failed with "no theme". While a loaded ThemeFile is the theme in effect they now say the word that loaded it. A Dump carries the line too, so a Restore reloads the file. Co-Authored-By: Claude Opus 5.5 <[email protected]>
-rw-r--r--src/Output.zig1
-rw-r--r--src/colors.zig8
-rw-r--r--src/config.zig8
-rw-r--r--src/ninep/ctl.zig39
4 files changed, 54 insertions, 2 deletions
diff --git a/src/Output.zig b/src/Output.zig
index 01797cbe..47fcc7e4 100644
--- a/src/Output.zig
+++ b/src/Output.zig
@@ -870,6 +870,7 @@ pub fn openConfig(p: *Pardes, id: usize) !void {
.startup_config_path = p.opts.startup_config_path,
.platform = @tagName(pardes.platform),
.theme_name = p.theme().name,
+ .theme_file = pardes.colors.loadedThemeFile(p),
.compiled_default_shell = config.defaultShell(),
.hover_delay_frames = config.look_preview_delay_frames,
.native_images = p.native_images,
diff --git a/src/colors.zig b/src/colors.zig
index c78043cc..8d541c13 100644
--- a/src/colors.zig
+++ b/src/colors.zig
@@ -735,6 +735,14 @@ pub fn requestThemeFile(p: *Pardes, id: usize, argument: []const u8) void {
p.emit(.{ .theme_file = .{ .generation = generation, .on = true } });
}
+/// The .zon a ThemeFile loaded, while it is the theme in effect: what
+/// /ctl and DumpConfig say in place of `Theme <name>`, since the name
+/// inside the file is no theme `Theme` knows.
+pub fn loadedThemeFile(p: *const Pardes) ?[]const u8 {
+ const path = p.theme_file_path.get();
+ return if (p.custom_theme != null and path.len > 0) path else null;
+}
+
pub const ThemeFileRequest = struct {
path: []const u8,
pane: u8,
diff --git a/src/config.zig b/src/config.zig
index 592281d3..a7377cfa 100644
--- a/src/config.zig
+++ b/src/config.zig
@@ -1267,6 +1267,9 @@ pub const Runtime = struct {
startup_config_path: ?[]const u8,
platform: []const u8,
theme_name: []const u8,
+ /// The ThemeFile in effect, said as `ThemeFile <path>` in place
+ /// of the theme's name.
+ theme_file: ?[]const u8 = null,
compiled_default_shell: []const u8,
hover_delay_frames: ?u16,
/// Where dumps go when `DumpDir` is unset; empty without a host
@@ -1305,7 +1308,10 @@ pub const Runtime = struct {
};
try out.print("{s} {s}\n", .{ setting.word, onOff(value) });
},
- .theme => try out.print("{s} {s}\n", .{ setting.word, context.theme_name }),
+ .theme => if (context.theme_file) |path|
+ try out.print("ThemeFile {s}\n", .{path})
+ else
+ try out.print("{s} {s}\n", .{ setting.word, context.theme_name }),
.window_opacity => {
if (setting.enabled(context.capabilities))
try out.print("{s} {d}\n", .{ setting.word, state.window_opacity })
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 902a9647..bc27f72b 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -554,7 +554,9 @@ pub fn writeSettingsOf(p: *Pardes, s: *const config.Runtime, w: *std.Io.Writer)
const word = setting.word;
switch (setting.action) {
.toggle => |field| try w.print("{s} {s}\n", .{ word, if (@field(s, @tagName(field))) "on" else "off" }),
- .theme => try w.print("{s} {s}\n", .{ word, if (s == &p.settings) p.theme().name else pardes.themes[s.theme].name }),
+ .theme => if (s == &p.settings) {
+ if (pardes.colors.loadedThemeFile(p)) |path| try w.print("ThemeFile {s}\n", .{path}) else try w.print("{s} {s}\n", .{ word, p.theme().name });
+ } else try w.print("{s} {s}\n", .{ word, pardes.themes[s.theme].name }),
.shell => {
const chosen = s.shell.requested.get();
try w.print("{s} {s}\n", .{ word, if (chosen.len > 0) chosen else config.defaultShell() });
@@ -1829,6 +1831,41 @@ test "an error's words give the errno a mount reads: EINVAL for what is malforme
}
}
+test "after a ThemeFile the root ctl and DumpConfig say ThemeFile and its path, which writes back" {
+ if (comptime !pardes.hosted) return;
+ const gpa = testing.allocator;
+ const p = try withFile(gpa, "x\n");
+ defer p.deinit();
+ const root_ctl = @intFromEnum(tree.TopFile.ctl);
+ const path = "/tmp/pardes-mine-theme.zon";
+ try testing.expectEqual(Status.ok, wr(p, root_ctl, "ThemeFile " ++ path ++ "\n").reply.status);
+ var custom = pardes.themes[0];
+ custom.name = "mine";
+ var encoded: std.Io.Writer.Allocating = .init(gpa);
+ defer encoded.deinit();
+ try std.zon.stringify.serialize(custom, .{}, &encoded.writer);
+ try testing.expect(pardes.colors.loadThemeFile(p, p.theme_file_generation, encoded.written(), false));
+ const settings = rd(p, root_ctl, 0, 1 << 16).bytes;
+ try testing.expect(std.mem.indexOf(u8, settings, "ThemeFile " ++ path ++ "\n") != null);
+ try testing.expect(std.mem.indexOf(u8, settings, "Theme mine") == null);
+ var storage: [16384]u8 = undefined;
+ var out: std.Io.Writer = .fixed(&storage);
+ try config.Runtime.writeReport(&out, .{
+ .startup_config_path = null,
+ .platform = @tagName(pardes.platform),
+ .theme_name = p.theme().name,
+ .theme_file = pardes.colors.loadedThemeFile(p),
+ .compiled_default_shell = config.defaultShell(),
+ .hover_delay_frames = null,
+ .native_images = false,
+ .capabilities = builtins.capabilities,
+ .state = &p.settings,
+ });
+ try testing.expect(std.mem.indexOf(u8, out.buffered(), "ThemeFile " ++ path ++ "\n") != null);
+ // Written back, it is the same request again, not "no theme mine".
+ try testing.expectEqual(Status.ok, wr(p, root_ctl, "ThemeFile " ++ path ++ "\n").reply.status);
+}
+
test "Joincol with no column to the right and Theme with no such theme say so" {
const gpa = testing.allocator;
const p = try withFile(gpa, "x\n");