From 50628d78b79feec726694fd3321ee1dd72383868 Mon Sep 17 00:00:00 2001 From: phg Date: Sat, 16 Aug 2025 17:57:35 +0200 Subject: [PATCH] Refactor hosts TUI application: remove HelpModal, update help action, and clean up related code --- src/hosts/tui/add_entry_modal.py | 9 ++- src/hosts/tui/app.py | 13 +++- src/hosts/tui/help_modal.py | 127 ------------------------------- src/hosts/tui/keybindings.py | 32 ++++---- src/hosts/tui/password_modal.py | 2 +- src/hosts/tui/styles.py | 50 +----------- tests/test_main.py | 11 +-- 7 files changed, 38 insertions(+), 206 deletions(-) delete mode 100644 src/hosts/tui/help_modal.py diff --git a/src/hosts/tui/add_entry_modal.py b/src/hosts/tui/add_entry_modal.py index bee3f06..fe89920 100644 --- a/src/hosts/tui/add_entry_modal.py +++ b/src/hosts/tui/add_entry_modal.py @@ -6,7 +6,7 @@ This module provides a floating modal window for creating new host entries. from textual.app import ComposeResult from textual.containers import Vertical, Horizontal -from textual.widgets import Static, Button, Input, Checkbox, Label +from textual.widgets import Static, Button, Input, Checkbox from textual.screen import ModalScreen from textual.binding import Binding @@ -64,7 +64,12 @@ class AddEntryModal(ModalScreen): with Vertical(classes="default-section") as active: active.border_title = "Activate Entry" - yield Checkbox("Active", value=True, id="active-checkbox", classes="default-checkbox") + yield Checkbox( + "Active", + value=True, + id="active-checkbox", + classes="default-checkbox", + ) with Horizontal(classes="button-row"): yield Button( diff --git a/src/hosts/tui/app.py b/src/hosts/tui/app.py index 265af1f..003727c 100644 --- a/src/hosts/tui/app.py +++ b/src/hosts/tui/app.py @@ -18,7 +18,6 @@ from .config_modal import ConfigModal from .password_modal import PasswordModal from .add_entry_modal import AddEntryModal from .delete_confirmation_modal import DeleteConfirmationModal -from .help_modal import HelpModal from .styles import HOSTS_MANAGER_CSS from .keybindings import HOSTS_MANAGER_BINDINGS from .table_handler import TableHandler @@ -35,9 +34,12 @@ class HostsManagerApp(App): with read-only mode by default and explicit edit mode. """ + ENABLE_COMMAND_PALETTE = False CSS = HOSTS_MANAGER_CSS BINDINGS = HOSTS_MANAGER_BINDINGS + help_visible = False + # Reactive attributes hosts_file: reactive[HostsFile] = reactive(HostsFile()) selected_entry_index: reactive[int] = reactive(0) @@ -248,8 +250,13 @@ class HostsManagerApp(App): self.update_status("Hosts file reloaded") def action_help(self) -> None: - """Show help modal.""" - self.push_screen(HelpModal()) + """Show help panel.""" + if self.help_visible: + self.action_hide_help_panel() + self.help_visible = False + else: + self.action_show_help_panel() + self.help_visible = True def action_config(self) -> None: """Show configuration modal.""" diff --git a/src/hosts/tui/help_modal.py b/src/hosts/tui/help_modal.py deleted file mode 100644 index 88ee3c9..0000000 --- a/src/hosts/tui/help_modal.py +++ /dev/null @@ -1,127 +0,0 @@ -""" -Help modal window for the hosts TUI application. - -This module provides a help dialog showing keyboard shortcuts and usage information. -""" - -from textual.app import ComposeResult -from textual.containers import Vertical, Horizontal, ScrollableContainer -from textual.widgets import Static, Button -from textual.screen import ModalScreen -from textual.binding import Binding - -from .styles import HELP_MODAL_CSS - - -class HelpModal(ModalScreen): - """ - Modal screen showing help and keyboard shortcuts. - - Provides comprehensive help information for using the application. - """ - - CSS = HELP_MODAL_CSS - - BINDINGS = [ - Binding("escape", "close", "Close"), - Binding("enter", "close", "Close"), - ] - - def compose(self) -> ComposeResult: - """Create the help modal layout.""" - with Vertical(classes="help-container"): - yield Static("/etc/hosts Manager - Help", classes="help-title") - - # Navigation section - with Vertical(classes="help-section"): - yield Static("Navigation", classes="help-section-title") - yield Static("↑ ↓ - Navigate entries", classes="help-item") - yield Static("Enter - Select entry", classes="help-item") - yield Static("Tab - Navigate between panes", classes="help-item") - - # Main Commands section - with Vertical(classes="help-section"): - yield Static("Main Commands", classes="help-section-title") - yield Static( - "[bold]r[/bold] Reload [bold]h[/bold] Help [bold]c[/bold] Config [bold]q[/bold] Quit", - classes="help-item", - ) - yield Static( - "[bold]i[/bold] Sort by IP [bold]n[/bold] Sort by hostname", - classes="help-item", - ) - - # Edit Mode section - with Vertical(classes="help-section"): - yield Static("Edit Mode Commands", classes="help-section-title") - yield Static( - "[bold]Ctrl+E[/bold] - Toggle edit mode (requires sudo)", - classes="help-item", - ) - yield Static( - "[italic]Edit mode commands:[/italic] [bold]Space[/bold] Toggle [bold]a[/bold] Add [bold]d[/bold] Delete [bold]e[/bold] Edit", - classes="help-item", - ) - yield Static( - "[bold]Shift+↑/↓[/bold] Move entry [bold]Ctrl+S[/bold] Save file", - classes="help-item", - ) - - # Form Navigation section - with Vertical(classes="help-section"): - yield Static( - "Form & Modal Navigation", classes="help-section-title" - ) - yield Static( - "[bold]Tab/Shift+Tab[/bold] Navigate fields [bold]Enter[/bold] Confirm/Save [bold]Escape[/bold] Cancel/Exit", - classes="help-item", - ) - - # Special Commands section - with Vertical(classes="help-section"): - yield Static( - "Special Dialog Commands", classes="help-section-title" - ) - yield Static( - "[bold]s[/bold] Save changes [bold]d[/bold] Discard changes", - classes="help-item", - ) - - # Status and Tips section - with Vertical(classes="help-section"): - yield Static("Entry Status & Tips", classes="help-section-title") - yield Static( - "✓ Active (enabled) ✗ Inactive (commented out)", - classes="help-item", - ) - yield Static( - "• Edit mode commands require [bold]Ctrl+E[/bold] first", - classes="help-item", - ) - yield Static( - "• Search supports partial matches in IP, hostname, or comment", - classes="help-item", - ) - yield Static( - "• Edit mode creates automatic backups • System entries cannot be modified", - classes="help-item", - ) - - with Horizontal(classes="button-row"): - yield Button( - "Close", variant="primary", id="close-button", classes="default-button" - ) - - def on_mount(self) -> None: - """Focus close button when modal opens.""" - close_button = self.query_one("#close-button", Button) - close_button.focus() - - def on_button_pressed(self, event: Button.Pressed) -> None: - """Handle button presses.""" - if event.button.id == "close-button": - self.action_close() - - def action_close(self) -> None: - """Close the help modal.""" - self.dismiss() diff --git a/src/hosts/tui/keybindings.py b/src/hosts/tui/keybindings.py index d33b04f..a616e00 100644 --- a/src/hosts/tui/keybindings.py +++ b/src/hosts/tui/keybindings.py @@ -10,21 +10,21 @@ from textual.binding import Binding # Key bindings for the hosts manager application HOSTS_MANAGER_BINDINGS = [ Binding("q", "quit", "Quit"), - Binding("r", "reload", "Reload"), - Binding("question_mark", "help", "Help", key_display="?"), - Binding("i", "sort_by_ip", "Sort by IP"), - Binding("n", "sort_by_hostname", "Sort by Hostname"), - Binding("c", "config", "Config"), - Binding("ctrl+e", "toggle_edit_mode", "Edit Mode"), - Binding("a", "add_entry", "Add Entry", show=False), - Binding("d", "delete_entry", "Delete Entry", show=False), - Binding("e", "edit_entry", "Edit Entry", show=False), - Binding("space", "toggle_entry", "Toggle Entry", show=False), - Binding("ctrl+s", "save_file", "Save", show=False), - Binding("shift+up", "move_entry_up", "Move Up", show=False), - Binding("shift+down", "move_entry_down", "Move Down", show=False), - Binding("escape", "exit_edit_entry", "Exit Edit", show=False), - Binding("tab", "next_field", "Next Field", show=False), - Binding("shift+tab", "prev_field", "Prev Field", show=False), + Binding("r", "reload", "Reload hosts file"), + Binding("question_mark", "help", "Show help", True, key_display="?"), + Binding("i", "sort_by_ip", "Sort by IP address"), + Binding("n", "sort_by_hostname", "Sort by hostname"), + Binding("c", "config", "Configuration"), + Binding("ctrl+e", "toggle_edit_mode", "Toggle edit mode"), + Binding("a", "add_entry", "Add new entry", show=False), + Binding("d", "delete_entry", "Delete entry", show=False), + Binding("e", "edit_entry", "Edit entry", show=False), + Binding("space", "toggle_entry", "Toggle active/inactive", show=False), + Binding("ctrl+s", "save_file", "Save hosts file", show=False), + Binding("shift+up", "move_entry_up", "Move entry up", show=False), + Binding("shift+down", "move_entry_down", "Move entry down", show=False), + Binding("escape", "exit_edit_entry", "Exit edit mode", show=False), + Binding("tab", "next_field", "Next field", show=False), + Binding("shift+tab", "prev_field", "Previous field", show=False), ("ctrl+c", "quit", "Quit"), ] diff --git a/src/hosts/tui/password_modal.py b/src/hosts/tui/password_modal.py index c28466c..d445e66 100644 --- a/src/hosts/tui/password_modal.py +++ b/src/hosts/tui/password_modal.py @@ -5,7 +5,7 @@ This module provides a secure password input modal for sudo operations. """ from textual.app import ComposeResult -from textual.containers import Vertical, Horizontal +from textual.containers import Vertical from textual.widgets import Static, Button, Input from textual.screen import ModalScreen from textual.binding import Binding diff --git a/src/hosts/tui/styles.py b/src/hosts/tui/styles.py index 713be67..2ddc2bb 100644 --- a/src/hosts/tui/styles.py +++ b/src/hosts/tui/styles.py @@ -46,7 +46,7 @@ COMMON_CSS = """ # CSS styles for the hosts manager application HOSTS_MANAGER_CSS = ( COMMON_CSS - +""" + + """ .search-container { border: round $primary; height: 3; @@ -169,54 +169,6 @@ Header.-tall { """ ) -# Help Modal CSS -HELP_MODAL_CSS = ( - COMMON_CSS - + """ -HelpModal { - align: center middle; -} - -.help-container { - width: 90; - height: 40; - background: $surface; - border: thick $primary; - padding: 1; -} - -.help-title { - text-align: center; - text-style: bold; - color: $primary; - margin-bottom: 1; -} - -.help-content { - height: 35; - margin: 1 0; -} - -.help-section { - margin-bottom: 1; -} - -.help-section-title { - text-style: bold; - color: $primary; - margin-bottom: 0; -} - -.help-item { - margin: 0 2; -} - -.keyboard-shortcut { - text-style: bold; - color: $accent; -} -""" -) # Add Entry Modal CSS ADD_ENTRY_MODAL_CSS = ( diff --git a/tests/test_main.py b/tests/test_main.py index aca63f8..b75c39a 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -324,17 +324,12 @@ class TestHostsManagerApp: patch("hosts.tui.app.Config", return_value=mock_config), ): app = HostsManagerApp() - app.push_screen = Mock() + app.action_show_help_panel = Mock() app.action_help() - # Should push the help modal screen - app.push_screen.assert_called_once() - # Verify the modal is a HelpModal instance - from hosts.tui.help_modal import HelpModal - - args = app.push_screen.call_args[0] - assert isinstance(args[0], HelpModal) + # Should call the built-in help action + app.action_show_help_panel.assert_called_once() def test_action_config(self): """Test config action opens modal."""