╭ changed ─────────────╮ │src/ui/Repo/Issues.zig│ ╰──────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────╮ │14 const inp = @import("../input.zig"); │ │15 │ │16 const wasm = builtin.target.cpu.arch == .wasm32; │ │17 + │ │18 +// how much of a description the detail pane shows before linking to the│ │19 +// /description page, the same limit commit messages use. │ │20 +pub const max_description_size = 2 * 1024; │ │21 │ │22 // how many issues one window shows before a "next" link appears. │ │23 pub const page_size = 20; │ ╰────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────╮ │58 closed: Window, │ │59 // the view the page shows initially (a selected issue's status overrides the route's)│ │60 view: ui.RoutablePage.IssuesView, │ │61 +// the /description page: the detail pane shows the selected issue's whole │ │62 +// description behind a back link. │ │63 +description_page: bool = false, │ │64 // every tag in the repo, in sorted order, for the tags view. │ │65 tags: []const []const u8, │ │66 // the on-disk repo this page was read from, for the terminal submit path │ ╰──────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │ 96 .selected_id = try aa.dupe(u8, selected_id), │ │ 97 .open = .empty, │ │ 98 .closed = .empty, │ │ 92 - .view = view, │ │ 99 + // a description url shows a status list; the issue's own status picks│ │100 + // it once the issue is read. │ │101 + .view = if (view == .description) .open else view, │ │102 + .description_page = view == .description, │ │103 .tags = &.{}, │ │104 }; │ │105 } │ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────╮ │231 .open = open_window, │ │232 .closed = closed_window, │ │233 .view = resolved_view, │ │234 + .description_page = empty.description_page,│ │235 .tags = tags.items, │ │236 }; │ │237 } │ ╰────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────╮ │361 .closed => closed_view_index, │ │362 .tags => tags_view_index, │ │363 .new, .edit => form_view_index, │ │364 + // init resolves a description url to its issue's status list│ │365 + .description => unreachable, │ │366 }; │ │367 } │ │368 │ ╰──────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │696 self.session.data.current_page = (if (index == form_view_index and self.data.view == .edit) │ │697 ui.RoutablePage.repoIssuesEditRoute(self.data.identity, self.data.selected_id) │ │698 else if (index == viewIndex(self.data.view) and self.data.selected_id.len != 0) │ │686 - ui.RoutablePage.repoIssuesRoute(self.data.identity, .open, self.data.tag, self.data.selected_id) │ │699 + (if (self.data.description_page) │ │700 + ui.RoutablePage.repoIssuesDescriptionRoute(self.data.identity, self.data.selected_id) │ │701 + else │ │702 + ui.RoutablePage.repoIssuesRoute(self.data.identity, .open, self.data.tag, self.data.selected_id))│ │703 else if (index == tags_view_index) │ │704 ui.RoutablePage.repoIssuesTagsRoute(self.data.identity, self.data.tag) │ │705 else if (index == form_view_index) │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │718 if (root_focus.grandchild_id) |g| { │ │719 if (self.resultsBox(i).getFocus().children.contains(g)) { │ │720 if (self.selectedIssueIndex(i)) |sel| { │ │705 - if (ui.RoutablePage.repoIssuesRoute(self.data.identity, splitStatus(i), "", self.window(i).issues[sel].id)) |route|│ │706 - self.session.data.current_page = route; │ │721 + // selecting another issue leaves the description page │ │722 + // behind, like it leaves the tag filter behind. │ │723 + const id = self.window(i).issues[sel].id; │ │724 + const mirrored = if (self.data.description_page and std.mem.eql(u8, id, self.data.selected_id)) │ │725 + ui.RoutablePage.repoIssuesDescriptionRoute(self.data.identity, id) │ │726 + else │ │727 + ui.RoutablePage.repoIssuesRoute(self.data.identity, splitStatus(i), "", id); │ │728 + if (mirrored) |route| self.session.data.current_page = route; │ │729 } │ │730 } │ │731 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │794 fn populateDetail(self: *View, allocator: std.mem.Allocator, index: usize, sel: usize) !void { │ │795 const entry = self.window(index).issues[sel]; │ │796 const inner = self.detailInner(index); │ │797 + // the /description page replaces its issue's detail with a back link │ │798 + // and the whole description; other issues keep their normal detail. │ │799 + const description_page = self.data.description_page and std.mem.eql(u8, entry.id, self.data.selected_id);│ │800 │ │801 for (inner.children.values()) |*child| child.widget.deinit(allocator); │ │802 inner.children.clearAndFree(allocator); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │803 inner.getFocus().child_id = null; │ │804 │ │780 - // the open/close button │ │805 + // the tool row: the open/close and edit buttons; the description page │ │806 + // shows none. │ │807 { │ │808 + const row = self.toolRow(index); │ │809 + for (row.children.values()) |*child| child.widget.deinit(allocator);│ │810 + row.children.clearAndFree(allocator); │ │811 + row.getFocus().child_id = null; │ │812 + row.getFocus().kind = .container; │ │813 + } │ │814 + if (!description_page) { │ │815 const action: []const u8 = switch (entry.issue.status) { │ │816 .open => "close", │ │817 .closed => "open", │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │818 }; │ │819 const row = self.toolRow(index); │ │787 - for (row.children.values()) |*child| child.widget.deinit(allocator); │ │788 - row.children.clearAndFree(allocator); │ │820 const pa = self.session.page_arena.allocator(); │ │821 row.getFocus().kind = .{ .custom = if (self.data.identity.len == 0) │ │822 try std.fmt.allocPrint(pa, "form:/issues/{s}/{s}", .{ entry.id, action })│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯