gum 0.3.0-x86_64-linux
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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +460 -0
- data/exe/gum +11 -0
- data/exe/x86_64-linux/gum +0 -0
- data/gum.gemspec +35 -0
- data/lib/gum/command.rb +159 -0
- data/lib/gum/commands/choose.rb +95 -0
- data/lib/gum/commands/confirm.rb +57 -0
- data/lib/gum/commands/file.rb +84 -0
- data/lib/gum/commands/filter.rb +119 -0
- data/lib/gum/commands/format.rb +74 -0
- data/lib/gum/commands/input.rb +68 -0
- data/lib/gum/commands/join.rb +41 -0
- data/lib/gum/commands/log.rb +98 -0
- data/lib/gum/commands/pager.rb +55 -0
- data/lib/gum/commands/spin.rb +167 -0
- data/lib/gum/commands/style.rb +93 -0
- data/lib/gum/commands/table.rb +93 -0
- data/lib/gum/commands/write.rb +84 -0
- data/lib/gum/upstream.rb +17 -0
- data/lib/gum/version.rb +7 -0
- data/lib/gum.rb +174 -0
- data/sig/gum/command.rbs +23 -0
- data/sig/gum/commands/choose.rbs +42 -0
- data/sig/gum/commands/confirm.rbs +30 -0
- data/sig/gum/commands/file.rbs +38 -0
- data/sig/gum/commands/filter.rbs +48 -0
- data/sig/gum/commands/format.rbs +47 -0
- data/sig/gum/commands/input.rbs +32 -0
- data/sig/gum/commands/join.rbs +24 -0
- data/sig/gum/commands/log.rbs +56 -0
- data/sig/gum/commands/pager.rbs +28 -0
- data/sig/gum/commands/spin.rbs +55 -0
- data/sig/gum/commands/style.rbs +44 -0
- data/sig/gum/commands/table.rbs +35 -0
- data/sig/gum/commands/write.rbs +35 -0
- data/sig/gum/upstream.rbs +9 -0
- data/sig/gum/version.rbs +5 -0
- data/sig/gum.rbs +79 -0
- metadata +84 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
3
|
+
# rbs_inline: enabled
|
|
4
|
+
|
|
5
|
+
require "shellwords"
|
|
6
|
+
|
|
7
|
+
module Gum
|
|
8
|
+
# Display a spinner while running a command or block
|
|
9
|
+
#
|
|
10
|
+
# @example With shell command
|
|
11
|
+
# Gum.spin("Installing...", command: "npm install")
|
|
12
|
+
#
|
|
13
|
+
# @example With Ruby block
|
|
14
|
+
# result = Gum.spin("Processing...") do
|
|
15
|
+
# # long running operation
|
|
16
|
+
# expensive_computation
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# @example Custom spinner type
|
|
20
|
+
# Gum.spin("Loading...", spinner: :dot, command: "sleep 5")
|
|
21
|
+
#
|
|
22
|
+
class Spin
|
|
23
|
+
SPINNERS = [
|
|
24
|
+
:line, :dot, :minidot, :jump, :pulse, :points, :globe, :moon, :monkey, :meter, :hamburger
|
|
25
|
+
].freeze #: Array[Symbol]
|
|
26
|
+
|
|
27
|
+
# Display a spinner while running a command or block
|
|
28
|
+
#
|
|
29
|
+
# @rbs title: String -- spinner title text (default: "Loading...")
|
|
30
|
+
# @rbs command: String? -- shell command to execute
|
|
31
|
+
# @rbs spinner: Symbol | String | nil -- spinner type (see SPINNERS constant)
|
|
32
|
+
# @rbs show_output: bool? -- show command stdout after completion
|
|
33
|
+
# @rbs show_error: bool? -- show command stderr after completion
|
|
34
|
+
# @rbs align: Symbol? -- alignment of spinner and title (:left, :right)
|
|
35
|
+
# @rbs timeout: Integer? -- timeout in seconds (0 = no timeout)
|
|
36
|
+
# @rbs spinner_style: Hash[Symbol, untyped]? -- spinner animation style
|
|
37
|
+
# @rbs title_style: Hash[Symbol, untyped]? -- title text style
|
|
38
|
+
# @rbs &block: ^() -> untyped | nil -- Ruby block to execute (alternative to command)
|
|
39
|
+
# @rbs return: String | untyped | nil -- command output or block result, nil if cancelled
|
|
40
|
+
def self.call(
|
|
41
|
+
title = "Loading...",
|
|
42
|
+
command: nil,
|
|
43
|
+
spinner: nil,
|
|
44
|
+
show_output: nil,
|
|
45
|
+
show_error: nil,
|
|
46
|
+
align: nil,
|
|
47
|
+
timeout: nil,
|
|
48
|
+
spinner_style: nil,
|
|
49
|
+
title_style: nil,
|
|
50
|
+
&block
|
|
51
|
+
)
|
|
52
|
+
raise ArgumentError, "Cannot specify both command and block" if block && command
|
|
53
|
+
|
|
54
|
+
if block
|
|
55
|
+
run_with_block(title, spinner: spinner, spinner_style: spinner_style, title_style: title_style, &block)
|
|
56
|
+
else
|
|
57
|
+
run_with_command(
|
|
58
|
+
title,
|
|
59
|
+
command: command,
|
|
60
|
+
spinner: spinner,
|
|
61
|
+
show_output: show_output,
|
|
62
|
+
show_error: show_error,
|
|
63
|
+
align: align,
|
|
64
|
+
timeout: timeout,
|
|
65
|
+
spinner_style: spinner_style,
|
|
66
|
+
title_style: title_style
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @rbs title: String -- spinner title text
|
|
72
|
+
# @rbs command: String -- shell command to execute
|
|
73
|
+
# @rbs spinner: Symbol | String | nil -- spinner type
|
|
74
|
+
# @rbs show_output: bool? -- show command stdout
|
|
75
|
+
# @rbs show_error: bool? -- show command stderr
|
|
76
|
+
# @rbs align: Symbol? -- alignment of spinner
|
|
77
|
+
# @rbs timeout: Integer? -- timeout in seconds
|
|
78
|
+
# @rbs spinner_style: Hash[Symbol, untyped]? -- spinner animation style
|
|
79
|
+
# @rbs title_style: Hash[Symbol, untyped]? -- title text style
|
|
80
|
+
# @rbs return: bool? -- true if command succeeded, false/nil otherwise
|
|
81
|
+
def self.run_with_command(
|
|
82
|
+
title,
|
|
83
|
+
command:,
|
|
84
|
+
spinner:,
|
|
85
|
+
show_output:,
|
|
86
|
+
show_error:,
|
|
87
|
+
align:,
|
|
88
|
+
timeout:,
|
|
89
|
+
spinner_style:,
|
|
90
|
+
title_style:
|
|
91
|
+
)
|
|
92
|
+
raise ArgumentError, "Command is required when not using a block" unless command
|
|
93
|
+
|
|
94
|
+
options = {
|
|
95
|
+
title: title,
|
|
96
|
+
spinner: spinner&.to_s,
|
|
97
|
+
"show-output": show_output,
|
|
98
|
+
"show-error": show_error,
|
|
99
|
+
align: align&.to_s,
|
|
100
|
+
timeout: timeout ? "#{timeout}s" : nil,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
args = Command.build_args("spin", **options.compact)
|
|
104
|
+
|
|
105
|
+
Command.add_style_args(args, :spinner, spinner_style)
|
|
106
|
+
Command.add_style_args(args, :title, title_style)
|
|
107
|
+
|
|
108
|
+
args << "--"
|
|
109
|
+
args.concat(command.shellsplit)
|
|
110
|
+
|
|
111
|
+
system(Gum.executable, *args.map(&:to_s))
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# @rbs title: String -- spinner title text
|
|
115
|
+
# @rbs spinner: Symbol | String | nil -- spinner type
|
|
116
|
+
# @rbs spinner_style: Hash[Symbol, untyped]? -- spinner animation style
|
|
117
|
+
# @rbs title_style: Hash[Symbol, untyped]? -- title text style
|
|
118
|
+
# @rbs &block: ^() -> untyped -- Ruby block to execute
|
|
119
|
+
# @rbs return: untyped -- block result
|
|
120
|
+
def self.run_with_block(
|
|
121
|
+
title,
|
|
122
|
+
spinner:,
|
|
123
|
+
spinner_style:,
|
|
124
|
+
title_style:,
|
|
125
|
+
&block
|
|
126
|
+
)
|
|
127
|
+
require "fileutils"
|
|
128
|
+
|
|
129
|
+
result = nil
|
|
130
|
+
error = nil
|
|
131
|
+
marker_path = "/tmp/gum_spin_done_#{Process.pid}_#{Time.now.to_i}"
|
|
132
|
+
|
|
133
|
+
options = {
|
|
134
|
+
title: title,
|
|
135
|
+
spinner: spinner&.to_s,
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
args = Command.build_args("spin", **options.compact)
|
|
139
|
+
|
|
140
|
+
Command.add_style_args(args, :spinner, spinner_style)
|
|
141
|
+
Command.add_style_args(args, :title, title_style)
|
|
142
|
+
|
|
143
|
+
args << "--"
|
|
144
|
+
args.push("bash", "-c", "while [ ! -f #{marker_path} ]; do sleep 0.1; done")
|
|
145
|
+
|
|
146
|
+
spinner_pid = Process.fork do
|
|
147
|
+
exec(Gum.executable, *args.map(&:to_s))
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
begin
|
|
151
|
+
result = block.call
|
|
152
|
+
rescue StandardError => e
|
|
153
|
+
error = e
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
FileUtils.touch(marker_path)
|
|
157
|
+
Process.wait(spinner_pid)
|
|
158
|
+
FileUtils.rm_f(marker_path)
|
|
159
|
+
|
|
160
|
+
raise error if error
|
|
161
|
+
|
|
162
|
+
result
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private_class_method :run_with_command, :run_with_block
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
3
|
+
# rbs_inline: enabled
|
|
4
|
+
|
|
5
|
+
module Gum
|
|
6
|
+
# Apply styling to text
|
|
7
|
+
#
|
|
8
|
+
# @example Basic styling
|
|
9
|
+
# styled = Gum.style("Hello", foreground: "212", bold: true)
|
|
10
|
+
#
|
|
11
|
+
# @example With border
|
|
12
|
+
# box = Gum.style("Content", border: :double, padding: "1 2")
|
|
13
|
+
#
|
|
14
|
+
# @example Multiple lines
|
|
15
|
+
# styled = Gum.style("Line 1", "Line 2", align: :center, width: 50)
|
|
16
|
+
#
|
|
17
|
+
class Style
|
|
18
|
+
BORDERS = [:none, :hidden, :rounded, :double, :thick, :normal].freeze #: Array[Symbol]
|
|
19
|
+
ALIGNMENTS = [:left, :center, :right, :top, :middle, :bottom].freeze #: Array[Symbol]
|
|
20
|
+
|
|
21
|
+
# Apply styling to text
|
|
22
|
+
#
|
|
23
|
+
# @rbs *text: String -- text content to style (multiple strings become separate lines)
|
|
24
|
+
# @rbs foreground: String | Integer | nil -- text color (ANSI code, hex, or color name)
|
|
25
|
+
# @rbs background: String | Integer | nil -- background color (ANSI code, hex, or color name)
|
|
26
|
+
# @rbs border: Symbol | String | nil -- border style (:none, :hidden, :rounded, :double, :thick, :normal)
|
|
27
|
+
# @rbs border_foreground: String | Integer | nil -- border color
|
|
28
|
+
# @rbs border_background: String | Integer | nil -- border background color
|
|
29
|
+
# @rbs align: Symbol | String | nil -- text alignment (:left, :center, :right)
|
|
30
|
+
# @rbs height: Integer? -- fixed height in lines
|
|
31
|
+
# @rbs width: Integer? -- fixed width in characters
|
|
32
|
+
# @rbs margin: String | Array[Integer] | nil -- margin around the box ("1" or "1 2" or [1, 2, 1, 2])
|
|
33
|
+
# @rbs padding: String | Array[Integer] | nil -- padding inside the box ("1" or "1 2" or [1, 2, 1, 2])
|
|
34
|
+
# @rbs bold: bool? -- bold text
|
|
35
|
+
# @rbs italic: bool? -- italic text
|
|
36
|
+
# @rbs strikethrough: bool? -- strikethrough text
|
|
37
|
+
# @rbs underline: bool? -- underlined text
|
|
38
|
+
# @rbs faint: bool? -- faint/dim text
|
|
39
|
+
# @rbs return: String? -- styled text output
|
|
40
|
+
def self.call(
|
|
41
|
+
*text,
|
|
42
|
+
foreground: nil,
|
|
43
|
+
background: nil,
|
|
44
|
+
border: nil,
|
|
45
|
+
border_foreground: nil,
|
|
46
|
+
border_background: nil,
|
|
47
|
+
align: nil,
|
|
48
|
+
height: nil,
|
|
49
|
+
width: nil,
|
|
50
|
+
margin: nil,
|
|
51
|
+
padding: nil,
|
|
52
|
+
bold: nil,
|
|
53
|
+
italic: nil,
|
|
54
|
+
strikethrough: nil,
|
|
55
|
+
underline: nil,
|
|
56
|
+
faint: nil
|
|
57
|
+
)
|
|
58
|
+
options = {
|
|
59
|
+
foreground: foreground&.to_s,
|
|
60
|
+
background: background&.to_s,
|
|
61
|
+
border: border&.to_s,
|
|
62
|
+
"border-foreground": border_foreground&.to_s,
|
|
63
|
+
"border-background": border_background&.to_s,
|
|
64
|
+
align: align&.to_s,
|
|
65
|
+
height: height,
|
|
66
|
+
width: width,
|
|
67
|
+
margin: format_spacing(margin),
|
|
68
|
+
padding: format_spacing(padding),
|
|
69
|
+
bold: bold,
|
|
70
|
+
italic: italic,
|
|
71
|
+
strikethrough: strikethrough,
|
|
72
|
+
underline: underline,
|
|
73
|
+
faint: faint,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
args = Command.build_args("style", *text, **options.compact)
|
|
77
|
+
Command.run(*args, interactive: false)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @rbs value: String | Integer | Array[Integer] | nil -- spacing value to format
|
|
81
|
+
# @rbs return: String? -- formatted spacing string
|
|
82
|
+
def self.format_spacing(value)
|
|
83
|
+
case value
|
|
84
|
+
when Array
|
|
85
|
+
value.join(" ")
|
|
86
|
+
when String, Integer
|
|
87
|
+
value.to_s
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private_class_method :format_spacing
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
3
|
+
# rbs_inline: enabled
|
|
4
|
+
|
|
5
|
+
module Gum
|
|
6
|
+
# Display and select from tabular data
|
|
7
|
+
#
|
|
8
|
+
# @example From array of arrays
|
|
9
|
+
# selection = Gum.table([["Alice", 30], ["Bob", 25]], columns: %w[Name Age])
|
|
10
|
+
#
|
|
11
|
+
# @example From CSV string
|
|
12
|
+
# Gum.table(File.read("data.csv"))
|
|
13
|
+
#
|
|
14
|
+
# @example With custom separator
|
|
15
|
+
# Gum.table(data, separator: "\t")
|
|
16
|
+
#
|
|
17
|
+
class Table
|
|
18
|
+
# Display tabular data and optionally allow row selection
|
|
19
|
+
#
|
|
20
|
+
# @rbs data: String | Array[Array[untyped]] -- CSV string or array of row arrays
|
|
21
|
+
# @rbs columns: Array[String]? -- column headers (required when data is array)
|
|
22
|
+
# @rbs separator: String? -- column separator (default: ",")
|
|
23
|
+
# @rbs widths: Array[Integer]? -- column widths
|
|
24
|
+
# @rbs height: Integer? -- table height in rows
|
|
25
|
+
# @rbs print: bool? -- print table without selection (non-interactive)
|
|
26
|
+
# @rbs border: Symbol | String | nil -- border style (:rounded, :thick, :normal, :hidden, :double, :none)
|
|
27
|
+
# @rbs border_foreground: String | Integer | nil -- border color
|
|
28
|
+
# @rbs border_background: String | Integer | nil -- border background color
|
|
29
|
+
# @rbs cell_foreground: String | Integer | nil -- cell text color
|
|
30
|
+
# @rbs cell_background: String | Integer | nil -- cell background color
|
|
31
|
+
# @rbs header_foreground: String | Integer | nil -- header text color
|
|
32
|
+
# @rbs header_background: String | Integer | nil -- header background color
|
|
33
|
+
# @rbs selected_foreground: String | Integer | nil -- selected row text color
|
|
34
|
+
# @rbs selected_background: String | Integer | nil -- selected row background color
|
|
35
|
+
# @rbs return: String? -- selected row data, or nil if cancelled
|
|
36
|
+
def self.call(
|
|
37
|
+
data,
|
|
38
|
+
columns: nil,
|
|
39
|
+
separator: nil,
|
|
40
|
+
widths: nil,
|
|
41
|
+
height: nil,
|
|
42
|
+
print: nil,
|
|
43
|
+
border: nil,
|
|
44
|
+
border_foreground: nil,
|
|
45
|
+
border_background: nil,
|
|
46
|
+
cell_foreground: nil,
|
|
47
|
+
cell_background: nil,
|
|
48
|
+
header_foreground: nil,
|
|
49
|
+
header_background: nil,
|
|
50
|
+
selected_foreground: nil,
|
|
51
|
+
selected_background: nil
|
|
52
|
+
)
|
|
53
|
+
separator ||= ","
|
|
54
|
+
|
|
55
|
+
csv_data = if data.is_a?(Array)
|
|
56
|
+
rows = [] #: Array[String]
|
|
57
|
+
rows << columns.join(separator) if columns
|
|
58
|
+
rows.concat(data.map { |row| row.join(separator) })
|
|
59
|
+
rows.join("\n")
|
|
60
|
+
else
|
|
61
|
+
data.to_s
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
options = {
|
|
65
|
+
separator: separator,
|
|
66
|
+
widths: widths&.join(","),
|
|
67
|
+
height: height,
|
|
68
|
+
print: print,
|
|
69
|
+
border: border&.to_s,
|
|
70
|
+
"border.foreground": border_foreground&.to_s,
|
|
71
|
+
"border.background": border_background&.to_s,
|
|
72
|
+
"cell.foreground": cell_foreground&.to_s,
|
|
73
|
+
"cell.background": cell_background&.to_s,
|
|
74
|
+
"header.foreground": header_foreground&.to_s,
|
|
75
|
+
"header.background": header_background&.to_s,
|
|
76
|
+
"selected.foreground": selected_foreground&.to_s,
|
|
77
|
+
"selected.background": selected_background&.to_s,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
args = Command.build_args("table", **options.compact)
|
|
81
|
+
|
|
82
|
+
if print
|
|
83
|
+
IO.popen([Gum.executable, *args.map(&:to_s)], "w") do |io|
|
|
84
|
+
io.write(csv_data)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
nil
|
|
88
|
+
else
|
|
89
|
+
Command.run(*args, input: csv_data, interactive: true)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
3
|
+
# rbs_inline: enabled
|
|
4
|
+
|
|
5
|
+
module Gum
|
|
6
|
+
# Prompt for multi-line text input
|
|
7
|
+
#
|
|
8
|
+
# @example Basic multi-line input
|
|
9
|
+
# description = Gum.write(placeholder: "Enter description...")
|
|
10
|
+
#
|
|
11
|
+
# @example With dimensions
|
|
12
|
+
# notes = Gum.write(width: 80, height: 10, header: "Notes")
|
|
13
|
+
#
|
|
14
|
+
class Write
|
|
15
|
+
# Prompt for multi-line text input (Ctrl+D to submit)
|
|
16
|
+
#
|
|
17
|
+
# @rbs placeholder: String? -- placeholder text shown when empty
|
|
18
|
+
# @rbs prompt: String? -- prompt string shown at the start of each line
|
|
19
|
+
# @rbs value: String? -- initial value for the text area
|
|
20
|
+
# @rbs char_limit: Integer? -- maximum number of characters allowed
|
|
21
|
+
# @rbs width: Integer? -- width of the text area
|
|
22
|
+
# @rbs height: Integer? -- height of the text area in lines
|
|
23
|
+
# @rbs header: String? -- header text displayed above the input
|
|
24
|
+
# @rbs show_cursor_line: bool? -- highlight the line with the cursor
|
|
25
|
+
# @rbs show_line_numbers: bool? -- display line numbers
|
|
26
|
+
# @rbs timeout: Integer? -- timeout in seconds (0 = no timeout)
|
|
27
|
+
# @rbs cursor: Hash[Symbol, untyped]? -- cursor style options
|
|
28
|
+
# @rbs cursor_line: Hash[Symbol, untyped]? -- cursor line highlight style
|
|
29
|
+
# @rbs placeholder_style: Hash[Symbol, untyped]? -- placeholder text style
|
|
30
|
+
# @rbs prompt_style: Hash[Symbol, untyped]? -- prompt text style
|
|
31
|
+
# @rbs end_of_buffer: Hash[Symbol, untyped]? -- end of buffer character style
|
|
32
|
+
# @rbs line_number: Hash[Symbol, untyped]? -- line number style
|
|
33
|
+
# @rbs header_style: Hash[Symbol, untyped]? -- header text style
|
|
34
|
+
# @rbs base: Hash[Symbol, untyped]? -- base text style
|
|
35
|
+
# @rbs return: String? -- the entered text, or nil if cancelled
|
|
36
|
+
def self.call(
|
|
37
|
+
placeholder: nil,
|
|
38
|
+
prompt: nil,
|
|
39
|
+
value: nil,
|
|
40
|
+
char_limit: nil,
|
|
41
|
+
width: nil,
|
|
42
|
+
height: nil,
|
|
43
|
+
header: nil,
|
|
44
|
+
show_cursor_line: nil,
|
|
45
|
+
show_line_numbers: nil,
|
|
46
|
+
timeout: nil,
|
|
47
|
+
cursor: nil,
|
|
48
|
+
cursor_line: nil,
|
|
49
|
+
placeholder_style: nil,
|
|
50
|
+
prompt_style: nil,
|
|
51
|
+
end_of_buffer: nil,
|
|
52
|
+
line_number: nil,
|
|
53
|
+
header_style: nil,
|
|
54
|
+
base: nil
|
|
55
|
+
)
|
|
56
|
+
options = {
|
|
57
|
+
placeholder: placeholder,
|
|
58
|
+
prompt: prompt,
|
|
59
|
+
value: value,
|
|
60
|
+
char_limit: char_limit,
|
|
61
|
+
width: width,
|
|
62
|
+
height: height,
|
|
63
|
+
header: header,
|
|
64
|
+
show_cursor_line: show_cursor_line,
|
|
65
|
+
show_line_numbers: show_line_numbers,
|
|
66
|
+
timeout: timeout ? "#{timeout}s" : nil,
|
|
67
|
+
base: base,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
options[:cursor] = cursor if cursor
|
|
71
|
+
options[:cursor_line] = cursor_line if cursor_line
|
|
72
|
+
options[:end_of_buffer] = end_of_buffer if end_of_buffer
|
|
73
|
+
options[:line_number] = line_number if line_number
|
|
74
|
+
|
|
75
|
+
args = Command.build_args("write", **options.compact)
|
|
76
|
+
|
|
77
|
+
Command.add_style_args(args, :placeholder, placeholder_style)
|
|
78
|
+
Command.add_style_args(args, :prompt, prompt_style)
|
|
79
|
+
Command.add_style_args(args, :header, header_style)
|
|
80
|
+
|
|
81
|
+
Command.run(*args)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
data/lib/gum/upstream.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
3
|
+
# rbs_inline: enabled
|
|
4
|
+
|
|
5
|
+
module Gum
|
|
6
|
+
module Upstream
|
|
7
|
+
VERSION = "0.17.0"
|
|
8
|
+
|
|
9
|
+
NATIVE_PLATFORMS = {
|
|
10
|
+
"arm64-darwin" => "gum_#{VERSION}_Darwin_arm64.tar.gz",
|
|
11
|
+
"x86_64-darwin" => "gum_#{VERSION}_Darwin_x86_64.tar.gz",
|
|
12
|
+
"arm64-linux" => "gum_#{VERSION}_Linux_arm64.tar.gz",
|
|
13
|
+
"aarch64-linux" => "gum_#{VERSION}_Linux_arm64.tar.gz",
|
|
14
|
+
"x86_64-linux" => "gum_#{VERSION}_Linux_x86_64.tar.gz",
|
|
15
|
+
}.freeze
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/gum/version.rb
ADDED
data/lib/gum.rb
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
3
|
+
# rbs_inline: enabled
|
|
4
|
+
|
|
5
|
+
require_relative "gum/version"
|
|
6
|
+
require_relative "gum/command"
|
|
7
|
+
|
|
8
|
+
module Gum
|
|
9
|
+
autoload :Upstream, "gum/upstream"
|
|
10
|
+
autoload :Choose, "gum/commands/choose"
|
|
11
|
+
autoload :Confirm, "gum/commands/confirm"
|
|
12
|
+
autoload :FilePicker, "gum/commands/file"
|
|
13
|
+
autoload :Filter, "gum/commands/filter"
|
|
14
|
+
autoload :Format, "gum/commands/format"
|
|
15
|
+
autoload :Input, "gum/commands/input"
|
|
16
|
+
autoload :Join, "gum/commands/join"
|
|
17
|
+
autoload :Log, "gum/commands/log"
|
|
18
|
+
autoload :Pager, "gum/commands/pager"
|
|
19
|
+
autoload :Spin, "gum/commands/spin"
|
|
20
|
+
autoload :Style, "gum/commands/style"
|
|
21
|
+
autoload :Table, "gum/commands/table"
|
|
22
|
+
autoload :Write, "gum/commands/write"
|
|
23
|
+
|
|
24
|
+
GEM_NAME = "gum"
|
|
25
|
+
DEFAULT_DIR = File.expand_path(File.join(__dir__, "..", "exe"))
|
|
26
|
+
|
|
27
|
+
class Error < StandardError; end
|
|
28
|
+
class ExecutableNotFoundException < Error; end
|
|
29
|
+
class UnsupportedPlatformException < Error; end
|
|
30
|
+
class DirectoryNotFoundException < Error; end
|
|
31
|
+
|
|
32
|
+
class << self
|
|
33
|
+
def execute(*args)
|
|
34
|
+
system(executable, *args.map(&:to_s))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def platform
|
|
38
|
+
[:cpu, :os].map { |m| Gem::Platform.local.public_send(m) }.join("-")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def version
|
|
42
|
+
"gum v#{VERSION} (upstream v#{Upstream::VERSION}) [#{platform}]"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def executable(exe_path: DEFAULT_DIR)
|
|
46
|
+
gum_install_dir = ENV.fetch("GUM_INSTALL_DIR", nil)
|
|
47
|
+
|
|
48
|
+
if gum_install_dir
|
|
49
|
+
raise DirectoryNotFoundException, <<~MESSAGE unless File.directory?(gum_install_dir)
|
|
50
|
+
GUM_INSTALL_DIR is set to #{gum_install_dir}, but that directory does not exist.
|
|
51
|
+
MESSAGE
|
|
52
|
+
|
|
53
|
+
warn "NOTE: using GUM_INSTALL_DIR to find gum executable: #{gum_install_dir}"
|
|
54
|
+
exe_file = File.expand_path(File.join(gum_install_dir, "gum"))
|
|
55
|
+
else
|
|
56
|
+
if Gum::Upstream::NATIVE_PLATFORMS.keys.none? { |p| Gem::Platform.match_gem?(Gem::Platform.new(p), GEM_NAME) }
|
|
57
|
+
raise UnsupportedPlatformException, <<~MESSAGE
|
|
58
|
+
gum does not support the #{platform} platform.
|
|
59
|
+
Please install gum following instructions at https://github.com/charmbracelet/gum#installation
|
|
60
|
+
MESSAGE
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
exe_file = Dir.glob(File.expand_path(File.join(exe_path, "**", "gum"))).find do |f|
|
|
64
|
+
Gem::Platform.match_gem?(Gem::Platform.new(File.basename(File.dirname(f))), GEM_NAME)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
if exe_file.nil? || !File.exist?(exe_file)
|
|
69
|
+
raise ExecutableNotFoundException, <<~MESSAGE
|
|
70
|
+
Cannot find the gum executable for #{platform} in #{exe_path}
|
|
71
|
+
|
|
72
|
+
If you're using bundler, please make sure you're on the latest bundler version:
|
|
73
|
+
|
|
74
|
+
gem install bundler
|
|
75
|
+
bundle update --bundler
|
|
76
|
+
|
|
77
|
+
Then make sure your lock file includes this platform by running:
|
|
78
|
+
|
|
79
|
+
bundle lock --add-platform #{platform}
|
|
80
|
+
bundle install
|
|
81
|
+
|
|
82
|
+
See `bundle lock --help` output for details.
|
|
83
|
+
|
|
84
|
+
If you're still seeing this message after taking those steps, try running
|
|
85
|
+
`bundle config` and ensure `force_ruby_platform` isn't set to `true`.
|
|
86
|
+
MESSAGE
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
exe_file
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# ─────────────────────────────────────────────────────────────────────────
|
|
93
|
+
# High-level Ruby API
|
|
94
|
+
# ─────────────────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
# Prompt for single-line input
|
|
97
|
+
# @see Gum::Input#call
|
|
98
|
+
def input(**)
|
|
99
|
+
Input.call(**)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Prompt for multi-line text input
|
|
103
|
+
# @see Gum::Write#call
|
|
104
|
+
def write(**)
|
|
105
|
+
Write.call(**)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Choose from a list of options
|
|
109
|
+
# @see Gum::Choose#call
|
|
110
|
+
def choose(...)
|
|
111
|
+
Choose.call(...)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Filter items with fuzzy matching
|
|
115
|
+
# @see Gum::Filter#call
|
|
116
|
+
def filter(...)
|
|
117
|
+
Filter.call(...)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Prompt for confirmation
|
|
121
|
+
# @see Gum::Confirm#call
|
|
122
|
+
def confirm(prompt = "Are you sure?", **)
|
|
123
|
+
Confirm.call(prompt, **)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Pick a file from the filesystem
|
|
127
|
+
# @see Gum::FilePicker#call
|
|
128
|
+
def file(path = nil, **)
|
|
129
|
+
FilePicker.call(path, **)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Display content in a scrollable pager
|
|
133
|
+
# @see Gum::Pager#call
|
|
134
|
+
def pager(content, **)
|
|
135
|
+
Pager.call(content, **)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Display a spinner while running a command or block
|
|
139
|
+
# @see Gum::Spin#call
|
|
140
|
+
def spin(title = "Loading...", **, &)
|
|
141
|
+
Spin.call(title, **, &)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Apply styling to text
|
|
145
|
+
# @see Gum::Style#call
|
|
146
|
+
def style(*text, **)
|
|
147
|
+
Style.call(*text, **)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Join text blocks together
|
|
151
|
+
# @see Gum::Join#call
|
|
152
|
+
def join(*texts, **)
|
|
153
|
+
Join.call(*texts, **)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Format text (markdown, code, template, emoji)
|
|
157
|
+
# @see Gum::Format#call
|
|
158
|
+
def format(*text, **)
|
|
159
|
+
Format.call(*text, **)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Display and select from tabular data
|
|
163
|
+
# @see Gum::Table#call
|
|
164
|
+
def table(data, **)
|
|
165
|
+
Table.call(data, **)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Log a message
|
|
169
|
+
# @see Gum::Log#call
|
|
170
|
+
def log(message, **)
|
|
171
|
+
Log.call(message, **)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
data/sig/gum/command.rbs
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Generated from lib/gum/command.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module Gum
|
|
4
|
+
module Command
|
|
5
|
+
def self?.run: (*untyped, ?input: untyped, ?interactive: untyped) -> untyped
|
|
6
|
+
|
|
7
|
+
def self?.run_non_interactive: (*untyped args, input: untyped) -> untyped
|
|
8
|
+
|
|
9
|
+
def self?.run_interactive: (*untyped args) -> untyped
|
|
10
|
+
|
|
11
|
+
def self?.run_interactive_with_input: (*untyped args, input: untyped) -> untyped
|
|
12
|
+
|
|
13
|
+
def self?.run_display_only: (*untyped args, input: untyped) -> untyped
|
|
14
|
+
|
|
15
|
+
def self?.run_with_status: (*untyped args, ?input: untyped) -> untyped
|
|
16
|
+
|
|
17
|
+
def self?.build_args: (untyped command, *untyped positional, **untyped options) -> untyped
|
|
18
|
+
|
|
19
|
+
def self?.add_style_args: (untyped args, untyped flag, untyped style_hash) -> untyped
|
|
20
|
+
|
|
21
|
+
def self?.flag_supports_negation?: (untyped command, untyped flag) -> untyped
|
|
22
|
+
end
|
|
23
|
+
end
|