╭ changed ────────╮ │src/main_wasm.zig│ ╰─────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │17 setHtml(html); │ │18 } │ │19 │ │20 -// the page is allocated into this arena via parseFromSliceLeaky. it has │ │21 -// to outlive `root`, because `root` holds slices into the parsed strings. │ │20 +// the snapshot is allocated into this arena via parseFromSliceLeaky. it │ │21 +// has to outlive `root`, because `root` holds slices into the parsed strings.│ │22 var page_arena: ?std.heap.ArenaAllocator = null; │ │23 -var page: ?ui.Page = null; │ │23 +var snapshot: ?ui.Snapshot = null; │ │24 var session: ui.Session = .{}; │ │25 │ │26 fn start(json: []const u8, min_height: u32, max_width: u32) !void { │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │29 │ │30 // alloc_always so the parsed strings live entirely inside the arena │ │31 // and don't borrow from the caller's json buffer. │ │32 - page = try std.json.parseFromSliceLeaky(ui.Page, (page_arena orelse unreachable).allocator(), json, .{ │ │32 + snapshot = try std.json.parseFromSliceLeaky(ui.Snapshot, (page_arena orelse unreachable).allocator(), json, .{│ │33 .allocate = .alloc_always, │ │34 }); │ │35 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │36 - // restore session.user_id from the serialized page so the first render │ │37 - // matches what the server rendered (otherwise wasm would render the │ │38 - // page in a logged-out state on top of a logged-in server-rendered HTML). │ │39 - session.user_id = switch (page orelse unreachable) { │ │40 - .home => |h| h.user_id, │ │41 - }; │ │36 + // adopt the server's session view wholesale — one assignment instead of │ │37 + // hand-restoring each field by reaching into the page's data tree. │ │38 + session.data = (snapshot orelse unreachable).session; │ │39 │ │43 - var next_root = try ui.initRoot(allocator, &(page orelse unreachable), &session); │ │40 + var next_root = try ui.initRoot(allocator, &(snapshot orelse unreachable).page, &session);│ │41 errdefer next_root.deinit(allocator); │ │42 │ │43 if (root) |*old_root| old_root.deinit(allocator); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │153 } │ │154 const ti: *wgt.TextInput(ui.Widget) = @fieldParentPtr("focus", child.focus);│ │155 try ti.setContent(allocator, bytes); │ │156 + // mirror the TTY behavior in Login.View.input: editing an input clears │ │157 + // the stale failure flag so the "(invalid)" label doesn't linger after │ │158 + // the user has typed a correction │ │159 + session.data.login_failure = null; │ │160 } │ │161 │ ╰─────────────────────────────────────────────────────────────────────────────────────╯