╭ changed ─────────────╮ │src/ui/Repo/Issues.zig│ ╰──────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │252 // seek once to the window start: the root key, or the set's first entry. │ │253 var prev_id: ?[]const u8 = null; │ │254 var iter = if (root_key) |key| blk: { │ │255 - // the previous window starts page_size ranks back. │ │255 + // the previous window starts page_size ranks back ("" = the first │ │256 + // window, linked as the bare list route). │ │257 const rank = try set.rank(key); │ │257 - if (rank > 0) { │ │258 - const prev_rank = rank -| page_size; │ │259 - const kv = try set.getIndexKeyValuePair(@intCast(prev_rank)) orelse return error.NotFound; │ │258 + if (rank > 0 and rank <= page_size) { │ │259 + prev_id = ""; │ │260 + } else if (rank > page_size) { │ │261 + const kv = try set.getIndexKeyValuePair(@intCast(rank - page_size)) orelse return error.NotFound;│ │262 var prev_key: [@sizeOf(u64) + evt.event_id_size]u8 = undefined; │ │263 _ = try kv.key_cursor.readBytes(&prev_key); │ │264 const prev_hex = std.fmt.bytesToHex(prev_key[@sizeOf(u64)..].*, .lower); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │371 defer items.deinit(allocator); │ │372 // when filtered, the first item clears the filter │ │373 if (data.tag.len != 0) │ │372 - try items.append(allocator, .{ .text = "╳", .link = try issuesLink(session.page_arena, data.identity, "", "") }); │ │374 + try items.append(allocator, .{ .text = "╳", .link = try issuesLink(session.page_arena, data.identity, .open, "", "") });│ │375 for (data.tags) |tag| │ │376 try items.append(allocator, .{ .text = tag, .link = try tagLink(session.page_arena, data.identity, .open, tag) }); │ │377 try tf.setItems(allocator, items.items); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │407 var list_box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert }); │ │408 errdefer list_box.deinit(allocator); │ │409 if (win.prev_id) |prev| │ │408 - try addRow(allocator, &list_box, "← previous", try issuesLink(session.page_arena, data.identity, data.tag, prev)); │ │410 + try addRow(allocator, &list_box, "← previous", try issuesLink(session.page_arena, data.identity, status, data.tag, prev));│ │411 for (win.issues) |entry| │ │412 try addRow(allocator, &list_box, entry.issue.title, try issueRowLink(session.page_arena, data.identity, entry.id)); │ │413 if (win.next_id) |next| │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │412 - try addRow(allocator, &list_box, "next →", try issuesLink(session.page_arena, data.identity, data.tag, next)); │ │414 + try addRow(allocator, &list_box, "next →", try issuesLink(session.page_arena, data.identity, status, data.tag, next));│ │415 // select the window's first issue (past a leading "previous" │ │416 // row) so its description shows on load. │ │417 if (win.issues.len > 0) │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │546 self.clearGrid(); │ │547 │ │548 // each header tab maps 1:1 to a stack child by position. mirror the │ │547 - // selection into the url (the list routes keep the tag filter). │ │549 + // selection into the url: the view the page's window is rooted at │ │550 + // keeps its rooted url, the others get their list route (keeping the│ │551 + // tag filter). │ │552 if (self.header().getSelectedIndex()) |index| { │ │553 const stack = self.viewStack(); │ │554 stack.getFocus().child_id = stack.children.keys()[index]; │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │551 - self.session.data.current_page = (if (index == tags_view_index) │ │555 + self.session.data.current_page = (if (index == viewIndex(self.data.view) and self.data.selected_id.len != 0) │ │556 + ui.RoutablePage.repoIssuesRoute(self.data.identity, .open, self.data.tag, self.data.selected_id) │ │557 + else if (index == tags_view_index) │ │558 ui.RoutablePage.repoIssuesTagsRoute(self.data.identity, self.data.tag) │ │559 else │ │560 ui.RoutablePage.repoIssuesRoute(self.data.identity, splitStatus(index), self.data.tag, "")) orelse self.session.data.current_page;│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │879 } │ │880 }; │ │881 │ │876 -// the "a:" navigation link for the issues page filtered to the url-encoded │ │877 -// `tag` and rooted at issue `id` within `identity` ("owner/name"). a rooted │ │878 -// url derives its view from the issue's status; the bare list is the open view. │ │879 -fn issuesLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, tag: []const u8, id: []const u8) ![]const u8 { │ │880 - const route = ui.RoutablePage.repoIssuesRoute(identity, .open, tag, id) orelse return error.RouteTooLong; │ │882 +// the "a:" navigation link for `status`'s issues page filtered to the │ │883 +// url-encoded `tag` and rooted at issue `id` within `identity` ("owner/name"). │ │884 +// a rooted url ignores `status` (it derives its view from the issue's own); │ │885 +// with an empty `id` this is the bare list link. │ │886 +fn issuesLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, status: evt.Issue.Status, tag: []const u8, id: []const u8) ![]const u8 {│ │887 + const route = ui.RoutablePage.repoIssuesRoute(identity, status, tag, id) orelse return error.RouteTooLong; │ │888 const url = try route.urlAlloc(page_arena); │ │889 return std.fmt.allocPrint(page_arena.allocator(), "a:{s}", .{url}); │ │890 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯