Revert OperationLog and all screens to pre-spinner working state
Reverts all spinner/loading indicator changes and debug code.
Restores the exact code from commit 0e02ba6 which was confirmed working.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -193,21 +193,10 @@ SelectionList {
|
|||||||
border: thick $accent;
|
border: thick $accent;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ol-header {
|
|
||||||
height: auto;
|
|
||||||
margin: 0 0 1 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ol-title {
|
#ol-title {
|
||||||
text-style: bold;
|
text-style: bold;
|
||||||
color: #00cc00;
|
color: #00cc00;
|
||||||
width: 1fr;
|
margin: 0 0 1 0;
|
||||||
}
|
|
||||||
|
|
||||||
#ol-spinner {
|
|
||||||
width: auto;
|
|
||||||
min-width: 4;
|
|
||||||
height: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ol-log {
|
#ol-log {
|
||||||
|
|||||||
@@ -42,8 +42,6 @@ class BackupScreen(Screen):
|
|||||||
yield Footer()
|
yield Footer()
|
||||||
|
|
||||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
with open("/tmp/gniza_debug.log", "a") as f:
|
|
||||||
f.write(f"on_button_pressed: {event.button.id}\n")
|
|
||||||
if event.button.id == "btn-back":
|
if event.button.id == "btn-back":
|
||||||
self.app.pop_screen()
|
self.app.pop_screen()
|
||||||
elif event.button.id == "btn-backup":
|
elif event.button.id == "btn-backup":
|
||||||
@@ -54,38 +52,23 @@ class BackupScreen(Screen):
|
|||||||
target = str(target_sel.value)
|
target = str(target_sel.value)
|
||||||
remote_sel = self.query_one("#backup-remote", Select)
|
remote_sel = self.query_one("#backup-remote", Select)
|
||||||
remote = str(remote_sel.value) if isinstance(remote_sel.value, str) else ""
|
remote = str(remote_sel.value) if isinstance(remote_sel.value, str) else ""
|
||||||
# Skip ConfirmDialog — go straight to OperationLog for debugging
|
msg = f"Run backup for target '{target}'?"
|
||||||
self._confirmed_backup(target, remote)
|
if remote:
|
||||||
|
msg += f"\nRemote: {remote}"
|
||||||
|
self.app.push_screen(
|
||||||
|
ConfirmDialog(msg, "Confirm Backup"),
|
||||||
|
callback=lambda ok: self._do_backup(target, remote) if ok else None,
|
||||||
|
)
|
||||||
elif event.button.id == "btn-backup-all":
|
elif event.button.id == "btn-backup-all":
|
||||||
self.app.push_screen(
|
self.app.push_screen(
|
||||||
ConfirmDialog("Backup ALL targets now?", "Confirm Backup"),
|
ConfirmDialog("Backup ALL targets now?", "Confirm Backup"),
|
||||||
callback=lambda ok: self._confirmed_backup_all() if ok else None,
|
callback=lambda ok: self._do_backup_all() if ok else None,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _confirmed_backup(self, target: str, remote: str) -> None:
|
|
||||||
import traceback
|
|
||||||
dbg = open("/tmp/gniza_debug.log", "a")
|
|
||||||
dbg.write("=== _confirmed_backup called ===\n")
|
|
||||||
dbg.write(f"target={target} remote={remote}\n")
|
|
||||||
try:
|
|
||||||
log_screen = OperationLog(f"Backup: {target}")
|
|
||||||
dbg.write("OperationLog created\n")
|
|
||||||
self.app.push_screen(log_screen)
|
|
||||||
dbg.write("push_screen called\n")
|
|
||||||
self._run_backup(log_screen, target, remote)
|
|
||||||
dbg.write("_run_backup started\n")
|
|
||||||
except Exception as e:
|
|
||||||
dbg.write(f"ERROR: {e}\n")
|
|
||||||
dbg.write(traceback.format_exc())
|
|
||||||
dbg.close()
|
|
||||||
|
|
||||||
def _confirmed_backup_all(self) -> None:
|
|
||||||
log_screen = OperationLog("Backup All Targets")
|
|
||||||
self.app.push_screen(log_screen)
|
|
||||||
self._run_backup_all(log_screen)
|
|
||||||
|
|
||||||
@work
|
@work
|
||||||
async def _run_backup(self, log_screen: OperationLog, target: str, remote: str) -> None:
|
async def _do_backup(self, target: str, remote: str) -> None:
|
||||||
|
log_screen = OperationLog(f"Backup: {target}")
|
||||||
|
self.app.push_screen(log_screen)
|
||||||
args = ["backup", f"--target={target}"]
|
args = ["backup", f"--target={target}"]
|
||||||
if remote:
|
if remote:
|
||||||
args.append(f"--remote={remote}")
|
args.append(f"--remote={remote}")
|
||||||
@@ -94,16 +77,16 @@ class BackupScreen(Screen):
|
|||||||
log_screen.write("\n[green]Backup completed successfully.[/green]")
|
log_screen.write("\n[green]Backup completed successfully.[/green]")
|
||||||
else:
|
else:
|
||||||
log_screen.write(f"\n[red]Backup failed (exit code {rc}).[/red]")
|
log_screen.write(f"\n[red]Backup failed (exit code {rc}).[/red]")
|
||||||
log_screen.finish()
|
|
||||||
|
|
||||||
@work
|
@work
|
||||||
async def _run_backup_all(self, log_screen: OperationLog) -> None:
|
async def _do_backup_all(self) -> None:
|
||||||
|
log_screen = OperationLog("Backup All Targets")
|
||||||
|
self.app.push_screen(log_screen)
|
||||||
rc = await stream_cli(log_screen.write, "backup", "--all")
|
rc = await stream_cli(log_screen.write, "backup", "--all")
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
log_screen.write("\n[green]All backups completed.[/green]")
|
log_screen.write("\n[green]All backups completed.[/green]")
|
||||||
else:
|
else:
|
||||||
log_screen.write(f"\n[red]Backup failed (exit code {rc}).[/red]")
|
log_screen.write(f"\n[red]Backup failed (exit code {rc}).[/red]")
|
||||||
log_screen.finish()
|
|
||||||
|
|
||||||
def action_go_back(self) -> None:
|
def action_go_back(self) -> None:
|
||||||
self.app.pop_screen()
|
self.app.pop_screen()
|
||||||
|
|||||||
@@ -81,13 +81,10 @@ class RemotesScreen(Screen):
|
|||||||
else:
|
else:
|
||||||
self.notify("Select a remote first", severity="warning")
|
self.notify("Select a remote first", severity="warning")
|
||||||
|
|
||||||
def _test_remote(self, name: str) -> None:
|
@work
|
||||||
|
async def _test_remote(self, name: str) -> None:
|
||||||
log_screen = OperationLog(f"Testing Remote: {name}")
|
log_screen = OperationLog(f"Testing Remote: {name}")
|
||||||
self.app.push_screen(log_screen)
|
self.app.push_screen(log_screen)
|
||||||
self._run_test_remote(log_screen, name)
|
|
||||||
|
|
||||||
@work
|
|
||||||
async def _run_test_remote(self, log_screen: OperationLog, name: str) -> None:
|
|
||||||
rc, stdout, stderr = await run_cli("remotes", "test", f"--name={name}")
|
rc, stdout, stderr = await run_cli("remotes", "test", f"--name={name}")
|
||||||
if stdout:
|
if stdout:
|
||||||
log_screen.write(stdout)
|
log_screen.write(stdout)
|
||||||
@@ -97,7 +94,6 @@ class RemotesScreen(Screen):
|
|||||||
log_screen.write("\n[green]Connection test passed.[/green]")
|
log_screen.write("\n[green]Connection test passed.[/green]")
|
||||||
else:
|
else:
|
||||||
log_screen.write(f"\n[red]Connection test failed (exit code {rc}).[/red]")
|
log_screen.write(f"\n[red]Connection test failed (exit code {rc}).[/red]")
|
||||||
log_screen.finish()
|
|
||||||
|
|
||||||
def _delete_remote(self, name: str) -> None:
|
def _delete_remote(self, name: str) -> None:
|
||||||
conf = CONFIG_DIR / "remotes.d" / f"{name}.conf"
|
conf = CONFIG_DIR / "remotes.d" / f"{name}.conf"
|
||||||
|
|||||||
@@ -107,16 +107,13 @@ class RestoreScreen(Screen):
|
|||||||
msg += "\nLocation: In-place"
|
msg += "\nLocation: In-place"
|
||||||
self.app.push_screen(
|
self.app.push_screen(
|
||||||
ConfirmDialog(msg, "Confirm Restore"),
|
ConfirmDialog(msg, "Confirm Restore"),
|
||||||
callback=lambda ok: self._confirmed_restore(target, remote, snapshot, dest) if ok else None,
|
callback=lambda ok: self._do_restore(target, remote, snapshot, dest) if ok else None,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _confirmed_restore(self, target: str, remote: str, snapshot: str, dest: str) -> None:
|
@work
|
||||||
|
async def _do_restore(self, target: str, remote: str, snapshot: str, dest: str) -> None:
|
||||||
log_screen = OperationLog(f"Restore: {target}")
|
log_screen = OperationLog(f"Restore: {target}")
|
||||||
self.app.push_screen(log_screen)
|
self.app.push_screen(log_screen)
|
||||||
self._run_restore(log_screen, target, remote, snapshot, dest)
|
|
||||||
|
|
||||||
@work
|
|
||||||
async def _run_restore(self, log_screen: OperationLog, target: str, remote: str, snapshot: str, dest: str) -> None:
|
|
||||||
args = ["restore", f"--target={target}", f"--remote={remote}", f"--snapshot={snapshot}"]
|
args = ["restore", f"--target={target}", f"--remote={remote}", f"--snapshot={snapshot}"]
|
||||||
if dest:
|
if dest:
|
||||||
args.append(f"--dest={dest}")
|
args.append(f"--dest={dest}")
|
||||||
@@ -125,7 +122,6 @@ class RestoreScreen(Screen):
|
|||||||
log_screen.write("\n[green]Restore completed successfully.[/green]")
|
log_screen.write("\n[green]Restore completed successfully.[/green]")
|
||||||
else:
|
else:
|
||||||
log_screen.write(f"\n[red]Restore failed (exit code {rc}).[/red]")
|
log_screen.write(f"\n[red]Restore failed (exit code {rc}).[/red]")
|
||||||
log_screen.finish()
|
|
||||||
|
|
||||||
def action_go_back(self) -> None:
|
def action_go_back(self) -> None:
|
||||||
self.app.pop_screen()
|
self.app.pop_screen()
|
||||||
|
|||||||
@@ -51,12 +51,12 @@ class RetentionScreen(Screen):
|
|||||||
target = str(target_sel.value)
|
target = str(target_sel.value)
|
||||||
self.app.push_screen(
|
self.app.push_screen(
|
||||||
ConfirmDialog(f"Run retention cleanup for '{target}'?", "Confirm"),
|
ConfirmDialog(f"Run retention cleanup for '{target}'?", "Confirm"),
|
||||||
callback=lambda ok: self._confirmed_cleanup(target) if ok else None,
|
callback=lambda ok: self._do_cleanup(target) if ok else None,
|
||||||
)
|
)
|
||||||
elif event.button.id == "btn-cleanup-all":
|
elif event.button.id == "btn-cleanup-all":
|
||||||
self.app.push_screen(
|
self.app.push_screen(
|
||||||
ConfirmDialog("Run retention cleanup for ALL targets?", "Confirm"),
|
ConfirmDialog("Run retention cleanup for ALL targets?", "Confirm"),
|
||||||
callback=lambda ok: self._confirmed_cleanup_all() if ok else None,
|
callback=lambda ok: self._do_cleanup_all() if ok else None,
|
||||||
)
|
)
|
||||||
elif event.button.id == "btn-save-count":
|
elif event.button.id == "btn-save-count":
|
||||||
val = self.query_one("#ret-count", Input).value.strip()
|
val = self.query_one("#ret-count", Input).value.strip()
|
||||||
@@ -66,33 +66,25 @@ class RetentionScreen(Screen):
|
|||||||
update_conf_key(CONFIG_DIR / "gniza.conf", "RETENTION_COUNT", val)
|
update_conf_key(CONFIG_DIR / "gniza.conf", "RETENTION_COUNT", val)
|
||||||
self.notify(f"Retention count set to {val}.")
|
self.notify(f"Retention count set to {val}.")
|
||||||
|
|
||||||
def _confirmed_cleanup(self, target: str) -> None:
|
@work
|
||||||
|
async def _do_cleanup(self, target: str) -> None:
|
||||||
log_screen = OperationLog(f"Retention: {target}")
|
log_screen = OperationLog(f"Retention: {target}")
|
||||||
self.app.push_screen(log_screen)
|
self.app.push_screen(log_screen)
|
||||||
self._run_cleanup(log_screen, target)
|
|
||||||
|
|
||||||
def _confirmed_cleanup_all(self) -> None:
|
|
||||||
log_screen = OperationLog("Retention: All Targets")
|
|
||||||
self.app.push_screen(log_screen)
|
|
||||||
self._run_cleanup_all(log_screen)
|
|
||||||
|
|
||||||
@work
|
|
||||||
async def _run_cleanup(self, log_screen: OperationLog, target: str) -> None:
|
|
||||||
rc = await stream_cli(log_screen.write, "retention", f"--target={target}")
|
rc = await stream_cli(log_screen.write, "retention", f"--target={target}")
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
log_screen.write("\n[green]Cleanup completed.[/green]")
|
log_screen.write("\n[green]Cleanup completed.[/green]")
|
||||||
else:
|
else:
|
||||||
log_screen.write(f"\n[red]Cleanup failed (exit code {rc}).[/red]")
|
log_screen.write(f"\n[red]Cleanup failed (exit code {rc}).[/red]")
|
||||||
log_screen.finish()
|
|
||||||
|
|
||||||
@work
|
@work
|
||||||
async def _run_cleanup_all(self, log_screen: OperationLog) -> None:
|
async def _do_cleanup_all(self) -> None:
|
||||||
|
log_screen = OperationLog("Retention: All Targets")
|
||||||
|
self.app.push_screen(log_screen)
|
||||||
rc = await stream_cli(log_screen.write, "retention", "--all")
|
rc = await stream_cli(log_screen.write, "retention", "--all")
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
log_screen.write("\n[green]All cleanups completed.[/green]")
|
log_screen.write("\n[green]All cleanups completed.[/green]")
|
||||||
else:
|
else:
|
||||||
log_screen.write(f"\n[red]Cleanup failed (exit code {rc}).[/red]")
|
log_screen.write(f"\n[red]Cleanup failed (exit code {rc}).[/red]")
|
||||||
log_screen.finish()
|
|
||||||
|
|
||||||
def action_go_back(self) -> None:
|
def action_go_back(self) -> None:
|
||||||
self.app.pop_screen()
|
self.app.pop_screen()
|
||||||
|
|||||||
@@ -216,47 +216,35 @@ class ScheduleScreen(Screen):
|
|||||||
self.notify(f"Schedule '{name}' deleted.")
|
self.notify(f"Schedule '{name}' deleted.")
|
||||||
self._refresh_table()
|
self._refresh_table()
|
||||||
|
|
||||||
def _install_schedules(self) -> None:
|
@work
|
||||||
|
async def _install_schedules(self) -> None:
|
||||||
log_screen = OperationLog("Install Schedules")
|
log_screen = OperationLog("Install Schedules")
|
||||||
self.app.push_screen(log_screen)
|
self.app.push_screen(log_screen)
|
||||||
self._run_install(log_screen)
|
|
||||||
|
|
||||||
@work
|
|
||||||
async def _run_install(self, log_screen: OperationLog) -> None:
|
|
||||||
rc, stdout, stderr = await run_cli("schedule", "install")
|
rc, stdout, stderr = await run_cli("schedule", "install")
|
||||||
if stdout:
|
if stdout:
|
||||||
log_screen.write(stdout)
|
log_screen.write(stdout)
|
||||||
if stderr:
|
if stderr:
|
||||||
log_screen.write(stderr)
|
log_screen.write(stderr)
|
||||||
log_screen.finish()
|
|
||||||
|
|
||||||
def _remove_schedules(self) -> None:
|
|
||||||
log_screen = OperationLog("Remove Schedules")
|
|
||||||
self.app.push_screen(log_screen)
|
|
||||||
self._run_remove(log_screen)
|
|
||||||
|
|
||||||
@work
|
@work
|
||||||
async def _run_remove(self, log_screen: OperationLog) -> None:
|
async def _remove_schedules(self) -> None:
|
||||||
|
log_screen = OperationLog("Remove Schedules")
|
||||||
|
self.app.push_screen(log_screen)
|
||||||
rc, stdout, stderr = await run_cli("schedule", "remove")
|
rc, stdout, stderr = await run_cli("schedule", "remove")
|
||||||
if stdout:
|
if stdout:
|
||||||
log_screen.write(stdout)
|
log_screen.write(stdout)
|
||||||
if stderr:
|
if stderr:
|
||||||
log_screen.write(stderr)
|
log_screen.write(stderr)
|
||||||
log_screen.finish()
|
|
||||||
|
|
||||||
def _show_crontab(self) -> None:
|
|
||||||
log_screen = OperationLog("Current Crontab")
|
|
||||||
self.app.push_screen(log_screen)
|
|
||||||
self._run_show(log_screen)
|
|
||||||
|
|
||||||
@work
|
@work
|
||||||
async def _run_show(self, log_screen: OperationLog) -> None:
|
async def _show_crontab(self) -> None:
|
||||||
|
log_screen = OperationLog("Current Crontab")
|
||||||
|
self.app.push_screen(log_screen)
|
||||||
rc, stdout, stderr = await run_cli("schedule", "show")
|
rc, stdout, stderr = await run_cli("schedule", "show")
|
||||||
if stdout:
|
if stdout:
|
||||||
log_screen.write(stdout)
|
log_screen.write(stdout)
|
||||||
if stderr:
|
if stderr:
|
||||||
log_screen.write(stderr)
|
log_screen.write(stderr)
|
||||||
log_screen.finish()
|
|
||||||
|
|
||||||
def action_go_back(self) -> None:
|
def action_go_back(self) -> None:
|
||||||
self.app.pop_screen()
|
self.app.pop_screen()
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ class ConfirmDialog(ModalScreen[bool]):
|
|||||||
yield Button("No", variant="default", id="cd-no")
|
yield Button("No", variant="default", id="cd-no")
|
||||||
|
|
||||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
self.notify(f"DEBUG CD: button={event.button.id}, dismissing with {event.button.id == 'cd-yes'}")
|
|
||||||
self.dismiss(event.button.id == "cd-yes")
|
self.dismiss(event.button.id == "cd-yes")
|
||||||
|
|
||||||
def action_cancel(self) -> None:
|
def action_cancel(self) -> None:
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
from textual.app import ComposeResult
|
from textual.app import ComposeResult
|
||||||
from textual.screen import ModalScreen
|
from textual.screen import ModalScreen
|
||||||
@@ -12,29 +14,41 @@ class OperationLog(ModalScreen[None]):
|
|||||||
def __init__(self, title: str = "Operation Output"):
|
def __init__(self, title: str = "Operation Output"):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._title = title
|
self._title = title
|
||||||
self._running = True
|
self._mounted_event = asyncio.Event()
|
||||||
|
self._buffer: list[str] = []
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
with Vertical(id="op-log"):
|
with Vertical(id="op-log"):
|
||||||
yield Static(self._title, id="ol-title")
|
yield Static(self._title, id="ol-title")
|
||||||
yield Static("Running...", id="ol-status")
|
yield RichLog(id="ol-log", wrap=True, highlight=True, markup=True)
|
||||||
yield Button("Close", variant="primary", id="ol-close")
|
yield Button("Close", variant="primary", id="ol-close")
|
||||||
|
|
||||||
|
def on_mount(self) -> None:
|
||||||
|
# Flush any buffered writes
|
||||||
|
log = self.query_one("#ol-log", RichLog)
|
||||||
|
for text in self._buffer:
|
||||||
|
self._write_to_log(log, text)
|
||||||
|
self._buffer.clear()
|
||||||
|
self._mounted_event.set()
|
||||||
|
|
||||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
self.dismiss(None)
|
self.dismiss(None)
|
||||||
|
|
||||||
def action_close(self) -> None:
|
def action_close(self) -> None:
|
||||||
self.dismiss(None)
|
self.dismiss(None)
|
||||||
|
|
||||||
def finish(self) -> None:
|
def _write_to_log(self, log: RichLog, text: str) -> None:
|
||||||
self._running = False
|
if "[" in text and "[/" in text:
|
||||||
try:
|
log.write(Text.from_markup(text))
|
||||||
self.query_one("#ol-status", Static).update("Done ✅")
|
else:
|
||||||
except Exception:
|
log.write(text)
|
||||||
pass
|
|
||||||
|
|
||||||
def write(self, text: str) -> None:
|
def write(self, text: str) -> None:
|
||||||
|
if not self._mounted_event.is_set():
|
||||||
|
self._buffer.append(text)
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
self.query_one("#ol-status", Static).update(text)
|
log = self.query_one("#ol-log", RichLog)
|
||||||
|
self._write_to_log(log, text)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
self._buffer.append(text)
|
||||||
|
|||||||
Reference in New Issue
Block a user