Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 54 additions & 9 deletions tui/src/views/backups.zig
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ pub const BackupView = struct {
self.total_rows = pve_count + k8s_count;

if (self.total_rows == 0) {
self.selected = 0;
self.scroll = 0;
if (filter.len > 0) {
drawCentered(win, "No backups matching filter");
} else {
Expand All @@ -148,6 +150,24 @@ pub const BackupView = struct {
// Clamp selection
if (self.selected >= self.total_rows) self.selected = self.total_rows - 1;

const footer_rows = self.filterBarRows();
const content_height = win.height -| footer_rows;
if (content_height == 0) {
self.scroll = 0;
self.drawFilterBar(win);
return;
}

const visible_rows = calcVisibleRows(content_height, pve_count, k8s_count);
if (self.selected < self.scroll) {
self.scroll = self.selected;
} else if (self.selected >= self.scroll + visible_rows) {
self.scroll = self.selected - visible_rows + 1;
}

if (self.scroll >= self.total_rows) self.scroll = self.total_rows - 1;
const end_idx = self.scroll +| visible_rows;

var current_row: u16 = 0;

// PVE Backups section
Expand All @@ -171,24 +191,27 @@ pub const BackupView = struct {
var pve_idx: u16 = 0;
for (backups) |b| {
if (!self.matchesFilter(b, filter)) continue;
if (current_row >= win.height -| 1) break;
const is_selected = (pve_idx == self.selected);
const logical_idx = pve_idx;
pve_idx += 1;
if (logical_idx < self.scroll) continue;
if (logical_idx >= end_idx) continue;
if (current_row >= content_height) break;
const is_selected = (logical_idx == self.selected);
drawBackupRow(win, current_row, b, is_selected, self.stale_days);
current_row += 1;
pve_idx += 1;
}
}

// K8s Backups section
if (k8s_count > 0) {
if (pve_count > 0 and current_row < win.height -| 3) {
if (pve_count > 0 and current_row < content_height -| 3) {
// Separator
current_row += 1;
}

var k8s_header_buf: [48]u8 = undefined;
const k8s_header = std.fmt.bufPrint(&k8s_header_buf, " K8s Backups ({d})", .{k8s_count}) catch " K8s Backups";
if (current_row < win.height -| 1) {
if (current_row < content_height -| 1) {
const hdr_style: vaxis.Style = .{ .fg = .{ .index = 5 }, .bg = .{ .index = 8 }, .bold = true };
_ = win.print(&.{.{ .text = k8s_header, .style = hdr_style }}, .{
.row_offset = current_row,
Expand All @@ -197,7 +220,7 @@ pub const BackupView = struct {
current_row += 1;
}

if (current_row < win.height -| 1) {
if (current_row < content_height) {
const col_hdr_style: vaxis.Style = .{ .fg = .{ .index = 7 }, .bold = true };
_ = win.print(&.{.{ .text = k8s_col_header, .style = col_hdr_style }}, .{
.row_offset = current_row,
Expand All @@ -209,14 +232,16 @@ pub const BackupView = struct {
var k8s_idx: u16 = 0;
for (k8s_backups) |b| {
if (!self.matchesK8sFilter(b, filter)) continue;
if (current_row >= win.height -| 1) break;
const logical_idx = pve_count + k8s_idx;
k8s_idx += 1;
if (logical_idx < self.scroll) continue;
if (logical_idx >= end_idx) continue;
if (current_row >= content_height) break;
const is_selected = (logical_idx == self.selected);
drawK8sRow(win, current_row, b, is_selected);
current_row += 1;
k8s_idx += 1;
}
} else if (pve_count > 0 and current_row < win.height -| 2) {
} else if (pve_count > 0 and current_row < content_height -| 1) {
// Show "no K8s providers" hint
current_row += 1;
const hint_style: vaxis.Style = .{ .fg = .{ .index = 8 } };
Expand Down Expand Up @@ -412,6 +437,26 @@ pub const BackupView = struct {
return if (s.len > max) s[0..max] else s;
}

fn calcHeaderRows(pve_count: u16, k8s_count: u16) u16 {
var rows: u16 = 0;
if (pve_count > 0) rows += 2;
if (k8s_count > 0) {
if (pve_count > 0) rows += 1;
rows += 2;
}
return rows;
}

fn filterBarRows(self: *BackupView) u16 {
return if (self.filter_active or self.filter_len > 0) 1 else 0;
}

fn calcVisibleRows(content_height: u16, pve_count: u16, k8s_count: u16) u16 {
if (content_height == 0) return 0;
const header_rows = calcHeaderRows(pve_count, k8s_count);
return @max(@as(u16, 1), content_height -| header_rows);
}

fn filteredBackupIndex(self: *BackupView, backups: []const poll.BackupRow, filtered_idx: u16) ?u16 {
const filter = if (self.filter_len > 0) self.filter_buf[0..self.filter_len] else "";
var matched: u16 = 0;
Expand Down
Loading