╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────╮ │src/ui/Repo.zig│ ╰───────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │201 try Files.emptyResult(aa, repo_identity.identity, requested_ref_or_oid orelse .branch, requested_ref_value, files_dir), │ │202 try Commits.emptyResult(aa, repo_identity.identity, requested_ref_or_oid orelse .branch, requested_ref_value, commits_content), │ │203 try Refs.emptyResult(arena, repo_identity.identity, refs_kind, refs_from), │ │190 - try Issues.emptyResult(aa, repo_identity.identity, issues_tag, issues_selected, issues_theirs, issues_view), │ │204 + try Issues.emptyResult(aa, repo_identity.identity, issues_tag, issues_selected, issues_comments_start, issues_theirs, issues_view),│ │205 + null, │ │206 }; │ │207 }; │ │208 + if (std.meta.activeTag(route) == .repo_comments and comments == null) return error.NotFound; │ │209 issues.repo_source = source; │ │210 │ │211 // each tab mirror carries this page's route for that tab; tabs not targeted │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────╮ │233 .commits = commits, │ │234 .refs = refs, │ │235 .issues = issues, │ │236 + .comments = comments, │ │237 .settings = Settings.init(),│ │238 .auth = Auth.init(), │ │239 .quit = Quit.init(), │ ╰─────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │288 try stack.children.put(allocator, refs_view.getFocus().id, .{ .repo_refs = refs_view }); │ │289 } │ │290 │ │274 - // issues — the repo's issue tracker. │ │291 + // issues — the repo's issue tracker or a comment permalink. │ │292 { │ │276 - var issues_view = try Issues.View.init(allocator, &data.issues, session); │ │277 - errdefer issues_view.deinit(allocator); │ │278 - try stack.children.put(allocator, issues_view.getFocus().id, .{ .repo_issues = issues_view }); │ │293 + if (data.comments) |*comments| { │ │294 + var comments_view = try Comments.View.init(allocator, comments, session); │ │295 + errdefer comments_view.deinit(allocator); │ │296 + try stack.children.put(allocator, comments_view.getFocus().id, .{ .repo_comments = comments_view });│ │297 + } else { │ │298 + var issues_view = try Issues.View.init(allocator, &data.issues, session); │ │299 + errdefer issues_view.deinit(allocator); │ │300 + try stack.children.put(allocator, issues_view.getFocus().id, .{ .repo_issues = issues_view }); │ │301 + } │ │302 } │ │303 │ │304 // the header only shows the settings tab with a login and the auth │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │354 // the issues tab mirrors this page's tag filter (issue urls │ │355 // themselves never carry the tag). │ │356 .repo_issues => self.session.data.current_page = ui.RoutablePage.repoIssuesRoute(self.data.identity.slice(), .open, self.data.issues.tag, "") orelse self.session.data.current_page,│ │357 + // the comment view mirrors its own content route when built. │ │358 + .repo_comments => {}, │ │359 .home_settings => { │ │360 if (ui.RoutablePage.Array(ui.RoutablePage.repo_route_max_len).from(self.data.identity.slice())) |identity| │ │361 self.session.data.current_page = .{ .repo_settings = identity }; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │395 .repo_commits => |*v| v.getSelectedIndex() == 0, │ │396 .repo_refs => |*v| v.getSelectedIndex() == 0, │ │397 .repo_issues => |*v| v.getSelectedIndex() == 0, │ │398 + .repo_comments => |*v| v.getSelectedIndex() == 0,│ │399 .home_settings => |*v| v.getSelectedIndex() == 0,│ │400 .home_auth => |*v| v.getSelectedIndex() == 0, │ │401 .quit => |*v| v.getSelectedIndex() == 0, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────────╮ │src/ui/Repo/Header.zig│ ╰──────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │130 const current_link: []const u8 = switch (session.data.current_page) {│ │131 .repo_commits => commits_link, │ │132 .repo_refs => refs_link, │ │133 - .repo_issues => issues_link, │ │133 + .repo_issues, .repo_comments => issues_link, │ │134 .repo_settings => settings_link, │ │135 .repo_auth => auth_link, │ │136 else => files_link, │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────────╮ │src/ui/Repo/Issues.zig│ ╰──────────────────────╯ ╭────────────────────────────────────────────────────╮ │13 const Focus = xitui.focus.Focus; │ │14 const inp = @import("../input.zig"); │ │15 const diff3 = @import("../../diff3.zig"); │ │16 +const Comments = @import("Comments.zig"); │ │17 │ │18 const wasm = builtin.target.cpu.arch == .wasm32;│ │19 │ ╰────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────╮ │36 author: ui.Author = .unknown, │ │37 // whether the issue has an unresolved merge conflict │ │38 conflicted: bool = false, │ │39 + comments: Comments.Window = .empty, │ │40 }; │ │41 │ │42 // one side of a conflicted field: its value and who set it│ ╰───────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────╮ │84 // the hex event id of the issue its status's window is rooted at ("" = the│ │85 // first window), mirrored into the url. │ │86 selected_id: []const u8, │ │87 +// the selected issue's comment window (0 = the first window). │ │88 +comments_start: usize, │ │89 open: Window, │ │90 closed: Window, │ │91 // the conflicted issues' listing; its count also gates the conflicts tab. │ ╰───────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │138 } │ │139 │ │140 // an empty listing, for the wasm / no-repo paths. │ │137 -pub fn emptyResult(aa: std.mem.Allocator, identity: []const u8, tag: []const u8, selected_id: []const u8, theirs_picks: []const u8, view: ui.RoutablePage.IssuesView) !Self { │ │141 +pub fn emptyResult(aa: std.mem.Allocator, identity: []const u8, tag: []const u8, selected_id: []const u8, comments_start: usize, theirs_picks: []const u8, view: ui.RoutablePage.IssuesView) !Self {│ │142 return .{ │ │143 .identity = try aa.dupe(u8, identity), │ │144 .tag = try aa.dupe(u8, tag), │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯