╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Commits.zig│ ╰───────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │235 const text, const truncated = try readMessage(repo_kind, repo_opts, aa, commit_object, full_message); │ │236 return .{ │ │237 .oid = try aa.dupe(u8, &commit_object.oid), │ │238 + .parent_oid = if (md.firstParent()) |parent| try aa.dupe(u8, parent) else "0" ** xit.hash.hexLen(repo_opts.hash),│ │239 .date = try formatDate(aa, md.timestamp), │ │240 .message = text, │ │241 .message_truncated = truncated, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │285 } │ │286 │ │287 // an empty listing pinned to a ref, for the wasm / no-repo / unresolved paths. │ │328 -pub fn emptyResult(aa: std.mem.Allocator, handle: ui.RepoHandle, ref_or_oid: ui.RoutablePage.RefOrOid, value: []const u8, content: ui.RoutablePage.RepoCommitsRoute.Content, base_oid: []const u8) !Self {│ │288 +pub fn emptyResult(aa: std.mem.Allocator, handle: ui.RepoHandle, ref_or_oid: ui.RoutablePage.RefOrOid, value: []const u8, base_oid: []const u8) !Self { │ │289 return .{ │ │290 .handle = try handle.dupe(aa), │ │291 .ref_or_oid = ref_or_oid, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────╮ │294 .commit_count = null, │ │295 .commits = &.{}, │ │296 .next_start = null, │ │337 - .content = try pageContent(aa, content),│ │297 }; │ │298 } │ │299 │ ╰─────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │341 -// the page's content for a route's, duped into `aa`. the diff window doesn't │ │342 -// carry over: each commit holds the one its pane shows as `window.start`. │ │343 -fn pageContent(aa: std.mem.Allocator, content: ui.RoutablePage.RepoCommitsRoute.Content) !Content {│ │344 - return switch (content) { │ │345 - .diff => |d| .{ .diff = .{ .path = try aa.dupe(u8, d.path.slice()) } }, │ │346 - .message => .message, │ │347 - }; │ │348 -} │ │349 - │ │350 -// render the window [start, start+len) of a commit's diff against its first │ │351 -// parent into `arena`-owned hunks. start/has_more identify adjacent windows. │ │352 -// a non-empty `path` filters to that file, so the window indexes its hunks. │ │353 -fn renderCommitDiff( │ │354 - comptime repo_kind: rp.RepoKind, │ │355 - comptime repo_opts: rp.RepoOpts(repo_kind), │ │356 - io: std.Io, │ │357 - gpa: std.mem.Allocator, │ │358 - arena: std.mem.Allocator, │ │359 - repo: *rp.Repo(repo_kind, repo_opts), │ │360 - oid: [xit.hash.hexLen(repo_opts.hash)]u8, │ │361 - start: usize, │ │362 - path: []const u8, │ │363 -) !Diff.Window { │ │364 - const empty = Diff.Window{ .start = start }; │ │365 - │ │366 - // load the commit so we can diff it against its first parent. │ │367 - var commit_iter = repo.log(io, gpa, .{ .start_oids = &.{oid} }) catch return empty; │ │368 - defer commit_iter.deinit(); │ │369 - const commit_object = (commit_iter.next(gpa) catch return empty) orelse return empty; │ │370 - defer commit_object.deinit(); │ │371 - │ │372 - const parent_maybe = commit_object.content.commit.metadata.firstParent(); │ │373 - │ │374 - return Diff.render(repo_kind, repo_opts, io, gpa, arena, repo, parent_maybe, oid, start, path);│ │375 -} │ │376 - │ │300 // "YYYY-MM-DD" for a unix timestamp. │ │301 fn formatDate(arena: std.mem.Allocator, timestamp: u64) ![]const u8 { │ │302 const epoch_secs = std.time.epoch.EpochSeconds{ .secs = timestamp }; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │371 │ │372 pub const View = struct { │ │373 // a vertical stack: the clone url row on top, then a horizontal │ │451 - // split with the commit list on the left and a diff pane on the right │ │452 - // showing the selected commit's diff. │ │374 + // split with the commit list on the left and a pane on the right │ │375 + // showing the selected commit's details. │ │376 box: wgt.Box(ui.Widget), // vert: [header_index] = clone url row, [content_index] = split│ │377 data: *const Self, │ │378 session: *ui.Session, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │456 - // the commit whose diff the pane currently shows (index into data.commits).│ │457 - diffed_index: ?usize, │ │379 + // the commit the pane currently shows (index into data.commits). │ │380 + detailed_index: ?usize, │ │381 │ │382 // the sub-header leads the box where there is one; local mode has none │ │383 const header_index: usize = 0; │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │384 // indices within the content box (the horizontal split). │ │385 const list_index: usize = 0; │ │463 - const diff_index: usize = 1; │ │386 + const detail_index: usize = 1; │ │387 const list_max_width: usize = 35; │ │465 - const diff_min_width: usize = 40; │ │388 + const detail_min_width: usize = 40; │ │389 │ │390 pub fn init(allocator: std.mem.Allocator, data: *const Self, session: *ui.Session) !View { │ │391 var outer = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert });│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │396 errdefer center.deinit(allocator); │ │397 outer.getFocus().child_id = center.getFocus().id; │ │398 try outer.children.put(allocator, center.getFocus().id, .{ .widget = .{ .center = center }, .rect = null, .min_size = null });│ │476 - return .{ .box = outer, .data = data, .session = session, .diffed_index = null }; │ │399 + return .{ .box = outer, .data = data, .session = session, .detailed_index = null }; │ │400 } │ │401 │ │402 // the search box and the clone url at the top. local mode has neither. │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │418 for (data.commits, 0..) |commit, index| { │ │419 // an in-page "ai:" anchor so a commit row is clickable with │ │420 // js off (the browser follows it, rooting the list there); │ │498 - // with wasm the click just selects it and swaps the diff pane. │ │499 - const content = if (index == 0) data.content else Content{ .diff = .{} }; │ │500 - try addRow(allocator, &list_box, firstLine(commit.message), try commitRowLink(session.page_arena, data, commit, content), if (commit.merge) "(merge)" else ""); │ │421 + // with wasm the click just selects it and swaps the pane. │ │422 + try addRow(allocator, &list_box, firstLine(commit.message), try commitRowLink(session.page_arena, data, commit, index == 0 and data.message), if (commit.merge) "(merge)" else "");│ │423 } │ │424 if (data.next_start) |next| { │ │425 try addRow(allocator, &list_box, "next →", try nextPageLink(session.page_arena, data, next), ""); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │431 try box.children.put(allocator, list_scroll.getFocus().id, .{ .widget = .{ .scroll = list_scroll }, .rect = null, .min_size = .{ .width = list_max_width, .height = null }, .max_size = .{ .width = list_max_width, .height = null } });│ │432 } │ │433 │ │512 - // the diff pane — a frame around a scroll of the hunks │ │434 + // the detail pane: a frame around a scroll of its rows │ │435 { │ │514 - var diff_outer = blk: { │ │515 - var diff_scroll = try Diff.View.initEmpty(allocator, session); │ │516 - errdefer diff_scroll.deinit(allocator); │ │436 + var detail_outer = blk: { │ │437 + var detail_scroll = try Diff.View.initEmpty(allocator, session); │ │438 + errdefer detail_scroll.deinit(allocator); │ │439 var frame = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = .hidden, .direction = .vert }); │ │440 errdefer frame.deinit(allocator); │ │441 // the frame's selected child is its scroll, so the focus chain │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯