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
|
||||||
|
|
|
@ -46,7 +46,7 @@ COMMON_CSS = """
|
||||||
# CSS styles for the hosts manager application
|
# CSS styles for the hosts manager application
|
||||||
HOSTS_MANAGER_CSS = (
|
HOSTS_MANAGER_CSS = (
|
||||||
COMMON_CSS
|
COMMON_CSS
|
||||||
+"""
|
+ """
|
||||||
.search-container {
|
.search-container {
|
||||||
border: round $primary;
|
border: round $primary;
|
||||||
height: 3;
|
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
|
||||||
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