╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │111 │ │112 pub const RefKind = enum { branch, tag }; │ │113 │ │114 - pub const IssuesView = enum { open, closed, tags, new, edit, description, conflicts, resolve }; │ │114 + pub const IssuesView = enum { open, closed, tags, new, edit, description, new_comment, conflicts, resolve };│ │115 │ │116 pub const RefOrOid = enum { │ │117 branch, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │366 return route; │ │367 } │ │368 │ │369 + // build the form route for replying to an issue or one of its comments. │ │370 + pub fn repoCommentNewRoute(identity: []const u8, thread: []const u8, parent_id: []const u8) ?RoutablePage {│ │371 + if (thread.len == 0) return null; │ │372 + var route = repoIssueCommentsRoute(identity, thread, 0) orelse return null; │ │373 + route.repo_issues.comment = Array(evt.event_id_size * 2).from(parent_id) orelse return null; │ │374 + route.repo_issues.view = .new_comment; │ │375 + return route; │ │376 + } │ │377 + │ │378 // build a `.repo_issues` route showing tags view, keeping the url-encoded │ │379 // `tag` filter ("" = unfiltered). │ │380 pub fn repoIssuesTagsRoute(identity: []const u8, tag: []const u8) ?RoutablePage { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │521 try std.fmt.allocPrint(arena.allocator(), "{s}/" ++ issue_seg ++ "{s}/resolve", .{ prefix, i.selected.slice() }) │ │522 else │ │523 try std.fmt.allocPrint(arena.allocator(), "{s}/" ++ issue_seg ++ "{s}/" ++ theirs_seg ++ "{s}/resolve", .{ prefix, i.selected.slice(), i.theirs.slice() });│ │524 + if (i.view == .new_comment) break :blk if (i.comment.len == 0) │ │525 + try std.fmt.allocPrint(arena.allocator(), "{s}/" ++ issue_seg ++ "{s}/new", .{ prefix, i.selected.slice() }) │ │526 + else │ │527 + try std.fmt.allocPrint(arena.allocator(), "{s}/" ++ issue_seg ++ "{s}/" ++ comment_seg ++ "{s}/new", .{ prefix, i.selected.slice(), i.comment.slice() }); │ │528 if (i.view == .conflicts) break :blk if (i.selected.len == 0) │ │529 try std.fmt.allocPrint(arena.allocator(), "{s}/issues/conflicts", .{prefix}) │ │530 else │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │603 // browser scroll the whole page, rather than being pinned to the viewport│ │604 pub fn fullHeight(self: RoutablePage) bool { │ │605 return switch (self) { │ │593 - .repo_issues => |i| i.view == .resolve, │ │606 + .repo_issues => |i| i.view == .resolve or i.view == .new_comment, │ │607 else => false, │ │608 }; │ │609 } │ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │680 if (std.mem.startsWith(u8, tab, issue_seg)) { │ │681 const issue_id = tab[issue_seg.len..]; │ │682 if (issue_id.len == 0) return null; │ │670 - const word = params.scanTail(&segments) catch return null; │ │671 - if (word) |tail| { │ │683 + if (segments.peek()) |tail| { │ │684 if (std.mem.startsWith(u8, tail, comment_seg)) { │ │673 - if (!params.only(&.{.start})) return null; │ │685 + _ = segments.next(); │ │686 const comment_id = tail[comment_seg.len..]; │ │687 if (comment_id.len == 0) return null; │ │688 + const word = params.scanTail(&segments) catch return null; │ │689 + if (word) |last| { │ │690 + if (!std.mem.eql(u8, last, "new") or !params.only(&.{})) return null; │ │691 + return repoCommentNewRoute(pair, issue_id, comment_id); │ │692 + } │ │693 + if (!params.only(&.{.start})) return null; │ │694 return repoCommentsRoute(pair, issue_id, comment_id, params.start() orelse return null); │ │695 } │ │696 + } │ │697 + const word = params.scanTail(&segments) catch return null; │ │698 + if (word) |tail| { │ │699 + if (std.mem.eql(u8, tail, "new")) return if (params.only(&.{})) repoCommentNewRoute(pair, issue_id, "") else null; │ │700 if (std.mem.eql(u8, tail, "edit")) return if (params.only(&.{})) repoIssuesEditRoute(pair, issue_id) else null; │ │701 if (std.mem.eql(u8, tail, "description")) return if (params.only(&.{})) repoIssuesDescriptionRoute(pair, issue_id) else null;│ │702 if (std.mem.eql(u8, tail, "resolve")) { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2186 box: wgt.Box(Widget), │ │2187 │ │2188 pub fn init(allocator: std.mem.Allocator) !SubmitButton { │ │2189 + return initLabeled(allocator, "submit"); │ │2190 + } │ │2191 + │ │2192 + pub fn initLabeled(allocator: std.mem.Allocator, label: []const u8) !SubmitButton { │ │2193 var box = try wgt.Box(Widget).init(allocator, .{ .border_style = null, .direction = .horiz });│ │2194 errdefer box.deinit(allocator); │ │2195 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2200 } │ │2201 │ │2202 { │ │2177 - const label = "submit"; │ │2203 var button = try wgt.TextBox(Widget).init(allocator, label, .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none });│ │2204 errdefer button.deinit(allocator); │ │2205 button.getFocus().focusable = true; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯