inquirex-tty 0.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 18c484c5cbd16a7bfa1563c39d859fe48a3e2d783d5a06b186c1fae848703780
4
- data.tar.gz: f9f2a1e74c8db8028db8b5d1581ff35b94dd376caf5fbd1acf96ce7b963c72ec
3
+ metadata.gz: ef0bce06b980c6e167a092cff784d1f63f2eea80d72b78384841455baac1865c
4
+ data.tar.gz: 87306ae23bf1cb5472d8913970b08c26e420cc1ee5ae3659974195aa0639c97c
5
5
  SHA512:
6
- metadata.gz: ffd4ae10ed877234d10bdba11d68f82d436d6422bac2d5471759ce14f0ed26ac9fe046aec0ed994693b7f989299ddf13f188c6e8122ec8263ff5fa49b7f79646
7
- data.tar.gz: 5290b611af119b6222df018d542e3e41a03d169efdf10cb135f5d565e303e03e5fcdc270f5f778c32856b95f34eed9762bfdc2e40dbd9879e2d0839ab449ef48
6
+ metadata.gz: 288b6c184460ea72e6e75df023ee6840ebc4a805eb47607c2e0735235f02d43842be5526219dde5a0caae18668017c87430d51995f412e55458b938dd69b1abe
7
+ data.tar.gz: a178284089b5f66e270d6f9aa4c940be8445edd9d09b2b04ac7b8cec5c132b637ec3575567423c609cde82cddb13af9fe1575512dd7c84d9806c299d826c5b32
data/CHANGELOG.md CHANGED
@@ -5,11 +5,12 @@
5
5
  Hand-written until `github_changelog_generator` regenerates this file at release. The family-wide notes are in inquirex's `INQUIREX_VERSION_LOG.md`.
6
6
 
7
7
  - **BREAKING: results go to stdout.** `run` printed its JSON results on stderr; it now prints them on stdout, and draws every prompt, box and progress line on stderr, so `inqt run flow.rb > answers.json` captures a clean document. A caller that read the results from stderr must read stdout. `-o FILE` still writes a file (and now leaves stdout empty); `-o -` names stdout.
8
- - **Ctrl-B goes back.** At any question after the first, Ctrl-B returns to the previous question with the earlier answer as its default (`Engine#back`, `#can_back?`, `#previous_answer` from inquirex 0.12.0). A tip line under the question names the key. Display and LLM steps in between are stepped over. Cmd-Backspace was considered and rejected: terminals do not send it to a program unless the user maps it by hand.
8
+ - **Going back, skipping and a keymap.** At any question after the first, Backspace on an empty answer (or anywhere in a list) or Ctrl-B returns to the previous question with the earlier answer as its default (`Engine#back`, `#can_back?`, `#previous_answer` from inquirex 0.12). Ctrl-S skips an optional question (`Engine#skip`) and is refused on a required one. `?` on an empty answer shows the keymap. A hint line above each question lists the keys. Display and LLM steps in between are stepped over. Cmd-Backspace was considered and rejected: terminals do not send it to a program unless the user maps it by hand.
9
+ - **Ctrl-C aborts cleanly** with exit status 130 and a one-line message, instead of a stack trace.
9
10
  - **Headless runs: `run --answers FILE` and `--yes`.** Preset answers (`{step_id: value}`, JSON or YAML by extension) are fed to the engine through `Inquirex::Headless`; no prompt or renderer is built, standard input is never read, no LLM adapter is called, and the public-IP probe is skipped. `--yes` answers the remaining steps with their defaults. A required step left without an answer exits 1 and names the step on stderr.
10
11
  - **Adapter selection comes from inquirex-llm.** `run` builds its LLM adapter with `Inquirex::LLM.adapter`, re-reading inquirex-llm's configuration after `.env` loads. The documented `INQUIREX_LLM_ADAPTER` / `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` order is unchanged, and the default models and limits now follow inquirex-llm's `INQUIREX_LLM_*` variables. An unknown `INQUIREX_LLM_ADAPTER` value now raises an error naming the variable instead of falling through to auto-detection.
11
12
  - Step numbers in the progress line start at 1 again (the first step printed as "Step 2").
12
- - **Requires `inquirex ~> 0.12` and `inquirex-llm ~> 0.12`.** 0.10.0 still required `~> 0.9.5` of both, so it could not be installed beside the 0.10.0 core.
13
+ - **Requires `inquirex ~> 0.12` and `inquirex-llm ~> 0.12`** (locked at 0.12.1; the `openrouter` and `jev` adapters it adds are selectable through `INQUIREX_LLM_ADAPTER`). `irb` is no longer in the Gemfile: through rdoc it pulled in `erb`, a default gem that a local bundle locks with an empty checksum and a frozen CI install then rejects. 0.10.0 still required `~> 0.9.5` of both, so it could not be installed beside the 0.10.0 core.
13
14
  - README samples use a fictional person (Alan Turing, `alan.turing@manchester.edu`), as does `examples/10_answers.json`.
14
15
  - Ruby 4.1.0-preview1: the suite passes with Bundler 4.0.16; Gemfile.lock moves off Bundler 4.0.10, which fails on 4.1 with `uninitialized constant Pathname::SEPARATOR_PAT`. `spec/inquirex/tty/gemspec_spec.rb` fails when `lib/` requires a library that neither ships with the running Ruby nor is a declared runtime dependency.
15
16
  - CircleCI config copied from inquirex core; `.ruby-version` pins 4.1.0-preview1.
data/README.md CHANGED
@@ -83,7 +83,19 @@ Options:
83
83
 
84
84
  **Results channel.** The results JSON goes to stdout, and only the results: every prompt, box and progress line of an interactive run is drawn on stderr. `-o FILE` writes the results to the file instead and leaves stdout empty. Until 0.12.0 the results went to stderr.
85
85
 
86
- **Going back.** Press **Ctrl-B** at any question to return to the previous one, with your earlier answer filled in as the default. The tip line under each question after the first names the key. Display steps and LLM steps in between are stepped over.
86
+ **Keys.** A dim hint line above each question lists the navigation keys (`⌫ back · ^S skip · ? keys · ^C abort`); press `?` for the full keymap.
87
+
88
+ | Key | Action |
89
+ | --------------- | ----------------------------------------------------------------------- |
90
+ | `Enter` | submit the answer; an empty answer keeps the one shown in parentheses |
91
+ | `Backspace` | go back to the previous question (on an empty answer; always in a list) |
92
+ | `Ctrl-B` | go back to the previous question |
93
+ | `Ctrl-S` | skip the question (optional questions only) |
94
+ | `↑` `↓` `Space` | move and toggle in a list |
95
+ | `?` | show the keymap (on an empty answer; always in a list) |
96
+ | `Ctrl-C` | abort without saving (exit status 130) |
97
+
98
+ **Going back** returns to the previous question with your earlier answer filled in as the default. Display steps and LLM steps in between are stepped over. On the first question the back keys do nothing, and `Backspace` with text typed simply deletes a character.
87
99
 
88
100
  **Headless runs.** With `--answers` or `--yes` the flow runs without a terminal: no prompt is drawn, standard input is never read, and no LLM adapter is called, which makes it the way to run a flow in tests and CI.
89
101
 
@@ -454,8 +466,8 @@ inquirex-tty/
454
466
  │ ├── graph.rb # Mermaid diagram export
455
467
  │ ├── export.rb # JSON / YAML serialization
456
468
  │ └── version.rb # Print version info
457
- ├── renderer.rb # Node → tty-prompt widget dispatcher, Ctrl-B back
458
- ├── back_key.rb # The back key and the exception that unwinds a prompt
469
+ ├── renderer.rb # Node → tty-prompt widget dispatcher, back/skip/help
470
+ ├── key_map.rb # Navigation keys and the exception that unwinds a prompt
459
471
  ├── answers_file.rb # --answers: JSON or YAML preset answers
460
472
  ├── flow_loader.rb # Load .rb flow definitions
461
473
  ├── output_path.rb # Shared -o/--output path resolution
@@ -467,7 +479,7 @@ inquirex-tty/
467
479
 
468
480
  `Inquirex::TTY::Renderer` is the core class that maps nodes to tty-prompt calls. It reads the `:tty` widget hint from each node (via `WidgetRegistry` defaults or explicit DSL hints) and dispatches to the matching `render_*` method.
469
481
 
470
- `Renderer#collect(engine)` asks the engine's current step and answers it, or, when the user presses Ctrl-B, calls `Engine#back` and returns `:back`; the run loop then asks the previous question again with `Engine#previous_answer` as its default. Ctrl-B raises `Inquirex::TTY::BackKey::Pressed` from a handler on tty-reader's `:keyctrl_b` event, which unwinds whichever tty-prompt widget is open.
482
+ `Renderer#collect(engine)` asks the engine's current step and answers it, unless the user presses a navigation key: back calls `Engine#back` and returns `:back` (the run loop then asks the previous question again with `Engine#previous_answer` as its default), skip calls `Engine#skip` on an optional step and returns `:skipped` (`:stay` on a required one), and `?` shows the keymap and returns `:help`. `Inquirex::TTY::KeyMap`, a tty-reader listener, raises `Inquirex::TTY::Navigation` for the key, which unwinds whichever tty-prompt widget is open.
471
483
 
472
484
  The `TTY::Prompt` instance is injectable for testing:
473
485
 
@@ -29,6 +29,9 @@ module Inquirex
29
29
  def call(flow_file:, **options)
30
30
  json = on_stderr { JSON.pretty_generate(results_for(flow_file, **options)) }
31
31
  write_results(json, options[:output])
32
+ rescue ::TTY::Reader::InputInterrupt
33
+ on_stderr { error("Aborted — no answers were saved.") }
34
+ exit 130
32
35
  rescue Inquirex::TTY::Error => e
33
36
  on_stderr { error(e.message) }
34
37
  exit 1
@@ -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
@@ -25,9 +25,9 @@ module Inquirex
25
25
  # Header display verb uses TTY::Font for large ASCII-art section titles.
26
26
  #
27
27
  # {#collect} drives one collecting step against an engine and binds the
28
- # back key ({BackKey}): pressed in any open prompt, it steps the engine
29
- # back and the caller's loop re-asks the previous question with its
30
- # answer as the default.
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.
31
31
  class Renderer
32
32
  include UIHelper
33
33
 
@@ -67,14 +67,16 @@ module Inquirex
67
67
  sep(:magenta, "─")
68
68
  end
69
69
 
70
- # Asks the engine's current collecting step and answers it. When the
71
- # user presses the back key instead, the engine steps back and nothing
72
- # is answered; the caller's loop then asks the previous question again,
73
- # pre-filled with the answer it had.
70
+ # Asks the engine's current collecting step and answers it, unless the
71
+ # user presses a navigation key ({KeyMap}) instead:
74
72
  #
75
- # The tip line naming the key appears only when there is somewhere to
76
- # go back to, and the key is live only then: on the first question it
77
- # does nothing.
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.
78
80
  #
79
81
  # @example Drive a flow's collecting steps
80
82
  # until engine.finished?
@@ -82,22 +84,33 @@ module Inquirex
82
84
  # end
83
85
  #
84
86
  # @param engine [Inquirex::Engine] positioned on a collecting step
85
- # @return [Symbol] +:answered+, or +:back+ when the engine stepped back
87
+ # @return [Symbol] +:answered+, +:back+, +:skipped+, +:help+, or +:stay+
88
+ # when a required question refused a skip
86
89
  def collect(engine)
87
90
  step_id = engine.current_step_id
88
91
  can_back = engine.can_back?
89
- puts pastel.dim("#{BackKey::LABEL}: back to the previous question") if can_back
92
+ puts pastel.dim(KeyMap.hint(can_back:))
90
93
 
91
- answer = with_back_key(can_back) do
94
+ answer = with_keys(can_back) do
92
95
  render(engine.current_step,
93
96
  suggestion: engine.suggestion_for(step_id),
94
97
  default: engine.previous_answer(step_id))
95
98
  end
96
99
  engine.answer(answer)
97
100
  :answered
98
- rescue BackKey::Pressed
99
- go_back(engine)
100
- :back
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..."))
101
114
  end
102
115
 
103
116
  # Renders a node. Returns the collected answer, or +nil+ for display verbs.
@@ -131,40 +144,60 @@ module Inquirex
131
144
 
132
145
  private
133
146
 
134
- # Runs the block with the back key live, when +enabled+.
135
- #
136
- # The handler is registered on the prompt's reader once and consulted
137
- # through @back_armed, so the key raises only inside a collecting
138
- # prompt: a "Press any key" on a display step treats it as any key.
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.
139
150
  #
140
151
  # tty-prompt subscribes each question to the reader and unsubscribes it
141
- # only when the question returns normally. A question unwound by
142
- # {BackKey::Pressed} would stay subscribed and go on reacting to every
143
- # later keypress, so the listeners it added are removed here.
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.
144
157
  #
145
- # @param enabled [Boolean]
158
+ # @param can_back [Boolean]
146
159
  # @return [Object] the block's value
147
- # @raise [BackKey::Pressed] when the key is pressed
148
- def with_back_key(enabled)
149
- return yield unless enabled && prompt.respond_to?(:on)
160
+ # @raise [Navigation] when a navigation key is pressed
161
+ def with_keys(can_back)
162
+ return yield unless prompt.respond_to?(:subscribe)
150
163
 
151
- bind_back_key
152
164
  listeners = reader_registrations&.to_a
153
- @back_armed = true
165
+ key_map = KeyMap.new(can_back:)
166
+ prompt.subscribe(key_map)
154
167
  yield
155
- rescue BackKey::Pressed
156
- drop_listeners_since(listeners)
157
- raise
158
168
  ensure
159
- @back_armed = false
169
+ if key_map
170
+ drop_listeners_since(listeners)
171
+ prompt.unsubscribe(key_map)
172
+ end
160
173
  end
161
174
 
162
- # @return [void]
163
- def bind_back_key
164
- return if @back_key_bound
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
165
189
 
166
- prompt.on(BackKey::EVENT) { |*| raise BackKey::Pressed if @back_armed }
167
- @back_key_bound = true
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
168
201
  end
169
202
 
170
203
  # @param before [Array, nil] the reader's registrations before the prompt opened
@@ -192,11 +225,12 @@ module Inquirex
192
225
  # would rerun the extraction and carry the user straight forward again.
193
226
  #
194
227
  # @param engine [Inquirex::Engine]
195
- # @return [void]
228
+ # @return [Symbol] :back
196
229
  def go_back(engine)
197
230
  engine.back
198
231
  engine.back while llm_step?(engine.current_step) && engine.can_back?
199
232
  puts pastel.dim("↩ back to #{engine.current_step_id}")
233
+ :back
200
234
  end
201
235
 
202
236
  # @param node [Inquirex::Node, nil]
@@ -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: :center,
49
+ align: align,
49
50
  style: { fg: fg, bg: bg,
50
51
  border: { type: :thin, fg: fg, bg: bg } }
51
52
  }
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Inquirex
4
4
  module TTY
5
- VERSION = "0.12.0"
5
+ VERSION = "0.12.1"
6
6
  end
7
7
  end
data/lib/inquirex/tty.rb CHANGED
@@ -41,7 +41,7 @@ require_relative "tty/ui_helper"
41
41
  require_relative "tty/flow_loader"
42
42
  require_relative "tty/output_path"
43
43
  require_relative "tty/answers_file"
44
- require_relative "tty/back_key"
44
+ require_relative "tty/key_map"
45
45
  require_relative "tty/renderer"
46
46
  require_relative "tty/commands/run"
47
47
  require_relative "tty/commands/validate"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inquirex-tty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.12.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Gredeskoul
@@ -195,7 +195,6 @@ files:
195
195
  - lefthook.yml
196
196
  - lib/inquirex/tty.rb
197
197
  - lib/inquirex/tty/answers_file.rb
198
- - lib/inquirex/tty/back_key.rb
199
198
  - lib/inquirex/tty/commands.rb
200
199
  - lib/inquirex/tty/commands/export.rb
201
200
  - lib/inquirex/tty/commands/graph.rb
@@ -203,6 +202,7 @@ files:
203
202
  - lib/inquirex/tty/commands/validate.rb
204
203
  - lib/inquirex/tty/commands/version.rb
205
204
  - lib/inquirex/tty/flow_loader.rb
205
+ - lib/inquirex/tty/key_map.rb
206
206
  - lib/inquirex/tty/output_path.rb
207
207
  - lib/inquirex/tty/renderer.rb
208
208
  - lib/inquirex/tty/ui_helper.rb
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Inquirex
4
- module TTY
5
- # The key that takes a TTY run back to the previous question.
6
- #
7
- # Ctrl-B, because it arrives: tty-reader maps byte 0x02 to +:ctrl_b+ in
8
- # every terminal, and tty-prompt binds nothing to it. Cmd-Backspace, the
9
- # first candidate, reaches a program only where the user has mapped it by
10
- # hand (iTerm2 sends nothing by default), so a run could never rely on it.
11
- module BackKey
12
- # @return [Symbol] the tty-reader key name
13
- NAME = :ctrl_b
14
-
15
- # @return [Symbol] the event tty-reader publishes when the key is pressed
16
- EVENT = :"key#{NAME}"
17
-
18
- # @return [String] how the tip line names the key
19
- LABEL = "Ctrl-B"
20
-
21
- # Raised from inside a tty-prompt read loop when the back key is pressed,
22
- # so whichever widget is open (ask, select, multi_select, yes?) unwinds
23
- # without returning an answer. tty-prompt offers no hook that aborts a
24
- # question, and a subclass per widget would mean overriding the read
25
- # loop of each one; one exception covers them all.
26
- class Pressed < StandardError
27
- def initialize(msg = "#{LABEL} pressed") = super
28
- end
29
- end
30
- end
31
- end