muxr 0.1.0 → 0.1.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 +28 -1
- data/README.md +45 -8
- data/bin/muxr +2 -2
- data/lib/muxr/application.rb +43 -3
- data/lib/muxr/input_handler.rb +11 -1
- data/lib/muxr/renderer.rb +4 -2
- data/lib/muxr/terminal.rb +131 -1
- data/lib/muxr/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: ab53a383e7781f83ff106e503047cf5952b12193d0d65e2b29631f2beda6bcf5
|
|
4
|
+
data.tar.gz: 0fcef17549ae1745d76fe530a97f12740c9556704bbe724f1b4690932c7cabc5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4440cb89e4295407dce42ca26c474c64441ff556cb285769b085e0474448132085d9bb6ad9af91699e6349a915c90fe6ebd33825d7ee68f315cafc26ccac5244
|
|
7
|
+
data.tar.gz: e17c2f092dcd76ca617bfbfd6acdf1e5475ea9fd7afe0a7458539af95eaa32b0282d499a094f19a6887b6fd228f03d470d5a8c6b9b167b9838e6989edd20ee74
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,31 @@ follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.1.2] - 2026-05-11
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Vim-style word and viewport motions in copy-mode selection cursor:
|
|
13
|
+
`w`/`W`/`e`/`E`/`b`/`B` walk word and WORD boundaries, `^` jumps to
|
|
14
|
+
the first non-blank on the line, and `H`/`M`/`L` land on the visible
|
|
15
|
+
top/middle/bottom rows. Yanking now drops straight back to the live
|
|
16
|
+
shell (matching vim's `v…y` returning to normal mode); the tmux-style
|
|
17
|
+
`b` alias for page-back is now `Ctrl-b` only.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- Honor SGR 2 (dim) so faint text actually renders faint. The emulator
|
|
21
|
+
was silently dropping the attribute, which left Claude Code's
|
|
22
|
+
suggested-prompt placeholder rendering at normal intensity. SGR 22
|
|
23
|
+
now correctly clears both bold and dim per spec.
|
|
24
|
+
|
|
25
|
+
## [0.1.1] - 2026-05-11
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- `muxr --list` now reports sessions whose server is actually running
|
|
29
|
+
(live sockets in `~/.muxr/sockets/`) instead of `~/.muxr/sessions/*.json`,
|
|
30
|
+
which only exist after an explicit `:save` and so missed every live
|
|
31
|
+
session. The saved-snapshot enumeration is still available internally
|
|
32
|
+
via `Muxr::Session.list`.
|
|
33
|
+
|
|
9
34
|
## [0.1.0] - 2026-05-11
|
|
10
35
|
|
|
11
36
|
Initial release.
|
|
@@ -42,5 +67,7 @@ Initial release.
|
|
|
42
67
|
boundaries.
|
|
43
68
|
- Renderer that composes one frame and diff-emits ANSI to STDOUT.
|
|
44
69
|
|
|
45
|
-
[Unreleased]: https://github.com/roelbondoc/muxr/compare/v0.1.
|
|
70
|
+
[Unreleased]: https://github.com/roelbondoc/muxr/compare/v0.1.2...HEAD
|
|
71
|
+
[0.1.2]: https://github.com/roelbondoc/muxr/releases/tag/v0.1.2
|
|
72
|
+
[0.1.1]: https://github.com/roelbondoc/muxr/releases/tag/v0.1.1
|
|
46
73
|
[0.1.0]: https://github.com/roelbondoc/muxr/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -20,6 +20,27 @@ the active layout decides geometry.
|
|
|
20
20
|
[default] panes:3 layout:tall focused:#1 drawer:shown muxr ^a ?
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
## Screenshots
|
|
24
|
+
|
|
25
|
+
The three built-in layouts (cycle with `C-a Tab`):
|
|
26
|
+
|
|
27
|
+
<table>
|
|
28
|
+
<tr>
|
|
29
|
+
<td align="center"><strong>tall</strong><br/>master + stacked slaves</td>
|
|
30
|
+
<td align="center"><strong>grid</strong><br/>even tiling</td>
|
|
31
|
+
<td align="center"><strong>monocle</strong><br/>focused pane fullscreen</td>
|
|
32
|
+
</tr>
|
|
33
|
+
<tr>
|
|
34
|
+
<td><img src="docs/screenshots/01-layout-tall.png" alt="tall layout"></td>
|
|
35
|
+
<td><img src="docs/screenshots/02-layout-grid.png" alt="grid layout"></td>
|
|
36
|
+
<td><img src="docs/screenshots/03-layout-monocle.png" alt="monocle layout"></td>
|
|
37
|
+
</tr>
|
|
38
|
+
</table>
|
|
39
|
+
|
|
40
|
+
The Quake-style drawer overlay (`C-a ~`):
|
|
41
|
+
|
|
42
|
+

|
|
43
|
+
|
|
23
44
|
## Install / run
|
|
24
45
|
|
|
25
46
|
```bash
|
|
@@ -70,17 +91,33 @@ the pane title gains `[scrollback N/M]`.
|
|
|
70
91
|
|-------------------------|-------------------------------------|
|
|
71
92
|
| `j` / `k` | scroll one line |
|
|
72
93
|
| `d` / `u` (or `C-d`/`C-u`) | half page |
|
|
73
|
-
| `f` /
|
|
94
|
+
| `f` / Space (or `C-f`/`C-b`) | full page |
|
|
74
95
|
| `g` / `G` | top / bottom |
|
|
75
96
|
| `q` / `Esc` / `C-c` | exit back to live view |
|
|
76
97
|
|
|
77
|
-
Press `v` inside scrollback to enter a movable-cursor selection mode
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
`
|
|
83
|
-
|
|
98
|
+
Press `v` inside scrollback to enter a movable-cursor selection mode.
|
|
99
|
+
Vim-style motions are supported:
|
|
100
|
+
|
|
101
|
+
| Keys | Action |
|
|
102
|
+
|-------------------------|-------------------------------------|
|
|
103
|
+
| `h` / `j` / `k` / `l` | left / down / up / right |
|
|
104
|
+
| `0` / `^` / `$` | line start / first non-blank / line end |
|
|
105
|
+
| `w` / `W` | next word / WORD start |
|
|
106
|
+
| `e` / `E` | next word / WORD end |
|
|
107
|
+
| `b` / `B` | previous word / WORD start |
|
|
108
|
+
| `g` / `G` | top / bottom of timeline |
|
|
109
|
+
| `H` / `M` / `L` | top / middle / bottom of viewport |
|
|
110
|
+
| `C-d`/`C-u`, `C-f`/`C-b`, Space | half / full page |
|
|
111
|
+
| `v` / `C-v` | anchor char / block selection (toggle) |
|
|
112
|
+
| `y` or Enter | yank and exit to live shell |
|
|
113
|
+
| `q` / `Esc` / `C-c` | cancel back to scrollback |
|
|
114
|
+
|
|
115
|
+
`v` and `C-v` toggle between character and block (rectangular) selection
|
|
116
|
+
— switching between the two preserves the anchor. `y` or Enter yanks the
|
|
117
|
+
selection into an internal buffer, pipes it to `pbcopy` in the background
|
|
118
|
+
(silent no-op when `pbcopy` is unavailable), and drops you straight back
|
|
119
|
+
to the live shell. `C-a ]` writes the yank buffer back into the focused
|
|
120
|
+
pane.
|
|
84
121
|
|
|
85
122
|
## Commands (typed after `C-a :`)
|
|
86
123
|
|
data/bin/muxr
CHANGED
|
@@ -13,7 +13,7 @@ if ARGV.include?("-v") || ARGV.include?("--version")
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
if ARGV.include?("-l") || ARGV.include?("--list")
|
|
16
|
-
names = Muxr::
|
|
16
|
+
names = Muxr::Application.list_active
|
|
17
17
|
puts names.join("\n") unless names.empty?
|
|
18
18
|
exit 0
|
|
19
19
|
end
|
|
@@ -26,7 +26,7 @@ if ARGV.include?("-h") || ARGV.include?("--help")
|
|
|
26
26
|
muxr attach the default session (auto-spawn if needed)
|
|
27
27
|
muxr <name> attach (or start) the named session
|
|
28
28
|
muxr -s <name> same as above
|
|
29
|
-
muxr --list list
|
|
29
|
+
muxr --list list running sessions and exit
|
|
30
30
|
muxr --version print version and exit
|
|
31
31
|
muxr --help this help
|
|
32
32
|
|
data/lib/muxr/application.rb
CHANGED
|
@@ -26,6 +26,27 @@ module Muxr
|
|
|
26
26
|
File.join(SOCKETS_DIR, "#{name}.sock")
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
# Names of sessions whose server socket is currently accepting connections.
|
|
30
|
+
# Stale sockets (file exists, no listener) are skipped but left in place;
|
|
31
|
+
# cleanup happens on the next attach attempt.
|
|
32
|
+
def self.list_active
|
|
33
|
+
return [] unless File.directory?(SOCKETS_DIR)
|
|
34
|
+
Dir.children(SOCKETS_DIR).filter_map do |entry|
|
|
35
|
+
next unless entry.end_with?(".sock")
|
|
36
|
+
path = File.join(SOCKETS_DIR, entry)
|
|
37
|
+
next unless alive_socket?(path)
|
|
38
|
+
File.basename(entry, ".sock")
|
|
39
|
+
end.sort
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.alive_socket?(path)
|
|
43
|
+
return false unless File.exist?(path)
|
|
44
|
+
UNIXSocket.new(path).close
|
|
45
|
+
true
|
|
46
|
+
rescue SystemCallError
|
|
47
|
+
false
|
|
48
|
+
end
|
|
49
|
+
|
|
29
50
|
def initialize(argv = [])
|
|
30
51
|
@argv = argv
|
|
31
52
|
@session_name = parse_session_name(argv)
|
|
@@ -281,6 +302,7 @@ module Muxr
|
|
|
281
302
|
def exit_selection(yank:)
|
|
282
303
|
target = focused_target
|
|
283
304
|
term = target&.terminal
|
|
305
|
+
yanked = false
|
|
284
306
|
if yank
|
|
285
307
|
# No anchor → no-op. User is still positioning; they can press v
|
|
286
308
|
# first, then yank. Esc/q is the way to exit from navigation.
|
|
@@ -290,10 +312,18 @@ module Muxr
|
|
|
290
312
|
@paste_buffer = text
|
|
291
313
|
spawn_pbcopy(text)
|
|
292
314
|
flash("yanked #{text.bytesize} bytes")
|
|
315
|
+
yanked = true
|
|
293
316
|
end
|
|
294
317
|
end
|
|
295
318
|
term&.clear_selection
|
|
296
|
-
|
|
319
|
+
if yanked
|
|
320
|
+
# vim-style: yanking drops you straight back to "normal" (idle),
|
|
321
|
+
# not back into scrollback navigation.
|
|
322
|
+
term&.scroll_to_bottom
|
|
323
|
+
@input.enter_idle_mode
|
|
324
|
+
else
|
|
325
|
+
@input.enter_scrollback_mode
|
|
326
|
+
end
|
|
297
327
|
@renderer.reset_frame!
|
|
298
328
|
invalidate
|
|
299
329
|
end
|
|
@@ -315,8 +345,18 @@ module Muxr
|
|
|
315
345
|
when :full_down then term.move_selection_cursor_by([rows - 1, 1].max, 0)
|
|
316
346
|
when :line_start then term.selection_cursor_to_line_start
|
|
317
347
|
when :line_end then term.selection_cursor_to_line_end
|
|
318
|
-
when :
|
|
319
|
-
when :
|
|
348
|
+
when :line_first_nonblank then term.selection_cursor_to_first_non_blank
|
|
349
|
+
when :top then term.selection_cursor_to_top
|
|
350
|
+
when :bottom then term.selection_cursor_to_bottom
|
|
351
|
+
when :screen_top then term.selection_cursor_to_viewport(:top)
|
|
352
|
+
when :screen_middle then term.selection_cursor_to_viewport(:middle)
|
|
353
|
+
when :screen_bottom then term.selection_cursor_to_viewport(:bottom)
|
|
354
|
+
when :word_forward then term.selection_cursor_word_forward(big: false)
|
|
355
|
+
when :word_forward_big then term.selection_cursor_word_forward(big: true)
|
|
356
|
+
when :word_end then term.selection_cursor_word_end(big: false)
|
|
357
|
+
when :word_end_big then term.selection_cursor_word_end(big: true)
|
|
358
|
+
when :word_backward then term.selection_cursor_word_backward(big: false)
|
|
359
|
+
when :word_backward_big then term.selection_cursor_word_backward(big: true)
|
|
320
360
|
end
|
|
321
361
|
invalidate
|
|
322
362
|
end
|
data/lib/muxr/input_handler.rb
CHANGED
|
@@ -48,8 +48,19 @@ module Muxr
|
|
|
48
48
|
"k" => :up,
|
|
49
49
|
"0" => :line_start,
|
|
50
50
|
"$" => :line_end,
|
|
51
|
+
"^" => :line_first_nonblank,
|
|
51
52
|
"g" => :top,
|
|
52
53
|
"G" => :bottom,
|
|
54
|
+
"H" => :screen_top,
|
|
55
|
+
"M" => :screen_middle,
|
|
56
|
+
"L" => :screen_bottom,
|
|
57
|
+
"w" => :word_forward,
|
|
58
|
+
"W" => :word_forward_big,
|
|
59
|
+
"e" => :word_end,
|
|
60
|
+
"E" => :word_end_big,
|
|
61
|
+
# `b` is vim word-back here; the tmux-style page-back alias lives on Ctrl-b.
|
|
62
|
+
"b" => :word_backward,
|
|
63
|
+
"B" => :word_backward_big,
|
|
53
64
|
"\x04" => :half_down, # Ctrl-d
|
|
54
65
|
"\x15" => :half_up, # Ctrl-u
|
|
55
66
|
"d" => :half_down,
|
|
@@ -57,7 +68,6 @@ module Muxr
|
|
|
57
68
|
"\x06" => :full_down, # Ctrl-f
|
|
58
69
|
"\x02" => :full_up, # Ctrl-b
|
|
59
70
|
"f" => :full_down,
|
|
60
|
-
"b" => :full_up,
|
|
61
71
|
" " => :full_down
|
|
62
72
|
}.freeze
|
|
63
73
|
|
data/lib/muxr/renderer.rb
CHANGED
|
@@ -264,8 +264,9 @@ module Muxr
|
|
|
264
264
|
" C-a Tab cycle layout (tall → grid → monocle)",
|
|
265
265
|
" C-a Enter promote focused pane to master",
|
|
266
266
|
" C-a ~ toggle drawer",
|
|
267
|
-
" C-a [ enter scrollback (j/k d/u f
|
|
268
|
-
"
|
|
267
|
+
" C-a [ enter scrollback (j/k d/u f g/G C-b/C-f; v→cursor, q quits)",
|
|
268
|
+
" cursor: v select, C-v block, y yank, q cancel",
|
|
269
|
+
" motions: h/j/k/l 0/^/$ w/e/b W/E/B H/M/L g/G",
|
|
269
270
|
" C-a ] paste internal copy buffer",
|
|
270
271
|
" C-a d detach (server keeps running)",
|
|
271
272
|
" C-a q kill session (asks y/n)",
|
|
@@ -439,6 +440,7 @@ module Muxr
|
|
|
439
440
|
parts = ["0"]
|
|
440
441
|
attrs = cell.attrs.to_i
|
|
441
442
|
parts << "1" if (attrs & Terminal::BOLD) != 0
|
|
443
|
+
parts << "2" if (attrs & Terminal::DIM) != 0
|
|
442
444
|
parts << "4" if (attrs & Terminal::UNDERLINE) != 0
|
|
443
445
|
parts << "7" if (attrs & Terminal::REVERSE) != 0
|
|
444
446
|
append_color(parts, cell.fg, true)
|
data/lib/muxr/terminal.rb
CHANGED
|
@@ -8,6 +8,7 @@ module Muxr
|
|
|
8
8
|
BOLD = 1
|
|
9
9
|
UNDERLINE = 2
|
|
10
10
|
REVERSE = 4
|
|
11
|
+
DIM = 8
|
|
11
12
|
|
|
12
13
|
SCROLLBACK_MAX = 5000
|
|
13
14
|
|
|
@@ -183,6 +184,96 @@ module Muxr
|
|
|
183
184
|
selection_cursor_to(timeline_size - 1, @cols - 1)
|
|
184
185
|
end
|
|
185
186
|
|
|
187
|
+
def selection_cursor_to_first_non_blank
|
|
188
|
+
return unless @selection_cursor
|
|
189
|
+
tr = @selection_cursor[0]
|
|
190
|
+
selection_cursor_to(tr, first_non_blank_col(tr))
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Jump to top/middle/bottom of the visible viewport (vim H/M/L), landing
|
|
194
|
+
# on the first non-blank column of the destination line.
|
|
195
|
+
def selection_cursor_to_viewport(where)
|
|
196
|
+
return unless @selection_cursor
|
|
197
|
+
vr = case where
|
|
198
|
+
when :top then 0
|
|
199
|
+
when :middle then @rows / 2
|
|
200
|
+
when :bottom then @rows - 1
|
|
201
|
+
end
|
|
202
|
+
return if vr.nil?
|
|
203
|
+
tr = timeline_row_for_visible(vr).clamp(0, timeline_size - 1)
|
|
204
|
+
selection_cursor_to(tr, first_non_blank_col(tr))
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def selection_cursor_word_forward(big: false)
|
|
208
|
+
return unless @selection_cursor
|
|
209
|
+
tr, tc = @selection_cursor
|
|
210
|
+
prev_cls = char_class_at(tr, tc, big: big)
|
|
211
|
+
loop do
|
|
212
|
+
nxt = step_forward(tr, tc)
|
|
213
|
+
break unless nxt
|
|
214
|
+
ntr, ntc = nxt
|
|
215
|
+
cur_cls = char_class_at(ntr, ntc, big: big)
|
|
216
|
+
# Row boundaries act as whitespace breaks even when the row is fully
|
|
217
|
+
# packed (no trailing pad) — visually the user sees a new line.
|
|
218
|
+
effective_prev = (ntr != tr) ? :space : prev_cls
|
|
219
|
+
if effective_prev != cur_cls && cur_cls != :space
|
|
220
|
+
selection_cursor_to(ntr, ntc)
|
|
221
|
+
return
|
|
222
|
+
end
|
|
223
|
+
tr, tc = ntr, ntc
|
|
224
|
+
prev_cls = cur_cls
|
|
225
|
+
end
|
|
226
|
+
selection_cursor_to(timeline_size - 1, @cols - 1)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def selection_cursor_word_end(big: false)
|
|
230
|
+
return unless @selection_cursor
|
|
231
|
+
tr, tc = @selection_cursor
|
|
232
|
+
pos = step_forward(tr, tc)
|
|
233
|
+
return unless pos
|
|
234
|
+
tr, tc = pos
|
|
235
|
+
while char_class_at(tr, tc, big: big) == :space
|
|
236
|
+
pos = step_forward(tr, tc)
|
|
237
|
+
break unless pos
|
|
238
|
+
tr, tc = pos
|
|
239
|
+
end
|
|
240
|
+
return if char_class_at(tr, tc, big: big) == :space
|
|
241
|
+
cls = char_class_at(tr, tc, big: big)
|
|
242
|
+
loop do
|
|
243
|
+
pos = step_forward(tr, tc)
|
|
244
|
+
if pos.nil? || pos[0] != tr || char_class_at(pos[0], pos[1], big: big) != cls
|
|
245
|
+
selection_cursor_to(tr, tc)
|
|
246
|
+
return
|
|
247
|
+
end
|
|
248
|
+
tr, tc = pos
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def selection_cursor_word_backward(big: false)
|
|
253
|
+
return unless @selection_cursor
|
|
254
|
+
tr, tc = @selection_cursor
|
|
255
|
+
pos = step_backward(tr, tc)
|
|
256
|
+
return unless pos
|
|
257
|
+
tr, tc = pos
|
|
258
|
+
while char_class_at(tr, tc, big: big) == :space
|
|
259
|
+
pos = step_backward(tr, tc)
|
|
260
|
+
unless pos
|
|
261
|
+
selection_cursor_to(tr, tc)
|
|
262
|
+
return
|
|
263
|
+
end
|
|
264
|
+
tr, tc = pos
|
|
265
|
+
end
|
|
266
|
+
cls = char_class_at(tr, tc, big: big)
|
|
267
|
+
loop do
|
|
268
|
+
pos = step_backward(tr, tc)
|
|
269
|
+
if pos.nil? || pos[0] != tr || char_class_at(pos[0], pos[1], big: big) != cls
|
|
270
|
+
selection_cursor_to(tr, tc)
|
|
271
|
+
return
|
|
272
|
+
end
|
|
273
|
+
tr, tc = pos
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
186
277
|
def clear_selection
|
|
187
278
|
return unless @selection_anchor
|
|
188
279
|
@selection_anchor = nil
|
|
@@ -594,6 +685,44 @@ module Muxr
|
|
|
594
685
|
@scrollback.size - @view_offset + r
|
|
595
686
|
end
|
|
596
687
|
|
|
688
|
+
def first_non_blank_col(tr)
|
|
689
|
+
row = timeline_row(tr)
|
|
690
|
+
return 0 unless row
|
|
691
|
+
@cols.times do |c|
|
|
692
|
+
ch = row[c]&.char
|
|
693
|
+
return c if ch && ch != " " && ch != "\t"
|
|
694
|
+
end
|
|
695
|
+
0
|
|
696
|
+
end
|
|
697
|
+
|
|
698
|
+
def char_class_at(tr, tc, big:)
|
|
699
|
+
row = timeline_row(tr)
|
|
700
|
+
classify_char(row && row[tc] && row[tc].char, big: big)
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
# vim "word" = run of \w (alnum + _); "WORD" = any run of non-whitespace.
|
|
704
|
+
def classify_char(ch, big:)
|
|
705
|
+
return :space if ch.nil? || ch == " " || ch == "\t" || ch == ""
|
|
706
|
+
return :word if big
|
|
707
|
+
ch.match?(/\A\w\z/) ? :word : :punct
|
|
708
|
+
end
|
|
709
|
+
|
|
710
|
+
def step_forward(tr, tc)
|
|
711
|
+
if tc + 1 < @cols
|
|
712
|
+
[tr, tc + 1]
|
|
713
|
+
elsif tr + 1 < timeline_size
|
|
714
|
+
[tr + 1, 0]
|
|
715
|
+
end
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
def step_backward(tr, tc)
|
|
719
|
+
if tc > 0
|
|
720
|
+
[tr, tc - 1]
|
|
721
|
+
elsif tr > 0
|
|
722
|
+
[tr - 1, @cols - 1]
|
|
723
|
+
end
|
|
724
|
+
end
|
|
725
|
+
|
|
597
726
|
def ordered_selection
|
|
598
727
|
a = @selection_anchor
|
|
599
728
|
b = @selection_cursor
|
|
@@ -719,9 +848,10 @@ module Muxr
|
|
|
719
848
|
@bg = nil
|
|
720
849
|
@attrs = 0
|
|
721
850
|
when 1 then @attrs |= BOLD
|
|
851
|
+
when 2 then @attrs |= DIM
|
|
722
852
|
when 4 then @attrs |= UNDERLINE
|
|
723
853
|
when 7 then @attrs |= REVERSE
|
|
724
|
-
when 22 then @attrs &= ~BOLD
|
|
854
|
+
when 22 then @attrs &= ~(BOLD | DIM)
|
|
725
855
|
when 24 then @attrs &= ~UNDERLINE
|
|
726
856
|
when 27 then @attrs &= ~REVERSE
|
|
727
857
|
when 30..37 then @fg = p - 30
|
data/lib/muxr/version.rb
CHANGED