╭ changed ───────────╮ │src/ui/Repo/Undo.zig│ ╰────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │203 var details = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert }); │ │204 errdefer details.deinit(allocator); │ │205 if (data.items.len > 0) { │ │206 + try addText(allocator, &details, "", null, .single); │ │207 try addText(allocator, &details, "", null, .single); │ │208 + details.children.values()[1].widget.text_box.options.label = " timestamp "; │ │209 try addText(allocator, &details, "", null, .single); │ │210 + details.children.values()[2].widget.text_box.options.label = " description "; │ │211 details.getFocus().child_id = details.children.keys()[0]; │ │212 } │ │213 break :blk try wgt.Scroll(ui.Widget).init(allocator, .{ .box = details }, .{ .direction = .vert, .web_native = !session.is_terminal, .fill = true });│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │245 │ │246 fn refreshDetail(self: *View, allocator: std.mem.Allocator) !void { │ │247 const index = self.selected() orelse return; │ │176 - if (self.detailed_index == index) return; │ │248 + const failure = self.session.data.undo_failure orelse self.data.failure; │ │249 + if (self.detailed_index == index and std.meta.eql(self.shown_failure, failure)) return; │ │250 const details = &self.detailScroll().child.box; │ │251 + if (self.failure_id) |id| { │ │252 + if (details.children.getPtr(id)) |child| child.widget.deinit(allocator); │ │253 + _ = details.children.orderedRemove(id); │ │254 + self.failure_id = null; │ │255 + } │ │256 const item = self.data.items[index]; │ │257 const button = &details.children.values()[0].widget.text_box; │ │180 - const description = &details.children.values()[1].widget.text_box; │ │258 + const description = &details.children.values()[2].widget.text_box; │ │259 try button.setContent(allocator, if (item.index == 0) "initial state cannot be undone" else if (item.index == self.data.count - 1) "undo this" else "undo this and all above it");│ │182 - try description.setContent(allocator, item.description); │ │183 - details.getFocus().kind = if (item.index > 0) .{ .custom = item.form } else .container; │ │184 - button.getFocus().kind = if (item.index > 0) .{ .custom = "submit" } else .text_box; │ │260 + // a transaction that wrote events links to them instead of describing itself │ │261 + if (item.events) |events| { │ │262 + try description.setContent(allocator, events.label); │ │263 + description.options.label = " events "; │ │264 + description.getFocus().kind = .{ .custom = events.link }; │ │265 + } else { │ │266 + try description.setContent(allocator, item.description); │ │267 + description.options.label = " description "; │ │268 + description.getFocus().kind = .text_box; │ │269 + } │ │270 + const enabled = self.data.can_undo and item.index > 0; │ │271 + button.getFocus().kind = if (enabled) .{ .custom = "submit" } else .text_box; │ │272 + try details.children.values()[1].widget.text_box.setContent(allocator, item.timestamp); │ │273 + if (failure) |message| { │ │274 + try addText(allocator, details, try std.fmt.allocPrint(self.session.page_arena.allocator(), "error: {s}", .{message}), null, .single); │ │275 + self.failure_id = details.children.keys()[details.children.count() - 1]; │ │276 + } │ │277 + self.shown_failure = failure; │ │278 + details.getFocus().kind = if (enabled) .{ .custom = item.form } else .container; │ │279 details.getFocus().child_id = details.children.keys()[0]; │ │280 self.detailScroll().x = 0; │ │281 self.detailScroll().y = 0; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │287 self.clearGrid(); │ │288 try self.refreshDetail(allocator); │ │289 const rows = &self.listScroll().child.box; │ │290 + if (self.data.items.len == 0) { │ │291 + const failure = self.session.data.undo_failure orelse self.data.failure; │ │292 + if (!std.meta.eql(self.shown_failure, failure)) { │ │293 + const message = if (failure) |name| try std.fmt.allocPrint(self.session.page_arena.allocator(), "error: {s}", .{name}) else "no undo history"; │ │294 + try rows.children.values()[0].widget.text_box.setContent(allocator, message); │ │295 + self.shown_failure = failure; │ │296 + } │ │297 + } │ │298 for (rows.children.keys(), rows.children.values()) |id, *child| child.widget.text_box.options.border_style = if (rows.getFocus().child_id == id) .single else .hidden;│ │299 const both_fit = if (constraint.max_size.width) |width| width >= list_max_width + detail_min_width else true; │ │300 self.box.children.values()[0].max_size = if (both_fit) .{ .width = list_max_width, .height = null } else null; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │315 .mouse => |mouse| inp.leftClickOn(root_focus, button_id, mouse), │ │316 else => false, │ │317 }; │ │216 - if (activated and self.data.items[index].index > 0) { │ │318 + if (activated and self.data.can_undo and self.data.items[index].index > 0) { │ │319 if (builtin.target.cpu.arch != .wasm32 and self.session.is_terminal and self.session.host_request == null) { │ │320 const route = ui.RoutablePage.repoUndoRoute(self.data.identity, self.data.items[index].index) orelse return error.RouteTooLong;│ │321 self.session.host_request = .{ .undo = route.repo_undo }; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯