╭ changed ──────────────╮ │src/ui/Repo/Commits.zig│ ╰───────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │203 file_loop: while (file_iter.next() catch null) |pair_val| { │ │204 var pair = pair_val; │ │205 defer pair.deinit(); │ │206 + │ │207 + if (path.len != 0 and !std.mem.eql(u8, pair.path, path)) continue; │ │208 │ │209 var hunk_iter = df.HunkIterator(repo_kind, repo_opts).init(gpa, &pair.a, &pair.b) catch continue;│ │210 defer hunk_iter.deinit(gpa); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │347 // an in-page "ai:" anchor so a commit row is clickable with │ │348 // js off (the browser follows it, rooting the list there); │ │349 // with wasm the click just selects it and swaps the diff pane. │ │340 - try addRow(allocator, &list_box, commit.message, try commitRowLink(session.page_arena, data.identity, commit.oid)); │ │350 + try addRow(allocator, &list_box, commit.message, try commitRowLink(session.page_arena, data.identity, commit.oid, "")); │ │351 } │ │352 if (data.next_start) |next| { │ │343 - try addRow(allocator, &list_box, "next →", try commitsLink(session.page_arena, data.identity, next, 0)); │ │353 + try addRow(allocator, &list_box, "next →", try commitsLink(session.page_arena, data.identity, next, 0, "")); │ │354 } │ │355 if (list_box.children.count() > 0) list_box.getFocus().child_id = list_box.children.keys()[0]; │ │356 break :blk try wgt.Scroll(ui.Widget).init(allocator, .{ .box = list_box }, .{ .direction = .vert, .web_native = !session.is_terminal, .fill = true });│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │419 try box.children.put(allocator, tb.getFocus().id, .{ .widget = .{ .text_box = tb }, .rect = null, .min_size = null }); │ │420 } │ │421 │ │412 - // a focusable file-path label shown above the first hunk of each file. │ │413 - fn addPathBox(self: *View, allocator: std.mem.Allocator, box: *wgt.Box(ui.Widget), path: []const u8) !void { │ │414 - _ = self; │ │422 + // a focusable file-path label shown above the first hunk of each file. it │ │423 + // links to this page filtered to that file, so activating it navigates │ │424 + // there. │ │425 + fn addPathBox(self: *View, allocator: std.mem.Allocator, box: *wgt.Box(ui.Widget), path: []const u8, oid: []const u8) !void { │ │426 + const link = try commitsLink(self.session.page_arena, self.data.identity, oid, 0, path); │ │427 var tb = try wgt.TextBox(ui.Widget).init(allocator, path, .{ .border_style = .hidden, .rounded_corners = true, .wrap_kind = .none });│ │428 errdefer tb.deinit(allocator); │ │429 tb.getFocus().focusable = true; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │430 + tb.getFocus().kind = .{ .custom = link }; │ │431 try box.children.put(allocator, tb.getFocus().id, .{ .widget = .{ .text_box = tb }, .rect = null, .min_size = null });│ │432 } │ │433 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │434 // a focusable window-navigation row ("previous"/"next"). it's a link to this │ │435 // commit's route at `target_start`, so activating it (the host follows the │ │436 // "a:" link) reloads the page on the adjacent window — same on TUI and web. │ │424 - fn addNavLink(self: *View, allocator: std.mem.Allocator, box: *wgt.Box(ui.Widget), label: []const u8, oid: []const u8, target_start: usize) !void { │ │425 - const link = try commitsLink(self.session.page_arena, self.data.identity, oid, target_start); │ │437 + fn addNavLink(self: *View, allocator: std.mem.Allocator, box: *wgt.Box(ui.Widget), label: []const u8, oid: []const u8, target_start: usize, path: []const u8) !void {│ │438 + const link = try commitsLink(self.session.page_arena, self.data.identity, oid, target_start, path); │ │439 var tb = try wgt.TextBox(ui.Widget).init(allocator, label, .{ .border_style = .hidden, .rounded_corners = true, .wrap_kind = .none }); │ │440 errdefer tb.deinit(allocator); │ │441 tb.getFocus().focusable = true; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │511 if (self.selectedCommitIndex()) |sel| { │ │512 // mirror the commit's current diff window so the url stays │ │513 // linkable (0 for any commit but the windowed start one). │ │501 - if (ui.RoutablePage.repoCommitsRoute(self.data.identity, .object, self.data.commits[sel].oid, self.data.commits[sel].window_start)) |route| │ │514 + if (ui.RoutablePage.repoCommitsRoute(self.data.identity, .object, self.data.commits[sel].oid, self.data.commits[sel].window_start, if (sel == 0) self.data.path else "")) |route|│ │515 self.session.data.current_page = route; │ │516 } │ │517 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────╮ │575 │ │576 fn populateDiff(self: *View, allocator: std.mem.Allocator, sel: usize) !void {│ │577 const commit = self.data.commits[sel]; │ │578 + // the path filter only applies to the top commit (the walk root). │ │579 + const path = if (sel == 0) self.data.path else ""; │ │580 const inner = self.diffInner(); │ │581 │ │582 for (inner.children.values()) |*child| child.widget.deinit(allocator); │ ╰───────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │583 inner.children.clearAndFree(allocator); │ │584 inner.getFocus().child_id = null; │ │585 │ │571 - // the first row always links to viewing the repo's files at this commit. │ │572 - try self.addViewFilesLink(allocator, inner, commit.oid); │ │586 + if (path.len != 0) { │ │587 + // the file's path, then a row returning to this commit's unfiltered diff. │ │588 + var label = try wgt.TextBox(ui.Widget).init(allocator, path, .{ .border_style = .hidden, .rounded_corners = true, .wrap_kind = .none });│ │589 + errdefer label.deinit(allocator); │ │590 + label.getFocus().focusable = true; │ │591 + try inner.children.put(allocator, label.getFocus().id, .{ .widget = .{ .text_box = label }, .rect = null, .min_size = null }); │ │592 + try self.addNavLink(allocator, inner, "← all files", commit.oid, 0, ""); │ │593 + } else { │ │594 + // the first row always links to viewing the repo's files at this commit. │ │595 + try self.addViewFilesLink(allocator, inner, commit.oid); │ │596 + } │ │597 │ │598 // a "previous" row and a "next" row at the bottom reload the page on the │ │599 // adjacent diff window. │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │576 - if (commit.has_prev) try self.addNavLink(allocator, inner, "← previous", commit.oid, commit.window_start -| diff_page); │ │600 + if (commit.has_prev) try self.addNavLink(allocator, inner, "← previous", commit.oid, commit.window_start -| diff_page, path);│ │601 for (commit.hunks) |hunk| { │ │578 - if (hunk.path) |path| try self.addPathBox(allocator, inner, path); │ │602 + if (path.len == 0) if (hunk.path) |hunk_path| try self.addPathBox(allocator, inner, hunk_path, commit.oid); │ │603 try self.addHunkBox(allocator, inner, hunk.lines); │ │604 } │ │581 - if (commit.has_more) try self.addNavLink(allocator, inner, "next →", commit.oid, commit.window_start + diff_page); │ │605 + if (commit.has_more) try self.addNavLink(allocator, inner, "next →", commit.oid, commit.window_start + diff_page, path); │ │606 │ │607 // point the pane at its first row so focus recovery can land here. │ │608 if (inner.children.count() > 0) inner.getFocus().child_id = inner.children.keys()[0]; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │637 .enter => if (self.selectedCommitIndex() != null) │ │638 try self.focusDiff(root_focus) │ │639 else if (self.data.next_start) |next| { │ │616 - if (ui.RoutablePage.repoCommitsRoute(self.data.identity, .object, next, 0)) |route| │ │640 + if (ui.RoutablePage.repoCommitsRoute(self.data.identity, .object, next, 0, "")) |route|│ │641 try self.session.navigate(route); │ │642 }, │ │643 .arrow_right => try self.focusDiff(root_focus), │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯