feat(shortcuts): add Alt+1-9, Alt+0, and Alt+Tab for direct tab switching
- Add ActionSwitchTabIndex1-10 actions for direct tab access by position - Add ActionSwitchLastTab action for Alt+Tab style switching - Wire Alt+1-9 and Alt+0 keybindings to switch tabs by index - Wire Alt+Tab and Shift+Alt+Tab to switch to last active tab - Dispatch new actions through KeyboardDispatcher to TabCoordinator
- commit
- 3d9e595763b2c3129c323a53397d8761b5567dbe
- author
- brice · · 2025-12-18 06:44 UTC
- parent
- 5690117
- signature
- unsigned
2 files · +54 -5
M internal/ui/dispatcher/keyboard.go +22 -0
| @@ -59,6 +59,28 @@ func (d *KeyboardDispatcher) Dispatch(ctx context.Context, action input.Action) | |||
| 59 | 59 | return d.tabCoord.SwitchNext(ctx) | |
| 60 | 60 | case input.ActionPreviousTab: | |
| 61 | 61 | return d.tabCoord.SwitchPrev(ctx) | |
| 62 | + | case input.ActionSwitchLastTab: | |
| 63 | + | return d.tabCoord.SwitchToLastActive(ctx) | |
| 64 | + | case input.ActionSwitchTabIndex1: | |
| 65 | + | return d.tabCoord.SwitchByIndex(ctx, 0) | |
| 66 | + | case input.ActionSwitchTabIndex2: | |
| 67 | + | return d.tabCoord.SwitchByIndex(ctx, 1) | |
| 68 | + | case input.ActionSwitchTabIndex3: | |
| 69 | + | return d.tabCoord.SwitchByIndex(ctx, 2) | |
| 70 | + | case input.ActionSwitchTabIndex4: | |
| 71 | + | return d.tabCoord.SwitchByIndex(ctx, 3) | |
| 72 | + | case input.ActionSwitchTabIndex5: | |
| 73 | + | return d.tabCoord.SwitchByIndex(ctx, 4) | |
| 74 | + | case input.ActionSwitchTabIndex6: | |
| 75 | + | return d.tabCoord.SwitchByIndex(ctx, 5) | |
| 76 | + | case input.ActionSwitchTabIndex7: | |
| 77 | + | return d.tabCoord.SwitchByIndex(ctx, 6) | |
| 78 | + | case input.ActionSwitchTabIndex8: | |
| 79 | + | return d.tabCoord.SwitchByIndex(ctx, 7) | |
| 80 | + | case input.ActionSwitchTabIndex9: | |
| 81 | + | return d.tabCoord.SwitchByIndex(ctx, 8) | |
| 82 | + | case input.ActionSwitchTabIndex10: | |
| 83 | + | return d.tabCoord.SwitchByIndex(ctx, 9) | |
| 62 | 84 | ||
| 63 | 85 | // Pane actions | |
| 64 | 86 | case input.ActionSplitRight: | |
| @@ -59,6 +59,28 @@ func (d *KeyboardDispatcher) Dispatch(ctx context.Context, action input.Action) | |||||
| 59 | return d.tabCoord.SwitchNext(ctx) | 59 | return d.tabCoord.SwitchNext(ctx) | ||
| 60 | case input.ActionPreviousTab: | 60 | case input.ActionPreviousTab: | ||
| 61 | return d.tabCoord.SwitchPrev(ctx) | 61 | return d.tabCoord.SwitchPrev(ctx) | ||
| 62 | + | case input.ActionSwitchLastTab: | |||
| 63 | + | return d.tabCoord.SwitchToLastActive(ctx) | |||
| 64 | + | case input.ActionSwitchTabIndex1: | |||
| 65 | + | return d.tabCoord.SwitchByIndex(ctx, 0) | |||
| 66 | + | case input.ActionSwitchTabIndex2: | |||
| 67 | + | return d.tabCoord.SwitchByIndex(ctx, 1) | |||
| 68 | + | case input.ActionSwitchTabIndex3: | |||
| 69 | + | return d.tabCoord.SwitchByIndex(ctx, 2) | |||
| 70 | + | case input.ActionSwitchTabIndex4: | |||
| 71 | + | return d.tabCoord.SwitchByIndex(ctx, 3) | |||
| 72 | + | case input.ActionSwitchTabIndex5: | |||
| 73 | + | return d.tabCoord.SwitchByIndex(ctx, 4) | |||
| 74 | + | case input.ActionSwitchTabIndex6: | |||
| 75 | + | return d.tabCoord.SwitchByIndex(ctx, 5) | |||
| 76 | + | case input.ActionSwitchTabIndex7: | |||
| 77 | + | return d.tabCoord.SwitchByIndex(ctx, 6) | |||
| 78 | + | case input.ActionSwitchTabIndex8: | |||
| 79 | + | return d.tabCoord.SwitchByIndex(ctx, 7) | |||
| 80 | + | case input.ActionSwitchTabIndex9: | |||
| 81 | + | return d.tabCoord.SwitchByIndex(ctx, 8) | |||
| 82 | + | case input.ActionSwitchTabIndex10: | |||
| 83 | + | return d.tabCoord.SwitchByIndex(ctx, 9) | |||
| 62 | 84 | ||||
| 63 | // Pane actions | 85 | // Pane actions | ||
| 64 | case input.ActionSplitRight: | 86 | case input.ActionSplitRight: | ||
M internal/ui/input/shortcuts.go +32 -5
| @@ -44,11 +44,22 @@ const ( | |||
| 44 | 44 | ActionExitMode Action = "exit_mode" | |
| 45 | 45 | ||
| 46 | 46 | // Tab actions (global and modal) | |
| 47 | - | ActionNewTab Action = "new_tab" | |
| 48 | - | ActionCloseTab Action = "close_tab" | |
| 49 | - | ActionNextTab Action = "next_tab" | |
| 50 | - | ActionPreviousTab Action = "previous_tab" | |
| 51 | - | ActionRenameTab Action = "rename_tab" | |
| 47 | + | ActionNewTab Action = "new_tab" | |
| 48 | + | ActionCloseTab Action = "close_tab" | |
| 49 | + | ActionNextTab Action = "next_tab" | |
| 50 | + | ActionPreviousTab Action = "previous_tab" | |
| 51 | + | ActionRenameTab Action = "rename_tab" | |
| 52 | + | ActionSwitchLastTab Action = "switch_last_tab" // Alt+Tab style switching | |
| 53 | + | ActionSwitchTabIndex1 Action = "switch_tab_1" | |
| 54 | + | ActionSwitchTabIndex2 Action = "switch_tab_2" | |
| 55 | + | ActionSwitchTabIndex3 Action = "switch_tab_3" | |
| 56 | + | ActionSwitchTabIndex4 Action = "switch_tab_4" | |
| 57 | + | ActionSwitchTabIndex5 Action = "switch_tab_5" | |
| 58 | + | ActionSwitchTabIndex6 Action = "switch_tab_6" | |
| 59 | + | ActionSwitchTabIndex7 Action = "switch_tab_7" | |
| 60 | + | ActionSwitchTabIndex8 Action = "switch_tab_8" | |
| 61 | + | ActionSwitchTabIndex9 Action = "switch_tab_9" | |
| 62 | + | ActionSwitchTabIndex10 Action = "switch_tab_10" | |
| 52 | 63 | ||
| 53 | 64 | // Pane actions (modal) | |
| 54 | 65 | ActionSplitRight Action = "split_right" | |
| @@ -182,6 +193,22 @@ func (s *ShortcutSet) buildGlobalShortcuts(ctx context.Context, cfg *config.Work | |||
| 182 | 193 | // Also support Alt+Arrow keys for pane navigation (except Left/Right used for back/forward) | |
| 183 | 194 | s.Global[KeyBinding{uint(gdk.KEY_Up), ModAlt}] = ActionFocusUp | |
| 184 | 195 | s.Global[KeyBinding{uint(gdk.KEY_Down), ModAlt}] = ActionFocusDown | |
| 196 | + | ||
| 197 | + | // Direct tab switching (Alt+1-9, Alt+0 for tab 10) | |
| 198 | + | s.Global[KeyBinding{uint(gdk.KEY_1), ModAlt}] = ActionSwitchTabIndex1 | |
| 199 | + | s.Global[KeyBinding{uint(gdk.KEY_2), ModAlt}] = ActionSwitchTabIndex2 | |
| 200 | + | s.Global[KeyBinding{uint(gdk.KEY_3), ModAlt}] = ActionSwitchTabIndex3 | |
| 201 | + | s.Global[KeyBinding{uint(gdk.KEY_4), ModAlt}] = ActionSwitchTabIndex4 | |
| 202 | + | s.Global[KeyBinding{uint(gdk.KEY_5), ModAlt}] = ActionSwitchTabIndex5 | |
| 203 | + | s.Global[KeyBinding{uint(gdk.KEY_6), ModAlt}] = ActionSwitchTabIndex6 | |
| 204 | + | s.Global[KeyBinding{uint(gdk.KEY_7), ModAlt}] = ActionSwitchTabIndex7 | |
| 205 | + | s.Global[KeyBinding{uint(gdk.KEY_8), ModAlt}] = ActionSwitchTabIndex8 | |
| 206 | + | s.Global[KeyBinding{uint(gdk.KEY_9), ModAlt}] = ActionSwitchTabIndex9 | |
| 207 | + | s.Global[KeyBinding{uint(gdk.KEY_0), ModAlt}] = ActionSwitchTabIndex10 | |
| 208 | + | ||
| 209 | + | // Alt+Tab style switching (switch to last active tab) | |
| 210 | + | s.Global[KeyBinding{uint(gdk.KEY_Tab), ModAlt}] = ActionSwitchLastTab | |
| 211 | + | s.Global[KeyBinding{uint(gdk.KEY_Tab), ModAlt | ModShift}] = ActionSwitchLastTab | |
| 185 | 212 | } | |
| 186 | 213 | ||
| 187 | 214 | // buildTabModeShortcuts populates tab mode shortcuts from config. | |
| @@ -44,11 +44,22 @@ const ( | |||||
| 44 | ActionExitMode Action = "exit_mode" | 44 | ActionExitMode Action = "exit_mode" | ||
| 45 | 45 | ||||
| 46 | // Tab actions (global and modal) | 46 | // Tab actions (global and modal) | ||
| 47 | - | ActionNewTab Action = "new_tab" | 47 | + | ActionNewTab Action = "new_tab" |
| 48 | - | ActionCloseTab Action = "close_tab" | 48 | + | ActionCloseTab Action = "close_tab" |
| 49 | - | ActionNextTab Action = "next_tab" | 49 | + | ActionNextTab Action = "next_tab" |
| 50 | - | ActionPreviousTab Action = "previous_tab" | 50 | + | ActionPreviousTab Action = "previous_tab" |
| 51 | - | ActionRenameTab Action = "rename_tab" | 51 | + | ActionRenameTab Action = "rename_tab" |
| 52 | + | ActionSwitchLastTab Action = "switch_last_tab" // Alt+Tab style switching | |||
| 53 | + | ActionSwitchTabIndex1 Action = "switch_tab_1" | |||
| 54 | + | ActionSwitchTabIndex2 Action = "switch_tab_2" | |||
| 55 | + | ActionSwitchTabIndex3 Action = "switch_tab_3" | |||
| 56 | + | ActionSwitchTabIndex4 Action = "switch_tab_4" | |||
| 57 | + | ActionSwitchTabIndex5 Action = "switch_tab_5" | |||
| 58 | + | ActionSwitchTabIndex6 Action = "switch_tab_6" | |||
| 59 | + | ActionSwitchTabIndex7 Action = "switch_tab_7" | |||
| 60 | + | ActionSwitchTabIndex8 Action = "switch_tab_8" | |||
| 61 | + | ActionSwitchTabIndex9 Action = "switch_tab_9" | |||
| 62 | + | ActionSwitchTabIndex10 Action = "switch_tab_10" | |||
| 52 | 63 | ||||
| 53 | // Pane actions (modal) | 64 | // Pane actions (modal) | ||
| 54 | ActionSplitRight Action = "split_right" | 65 | ActionSplitRight Action = "split_right" | ||
| @@ -182,6 +193,22 @@ func (s *ShortcutSet) buildGlobalShortcuts(ctx context.Context, cfg *config.Work | |||||
| 182 | // Also support Alt+Arrow keys for pane navigation (except Left/Right used for back/forward) | 193 | // Also support Alt+Arrow keys for pane navigation (except Left/Right used for back/forward) | ||
| 183 | s.Global[KeyBinding{uint(gdk.KEY_Up), ModAlt}] = ActionFocusUp | 194 | s.Global[KeyBinding{uint(gdk.KEY_Up), ModAlt}] = ActionFocusUp | ||
| 184 | s.Global[KeyBinding{uint(gdk.KEY_Down), ModAlt}] = ActionFocusDown | 195 | s.Global[KeyBinding{uint(gdk.KEY_Down), ModAlt}] = ActionFocusDown | ||
| 196 | + | ||||
| 197 | + | // Direct tab switching (Alt+1-9, Alt+0 for tab 10) | |||
| 198 | + | s.Global[KeyBinding{uint(gdk.KEY_1), ModAlt}] = ActionSwitchTabIndex1 | |||
| 199 | + | s.Global[KeyBinding{uint(gdk.KEY_2), ModAlt}] = ActionSwitchTabIndex2 | |||
| 200 | + | s.Global[KeyBinding{uint(gdk.KEY_3), ModAlt}] = ActionSwitchTabIndex3 | |||
| 201 | + | s.Global[KeyBinding{uint(gdk.KEY_4), ModAlt}] = ActionSwitchTabIndex4 | |||
| 202 | + | s.Global[KeyBinding{uint(gdk.KEY_5), ModAlt}] = ActionSwitchTabIndex5 | |||
| 203 | + | s.Global[KeyBinding{uint(gdk.KEY_6), ModAlt}] = ActionSwitchTabIndex6 | |||
| 204 | + | s.Global[KeyBinding{uint(gdk.KEY_7), ModAlt}] = ActionSwitchTabIndex7 | |||
| 205 | + | s.Global[KeyBinding{uint(gdk.KEY_8), ModAlt}] = ActionSwitchTabIndex8 | |||
| 206 | + | s.Global[KeyBinding{uint(gdk.KEY_9), ModAlt}] = ActionSwitchTabIndex9 | |||
| 207 | + | s.Global[KeyBinding{uint(gdk.KEY_0), ModAlt}] = ActionSwitchTabIndex10 | |||
| 208 | + | ||||
| 209 | + | // Alt+Tab style switching (switch to last active tab) | |||
| 210 | + | s.Global[KeyBinding{uint(gdk.KEY_Tab), ModAlt}] = ActionSwitchLastTab | |||
| 211 | + | s.Global[KeyBinding{uint(gdk.KEY_Tab), ModAlt | ModShift}] = ActionSwitchLastTab | |||
| 185 | } | 212 | } | ||
| 186 | 213 | ||||
| 187 | // buildTabModeShortcuts populates tab mode shortcuts from config. | 214 | // buildTabModeShortcuts populates tab mode shortcuts from config. | ||