╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭───────────────────────────────────────────────────────╮ │1188 return switch (self.*) { │ │1189 .repo_files => |*f| f.name.slice(), │ │1190 .repo_commits => |*c| c.name.slice(),│ │1191 + .repo_diff => |*d| d.name.slice(), │ │1192 .repo_refs => |*r| r.name.slice(), │ │1193 .repo_issues => |*i| i.name.slice(), │ │1194 .repo_patches => |*p| p.name.slice(),│ ╰───────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1235 return switch (self) { │ │1236 .home_users, .home_repos, .home_settings, .home_auth => .home, │ │1237 .user_repos, .user_forks, .user_settings, .user_auth => .user, │ │1181 - .repo_files, .repo_commits, .repo_refs, .repo_issues, .repo_patches, .repo_discussions, .repo_events, .repo_settings, .repo_auth => .repo, │ │1238 + .repo_files, .repo_commits, .repo_diff, .repo_refs, .repo_issues, .repo_patches, .repo_discussions, .repo_events, .repo_settings, .repo_auth => .repo,│ │1239 .fork_patch, .fork_diff, .fork_files, .fork_commits, .fork_settings, .fork_auth => .fork, │ │1240 }; │ │1241 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1435 } │ │1436 if (std.mem.eql(u8, tab, files_seg)) { │ │1437 params.scanPairs(&segments) catch return null; │ │1438 + if (params.values.get(.patchrev)) |id| { │ │1439 + if (!params.only(&.{ .line, .patchrev })) return null; │ │1440 + return repoPatchRevFilesRoute(pair, id, pathValue(segments.rest()) orelse return null, params.line() orelse return null);│ │1441 + } │ │1442 if (!params.only(&.{ .line, .branch, .tag, .object })) return null; │ │1443 const line = params.line() orelse return null; │ │1444 const dir = pathValue(segments.rest()) orelse return null; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1460 return if (start == 0) repoCommitMessageRoute(pair, ref.kind, ref.value, base_oid) else null; │ │1461 const file = pathValue(segments.rest()) orelse return null; │ │1462 return repoCommitsRoute(pair, ref.kind, ref.value, start, file, base_oid); │ │1463 + } │ │1464 + if (std.mem.eql(u8, tab, "diff")) { │ │1465 + params.scanPairs(&segments) catch return null; │ │1466 + if (params.values.get(.patchrev)) |id| { │ │1467 + if (!params.only(&.{ .start, .patchrev })) return null; │ │1468 + return repoPatchRevDiffRoute(pair, id, params.start() orelse return null, pathValue(segments.rest()) orelse return null);│ │1469 + } │ │1470 + if (!params.only(&.{ .start, .branch, .tag, .object, .base })) return null; │ │1471 + const base_oid = params.values.get(.base) orelse return null; │ │1472 + const start = params.start() orelse return null; │ │1473 + const ref = params.ref() catch return null; │ │1474 + const file = pathValue(segments.rest()) orelse return null; │ │1475 + return repoDiffRoute(pair, if (ref) |r| r.kind else null, if (ref) |r| r.value else "", start, file, base_oid); │ │1476 } │ │1477 return null; // unknown sub-path │ │1478 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1489 .repo_files => |a_f| std.mem.eql(u8, a_f.name.slice(), b.repo_files.name.slice()) and │ │1490 a_f.ref_kind == b.repo_files.ref_kind and │ │1491 std.mem.eql(u8, a_f.ref_value.slice(), b.repo_files.ref_value.slice()) and │ │1492 + std.mem.eql(u8, a_f.patchrev_id.slice(), b.repo_files.patchrev_id.slice()) and │ │1493 std.mem.eql(u8, a_f.path.slice(), b.repo_files.path.slice()) and │ │1494 a_f.line == b.repo_files.line, │ │1495 .repo_commits => |a_c| std.mem.eql(u8, a_c.name.slice(), b.repo_commits.name.slice()) and│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1503 }, │ │1504 .message => std.meta.activeTag(b.repo_commits.content) == .message, │ │1505 }, │ │1506 + .repo_diff => |a_d| std.mem.eql(u8, a_d.name.slice(), b.repo_diff.name.slice()) and │ │1507 + a_d.ref_or_oid == b.repo_diff.ref_or_oid and │ │1508 + std.mem.eql(u8, a_d.value.slice(), b.repo_diff.value.slice()) and │ │1509 + std.mem.eql(u8, a_d.base_oid.slice(), b.repo_diff.base_oid.slice()) and │ │1510 + std.mem.eql(u8, a_d.patchrev_id.slice(), b.repo_diff.patchrev_id.slice()) and │ │1511 + a_d.start == b.repo_diff.start and std.mem.eql(u8, a_d.path.slice(), b.repo_diff.path.slice()), │ │1512 .repo_refs => |a_r| std.mem.eql(u8, a_r.name.slice(), b.repo_refs.name.slice()) and a_r.kind == b.repo_refs.kind and std.mem.eql(u8, a_r.from.slice(), b.repo_refs.from.slice()),│ │1513 .repo_issues => |a_i| std.mem.eql(u8, a_i.name.slice(), b.repo_issues.name.slice()) and │ │1514 std.mem.eql(u8, a_i.tag.slice(), b.repo_issues.tag.slice()) and │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──────╮ │src/ui/Repo.zig│ ╰───────────────╯ ╭──────────────────────────────────────────────────────╮ │15 pub const Header = @import("./Repo/Header.zig"); │ │16 pub const Files = @import("./Repo/Files.zig"); │ │17 pub const Commits = @import("./Repo/Commits.zig");│ │18 +pub const Diff = @import("./Repo/Diff.zig"); │ │19 pub const Refs = @import("./Repo/Refs.zig"); │ │20 pub const Issues = @import("./Repo/Issues.zig"); │ │21 pub const Patches = @import("./Repo/Patches.zig");│ ╰──────────────────────────────────────────────────────╯ ╭──────────────────────────╮ │29 header: Header, │ │30 repo: evt.Repo.Record,│ │31 files: Files, │ │31 -commits: Commits, │ │32 +changes: Changes, │ │33 refs: Refs, │ │34 issues: Issues, │ │35 patches: Patches, │ ╰──────────────────────────╯ ╭────────────────────────────────────────╮ │41 │ │42 const Self = @This(); │ │43 │ │44 +const Changes = union(enum) { │ │45 + commits: Commits, │ │46 + diff: Diff, │ │47 +}; │ │48 + │ │49 pub fn init( │ │50 arena: *std.heap.ArenaAllocator,│ │51 session: *ui.Session, │ ╰────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │54 const DB = evt.AdminDB; │ │55 const hash_kind = evt.admin_repo_opts.hash; │ │56 │ │51 - // every repo route stores its identity as "owner/name" (or elides it in │ │52 - // local mode); files and commits carry their remaining fields directly. │ │57 + // every repo route stores its identity as "owner/name" (or elides it in local mode) │ │58 const name_str = route.repoIdentity() orelse return error.UnexpectedRoute; │ │59 const repo_identity = ui.RoutablePage.RepoIdentity.parse(name_str) orelse return error.NotFound;│ │60 const location = ui.RoutablePage.RepoLocation{ .repo = repo_identity.identity }; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯