╭ changed ╮ │build.zig│ ╰─────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │185 std.debug.panic("unable to read src/embed/ansi/{s}: {t}", .{ name, err }); │ │186 }; │ │187 } │ │188 + │ │189 + const not_found = b.build_root.handle.readFileAlloc(io, "src/embed/not-found.txt", b.allocator, .limited(16 * 1024 * 1024)) catch unreachable;│ │190 │ │191 const options = b.addOptions(); │ │192 options.addOption([]const []const u8, "art", contents); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────╮ │193 + options.addOption([]const u8, "not_found", not_found);│ │194 return options.createModule(); │ │195 } │ │196 │ ╰───────────────────────────────────────────────────────────────╯ ╭ added ────────────────╮ │src/embed/not-found.txt│ ╰───────────────────────╯ ╭ changed ────────╮ │src/main_wasm.zig│ ╰─────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │74 // push input-driven in-page route changes into the browser url without │ │75 // adding history entries. real cross-page navigations go through _navigate. │ │76 const current_page = session.data.current_page; │ │77 - const should_push = if (last_pushed_page_maybe) |lp| !lp.eql(current_page) else true; │ │77 + // the not-found page keeps the url that named the missing page │ │78 + const should_push = current_page != .not_found and if (last_pushed_page_maybe) |lp| !lp.eql(current_page) else true;│ │79 if (should_push) { │ │80 last_pushed_page_maybe = current_page; │ │81 const url = try current_page.toUrl(&page_arena); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │11 const Key = xitui.input.Key; │ │12 const Grid = xitui.grid.Grid; │ │13 const Focus = xitui.focus.Focus; │ │14 -const ansi_arts: []const []const u8 = if (builtin.cpu.arch == .wasm32) &.{} else @import("ansi_art").art;│ │14 +// the random page art and the not-found page's fixed art. wasm receives its art │ │15 +// from the server, so it links none. │ │16 +const ansi_art = if (builtin.cpu.arch == .wasm32) struct { │ │17 + const art: []const []const u8 = &.{}; │ │18 + const not_found: []const u8 = ""; │ │19 +} else @import("ansi_art"); │ │20 const evt = @import("./event.zig"); │ │21 const inp = @import("./ui/input.zig"); │ │22 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────╮ │28 pub const SubTitle = @import("./ui/SubTitle.zig"); │ │29 pub const Quit = @import("./ui/Quit.zig"); │ │30 pub const Unauthorized = @import("./ui/Unauthorized.zig");│ │31 +pub const NotFound = @import("./ui/NotFound.zig"); │ │32 pub const NewRepo = @import("./ui/NewRepo.zig"); │ │33 pub const widget = @import("./ui/widget.zig"); │ │34 pub const Widget = widget.Widget; │ ╰──────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────╮ │65 user, │ │66 repo, │ │67 fork, │ │68 + not_found, │ │69 }; │ │70 │ │71 pub const Page = union(PageKind) {│ ╰──────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────╮ │73 user: User, │ │74 repo: Repo, │ │75 fork: Fork, │ │76 + not_found, │ │77 │ │78 pub fn init(arena: *std.heap.ArenaAllocator, session: *Session, route: RoutablePage) !Page {│ │79 // the repo page can build without a moment in local mode; the home and │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │100 .fork_patch, .fork_diff, .fork_files, .fork_commits, .fork_new_repo, .fork_auth => .{ .fork = try Fork.init(arena, session, route) },│ │101 else => return error.UnexpectedRoute, │ │102 }, │ │103 + .not_found => .not_found, │ │104 }; │ │105 } │ │106 }; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────╮ │191 fork_commits: ForkCommitsRoute, │ │192 fork_new_repo: ForkRoute, │ │193 fork_auth: ForkRoute, │ │194 + not_found, │ │195 │ │196 pub const default: RoutablePage = .home_about;│ │197 │ ╰───────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯