summaryrefslogtreecommitdiff
path: root/src/runtime_config.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-06 18:11:36 -0300
committerGabriel Schneider <[email protected]>2026-09-07 13:59:12 -0300
commit60367d8fe23f6af98ec28e3cf6c2094dfe332df0 (patch)
tree310fc734173cf771881f4691c71909135fadde97 /src/runtime_config.zig
parentfa82cac885cb4738fe36d1e49b4749b5a3e31a4a (diff)
downloadpardes-60367d8fe23f6af98ec28e3cf6c2094dfe332df0.tar.gz
pardes-60367d8fe23f6af98ec28e3cf6c2094dfe332df0.zip
Refactor panes and filesystem; replace FUSE with 9P
Consolidate pane, layout, memory and host code. Serve 9P by default over Unix sockets, with runtime mounts and optional TCP/QUIC transports. Remove FUSE and obsolete proof-of-concept examples. Fix highlighting and terminal-history performance, expand differential and stress-test infrastructure, sort navigation results while preserving the next occurrence, add syntax-colored Braille minimaps, remove SPC-k, and document 9P interaction as a repository skill.
Diffstat (limited to 'src/runtime_config.zig')
-rw-r--r--src/runtime_config.zig579
1 files changed, 0 insertions, 579 deletions
diff --git a/src/runtime_config.zig b/src/runtime_config.zig
deleted file mode 100644
index 69d2cdfe..00000000
--- a/src/runtime_config.zig
+++ /dev/null
@@ -1,579 +0,0 @@
-//! Runtime choices in one plain, owned record.
-//!
-//! `Setting` is compile-time metadata for the builtin registry; `State` is the
-//! data it mutates and the Config report reads. Neither contains callbacks.
-const std = @import("std");
-const panel_animation = @import("panel_animation.zig");
-const limits = @import("limits.zig");
-
-/// Tagline glyphs retain body-cell geometry, so allowing a face larger than
-/// the body would clip into neighbouring cells. Zero would make the role
-/// invisible. Keep the runtime command on the same 1...100 contract as the
-/// build-time default in config.zig.
-pub const tagline_percent_min: u8 = 1;
-pub const tagline_percent_max: u8 = 100;
-
-pub fn Text(comptime capacity: usize) type {
- return struct {
- bytes: [capacity]u8 = @splat(0),
- len: std.math.IntFittingRange(0, capacity) = 0,
-
- pub fn get(value: *const @This()) []const u8 {
- return value.bytes[0..value.len];
- }
-
- pub fn set(value: *@This(), text: []const u8) bool {
- if (text.len > capacity) return false;
- @memcpy(value.bytes[0..text.len], text);
- value.len = @intCast(text.len);
- return true;
- }
-
- pub fn clear(value: *@This()) void {
- value.len = 0;
- }
- };
-}
-
-pub const State = struct {
- theme: usize = 0,
- colors: bool = true,
- wrap: bool = true,
- tag_bottom: bool = false,
- debug: bool = false,
-
- /// Empty requested text means config.default_shell. Resolution belongs to
- /// the native host (the core deliberately has no filesystem), so retain
- /// the executable it actually chose separately and mark a changed request
- /// pending until the next terminal spawn acknowledges it.
- shell: struct {
- requested: Text(255) = .{},
- // One data schema across native, browser and freestanding builds; only
- // its one absolute-path field follows `limits.host_path_cap`.
- effective: Text(limits.host_path_cap) = .{},
- pending: bool = true,
- } = .{},
-
- /// The shell acknowledges a font before `effective` changes. A rejected
- /// request therefore remains queryable without claiming it is on screen.
- font: struct {
- requested_path: Text(limits.host_path_cap) = .{},
- requested_name: Text(255) = .{},
- effective_name: Text(255) = .{},
- pending: bool = false,
- effective_size_hundredths: u16 = 0,
- effective_size_unit: FontSizeUnit = .unknown,
- // Pardes.init copies config.gui_tagline_font_percent here. Keeping
- // the compiled choice in the live record makes Config truthful while
- // avoiding the config -> builtins -> runtime_config import cycle.
- tagline_percent: u8 = 100,
- } = .{},
-
- panel_transition: panel_animation.Transition = .off,
- scene_effects: panel_animation.SceneEffect = .{},
-
- pub fn toggleTransition(state: *State, effect: panel_animation.Transition) void {
- std.debug.assert(effect != .off);
- state.panel_transition = if (state.panel_transition == effect) .off else effect;
- }
-};
-
-pub const FontSizeUnit = enum { unknown, pixels, points };
-
-/// Replace the requested font tuple atomically. Both fixed strings are
-/// preflighted before either changes, so a rejected long name cannot leave a
-/// new path paired with stale metadata.
-pub fn requestFont(state: *State, path: []const u8, name: []const u8) bool {
- if (path.len > state.font.requested_path.bytes.len or
- name.len > state.font.requested_name.bytes.len) return false;
- std.debug.assert(state.font.requested_path.set(path));
- std.debug.assert(state.font.requested_name.set(name));
- state.font.pending = true;
- return true;
-}
-
-/// Backend facilities are plain data supplied once by the platform-facing
-/// registry. The same value gates command generation, leader paths, source
-/// queries, and the Config report, so "unsupported" cannot mean four subtly
-/// different things at those four call sites.
-pub const Capabilities = struct {
- font_picker: bool,
- panel_transitions: bool,
- scene_shaders: bool,
- /// A smaller tagline face is also supported by the browser, which does
- /// not own a native font picker. Keep this fact deliberately independent.
- tagline_font_size: bool,
-};
-
-pub const Capability = std.meta.FieldEnum(Capabilities);
-
-pub const Toggle = enum { colors, wrap, tag_bottom, debug };
-pub const Scene = std.meta.FieldEnum(panel_animation.SceneEffect);
-
-pub const Action = union(enum) {
- toggle: Toggle,
- shell,
- theme,
- font,
- tagline_size,
- transition: panel_animation.Transition,
- scene: Scene,
-};
-
-pub const Setting = struct {
- word: []const u8,
- action: Action,
- availability: ?Capability = null,
-
- pub fn takesArg(setting: Setting) bool {
- return switch (setting.action) {
- .shell, .theme, .font, .tagline_size => true,
- else => false,
- };
- }
-
- pub fn enabled(setting: Setting, capabilities: Capabilities) bool {
- const capability = setting.availability orelse return true;
- return switch (capability) {
- inline else => |field| @field(capabilities, @tagName(field)),
- };
- }
-};
-
-/// This table is both the generated-setting builtin input and the Config
-/// report order. Adding mutable config without adding a query row is therefore
-/// impossible unless it is deliberately kept out of this user-facing state.
-pub const settings = [_]Setting{
- .{ .word = "Colors", .action = .{ .toggle = .colors } },
- .{ .word = "Wrap", .action = .{ .toggle = .wrap } },
- .{ .word = "Tagbottom", .action = .{ .toggle = .tag_bottom } },
- .{ .word = "Debug", .action = .{ .toggle = .debug } },
- .{ .word = "Theme", .action = .theme },
- .{ .word = "Shell", .action = .shell },
- .{ .word = "Font", .action = .font, .availability = .font_picker },
- .{ .word = "TaglineSize", .action = .tagline_size, .availability = .font_picker },
- .{ .word = "PanelSlide", .action = .{ .transition = .slide }, .availability = .panel_transitions },
- .{ .word = "PanelZoom", .action = .{ .transition = .zoom }, .availability = .panel_transitions },
- .{ .word = "PanelDissolve", .action = .{ .transition = .dissolve }, .availability = .panel_transitions },
- .{ .word = "PanelAscii", .action = .{ .transition = .ascii }, .availability = .panel_transitions },
- .{ .word = "PanelVertical", .action = .{ .transition = .vertical }, .availability = .panel_transitions },
- .{ .word = "PanelEdges", .action = .{ .transition = .edges }, .availability = .panel_transitions },
- .{ .word = "PanelFall", .action = .{ .transition = .fall }, .availability = .panel_transitions },
- .{ .word = "PanelWave", .action = .{ .transition = .wave }, .availability = .panel_transitions },
- .{ .word = "PanelCurtain", .action = .{ .transition = .curtain }, .availability = .panel_transitions },
- .{ .word = "PanelScramble", .action = .{ .transition = .scramble }, .availability = .panel_transitions },
- .{ .word = "PanelType", .action = .{ .transition = .typewriter }, .availability = .panel_transitions },
- .{ .word = "Crt", .action = .{ .scene = .crt }, .availability = .scene_shaders },
- .{ .word = "Ripple", .action = .{ .scene = .ripple }, .availability = .scene_shaders },
- .{ .word = "Glitch", .action = .{ .scene = .glitch }, .availability = .scene_shaders },
-};
-
-pub fn find(name: []const u8) ?Setting {
- for (settings) |setting| if (std.mem.eql(u8, setting.word, name)) return setting;
- return null;
-}
-
-/// The table is also the sole action-to-command vocabulary. Compile-time
-/// callers use this for picker rows; the Config report uses it for the active
-/// transition. No parallel enum or transition-name switch can drift.
-pub fn findAction(action: Action) ?Setting {
- for (settings) |setting| if (std.meta.eql(setting.action, action)) return setting;
- return null;
-}
-
-fn actionCount(comptime action: Action) comptime_int {
- var count = 0;
- for (settings) |setting| count += @intFromBool(std.meta.eql(setting.action, action));
- return count;
-}
-
-comptime {
- @setEvalBranchQuota(20_000);
- for (settings, 0..) |setting, i| {
- if (setting.word.len == 0) @compileError("runtime setting has an empty command word");
- for (settings[i + 1 ..]) |later| if (std.mem.eql(u8, setting.word, later.word))
- @compileError("duplicate runtime setting command word: " ++ setting.word);
- switch (setting.action) {
- .font, .tagline_size => if (setting.availability != .font_picker)
- @compileError("native font settings must use the font-picker capability"),
- .transition => if (setting.availability != .panel_transitions)
- @compileError("panel effects must use the panel-transition capability"),
- .scene => if (setting.availability != .scene_shaders)
- @compileError("scene effects must use the scene-shader capability"),
- else => if (setting.availability != null)
- @compileError("unconditional settings cannot carry a backend capability"),
- }
- }
- for (std.enums.values(Toggle)) |field| if (actionCount(.{ .toggle = field }) != 1)
- @compileError("runtime toggle must occur exactly once: " ++ @tagName(field));
- if (actionCount(.shell) != 1 or actionCount(.theme) != 1 or actionCount(.font) != 1 or
- actionCount(.tagline_size) != 1)
- @compileError("Shell, Theme, Font, and TaglineSize actions must each occur exactly once");
- for (std.enums.values(panel_animation.Transition)) |effect| {
- const expected: comptime_int = @intFromBool(effect != .off);
- if (actionCount(.{ .transition = effect }) != expected)
- @compileError("non-off panel transition must occur exactly once: " ++ @tagName(effect));
- }
- for (std.enums.values(Scene)) |effect| {
- if (actionCount(.{ .scene = effect }) != 1)
- @compileError("scene effect must occur exactly once: " ++ @tagName(effect));
- if (@FieldType(panel_animation.SceneEffect, @tagName(effect)) != bool)
- @compileError("scene effect fields must be booleans: " ++ @tagName(effect));
- }
-}
-
-/// Apply settings whose operation is independent of themes, font discovery,
-/// or a shell. Those three remain explicit at the generated builtin's edge.
-pub fn applySimple(state: *State, setting: Setting, argument: ?[]const u8) bool {
- switch (setting.action) {
- .toggle => |field| switch (field) {
- .colors => state.colors = !state.colors,
- .wrap => state.wrap = !state.wrap,
- .tag_bottom => state.tag_bottom = !state.tag_bottom,
- .debug => state.debug = !state.debug,
- },
- .shell => {
- const value = std.mem.trim(u8, argument orelse return false, " \t\r\n");
- if (value.len == 0 or !state.shell.requested.set(value)) return false;
- state.shell.pending = true;
- },
- .tagline_size => {
- const text = std.mem.trim(u8, argument orelse return false, " \t\r\n");
- const percent = std.fmt.parseInt(u16, text, 10) catch return false;
- if (percent < tagline_percent_min or percent > tagline_percent_max) return false;
- // Parse and validate before the sole write: malformed commands
- // cannot partially alter the live configuration.
- state.font.tagline_percent = @intCast(percent);
- },
- .transition => |effect| state.toggleTransition(effect),
- .scene => |effect| switch (effect) {
- inline else => |field| {
- const value = &@field(state.scene_effects, @tagName(field));
- value.* = !value.*;
- },
- },
- .theme, .font => return false,
- }
- return true;
-}
-
-/// The runtime facts which do not belong to mutable `State`, supplied by the
-/// core when it materialises +Config. Slices are borrowed for this one write.
-pub const ReportContext = struct {
- startup_config_path: ?[]const u8,
- platform: []const u8,
- theme_name: []const u8,
- compiled_default_shell: []const u8,
- /// Null outside the SDL GUI. The borrowed label comes from effect_sources,
- /// which knows whether its embedded GLSL import is live or paired prebuilt.
- gui_shader_source_mode: ?[]const u8 = null,
- hover_delay_frames: ?u16,
- native_images: bool,
- capabilities: Capabilities,
- state: *const State,
-};
-
-fn onOff(value: bool) []const u8 {
- return if (value) "on" else "off";
-}
-
-fn shown(text: []const u8) []const u8 {
- return if (text.len == 0) "(none)" else text;
-}
-
-/// Name the setting which selected the one active transition. This keeps the
-/// report vocabulary identical to the generated builtin registry.
-fn transitionSettingName(transition: panel_animation.Transition) []const u8 {
- if (transition == .off) return "off";
- return findAction(.{ .transition = transition }).?.word;
-}
-
-/// Write the complete live Config report without allocation. Setting-backed
-/// rows follow `settings` order; host facts follow them as a compact footer.
-pub fn writeReport(out: *std.Io.Writer, context: ReportContext) !void {
- const state = context.state;
- var wrote_transition = false;
- for (settings) |setting| switch (setting.action) {
- .toggle => |field| {
- const value = switch (field) {
- .colors => state.colors,
- .wrap => state.wrap,
- .tag_bottom => state.tag_bottom,
- .debug => state.debug,
- };
- try out.print("{s}: {s}\n", .{ setting.word, onOff(value) });
- },
- .theme => try out.print("{s}: {s}\n", .{ setting.word, context.theme_name }),
- .shell => {
- const chosen = state.shell.requested.get();
- try out.print(
- "{s} requested (new panes): {s}{s}\n" ++
- "{s} effective (last spawn): {s}\n" ++
- "{s} pending: {s}\n",
- .{
- setting.word,
- if (chosen.len == 0) context.compiled_default_shell else chosen,
- if (chosen.len == 0) " (default)" else "",
- setting.word,
- shown(state.shell.effective.get()),
- setting.word,
- onOff(state.shell.pending),
- },
- );
- },
- .font => if (!setting.enabled(context.capabilities))
- try out.print("{s}: unsupported\n", .{setting.word})
- else
- try out.print(
- "{s} requested: {s}\n" ++
- "{s} requested path: {s}\n" ++
- "{s} effective: {s}\n" ++
- "{s} pending: {s}\n" ++
- "{s} effective size: {d}.{d:0>2} {s}\n",
- .{
- setting.word,
- shown(state.font.requested_name.get()),
- setting.word,
- shown(state.font.requested_path.get()),
- setting.word,
- shown(state.font.effective_name.get()),
- setting.word,
- onOff(state.font.pending),
- setting.word,
- state.font.effective_size_hundredths / 100,
- state.font.effective_size_hundredths % 100,
- @tagName(state.font.effective_size_unit),
- },
- ),
- .tagline_size => if (!context.capabilities.tagline_font_size)
- try out.print("{s}: unsupported\n", .{setting.word})
- else if (!setting.enabled(context.capabilities))
- try out.print("{s}: {d}% (build-time only)\n", .{ setting.word, state.font.tagline_percent })
- else
- try out.print("{s}: {d}%\n", .{ setting.word, state.font.tagline_percent }),
- .transition => {
- if (wrote_transition) continue;
- wrote_transition = true;
- if (!setting.enabled(context.capabilities))
- try out.writeAll("Panel transition: unsupported\n")
- else
- try out.print("Panel transition: {s}\n", .{transitionSettingName(state.panel_transition)});
- },
- .scene => |effect| {
- if (!setting.enabled(context.capabilities)) {
- try out.print("{s}: unsupported\n", .{setting.word});
- continue;
- }
- const enabled = switch (effect) {
- inline else => |field| @field(state.scene_effects, @tagName(field)),
- };
- try out.print("{s}: {s}\n", .{ setting.word, onOff(enabled) });
- },
- };
-
- if (context.startup_config_path) |path|
- try out.print("Startup config: {s}\n", .{path})
- else
- try out.writeAll("Startup config: no per-user config path\n");
- try out.print(
- "Platform: {s}\n" ++
- "Compiled default shell: {s}\n",
- .{ context.platform, context.compiled_default_shell },
- );
- if (context.gui_shader_source_mode) |mode|
- try out.print("GUI shader source: {s}\n", .{mode});
- if (context.hover_delay_frames) |frames|
- try out.print("Look hover delay: {d} frames\n", .{frames})
- else
- try out.writeAll("Look hover delay: off\n");
- try out.print("Native images: {s}\n", .{onOff(context.native_images)});
-}
-
-test "setting names are unique and argument metadata follows actions" {
- for (settings, 0..) |setting, i| {
- try std.testing.expect(setting.word.len > 0);
- for (settings[i + 1 ..]) |later|
- try std.testing.expect(!std.mem.eql(u8, setting.word, later.word));
- try std.testing.expectEqual(switch (setting.action) {
- .shell, .theme, .font, .tagline_size => true,
- else => false,
- }, setting.takesArg());
- }
-}
-
-test "simple setting application mutates only its plain field" {
- var state: State = .{};
- try std.testing.expect(applySimple(&state, find("Colors").?, null));
- try std.testing.expect(!state.colors);
- try std.testing.expect(applySimple(&state, find("Shell").?, " fish\n"));
- try std.testing.expectEqualStrings("fish", state.shell.requested.get());
- try std.testing.expect(state.shell.pending);
- try std.testing.expect(applySimple(&state, find("PanelAscii").?, null));
- try std.testing.expectEqual(panel_animation.Transition.ascii, state.panel_transition);
- try std.testing.expect(applySimple(&state, find("PanelAscii").?, null));
- try std.testing.expectEqual(panel_animation.Transition.off, state.panel_transition);
- try std.testing.expect(applySimple(&state, find("Crt").?, null));
- try std.testing.expect(state.scene_effects.crt);
-}
-
-test "tagline size validates before mutating live state" {
- const setting = find("TaglineSize").?;
- var state: State = .{};
-
- for ([_][]const u8{ "1", " 82\n", "100" }) |argument| {
- try std.testing.expect(applySimple(&state, setting, argument));
- try std.testing.expectEqual(try std.fmt.parseInt(u8, std.mem.trim(u8, argument, " \t\r\n"), 10), state.font.tagline_percent);
- }
-
- state.font.tagline_percent = 67;
- for ([_]?[]const u8{ null, "", "0", "101", "-1", "50%", "999999999999999999999" }) |argument| {
- try std.testing.expect(!applySimple(&state, setting, argument));
- try std.testing.expectEqual(@as(u8, 67), state.font.tagline_percent);
- }
-}
-
-test "font request tuple rejects atomically" {
- var state: State = .{};
- try std.testing.expect(requestFont(&state, "/fonts/old.ttf", "Old"));
- var too_long: [256]u8 = @splat('x');
- try std.testing.expect(!requestFont(&state, "/fonts/new.ttf", &too_long));
- try std.testing.expectEqualStrings("/fonts/old.ttf", state.font.requested_path.get());
- try std.testing.expectEqualStrings("Old", state.font.requested_name.get());
-}
-
-test "Config report observes every simple setting and all live context" {
- var state: State = .{};
- var storage: [4096]u8 = undefined;
- const context: ReportContext = .{
- .startup_config_path = "/tmp/pardes/init",
- .platform = "gui",
- .theme_name = "acme",
- .compiled_default_shell = "/bin/sh",
- .gui_shader_source_mode = "live GLSL compiled during this build",
- .hover_delay_frames = 18,
- .native_images = true,
- .capabilities = .{
- .font_picker = true,
- .panel_transitions = true,
- .scene_shaders = true,
- .tagline_font_size = true,
- },
- .state = &state,
- };
-
- for (settings) |setting| {
- switch (setting.action) {
- .theme, .font => continue,
- else => {},
- }
- const argument: ?[]const u8 = switch (setting.action) {
- .shell => "fish",
- .tagline_size => "73",
- else => null,
- };
- try std.testing.expect(applySimple(&state, setting, argument));
-
- var out: std.Io.Writer = .fixed(&storage);
- try writeReport(&out, context);
- const report = storage[0..out.end];
- const expected = switch (setting.action) {
- .toggle => |field| switch (field) {
- .colors => "Colors: off\n",
- .wrap => "Wrap: off\n",
- .tag_bottom => "Tagbottom: on\n",
- .debug => "Debug: on\n",
- },
- .shell => "Shell requested (new panes): fish\n",
- .tagline_size => "TaglineSize: 73%\n",
- .transition => |transition| switch (transition) {
- .off => unreachable,
- .slide => "Panel transition: PanelSlide\n",
- .zoom => "Panel transition: PanelZoom\n",
- .dissolve => "Panel transition: PanelDissolve\n",
- .ascii => "Panel transition: PanelAscii\n",
- .vertical => "Panel transition: PanelVertical\n",
- .edges => "Panel transition: PanelEdges\n",
- .fall => "Panel transition: PanelFall\n",
- .wave => "Panel transition: PanelWave\n",
- .curtain => "Panel transition: PanelCurtain\n",
- .scramble => "Panel transition: PanelScramble\n",
- .typewriter => "Panel transition: PanelType\n",
- },
- .scene => |effect| switch (effect) {
- .crt => "Crt: on\n",
- .ripple => "Ripple: on\n",
- .glitch => "Glitch: on\n",
- },
- .theme, .font => unreachable,
- };
- try std.testing.expect(std.mem.indexOf(u8, report, expected) != null);
- }
-
- try std.testing.expect(state.font.requested_name.set("Wanted Mono"));
- try std.testing.expect(state.font.requested_path.set("/fonts/wanted.ttf"));
- try std.testing.expect(state.font.effective_name.set("Effective Mono"));
- state.font.pending = true;
- state.font.effective_size_hundredths = 1375;
- state.font.effective_size_unit = .points;
- state.font.tagline_percent = 82;
-
- var out: std.Io.Writer = .fixed(&storage);
- try writeReport(&out, context);
- const report = storage[0..out.end];
- for ([_][]const u8{
- "Theme: acme\n",
- "Font requested: Wanted Mono\n",
- "Font requested path: /fonts/wanted.ttf\n",
- "Font effective: Effective Mono\n",
- "Font pending: on\n",
- "Font effective size: 13.75 points\n",
- "TaglineSize: 82%\n",
- "Startup config: /tmp/pardes/init\n",
- "Platform: gui\n",
- "Compiled default shell: /bin/sh\n",
- "GUI shader source: live GLSL compiled during this build\n",
- "Look hover delay: 18 frames\n",
- "Native images: on\n",
- }) |expected| try std.testing.expect(std.mem.indexOf(u8, report, expected) != null);
-
- var defaults: State = .{};
- var defaults_context = context;
- defaults_context.startup_config_path = null;
- defaults_context.platform = "tty";
- defaults_context.gui_shader_source_mode = null;
- defaults_context.hover_delay_frames = null;
- defaults_context.native_images = false;
- defaults_context.capabilities = .{
- .font_picker = false,
- .panel_transitions = true,
- .scene_shaders = false,
- .tagline_font_size = false,
- };
- defaults_context.state = &defaults;
- out = .fixed(&storage);
- try writeReport(&out, defaults_context);
- const defaults_report = storage[0..out.end];
- try std.testing.expect(std.mem.indexOf(u8, defaults_report, "Shell requested (new panes): /bin/sh (default)\n") != null);
- try std.testing.expect(std.mem.indexOf(u8, defaults_report, "Shell effective (last spawn): (none)\n") != null);
- try std.testing.expect(std.mem.indexOf(u8, defaults_report, "Shell pending: on\n") != null);
- try std.testing.expect(std.mem.indexOf(u8, defaults_report, "Startup config: no per-user config path\n") != null);
- try std.testing.expect(std.mem.indexOf(u8, defaults_report, "GUI shader source:") == null);
- try std.testing.expect(std.mem.indexOf(u8, defaults_report, "Font: unsupported\n") != null);
- try std.testing.expect(std.mem.indexOf(u8, defaults_report, "Font requested:") == null);
- try std.testing.expect(std.mem.indexOf(u8, defaults_report, "Panel transition: off\n") != null);
- try std.testing.expect(std.mem.indexOf(u8, defaults_report, "Crt: unsupported\n") != null);
- try std.testing.expect(std.mem.indexOf(u8, defaults_report, "Ripple: unsupported\n") != null);
- try std.testing.expect(std.mem.indexOf(u8, defaults_report, "Glitch: unsupported\n") != null);
- try std.testing.expect(std.mem.indexOf(u8, defaults_report, "TaglineSize: unsupported\n") != null);
- try std.testing.expect(std.mem.indexOf(u8, defaults_report, "Look hover delay: off\n") != null);
- try std.testing.expect(std.mem.indexOf(u8, defaults_report, "Native images: off\n") != null);
-
- defaults_context.platform = "web";
- defaults_context.capabilities.panel_transitions = false;
- defaults_context.capabilities.tagline_font_size = true;
- out = .fixed(&storage);
- try writeReport(&out, defaults_context);
- const web_report = storage[0..out.end];
- try std.testing.expect(std.mem.indexOf(u8, web_report, "Panel transition: unsupported\n") != null);
- try std.testing.expect(std.mem.indexOf(u8, web_report, "TaglineSize: 100% (build-time only)\n") != null);
-}