legion-tty 0.5.1 → 0.5.2
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/CHANGELOG.md +8 -0
- data/lib/legion/tty/app.rb +40 -3
- data/lib/legion/tty/components/input_bar.rb +10 -0
- data/lib/legion/tty/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7e484905e47a82bedca53b308ff66880c8b64bcb320d9af6a1130fe4ad50a056
|
|
4
|
+
data.tar.gz: f5c49491e1227c5984e99f161662eef56a5d5924903666aa2ea9dcaecccf098f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f2eb92406a0f61f109213eec5858122038e270052cdca8c9c4f3bfcfba7d64d0ead54a1f40b53dd356aa08dc172f920a09f47c46dd108c15a708e2a680ffe474
|
|
7
|
+
data.tar.gz: e107d25bbb2ce75ce886329f8ef02f2fb5991278c5a4d140cc809d940adf34aba9f14a9c4269309be847ae78c78d48ee4d33540d03bfc71712d4b4261ae7a080
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.5.2] - 2026-04-20
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **Bracketed paste support** — `App#run_loop` now enables bracketed paste mode (`\e[?2004h`) on startup and disables (`\e[?2004l`) on shutdown; `read_csi_sequence` detects `\e[200~` / `\e[201~` paste markers and buffers the full pasted text into a `{ paste: text }` event (closes #22)
|
|
7
|
+
- **InputBar paste handling** — `handle_key` recognizes paste Hash events and inserts the full pasted text at the cursor position, replacing newlines with spaces, without triggering submit (closes #22)
|
|
8
|
+
- **Non-symbol key guard** — `dispatch_key` checks `key.is_a?(Symbol)` before the scroll-event include check, preventing `NoMethodError` on Hash paste events (closes #22)
|
|
9
|
+
- **`normalize_key` paste passthrough** — Hash events (paste) pass through without KEY_MAP lookup (closes #22)
|
|
10
|
+
|
|
3
11
|
## [0.5.1] - 2026-04-18
|
|
4
12
|
|
|
5
13
|
### Fixed
|
data/lib/legion/tty/app.rb
CHANGED
|
@@ -40,6 +40,8 @@ module Legion
|
|
|
40
40
|
DISABLE_ALT_SCREEN = "\e[?1049l"
|
|
41
41
|
ENABLE_MOUSE = "\e[?1000h\e[?1006h"
|
|
42
42
|
DISABLE_MOUSE = "\e[?1000h\e[?1006l"
|
|
43
|
+
ENABLE_BRACKETED_PASTE = "\e[?2004h"
|
|
44
|
+
DISABLE_BRACKETED_PASTE = "\e[?2004l"
|
|
43
45
|
SGR_MOUSE_RE = /\A\e\[<(\d+);(\d+);(\d+)([Mm])\z/
|
|
44
46
|
|
|
45
47
|
attr_reader :config, :credentials, :screen_manager, :hotkeys, :llm_chat, :input_bar
|
|
@@ -158,7 +160,7 @@ module Legion
|
|
|
158
160
|
|
|
159
161
|
# --- Event Loop ---
|
|
160
162
|
|
|
161
|
-
# rubocop:disable Metrics/AbcSize
|
|
163
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
162
164
|
def run_loop
|
|
163
165
|
require 'io/console'
|
|
164
166
|
|
|
@@ -166,6 +168,7 @@ module Legion
|
|
|
166
168
|
@raw_mode = true
|
|
167
169
|
$stdout.print ENABLE_ALT_SCREEN
|
|
168
170
|
$stdout.print ENABLE_MOUSE
|
|
171
|
+
$stdout.print ENABLE_BRACKETED_PASTE
|
|
169
172
|
$stdout.print cursor.hide
|
|
170
173
|
$stdout.print cursor.clear_screen
|
|
171
174
|
|
|
@@ -178,25 +181,38 @@ module Legion
|
|
|
178
181
|
|
|
179
182
|
key = normalize_key(raw_key)
|
|
180
183
|
dispatch_key(key)
|
|
184
|
+
drain_burst_keys(raw_in)
|
|
181
185
|
end
|
|
182
186
|
end
|
|
183
187
|
rescue Interrupt
|
|
184
188
|
nil
|
|
185
189
|
ensure
|
|
186
190
|
@raw_mode = false
|
|
191
|
+
$stdout.print DISABLE_BRACKETED_PASTE
|
|
187
192
|
$stdout.print DISABLE_MOUSE
|
|
188
193
|
$stdout.print cursor.show
|
|
189
194
|
$stdout.print DISABLE_ALT_SCREEN
|
|
190
195
|
shutdown
|
|
191
196
|
end
|
|
192
|
-
# rubocop:enable Metrics/AbcSize
|
|
197
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
193
198
|
|
|
194
199
|
def needs_refresh?
|
|
195
200
|
active = @screen_manager.active_screen
|
|
196
201
|
active.respond_to?(:streaming?) && active.streaming?
|
|
197
202
|
end
|
|
198
203
|
|
|
204
|
+
def drain_burst_keys(raw_in)
|
|
205
|
+
while @running && raw_in.wait_readable(0)
|
|
206
|
+
burst_key = read_raw_key(raw_in, timeout: 0)
|
|
207
|
+
break unless burst_key
|
|
208
|
+
|
|
209
|
+
dispatch_key(normalize_key(burst_key))
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
199
213
|
def normalize_key(raw)
|
|
214
|
+
return raw if raw.is_a?(Hash)
|
|
215
|
+
|
|
200
216
|
mouse = parse_sgr_mouse(raw)
|
|
201
217
|
return mouse if mouse
|
|
202
218
|
|
|
@@ -205,6 +221,7 @@ module Legion
|
|
|
205
221
|
|
|
206
222
|
# --- Key Dispatch ---
|
|
207
223
|
|
|
224
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
208
225
|
def dispatch_key(key)
|
|
209
226
|
if key == :ctrl_c
|
|
210
227
|
@running = false
|
|
@@ -225,7 +242,7 @@ module Legion
|
|
|
225
242
|
active = @screen_manager.active_screen
|
|
226
243
|
return unless active
|
|
227
244
|
|
|
228
|
-
if %i[scroll_up scroll_down].include?(key)
|
|
245
|
+
if key.is_a?(Symbol) && %i[scroll_up scroll_down].include?(key)
|
|
229
246
|
dispatch_to_screen(active, key)
|
|
230
247
|
return
|
|
231
248
|
end
|
|
@@ -236,6 +253,7 @@ module Legion
|
|
|
236
253
|
dispatch_to_screen(active, key)
|
|
237
254
|
end
|
|
238
255
|
end
|
|
256
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
239
257
|
|
|
240
258
|
def dispatch_to_input_screen(screen, key)
|
|
241
259
|
result = @input_bar.handle_key(key)
|
|
@@ -311,9 +329,28 @@ module Legion
|
|
|
311
329
|
seq << c
|
|
312
330
|
break if c.ord.between?(0x40, 0x7E)
|
|
313
331
|
end
|
|
332
|
+
|
|
333
|
+
return read_paste_content(io) if seq == "\e[200~"
|
|
334
|
+
|
|
314
335
|
seq
|
|
315
336
|
end
|
|
316
337
|
|
|
338
|
+
def read_paste_content(io)
|
|
339
|
+
buf = +''
|
|
340
|
+
end_marker = "\e[201~"
|
|
341
|
+
loop do
|
|
342
|
+
break unless io.wait_readable(1)
|
|
343
|
+
|
|
344
|
+
c = io.getc
|
|
345
|
+
break unless c
|
|
346
|
+
|
|
347
|
+
buf << c
|
|
348
|
+
break if buf.end_with?(end_marker)
|
|
349
|
+
end
|
|
350
|
+
buf.delete_suffix!(end_marker)
|
|
351
|
+
{ paste: buf }
|
|
352
|
+
end
|
|
353
|
+
|
|
317
354
|
def parse_sgr_mouse(raw)
|
|
318
355
|
match = SGR_MOUSE_RE.match(raw)
|
|
319
356
|
return nil unless match
|
|
@@ -32,6 +32,8 @@ module Legion
|
|
|
32
32
|
|
|
33
33
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
34
34
|
def handle_key(key)
|
|
35
|
+
return handle_paste(key[:paste]) if key.is_a?(Hash) && key.key?(:paste)
|
|
36
|
+
|
|
35
37
|
case key
|
|
36
38
|
when :enter
|
|
37
39
|
submit_line
|
|
@@ -202,6 +204,14 @@ module Legion
|
|
|
202
204
|
:handled
|
|
203
205
|
end
|
|
204
206
|
|
|
207
|
+
def handle_paste(text)
|
|
208
|
+
sanitized = text.to_s.gsub(/\r\n?/, "\n").gsub("\n", ' ')
|
|
209
|
+
@buffer.insert(@cursor_pos, sanitized)
|
|
210
|
+
@cursor_pos += sanitized.length
|
|
211
|
+
@tab_matches = []
|
|
212
|
+
:handled
|
|
213
|
+
end
|
|
214
|
+
|
|
205
215
|
def insert_char(key)
|
|
206
216
|
return :pass unless key.is_a?(String) && key.length == 1 && key.ord >= 32
|
|
207
217
|
|
data/lib/legion/tty/version.rb
CHANGED