clack 0.1.4 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -1
- data/README.md +108 -8
- data/examples/advanced_prompts.rb +63 -0
- data/examples/basic.rb +15 -0
- data/examples/create_app.rb +86 -0
- data/examples/date_demo.rb +40 -0
- data/examples/demo.rb +179 -0
- data/examples/full_demo.rb +84 -0
- data/examples/group_demo.rb +79 -0
- data/examples/images/confirm_example.rb +12 -0
- data/examples/images/multiselect_example.rb +15 -0
- data/examples/images/password_example.rb +10 -0
- data/examples/images/select_example.rb +15 -0
- data/examples/images/spinner_example.rb +11 -0
- data/examples/images/text_example.rb +11 -0
- data/examples/spinner_demo.rb +38 -0
- data/examples/tasks_demo.rb +59 -0
- data/examples/validation.rb +73 -0
- data/lib/clack/colors.rb +97 -3
- data/lib/clack/core/cursor.rb +1 -0
- data/lib/clack/core/key_reader.rb +5 -0
- data/lib/clack/core/prompt.rb +52 -8
- data/lib/clack/core/settings.rb +4 -0
- data/lib/clack/core/text_input_helper.rb +4 -0
- data/lib/clack/log.rb +51 -0
- data/lib/clack/note.rb +7 -0
- data/lib/clack/prompts/autocomplete.rb +29 -11
- data/lib/clack/prompts/autocomplete_multiselect.rb +5 -6
- data/lib/clack/prompts/date.rb +280 -0
- data/lib/clack/prompts/group_multiselect.rb +46 -18
- data/lib/clack/prompts/multiline_text.rb +8 -9
- data/lib/clack/prompts/multiselect.rb +3 -5
- data/lib/clack/prompts/password.rb +5 -10
- data/lib/clack/prompts/path.rb +2 -2
- data/lib/clack/prompts/progress.rb +2 -6
- data/lib/clack/prompts/select.rb +2 -6
- data/lib/clack/prompts/select_key.rb +5 -7
- data/lib/clack/prompts/spinner.rb +2 -6
- data/lib/clack/prompts/tasks.rb +50 -9
- data/lib/clack/prompts/text.rb +4 -3
- data/lib/clack/stream.rb +32 -3
- data/lib/clack/symbols.rb +25 -0
- data/lib/clack/task_log.rb +3 -5
- data/lib/clack/transformers.rb +8 -7
- data/lib/clack/validators.rb +33 -2
- data/lib/clack/version.rb +2 -1
- data/lib/clack.rb +72 -213
- metadata +18 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9b2beeffde4af3c88491d8b659dd781e8bd3e652e06f8c159a3ca7910fc69c87
|
|
4
|
+
data.tar.gz: 86b779a202099d27cf702009ee649e19a49b11305e10d2033cf7a0425fe38e10
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9f8b20f670252b17132fcb664eeeaa3ba06b8a8fa0abbee5fdb8e40d17460f2e59badeda5b11b20aaa82b7e9b4b0fef6d06524933794dd20a3c6fcd159cc2115
|
|
7
|
+
data.tar.gz: 21642c6a8cea0671cff152932e806ea7a83163612ecd35739fa2e2bd98179b5e5b714ae6de3e5a2153206ac5f3b52f02ec2e28fc82541056a726d60071afbdab
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [
|
|
3
|
+
## [0.2.0] - 2026-02-19
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `date` prompt for inline segmented date selection with Tab/arrow navigation and digit typing
|
|
7
|
+
- Date min/max enforcement: clamps values to bounds during navigation
|
|
8
|
+
- Date-specific validators: `Validators.future_date`, `Validators.past_date`, `Validators.date_range`
|
|
9
|
+
- `autocomplete` now accepts `filter:` proc for custom matching logic
|
|
10
|
+
- `tasks` now passes a message-update proc to task callbacks for mid-task status updates
|
|
11
|
+
- `tasks` now supports `enabled:` flag to conditionally skip tasks
|
|
12
|
+
- 100% YARD documentation coverage (was 79%)
|
|
13
|
+
- 30 new edge-case tests covering warning validation, transforms, date boundaries, and more
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- `Transformers.resolve` now accepts any object responding to `#call` (not just Proc)
|
|
17
|
+
- Standardized required-validation error messages across multiselect variants
|
|
18
|
+
- Extracted `dispatch_key` from `handle_key` in base `Prompt` so warning/error state transitions are handled centrally for all prompts
|
|
19
|
+
- Ruby idiom improvements: pattern matching, endless methods, guard clauses, `find_index`
|
|
20
|
+
- Gemspec now excludes dev-only files (cast, exp, gif, svg) from the gem package
|
|
21
|
+
- Examples include `require "clack"` alternative comment for gem users
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- `Password#handle_input` now correctly handles backspace (was unreachable due to guard order)
|
|
25
|
+
- `Validators.as_warning` no longer double-wraps values that are already `Warning` instances
|
|
26
|
+
- `AutocompleteMultiselect` backspace was dead code (printable guard blocked it)
|
|
27
|
+
- `MultilineText` and `Autocomplete` now render warning validation messages (was error-only)
|
|
28
|
+
- Removed dead `@buffer` and `@name` instance variables from `TaskLogGroup`
|
|
4
29
|
|
|
5
30
|
## [0.1.4] - 2026-01-23
|
|
6
31
|
|
data/README.md
CHANGED
|
@@ -95,9 +95,16 @@ agg examples/demo.cast examples/demo.gif --font-size 18 --cols 80 --rows 28 --sp
|
|
|
95
95
|
All prompts return the user's input, or `Clack::CANCEL` if they pressed Escape/Ctrl+C.
|
|
96
96
|
|
|
97
97
|
```ruby
|
|
98
|
-
#
|
|
98
|
+
# Check for cancellation
|
|
99
99
|
result = Clack.text(message: "Name?")
|
|
100
100
|
exit 1 if Clack.cancel?(result)
|
|
101
|
+
|
|
102
|
+
# Or use handle_cancel for a one-liner that prints "Cancelled" and returns true
|
|
103
|
+
result = Clack.text(message: "Name?")
|
|
104
|
+
exit 1 if Clack.handle_cancel(result)
|
|
105
|
+
|
|
106
|
+
# With a custom message
|
|
107
|
+
exit 1 if Clack.handle_cancel(result, "Aborted by user")
|
|
101
108
|
```
|
|
102
109
|
|
|
103
110
|
### Validation & Transforms
|
|
@@ -144,15 +151,25 @@ Built-in validators and transformers:
|
|
|
144
151
|
|
|
145
152
|
```ruby
|
|
146
153
|
# Validators - return error message, Warning, or nil
|
|
147
|
-
Clack::Validators.required
|
|
148
|
-
Clack::Validators.min_length(
|
|
154
|
+
Clack::Validators.required # Non-empty input
|
|
155
|
+
Clack::Validators.min_length(3) # Minimum character count
|
|
156
|
+
Clack::Validators.max_length(100) # Maximum character count
|
|
149
157
|
Clack::Validators.format(/\A[a-z]+\z/, "Only lowercase")
|
|
150
|
-
Clack::Validators.email
|
|
151
|
-
Clack::Validators.
|
|
158
|
+
Clack::Validators.email # Email format (user@host.tld)
|
|
159
|
+
Clack::Validators.url # URL format (http/https)
|
|
160
|
+
Clack::Validators.integer # Integer string ("-5", "42")
|
|
161
|
+
Clack::Validators.in_range(1..100) # Numeric range (parses as int)
|
|
162
|
+
Clack::Validators.one_of(%w[a b c]) # Allowlist check
|
|
163
|
+
Clack::Validators.path_exists # File/dir exists on disk
|
|
164
|
+
Clack::Validators.directory_exists # Directory exists on disk
|
|
165
|
+
Clack::Validators.future_date # Date strictly after today
|
|
166
|
+
Clack::Validators.past_date # Date strictly before today
|
|
167
|
+
Clack::Validators.date_range(min: d1, max: d2) # Date within range
|
|
168
|
+
Clack::Validators.combine(v1, v2) # First error/warning wins
|
|
152
169
|
|
|
153
170
|
# Warning validators - allow user to confirm or edit
|
|
154
|
-
Clack::Validators.file_exists_warning
|
|
155
|
-
Clack::Validators.as_warning(validator)
|
|
171
|
+
Clack::Validators.file_exists_warning # For file overwrite confirmations
|
|
172
|
+
Clack::Validators.as_warning(validator) # Convert any validator to warning
|
|
156
173
|
|
|
157
174
|
# Transformers - normalize the value (use :symbol or Clack::Transformers.name)
|
|
158
175
|
:strip / :trim # Remove leading/trailing whitespace
|
|
@@ -191,6 +208,18 @@ secret = Clack.password(
|
|
|
191
208
|
)
|
|
192
209
|
```
|
|
193
210
|
|
|
211
|
+
### Multiline Text
|
|
212
|
+
|
|
213
|
+
Multi-line text input. Enter inserts a newline, Ctrl+D submits.
|
|
214
|
+
|
|
215
|
+
```ruby
|
|
216
|
+
bio = Clack.multiline_text(
|
|
217
|
+
message: "Tell us about yourself",
|
|
218
|
+
initial_value: "Hello!\n",
|
|
219
|
+
validate: ->(v) { "Too short" if v.strip.length < 10 }
|
|
220
|
+
)
|
|
221
|
+
```
|
|
222
|
+
|
|
194
223
|
### Confirm
|
|
195
224
|
|
|
196
225
|
<img src="examples/images/confirm.svg" alt="Confirm prompt">
|
|
@@ -255,6 +284,13 @@ color = Clack.autocomplete(
|
|
|
255
284
|
options: %w[red orange yellow green blue indigo violet],
|
|
256
285
|
placeholder: "Type to search..."
|
|
257
286
|
)
|
|
287
|
+
|
|
288
|
+
# Custom filter logic (receives option hash and query string)
|
|
289
|
+
cmd = Clack.autocomplete(
|
|
290
|
+
message: "Select command",
|
|
291
|
+
options: commands,
|
|
292
|
+
filter: ->(opt, query) { opt[:label].start_with?(query) }
|
|
293
|
+
)
|
|
258
294
|
```
|
|
259
295
|
|
|
260
296
|
### Autocomplete Multiselect
|
|
@@ -287,6 +323,23 @@ project_dir = Clack.path(
|
|
|
287
323
|
|
|
288
324
|
**Navigation:** Type to filter | `Tab` to autocomplete | `↑↓` to select
|
|
289
325
|
|
|
326
|
+
### Date
|
|
327
|
+
|
|
328
|
+
Segmented date picker with three format modes.
|
|
329
|
+
|
|
330
|
+
```ruby
|
|
331
|
+
date = Clack.date(
|
|
332
|
+
message: "Release date?",
|
|
333
|
+
format: :us, # :iso (YYYY-MM-DD), :us (MM/DD/YYYY), :eu (DD/MM/YYYY)
|
|
334
|
+
initial_value: Date.today + 7,
|
|
335
|
+
min: Date.today,
|
|
336
|
+
max: Date.today + 365,
|
|
337
|
+
validate: ->(d) { "Not a Friday" unless d.friday? }
|
|
338
|
+
)
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Navigation:** `Tab`/`←→` between segments | `↑↓` adjust value | type digits directly
|
|
342
|
+
|
|
290
343
|
### Select Key
|
|
291
344
|
|
|
292
345
|
Quick selection using keyboard shortcuts.
|
|
@@ -320,6 +373,23 @@ spinner.stop("Dependencies installed!")
|
|
|
320
373
|
# Or: spinner.cancel("Cancelled")
|
|
321
374
|
```
|
|
322
375
|
|
|
376
|
+
**Block form** - wraps a block with automatic success/error handling:
|
|
377
|
+
|
|
378
|
+
```ruby
|
|
379
|
+
result = Clack.spin("Installing dependencies...") { system("npm install") }
|
|
380
|
+
|
|
381
|
+
# With custom messages
|
|
382
|
+
Clack.spin("Compiling...", success: "Build complete!") { build_project }
|
|
383
|
+
|
|
384
|
+
# Access the spinner inside the block
|
|
385
|
+
Clack.spin("Working...") do |s|
|
|
386
|
+
s.message "Step 1..."
|
|
387
|
+
do_step_1
|
|
388
|
+
s.message "Step 2..."
|
|
389
|
+
do_step_2
|
|
390
|
+
end
|
|
391
|
+
```
|
|
392
|
+
|
|
323
393
|
### Progress
|
|
324
394
|
|
|
325
395
|
Visual progress bar for measurable operations.
|
|
@@ -346,6 +416,22 @@ results = Clack.tasks(tasks: [
|
|
|
346
416
|
{ title: "Building project", task: -> { build } },
|
|
347
417
|
{ title: "Running tests", task: -> { run_tests } }
|
|
348
418
|
])
|
|
419
|
+
|
|
420
|
+
# Update the spinner message mid-task
|
|
421
|
+
Clack.tasks(tasks: [
|
|
422
|
+
{ title: "Installing", task: ->(message) {
|
|
423
|
+
message.call("Fetching packages...")
|
|
424
|
+
fetch_packages
|
|
425
|
+
message.call("Compiling...")
|
|
426
|
+
compile
|
|
427
|
+
}}
|
|
428
|
+
])
|
|
429
|
+
|
|
430
|
+
# Conditionally skip tasks with enabled:
|
|
431
|
+
Clack.tasks(tasks: [
|
|
432
|
+
{ title: "Lint", task: -> { lint } },
|
|
433
|
+
{ title: "Deploy", task: -> { deploy }, enabled: ENV["DEPLOY"] == "true" }
|
|
434
|
+
])
|
|
349
435
|
```
|
|
350
436
|
|
|
351
437
|
### Group Multiselect
|
|
@@ -370,7 +456,9 @@ features = Clack.group_multiselect(
|
|
|
370
456
|
{ value: "solid_queue", label: "Solid Queue" }
|
|
371
457
|
]
|
|
372
458
|
}
|
|
373
|
-
]
|
|
459
|
+
],
|
|
460
|
+
selectable_groups: true, # Toggle all options in a group at once
|
|
461
|
+
group_spacing: 1 # Blank lines between groups
|
|
374
462
|
)
|
|
375
463
|
```
|
|
376
464
|
|
|
@@ -485,6 +573,18 @@ Clack.outro("Done!") # └ Done!
|
|
|
485
573
|
Clack.cancel("Aborted") # └ Aborted (red)
|
|
486
574
|
```
|
|
487
575
|
|
|
576
|
+
## Configuration
|
|
577
|
+
|
|
578
|
+
Customize key bindings and display options globally:
|
|
579
|
+
|
|
580
|
+
```ruby
|
|
581
|
+
# Add custom key bindings (merged with defaults)
|
|
582
|
+
Clack.update_settings(aliases: { "y" => :enter, "n" => :cancel })
|
|
583
|
+
|
|
584
|
+
# Disable guide bars
|
|
585
|
+
Clack.update_settings(with_guide: false)
|
|
586
|
+
```
|
|
587
|
+
|
|
488
588
|
## Requirements
|
|
489
589
|
|
|
490
590
|
- Ruby 3.2+
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Demonstrates advanced prompt types: multiline_text, path, select_key, autocomplete
|
|
5
|
+
# Run with: ruby examples/advanced_prompts.rb
|
|
6
|
+
|
|
7
|
+
require_relative "../lib/clack" # Or: require "clack" if installed as a gem
|
|
8
|
+
|
|
9
|
+
Clack.intro "advanced-prompts"
|
|
10
|
+
|
|
11
|
+
# Multi-line text with initial value (Ctrl+D to submit)
|
|
12
|
+
description = Clack.multiline_text(
|
|
13
|
+
message: "Project description:",
|
|
14
|
+
initial_value: "A Ruby CLI tool that "
|
|
15
|
+
)
|
|
16
|
+
exit 0 if Clack.cancel?(description)
|
|
17
|
+
|
|
18
|
+
Clack.log.info "Description:\n#{description}"
|
|
19
|
+
|
|
20
|
+
# Path selector (directories only, rooted at home)
|
|
21
|
+
project_dir = Clack.path(
|
|
22
|
+
message: "Choose project directory:",
|
|
23
|
+
root: Dir.home,
|
|
24
|
+
only_directories: true
|
|
25
|
+
)
|
|
26
|
+
exit 0 if Clack.cancel?(project_dir)
|
|
27
|
+
|
|
28
|
+
Clack.log.info "Directory: #{project_dir}"
|
|
29
|
+
|
|
30
|
+
# Select by key press (instant selection)
|
|
31
|
+
action = Clack.select_key(
|
|
32
|
+
message: "What next?",
|
|
33
|
+
options: [
|
|
34
|
+
{value: "init", label: "Initialize repo", key: "i", hint: "git init"},
|
|
35
|
+
{value: "clone", label: "Clone existing", key: "c", hint: "git clone"},
|
|
36
|
+
{value: "open", label: "Open in editor", key: "o"},
|
|
37
|
+
{value: "skip", label: "Skip", key: "s"}
|
|
38
|
+
]
|
|
39
|
+
)
|
|
40
|
+
exit 0 if Clack.cancel?(action)
|
|
41
|
+
|
|
42
|
+
Clack.log.step "Action: #{action}"
|
|
43
|
+
|
|
44
|
+
# Autocomplete with placeholder
|
|
45
|
+
license = Clack.autocomplete(
|
|
46
|
+
message: "Pick a license:",
|
|
47
|
+
placeholder: "Type to search...",
|
|
48
|
+
options: [
|
|
49
|
+
{value: "mit", label: "MIT", hint: "permissive"},
|
|
50
|
+
{value: "apache2", label: "Apache 2.0", hint: "permissive"},
|
|
51
|
+
{value: "gpl3", label: "GPL 3.0", hint: "copyleft"},
|
|
52
|
+
{value: "agpl3", label: "AGPL 3.0", hint: "copyleft"},
|
|
53
|
+
{value: "bsd2", label: "BSD 2-Clause", hint: "permissive"},
|
|
54
|
+
{value: "bsd3", label: "BSD 3-Clause", hint: "permissive"},
|
|
55
|
+
{value: "mpl2", label: "MPL 2.0", hint: "weak copyleft"},
|
|
56
|
+
{value: "unlicense", label: "Unlicense", hint: "public domain"}
|
|
57
|
+
]
|
|
58
|
+
)
|
|
59
|
+
exit 0 if Clack.cancel?(license)
|
|
60
|
+
|
|
61
|
+
Clack.log.success "License: #{license}"
|
|
62
|
+
|
|
63
|
+
Clack.outro "Done!"
|
data/examples/basic.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Run with: ruby examples/basic.rb
|
|
5
|
+
|
|
6
|
+
require_relative "../lib/clack" # Or: require "clack" if installed as a gem
|
|
7
|
+
|
|
8
|
+
Clack.intro "basic-example"
|
|
9
|
+
|
|
10
|
+
name = Clack.text(message: "What is your name?", placeholder: "Anonymous")
|
|
11
|
+
exit 0 if Clack.cancel?(name)
|
|
12
|
+
|
|
13
|
+
Clack.log.success "Hello, #{name}!"
|
|
14
|
+
|
|
15
|
+
Clack.outro "Goodbye!"
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Run with: ruby examples/create_app.rb
|
|
5
|
+
|
|
6
|
+
require_relative "../lib/clack" # Or: require "clack" if installed as a gem
|
|
7
|
+
|
|
8
|
+
Clack.intro "create-my-app"
|
|
9
|
+
|
|
10
|
+
# Project name
|
|
11
|
+
name = Clack.text(
|
|
12
|
+
message: "Project name?",
|
|
13
|
+
placeholder: "my-app",
|
|
14
|
+
validate: ->(v) { "Project name is required" if v.to_s.strip.empty? }
|
|
15
|
+
)
|
|
16
|
+
exit 0 if Clack.cancel?(name)
|
|
17
|
+
|
|
18
|
+
# Framework selection
|
|
19
|
+
framework = Clack.select(
|
|
20
|
+
message: "Pick a framework",
|
|
21
|
+
options: [
|
|
22
|
+
{value: "rails", label: "Ruby on Rails", hint: "full-stack"},
|
|
23
|
+
{value: "sinatra", label: "Sinatra", hint: "micro"},
|
|
24
|
+
{value: "hanami", label: "Hanami"},
|
|
25
|
+
{value: "roda", label: "Roda"}
|
|
26
|
+
]
|
|
27
|
+
)
|
|
28
|
+
exit 0 if Clack.cancel?(framework)
|
|
29
|
+
|
|
30
|
+
# Feature selection
|
|
31
|
+
features = Clack.multiselect(
|
|
32
|
+
message: "Select features",
|
|
33
|
+
options: [
|
|
34
|
+
{value: "api", label: "API Mode"},
|
|
35
|
+
{value: "auth", label: "Authentication"},
|
|
36
|
+
{value: "admin", label: "Admin Panel"},
|
|
37
|
+
{value: "docker", label: "Docker Setup"},
|
|
38
|
+
{value: "ci", label: "GitHub Actions CI"}
|
|
39
|
+
],
|
|
40
|
+
required: false
|
|
41
|
+
)
|
|
42
|
+
exit 0 if Clack.cancel?(features)
|
|
43
|
+
|
|
44
|
+
# Database
|
|
45
|
+
database = Clack.select(
|
|
46
|
+
message: "Select database",
|
|
47
|
+
options: [
|
|
48
|
+
{value: "postgresql", label: "PostgreSQL", hint: "recommended"},
|
|
49
|
+
{value: "mysql", label: "MySQL"},
|
|
50
|
+
{value: "sqlite", label: "SQLite"}
|
|
51
|
+
]
|
|
52
|
+
)
|
|
53
|
+
exit 0 if Clack.cancel?(database)
|
|
54
|
+
|
|
55
|
+
# Confirm
|
|
56
|
+
proceed = Clack.confirm(message: "Create project?")
|
|
57
|
+
exit 0 if Clack.cancel?(proceed)
|
|
58
|
+
|
|
59
|
+
unless proceed
|
|
60
|
+
Clack.outro "Project creation cancelled."
|
|
61
|
+
exit 0
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Simulate installation
|
|
65
|
+
s = Clack.spinner
|
|
66
|
+
s.start "Creating project..."
|
|
67
|
+
sleep 1
|
|
68
|
+
s.message "Installing dependencies..."
|
|
69
|
+
sleep 1
|
|
70
|
+
s.message "Configuring #{framework}..."
|
|
71
|
+
sleep 0.5
|
|
72
|
+
s.stop "Project created!"
|
|
73
|
+
|
|
74
|
+
# Summary
|
|
75
|
+
Clack.log.info "Project: #{name}"
|
|
76
|
+
Clack.log.success "Framework: #{framework}"
|
|
77
|
+
Clack.log.step "Database: #{database}"
|
|
78
|
+
Clack.log.step "Features: #{features.join(", ")}" unless features.empty?
|
|
79
|
+
|
|
80
|
+
Clack.note <<~NOTE, title: "Next Steps"
|
|
81
|
+
cd #{name}
|
|
82
|
+
bundle install
|
|
83
|
+
bin/rails server
|
|
84
|
+
NOTE
|
|
85
|
+
|
|
86
|
+
Clack.outro "Happy coding!"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Run with: ruby examples/date_demo.rb
|
|
5
|
+
|
|
6
|
+
require_relative "../lib/clack" # Or: require "clack" if installed as a gem
|
|
7
|
+
|
|
8
|
+
Clack.intro "date-picker-demo"
|
|
9
|
+
|
|
10
|
+
# Basic date selection
|
|
11
|
+
date = Clack.date(
|
|
12
|
+
message: "When should this deploy?",
|
|
13
|
+
help: "Use Tab/arrows to navigate, Up/Down to adjust, or type digits"
|
|
14
|
+
)
|
|
15
|
+
exit 0 if Clack.cancel?(date)
|
|
16
|
+
|
|
17
|
+
Clack.log.info "Selected: #{date}"
|
|
18
|
+
|
|
19
|
+
# Date with bounds
|
|
20
|
+
bounded_date = Clack.date(
|
|
21
|
+
message: "Pick a date this year",
|
|
22
|
+
format: :us,
|
|
23
|
+
initial_value: Date.today,
|
|
24
|
+
min: Date.new(Date.today.year, 1, 1),
|
|
25
|
+
max: Date.new(Date.today.year, 12, 31)
|
|
26
|
+
)
|
|
27
|
+
exit 0 if Clack.cancel?(bounded_date)
|
|
28
|
+
|
|
29
|
+
Clack.log.info "Bounded date: #{bounded_date}"
|
|
30
|
+
|
|
31
|
+
# Date with custom validation
|
|
32
|
+
future_date = Clack.date(
|
|
33
|
+
message: "Schedule for future date",
|
|
34
|
+
validate: Clack::Validators.future_date
|
|
35
|
+
)
|
|
36
|
+
exit 0 if Clack.cancel?(future_date)
|
|
37
|
+
|
|
38
|
+
Clack.log.success "Scheduled for #{future_date}"
|
|
39
|
+
|
|
40
|
+
Clack.outro "Done!"
|
data/examples/demo.rb
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Demo script showcasing all Clack Ruby prompts
|
|
5
|
+
# Run with: ruby examples/demo.rb
|
|
6
|
+
|
|
7
|
+
require_relative "../lib/clack" # Or: require "clack" if installed as a gem
|
|
8
|
+
|
|
9
|
+
def run_demo
|
|
10
|
+
Clack.intro "clack-demo"
|
|
11
|
+
|
|
12
|
+
result = Clack.group(on_cancel: ->(_) { Clack.cancel("Operation cancelled.") }) do |g|
|
|
13
|
+
g.prompt(:name) do
|
|
14
|
+
Clack.text(
|
|
15
|
+
message: "What is your project named?",
|
|
16
|
+
placeholder: "my-app",
|
|
17
|
+
validate: ->(v) { "Project name is required" if v.to_s.strip.empty? }
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
g.prompt(:directory) do |r|
|
|
22
|
+
Clack.text(
|
|
23
|
+
message: "Where should we create your project?",
|
|
24
|
+
initial_value: "./#{r[:name]}"
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
g.prompt(:template) do
|
|
29
|
+
Clack.select(
|
|
30
|
+
message: "Which template would you like to use?",
|
|
31
|
+
options: [
|
|
32
|
+
{value: "default", label: "Default", hint: "recommended"},
|
|
33
|
+
{value: "minimal", label: "Minimal", hint: "bare bones"},
|
|
34
|
+
{value: "api", label: "API Only", hint: "no frontend"},
|
|
35
|
+
{value: "full", label: "Full Stack", hint: "everything included"}
|
|
36
|
+
]
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
g.prompt(:typescript) do
|
|
41
|
+
Clack.confirm(
|
|
42
|
+
message: "Would you like to use TypeScript?",
|
|
43
|
+
initial_value: true
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
g.prompt(:features) do
|
|
48
|
+
Clack.multiselect(
|
|
49
|
+
message: "Which features would you like to include?",
|
|
50
|
+
options: [
|
|
51
|
+
{value: "eslint", label: "ESLint", hint: "code linting"},
|
|
52
|
+
{value: "prettier", label: "Prettier", hint: "code formatting"},
|
|
53
|
+
{value: "tailwind", label: "Tailwind CSS", hint: "utility-first CSS"},
|
|
54
|
+
{value: "docker", label: "Docker", hint: "containerization"},
|
|
55
|
+
{value: "ci", label: "GitHub Actions", hint: "CI/CD pipeline"}
|
|
56
|
+
],
|
|
57
|
+
initial_values: %w[eslint prettier],
|
|
58
|
+
required: false
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
g.prompt(:package_manager) do
|
|
63
|
+
Clack.select(
|
|
64
|
+
message: "Which package manager do you prefer?",
|
|
65
|
+
options: [
|
|
66
|
+
{value: "npm", label: "npm"},
|
|
67
|
+
{value: "yarn", label: "yarn"},
|
|
68
|
+
{value: "pnpm", label: "pnpm", hint: "recommended"},
|
|
69
|
+
{value: "bun", label: "bun", hint: "fast"}
|
|
70
|
+
],
|
|
71
|
+
initial_value: "pnpm"
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
return if Clack.handle_cancel(result)
|
|
77
|
+
|
|
78
|
+
# Autocomplete prompt
|
|
79
|
+
color = Clack.autocomplete(
|
|
80
|
+
message: "Pick a theme color:",
|
|
81
|
+
options: %w[red orange yellow green blue indigo violet pink cyan magenta]
|
|
82
|
+
)
|
|
83
|
+
return if Clack.handle_cancel(color)
|
|
84
|
+
|
|
85
|
+
# Select key prompt (quick keyboard shortcuts)
|
|
86
|
+
action = Clack.select_key(
|
|
87
|
+
message: "What would you like to do first?",
|
|
88
|
+
options: [
|
|
89
|
+
{value: "dev", label: "Start dev server", key: "d"},
|
|
90
|
+
{value: "build", label: "Build for production", key: "b"},
|
|
91
|
+
{value: "test", label: "Run tests", key: "t"}
|
|
92
|
+
]
|
|
93
|
+
)
|
|
94
|
+
return if Clack.handle_cancel(action)
|
|
95
|
+
|
|
96
|
+
# Path prompt
|
|
97
|
+
config_path = Clack.path(
|
|
98
|
+
message: "Select config directory:",
|
|
99
|
+
only_directories: true
|
|
100
|
+
)
|
|
101
|
+
return if Clack.handle_cancel(config_path)
|
|
102
|
+
|
|
103
|
+
# Group multiselect
|
|
104
|
+
stack = Clack.group_multiselect(
|
|
105
|
+
message: "Select additional integrations:",
|
|
106
|
+
options: [
|
|
107
|
+
{
|
|
108
|
+
label: "Frontend",
|
|
109
|
+
options: [
|
|
110
|
+
{value: "react", label: "React"},
|
|
111
|
+
{value: "vue", label: "Vue"},
|
|
112
|
+
{value: "svelte", label: "Svelte"}
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
label: "Backend",
|
|
117
|
+
options: [
|
|
118
|
+
{value: "express", label: "Express"},
|
|
119
|
+
{value: "fastify", label: "Fastify"},
|
|
120
|
+
{value: "hono", label: "Hono"}
|
|
121
|
+
]
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
label: "Database",
|
|
125
|
+
options: [
|
|
126
|
+
{value: "postgres", label: "PostgreSQL"},
|
|
127
|
+
{value: "mysql", label: "MySQL"},
|
|
128
|
+
{value: "sqlite", label: "SQLite"}
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
required: false
|
|
133
|
+
)
|
|
134
|
+
return if Clack.handle_cancel(stack)
|
|
135
|
+
|
|
136
|
+
# Progress bar
|
|
137
|
+
prog = Clack.progress(total: 100, message: "Downloading assets...")
|
|
138
|
+
prog.start
|
|
139
|
+
20.times do
|
|
140
|
+
sleep 0.03
|
|
141
|
+
prog.advance(5)
|
|
142
|
+
end
|
|
143
|
+
prog.stop("Assets downloaded!")
|
|
144
|
+
|
|
145
|
+
# Tasks
|
|
146
|
+
Clack.tasks(tasks: [
|
|
147
|
+
{title: "Validating configuration", task: -> { sleep 0.3 }},
|
|
148
|
+
{title: "Generating types", task: -> { sleep 0.4 }},
|
|
149
|
+
{title: "Compiling assets", task: -> { sleep 0.3 }}
|
|
150
|
+
])
|
|
151
|
+
|
|
152
|
+
# Spinner
|
|
153
|
+
s = Clack.spinner
|
|
154
|
+
s.start "Installing dependencies via #{result[:package_manager]}..."
|
|
155
|
+
sleep 1.0
|
|
156
|
+
s.message "Configuring #{result[:template]} template..."
|
|
157
|
+
sleep 0.6
|
|
158
|
+
s.stop "Project created successfully!"
|
|
159
|
+
|
|
160
|
+
# Summary
|
|
161
|
+
Clack.log.step "Project: #{result[:name]}"
|
|
162
|
+
Clack.log.step "Directory: #{result[:directory]}"
|
|
163
|
+
Clack.log.step "Template: #{result[:template]}"
|
|
164
|
+
Clack.log.step "TypeScript: #{result[:typescript] ? "Yes" : "No"}"
|
|
165
|
+
Clack.log.step "Features: #{result[:features].join(", ")}" unless result[:features].empty?
|
|
166
|
+
Clack.log.step "Color: #{color}"
|
|
167
|
+
Clack.log.step "Action: #{action}"
|
|
168
|
+
Clack.log.step "Config: #{config_path}"
|
|
169
|
+
Clack.log.step "Stack: #{stack.join(", ")}" unless stack.empty?
|
|
170
|
+
|
|
171
|
+
Clack.note <<~MSG, title: "Next steps"
|
|
172
|
+
cd #{result[:directory]}
|
|
173
|
+
#{result[:package_manager]} run dev
|
|
174
|
+
MSG
|
|
175
|
+
|
|
176
|
+
Clack.outro "Happy coding!"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
run_demo if __FILE__ == $PROGRAM_NAME
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Full demo showcasing Clack Ruby features
|
|
5
|
+
# Run with: ruby examples/full_demo.rb
|
|
6
|
+
|
|
7
|
+
require_relative "../lib/clack" # Or: require "clack" if installed as a gem
|
|
8
|
+
|
|
9
|
+
Clack.intro "create-service"
|
|
10
|
+
|
|
11
|
+
# Text input
|
|
12
|
+
name = Clack.text(
|
|
13
|
+
message: "Service name:",
|
|
14
|
+
placeholder: "order-service"
|
|
15
|
+
)
|
|
16
|
+
exit 1 if Clack.handle_cancel(name)
|
|
17
|
+
|
|
18
|
+
# Password input (masked)
|
|
19
|
+
token = Clack.password(
|
|
20
|
+
message: "GitHub token:",
|
|
21
|
+
mask: "•"
|
|
22
|
+
)
|
|
23
|
+
exit 1 if Clack.handle_cancel(token)
|
|
24
|
+
|
|
25
|
+
# Confirm
|
|
26
|
+
use_openapi = Clack.confirm(
|
|
27
|
+
message: "Generate OpenAPI spec?",
|
|
28
|
+
initial_value: true
|
|
29
|
+
)
|
|
30
|
+
exit 1 if Clack.handle_cancel(use_openapi)
|
|
31
|
+
|
|
32
|
+
# Select (single choice)
|
|
33
|
+
language = Clack.select(
|
|
34
|
+
message: "Language:",
|
|
35
|
+
options: [
|
|
36
|
+
{value: "java", label: "Java 21", hint: "Spring Boot 3"},
|
|
37
|
+
{value: "python", label: "Python 3.12", hint: "FastAPI"},
|
|
38
|
+
{value: "go", label: "Go 1.22", hint: "chi"},
|
|
39
|
+
{value: "node", label: "Node.js 22", hint: "Fastify"}
|
|
40
|
+
]
|
|
41
|
+
)
|
|
42
|
+
exit 1 if Clack.handle_cancel(language)
|
|
43
|
+
|
|
44
|
+
# Multiselect
|
|
45
|
+
integrations = Clack.multiselect(
|
|
46
|
+
message: "Integrations:",
|
|
47
|
+
options: [
|
|
48
|
+
{value: "postgres", label: "PostgreSQL"},
|
|
49
|
+
{value: "redis", label: "Redis"},
|
|
50
|
+
{value: "kafka", label: "Kafka"},
|
|
51
|
+
{value: "s3", label: "S3"}
|
|
52
|
+
],
|
|
53
|
+
required: false
|
|
54
|
+
)
|
|
55
|
+
exit 1 if Clack.handle_cancel(integrations)
|
|
56
|
+
|
|
57
|
+
# Progress bar
|
|
58
|
+
prog = Clack.progress(total: 100, message: "Scaffolding...")
|
|
59
|
+
prog.start
|
|
60
|
+
5.times do
|
|
61
|
+
sleep 0.4
|
|
62
|
+
prog.advance(20)
|
|
63
|
+
end
|
|
64
|
+
prog.stop("Done")
|
|
65
|
+
|
|
66
|
+
# Spinner
|
|
67
|
+
s = Clack.spinner
|
|
68
|
+
s.start "Installing dependencies..."
|
|
69
|
+
sleep 0.8
|
|
70
|
+
s.message "Configuring CI..."
|
|
71
|
+
sleep 0.6
|
|
72
|
+
s.stop "Ready"
|
|
73
|
+
|
|
74
|
+
# Summary
|
|
75
|
+
Clack.log.step "Service: #{name}"
|
|
76
|
+
Clack.log.step "Stack: #{language}"
|
|
77
|
+
Clack.log.step "Integrations: #{integrations.join(", ")}" unless integrations.empty?
|
|
78
|
+
|
|
79
|
+
Clack.note <<~MSG, title: "Next steps"
|
|
80
|
+
cd #{name}
|
|
81
|
+
make dev
|
|
82
|
+
MSG
|
|
83
|
+
|
|
84
|
+
Clack.outro "Ship it"
|