typr 1.7.0 → 1.8.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: 75b5f8bf6bcd9c1e957f87f8ef47eaec83784cefcc3903ea9e68b25f0280d7af
4
- data.tar.gz: 1e92d998f96105408ac13e6c43f49ae2a44cdf0b2e6e34596f55749228beff29
3
+ metadata.gz: 02fb4a45192b9a6bc2b3a751033a71e058b2abcb33b77d8873c991a2d6031dd1
4
+ data.tar.gz: 878f286e0c40eb45b0b5c5216633bc0a9ff82c7f6bc03d18531a80090936e2af
5
5
  SHA512:
6
- metadata.gz: 3fb8fb7f24580c81250951138b3600037ce3eef21acc48a17c79db4c3fe5cceddd8037e1cb04548d85d2fde048426fa90c08846b09b9c8b2ad06472106813058
7
- data.tar.gz: 83f72c57b64e9515c27ddea087bdce0ad2d935e98b697a7e8b5d0de9885aa9dab18f15143437e4b8f0136899d5a6b08c5b7712dbd8f5b8d2f41ab56cb8c5d598
6
+ metadata.gz: 921906f7edae965d3ea48ed62e8eae91a770ad63dcf7cc3f20bb8463c9bbef280bcca2d2577317e4dd6c71d48b3740630bfaedf2d8c617cc9878d01381eebb9e
7
+ data.tar.gz: d0db793255d8969a1181193dee6b06227efc2c0248fff99ca6407f6d5944ece37dc330e8cdabafa26229e98a299a576ebc245ea4e4ebd5a25661b20526d094cc
data/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  ## [vx.y.z] — __DATE__
4
4
 
5
+ ## [v1.8.0] — 2026-10-11
6
+
7
+ ### Changed
8
+ - Sort indicator uses solid triangles (▲▼) instead of thin arrows, for a heavier mark at the same single-cell width
9
+
10
+ ### Fixed
11
+ - Text paints new spans once at entry and rebuilds only reflow: scheme and content colors survive resizes and appends without stacking or stripping
12
+ - Terminal self-test renders without scrolling and covers all named colors — status line uses raw output on the last row (`p` scrolled with inspect quotes and a newline), rows are capped to the visible area, the 256-ramp starts at a known position, the color list includes all `COLOR_MAP` names plus the full grey ramp, and the fg/bg steppers clamp to valid ids
13
+ - String headers fit inside minimal-width boxes — the box ignored the margin in the header width, so the border overwrote the last header character; single `:max` columns provably span the widget width
14
+ - Word jumps are documented as alt-arrow, not ctrl-arrow — README, the `read_line` doc comment, and the v1.2.0 entry called them ctrl-arrow while the bindings are `KEY_ALT_LEFT`/`KEY_ALT_RIGHT` (SGR modifier 3 = alt); the package's key modifier is alt everywhere
15
+ - Command-line redraw overwrites in place — `read_line` cleared the row on every keypress, flashing on cursor moves; each frame now pads to end-of-line with spaces instead, so shrinking content leaves no ghosts either
16
+ - Cursor stays hidden while the command line redraws — every keypress parked the visible cursor at the prompt between the home move and the edit-position move; each frame now hides it first and shows it at the edit position
17
+ - Mode table corrected to standard SGR order (`dim` restored, so `slow→5` and `invert→7`) with `:steady` at position 25 for blink-off; `mode_code` stays index-by-position
18
+
5
19
  ## [v1.7.0] — 2026-10-02
6
20
 
7
21
  ### Fixed
@@ -100,7 +114,7 @@
100
114
  ## [v1.2.0] — 2026-08-02
101
115
 
102
116
  ### Added
103
- - `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
117
+ - `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, alt-arrow word jumps, Home/End/Delete/Backspace, Escape aborts (`nil`), Enter confirms, and an optional block runs after each keypress
104
118
  - `Line#ask` strict question types — `Array` (pick from an object/column), `:key`, `:line`; the question and answer are rendered in `@colors[:question]`/`@colors[:answer]`
105
119
  - `Line#align` attribute (`:left`/`:right`) for text alignment in draw
106
120
  - `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`
data/README.md CHANGED
@@ -185,7 +185,7 @@ Clears the full screen or a single line (`:line`).
185
185
  Reads a single keypress in raw mode. Returns the key as a String (`KEY_ESCAPE`, `?q`, arrow escape sequences), a `Typr::Mouse` struct for mouse reports, or `nil` when input is unavailable.
186
186
 
187
187
  ### `Typr.read_line(prompt, ...)`
188
- The shared interactive line editor. Renders a prompt and query at any position with arrow keys, ctrl-arrow word jumps, Home/End/Delete/Backspace; Enter confirms, Escape aborts (`nil`). An optional block runs after each keypress and may short-circuit with a result. Pass `completions:` — a callable `->(query, cursor) { candidates }` — and TAB matches the current word against `candidates` in a pick grid: a candidate is either a string or `[text, color-pair]` to tint its pick entry. Pass `regex:` — a Hash of `Regexp => color`, or a scheme name (`:generic`, `:shell`) — and matching regions of the query are colored as they are typed, restoring the prompt's trailing color code. The editor is agnostic to where candidates come from.
188
+ The shared interactive line editor. Renders a prompt and query at any position with arrow keys, alt-arrow word jumps, Home/End/Delete/Backspace; Enter confirms, Escape aborts (`nil`). An optional block runs after each keypress and may short-circuit with a result. Pass `completions:` — a callable `->(query, cursor) { candidates }` — and TAB matches the current word against `candidates` in a pick grid: a candidate is either a string or `[text, color-pair]` to tint its pick entry. Pass `regex:` — a Hash of `Regexp => color`, or a scheme name (`:generic`, `:shell`) — and matching regions of the query are colored as they are typed, restoring the prompt's trailing color code. The editor is agnostic to where candidates come from.
189
189
 
190
190
  ```ruby
191
191
  Typr.read_line "/", completions: ->(query, cursor) {
data/lib/browser.rb CHANGED
@@ -503,7 +503,7 @@ full: %i[ name size permissions executable printable owner group
503
503
  input: [ [->{Etc.getpwuid(Process.euid).name rescue ENV["USER"] || Etc.getlogin}, ->{@parent.directory},
504
504
  ->{@parent.filter_display},
505
505
  ->{ (s = @parent.sorted_by(true)) &&
506
- "#{s} #{@parent.reverse ? "\u2193" : "\u2191"}"},
506
+ "#{s} #{@parent.reverse ? "\u25BC" : "\u25B2"}"},
507
507
  ->{@parent.view} ] ], format: [:min, :max, :min, :min, :min],
508
508
  colors: { columns: [@colors[:default], @colors[:default],
509
509
  @colors[:actions][:filter], @colors[:actions][:sort],
@@ -549,7 +549,7 @@ full: %i[ name size permissions executable printable owner group
549
549
  wire.(label, cmd)
550
550
  end
551
551
  @user ||= Line.new( parent:self, left:->{@parent.left}, top:->{@parent.bottom+1},
552
- colors: { regex: @colors[:regex] }, bindings: bindings_labels )
552
+ bindings: bindings_labels )
553
553
  else
554
554
  cmds = { "[h]elp" => :help, "[/]:search" => :search,
555
555
  "[f]ilter" => :filter_view, "[s]ort" => :sort_view, "[v]iews" => :views,
@@ -563,7 +563,6 @@ full: %i[ name size permissions executable printable owner group
563
563
  [i]nvert_mark [~]:home [H]idden_files [back]:.. [up] [down] [pageup] [pagedown] [home] [end] [esc]:quit [return]:confirm\ ]
564
564
  plain.each { |label| wire.(label, cmds[label]) if cmds[label] }
565
565
  @user ||= Line.new( parent:self, left:->{@parent.left}, top:->{@parent.bottom+1},
566
- colors: { regex: @colors[:regex] },
567
566
  bindings: plain.map { |label|
568
567
  name = label.gsub( /[\[\]]/, '' ).downcase.to_sym
569
568
  @colors[:actions][name] ?
data/lib/grid.rb CHANGED
@@ -411,7 +411,7 @@ module Typr
411
411
  gaps = @margin.size + @separator.size * (columns - 1)
412
412
  sum = @widths.sum
413
413
  @max = sum + gaps - 1
414
- @max = [ @max, @header.length ].max if @header.is_a?(String)
414
+ @max = [ @max, @header.length + @margin.size ].max if @header.is_a?(String)
415
415
  space = width - gaps
416
416
  if (extra = sum - space ) > 0
417
417
  extra.times{ |i| @widths[ @widths.index(@widths.max) ] -= 1 }
@@ -434,7 +434,7 @@ module Typr
434
434
  def print row=nil
435
435
  return super unless row
436
436
  header = ( row == :header )
437
- return draw @header[0..width-1].ljust(width) if
437
+ return draw @header[0...(width - @margin.size)].ljust(width - @margin.size) if
438
438
  header and @header.is_a? String
439
439
  return unless row_data = header ? @header : @data[row]
440
440
  fields = row_data.dup
data/lib/line.rb CHANGED
@@ -159,13 +159,13 @@ class Line < Space
159
159
  # line.cli "> ", initial: "cp %name"
160
160
  # #=> "cp source.txt" (or nil on Escape)
161
161
 
162
- def cli prompt, initial: '', redraw: nil, history: nil, prefixes: nil
162
+ def cli prompt, initial: '', redraw: nil, history: nil, prefixes: nil, regex: nil
163
163
  re = /\A[#{Regexp.escape(prefixes || '|+=!')}]/
164
164
  source = ->(query) {
165
165
  bash_candidates(query.sub(re, '')).split("\n").reject(&:empty?).uniq }
166
166
  colored = color_code(@colors[:default]).to_s + prompt + color_code(@colors[:answer])
167
167
  Typr.read_line(colored, left: left, top: top, initial: initial,
168
- completions: source, redraw: redraw, regex: @colors[:regex], history: history)
168
+ completions: source, redraw: redraw, regex: regex || @colors[:regex], history: history)
169
169
  end
170
170
 
171
171
  private
data/lib/terminal.rb CHANGED
@@ -86,7 +86,8 @@ module Typr
86
86
  end
87
87
 
88
88
  # Text attributes (reset, bold, italic, ...) and named 8/256-color tables.
89
- MODES = %i[ reset bold italic underline slow fast invert ]
89
+ MODES = %i[ reset bold dim italic underline slow fast invert ]
90
+ MODES[25] = :steady
90
91
  COLORS = %i[ black red green yellow blue magenta cyan white ]
91
92
  COLOR_MAP = {
92
93
  brown: 130, orange: 208, lime: 118, pink: 218,
@@ -407,7 +408,7 @@ module Typr
407
408
  ## Interactive line editor used for string prompts (search, %str, ...).
408
409
  #
409
410
  # Renders +prompt+ and the query at (left, top) and edits it with
410
- # arrow keys, ctrl-arrow word jumps, home/end, delete and backspace.
411
+ # arrow keys, alt-arrow word jumps, home/end, delete and backspace.
411
412
  # Returns the query on enter, nil on escape. A block may inspect each
412
413
  # keypress after it is applied; a non-nil return short-circuits the
413
414
  # editor and becomes its return value. When stdin is not a tty, the
@@ -434,13 +435,16 @@ module Typr
434
435
  $>.print CURSOR_NORMAL
435
436
  begin
436
437
  loop do
438
+ $>.print CURSOR_INVISIBLE
437
439
  $>.print "\e[%i;%if" % [ top + 1, left + 1 ]
438
- $>.print ERASE_LINE
439
440
  before = text_width( prompt + query[0...cursor] )
440
441
  offset = [ left + before - Typr.width, 0 ].max
441
442
  display = prompt + paint( query, regex, prompt[/\e\[[0-9;]*m\z/].to_s )
442
- $>.print slice_width( display, offset, Typr.width - left + 1 )
443
+ line = slice_width( display, offset, Typr.width - left + 1 )
444
+ $>.print line
445
+ $>.print ' ' * (Typr.width - left + 1 - text_width(line))
443
446
  $>.print "\e[%i;%if" % [ top + 1, left + 1 + before - offset ]
447
+ $>.print CURSOR_NORMAL
444
448
  key = read_key
445
449
  return nil if key.nil? or key == KEY_ESCAPE
446
450
  return query if key == KEY_RETURN or key == "\n"
@@ -619,14 +623,15 @@ if __FILE__ == $0
619
623
  begin
620
624
  include Typr
621
625
  Typr.init
622
- colors = COLORS + (1..9).map{|i| "grey#{i*10}" }
626
+ colors = COLORS + COLOR_MAP.keys.map(&:to_s) + (0..10).map{|i| "grey#{i*10}" }
623
627
  chars = (33..126).map(&:chr)
624
628
  slice = chars.count / colors.count + 1
625
629
  characters = chars.each_slice( slice ).map(&:join)
626
630
  key,fg,bg = "",150,0
627
631
  Typr.clear
632
+ rows = [colors.count, Typr.height - 4].min
628
633
  loop do
629
- colors.each_with_index{ |color,y|
634
+ colors.first(rows).each_with_index{ |color,y|
630
635
  characters.each_with_index{ |chars,x|
631
636
  foreground color.to_sym;
632
637
  background colors[x].to_sym;
@@ -634,20 +639,21 @@ begin
634
639
  draw chars
635
640
  }
636
641
  }
642
+ move 0, rows
637
643
  (0...256).each{ |i| background i; draw ' ' }
638
644
 
639
645
  move 0, Typr.height
640
646
  color [:white, :black]
641
- p "KEY: %s / %s" % [key, key]
647
+ draw "KEY: %s / %s" % [key, key]
642
648
  move 15, Typr.height
643
649
  color [fg,bg]
644
650
  print " %s / %s " % [fg,bg]
645
651
  key = Typr.read_key
646
652
  case key
647
- when KEY_UP; fg+=1
648
- when KEY_DOWN; fg-=1
649
- when KEY_LEFT; bg-=1
650
- when KEY_RIGHT; bg+=1
653
+ when KEY_UP; fg = [fg + 1, 255].min
654
+ when KEY_DOWN; fg = [fg - 1, 0].max
655
+ when KEY_LEFT; bg = [bg - 1, 0].max
656
+ when KEY_RIGHT; bg = [bg + 1, 255].max
651
657
  when KEY_ESCAPE; exit
652
658
  end
653
659
  end
data/lib/text.rb CHANGED
@@ -33,13 +33,19 @@ module Typr
33
33
  # text << "appended line" # alias for build
34
34
 
35
35
  def build input=nil
36
- ( input, @data, @lines = sanitize(@data), "", [0] ) if rebuild = !input
36
+ ( input, @data, @lines = @data, "", [0] ) if rebuild = !input
37
37
  return self if rebuild && input == ""
38
- rows = @lines[0...-1].each_index.map{ |i| sanitize(@data[@lines[i]...@lines[i+1]]) }
38
+ rows = @lines[0...-1].each_index.map{ |i| @data[@lines[i]...@lines[i+1]] }
39
39
  @data = rows.join
40
40
  off = [ 0 ]; rows.each{ |row| off << off.last + row.length }
41
41
  @lines = off
42
42
  str = input.respond_to?(:read) ? input.read : input.to_s
43
+ if !rebuild and (r = @colors[:regex]) and r != {}
44
+ base = color_code( @colors[:default][0] ).to_s
45
+ # Paint new spans once, whole (multiline matches included); rebuilds
46
+ # below only reflow, so paint can neither stack nor need stripping.
47
+ str = Typr.paint(str, r, base)
48
+ end
43
49
  str = str.gsub("\t", " " * @tabspace)
44
50
  @tail = ""
45
51
  pos = 0
@@ -85,14 +91,6 @@ module Typr
85
91
  end
86
92
  end
87
93
  @lines << @data.length unless @lines.last == @data.length
88
- if (r = @colors[:regex]) and r != {}
89
- base = color_code( @colors[:default][0] ).to_s
90
- painted = @lines[0...-1].each_index.map{ |i|
91
- Typr.paint @data[@lines[i]...@lines[i+1]], r, base }
92
- @data = painted.join
93
- off = [ 0 ]; painted.each{ |line| off << off.last + line.length }
94
- @lines = off
95
- end
96
94
  highlight if @re
97
95
  end
98
96
  alias :<< :build
data/typr.gemspec CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "typr"
5
- s.version = "1.7.0"
6
- s.date = "2026-10-02"
5
+ s.version = "1.8.0"
6
+ s.date = "2026-10-11"
7
7
  s.homepage = "https://codeberg.org/typr/typr"
8
8
  s.licenses = ["MIT"]
9
9
  s.require_paths = ["lib"]
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: typr
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.0
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kilian Reitmayr
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-10-02 00:00:00.000000000 Z
10
+ date: 2026-10-11 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: unicode-display_width