Class: Legion::TTY::KeybindingManager
- Inherits:
-
Object
- Object
- Legion::TTY::KeybindingManager
- Includes:
- Logging::Helper
- Defined in:
- lib/legion/tty/keybinding_manager.rb
Overview
KeybindingManager — context-aware keybinding system with chord support and user customization.
Named contexts:
:global, :chat, :dashboard, :extensions, :config, :command_palette, :session_picker, :history
Chord sequences: two-key combos stored as “key1+key2” strings. Set @pending_chord between key presses; the second key resolves the chord action.
User overrides loaded from ~/.legionio/keybindings.json at boot.
Constant Summary collapse
- CONTEXTS =
%i[global chat dashboard extensions config command_palette session_picker history].freeze
- OVERRIDES_PATH =
File.('~/.legionio/keybindings.json')
- DEFAULT_BINDINGS =
{ ctrl_d: { contexts: %i[global chat], action: :toggle_dashboard, description: 'Toggle dashboard (Ctrl+D)' }, ctrl_k: { contexts: %i[global chat], action: :command_palette, description: 'Open command palette (Ctrl+K)' }, ctrl_s: { contexts: %i[global chat], action: :session_picker, description: 'Open session picker (Ctrl+S)' }, ctrl_l: { contexts: %i[global chat dashboard], action: :refresh, description: 'Refresh screen (Ctrl+L)' }, escape: { contexts: CONTEXTS, action: :back, description: 'Go back / dismiss overlay (Escape)' }, tab: { contexts: %i[chat], action: :autocomplete, description: 'Auto-complete (Tab)' }, ctrl_c: { contexts: CONTEXTS, action: :interrupt, description: 'Interrupt / quit (Ctrl+C)' } }.freeze
Instance Method Summary collapse
-
#bind(key, action:, contexts: [:global], description: '') ⇒ Object
Register or override a single binding.
-
#cancel_chord ⇒ Object
Cancel any in-progress chord sequence.
-
#chord_pending? ⇒ Boolean
Whether a chord is currently waiting for its second key.
-
#initialize(overrides_path: OVERRIDES_PATH) ⇒ KeybindingManager
constructor
A new instance of KeybindingManager.
-
#list ⇒ Object
All registered bindings as an array of hashes.
-
#load_defaults ⇒ Object
Reload default bindings (resets user overrides).
-
#load_user_overrides ⇒ Object
Load user overrides from ~/.legionio/keybindings.json.
-
#resolve(key, active_contexts: [:global]) ⇒ Symbol?
Resolve a key press given the currently active contexts.
-
#unbind(key) ⇒ Object
Remove a binding.
Constructor Details
#initialize(overrides_path: OVERRIDES_PATH) ⇒ KeybindingManager
Returns a new instance of KeybindingManager.
35 36 37 38 39 40 41 |
# File 'lib/legion/tty/keybinding_manager.rb', line 35 def initialize(overrides_path: OVERRIDES_PATH) @overrides_path = overrides_path @bindings = {} @pending_chord = nil load_defaults load_user_overrides end |
Instance Method Details
#bind(key, action:, contexts: [:global], description: '') ⇒ Object
Register or override a single binding.
82 83 84 |
# File 'lib/legion/tty/keybinding_manager.rb', line 82 def bind(key, action:, contexts: [:global], description: '') @bindings[key.to_s.to_sym] = { contexts: contexts, action: action, description: description } end |
#cancel_chord ⇒ Object
Cancel any in-progress chord sequence.
68 69 70 |
# File 'lib/legion/tty/keybinding_manager.rb', line 68 def cancel_chord @pending_chord = nil end |
#chord_pending? ⇒ Boolean
Whether a chord is currently waiting for its second key.
73 74 75 |
# File 'lib/legion/tty/keybinding_manager.rb', line 73 def chord_pending? !@pending_chord.nil? end |
#list ⇒ Object
All registered bindings as an array of hashes.
92 93 94 95 96 |
# File 'lib/legion/tty/keybinding_manager.rb', line 92 def list @bindings.map do |key, b| { key: key, action: b[:action], contexts: b[:contexts], description: b[:description] } end end |
#load_defaults ⇒ Object
Reload default bindings (resets user overrides).
99 100 101 102 103 104 |
# File 'lib/legion/tty/keybinding_manager.rb', line 99 def load_defaults @bindings = {} DEFAULT_BINDINGS.each do |key, binding| @bindings[key] = binding.dup end end |
#load_user_overrides ⇒ Object
Load user overrides from ~/.legionio/keybindings.json. File format: { “ctrl_d”: { “action”: “toggle_dashboard”, “contexts”: [“global”], “description”: “…” } }
108 109 110 111 112 113 114 115 |
# File 'lib/legion/tty/keybinding_manager.rb', line 108 def load_user_overrides return unless File.exist?(@overrides_path) raw = Legion::JSON.parse(File.read(@overrides_path), symbolize_names: true) raw.each { |key, cfg| apply_override(key, cfg) } rescue Legion::JSON::ParseError => e log.warn { "keybindings load failed: #{e.}" } end |
#resolve(key, active_contexts: [:global]) ⇒ Symbol?
Resolve a key press given the currently active contexts.
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/legion/tty/keybinding_manager.rb', line 48 def resolve(key, active_contexts: [:global]) key_sym = key.to_s.to_sym # Chord resolution: if a chord is pending, try to complete it if @pending_chord chord = :"#{@pending_chord}+#{key_sym}" @pending_chord = nil return action_for(chord, active_contexts) end # Check if this key starts a chord if chord_starter?(key_sym) @pending_chord = key_sym return :chord_pending end action_for(key_sym, active_contexts) end |
#unbind(key) ⇒ Object
Remove a binding.
87 88 89 |
# File 'lib/legion/tty/keybinding_manager.rb', line 87 def unbind(key) @bindings.delete(key.to_s.to_sym) end |