Refactor hosts TUI application: remove HelpModal, update help action, and clean up related code

This commit is contained in:
Philip Henning 2025-08-16 17:57:35 +02:00
parent 502bbd87f3
commit 50628d78b7
7 changed files with 38 additions and 206 deletions

View file

@ -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(

View file

@ -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."""

View file

@ -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()

View file

@ -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"),
] ]

View file

@ -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

View file

@ -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 = (

View file

@ -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."""