From 99777f0f3f2d709334b15ce8b55a260892b64891 Mon Sep 17 00:00:00 2001 From: "fizzy-linux[bot]" <346940526+fizzy-linux[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 01:52:27 -0500 Subject: [PATCH 1/2] linux: work-564 (codex) Agent-Team: linux Worker: codex --- app/layout/Rail.zig | 11 +++-------- src/editor/layout.zig | 3 ++- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/app/layout/Rail.zig b/app/layout/Rail.zig index a2cc01cd1..b00c8fa56 100644 --- a/app/layout/Rail.zig +++ b/app/layout/Rail.zig @@ -3,7 +3,6 @@ const std = @import("std"); const dvui = @import("dvui"); const core = @import("core"); -const PluginStore = @import("../store/PluginStore.zig"); const Layout = @import("Layout.zig"); const Chooser = Layout.Chooser; @@ -20,8 +19,8 @@ pub const Host = struct { drawFooterItems: *const fn (ctx: ?*anyopaque, size: f32) void, /// Whether the side pane is on screen, as opposed to closed or folded away. sidePaneShown: *const fn (ctx: ?*anyopaque) bool, - /// How many installed plugins wait for the person to say whether to load them. - undecidedPluginCount: *const fn (ctx: ?*anyopaque) usize, + /// How many plugins wait for a load decision in this view; zero for other views. + undecidedPluginCount: *const fn (ctx: ?*anyopaque, view_id: []const u8) usize, /// An icon's rect in physical pixels, to register with a custom title bar it overlaps. iconRect: *const fn (ctx: ?*anyopaque, r: dvui.Rect.Physical) void, }; @@ -177,11 +176,7 @@ fn pickAction(host: Host, c: *const Chooser, so_far: Action) Action { /// One rail icon: the chooser's item, drawn as the view's glyph with the store's attention badge. /// Returns the bottom of the item's cell, in physical pixels. fn drawIcon(host: Host, c: *Chooser, view: *Layout.Surface, index: usize) f32 { - // Only the store view can carry one; nothing else in the rail has a pending-decision notion. - const undecided_count: usize = if (std.mem.eql(u8, view.id, PluginStore.view_id)) - host.vtable.undecidedPluginCount(host.ctx) - else - 0; + const undecided_count = host.vtable.undecidedPluginCount(host.ctx, view.id); // Say what the dot means — a bare badge on a 20px rail icon is otherwise a riddle. const detail: ?[]const u8 = if (undecided_count > 0) dvui.currentWindow().arena().print("{d} plugin{s} ready to load", .{ diff --git a/src/editor/layout.zig b/src/editor/layout.zig index 40d70df6b..f86a81123 100644 --- a/src/editor/layout.zig +++ b/src/editor/layout.zig @@ -217,7 +217,8 @@ fn railSidePaneShown(ctx: ?*anyopaque) bool { return if (editor.regionFor(sidebar)) |r| !r.isClosed() and !r.isFolded() else !editor.explorer.closed; } -fn railUndecidedCount(ctx: ?*anyopaque) usize { +fn railUndecidedCount(ctx: ?*anyopaque, view_id: []const u8) usize { + if (!std.mem.eql(u8, view_id, app.store.Store.view_id)) return 0; const editor: *fizzy.Editor = @ptrCast(@alignCast(ctx.?)); return editor.app.undecidedPluginCount(); } From f9a07b15c35b63eca7de3d983443c4dac65e09e8 Mon Sep 17 00:00:00 2001 From: "fizzy-linux[bot]" <346940526+fizzy-linux[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 03:08:04 -0500 Subject: [PATCH 2/2] layout: test the rail's undecided badge count Co-Authored-By: Claude Sonnet 5.5 Agent-Team: linux Worker: claude --- app/layout/Rail.zig | 2 +- src/editor/layout.zig | 3 ++- tests/integration.zig | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/layout/Rail.zig b/app/layout/Rail.zig index b00c8fa56..eef483634 100644 --- a/app/layout/Rail.zig +++ b/app/layout/Rail.zig @@ -173,7 +173,7 @@ fn pickAction(host: Host, c: *const Chooser, so_far: Action) Action { return if (host.vtable.sidePaneShown(host.ctx)) .close else .open; } -/// One rail icon: the chooser's item, drawn as the view's glyph with the store's attention badge. +/// One rail icon: the chooser's item, drawn as the view's glyph with the host's attention badge for that view. /// Returns the bottom of the item's cell, in physical pixels. fn drawIcon(host: Host, c: *Chooser, view: *Layout.Surface, index: usize) f32 { const undecided_count = host.vtable.undecidedPluginCount(host.ctx, view.id); diff --git a/src/editor/layout.zig b/src/editor/layout.zig index f86a81123..4d6412d68 100644 --- a/src/editor/layout.zig +++ b/src/editor/layout.zig @@ -190,7 +190,7 @@ fn explorerPane(ctx: ?*anyopaque, f: *Layout, keywords: []const []const u8) Layo return editor.explorer.draw(editor, f, keywords); } -fn railHost(editor: *fizzy.Editor) Rail.Host { +pub fn railHost(editor: *fizzy.Editor) Rail.Host { return .{ .ctx = editor, .vtable = &.{ .drawFooterItems = railFooterItems, .sidePaneShown = railSidePaneShown, @@ -218,6 +218,7 @@ fn railSidePaneShown(ctx: ?*anyopaque) bool { } fn railUndecidedCount(ctx: ?*anyopaque, view_id: []const u8) usize { + // The rail knows no view by name; only the store's view holds the card that offers a build. if (!std.mem.eql(u8, view_id, app.store.Store.view_id)) return 0; const editor: *fizzy.Editor = @ptrCast(@alignCast(ctx.?)); return editor.app.undecidedPluginCount(); diff --git a/tests/integration.zig b/tests/integration.zig index 888ae523b..663648f27 100644 --- a/tests/integration.zig +++ b/tests/integration.zig @@ -3067,6 +3067,23 @@ fn expectNoPanel(editor: *fizzy.Editor) !void { try std.testing.expect(editor.regionFor(fizzy.sdk.keywords.ide.panel) == null); } +test "rail: the undecided-plugin badge counts for the store view and no other" { + var ctx = try shim.init(std.testing.allocator); + defer ctx.deinit(std.testing.allocator); + const editor = ctx.editor; + editor.app.gpa = std.testing.allocator; + defer { + for (editor.app.undecided_plugin_ids.items) |id| editor.app.gpa.free(id); + editor.app.undecided_plugin_ids.clearAndFree(editor.app.gpa); + } + try editor.app.trackUndecidedPlugin("one"); + try editor.app.trackUndecidedPlugin("two"); + + const host = fizzy.layout.railHost(editor); + try std.testing.expectEqual(@as(usize, 2), host.vtable.undecidedPluginCount(host.ctx, @import("app").store.Store.view_id)); + try std.testing.expectEqual(@as(usize, 0), host.vtable.undecidedPluginCount(host.ctx, "test.files")); +} + test "chrome: the defaults are fizzy's own frame, Sidebar left of Main with a Panel" { try std.testing.expectEqual(fizzy.Editor.Chrome{}, fizzy.Editor.Chrome.configured); try withChrome(.{}, expectSidebarLeft);