clack 0.4.1 → 0.4.3

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: c0622f234b9f83906c6440e54c29a12715ecfc7a2b3777ec68efaf3882defcb7
4
- data.tar.gz: 450c6376c40ad88a27683294e97292f227e2081ccc90a0c076949bba3e5afd1d
3
+ metadata.gz: 1a59e5250ad3fb130ffda74b6fdfa5bda7bb4f57062cd6305c5c07f0af7b82ee
4
+ data.tar.gz: 8fb2da61ab0120a8f95189c9112463e742d87a7a915a30d9c5ae7d192c90475b
5
5
  SHA512:
6
- metadata.gz: '08a7d471122e503168817430b65ad3618488eef6a2c79229a50e20a0a3ff3f2df741faef330a81b57875ddca3a37a68d0aebd19e770b7bbe73addd9a737bc9ec'
7
- data.tar.gz: 76e01f5eef142fd64069e1d9ba705edebc5694834ab68d5f7a8370eb345f1bf1473f2f1771d0178ed80e0b6c977c05d032d0059112bf36e72dd2fbe18f3850a3
6
+ metadata.gz: f64a271d61f1e0b93938b418d2907a173cde44a901a98ba1f691b3c45c84e97ef8096e6b1a1b6e7b94470287920340f75df65ec62a213b661707ab935f266ead
7
+ data.tar.gz: 6bf0a82964715fa130c64f15dc974a373aa00e996d285e19cf0518175b9916ff042b2fe7a042e1ea3d5720d3dee20c1fdbd914d5a77fd9f5a1f428f24622f7ee
data/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.3] - 2026-02-21
4
+
5
+ ### Fixed
6
+ - Multiselect variants now ignore `initial_values` that don't match any option (prevents phantom values in return)
7
+ - SIGWINCH handler now uses `.dup` for safe array iteration inside signal trap
8
+ - `GroupMultiselect` propagates `:hint` into flat items so hints actually render
9
+
10
+ ### Changed
11
+ - `AutocompleteMultiselect` final frame now shows selected labels instead of "N items selected", matching `Multiselect` behavior
12
+ - Removed dead `@mutex` from `Testing` module
13
+ - Removed unused `raw:` parameter from `TaskLog` and `TaskLogGroup` message methods
14
+
15
+ ## [0.4.2] - 2026-02-20
16
+
17
+ ### Fixed
18
+ - `GroupMultiselect` now preserves `:hint` on options (was silently dropped during normalization)
19
+ - `GroupMultiselect` renders hints on active options, matching `Select` behavior
20
+ - `Spinner` no longer raises when finished in timer mode before the animation thread starts
21
+ - YARD docs: corrected `selectable_groups` default from `true` to `false`
22
+
23
+ ### Changed
24
+ - `Range` prompt now accepts `initial_value:` for consistency with all other prompts (`default:` still works)
25
+ - README: corrected Range example to use `initial_value:` and tab completion description
26
+
3
27
  ## [0.4.1] - 2026-02-20
4
28
 
5
29
  ### Fixed
@@ -21,7 +45,7 @@
21
45
  ## [0.4.0] - 2026-02-19
22
46
 
23
47
  ### Added
24
- - `range` slider prompt for numeric selection (`Clack.range(message:, min:, max:, step:, default:)`)
48
+ - `range` slider prompt for numeric selection (`Clack.range(message:, min:, max:, step:, initial_value:)`)
25
49
  - Tab completion on `text` prompt via `completions:` parameter (array or proc)
26
50
  - Minimum terminal width warning (non-blocking, 40 columns)
27
51
 
data/README.md CHANGED
@@ -198,7 +198,7 @@ name = Clack.text(
198
198
  )
199
199
  ```
200
200
 
201
- **Tab completion** - press `Tab` to cycle through matching candidates:
201
+ **Tab completion** - press `Tab` to fill the longest common prefix of matching candidates:
202
202
 
203
203
  ```ruby
204
204
  # Tab completion from a static list
@@ -367,7 +367,7 @@ volume = Clack.range(
367
367
  min: 0,
368
368
  max: 100,
369
369
  step: 5,
370
- default: 50
370
+ initial_value: 50
371
371
  )
372
372
  ```
373
373
 
@@ -32,9 +32,6 @@ module Clack
32
32
  MIN_TERMINAL_WIDTH = 40
33
33
 
34
34
  # Track active prompts for SIGWINCH notification.
35
- # Signal handler may fire during register/unregister. We can't use
36
- # .dup (allocates, forbidden in trap context) so we accept a benign
37
- # race: worst case, a prompt misses one resize notification.
38
35
  @active_prompts = []
39
36
 
40
37
  class << self
@@ -57,7 +54,7 @@ module Clack
57
54
  return unless Signal.list.key?("WINCH")
58
55
 
59
56
  Signal.trap("WINCH") do
60
- @active_prompts.each(&:request_redraw)
57
+ @active_prompts.dup.each(&:request_redraw)
61
58
  end
62
59
  end
63
60
  end
@@ -53,7 +53,8 @@ module Clack
53
53
  @cursor = 0
54
54
  @selected_index = 0
55
55
  @scroll_offset = 0
56
- @selected_values = Set.new(initial_values || [])
56
+ valid_values = Set.new(@all_options.map { |o| o[:value] })
57
+ @selected_values = Set.new(initial_values || []) & valid_values
57
58
  update_filtered
58
59
  end
59
60
 
@@ -167,11 +168,9 @@ module Clack
167
168
  lines << "#{bar}\n"
168
169
  lines << "#{symbol_for_state} #{@message}\n"
169
170
 
170
- display = if @state == :cancel
171
- Colors.strikethrough(Colors.dim("cancelled"))
172
- else
173
- Colors.dim("#{@selected_values.size} items selected")
174
- end
171
+ labels = @all_options.select { |o| @selected_values.include?(o[:value]) }.map { |o| o[:label] }
172
+ display_text = labels.join(", ")
173
+ display = (@state == :cancel) ? Colors.strikethrough(Colors.dim(display_text)) : Colors.dim(display_text)
175
174
  lines << "#{bar} #{display}\n"
176
175
 
177
176
  lines.join
@@ -55,7 +55,8 @@ module Clack
55
55
  super(message:, **opts)
56
56
  @groups = normalize_groups(options)
57
57
  @flat_items = build_flat_items
58
- @selected = Set.new(initial_values)
58
+ valid_values = Set.new(@flat_items.select { |item| item[:type] == :option }.map { |item| item[:value] })
59
+ @selected = Set.new(initial_values) & valid_values
59
60
  @required = required
60
61
  @selectable_groups = selectable_groups
61
62
  @group_spacing = group_spacing
@@ -152,9 +153,9 @@ module Clack
152
153
  def normalize_option(opt)
153
154
  case opt
154
155
  when Hash
155
- {value: opt[:value], label: opt[:label] || opt[:value].to_s, disabled: opt[:disabled] || false}
156
+ {value: opt[:value], label: opt[:label] || opt[:value].to_s, hint: opt[:hint], disabled: opt[:disabled] || false}
156
157
  else
157
- {value: opt, label: opt.to_s, disabled: false}
158
+ {value: opt, label: opt.to_s, hint: nil, disabled: false}
158
159
  end
159
160
  end
160
161
 
@@ -167,6 +168,7 @@ module Clack
167
168
  type: :option,
168
169
  value: opt[:value],
169
170
  label: opt[:label],
171
+ hint: opt[:hint],
170
172
  disabled: opt[:disabled],
171
173
  group: group,
172
174
  last_in_group: idx == group[:options].length - 1
@@ -261,13 +263,14 @@ module Clack
261
263
  else
262
264
  " "
263
265
  end
266
+ hint = (item[:hint] && active) ? " #{Colors.dim("(#{item[:hint]})")}" : ""
264
267
 
265
268
  if item[:disabled]
266
269
  "#{active_bar} #{Colors.dim(prefix)}#{Colors.dim(Symbols::S_CHECKBOX_INACTIVE)} #{Colors.strikethrough(Colors.dim(item[:label]))}\n"
267
270
  elsif active && selected
268
- "#{active_bar} #{Colors.dim(prefix)}#{Colors.green(Symbols::S_CHECKBOX_SELECTED)} #{item[:label]}\n"
271
+ "#{active_bar} #{Colors.dim(prefix)}#{Colors.green(Symbols::S_CHECKBOX_SELECTED)} #{item[:label]}#{hint}\n"
269
272
  elsif active
270
- "#{active_bar} #{Colors.dim(prefix)}#{Colors.cyan(Symbols::S_CHECKBOX_ACTIVE)} #{item[:label]}\n"
273
+ "#{active_bar} #{Colors.dim(prefix)}#{Colors.cyan(Symbols::S_CHECKBOX_ACTIVE)} #{item[:label]}#{hint}\n"
271
274
  elsif selected
272
275
  "#{active_bar} #{Colors.dim(prefix)}#{Colors.green(Symbols::S_CHECKBOX_SELECTED)} #{Colors.dim(item[:label])}\n"
273
276
  else
@@ -40,7 +40,8 @@ module Clack
40
40
  def initialize(message:, options:, initial_values: [], required: true, max_items: nil, cursor_at: nil, **opts)
41
41
  super(message:, **opts)
42
42
  @options = normalize_options(options)
43
- @selected = Set.new(initial_values)
43
+ valid_values = Set.new(@options.map { |o| o[:value] })
44
+ @selected = Set.new(initial_values) & valid_values
44
45
  @required = required
45
46
  @max_items = max_items
46
47
  @scroll_offset = 0
@@ -10,11 +10,11 @@ module Clack
10
10
  # @example Basic usage
11
11
  # level = Clack.range(message: "Volume", min: 0, max: 100, step: 5)
12
12
  #
13
- # @example With default value
13
+ # @example With initial value
14
14
  # workers = Clack.range(
15
15
  # message: "Concurrency",
16
16
  # min: 1, max: 16,
17
- # step: 1, default: 4
17
+ # step: 1, initial_value: 4
18
18
  # )
19
19
  #
20
20
  class Range < Core::Prompt
@@ -26,9 +26,10 @@ module Clack
26
26
  # @param min [Numeric] minimum value (default: 0)
27
27
  # @param max [Numeric] maximum value (default: 100)
28
28
  # @param step [Numeric] increment size (default: 1)
29
- # @param default [Numeric, nil] initial value (defaults to min)
29
+ # @param initial_value [Numeric, nil] initial value (defaults to min)
30
+ # @param default [Numeric, nil] deprecated alias for initial_value
30
31
  # @param opts [Hash] additional options passed to {Core::Prompt}
31
- def initialize(message:, min: 0, max: 100, step: 1, default: nil, **opts)
32
+ def initialize(message:, min: 0, max: 100, step: 1, initial_value: nil, default: nil, **opts)
32
33
  super(message:, **opts)
33
34
 
34
35
  raise ArgumentError, "min must be less than max" if min >= max
@@ -37,7 +38,7 @@ module Clack
37
38
  @min = min
38
39
  @max = max
39
40
  @step = step
40
- @value = clamp(default || min)
41
+ @value = clamp(initial_value || default || min)
41
42
  end
42
43
 
43
44
  protected
@@ -178,7 +178,7 @@ module Clack
178
178
  @finished = true
179
179
  @running = false
180
180
  thread_to_join = @thread
181
- suffix = (@indicator == :timer) ? " #{format_timer}" : ""
181
+ suffix = (@indicator == :timer && @start_time) ? " #{format_timer}" : ""
182
182
  [message || @message, suffix]
183
183
  end
184
184
 
@@ -32,8 +32,7 @@ module Clack
32
32
 
33
33
  # Add a message to the log
34
34
  # @param msg [String] Message to display
35
- # @param raw [Boolean] If true, don't add newline between messages
36
- def message(msg, raw: false)
35
+ def message(msg)
37
36
  clear_buffer
38
37
  @buffer << msg.to_s.gsub(/\e\[[\d;]*[ABCDEFGHfJKSTsu]/, "") # Strip cursor movement codes
39
38
  apply_limit
@@ -155,7 +154,7 @@ module Clack
155
154
  end
156
155
 
157
156
  # Add a message to this group
158
- def message(msg, raw: false)
157
+ def message(msg)
159
158
  @parent.add_group_message(self, msg)
160
159
  end
161
160
 
data/lib/clack/testing.rb CHANGED
@@ -105,8 +105,6 @@ module Clack
105
105
  end
106
106
  end
107
107
 
108
- @mutex = Mutex.new
109
-
110
108
  class << self
111
109
  # Simulate a prompt interaction by feeding a predefined key sequence.
112
110
  #
data/lib/clack/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Clack
4
4
  # Current gem version.
5
- VERSION = "0.4.1"
5
+ VERSION = "0.4.3"
6
6
  end
data/lib/clack.rb CHANGED
@@ -343,7 +343,7 @@ module Clack
343
343
  # @option opts [Array, nil] :initial_values initially selected values
344
344
  # @option opts [Boolean] :required require at least one selection (default: true)
345
345
  # @option opts [Object, nil] :cursor_at value of initially focused option
346
- # @option opts [Boolean] :selectable_groups allow toggling entire groups (default: true)
346
+ # @option opts [Boolean] :selectable_groups allow toggling entire groups (default: false)
347
347
  # @option opts [Integer] :group_spacing lines between groups (default: 0)
348
348
  # @return [Array, CANCEL] selected values or CANCEL if cancelled
349
349
  def group_multiselect(message:, options:, **opts)
@@ -375,7 +375,7 @@ module Clack
375
375
  # @option opts [Numeric] :min minimum value (default: 0)
376
376
  # @option opts [Numeric] :max maximum value (default: 100)
377
377
  # @option opts [Numeric] :step increment size (default: 1)
378
- # @option opts [Numeric, nil] :default initial value (defaults to min)
378
+ # @option opts [Numeric, nil] :initial_value initial value (defaults to min)
379
379
  # @option opts [Proc, nil] :validate validation proc
380
380
  # @option opts [String, nil] :help help text shown below the message
381
381
  # @return [Numeric, CANCEL] selected value or CANCEL if cancelled
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Whittaker