fatty 0.99.0 → 0.99.3

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.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.org +11 -29
  3. data/README.md +2207 -18
  4. data/README.org +2066 -104
  5. data/exe/fatty +236 -93
  6. data/help/help.md +233 -0
  7. data/lib/fatty/action.rb +3 -7
  8. data/lib/fatty/actionable.rb +2 -0
  9. data/lib/fatty/alert.rb +65 -39
  10. data/lib/fatty/ansi/renderer.rb +26 -6
  11. data/lib/fatty/ansi.rb +1 -0
  12. data/lib/fatty/api/alert.rb +15 -0
  13. data/lib/fatty/api/choose.rb +145 -0
  14. data/lib/fatty/api/environment.rb +12 -0
  15. data/lib/fatty/api/keytest.rb +11 -0
  16. data/lib/fatty/api/menu.rb +104 -0
  17. data/lib/fatty/api/output.rb +55 -0
  18. data/lib/fatty/api/progress.rb +24 -0
  19. data/lib/fatty/api/prompt.rb +77 -0
  20. data/lib/fatty/api/status.rb +55 -0
  21. data/lib/fatty/api.rb +38 -0
  22. data/lib/fatty/callback_environment.rb +74 -0
  23. data/lib/fatty/colors/color.rb +366 -232
  24. data/lib/fatty/colors/pairs.rb +2 -0
  25. data/lib/fatty/command.rb +78 -0
  26. data/lib/fatty/config.rb +227 -39
  27. data/lib/fatty/config_files/config.yml +4 -12
  28. data/lib/fatty/config_files/keydefs.yml +13 -23
  29. data/lib/fatty/config_files/themes/capuchin_monk.yml +140 -0
  30. data/lib/fatty/config_files/themes/catppuccin_latte.yml +140 -0
  31. data/lib/fatty/config_files/themes/catppuccin_mocha.yml +140 -0
  32. data/lib/fatty/config_files/themes/cyberpunk.yml +141 -0
  33. data/lib/fatty/config_files/themes/dracula.yml +140 -0
  34. data/lib/fatty/config_files/themes/everforest_dark.yml +140 -0
  35. data/lib/fatty/config_files/themes/gruvbox_dark.yml +140 -0
  36. data/lib/fatty/config_files/themes/gruvbox_light.yml +140 -0
  37. data/lib/fatty/config_files/themes/mono.yml +119 -47
  38. data/lib/fatty/config_files/themes/monokai.yml +140 -0
  39. data/lib/fatty/config_files/themes/nordic.yml +1 -0
  40. data/lib/fatty/config_files/themes/onedark.yml +140 -0
  41. data/lib/fatty/config_files/themes/solarized_dark.yml +1 -0
  42. data/lib/fatty/config_files/themes/solarized_light.yml +140 -0
  43. data/lib/fatty/config_files/themes/terminal.yml +67 -0
  44. data/lib/fatty/config_files/themes/tokyo_night.yml +140 -0
  45. data/lib/fatty/config_files/themes/wordperfect.yml +80 -9
  46. data/lib/fatty/core_ext/hash.rb +19 -0
  47. data/lib/fatty/core_ext/string.rb +19 -12
  48. data/lib/fatty/core_ext.rb +1 -0
  49. data/lib/fatty/curses/context.rb +86 -84
  50. data/lib/fatty/curses/event_source.rb +128 -40
  51. data/lib/fatty/curses/key_decoder.rb +77 -11
  52. data/lib/fatty/env.rb +21 -20
  53. data/lib/fatty/help.rb +1 -29
  54. data/lib/fatty/history/entry.rb +2 -0
  55. data/lib/fatty/history.rb +17 -14
  56. data/lib/fatty/input_buffer.rb +71 -47
  57. data/lib/fatty/input_field.rb +216 -149
  58. data/lib/fatty/key_event.rb +67 -66
  59. data/lib/fatty/key_map.rb +54 -16
  60. data/lib/fatty/keymaps/emacs.rb +40 -6
  61. data/lib/fatty/log_formats/json.rb +2 -0
  62. data/lib/fatty/log_formats/text.rb +2 -0
  63. data/lib/fatty/logger.rb +7 -2
  64. data/lib/fatty/markdown/ansi_renderer.rb +75 -6
  65. data/lib/fatty/markdown/render.rb +2 -3
  66. data/lib/fatty/mouse_event.rb +142 -2
  67. data/lib/fatty/output_buffer.rb +2 -0
  68. data/lib/fatty/pager.rb +158 -47
  69. data/lib/fatty/progress.rb +417 -0
  70. data/lib/fatty/renderer/curses.rb +340 -184
  71. data/lib/fatty/renderer/truecolor.rb +266 -163
  72. data/lib/fatty/renderer.rb +115 -97
  73. data/lib/fatty/screen.rb +2 -0
  74. data/lib/fatty/session/alert_session.rb +39 -21
  75. data/lib/fatty/session/isearch_session.rb +92 -38
  76. data/lib/fatty/session/keytest_session.rb +105 -85
  77. data/lib/fatty/session/modal_session.rb +38 -1
  78. data/lib/fatty/session/output_session.rb +472 -21
  79. data/lib/fatty/session/popup_session.rb +251 -134
  80. data/lib/fatty/session/prompt_session.rb +68 -26
  81. data/lib/fatty/session/search_session.rb +79 -31
  82. data/lib/fatty/session/shell_session.rb +383 -368
  83. data/lib/fatty/session/status_session.rb +167 -0
  84. data/lib/fatty/session.rb +95 -95
  85. data/lib/fatty/terminal/popup_owner.rb +14 -11
  86. data/lib/fatty/terminal.rb +515 -813
  87. data/lib/fatty/themes/loader.rb +4 -2
  88. data/lib/fatty/themes/manager.rb +4 -0
  89. data/lib/fatty/themes/registry.rb +2 -0
  90. data/lib/fatty/themes/resolver.rb +175 -162
  91. data/lib/fatty/version.rb +1 -1
  92. data/lib/fatty/viewport.rb +8 -1
  93. data/lib/fatty.rb +35 -7
  94. metadata +42 -40
  95. data/.envrc +0 -2
  96. data/.simplecov +0 -23
  97. data/.yardopts +0 -4
  98. data/CHANGELOG.md +0 -34
  99. data/Rakefile +0 -15
  100. data/TODO.org +0 -163
  101. data/examples/markdown/native-markdown.md +0 -370
  102. data/examples/markdown/ox-gfm-markdown.md +0 -373
  103. data/examples/markdown/ox-gfm-markdown.org +0 -376
  104. data/fatty.gemspec +0 -42
  105. data/lib/fatty/accept_env.rb +0 -32
  106. data/lib/fatty/config_files/help.md +0 -120
  107. data/lib/fatty/config_files/help.org +0 -124
  108. data/lib/fatty/config_files/themes/wordperfect_light.yml +0 -77
  109. data/lib/fatty/menu_env.rb +0 -22
  110. data/lib/fatty/session/input_session.rb +0 -99
  111. data/lib/fatty/sessions.rb +0 -14
  112. data/lib/fatty/terminal/progress.rb +0 -374
  113. data/lib/fatty/view/alert_view.rb +0 -14
  114. data/lib/fatty/view/cursor_view.rb +0 -18
  115. data/lib/fatty/view/input_view.rb +0 -9
  116. data/lib/fatty/view/output_view.rb +0 -9
  117. data/lib/fatty/view/status_view.rb +0 -14
  118. data/lib/fatty/view.rb +0 -33
  119. data/lib/fatty/views.rb +0 -9
  120. data/sig/fatty.rbs +0 -4
data/exe/fatty CHANGED
@@ -7,21 +7,32 @@ require "shellwords"
7
7
 
8
8
  require "rainbow/refinement"
9
9
  using Rainbow
10
+ using Fatty::CoreExt::String
10
11
 
11
12
  prompt = Fatty::Prompt.new { "#{Dir.pwd}> " }
12
13
  history_ctx_proc = -> { { pwd: Dir.pwd } }
13
14
 
14
15
  on_accept_proc = lambda do |line, env|
15
- session = env.session
16
- terminal = env.terminal
17
-
18
16
  cmd, *args = Shellwords.split(line)
19
-
20
17
  case cmd
21
18
  when "cd"
22
19
  dir = args.first || ENV["HOME"]
23
20
  dir = File.expand_path(dir)
24
21
  Dir.chdir(dir)
22
+ when "environment"
23
+ env.append("The environment Fatty has detected is:\n")
24
+ env.environment.each do |key, value|
25
+ if value.is_a?(Hash)
26
+ env.append("#{key}:\n")
27
+ value.each do |sub_key, sub_value|
28
+ env.append(" #{sub_key} -> #{sub_value}\n")
29
+ end
30
+ else
31
+ env.append("#{key} -> #{value}\n")
32
+ end
33
+ end
34
+
35
+ nil
25
36
  when "choose"
26
37
  choices =
27
38
  if args.empty?
@@ -29,42 +40,30 @@ on_accept_proc = lambda do |line, env|
29
40
  else
30
41
  args
31
42
  end
32
-
33
- choice = terminal.choose(
43
+ choice = env.choose(
34
44
  "Select one",
35
45
  choices: choices,
36
46
  initial_choice_idx: 0,
37
- quit_value: nil,
47
+ cancel_value: nil,
38
48
  )
39
-
40
- if choice.nil?
41
- session.append_output("choose: cancelled\n")
42
- else
43
- session.append_output("choose: selected #{choice.inspect}\n")
44
- end
49
+ choice ? "choose: #{choice}\n" : "choose: cancelled\n"
45
50
  when "choosevals"
46
51
  pairs =
47
52
  if args.empty?
48
- [["Choice A", 55], ["Choice B", 3.14159], ["Choice C", :done]]
53
+ { "Choice A" => 55, "Choice B" => 3.14159, "Choice C" => 2.7181828 }
49
54
  elsif args.length.odd?
50
55
  session.append_output("choosevals: needs even number of args\n")
51
56
  next
52
57
  else
53
- args.each_slice(2).map { |label, value| [label, value] }
58
+ args.each_slice(2).to_h
54
59
  end
55
-
56
- choice = terminal.choose(
60
+ choice = env.choose(
57
61
  "Select one",
58
62
  choices: pairs,
59
63
  initial_choice_idx: 1,
60
- quit_value: nil,
64
+ cancel_value: nil,
61
65
  )
62
-
63
- if choice.nil?
64
- session.append_output("choosevals: cancelled\n")
65
- else
66
- session.append_output("choosevals: selected #{choice.inspect}\n")
67
- end
66
+ choice ? "choosevals: #{choice}\n" : "choosevals: cancelled\n"
68
67
  when "choose_multi"
69
68
  choices =
70
69
  if args.empty?
@@ -72,90 +71,89 @@ on_accept_proc = lambda do |line, env|
72
71
  else
73
72
  args
74
73
  end
75
-
76
- choices = terminal.choose_multi(
74
+ choices = env.choose_multi(
77
75
  "Select one or more",
78
76
  choices: choices,
79
- quit_value: nil,
77
+ cancel_value: nil,
80
78
  )
81
-
82
- if choices.nil?
83
- session.append_output("choose_multi: cancelled\n")
84
- else
85
- session.append_output("choose_multi: selected #{choices.inspect}\n")
86
- end
79
+ choices ? "choose_multi: #{choices}\n" : "choose_multi: cancelled\n"
87
80
  when "choosevals_multi"
88
81
  pairs =
89
82
  if args.empty?
90
- pairs = [["Choice A", 55], ["Choice B", 3.14159], ["Choice C", :done]]
83
+ { "Choice A" => 55, "Choice B" => 3.14159, "Choice C" => 2.7181828 }
91
84
  elsif args.length.odd?
92
85
  session.append_output("choosevals_multi: needs even number of args\n")
93
86
  next
94
87
  else
95
- pairs = args.each_slice(2).map { |label, value| [label, value] }
88
+ args.each_slice(2).to_h
96
89
  end
97
-
98
- choices = terminal.choose_multi(
90
+ choices = env.choose_multi(
99
91
  "Select one or more",
100
92
  choices: pairs,
101
- quit_value: nil,
93
+ cancel_value: nil,
102
94
  )
103
-
104
- if choices.nil?
105
- session.append_output("choosevals_multi: cancelled\n")
106
- else
107
- session.append_output("choosevals_multi: selected #{choices.inspect}\n")
108
- end
95
+ choices ? "choosevals_multi: #{choices}\n" : "choosevals_multi: cancelled\n"
96
+ when "confirm"
97
+ prompt =
98
+ if args.empty?
99
+ "Do you like fatty?"
100
+ else
101
+ args
102
+ end
103
+ reply = env.confirm(prompt)
104
+ reply ? "confirm: True\n" : "confirm: False\n"
109
105
  when "menu"
110
- result = terminal.menu(
106
+ result = env.menu(
111
107
  "Demo menu",
112
108
  choices: {
113
109
  "Write to output pane" => proc { |env|
114
- env.output("menu: wrote this from MenuEnv#output\n")
115
- :output_written
116
- },
117
-
110
+ env.append("menu: wrote this from MenuEnv#output\n")
111
+ :output_written
112
+ },
118
113
  "Show status message" => proc { |env|
119
- env.status("menu: status set from MenuEnv#status")
120
- :status_written
121
- },
122
-
123
- "Return plain value" => :plain_value,
114
+ env.status("menu: status set from MenuEnv#status")
115
+ :status_written
116
+ },
117
+ "Show markdown" => proc { |env|
118
+ env.markdown("\# Your Choices\n\n- one\n- two\n- three\n")
119
+ :markdown_written
120
+ },
124
121
  },
125
- initial_choice_idx: 0,
126
- quit_value: nil,
127
122
  )
128
123
  if result.nil?
129
- session.append_output("menu: cancelled\n")
124
+ env.append("menu: cancelled\n")
130
125
  else
131
- session.append_output("menu: result #{result.inspect}\n")
126
+ env.append("menu: result #{result.inspect}\n")
132
127
  end
133
128
  when "info"
134
129
  message = args.empty? ? "Information message" : args.join(" ")
135
130
  message = message.gsub(/ ?~~ ?/, "\n")
136
- terminal.info(message)
137
- session.append_output("info: #{message}\n")
131
+ env.info(message)
132
+ env.append("info: #{message}\n")
138
133
  when "good"
139
134
  message = args.empty? ? "Success message" : args.join(" ")
140
135
  message = message.gsub(/ ?~~ ?/, "\n")
141
- terminal.good(message)
142
- session.append_output("good: #{message}\n")
136
+ env.good(message)
137
+ env.append("good: #{message}\n")
143
138
  when "warn"
144
139
  message = args.empty? ? "Danger Will Robinson!" : args.join(" ")
145
140
  message = message.gsub(/ ?~~ ?/, "\n")
146
- terminal.warn(message)
147
- session.append_output("warn: #{message}\n")
141
+ env.warn(message)
142
+ env.append("warn: #{message}\n")
148
143
  when "oops"
149
144
  message = args.empty? ? "Something went wrong" : args.join(" ")
150
145
  message = message.gsub(/ ?~~ ?/, "\n")
151
- terminal.oops(message)
152
- session.append_output("oops: #{message}\n")
146
+ env.oops(message)
147
+ env.append("oops: #{message}\n")
153
148
  when "prompt"
154
149
  question = args.empty? ? "Enter value:" : args.join(" ")
155
- value = terminal.prompt(question, initial: "demo")
156
- session.append_output("prompt: #{value.inspect}\n")
150
+ value = env.prompt(question, initial: "demo")
151
+ env.append("prompt: #{value.inspect}\n")
157
152
  when "progress"
158
- styles = %w[count percent simple_percent trail bar unicode_bar braille_bar spinner]
153
+ # The first argument is the style (or :trail if not given), the optional
154
+ # second argument is the total if it is all digits, or the role otherwise.
155
+ # If the role is not recognized, it defaults to :info
156
+ styles = %w[count percent count_percent trail bar unicode_bar braille_bar spinner]
159
157
  style_name = args.first
160
158
  style =
161
159
  if style_name && styles.include?(style_name)
@@ -163,35 +161,38 @@ on_accept_proc = lambda do |line, env|
163
161
  else
164
162
  :trail
165
163
  end
166
-
167
- total =
168
- begin
169
- args.first ? Integer(args.first) : nil
170
- rescue StandardError
171
- nil
164
+ total = 40
165
+ width = 60
166
+ role = :info
167
+ args.each do |arg|
168
+ case arg
169
+ when /\A\d+\z/
170
+ total = Integer(arg)
171
+ when Symbol, String
172
+ md = arg.match(/good|info|warn|error/i)
173
+ role = md ? md[0].to_sym : :info
172
174
  end
173
-
175
+ end
174
176
  if style != :spinner
175
177
  total ||= 40
176
178
  total = 1 if total < 1
177
179
  elsif total && total < 1
178
180
  total = 1
179
181
  end
180
-
181
- prog = env.progress(
182
+ prog = env.add_progress(
182
183
  label: "Demo progress",
183
184
  total: total,
184
185
  style: style,
186
+ role: role || :info,
187
+ width: width,
185
188
  )
186
-
187
189
  if style == :spinner && total.nil?
188
190
  40.times do
189
191
  prog.update(render: true)
190
192
  sleep 0.1
191
193
  end
192
-
193
- prog.finish("Demo progress complete.", render: true)
194
- session.append_output("progress: style=#{style} completed indeterminate demo\n")
194
+ prog.finish("Done", render: true)
195
+ env.append("progress: style=#{style} completed indeterminate demo\n")
195
196
  else
196
197
  total.times do |i|
197
198
  indicator =
@@ -201,15 +202,14 @@ on_accept_proc = lambda do |line, env|
201
202
  when 2 then "^".fatty_warn
202
203
  else "."
203
204
  end
204
-
205
205
  prog.update(current: i + 1, indicator: indicator, render: true)
206
206
  sleep 0.1
207
207
  end
208
- prog.finish("Demo progress complete.".green, render: true)
209
- session.append_output("progress: style=#{style} completed #{total} steps\n")
208
+ prog.finish("Done".green, render: true)
209
+ env.append("progress: style=#{style} completed #{total} steps\n")
210
210
  end
211
211
  when "keytest"
212
- return [[:terminal, :push_modal, Fatty::KeyTestSession.new(owner: session)], [:send, :alert, :clear, {}]]
212
+ env.keytest
213
213
  when 'markdown'
214
214
  file = args.first || File.expand_path("../examples/markdown/native-markdown.md", __dir__)
215
215
  text = File.read(File.expand_path(file))
@@ -218,17 +218,158 @@ on_accept_proc = lambda do |line, env|
218
218
  file = File.expand_path("../examples/markdown/ox-gfm-markdown.md", __dir__)
219
219
  text = File.read(File.expand_path(file))
220
220
  env.markdown(text)
221
+ when "interrupt"
222
+ seconds =
223
+ if args.first&.match?(/\A\d+\z/)
224
+ Integer(args.first)
225
+ else
226
+ 120
227
+ end
228
+ deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + seconds
229
+ last_remaining = nil
230
+ begin
231
+ loop do
232
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
233
+ remaining = (deadline - now).ceil
234
+ break if remaining <= 0
235
+
236
+ if remaining != last_remaining
237
+ env.status(
238
+ "Interrupt demo: #{remaining} seconds remaining — press C-c or C-g",
239
+ replace: true,
240
+ )
241
+ last_remaining = remaining
242
+ end
243
+ env.check_interrupt!
244
+ sleep 0.1
245
+ end
246
+ rescue Fatty::Interrupt
247
+ env.warn("Interrupt demo interrupted.", replace: true)
248
+ raise
249
+ else
250
+ env.good("Interrupt demo completed.", replace: true)
251
+ end
252
+ when "colors"
253
+ environment = env.environment
254
+ available = environment.dig(:curses, :colors).to_i
255
+ available = 16 if available <= 0
256
+ truecolor_enabled = environment[:truecolor_enabled]
257
+
258
+ env.append("Fatty color report\n")
259
+ env.append("truecolor enabled -> #{truecolor_enabled}\n")
260
+ env.append("available indexed colors -> #{available}\n")
261
+ env.append("rendering mode -> #{truecolor_enabled ? '24-bit RGB' : "#{available}-color indexed"}\n")
262
+ env.append("\n")
263
+
264
+
265
+ env.append("ANSI colors\n")
266
+ Fatty::Color.ansi_colors.each do |name, index|
267
+ rgb = Fatty::Color.xterm_rgb_for_index(index)
268
+ env.append(
269
+ color_swatch_line(
270
+ name: name,
271
+ requested_rgb: rgb,
272
+ fallback_index: Fatty::Color.resolve(index, available_colors: available),
273
+ truecolor: truecolor_enabled,
274
+ )
275
+ )
276
+ end
277
+
278
+ env.append("\n256 colors\n")
279
+ 256.times do |index|
280
+ rgb = Fatty::Color.xterm_rgb_for_index(index)
281
+ env.append(
282
+ color_swatch_line(
283
+ name: Fatty::Color.xterm_color_name(index),
284
+ requested_rgb: rgb,
285
+ fallback_index: Fatty::Color.resolve(index, available_colors: available),
286
+ truecolor: truecolor_enabled,
287
+ )
288
+ )
289
+ end
290
+
291
+ env.append("\nX11 colors\n")
292
+ Fatty::Color.x11_colors_by_name.each do |name, rgb|
293
+ fallback_index = Fatty::Color.resolve(
294
+ Fatty::Color.hex_for_rgb(rgb),
295
+ available_colors: available,
296
+ )
297
+
298
+ env.append(
299
+ color_swatch_line(
300
+ name: name,
301
+ requested_rgb: rgb,
302
+ fallback_index: fallback_index,
303
+ truecolor: truecolor_enabled,
304
+ )
305
+ )
306
+ end
307
+
308
+ nil
221
309
  when 'help'
222
310
  env.markdown(Fatty::Help.text)
223
311
  else
224
- out, _ = Open3.capture2e(line)
225
- session.append_output(out)
312
+ unless line.match?(/\A\s*\z/)
313
+ out, _ = Open3.capture2e(line)
314
+ out
315
+ end
316
+ end
317
+ rescue Errno::ENOENT
318
+ env.alert("Command not found (#{line})", role: :error)
319
+ nil
320
+ rescue SystemCallError => ex
321
+ env.alert(ex.message, role: :error)
322
+ nil
323
+ end
324
+
325
+ def color_swatch_line(name:, requested_rgb:, fallback_index:, truecolor:, width: 24)
326
+ fallback_rgb = Fatty::Color.xterm_rgb_for_index(fallback_index)
327
+ rendered_rgb =
328
+ if truecolor
329
+ requested_rgb
330
+ else
331
+ fallback_rgb
332
+ end
333
+
334
+ band = color_band_for_rgb(
335
+ name: name,
336
+ rgb: rendered_rgb,
337
+ truecolor: truecolor,
338
+ fallback_index: fallback_index,
339
+ width: width,
340
+ )
341
+
342
+ format(
343
+ "%3s %-28s requested %s rendered %s fallback %3d %s %s\n",
344
+ truecolor ? "-" : fallback_index,
345
+ name,
346
+ Fatty::Color.hex_for_rgb(requested_rgb),
347
+ Fatty::Color.hex_for_rgb(rendered_rgb),
348
+ fallback_index,
349
+ Fatty::Color.hex_for_rgb(fallback_rgb),
350
+ band,
351
+ )
352
+ end
353
+
354
+ def color_band_for_rgb(name:, rgb:, truecolor:, fallback_index:, width: 36)
355
+ label = " #{name} "
356
+ label = label[0, width].center(width)
357
+
358
+ if truecolor
359
+ fg = Fatty::Color.contrasting_rgb(rgb)
360
+
361
+ "\e[38;2;#{fg[0]};#{fg[1]};#{fg[2]}m" \
362
+ "\e[48;2;#{rgb[0]};#{rgb[1]};#{rgb[2]}m" \
363
+ "#{label}" \
364
+ "\e[0m"
365
+ else
366
+ fg_index = Fatty::Color.contrasting_ansi_index(fallback_index)
367
+
368
+ "\e[38;5;#{fg_index}m" \
369
+ "\e[48;5;#{fallback_index}m" \
370
+ "#{label}" \
371
+ "\e[0m"
226
372
  end
227
- [[:send, :alert, :clear, {}]]
228
- rescue Errno::ENOENT
229
- [[:send, :alert, :show, { level: :error, message: "Command not found (#{line})" }]]
230
- rescue SystemCallError => ex
231
- [[:send, :alert, :show, { level: :error, message: ex.message }]]
232
373
  end
233
374
 
234
375
  cutes = []
@@ -250,8 +391,10 @@ builtins = %w[
250
391
  choosevals
251
392
  choose_multi
252
393
  choosevals_multi
394
+ colors
253
395
  help
254
396
  info
397
+ interrupt
255
398
  good
256
399
  keytest
257
400
  markdown