╭ changed ───────────╮ │src/ui/Repo/Undo.zig│ ╰────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │48 // the clear-history confirmation stands in for the list │ │49 clear: bool = false, │ │50 │ │51 +// no history and no failure outside the clear form, which shows the empty message │ │52 +fn isEmpty(self: *const Self, session: *ui.Session) bool { │ │53 + return !self.clear and self.items.len == 0 and (session.data.undo_failure orelse self.failure) == null; │ │54 +} │ │55 + │ │56 pub fn init(comptime opts: rp.RepoOpts(.xit), arena: *std.heap.ArenaAllocator, repo: *rp.Repo(.xit, opts), haxy_moment: ?evt.AdminDB.HashMap(.read_only), identity: []const u8, selected: ?u64, handle: ui.RepoHandle) !Self {│ │57 const aa = arena.allocator(); │ │58 const DB = rp.Repo(.xit, opts).DB; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │406 try outer.children.put(allocator, header.getFocus().id, .{ .widget = .{ .box = header }, .rect = null, .min_size = .{ .width = null, .height = 4 } });│ │407 } │ │408 │ │404 - if (data.clear) { │ │405 - var center = try initClearForm(allocator, data, session); │ │409 + // the clear form, or the empty message when there is no history or failure to show │ │410 + if (data.clear or data.isEmpty(session)) { │ │411 + var center = if (data.clear) try initClearForm(allocator, data, session) else try ui.widget.initEmpty(allocator); │ │412 errdefer center.deinit(allocator); │ │413 try outer.children.put(allocator, center.getFocus().id, .{ .widget = .{ .center = center }, .rect = null, .min_size = null }); │ │408 - outer.getFocus().child_id = outer.children.keys()[content_index]; │ │414 + // the empty message has nothing to focus, so focus rests on the sub-header │ │415 + outer.getFocus().child_id = outer.children.keys()[if (data.clear) content_index else header_index]; │ │416 return .{ .box = outer, .data = data, .session = session }; │ │417 } │ │418 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │431 const route = ui.RoutablePage.repoUndoRoute(data.identity, next) orelse return error.RouteTooLong; │ │432 try addText(allocator, &rows, "next →", try session.page_arena.allocator().print("a:{s}", .{try route.toUrl(session.page_arena)}), .hidden); │ │433 } │ │427 - if (data.items.len == 0) try addText(allocator, &rows, "no undo history", null, .hidden); │ │434 + // the failure row, filled in by build │ │435 + if (data.items.len == 0) try addText(allocator, &rows, "", null, .hidden); │ │436 rows.getFocus().child_id = rows.children.keys()[0]; │ │437 break :blk try wgt.Scroll(ui.Widget).init(allocator, .{ .box = rows }, .{ .direction = .vert, .web_native = !session.is_terminal, .fill = true });│ │438 }; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │578 │ │579 pub fn build(self: *View, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void { │ │580 self.clearGrid(); │ │573 - if (self.data.clear) return try self.box.build(allocator, constraint, root_focus); │ │581 + // the clear form or the empty message stands in for the list │ │582 + if (self.data.clear or self.data.isEmpty(self.session)) return try self.box.build(allocator, constraint, root_focus);│ │583 try self.refreshDetail(allocator); │ │584 const rows = &self.listScroll().child.box; │ │585 if (self.data.items.len == 0) { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │577 - const failure = self.session.data.undo_failure orelse self.data.failure; │ │586 + // an empty list only shows to report its failure │ │587 + const failure = self.session.data.undo_failure orelse self.data.failure orelse unreachable; │ │588 if (!std.meta.eql(self.shown_failure, failure)) { │ │579 - const message = if (failure) |name| try self.session.page_arena.allocator().print("error: {s}", .{name}) else "no undo history"; │ │580 - try rows.children.values()[0].widget.text_box.setContent(allocator, message); │ │589 + try rows.children.values()[0].widget.text_box.setContent(allocator, try self.session.page_arena.allocator().print("error: {s}", .{failure}));│ │590 self.shown_failure = failure; │ │591 } │ │592 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │605 // scrolling crosses the boundary the same way the arrow keys do │ │606 const direction = inp.vertDirection(key); │ │607 if (self.headerActive()) { │ │599 - if (direction == .down) root_focus.setFocus(self.box.children.values()[content_index].widget.getFocus().id); │ │608 + // the empty message has nothing to focus │ │609 + if (direction == .down and !self.data.isEmpty(self.session)) root_focus.setFocus(self.box.children.values()[content_index].widget.getFocus().id);│ │610 return; │ │611 } │ │612 if (self.data.clear) { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯