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 +4 -4
- data/CHANGELOG.md +3 -2
- data/README.md +16 -4
- data/lib/inquirex/tty/commands/run.rb +3 -0
- data/lib/inquirex/tty/key_map.rb +127 -0
- data/lib/inquirex/tty/renderer.rb +74 -40
- data/lib/inquirex/tty/ui_helper.rb +3 -2
- data/lib/inquirex/tty/version.rb +1 -1
- data/lib/inquirex/tty.rb +1 -1
- metadata +2 -2
- data/lib/inquirex/tty/back_key.rb +0 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ef0bce06b980c6e167a092cff784d1f63f2eea80d72b78384841455baac1865c
|
|
4
|
+
data.tar.gz: 87306ae23bf1cb5472d8913970b08c26e420cc1ee5ae3659974195aa0639c97c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
- **
|
|
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
|
|
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
|
-
**
|
|
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,
|
|
458
|
-
├──
|
|
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,
|
|
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
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
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
|
|
71
|
-
# user presses
|
|
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
|
-
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
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+,
|
|
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(
|
|
92
|
+
puts pastel.dim(KeyMap.hint(can_back:))
|
|
90
93
|
|
|
91
|
-
answer =
|
|
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
|
|
99
|
-
|
|
100
|
-
|
|
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
|
|
135
|
-
#
|
|
136
|
-
#
|
|
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
|
|
142
|
-
#
|
|
143
|
-
#
|
|
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
|
|
158
|
+
# @param can_back [Boolean]
|
|
146
159
|
# @return [Object] the block's value
|
|
147
|
-
# @raise [
|
|
148
|
-
def
|
|
149
|
-
return yield unless
|
|
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
|
-
|
|
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
|
-
|
|
169
|
+
if key_map
|
|
170
|
+
drop_listeners_since(listeners)
|
|
171
|
+
prompt.unsubscribe(key_map)
|
|
172
|
+
end
|
|
160
173
|
end
|
|
161
174
|
|
|
162
|
-
#
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
|
|
167
|
-
|
|
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 [
|
|
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:
|
|
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
|
@@ -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/
|
|
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.
|
|
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
|