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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: baf5925e72ba2632808c7ab3b981b00ed2a94cf1f4fbb634768c4b544251b822
4
- data.tar.gz: 5786405ffab8b20d1bc1623f070483cc6f39d519e1fdc71e8d8ebe9464a7489d
3
+ metadata.gz: 5a6c88b12b4be9c1fe406898172147d1d874e0353af72d211d2530ef5a48d82f
4
+ data.tar.gz: 36dd949f072aa3ab232256d4c7d7c10d6989812c97977f65f72bd25ef7d6124a
5
5
  SHA512:
6
- metadata.gz: b15ea70ada75c5608a69d55e1220dd93a50dda8ce0af64b44ab0f15e6a6da7d075b70159267313d8cdd1f36495a848c38c8827ce267d2f2db503d46445fb2ed1
7
- data.tar.gz: d1368cfd4af314c40ca8af3646e4e10facd508281e9e3180b79c30d17c7fd1b2466e0375826be4e86609898eb4253501c4a3540e8904f78a29eabb41146f515b
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 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/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, :dir_history
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] + [??]*7 + ["inode","symlink"] unless File.exist?(file)
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
- `file --mime-type '#{missing.keys.join "' '"}'
97
- `.split($/).each.with_index{ |line,id|
98
- next unless line[/^.+:\s+\w+\/[\w\-\.]+$/]
99
- file, mime = line.split(/:\s*/)
100
- data[ missing[file] ][-2,2] = mime.rstrip.split( ?/ ) } if
101
- @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]) }
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
- draw
280
+ show
284
281
  draw_hints :row
285
- key = Typr.read( :key )
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 = ?' + choice.map{ |id|
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 = ?'+@directory+?/+file[NAME]+?' end
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.draw # draws browser with info bar
304
-
305
- def draw; super
306
- info = { "[f]ilter": @filters.empty? ? "no filter" : @filters.to_s[1..-2],
307
- "[s]ort": sorted_by(true), "[v]iews": @view }
308
- move left, top - 1
309
- show prepare( @directory, width - info.values.join.size - 3, :left, :left, 5 )
310
- @colors[:actions].each do |type,col|
311
- color col
312
- show ' ' + info[type].to_s
313
- @positions[type] = Typr.column - (type==@positions.keys.last ? 0:1 )
314
- end
315
- @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(" / ")
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
- @dir_history, @mimetype_db, @mimetype_magic = [], true, true
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, sort_view: ?s, dir_history: ?d,
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, recurse: ?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 accessed
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}, top: ->{@parent.top-1},
426
- input: [ [->{@parent.directory}, ->{@parent.filters.empty? ? "no filter" :
427
- @parent.filters.to_s[1..-2] }, ->{@parent.sorted_by(true)},
428
- ->{@parent.view} ] ], format: [:max, :min, :min, :min],
429
- 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"]] } )
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[ [H]idden_files [r]eset [d]irectories [m]ark [M]ark_range mark_[a]ll
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
- Ncurses.echo
48
- Ncurses.curs_set 1
49
- str = Readline.readline
50
- Ncurses.curs_set 0
51
- Ncurses.noecho
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 show string; Ncurses.addstr string.to_s end
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; show characters }
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 show( str )
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
- x, y = Visuals.row, Visuals.column
85
- listen = Thread.new{
86
- loop do
87
- move x, y
88
- show line
89
- sleep 0.1
90
- end
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; show characters }
117
+ foreground c; move 0,i; draw characters }
121
118
  refresh
122
119
  exit if read(:key) == KEY_ESCAPE
123
120
  end