╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1723 .home_users => |a_u| a_u.start == b.home_users.start and std.mem.eql(u8, a_u.search.slice(), b.home_users.search.slice()), │ │1724 .user_repos => |a_u| std.mem.eql(u8, a_u.name.slice(), b.user_repos.name.slice()) and a_u.start == b.user_repos.start and std.mem.eql(u8, a_u.search.slice(), b.user_repos.search.slice()),│ │1725 .user_forks => |a_u| std.mem.eql(u8, a_u.name.slice(), b.user_forks.name.slice()) and a_u.start == b.user_forks.start, │ │1743 - .user_settings => |a_name| std.mem.eql(u8, a_name.slice(), b.user_settings.slice()), │ │1726 .user_new_repo => |a_name| std.mem.eql(u8, a_name.slice(), b.user_new_repo.slice()), │ │1727 .user_auth => |a_name| std.mem.eql(u8, a_name.slice(), b.user_auth.slice()), │ │1728 .repo_files => |a_f| std.mem.eql(u8, a_f.name.slice(), b.repo_files.name.slice()) and │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1784 a_e.moment == b.repo_events.moment and │ │1785 a_e.kind == b.repo_events.kind and │ │1786 std.mem.eql(u8, a_e.selected.slice(), b.repo_events.selected.slice()), │ │1805 - .repo_settings => |a_name| std.mem.eql(u8, a_name.slice(), b.repo_settings.slice()), │ │1787 .repo_new_repo => |a_name| std.mem.eql(u8, a_name.slice(), b.repo_new_repo.slice()), │ │1788 .repo_auth => |a_name| std.mem.eql(u8, a_name.slice(), b.repo_auth.slice()), │ │1789 .fork_patch => |a_f| std.mem.eql(u8, a_f.name.slice(), b.fork_patch.name.slice()) and std.mem.eql(u8, a_f.id.slice(), b.fork_patch.id.slice()),│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1806 }, │ │1807 .message => std.meta.activeTag(b.fork_commits.content) == .message, │ │1808 }, │ │1828 - .fork_settings => |a_f| std.mem.eql(u8, a_f.name.slice(), b.fork_settings.name.slice()) and std.mem.eql(u8, a_f.id.slice(), b.fork_settings.id.slice()),│ │1809 .fork_new_repo => |a_f| std.mem.eql(u8, a_f.name.slice(), b.fork_new_repo.name.slice()) and std.mem.eql(u8, a_f.id.slice(), b.fork_new_repo.id.slice()),│ │1810 .fork_auth => |a_f| std.mem.eql(u8, a_f.name.slice(), b.fork_auth.name.slice()) and std.mem.eql(u8, a_f.id.slice(), b.fork_auth.id.slice()), │ │1811 else => true, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────────╮ │src/ui/Auth/Logout.zig│ ╰──────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │19 center: ui.widget.Center, │ │20 data: *const Self, │ │21 session: *ui.Session, │ │22 - button_id: usize, │ │22 + ansi_id: usize, │ │23 + logout_id: usize, │ │24 │ │24 - const prompt_index: usize = 0; │ │25 - const button_index: usize = 1; │ │25 + const ansi_index: usize = 0; │ │26 │ │27 pub fn init(allocator: std.mem.Allocator, data: *const Self, session: *ui.Session) !View { │ │28 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .round_corners = true, .direction = .vert });│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────╮ │29 errdefer box.deinit(allocator); │ │30 - // marks this subtree as an HTML form scope for the web renderer │ │31 - box.getFocus().kind = .{ .custom = "form:logout" }; │ │30 + │ │31 + const ansi_id = try addButton(allocator, &box, "form:ansi", ansiLabel(session));│ │32 │ │33 { │ │34 - var prompt = try wgt.Text.init(allocator, "are you sure?"); │ │35 - errdefer prompt.deinit(allocator); │ │36 - try box.children.put(allocator, prompt.getFocus().id, .{ │ │37 - .widget = .{ .text = prompt }, │ │34 + var gap = try wgt.Text.init(allocator, " "); │ │35 + errdefer gap.deinit(allocator); │ │36 + try box.children.put(allocator, gap.getFocus().id, .{ │ │37 + .widget = .{ .text = gap }, │ │38 .rect = null, │ │39 .min_size = null, │ │40 }); │ ╰────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │41 } │ │42 │ │43 - var button_id: usize = undefined; │ │44 - { │ │45 - var button = try wgt.TextBox.init(allocator, "logout", .{ .border_style = .single, .round_corners = true, .wrap_kind = .none });│ │46 - errdefer button.deinit(allocator); │ │47 - button.getFocus().mode = .all; │ │48 - // the renderer distinguishes plain clickables from buttons that │ │49 - // should POST to a server route by this kind. │ │50 - button.getFocus().kind = .{ .custom = "submit" }; │ │51 - button_id = button.getFocus().id; │ │52 - try box.children.put(allocator, button.getFocus().id, .{ │ │53 - .widget = .{ .text_box = button }, │ │54 - .rect = null, │ │55 - .min_size = null, │ │56 - }); │ │57 - } │ │43 + const logout_id = try addButton(allocator, &box, "form:logout", "logout"); │ │44 │ │59 - box.getFocus().child_id = button_id; │ │45 + box.getFocus().child_id = box.children.keys()[ansi_index]; │ │46 │ │47 return .{ │ │48 .center = try ui.widget.Center.init(allocator, .{ .box = box }), │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │49 .data = data, │ │50 .session = session, │ │65 - .button_id = button_id, │ │51 + .ansi_id = ansi_id, │ │52 + .logout_id = logout_id, │ │53 }; │ │54 } │ │55 + │ │56 + // each button sits in its own form box so the web renderer posts it to its own route │ │57 + fn addButton(allocator: std.mem.Allocator, parent: *wgt.Box(ui.Widget), form: []const u8, label: []const u8) !usize { │ │58 + var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert }); │ │59 + errdefer box.deinit(allocator); │ │60 + box.getFocus().kind = .{ .custom = form }; │ │61 │ │62 + var button = try wgt.TextBox.init(allocator, label, .{ .border_style = .single, .round_corners = true, .wrap_kind = .none });│ │63 + errdefer button.deinit(allocator); │ │64 + button.getFocus().mode = .all; │ │65 + // the renderer distinguishes plain clickables from buttons that │ │66 + // should POST to a server route by this kind. │ │67 + button.getFocus().kind = .{ .custom = "submit" }; │ │68 + const button_id = button.getFocus().id; │ │69 + try box.children.put(allocator, button_id, .{ │ │70 + .widget = .{ .text_box = button }, │ │71 + .rect = null, │ │72 + .min_size = null, │ │73 + }); │ │74 + box.getFocus().child_id = button_id; │ │75 + │ │76 + try parent.children.put(allocator, box.getFocus().id, .{ │ │77 + .widget = .{ .box = box }, │ │78 + .rect = null, │ │79 + .min_size = null, │ │80 + }); │ │81 + return button_id; │ │82 + } │ │83 + │ │84 pub fn deinit(self: *View, allocator: std.mem.Allocator) void { │ │85 self.center.deinit(allocator); │ │86 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │87 │ │88 pub fn build(self: *View, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void {│ │89 + const ansi_box = &self.center.child.box.children.values()[ansi_index].widget.box; │ │90 + try ansi_box.children.values()[0].widget.text_box.setContent(allocator, ansiLabel(self.session)); │ │91 try self.center.build(allocator, constraint, root_focus); │ │92 } │ │93 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 94 pub fn input(self: *View, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void { │ │ 95 _ = allocator; │ │ 96 + const focused_id = if (self.atTop()) self.ansi_id else self.logout_id; │ │ 97 switch (key) { │ │ 80 - .enter => { │ │ 81 - try self.logout(); │ │ 82 - return; │ │ 83 - }, │ │ 84 - .mouse => |mouse| { │ │ 85 - if (inp.leftClickOn(root_focus, self.button_id, mouse)) { │ │ 86 - try self.logout(); │ │ 87 - return; │ │ 88 - } │ │ 89 - }, │ │ 98 + .arrow_up => root_focus.setFocus(self.ansi_id), │ │ 99 + .arrow_down => root_focus.setFocus(self.logout_id), │ │100 + .enter => try self.activate(focused_id), │ │101 + .mouse => |mouse| if (inp.leftClickOn(root_focus, focused_id, mouse)) try self.activate(focused_id),│ │102 else => {}, │ │103 } │ │104 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────╮ │105 │ │ 94 - fn logout(self: *View) !void { │ │106 + fn activate(self: *View, button_id: usize) !void { │ │107 + if (button_id == self.ansi_id) { │ │108 + try self.session.push(.toggle_ansi); │ │109 + return; │ │110 + } │ │111 self.session.logOut(); │ │112 // leave the auth tab for the page it belongs to, where the web's│ │113 // /logout redirect also lands │ ╰──────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯