╭ changed ─────────────╮ │src/ui/Repo/Events.zig│ ╰──────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │434 box.getFocus().child_id = null; │ │435 │ │406 - const item = self.data.events[index]; │ │407 - try self.addViewEvent(allocator, box, item); │ │408 - try addField(allocator, box, @tagName(item.kind), " kind "); │ │409 - try self.addAuthor(allocator, box, item.author); │ │410 - if (item.deleted) try addField(allocator, box, "(deleted)", " data "); │ │436 + const item = self.eventWindow().events[index]; │ │437 + if (item.view_url) |url| { │ │438 + var link = try wgt.TextBox(ui.Widget).init(allocator, "view event", .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none }); │ │439 + errdefer link.deinit(allocator); │ │440 + link.getFocus().focusable = true; │ │441 + link.getFocus().kind = .{ .custom = try std.fmt.allocPrint(self.session.page_arena.allocator(), "a:{s}", .{url}) }; │ │442 + try box.children.put(allocator, link.getFocus().id, .{ .widget = .{ .text_box = link }, .rect = null, .min_size = null }); │ │443 + } │ │444 + { │ │445 + var kind = try wgt.TextBox(ui.Widget).init(allocator, @tagName(item.kind), .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .word, .label = " kind " });│ │446 + errdefer kind.deinit(allocator); │ │447 + kind.getFocus().focusable = true; │ │448 + try box.children.put(allocator, kind.getFocus().id, .{ .widget = .{ .text_box = kind }, .rect = null, .min_size = null }); │ │449 + } │ │450 + { │ │451 + var author = try ui.authorBox(allocator, self.session.page_arena, item.author); │ │452 + errdefer author.deinit(allocator); │ │453 + try box.children.put(allocator, author.getFocus().id, .{ .widget = .{ .text_box = author }, .rect = null, .min_size = null }); │ │454 + } │ │455 │ │456 if (box.children.count() > 0) box.getFocus().child_id = box.children.keys()[0]; │ │457 self.detailScroll().x = 0; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │458 self.detailScroll().y = 0; │ │459 self.detailScroll().getFocus().version +%= 1; │ │416 - self.detailed_index = index; │ │460 + detailed_index.* = index; │ │461 } │ │462 │ │463 pub fn input(self: *View, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void {│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │465 if (key == .arrow_down) root_focus.setFocus(self.content().getFocus().id) else try self.header().input(allocator, key, root_focus);│ │466 return; │ │467 } │ │424 - if (key == .arrow_up and self.contentAtTop() and self.header().focusButton(root_focus)) return; │ │468 + if (key == .arrow_up and self.contentAtTop()) { │ │469 + self.header().focusCurrent(root_focus); │ │470 + return; │ │471 + } │ │472 if (self.detailActive()) { │ │473 if (key == .arrow_left) { │ │474 root_focus.setFocus(self.listScroll().getFocus().id); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │481 .arrow_right => if (self.detailBox().children.count() > 0) root_focus.setFocus(self.detailScroll().getFocus().id), │ │482 .enter => if (self.selectedEventIndex() != null) │ │483 root_focus.setFocus(self.detailScroll().getFocus().id) │ │437 - else if (self.data.next) |next| if (ui.RoutablePage.repoEventsRoute(self.data.identity, next.kind, next.id)) |route| try self.session.navigate(route), │ │484 + else if (self.eventWindow().next) |next| if (ui.RoutablePage.repoEventsRoute(self.data.identity, self.selectedView(), next.kind, next.id)) |route| try self.session.navigate(route),│ │485 else => {}, │ │486 } │ │487 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │494 } │ │495 │ │496 pub fn getSelectedIndex(self: *View) ?usize { │ │450 - if (self.headerActive()) return 0; │ │451 - return if (self.header().isFocusable() or !self.contentAtTop()) 1 else 0;│ │497 + return if (self.headerActive()) 0 else 1; │ │498 } │ │499 │ │500 pub fn clearGrid(self: *View) void { │ ╰──────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────╮ │511 }; │ │512 │ │513 pub const Header = struct { │ │468 - status: []const u8, │ │469 - is_local: bool, │ │514 + sync_status: ?[]const u8, │ │515 │ │516 const nothing = "nothing to sync";│ │517 const unsynced = "sync needed!"; │ ╰───────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │539 failure: ?[]const u8, │ │540 ) !Header { │ │541 if (failure != null) return Header.empty(aa, failure); │ │497 - const remote_name = (try remoteName(repo_kind, repo_opts, repo, io, aa)) orelse return .{ .status = nothing, .is_local = true };│ │542 + const remote_name = (try remoteName(repo_kind, repo_opts, repo, io, aa)) orelse return .{ .sync_status = nothing }; │ │543 const remote_ref: rf.Ref = .{ .kind = .{ .remote = remote_name }, .name = evt.events_ref.name }; │ │499 - const local_oid = (try repo.readRef(io, evt.events_ref)) orelse return .{ .status = nothing, .is_local = true }; │ │500 - const remote_oid = (try repo.readRef(io, remote_ref)) orelse return .{ .status = nothing, .is_local = true }; │ │501 - return .{ .status = if (std.mem.eql(u8, &local_oid, &remote_oid)) nothing else unsynced, .is_local = true }; │ │544 + const local_oid = (try repo.readRef(io, evt.events_ref)) orelse return .{ .sync_status = nothing }; │ │545 + const remote_oid = (try repo.readRef(io, remote_ref)) orelse return .{ .sync_status = nothing }; │ │546 + return .{ .sync_status = if (std.mem.eql(u8, &local_oid, &remote_oid)) nothing else unsynced }; │ │547 } │ │548 │ │549 pub fn empty(aa: std.mem.Allocator, failure: ?[]const u8) !Header { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │505 - return .{ .status = if (failure) |name| try std.fmt.allocPrint(aa, "error: {s}", .{name}) else nothing, .is_local = true }; │ │550 + return .{ .sync_status = if (failure) |name| try std.fmt.allocPrint(aa, "error: {s}", .{name}) else nothing }; │ │551 } │ │552 │ │508 - pub fn remote(aa: std.mem.Allocator, count: usize) !Header { │ │509 - return .{ .status = try std.fmt.allocPrint(aa, "{d} events found", .{count}), .is_local = false }; │ │553 + pub fn remote() Header { │ │554 + return .{ .sync_status = null }; │ │555 } │ │556 │ │512 - fn withCount(self: Header, aa: std.mem.Allocator, count: usize) !Header { │ │513 - return .{ .status = try std.fmt.allocPrint(aa, "{d} events found - {s}", .{ count, self.status }), .is_local = self.is_local };│ │514 - } │ │515 - │ │557 pub const View = struct { │ │558 box: wgt.Box(ui.Widget), │ │559 session: *ui.Session, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │560 + tab_ids: [2]usize, │ │561 button_id: ?usize, │ │562 │ │521 - pub fn init(allocator: std.mem.Allocator, data: *const Header, session: *ui.Session) !Header.View { │ │563 + pub fn init(allocator: std.mem.Allocator, data: *const Self, session: *ui.Session) !Header.View { │ │564 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .horiz }); │ │565 errdefer box.deinit(allocator); │ │566 + var tab_ids: [2]usize = undefined; │ │567 + inline for ([_]ui.RoutablePage.EventsView{ .active, .removed }, 0..) |view, i| { │ │568 + const route = ui.RoutablePage.repoEventsRoute(data.identity, view, null, "") orelse return error.RouteTooLong; │ │569 + const count = data.window(view).count; │ │570 + const label = try std.fmt.allocPrint(session.page_arena.allocator(), "{s} ({d})", .{ @tagName(view), count }); │ │571 + var tab = try wgt.TextBox(ui.Widget).init(allocator, label, .{ .border_style = .hidden, .rounded_corners = true, .wrap_kind = .none }); │ │572 + errdefer tab.deinit(allocator); │ │573 + tab.getFocus().focusable = true; │ │574 + tab.getFocus().kind = .{ .custom = try std.fmt.allocPrint(session.page_arena.allocator(), "ai:{s}", .{try route.toUrl(session.page_arena)}) }; │ │575 + tab_ids[i] = tab.getFocus().id; │ │576 + try box.children.put(allocator, tab.getFocus().id, .{ .widget = .{ .text_box = tab }, .rect = null, .min_size = .{ .width = label.len + 2, .height = null } });│ │577 + } │ │578 + │ │579 var button_id: ?usize = null; │ │525 - if (data.is_local) { │ │580 + if (data.header.sync_status != null) { │ │581 box.getFocus().kind = .{ .custom = "form:/sync" }; │ │582 var button = try wgt.TextBox(ui.Widget).init(allocator, "sync", .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none }); │ │583 errdefer button.deinit(allocator); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │584 button.getFocus().focusable = true; │ │585 button.getFocus().kind = .{ .custom = "submit" }; │ │586 button_id = button.getFocus().id; │ │532 - box.getFocus().child_id = button_id; │ │587 try box.children.put(allocator, button.getFocus().id, .{ .widget = .{ .text_box = button }, .rect = null, .min_size = null });│ │588 } │ │589 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯