ratatui_ruby 0.10.2 → 1.0.0.pre.beta.1

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.
@@ -0,0 +1,44 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Website Spinner Verification
7
+
8
+ Verifies the inline spinner example on the [RatatuiRuby website](https://ratatui-ruby.dev).
9
+
10
+ This example exists as a documentation regression test. It ensures the website's inline viewport spinner demo remains functional.
11
+
12
+ ## Usage
13
+
14
+ ```ruby
15
+ class Spinner
16
+ def main
17
+ RatatuiRuby.run(viewport: :inline, height: 1) do |tui|
18
+ until connected?
19
+ status = tui.paragraph(text: "#{spin} Connecting...")
20
+ tui.draw { |frame| frame.render_widget(status, frame.area) }
21
+ return ending(tui, "Canceled!", :red) if tui.poll_event.ctrl_c?
22
+ end
23
+ ending(tui, "Connected!", :green)
24
+ end
25
+ end
26
+
27
+ def ending(tui, message, color) = tui.draw do |frame|
28
+ frame.render_widget(tui.paragraph(text: message, fg: color), frame.area)
29
+ end
30
+
31
+ def initialize = (@i, @end = 0, Time.now + 2)
32
+ def connected? = Time.now >= @end
33
+ def spin = SPINNER[(@i += 1) % SPINNER.length]
34
+ SPINNER = %w[⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏]
35
+ end
36
+ Spinner.new.main; puts
37
+ ```
38
+
39
+ ## Features Demonstrated
40
+
41
+ - **Inline viewport**: 1-line viewport that preserves terminal scrollback
42
+ - **Ctrl+C handling**: Graceful cancellation with `poll_event.ctrl_c?`
43
+ - **Colored output**: Status messages with `:red` and `:green` colors
44
+ - **Timeout pattern**: Uses `Time.now` for connection simulation
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ #
6
+ # SPDX-License-Identifier: AGPL-3.0-or-later
7
+ #++
8
+
9
+ # Test 1: Inline spinner example
10
+ require "ratatui_ruby"
11
+
12
+ class Spinner
13
+ def main
14
+ RatatuiRuby.run(viewport: :inline, height: 1) do |tui|
15
+ until connected?
16
+ status = tui.paragraph(text: "#{spin} Connecting...")
17
+ tui.draw { |frame| frame.render_widget(status, frame.area) }
18
+ return ending(tui, "Canceled!", :red) if tui.poll_event.ctrl_c?
19
+ end
20
+ ending(tui, "Connected!", :green)
21
+ end
22
+ end
23
+
24
+ def ending(tui, text, fg) = tui.draw do |frame|
25
+ frame.render_widget(tui.paragraph(text:, fg:), frame.area)
26
+ puts # Prepare a new line for the shell prompt
27
+ end
28
+
29
+ def initialize = (@frame, @finish = 0, Time.now + 2)
30
+ def connected? = Time.now >= @finish # Simulate work
31
+ def spin = SPINNER[(@frame += 1) % SPINNER.length]
32
+ SPINNER = %w[⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏]
33
+ end
34
+ Spinner.new.main
@@ -1059,7 +1059,7 @@ dependencies = [
1059
1059
 
1060
1060
  [[package]]
1061
1061
  name = "ratatui_ruby"
1062
- version = "0.10.2"
1062
+ version = "1.0.0-beta.1"
1063
1063
  dependencies = [
1064
1064
  "bumpalo",
1065
1065
  "lazy_static",
@@ -3,7 +3,7 @@
3
3
 
4
4
  [package]
5
5
  name = "ratatui_ruby"
6
- version = "0.10.2"
6
+ version = "1.0.0-beta.1"
7
7
  edition = "2021"
8
8
 
9
9
  [lib]
@@ -6,7 +6,6 @@
6
6
  #++
7
7
 
8
8
  require "tmpdir"
9
- require "rexml/document"
10
9
 
11
10
  module RatatuiRuby
12
11
  module Labs
@@ -21,6 +20,7 @@ module RatatuiRuby
21
20
  class << self
22
21
  # Dumps the widget tree to XML (single widget for tree mode).
23
22
  def dump_widget_tree(widget, _area = nil)
23
+ ensure_rexml_loaded
24
24
  doc = REXML::Document.new
25
25
  doc.add(REXML::XMLDecl.new("1.0", "UTF-8"))
26
26
  doc.add(build_element(widget))
@@ -42,6 +42,7 @@ module RatatuiRuby
42
42
 
43
43
  # Dumps multiple widgets captured from Frame API mode.
44
44
  def dump_widgets(widgets_with_areas)
45
+ ensure_rexml_loaded
45
46
  Labs.warn_once!("Labs::A11y (RR_LABS=A11Y)")
46
47
 
47
48
  # Reset counter each frame for stable IDs
@@ -167,6 +168,14 @@ module RatatuiRuby
167
168
 
168
169
  element
169
170
  end
171
+
172
+ # Lazily loads REXML when first needed.
173
+ private def ensure_rexml_loaded
174
+ return if defined?(@rexml_loaded) && @rexml_loaded
175
+
176
+ require "rexml/document"
177
+ @rexml_loaded = true
178
+ end
170
179
  end
171
180
  end
172
181
  end
@@ -701,6 +701,30 @@ module RatatuiRuby
701
701
  centered_horizontally(horizontal_constraint).centered_vertically(vertical_constraint)
702
702
  end
703
703
 
704
+ # Enables array destructuring of the rectangle.
705
+ #
706
+ # Inline viewports and layout code often need position and size together.
707
+ # Accessing x, y, width, height individually is verbose.
708
+ #
709
+ # This method allows convenient array destructuring.
710
+ #
711
+ # === Example
712
+ #
713
+ #--
714
+ # SPDX-SnippetBegin
715
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
716
+ # SPDX-License-Identifier: MIT-0
717
+ #++
718
+ # area = tui.viewport_area
719
+ # x, y, width, height = area
720
+ # # Now you can use x, y, width, height directly
721
+ #--
722
+ # SPDX-SnippetEnd
723
+ #++
724
+ def to_ary
725
+ [x, y, width, height]
726
+ end
727
+
704
728
  # Ruby-idiomatic aliases (TIMTOWTDI)
705
729
  alias position as_position
706
730
  alias size as_size
@@ -8,5 +8,5 @@
8
8
  module RatatuiRuby
9
9
  # The version of the ratatui_ruby gem.
10
10
  # See https://semver.org/spec/v2.0.0.html
11
- VERSION = "0.10.2"
11
+ VERSION = "1.0.0-beta.1"
12
12
  end
@@ -58,6 +58,8 @@ module RatatuiRuby
58
58
  module A11y
59
59
  OUTPUT_PATH: String
60
60
 
61
+ @rexml_loaded: bool?
62
+
61
63
  def self.dump_widget_tree: ((_CustomWidget | widget) widget, ?Layout::Rect? area) -> void
62
64
  def self.dump_widgets: (Array[[(_CustomWidget | widget), Layout::Rect]] widgets_with_areas) -> void
63
65
  def self.startup_message: () -> String
@@ -70,6 +72,7 @@ module RatatuiRuby
70
72
  def self.add_members: (REXML::Element element, untyped node, ?parent_id: String?) -> void
71
73
  def self.scalar?: (untyped value) -> bool
72
74
  def self.build_child_element: (Symbol key, untyped value, ?is_wrapper: bool, ?parent_id: String?) -> REXML::Element
75
+ def self.ensure_rexml_loaded: () -> void
73
76
  end
74
77
  end
75
78
 
@@ -11,12 +11,15 @@ class SemVer
11
11
 
12
12
  def self.parse(string)
13
13
  require "rubygems"
14
- segments = Gem::Version.new(string).segments.fill(0, 3).first(3)
15
- new(segments)
14
+ # Extract prerelease suffix (e.g., "-beta.1", "-alpha.2", "-rc.1")
15
+ base, prerelease = string.split("-", 2)
16
+ segments = Gem::Version.new(base).segments.fill(0, 3).first(3)
17
+ new(segments, prerelease:)
16
18
  end
17
19
 
18
- def initialize(segments)
20
+ def initialize(segments, prerelease: nil)
19
21
  @segments = segments
22
+ @prerelease = prerelease
20
23
  end
21
24
 
22
25
  def next(segment)
@@ -31,6 +34,7 @@ class SemVer
31
34
  end
32
35
 
33
36
  def to_s
34
- @segments.join(".")
37
+ base = @segments.join(".")
38
+ @prerelease ? "#{base}-#{@prerelease}" : base
35
39
  end
36
40
  end
@@ -5,40 +5,137 @@ SPDX-License-Identifier: AGPL-3.0-or-later
5
5
  -->
6
6
 
7
7
  <!DOCTYPE html>
8
- <html>
8
+ <html lang="en">
9
9
  <head>
10
+ <meta charset="UTF-8">
10
11
  <title><%= project_name %> documentation</title>
11
12
  <meta name="viewport" content="width=device-width, initial-scale=1">
12
- <style>
13
- :root { color-scheme: light dark; }
14
- body { font-family: system-ui, sans-serif; max-width: 800px; margin: 0 auto; padding: 2rem; line-height: 1.5; color: light-dark(#111, #eee); background: light-dark(#fff, #111); }
15
- h1 { border-bottom: 2px solid light-dark(#eee, #333); padding-bottom: 0.5rem; }
16
- ul { list-style: none; padding: 0; }
17
- li { margin: 0.5rem 0; border: 1px solid light-dark(#ddd, #444); border-radius: 4px; }
18
- a { display: block; padding: 1rem; text-decoration: none; color: light-dark(#0055aa, #44aaff); font-weight: bold; }
19
- a:hover { background: light-dark(#f5f5f5, #222); }
20
- .meta { font-weight: normal; color: light-dark(#666, #aaa); font-size: 0.9em; float: right; }
21
- </style>
13
+ <meta name="description" content="API documentation and guides for <%= project_name %>, organized by version.">
14
+ <link rel="stylesheet" href="../styles.css">
15
+ <link rel="preconnect" href="https://fonts.googleapis.com">
16
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
17
+ <link
18
+ href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300..900;1,9..144,300..900&family=Source+Sans+3:ital,wght@0,300..900;1,300..900&display=swap"
19
+ rel="stylesheet">
22
20
  </head>
23
21
  <body>
24
- <h1><%= project_name %> documentation</h1>
25
- <ul>
26
- <% versions.each do |version| %>
27
- <li>
28
- <a href='<%= version.slug %>/index.html'>
29
- <%= version.name %>
30
- <span class='meta'>
31
- <% if version.latest? %>
32
- Latest
33
- <% elsif version.edge? %>
34
- Edge
35
- <% else %>
36
- Historical
37
- <% end %>
38
- </span>
39
- </a>
40
- </li>
41
- <% end %>
42
- </ul>
22
+ <header class="header">
23
+ <div class="header__inner">
24
+ <a href="/" class="header__brand">
25
+ <img src="../logo-small.png" alt="RatatuiRuby Logo" class="header__logo" width="48" height="48">
26
+ <span class="header__wordmark">RatatuiRuby</span>
27
+ </a>
28
+ <nav class="header__nav" aria-label="Primary">
29
+ <a href="/#rich-moments">Better <abbr title="Command Line Interfaces">CLIs</abbr></a>
30
+ <a href="/#build">Full <abbr title="Terminal User Interfaces">TUIs</abbr></a>
31
+ <a href="/#frameworks">Frameworks</a>
32
+ <a href="/docs/v0.10/" class="header__nav-external">API →</a>
33
+ <a href="/docs/v0.10/doc/index_md.html" class="header__nav-external">Guides →</a>
34
+ </nav>
35
+ </div>
36
+ </header>
37
+
38
+ <main>
39
+ <section class="section">
40
+ <div class="section__inner">
41
+ <header class="section__header">
42
+ <span class="section__eyebrow">Reference</span>
43
+ <h1 class="section__title">Documentation</h1>
44
+ <p class="section__intro">API reference, guides, and examples for current and past versions of <%= project_name %>.</p>
45
+ </header>
46
+
47
+ <ul class="version-list">
48
+ <% versions.each do |version| %>
49
+ <li class="version-list__item<%= ' version-list__item--latest' if version.latest? %>">
50
+ <a href="<%= version.slug %>/index.html" class="version-list__link">
51
+ <span class="version-list__name"><%= version.name %></span>
52
+ <span class="version-list__meta">
53
+ <% if version.latest? %>
54
+ Latest release via RubyGems
55
+ <% elsif version.edge? %>
56
+ Pre-release via git
57
+ <% else %>
58
+ Historical
59
+ <% end %>
60
+ </span>
61
+ </a>
62
+ </li>
63
+ <% end %>
64
+ </ul>
65
+ </div>
66
+ </section>
67
+ </main>
68
+
69
+ <footer class="footer">
70
+ <div class="footer__inner">
71
+ <!-- Megafooter columns -->
72
+ <div class="footer__columns">
73
+ <div class="footer__column">
74
+ <h3 class="footer__heading">Get Started</h3>
75
+ <ul class="footer__list">
76
+ <li><a href="/docs/v0.10/doc/getting_started/quickstart_md.html">Quickstart <span
77
+ class="footer__tag">Alpha</span></a>
78
+ </li>
79
+ <li><a href="/docs/v0.10/examples/app_cli_rich_moments/README_md.html">Examples</a></li>
80
+ <li><a href="/docs/v0.10/"><abbr title="Application Programming Interface">API</abbr> Reference</a></li>
81
+ <li><a href="/docs/v0.10/doc/index_md.html">Guides</a></li>
82
+ </ul>
83
+ </div>
84
+
85
+ <div class="footer__column">
86
+ <h3 class="footer__heading">Ecosystem</h3>
87
+ <ul class="footer__list">
88
+ <li><a href="https://git.sr.ht/~kerrick/ratatui_ruby-tea">Tea <span
89
+ class="footer__tag">Alpha</span></a></li>
90
+ <li><a href="https://sr.ht/~kerrick/ratatui_ruby/#chapter-3-the-object-path--kit">Kit <span
91
+ class="footer__tag footer__tag--muted">Planned</span></a></li>
92
+ <li><a href="https://sr.ht/~kerrick/ratatui_ruby/#chapter-5-the-framework">Framework <span
93
+ class="footer__tag footer__tag--muted">Planned</span></a></li>
94
+ <li><a href="https://sr.ht/~kerrick/ratatui_ruby/#chapter-6-licensing"><abbr
95
+ title="User Interface">UI</abbr> Widgets
96
+ <span class="footer__tag footer__tag--muted">Planned</span></a></li>
97
+ </ul>
98
+ </div>
99
+
100
+ <div class="footer__column">
101
+ <h3 class="footer__heading">Community</h3>
102
+ <ul class="footer__list">
103
+ <li><a href="https://lists.sr.ht/~kerrick/ratatui_ruby-discuss">Discuss and Chat</a></li>
104
+ <li><a href="https://lists.sr.ht/~kerrick/ratatui_ruby-announce">Announcements</a></li>
105
+ <li><a href="https://lists.sr.ht/~kerrick/ratatui_ruby-devel">Development</a></li>
106
+ <li><a href="https://todo.sr.ht/~kerrick/ratatui_ruby">Bug Tracker</a></li>
107
+ </ul>
108
+ </div>
109
+
110
+ <div class="footer__column">
111
+ <h3 class="footer__heading">Contribute</h3>
112
+ <ul class="footer__list">
113
+ <li><a href="https://man.sr.ht/~kerrick/ratatui_ruby/contributing.md">Contributing Guide</a></li>
114
+ <li><a href="https://man.sr.ht/~kerrick/ratatui_ruby/code_of_conduct.md">Code of Conduct</a></li>
115
+ <li><a href="https://man.sr.ht/~kerrick/ratatui_ruby/history/index.md">Project History</a></li>
116
+ <li><a href="https://lists.sr.ht/~kerrick/ratatui_ruby-devel/patches">Pull Requests</a></li>
117
+ </ul>
118
+ </div>
119
+ </div>
120
+
121
+ <!-- Bottom bar -->
122
+ <div class="footer__bottom">
123
+ <div class="footer__brand">
124
+ <img src="../logo-small.png" alt="" class="footer__logo" width="32" height="32" aria-hidden="true">
125
+ <span class="footer__wordmark">RatatuiRuby</span>
126
+ </div>
127
+ <nav class="footer__meta" aria-label="Meta links">
128
+ <a href="https://git.sr.ht/~kerrick/ratatui_ruby">Source</a>
129
+ <a href="https://rubygems.org/gems/ratatui_ruby">RubyGems</a>
130
+ <a href="https://ratatui.rs">Ratatui</a>
131
+ <a href="https://builds.sr.ht/~kerrick/ratatui_ruby?">Build Status</a>
132
+ </nav>
133
+ <p class="footer__copy">© 2026&nbsp;Kerrick&nbsp;Long · Library:&nbsp;<a
134
+ href="https://spdx.org/licenses/LGPL-3.0-or-later.html">LGPL-3.0-or-later</a> · Website:&nbsp;<a
135
+ href="https://spdx.org/licenses/CC-BY-NC-ND-4.0.html">CC-BY-NC-ND-4.0</a> · Snippets:&nbsp;<a
136
+ href="https://spdx.org/licenses/MIT-0.html">MIT-0</a></p>
137
+ </div>
138
+ </div>
139
+ </footer>
43
140
  </body>
44
141
  </html>
data/tasks/sourcehut.rake CHANGED
@@ -25,7 +25,11 @@ namespace :sourcehut do
25
25
  version_content = File.read("lib/ratatui_ruby/version.rb")
26
26
  version = version_content.match(/VERSION = "(.+?)"/)[1]
27
27
 
28
- gem_filename = "#{spec.name}-#{version}.gem"
28
+ # Normalize version using Gem::Version - RubyGems converts hyphens
29
+ # (e.g., "1.0.0-beta.1" -> "1.0.0.pre.beta.1")
30
+ normalized_version = Gem::Version.new(version).to_s
31
+
32
+ gem_filename = "#{spec.name}-#{normalized_version}.gem"
29
33
 
30
34
  rubies = YAML.load_file("tasks/resources/rubies.yml")
31
35
 
@@ -52,11 +52,11 @@ end
52
52
 
53
53
  class Edge < Version
54
54
  def slug
55
- "main"
55
+ "trunk"
56
56
  end
57
57
 
58
58
  def name
59
- "main"
59
+ "trunk"
60
60
  end
61
61
 
62
62
  def type
@@ -29,7 +29,7 @@ class VersionedDocumentation
29
29
 
30
30
  Dir.chdir(source_path) do
31
31
  title = "#{project_name} #{@version.name}"
32
- title = "#{project_name} (main)" if @version.edge?
32
+ title = "#{project_name} (trunk)" if @version.edge?
33
33
 
34
34
  # Use rake rerdoc to ensure copy_examples runs
35
35
  # Set environment variables to override rdoc settings
data/tasks/website.rake CHANGED
@@ -11,7 +11,7 @@ require "tmpdir"
11
11
  require_relative "rdoc_config"
12
12
 
13
13
  namespace :website do
14
- desc "Build documentation for main (current dir) and all git tags"
14
+ desc "Build documentation for trunk (current dir) and all git tags"
15
15
  task :build do
16
16
  require_relative "website/website"
17
17