Refactor hosts TUI application: remove main entry point, enhance modal styles, and unify button classes

- Deleted the main entry point file for the hosts application.
- Updated the Add Entry Modal to improve section titles and input styles.
- Refactored HostsManagerApp layout to use common pane styles for left and right sections.
- Enhanced Config, Delete Confirmation, and Help modals with consistent button labels and styles.
- Improved Password Modal layout and removed unnecessary parameters.
- Consolidated common CSS styles for buttons, inputs, and sections to ensure consistent styling across the application.
This commit is contained in:
Philip Henning 2025-08-16 16:52:53 +02:00
parent 54a2e00e29
commit 502bbd87f3
10 changed files with 155 additions and 1218 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,17 +0,0 @@
"""
Main entry point for the hosts TUI application.
This module contains the main application entry point function.
"""
from .tui.app import HostsManagerApp
def main():
"""Main entry point for the hosts application."""
app = HostsManagerApp()
app.run()
if __name__ == "__main__":
main()

View file

@ -36,47 +36,48 @@ class AddEntryModal(ModalScreen):
with Vertical(classes="add-entry-container"):
yield Static("Add New Host Entry", classes="add-entry-title")
with Vertical(classes="add-entry-section"):
yield Label("IP Address:")
with Vertical(classes="default-section") as ip_address:
ip_address.border_title = "IP Address"
yield Input(
placeholder="e.g., 192.168.1.1 or 2001:db8::1",
id="ip-address-input",
classes="add-entry-input",
classes="default-input",
)
yield Static("", id="ip-error", classes="validation-error")
with Vertical(classes="add-entry-section"):
yield Label("Hostnames (comma-separated):")
with Vertical(classes="default-section") as hostnames:
hostnames.border_title = "Hostnames"
yield Input(
placeholder="e.g., example.com, www.example.com",
id="hostnames-input",
classes="add-entry-input",
classes="default-input",
)
yield Static("", id="hostnames-error", classes="validation-error")
with Vertical(classes="add-entry-section"):
yield Label("Comment (optional):")
with Vertical(classes="default-section") as comment:
comment.border_title = "Comment (optional)"
yield Input(
placeholder="e.g., Development server",
id="comment-input",
classes="add-entry-input",
classes="default-input",
)
with Vertical(classes="add-entry-section"):
yield Checkbox("Active (enabled)", value=True, id="active-checkbox")
with Vertical(classes="default-section") as active:
active.border_title = "Activate Entry"
yield Checkbox("Active", value=True, id="active-checkbox", classes="default-checkbox")
with Horizontal(classes="button-row"):
yield Button(
"Add Entry",
"Add Entry (CTRL+S)",
variant="primary",
id="add-button",
classes="add-entry-button",
classes="default-button",
)
yield Button(
"Cancel",
"Cancel (ESC)",
variant="default",
id="cancel-button",
classes="add-entry-button",
classes="default-button",
)
def on_mount(self) -> None:

View file

@ -82,12 +82,12 @@ class HostsManagerApp(App):
with Horizontal(classes="hosts-container"):
# Left pane - entries table
with Vertical(classes="left-pane") as left_pane:
with Vertical(classes="common-pane left-pane") as left_pane:
left_pane.border_title = "Host Entries"
yield DataTable(id="entries-table")
# Right pane - entry details or edit form
with Vertical(classes="right-pane") as right_pane:
with Vertical(classes="common-pane right-pane") as right_pane:
right_pane.border_title = "Entry Details"
yield DataTable(
id="entry-details-table",

View file

@ -51,10 +51,10 @@ class ConfigModal(ModalScreen):
"Save", variant="primary", id="save-button", classes="config-button"
)
yield Button(
"Cancel",
"Cancel (ESC)",
variant="default",
id="cancel-button",
classes="config-button",
classes="default-button",
)
def on_button_pressed(self, event: Button.Pressed) -> None:

View file

@ -55,13 +55,13 @@ class DeleteConfirmationModal(ModalScreen):
"Delete",
variant="error",
id="delete-button",
classes="delete-button",
classes="default-button",
)
yield Button(
"Cancel",
"Cancel (ESC)",
variant="default",
id="cancel-button",
classes="delete-button",
classes="default-button",
)
def on_mount(self) -> None:

View file

@ -32,85 +32,84 @@ class HelpModal(ModalScreen):
with Vertical(classes="help-container"):
yield Static("/etc/hosts Manager - Help", classes="help-title")
with ScrollableContainer(classes="help-content"):
# 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")
# 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",
)
# 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",
)
# 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",
)
# 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",
)
# 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",
)
# 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="help-button"
"Close", variant="primary", id="close-button", classes="default-button"
)
def on_mount(self) -> None:

View file

@ -11,7 +11,7 @@ from textual.binding import Binding
HOSTS_MANAGER_BINDINGS = [
Binding("q", "quit", "Quit"),
Binding("r", "reload", "Reload"),
Binding("h", "help", "Help"),
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"),

View file

@ -27,38 +27,27 @@ class PasswordModal(ModalScreen):
Binding("enter", "submit", "Submit"),
]
def __init__(self, message: str = "Enter your password for sudo access:"):
def __init__(self):
super().__init__()
self.message = message
self.error_message = ""
def compose(self) -> ComposeResult:
"""Create the password modal layout."""
with Vertical(classes="password-container"):
yield Static("Sudo Authentication", classes="password-title")
yield Static(self.message, classes="password-message")
yield Input(
placeholder="Password",
password=True,
id="password-input",
classes="password-input",
)
with Vertical(classes="default-section") as password_input:
password_input.border_title = "Enter sudo Password"
yield Input(
placeholder="Password",
password=True,
id="password-input",
classes="default-input",
)
# Error message placeholder (initially empty)
yield Static("", id="error-message", classes="error-message")
with Horizontal(classes="button-row"):
yield Button(
"OK", variant="primary", id="ok-button", classes="password-button"
)
yield Button(
"Cancel",
variant="default",
id="cancel-button",
classes="password-button",
)
def on_mount(self) -> None:
"""Focus the password input when modal opens."""
password_input = self.query_one("#password-input", Input)

View file

@ -5,8 +5,48 @@ This module contains all CSS definitions for consistent styling
across the application.
"""
# Common CSS classes shared across components
COMMON_CSS = """
.default-button {
margin: 0 1;
min-width: 10;
}
.default-checkbox {
height: 1fr;
margin: 0 2;
border: none;
}
.default-input {
height: 1fr;
width: 1fr;
margin: 0 2;
border: none;
}
.default-section {
border: round $primary;
height: 3;
padding: 0;
margin: 1 0;
}
.button-row {
margin-top: 2;
height: 3;
align: center middle;
}
.hidden {
display: none;
}
"""
# CSS styles for the hosts manager application
HOSTS_MANAGER_CSS = """
HOSTS_MANAGER_CSS = (
COMMON_CSS
+"""
.search-container {
border: round $primary;
height: 3;
@ -16,28 +56,27 @@ HOSTS_MANAGER_CSS = """
}
.search-input {
height: 1fr;
width: 1fr;
height: 1;
border: none;
}
.hosts-container {
# height: 1fr;
height: 1fr;
margin-top: 0;
}
.common-pane {
border: round $primary;
margin: 0;
}
.left-pane {
width: 60%;
border: round $primary;
margin: 0;
padding: 1;
}
.right-pane {
width: 40%;
border: round $primary;
margin: 0;
padding: 1;
}
.entry-active {
@ -128,18 +167,7 @@ Header.-tall {
height: 1; /* Fix tall header also to height 1 */
}
"""
# Common CSS classes shared across components
COMMON_CSS = """
.button-row {
margin-top: 1;
align: center middle;
}
.hidden {
display: none;
}
"""
)
# Help Modal CSS
HELP_MODAL_CSS = (
@ -187,10 +215,6 @@ HelpModal {
text-style: bold;
color: $accent;
}
.help-button {
min-width: 10;
}
"""
)
@ -204,7 +228,7 @@ AddEntryModal {
.add-entry-container {
width: 80;
height: 25;
height: 26;
background: $surface;
border: thick $primary;
padding: 1;
@ -217,25 +241,6 @@ AddEntryModal {
margin-bottom: 1;
}
.add-entry-section {
margin: 1 0;
}
.add-entry-input {
margin: 0 2;
width: 1fr;
}
.button-row {
margin-top: 2;
align: center middle;
}
.add-entry-button {
margin: 0 1;
min-width: 10;
}
.validation-error {
color: $error;
margin: 0 2;
@ -254,7 +259,7 @@ DeleteConfirmationModal {
.delete-container {
width: 60;
height: 15;
height: 16;
background: $surface;
border: thick $error;
padding: 1;
@ -278,16 +283,6 @@ DeleteConfirmationModal {
color: $primary;
margin: 1 0;
}
.button-row {
margin-top: 2;
align: center middle;
}
.delete-button {
margin: 0 1;
min-width: 10;
}
"""
)
@ -301,7 +296,7 @@ PasswordModal {
.password-container {
width: 60;
height: 12;
height: 11;
background: $surface;
border: thick $primary;
padding: 1;
@ -320,15 +315,6 @@ PasswordModal {
margin-bottom: 1;
}
.password-input {
margin: 1 0;
}
.password-button {
margin: 0 1;
min-width: 10;
}
.error-message {
color: $error;
text-align: center;
@ -367,16 +353,6 @@ ConfigModal {
.config-option {
margin: 0 2;
}
.button-row {
margin-top: 2;
align: center middle;
}
.config-button {
margin: 0 1;
min-width: 10;
}
"""
)
@ -413,9 +389,5 @@ SaveConfirmationModal {
margin: 0 1;
min-width: 12;
}
.save-confirmation-button:focus {
border: thick $accent;
}
"""
)