╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Commits.zig│ ╰───────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │481 } │ │482 │ │483 pub fn input(self: *View, allocator: std.mem.Allocator, key: inp.Key, root_focus: *Focus) !void {│ │484 + _ = allocator; │ │485 if (self.diffActive()) { │ │502 - try self.diffInput(allocator, key, root_focus); │ │486 + try self.diffInput(key, root_focus); │ │487 } else { │ │488 try self.listInput(key, root_focus); │ │489 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │507 } │ │508 } │ │509 │ │526 - fn diffInput(self: *View, allocator: std.mem.Allocator, key: inp.Key, root_focus: *Focus) !void {│ │510 + fn diffInput(self: *View, key: inp.Key, root_focus: *Focus) !void { │ │511 const sc = self.diffScroll(); │ │512 switch (key) { │ │513 // left scrolls horizontally, then leaves for the list once flush left. │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │525 .arrow_down => try self.moveDiff(root_focus, 1), │ │526 .page_up => try self.pageDiff(root_focus, -page_rows), │ │527 .page_down => try self.pageDiff(root_focus, page_rows), │ │544 - .enter => if (self.focusedIsLoadMore(root_focus)) try self.loadMore(allocator, root_focus), │ │528 + // Enter / a click on the "load more" row follow its "a:" link in the │ │529 + // host (it reloads the page with more hunks), so they don't reach here. │ │530 .mouse => |mouse| switch (mouse.action) { │ │531 .scroll => |dir| try self.moveDiff(root_focus, if (dir == .up) -1 else 1), │ │547 - .press => |btn| if (btn == .left and self.focusedIsLoadMore(root_focus)) try self.loadMore(allocator, root_focus),│ │532 else => {}, │ │533 }, │ │534 else => {}, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────╮ │602 if (chosen) |id| try root_focus.setFocus(id); │ │603 } │ │604 │ │621 - fn focusedIsLoadMore(self: *View, root_focus: *Focus) bool { │ │622 - const id = self.load_more_id orelse return false; │ │623 - return root_focus.grandchild_id == id; │ │624 - } │ │625 - │ │605 // keep the diff scroll within its content, using the last build's grids. the │ │606 // scroll bar's reserved column/row isn't part of the content viewport, so │ │607 // exclude it (as scrollToRect does) or the last column/row stays unreachable.│ ╰───────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │650 if (next == @as(usize, @intCast(cur))) return; │ │651 try root_focus.setFocus(keys[next]); │ │652 if (lb.children.values()[next].rect) |rect| self.listScroll().scrollToRect(rect); │ │674 - } │ │675 - │ │676 - // render the next budget of hunks for the displayed commit and append them. │ │677 - // the TUI reopens the repo here; the wasm path has no repo and the web host │ │678 - // drives "load more" via the GET endpoint instead, so the repo code is │ │679 - // comptime-elided from the wasm build. │ │680 - fn loadMore(self: *View, allocator: std.mem.Allocator, root_focus: *Focus) !void { │ │681 - // the wasm build has no repo; it drives "load more" via the GET endpoint. │ │682 - if (!builtin.cpu.arch.isWasm()) try self.loadMoreNative(allocator, root_focus); │ │653 } │ │654 │ │685 - fn loadMoreNative(self: *View, allocator: std.mem.Allocator, root_focus: *Focus) !void { │ │686 - const io = self.session.io orelse return; │ │687 - const repos_dir = self.session.repos_dir orelse return; │ │688 - const sel = self.diffed_index orelse return; │ │689 - const commit = self.data.commits[sel]; │ │690 - if (commit.oid.len != xit.hash.hexLen(.sha1)) return; │ │691 - │ │692 - const aa = self.session.page_arena.allocator(); │ │693 - const gpa = self.session.page_arena.child_allocator; │ │694 - const repo_path = try std.fs.path.join(aa, &.{ repos_dir, self.data.repo_dir_hex }); │ │695 - var repo = rp.Repo(.xit, .{}).open(io, gpa, .{ .path = repo_path }) catch return; │ │696 - defer repo.deinit(io, gpa); │ │697 - │ │698 - var oid: [xit.hash.hexLen(.sha1)]u8 = undefined; │ │699 - @memcpy(&oid, commit.oid); │ │700 - const rendered = renderCommitDiff(io, gpa, aa, &repo, oid, self.shown_hunks, diff_budget) catch return;│ │701 - │ │702 - const inner = self.diffInner(); │ │703 - if (self.load_more_id) |id| { │ │704 - if (inner.children.fetchOrderedRemove(id)) |kv| { │ │705 - var w = kv.value.widget; │ │706 - w.deinit(allocator); │ │707 - } │ │708 - self.load_more_id = null; │ │709 - } │ │710 - // focus the last already-shown hunk (the one just above where "load │ │711 - // more" was). it's already in the focus tree from the last build, so the │ │712 - // focus lands; the freshly-appended hunks aren't in the tree until the │ │713 - // next build, so focusing one here would silently no-op. the user scrolls │ │714 - // down into the new hunks from here. │ │715 - const shown = inner.children.count(); // old hunk count ("load more" removed) │ │716 - for (rendered.hunks) |hunk| try self.addHunkBox(allocator, inner, hunk.lines); │ │717 - self.shown_hunks = rendered.shown_hunks; │ │718 - self.has_more = rendered.has_more; │ │719 - if (rendered.has_more) try self.addLoadMore(allocator, inner, commit.oid, rendered.shown_hunks); │ │720 - if (shown > 0) try root_focus.setFocus(inner.children.keys()[shown - 1]); │ │721 - } │ │722 - │ │655 pub fn clearGrid(self: *View) void { │ │656 self.box.clearGrid(); │ │657 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │681 } │ │682 }; │ │683 │ │752 -// the "a:" link for the commits page starting at `start_oid` within `identity`. │ │753 -fn commitsPageLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, start_oid: []const u8) ![]const u8 { │ │754 - const route = ui.RoutablePage.repoCommitsRoute(identity, start_oid) orelse return error.RouteTooLong; │ │684 +// the "a:" navigation link for the commits page starting at `start_oid` within │ │685 +// `identity`, showing the selected commit's first `after` hunks (0 = default). │ │686 +fn commitsLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, start_oid: []const u8, after: usize) ![]const u8 {│ │687 + const route = ui.RoutablePage.repoCommitsRoute(identity, start_oid, after) orelse return error.RouteTooLong; │ │688 const url = try route.urlAlloc(page_arena); │ │689 return std.fmt.allocPrint(page_arena.allocator(), "a:{s}", .{url}); │ │690 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────────╮ │src/ui/Repo/Header.zig│ ╰──────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │90 const identity = try std.fmt.allocPrint(aa, "{s}/{s}", .{ data.owner_name, data.name }); │ │91 const files_route = ui.RoutablePage.repoFilesRoute(identity, "") orelse return error.RouteTooLong; │ │92 const files_link = try std.fmt.allocPrint(aa, "a:{s}", .{try files_route.urlAlloc(session.page_arena)}); │ │93 - const commits_route = ui.RoutablePage.repoCommitsRoute(identity, "") orelse return error.RouteTooLong; │ │93 + const commits_route = ui.RoutablePage.repoCommitsRoute(identity, "", 0) orelse return error.RouteTooLong; │ │94 const commits_link = try std.fmt.allocPrint(aa, "a:{s}", .{try commits_route.urlAlloc(session.page_arena)}); │ │95 const settings_link = try std.fmt.allocPrint(aa, "a:/repo/{s}/{s}/settings", .{ data.owner_name, data.name });│ │96 const auth_link = try std.fmt.allocPrint(aa, "a:/repo/{s}/{s}/auth", .{ data.owner_name, data.name }); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │177 return; │ │178 } │ │179 │ │180 - if (ui.RoutablePage.fromUrl(path)) |current_page| { │ │180 + if (ui.RoutablePage.fromUrl(path, if (uri.query) |comp| comp.percent_encoded else null)) |current_page| {│ │181 // resolve the haxy_session cookie's token to a user_id via the │ │182 // store. user_id_buf lives on the stack for the rest of handleRequest, │ │183 // which is plenty for renderIndexHtml to consume. │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯