diff --git a/src/tui/screens/monitor.py b/src/tui/screens/monitor.py index 081973c6..12a16d59 100644 --- a/src/tui/screens/monitor.py +++ b/src/tui/screens/monitor.py @@ -469,16 +469,17 @@ class MonitorScreen(Screen): return # Check for flow backups before resetting + delete_backups = False if self._check_flow_backups(): # Show warning modal and wait for user decision - should_continue = await self.app.push_screen_wait( + should_continue, delete_backups = await self.app.push_screen_wait( FlowBackupWarningModal(operation="reset") ) if not should_continue: self.notify("Factory reset cancelled", severity="information") return - # Clear config, conversations.json, and flow backups first (before stopping containers) + # Clear config, conversations.json, and optionally flow backups (before stopping containers) try: config_path = Path("config") conversations_file = Path("conversations.json") @@ -492,17 +493,19 @@ class MonitorScreen(Screen): if conversations_file.exists(): conversations_file.unlink() - # Delete flow backups if they exist - if flows_backup_path.exists(): + # Delete flow backups only if user chose to + if delete_backups and flows_backup_path.exists(): shutil.rmtree(flows_backup_path) # Recreate empty backup directory flows_backup_path.mkdir(parents=True, exist_ok=True) + self.notify("Flow backups deleted", severity="information") + elif flows_backup_path.exists(): + self.notify("Flow backups preserved in ./flows/backup", severity="information") except Exception as e: self.notify( f"Error clearing config: {str(e)}", severity="error", - timeout=10, ) return diff --git a/src/tui/widgets/diagnostics_notification.py b/src/tui/widgets/diagnostics_notification.py index 3018ffb8..6a5f8619 100644 --- a/src/tui/widgets/diagnostics_notification.py +++ b/src/tui/widgets/diagnostics_notification.py @@ -9,7 +9,7 @@ def notify_with_diagnostics( app: App, message: str, severity: Literal["information", "warning", "error"] = "error", - timeout: float = 10.0, + timeout: float | None = None, ) -> None: """Show a notification with a button to open the diagnostics screen. @@ -17,7 +17,7 @@ def notify_with_diagnostics( app: The Textual app message: The notification message severity: The notification severity - timeout: The notification timeout in seconds + timeout: The notification timeout in seconds (None for default 20s) """ # First show the notification app.notify(message, severity=severity, timeout=timeout) diff --git a/src/tui/widgets/error_notification.py b/src/tui/widgets/error_notification.py index a06fe188..a0b0e798 100644 --- a/src/tui/widgets/error_notification.py +++ b/src/tui/widgets/error_notification.py @@ -9,7 +9,7 @@ def notify_with_diagnostics( app: App, message: str, severity: Literal["information", "warning", "error"] = "error", - timeout: float = 10.0, + timeout: float | None = None, ) -> None: """Show a notification with a button to open the diagnostics screen. @@ -17,7 +17,7 @@ def notify_with_diagnostics( app: The Textual app message: The notification message severity: The notification severity - timeout: The notification timeout in seconds + timeout: The notification timeout in seconds (None for default 20s) """ # First show the notification app.notify(message, severity=severity, timeout=timeout) diff --git a/src/tui/widgets/flow_backup_warning_modal.py b/src/tui/widgets/flow_backup_warning_modal.py index b13bf69b..5cdb3516 100644 --- a/src/tui/widgets/flow_backup_warning_modal.py +++ b/src/tui/widgets/flow_backup_warning_modal.py @@ -1,13 +1,16 @@ """Flow backup warning modal for OpenRAG TUI.""" from textual.app import ComposeResult -from textual.containers import Container, Horizontal +from textual.containers import Container, Horizontal, Vertical from textual.screen import ModalScreen -from textual.widgets import Button, Static, Label +from textual.widgets import Button, Static, Label, Checkbox -class FlowBackupWarningModal(ModalScreen[bool]): - """Modal dialog to warn about flow backups before upgrade/reset.""" +class FlowBackupWarningModal(ModalScreen[tuple[bool, bool]]): + """Modal dialog to warn about flow backups before upgrade/reset. + + Returns tuple of (continue, delete_backups) + """ DEFAULT_CSS = """ FlowBackupWarningModal { @@ -37,6 +40,17 @@ class FlowBackupWarningModal(ModalScreen[bool]): text-align: center; } + #checkbox-container { + width: 100%; + height: auto; + align: center middle; + padding: 0 2; + } + + #delete-backups-checkbox { + width: auto; + } + #button-row { width: 100%; height: auto; @@ -88,11 +102,12 @@ class FlowBackupWarningModal(ModalScreen[bool]): yield Static( f"Flow backups found in ./flows/backup\n\n" f"Proceeding with {self.operation} will reset custom flows to defaults.\n" - f"Your customizations are backed up and will need to be\n" - f"manually imported and upgraded to work with the latest version.\n\n" - f"Do you want to continue?", + f"Your customizations are backed up in ./flows/backup/\n\n" + f"Choose whether to keep or delete the backup files:", id="message" ) + with Vertical(id="checkbox-container"): + yield Checkbox("Delete backup files", id="delete-backups-checkbox", value=False) with Horizontal(id="button-row"): yield Button("Cancel", id="cancel-btn") yield Button(f"Continue {self.operation.title()}", id="continue-btn") @@ -104,6 +119,7 @@ class FlowBackupWarningModal(ModalScreen[bool]): def on_button_pressed(self, event: Button.Pressed) -> None: """Handle button presses.""" if event.button.id == "continue-btn": - self.dismiss(True) # User wants to continue + delete_backups = self.query_one("#delete-backups-checkbox", Checkbox).value + self.dismiss((True, delete_backups)) # User wants to continue, with delete preference else: - self.dismiss(False) # User cancelled + self.dismiss((False, False)) # User cancelled