╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭─────────────────────────────────────────────────────────────────────╮ │1316 .repo_patches => |*p| p.name.slice(), │ │1317 .repo_discussions => |*t| t.name.slice(), │ │1318 .repo_events => |*e| e.name.slice(), │ │1319 + .repo_undo => |*u| u.name.slice(), │ │1320 .repo_settings, .repo_auth => |*name| name.slice(),│ │1321 else => null, │ │1322 }; │ ╰─────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1358 return switch (self) { │ │1359 .home_users, .home_repos, .home_settings, .home_auth => .home, │ │1360 .user_repos, .user_forks, .user_settings, .user_auth => .user, │ │1346 - .repo_files, .repo_commits, .repo_diff, .repo_refs, .repo_issues, .repo_patches, .repo_discussions, .repo_events, .repo_settings, .repo_auth => .repo, │ │1361 + .repo_files, .repo_commits, .repo_diff, .repo_refs, .repo_issues, .repo_patches, .repo_discussions, .repo_events, .repo_undo, .repo_settings, .repo_auth => .repo,│ │1362 .fork_patch, .fork_diff, .fork_files, .fork_commits, .fork_settings, .fork_auth => .fork, │ │1363 }; │ │1364 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1544 if (std.mem.eql(u8, view, "new")) return if (tag_value.len == 0 and search_value.len == 0) repoThreadNewRoute(.discuss, pair) else null;│ │1545 return null; │ │1546 } │ │1547 + if (std.mem.eql(u8, tab, "undo")) { │ │1548 + const index = if (segments.next()) |word| std.fmt.parseInt(u64, word, 10) catch return null else null; │ │1549 + return if (segments.next() == null) repoUndoRoute(pair, index) else null; │ │1550 + } │ │1551 if (std.mem.eql(u8, tab, "events")) { │ │1552 const view = if (segments.next()) |word| │ │1553 std.meta.stringToEnum(EventsView, word) orelse return null │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1687 a_p.view == b.repo_patches.view and │ │1688 a_p.comments_start == b.repo_patches.comments_start and │ │1689 std.mem.eql(u8, a_p.comment.slice(), b.repo_patches.comment.slice()), │ │1690 + .repo_undo => |a_u| std.mem.eql(u8, a_u.name.slice(), b.repo_undo.name.slice()) and a_u.index == b.repo_undo.index,│ │1691 .repo_events => |a_e| std.mem.eql(u8, a_e.name.slice(), b.repo_events.name.slice()) and │ │1692 a_e.view == b.repo_events.view and │ │1693 a_e.kind == b.repo_events.kind and │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────╮ │2037 pub const HostRequest = union(enum) { │ │2038 show_copyable_text: []const u8, │ │2039 sync_events, │ │2040 + undo: RoutablePage.RepoUndoRoute, │ │2041 }; │ │2042 │ │2043 pub const FormFeedback = union(enum) {│ ╰────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────╮ │2313 terminal_live = true; │ │2314 term.setActive(&terminal); │ │2315 }, │ │2316 + .undo => |target| try Repo.Undo.perform(allocator, session, target),│ │2317 .sync_events => { │ │2318 try nav.root.build(allocator, .{ │ │2319 .min_size = .{ .width = null, .height = null }, │ ╰──────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──────╮ │src/ui/Repo.zig│ ╰───────────────╯ ╭──────────────────────────────────────────────────────────────╮ │21 pub const Patches = @import("./Repo/Patches.zig"); │ │22 pub const Discussions = @import("./Repo/Discussions.zig");│ │23 pub const Comment = @import("./Repo/Comment.zig"); │ │24 +pub const Undo = @import("./Repo/Undo.zig"); │ │25 pub const Events = @import("./Repo/Events.zig"); │ │26 pub const Settings = @import("./Settings.zig"); │ │27 pub const Auth = @import("./Auth.zig"); │ ╰──────────────────────────────────────────────────────────────╯ ╭─────────────────────────────╮ │36 patches: Patches, │ │37 discussions: Discussions,│ │38 events: Events, │ │39 +undo: ?Undo = null, │ │40 settings: Settings, │ │41 auth: Auth, │ │42 quit: Quit, │ ╰─────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │256 │ │257 // open the repo once for every tab. files and changes share a ref or revision. │ │258 // no filesystem (wasm), nowhere to look, or a failed open: empty tabs. │ │259 + const undo_allowed = if (session.local) |local| local.repo_kind == .xit else session.userId() != null and evt.Repo.roleOf(repo, session.userId()).atLeast(.write);│ │260 + if (route == .repo_undo and !undo_allowed) return error.NotFound; │ │261 + // a server shows events to whoever it shows undo to, so the route follows │ │262 + if (route == .repo_events and session.data.host_kind != .local and !undo_allowed) return error.NotFound; │ │263 + const undo_index = if (route == .repo_undo) route.repo_undo.index else null; │ │264 + var undo_data: ?Undo = null; │ │265 const files, const changes, const refs, var issues, var patches, var discussions, const events = blk: { │ │266 read: { │ │267 const io = session.io orelse break :read; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │278 try evt.consume(.local, .repo, repo_kind, opened.self_repo_opts, io, gpa, opened, evt.events_ref, &.{}); │ │279 try pch.refreshBranches(.local, repo_kind, opened.self_repo_opts, io, gpa, opened, null, null); │ │280 } │ │281 + if (repo_kind == .xit and undo_allowed) undo_data = try Undo.init(opened.self_repo_opts, arena, opened, repo_identity.identity, undo_index);│ │282 const files_data = if (patchrev_id.len != 0) │ │283 try Files.initPatchRev(repo_kind, opened.self_repo_opts, arena, opened, io, gpa, location, patchrev_id, files_dir, files_line) │ │284 else │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯