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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 756f4c526a47e141947600b8ab1f712f5f55809709415bdc08182f7c84a02885
4
- data.tar.gz: 0ffb023fb815985b242d77c95e0b7fb314ffe1025c3eaa309b1df615ee7575bd
3
+ metadata.gz: 5a6c88b12b4be9c1fe406898172147d1d874e0353af72d211d2530ef5a48d82f
4
+ data.tar.gz: 36dd949f072aa3ab232256d4c7d7c10d6989812c97977f65f72bd25ef7d6124a
5
5
  SHA512:
6
- metadata.gz: f5bcf29384273fe9a5fbdf47474b7160485d180b0c29ddb963644a38eaf3a9695cd54d74dc7a2955fa8e25331701f3bc0295f0a8570f85a66c9b8250ef460290
7
- data.tar.gz: a05c2a49dfaf00d42510e9961ba21eb0a5075ee9f37fb2be685b212dd91a17c10e4622bbac1b958c44f5fdb385e5895f3f4d8dbbb68e8b31b949fc70252fb192
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 views and keyboard-driven interaction.
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
- ### Views
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 views
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 views work identically across frontends.
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.draw
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.draw
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.draw
142
- @prompt.draw
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 views)
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{ show " %s, sort:%s filter:%s selected:%s" %
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.draw
56
- @user.draw
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 @user.draw eval( Typr.read(:line) )
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 / [R]ows, pick [w]ord / [W]ords, [esc] quit",
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.draw
46
- @user.draw
48
+ @text.show
49
+ @user.show
47
50
  case key = Typr.read( :key )
48
51
  when KEY_ESCAPE; exit
49
- # when ?/; @text.search( @user.ask filter: :string )
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
@@ -1,3 +1,4 @@
1
+ rank,word,pos,count,quot
1
2
  1,the,a,22038615,0.98
2
3
  2,be,v,12545825,0.97
3
4
  3,and,c,10741073,0.99
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, :dir_history
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 - 2 end
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.map.with_index do |file,id|
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] + [??]*7 + ["inode","symlink"] unless File.exist?(file)
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
- `file --mime-type '#{missing.keys.join "' '"}'
100
- `.split($/).each.with_index{ |line,id|
101
- next unless line[/^.+:\s+\w+\/[\w\-\.]+$/]
102
- file, mime = line.split(/:\s*/)
103
- data[ missing[file] ][-2,2] = mime.rstrip.split( ?/ ) } if
104
- @mimetype_magic and not missing.empty?
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
- draw
280
+ show
278
281
  draw_hints :row
279
- key = Typr.read( :key )
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 = ?' + choice.map{ |id|
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 = ?'+@directory+?/+file[NAME]+?' end
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.draw # draws browser with info bar
298
-
299
- def draw; super
300
- info = { "[f]ilter": @filters.empty? ? "no filter" : @filters.to_s[1..-2],
301
- "[s]ort": sorted_by(true), "[v]iews": @view }
302
- move left, top - 1
303
- show prepare( @directory, width - info.values.join.size - 3, :left, :left, 5 )
304
- @colors[:actions].each do |type,col|
305
- color col
306
- show ' ' + info[type].to_s
307
- @positions[type] = Typr.column - (type==@positions.keys.last ? 0:1 )
308
- end
309
- @user.draw
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
- @dir_history, @mimetype_db, @mimetype_magic = [], true, true
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: ?~, root: ?`, views: ?v, sort_view: ?s, dir_history: ?d,
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, recurse: ?R}.merge @keymap
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 accessed
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}, top: ->{@parent.top-1},
417
- input: [ [->{@parent.directory}, ->{@parent.filters.empty? ? "no filter" :
418
- @parent.filters.to_s[1..-2] }, ->{@parent.sorted_by(true)},
419
- ->{@parent.view} ] ], format: [:max, :min, :min, :min],
420
- colors: { columns: [@colors[:default]] + @colors[:actions].values } )
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]ark\ range mark\ [a]ll
426
- [i]nvert\ mark [`]:root [~]: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\ ])
427
457
  cd @directory
428
458
  end
429
459
  end