╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ─────────────────╮ │src/ui/Repo/Attachment.zig│ ╰──────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │100 return if (identity.len == 0) │ │101 try std.fmt.allocPrint(page_arena.allocator(), "/attachment:{s}", .{id}) │ │102 else │ │103 - try std.fmt.allocPrint(page_arena.allocator(), "/repo/{s}/attachment:{s}", .{ identity, id });│ │103 + try std.fmt.allocPrint(page_arena.allocator(), "/{s}/attachment:{s}", .{ identity, id }); │ │104 } │ │105 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────────╮ │src/ui/Repo/Header.zig│ ╰──────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │92 if (session.data.host_kind == .server) { │ │93 var text_buf: [evt.User.name_max_len + 1]u8 = undefined; │ │94 const text = try std.fmt.bufPrint(&text_buf, "{s}/", .{data.owner_name}); │ │95 - const link = try std.fmt.allocPrint(aa, "a:/user/{s}", .{data.owner_name}); │ │95 + const link = try std.fmt.allocPrint(aa, "a:/{s}", .{data.owner_name}); │ │96 │ │97 var text_box = try wgt.TextBox.init(allocator, text, .{ .border_style = .hidden, .wrap_kind = .none });│ │98 errdefer text_box.deinit(allocator); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │107 } │ │108 │ │109 // local routes carry no identity, so their urls come out elided │ │110 - const identity = if (session.data.host_kind == .local) "" else try std.fmt.allocPrint(aa, "{s}/{s}", .{ data.owner_name, data.name });│ │110 + const identity = if (session.data.host_kind == .local) "" else try std.fmt.allocPrint(aa, "{s}:{s}", .{ data.owner_name, data.name });│ │111 │ │112 // title links to the repo's files root (the bare route, so it resolves │ │113 // to the default branch). │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │142 // in-page anchor: crossPageLink ignores it so a wasm click just switches│ │143 // tabs (the page already holds every tab's content), while the href is │ │144 // still followed with js off. the files tab routes through the shared │ │145 - // helper so the /repo/.../files url format lives in one place. │ │145 + // helper so the /.../files url format lives in one place. │ │146 // files and changes keep the same ref or revision when switching tabs. │ │147 // the files tab opens at its root directory. │ │148 const current_page = session.data.current_page; │ ╰──────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────────╮ │src/ui/Repo/Issues.zig│ ╰──────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────╮ │73 pub const empty: Window = .{ .items = &.{}, .prev_id = null, .next_id = null, .count = 0 };│ │74 }; │ │75 │ │76 -// "owner/name", so the view can build /repo/owner/name/issues/... links. │ │76 +// "owner:name", so the view can build /owner:name/issues/... links. │ │77 identity: []const u8, │ │78 // the url-encoded label the lists are filtered to ("" = unfiltered). │ │79 label: []const u8, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Patches.zig│ ╰───────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────╮ │203 pub const empty: Window = .{ .items = &.{}, .prev_id = null, .next_id = null, .count = 0 };│ │204 }; │ │205 │ │206 -// "owner/name", so the view can build /repo/owner/name/patches/... links. │ │206 +// "owner:name", so the view can build /owner:name/patches/... links. │ │207 identity: []const u8, │ │208 default_target_branch: []const u8 = "", │ │209 // the url-encoded label the lists are filtered to ("" = unfiltered). │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │263 // a fork route's identity names the forker and the target repo │ │264 fn forkIdentity(buffer: []u8, identity: []const u8, entry: PatchWithId) ![]const u8 { │ │265 const repo_identity = ui.RoutablePage.RepoIdentity.parse(identity) orelse return error.RouteTooLong; │ │266 - return std.fmt.bufPrint(buffer, "{s}/{s}", .{ entry.forker, repo_identity.name }) catch error.RouteTooLong;│ │266 + return std.fmt.bufPrint(buffer, "{s}:{s}", .{ entry.forker, repo_identity.name }) catch error.RouteTooLong;│ │267 } │ │268 │ │269 pub fn commitsRoute(identity: []const u8, entry: PatchWithId) !?ui.RoutablePage { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │945 const target_branch = entry.record.event.target_branch; │ │946 │ │947 if (status_kind != .merged and entry.fork_exists) if (session.data.git_ssh_port) |port| { │ │948 - const url = try std.fmt.allocPrint(aa, "ssh://localhost:{d}/fork/{s}/patch:{s}", .{ port, self.identity, entry.id }); │ │948 + var buffer: [ui.RoutablePage.repo_identity_max_len]u8 = undefined; │ │949 + const url = try std.fmt.allocPrint(aa, "ssh://localhost:{d}/{s}+{s}", .{ port, try forkIdentity(&buffer, self.identity, entry), entry.id });│ │950 const push_command = try std.fmt.allocPrint(aa, "git push {s} HEAD:patch", .{url}); │ │950 - const clone_name = try cloneDirectoryName(aa, entry.record.event.title); │ │951 - const clone_command = if (clone_name.len == 0) │ │952 - try std.fmt.allocPrint(aa, "git clone {s}", .{url}) │ │953 - else │ │954 - try std.fmt.allocPrint(aa, "git clone {s} {s}", .{ url, clone_name }); │ │951 + const clone_command = try std.fmt.allocPrint(aa, "git clone {s}", .{url}); │ │952 const merge_command = try std.fmt.allocPrint(aa, "git fetch '{s}' refs/heads/patch && git merge FETCH_HEAD", .{url}); │ │953 const choices: [3]ui.widget.CopyableText.Choice = .{ │ │954 .{ │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 996 errdefer copyable_text.deinit(allocator); │ │ 997 try box.children.put(allocator, copyable_text.getFocus().id, .{ .widget = .{ .copyable_text = copyable_text }, .rect = null, .min_size = null });│ │ 998 } │ │1002 - │ │1003 -fn cloneDirectoryName(allocator: std.mem.Allocator, title: []const u8) ![]const u8 { │ │1004 - var name: [64]u8 = undefined; │ │1005 - var len: usize = 0; │ │1006 - var pending: ?u8 = null; │ │ 999 │ │1008 - for (title) |char| { │ │1009 - if (std.ascii.isAlphanumeric(char)) { │ │1010 - const needed: usize = if (pending == null) 1 else 2; │ │1011 - if (len + needed > name.len) break; │ │1012 - if (pending) |value| { │ │1013 - name[len] = value; │ │1014 - len += 1; │ │1015 - } │ │1016 - name[len] = std.ascii.toLower(char); │ │1017 - len += 1; │ │1018 - pending = null; │ │1019 - } else if (char == ' ') { │ │1020 - if (len != 0) pending = '-'; │ │1021 - } else if (char == '.') { │ │1022 - if (len != 0 and pending != '-') pending = '.'; │ │1023 - } │ │1024 - } │ │1025 - if (len == 0) return ""; │ │1026 - return allocator.dupe(u8, name[0..len]); │ │1027 -} │ │1028 - │ │1000 // tabs switching between the patches page's views │ │1001 pub fn initHeader(allocator: std.mem.Allocator, session: *ui.Session, data: *const Self) !Header { │ │1002 var header = try Header.init(allocator, session, data.search); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ───────────╮ │src/ui/Repo/Refs.zig│ ╰────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │14 // how many refs one window of a column shows. │ │15 pub const page_size = 50; │ │16 │ │17 -// "owner/name", needed to build the columns' window-navigation links. │ │17 +// "owner:name", needed to build the columns' window-navigation links. │ │18 identity: []const u8, │ │19 // only one column windows at a time: `from` (a url-encoded ref name, "" = the │ │20 // first window) roots `kind`'s column; the other always shows its first window.│ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯