chamomile 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/chamomile/application.rb +141 -0
- data/lib/chamomile/commands.rb +42 -21
- data/lib/chamomile/configuration.rb +35 -0
- data/lib/chamomile/escape_parser.rb +36 -36
- data/lib/chamomile/frame.rb +287 -0
- data/lib/chamomile/input_reader.rb +1 -1
- data/lib/chamomile/key_map.rb +3 -3
- data/lib/chamomile/keymap.rb +3 -3
- data/lib/chamomile/layout/base.rb +11 -0
- data/lib/chamomile/layout/horizontal.rb +34 -0
- data/lib/chamomile/layout/list.rb +32 -0
- data/lib/chamomile/layout/panel.rb +81 -0
- data/lib/chamomile/layout/raw.rb +15 -0
- data/lib/chamomile/layout/spinner.rb +19 -0
- data/lib/chamomile/layout/status_bar.rb +19 -0
- data/lib/chamomile/layout/table.rb +38 -0
- data/lib/chamomile/layout/text.rb +29 -0
- data/lib/chamomile/layout/vertical.rb +23 -0
- data/lib/chamomile/messages.rb +28 -12
- data/lib/chamomile/model.rb +5 -1
- data/lib/chamomile/program.rb +32 -28
- data/lib/chamomile/renderer.rb +16 -5
- data/lib/chamomile/testing.rb +15 -12
- data/lib/chamomile/version.rb +1 -1
- data/lib/chamomile/view_dsl.rb +81 -0
- data/lib/chamomile.rb +22 -3
- metadata +19 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cf8690ed6eaa09698bd3a64193250f4caef5ccbda8612da9dffd1f0b5a1125e6
|
|
4
|
+
data.tar.gz: cce5da2531a8feb9984f18603c722dd522780f2f2ead5075e9d6707a99f8c56d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ae7732d2962818be4e4441a2a8dfe67b31df0db40fc3c8831e1020521080d90b6308fe235535b2b559bac361ec35e75ed88117396e5382bd2512de659bd1a9d6
|
|
7
|
+
data.tar.gz: bad50c84d2f86aca40ba4c09bd110aed17cf0c7e7e538aa391990c25b6ab5ed3177919e24843191bd4501267668bfb7a7cf38fb776039970a1d84fad1bc2defb
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chamomile
|
|
4
|
+
# Primary mixin for Chamomile applications. Combines Model + Commands
|
|
5
|
+
# and provides a declarative callback DSL for handling events.
|
|
6
|
+
#
|
|
7
|
+
# class Counter
|
|
8
|
+
# include Chamomile::Application
|
|
9
|
+
#
|
|
10
|
+
# def initialize
|
|
11
|
+
# @count = 0
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# on_key(:up, "k") { @count += 1 }
|
|
15
|
+
# on_key(:down, "j") { @count -= 1 }
|
|
16
|
+
# on_key("q") { quit }
|
|
17
|
+
#
|
|
18
|
+
# def view
|
|
19
|
+
# "Count: #{@count}"
|
|
20
|
+
# end
|
|
21
|
+
# end
|
|
22
|
+
module Application
|
|
23
|
+
# Include Model and Commands into Application itself (not the base class)
|
|
24
|
+
# so that Application's methods appear earlier in the MRO and can
|
|
25
|
+
# override Model's default NotImplementedError on update.
|
|
26
|
+
include Model
|
|
27
|
+
include Commands
|
|
28
|
+
include ViewDSL
|
|
29
|
+
|
|
30
|
+
def self.included(base)
|
|
31
|
+
base.extend(ClassMethods)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Default update that dispatches to DSL-registered handlers.
|
|
35
|
+
# If a class defines its own `update(msg)`, that takes precedence
|
|
36
|
+
# over this (standard Ruby MRO).
|
|
37
|
+
def update(msg)
|
|
38
|
+
self.class._dispatch_event(self, msg)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
module ClassMethods
|
|
42
|
+
# Register a handler for one or more key values.
|
|
43
|
+
# Keys can be symbols (:up, :down, :enter, etc.) or strings ("q", "k").
|
|
44
|
+
#
|
|
45
|
+
# on_key(:up, "k") { @count += 1 }
|
|
46
|
+
# on_key("q") { quit }
|
|
47
|
+
def on_key(*keys, &block)
|
|
48
|
+
keys.each do |key|
|
|
49
|
+
_key_handlers[key] = block
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Register a handler for terminal resize events.
|
|
54
|
+
#
|
|
55
|
+
# on_resize { |e| @width = e.width; @height = e.height }
|
|
56
|
+
def on_resize(&block)
|
|
57
|
+
_event_handlers[:resize] = block
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Register a handler for tick events.
|
|
61
|
+
#
|
|
62
|
+
# on_tick { refresh_data }
|
|
63
|
+
def on_tick(&block)
|
|
64
|
+
_event_handlers[:tick] = block
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Register a handler for mouse events.
|
|
68
|
+
#
|
|
69
|
+
# on_mouse { |e| handle_click(e) if e.press? }
|
|
70
|
+
def on_mouse(&block)
|
|
71
|
+
_event_handlers[:mouse] = block
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Register a handler for focus events.
|
|
75
|
+
#
|
|
76
|
+
# on_focus { @focused = true }
|
|
77
|
+
def on_focus(&block)
|
|
78
|
+
_event_handlers[:focus] = block
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Register a handler for blur events.
|
|
82
|
+
#
|
|
83
|
+
# on_blur { @focused = false }
|
|
84
|
+
def on_blur(&block)
|
|
85
|
+
_event_handlers[:blur] = block
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Register a handler for paste events.
|
|
89
|
+
#
|
|
90
|
+
# on_paste { |e| @clipboard = e.content }
|
|
91
|
+
def on_paste(&block)
|
|
92
|
+
_event_handlers[:paste] = block
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @api private — Key handlers hash (key_value → block)
|
|
96
|
+
def _key_handlers
|
|
97
|
+
@_key_handlers ||= {}
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# @api private — Event handlers hash (event_type → block)
|
|
101
|
+
def _event_handlers
|
|
102
|
+
@_event_handlers ||= {}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# @api private — Dispatch an event to registered handlers.
|
|
106
|
+
# Returns command (a callable) or nil.
|
|
107
|
+
#
|
|
108
|
+
# The block's return value is the command. If you need to return a command
|
|
109
|
+
# (like `quit` or `tick`), ensure it is the last expression in the block:
|
|
110
|
+
#
|
|
111
|
+
# on_key("q") { @cleaning_up = true; quit } # correct — quit is last
|
|
112
|
+
# on_key("q") { quit; @cleaning_up = true } # BUG — quit is discarded
|
|
113
|
+
#
|
|
114
|
+
def _dispatch_event(instance, msg)
|
|
115
|
+
handler = case msg
|
|
116
|
+
when KeyEvent then _key_handlers[msg.key]
|
|
117
|
+
when ResizeEvent then _event_handlers[:resize]
|
|
118
|
+
when TickEvent then _event_handlers[:tick]
|
|
119
|
+
when MouseEvent then _event_handlers[:mouse]
|
|
120
|
+
when FocusEvent then _event_handlers[:focus]
|
|
121
|
+
when BlurEvent then _event_handlers[:blur]
|
|
122
|
+
when PasteEvent then _event_handlers[:paste]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
return nil unless handler
|
|
126
|
+
|
|
127
|
+
result = instance.instance_exec(msg, &handler)
|
|
128
|
+
# Only return the result if it's a callable command (Proc/Lambda).
|
|
129
|
+
# Handler blocks often return non-command values (e.g., @count += 1 returns an Integer).
|
|
130
|
+
result.respond_to?(:call) ? result : nil
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def inherited(subclass)
|
|
134
|
+
super
|
|
135
|
+
# Copy handlers to subclass so parent handlers are inherited
|
|
136
|
+
subclass.instance_variable_set(:@_key_handlers, _key_handlers.dup)
|
|
137
|
+
subclass.instance_variable_set(:@_event_handlers, _event_handlers.dup)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
data/lib/chamomile/commands.rb
CHANGED
|
@@ -5,16 +5,30 @@ require "open3"
|
|
|
5
5
|
|
|
6
6
|
module Chamomile
|
|
7
7
|
# Internal command types intercepted by Program (not delivered to model)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
WindowTitleCommand = Data.define(:title)
|
|
9
|
+
WindowTitleCmd = WindowTitleCommand # backward compat
|
|
10
|
+
|
|
11
|
+
CursorPositionCommand = Data.define(:row, :col)
|
|
12
|
+
CursorPositionCmd = CursorPositionCommand # backward compat
|
|
13
|
+
|
|
14
|
+
CursorShapeCommand = Data.define(:shape)
|
|
15
|
+
CursorShapeCmd = CursorShapeCommand # backward compat
|
|
16
|
+
|
|
17
|
+
CursorVisibilityCommand = Data.define(:visible)
|
|
18
|
+
CursorVisibilityCmd = CursorVisibilityCommand # backward compat
|
|
19
|
+
|
|
20
|
+
ExecCommand = Data.define(:command, :args, :callback)
|
|
21
|
+
ExecCmd = ExecCommand # backward compat
|
|
22
|
+
|
|
23
|
+
PrintlnCommand = Data.define(:text)
|
|
24
|
+
PrintlnCmd = PrintlnCommand # backward compat
|
|
14
25
|
|
|
15
26
|
# Internal compound/control command types
|
|
16
|
-
|
|
17
|
-
|
|
27
|
+
CancelCommand = Data.define(:token)
|
|
28
|
+
CancelCmd = CancelCommand # backward compat
|
|
29
|
+
|
|
30
|
+
StreamCommand = Data.define(:token, :producer)
|
|
31
|
+
StreamCmd = StreamCommand # backward compat
|
|
18
32
|
|
|
19
33
|
# Typed envelope for shell command results
|
|
20
34
|
ShellResult = Data.define(:envelope, :stdout, :stderr, :status, :success)
|
|
@@ -53,7 +67,7 @@ module Chamomile
|
|
|
53
67
|
# Helper methods for creating command lambdas (quit, batch, tick, etc.).
|
|
54
68
|
module Commands
|
|
55
69
|
def quit
|
|
56
|
-
-> {
|
|
70
|
+
-> { QuitEvent.new }
|
|
57
71
|
end
|
|
58
72
|
|
|
59
73
|
def none
|
|
@@ -74,10 +88,16 @@ module Chamomile
|
|
|
74
88
|
-> { [:sequence, *valid] }
|
|
75
89
|
end
|
|
76
90
|
|
|
91
|
+
# Run commands concurrently (Ruby-idiomatic alias for batch)
|
|
92
|
+
alias parallel batch
|
|
93
|
+
|
|
94
|
+
# Run commands in order (Ruby-idiomatic alias for sequence)
|
|
95
|
+
alias serial sequence
|
|
96
|
+
|
|
77
97
|
def tick(duration, &block)
|
|
78
98
|
-> {
|
|
79
99
|
sleep(duration)
|
|
80
|
-
block ? block.call :
|
|
100
|
+
block ? block.call : TickEvent.new(time: Time.now)
|
|
81
101
|
}
|
|
82
102
|
end
|
|
83
103
|
|
|
@@ -86,7 +106,7 @@ module Chamomile
|
|
|
86
106
|
now = Time.now
|
|
87
107
|
next_tick = (now + duration) - (now.to_f % duration)
|
|
88
108
|
sleep(next_tick - Time.now)
|
|
89
|
-
block ? block.call :
|
|
109
|
+
block ? block.call : TickEvent.new(time: Time.now)
|
|
90
110
|
}
|
|
91
111
|
end
|
|
92
112
|
|
|
@@ -123,7 +143,7 @@ module Chamomile
|
|
|
123
143
|
|
|
124
144
|
# Returns a command that cancels a running token.
|
|
125
145
|
def cancel(token)
|
|
126
|
-
-> {
|
|
146
|
+
-> { CancelCommand.new(token: token) }
|
|
127
147
|
end
|
|
128
148
|
|
|
129
149
|
# A streaming command that emits multiple messages over time.
|
|
@@ -134,7 +154,7 @@ module Chamomile
|
|
|
134
154
|
cmd = -> {
|
|
135
155
|
return nil if token.cancelled?
|
|
136
156
|
|
|
137
|
-
|
|
157
|
+
StreamCommand.new(token: token, producer: block)
|
|
138
158
|
}
|
|
139
159
|
[token, cmd]
|
|
140
160
|
end
|
|
@@ -162,32 +182,32 @@ module Chamomile
|
|
|
162
182
|
end
|
|
163
183
|
|
|
164
184
|
def window_title(title)
|
|
165
|
-
-> {
|
|
185
|
+
-> { WindowTitleCommand.new(title: title) }
|
|
166
186
|
end
|
|
167
187
|
|
|
168
188
|
def cursor_position(row, col)
|
|
169
|
-
-> {
|
|
189
|
+
-> { CursorPositionCommand.new(row: row, col: col) }
|
|
170
190
|
end
|
|
171
191
|
|
|
172
192
|
def cursor_shape(shape)
|
|
173
|
-
-> {
|
|
193
|
+
-> { CursorShapeCommand.new(shape: shape) }
|
|
174
194
|
end
|
|
175
195
|
|
|
176
196
|
def show_cursor
|
|
177
|
-
-> {
|
|
197
|
+
-> { CursorVisibilityCommand.new(visible: true) }
|
|
178
198
|
end
|
|
179
199
|
|
|
180
200
|
def hide_cursor
|
|
181
|
-
-> {
|
|
201
|
+
-> { CursorVisibilityCommand.new(visible: false) }
|
|
182
202
|
end
|
|
183
203
|
|
|
184
204
|
def exec(command, *args, &callback)
|
|
185
|
-
-> {
|
|
205
|
+
-> { ExecCommand.new(command: command, args: args, callback: callback) }
|
|
186
206
|
end
|
|
187
207
|
|
|
188
208
|
# Print a line above the rendered TUI area
|
|
189
209
|
def println(text)
|
|
190
|
-
-> {
|
|
210
|
+
-> { PrintlnCommand.new(text: text) }
|
|
191
211
|
end
|
|
192
212
|
|
|
193
213
|
# Runtime mode toggles — return these as commands from update
|
|
@@ -235,7 +255,8 @@ module Chamomile
|
|
|
235
255
|
-> { RequestWindowSizeMsg.new }
|
|
236
256
|
end
|
|
237
257
|
|
|
238
|
-
module_function :quit, :none, :batch, :sequence, :
|
|
258
|
+
module_function :quit, :none, :batch, :sequence, :parallel, :serial,
|
|
259
|
+
:tick, :every, :cmd,
|
|
239
260
|
:deliver, :map, :cancellable, :cancel, :stream,
|
|
240
261
|
:shell, :timer,
|
|
241
262
|
:window_title, :cursor_position, :cursor_shape,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Chamomile
|
|
4
|
+
# Block-style configuration for Chamomile.run.
|
|
5
|
+
#
|
|
6
|
+
# Chamomile.run(MyApp.new) do |config|
|
|
7
|
+
# config.alt_screen = true
|
|
8
|
+
# config.mouse = :cell_motion
|
|
9
|
+
# config.fps = 30
|
|
10
|
+
# end
|
|
11
|
+
class Configuration
|
|
12
|
+
attr_accessor :alt_screen, :mouse, :bracketed_paste,
|
|
13
|
+
:report_focus, :fps, :catch_panics
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
@alt_screen = true
|
|
17
|
+
@mouse = :none
|
|
18
|
+
@bracketed_paste = true
|
|
19
|
+
@report_focus = false
|
|
20
|
+
@fps = 60
|
|
21
|
+
@catch_panics = true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_h
|
|
25
|
+
{
|
|
26
|
+
alt_screen: @alt_screen,
|
|
27
|
+
mouse: @mouse,
|
|
28
|
+
bracketed_paste: @bracketed_paste,
|
|
29
|
+
report_focus: @report_focus,
|
|
30
|
+
fps: @fps,
|
|
31
|
+
catch_panics: @catch_panics,
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -44,7 +44,7 @@ module Chamomile
|
|
|
44
44
|
|
|
45
45
|
@state = GROUND
|
|
46
46
|
@buf.clear
|
|
47
|
-
yield
|
|
47
|
+
yield KeyEvent.new(key: :escape, mod: [])
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
private
|
|
@@ -72,16 +72,16 @@ module Chamomile
|
|
|
72
72
|
@state = ESC_SEEN
|
|
73
73
|
@buf = +"\e"
|
|
74
74
|
when "\r", "\n"
|
|
75
|
-
yield
|
|
75
|
+
yield KeyEvent.new(key: :enter, mod: [])
|
|
76
76
|
when "\t"
|
|
77
|
-
yield
|
|
77
|
+
yield KeyEvent.new(key: :tab, mod: [])
|
|
78
78
|
when "\x7f", "\x08"
|
|
79
|
-
yield
|
|
79
|
+
yield KeyEvent.new(key: :backspace, mod: [])
|
|
80
80
|
when ->(c) { c.ord.between?(1, 26) }
|
|
81
81
|
letter = (ch.ord + 96).chr # \x01 -> 'a', \x02 -> 'b', etc.
|
|
82
|
-
yield
|
|
82
|
+
yield KeyEvent.new(key: letter, mod: [:ctrl])
|
|
83
83
|
when ->(c) { c.ord >= 32 }
|
|
84
|
-
yield
|
|
84
|
+
yield KeyEvent.new(key: ch, mod: [])
|
|
85
85
|
end
|
|
86
86
|
end
|
|
87
87
|
|
|
@@ -95,7 +95,7 @@ module Chamomile
|
|
|
95
95
|
@buf << ch
|
|
96
96
|
when "\e"
|
|
97
97
|
# Another ESC: flush previous ESC as :escape, start new ESC
|
|
98
|
-
yield
|
|
98
|
+
yield KeyEvent.new(key: :escape, mod: [])
|
|
99
99
|
@buf = +"\e"
|
|
100
100
|
# Stay in ESC_SEEN
|
|
101
101
|
else
|
|
@@ -103,16 +103,16 @@ module Chamomile
|
|
|
103
103
|
@state = GROUND
|
|
104
104
|
@buf.clear
|
|
105
105
|
if ["\x7f", "\x08"].include?(ch)
|
|
106
|
-
yield
|
|
106
|
+
yield KeyEvent.new(key: :backspace, mod: [:alt])
|
|
107
107
|
elsif ch.ord >= 32
|
|
108
|
-
yield
|
|
108
|
+
yield KeyEvent.new(key: ch, mod: [:alt])
|
|
109
109
|
elsif ["\r", "\n"].include?(ch)
|
|
110
|
-
yield
|
|
110
|
+
yield KeyEvent.new(key: :enter, mod: [:alt])
|
|
111
111
|
elsif ch == "\t"
|
|
112
|
-
yield
|
|
112
|
+
yield KeyEvent.new(key: :tab, mod: [:alt])
|
|
113
113
|
elsif ch.ord.between?(1, 26)
|
|
114
114
|
letter = (ch.ord + 96).chr
|
|
115
|
-
yield
|
|
115
|
+
yield KeyEvent.new(key: letter, mod: %i[alt ctrl])
|
|
116
116
|
end
|
|
117
117
|
end
|
|
118
118
|
end
|
|
@@ -147,18 +147,18 @@ module Chamomile
|
|
|
147
147
|
@buf.clear
|
|
148
148
|
|
|
149
149
|
case ch
|
|
150
|
-
when "P" then yield
|
|
151
|
-
when "Q" then yield
|
|
152
|
-
when "R" then yield
|
|
153
|
-
when "S" then yield
|
|
154
|
-
when "A" then yield
|
|
155
|
-
when "B" then yield
|
|
156
|
-
when "C" then yield
|
|
157
|
-
when "D" then yield
|
|
158
|
-
when "H" then yield
|
|
159
|
-
when "F" then yield
|
|
150
|
+
when "P" then yield KeyEvent.new(key: :f1, mod: [])
|
|
151
|
+
when "Q" then yield KeyEvent.new(key: :f2, mod: [])
|
|
152
|
+
when "R" then yield KeyEvent.new(key: :f3, mod: [])
|
|
153
|
+
when "S" then yield KeyEvent.new(key: :f4, mod: [])
|
|
154
|
+
when "A" then yield KeyEvent.new(key: :up, mod: [])
|
|
155
|
+
when "B" then yield KeyEvent.new(key: :down, mod: [])
|
|
156
|
+
when "C" then yield KeyEvent.new(key: :right, mod: [])
|
|
157
|
+
when "D" then yield KeyEvent.new(key: :left, mod: [])
|
|
158
|
+
when "H" then yield KeyEvent.new(key: :home, mod: [])
|
|
159
|
+
when "F" then yield KeyEvent.new(key: :end_key, mod: [])
|
|
160
160
|
else
|
|
161
|
-
yield
|
|
161
|
+
yield KeyEvent.new(key: seq, mod: [:unknown])
|
|
162
162
|
end
|
|
163
163
|
end
|
|
164
164
|
|
|
@@ -172,18 +172,18 @@ module Chamomile
|
|
|
172
172
|
|
|
173
173
|
if seq == PASTE_END
|
|
174
174
|
# Shouldn't happen in CSI (we'd be in PASTE state), but handle gracefully
|
|
175
|
-
yield
|
|
175
|
+
yield PasteEvent.new(content: @paste_buf.dup)
|
|
176
176
|
@paste_buf.clear
|
|
177
177
|
return
|
|
178
178
|
end
|
|
179
179
|
|
|
180
180
|
# Focus/Blur: \e[I (focus) and \e[O (blur)
|
|
181
181
|
if seq == "\e[I"
|
|
182
|
-
yield
|
|
182
|
+
yield FocusEvent.new
|
|
183
183
|
return
|
|
184
184
|
end
|
|
185
185
|
if seq == "\e[O"
|
|
186
|
-
yield
|
|
186
|
+
yield BlurEvent.new
|
|
187
187
|
return
|
|
188
188
|
end
|
|
189
189
|
|
|
@@ -208,11 +208,11 @@ module Chamomile
|
|
|
208
208
|
when "D" then yield key_with_modifiers(:left, params)
|
|
209
209
|
when "H" then yield key_with_modifiers(:home, params)
|
|
210
210
|
when "F" then yield key_with_modifiers(:end_key, params)
|
|
211
|
-
when "Z" then yield
|
|
211
|
+
when "Z" then yield KeyEvent.new(key: :tab, mod: [:shift]) # Shift+Tab
|
|
212
212
|
when "~"
|
|
213
213
|
dispatch_tilde(params, &)
|
|
214
214
|
else
|
|
215
|
-
yield
|
|
215
|
+
yield KeyEvent.new(key: seq, mod: [:unknown])
|
|
216
216
|
end
|
|
217
217
|
end
|
|
218
218
|
|
|
@@ -240,14 +240,14 @@ module Chamomile
|
|
|
240
240
|
@paste_buf.clear
|
|
241
241
|
return
|
|
242
242
|
when 201
|
|
243
|
-
yield
|
|
243
|
+
yield PasteEvent.new(content: @paste_buf.dup)
|
|
244
244
|
@paste_buf.clear
|
|
245
245
|
return
|
|
246
246
|
else
|
|
247
|
-
return yield
|
|
247
|
+
return yield KeyEvent.new(key: "\e[#{params.join(";")}~", mod: [:unknown])
|
|
248
248
|
end
|
|
249
249
|
|
|
250
|
-
yield
|
|
250
|
+
yield KeyEvent.new(key: key, mod: mod || [])
|
|
251
251
|
end
|
|
252
252
|
|
|
253
253
|
def dispatch_sgr_mouse(seq)
|
|
@@ -283,7 +283,7 @@ module Chamomile
|
|
|
283
283
|
action = pressed ? MOUSE_PRESS : MOUSE_RELEASE
|
|
284
284
|
end
|
|
285
285
|
|
|
286
|
-
yield
|
|
286
|
+
yield MouseEvent.new(x: cx, y: cy, button: button, action: action, mod: mod)
|
|
287
287
|
end
|
|
288
288
|
|
|
289
289
|
def paste_char(ch)
|
|
@@ -294,13 +294,13 @@ module Chamomile
|
|
|
294
294
|
content = @paste_buf[0..-(PASTE_END.length + 1)]
|
|
295
295
|
@paste_buf.clear
|
|
296
296
|
@state = GROUND
|
|
297
|
-
yield
|
|
297
|
+
yield PasteEvent.new(content: content)
|
|
298
298
|
elsif @paste_buf.bytesize > MAX_PASTE_SIZE
|
|
299
299
|
# Prevent memory exhaustion — flush what we have and reset
|
|
300
300
|
content = @paste_buf.dup
|
|
301
301
|
@paste_buf.clear
|
|
302
302
|
@state = GROUND
|
|
303
|
-
yield
|
|
303
|
+
yield PasteEvent.new(content: content)
|
|
304
304
|
end
|
|
305
305
|
end
|
|
306
306
|
|
|
@@ -316,9 +316,9 @@ module Chamomile
|
|
|
316
316
|
def key_with_modifiers(key, params)
|
|
317
317
|
if params.length >= 2
|
|
318
318
|
mod = extract_modifiers(params[1])
|
|
319
|
-
|
|
319
|
+
KeyEvent.new(key: key, mod: mod)
|
|
320
320
|
else
|
|
321
|
-
|
|
321
|
+
KeyEvent.new(key: key, mod: [])
|
|
322
322
|
end
|
|
323
323
|
end
|
|
324
324
|
|