╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──╮ │src/try.zig│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │139 for (&repo_event_ids) |*id| id.* = evt.EventWithId.randomId(prng.random()); │ │140 │ │141 var password_hash_buf: [evt.User.password_hash_max_len]u8 = undefined; │ │141 - const password_hash = try evt.User.hashPassword("correct horse battery staple", &password_hash_buf, io);│ │142 + const password_hash = try evt.User.hashPassword("password", &password_hash_buf, io); │ │143 │ │144 var events_to_consume: [user_data.len + repo_data.len]evt.EventWithId = undefined; │ │145 for (user_data, 0..) |u, i| { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮ │187 // consume events into the database │ │188 try evt.consume(repo_opts, io, allocator, &repo, .{ .kind = .head, .name = "haxy/meta" });│ │189 │ │189 - break :blk .{ .home = try .init(repo_opts, &page_arena, &repo) }; │ │190 + break :blk .{ .home = try .init(repo_opts, &page_arena, &repo, &session) }; │ │191 }; │ │192 │ │193 // start the server │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────╮ │252 io: std.Io, │ │253 allocator: std.mem.Allocator, │ │254 page: *const ui.Page, │ │255 + session: *ui.Session, │ │256 │ │257 pub fn run(self: @This()) !void {│ │258 // launch the TUI │ ╰──────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │257 - try hx.ui.run(self.io, self.allocator, self.page); │ │259 + try hx.ui.run(self.io, self.allocator, self.page, self.session);│ │260 } │ │261 }; │ │262 │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │263 try srv.run(.xit, .{}, io, allocator, cwd_path, .{ │ │264 .data_dir = server_path, │ │263 - }, run_opts.err, Runnable{ .io = io, .allocator = allocator, .page = &page }); │ │265 + }, run_opts.err, Runnable{ .io = io, .allocator = allocator, .page = &page, .session = &session });│ │266 } │ │267 } │ │268 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭───────────────────────────────╮ │1 const std = @import("std"); │ │2 const xit = @import("xit"); │ │3 +const rp = xit.repo; │ │4 const xitui = xit.xitui; │ │5 const term = xitui.terminal;│ │6 const wgt = xitui.widget; │ ╰───────────────────────────────╯ ╭───────────────────────────────────────────────────────────╮ │ 9 const Grid = xitui.grid.Grid; │ │10 const Focus = xitui.focus.Focus; │ │11 const evt = @import("./event.zig"); │ │12 + │ │13 +pub const canonical_repo_opts: rp.RepoOpts(.xit) = .{};│ │14 +pub const DB = rp.Repo(.xit, canonical_repo_opts).DB; │ │15 │ │16 pub const Home = @import("./ui/Home.zig"); │ │17 │ ╰───────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │18 pub const Page = union(enum) { │ │19 home: Home, │ │20 +}; │ │21 + │ │22 +// per-connection mutable state. each SSH session / web session / local TUI │ │23 +// run gets its own │ │24 +pub const Session = struct { │ │25 + user_id: ?[]const u8 = null, │ │26 + arena: ?*std.heap.ArenaAllocator = null, │ │27 + haxy_moment: ?DB.HashMap(.read_only) = null, │ │28 }; │ │29 │ │18 -pub fn run(io: std.Io, allocator: std.mem.Allocator, page: *const Page) !void { │ │19 - var root = try initRoot(allocator, page); │ │30 +pub fn run(io: std.Io, allocator: std.mem.Allocator, page: *const Page, session: *Session) !void {│ │31 + var root = try initRoot(allocator, page, session); │ │32 defer root.deinit(); │ │33 │ │34 var terminal = try term.Terminal.init(io, allocator); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │64 } │ │65 } │ │66 │ │55 -// route a single decoded key into the widget tree, handling 'q'-to-quit and │ │56 -// mouse-click focus traversal along the way. shared between the tty event │ │57 -// loop (ui.run) and the SSH session event loop in serve_ssh.zig. │ │58 -// `terminal` is whatever terminal type the caller is driving — it must │ │59 -// expose requestQuit(). │ │67 pub fn inputKey(root: *Widget, key: inp.Key, terminal: anytype) !void { │ │68 switch (key) { │ │69 .codepoint => |cp| if (cp == 'q') terminal.requestQuit() else try root.input(key, root.getFocus()),│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │82 break; │ │83 } │ │84 } │ │85 + // forward the press into the widget tree so buttons (and any│ │86 + // future click-aware widgets) can react. widgets that don't │ │87 + // care about presses ignore it. │ │88 + try root.input(key, root.getFocus()); │ │89 } else { │ │90 try root.input(key, root.getFocus()); │ │91 } │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯