╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │309 // the content a repo route renders: which repo, plus the files directory and │ │310 // commits page. tab switches keep this fixed (the page already holds every │ │311 // tab's data); a files-directory or commits-page change alters it. │ │281 - const RepoContent = struct { identity: []const u8, files_dir: []const u8, commits_start: []const u8, commits_after: usize }; │ │312 + const RepoContent = struct { identity: []const u8, files_dir: []const u8, commits_start: []const u8, commits_after: usize, refs_kind: RefKind, refs_after: usize };│ │313 │ │314 fn repoContent(r: RoutablePage) ?RepoContent { │ │315 const name = switch (r) { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────╮ │316 .repo, .repo_settings, .repo_auth => |n| n.slice(),│ │317 .repo_commits => |c| c.name.slice(), │ │318 + .repo_refs => |r2| r2.name.slice(), │ │319 else => return null, │ │320 }; │ │321 const rf = RepoFiles.parse(name) orelse return null; │ ╰────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │322 + // a 0 offset means no refs pagination, so canonicalize the column to │ │323 + // branch then — otherwise the base /refs route would look like a content│ │324 + // change relative to the same page mirrored with kind=tag. │ │325 + const refs_after = switch (r) { │ │326 + .repo_refs => |r2| r2.after, │ │327 + else => 0, │ │328 + }; │ │329 return .{ │ │330 .identity = rf.identity, │ │331 .files_dir = if (std.meta.activeTag(r) == .repo) rf.dir else "", │ ╰──────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │334 .repo_commits => |c| c.after, │ │335 else => 0, │ │336 }, │ │337 + .refs_kind = switch (r) { │ │338 + .repo_refs => |r2| if (refs_after == 0) .branch else r2.kind,│ │339 + else => .branch, │ │340 + }, │ │341 + .refs_after = refs_after, │ │342 }; │ │343 } │ │344 │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │349 return !std.mem.eql(u8, ca.identity, cb.identity) or │ │350 !std.mem.eql(u8, ca.files_dir, cb.files_dir) or │ │351 !std.mem.eql(u8, ca.commits_start, cb.commits_start) or │ │308 - ca.commits_after != cb.commits_after; │ │352 + ca.commits_after != cb.commits_after or │ │353 + ca.refs_kind != cb.refs_kind or │ │354 + ca.refs_after != cb.refs_after; │ │355 } │ │356 │ │357 // true when `a` and `b` are the same home list tab paginated to a different│ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │394 try jw.objectField("after"); │ │395 try jw.write(c.after); │ │396 }, │ │397 + .repo_refs => |r| { │ │398 + try jw.objectField("name"); │ │399 + try jw.write(r.name.slice()); │ │400 + // "kind" already holds the union tag, so the paginated column│ │401 + // travels under a distinct field. │ │402 + try jw.objectField("ref_kind"); │ │403 + try jw.write(@tagName(r.kind)); │ │404 + try jw.objectField("after"); │ │405 + try jw.write(r.after); │ │406 + }, │ │407 .home_users, .home_repos => |after| { │ │408 try jw.objectField("after"); │ │409 try jw.write(after); │ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │414 } │ │415 │ │416 pub fn jsonParse(allocator: std.mem.Allocator, source: anytype, options: std.json.ParseOptions) !RoutablePage { │ │361 - const Helper = struct { kind: std.meta.Tag(RoutablePage), name: ?[]const u8 = null, after: usize = 0 }; │ │417 + const Helper = struct { kind: std.meta.Tag(RoutablePage), name: ?[]const u8 = null, after: usize = 0, ref_kind: ?[]const u8 = null };│ │418 const helper = try std.json.innerParse(Helper, allocator, source, options); │ │419 const parseName = struct { │ │420 // errors must stay within std.json's ParseError set; ValueTooLong │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │433 .user_auth => .{ .user_auth = try parseName(evt.User.name_max_len, helper.name) }, │ │434 .repo => .{ .repo = try parseName(repo_route_max_len, helper.name) }, │ │435 .repo_commits => .{ .repo_commits = .{ .name = try parseName(repo_route_max_len, helper.name), .after = helper.after } },│ │436 + .repo_refs => .{ .repo_refs = .{ │ │437 + .name = try parseName(repo_route_max_len, helper.name), │ │438 + .kind = if (helper.ref_kind) |k| (if (std.mem.eql(u8, k, "tag")) .tag else .branch) else .branch, │ │439 + .after = helper.after, │ │440 + } }, │ │441 .repo_settings => .{ .repo_settings = try parseName(repo_route_max_len, helper.name) }, │ │442 .repo_auth => .{ .repo_auth = try parseName(repo_route_max_len, helper.name) }, │ │443 }; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────╮ │891 repo_header: Repo.Header.View, │ │892 repo_files: Repo.Files.View, │ │893 repo_commits: Repo.Commits.View, │ │894 + repo_refs: Repo.Refs.View, │ │895 home_users: Home.Users.View, │ │896 home_repos: Home.Repos.View, │ │897 auth_tab: Home.Header.AuthTab.View,│ ╰────────────────────────────────────────────╯