╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ─────────────╮ │src/ui/Repo/Issues.zig│ ╰──────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │314 } │ │315 const issue_cursor = try event_id_to_issue.getCursor(hash.hashInt(hash_kind, order_key[@sizeOf(u64)..])) orelse continue;│ │316 const issue_map = try DB.HashMap(.read_only).init(issue_cursor); │ │317 + const issue_event = try evt.read(evt.Issue, DB, hash_kind, arena, issue_map); │ │318 try issues.append(aa, .{ │ │319 .id = try aa.dupe(u8, &id_hex), │ │313 - .issue = try evt.read(evt.Issue, DB, hash_kind, arena, issue_map), │ │320 + .issue = issue_event, │ │321 + .author = try ui.Author.initFromDb(admin_moment, arena, issue_event.author_email), │ │322 }); │ │323 } │ │324 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │338 data: *const Self, │ │339 session: *ui.Session, │ │340 // per-split state, indexed like the stack's split children: the issue the│ │333 - // pane shows, and its description text box's focus id. │ │341 + // pane shows, and its description and author text boxes' focus ids. │ │342 detailed_index: [split_count]?usize, │ │343 description_id: [split_count]?usize, │ │344 + author_id: [split_count]?usize, │ │345 │ │346 const header_index: usize = 0; │ │347 const stack_index: usize = 1; │ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────╮ │455 .session = session, │ │456 .detailed_index = .{ null, null },│ │457 .description_id = .{ null, null },│ │458 + .author_id = .{ null, null }, │ │459 }; │ │460 } │ │461 │ ╰───────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │811 for (inner.children.values()) |*child| child.widget.deinit(allocator);│ │812 inner.children.clearAndFree(allocator); │ │813 inner.getFocus().child_id = null; │ │814 + self.author_id[index] = null; │ │815 │ │816 // the tool row: the open/close and edit buttons; the description page│ │817 // shows none. │ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │879 tb.getFocus().focusable = true; │ │880 try inner.children.put(allocator, tb.getFocus().id, .{ .widget = .{ .text_box = tb }, .rect = null, .min_size = null });│ │881 } │ │882 + │ │883 + self.author_id[index] = blk: { │ │884 + var tb = try ui.authorBox(allocator, self.session.page_arena, entry.author); │ │885 + errdefer tb.deinit(allocator); │ │886 + try inner.children.put(allocator, tb.getFocus().id, .{ .widget = .{ .text_box = tb }, .rect = null, .min_size = null });│ │887 + break :blk tb.getFocus().id; │ │888 + }; │ │889 │ │890 // the issue's tags, each linking to this status's list filtered to │ │891 // that tag. │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────╮ │1015 try self.toolRowInput(allocator, index, key, root_focus);│ │1016 } else if (self.titleFocused(index)) { │ │1017 try self.titleInput(index, key, root_focus); │ │1018 + } else if (self.authorFocused(index)) { │ │1019 + try self.authorInput(index, key, root_focus); │ │1020 } else if (self.tagsFocused(index)) { │ │1021 try self.tagsInput(index, key, root_focus); │ │1022 } else { │ ╰───────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1055 switch (key) { │ │1056 .arrow_left => try self.focusList(index, root_focus), │ │1057 .arrow_up => self.focusToolRow(index, root_focus), │ │1058 + .arrow_down => if (self.authorPresent(index)) self.focusAuthor(index, root_focus) else try self.focusDescription(index, root_focus), │ │1059 + .mouse => |mouse| switch (mouse.action) { │ │1060 + .scroll => |dir| { │ │1061 + sc.y += if (dir == .up) @as(isize, -1) else 1; │ │1062 + sc.clampToContent(); │ │1063 + }, │ │1064 + else => {}, │ │1065 + }, │ │1066 + else => {}, │ │1067 + } │ │1068 + } │ │1069 + │ │1070 + // the author box's a: link (when it has one) is followed by the host on │ │1071 + // enter / a click; arrows cross to the neighboring widgets. │ │1072 + fn authorInput(self: *View, index: usize, key: Key, root_focus: *Focus) !void { │ │1073 + const sc = self.detailScroll(index); │ │1074 + switch (key) { │ │1075 + .arrow_left => try self.focusList(index, root_focus), │ │1076 + .arrow_up => self.focusTitle(index, root_focus), │ │1077 .arrow_down => if (self.tagFlow(index) != null) try self.focusTags(index, root_focus) else try self.focusDescription(index, root_focus),│ │1078 .mouse => |mouse| switch (mouse.action) { │ │1079 .scroll => |dir| { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1097 sc.y -= 1; │ │1098 sc.clampToContent(); │ │1099 if (sc.y == before) { │ │1061 - if (self.tagFlow(index) != null) try self.focusTags(index, root_focus) else self.focusTitle(index, root_focus);│ │1100 + if (self.tagFlow(index) != null) │ │1101 + try self.focusTags(index, root_focus) │ │1102 + else if (self.authorPresent(index)) │ │1103 + self.focusAuthor(index, root_focus) │ │1104 + else │ │1105 + self.focusTitle(index, root_focus); │ │1106 } │ │1107 return; │ │1108 }, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1311 switch (key) { │ │1312 .arrow_left => if (cur > 0) self.focusTag(index, tf, root_focus, cur - 1) else try self.focusList(index, root_focus), │ │1313 .arrow_right => if (cur + 1 < count) self.focusTag(index, tf, root_focus, cur + 1), │ │1270 - .arrow_up => if (tf.rowStep(cur, false)) |i| self.focusTag(index, tf, root_focus, i) else self.focusTitle(index, root_focus), │ │1314 + .arrow_up => if (tf.rowStep(cur, false)) |i| self.focusTag(index, tf, root_focus, i) else self.focusAuthor(index, root_focus), │ │1315 .arrow_down => if (tf.rowStep(cur, true)) |i| self.focusTag(index, tf, root_focus, i) else try self.focusDescription(index, root_focus),│ │1316 .home => self.focusTag(index, tf, root_focus, 0), │ │1317 .end => self.focusTag(index, tf, root_focus, count - 1), │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────╮ │1329 const button_in_row_index: usize = 1; │ │1330 const edit_in_row_index: usize = 2; │ │1331 const title_child_index: usize = 0; │ │1288 - const tags_child_index: usize = 1; │ │1332 + const tags_child_index: usize = 2; │ │1333 │ │1334 fn tagFlow(self: *View, index: usize) ?*ui.TagFlow {│ │1335 const inner = self.detailInner(index); │ ╰──────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯