Class: Muxr::InputHandler
- Inherits:
-
Object
- Object
- Muxr::InputHandler
- Defined in:
- lib/muxr/input_handler.rb
Overview
Translates raw keystrokes into either commands or pane input. Two top-level modes:
:normal Default. Single-key bindings (hjkl navigation, t/g/m for
layouts, c/K for create/kill, etc.) act directly without
any prefix. `i` drops into passthrough.
:passthrough Historical mode: every key is forwarded to the focused
pane unless prefixed by Ctrl-a. `Ctrl-a Esc` returns to
normal mode.
Plus the sub-states pre-existing from before modes existed:
:prefix, :command, :confirm_quit, :help, :scrollback, :selection.
One-shot sub-states (prefix, command, confirm_quit, help) return to finish. :scrollback and :selection also return to @base_mode so that exiting back from a scroll/yank lands you back in passthrough if that’s where you came from.
Constant Summary collapse
- PREFIX =
Ctrl-a
"\x01".freeze
- NORMAL_BINDINGS =
Single-key bindings in normal mode. Same actions as their Ctrl-a- prefixed counterparts in passthrough, just without the prefix. Value forms:
:symbol → @app.public_send(:symbol) [:symbol, *args] → @app.public_send(:symbol, *args) { "c" => :new_pane, "K" => :close_focused, "t" => [:set_layout, :tall], "g" => [:set_layout, :grid], "m" => [:set_layout, :monocle], "\t" => :cycle_layout, "\r" => :promote_master, "\n" => :promote_master, "h" => [:focus_direction, :left], "j" => [:focus_direction, :down], "k" => [:focus_direction, :up], "l" => [:focus_direction, :right], "a" => :focus_last, "~" => :toggle_drawer, "C" => :toggle_claude_drawer, "P" => :toggle_private_focused, "d" => :detach, "?" => :show_help, "q" => :quit_immediate, "s" => :enter_scrollback, "]" => :paste_from_buffer }.freeze
- PREFIX_BINDINGS =
{ "c" => :new_pane, "n" => :focus_next, "p" => :focus_prev, "a" => :focus_last, "K" => :close_focused, "\t" => :cycle_layout, "\r" => :promote_master, "\n" => :promote_master, "~" => :toggle_drawer, "C" => :toggle_claude_drawer, "P" => :toggle_private_focused, "d" => :detach, "?" => :show_help, "q" => :quit_immediate, "[" => :enter_scrollback, "]" => :paste_from_buffer }.freeze
- SCROLLBACK_BINDINGS =
{ "j" => :line_forward, "k" => :line_back, "\x04" => :half_forward, # Ctrl-d "\x15" => :half_back, # Ctrl-u "d" => :half_forward, "u" => :half_back, "\x06" => :full_forward, # Ctrl-f "\x02" => :full_back, # Ctrl-b "f" => :full_forward, "b" => :full_back, " " => :full_forward, "g" => :top, "G" => :bottom }.freeze
- SCROLLBACK_EXITS =
q, Esc, Ctrl-c
["q", "\e", "\x03"].freeze
- SELECTION_BINDINGS =
{ "h" => :left, "l" => :right, "j" => :down, "k" => :up, "0" => :line_start, "$" => :line_end, "^" => :line_first_nonblank, "g" => :top, "G" => :bottom, "H" => :screen_top, "M" => :screen_middle, "L" => :screen_bottom, "w" => :word_forward, "W" => :word_forward_big, "e" => :word_end, "E" => :word_end_big, # `b` is vim word-back here; the tmux-style page-back alias lives on Ctrl-b. "b" => :word_backward, "B" => :word_backward_big, "\x04" => :half_down, # Ctrl-d "\x15" => :half_up, # Ctrl-u "d" => :half_down, "u" => :half_up, "\x06" => :full_down, # Ctrl-f "\x02" => :full_up, # Ctrl-b "f" => :full_down # NOTE: space is intentionally absent here — it's a top-level toggle # for linear selection (see handle_selection_input), mirroring vim's # `v` so the right thumb has a one-key way to anchor/release. }.freeze
- SELECTION_YANK =
["\r", "\n", "y"].freeze
- SELECTION_CANCEL =
q, Esc, Ctrl-c
["q", "\e", "\x03"].freeze
- DIGIT_RE =
/\A[1-9]\z/.freeze
Instance Attribute Summary collapse
-
#base_mode ⇒ Object
readonly
Returns the value of attribute base_mode.
-
#command_buffer ⇒ Object
readonly
Returns the value of attribute command_buffer.
-
#state ⇒ Object
readonly
Returns the value of attribute state.
Instance Method Summary collapse
- #cancel ⇒ Object
- #enter_confirm_quit ⇒ Object
- #enter_help_mode ⇒ Object
-
#enter_idle_mode ⇒ Object
Exit a sub-state (scrollback, selection-yank) and resume the mode the user was in before they entered scrollback.
-
#enter_normal_mode ⇒ Object
Return to normal mode.
-
#enter_passthrough_mode ⇒ Object
Drop into passthrough — every key reaches the focused pane until the user issues Ctrl-a Esc.
- #enter_scrollback_mode ⇒ Object
- #enter_selection_mode ⇒ Object
- #feed(data) ⇒ Object
-
#initialize(app) ⇒ InputHandler
constructor
A new instance of InputHandler.
Constructor Details
#initialize(app) ⇒ InputHandler
Returns a new instance of InputHandler.
129 130 131 132 133 134 |
# File 'lib/muxr/input_handler.rb', line 129 def initialize(app) @app = app @state = :normal @base_mode = :normal @command_buffer = +"" end |
Instance Attribute Details
#base_mode ⇒ Object (readonly)
Returns the value of attribute base_mode.
127 128 129 |
# File 'lib/muxr/input_handler.rb', line 127 def base_mode @base_mode end |
#command_buffer ⇒ Object (readonly)
Returns the value of attribute command_buffer.
127 128 129 |
# File 'lib/muxr/input_handler.rb', line 127 def command_buffer @command_buffer end |
#state ⇒ Object (readonly)
Returns the value of attribute state.
127 128 129 |
# File 'lib/muxr/input_handler.rb', line 127 def state @state end |
Instance Method Details
#cancel ⇒ Object
214 215 216 217 |
# File 'lib/muxr/input_handler.rb', line 214 def cancel @state = @base_mode @command_buffer = +"" end |
#enter_confirm_quit ⇒ Object
180 181 182 |
# File 'lib/muxr/input_handler.rb', line 180 def enter_confirm_quit @state = :confirm_quit end |
#enter_help_mode ⇒ Object
176 177 178 |
# File 'lib/muxr/input_handler.rb', line 176 def enter_help_mode @state = :help end |
#enter_idle_mode ⇒ Object
Exit a sub-state (scrollback, selection-yank) and resume the mode the user was in before they entered scrollback. Preserves @base_mode so a passthrough → scrollback → exit round-trip lands back in passthrough.
210 211 212 |
# File 'lib/muxr/input_handler.rb', line 210 def enter_idle_mode @state = @base_mode end |
#enter_normal_mode ⇒ Object
Return to normal mode. Used by the ‘Ctrl-a Esc` binding from passthrough — explicitly resets @base_mode so the user genuinely leaves passthrough.
202 203 204 205 |
# File 'lib/muxr/input_handler.rb', line 202 def enter_normal_mode @state = :normal @base_mode = :normal end |
#enter_passthrough_mode ⇒ Object
Drop into passthrough — every key reaches the focused pane until the user issues Ctrl-a Esc.
194 195 196 197 |
# File 'lib/muxr/input_handler.rb', line 194 def enter_passthrough_mode @state = :passthrough @base_mode = :passthrough end |
#enter_scrollback_mode ⇒ Object
184 185 186 |
# File 'lib/muxr/input_handler.rb', line 184 def enter_scrollback_mode @state = :scrollback end |
#enter_selection_mode ⇒ Object
188 189 190 |
# File 'lib/muxr/input_handler.rb', line 188 def enter_selection_mode @state = :selection end |
#feed(data) ⇒ Object
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/muxr/input_handler.rb', line 136 def feed(data) remaining = data until remaining.empty? if @state == :passthrough # Fast path: batch everything up to the next Ctrl-a as one chunk so # a large paste doesn't turn into one PTY write per byte. PREFIX is # single-byte ASCII (\x01) and never appears mid-UTF-8. idx = remaining.index(PREFIX) if idx.nil? @app.send_to_focused(remaining) return end @app.send_to_focused(remaining[0...idx]) if idx > 0 @state = :prefix remaining = remaining[(idx + 1)..] || "" next end ch = remaining[0] remaining = remaining[1..] || "" case @state when :normal handle_normal(ch) when :help @app.dismiss_help @state = @base_mode when :confirm_quit handle_confirm_quit(ch) when :prefix handle_prefix(ch) when :command handle_command_input(ch) when :scrollback handle_scrollback_input(ch) when :selection handle_selection_input(ch) end end end |