╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ───────────╮ │src/ui/Repo/Undo.zig│ ╰────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │339 row.getFocus().mode = .all; │ │340 if (link) |value| row.getFocus().kind = .{ .custom = value }; │ │341 try box.children.put(allocator, row.getFocus().id, .{ .widget = .{ .text_box = row }, .rect = null, .min_size = null }); │ │342 + } │ │343 + │ │344 + // a labeled row below the timestamp, added as the selected action needs it │ │345 + fn addLabeled(allocator: std.mem.Allocator, box: *wgt.Box(ui.Widget), label: []const u8, text: []const u8, link: ?[]const u8) !void {│ │346 + try addText(allocator, box, text, link, .single); │ │347 + box.children.values()[box.children.count() - 1].widget.text_box.options.label = label; │ │348 } │ │349 │ │350 // enter counts only on the focused button; a click counts anywhere on it │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │393 const failure = self.session.data.undo_failure orelse self.data.failure; │ │394 if (self.detailed_index == index and std.meta.eql(self.shown_failure, failure)) return; │ │395 const details = &self.detailScroll().child.box; │ │384 - if (self.failure_id) |id| { │ │385 - if (details.children.getPtr(id)) |child| child.widget.deinit(allocator); │ │386 - _ = details.children.orderedRemove(id); │ │387 - self.failure_id = null; │ │396 + while (details.children.count() > fixed_rows) { │ │397 + var entry = details.children.pop() orelse break; │ │398 + entry.value.widget.deinit(allocator); │ │399 } │ │400 const item = self.data.items[index]; │ │390 - const button = &details.children.values()[0].widget.text_box; │ │391 - const description = &details.children.values()[2].widget.text_box; │ │392 - 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"); │ │393 - // some actions put a button where the description would go │ │394 - if (item.button) |detail_button| { │ │395 - try description.setContent(allocator, detail_button.text); │ │396 - description.options.label = detail_button.label; │ │397 - description.getFocus().kind = if (detail_button.link) |link| .{ .custom = link } else .text_box; │ │398 - } else { │ │399 - try description.setContent(allocator, item.description); │ │400 - description.options.label = " description "; │ │401 - description.getFocus().kind = .text_box; │ │401 + const enabled = self.data.can_undo and item.index > 0; │ │402 + │ │403 + // the fixed rows are filled first, since adding the ones below │ │404 + // invalidates every pointer into the box │ │405 + { │ │406 + const button = &details.children.values()[0].widget.text_box; │ │407 + 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");│ │408 + button.getFocus().kind = if (enabled) .{ .custom = "submit" } else .text_box; │ │409 } │ │403 - const enabled = self.data.can_undo and item.index > 0; │ │404 - button.getFocus().kind = if (enabled) .{ .custom = "submit" } else .text_box; │ │410 try details.children.values()[1].widget.text_box.setContent(allocator, item.timestamp); │ │406 - if (failure) |message| { │ │407 - try addText(allocator, details, try std.fmt.allocPrint(self.session.page_arena.allocator(), "error: {s}", .{message}), null, .single); │ │408 - self.failure_id = details.children.keys()[details.children.count() - 1]; │ │411 + │ │412 + // some actions put buttons where the description would go │ │413 + if (item.buttons.len == 0) { │ │414 + try addLabeled(allocator, details, " description ", item.description, null); │ │415 + } else for (item.buttons) |detail_button| { │ │416 + try addLabeled(allocator, details, detail_button.label, detail_button.text, detail_button.link); │ │417 } │ │418 + if (failure) |message| try addText(allocator, details, try std.fmt.allocPrint(self.session.page_arena.allocator(), "error: {s}", .{message}), null, .single); │ │419 self.shown_failure = failure; │ │420 details.getFocus().kind = if (enabled) .{ .custom = item.form } else .container; │ │421 details.getFocus().child_id = details.children.keys()[0]; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯