╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────╮ │1046 }, │ │1047 .home_new_repo => "/new-repo", │ │1048 .home_auth => "/auth", │ │1049 + .not_found => "/not-found", │ │1050 .user_repos => |u| blk: { │ │1051 var out: std.Io.Writer.Allocating = .init(arena.allocator()); │ │1052 try out.writer.print(user_segment ++ "{s}/repos", .{u.name.slice()});│ ╰───────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────╮ │1430 const f = self.forkRoute() orelse break :blk self; │ │1431 break :blk forkPatchRoute(f.name.slice(), f.id.slice()) orelse self;│ │1432 }, │ │1433 + .not_found => self, │ │1434 }; │ │1435 } │ │1436 │ ╰──────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1440 .user_repos, .user_forks, .user_new_repo, .user_auth => .user, │ │1441 .repo_files, .repo_commits, .repo_diff, .repo_refs, .repo_issues, .repo_patches, .repo_discussions, .repo_events, .repo_undo, .repo_new_repo, .repo_auth => .repo,│ │1442 .fork_patch, .fork_diff, .fork_files, .fork_commits, .fork_new_repo, .fork_auth => .fork, │ │1443 + .not_found => .not_found, │ │1444 }; │ │1445 } │ │1446 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────╮ │2736 allocator.destroy(arena); │ │2737 } │ │2738 │ │2726 - session.page_arena = arena; │ │2727 - const route = session.data.current_page; │ │2728 - │ │2729 - const page = try arena.allocator().create(Page); │ │2730 - page.* = try Page.init(arena, session, route); │ │2739 return .{ │ │2732 - .root = try initRoot(allocator, page, session), │ │2740 + .root = try initCurrentRoot(allocator, arena, session),│ │2741 .arena = arena, │ │2742 .user_id = session.userId(), │ │2743 .history = .empty, │ ╰─────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2752 freeArena(allocator, entry.arena); │ │2753 } │ │2754 self.history.deinit(allocator); │ │2755 + } │ │2756 + │ │2757 + // rebuild the current page in place from a current moment │ │2758 + fn rebuild(self: *Nav, allocator: std.mem.Allocator, session: *Session) !void { │ │2759 + if (session.haxy_moment == null and session.local == null) return; │ │2760 + const arena = try allocator.create(std.heap.ArenaAllocator); │ │2761 + arena.* = std.heap.ArenaAllocator.init(allocator); │ │2762 + errdefer freeArena(allocator, arena); │ │2763 + │ │2764 + const new_root = try initCurrentRoot(allocator, arena, session); │ │2765 + │ │2766 + self.root.deinit(allocator); │ │2767 + freeArena(allocator, self.arena); │ │2768 + self.root = new_root; │ │2769 + self.arena = arena; │ │2770 + self.user_id = session.userId(); │ │2771 } │ │2772 │ │2773 + // build the current page's root in `arena`, or the not-found page's when │ │2774 + // the route names nothing │ │2775 + fn initCurrentRoot(allocator: std.mem.Allocator, arena: *std.heap.ArenaAllocator, session: *Session) !Widget {│ │2776 + session.page_arena = arena; │ │2777 + const page = try arena.allocator().create(Page); │ │2778 + page.* = Page.init(arena, session, session.data.current_page) catch |err| switch (err) { │ │2779 + error.NotFound => blk: { │ │2780 + session.data.current_page = .not_found; │ │2781 + break :blk .not_found; │ │2782 + }, │ │2783 + else => |e| return e, │ │2784 + }; │ │2785 + return initRoot(allocator, page, session); │ │2786 + } │ │2787 + │ │2788 fn freeArena(allocator: std.mem.Allocator, arena: *std.heap.ArenaAllocator) void { │ │2789 arena.deinit(); │ │2790 allocator.destroy(arena); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │2799 // refresh: rebuild the current page in place from a current moment │ │2800 if (session.refresh_requested) { │ │2801 session.refresh_requested = false; │ │2763 - if (session.haxy_moment != null or session.local != null) { │ │2764 - const arena = try allocator.create(std.heap.ArenaAllocator);│ │2765 - arena.* = std.heap.ArenaAllocator.init(allocator); │ │2766 - errdefer freeArena(allocator, arena); │ │2767 - │ │2768 - session.page_arena = arena; │ │2769 - │ │2770 - const route = session.data.current_page; │ │2771 - const page = try arena.allocator().create(Page); │ │2772 - page.* = try Page.init(arena, session, route); │ │2773 - const new_root = try initRoot(allocator, page, session); │ │2774 - │ │2775 - self.root.deinit(allocator); │ │2776 - freeArena(allocator, self.arena); │ │2777 - self.root = new_root; │ │2778 - self.arena = arena; │ │2779 - self.user_id = session.userId(); │ │2780 - } │ │2802 + try self.rebuild(allocator, session); │ │2803 return; │ │2804 } │ │2805 │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────╮ │2815 session.data.current_page = entry.route; │ │2816 // a page built under another login is rebuilt rather than restored│ │2817 if (!std.meta.eql(entry.user_id, session.userId())) { │ │2796 - session.refresh_requested = true; │ │2797 - return self.sync(allocator, session); │ │2818 + try self.rebuild(allocator, session); │ │2819 + return; │ │2820 } │ │2821 chooseAnsiArtForNavigation(session); │ │2822 return; │ ╰─────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2861 arena.* = std.heap.ArenaAllocator.init(allocator); │ │2862 errdefer freeArena(allocator, arena); │ │2863 │ │2842 - session.page_arena = arena; │ │2843 - │ │2844 - const page = try arena.allocator().create(Page); │ │2845 - page.* = try Page.init(arena, session, route); │ │2846 - const new_root = try initRoot(allocator, page, session); │ │2864 + const new_root = try initCurrentRoot(allocator, arena, session); │ │2865 │ │2866 try self.history.append(allocator, .{ .root = self.root, .route = previous_route, .arena = self.arena, .user_id = self.user_id });│ │2867 // drop the oldest entry (freeing its widget tree and arena) once over cap │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────╮ │2883 .user => |*p| .{ .user = try .init(allocator, p, session) }, │ │2884 .repo => |*p| .{ .repo = try .init(allocator, p, session) }, │ │2885 .fork => |*p| .{ .fork = try .init(allocator, p, session) }, │ │2886 + .not_found => .{ .not_found = try .init(allocator, session) },│ │2887 }; │ │2888 │ │2889 chooseAnsiArtForNavigation(session); │ ╰────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │2925 // native and server-side sessions choose from the embedded collection. WASM│ │2926 // keeps the art serialized by the server instead. │ │2927 fn chooseAnsiArtForNavigation(session: *Session) void { │ │2909 - if (comptime ansi_arts.len == 0) return; │ │2928 + if (comptime ansi_art.art.len == 0) return; │ │2929 + if (session.data.current_page == .not_found) { │ │2930 + session.data.ansi_art = ansi_art.not_found; │ │2931 + return; │ │2932 + } │ │2933 const io = session.io orelse return; │ │2934 var random: usize = undefined; │ │2935 io.random(std.mem.asBytes(&random)); │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯