╭ changed ────────╮ │src/main_wasm.zig│ ╰─────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │16 │ │17 fn init(json: []const u8, min_height: u32, max_width: u32) !void { │ │18 _ = page_arena.reset(.free_all); │ │19 + // the stored page can borrow a name slice from page_arena (a .user route),│ │20 + // which we just freed — drop it so the next tick's eql never reads it and │ │21 + // always pushes the fresh page's url. │ │22 + last_pushed_page_maybe = null; │ │23 │ │24 // alloc_always so the parsed strings live entirely inside the arena │ │25 // and don't borrow from the caller's json buffer. │ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────╮ │63 // so the first tick after _init is a no-op even though we always send │ │64 // something through. │ │65 const current_page = session.data.current_page; │ │62 - const should_push = if (last_pushed_page_maybe) |lp| lp != current_page else true; │ │66 + const should_push = if (last_pushed_page_maybe) |lp| !lp.eql(current_page) else true;│ │67 if (should_push) { │ │68 last_pushed_page_maybe = current_page; │ │65 - const url = current_page.url(); │ │69 + const url = try current_page.urlAlloc(&page_arena); │ │70 _pushState(url.ptr, @intCast(url.len)); │ │71 } │ │72 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮ │113 40 => .arrow_down, │ │114 else => return, │ │115 }; │ │116 + │ │117 + if (key == .enter) { │ │118 + if (root_ptr.getFocus().grandchild_id) |gid| { │ │119 + // follow a cross-page link │ │120 + if (ui.crossPageLink(root_ptr.getFocus(), gid, session.data.current_page)) |route| {│ │121 + const url = try route.urlAlloc(&page_arena); │ │122 + navigate(url); │ │123 + return; │ │124 + } │ │125 + } │ │126 + } │ │127 + │ │128 try root_ptr.input(allocator, key, root_ptr.getFocus()); │ │129 } │ │130 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │131 fn onMouseClick(focus_id: usize) !void { │ │132 const root_ptr = if (root) |*root_value| root_value else return error.NotStarted; │ │133 + // follow a cross-page link │ │134 + if (ui.crossPageLink(root_ptr.getFocus(), focus_id, session.data.current_page)) |route| {│ │135 + const url = try route.urlAlloc(&page_arena); │ │136 + navigate(url); │ │137 + return; │ │138 + } │ │139 try root_ptr.getFocus().setFocus(focus_id); │ │140 } │ │141 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────╮ │151 _setOverlay(arg.ptr, @intCast(arg.len)); │ │152 } │ │153 │ │154 +fn navigate(arg: []const u8) void { │ │155 + _navigate(arg.ptr, @intCast(arg.len)); │ │156 +} │ │157 + │ │158 extern fn _consoleLog(arg: [*]const u8, len: u32) void;│ │159 extern fn _setHtml(arg: [*]const u8, len: u32) void; │ │160 extern fn _setOverlay(arg: [*]const u8, len: u32) void;│ ╰────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │161 extern fn _pushState(arg: [*]const u8, len: u32) void; │ │162 extern fn _focusInput(focus_id: u32) void; │ │163 +extern fn _navigate(arg: [*]const u8, len: u32) void; │ │164 │ │165 /// js calls this first to get a wasm pointer it can write the page json into.│ │166 export fn _alloc(len: u32) ?[*]u8 { │ ╰───────────────────────────────────────────────────────────────────────────────────╯