╭ changed ──────────────╮ │src/ui/Repo/Threads.zig│ ╰───────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────╮ │686 // conflicts, so skip the child too to keep the stack 1:1 with the │ │687 // tabs. │ │688 if (has_conflicts and data.conflicts.count > 0) { │ │326 - var split = try initSplit(allocator, session, data, null); │ │689 + var split = try initSplit(allocator, session, data, null, false); │ │690 errdefer split.deinit(allocator); │ │691 try stack.children.put(allocator, split.getFocus().id, .{ .box = split });│ │692 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────╮ │704 .detailed_index = @splat(null), │ │705 .description_id = @splat(null), │ │706 .author_id = @splat(null), │ │344 - .status_button_id = @splat(null), │ │707 + .primary_button_id = @splat(null),│ │708 }; │ │709 } │ │710 │ ╰───────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │711 // the master-detail split showing `status`'s window, or the conflicts │ │712 // window when null. │ │350 - fn initSplit(allocator: std.mem.Allocator, session: *ui.Session, data: *const Self, status_maybe: ?Status) !wgt.Box(ui.Widget) { │ │713 + fn initSplit(allocator: std.mem.Allocator, session: *ui.Session, data: *const Self, status_maybe: ?Status, drafts: bool) !wgt.Box(ui.Widget) {│ │714 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .horiz }); │ │715 errdefer box.deinit(allocator); │ │716 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │354 - const win = if (status_maybe) |status| data.window(status) else if (has_conflicts) &data.conflicts else unreachable; │ │717 + const win = if (status_maybe) |status| data.window(status) else if (drafts and has_drafts) &data.drafts else if (has_conflicts) &data.conflicts else unreachable;│ │718 │ │719 // the issue list (one focusable row per title), plus a "next" link that │ │720 // reloads the page rooted at the following issue. │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │723 var list_box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert, .stretch = true }); │ │724 errdefer list_box.deinit(allocator); │ │725 if (win.prev_id) |prev| │ │363 - try addRow(allocator, &list_box, "← previous", "", try windowLink(session.page_arena, data, status_maybe, prev)); │ │726 + try addRow(allocator, &list_box, "← previous", "", try windowLink(session.page_arena, data, status_maybe, drafts, prev));│ │727 for (win.items) |entry| { │ │728 // conflicts take precedence over the comment count │ │729 const label: []const u8 = if (entryConflicted(entry) and status_maybe != null) │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │735 try addRow(allocator, &list_box, entry.record.event.title, label, try rowLink(session.page_arena, data.identity, entry.id));│ │736 } │ │737 if (win.next_id) |next| │ │375 - try addRow(allocator, &list_box, "next →", "", try windowLink(session.page_arena, data, status_maybe, next)); │ │738 + try addRow(allocator, &list_box, "next →", "", try windowLink(session.page_arena, data, status_maybe, drafts, next)); │ │739 // select the window's first issue (past a leading "previous" │ │740 // row) so its description shows on load. │ │741 if (win.items.len > 0) │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │816 try box.children.put(allocator, description.getFocus().id, .{ .widget = .{ .text_input = description }, .rect = null, .min_size = null });│ │817 } │ │818 │ │456 - try addSubmitButton(allocator, &box); │ │819 + try addSubmitButtonLabeled(allocator, &box, if (kind == .patch and record == null) "submit private draft" else "submit"); │ │820 │ │821 box.getFocus().child_id = box.children.keys()[title_field_index]; │ │822 return box; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │907 │ │908 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert }); │ │909 errdefer box.deinit(allocator); │ │547 - const route = resolveRoute(data.identity, data.selected_id, "") orelse return error.RouteTooLong; │ │910 + const route = resolveRoute(data.identity, data.selected_id, data.theirs_picks) orelse return error.RouteTooLong; │ │911 box.getFocus().kind = .{ .custom = try std.fmt.allocPrint(aa, "form:{s}", .{try route.toUrl(session.page_arena)}) };│ │912 │ │913 if (conflict.title) |*fc| { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────╮ │917 if (conflict.tags) |*fc| { │ │918 try addLabel(allocator, &box, "tags conflict:"); │ │919 try addFieldConflict(allocator, &box, session, data, "tags", fc); │ │920 + } │ │921 + if (comptime kind == .patch) { │ │922 + if (conflict.status) |*fc| { │ │923 + try addLabel(allocator, &box, "status conflict:"); │ │924 + try addAtomicConflict(allocator, &box, session, data, "status", fc); │ │925 + } │ │926 + if (conflict.revision) |*fc| { │ │927 + try addLabel(allocator, &box, "revision conflict:"); │ │928 + try addAtomicConflict(allocator, &box, session, data, "revision", fc);│ │929 + } │ │930 } │ │931 │ │932 if (conflict.description) |*desc| { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 992 resolution_input.getFocus().focusable = true; │ │ 993 try resolution_input.setContent(allocator, if (data.theirsPicked(name)) fc.theirs.text else fc.ours.text); │ │ 994 try box.children.put(allocator, resolution_input.getFocus().id, .{ .widget = .{ .text_input = resolution_input }, .rect = null, .min_size = null }); │ │ 995 + } │ │ 996 + │ │ 997 + // atomic fields can only pick one complete side │ │ 998 + fn addAtomicConflict(allocator: std.mem.Allocator, box: *wgt.Box(ui.Widget), session: *ui.Session, data: *const Self, comptime name: []const u8, fc: *const FieldConflict) !void {│ │ 999 + const aa = session.page_arena.allocator(); │ │1000 + try addVersionRow(allocator, box, try sideLabel(aa, fc.ours.author, true), fc.ours.text, try useThisLink(session, data, name, false)); │ │1001 + try addVersionRow(allocator, box, try sideLabel(aa, fc.theirs.author, false), fc.theirs.text, try useThisLink(session, data, name, true)); │ │1002 + var selected = try wgt.TextBox(ui.Widget).init(allocator, if (data.theirsPicked(name)) fc.theirs.text else fc.ours.text, .{ │ │1003 + .border_style = .single, │ │1004 + .rounded_corners = true, │ │1005 + .wrap_kind = .word, │ │1006 + .label = " resolution ", │ │1007 + }); │ │1008 + errdefer selected.deinit(allocator); │ │1009 + selected.getFocus().focusable = true; │ │1010 + try box.children.put(allocator, selected.getFocus().id, .{ .widget = .{ .text_box = selected }, .rect = null, .min_size = null }); │ │1011 } │ │1012 │ │1013 // a blank row setting a conflict group apart from its neighbors │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯