╭ main_wasm.zig ────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
1 const std = @import("std"); 2 const ui = @import("./ui.zig"); 3 const pg = @import("./page.zig"); 4 const xitui = @import("xit").xitui; 5 const inp = xitui.input; 6 7 const allocator = std.heap.wasm_allocator; 8 9 var root: ?ui.Widget = null; 10 11 fn updateHtml() !void { 12 const root_ptr = if (root) |*root_value| root_value else return error.NotStarted; 13 const html = try ui.generateHtml(allocator, root_ptr); 14 defer allocator.free(html); 15 setHtml(html); 16 } 17 18 // the page is allocated into this arena via parseFromSliceLeaky. it has 19 // to outlive `root`, because `root` holds slices into the parsed strings. 20 var page_arena: ?std.heap.ArenaAllocator = null; 21 var page: ?pg.Page = null; 22 23 fn start(json: []const u8) !void { 24 if (page_arena) |*a| a.deinit(); 25 page_arena = std.heap.ArenaAllocator.init(allocator); 26 27 // alloc_always so the parsed strings live entirely inside the arena 28 // and don't borrow from the caller's json buffer. 29 page = try std.json.parseFromSliceLeaky(pg.Page, (page_arena orelse unreachable).allocator(), json, .{ 30 .allocate = .alloc_always, 31 }); 32 33 var next_root = try ui.initRoot(allocator, &(page orelse unreachable)); 34 errdefer next_root.deinit(); 35 36 if (root) |*old_root| old_root.deinit(); 37 root = next_root; 38 39 try updateHtml(); 40 } 41 42 fn tick() !void { 43 const root_ptr = if (root) |*root_value| root_value else return error.NotStarted; 44 // TODO: make this fit the current size of the browser window 45 try root_ptr.build(.{ 46 .min_size = .{ .width = null, .height = null }, 47 .max_size = .{ .width = 80, .height = 24 }, 48 }, root_ptr.getFocus()); 49 try updateHtml(); 50 } 51 52 fn onKeyDown(key_code: u32) !void { 53 const root_ptr = if (root) |*root_value| root_value else return error.NotStarted; 54 const key: inp.Key = switch (key_code) { 55 33 => .page_up, 56 34 => .page_down, 57 35 => .end, 58 36 => .home, 59 37 => .arrow_left, 60 38 => .arrow_up, 61 39 => .arrow_right, 62 40 => .arrow_down, 63 else => return, 64 }; 65 try root_ptr.input(key, root_ptr.getFocus()); 66 } 67 68 fn consoleLog(arg: []const u8) void { 69 _consoleLog(arg.ptr, @intCast(arg.len)); 70 } 71 72 fn setHtml(arg: []const u8) void { 73 _setHtml(arg.ptr, @intCast(arg.len)); 74 } 75 76 extern fn _consoleLog(arg: [*]const u8, len: u32) void; 77 extern fn _setHtml(arg: [*]const u8, len: u32) void; 78 79 /// js calls this first to get a wasm pointer it can write the page json into. 80 export fn _alloc(len: u32) ?[*]u8 { 81 const slice = allocator.alloc(u8, len) catch return null; 82 return slice.ptr; 83 } 84 85 export fn _start(json_ptr: [*]u8, json_len: u32) void { 86 const json = json_ptr[0..json_len]; 87 defer allocator.free(json); 88 start(json) catch |err| { 89 var buf: [256]u8 = undefined; 90 const str = std.fmt.bufPrint(&buf, "start: {}", .{err}) catch unreachable; 91 consoleLog(str); 92 }; 93 } 94 95 export fn _tick() bool { 96 tick() catch |err| { 97 var buf: [256]u8 = undefined; 98 const str = std.fmt.bufPrint(&buf, "tick: {}", .{err}) catch unreachable; 99 consoleLog(str); 100 return false; 101 }; 102 return true; 103 } 104 105 export fn _onKeyDown(key_code: u32) void { 106 onKeyDown(key_code) catch |err| { 107 var buf: [256]u8 = undefined; 108 const str = std.fmt.bufPrint(&buf, "onKeyDown: {}", .{err}) catch unreachable; 109 consoleLog(str); 110 }; 111 } 112