╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Patches.zig│ ╰───────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │863 errdefer copyable_text.deinit(allocator); │ │864 try box.children.put(allocator, copyable_text.getFocus().id, .{ .widget = .{ .copyable_text = copyable_text }, .rect = null, .min_size = null }); │ │865 }; │ │764 - if (entry.fork_oid.len != 0) { │ │765 - fields[field_count] = .{ │ │866 + if (entry.record.event.source_branch) |branch| { │ │867 + var source = try wgt.TextBox.init(allocator, branch, .{ .label = " source branch ", .border_style = .single, .rounded_corners = true, .wrap_kind = .none });│ │868 + errdefer source.deinit(allocator); │ │869 + source.getFocus().mode = .all; │ │870 + try box.children.put(allocator, source.getFocus().id, .{ .widget = .{ .text_box = source }, .rect = null, .min_size = null }); │ │871 + } │ │872 + const fields = [_]ui.widget.CopyableText.Choice{ │ │873 + .{ │ │874 .selector = "from", │ │767 - .text = entry.fork_oid, │ │875 + .text = entry.revision_oid, │ │876 .label = if (entry.no_changes) " object id of this patch (no changes) " else " object id of this patch ", │ │769 - }; │ │770 - field_count += 1; │ │771 - } │ │772 - fields[field_count] = .{ │ │773 - .selector = "to", │ │774 - .text = target_branch, │ │775 - .label = if (status_kind == .merged) " target branch this patch was merged into " else " target branch this patch will go to ", │ │877 + }, │ │878 + .{ │ │879 + .selector = "to", │ │880 + .text = target_branch, │ │881 + .label = if (status_kind == .merged) " target branch this patch was merged into " else " target branch this patch will go to ", │ │882 + }, │ │883 }; │ │777 - field_count += 1; │ │778 - │ │779 - if (field_count > 0) { │ │780 - var copyable_text = try ui.widget.CopyableText.init(allocator, session, fields[0..field_count]); │ │781 - errdefer copyable_text.deinit(allocator); │ │782 - try box.children.put(allocator, copyable_text.getFocus().id, .{ .widget = .{ .copyable_text = copyable_text }, .rect = null, .min_size = null }); │ │783 - } │ │884 + var copyable_text = try ui.widget.CopyableText.init(allocator, session, fields[@intFromBool(entry.revision_oid.len == 0)..]); │ │885 + errdefer copyable_text.deinit(allocator); │ │886 + try box.children.put(allocator, copyable_text.getFocus().id, .{ .widget = .{ .copyable_text = copyable_text }, .rect = null, .min_size = null }); │ │887 } │ │888 │ │889 fn cloneDirectoryName(allocator: std.mem.Allocator, title: []const u8) ![]const u8 { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │941 } │ │942 │ │943 // new-patch tab; an edit or resolve url shows its tab in this place │ │841 - if (session.local == null) { │ │944 + { │ │945 const route = switch (data.view) { │ │946 .edit => ui.RoutablePage.repoThreadEditRoute(.patch, data.identity, data.selected_id) orelse return error.RouteTooLong,│ │947 .publish => ui.RoutablePage.repoPatchPublishRoute(data.identity, data.selected_id) orelse return error.RouteTooLong, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │967 } │ │968 │ │969 // drafts belong to the logged-in user and are not available locally │ │867 - if (session.local == null) { │ │970 + if (session.data.host_kind != .local) { │ │971 const route = ui.RoutablePage.repoPatchesDraftsRoute(data.identity) orelse return error.RouteTooLong; │ │972 const link = try ui.inPageTabLink(session, route, page_selected and selected_index == View.viewIndex(.drafts));│ │973 var label_buf: [64]u8 = undefined; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────────╮ │src/ui/widget.zig│ ╰─────────────────╯ ╭──────────────────────────────────────────────╮ │57 repo_events: ui.Repo.Events.View, │ │58 repo_comment: ui.Repo.Comment.Item, │ │59 copyable_text: CopyableText, │ │60 + patch_source: ui.Repo.Patches.Source, │ │61 home_users: ui.Home.Users.View, │ │62 home_repos: ui.Home.Repos.View, │ │63 auth_tab: ui.Home.Header.AuthTab.View,│ ╰──────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │830 self.box.deinit(allocator); │ │831 } │ │832 │ │833 + pub fn setLabel(self: *SubmitButton, allocator: std.mem.Allocator, label: []const u8) !void { │ │834 + const button = &self.box.children.values()[1]; │ │835 + try button.widget.text_box.setContent(allocator, label); │ │836 + button.min_size = .{ .width = label.len + 2, .height = null }; │ │837 + } │ │838 + │ │839 pub fn build(self: *SubmitButton, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void {│ │840 try self.box.build(allocator, constraint, root_focus); │ │841 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ───────────────╮ │src/ui/widget/thread.zig│ ╰────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │1271 const title_field_index: usize = 0; │ │1272 const tags_field_index: usize = 1; │ │1273 const description_field_index: usize = 2; │ │1274 - const target_branch_field_index: usize = 3; │ │1275 - const submit_field_index: usize = 3 + @as(usize, @intFromBool(supports_drafts));│ │1274 + const source_field_index: usize = 3; │ │1275 const comment_author_field_index: usize = 0; │ │1276 const comment_body_field_index: usize = 1; │ │1277 const comment_submit_field_index: usize = 2; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1657 try box.children.put(allocator, description.getFocus().id, .{ .widget = .{ .text_input = description }, .rect = null, .min_size = null }); │ │1658 } │ │1659 │ │1660 + if (supports_drafts and record == null) { │ │1661 + var source = try Data.Source.init(allocator, session, if (saved_fields) |saved| saved.source_branch else "", if (saved_fields) |saved| saved.branch_source else (session.data.host_kind == .local));│ │1662 + errdefer source.deinit(allocator); │ │1663 + try box.children.put(allocator, source.getFocus().id, .{ .widget = .{ .patch_source = source }, .rect = null, .min_size = .{ .width = null, .height = 3 } }); │ │1664 + } │ │1665 + │ │1666 if (supports_drafts) { │ │1667 var target = try wgt.TextInput.init(allocator, .{ .label = " target branch ", .name = "target_branch", .visible_width = null, .rounded_corners = true, .render_content = session.is_terminal }); │ │1668 errdefer target.deinit(allocator); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2130 const failure = if (self.session.formFeedback(feedback_tag)) |saved| saved.failure else null; │ │2131 form.children.values()[title_field_index].widget.text_input.options.label = if (failure == .required_title) " title (required) " else " title "; │ │2132 if (comptime supports_drafts) { │ │2128 - const invalid_target = failure == .invalid_target_branch; │ │2129 - form.children.values()[target_branch_field_index].widget.text_input.options.label = if (invalid_target) " target branch (not found) " else " target branch "; │ │2133 + if (form.children.values()[source_field_index].widget == .patch_source) { │ │2134 + const source = &form.children.values()[source_field_index].widget.patch_source; │ │2135 + source.field().options.label = if (failure == .invalid_source_branch) " source branch (not found) " else if (failure == .unrelated_branches) " source branch (unrelated) " else " source branch "; │ │2136 + const submit = &form.children.values()[form.children.count() - 1].widget.submit_button; │ │2137 + try submit.setLabel(allocator, if (source.existing) "submit patch" else "submit draft"); │ │2138 + } │ │2139 + const target_branch_field_index = form.children.count() - 2; │ │2140 + form.children.values()[target_branch_field_index].widget.text_input.options.label = │ │2141 + if (failure == .invalid_target_branch) " target branch (not found) " else if (self.data.view == .edit and failure == .unrelated_branches) " target branch (unrelated to source) " else if (self.data.view == .edit and failure == .invalid_source_branch) " target branch (source branch not found) " else " target branch ";│ │2142 } │ │2143 } │ │2144 if (self.formBox()) |form| { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────╮ │2242 const form = self.threadForm() orelse return; │ │2243 const cid = form.getFocus().child_id orelse return; │ │2244 const cur = form.children.getIndex(cid) orelse return; │ │2245 + const submit_field_index = form.children.count() - 1; │ │2246 + const child = &form.children.values()[cur]; │ │2247 │ │2248 if (cur == description_field_index) { │ │2235 - const child = &form.children.values()[cur]; │ │2249 if (try multilineKeepsKey(&child.widget.text_input, allocator, key))│ │2250 return child.widget.input(allocator, key, root_focus); │ │2251 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────╮ │2252 │ │2253 switch (key) { │ │2241 - .arrow_up, .back_tab => if (cur > 0) │ │2254 + .arrow_up, .back_tab => return if (cur > 0) │ │2255 root_focus.setFocus(form.children.keys()[cur - 1])│ │2256 else │ │2257 self.focusHeader(root_focus), │ ╰────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯