╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────╮ │src/ui/Repo.zig│ ╰───────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │315 try Events.empty(aa, repo_identity.identity, events_view, session.local != null, session.data.sync_failure),│ │316 }; │ │317 }; │ │318 + if (route == .repo_undo and undo_data == null) return error.NotFound; │ │319 issues.repo_source = source; │ │320 patches.repo_source = source; │ │321 patches.repo_id = repo_id_maybe; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────╮ │332 .patches = patches, │ │333 .discussions = discussions, │ │334 .events = events, │ │335 + .undo = undo_data, │ │336 .settings = Settings.init(),│ │337 .auth = Auth.init(), │ │338 .quit = Quit.init(), │ ╰─────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │409 try stack.children.put(allocator, discussions_view.getFocus().id, .{ .repo_discussions = discussions_view });│ │410 } │ │411 │ │401 - { │ │412 + if (session.data.host_kind == .local or data.undo != null) { │ │413 var events_view = try Events.View.init(allocator, &data.events, session); │ │414 errdefer events_view.deinit(allocator); │ │415 try stack.children.put(allocator, events_view.getFocus().id, .{ .repo_events = events_view }); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │416 } │ │417 │ │418 + if (data.undo) |*undo| { │ │419 + var undo_view = try Undo.View.init(allocator, undo, session); │ │420 + errdefer undo_view.deinit(allocator); │ │421 + try stack.children.put(allocator, undo_view.getFocus().id, .{ .repo_undo = undo_view });│ │422 + } │ │423 + │ │424 // the header only shows the settings tab with a login and the auth │ │425 // tab outside local mode, so keep the stack's children 1:1 with │ │426 // the tabs by skipping the same views. │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────────╮ │src/ui/Repo/Header.zig│ ╰──────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │264 }); │ │265 } │ │266 │ │267 - // events │ │268 - { │ │267 + // events, which a server only shows to whoever it shows undo to │ │268 + if (session.data.host_kind == .local or page.undo != null) { │ │269 var text_box = try wgt.TextBox.init(allocator, events_tab_label, .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none });│ │270 errdefer text_box.deinit(allocator); │ │271 text_box.getFocus().mode = .all; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │279 }); │ │280 } │ │281 │ │282 + if (page.undo != null) { │ │283 + const route = ui.RoutablePage.repoUndoRoute(identity, null) orelse return error.RouteTooLong; │ │284 + var text_box = try wgt.TextBox.init(allocator, "undo", .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none });│ │285 + errdefer text_box.deinit(allocator); │ │286 + text_box.getFocus().mode = .all; │ │287 + text_box.getFocus().kind = .{ .custom = try ui.inPageTabLink(session, route, current_tag == .repo_undo) }; │ │288 + try tab_ids.put(allocator, text_box.getFocus().id, {}); │ │289 + if (current_tag == .repo_undo) selected_tab = text_box.getFocus().id; │ │290 + try tabs_box.children.put(allocator, text_box.getFocus().id, .{ │ │291 + .widget = .{ .text_box = text_box }, │ │292 + .rect = null, │ │293 + .min_size = .{ .width = 6, .height = null }, │ │294 + }); │ │295 + } │ │296 + │ │297 // spacer pushes settings + auth to the right │ │298 { │ │299 var spacer = try ui.widget.Spacer.init(allocator); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ added ─────────────╮ │src/ui/Repo/Undo.zig│ ╰────────────────────╯ ╭ changed ────────╮ │src/ui/widget.zig│ ╰─────────────────╯ ╭───────────────────────────────────────────────────────────╮ │55 repo_discussion_detail: ui.Repo.Discussions.Detail,│ │56 repo_events_header: ui.Repo.Events.Header.View, │ │57 repo_events: ui.Repo.Events.View, │ │58 + repo_undo: ui.Repo.Undo.View, │ │59 repo_comment: ui.Repo.Comment.Item, │ │60 copyable_text: CopyableText, │ │61 patch_source: ui.Repo.Patches.Source, │ ╰───────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │107 // whether moving up should return to the page header │ │108 pub fn atTop(self: *Widget, root_focus: *Focus) bool { │ │109 return switch (self.*) { │ │109 - inline .home_users, .home_repos, .flow_box_scroll, .home_settings, .auth_login, .auth_logout, .quit, .repo_files, .repo_commits, .diff_view, .repo_refs, .repo_issues, .repo_patches, .repo_discussions, .repo_events => |*view| view.atTop(), │ │110 + inline .home_users, .home_repos, .flow_box_scroll, .home_settings, .auth_login, .auth_logout, .quit, .repo_files, .repo_commits, .diff_view, .repo_refs, .repo_issues, .repo_patches, .repo_discussions, .repo_events, .repo_undo => |*view| view.atTop(),│ │111 inline .home_auth, .repo_patch_detail => |*view| view.atTop(root_focus), │ │112 else => false, │ │113 }; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │133 if (method == .POST) { │ │134 switch (host) { │ │135 .server => |server| { │ │136 - const PostRoute = enum { login, logout, ansi, new, edit, remove, open, close, resolve, publish, merge, squash, attach }; │ │136 + const PostRoute = enum { login, logout, ansi, new, edit, remove, open, close, resolve, publish, merge, squash, attach, undo };│ │137 inline for (@typeInfo(PostRoute).@"enum".fields) |field| { │ │138 const suffix = "/" ++ field.name; │ │139 if (std.mem.endsWith(u8, path, suffix)) { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯