╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ─────────────╮ │src/ui/Repo/Events.zig│ ╰──────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │399 if (event_window.next) |next| { │ │340 - const route = ui.RoutablePage.repoEventsRoute(data.identity, view, next.kind, next.id) orelse return error.RouteTooLong; │ │400 + const route = ui.RoutablePage.repoEventsRoute(data.identity, view, next.kind, next.id, data.moment) orelse return error.RouteTooLong;│ │401 const link = try std.fmt.allocPrint(session.page_arena.allocator(), "a:{s}", .{try route.toUrl(session.page_arena)}); │ │402 try addRow(allocator, &list_box, "next →", link); │ │403 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │643 var tab_ids: [2]usize = undefined; │ │644 const view_labels = [_][]const u8{ active_tab_label, removed_tab_label }; │ │645 inline for ([_]ui.RoutablePage.EventsView{ .active, .removed }, view_labels, 0..) |view, view_label, i| { │ │586 - const route = ui.RoutablePage.repoEventsRoute(data.identity, view, null, "") orelse return error.RouteTooLong; │ │646 + const route = ui.RoutablePage.repoEventsRoute(data.identity, view, null, "", data.moment) orelse return error.RouteTooLong;│ │647 const count = data.window(view).count; │ │648 var label_buf: [64]u8 = undefined; │ │649 const label = try std.fmt.bufPrint(&label_buf, "{s} ({d})", .{ view_label, count }); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │766 session.data.sync_failure = null; │ │767 switch (session.data.current_page) { │ │768 .repo_events => |route| { │ │709 - if (ui.RoutablePage.repoEventsRoute(route.name.slice(), route.view, null, "")) |first| │ │769 + if (ui.RoutablePage.repoEventsRoute(route.name.slice(), route.view, null, "", route.moment)) |first|│ │770 session.data.current_page = first; │ │771 }, │ │772 else => {}, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────────╮ │src/ui/Repo/Header.zig│ ╰──────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │163 const patches_link = try ui.inPageTabLink(session, patches_route, current_tag == .repo_patches); │ │164 const discussions_route = ui.RoutablePage.repoDiscussionsRoute(identity, data.discussions_tag, "") orelse return error.RouteTooLong;│ │165 const discussions_link = try ui.inPageTabLink(session, discussions_route, current_tag == .repo_discussions); │ │166 - const events_route = ui.RoutablePage.repoEventsRoute(identity, .active, null, "") orelse return error.RouteTooLong; │ │166 + const events_route = ui.RoutablePage.repoEventsRoute(identity, .active, null, "", null) orelse return error.RouteTooLong; │ │167 const events_link = try ui.inPageTabLink(session, events_route, current_tag == .repo_events); │ │168 const settings_route = ui.RoutablePage{ .repo_settings = Array.from(identity) orelse return error.RouteTooLong }; │ │169 const settings_link = try ui.inPageTabLink(session, settings_route, current_tag == .repo_settings); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ───────────╮ │src/ui/Repo/Undo.zig│ ╰────────────────────╯ ╭────────────────────────────────────────╮ │ 5 const inp = @import("../input.zig");│ │ 6 const xit = @import("xit"); │ │ 7 const rp = xit.repo; │ │ 8 +const hash = xit.hash; │ │ 9 const wgt = xit.xitui.widget; │ │10 const layout = xit.xitui.layout; │ │11 const Key = xit.xitui.input.Key; │ ╰────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────╮ │14 const Self = @This(); │ │15 │ │16 pub const page_size = 20; │ │17 + │ │18 +// replaces the description for a transaction that wrote events│ │19 +pub const EventsLink = struct { │ │20 + label: []const u8, │ │21 + link: []const u8, │ │22 +}; │ │23 + │ │24 pub const Item = struct { │ │25 index: u64, │ │26 action: []const u8, │ ╰───────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────╮ │27 description: []const u8, │ │28 + timestamp: []const u8, │ │29 link: []const u8, │ │30 form: []const u8, │ │31 + events: ?EventsLink = null,│ │32 }; │ │33 │ │34 identity: []const u8, │ ╰───────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │25 -count: u64, │ │26 -items: []const Item, │ │27 -next: ?u64, │ │35 +count: u64 = 0, │ │36 +items: []const Item = &.{}, │ │37 +next: ?u64 = null, │ │38 +failure: ?[]const u8 = null, │ │39 +can_undo: bool = false, │ │40 │ │41 pub fn init(comptime opts: rp.RepoOpts(.xit), arena: *std.heap.ArenaAllocator, repo: *rp.Repo(.xit, opts), identity: []const u8, selected: ?u64) !Self {│ │42 const aa = arena.allocator(); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │51 remaining -= 1; │ │52 const cursor = try history.getCursor(remaining) orelse return error.TransactionNotFound; │ │53 const moment = try DB.HashMap(.read_only).init(cursor); │ │42 - var record = try xit.undo.read(opts, moment, &buffer); │ │43 - if (record) |*value| { │ │44 - if (std.mem.eql(u8, value.action, "update_branch_patch")) value.action = "update_patch"; │ │45 - } │ │54 + const record = try xit.undo.read(opts, moment, &buffer); │ │55 var description: std.Io.Writer.Allocating = .init(aa); │ │47 - if (record) |value| { │ │56 + const detail = if (record) |value| try eventDetail(opts, arena, identity, value, moment) else null;│ │57 + if (detail) |value| { │ │58 + try description.writer.writeAll(value.description); │ │59 + } else if (record) |value| { │ │60 try xit.undo.format(opts, &repo.core.db, arena.child_allocator, value, &description.writer); │ │49 - try description.writer.print("\n\n{s}", .{try formatTimestamp(aa, value.timestamp)}); │ │61 } else { │ │51 - try description.writer.writeAll("(empty description)\n\ntimestamp unavailable"); │ │62 + try description.writer.writeAll("no undo record"); │ │63 } │ │64 const route = ui.RoutablePage.repoUndoRoute(identity, remaining) orelse return error.RouteTooLong; │ │65 const url = try route.toUrl(arena); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │66 try items.append(aa, .{ │ │67 .index = remaining, │ │57 - .action = if (record) |value| try aa.dupe(u8, value.action) else "(empty description)", │ │68 + .action = if (detail) |value| value.action else if (record) |value| try aa.dupe(u8, value.action) else "unknown",│ │69 .description = description.written(), │ │70 + .events = if (detail) |value| value.events else null, │ │71 + .timestamp = if (record) |value| try formatTimestamp(aa, value.timestamp) else "timestamp unavailable", │ │72 .link = try std.fmt.allocPrint(aa, "ai:{s}", .{url}), │ │73 .form = try std.fmt.allocPrint(aa, "form:{s}/undo", .{url}), │ │74 }); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯