typr 1.1.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.
data/lib/space.rb ADDED
@@ -0,0 +1,85 @@
1
+ require_relative 'terminal.rb'
2
+
3
+ module Typr
4
+
5
+ ##
6
+ # Base layout class for all TUI widgets. Defines a viewport rectangle and border drawing.
7
+ #
8
+ # Boundaries accept integers (absolute), floats (0..1 fraction of terminal), Procs, or
9
+ # negative ints (offset from opposite edge: -1 = last row/column).
10
+ #
11
+ # @example
12
+ # box = Space.new(left: 0, top: 2, right: 0.5, bottom: -4, border: :round)
13
+ # box.draw
14
+
15
+ class Space
16
+ include Typr
17
+
18
+ %w[left top right bottom].each{ |name| method = <<STR
19
+ def %s_setting; @%s; end
20
+ def %s
21
+ max=Typr.#{ name[/left|right/] ? 'width' : 'height' }
22
+ if !@%s;[max, #{name=='right' ? 'left+@max' :
23
+ 'top+rows-1+headspace'}].min
24
+ elsif @%s.is_a? Proc; @%s.call
25
+ elsif @%s.is_a? Float; (max * @%s).round
26
+ elsif @%s.negative?; max+@%s+1
27
+ else @%s end
28
+ end
29
+ STR
30
+ eval( method % ([name]*11) ) }
31
+
32
+ attr_writer :left, :top, :right, :bottom
33
+ attr_accessor :margin, :colors, :interval, :borders
34
+
35
+ def width; right - left + 1 end
36
+ def height; bottom - top + 1 end
37
+
38
+ def start; @updater = Thread.new{ loop{draw; sleep @interval }} end
39
+ def stop; @updater.terminate end
40
+
41
+ def draw_border x,y,char
42
+ show move_code(x, y) + char unless not char or
43
+ x < 0 or x > Typr.width or y < 0 or y > Typr.height
44
+ end
45
+
46
+ def draw
47
+ return if @borders.empty?
48
+ color @colors[:border]
49
+ draw_border(left - 1, top - 1, @borders[:top_left])
50
+ draw_border(left, top - 1, @borders[:top] * width) if @borders[:top]
51
+ draw_border(right + 1, top - 1, @borders[:top_right])
52
+ (height+1).times do |pos|
53
+ draw_border(left - 1, top + pos, @borders[:left] )
54
+ draw_border(right + 1, top + pos, @borders[:right] )
55
+ end
56
+ draw_border(left - 1, bottom + 1, @borders[:bottom_left] )
57
+ draw_border(left, bottom + 1, @borders[:bottom] * width ) if @borders[:bottom]
58
+ draw_border(right + 1, bottom + 1, @borders[:bottom_right])
59
+ end
60
+
61
+ def border=(border)
62
+ chars = case border
63
+ when Symbol
64
+ { light: '──││┌┐└┘', heavy: '━━┃┃┏┓┗┛', double: '══║║╔╗╚╝',
65
+ round: '──││╭╮╰╯' }[border]
66
+ when String; if border.size == 1 then border * 8 else border.chars end
67
+ when nil; [nil] * 8
68
+ end
69
+ @borders = %w[top bottom left right top_left top_right bottom_left
70
+ bottom_right ].map.with_index{ |part,id| [part.to_sym, chars[id]] }.to_h if chars
71
+ end
72
+
73
+ def initialize args={}
74
+ @max, @keymap, @colors, @margin = 0, {}, {}, ' '
75
+ @left, @top, @interval, @borders = 0, 0, 1, {}
76
+ args.each{ |name, value|
77
+ instance_variable_set ?@ + name.to_s, value }
78
+ @keymap = { exit: KEY_ESCAPE }.merge @keymap
79
+ @colors = { default: [:white, :black], border: [:white, :black] }.merge(@colors)
80
+ borders = @borders.dup
81
+ self.border = @border
82
+ @borders.merge! borders
83
+ end
84
+ end
85
+ end
data/lib/stack.rb ADDED
@@ -0,0 +1,278 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+ # frozen_string_literal: true
4
+
5
+ module Typr
6
+
7
+ ##
8
+ # A vertically scrollable list of items displayed one per row or column.
9
+ #
10
+ # Provides paging, keyboard navigation, key-hint overlays, and alternating
11
+ # row colors. Subclasses Grid, Text, and Browser add domain-specific
12
+ # rendering on top of this foundation.
13
+ #
14
+ # == Key bindings (overridable via +keymap+):
15
+ #
16
+ # KEY_TAB -> cycle (advance hint numbering to the next chunk)
17
+ # KEY_RETURN -> confirm (returns currently selected items, or nil)
18
+ # KEY_ESCAPE -> exit (abort selection and return nil)
19
+ # KEY_UP / KEY_DOWN (step by one row in the given direction)
20
+ # KEY_PAGEUP / KEY_PAGEDOWN (jump by one viewport height)
21
+ # KEY_HOME / KEY_END (jump to first or last pageable row)
22
+
23
+ class Stack < Space
24
+ ## Widget configuration. +left+, +top+, +right+, +bottom+ set the viewport; see {Space}.
25
+ ## +alternate+ toggles striped rows (+:grey10+ background alternating with the default bg).
26
+
27
+ attr_accessor :start, :keymap, :selected, :header
28
+ attr_accessor :separator, :hints, :hints_start
29
+
30
+ ##
31
+ # Returns a +Range+ of internal data indices visible on the current page.
32
+ #
33
+ # stack.page #=> 0..14
34
+
35
+ def page; @start..(@start + height - 1) end
36
+
37
+ ##
38
+ # The number of rows reserved for a header bar within the widget's footprint.
39
+ #
40
+ # stack.headspace #=> 1 (if header is set)
41
+ # stack.headspace #=> 0 (if no header)
42
+
43
+ def headspace; @header ? 1 : 0 end
44
+
45
+ ##
46
+ # The usable height after subtracting any header space.
47
+ #
48
+ # stack.height #=> 14
49
+
50
+ def height; super - headspace end
51
+
52
+ ##
53
+ # Placeholder renderer used when no subclass overrides +print+ for an item id.
54
+ #
55
+ # stack.print 42 # renders a blank row
56
+
57
+ def print id; show " " * (width-@margin.size) end
58
+
59
+ ##
60
+ # Resets widget state by one or more categories.
61
+ #
62
+ # stack.reset # resets position and selection
63
+ # stack.reset :position # only scrolls back to top
64
+ # stack.reset :selection # clears all selected rows/columns/fields
65
+ # stack.reset [:display, :format]
66
+
67
+ def reset type=:all
68
+ case type
69
+ when Array; type.each{ |type| reset type }
70
+ when :position; @start = @hints_start = 0
71
+ when :selection; @selected = @selected.keys.map{ |type| [ type, [] ] }.to_h
72
+ when :all; reset [ :position, :selection ]
73
+ end
74
+ end
75
+
76
+ ##
77
+ # Renders the numeric key-hint overlay for items in the current page.
78
+ #
79
+ # stack.draw_hints # vertical hints along left margin
80
+ # stack.draw_hints :column, 2 # horizontal hints across columns of row 2
81
+
82
+ def draw_hints type=nil, row_id=0
83
+ color @colors[ :hints ]
84
+ if type.to_s[ /column/ ]
85
+ positions( row_id )[ @hints_start..-1 ].each_with_index{ |pos, idx|
86
+ move( left + pos, top + row_id )
87
+ show @hints[idx] }
88
+ else @hints.chars.each_with_index{ |char, idx|
89
+ pos = idx + @hints_start + headspace
90
+ break if pos > [height, rows-1+headspace].min
91
+ move( left, top + pos )
92
+ show char }
93
+ end
94
+ end
95
+
96
+ ##
97
+ # Renders the full widget: header bar (if present), all visible items with background
98
+ # coloring and alternating stripes, a selected-item highlight, and bottom borders.
99
+ #
100
+ # This method is called by your application's main loop each frame; it does not handle
101
+ # events itself (see {#send}).
102
+
103
+ def draw
104
+ if @header
105
+ color @colors[ :header ]
106
+ move left,top
107
+ show @margin
108
+ print :header
109
+ end
110
+ list = page.to_a
111
+ list[ height-1 ] = nil if list.length < height
112
+ for id, relative_id in list.each_with_index
113
+ move left,top + relative_id + headspace
114
+ foreground ( @colors[:rows][id] || @colors[:default][0] )
115
+ select = @selected[ :rows ].include?( id )
116
+ dark=!dark if @alternate
117
+ background ( select ? @colors[:selected] :
118
+ (@alternate and dark) ? @colors[:alternate] : @colors[:default][1] )
119
+ show @margin
120
+ print id
121
+ background if select
122
+ end
123
+ background
124
+ super
125
+ end
126
+
127
+ ##
128
+ # Dispatches a key from the terminal to its semantic action and clamps the current page
129
+ # offset.
130
+ #
131
+ # stack.send Typr::KEY_UP # scrolls up one row
132
+ # stack.send Typr::KEY_RETURN # confirms selection
133
+
134
+ def send key#, map=nil
135
+ response = eval @keymap.invert[key].to_s if @keymap.values.include? key
136
+ @start = rows - height if @start > rows - height
137
+ @start = 0 if @start < 0
138
+ return response
139
+ end
140
+
141
+ ## Scroll to the top of the dataset.
142
+ #
143
+ # stack.to_top
144
+
145
+ def to_top; @start = 0; return end
146
+
147
+ ## Scroll to the bottom pageable position (last row minus viewport height).
148
+ #
149
+ # stack.to_bottom
150
+
151
+ def to_bottom; @start = rows - height; return end
152
+
153
+ ## Step down by one row.
154
+ #
155
+ # stack.down
156
+
157
+ def down; @start += 1; return end
158
+
159
+ ## Step up by one row.
160
+ #
161
+ # stack.up
162
+
163
+ def up; @start -= 1; return end
164
+
165
+ ## Jump down by one viewport of rows.
166
+ #
167
+ # stack.page_down
168
+
169
+ def page_down; @start += height; return end
170
+
171
+ ## Jump up by one viewport of rows.
172
+ #
173
+ # stack.page_up
174
+
175
+ def page_up; @start -= height; return end
176
+
177
+ ##
178
+ # Blocks the calling thread and lets the user interactively pick items from the current
179
+ # page using hint characters or arrow keys. After selection, the user must press Return to
180
+ # confirm (or Escape to abort).
181
+ #
182
+ # The +type+ argument controls what kind of picker is shown:
183
+ #
184
+ # Symbol/String type | Purpose
185
+ # -----------------------------|-------------------------------------------
186
+ # +:row, +"file"+ | Pick a single row id
187
+ # +"rows"+ | Multi-select rows
188
+ # +:column, +"field"+ | Nested: pick a row then a column
189
+ # +:fields+ | Multi-select fields
190
+ # +:none+ | Navigation-only mode
191
+ # anything with +relative_| | Returns the internal integer index
192
+ #
193
+ # stack.pick # single row picker
194
+ # stack.pick :column, 3 # pick a column in row 3
195
+ # stack.pick "rows" # multi-select rows
196
+
197
+ def pick type = :row, row=0 #, key=nil
198
+ @hints_start = 0
199
+ type = type.to_s
200
+ multiple = type[-1] == ?s
201
+ loop do
202
+ if type['field']
203
+ row = pick( :row ) or return
204
+ column = pick( :column, page.to_a.index( row + headspace)) or return
205
+ value = [ row , column ]
206
+ else
207
+ draw
208
+ draw_hints type, row unless type['none']
209
+ limit = type['row'] ? height : positions(row).count
210
+ key = Typr.read :key
211
+ if key.is_a?(String) and value =
212
+ @hints[0..limit-@hints_start-1].index(key)
213
+ value += @hints_start
214
+ relative = value + @start if type['relative_']
215
+ if type['row'] then value = page.to_a[ value ]
216
+ elsif type['column'] and @sequence; value = @sequence[value] end
217
+ end unless type['none']
218
+ end
219
+
220
+ if value
221
+ type = type[9..-1] if type['relative_']
222
+ type += ?s unless multiple
223
+ type = type.to_sym
224
+ if @selected[type].include? value
225
+ @selected[type].delete value
226
+ else @selected[type] << value end if multiple
227
+ return relative || value unless multiple
228
+ else
229
+ case key
230
+ when @keymap[:exit]; return
231
+ when @keymap[:confirm]; return @selected[type]
232
+ else send key #if type['id']
233
+ end
234
+ end
235
+ end
236
+ end
237
+
238
+ ##
239
+ # Advance the numeric hints by one full set of hint characters so that newly-fitted
240
+ # items beyond the first viewport also get labeled numbers.
241
+ #
242
+ # stack.cycle # hints now start at index 10 (or wrap back to 0)
243
+
244
+ def cycle
245
+ @hints_start += @hints.size
246
+ @hints_start = 0 if @hints_start > [height-1, rows-1+headspace].min
247
+ return
248
+ end
249
+
250
+ ##
251
+ # Constructs a Stack widget.
252
+ #
253
+ # All positional and visual parameters from {Space#initialize} are available. Additionally:
254
+ #
255
+ # Option | Default | Description
256
+ # ----------------|---------|-------------------------------------------
257
+ # +keysyms+ | {} | Custom key-to-method mappings
258
+ # +hints+ | +"1234..."+ | Characters used in the hint overlay
259
+ # +alternate+ | true | Striped row backgrounds
260
+ # +header+ | nil | Label shown in a header bar
261
+ # +separator+ | +" "+ | String drawn between columns (used by Grid)
262
+
263
+ def initialize args={}
264
+ @keymap, @separator, @selected = {}, " ", {}
265
+ @start, @hints_start, @highlight = 0, 0, 0#A_STANDOUT
266
+ @alternate = true
267
+ super #args
268
+ @colors = { hints: [255, :black ], header: [232,:grey60 ],
269
+ selected: :grey25, alternate: :grey10, rows: {} }.merge @colors
270
+ @selected = { fields:[], columns:[], rows:[] }.merge @selected
271
+ @keymap = { cycle: KEY_TAB, confirm: KEY_RETURN, exit: KEY_ESCAPE,
272
+ page_down: KEY_PAGEDOWN, page_up: KEY_PAGEUP, up: KEY_UP,
273
+ down: KEY_DOWN, to_top: KEY_HOME, to_bottom: KEY_END }.merge @keymap
274
+ @hints ||= "1234567890qwertyuiopasdfghjklzxcvbnm-=[];'\,./"
275
+ end
276
+ end
277
+
278
+ end
data/lib/terminal.rb ADDED
@@ -0,0 +1,286 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'io/console'
4
+ require 'readline'
5
+ require 'unicode/display_width'
6
+
7
+ module Typr
8
+ # alias :show :print
9
+
10
+ INPUT = (IO.new IO.sysopen "/dev/tty", "r")
11
+ OUTPUT = (IO.new IO.sysopen "/dev/stdin", "w")
12
+ `infocmp -L1`.split.each{ |info| key,value = info[0..-2].split("=")
13
+ if value
14
+ # p key + value if key[/dis/]
15
+ # value[1] = ?e if value[0,2] == "\\E"
16
+ value.gsub! '\E', '\e'
17
+ # value = '\x' + value[/\d+/].to_i(8).to_s(16).upcase if value[/\\\d+/]
18
+ # value = "\\x" + value[/\d+/].to_i(8).to_s(16).upcase if value[/\\\d+/]
19
+ value += "\\" if value[-1] == "\\"
20
+ # eval "%s=\'%s\'" % [key.upcase,value] if key
21
+ eval "%s=\"%s\"" % [key.upcase,value] if key
22
+ #and key.start_with? "key_"
23
+ end }
24
+
25
+ KEY_ESCAPE = "\e"
26
+ KEY_RETURN = CARRIAGE_RETURN
27
+ KEY_TAB = "\t"
28
+ KEY_ENTER = CARRIAGE_RETURN
29
+ KEY_PAGEDOWN = KEY_NPAGE
30
+ KEY_PAGEUP = KEY_PPAGE
31
+ KEY_INSERT = KEY_IC
32
+ KEY_DELETE = KEY_DC
33
+
34
+ MODES = %i[ reset bold faint italic underline slow fast invert ]
35
+ COLORS = %i[ black red green yellow blue magenta cyan white ]
36
+
37
+ def prepare str, max, align=:left, side=:right, fade=false
38
+ stop = width = real_size( str )
39
+ # unless str[0..max-1].ascii_only? and width == str.size
40
+ unless str.ascii_only? and width == str.size
41
+ stop = width = 0
42
+ str.each_char do |c|
43
+ if c == ?\e
44
+ width -= str[stop..-1][/^\x1b\[[^m]+m|/].size-1
45
+ stop += 1
46
+ elsif (cwidth = (c.ascii_only? ? 1 : Unicode::DisplayWidth.of(c))) +
47
+ width >= max
48
+ break
49
+ else width += cwidth; stop += 1 end
50
+ end
51
+ end
52
+ # return
53
+ if ( space = ( max - width ) ) > 0 #.clamp 0, max
54
+ str = [ str[0..stop-1], " " * space ]
55
+ str.reverse! if align == :right
56
+ str.join
57
+ else #case align
58
+ # when :left; str = str[0..max-1]
59
+ # when :right;
60
+ str = str[side == :left ? -max..-1 : 0..max-1]
61
+ # end
62
+ # str = fade str, align, str.length / 8 + 1 if fade
63
+ str = fade str, side, fade if fade
64
+ return str
65
+ end
66
+ # return str.join
67
+ end
68
+
69
+ def fade str, side=:left, num=3
70
+ isleft = side == :left
71
+ # chars = (side == ? 0..num : (-num-1..-2).to_a.reverse)
72
+ chars = str[ isleft ? 0..num : -num..-1 ]
73
+ chars = chars.chars.map.with_index do |char,id|
74
+ id = chars.size-id unless isleft
75
+ color_code( "grey#{(80/chars.size)*id+10}".to_sym ) + char
76
+ end.join
77
+ # chars.each.with_index do |pos,id|
78
+ # str.insert pos, color_code( "grey#{(80/chars.size)*id+10}".to_sym )
79
+ # str.insert pos, color_code( "grey#{(100/id+3)*10}".to_sym )
80
+ # end
81
+ return ( isleft ? chars + color_code($color[0]) + str[num+1..-1] :
82
+ str[0..-num-1] + chars )
83
+
84
+ #+ color_code( $color[1] )
85
+ # return str
86
+ # length.times{|i|
87
+ # color [ ("grey"+((i+3)*10).to_s ).to_sym, @colors[:default][1] ]
88
+ # show @directory[-space+i] }
89
+ # color @colors[:default]
90
+ # show @directory[-space+6..-1]
91
+ end
92
+ # def printables str; str.dump.gsub(/\\e\[.+m|\"/, '') end
93
+ def printables str; str.gsub(/\x1b\[[^m]+m|/,'') end
94
+ # def real_size str; printables(str).size end
95
+ def real_size str; Unicode::DisplayWidth.of( printables(str) ) end
96
+
97
+ # $>.print "\e[%iJ" % %w[down line screen].index(mode.to_s)
98
+ def move x=0,y=0; show move_code( x, y ) end
99
+ def move_code x=0,y=0; "\e[%i;%if" % [ y+1, x+1 ] end
100
+
101
+ def mode name; show mode_code(name) end
102
+ def mode_code name; if id = MODES.index(name.to_s) then "\e[#{ id }m" end end
103
+
104
+ def color_code color, bg=false
105
+ color = color.to_sym if color.is_a? String
106
+ case color
107
+ when Symbol; #color = color.to_s
108
+ if id = COLORS.index(color); "\e[#{ id + (bg ? 40 : 30) }m"
109
+ elsif color[/^gr[ae]y\d{,2}$/]
110
+ "\e[%i;5;%im" % [bg ? 48 : 38, 232 + (color[/\d+/].to_f/100*23).to_i]
111
+ end
112
+ when Integer; "\e[%i;5;%im" % [ bg ? 48 : 38, color]
113
+ when Array; case color.count
114
+ when 3; "\e[%i;2;%i;%i;%im" % [ bg ? 48 : 38, *color ]
115
+ when 2; color_code( color[0] ) + color_code( color[1], true )
116
+ end
117
+ end
118
+ end
119
+
120
+ def get_background; $color[1] end
121
+ def get_foreground; $color[0] end
122
+ def foreground c=$default[0]; $color[0]=c; show color_code(c) end
123
+ def background c=$default[1]; $color[1]=c; show color_code(c,true) end
124
+ # def color c=$default
125
+ # c = [c] unless c.is_a? Array and c.count == 2
126
+ # show color_code(c)
127
+ # end
128
+ def color c=$default
129
+ c = [c] unless c.is_a? Array and c.count == 2
130
+ foreground c[0] if c[0]
131
+ background c[1] if c[1]
132
+ end
133
+
134
+ def show str; $>.print str end
135
+ # def enable attr; mode attr end
136
+ # def disable attr=nil; mode :reset end
137
+ # def refresh; end
138
+ def self.clear mode=:screen
139
+ $>.print( { screen: CLEAR_SCREEN, line: DELETE_LINE }[mode] )
140
+ end
141
+
142
+ def self.read obj, prompt=''
143
+ case obj
144
+ when :key then INPUT.raw{ |tty| tty.sysread 6 } rescue return
145
+ when :line then line=Readline.readline prompt; print CURSOR_INVISIBLE;line
146
+ end
147
+ end
148
+
149
+ def self.position
150
+ result = ''
151
+ $stdin.raw do |stdin|
152
+ $stdout << USER7 #"\e[6n"
153
+ $stdout.flush
154
+ until (char = stdin.getc) == 'R'
155
+ result << char if char
156
+ end
157
+ end
158
+ result[/[\d;]+/].split(?;).map &:to_i
159
+ # m = res.match /(?<row>\d+);(?<column>\d+)/
160
+ # { row: Integer(m[:row]), column: Integer(m[:column]) }
161
+ end
162
+
163
+ def self.size; IO.console.winsize end
164
+ def self.width; size.last-1 end
165
+ def self.height; size.first end
166
+ def self.row; position.first end
167
+ def self.column; position.last end
168
+ def self.init default=[ :white, :black ]
169
+ $color = $default = default
170
+ # include Typr
171
+ # extend self
172
+ # Object.undef_method :read
173
+ print CURSOR_INVISIBLE
174
+ system "tput smkx"
175
+ end
176
+ def self.exit;
177
+ # background
178
+ extend self
179
+
180
+ $>.print CURSOR_NORMAL;
181
+ $>.print ORIG_COLORS; color; clear
182
+
183
+ end
184
+ end
185
+
186
+ # include Typr
187
+ # extend Typr
188
+
189
+ if __FILE__ == $0
190
+ begin
191
+ include Typr
192
+ Typr.init #background: :blue
193
+ colors = COLORS + (1..9).map{|i| "grey#{i*10}" }
194
+ chars = (33..126).map(&:chr)
195
+ slice = chars.count / colors.count + 1
196
+ characters = chars.each_slice( slice ).map(&:join)
197
+ # key = read :key
198
+ key,fg,bg = "",150,0
199
+ Typr.clear
200
+ loop do
201
+ # move 0,0
202
+ colors.each_with_index{ |color,y|
203
+ characters.each_with_index{ |chars,x|
204
+ foreground color.to_sym;
205
+ background colors[x].to_sym;
206
+ move x*slice,y
207
+ show chars
208
+ }
209
+ }
210
+ (0...256).each{ |i| background i; show ' ' }
211
+
212
+ move 0, Typr.height-1
213
+ color [:white, :black]
214
+ # show "KEY: #{key}"
215
+ p "KEY: %s / %s" % [key, key]
216
+ move 15, Typr.height-1
217
+ color [fg,bg]
218
+ print " %s / %s " % [fg,bg] #).center 20
219
+ key = Typr.read(:key)
220
+ case key
221
+ when KEY_UP; fg+=1
222
+ when KEY_DOWN; fg-=1
223
+ when KEY_LEFT; bg-=1
224
+ when KEY_RIGHT; bg+=1
225
+ when KEY_ESCAPE; exit
226
+ end
227
+ end
228
+ ensure
229
+ Typr.exit
230
+ end
231
+ end
232
+
233
+
234
+ # KEY_ESC = 27
235
+ # KEY_TAB =
236
+ # KEY_ESC = 27
237
+ # A_STANDOUT = :invert
238
+ # BG = :black
239
+ # `infocmp -L`.read.join.split(', ').each{ |info| }
240
+ # .split('=').to_h
241
+
242
+ # KEY_PAGEDOWN = "\e[6~"
243
+ # KEY_PAGEUP = "\e[5~"
244
+ # KEY_LEFT = "\e[D"
245
+ # KEY_RIGHT = "\e[C"
246
+ # KEY_UP = "\e[A"
247
+ # KEY_DOWN = "\e[B"
248
+ # KEY_HOME = "\e[H" #xterm
249
+ # KEY_END = "\e[F" #xterm
250
+ # INSERT = "\e[2~" #xterm
251
+ # DELETE = "\e[3~" #xterm
252
+ # KEY_BACKSPACE = "\b" #xterm
253
+
254
+ # case ENV["TERM"]
255
+ # when
256
+ # DELETE = "\e[P" #st
257
+ # INSERT = "\e[4h" #st
258
+ # BACKSPACE = "\x7F" #st
259
+ # KEY_END = "\e[4~" #st
260
+
261
+ # KEY_HOME = "\e[7~" #urxvt
262
+ # KEY_END = "\e[8~" #urxvt
263
+
264
+ # HIDE_CURSOR = "\e[?25l"
265
+ # SHOW_CURSOR = "\e[?25h"
266
+ # HEIGHT,WIDTH = IO.console.winsize
267
+
268
+
269
+ # def mod key,char,extra;
270
+ # if key.length == 1; key = char + key.upcase
271
+ # elsif key[/~$/]; key[0..-2] + char
272
+ # else key[0..-2] + extra + key[-1].downcase end
273
+ # end
274
+ # def shift key; mod key, "$" end
275
+ # def ctrl key; mod key, "^", "O" end
276
+ # def alt key; KEYMAP[:esc] + key end
277
+ # def clear mode=2; IO.console.erase_screen 1 end
278
+ # def mode name; "\e[%im" % [(MODES.index name.to_s)] end
279
+ # def ansi arg, type, inc=0
280
+ # case arg
281
+ # when Integer; show "\e[%i;5;%im" % [ inc + 8, arg]
282
+ # else show "\e[%im" % [ ( type.index arg.to_s ) + inc ]
283
+ # end
284
+ # end
285
+
286
+