Refactor hosts TUI application: remove HelpModal, update help action, and clean up related code
This commit is contained in:
		
							parent
							
								
									502bbd87f3
								
							
						
					
					
						commit
						50628d78b7
					
				
					 7 changed files with 38 additions and 206 deletions
				
			
		| 
						 | 
					@ -6,7 +6,7 @@ This module provides a floating modal window for creating new host entries.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from textual.app import ComposeResult
 | 
					from textual.app import ComposeResult
 | 
				
			||||||
from textual.containers import Vertical, Horizontal
 | 
					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.screen import ModalScreen
 | 
				
			||||||
from textual.binding import Binding
 | 
					from textual.binding import Binding
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -64,7 +64,12 @@ class AddEntryModal(ModalScreen):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            with Vertical(classes="default-section") as active:
 | 
					            with Vertical(classes="default-section") as active:
 | 
				
			||||||
                active.border_title = "Activate Entry"
 | 
					                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"):
 | 
					            with Horizontal(classes="button-row"):
 | 
				
			||||||
                yield Button(
 | 
					                yield Button(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,6 @@ from .config_modal import ConfigModal
 | 
				
			||||||
from .password_modal import PasswordModal
 | 
					from .password_modal import PasswordModal
 | 
				
			||||||
from .add_entry_modal import AddEntryModal
 | 
					from .add_entry_modal import AddEntryModal
 | 
				
			||||||
from .delete_confirmation_modal import DeleteConfirmationModal
 | 
					from .delete_confirmation_modal import DeleteConfirmationModal
 | 
				
			||||||
from .help_modal import HelpModal
 | 
					 | 
				
			||||||
from .styles import HOSTS_MANAGER_CSS
 | 
					from .styles import HOSTS_MANAGER_CSS
 | 
				
			||||||
from .keybindings import HOSTS_MANAGER_BINDINGS
 | 
					from .keybindings import HOSTS_MANAGER_BINDINGS
 | 
				
			||||||
from .table_handler import TableHandler
 | 
					from .table_handler import TableHandler
 | 
				
			||||||
| 
						 | 
					@ -35,9 +34,12 @@ class HostsManagerApp(App):
 | 
				
			||||||
    with read-only mode by default and explicit edit mode.
 | 
					    with read-only mode by default and explicit edit mode.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ENABLE_COMMAND_PALETTE = False
 | 
				
			||||||
    CSS = HOSTS_MANAGER_CSS
 | 
					    CSS = HOSTS_MANAGER_CSS
 | 
				
			||||||
    BINDINGS = HOSTS_MANAGER_BINDINGS
 | 
					    BINDINGS = HOSTS_MANAGER_BINDINGS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    help_visible = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Reactive attributes
 | 
					    # Reactive attributes
 | 
				
			||||||
    hosts_file: reactive[HostsFile] = reactive(HostsFile())
 | 
					    hosts_file: reactive[HostsFile] = reactive(HostsFile())
 | 
				
			||||||
    selected_entry_index: reactive[int] = reactive(0)
 | 
					    selected_entry_index: reactive[int] = reactive(0)
 | 
				
			||||||
| 
						 | 
					@ -248,8 +250,13 @@ class HostsManagerApp(App):
 | 
				
			||||||
        self.update_status("Hosts file reloaded")
 | 
					        self.update_status("Hosts file reloaded")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def action_help(self) -> None:
 | 
					    def action_help(self) -> None:
 | 
				
			||||||
        """Show help modal."""
 | 
					        """Show help panel."""
 | 
				
			||||||
        self.push_screen(HelpModal())
 | 
					        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:
 | 
					    def action_config(self) -> None:
 | 
				
			||||||
        """Show configuration modal."""
 | 
					        """Show configuration modal."""
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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()
 | 
					 | 
				
			||||||
| 
						 | 
					@ -10,21 +10,21 @@ from textual.binding import Binding
 | 
				
			||||||
# Key bindings for the hosts manager application
 | 
					# Key bindings for the hosts manager application
 | 
				
			||||||
HOSTS_MANAGER_BINDINGS = [
 | 
					HOSTS_MANAGER_BINDINGS = [
 | 
				
			||||||
    Binding("q", "quit", "Quit"),
 | 
					    Binding("q", "quit", "Quit"),
 | 
				
			||||||
    Binding("r", "reload", "Reload"),
 | 
					    Binding("r", "reload", "Reload hosts file"),
 | 
				
			||||||
    Binding("question_mark", "help", "Help", key_display="?"),
 | 
					    Binding("question_mark", "help", "Show help", True, key_display="?"),
 | 
				
			||||||
    Binding("i", "sort_by_ip", "Sort by IP"),
 | 
					    Binding("i", "sort_by_ip", "Sort by IP address"),
 | 
				
			||||||
    Binding("n", "sort_by_hostname", "Sort by Hostname"),
 | 
					    Binding("n", "sort_by_hostname", "Sort by hostname"),
 | 
				
			||||||
    Binding("c", "config", "Config"),
 | 
					    Binding("c", "config", "Configuration"),
 | 
				
			||||||
    Binding("ctrl+e", "toggle_edit_mode", "Edit Mode"),
 | 
					    Binding("ctrl+e", "toggle_edit_mode", "Toggle edit mode"),
 | 
				
			||||||
    Binding("a", "add_entry", "Add Entry", show=False),
 | 
					    Binding("a", "add_entry", "Add new entry", show=False),
 | 
				
			||||||
    Binding("d", "delete_entry", "Delete Entry", show=False),
 | 
					    Binding("d", "delete_entry", "Delete entry", show=False),
 | 
				
			||||||
    Binding("e", "edit_entry", "Edit Entry", show=False),
 | 
					    Binding("e", "edit_entry", "Edit entry", show=False),
 | 
				
			||||||
    Binding("space", "toggle_entry", "Toggle Entry", show=False),
 | 
					    Binding("space", "toggle_entry", "Toggle active/inactive", show=False),
 | 
				
			||||||
    Binding("ctrl+s", "save_file", "Save", show=False),
 | 
					    Binding("ctrl+s", "save_file", "Save hosts file", show=False),
 | 
				
			||||||
    Binding("shift+up", "move_entry_up", "Move Up", show=False),
 | 
					    Binding("shift+up", "move_entry_up", "Move entry up", show=False),
 | 
				
			||||||
    Binding("shift+down", "move_entry_down", "Move Down", show=False),
 | 
					    Binding("shift+down", "move_entry_down", "Move entry down", show=False),
 | 
				
			||||||
    Binding("escape", "exit_edit_entry", "Exit Edit", show=False),
 | 
					    Binding("escape", "exit_edit_entry", "Exit edit mode", show=False),
 | 
				
			||||||
    Binding("tab", "next_field", "Next Field", show=False),
 | 
					    Binding("tab", "next_field", "Next field", show=False),
 | 
				
			||||||
    Binding("shift+tab", "prev_field", "Prev Field", show=False),
 | 
					    Binding("shift+tab", "prev_field", "Previous field", show=False),
 | 
				
			||||||
    ("ctrl+c", "quit", "Quit"),
 | 
					    ("ctrl+c", "quit", "Quit"),
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@ This module provides a secure password input modal for sudo operations.
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from textual.app import ComposeResult
 | 
					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.widgets import Static, Button, Input
 | 
				
			||||||
from textual.screen import ModalScreen
 | 
					from textual.screen import ModalScreen
 | 
				
			||||||
from textual.binding import Binding
 | 
					from textual.binding import Binding
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
ADD_ENTRY_MODAL_CSS = (
 | 
					ADD_ENTRY_MODAL_CSS = (
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -324,17 +324,12 @@ class TestHostsManagerApp:
 | 
				
			||||||
            patch("hosts.tui.app.Config", return_value=mock_config),
 | 
					            patch("hosts.tui.app.Config", return_value=mock_config),
 | 
				
			||||||
        ):
 | 
					        ):
 | 
				
			||||||
            app = HostsManagerApp()
 | 
					            app = HostsManagerApp()
 | 
				
			||||||
            app.push_screen = Mock()
 | 
					            app.action_show_help_panel = Mock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            app.action_help()
 | 
					            app.action_help()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Should push the help modal screen
 | 
					            # Should call the built-in help action
 | 
				
			||||||
            app.push_screen.assert_called_once()
 | 
					            app.action_show_help_panel.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)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_action_config(self):
 | 
					    def test_action_config(self):
 | 
				
			||||||
        """Test config action opens modal."""
 | 
					        """Test config action opens modal."""
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue