╭ changed ────╮ │build.zig.zon│ ╰─────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 3 .version = "0.0.0", │ │ 4 .dependencies = .{ │ │ 5 .xit = .{ │ │ 6 - .url = "git+https://github.com/xit-vcs/xit#ca5f410ecb304e78fd694d1619cdedfc7bcfd6bb",│ │ 7 - .hash = "xit-0.0.0-Ub3nDKXiTwE-1eGohTWT7GK2G7xQYMzcuJ0GjY4ttJzX", │ │ 6 + .url = "git+https://github.com/xit-vcs/xit#1b99cc2983bd6d1cf064bcf29656571f806c86d0",│ │ 7 + .hash = "xit-0.0.0-Ub3nDLvmTwGnsCaUgT3UwHN0PcHJuWnH_POiriug8wjq", │ │ 8 }, │ │ 9 }, │ │10 .paths = .{ │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ───╮ │src/main.zig│ ╰────────────╯ ╭───────────────────────────────────────────────────────────────────────────────╮ │ 4 const rp = xit.repo; │ │ 5 const cmd = @import("./command.zig"); │ │ 6 const srv = @import("./serve.zig"); │ │ 7 + │ │ 8 +// cook the terminal before a panic/segfault trace is printed, so the trace│ │ 9 +// isn't mangled by raw mode and the alternate buffer │ │10 +pub const std_options_debug_io = xit.xitui.terminal.crash_debug_io; │ │11 │ │12 pub const RunOpts = struct { │ │13 out: *std.Io.Writer, │ ╰───────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────────╮ │src/main_wasm.zig│ ╰─────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────╮ │83 // leaves the live <form> alone — crucial since wiping it mid-click │ │84 // would detach the submit button before the browser can dispatch the │ │85 // form submission. │ │86 - const overlay = try web.generateOverlay(allocator, root_ptr); │ │86 + const overlay = try web.generateOverlay(allocator, root_ptr, &session);│ │87 defer allocator.free(overlay); │ │88 setOverlay(overlay); │ │89 │ ╰───────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────╮ │248 .text_input, .text_input_password => {}, │ │249 else => return error.NotATextInput, │ │250 } │ │251 - const ti: *wgt.TextInput(ui.Widget) = @fieldParentPtr("focus", child.focus); │ │251 + const ti = session.text_inputs.get(focus_id) orelse return error.UnknownFocusId;│ │252 try ti.setContent(allocator, bytes); │ │253 // mirror the TTY behavior in Login.View.input: editing an input clears │ │254 // the stale failure flag so the "(invalid)" label doesn't linger after │ ╰─────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──╮ │src/try.zig│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────╮ │10 const xit = hx.xit; │ │11 const rp = xit.repo; │ │12 const ui = hx.ui; │ │13 + │ │14 +// cook the terminal before a panic/segfault trace is printed, so the trace│ │15 +// isn't mangled by raw mode and the alternate buffer │ │16 +pub const std_options_debug_io = xit.xitui.terminal.crash_debug_io; │ │17 │ │18 pub fn main(init: std.process.Init) !void { │ │19 var gpa: std.heap.DebugAllocator(.{}) = .init; │ ╰───────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮ │343 io: ?std.Io = null, │ │344 repos_dir: ?[]const u8 = null, │ │345 pending: std.ArrayList(Action) = .empty, // actions queued by widgets this frame │ │346 + // focus id -> the live TextInput, refreshed each frame by the views that own │ │347 + // inputs. web/wasm form handling looks widgets up here by focus id. │ │348 + text_inputs: std.AutoHashMapUnmanaged(usize, *wgt.TextInput(Widget)) = .empty, │ │349 nav_back: bool = false, // set by input (escape) to request the native TUI pop a page; see Nav│ │350 is_terminal: bool = false, // true on remote SSH and local TUI │ │351 quit_requested: bool = false, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────╮ │450 │ │451 var terminal = try term.Terminal.init(io, allocator); │ │452 defer terminal.deinit(io); │ │453 + │ │454 + // set term as active so it will be properly cooked │ │455 + // when a panic/segfault happens │ │456 + term.setActive(&terminal); │ │457 + defer term.setActive(null); │ │458 │ │459 var last_size = layout.Size{ .width = 0, .height = 0 };│ │460 var last_grid = try Grid.init(allocator, last_size); │ ╰────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │705 var root = Widget{ .background = try AnsiBackground.init(allocator, page_widget, demon_art, session) };│ │706 errdefer root.deinit(allocator); │ │707 │ │708 + // input-owning views build their TextInputs in init — so reset the │ │709 + // focus-id -> *TextInput map here │ │710 + session.text_inputs.clearRetainingCapacity(); │ │711 + │ │712 try root.build(allocator, .{ │ │713 .min_size = .{ .width = null, .height = 40 }, │ │714 .max_size = .{ .width = 80, .height = null }, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │787 }; │ │788 │ │789 pub const FlowBox = struct { │ │778 - focus: Focus, │ │790 + focus: *Focus, │ │791 grid: ?Grid, │ │792 text_boxes: std.ArrayList(wgt.TextBox(Widget)), │ │793 // backs the per-item strings the text boxes borrow: each box's `content`│ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────╮ │806 cell_height: usize = 3, │ │807 }; │ │808 │ │797 - pub fn init(allocator: std.mem.Allocator, options: Options) FlowBox { │ │809 + pub fn init(allocator: std.mem.Allocator, options: Options) !FlowBox {│ │810 return .{ │ │799 - .focus = Focus.init(.container), │ │811 + .focus = try Focus.create(allocator, .container), │ │812 .grid = null, │ │813 .text_boxes = .empty, │ │814 .arena = std.heap.ArenaAllocator.init(allocator), │ ╰───────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯