╭ changed ─────────────╮ │src/ui/Repo/Issues.zig│ ╰──────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │806 // natural height │ │807 fn addSubmitButton(allocator: std.mem.Allocator, box: *wgt.Box(ui.Widget)) !void { │ │808 { │ │809 - var button = try wgt.TextBox(ui.Widget).init(allocator, "submit", .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none }); │ │810 - errdefer button.deinit(allocator); │ │811 - button.getFocus().focusable = true; │ │812 - // the renderer distinguishes plain clickables from buttons that │ │813 - // should POST to a server route by this kind. │ │814 - button.getFocus().kind = .{ .custom = "submit" }; │ │815 - try box.children.put(allocator, button.getFocus().id, .{ .widget = .{ .text_box = button }, .rect = null, .min_size = .{ .width = null, .height = 3 } }); │ │809 + var submit = try ui.SubmitButton.init(allocator); │ │810 + errdefer submit.deinit(allocator); │ │811 + try box.children.put(allocator, submit.getFocus().id, .{ .widget = .{ .submit_button = submit }, .rect = null, .min_size = .{ .width = null, .height = 3 } });│ │812 } │ │813 { │ │814 var spacer = try ui.Spacer.init(allocator); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │846 try addLabel(allocator, &box, "description conflict:"); │ │847 var hunk_index: usize = 0; │ │848 for (desc.chunks, 0..) |*chunk, chunk_index| { │ │853 - if (chunk_index > 0) try addGap(allocator, &box); │ │849 switch (chunk.*) { │ │850 .same => |text| { │ │851 var tb = try wgt.TextBox(ui.Widget).init(allocator, text, .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .word });│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │868 try box.children.put(allocator, tb.getFocus().id, .{ .widget = .{ .text_box = tb }, .rect = null, .min_size = null });│ │869 }, │ │870 .conflict => |hunk| { │ │871 + // a blank row on each side sets the group apart from │ │872 + // the flowing shared and auto-resolved chunks │ │873 + if (chunk_index > 0) try addGap(allocator, &box); │ │874 const name = try std.fmt.allocPrint(aa, "d{d}", .{hunk_index}); │ │875 hunk_index += 1; │ │876 const picked_theirs = data.theirsPicked(name); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │881 resolution_input.getFocus().focusable = true; │ │882 try resolution_input.setContent(allocator, (if (picked_theirs) hunk.theirs else hunk.ours) orelse ""); │ │883 try box.children.put(allocator, resolution_input.getFocus().id, .{ .widget = .{ .text_input = resolution_input }, .rect = null, .min_size = null });│ │884 + if (chunk_index + 1 < desc.chunks.len) try addGap(allocator, &box); │ │885 }, │ │886 } │ │887 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │907 try box.children.put(allocator, resolution_input.getFocus().id, .{ .widget = .{ .text_input = resolution_input }, .rect = null, .min_size = null });│ │908 } │ │909 │ │911 - // a blank row separating adjacent chunks │ │910 + // a blank row setting a conflict group apart from its neighbors │ │911 fn addGap(allocator: std.mem.Allocator, box: *wgt.Box(ui.Widget)) !void { │ │912 var text = try wgt.Text(ui.Widget).init(allocator, " "); │ │913 errdefer text.deinit(allocator); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │914 try box.children.put(allocator, text.getFocus().id, .{ .widget = .{ .text = text }, .rect = null, .min_size = null }); │ │915 } │ │916 │ │918 - // a section label │ │917 fn addLabel(allocator: std.mem.Allocator, box: *wgt.Box(ui.Widget), content: []const u8) !void { │ │920 - var tb = try wgt.TextBox(ui.Widget).init(allocator, content, .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none });│ │921 - errdefer tb.deinit(allocator); │ │922 - tb.getFocus().focusable = true; │ │923 - try box.children.put(allocator, tb.getFocus().id, .{ .widget = .{ .text_box = tb }, .rect = null, .min_size = null }); │ │918 + var label = try ui.SectionLabel.init(allocator, content); │ │919 + errdefer label.deinit(allocator); │ │920 + try box.children.put(allocator, label.getFocus().id, .{ .widget = .{ .section_label = label }, .rect = null, .min_size = null }); │ │921 } │ │922 │ │923 // one side of a conflict: the "use this" link on the left, the version │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │927 errdefer row.deinit(allocator); │ │928 │ │929 { │ │933 - var use = try wgt.TextBox(ui.Widget).init(allocator, "use this", .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none });│ │930 + const use_label = "use this"; │ │931 + var use = try wgt.TextBox(ui.Widget).init(allocator, use_label, .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none }); │ │932 errdefer use.deinit(allocator); │ │933 use.getFocus().focusable = true; │ │934 use.getFocus().kind = .{ .custom = link }; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │935 try row.children.put(allocator, use.getFocus().id, .{ │ │936 .widget = .{ .text_box = use }, │ │937 .rect = null, │ │940 - .min_size = .{ .width = "use this".len + 2, .height = null },│ │941 - .max_size = .{ .width = "use this".len + 2, .height = null },│ │938 + .min_size = .{ .width = use_label.len + 2, .height = null }, │ │939 + .max_size = .{ .width = use_label.len + 2, .height = null }, │ │940 }); │ │941 } │ │942 │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1293 // stays visible logged out; the resolve page shows the │ │1294 // unauthorized view then. │ │1295 if (entry.conflicted) { │ │1298 - var button = try wgt.TextBox(ui.Widget).init(allocator, "resolve", .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none });│ │1296 + const label = "resolve conflict"; │ │1297 + var button = try wgt.TextBox(ui.Widget).init(allocator, label, .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none }); │ │1298 errdefer button.deinit(allocator); │ │1299 button.getFocus().focusable = true; │ │1300 const route = ui.RoutablePage.repoIssuesResolveRoute(self.data.identity, entry.id, "") orelse return error.RouteTooLong; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1301 button.getFocus().kind = .{ .custom = try std.fmt.allocPrint(pa, "a:{s}", .{try route.toUrl(self.session.page_arena)}) }; │ │1303 - try row.children.put(allocator, button.getFocus().id, .{ .widget = .{ .text_box = button }, .rect = null, .min_size = .{ .width = "resolve".len + 2, .height = null } });│ │1302 + try row.children.put(allocator, button.getFocus().id, .{ .widget = .{ .text_box = button }, .rect = null, .min_size = .{ .width = label.len + 2, .height = null } }); │ │1303 } else { │ │1304 // a logged-out session can't flip the status, so it gets no │ │1305 // open/close button (and no form action on the row) │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯