╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Threads.zig│ ╰───────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────╮ │860 try addLabel(allocator, &box, "tags conflict:"); │ │861 try addFieldConflict(allocator, &box, session, data, "tags", fc); │ │862 } │ │889 - if (comptime kind == .patch) { │ │863 + if (comptime @hasField(Data.Conflict, "status")) { │ │864 if (conflict.status) |*fc| { │ │865 try addLabel(allocator, &box, "status conflict:"); │ │866 try addAtomicConflict(allocator, &box, session, data, "status", fc);│ ╰─────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────╮ │867 } │ │868 + } │ │869 + if (comptime @hasField(Data.Conflict, "revision")) { │ │870 if (conflict.revision) |*fc| { │ │871 try addLabel(allocator, &box, "revision conflict:"); │ │872 try addAtomicConflict(allocator, &box, session, data, "revision", fc);│ ╰───────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1069 try row.children.put(allocator, button.getFocus().id, .{ .widget = .{ .text_box = button }, .rect = null, .min_size = .{ .width = try xitui.width.displayWidth(label) + 2, .height = null } }); │ │1070 } │ │1071 │ │1096 - fn addDetailGap(allocator: std.mem.Allocator, box: *wgt.Box(ui.Widget)) !void { │ │1097 - var spacer = try ui.Spacer.init(allocator); │ │1098 - errdefer spacer.deinit(allocator); │ │1099 - try box.children.put(allocator, spacer.getFocus().id, .{ .widget = .{ .spacer = spacer }, .rect = null, .min_size = .{ .width = null, .height = 1 }, .max_size = .{ .width = null, .height = 1 } });│ │1100 - } │ │1101 - │ │1072 pub fn deinit(self: *This, allocator: std.mem.Allocator) void { │ │1073 self.box.deinit(allocator); │ │1074 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1075 │ │1076 fn header(self: *This) *HeaderType { │ │1107 - return switch (kind) { │ │1108 - .issue => &self.box.children.values()[header_index].widget.repo_issues_header, │ │1109 - .patch => &self.box.children.values()[header_index].widget.repo_patches_header, │ │1110 - .discuss => &self.box.children.values()[header_index].widget.repo_discussions_header,│ │1111 - else => @compileError("unsupported thread event kind"), │ │1112 - }; │ │1077 + return &@field(self.box.children.values()[header_index].widget, Data.header_widget_name);│ │1078 } │ │1079 │ │1080 fn viewStack(self: *This) *wgt.Stack(ui.Widget) { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1149 } │ │1150 │ │1151 fn window(self: *This, index: usize) *const Window { │ │1187 - if (has_conflicts and index == conflict_view_index) return &self.data.conflicts; │ │1188 - if (has_drafts and index == drafts_view_index) return &self.data.drafts; │ │1152 + if (supports_conflicts and index == conflict_view_index) return &self.data.conflicts;│ │1153 + if (supports_drafts and index == drafts_view_index) return &self.data.drafts; │ │1154 return self.data.window(splitStatus(index)); │ │1155 } │ │1156 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1164 // new-thread form shows). │ │1165 fn selectedSplitIndex(self: *This) ?usize { │ │1166 const idx = self.stackSelectedIndex() orelse return null; │ │1202 - return if (idx < status_count or (has_drafts and idx == drafts_view_index) or idx == conflict_view_index) idx else null; │ │1167 + return if (idx < status_count or (supports_drafts and idx == drafts_view_index) or idx == conflict_view_index) idx else null;│ │1168 } │ │1169 │ │1170 fn detailActive(self: *This, index: usize) bool { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1279 │ │1280 // the resolve form's inputs prefill from the picked side, which │ │1281 // is url state and so page-constant like the edit values above │ │1317 - if (has_conflicts and self.data.view == .resolve) { │ │1282 + if (supports_conflicts and self.data.view == .resolve) { │ │1283 for (form.children.values()) |*child| switch (child.widget) { │ │1284 .text_input => |*ti| if (self.resolvePrefill(ti.options.name)) |value| { │ │1285 try self.session.input_values.put(inputs_arena, ti.getFocus().id, value);│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1324 try row.children.put(allocator, spacer.getFocus().id, .{ .widget = .{ .spacer = spacer }, .rect = null, .min_size = null }); │ │1325 } │ │1326 │ │1362 - if (has_drafts and entryDraft(entry)) { │ │1327 + if (supports_drafts and entryDraft(entry)) { │ │1328 if (entry.revision_ready) { │ │1329 const route = listRoute(self.data.identity, @enumFromInt(0), "", entry.id) orelse return error.RouteTooLong; │ │1330 row.getFocus().kind = .{ .custom = try std.fmt.allocPrint(pa, "form:{s}/post", .{try route.toUrl(self.session.page_arena)}) };│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1348 // a conflicted thread's only action is resolving it. the button │ │1349 // stays visible logged out; the resolve page shows the │ │1350 // unauthorized view then. │ │1386 - if (has_conflicts and entryConflicted(entry)) { │ │1351 + if (supports_conflicts and entryConflicted(entry)) { │ │1352 const label = "resolve conflict"; │ │1353 const route = resolveRoute(self.data.identity, entry.id, "") orelse return error.RouteTooLong; │ │1354 try addToolButton(allocator, row, label, try std.fmt.allocPrint(pa, "a:{s}", .{try route.toUrl(self.session.page_arena)}));│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1381 row.getFocus().child_id = row.children.keys()[first_in_row_index]; │ │1382 } │ │1383 } │ │1419 - │ │1420 - if (has_drafts and entryDraft(entry) and !description_page and comment_page == null) { │ │1421 - const aa = self.session.page_arena.allocator(); │ │1422 - var fields: [2]ui.CopyableText.Choice = undefined; │ │1423 - var field_count: usize = 0; │ │1424 - │ │1425 - if (entry.target_branch.len != 0) if (self.session.data.git_ssh_port) |port| { │ │1426 - const url = try std.fmt.allocPrint(aa, "ssh://localhost:{d}/repo/{s}/patch:{s}/branch:{s}", .{ port, self.data.identity, entry.id, entry.target_branch });│ │1427 - const push_command = try std.fmt.allocPrint(aa, "git push {s} HEAD:patch", .{url}); │ │1428 - const clone_name = try Data.cloneDirectoryName(aa, entry.record.event.title); │ │1429 - const clone_command = if (clone_name.len == 0) │ │1430 - try std.fmt.allocPrint(aa, "git clone {s}", .{url}) │ │1431 - else │ │1432 - try std.fmt.allocPrint(aa, "git clone {s} {s}", .{ url, clone_name }); │ │1433 - const choices: [2]ui.CopyableText.Choice = .{ │ │1434 - .{ │ │1435 - .selector = "push", │ │1436 - .text = push_command, │ │1437 - .copyable_text = try std.fmt.allocPrint(aa, "{s}{s}", .{ self.session.data.git_ssh_prefix, push_command }), │ │1438 - .label = " push to this patch from existing repo ", │ │1439 - }, │ │1440 - .{ │ │1441 - .selector = "clone", │ │1442 - .text = clone_command, │ │1443 - .copyable_text = try std.fmt.allocPrint(aa, "{s}{s}", .{ self.session.data.git_ssh_prefix, clone_command }), │ │1444 - .label = " clone this patch ", │ │1445 - }, │ │1446 - }; │ │1447 - var copyable_text = try ui.CopyableText.View.init(allocator, self.session, &choices); │ │1448 - errdefer copyable_text.deinit(allocator); │ │1449 - try inner.children.put(allocator, copyable_text.getFocus().id, .{ .widget = .{ .copyable_text = copyable_text }, .rect = null, .min_size = null }); │ │1450 - try addDetailGap(allocator, inner); │ │1451 - }; │ │1452 - if (entry.fork_oid.len != 0) { │ │1453 - fields[field_count] = .{ │ │1454 - .text = entry.fork_oid, │ │1455 - .label = if (entry.no_changes) " object id of this patch (no changes) " else " object id of this patch ", │ │1456 - }; │ │1457 - field_count += 1; │ │1458 - } │ │1459 - if (entry.target_branch.len != 0) { │ │1460 - fields[field_count] = .{ │ │1461 - .text = entry.target_branch, │ │1462 - .label = " target branch this patch will go to ", │ │1463 - .bottom_label = " (set by the url you push to above) ", │ │1464 - }; │ │1465 - field_count += 1; │ │1466 - } │ │1384 │ │1468 - for (fields[0..field_count], 0..) |field, i| { │ │1469 - var copyable_text = try ui.CopyableText.View.init(allocator, self.session, &.{field}); │ │1470 - errdefer copyable_text.deinit(allocator); │ │1471 - try inner.children.put(allocator, copyable_text.getFocus().id, .{ .widget = .{ .copyable_text = copyable_text }, .rect = null, .min_size = null }); │ │1472 - if (i + 1 < field_count) try addDetailGap(allocator, inner); │ │1473 - } │ │1474 - if (entry.target_branch.len != 0) try addDetailGap(allocator, inner); │ │1385 + if (supports_drafts and entryDraft(entry) and !description_page and comment_page == null) { │ │1386 + try self.data.appendDraftDetails(allocator, inner, self.session, entry); │ │1387 } │ │1388 │ │1389 if (comment_page) |page| { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯