diff --git a/README.md b/README.md index 249a516..31272ea 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ including persistence behavior and manual recovery. | `Ctrl+E` | Enter or leave Privileged Mode | | `b` | Review and restore the session Pre-edit Backup | | `n` | Add a Host Entry | -| `Shift+N` | Add a Host Entry based on the selected Host Entry (requires an enhanced keyboard protocol) | +| `Shift+N` | Add a Host Entry based on the selected Host Entry (`N` fallback without enhanced keyboard support; Caps Lock also triggers it) | | `e` | Open the selected Host Entry in the Entry Editor | | `d` | Delete the selected Host Entry | | `Space` | Activate or deactivate the selected Host Entry | diff --git a/docs/user-guide.md b/docs/user-guide.md index 6a256ae..5ec7d36 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -63,8 +63,9 @@ separate from the Entry Editor used to change one Host Entry. In Privileged Mode: - Press `n` to add a Host Entry. -- Press `Shift+N` to add a Host Entry based on the selected Host Entry. This - requires a terminal that supports the Kitty enhanced keyboard protocol. +- Press `Shift+N` to add a Host Entry based on the selected Host Entry. In a + terminal without the Kitty enhanced keyboard protocol, uppercase `N` is the + fallback, so Caps Lock also triggers this action. - Select a non-default Host Entry and press `e` to open the Entry Editor. - Press `d` and confirm to delete the selected Host Entry. - Press `Space` to activate or deactivate the selected Host Entry. @@ -255,7 +256,7 @@ continually retrying it. | Key | Action | | --- | --- | | `n` | Add a Host Entry | -| `Shift+N` | Add a Host Entry based on the selected Host Entry (requires an enhanced keyboard protocol) | +| `Shift+N` | Add a Host Entry based on the selected Host Entry (`N` fallback without enhanced keyboard support; Caps Lock also triggers it) | | `e` | Open the selected Host Entry in the Entry Editor | | `d` | Delete the selected Host Entry | | `Space` | Activate or deactivate the selected Host Entry | diff --git a/src/hosts/tui/help_modal.py b/src/hosts/tui/help_modal.py index 7fd12e6..63d5e1a 100644 --- a/src/hosts/tui/help_modal.py +++ b/src/hosts/tui/help_modal.py @@ -51,7 +51,7 @@ class HelpModal(ModalScreen[None]): yield Static("Privileged Mode", classes="help-section") yield Static( "Ctrl+E Enter or leave Privileged Mode n Add Host Entry\n" - "Shift+N New from selected (enhanced terminal) b Review Pre-edit Backup", + "Shift+N New from selected (N fallback) b Review Pre-edit Backup", classes="help-copy", ) yield Button("Close", id="help-close", variant="primary") diff --git a/src/hosts/tui/keybindings.py b/src/hosts/tui/keybindings.py index 2596b68..20a7a25 100644 --- a/src/hosts/tui/keybindings.py +++ b/src/hosts/tui/keybindings.py @@ -10,7 +10,13 @@ from textual.binding import Binding # Key bindings for the hosts manager application HOSTS_MANAGER_BINDINGS = [ Binding("n", "add_entry", "New entry", show=True, id="left:new_entry"), - Binding("shift+n", "new_from_selected", "New from selected", show=False), + Binding( + "shift+n,N", + "new_from_selected", + "New from selected", + show=False, + key_display="Shift+N", + ), Binding("d", "delete_entry", "Delete entry", show=True, id="left:delete_entry"), Binding("e", "edit_entry", "Edit entry", show=True, id="left:edit_entry"), Binding( diff --git a/tests/test_app.py b/tests/test_app.py index 378cb77..37a4ead 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -183,6 +183,21 @@ async def test_enhanced_shift_n_opens_new_from_selected_but_caps_lock_n_does_not assert not isinstance(app.screen, AddEntryModal) +@pytest.mark.asyncio +async def test_uppercase_n_is_a_fallback_when_the_terminal_lacks_enhanced_keys(): + """Legacy terminals use uppercase N for Shift+N and Caps Lock+N alike.""" + app = app_with_filterable_entries() + app.edit_mode = True + + async with app.run_test(size=(120, 40)) as pilot: + app.table_handler.populate_entries_table() + app.query_one("#entries-table").focus() + await pilot.press("N") + await pilot.pause() + + assert isinstance(app.screen, AddEntryModal) + + @pytest.mark.asyncio async def test_new_from_selected_requires_a_visible_host_entry(): """Shift+N does not reuse a stale selection when filters hide every entry."""