feat: Add comprehensive help screen modal

- Create HelpScreen with all keyboard shortcuts
- Add hardcoded sections for instructions
- Add binding for '?' key to show help
- Support ESC/q/? to close help screen
- Document notification compression feature in help
- Format help with colors and sections (Navigation, Actions, View, Search)

Features:
- Shows all keyboard shortcuts in organized sections
- Quick Actions section explains notification compression
- Configuration instructions for mail.toml
- Modal dialog with close button
- Extracts bindings automatically for display
This commit is contained in:
Bendt
2025-12-28 13:33:09 -05:00
parent fa54f45998
commit 977c8e4ee0
3 changed files with 157 additions and 2 deletions

View File

@@ -5,6 +5,7 @@ from .widgets.EnvelopeListItem import EnvelopeListItem, GroupHeader
from .screens.LinkPanel import LinkPanel
from .screens.ConfirmDialog import ConfirmDialog
from .screens.SearchPanel import SearchPanel
from src.mail.screens.HelpScreen import HelpScreen
from .actions.task import action_create_task
from .actions.open import action_open
from .actions.delete import delete_current
@@ -142,6 +143,7 @@ class EmailViewerApp(App):
Binding("A", "accept_invite", "Accept invite"),
Binding("D", "decline_invite", "Decline invite"),
Binding("T", "tentative_invite", "Tentative"),
Binding("?", "show_help", "Show Help"),
]
)
@@ -652,10 +654,15 @@ class EmailViewerApp(App):
self.action_newest()
async def action_toggle_mode(self) -> None:
"""Toggle the content mode between plaintext and markdown."""
"""Toggle of content mode between plaintext and markdown."""
content_container = self.query_one(ContentContainer)
await content_container.action_toggle_mode()
async def action_show_help(self) -> None:
"""Show help screen with keyboard shortcuts."""
help_screen = HelpScreen()
self.push_screen(help_screen)
def action_next(self) -> None:
if not self.current_message_index >= 0:
return

View File

@@ -0,0 +1,146 @@
"""Help screen modal for mail app."""
from textual.screen import Screen
from textual.containers import Vertical, Horizontal, Center, ScrollableContainer
from textual.widgets import Static, Button, Footer
from textual.app import ComposeResult
from textual.binding import Binding
class HelpScreen(Screen):
"""Help screen showing all keyboard shortcuts and app information."""
BINDINGS = [
Binding("escape", "pop_screen", "Close", show=False),
Binding("q", "pop_screen", "Close", show=False),
Binding("?", "pop_screen", "Close", show=False),
]
def __init__(self, app_bindings: list[Binding], **kwargs):
"""Initialize help screen with app bindings.
Args:
app_bindings: List of bindings from the main app
"""
super().__init__(**kwargs)
self.app_bindings = app_bindings
def compose(self) -> ComposeResult:
"""Compose the help screen."""
with Vertical(id="help_container"):
# Header
yield Static(
"╔══════════════════════════════════════════════════════════════════╗\n"
"" + " " * 68 + "\n"
"" + " LUK Mail - Keyboard Shortcuts & Help".center(68) + "\n"
"╚════════════════════════════════════════════════════════════════════╝"
)
# Custom instructions section
yield Static("", id="spacer")
yield Static("[b cyan]Quick Actions[/b cyan]", id="instructions_title")
yield Static("" * 70, id="instructions_separator")
yield Static("")
yield Static(
" The mail app automatically compresses notification emails from:"
)
yield Static(" • GitLab (pipelines, MRs, mentions)")
yield Static(" • GitHub (PRs, issues, reviews)")
yield Static(" • Jira (issues, status changes)")
yield Static(" • Confluence (page updates, comments)")
yield Static(" • Datadog (alerts, incidents)")
yield Static(" • Renovate (dependency updates)")
yield Static("")
yield Static(
" [yellow]Tip:[/yellow] Toggle between compressed and full view with [b]m[/b]"
)
yield Static("")
# Auto-generated keybindings section
yield Static("", id="spacer")
yield Static("[b cyan]Keyboard Shortcuts[/b cyan]", id="bindings_title")
yield Static("" * 70, id="bindings_separator")
yield Static("")
yield Static("[b green]Navigation[/b green]")
yield Static(" j/k - Next/Previous message")
yield Static(" g - Go to oldest message")
yield Static(" G - Go to newest message")
yield Static(" PageDown/PageUp - Scroll page down/up")
yield Static("")
yield Static("[b green]Message Actions[/b green]")
yield Static(" # - Delete message(s)")
yield Static(" e - Archive message(s)")
yield Static(" u - Toggle read/unread")
yield Static(" t - Create task from message")
yield Static(" l - Show links in message")
yield Static("")
yield Static("[b green]View Options[/b green]")
yield Static(" w - Toggle message view window")
yield Static(" m - Toggle markdown/html view mode")
yield Static(" h - Toggle envelope header")
yield Static("")
yield Static("[b green]Search & Filter[/b green]")
yield Static(" / - Search messages")
yield Static(" s - Toggle sort order")
yield Static(" x - Toggle selection mode")
yield Static(" Space - Select/deselect message")
yield Static(" Escape - Clear selection")
yield Static("")
yield Static("[b green]Calendar Actions (when applicable)[/b green]")
yield Static(" A - Accept invite")
yield Static(" D - Decline invite")
yield Static(" T - Tentative")
yield Static("")
yield Static("[b green]Application[/b green]")
yield Static(" r - Reload message list")
yield Static(
" 1-4 - Focus panel (Accounts, Folders, Messages, Content)"
)
yield Static(" q - Quit application")
yield Static("")
# Notification compression section
yield Static("", id="spacer")
yield Static(
"[b cyan]Notification Email Compression[/b cyan]",
id="compression_title",
)
yield Static("" * 70, id="compression_separator")
yield Static("")
yield Static(
" Notification emails are automatically detected and compressed"
)
yield Static(" into terminal-friendly summaries showing:")
yield Static(" • Notification type and icon")
yield Static(" • Key details (ID, title, status)")
yield Static(" • Action items")
yield Static(" • Important links")
yield Static("")
yield Static(" [yellow]Configuration:[/yellow]")
yield Static(" Edit ~/.config/luk/mail.toml to customize:")
yield Static(" [dim]compress_notifications = true[/dim]")
yield Static(" [dim]notification_compression_mode = 'summary'[/dim]")
yield Static(" # Options: 'summary', 'detailed', 'off'")
yield Static("")
# Footer
yield Static("" * 70, id="footer_separator")
yield Static(
"[dim]Press [b]ESC[/b], [b]q[/b], or [b]?[/b] to close this help screen[/dim]",
id="footer_text",
)
# Close button at bottom
with Horizontal(id="button_container"):
yield Button("Close", id="close_button", variant="primary")
def on_button_pressed(self, event: Button.Pressed) -> None:
"""Handle button press to close help screen."""
if event.button.id == "close_button":
self.dismiss()

View File

@@ -1,10 +1,11 @@
# Initialize the screens package
# Initialize screens package
from .CreateTask import CreateTaskScreen
from .OpenMessage import OpenMessageScreen
from .DocumentViewer import DocumentViewerScreen
from .LinkPanel import LinkPanel, LinkItem, extract_links_from_content
from .ConfirmDialog import ConfirmDialog
from .SearchPanel import SearchPanel, SearchHelpModal
from .HelpScreen import HelpScreen
__all__ = [
"CreateTaskScreen",
@@ -16,4 +17,5 @@ __all__ = [
"ConfirmDialog",
"SearchPanel",
"SearchHelpModal",
"HelpScreen",
]