╭ changed ──────────────╮ │src/ui/Repo/Commits.zig│ ╰───────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │302 var list_box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert }); │ │303 errdefer list_box.deinit(allocator); │ │304 for (data.commits) |commit| { │ │305 - try addRow(allocator, &list_box, commit.message, ""); │ │305 + // an in-page "ai:" anchor so a commit row is clickable with │ │306 + // js off (the browser follows it, rooting the list there); │ │307 + // with wasm the click just selects it and swaps the diff pane. │ │308 + try addRow(allocator, &list_box, commit.message, try commitRowLink(session.page_arena, data.identity, commit.oid));│ │309 } │ │310 if (data.next_start) |next| { │ │311 try addRow(allocator, &list_box, "next →", try commitsLink(session.page_arena, data.identity, next, 0)); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │769 const route = ui.RoutablePage.repoCommitsRoute(identity, start_oid, after) orelse return error.RouteTooLong;│ │770 const url = try route.urlAlloc(page_arena); │ │771 return std.fmt.allocPrint(page_arena.allocator(), "a:{s}", .{url}); │ │772 +} │ │773 + │ │774 +// the in-page "ai:" anchor for selecting `oid` in `identity`'s commit list. it │ │775 +// points at the same route as commitsLink but the "ai:" prefix keeps wasm clicks │ │776 +// in-page (crossPageLink ignores it); the href is only followed with js off. │ │777 +fn commitRowLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, oid: []const u8) ![]const u8 { │ │778 + const route = ui.RoutablePage.repoCommitsRoute(identity, oid, 0) orelse return error.RouteTooLong; │ │779 + const url = try route.urlAlloc(page_arena); │ │780 + return std.fmt.allocPrint(page_arena.allocator(), "ai:{s}", .{url}); │ │781 } │ │782 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │493 plain, // non-focusable, colored │ │494 │ │495 const a_prefix = "a:"; │ │496 + // an "in-page" anchor: rendered as a real <a href> so it works with js│ │497 + // disabled, but crossPageLink ignores it, so with wasm the click just │ │498 + // moves focus rather than navigating. │ │499 + const inpage_prefix = "ai:"; │ │500 │ │501 fn init(str: []const u8) @This() { │ │502 + if (std.mem.startsWith(u8, str, inpage_prefix)) { │ │503 + return .{ .a = str[inpage_prefix.len..] }; │ │504 + } │ │505 if (std.mem.startsWith(u8, str, a_prefix)) { │ │506 return .{ .a = str[a_prefix.len..] }; │ │507 } │ ╰────────────────────────────────────────────────────────────────────────────────────╯