bubbles 0.0.5 → 0.1.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 +5 -5
- data/LICENSE.txt +21 -0
- data/README.md +524 -80
- data/bubbles.gemspec +29 -21
- data/lib/bubbles/cursor.rb +169 -0
- data/lib/bubbles/file_picker.rb +397 -0
- data/lib/bubbles/help.rb +170 -0
- data/lib/bubbles/key.rb +96 -0
- data/lib/bubbles/list.rb +365 -0
- data/lib/bubbles/paginator.rb +158 -0
- data/lib/bubbles/progress.rb +276 -0
- data/lib/bubbles/spinner/spinners.rb +77 -0
- data/lib/bubbles/spinner.rb +122 -0
- data/lib/bubbles/stopwatch.rb +189 -0
- data/lib/bubbles/table.rb +248 -0
- data/lib/bubbles/text_area.rb +503 -0
- data/lib/bubbles/text_input.rb +543 -0
- data/lib/bubbles/timer.rb +196 -0
- data/lib/bubbles/version.rb +4 -1
- data/lib/bubbles/viewport.rb +296 -0
- data/lib/bubbles.rb +18 -35
- data/sig/bubbles/cursor.rbs +87 -0
- data/sig/bubbles/file_picker.rbs +138 -0
- data/sig/bubbles/help.rbs +88 -0
- data/sig/bubbles/key.rbs +63 -0
- data/sig/bubbles/list.rbs +138 -0
- data/sig/bubbles/paginator.rbs +90 -0
- data/sig/bubbles/progress.rbs +123 -0
- data/sig/bubbles/spinner/spinners.rbs +32 -0
- data/sig/bubbles/spinner.rbs +74 -0
- data/sig/bubbles/stopwatch.rbs +97 -0
- data/sig/bubbles/table.rbs +119 -0
- data/sig/bubbles/text_area.rbs +161 -0
- data/sig/bubbles/text_input.rbs +183 -0
- data/sig/bubbles/timer.rbs +107 -0
- data/sig/bubbles/version.rbs +5 -0
- data/sig/bubbles/viewport.rbs +125 -0
- data/sig/bubbles.rbs +4 -0
- metadata +66 -67
- data/.gitignore +0 -14
- data/.rspec +0 -2
- data/.travis.yml +0 -10
- data/Gemfile +0 -4
- data/LICENSE +0 -20
- data/Rakefile +0 -6
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/exe/bubbles +0 -5
- data/lib/bubbles/bubblicious_file.rb +0 -42
- data/lib/bubbles/command_queue.rb +0 -43
- data/lib/bubbles/common_uploader_interface.rb +0 -13
- data/lib/bubbles/config.rb +0 -149
- data/lib/bubbles/dir_watcher.rb +0 -53
- data/lib/bubbles/uploaders/local_dir.rb +0 -39
- data/lib/bubbles/uploaders/s3.rb +0 -36
- data/lib/bubbles/uploaders/s3_ensure_connection.rb +0 -26
- data/tmp/dummy_local_dir_uploader_dir/.gitkeep +0 -0
- data/tmp/dummy_processing_dir/.gitkeep +0 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
module Bubbles
|
|
5
|
+
# Timer is a countdown timer component.
|
|
6
|
+
#
|
|
7
|
+
# Example:
|
|
8
|
+
# timer = Bubbles::Timer.new(60.0) # 60 second countdown
|
|
9
|
+
#
|
|
10
|
+
# # In your model's init:
|
|
11
|
+
# def init
|
|
12
|
+
# [self, @timer.init]
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# # In your model's update:
|
|
16
|
+
# def update(message)
|
|
17
|
+
# case message
|
|
18
|
+
# when Bubbles::Timer::TickMessage, Bubbles::Timer::StartStopMessage
|
|
19
|
+
# @timer, command = @timer.update(message)
|
|
20
|
+
# [self, command]
|
|
21
|
+
# when Bubbles::Timer::TimeoutMessage
|
|
22
|
+
# # Timer finished!
|
|
23
|
+
# [self, nil]
|
|
24
|
+
# end
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
class Timer
|
|
28
|
+
class TickMessage < Bubbletea::Message
|
|
29
|
+
attr_reader :id #: Integer
|
|
30
|
+
attr_reader :tag #: Integer
|
|
31
|
+
attr_reader :timeout #: bool
|
|
32
|
+
|
|
33
|
+
#: (id: Integer, tag: Integer, ?timeout: bool) -> void
|
|
34
|
+
def initialize(id:, tag:, timeout: false)
|
|
35
|
+
super()
|
|
36
|
+
|
|
37
|
+
@id = id
|
|
38
|
+
@tag = tag
|
|
39
|
+
@timeout = timeout
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class TimeoutMessage < Bubbletea::Message
|
|
44
|
+
attr_reader :id #: Integer
|
|
45
|
+
|
|
46
|
+
#: (id: Integer) -> void
|
|
47
|
+
def initialize(id:)
|
|
48
|
+
super()
|
|
49
|
+
|
|
50
|
+
@id = id
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class StartStopMessage < Bubbletea::Message
|
|
55
|
+
attr_reader :id #: Integer
|
|
56
|
+
attr_reader :running #: bool
|
|
57
|
+
|
|
58
|
+
#: (id: Integer, running: bool) -> void
|
|
59
|
+
def initialize(id:, running:)
|
|
60
|
+
super()
|
|
61
|
+
|
|
62
|
+
@id = id
|
|
63
|
+
@running = running
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @rbs self.@next_id: Integer
|
|
68
|
+
# @rbs self.@id_mutex: Mutex
|
|
69
|
+
@next_id = 0
|
|
70
|
+
@id_mutex = Mutex.new
|
|
71
|
+
|
|
72
|
+
class << self
|
|
73
|
+
#: () -> Integer
|
|
74
|
+
def next_id
|
|
75
|
+
@id_mutex.synchronize do
|
|
76
|
+
@next_id += 1
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
attr_reader :timeout #: Float
|
|
82
|
+
attr_reader :interval #: Float
|
|
83
|
+
attr_reader :id #: Integer
|
|
84
|
+
|
|
85
|
+
#: (Float, ?interval: Float) -> void
|
|
86
|
+
def initialize(timeout, interval: 1.0)
|
|
87
|
+
@timeout = timeout.to_f
|
|
88
|
+
@interval = interval.to_f
|
|
89
|
+
@id = self.class.next_id
|
|
90
|
+
@tag = 0
|
|
91
|
+
@running = false
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
#: () -> Bubbletea::Command?
|
|
95
|
+
def init
|
|
96
|
+
@running = true
|
|
97
|
+
tick
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
#: () -> bool
|
|
101
|
+
def running?
|
|
102
|
+
@running && @timeout.positive?
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
#: () -> bool
|
|
106
|
+
def timed_out?
|
|
107
|
+
@timeout <= 0
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
#: (Bubbletea::Message) -> [Timer, Bubbletea::Command?]
|
|
111
|
+
def update(message)
|
|
112
|
+
case message
|
|
113
|
+
when StartStopMessage
|
|
114
|
+
return [self, nil] if message.id.positive? && message.id != @id
|
|
115
|
+
|
|
116
|
+
@running = message.running
|
|
117
|
+
[self, @running ? tick : nil]
|
|
118
|
+
|
|
119
|
+
when TickMessage
|
|
120
|
+
return [self, nil] unless @running
|
|
121
|
+
return [self, nil] if message.id.positive? && message.id != @id
|
|
122
|
+
return [self, nil] if message.tag.positive? && message.tag != @tag
|
|
123
|
+
|
|
124
|
+
@timeout -= @interval
|
|
125
|
+
@tag += 1
|
|
126
|
+
|
|
127
|
+
[self, Bubbletea.batch(tick, timeout_command)]
|
|
128
|
+
else
|
|
129
|
+
[self, nil]
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
#: () -> String
|
|
134
|
+
def view
|
|
135
|
+
format_duration(@timeout)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
#: () -> Bubbletea::Command
|
|
139
|
+
def start
|
|
140
|
+
start_stop(true)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
#: () -> Bubbletea::Command
|
|
144
|
+
def stop
|
|
145
|
+
start_stop(false)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
#: () -> Bubbletea::Command
|
|
149
|
+
def toggle
|
|
150
|
+
start_stop(!@running)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
private
|
|
154
|
+
|
|
155
|
+
#: (bool) -> Bubbletea::Command
|
|
156
|
+
def start_stop(running)
|
|
157
|
+
current_id = @id
|
|
158
|
+
Bubbletea.send_message(StartStopMessage.new(id: current_id, running: running))
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
#: () -> Bubbletea::Command?
|
|
162
|
+
def tick
|
|
163
|
+
return nil unless running?
|
|
164
|
+
|
|
165
|
+
current_id = @id
|
|
166
|
+
current_tag = @tag
|
|
167
|
+
|
|
168
|
+
Bubbletea.tick(@interval) { TickMessage.new(id: current_id, tag: current_tag) }
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
#: () -> Bubbletea::Command?
|
|
172
|
+
def timeout_command
|
|
173
|
+
return nil unless timed_out?
|
|
174
|
+
|
|
175
|
+
current_id = @id
|
|
176
|
+
Bubbletea.send_message(TimeoutMessage.new(id: current_id))
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
#: (Float) -> String
|
|
180
|
+
def format_duration(seconds)
|
|
181
|
+
return "0s" if seconds <= 0
|
|
182
|
+
|
|
183
|
+
total_seconds = seconds.to_i
|
|
184
|
+
hours = total_seconds / 3600
|
|
185
|
+
minutes = (total_seconds % 3600) / 60
|
|
186
|
+
secs = total_seconds % 60
|
|
187
|
+
|
|
188
|
+
parts = [] #: Array[String]
|
|
189
|
+
parts << "#{hours}h" if hours.positive?
|
|
190
|
+
parts << "#{minutes}m" if minutes.positive? || hours.positive?
|
|
191
|
+
parts << "#{secs}s"
|
|
192
|
+
|
|
193
|
+
parts.join
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
data/lib/bubbles/version.rb
CHANGED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
module Bubbles
|
|
5
|
+
# Viewport is a scrollable content pane.
|
|
6
|
+
#
|
|
7
|
+
# Example:
|
|
8
|
+
# viewport = Bubbles::Viewport.new(width: 80, height: 24)
|
|
9
|
+
# viewport.content = "Long text content..."
|
|
10
|
+
#
|
|
11
|
+
# # In update:
|
|
12
|
+
# viewport, command = viewport.update(message)
|
|
13
|
+
#
|
|
14
|
+
# # In view:
|
|
15
|
+
# viewport.view
|
|
16
|
+
#
|
|
17
|
+
class Viewport
|
|
18
|
+
attr_accessor :width #: Integer
|
|
19
|
+
attr_accessor :height #: Integer
|
|
20
|
+
attr_accessor :style #: Lipgloss::Style?
|
|
21
|
+
attr_accessor :mouse_wheel_enabled #: bool
|
|
22
|
+
attr_accessor :mouse_wheel_delta #: Integer
|
|
23
|
+
|
|
24
|
+
attr_reader :y_offset #: Integer
|
|
25
|
+
attr_reader :x_offset #: Integer
|
|
26
|
+
|
|
27
|
+
#: (?width: Integer, ?height: Integer) -> void
|
|
28
|
+
def initialize(width: 80, height: 24)
|
|
29
|
+
@width = width
|
|
30
|
+
@height = height
|
|
31
|
+
@style = nil
|
|
32
|
+
|
|
33
|
+
@mouse_wheel_enabled = true
|
|
34
|
+
@mouse_wheel_delta = 3
|
|
35
|
+
@horizontal_step = 0
|
|
36
|
+
|
|
37
|
+
@y_offset = 0
|
|
38
|
+
@x_offset = 0
|
|
39
|
+
|
|
40
|
+
@lines = [] #: Array[String]
|
|
41
|
+
@longest_line_width = 0
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
#: () -> nil
|
|
45
|
+
def init
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
#: () -> bool
|
|
50
|
+
def at_top?
|
|
51
|
+
@y_offset <= 0
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
#: () -> bool
|
|
55
|
+
def at_bottom?
|
|
56
|
+
@y_offset >= max_y_offset
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
#: () -> bool
|
|
60
|
+
def past_bottom?
|
|
61
|
+
@y_offset > max_y_offset
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
#: () -> Float -- scroll percentage (0.0 to 1.0)
|
|
65
|
+
def scroll_percent
|
|
66
|
+
return 1.0 if @height >= @lines.length
|
|
67
|
+
|
|
68
|
+
y = @y_offset.to_f
|
|
69
|
+
h = @height.to_f
|
|
70
|
+
t = @lines.length.to_f
|
|
71
|
+
|
|
72
|
+
v = y / (t - h)
|
|
73
|
+
|
|
74
|
+
v.clamp(0.0, 1.0).to_f
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
#: (String) -> void
|
|
78
|
+
def content=(content)
|
|
79
|
+
content = content.gsub("\r\n", "\n")
|
|
80
|
+
@lines = content.split("\n", -1)
|
|
81
|
+
@longest_line_width = @lines.map { |l| strip_ansi(l).length }.max || 0
|
|
82
|
+
|
|
83
|
+
goto_bottom if @y_offset > @lines.length - 1
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
#: () -> String
|
|
87
|
+
def content
|
|
88
|
+
@lines.join("\n")
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
#: (Integer) -> Integer
|
|
92
|
+
def y_offset=(offset)
|
|
93
|
+
@y_offset = offset.clamp(0, max_y_offset)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
#: (Integer) -> Integer
|
|
97
|
+
def x_offset=(offset)
|
|
98
|
+
max_x = [@longest_line_width - @width, 0].max
|
|
99
|
+
@x_offset = offset.clamp(0, max_x)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
#: (Integer) -> Integer
|
|
103
|
+
def horizontal_step=(step)
|
|
104
|
+
@horizontal_step = [step, 0].max
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
#: (Integer) -> Array[String]
|
|
108
|
+
def scroll_down(line_count)
|
|
109
|
+
return [] if at_bottom? || line_count.zero? || @lines.empty?
|
|
110
|
+
|
|
111
|
+
self.y_offset = @y_offset + line_count
|
|
112
|
+
visible_lines
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
#: (Integer) -> Array[String]
|
|
116
|
+
def scroll_up(line_count)
|
|
117
|
+
return [] if at_top? || line_count.zero? || @lines.empty?
|
|
118
|
+
|
|
119
|
+
self.y_offset = @y_offset - line_count
|
|
120
|
+
visible_lines
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
#: (Integer) -> void
|
|
124
|
+
def scroll_left(column_count)
|
|
125
|
+
self.x_offset = @x_offset - column_count
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
#: (Integer) -> void
|
|
129
|
+
def scroll_right(column_count)
|
|
130
|
+
self.x_offset = @x_offset + column_count
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
#: () -> Array[String]
|
|
134
|
+
def page_down
|
|
135
|
+
return [] if at_bottom?
|
|
136
|
+
|
|
137
|
+
scroll_down(@height)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
#: () -> Array[String]
|
|
141
|
+
def page_up
|
|
142
|
+
return [] if at_top?
|
|
143
|
+
|
|
144
|
+
scroll_up(@height)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
#: () -> Array[String]
|
|
148
|
+
def half_page_down
|
|
149
|
+
return [] if at_bottom?
|
|
150
|
+
|
|
151
|
+
scroll_down(@height / 2)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
#: () -> Array[String]
|
|
155
|
+
def half_page_up
|
|
156
|
+
return [] if at_top?
|
|
157
|
+
|
|
158
|
+
scroll_up(@height / 2)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
#: () -> Array[String]
|
|
162
|
+
def goto_top
|
|
163
|
+
return [] if at_top?
|
|
164
|
+
|
|
165
|
+
self.y_offset = 0
|
|
166
|
+
visible_lines
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
#: () -> Array[String]
|
|
170
|
+
def goto_bottom
|
|
171
|
+
self.y_offset = max_y_offset
|
|
172
|
+
visible_lines
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
#: () -> Integer
|
|
176
|
+
def total_line_count
|
|
177
|
+
@lines.length
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
#: () -> Integer
|
|
181
|
+
def visible_line_count
|
|
182
|
+
visible_lines.length
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
#: (Bubbletea::Message) -> [Viewport, Bubbletea::Command?]
|
|
186
|
+
def update(message)
|
|
187
|
+
case message
|
|
188
|
+
when Bubbletea::KeyMessage
|
|
189
|
+
handle_key(message)
|
|
190
|
+
when Bubbletea::MouseMessage
|
|
191
|
+
handle_mouse(message) if @mouse_wheel_enabled
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
[self, nil]
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
#: () -> String
|
|
198
|
+
def view
|
|
199
|
+
lines = visible_lines
|
|
200
|
+
content = lines.join("\n")
|
|
201
|
+
|
|
202
|
+
content += "\n" * (@height - lines.length) if lines.length < @height
|
|
203
|
+
|
|
204
|
+
if (style = @style)
|
|
205
|
+
style.width(@width).height(@height).render(content)
|
|
206
|
+
else
|
|
207
|
+
content
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
private
|
|
212
|
+
|
|
213
|
+
#: (Bubbletea::KeyMessage) -> void
|
|
214
|
+
def handle_key(message)
|
|
215
|
+
case message.to_s
|
|
216
|
+
when "pgdown", "ctrl+f"
|
|
217
|
+
page_down
|
|
218
|
+
when "pgup", "ctrl+b"
|
|
219
|
+
page_up
|
|
220
|
+
when "ctrl+d"
|
|
221
|
+
half_page_down
|
|
222
|
+
when "ctrl+u"
|
|
223
|
+
half_page_up
|
|
224
|
+
when "down", "j"
|
|
225
|
+
scroll_down(1)
|
|
226
|
+
when "up", "k"
|
|
227
|
+
scroll_up(1)
|
|
228
|
+
when "left", "h"
|
|
229
|
+
scroll_left(@horizontal_step) if @horizontal_step.positive?
|
|
230
|
+
when "right", "l"
|
|
231
|
+
scroll_right(@horizontal_step) if @horizontal_step.positive?
|
|
232
|
+
when "home", "g"
|
|
233
|
+
goto_top
|
|
234
|
+
when "end", "G"
|
|
235
|
+
goto_bottom
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
#: (Bubbletea::MouseMessage) -> void
|
|
240
|
+
def handle_mouse(message)
|
|
241
|
+
# Mouse wheel support would go here
|
|
242
|
+
# Depends on bubbletea-ruby mouse message support
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
#: () -> Integer
|
|
246
|
+
def max_y_offset
|
|
247
|
+
frame_size = if (style = @style) && style.respond_to?(:vertical_frame_size)
|
|
248
|
+
style.send(:vertical_frame_size).to_i
|
|
249
|
+
else
|
|
250
|
+
0
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
[0, @lines.length - @height + frame_size].max
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
#: () -> Array[String]
|
|
257
|
+
def visible_lines
|
|
258
|
+
h = @height
|
|
259
|
+
if (style = @style) && style.respond_to?(:vertical_frame_size)
|
|
260
|
+
h -= style.send(:vertical_frame_size).to_i
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
return [] if @lines.empty?
|
|
264
|
+
|
|
265
|
+
top = [0, @y_offset].max
|
|
266
|
+
bottom = (@y_offset + h).clamp(top, @lines.length)
|
|
267
|
+
lines = @lines[top...bottom] || []
|
|
268
|
+
|
|
269
|
+
if @x_offset.positive? || @longest_line_width > @width
|
|
270
|
+
w = @width
|
|
271
|
+
if (style = @style) && style.respond_to?(:horizontal_frame_size)
|
|
272
|
+
w -= style.send(:horizontal_frame_size).to_i
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
lines = lines.map do |line|
|
|
276
|
+
cut_string(line, @x_offset, @x_offset + w)
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
lines
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
#: (String, Integer, Integer) -> String
|
|
284
|
+
def cut_string(string, start_column, end_column)
|
|
285
|
+
plain = strip_ansi(string)
|
|
286
|
+
return "" if start_column >= plain.length
|
|
287
|
+
|
|
288
|
+
plain[start_column...end_column] || ""
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
#: (String) -> String
|
|
292
|
+
def strip_ansi(string)
|
|
293
|
+
string.gsub(/\e\[[0-9;]*[A-Za-z]/, "")
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
data/lib/bubbles.rb
CHANGED
|
@@ -1,39 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
require 'pathname'
|
|
3
|
-
require 'logger'
|
|
4
|
-
require 'securerandom'
|
|
5
|
-
require 'yaml'
|
|
6
|
-
require 'json'
|
|
7
|
-
require 'forwardable'
|
|
8
|
-
require 'aws-sdk'
|
|
9
|
-
require "bubbles/version"
|
|
10
|
-
require "bubbles/config"
|
|
11
|
-
require "bubbles/command_queue"
|
|
12
|
-
require "bubbles/bubblicious_file"
|
|
13
|
-
require "bubbles/dir_watcher"
|
|
14
|
-
require 'bubbles/common_uploader_interface'
|
|
15
|
-
require "bubbles/uploaders/s3"
|
|
16
|
-
require "bubbles/uploaders/s3_ensure_connection"
|
|
17
|
-
require "bubbles/uploaders/local_dir"
|
|
1
|
+
# frozen_string_literal: true
|
|
18
2
|
|
|
19
|
-
|
|
20
|
-
extend self
|
|
21
|
-
|
|
22
|
-
def config
|
|
23
|
-
@config ||= Config.new
|
|
24
|
-
end
|
|
3
|
+
require "bubbletea"
|
|
25
4
|
|
|
26
|
-
|
|
27
|
-
|
|
5
|
+
require_relative "bubbles/version"
|
|
6
|
+
require_relative "bubbles/spinner"
|
|
7
|
+
require_relative "bubbles/timer"
|
|
8
|
+
require_relative "bubbles/stopwatch"
|
|
9
|
+
require_relative "bubbles/key"
|
|
10
|
+
require_relative "bubbles/help"
|
|
11
|
+
require_relative "bubbles/paginator"
|
|
12
|
+
require_relative "bubbles/progress"
|
|
13
|
+
require_relative "bubbles/cursor"
|
|
14
|
+
require_relative "bubbles/text_input"
|
|
15
|
+
require_relative "bubbles/text_area"
|
|
16
|
+
require_relative "bubbles/viewport"
|
|
17
|
+
require_relative "bubbles/list"
|
|
18
|
+
require_relative "bubbles/table"
|
|
19
|
+
require_relative "bubbles/file_picker"
|
|
28
20
|
|
|
29
|
-
|
|
30
|
-
config: config,
|
|
31
|
-
command_queue: command_queue
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
loop do
|
|
35
|
-
command_queue.call_next
|
|
36
|
-
sleep config.sleep_interval
|
|
37
|
-
end
|
|
38
|
-
end
|
|
21
|
+
module Bubbles
|
|
39
22
|
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Generated from lib/bubbles/cursor.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module Bubbles
|
|
4
|
+
# Cursor provides cursor functionality for text input components.
|
|
5
|
+
#
|
|
6
|
+
# Example:
|
|
7
|
+
# cursor = Bubbles::Cursor.new
|
|
8
|
+
# cursor.char = "a"
|
|
9
|
+
# cursor.focus
|
|
10
|
+
#
|
|
11
|
+
# # In update:
|
|
12
|
+
# cursor, command = cursor.update(message)
|
|
13
|
+
#
|
|
14
|
+
# # In view:
|
|
15
|
+
# cursor.view
|
|
16
|
+
class Cursor
|
|
17
|
+
MODE_BLINK: Symbol
|
|
18
|
+
|
|
19
|
+
MODE_STATIC: Symbol
|
|
20
|
+
|
|
21
|
+
MODE_HIDE: Symbol
|
|
22
|
+
|
|
23
|
+
DEFAULT_BLINK_SPEED: Float
|
|
24
|
+
|
|
25
|
+
class InitialBlinkMessage < Bubbletea::Message
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class BlinkMessage < Bubbletea::Message
|
|
29
|
+
attr_reader id: Integer
|
|
30
|
+
|
|
31
|
+
attr_reader tag: Integer
|
|
32
|
+
|
|
33
|
+
# : (id: Integer, tag: Integer) -> void
|
|
34
|
+
def initialize: (id: Integer, tag: Integer) -> void
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class BlinkCanceledMessage < Bubbletea::Message
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# : () -> Integer
|
|
41
|
+
def self.next_id: () -> Integer
|
|
42
|
+
|
|
43
|
+
attr_reader id: Integer
|
|
44
|
+
|
|
45
|
+
attr_reader mode: Symbol
|
|
46
|
+
|
|
47
|
+
attr_accessor char: String
|
|
48
|
+
|
|
49
|
+
attr_accessor blink_speed: Float
|
|
50
|
+
|
|
51
|
+
attr_accessor style: Lipgloss::Style?
|
|
52
|
+
|
|
53
|
+
attr_accessor text_style: Lipgloss::Style?
|
|
54
|
+
|
|
55
|
+
# : () -> void
|
|
56
|
+
def initialize: () -> void
|
|
57
|
+
|
|
58
|
+
# : () -> bool
|
|
59
|
+
def blink?: () -> bool
|
|
60
|
+
|
|
61
|
+
# : () -> bool
|
|
62
|
+
def focused?: () -> bool
|
|
63
|
+
|
|
64
|
+
# : (Bubbletea::Message) -> [Cursor, Bubbletea::Command?]
|
|
65
|
+
def update: (Bubbletea::Message) -> [ Cursor, Bubbletea::Command? ]
|
|
66
|
+
|
|
67
|
+
# : (Symbol) -> Bubbletea::Command?
|
|
68
|
+
def set_mode: (Symbol) -> Bubbletea::Command?
|
|
69
|
+
|
|
70
|
+
# : () -> Bubbletea::Command?
|
|
71
|
+
def focus: () -> Bubbletea::Command?
|
|
72
|
+
|
|
73
|
+
# : () -> void
|
|
74
|
+
def blur: () -> void
|
|
75
|
+
|
|
76
|
+
# : () -> String
|
|
77
|
+
def view: () -> String
|
|
78
|
+
|
|
79
|
+
# : () -> Bubbletea::SendMessage
|
|
80
|
+
def self.blink: () -> Bubbletea::SendMessage
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
# : () -> Bubbletea::Command?
|
|
85
|
+
def blink_command: () -> Bubbletea::Command?
|
|
86
|
+
end
|
|
87
|
+
end
|