inquirex-tty 0.10.0 → 0.12.1
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 +4 -4
- data/.circleci/config.yml +57 -0
- data/.envrc +2 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +28 -12
- data/README.md +62 -12
- data/docs/badges/coverage_badge.svg +2 -2
- data/examples/08_tax_preparer.rb +2 -2
- data/examples/10_answers.json +1 -1
- data/lib/inquirex/tty/answers_file.rb +48 -0
- data/lib/inquirex/tty/commands/run.rb +106 -50
- data/lib/inquirex/tty/flow_loader.rb +1 -2
- data/lib/inquirex/tty/key_map.rb +127 -0
- data/lib/inquirex/tty/renderer.rb +180 -10
- data/lib/inquirex/tty/ui_helper.rb +3 -2
- data/lib/inquirex/tty/version.rb +1 -1
- data/lib/inquirex/tty.rb +2 -0
- metadata +213 -191
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Inquirex
|
|
4
|
+
module TTY
|
|
5
|
+
# Raised out of a tty-prompt widget when the user presses a navigation
|
|
6
|
+
# key, so whichever widget is open (ask, select, multi_select, yes?)
|
|
7
|
+
# unwinds without returning an answer. tty-prompt offers no hook that
|
|
8
|
+
# aborts a question, and a subclass per widget would mean overriding the
|
|
9
|
+
# read loop of each one; one exception covers them all.
|
|
10
|
+
class Navigation < StandardError
|
|
11
|
+
# @return [Symbol] :back, :skip or :help
|
|
12
|
+
attr_reader :action
|
|
13
|
+
|
|
14
|
+
# @param action [Symbol] :back, :skip or :help
|
|
15
|
+
def initialize(action)
|
|
16
|
+
@action = action
|
|
17
|
+
super("navigation: #{action}")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# The keys that navigate a TTY run, as a tty-reader listener that turns
|
|
22
|
+
# them into {Navigation} exceptions while a collecting prompt is open.
|
|
23
|
+
#
|
|
24
|
+
# Backspace and "?" are also editing keys, so in a text answer they
|
|
25
|
+
# navigate only while the answer is empty: Backspace on an empty line
|
|
26
|
+
# goes back, Backspace after text deletes a character, and "what?" types
|
|
27
|
+
# a question mark. Lists have no text, so there they always navigate.
|
|
28
|
+
# Ctrl-B goes back from anywhere: tty-reader maps byte 0x02 to :ctrl_b in
|
|
29
|
+
# every terminal and tty-prompt binds nothing to it.
|
|
30
|
+
#
|
|
31
|
+
# tty-reader reports a key after it has edited the line, so the listener
|
|
32
|
+
# cannot ask "was the line empty?" directly. It keeps its own count of the
|
|
33
|
+
# characters typed instead, from how much each event's line grew or
|
|
34
|
+
# shrank. A pre-filled answer is a tty-prompt +default:+, shown beside
|
|
35
|
+
# the question rather than typed into the line, so every line starts empty.
|
|
36
|
+
#
|
|
37
|
+
# @example Wrap a prompt call
|
|
38
|
+
# prompt.subscribe(KeyMap.new(can_back: engine.can_back?))
|
|
39
|
+
class KeyMap
|
|
40
|
+
# tty-reader key names that edit text and go back on an empty answer.
|
|
41
|
+
BACKSPACE = %i[backspace ctrl_h].freeze
|
|
42
|
+
|
|
43
|
+
# Every navigation key, for the help box.
|
|
44
|
+
BINDINGS = [
|
|
45
|
+
["Enter", "submit the answer (an empty answer keeps the one shown)"],
|
|
46
|
+
["Backspace", "go back to the previous question (on an empty answer)"],
|
|
47
|
+
["Ctrl-B", "go back to the previous question"],
|
|
48
|
+
["Ctrl-S", "skip the question (optional questions only)"],
|
|
49
|
+
["↑ ↓ Space", "move and toggle in a list"],
|
|
50
|
+
["?", "show this help (on an empty answer)"],
|
|
51
|
+
["Ctrl-C", "abort without saving"]
|
|
52
|
+
].freeze
|
|
53
|
+
|
|
54
|
+
# @param can_back [Boolean] whether there is a question to go back to;
|
|
55
|
+
# when false the back keys do nothing
|
|
56
|
+
def initialize(can_back: true)
|
|
57
|
+
@can_back = can_back
|
|
58
|
+
@length = 0
|
|
59
|
+
@line = nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# The one-line reminder drawn above each collecting prompt.
|
|
63
|
+
#
|
|
64
|
+
# @param can_back [Boolean]
|
|
65
|
+
# @return [String]
|
|
66
|
+
def self.hint(can_back:)
|
|
67
|
+
[("⌫ back" if can_back), "^S skip", "? keys", "^C abort"].compact.join(" · ")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# tty-reader calls this for every key, after any key-specific handler.
|
|
71
|
+
#
|
|
72
|
+
# @param event [TTY::Reader::KeyEvent]
|
|
73
|
+
# @return [void]
|
|
74
|
+
# @raise [Navigation] when the key navigates
|
|
75
|
+
def keypress(event)
|
|
76
|
+
name = event.key.name
|
|
77
|
+
raise Navigation, :skip if name == :ctrl_s
|
|
78
|
+
raise Navigation, :back if @can_back && (name == :ctrl_b || (BACKSPACE.include?(name) && @length.zero?))
|
|
79
|
+
raise Navigation, :help if event.value == "?" && @length.zero?
|
|
80
|
+
|
|
81
|
+
track(event)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
# Updates the typed-character count after an editing key.
|
|
87
|
+
#
|
|
88
|
+
# A line widget passes its whole line (prompt plus text) with each
|
|
89
|
+
# event; list widgets pass an empty string, and the only text they hold
|
|
90
|
+
# is the number typed into an enum_select.
|
|
91
|
+
#
|
|
92
|
+
# @param event [TTY::Reader::KeyEvent]
|
|
93
|
+
# @return [void]
|
|
94
|
+
def track(event)
|
|
95
|
+
return reset if %i[return enter].include?(event.key.name)
|
|
96
|
+
return track_keys(event) if event.line.to_s.empty?
|
|
97
|
+
|
|
98
|
+
delta = @line ? event.line.length - @line.length : first_delta(event)
|
|
99
|
+
@length = [@length + delta, 0].max
|
|
100
|
+
@line = event.line
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# The first event of a line has nothing to diff against; the line
|
|
104
|
+
# started empty, so only a printable character changed it.
|
|
105
|
+
def first_delta(event) = printable?(event.value) ? 1 : 0
|
|
106
|
+
|
|
107
|
+
def track_keys(event)
|
|
108
|
+
if BACKSPACE.include?(event.key.name) || event.key.name == :delete
|
|
109
|
+
@length = [@length - 1, 0].max
|
|
110
|
+
elsif event.key.name == :num
|
|
111
|
+
@length += event.value.length
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Enter submits; a widget that reads several lines (multiline text)
|
|
116
|
+
# starts the next one empty.
|
|
117
|
+
def reset
|
|
118
|
+
@length = 0
|
|
119
|
+
@line = nil
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def printable?(value)
|
|
123
|
+
value.to_s.length == 1 && value.match?(/[[:print:]]/)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -23,6 +23,11 @@ module Inquirex
|
|
|
23
23
|
# (fallback) → prompt.ask
|
|
24
24
|
#
|
|
25
25
|
# Header display verb uses TTY::Font for large ASCII-art section titles.
|
|
26
|
+
#
|
|
27
|
+
# {#collect} drives one collecting step against an engine and binds the
|
|
28
|
+
# navigation keys ({KeyMap}): back steps the engine back and the caller's
|
|
29
|
+
# loop re-asks the previous question with its answer as the default; skip
|
|
30
|
+
# declines an optional question; "?" shows the keymap.
|
|
26
31
|
class Renderer
|
|
27
32
|
include UIHelper
|
|
28
33
|
|
|
@@ -62,6 +67,52 @@ module Inquirex
|
|
|
62
67
|
sep(:magenta, "─")
|
|
63
68
|
end
|
|
64
69
|
|
|
70
|
+
# Asks the engine's current collecting step and answers it, unless the
|
|
71
|
+
# user presses a navigation key ({KeyMap}) instead:
|
|
72
|
+
#
|
|
73
|
+
# - back: the engine steps back and nothing is answered; the caller's
|
|
74
|
+
# loop then asks the previous question again, pre-filled with the
|
|
75
|
+
# answer it had. Back is offered only when there is somewhere to go
|
|
76
|
+
# back to: on the first question the keys do nothing.
|
|
77
|
+
# - skip: an optional question is skipped (Engine#skip); a required one
|
|
78
|
+
# is refused with a notice and asked again.
|
|
79
|
+
# - help: the keymap is shown, then the loop asks the question again.
|
|
80
|
+
#
|
|
81
|
+
# @example Drive a flow's collecting steps
|
|
82
|
+
# until engine.finished?
|
|
83
|
+
# engine.current_step.display? ? (renderer.render(engine.current_step); engine.advance) : renderer.collect(engine)
|
|
84
|
+
# end
|
|
85
|
+
#
|
|
86
|
+
# @param engine [Inquirex::Engine] positioned on a collecting step
|
|
87
|
+
# @return [Symbol] +:answered+, +:back+, +:skipped+, +:help+, or +:stay+
|
|
88
|
+
# when a required question refused a skip
|
|
89
|
+
def collect(engine)
|
|
90
|
+
step_id = engine.current_step_id
|
|
91
|
+
can_back = engine.can_back?
|
|
92
|
+
puts pastel.dim(KeyMap.hint(can_back:))
|
|
93
|
+
|
|
94
|
+
answer = with_keys(can_back) do
|
|
95
|
+
render(engine.current_step,
|
|
96
|
+
suggestion: engine.suggestion_for(step_id),
|
|
97
|
+
default: engine.previous_answer(step_id))
|
|
98
|
+
end
|
|
99
|
+
engine.answer(answer)
|
|
100
|
+
:answered
|
|
101
|
+
rescue Navigation => e
|
|
102
|
+
navigate(engine, e.action)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Shows every navigation key in a box and waits for a key.
|
|
106
|
+
#
|
|
107
|
+
# @return [void]
|
|
108
|
+
def show_keymap
|
|
109
|
+
width = KeyMap::BINDINGS.map { |key, _| key.length }.max
|
|
110
|
+
lines = KeyMap::BINDINGS.map { |key, action| "#{key.ljust(width)} #{action}" }
|
|
111
|
+
puts
|
|
112
|
+
puts box(lines.join("\n"), title: "Keys", bg: :blue, fg: :white, align: :left)
|
|
113
|
+
prompt.keypress(pastel.dim("Press any key to return to the question..."))
|
|
114
|
+
end
|
|
115
|
+
|
|
65
116
|
# Renders a node. Returns the collected answer, or +nil+ for display verbs.
|
|
66
117
|
#
|
|
67
118
|
# @example Collect an answer for the current step, pre-checking LLM suggestions
|
|
@@ -74,9 +125,12 @@ module Inquirex
|
|
|
74
125
|
# @param suggestion [Array, nil] prefill suggestion for the step (option
|
|
75
126
|
# form values, e.g. from Engine#suggestion_for) — multi-select prompts
|
|
76
127
|
# render these choices pre-checked so the user confirms or extends
|
|
128
|
+
# @param default [Object, nil] the answer to pre-fill, e.g. the one the
|
|
129
|
+
# user gave before going back (Engine#previous_answer)
|
|
77
130
|
# @return [Object, nil]
|
|
78
|
-
def render(node, suggestion: nil)
|
|
131
|
+
def render(node, suggestion: nil, default: nil)
|
|
79
132
|
@current_suggestion = suggestion
|
|
133
|
+
@current_default = default
|
|
80
134
|
if node.display?
|
|
81
135
|
render_display_verb(node)
|
|
82
136
|
nil
|
|
@@ -85,10 +139,124 @@ module Inquirex
|
|
|
85
139
|
end
|
|
86
140
|
ensure
|
|
87
141
|
@current_suggestion = nil
|
|
142
|
+
@current_default = nil
|
|
88
143
|
end
|
|
89
144
|
|
|
90
145
|
private
|
|
91
146
|
|
|
147
|
+
# Runs the block with the navigation keys live. The {KeyMap} listens
|
|
148
|
+
# only while the block runs, so a "Press any key" on a display step
|
|
149
|
+
# treats every key as any key.
|
|
150
|
+
#
|
|
151
|
+
# tty-prompt subscribes each question to the reader and unsubscribes it
|
|
152
|
+
# only when the question returns normally, and tty-reader's
|
|
153
|
+
# subscribe-with-a-block does the same. A question unwound by
|
|
154
|
+
# {Navigation} would stay subscribed and go on reacting to every later
|
|
155
|
+
# keypress, so every listener added while the block ran, the KeyMap
|
|
156
|
+
# included, is removed here.
|
|
157
|
+
#
|
|
158
|
+
# @param can_back [Boolean]
|
|
159
|
+
# @return [Object] the block's value
|
|
160
|
+
# @raise [Navigation] when a navigation key is pressed
|
|
161
|
+
def with_keys(can_back)
|
|
162
|
+
return yield unless prompt.respond_to?(:subscribe)
|
|
163
|
+
|
|
164
|
+
listeners = reader_registrations&.to_a
|
|
165
|
+
key_map = KeyMap.new(can_back:)
|
|
166
|
+
prompt.subscribe(key_map)
|
|
167
|
+
yield
|
|
168
|
+
ensure
|
|
169
|
+
if key_map
|
|
170
|
+
drop_listeners_since(listeners)
|
|
171
|
+
prompt.unsubscribe(key_map)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Acts on a navigation key pressed during {#collect}.
|
|
176
|
+
#
|
|
177
|
+
# @param engine [Inquirex::Engine]
|
|
178
|
+
# @param action [Symbol] :back, :skip or :help
|
|
179
|
+
# @return [Symbol] what {#collect} returns
|
|
180
|
+
def navigate(engine, action)
|
|
181
|
+
case action
|
|
182
|
+
when :back then go_back(engine)
|
|
183
|
+
when :skip then skip(engine)
|
|
184
|
+
else
|
|
185
|
+
show_keymap
|
|
186
|
+
:help
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# @param engine [Inquirex::Engine]
|
|
191
|
+
# @return [Symbol] :skipped, or :stay when the question is required
|
|
192
|
+
def skip(engine)
|
|
193
|
+
if engine.current_step.required?
|
|
194
|
+
puts pastel.yellow(" ⚠ This question is required and cannot be skipped.")
|
|
195
|
+
return :stay
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
puts pastel.dim("↷ skipped #{engine.current_step_id}")
|
|
199
|
+
engine.skip
|
|
200
|
+
:skipped
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# @param before [Array, nil] the reader's registrations before the prompt opened
|
|
204
|
+
# @return [void]
|
|
205
|
+
def drop_listeners_since(before)
|
|
206
|
+
live = reader_registrations
|
|
207
|
+
return unless before && live
|
|
208
|
+
|
|
209
|
+
(live.to_a - before).each { |stale| live.delete(stale) }
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# tty-reader keeps its listeners in Wisper's registration set, which it
|
|
213
|
+
# exposes only privately (its own #unsubscribe reads it the same way).
|
|
214
|
+
#
|
|
215
|
+
# @return [Set, nil]
|
|
216
|
+
def reader_registrations
|
|
217
|
+
reader = prompt.respond_to?(:reader) ? prompt.reader : nil
|
|
218
|
+
return nil unless reader.respond_to?(:local_registrations, true)
|
|
219
|
+
|
|
220
|
+
reader.send(:local_registrations)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Steps the engine back to the previous question. An LLM step between
|
|
224
|
+
# them is stepped over too: it asks the user nothing, and landing on it
|
|
225
|
+
# would rerun the extraction and carry the user straight forward again.
|
|
226
|
+
#
|
|
227
|
+
# @param engine [Inquirex::Engine]
|
|
228
|
+
# @return [Symbol] :back
|
|
229
|
+
def go_back(engine)
|
|
230
|
+
engine.back
|
|
231
|
+
engine.back while llm_step?(engine.current_step) && engine.can_back?
|
|
232
|
+
puts pastel.dim("↩ back to #{engine.current_step_id}")
|
|
233
|
+
:back
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# @param node [Inquirex::Node, nil]
|
|
237
|
+
# @return [Boolean]
|
|
238
|
+
def llm_step?(node) = node.respond_to?(:llm_verb?) && node.llm_verb?
|
|
239
|
+
|
|
240
|
+
# tty-prompt's `default:` option for the value being pre-filled.
|
|
241
|
+
#
|
|
242
|
+
# @param value [Object, nil] defaults to the value passed to #render
|
|
243
|
+
# @return [Hash] {} or { default: value }
|
|
244
|
+
def default_option(value = @current_default)
|
|
245
|
+
value.nil? ? {} : { default: value }
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# select and enum_select preselect by choice NAME, the label the user
|
|
249
|
+
# sees, while answers are option values; translate back.
|
|
250
|
+
#
|
|
251
|
+
# @param node [Inquirex::Node]
|
|
252
|
+
# @return [Hash] {} or { default: label }
|
|
253
|
+
def choice_default(node)
|
|
254
|
+
return {} if @current_default.nil?
|
|
255
|
+
|
|
256
|
+
labels = node.respond_to?(:option_labels) ? node.option_labels : nil
|
|
257
|
+
{ default: labels&.fetch(@current_default.to_s, nil) || @current_default.to_s }
|
|
258
|
+
end
|
|
259
|
+
|
|
92
260
|
# Dispatches display verbs to their styled renderers.
|
|
93
261
|
# :header uses TTY::Font for a large ASCII-art title.
|
|
94
262
|
# :btw and :warning use TTY::Box info/warn boxes.
|
|
@@ -164,13 +332,14 @@ module Inquirex
|
|
|
164
332
|
|
|
165
333
|
# Single-line text.
|
|
166
334
|
def render_text_input(node)
|
|
167
|
-
prompt.ask(node.question)
|
|
335
|
+
prompt.ask(node.question, **default_option)
|
|
168
336
|
end
|
|
169
337
|
|
|
170
338
|
# Multi-line text (empty line to submit).
|
|
171
339
|
def render_multiline(node)
|
|
172
340
|
puts pastel.bold(node.question)
|
|
173
341
|
puts pastel.dim("Enter your response. Press Enter on a blank line to submit.")
|
|
342
|
+
puts pastel.dim("Previous answer:\n#{@current_default}") unless @current_default.nil?
|
|
174
343
|
sep(:cyan, "─")
|
|
175
344
|
collect_multiline_text
|
|
176
345
|
end
|
|
@@ -210,9 +379,9 @@ module Inquirex
|
|
|
210
379
|
# @param convert [Symbol] :int or :float
|
|
211
380
|
# @return [Numeric, nil]
|
|
212
381
|
def ask_bounded(node, convert:)
|
|
213
|
-
return prompt.ask(node.question, convert
|
|
382
|
+
return prompt.ask(node.question, convert:, **default_option) unless node.bounded?
|
|
214
383
|
|
|
215
|
-
prompt.ask(node.question, convert:, in: bounds_range(node)) do |q|
|
|
384
|
+
prompt.ask(node.question, convert:, in: bounds_range(node), **default_option) do |q|
|
|
216
385
|
q.messages[:range?] = "Enter a value #{describe_bounds(node)}."
|
|
217
386
|
end
|
|
218
387
|
end
|
|
@@ -239,16 +408,17 @@ module Inquirex
|
|
|
239
408
|
|
|
240
409
|
# Boolean — tty-prompt yes?.
|
|
241
410
|
def render_yes_no(node)
|
|
242
|
-
prompt.yes?(node.question)
|
|
411
|
+
prompt.yes?(node.question, **default_option)
|
|
243
412
|
end
|
|
244
413
|
|
|
245
414
|
# Single-choice scrollable menu.
|
|
246
415
|
def render_select(node)
|
|
247
|
-
prompt.select(node.question, select_options(node))
|
|
416
|
+
prompt.select(node.question, select_options(node), **choice_default(node))
|
|
248
417
|
end
|
|
249
418
|
|
|
250
419
|
# Multiple-choice list (space to toggle, min 1 selection). Suggested
|
|
251
|
-
# choices (an LLM extraction the user should confirm) render pre-checked
|
|
420
|
+
# choices (an LLM extraction the user should confirm) render pre-checked,
|
|
421
|
+
# and after going back, the choices the user had made.
|
|
252
422
|
def render_multi_select(node)
|
|
253
423
|
prompt.multi_select(node.question, select_options(node), min: 1, **multi_select_defaults(node))
|
|
254
424
|
end
|
|
@@ -261,7 +431,7 @@ module Inquirex
|
|
|
261
431
|
# @param node [Inquirex::Node]
|
|
262
432
|
# @return [Hash] {} or { default: Array<String> }
|
|
263
433
|
def multi_select_defaults(node)
|
|
264
|
-
suggested = Array(@current_suggestion)
|
|
434
|
+
suggested = Array(@current_default.nil? ? @current_suggestion : @current_default)
|
|
265
435
|
return {} if suggested.empty?
|
|
266
436
|
|
|
267
437
|
labels = node.respond_to?(:option_labels) ? node.option_labels : nil
|
|
@@ -271,7 +441,7 @@ module Inquirex
|
|
|
271
441
|
|
|
272
442
|
# Numbered choice menu.
|
|
273
443
|
def render_enum_select(node)
|
|
274
|
-
prompt.enum_select(node.question, select_options(node))
|
|
444
|
+
prompt.enum_select(node.question, select_options(node), **choice_default(node))
|
|
275
445
|
end
|
|
276
446
|
|
|
277
447
|
# Hidden / masked input.
|
|
@@ -288,7 +458,7 @@ module Inquirex
|
|
|
288
458
|
opts[:max] = hint_opts[:max] if hint_opts[:max]
|
|
289
459
|
opts[:step] = hint_opts[:step] if hint_opts[:step]
|
|
290
460
|
end
|
|
291
|
-
prompt.slider(node.question, **opts)
|
|
461
|
+
prompt.slider(node.question, **opts, **default_option)
|
|
292
462
|
end
|
|
293
463
|
|
|
294
464
|
# Aliases: date, email, phone degrade to plain text_input in TTY.
|
|
@@ -39,13 +39,14 @@ module Inquirex
|
|
|
39
39
|
# @param title [String, nil] optional title on the top border
|
|
40
40
|
# @param bg [Symbol] background color name
|
|
41
41
|
# @param fg [Symbol] foreground color name
|
|
42
|
+
# @param align [Symbol] :center, :left or :right
|
|
42
43
|
# @return [void]
|
|
43
|
-
def box(text, title: nil, bg: :green, fg: :white) # rubocop:disable Naming/MethodParameterName
|
|
44
|
+
def box(text, title: nil, bg: :green, fg: :white, align: :center) # rubocop:disable Naming/MethodParameterName
|
|
44
45
|
w = [width, 80].min
|
|
45
46
|
args = {
|
|
46
47
|
width: w,
|
|
47
48
|
padding: { top: 0, bottom: 0, left: 1, right: 1 },
|
|
48
|
-
align:
|
|
49
|
+
align: align,
|
|
49
50
|
style: { fg: fg, bg: bg,
|
|
50
51
|
border: { type: :thin, fg: fg, bg: bg } }
|
|
51
52
|
}
|
data/lib/inquirex/tty/version.rb
CHANGED
data/lib/inquirex/tty.rb
CHANGED
|
@@ -40,6 +40,8 @@ require_relative "tty/version"
|
|
|
40
40
|
require_relative "tty/ui_helper"
|
|
41
41
|
require_relative "tty/flow_loader"
|
|
42
42
|
require_relative "tty/output_path"
|
|
43
|
+
require_relative "tty/answers_file"
|
|
44
|
+
require_relative "tty/key_map"
|
|
43
45
|
require_relative "tty/renderer"
|
|
44
46
|
require_relative "tty/commands/run"
|
|
45
47
|
require_relative "tty/commands/validate"
|