╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │101 │ │102 pub const RefKind = enum { branch, tag }; │ │103 │ │104 - pub const IssuesView = enum { open, closed, tags, new, edit }; │ │104 + pub const IssuesView = enum { open, closed, tags, new, edit, description };│ │105 │ │106 pub const RefOrOid = enum { │ │107 branch, │ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │361 .name = Array(repo_route_max_len).from(identity) orelse return null, │ │362 .selected = Array(evt.event_id_size * 2).from(selected) orelse return null, │ │363 .view = .edit, │ │364 + } }; │ │365 + } │ │366 + │ │367 + // build a `.repo_issues` route showing the whole description of the issue │ │368 + // with hex event id `selected`. │ │369 + pub fn repoIssuesDescriptionRoute(identity: []const u8, selected: []const u8) ?RoutablePage {│ │370 + if (selected.len == 0) return null; │ │371 + return .{ .repo_issues = .{ │ │372 + .name = Array(repo_route_max_len).from(identity) orelse return null, │ │373 + .selected = Array(evt.event_id_size * 2).from(selected) orelse return null, │ │374 + .view = .description, │ │375 } }; │ │376 } │ │377 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │453 .repo_issues => |i| blk: { │ │454 const prefix = try repoUrlPrefix(arena, i.name.slice()); │ │455 if (i.view == .edit) break :blk try std.fmt.allocPrint(arena.allocator(), "{s}/issues/{s}/edit", .{ prefix, i.selected.slice() }); │ │456 + if (i.view == .description) break :blk try std.fmt.allocPrint(arena.allocator(), "{s}/issues/{s}/description", .{ prefix, i.selected.slice() });│ │457 // a selected issue's url carries its id instead of a view │ │458 // name, so it survives status changes │ │459 const word = if (i.selected.len != 0) i.selected.slice() else @tagName(i.view); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │561 return repoRefsRoute(pair, kind, ref.value); │ │562 } │ │563 if (std.mem.eql(u8, tab, "issues")) { │ │552 - // "<id>/edit" is the edit form for that issue, with no params.│ │564 + // "<id>/edit" is the edit form for that issue, with no params;│ │565 + // "<id>/description" is its whole-description page. │ │566 const after_tab = segments.rest(); │ │567 if (std.mem.endsWith(u8, after_tab, "/edit")) { │ │568 const id = after_tab[0 .. after_tab.len - "/edit".len]; │ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────╮ │569 if (id.len == 0 or std.mem.indexOfScalar(u8, id, '/') != null) return null;│ │570 return repoIssuesEditRoute(pair, id); │ │571 } │ │572 + if (std.mem.endsWith(u8, after_tab, "/description")) { │ │573 + const id = after_tab[0 .. after_tab.len - "/description".len]; │ │574 + if (id.len == 0 or std.mem.indexOfScalar(u8, id, '/') != null) return null;│ │575 + return repoIssuesDescriptionRoute(pair, id); │ │576 + } │ │577 // the word is a view name or an issue event id (whose status │ │578 // picks the view); a filter with neither is never emitted. │ │579 const word = params.scanTail(&segments) catch return null; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────╯