Add Browse Files button to snapshots screen

Select a snapshot and click Browse Files to see all files in that
snapshot on the remote. Also adds 'gniza snapshots browse' CLI command.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
shuki
2026-03-06 05:08:45 +02:00
parent d6032d2547
commit e73f38df53
3 changed files with 57 additions and 3 deletions

View File

@@ -305,8 +305,19 @@ run_cli() {
fi
_restore_remote_globals
;;
browse)
[[ -z "$target" ]] && die "browse requires --target=NAME"
local snapshot=""
snapshot=$(_parse_flag "--snapshot" "${SUBCMD_ARGS[@]+"${SUBCMD_ARGS[@]}"}") || true
[[ -z "$snapshot" ]] && die "browse requires --snapshot=TS"
if [[ -z "$remote" ]]; then
remote=$(list_remotes | head -1)
[[ -z "$remote" ]] && die "No remotes configured"
fi
list_snapshot_contents "$target" "$snapshot" "$remote"
;;
*)
die "Unknown snapshots action: $action (expected list)"
die "Unknown snapshots action: $action (expected list|browse)"
;;
esac
;;

View File

@@ -111,6 +111,7 @@ SelectionList {
#targets-buttons,
#remotes-buttons,
#logs-buttons,
#snapshots-buttons,
#sched-buttons,
#sched-edit-buttons,
#te-buttons,
@@ -126,6 +127,7 @@ SelectionList {
#targets-buttons Button,
#remotes-buttons Button,
#logs-buttons Button,
#snapshots-buttons Button,
#sched-buttons Button,
#sched-edit-buttons Button,
#te-buttons Button,

View File

@@ -6,6 +6,7 @@ from textual import work
from tui.config import list_conf_dir
from tui.backend import run_cli
from tui.widgets import OperationLog
class SnapshotsScreen(Screen):
@@ -27,7 +28,9 @@ class SnapshotsScreen(Screen):
yield Select([(r, r) for r in remotes], id="snap-remote", prompt="Select remote")
yield Button("Load Snapshots", id="btn-load", variant="primary")
yield DataTable(id="snap-table")
yield Button("Back", id="btn-back")
with Horizontal(id="snapshots-buttons"):
yield Button("Browse Files", id="btn-browse")
yield Button("Back", id="btn-back")
yield Footer()
def on_mount(self) -> None:
@@ -42,6 +45,19 @@ class SnapshotsScreen(Screen):
self.app.pop_screen()
elif event.button.id == "btn-load":
self._load_snapshots()
elif event.button.id == "btn-browse":
self._browse_snapshot()
def _selected_snapshot(self) -> str | None:
try:
table = self.query_one("#snap-table", DataTable)
if table.cursor_row is not None and table.row_count > 0:
row_key = table.coordinate_to_cell_key((table.cursor_row, 0)).row_key.value
# The snapshot name is in the first (only) column
return str(table.get_cell(row_key, "Snapshot"))
return None
except Exception:
return None
@work
async def _load_snapshots(self) -> None:
@@ -58,11 +74,36 @@ class SnapshotsScreen(Screen):
lines = [l.strip() for l in stdout.splitlines() if l.strip() and not l.startswith("===")]
if lines:
for s in lines:
table.add_row(s)
table.add_row(s, key=s)
else:
self.notify("No snapshots found", severity="warning")
if stderr:
self.notify(stderr.strip(), severity="error")
@work
async def _browse_snapshot(self) -> None:
target_sel = self.query_one("#snap-target", Select)
remote_sel = self.query_one("#snap-remote", Select)
if not isinstance(target_sel.value, str) or not isinstance(remote_sel.value, str):
self.notify("Select target and remote first", severity="error")
return
snapshot = self._selected_snapshot()
if not snapshot:
self.notify("Select a snapshot first", severity="warning")
return
target = str(target_sel.value)
remote = str(remote_sel.value)
log_screen = OperationLog(f"Files: {target}/{snapshot}", show_spinner=False)
self.app.push_screen(log_screen)
rc, stdout, stderr = await run_cli(
"snapshots", "browse", f"--target={target}", f"--remote={remote}", f"--snapshot={snapshot}"
)
if stdout:
log_screen.write(stdout)
if stderr:
log_screen.write(stderr)
if not stdout and not stderr:
log_screen.write("No files found.")
def action_go_back(self) -> None:
self.app.pop_screen()