ratatui_ruby 0.9.1 → 0.10.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 (267) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +1 -1
  3. data/.builds/ruby-3.3.yml +1 -1
  4. data/.builds/ruby-3.4.yml +1 -1
  5. data/.builds/ruby-4.0.0.yml +1 -1
  6. data/AGENTS.md +2 -1
  7. data/CHANGELOG.md +98 -0
  8. data/REUSE.toml +5 -0
  9. data/Rakefile +1 -1
  10. data/Steepfile +49 -0
  11. data/doc/concepts/debugging.md +401 -0
  12. data/doc/getting_started/quickstart.md +8 -3
  13. data/doc/images/app_all_events.png +0 -0
  14. data/doc/images/app_color_picker.png +0 -0
  15. data/doc/images/app_debugging_showcase.gif +0 -0
  16. data/doc/images/app_debugging_showcase.png +0 -0
  17. data/doc/images/app_login_form.png +0 -0
  18. data/doc/images/app_stateful_interaction.png +0 -0
  19. data/doc/images/verify_quickstart_dsl.png +0 -0
  20. data/doc/images/verify_quickstart_layout.png +0 -0
  21. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  22. data/doc/images/verify_readme_usage.png +0 -0
  23. data/doc/images/widget_barchart.png +0 -0
  24. data/doc/images/widget_block.png +0 -0
  25. data/doc/images/widget_box.png +0 -0
  26. data/doc/images/widget_calendar.png +0 -0
  27. data/doc/images/widget_canvas.png +0 -0
  28. data/doc/images/widget_cell.png +0 -0
  29. data/doc/images/widget_center.png +0 -0
  30. data/doc/images/widget_chart.png +0 -0
  31. data/doc/images/widget_gauge.png +0 -0
  32. data/doc/images/widget_layout_split.png +0 -0
  33. data/doc/images/widget_line_gauge.png +0 -0
  34. data/doc/images/widget_list.png +0 -0
  35. data/doc/images/widget_map.png +0 -0
  36. data/doc/images/widget_overlay.png +0 -0
  37. data/doc/images/widget_popup.png +0 -0
  38. data/doc/images/widget_ratatui_logo.png +0 -0
  39. data/doc/images/widget_ratatui_mascot.png +0 -0
  40. data/doc/images/widget_rect.png +0 -0
  41. data/doc/images/widget_render.png +0 -0
  42. data/doc/images/widget_rich_text.png +0 -0
  43. data/doc/images/widget_scroll_text.png +0 -0
  44. data/doc/images/widget_scrollbar.png +0 -0
  45. data/doc/images/widget_sparkline.png +0 -0
  46. data/doc/images/widget_style_colors.png +0 -0
  47. data/doc/images/widget_table.png +0 -0
  48. data/doc/images/widget_tabs.png +0 -0
  49. data/doc/images/widget_text_width.png +0 -0
  50. data/doc/troubleshooting/async.md +4 -0
  51. data/examples/app_debugging_showcase/README.md +119 -0
  52. data/examples/app_debugging_showcase/app.rb +318 -0
  53. data/examples/widget_canvas/app.rb +19 -14
  54. data/examples/widget_gauge/app.rb +18 -3
  55. data/examples/widget_layout_split/app.rb +10 -4
  56. data/examples/widget_list/app.rb +22 -6
  57. data/examples/widget_rect/app.rb +7 -6
  58. data/examples/widget_rich_text/app.rb +62 -37
  59. data/examples/widget_style_colors/app.rb +26 -47
  60. data/examples/widget_table/app.rb +28 -5
  61. data/examples/widget_text_width/app.rb +6 -4
  62. data/ext/ratatui_ruby/Cargo.lock +48 -1
  63. data/ext/ratatui_ruby/Cargo.toml +6 -2
  64. data/ext/ratatui_ruby/src/color.rs +82 -0
  65. data/ext/ratatui_ruby/src/errors.rs +28 -0
  66. data/ext/ratatui_ruby/src/events.rs +15 -14
  67. data/ext/ratatui_ruby/src/lib.rs +56 -0
  68. data/ext/ratatui_ruby/src/rendering.rs +3 -1
  69. data/ext/ratatui_ruby/src/style.rs +48 -21
  70. data/ext/ratatui_ruby/src/terminal.rs +40 -9
  71. data/ext/ratatui_ruby/src/text.rs +21 -9
  72. data/ext/ratatui_ruby/src/widgets/chart.rs +2 -1
  73. data/ext/ratatui_ruby/src/widgets/layout.rs +90 -2
  74. data/ext/ratatui_ruby/src/widgets/list.rs +6 -5
  75. data/ext/ratatui_ruby/src/widgets/overlay.rs +2 -1
  76. data/ext/ratatui_ruby/src/widgets/table.rs +7 -6
  77. data/ext/ratatui_ruby/src/widgets/table_state.rs +55 -0
  78. data/ext/ratatui_ruby/src/widgets/tabs.rs +3 -2
  79. data/lib/ratatui_ruby/buffer/cell.rb +25 -15
  80. data/lib/ratatui_ruby/buffer.rb +134 -2
  81. data/lib/ratatui_ruby/cell.rb +13 -5
  82. data/lib/ratatui_ruby/debug.rb +215 -0
  83. data/lib/ratatui_ruby/event/key.rb +3 -2
  84. data/lib/ratatui_ruby/event.rb +1 -1
  85. data/lib/ratatui_ruby/layout/constraint.rb +49 -0
  86. data/lib/ratatui_ruby/layout/layout.rb +119 -13
  87. data/lib/ratatui_ruby/layout/position.rb +55 -0
  88. data/lib/ratatui_ruby/layout/rect.rb +188 -0
  89. data/lib/ratatui_ruby/layout/size.rb +55 -0
  90. data/lib/ratatui_ruby/layout.rb +4 -0
  91. data/lib/ratatui_ruby/style/color.rb +149 -0
  92. data/lib/ratatui_ruby/style/style.rb +51 -4
  93. data/lib/ratatui_ruby/style.rb +2 -0
  94. data/lib/ratatui_ruby/symbols.rb +435 -0
  95. data/lib/ratatui_ruby/synthetic_events.rb +1 -1
  96. data/lib/ratatui_ruby/table_state.rb +51 -0
  97. data/lib/ratatui_ruby/terminal_lifecycle.rb +2 -1
  98. data/lib/ratatui_ruby/test_helper/event_injection.rb +6 -1
  99. data/lib/ratatui_ruby/test_helper.rb +9 -0
  100. data/lib/ratatui_ruby/text/line.rb +245 -0
  101. data/lib/ratatui_ruby/text/span.rb +158 -0
  102. data/lib/ratatui_ruby/text.rb +99 -0
  103. data/lib/ratatui_ruby/tui/canvas_factories.rb +103 -0
  104. data/lib/ratatui_ruby/tui/core.rb +13 -2
  105. data/lib/ratatui_ruby/tui/layout_factories.rb +50 -3
  106. data/lib/ratatui_ruby/tui/state_factories.rb +42 -0
  107. data/lib/ratatui_ruby/tui/text_factories.rb +40 -0
  108. data/lib/ratatui_ruby/tui/widget_factories.rb +135 -60
  109. data/lib/ratatui_ruby/tui.rb +22 -1
  110. data/lib/ratatui_ruby/version.rb +1 -1
  111. data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +2 -0
  112. data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +2 -0
  113. data/lib/ratatui_ruby/widgets/bar_chart.rb +30 -20
  114. data/lib/ratatui_ruby/widgets/block.rb +14 -6
  115. data/lib/ratatui_ruby/widgets/calendar.rb +2 -0
  116. data/lib/ratatui_ruby/widgets/canvas.rb +56 -0
  117. data/lib/ratatui_ruby/widgets/cell.rb +2 -0
  118. data/lib/ratatui_ruby/widgets/center.rb +2 -0
  119. data/lib/ratatui_ruby/widgets/chart.rb +6 -0
  120. data/lib/ratatui_ruby/widgets/clear.rb +2 -0
  121. data/lib/ratatui_ruby/widgets/coerceable_widget.rb +77 -0
  122. data/lib/ratatui_ruby/widgets/cursor.rb +2 -0
  123. data/lib/ratatui_ruby/widgets/gauge.rb +61 -3
  124. data/lib/ratatui_ruby/widgets/line_gauge.rb +66 -4
  125. data/lib/ratatui_ruby/widgets/list.rb +87 -3
  126. data/lib/ratatui_ruby/widgets/list_item.rb +2 -0
  127. data/lib/ratatui_ruby/widgets/overlay.rb +2 -0
  128. data/lib/ratatui_ruby/widgets/paragraph.rb +4 -0
  129. data/lib/ratatui_ruby/widgets/ratatui_logo.rb +2 -0
  130. data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +2 -0
  131. data/lib/ratatui_ruby/widgets/row.rb +45 -0
  132. data/lib/ratatui_ruby/widgets/scrollbar.rb +2 -0
  133. data/lib/ratatui_ruby/widgets/shape/label.rb +2 -0
  134. data/lib/ratatui_ruby/widgets/sparkline.rb +21 -13
  135. data/lib/ratatui_ruby/widgets/table.rb +13 -3
  136. data/lib/ratatui_ruby/widgets/tabs.rb +6 -4
  137. data/lib/ratatui_ruby/widgets.rb +1 -0
  138. data/lib/ratatui_ruby.rb +40 -9
  139. data/sig/examples/app_all_events/model/app_model.rbs +23 -0
  140. data/sig/examples/app_all_events/model/event_entry.rbs +15 -8
  141. data/sig/examples/app_all_events/model/timestamp.rbs +1 -1
  142. data/sig/examples/app_all_events/view.rbs +1 -1
  143. data/sig/examples/app_stateful_interaction/app.rbs +5 -5
  144. data/sig/examples/widget_block_demo/app.rbs +6 -6
  145. data/sig/manifest.yaml +5 -0
  146. data/sig/patches/data.rbs +26 -0
  147. data/sig/patches/debugger__.rbs +8 -0
  148. data/sig/ratatui_ruby/buffer/cell.rbs +46 -0
  149. data/sig/ratatui_ruby/buffer.rbs +18 -0
  150. data/sig/ratatui_ruby/cell.rbs +44 -0
  151. data/sig/ratatui_ruby/clear.rbs +18 -0
  152. data/sig/ratatui_ruby/constraint.rbs +26 -0
  153. data/sig/ratatui_ruby/debug.rbs +45 -0
  154. data/sig/ratatui_ruby/draw.rbs +30 -0
  155. data/sig/ratatui_ruby/event.rbs +68 -8
  156. data/sig/ratatui_ruby/frame.rbs +4 -4
  157. data/sig/ratatui_ruby/interfaces.rbs +25 -0
  158. data/sig/ratatui_ruby/layout/constraint.rbs +39 -0
  159. data/sig/ratatui_ruby/layout/layout.rbs +45 -0
  160. data/sig/ratatui_ruby/layout/position.rbs +18 -0
  161. data/sig/ratatui_ruby/layout/rect.rbs +64 -0
  162. data/sig/ratatui_ruby/layout/size.rbs +18 -0
  163. data/sig/ratatui_ruby/output_guard.rbs +23 -0
  164. data/sig/ratatui_ruby/ratatui_ruby.rbs +83 -4
  165. data/sig/ratatui_ruby/rect.rbs +17 -0
  166. data/sig/ratatui_ruby/style/color.rbs +22 -0
  167. data/sig/ratatui_ruby/style/style.rbs +29 -0
  168. data/sig/ratatui_ruby/symbols.rbs +141 -0
  169. data/sig/ratatui_ruby/synthetic_events.rbs +21 -0
  170. data/sig/ratatui_ruby/table_state.rbs +6 -0
  171. data/sig/ratatui_ruby/terminal_lifecycle.rbs +31 -0
  172. data/sig/ratatui_ruby/test_helper/event_injection.rbs +2 -2
  173. data/sig/ratatui_ruby/test_helper/snapshot.rbs +22 -3
  174. data/sig/ratatui_ruby/test_helper/style_assertions.rbs +8 -1
  175. data/sig/ratatui_ruby/test_helper/test_doubles.rbs +7 -3
  176. data/sig/ratatui_ruby/text/line.rbs +27 -0
  177. data/sig/ratatui_ruby/text/span.rbs +23 -0
  178. data/sig/ratatui_ruby/text.rbs +12 -0
  179. data/sig/ratatui_ruby/tui/buffer_factories.rbs +1 -1
  180. data/sig/ratatui_ruby/tui/canvas_factories.rbs +23 -5
  181. data/sig/ratatui_ruby/tui/core.rbs +2 -2
  182. data/sig/ratatui_ruby/tui/layout_factories.rbs +16 -2
  183. data/sig/ratatui_ruby/tui/state_factories.rbs +8 -3
  184. data/sig/ratatui_ruby/tui/style_factories.rbs +3 -1
  185. data/sig/ratatui_ruby/tui/text_factories.rbs +7 -4
  186. data/sig/ratatui_ruby/tui/widget_factories.rbs +123 -30
  187. data/sig/ratatui_ruby/widgets/bar_chart.rbs +95 -0
  188. data/sig/ratatui_ruby/widgets/block.rbs +51 -0
  189. data/sig/ratatui_ruby/widgets/calendar.rbs +45 -0
  190. data/sig/ratatui_ruby/widgets/canvas.rbs +95 -0
  191. data/sig/ratatui_ruby/widgets/chart.rbs +91 -0
  192. data/sig/ratatui_ruby/widgets/coerceable_widget.rbs +26 -0
  193. data/sig/ratatui_ruby/widgets/gauge.rbs +44 -0
  194. data/sig/ratatui_ruby/widgets/line_gauge.rbs +48 -0
  195. data/sig/ratatui_ruby/widgets/list.rbs +63 -0
  196. data/sig/ratatui_ruby/widgets/misc.rbs +158 -0
  197. data/sig/ratatui_ruby/widgets/paragraph.rbs +45 -0
  198. data/sig/ratatui_ruby/widgets/row.rbs +43 -0
  199. data/sig/ratatui_ruby/widgets/scrollbar.rbs +53 -0
  200. data/sig/ratatui_ruby/widgets/shape/label.rbs +37 -0
  201. data/sig/ratatui_ruby/widgets/sparkline.rbs +45 -0
  202. data/sig/ratatui_ruby/widgets/table.rbs +78 -0
  203. data/sig/ratatui_ruby/widgets/tabs.rbs +44 -0
  204. data/sig/ratatui_ruby/{schema/list_item.rbs → widgets.rbs} +4 -4
  205. data/tasks/steep.rake +11 -0
  206. metadata +80 -63
  207. data/doc/contributors/v1.0.0_blockers.md +0 -870
  208. data/doc/troubleshooting/debugging.md +0 -101
  209. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +0 -47
  210. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +0 -25
  211. data/lib/ratatui_ruby/schema/bar_chart.rb +0 -287
  212. data/lib/ratatui_ruby/schema/block.rb +0 -198
  213. data/lib/ratatui_ruby/schema/calendar.rb +0 -84
  214. data/lib/ratatui_ruby/schema/canvas.rb +0 -239
  215. data/lib/ratatui_ruby/schema/center.rb +0 -67
  216. data/lib/ratatui_ruby/schema/chart.rb +0 -159
  217. data/lib/ratatui_ruby/schema/clear.rb +0 -62
  218. data/lib/ratatui_ruby/schema/constraint.rb +0 -151
  219. data/lib/ratatui_ruby/schema/cursor.rb +0 -50
  220. data/lib/ratatui_ruby/schema/gauge.rb +0 -72
  221. data/lib/ratatui_ruby/schema/layout.rb +0 -122
  222. data/lib/ratatui_ruby/schema/line_gauge.rb +0 -80
  223. data/lib/ratatui_ruby/schema/list.rb +0 -135
  224. data/lib/ratatui_ruby/schema/list_item.rb +0 -51
  225. data/lib/ratatui_ruby/schema/overlay.rb +0 -51
  226. data/lib/ratatui_ruby/schema/paragraph.rb +0 -107
  227. data/lib/ratatui_ruby/schema/ratatui_logo.rb +0 -31
  228. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +0 -36
  229. data/lib/ratatui_ruby/schema/rect.rb +0 -174
  230. data/lib/ratatui_ruby/schema/row.rb +0 -76
  231. data/lib/ratatui_ruby/schema/scrollbar.rb +0 -143
  232. data/lib/ratatui_ruby/schema/shape/label.rb +0 -76
  233. data/lib/ratatui_ruby/schema/sparkline.rb +0 -142
  234. data/lib/ratatui_ruby/schema/style.rb +0 -97
  235. data/lib/ratatui_ruby/schema/table.rb +0 -141
  236. data/lib/ratatui_ruby/schema/tabs.rb +0 -85
  237. data/lib/ratatui_ruby/schema/text.rb +0 -217
  238. data/sig/examples/app_all_events/model/events.rbs +0 -15
  239. data/sig/examples/app_all_events/view_state.rbs +0 -21
  240. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +0 -22
  241. data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +0 -19
  242. data/sig/ratatui_ruby/schema/bar_chart.rbs +0 -38
  243. data/sig/ratatui_ruby/schema/block.rbs +0 -18
  244. data/sig/ratatui_ruby/schema/calendar.rbs +0 -23
  245. data/sig/ratatui_ruby/schema/canvas.rbs +0 -81
  246. data/sig/ratatui_ruby/schema/center.rbs +0 -17
  247. data/sig/ratatui_ruby/schema/chart.rbs +0 -39
  248. data/sig/ratatui_ruby/schema/constraint.rbs +0 -30
  249. data/sig/ratatui_ruby/schema/cursor.rbs +0 -16
  250. data/sig/ratatui_ruby/schema/draw.rbs +0 -33
  251. data/sig/ratatui_ruby/schema/gauge.rbs +0 -23
  252. data/sig/ratatui_ruby/schema/layout.rbs +0 -27
  253. data/sig/ratatui_ruby/schema/line_gauge.rbs +0 -24
  254. data/sig/ratatui_ruby/schema/list.rbs +0 -28
  255. data/sig/ratatui_ruby/schema/overlay.rbs +0 -15
  256. data/sig/ratatui_ruby/schema/paragraph.rbs +0 -20
  257. data/sig/ratatui_ruby/schema/ratatui_logo.rbs +0 -14
  258. data/sig/ratatui_ruby/schema/ratatui_mascot.rbs +0 -17
  259. data/sig/ratatui_ruby/schema/rect.rbs +0 -48
  260. data/sig/ratatui_ruby/schema/row.rbs +0 -28
  261. data/sig/ratatui_ruby/schema/scrollbar.rbs +0 -42
  262. data/sig/ratatui_ruby/schema/sparkline.rbs +0 -22
  263. data/sig/ratatui_ruby/schema/style.rbs +0 -19
  264. data/sig/ratatui_ruby/schema/table.rbs +0 -32
  265. data/sig/ratatui_ruby/schema/tabs.rbs +0 -21
  266. data/sig/ratatui_ruby/schema/text.rbs +0 -31
  267. /data/lib/ratatui_ruby/{schema/draw.rb → draw.rb} +0 -0
@@ -29,6 +29,10 @@ class WidgetGauge
29
29
  @ratios = [0.0, 0.25, 0.5, 0.65, 0.8, 0.95, 1.0]
30
30
  @ratio_index = 3
31
31
 
32
+ # Demonstrates both ratio (0.0-1.0) and percent (0-100) input modes
33
+ @input_modes = [:ratio, :percent]
34
+ @input_mode_index = 0
35
+
32
36
  @gauge_colors = [
33
37
  { name: "Green", color: :green },
34
38
  { name: "Yellow", color: :yellow },
@@ -116,13 +120,20 @@ class WidgetGauge
116
120
  frame.render_widget(title, title_area)
117
121
 
118
122
  # Gauge 1: Main interactive gauge
123
+ # Demonstrates both ratio (0.0-1.0) and percent (0-100) input modes
124
+ input_mode = @input_modes[@input_mode_index]
125
+ gauge_opts = if input_mode == :percent
126
+ { percent: (@ratio * 100).to_i } # percent: accepts 0-100
127
+ else
128
+ { ratio: @ratio } # ratio: accepts 0.0-1.0
129
+ end
119
130
  gauge1 = @tui.gauge(
120
- ratio: @ratio,
131
+ **gauge_opts,
121
132
  label:,
122
133
  style: bg_style,
123
134
  gauge_style:,
124
135
  use_unicode:,
125
- block: @tui.block(title: "Interactive Gauge")
136
+ block: @tui.block(title: "Interactive Gauge (#{input_mode}:)")
126
137
  )
127
138
  frame.render_widget(gauge1, gauge1_area)
128
139
 
@@ -178,7 +189,9 @@ class WidgetGauge
178
189
  @tui.text_span(content: "u", style: @hotkey_style),
179
190
  @tui.text_span(content: ": Unicode (#{use_unicode ? 'On' : 'Off'}) "),
180
191
  @tui.text_span(content: "l", style: @hotkey_style),
181
- @tui.text_span(content: ": Label (#{@label_modes[@label_mode_index][:name]})"),
192
+ @tui.text_span(content: ": Label (#{@label_modes[@label_mode_index][:name]}) "),
193
+ @tui.text_span(content: "i", style: @hotkey_style),
194
+ @tui.text_span(content: ": Input (#{@input_modes[@input_mode_index]}:)"),
182
195
  ]),
183
196
  ]
184
197
  ),
@@ -204,6 +217,8 @@ class WidgetGauge
204
217
  @use_unicode_index = (@use_unicode_index + 1) % @use_unicode_options.length
205
218
  in type: :key, code: "l"
206
219
  @label_mode_index = (@label_mode_index + 1) % @label_modes.length
220
+ in type: :key, code: "i"
221
+ @input_mode_index = (@input_mode_index + 1) % @input_modes.length
207
222
  else
208
223
  # Ignore other events
209
224
  nil
@@ -206,6 +206,11 @@ class WidgetLayoutSplit
206
206
  end
207
207
 
208
208
  private def render_controls(frame, area)
209
+ # Demonstrate Constraint#call - show computed sizes for 100 units
210
+ constraints = current_constraints
211
+ applied = constraints.map { |c| c.(100) } # proc-like invocation
212
+ apply_str = "constraint.(100): #{applied.join(', ')}"
213
+
209
214
  controls = @tui.block(
210
215
  title: "Controls",
211
216
  borders: [:all],
@@ -221,13 +226,14 @@ class WidgetLayoutSplit
221
226
  ]),
222
227
  @tui.text_line(spans: [
223
228
  @tui.text_span(content: "c", style: @hotkey_style),
224
- @tui.text_span(content: ": Constraints (#{current_constraint_name})"),
225
- ]),
226
- # Row 3: Quit
227
- @tui.text_line(spans: [
229
+ @tui.text_span(content: ": Constraints (#{current_constraint_name}) "),
228
230
  @tui.text_span(content: "q", style: @hotkey_style),
229
231
  @tui.text_span(content: ": Quit"),
230
232
  ]),
233
+ # Row 3: Apply demonstration
234
+ @tui.text_line(spans: [
235
+ @tui.text_span(content: apply_str, style: @tui.style(fg: :dark_gray)),
236
+ ]),
231
237
  ]
232
238
  ),
233
239
  ]
@@ -230,8 +230,6 @@ class WidgetList
230
230
  # Determine selection/offset based on mode
231
231
  effective_selection = offset_mode_config[:allow_selection] ? @selected_index : nil
232
232
  effective_offset = offset_mode_config[:offset]
233
- selection_label = effective_selection.nil? ? "none" : effective_selection.to_s
234
- offset_label = effective_offset.nil? ? "auto" : effective_offset.to_s
235
233
 
236
234
  @tui.draw do |frame|
237
235
  # Split into main content and control panel
@@ -258,8 +256,11 @@ class WidgetList
258
256
  title = @tui.paragraph(text: "List Widget - Interactive Attribute Cycling")
259
257
  frame.render_widget(title, title_area)
260
258
 
261
- # Render list
262
- list = @tui.list(
259
+ # Build list first to demonstrate query methods:
260
+ # - List#len (with #length, #size aliases)
261
+ # - List#selection (alias for #selected_index)
262
+ # - List#selected_item (returns item at selection, or nil)
263
+ base_list = @tui.list(
263
264
  items:,
264
265
  selected_index: effective_selection,
265
266
  offset: effective_offset,
@@ -269,9 +270,24 @@ class WidgetList
269
270
  repeat_highlight_symbol: repeat_config[:repeat],
270
271
  highlight_spacing: spacing_config[:spacing],
271
272
  direction: direction_config[:direction],
272
- scroll_padding: scroll_padding_config[:padding],
273
+ scroll_padding: scroll_padding_config[:padding]
274
+ )
275
+
276
+ # Demonstrate query methods: len, selected_index, selected_item
277
+ item_count = base_list.len
278
+ current_index = base_list.selected_index # Explicit name - returns index
279
+ current_item = base_list.selected_item # Explicit name - returns item
280
+
281
+ # Format the selected item for display (handle rich text objects)
282
+ item_preview = case current_item
283
+ when nil then "none"
284
+ when String then (current_item.length > 12) ? "#{current_item[0..11]}…" : current_item
285
+ else current_item.class.name.split("::").last # Show type for rich text
286
+ end
287
+
288
+ list = base_list.with(
273
289
  block: @tui.block(
274
- title: "#{@item_sets[@item_set_index][:name]} | Sel: #{selection_label} | Offset: #{offset_label}",
290
+ title: "#{@item_sets[@item_set_index][:name]} (len: #{item_count}) | index: #{current_index.inspect} → #{item_preview}",
275
291
  borders: [:all]
276
292
  )
277
293
  )
@@ -97,6 +97,10 @@ class WidgetRect
97
97
  clamped = r.clamp(bounds)
98
98
  union_r = r.union(@sidebar_rect)
99
99
 
100
+ # Extract position and size from rect
101
+ pos = r.position # => Position(x:, y:)
102
+ size = r.size # => Size(width:, height:)
103
+
100
104
  text_content = [
101
105
  @tui.text_line(spans: [
102
106
  @tui.text_span(content: "Active View: ", style: @label_style),
@@ -112,19 +116,16 @@ class WidgetRect
112
116
  ]),
113
117
  " left:#{r.left} right:#{r.right} top:#{r.top} bottom:#{r.bottom}",
114
118
  @tui.text_line(spans: [
115
- @tui.text_span(content: "Size Methods:", style: @label_style),
119
+ @tui.text_span(content: "Conversion Methods ", style: @label_style),
120
+ @tui.text_span(content: "(as_position/as_size):", style: @dim_style),
116
121
  ]),
117
- " area:#{r.area} empty?:#{r.empty?}",
122
+ " Position: x=#{pos.x} y=#{pos.y} Size: #{size.width}x#{size.height}",
118
123
  @tui.text_line(spans: [
119
124
  @tui.text_span(content: "Geometry Transformations:", style: @label_style),
120
125
  ]),
121
126
  " inner(2): x:#{inner_r.x} y:#{inner_r.y} w:#{inner_r.width} h:#{inner_r.height}",
122
127
  " offset(3,2): x:#{offset_r.x} y:#{offset_r.y} clamp: x:#{clamped.x} y:#{clamped.y}",
123
128
  " union(sidebar): w:#{union_r.width} h:#{union_r.height}",
124
- @tui.text_line(spans: [
125
- @tui.text_span(content: "Iterators:", style: @label_style),
126
- ]),
127
- " rows:#{r.height} columns:#{r.width} positions:#{r.area}",
128
129
  @tui.text_line(spans: [
129
130
  @tui.text_span(content: "Hit Testing ", style: @label_style),
130
131
  @tui.text_span(content: "(Rect#contains?):", style: @dim_style),
@@ -10,11 +10,13 @@ require "ratatui_ruby"
10
10
 
11
11
  # Rich Text Example
12
12
  # Demonstrates the Span and Line objects for styling individual words
13
- # within a block of text.
13
+ # within a block of text. Also demonstrates Line alignment methods.
14
14
  class WidgetRichText
15
15
  def initialize
16
16
  @scroll_pos = 0
17
17
  @color_index = 0
18
+ @alignment_index = 0
19
+ @alignments = [:left, :center, :right]
18
20
  end
19
21
 
20
22
  def run
@@ -46,40 +48,17 @@ class WidgetRichText
46
48
 
47
49
  private def simple_text_line_example
48
50
  # Example 1: A line with mixed styles
51
+ # Create a base line with spans, then apply alignment using the fluent methods
52
+ alignment = @alignments[@alignment_index]
53
+ aligned_line = case alignment
54
+ when :left then base_line.left_aligned
55
+ when :center then base_line.centered
56
+ when :right then base_line.right_aligned
57
+ end
58
+
49
59
  @tui.paragraph(
50
60
  text: [
51
- @tui.text_line(
52
- spans: [
53
- @tui.text_span(
54
- content: "Normal text, ",
55
- style: nil
56
- ),
57
- @tui.text_span(
58
- content: "Bold Text",
59
- style: @tui.style(modifiers: [:bold])
60
- ),
61
- @tui.text_span(
62
- content: ", ",
63
- style: nil
64
- ),
65
- @tui.text_span(
66
- content: "Italic Text",
67
- style: @tui.style(modifiers: [:italic])
68
- ),
69
- @tui.text_span(
70
- content: ", ",
71
- style: nil
72
- ),
73
- @tui.text_span(
74
- content: "Red Text",
75
- style: @tui.style(fg: :red)
76
- ),
77
- @tui.text_span(
78
- content: ".",
79
- style: nil
80
- ),
81
- ]
82
- ),
61
+ aligned_line,
83
62
  @tui.text_line(spans: []),
84
63
  @tui.text_line(
85
64
  spans: [
@@ -87,11 +66,18 @@ class WidgetRichText
87
66
  @tui.text_span(content: "Color #{@color_index}", style: @tui.style(fg: @color_index)),
88
67
  @tui.text_span(content: " (Use "),
89
68
  @tui.text_span(content: "↑ ↓", style: @tui.style(modifiers: [:bold])),
90
- @tui.text_span(content: " for +/- 1,", style: nil),
91
- @tui.text_span(content: "→", style: @tui.style(modifiers: [:bold])),
69
+ @tui.text_span(content: " for +/- 1, ", style: nil),
70
+ @tui.text_span(content: "→", style: @tui.style(modifiers: [:bold])),
92
71
  @tui.text_span(content: " for +/- 10)", style: nil),
93
72
  ]
94
73
  ),
74
+ @tui.text_line(spans: []),
75
+ @tui.text_line(
76
+ spans: [
77
+ @tui.text_span(content: "A", style: @tui.style(modifiers: [:bold, :underlined])),
78
+ @tui.text_span(content: ": Alignment (#{alignment})", style: nil),
79
+ ]
80
+ ),
95
81
  ],
96
82
  block: @tui.block(
97
83
  title: "Simple Rich Text",
@@ -100,8 +86,33 @@ class WidgetRichText
100
86
  )
101
87
  end
102
88
 
89
+ private def base_line
90
+ # Demonstrates creating a styled line with various modifiers and colors
91
+ # Including: underline_color (distinct from fg) and remove_modifiers (to override inherited styles)
92
+ @tui.text_line(
93
+ spans: [
94
+ @tui.text_span(content: "Normal, ", style: nil),
95
+ @tui.text_span(content: "Bold", style: @tui.style(modifiers: [:bold])),
96
+ @tui.text_span(content: ", ", style: nil),
97
+ @tui.text_span(content: "Italic", style: @tui.style(modifiers: [:italic])),
98
+ @tui.text_span(content: ", ", style: nil),
99
+ @tui.text_span(content: "Red", style: @tui.style(fg: :red)),
100
+ @tui.text_span(content: ", ", style: nil),
101
+ # New: underline_color - underline in a different color than text
102
+ @tui.text_span(
103
+ content: "Red Underline",
104
+ style: @tui.style(fg: :white, modifiers: [:underlined], underline_color: :red)
105
+ ),
106
+ @tui.text_span(content: ".", style: nil),
107
+ ]
108
+ )
109
+ end
110
+
103
111
  private def complex_example
104
112
  # Example 2: Multiple lines with different styles
113
+ # Includes Symbols::Shade constants for density gradients
114
+ shade = RatatuiRuby::Symbols::Shade
115
+
105
116
  @tui.paragraph(
106
117
  text: [
107
118
  @tui.text_line(
@@ -126,15 +137,27 @@ class WidgetRichText
126
137
  ]
127
138
  ),
128
139
  @tui.text_line(spans: []),
140
+ # Demonstrate Symbols::Shade constants for density gradients
141
+ @tui.text_line(
142
+ spans: [
143
+ @tui.text_span(content: "Shade: ", style: @tui.style(fg: :cyan)),
144
+ @tui.text_span(content: shade::EMPTY * 4, style: nil),
145
+ @tui.text_span(content: shade::LIGHT * 4, style: @tui.style(fg: :dark_gray)),
146
+ @tui.text_span(content: shade::MEDIUM * 4, style: @tui.style(fg: :gray)),
147
+ @tui.text_span(content: shade::DARK * 4, style: @tui.style(fg: :white)),
148
+ @tui.text_span(content: shade::FULL * 4, style: @tui.style(fg: :white)),
149
+ ]
150
+ ),
151
+ @tui.text_line(spans: []),
129
152
  @tui.text_line(
130
153
  spans: [
131
154
  @tui.text_span(content: "Press ", style: nil),
132
155
  @tui.text_span(content: "Q", style: @tui.style(modifiers: [:bold])),
133
156
  @tui.text_span(content: " to quit, ", style: nil),
134
157
  @tui.text_span(content: "↑ ↓", style: @tui.style(modifiers: [:bold])),
135
- @tui.text_span(content: " to adjust color by 1, ", style: nil),
158
+ @tui.text_span(content: ": color by 1, ", style: nil),
136
159
  @tui.text_span(content: "← →", style: @tui.style(modifiers: [:bold])),
137
- @tui.text_span(content: " to adjust color by 10.", style: nil),
160
+ @tui.text_span(content: ": color by 10.", style: nil),
138
161
  ]
139
162
  ),
140
163
  ],
@@ -157,6 +180,8 @@ class WidgetRichText
157
180
  @color_index = (@color_index + 1) % 256
158
181
  elsif event.down?
159
182
  @color_index = (@color_index - 1) % 256
183
+ elsif event == "a"
184
+ @alignment_index = (@alignment_index + 1) % @alignments.size
160
185
  end
161
186
 
162
187
  nil
@@ -8,6 +8,15 @@
8
8
  $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
9
9
  require "ratatui_ruby"
10
10
 
11
+ # Demonstrates terminal color rendering and the Color module.
12
+ #
13
+ # Terminal apps need vibrant colors. Specifying colors as symbols or
14
+ # calculated hex strings works but limits expressiveness.
15
+ #
16
+ # This demo shows the Color module's constructors for creating colors
17
+ # from HSL values or hex integers, producing a full-spectrum gradient.
18
+ #
19
+ # Use it to understand color representation and the Color.hsl/Color.hex APIs.
11
20
  class WidgetStyleColors
12
21
  def initialize
13
22
  @width = 80
@@ -35,12 +44,19 @@ class WidgetStyleColors
35
44
  hue = (col.to_f / @width) * 360.0
36
45
  lightness = 50.0 - ((row.to_f / @height) * 50.0)
37
46
 
38
- rgb = hsl_to_rgb(hue, 100.0, lightness)
39
- hex = rgb_to_hex(rgb)
47
+ # Use Color.hsl for top half, Color.hsluv for bottom half
48
+ # HSLuv provides perceptually uniform colors (same visual brightness)
49
+ hex = if row < @height / 2
50
+ RatatuiRuby::Style::Color.hsl(hue, 100.0, lightness)
51
+ else
52
+ # HSLuv: perceptually uniform - all colors appear equal brightness
53
+ RatatuiRuby::Style::Color.hsluv(hue, 100.0, lightness)
54
+ end
40
55
 
56
+ # Demonstrate Style.with for concise inline styling
41
57
  span = tui.text_span(
42
58
  content: " ",
43
- style: tui.style(bg: hex)
59
+ style: RatatuiRuby::Style::Style.with(bg: hex)
44
60
  )
45
61
  spans << span
46
62
  end
@@ -48,57 +64,20 @@ class WidgetStyleColors
48
64
  lines << tui.text_line(spans:)
49
65
  end
50
66
 
67
+ # Also demonstrate Color.hex for the border
68
+ border_color = RatatuiRuby::Style::Color.hex(0xFFD700) # Gold
69
+
51
70
  tui.paragraph(
52
71
  text: lines,
53
72
  block: tui.block(
54
- title: "Hex Color Gradient (Press 'q' or Ctrl+C to exit)",
73
+ title: "HSL (top) vs HSLuv (bottom) - Style.with demo (Press 'q' to exit)",
55
74
  borders: [:all],
56
- border_type: :rounded
75
+ border_type: :rounded,
76
+ # Using Style.with for concise border styling
77
+ border_style: RatatuiRuby::Style::Style.with(fg: border_color)
57
78
  )
58
79
  )
59
80
  end
60
-
61
- private def hsl_to_rgb(hue, saturation, lightness)
62
- h = hue / 360.0
63
- s = saturation / 100.0
64
- l = lightness / 100.0
65
-
66
- if s == 0
67
- r = g = b = l
68
- else
69
- q = (l < 0.5) ? l * (1 + s) : l + s - (l * s)
70
- p = (2 * l) - q
71
-
72
- r = hue_to_rgb(p, q, h + (1.0 / 3.0))
73
- g = hue_to_rgb(p, q, h)
74
- b = hue_to_rgb(p, q, h - (1.0 / 3.0))
75
- end
76
-
77
- [
78
- (r * 255).round,
79
- (g * 255).round,
80
- (b * 255).round,
81
- ]
82
- end
83
-
84
- private def hue_to_rgb(p, q, t)
85
- t += 1 while t < 0
86
- t -= 1 while t > 1
87
-
88
- if t < 1.0 / 6.0
89
- p + ((q - p) * 6 * t)
90
- elsif t < 1.0 / 2.0
91
- q
92
- elsif t < 2.0 / 3.0
93
- p + ((q - p) * ((2.0 / 3.0) - t) * 6)
94
- else
95
- p
96
- end
97
- end
98
-
99
- private def rgb_to_hex(rgb)
100
- "##{rgb.map { |c| c.to_s(16).upcase.rjust(2, '0') }.join}"
101
- end
102
81
  end
103
82
 
104
83
  WidgetStyleColors.new.run if __FILE__ == $PROGRAM_NAME
@@ -55,6 +55,7 @@ class WidgetTable
55
55
  @show_cell_highlight = true
56
56
  @offset_mode_index = 0
57
57
  @flex_mode_index = 0
58
+ @strikethrough_pids = Set.new # Track which rows have strikethrough
58
59
  end
59
60
 
60
61
  def run
@@ -78,28 +79,38 @@ class WidgetTable
78
79
  { name: "Blue on White", style: @tui.style(fg: :blue, bg: :white) },
79
80
  { name: "Magenta", style: @tui.style(fg: :magenta, modifiers: [:bold]) },
80
81
  ]
81
- @column_highlight_style = @tui.style(fg: :magenta)
82
+ @column_highlight_style = @tui.style(fg: :red)
82
83
  @cell_highlight_style = @tui.style(fg: :white, bg: :red, modifiers: [:bold])
83
84
  @hotkey_style = @tui.style(modifiers: [:bold, :underlined])
84
85
  end
85
86
 
86
87
  private def render(frame)
87
88
  # v0.7.0: Create table rows using table_row and table_cell for per-cell styling
88
- rows = PROCESSES.map do |p|
89
+ rows = PROCESSES.each_with_index.map do |p, i|
89
90
  cpu_style = case p[:cpu]
90
91
  when 0...10 then @tui.style(fg: :green)
91
92
  when 10...30 then @tui.style(fg: :yellow)
92
93
  else @tui.style(fg: :red, modifiers: [:bold])
93
94
  end
94
- @tui.table_row(
95
+ row = @tui.table_row(
95
96
  cells: [
96
97
  p[:pid].to_s,
97
98
  p[:name],
98
99
  @tui.table_cell(content: "#{p[:cpu]}%", style: cpu_style),
99
100
  ],
100
- # Apply alternating row backgrounds for readability
101
- style: p[:pid].even? ? @tui.style(bg: :dark_gray) : nil
101
+ # Apply alternating row backgrounds for readability (using basic ANSI colors for compatibility)
102
+ style: i.even? ? @tui.style(bg: :white, fg: :black) : nil
102
103
  )
104
+
105
+ # Row#enable_strikethrough: Apply strikethrough to "tamped" (de-emphasized) processes.
106
+ # Note: Strikethrough (SGR 9) is not supported by all terminals. macOS Terminal.app
107
+ # notably lacks support, while Kitty, iTerm2, Alacritty, and WezTerm render it.
108
+ # We add :dim as a fallback so the effect is visible even without strikethrough.
109
+ if @strikethrough_pids.include?(p[:pid])
110
+ row.enable_strikethrough.with(style: (row.style || @tui.style).with(modifiers: ((row.style&.modifiers || []) + [:crossed_out, :dim]).uniq))
111
+ else
112
+ row
113
+ end
103
114
  end
104
115
 
105
116
  # Define column widths
@@ -170,6 +181,8 @@ class WidgetTable
170
181
  @tui.text_span(content: ": Style (#{current_style_entry[:name]}) "),
171
182
  @tui.text_span(content: "p", style: @hotkey_style),
172
183
  @tui.text_span(content: ": Spacing (#{current_spacing_entry[:name]}) "),
184
+ @tui.text_span(content: "t", style: @hotkey_style),
185
+ @tui.text_span(content: ": Tamp Row"),
173
186
  ]),
174
187
  # Line 3: More Controls
175
188
  @tui.text_line(spans: [
@@ -237,6 +250,16 @@ class WidgetTable
237
250
  @highlight_spacing_index = (@highlight_spacing_index + 1) % HIGHLIGHT_SPACINGS.length
238
251
  in type: :key, code: "x"
239
252
  @selected_index = @selected_index.nil? ? 0 : nil
253
+ in type: :key, code: "t"
254
+ # Toggle strikethrough for selected row (demonstrates Row#enable_strikethrough)
255
+ if @selected_index
256
+ pid = PROCESSES[@selected_index][:pid]
257
+ if @strikethrough_pids.include?(pid)
258
+ @strikethrough_pids.delete(pid)
259
+ else
260
+ @strikethrough_pids.add(pid)
261
+ end
262
+ end
240
263
  in type: :key, code: "c"
241
264
  @show_column_highlight = !@show_column_highlight
242
265
  in type: :key, code: "z"
@@ -51,10 +51,11 @@ class WidgetTextWidth
51
51
  sample = @text_samples[@selected_index]
52
52
  measured_width = @tui.text_width(sample[:text])
53
53
 
54
- # v0.7.0: Text::Line#width instance method for rich text measurement
55
- styled_line = @tui.text_line(spans: [
56
- @tui.text_span(content: sample[:text], style: @tui.style(fg: :cyan)),
57
- ])
54
+ # v0.7.0: Text::Span#width and Text::Line#width instance methods for rich text measurement
55
+ styled_span = @tui.text_span(content: sample[:text], style: @tui.style(fg: :cyan))
56
+ span_width = styled_span.width
57
+
58
+ styled_line = @tui.text_line(spans: [styled_span])
58
59
  line_width = styled_line.width
59
60
 
60
61
  # Build content text with newlines
@@ -62,6 +63,7 @@ class WidgetTextWidth
62
63
  content << "Sample: #{sample[:text]}"
63
64
  content << ""
64
65
  content << "Display Width (text_width): #{measured_width} cells"
66
+ content << "Display Width (span.width): #{span_width} cells"
65
67
  content << "Display Width (line.width): #{line_width} cells"
66
68
  content << "Character Count: #{sample[:text].length}"
67
69
  content << ""
@@ -23,6 +23,15 @@ version = "1.0.100"
23
23
  source = "registry+https://github.com/rust-lang/crates.io-index"
24
24
  checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
25
25
 
26
+ [[package]]
27
+ name = "approx"
28
+ version = "0.5.1"
29
+ source = "registry+https://github.com/rust-lang/crates.io-index"
30
+ checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
31
+ dependencies = [
32
+ "num-traits",
33
+ ]
34
+
26
35
  [[package]]
27
36
  name = "atomic"
28
37
  version = "0.6.1"
@@ -106,6 +115,12 @@ version = "3.19.1"
106
115
  source = "registry+https://github.com/rust-lang/crates.io-index"
107
116
  checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
108
117
 
118
+ [[package]]
119
+ name = "by_address"
120
+ version = "1.2.1"
121
+ source = "registry+https://github.com/rust-lang/crates.io-index"
122
+ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06"
123
+
109
124
  [[package]]
110
125
  name = "bytemuck"
111
126
  version = "1.24.0"
@@ -364,6 +379,12 @@ dependencies = [
364
379
  "regex",
365
380
  ]
366
381
 
382
+ [[package]]
383
+ name = "fast-srgb8"
384
+ version = "1.0.0"
385
+ source = "registry+https://github.com/rust-lang/crates.io-index"
386
+ checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1"
387
+
367
388
  [[package]]
368
389
  name = "filedescriptor"
369
390
  version = "0.8.3"
@@ -756,6 +777,30 @@ dependencies = [
756
777
  "num-traits",
757
778
  ]
758
779
 
780
+ [[package]]
781
+ name = "palette"
782
+ version = "0.7.6"
783
+ source = "registry+https://github.com/rust-lang/crates.io-index"
784
+ checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6"
785
+ dependencies = [
786
+ "approx",
787
+ "fast-srgb8",
788
+ "palette_derive",
789
+ "phf",
790
+ ]
791
+
792
+ [[package]]
793
+ name = "palette_derive"
794
+ version = "0.7.6"
795
+ source = "registry+https://github.com/rust-lang/crates.io-index"
796
+ checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30"
797
+ dependencies = [
798
+ "by_address",
799
+ "proc-macro2",
800
+ "quote",
801
+ "syn 2.0.111",
802
+ ]
803
+
759
804
  [[package]]
760
805
  name = "parking_lot"
761
806
  version = "0.12.5"
@@ -932,6 +977,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
932
977
  checksum = "d1ce67fb8ba4446454d1c8dbaeda0557ff5e94d39d5e5ed7f10a65eb4c8266bc"
933
978
  dependencies = [
934
979
  "instability",
980
+ "palette",
935
981
  "ratatui-core",
936
982
  "ratatui-crossterm",
937
983
  "ratatui-macros",
@@ -952,6 +998,7 @@ dependencies = [
952
998
  "itertools 0.14.0",
953
999
  "kasuari",
954
1000
  "lru",
1001
+ "palette",
955
1002
  "strum",
956
1003
  "thiserror 2.0.17",
957
1004
  "unicode-segmentation",
@@ -1012,7 +1059,7 @@ dependencies = [
1012
1059
 
1013
1060
  [[package]]
1014
1061
  name = "ratatui_ruby"
1015
- version = "0.9.1"
1062
+ version = "0.10.0"
1016
1063
  dependencies = [
1017
1064
  "bumpalo",
1018
1065
  "lazy_static",
@@ -3,7 +3,7 @@
3
3
 
4
4
  [package]
5
5
  name = "ratatui_ruby"
6
- version = "0.9.1"
6
+ version = "0.10.0"
7
7
  edition = "2021"
8
8
 
9
9
  [lib]
@@ -11,9 +11,13 @@ crate-type = ["cdylib", "staticlib"]
11
11
 
12
12
  [dependencies]
13
13
  magnus = "0.8.2"
14
- ratatui = { version = "0.30", features = ["widget-calendar", "layout-cache", "unstable-rendered-line-info"] }
14
+ ratatui = { version = "0.30", features = ["widget-calendar", "layout-cache", "unstable-rendered-line-info", "palette"] }
15
15
  unicode-width = "0.1"
16
16
 
17
17
  bumpalo = "3.16"
18
18
  lazy_static = "1.4"
19
19
  time = { version = "0.3", features = ["macros"] }
20
+
21
+ [profile.release]
22
+ debug = true
23
+ strip = false