╭ changed ───╮ │src/main.zig│ ╰────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────────────────────────────────────────────────────────────────────────╮ │3 const rp = xit.repo; │ │4 const cmd = @import("./command.zig"); │ │5 const srv = @import("./serve.zig"); │ │6 +const ui = @import("./ui.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 │ ╰──────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 74 }, │ │ 75 }, │ │ 76 .help => |cmd_kind_maybe| try cmd.printHelp(cmd_kind_maybe, run_opts.out), │ │ 77 + .local => { │ │ 78 + // session-lifetime allocations (the repo path, session state) │ │ 79 + var session_arena = std.heap.ArenaAllocator.init(allocator); │ │ 80 + defer session_arena.deinit(); │ │ 81 + │ │ 82 + // probe for a git repo first, then a xit repo. only the work path │ │ 83 + // and backend kind are kept; pages re-open the repo on each build. │ │ 84 + const local: ui.RepoSource = blk: { │ │ 85 + if (rp.AnyRepo(.git, .{}).open(io, allocator, .{ .path = cwd_path })) |repo| { │ │ 86 + var git_repo = repo; │ │ 87 + defer git_repo.deinit(io, allocator); │ │ 88 + const work_path = switch (git_repo) { │ │ 89 + inline else => |*r| r.core.work_path, │ │ 90 + }; │ │ 91 + break :blk .{ .path = try session_arena.allocator().dupe(u8, work_path), .repo_kind = .git }; │ │ 92 + } else |err| switch (err) { │ │ 93 + error.RepoNotFound => {}, │ │ 94 + else => |e| return e, │ │ 95 + } │ │ 96 + var xit_repo = rp.AnyRepo(.xit, .{}).open(io, allocator, .{ .path = cwd_path }) catch |err| switch (err) { │ │ 97 + error.RepoNotFound => { │ │ 98 + try run_opts.err.print("no git or xit repo found in the current directory\n", .{}); │ │ 99 + return error.HandledError; │ │100 + }, │ │101 + else => |e| return e, │ │102 + }; │ │103 + defer xit_repo.deinit(io, allocator); │ │104 + const work_path = switch (xit_repo) { │ │105 + inline else => |*r| r.core.work_path, │ │106 + }; │ │107 + break :blk .{ .path = try session_arena.allocator().dupe(u8, work_path), .repo_kind = .xit }; │ │108 + }; │ │109 + │ │110 + // every local route internally carries this synthetic │ │111 + // "owner/name"; urls elide it. │ │112 + const identity = try std.fmt.allocPrint(session_arena.allocator(), "local/{s}", .{std.fs.path.basename(local.path)});│ │113 + const route = ui.RoutablePage.repoFilesRoute(identity, null, "", "", 0) orelse { │ │114 + try run_opts.err.print("repo directory name is too long\n", .{}); │ │115 + return error.HandledError; │ │116 + }; │ │117 + │ │118 + var session = ui.Session{ │ │119 + .arena = &session_arena, │ │120 + .page_arena = &session_arena, │ │121 + .io = io, │ │122 + .local = local, │ │123 + .is_terminal = true, │ │124 + .data = .{ .current_page = route }, │ │125 + }; │ │126 + │ │127 + try ui.run(io, allocator, &session, null); │ │128 + }, │ │129 .cli => |cli_cmd| switch (cli_cmd) { │ │130 .serve => |options| try srv.run(repo_kind, any_repo_opts, io, allocator, cwd_path, options, run_opts.err, {}), │ │131 }, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯