typr 1.1.2 → 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 +70 -0
- data/README.md +9 -9
- data/example/grid +4 -4
- data/example/text +7 -4
- data/example/top500.csv +1 -0
- data/lib/browser.rb +104 -74
- data/lib/curses.rb +48 -77
- data/lib/frontend.rb +10 -20
- data/lib/graphical.rb +39 -92
- data/lib/grid.rb +76 -29
- data/lib/line.rb +57 -27
- data/lib/space.rb +14 -5
- data/lib/stack.rb +18 -19
- data/lib/terminal.rb +249 -169
- data/lib/text.rb +118 -14
- 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,3 +1,73 @@
|
|
|
1
|
+
# Changelog
|
|
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
|
+
|
|
36
|
+
## [v1.1.4] — 2026-07-28
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
- `Browser#toggle_hidden` — show/hide dotfiles with `H` key
|
|
40
|
+
- `Browser#help` keybinding (`?h`)
|
|
41
|
+
- `Terminal.on_resize` — trap SIGWINCH for terminal resize
|
|
42
|
+
- `Stack#draw_hints` clears hint area before redrawing
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
- `Space` boundary methods: per-boundary nil defaults (`left: 0`, `top: 0`, `right: left + @max`, `bottom: rows + headspace`) instead of a single clamped formula
|
|
46
|
+
- `Space#initialize` no longer sets `@left`/`@top` defaults
|
|
47
|
+
- `Browser#initialize` defaults `@left`/`@top` to 0
|
|
48
|
+
- `Browser#cd` filters dotfiles when `@show_hidden` is false
|
|
49
|
+
- `Browser#reset_view` re-calls `cd` to refresh directory listing
|
|
50
|
+
- `Stack#pick` no longer resets `@hints_start` on each call
|
|
51
|
+
- Removed unused `@totals` from Grid, `@highlight` from Stack
|
|
52
|
+
- Removed shebangs and `frozen_string_literal` comments from library files
|
|
53
|
+
- Terminal: graceful non-TTY handling for non-interactive environments
|
|
54
|
+
- Curses and graphical frontends: cleaned up dead code and whitespace
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
- `Space` specs check method returns instead of instance variables
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## [v1.1.3] — 2026-07-27
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
- `Stack#draw_hints` compared a relative offset against an absolute position, drawing one extra hint below the data
|
|
69
|
+
|
|
70
|
+
---
|
|
1
71
|
|
|
2
72
|
## [v1.1.0] — 2026-07-26
|
|
3
73
|
|
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/example/top500.csv
CHANGED
data/lib/browser.rb
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# coding: utf-8
|
|
3
|
-
# frozen_string_literal: true
|
|
4
|
-
|
|
5
1
|
require 'etc'
|
|
6
2
|
require 'find'
|
|
7
3
|
require_relative 'grid.rb'
|
|
@@ -37,12 +33,12 @@ module Typr
|
|
|
37
33
|
# Escape | quit, returns nil
|
|
38
34
|
|
|
39
35
|
class Browser < Grid
|
|
40
|
-
attr_accessor :directory, :user, :pwd, :view
|
|
36
|
+
attr_accessor :directory, :user, :pwd, :view
|
|
41
37
|
attr_accessor :mimetype_db, :mimetype_magic
|
|
42
38
|
|
|
43
39
|
## Height minus 2 rows reserved for path bar and command line.
|
|
44
40
|
|
|
45
|
-
def bottom; super -
|
|
41
|
+
def bottom; super - 1 end
|
|
46
42
|
|
|
47
43
|
## Top offset plus one row for the path info bar.
|
|
48
44
|
|
|
@@ -63,20 +59,17 @@ module Typr
|
|
|
63
59
|
data, missing = [],{}
|
|
64
60
|
unless append
|
|
65
61
|
@colors[:fields] = {}
|
|
66
|
-
dir = @dir_history[dir+1] if dir.is_a? Integer
|
|
67
62
|
clear
|
|
68
63
|
Dir.chdir( @directory = File.expand_path( dir ) )
|
|
69
|
-
unless [?/, ENV["HOME"]].include? dir
|
|
70
|
-
@dir_history.unshift @directory
|
|
71
|
-
@dir_history.uniq!
|
|
72
|
-
end
|
|
73
64
|
end
|
|
74
|
-
data = Dir.new(@directory).children.
|
|
65
|
+
data = Dir.new(@directory).children.reject{|f|
|
|
66
|
+
!@show_hidden and f[0] == ?. }.map.with_index do |file,id|
|
|
75
67
|
if File.symlink? file
|
|
76
68
|
target = File.readlink( file ) rescue $!.to_s
|
|
77
|
-
next [file, target
|
|
69
|
+
next [file, target, "????", false, "?", "?", 0, 0, 0, 0, "inode", "symlink"] unless File.exist?(file)
|
|
78
70
|
end
|
|
79
|
-
stat = File.stat(file)
|
|
71
|
+
stat = File.stat(file) rescue nil
|
|
72
|
+
next [file, "", "????", false, "?", "?", 0, 0, 0, 0, "?", "?"] unless stat
|
|
80
73
|
type = stat.ftype.to_sym
|
|
81
74
|
|
|
82
75
|
mime = { "directory": "inode/directory",
|
|
@@ -91,17 +84,23 @@ module Typr
|
|
|
91
84
|
( missing[file] = id ; mime = "?/?" ) unless mime
|
|
92
85
|
[ file, target, stat.mode.to_s(8)[-3..-1].chars.map{|d|
|
|
93
86
|
"rwx".chars.map.with_index{|c,i| d.to_i[2-i]==0 ? ?- : c } }.join,
|
|
94
|
-
stat.uid, stat.gid, stat.size,
|
|
87
|
+
File.executable?(file), stat.uid, stat.gid, stat.size,
|
|
95
88
|
stat.atime.to_i, stat.mtime.to_i, stat.ctime.to_i,
|
|
96
89
|
*mime.to_s.split( ?/ ) ]
|
|
97
90
|
end
|
|
98
91
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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]) }
|
|
105
104
|
self << data
|
|
106
105
|
sort
|
|
107
106
|
filter
|
|
@@ -203,6 +202,15 @@ module Typr
|
|
|
203
202
|
|
|
204
203
|
def home; cd ?~ end
|
|
205
204
|
|
|
205
|
+
## Toggle visibility of hidden files (those starting with a dot).
|
|
206
|
+
#
|
|
207
|
+
# browser.toggle_hidden
|
|
208
|
+
|
|
209
|
+
def toggle_hidden
|
|
210
|
+
@show_hidden = !@show_hidden
|
|
211
|
+
cd @directory
|
|
212
|
+
end
|
|
213
|
+
|
|
206
214
|
## Display a help grid listing all keybindings.
|
|
207
215
|
|
|
208
216
|
def help; @user.help end
|
|
@@ -213,23 +221,26 @@ module Typr
|
|
|
213
221
|
#
|
|
214
222
|
# browser.recurse # flattens tree into leaf names
|
|
215
223
|
|
|
216
|
-
def recurse
|
|
217
|
-
sort_by NAME
|
|
218
|
-
@procs[NAME] = :filetree
|
|
219
|
-
end
|
|
220
|
-
|
|
221
224
|
## Reset display, selection, and position state then re-sort.
|
|
222
225
|
#
|
|
223
226
|
# browser.reset_view
|
|
224
227
|
|
|
225
|
-
def reset_view; reset [:display, :selection, :position]; sort end
|
|
228
|
+
def reset_view; cd @directory; reset [:display, :selection, :position]; sort end
|
|
226
229
|
|
|
227
230
|
## Open a filter popup on the chosen row/column and apply the input.
|
|
228
231
|
#
|
|
229
232
|
# browser.filter_view # prompts for filter text
|
|
230
233
|
|
|
234
|
+
def search; pick :none, 0, column: NAME end
|
|
235
|
+
|
|
231
236
|
def filter_view; add_filter *@user.ask(
|
|
232
|
-
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
|
|
233
244
|
|
|
234
245
|
## Open a sort-picker popup to choose a column to sort by.
|
|
235
246
|
#
|
|
@@ -243,19 +254,11 @@ module Typr
|
|
|
243
254
|
|
|
244
255
|
def views; switch_to( popup( type: :"[v]iews", input: @views.keys ).pick :row ) end
|
|
245
256
|
|
|
246
|
-
## Open a directory history picker and cd to the chosen entry.
|
|
247
|
-
#
|
|
248
|
-
# browser.dir_history # pick from previously visited dirs
|
|
249
|
-
|
|
250
|
-
def dir_history; cd( popup( { input: @dir_history[1..-1],
|
|
251
|
-
colors: { columns: [ colors[:dir_history] ] },
|
|
252
|
-
align: [:left], left: left } ).pick(:row) ) end
|
|
253
|
-
|
|
254
257
|
## True when the row is a directory or passes base Grid filtering.
|
|
255
258
|
#
|
|
256
259
|
# browser.check 0 # => true if navigable
|
|
257
260
|
|
|
258
|
-
def check row; super or @data[row][SUBTYPE] == "directory" end
|
|
261
|
+
def check row; super or ( !@filter_dirs and @data[row][SUBTYPE] == "directory" ) end
|
|
259
262
|
|
|
260
263
|
##
|
|
261
264
|
# Interactive file/directory picker. Blocks until a selection or Escape.
|
|
@@ -267,23 +270,26 @@ module Typr
|
|
|
267
270
|
# browser.pick 'image' # => '/path/to/photo.png' (images only)
|
|
268
271
|
# browser.pick nil # => anything, same as 'file'
|
|
269
272
|
|
|
270
|
-
def pick type='file'
|
|
273
|
+
def pick type='file', **kw
|
|
271
274
|
type ||= 'file'
|
|
272
275
|
type = type.to_s
|
|
273
|
-
return super if type[/row|column|field|none/]
|
|
276
|
+
return super(type, **kw) if type[/row|column|field|none/]
|
|
274
277
|
add_filter ( %w[audio video image text application inode].include?(
|
|
275
278
|
type) ? TYPE : SUBTYPE ), type if type != 'file'
|
|
276
279
|
loop do
|
|
277
|
-
|
|
280
|
+
show
|
|
278
281
|
draw_hints :row
|
|
279
|
-
key = Typr.
|
|
282
|
+
key = Typr.read_key
|
|
283
|
+
if key == @keymap[:exit]
|
|
284
|
+
reset :display
|
|
285
|
+
return
|
|
286
|
+
end
|
|
280
287
|
next unless choice = send( key )
|
|
281
288
|
case choice
|
|
282
|
-
when Array; choice =
|
|
283
|
-
@directory+?/+@data[id][NAME] }.join("' '") + ?'
|
|
289
|
+
when Array; choice = choice.map{ |id| @data[id][NAME] }.join('" "')
|
|
284
290
|
when Integer; file = @data[ choice ]
|
|
285
291
|
if file[SUBTYPE] == 'directory'; cd file[TARGET] || file[NAME]; next
|
|
286
|
-
else choice =
|
|
292
|
+
else choice = file[NAME] end
|
|
287
293
|
end
|
|
288
294
|
reset :display
|
|
289
295
|
return choice
|
|
@@ -294,19 +300,35 @@ module Typr
|
|
|
294
300
|
# Renders the grid with an info bar above showing the current directory
|
|
295
301
|
# path and colored status items for filter, sort, and view.
|
|
296
302
|
#
|
|
297
|
-
# browser.
|
|
298
|
-
|
|
299
|
-
def
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
@
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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(" / ")
|
|
310
332
|
end
|
|
311
333
|
|
|
312
334
|
## Returns row ids for all directories in the current listing.
|
|
@@ -364,13 +386,15 @@ module Typr
|
|
|
364
386
|
# 'x-msdownload': :red 'x-pie-executable': :red
|
|
365
387
|
|
|
366
388
|
def initialize args={}
|
|
367
|
-
@
|
|
368
|
-
@view, @sort, @views, @positions = :compact, 0, {}, {}
|
|
389
|
+
@mimetype_db, @mimetype_magic = true, true
|
|
390
|
+
@view, @sort, @views, @positions, @show_hidden = :compact, 0, {}, {}, false
|
|
391
|
+
@filter_dirs = args[:filter_dirs]
|
|
369
392
|
super
|
|
393
|
+
@left ||= 0
|
|
394
|
+
@top ||= 0
|
|
370
395
|
@directory ||= ?~
|
|
371
396
|
@colors[:actions] = { "[f]ilter": :red, "[s]ort": :blue, "[v]iews": :green
|
|
372
397
|
}.merge @colors[:actions] || {}
|
|
373
|
-
@colors[:dir_history] ||= :yellow
|
|
374
398
|
@colors[ :types ] = {
|
|
375
399
|
image: :blue,
|
|
376
400
|
audio: :green,
|
|
@@ -385,12 +409,13 @@ module Typr
|
|
|
385
409
|
}.merge( @colors[ :types ] || {} )
|
|
386
410
|
|
|
387
411
|
@keymap = { back: KEY_BACKSPACE, confirm: KEY_RETURN, help: ?h,
|
|
388
|
-
home: ?~,
|
|
412
|
+
home: ?~, views: ?v, sort_view: ?s, search: ?/,
|
|
389
413
|
mark: ?m, mark_range: ?M, mark_all: ?a, mark_invert: ?i,
|
|
390
|
-
filter_view: ?f, reset_view: ?r,
|
|
414
|
+
filter_view: ?f, reset_view: ?r,
|
|
415
|
+
toggle_hidden: ?H}.merge @keymap
|
|
391
416
|
|
|
392
|
-
self.header = %w[ name target permissions owner group size
|
|
393
|
-
modified created type subtype ]
|
|
417
|
+
self.header = %w[ name target permissions executable owner group size
|
|
418
|
+
accessed modified created type subtype ]
|
|
394
419
|
|
|
395
420
|
@format[NAME] = :max
|
|
396
421
|
@format[TYPE] = 5
|
|
@@ -404,26 +429,31 @@ module Typr
|
|
|
404
429
|
@procs[GROUP] = Proc.new{|gid| Etc.getgrgid(gid).name rescue gid }
|
|
405
430
|
|
|
406
431
|
@procs[NAME] = Proc.new{ |name, row| target = @data[row][TARGET]
|
|
407
|
-
name + ( target ? " > " + color_code(File.exist?(target) ? :green : :red) +
|
|
432
|
+
File.basename(name.to_s) + ( target ? " > " + color_code(File.exist?(target) ? :green : :red) +
|
|
408
433
|
target : "" ) }
|
|
409
434
|
|
|
410
435
|
@views = { simple: %i[ name ],
|
|
411
436
|
compact: %i[ name size type],
|
|
412
437
|
stats: %i[ name size permissions owner group modified ],
|
|
413
|
-
full: %i[ name size permissions owner group
|
|
438
|
+
full: %i[ name size permissions executable owner group
|
|
414
439
|
accessed modified created type subtype ] }.merge @views
|
|
415
440
|
@parent = self
|
|
416
|
-
@head ||= Grid.new( parent:self, left: ->{@parent.left},
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
->{@parent.
|
|
420
|
-
|
|
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"]] } )
|
|
421
451
|
|
|
422
452
|
@user ||= Line.new( parent:self, left:->{@parent.left},top:->{@parent.bottom+1},
|
|
423
|
-
bindings: %w[ [h]elp ] + @colors[:actions].map{|name,color|
|
|
453
|
+
bindings: %w[ [h]elp [/]:search ] + @colors[:actions].map{|name,color|
|
|
424
454
|
color_code( color ) + name.to_s + color_code(@colors[:default]) } +
|
|
425
|
-
%w[ [r]eset [d]irectories [m]ark [M]
|
|
426
|
-
[i]
|
|
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\ ])
|
|
427
457
|
cd @directory
|
|
428
458
|
end
|
|
429
459
|
end
|