asktty 2.0.0 → 3.0.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.
@@ -1,156 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AskTTY
4
- module Internal
5
- class TextInputPrompt
6
- def initialize(title:, details: nil, placeholder: nil, value: nil, validator: nil, multiline: false)
7
- @title = title.to_s
8
- @details = details&.to_s
9
- @placeholder = placeholder&.to_s
10
- @value = value.to_s
11
- @validator = validator
12
- @multiline = multiline
13
- end
14
-
15
- def ask
16
- value = @value.dup
17
- error_message = nil
18
- validation_checked = false
19
-
20
- Terminal.open do |session|
21
- loop do
22
- session.render(
23
- render(
24
- value,
25
- width: session.width,
26
- error_message: error_message
27
- )
28
- )
29
-
30
- key = session.read_key
31
-
32
- case key
33
- when :enter
34
- unless validation_checked
35
- error_message = validation_message(value)
36
- validation_checked = true
37
- end
38
-
39
- next if error_message
40
-
41
- session.render(submitted_render(value, width: session.width))
42
- return value
43
- when :shift_enter, :ctrl_j
44
- next unless @multiline
45
-
46
- value << "\n"
47
- error_message = validation_message(value)
48
- validation_checked = true
49
- when :backspace
50
- updated_value = Rendering.chop_grapheme(value)
51
- next if updated_value == value
52
-
53
- value = updated_value
54
- error_message = validation_message(value)
55
- validation_checked = true
56
- when String
57
- next unless printable?(key)
58
-
59
- value << key
60
- error_message = validation_message(value)
61
- validation_checked = true
62
- end
63
- end
64
- end
65
- end
66
-
67
- private
68
-
69
- def render(value, width:, error_message: nil)
70
- Rendering.prompt_frame(
71
- title: @title, details: @details, help_items: help_items, error_message: error_message, width: width
72
- ) do |content_width|
73
- body_lines(value, content_width: content_width)
74
- end
75
- end
76
-
77
- def submitted_render(value, width:)
78
- Rendering.submitted_frame(@title, summary_value(value), width: width)
79
- end
80
-
81
- def body_lines(value, content_width:)
82
- return placeholder_lines(content_width) if @placeholder && value.empty?
83
-
84
- render_segments(value_lines(value), content_width: content_width, show_cursor: true)
85
- end
86
-
87
- def placeholder_lines(content_width)
88
- render_segments(
89
- @placeholder.split("\n", -1),
90
- content_width: content_width,
91
- show_cursor: false,
92
- first_style: method(:placeholder_first_line),
93
- style: ANSIStyle.method(:muted)
94
- )
95
- end
96
-
97
- def value_lines(value)
98
- return [value] unless @multiline
99
-
100
- value.split("\n", -1)
101
- end
102
-
103
- def help_items
104
- return ["enter (submit)"] unless @multiline
105
-
106
- ["enter (submit)", "shift+enter/ctrl+j (new line)"]
107
- end
108
-
109
- def placeholder_first_line(text)
110
- Rendering.placeholder_with_cursor(text)
111
- end
112
-
113
- def printable?(character)
114
- character.length == 1 && character >= " "
115
- end
116
-
117
- def render_segments(
118
- segments, content_width:, show_cursor:, first_style: ANSIStyle.method(:text), style: first_style
119
- )
120
- segments = [""] if segments.empty?
121
- wrap_width = [content_width - 2, 1].max
122
- last_index = segments.length - 1
123
-
124
- segments.each_with_index.flat_map do |segment, segment_index|
125
- wrapped_segments = Rendering.wrap_exact(segment, wrap_width)
126
-
127
- if show_cursor && segment_index == last_index && Rendering.display_width(wrapped_segments.last) >= wrap_width
128
- wrapped_segments << ""
129
- end
130
-
131
- wrapped_segments.each_with_index.map do |part, wrapped_index|
132
- prefix = segment_index.zero? && wrapped_index.zero? ? ANSIStyle.prompt("> ") : " "
133
- current_style = segment_index.zero? && wrapped_index.zero? ? first_style : style
134
- line = prefix + current_style.call(part)
135
-
136
- if show_cursor && segment_index == last_index && wrapped_index == wrapped_segments.length - 1
137
- line << ANSIStyle.cursor
138
- end
139
-
140
- line
141
- end
142
- end
143
- end
144
-
145
- def summary_value(value)
146
- return value unless @multiline
147
-
148
- value.tr("\n", " ")
149
- end
150
-
151
- def validation_message(value)
152
- Validation.message_for(value, @validator)
153
- end
154
- end
155
- end
156
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AskTTY
4
- module Internal
5
- module Validation
6
- module_function
7
-
8
- def message_for(value, validator)
9
- return nil unless validator
10
-
11
- result = validator.call(value)
12
- return nil if result == true
13
- return result if result.is_a?(String) && !result.empty?
14
-
15
- raise AskTTY::Error, "validator must return true or a non-empty error message"
16
- end
17
- end
18
- end
19
- end