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

View file

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

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

View file

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

View file

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

View file

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