thaum 0.1.0 → 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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +106 -14
  3. data/examples/checkbox.rb +89 -0
  4. data/examples/counter.rb +50 -0
  5. data/examples/hello_world.rb +28 -0
  6. data/examples/layout_demo.rb +138 -0
  7. data/examples/modal.rb +76 -0
  8. data/examples/mouse.rb +60 -0
  9. data/examples/octagram_picker.rb +224 -0
  10. data/examples/picker.rb +150 -0
  11. data/examples/progress_bar.rb +90 -0
  12. data/examples/scroll_view.rb +64 -0
  13. data/examples/select.rb +64 -0
  14. data/examples/spinner.rb +66 -0
  15. data/examples/status_bar.rb +65 -0
  16. data/examples/stopwatch.rb +84 -0
  17. data/examples/table.rb +196 -0
  18. data/examples/tabs.rb +112 -0
  19. data/examples/text.rb +101 -0
  20. data/examples/theme_picker.rb +95 -0
  21. data/examples/todo.rb +242 -0
  22. data/lib/thaum/action.rb +30 -0
  23. data/lib/thaum/app.rb +87 -0
  24. data/lib/thaum/color.rb +97 -0
  25. data/lib/thaum/concerns/context_update.rb +40 -0
  26. data/lib/thaum/concerns/focus.rb +53 -0
  27. data/lib/thaum/concerns/layout.rb +349 -0
  28. data/lib/thaum/concerns/modal.rb +102 -0
  29. data/lib/thaum/concerns/tab_navigation.rb +97 -0
  30. data/lib/thaum/dispatch.rb +149 -0
  31. data/lib/thaum/escape_parser.rb +265 -0
  32. data/lib/thaum/event.rb +13 -0
  33. data/lib/thaum/events.rb +28 -0
  34. data/lib/thaum/hit_test.rb +28 -0
  35. data/lib/thaum/input_reader.rb +46 -0
  36. data/lib/thaum/key_event.rb +13 -0
  37. data/lib/thaum/keys.rb +55 -0
  38. data/lib/thaum/minitest.rb +64 -0
  39. data/lib/thaum/octagram.rb +76 -0
  40. data/lib/thaum/painter.rb +49 -0
  41. data/lib/thaum/rect.rb +5 -0
  42. data/lib/thaum/rendering/box_drawing.rb +186 -0
  43. data/lib/thaum/rendering/buffer.rb +84 -0
  44. data/lib/thaum/rendering/canvas.rb +219 -0
  45. data/lib/thaum/rendering/cell.rb +11 -0
  46. data/lib/thaum/rendering/renderer.rb +98 -0
  47. data/lib/thaum/rendering/style.rb +13 -0
  48. data/lib/thaum/run_loop.rb +182 -0
  49. data/lib/thaum/seq.rb +91 -0
  50. data/lib/thaum/sigil.rb +41 -0
  51. data/lib/thaum/sigils/button.rb +47 -0
  52. data/lib/thaum/sigils/checkbox.rb +57 -0
  53. data/lib/thaum/sigils/progress_bar.rb +65 -0
  54. data/lib/thaum/sigils/scroll_view.rb +115 -0
  55. data/lib/thaum/sigils/select.rb +56 -0
  56. data/lib/thaum/sigils/spinner.rb +39 -0
  57. data/lib/thaum/sigils/status_bar.rb +89 -0
  58. data/lib/thaum/sigils/table.rb +156 -0
  59. data/lib/thaum/sigils/tabs.rb +59 -0
  60. data/lib/thaum/sigils/text.rb +22 -0
  61. data/lib/thaum/sigils/text_input.rb +86 -0
  62. data/lib/thaum/terminal.rb +46 -0
  63. data/lib/thaum/themes.rb +267 -0
  64. data/lib/thaum/tree.rb +16 -0
  65. data/lib/thaum/version.rb +1 -1
  66. data/lib/thaum.rb +64 -1
  67. metadata +114 -4
@@ -0,0 +1,267 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thaum
4
+ Theme = Data.define(
5
+ :bg, :fg, :accent, :border, :dim,
6
+ :selection, :selection_fg, :pressed,
7
+ :input_bg, :bar_bg,
8
+ :success_fg, :warning_fg, :error_fg, :info_fg,
9
+ :muted_fg, :disabled_fg
10
+ ) do
11
+ # New semantic fields are optional to keep existing Theme.new callers
12
+ # working. Defaults are chosen to be safe and legible.
13
+ def initialize(
14
+ bg:, fg:, accent:, border:, dim:,
15
+ selection:, selection_fg:, pressed:,
16
+ input_bg:, bar_bg:,
17
+ success_fg: nil, warning_fg: nil, error_fg: nil, info_fg: nil,
18
+ muted_fg: nil, disabled_fg: nil
19
+ )
20
+ success_fg ||= accent
21
+ warning_fg ||= accent
22
+ error_fg ||= accent
23
+ info_fg ||= accent
24
+ muted_fg ||= dim
25
+ disabled_fg ||= dim
26
+
27
+ super
28
+ end
29
+ end
30
+
31
+ module Themes
32
+ CATPPUCCIN_MOCHA = Theme.new(
33
+ bg: "#1e1e2e",
34
+ fg: "#cdd6f4",
35
+ accent: "#89b4fa",
36
+ border: "#45475a",
37
+ dim: "#585b70",
38
+ selection: "#313244",
39
+ selection_fg: "#cdd6f4",
40
+ pressed: "#181825",
41
+ input_bg: "#181825",
42
+ bar_bg: "#11111b",
43
+ success_fg: "#a6e3a1",
44
+ warning_fg: "#f9e2af",
45
+ error_fg: "#f38ba8",
46
+ info_fg: "#89dceb",
47
+ muted_fg: "#7f849c",
48
+ disabled_fg: "#6c7086"
49
+ )
50
+
51
+ CATPPUCCIN_LATTE = Theme.new(
52
+ bg: "#eff1f5",
53
+ fg: "#4c4f69",
54
+ accent: "#1e66f5",
55
+ border: "#bcc0cc",
56
+ dim: "#acb0be",
57
+ selection: "#ccd0da",
58
+ selection_fg: "#4c4f69",
59
+ pressed: "#dce0e8",
60
+ input_bg: "#e6e9ef",
61
+ bar_bg: "#dce0e8",
62
+ success_fg: "#40a02b",
63
+ warning_fg: "#df8e1d",
64
+ error_fg: "#d20f39",
65
+ info_fg: "#209fb5",
66
+ muted_fg: "#8c8fa1",
67
+ disabled_fg: "#9ca0b0"
68
+ )
69
+
70
+ GRUVBOX_DARK = Theme.new(
71
+ bg: "#282828",
72
+ fg: "#ebdbb2",
73
+ accent: "#fabd2f",
74
+ border: "#504945",
75
+ dim: "#7c6f64",
76
+ selection: "#3c3836",
77
+ selection_fg: "#ebdbb2",
78
+ pressed: "#1d2021",
79
+ input_bg: "#1d2021",
80
+ bar_bg: "#1d2021",
81
+ success_fg: "#b8bb26",
82
+ warning_fg: "#fabd2f",
83
+ error_fg: "#fb4934",
84
+ info_fg: "#83a598",
85
+ muted_fg: "#a89984",
86
+ disabled_fg: "#7c6f64"
87
+ )
88
+
89
+ NORD = Theme.new(
90
+ bg: "#2e3440",
91
+ fg: "#d8dee9",
92
+ accent: "#88c0d0",
93
+ border: "#3b4252",
94
+ dim: "#4c566a",
95
+ selection: "#434c5e",
96
+ selection_fg: "#eceff4",
97
+ pressed: "#242933",
98
+ input_bg: "#3b4252",
99
+ bar_bg: "#242933",
100
+ success_fg: "#a3be8c",
101
+ warning_fg: "#ebcb8b",
102
+ error_fg: "#bf616a",
103
+ info_fg: "#88c0d0",
104
+ muted_fg: "#81a1c1",
105
+ disabled_fg: "#616e88"
106
+ )
107
+
108
+ DRACULA = Theme.new(
109
+ bg: "#282a36",
110
+ fg: "#f8f8f2",
111
+ accent: "#bd93f9",
112
+ border: "#44475a",
113
+ dim: "#6272a4",
114
+ selection: "#44475a",
115
+ selection_fg: "#f8f8f2",
116
+ pressed: "#21222c",
117
+ input_bg: "#21222c",
118
+ bar_bg: "#191a21",
119
+ success_fg: "#50fa7b",
120
+ warning_fg: "#f1fa8c",
121
+ error_fg: "#ff5555",
122
+ info_fg: "#8be9fd",
123
+ muted_fg: "#6272a4",
124
+ disabled_fg: "#6272a4"
125
+ )
126
+
127
+ SOLARIZED_DARK = Theme.new(
128
+ bg: "#002b36",
129
+ fg: "#839496",
130
+ accent: "#268bd2",
131
+ border: "#073642",
132
+ dim: "#586e75",
133
+ selection: "#073642",
134
+ selection_fg: "#93a1a1",
135
+ pressed: "#001f27",
136
+ input_bg: "#073642",
137
+ bar_bg: "#001f27",
138
+ success_fg: "#859900",
139
+ warning_fg: "#b58900",
140
+ error_fg: "#dc322f",
141
+ info_fg: "#2aa198",
142
+ muted_fg: "#657b83",
143
+ disabled_fg: "#586e75"
144
+ )
145
+
146
+ SOLARIZED_LIGHT = Theme.new(
147
+ bg: "#fdf6e3",
148
+ fg: "#657b83",
149
+ accent: "#268bd2",
150
+ border: "#eee8d5",
151
+ dim: "#93a1a1",
152
+ selection: "#eee8d5",
153
+ selection_fg: "#586e75",
154
+ pressed: "#ddd6c1",
155
+ input_bg: "#eee8d5",
156
+ bar_bg: "#ddd6c1",
157
+ success_fg: "#859900",
158
+ warning_fg: "#b58900",
159
+ error_fg: "#dc322f",
160
+ info_fg: "#2aa198",
161
+ muted_fg: "#93a1a1",
162
+ disabled_fg: "#93a1a1"
163
+ )
164
+
165
+ MATERIAL = Theme.new(
166
+ bg: "#263238",
167
+ fg: "#eeffff",
168
+ accent: "#82aaff",
169
+ border: "#314549",
170
+ dim: "#546e7a",
171
+ selection: "#314549",
172
+ selection_fg: "#eeffff",
173
+ pressed: "#1e272c",
174
+ input_bg: "#1e272c",
175
+ bar_bg: "#1e272c",
176
+ success_fg: "#c3e88d",
177
+ warning_fg: "#ffcb6b",
178
+ error_fg: "#f07178",
179
+ info_fg: "#89ddff",
180
+ muted_fg: "#78909c",
181
+ disabled_fg: "#607d8b"
182
+ )
183
+
184
+ BY_NAME = {
185
+ catppuccin_mocha: CATPPUCCIN_MOCHA,
186
+ catppuccin_latte: CATPPUCCIN_LATTE,
187
+ gruvbox_dark: GRUVBOX_DARK,
188
+ nord: NORD,
189
+ dracula: DRACULA,
190
+ solarized_dark: SOLARIZED_DARK,
191
+ solarized_light: SOLARIZED_LIGHT,
192
+ material: MATERIAL
193
+ }.freeze
194
+
195
+ REQUIRED_KEYS = Theme.members.freeze
196
+
197
+ CONTRAST_RULES = [
198
+ [:fg, :bg, 4.0],
199
+ [:selection_fg, :selection, 4.3],
200
+ [:accent, :bg, 2.5],
201
+ [:muted_fg, :bg, 2.0],
202
+ [:disabled_fg, :bg, 1.5]
203
+ ].freeze
204
+
205
+ DEFAULT = CATPPUCCIN_MOCHA
206
+
207
+ def self.validate_all!
208
+ BY_NAME.each { |name, theme| validate_theme!(name: name, theme: theme) }
209
+ true
210
+ end
211
+
212
+ def self.[](name)
213
+ BY_NAME.fetch(name) do
214
+ raise ArgumentError, "unknown theme #{name.inspect} (known: #{BY_NAME.keys.join(", ")})"
215
+ end
216
+ end
217
+
218
+ def self.names = BY_NAME.keys
219
+
220
+ def self.validate_theme!(name:, theme:)
221
+ missing = REQUIRED_KEYS.select { |key| theme.public_send(key).nil? }
222
+ raise ArgumentError, "theme #{name} is missing keys: #{missing.join(', ')}" unless missing.empty?
223
+
224
+ CONTRAST_RULES.each do |fg_key, bg_key, min|
225
+ fg = theme.public_send(fg_key)
226
+ bg = theme.public_send(bg_key)
227
+ next unless hex_color?(fg) && hex_color?(bg)
228
+
229
+ ratio = contrast_ratio(hex_a: fg, hex_b: bg)
230
+ next if ratio >= min
231
+
232
+ raise ArgumentError,
233
+ format("theme %s contrast too low for %s/%s: %.2f < %.1f", name, fg_key, bg_key, ratio, min)
234
+ end
235
+
236
+ true
237
+ end
238
+
239
+ def self.hex_color?(value)
240
+ value.is_a?(String) && /\A#[0-9a-fA-F]{6}\z/.match?(value)
241
+ end
242
+
243
+ def self.contrast_ratio(hex_a:, hex_b:)
244
+ l1 = relative_luminance(hex_a)
245
+ l2 = relative_luminance(hex_b)
246
+ hi = [l1, l2].max
247
+ lo = [l1, l2].min
248
+ (hi + 0.05) / (lo + 0.05)
249
+ end
250
+
251
+ def self.relative_luminance(hex)
252
+ r = hex[1, 2].to_i(16) / 255.0
253
+ g = hex[3, 2].to_i(16) / 255.0
254
+ b = hex[5, 2].to_i(16) / 255.0
255
+
256
+ # WCAG sRGB transform
257
+ rs = r <= 0.03928 ? (r / 12.92) : (((r + 0.055) / 1.055)**2.4)
258
+ gs = g <= 0.03928 ? (g / 12.92) : (((g + 0.055) / 1.055)**2.4)
259
+ bs = b <= 0.03928 ? (b / 12.92) : (((b + 0.055) / 1.055)**2.4)
260
+ (0.2126 * rs) + (0.7152 * gs) + (0.0722 * bs)
261
+ end
262
+
263
+ private_class_method :validate_theme!, :hex_color?, :contrast_ratio, :relative_luminance
264
+
265
+ validate_all!
266
+ end
267
+ end
data/lib/thaum/tree.rb ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thaum
4
+ # Recursive walk over a Layout subtree. Yields every direct or transitive
5
+ # child of `node`, in declaration order. Callers filter by class.
6
+ module Tree
7
+ module_function
8
+
9
+ def walk(node, &block)
10
+ (node.subtree_children || []).each do |child|
11
+ block.call(child)
12
+ walk(child, &block) if child.respond_to?(:subtree_children)
13
+ end
14
+ end
15
+ end
16
+ end
data/lib/thaum/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Thaum
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/thaum.rb CHANGED
@@ -1,8 +1,71 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "concurrent"
4
+ require "unicode/display_width/string_ext"
5
+
3
6
  require_relative "thaum/version"
7
+ require_relative "thaum/rendering/style"
8
+ require_relative "thaum/rendering/cell"
9
+ require_relative "thaum/rect"
10
+ require_relative "thaum/rendering/box_drawing"
11
+ require_relative "thaum/rendering/buffer"
12
+ require_relative "thaum/rendering/canvas"
13
+ require_relative "thaum/seq"
14
+ require_relative "thaum/color"
15
+ require_relative "thaum/terminal"
16
+ require_relative "thaum/event"
17
+ require_relative "thaum/key_event"
18
+ require_relative "thaum/events"
19
+ require_relative "thaum/keys"
20
+ require_relative "thaum/escape_parser"
21
+ require_relative "thaum/input_reader"
22
+ require_relative "thaum/rendering/renderer"
23
+ require_relative "thaum/themes"
24
+ require_relative "thaum/sigil"
25
+ require_relative "thaum/concerns/layout"
26
+ require_relative "thaum/octagram"
27
+ require_relative "thaum/concerns/focus"
28
+ require_relative "thaum/concerns/context_update"
29
+ require_relative "thaum/concerns/modal"
30
+ require_relative "thaum/concerns/tab_navigation"
31
+ require_relative "thaum/app"
32
+ require_relative "thaum/action"
33
+ require_relative "thaum/tree"
34
+ require_relative "thaum/hit_test"
35
+ require_relative "thaum/painter"
36
+ require_relative "thaum/dispatch"
37
+ require_relative "thaum/run_loop"
38
+ require_relative "thaum/sigils/text"
39
+ require_relative "thaum/sigils/text_input"
40
+ require_relative "thaum/sigils/select"
41
+ require_relative "thaum/sigils/button"
42
+ require_relative "thaum/sigils/scroll_view"
43
+ require_relative "thaum/sigils/table"
44
+ require_relative "thaum/sigils/spinner"
45
+ require_relative "thaum/sigils/progress_bar"
46
+ require_relative "thaum/sigils/checkbox"
47
+ require_relative "thaum/sigils/status_bar"
48
+ require_relative "thaum/sigils/tabs"
4
49
 
5
50
  module Thaum
6
51
  class Error < StandardError; end
7
- # Your code goes here...
52
+ class LayoutError < Error; end
53
+ class EmitFromUpdateError < Error; end
54
+ class FocusOrderError < Error; end
55
+
56
+ # Invoke a handler, rescuing any exception so a bug in user code can't
57
+ # leave the terminal in raw mode + alt screen. Logs class, message, and
58
+ # backtrace to stderr.
59
+ def self.safe_invoke(label)
60
+ yield
61
+ rescue StandardError => e
62
+ warn "[Thaum] unhandled exception in #{label}: #{e.class}: #{e.message}"
63
+ warn e.backtrace.join("\n") if e.backtrace
64
+ nil
65
+ end
66
+
67
+ # Startup entry point. Blocks until app.quit is called. Returns nil.
68
+ def self.run(app, tick: 0.1, threads: 4)
69
+ RunLoop.run(app:, tick:, threads:)
70
+ end
8
71
  end
metadata CHANGED
@@ -1,15 +1,62 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thaum
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tad Thorley
8
8
  bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies: []
12
- description: coming soon
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: concurrent-ruby
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.3'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.3'
26
+ - !ruby/object:Gem::Dependency
27
+ name: unicode-display_width
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rubocop
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.0'
54
+ description: |
55
+ Build full-screen terminal apps in Ruby. Thaum provides a declarative
56
+ layout DSL, composable sigils (widgets) and octagrams (composite
57
+ components), a diff-based renderer with truecolor support, event
58
+ dispatch with ticks and background actions, focus management, themes,
59
+ and snapshot testing.
13
60
  executables: []
14
61
  extensions: []
15
62
  extra_rdoc_files: []
@@ -17,7 +64,69 @@ files:
17
64
  - LICENSE.txt
18
65
  - README.md
19
66
  - Rakefile
67
+ - examples/checkbox.rb
68
+ - examples/counter.rb
69
+ - examples/hello_world.rb
70
+ - examples/layout_demo.rb
71
+ - examples/modal.rb
72
+ - examples/mouse.rb
73
+ - examples/octagram_picker.rb
74
+ - examples/picker.rb
75
+ - examples/progress_bar.rb
76
+ - examples/scroll_view.rb
77
+ - examples/select.rb
78
+ - examples/spinner.rb
79
+ - examples/status_bar.rb
80
+ - examples/stopwatch.rb
81
+ - examples/table.rb
82
+ - examples/tabs.rb
83
+ - examples/text.rb
84
+ - examples/theme_picker.rb
85
+ - examples/todo.rb
20
86
  - lib/thaum.rb
87
+ - lib/thaum/action.rb
88
+ - lib/thaum/app.rb
89
+ - lib/thaum/color.rb
90
+ - lib/thaum/concerns/context_update.rb
91
+ - lib/thaum/concerns/focus.rb
92
+ - lib/thaum/concerns/layout.rb
93
+ - lib/thaum/concerns/modal.rb
94
+ - lib/thaum/concerns/tab_navigation.rb
95
+ - lib/thaum/dispatch.rb
96
+ - lib/thaum/escape_parser.rb
97
+ - lib/thaum/event.rb
98
+ - lib/thaum/events.rb
99
+ - lib/thaum/hit_test.rb
100
+ - lib/thaum/input_reader.rb
101
+ - lib/thaum/key_event.rb
102
+ - lib/thaum/keys.rb
103
+ - lib/thaum/minitest.rb
104
+ - lib/thaum/octagram.rb
105
+ - lib/thaum/painter.rb
106
+ - lib/thaum/rect.rb
107
+ - lib/thaum/rendering/box_drawing.rb
108
+ - lib/thaum/rendering/buffer.rb
109
+ - lib/thaum/rendering/canvas.rb
110
+ - lib/thaum/rendering/cell.rb
111
+ - lib/thaum/rendering/renderer.rb
112
+ - lib/thaum/rendering/style.rb
113
+ - lib/thaum/run_loop.rb
114
+ - lib/thaum/seq.rb
115
+ - lib/thaum/sigil.rb
116
+ - lib/thaum/sigils/button.rb
117
+ - lib/thaum/sigils/checkbox.rb
118
+ - lib/thaum/sigils/progress_bar.rb
119
+ - lib/thaum/sigils/scroll_view.rb
120
+ - lib/thaum/sigils/select.rb
121
+ - lib/thaum/sigils/spinner.rb
122
+ - lib/thaum/sigils/status_bar.rb
123
+ - lib/thaum/sigils/table.rb
124
+ - lib/thaum/sigils/tabs.rb
125
+ - lib/thaum/sigils/text.rb
126
+ - lib/thaum/sigils/text_input.rb
127
+ - lib/thaum/terminal.rb
128
+ - lib/thaum/themes.rb
129
+ - lib/thaum/tree.rb
21
130
  - lib/thaum/version.rb
22
131
  - sig/thaum.rbs
23
132
  homepage: https://github.com/urug/thaum
@@ -26,6 +135,7 @@ licenses:
26
135
  metadata:
27
136
  homepage_uri: https://github.com/urug/thaum
28
137
  source_code_uri: https://github.com/urug/thaum
138
+ rubygems_mfa_required: 'true'
29
139
  rdoc_options: []
30
140
  require_paths:
31
141
  - lib
@@ -42,5 +152,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
42
152
  requirements: []
43
153
  rubygems_version: 4.0.3
44
154
  specification_version: 4
45
- summary: A Ruby TUI framework
155
+ summary: A declarative TUI framework for Ruby
46
156
  test_files: []