typr 1.1.4 → 1.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/CHANGELOG.md +33 -0
- data/README.md +9 -9
- data/example/grid +4 -4
- data/example/text +7 -4
- data/lib/browser.rb +87 -66
- data/lib/curses.rb +11 -7
- data/lib/frontend.rb +7 -3
- data/lib/graphical.rb +10 -13
- data/lib/grid.rb +69 -14
- data/lib/line.rb +57 -27
- data/lib/space.rb +14 -5
- data/lib/stack.rb +9 -9
- data/lib/terminal.rb +185 -20
- data/lib/text.rb +122 -7
- data/lib/typr.rb +5 -6
- data/share/mimetypes +1298 -1
- data/typr.gemspec +2 -2
- metadata +2 -7
- data/Gemfile +0 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5a6c88b12b4be9c1fe406898172147d1d874e0353af72d211d2530ef5a48d82f
|
|
4
|
+
data.tar.gz: 36dd949f072aa3ab232256d4c7d7c10d6989812c97977f65f72bd25ef7d6124a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 87a3993d064a814f0c819a5466c4245029d38aeabd3b6b869a9d5f2f505b60daa45aecea6447dac70cee577605e8ec1d66ee5ce858751d1122614905a9983610
|
|
7
|
+
data.tar.gz: f29f2787e8cc2b606bfb215adebe21b1384bcc860b986318bfcff334105ebea72f30844124083dc6471c81a215698dbd3aa2fcfca9b63d92594c09ab89373bdf
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v1.2.0] — 2026-08-02
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `Typr.read_key` / `Typr.read_line` — the public terminal input API: `read_key` reads a raw key; `read_line` is the shared interactive line editor that renders a prompt + query at any position with arrow keys, ctrl-arrow word jumps, Home/End/Delete/Backspace, Escape aborts (`nil`), Enter confirms, and an optional block runs after each keypress
|
|
7
|
+
- `Line#ask` strict question types — `Array` (pick from an object/column), `:key`, `:line`; the question and answer are rendered in `@colors[:question]`/`@colors[:answer]`
|
|
8
|
+
- `Line#align` attribute (`:left`/`:right`) for text alignment in draw
|
|
9
|
+
- `dark_*` named colors (`dark_red`, `dark_green`, `dark_yellow`, `dark_blue`, `dark_magenta`, `dark_cyan`) and a `COLOR_MAP` of popular named colors in `color_code`
|
|
10
|
+
- `Text#search` / `Text#search_next` / `Text#search_prev` (`/`, `n`, `p`) — less-style live search with smart-case matching, String/Regexp queries, and wrap-around; `Text#highlight` / `Text#highlight_clear` per-line match highlighting (`@colors[:search]`)
|
|
11
|
+
- `Browser#search` — `/` binding routing through `pick :none, column: NAME`
|
|
12
|
+
- `Browser#cd` data rows include an `executable` column (`Typr::EXECUTABLE`, after `permissions`), shown in the `:full` view
|
|
13
|
+
- `Browser#pick` returns raw absolute paths (double-quoted, joined with a space for multi-select)
|
|
14
|
+
- `Space` symbolizes string keys in the `colors:` hash, so string-keyed config (as loaded from YAML) is honored
|
|
15
|
+
- Full documentation comments for the `Typr` module and `Line#ask`
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- Renamed `Typr.read` → `Typr.read_key` and `Typr.edit` → `Typr.read_line`
|
|
19
|
+
- Renamed the frontend `show` → `draw` and the widget render `draw` → `show`
|
|
20
|
+
- `Line#ask` dispatch is strict — only `Array`, `:key`, and `:line` question types; `:string`/`:integer` are gone
|
|
21
|
+
- Removed the Readline dependency
|
|
22
|
+
- `Typr.init` duplicates the default color array instead of aliasing the caller's
|
|
23
|
+
|
|
24
|
+
### Removed
|
|
25
|
+
- Alphabet hinting from `Grid#pick` and `Stack` — hints are numbers only
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- `Grid#pick column:` live-filter/search — prompt drawn at `self.bottom + 1`, backspace broadens the result set, Escape restores the pre-search state, PageUp/PageDown/Home/End scroll, TAB cycles hints, `:none` pick works
|
|
29
|
+
- `Stack#send` key normalization — only printable ASCII normalized so control keys match keymap entries
|
|
30
|
+
- `Browser#cd` MIME lookup handles filenames with `:`, `'`, `(`, and full-width UTF-8 (argv invocation, greedy regex parsing)
|
|
31
|
+
- `Typr.read_line` renders colored prompts without shifting the cursor (`Typr.text_width` is escape-aware)
|
|
32
|
+
- `Grid#sort` re-applies active filters after sorting
|
|
33
|
+
- `Typr.height`/`width` consistency so `bottom: -1` stays on screen
|
|
34
|
+
- Suppressed constant-redefinition warnings from `Grid#header=`
|
|
35
|
+
|
|
3
36
|
## [v1.1.4] — 2026-07-28
|
|
4
37
|
|
|
5
38
|
### Added
|
data/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# typr — Terminal UI Toolkit
|
|
2
2
|
|
|
3
|
-
typr is a Ruby library for building interactive terminal-based user interfaces. It provides data-object based
|
|
3
|
+
typr is a Ruby library for building interactive terminal-based user interfaces. It provides data-object based spaces and keyboard-driven interaction.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
###
|
|
7
|
+
### Spaces
|
|
8
8
|
|
|
9
9
|
- **Grid** — sortable, filterable table with formatted columns, colors, and row selection
|
|
10
10
|
- **Text** — scrollable text viewer with word/line picking, search, and borders
|
|
11
11
|
- **Browser** — file-system directory browser with MIME type detection
|
|
12
|
-
- **Stack, Space** — layout containers for composing
|
|
12
|
+
- **Stack, Space** — layout containers for composing spaces
|
|
13
13
|
- **Line** — interactive prompt/input with colored questions and answers
|
|
14
14
|
|
|
15
15
|
### Frontends
|
|
@@ -19,7 +19,7 @@ typr supports two rendering backends, selectable via `require`:
|
|
|
19
19
|
- **Terminal** (`require 'typr'`) — raw terminal IO, no dependencies beyond Ruby stdlib
|
|
20
20
|
- **Graphical** (`require 'typr/graphical'`) — SDL2-based, for desktop GUI windows
|
|
21
21
|
|
|
22
|
-
All
|
|
22
|
+
All spaces work identically across frontends.
|
|
23
23
|
|
|
24
24
|
### Keyboard interaction
|
|
25
25
|
|
|
@@ -58,7 +58,7 @@ Typr.init
|
|
|
58
58
|
)
|
|
59
59
|
|
|
60
60
|
Typr.clear
|
|
61
|
-
@grid.
|
|
61
|
+
@grid.show
|
|
62
62
|
key = Typr.read(:key) # reads a single keypress
|
|
63
63
|
@grid.reset # reset grid to default view
|
|
64
64
|
Typr.exit
|
|
@@ -81,7 +81,7 @@ Typr.init
|
|
|
81
81
|
|
|
82
82
|
Typr.clear
|
|
83
83
|
loop do
|
|
84
|
-
@text.
|
|
84
|
+
@text.show
|
|
85
85
|
key = Typr.read(:key)
|
|
86
86
|
case key
|
|
87
87
|
when ?q then break
|
|
@@ -138,8 +138,8 @@ class MyApp
|
|
|
138
138
|
def run
|
|
139
139
|
Typr.clear
|
|
140
140
|
loop do
|
|
141
|
-
@grid.
|
|
142
|
-
@prompt.
|
|
141
|
+
@grid.show
|
|
142
|
+
@prompt.show
|
|
143
143
|
key = Typr.read(:key)
|
|
144
144
|
case key
|
|
145
145
|
when ?s then @grid.sort_by(@prompt.ask("sort column": [@grid, :column]))
|
|
@@ -173,7 +173,7 @@ Reads input from the terminal. Types:
|
|
|
173
173
|
- `:key` — a single keypress (returns `KEY_ESCAPE`, `?q`, etc.)
|
|
174
174
|
- `:line` — a full line of text input
|
|
175
175
|
|
|
176
|
-
### Layout properties (all
|
|
176
|
+
### Layout properties (all spaces)
|
|
177
177
|
- `top`, `bottom`, `left`, `right` — placement. Integers for absolute offsets, floats (0-1) for relative sizing. Negative values position from the opposite edge. (`bottom: -1` = last row)
|
|
178
178
|
|
|
179
179
|
### Grid options
|
data/example/grid
CHANGED
|
@@ -43,7 +43,7 @@ class GridTest
|
|
|
43
43
|
@user = Line.new( top: -1, default:
|
|
44
44
|
"(s)ort, (f)ilter, (r)eset, select (R)ows/(C)olumns/(F)ields, [esc] quit")
|
|
45
45
|
@head = Line.new( interval: 1,
|
|
46
|
-
default: proc{
|
|
46
|
+
default: proc{ draw " %s, sort:%s filter:%s selected:%s" %
|
|
47
47
|
[ Time.now.strftime("%D %T"),
|
|
48
48
|
(@grid.header[@grid.sorted_by.abs] if @grid.sorted_by),
|
|
49
49
|
@grid.filters.join, @grid.selected ||= [] ] } )
|
|
@@ -52,14 +52,14 @@ class GridTest
|
|
|
52
52
|
def run
|
|
53
53
|
Typr.clear
|
|
54
54
|
loop do
|
|
55
|
-
@grid.
|
|
56
|
-
@user.
|
|
55
|
+
@grid.show
|
|
56
|
+
@user.show
|
|
57
57
|
@head.start
|
|
58
58
|
key = Typr.read( :key )
|
|
59
59
|
@head.stop
|
|
60
60
|
case key
|
|
61
61
|
when KEY_ESCAPE; exit
|
|
62
|
-
when ?` then
|
|
62
|
+
when ?` then (line = Typr.read(:line)) and @user.show(eval(line))
|
|
63
63
|
when ?s then @grid.sort_by( @user.ask "sort column": [@grid, :column] )
|
|
64
64
|
when ?F then @user.ask("pick fields": [@grid, :fields])
|
|
65
65
|
when ?R then @user.ask("pick rows": [@grid, :rows])
|
data/example/text
CHANGED
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
# word-wrapped scrollable viewport with a border.
|
|
10
10
|
#
|
|
11
11
|
# Keys:
|
|
12
|
+
# / Search (type pattern, Enter to run)
|
|
13
|
+
# n Next match
|
|
14
|
+
# p Previous match
|
|
12
15
|
# arrows Scroll up/down
|
|
13
16
|
# w Pick a single word (hint-driven)
|
|
14
17
|
# W Pick multiple words
|
|
@@ -35,18 +38,18 @@ class TextTest
|
|
|
35
38
|
colors: { header: [:yellow, :grey30], hints: [:black, :white] }
|
|
36
39
|
)
|
|
37
40
|
@user = Line.new( top: -1,
|
|
38
|
-
default: "pick [r]ow
|
|
41
|
+
default: "[/]:search [n]ext [p]rev, pick [r]ow/[R]ows, [w]ord/[W]ords, [esc] quit",
|
|
39
42
|
colors: { question: :blue, answer: :yellow } )
|
|
40
43
|
end
|
|
41
44
|
|
|
42
45
|
def run
|
|
43
46
|
Typr.clear
|
|
44
47
|
loop do
|
|
45
|
-
@text.
|
|
46
|
-
@user.
|
|
48
|
+
@text.show
|
|
49
|
+
@user.show
|
|
47
50
|
case key = Typr.read( :key )
|
|
48
51
|
when KEY_ESCAPE; exit
|
|
49
|
-
|
|
52
|
+
when ?/; @text.search
|
|
50
53
|
when ?w; @user.ask( 'pick word': [@text, :field] )
|
|
51
54
|
when ?W; @user.ask( 'pick words': [@text, :fields] )
|
|
52
55
|
when ?r; @user.ask( 'pick rows': [@text, :row] )
|
data/lib/browser.rb
CHANGED
|
@@ -33,7 +33,7 @@ module Typr
|
|
|
33
33
|
# Escape | quit, returns nil
|
|
34
34
|
|
|
35
35
|
class Browser < Grid
|
|
36
|
-
attr_accessor :directory, :user, :pwd, :view
|
|
36
|
+
attr_accessor :directory, :user, :pwd, :view
|
|
37
37
|
attr_accessor :mimetype_db, :mimetype_magic
|
|
38
38
|
|
|
39
39
|
## Height minus 2 rows reserved for path bar and command line.
|
|
@@ -59,21 +59,17 @@ module Typr
|
|
|
59
59
|
data, missing = [],{}
|
|
60
60
|
unless append
|
|
61
61
|
@colors[:fields] = {}
|
|
62
|
-
dir = @dir_history[dir+1] if dir.is_a? Integer
|
|
63
62
|
clear
|
|
64
63
|
Dir.chdir( @directory = File.expand_path( dir ) )
|
|
65
|
-
unless [?/, ENV["HOME"]].include? dir
|
|
66
|
-
@dir_history.unshift @directory
|
|
67
|
-
@dir_history.uniq!
|
|
68
|
-
end
|
|
69
64
|
end
|
|
70
65
|
data = Dir.new(@directory).children.reject{|f|
|
|
71
66
|
!@show_hidden and f[0] == ?. }.map.with_index do |file,id|
|
|
72
67
|
if File.symlink? file
|
|
73
68
|
target = File.readlink( file ) rescue $!.to_s
|
|
74
|
-
next [file, target
|
|
69
|
+
next [file, target, "????", false, "?", "?", 0, 0, 0, 0, "inode", "symlink"] unless File.exist?(file)
|
|
75
70
|
end
|
|
76
|
-
stat = File.stat(file)
|
|
71
|
+
stat = File.stat(file) rescue nil
|
|
72
|
+
next [file, "", "????", false, "?", "?", 0, 0, 0, 0, "?", "?"] unless stat
|
|
77
73
|
type = stat.ftype.to_sym
|
|
78
74
|
|
|
79
75
|
mime = { "directory": "inode/directory",
|
|
@@ -88,17 +84,23 @@ module Typr
|
|
|
88
84
|
( missing[file] = id ; mime = "?/?" ) unless mime
|
|
89
85
|
[ file, target, stat.mode.to_s(8)[-3..-1].chars.map{|d|
|
|
90
86
|
"rwx".chars.map.with_index{|c,i| d.to_i[2-i]==0 ? ?- : c } }.join,
|
|
91
|
-
stat.uid, stat.gid, stat.size,
|
|
87
|
+
File.executable?(file), stat.uid, stat.gid, stat.size,
|
|
92
88
|
stat.atime.to_i, stat.mtime.to_i, stat.ctime.to_i,
|
|
93
89
|
*mime.to_s.split( ?/ ) ]
|
|
94
90
|
end
|
|
95
91
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
92
|
+
if @mimetype_magic and not missing.empty?
|
|
93
|
+
begin
|
|
94
|
+
out = IO.popen(['file', '--mime-type', *missing.keys]){ |io| io.read }
|
|
95
|
+
rescue Errno::ENOENT
|
|
96
|
+
out = ''
|
|
97
|
+
end
|
|
98
|
+
out.split($/).each{ |line|
|
|
99
|
+
file, mime = line.match(/^(.*):\s+(\w+\/[\w\-\.]+)$/)&.captures
|
|
100
|
+
next unless file
|
|
101
|
+
data[ missing[file] ][-2,2] = mime.split( ?/ ) if missing[file] }
|
|
102
|
+
end
|
|
103
|
+
data.each { |row| row[0] = File.join(@directory, row[0]) }
|
|
102
104
|
self << data
|
|
103
105
|
sort
|
|
104
106
|
filter
|
|
@@ -219,11 +221,6 @@ module Typr
|
|
|
219
221
|
#
|
|
220
222
|
# browser.recurse # flattens tree into leaf names
|
|
221
223
|
|
|
222
|
-
def recurse
|
|
223
|
-
sort_by NAME
|
|
224
|
-
@procs[NAME] = :filetree
|
|
225
|
-
end
|
|
226
|
-
|
|
227
224
|
## Reset display, selection, and position state then re-sort.
|
|
228
225
|
#
|
|
229
226
|
# browser.reset_view
|
|
@@ -234,8 +231,16 @@ module Typr
|
|
|
234
231
|
#
|
|
235
232
|
# browser.filter_view # prompts for filter text
|
|
236
233
|
|
|
234
|
+
def search; pick :none, 0, column: NAME end
|
|
235
|
+
|
|
237
236
|
def filter_view; add_filter *@user.ask(
|
|
238
|
-
filter: [ popup( type: :"[f]ilter" ), :row], with: :line ) end
|
|
237
|
+
filter: [ popup( type: :"[f]ilter" ), :row, NAME], with: :line ) end
|
|
238
|
+
|
|
239
|
+
def search
|
|
240
|
+
@suppress_user = true
|
|
241
|
+
pick :none, column: NAME
|
|
242
|
+
@suppress_user = false
|
|
243
|
+
end
|
|
239
244
|
|
|
240
245
|
## Open a sort-picker popup to choose a column to sort by.
|
|
241
246
|
#
|
|
@@ -249,19 +254,11 @@ module Typr
|
|
|
249
254
|
|
|
250
255
|
def views; switch_to( popup( type: :"[v]iews", input: @views.keys ).pick :row ) end
|
|
251
256
|
|
|
252
|
-
## Open a directory history picker and cd to the chosen entry.
|
|
253
|
-
#
|
|
254
|
-
# browser.dir_history # pick from previously visited dirs
|
|
255
|
-
|
|
256
|
-
def dir_history; cd( popup( { input: @dir_history[1..-1],
|
|
257
|
-
colors: { columns: [ colors[:dir_history] ] },
|
|
258
|
-
align: [:left], left: left } ).pick(:row) ) end
|
|
259
|
-
|
|
260
257
|
## True when the row is a directory or passes base Grid filtering.
|
|
261
258
|
#
|
|
262
259
|
# browser.check 0 # => true if navigable
|
|
263
260
|
|
|
264
|
-
def check row; super or @data[row][SUBTYPE] == "directory" end
|
|
261
|
+
def check row; super or ( !@filter_dirs and @data[row][SUBTYPE] == "directory" ) end
|
|
265
262
|
|
|
266
263
|
##
|
|
267
264
|
# Interactive file/directory picker. Blocks until a selection or Escape.
|
|
@@ -273,23 +270,26 @@ module Typr
|
|
|
273
270
|
# browser.pick 'image' # => '/path/to/photo.png' (images only)
|
|
274
271
|
# browser.pick nil # => anything, same as 'file'
|
|
275
272
|
|
|
276
|
-
def pick type='file'
|
|
273
|
+
def pick type='file', **kw
|
|
277
274
|
type ||= 'file'
|
|
278
275
|
type = type.to_s
|
|
279
|
-
return super if type[/row|column|field|none/]
|
|
276
|
+
return super(type, **kw) if type[/row|column|field|none/]
|
|
280
277
|
add_filter ( %w[audio video image text application inode].include?(
|
|
281
278
|
type) ? TYPE : SUBTYPE ), type if type != 'file'
|
|
282
279
|
loop do
|
|
283
|
-
|
|
280
|
+
show
|
|
284
281
|
draw_hints :row
|
|
285
|
-
key = Typr.
|
|
282
|
+
key = Typr.read_key
|
|
283
|
+
if key == @keymap[:exit]
|
|
284
|
+
reset :display
|
|
285
|
+
return
|
|
286
|
+
end
|
|
286
287
|
next unless choice = send( key )
|
|
287
288
|
case choice
|
|
288
|
-
when Array; choice =
|
|
289
|
-
@directory+?/+@data[id][NAME] }.join("' '") + ?'
|
|
289
|
+
when Array; choice = choice.map{ |id| @data[id][NAME] }.join('" "')
|
|
290
290
|
when Integer; file = @data[ choice ]
|
|
291
291
|
if file[SUBTYPE] == 'directory'; cd file[TARGET] || file[NAME]; next
|
|
292
|
-
else choice =
|
|
292
|
+
else choice = file[NAME] end
|
|
293
293
|
end
|
|
294
294
|
reset :display
|
|
295
295
|
return choice
|
|
@@ -300,19 +300,35 @@ module Typr
|
|
|
300
300
|
# Renders the grid with an info bar above showing the current directory
|
|
301
301
|
# path and colored status items for filter, sort, and view.
|
|
302
302
|
#
|
|
303
|
-
# browser.
|
|
304
|
-
|
|
305
|
-
def
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
@
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
303
|
+
# browser.show # shows browser with info bar
|
|
304
|
+
|
|
305
|
+
def show; super
|
|
306
|
+
user = ENV["USER"] || Etc.getlogin
|
|
307
|
+
@head.colors[:columns][0] = user == "root" ? [:red, :black] : [:green, :black]
|
|
308
|
+
@head.reset :format
|
|
309
|
+
@head.show
|
|
310
|
+
move @head.positions(0)[1] - @head.separator.size, @parent.top - 1
|
|
311
|
+
color @colors[:default]
|
|
312
|
+
draw '@'
|
|
313
|
+
@positions = { :"[f]ilter" => 2, :"[s]ort" => 3, :"[v]iews" => 4
|
|
314
|
+
}.transform_values do |col|
|
|
315
|
+
@head.left + @head.positions(0)[col] + @head.width_for(col) - 1
|
|
316
|
+
end
|
|
317
|
+
@user.show unless @suppress_user
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
## Formats active filters as 'column: query', joined by ' / '.
|
|
321
|
+
#
|
|
322
|
+
# browser.filter_display # => "name: foo / size: big"
|
|
323
|
+
#
|
|
324
|
+
# @return [String]
|
|
325
|
+
|
|
326
|
+
def filter_display
|
|
327
|
+
return "no filter" if @filters.empty?
|
|
328
|
+
@filters.map { |col, q|
|
|
329
|
+
q = "/#{q.source}/" if q.is_a?(Regexp)
|
|
330
|
+
"#{col.is_a?(Symbol) ? col : @header[col] || col}:#{q}"
|
|
331
|
+
}.join(" / ")
|
|
316
332
|
end
|
|
317
333
|
|
|
318
334
|
## Returns row ids for all directories in the current listing.
|
|
@@ -370,15 +386,15 @@ module Typr
|
|
|
370
386
|
# 'x-msdownload': :red 'x-pie-executable': :red
|
|
371
387
|
|
|
372
388
|
def initialize args={}
|
|
373
|
-
@
|
|
389
|
+
@mimetype_db, @mimetype_magic = true, true
|
|
374
390
|
@view, @sort, @views, @positions, @show_hidden = :compact, 0, {}, {}, false
|
|
391
|
+
@filter_dirs = args[:filter_dirs]
|
|
375
392
|
super
|
|
376
393
|
@left ||= 0
|
|
377
394
|
@top ||= 0
|
|
378
395
|
@directory ||= ?~
|
|
379
396
|
@colors[:actions] = { "[f]ilter": :red, "[s]ort": :blue, "[v]iews": :green
|
|
380
397
|
}.merge @colors[:actions] || {}
|
|
381
|
-
@colors[:dir_history] ||= :yellow
|
|
382
398
|
@colors[ :types ] = {
|
|
383
399
|
image: :blue,
|
|
384
400
|
audio: :green,
|
|
@@ -393,13 +409,13 @@ module Typr
|
|
|
393
409
|
}.merge( @colors[ :types ] || {} )
|
|
394
410
|
|
|
395
411
|
@keymap = { back: KEY_BACKSPACE, confirm: KEY_RETURN, help: ?h,
|
|
396
|
-
home: ?~, views: ?v,
|
|
412
|
+
home: ?~, views: ?v, sort_view: ?s, search: ?/,
|
|
397
413
|
mark: ?m, mark_range: ?M, mark_all: ?a, mark_invert: ?i,
|
|
398
|
-
filter_view: ?f, reset_view: ?r,
|
|
414
|
+
filter_view: ?f, reset_view: ?r,
|
|
399
415
|
toggle_hidden: ?H}.merge @keymap
|
|
400
416
|
|
|
401
|
-
self.header = %w[ name target permissions owner group size
|
|
402
|
-
modified created type subtype ]
|
|
417
|
+
self.header = %w[ name target permissions executable owner group size
|
|
418
|
+
accessed modified created type subtype ]
|
|
403
419
|
|
|
404
420
|
@format[NAME] = :max
|
|
405
421
|
@format[TYPE] = 5
|
|
@@ -413,26 +429,31 @@ module Typr
|
|
|
413
429
|
@procs[GROUP] = Proc.new{|gid| Etc.getgrgid(gid).name rescue gid }
|
|
414
430
|
|
|
415
431
|
@procs[NAME] = Proc.new{ |name, row| target = @data[row][TARGET]
|
|
416
|
-
name + ( target ? " > " + color_code(File.exist?(target) ? :green : :red) +
|
|
432
|
+
File.basename(name.to_s) + ( target ? " > " + color_code(File.exist?(target) ? :green : :red) +
|
|
417
433
|
target : "" ) }
|
|
418
434
|
|
|
419
435
|
@views = { simple: %i[ name ],
|
|
420
436
|
compact: %i[ name size type],
|
|
421
437
|
stats: %i[ name size permissions owner group modified ],
|
|
422
|
-
full: %i[ name size permissions owner group
|
|
438
|
+
full: %i[ name size permissions executable owner group
|
|
423
439
|
accessed modified created type subtype ] }.merge @views
|
|
424
440
|
@parent = self
|
|
425
|
-
@head ||= Grid.new( parent:self, left: ->{@parent.left},
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
->{@parent.
|
|
429
|
-
|
|
441
|
+
@head ||= Grid.new( parent:self, left: ->{@parent.left}, margin: '',
|
|
442
|
+
right: ->{@parent.width + @parent.left - 1}, top: ->{@parent.top-1},
|
|
443
|
+
input: [ [->{ENV["USER"] || Etc.getlogin}, ->{@parent.directory},
|
|
444
|
+
->{@parent.filter_display},
|
|
445
|
+
->{ (s = @parent.sorted_by(true)) &&
|
|
446
|
+
"#{s} #{@parent.reverse ? "\u2193" : "\u2191"}"},
|
|
447
|
+
->{@parent.view} ] ], format: [:min, :max, :min, :min, :min],
|
|
448
|
+
colors: { columns: [@colors[:default], @colors[:default],
|
|
449
|
+
@colors[:actions][:"[f]ilter"], @colors[:actions][:"[s]ort"],
|
|
450
|
+
@colors[:actions][:"[v]iews"]] } )
|
|
430
451
|
|
|
431
452
|
@user ||= Line.new( parent:self, left:->{@parent.left},top:->{@parent.bottom+1},
|
|
432
|
-
bindings: %w[ [h]elp ] + @colors[:actions].map{|name,color|
|
|
453
|
+
bindings: %w[ [h]elp [/]:search ] + @colors[:actions].map{|name,color|
|
|
433
454
|
color_code( color ) + name.to_s + color_code(@colors[:default]) } +
|
|
434
|
-
%w[ [
|
|
435
|
-
[i]nvert_mark [~]:home [back]:.. [up] [down] [pageup] [pagedown] [home] [end] [esc]:quit [return]:confirm\ ])
|
|
455
|
+
%w[ [r]eset [d]irectories [m]ark [M]ark_range mark_[a]ll
|
|
456
|
+
[i]nvert_mark [~]:home [H]idden_files [back]:.. [up] [down] [pageup] [pagedown] [home] [end] [esc]:quit [return]:confirm\ ])
|
|
436
457
|
cd @directory
|
|
437
458
|
end
|
|
438
459
|
end
|
data/lib/curses.rb
CHANGED
|
@@ -44,11 +44,15 @@ eval( method % ([name]*4) )
|
|
|
44
44
|
key = key.chr if key.between? 32, 126
|
|
45
45
|
return key
|
|
46
46
|
when :line
|
|
47
|
-
|
|
48
|
-
Ncurses.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
str = ''
|
|
48
|
+
until (key = Ncurses.getch) == KEY_RETURN
|
|
49
|
+
return nil if key == KEY_ESCAPE
|
|
50
|
+
if [KEY_BACKSPACE, 127, 8].include? key
|
|
51
|
+
str.chop!
|
|
52
|
+
else
|
|
53
|
+
str << key.chr if key.between? 32, 126
|
|
54
|
+
end
|
|
55
|
+
end
|
|
52
56
|
return str
|
|
53
57
|
end
|
|
54
58
|
end
|
|
@@ -63,7 +67,7 @@ eval( method % ([name]*4) )
|
|
|
63
67
|
end
|
|
64
68
|
end
|
|
65
69
|
def change; Ncurses.color_set pair_for( $color ), 0 end
|
|
66
|
-
def
|
|
70
|
+
def draw string; Ncurses.addstr string.to_s end
|
|
67
71
|
|
|
68
72
|
def get_background; $color[1] end
|
|
69
73
|
def get_foreground; $color[0] end
|
|
@@ -113,7 +117,7 @@ if __FILE__ == $0
|
|
|
113
117
|
loop do
|
|
114
118
|
clear
|
|
115
119
|
$colors.each_with_index{ |c,i|
|
|
116
|
-
foreground c; move 0,i;
|
|
120
|
+
foreground c; move 0,i; draw characters }
|
|
117
121
|
refresh
|
|
118
122
|
exit if read(:key) == KEY_ESCAPE
|
|
119
123
|
end
|
data/lib/frontend.rb
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
require 'readline'
|
|
2
|
-
|
|
3
1
|
module Visuals
|
|
4
2
|
|
|
5
3
|
COLORS = {
|
|
@@ -11,7 +9,13 @@ module Visuals
|
|
|
11
9
|
magenta: [ 180, 100, 180],
|
|
12
10
|
cyan: [ 100, 180, 180],
|
|
13
11
|
white: [ 255, 255, 255],
|
|
14
|
-
grey: [ 158, 158, 158]
|
|
12
|
+
grey: [ 158, 158, 158],
|
|
13
|
+
dark_red: [ 90, 30, 30],
|
|
14
|
+
dark_green: [ 30, 90, 30],
|
|
15
|
+
dark_yellow: [ 90, 80, 30],
|
|
16
|
+
dark_blue: [ 30, 30, 90],
|
|
17
|
+
dark_magenta: [ 90, 30, 90],
|
|
18
|
+
dark_cyan: [ 30, 80, 80] }
|
|
15
19
|
|
|
16
20
|
COLORS.merge! 1.upto(9).map{ |i|
|
|
17
21
|
["grey#{ i*10 }".to_sym, [i*25]*3 ] }.to_h
|
data/lib/graphical.rb
CHANGED
|
@@ -57,7 +57,7 @@ module Visuals
|
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
def
|
|
60
|
+
def draw( str )
|
|
61
61
|
return unless str
|
|
62
62
|
if get_background
|
|
63
63
|
GO.draw_color = get_background
|
|
@@ -81,17 +81,14 @@ module Visuals
|
|
|
81
81
|
e = Event.poll until e.is_a? Event::KeyDown
|
|
82
82
|
return e.sym.between?(32, 126) ? e.sym.chr : e.sym
|
|
83
83
|
when :line
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
line = Readline.readline
|
|
93
|
-
listen.terminate
|
|
94
|
-
return line
|
|
84
|
+
str = ''
|
|
85
|
+
loop do
|
|
86
|
+
e = Event.poll
|
|
87
|
+
next unless e.is_a? Event::KeyDown
|
|
88
|
+
return nil if e.sym == KEY_ESCAPE
|
|
89
|
+
return str if e.sym == KEY_RETURN
|
|
90
|
+
str << e.sym.chr if e.sym.between? 32, 126
|
|
91
|
+
end
|
|
95
92
|
end
|
|
96
93
|
end
|
|
97
94
|
|
|
@@ -117,7 +114,7 @@ if __FILE__ == $0
|
|
|
117
114
|
loop do
|
|
118
115
|
clear
|
|
119
116
|
COLORS.keys.each_with_index{ |c,i|
|
|
120
|
-
foreground c; move 0,i;
|
|
117
|
+
foreground c; move 0,i; draw characters }
|
|
121
118
|
refresh
|
|
122
119
|
exit if read(:key) == KEY_ESCAPE
|
|
123
120
|
end
|