milktea 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2ed418298f9856106a16ea4b8547cddbdde2841e270faa7e7c5750e45498055c
4
- data.tar.gz: ebbc971d234ee535db7d4cda1d57a94c57fbe6a6cade430d3a715a13340fae24
3
+ metadata.gz: b8fc1c4405e6f8b4d6969285482fc16ec7d103ff9399f89a2606532e3545b9a6
4
+ data.tar.gz: 38638d551a1c7ffd34c0b2d32ff75824db9daff1371cb3ecf30ab7604f714f14
5
5
  SHA512:
6
- metadata.gz: 4733b94d3d650b662671a3d14fee70d7a4aec3b8dd7bbb64f69f4f5e4bd8744a2993791bcc02690b349fa95b4dfdf0d4f01eb26261a733ce800ad0fa8823137a
7
- data.tar.gz: 0f871c49c46ec816401b93cf7f16e856ccb2103e8bc256665d2a3fffcb0a8f669ba241f0d6279d37a0716acbe6e6ca1152d3f2a4283fdbedf925011a0f910eea
6
+ metadata.gz: 0f2e50699ec28cddbeaabfa11ceeda12bf544d4572d38d78fd8edbcaa2efe0ad7d4b7e0f47af1b7945ec29fb74614ae2661da2ede93e40daa2952bab82d7426f
7
+ data.tar.gz: 5b4397dfb287ace7627b374a005f62b93fd76ac578aff0c9067df52175eb2a4345ff9548998dd63b8cf8ba27b6ac0387b1c0e57fdd76afb19034922ece5c2bf5
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.2.0"
3
+ }
data/.rubocop.yml CHANGED
@@ -1,8 +1,9 @@
1
1
  AllCops:
2
2
  NewCops: enable
3
3
  SuggestExtensions: false
4
- TargetRubyVersion: 3.1
4
+ TargetRubyVersion: 3.2
5
5
  Exclude:
6
+ - 'vendor/**/*'
6
7
  - 'examples/**/*'
7
8
 
8
9
  Style/StringLiterals:
data/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+
3
+ ## [0.2.0](https://github.com/elct9620/milktea/compare/v0.1.0...v0.2.0) (2025-07-06)
4
+
5
+
6
+ ### Features
7
+
8
+ * add truncation mode to Milktea::Text component ([031f87a](https://github.com/elct9620/milktea/commit/031f87a6f55476098ac7247b8041e32e38e67961))
9
+ * bump minimum Ruby version to 3.2+ for Zeitwerk compatibility ([18cc801](https://github.com/elct9620/milktea/commit/18cc8011c2ac4586ad849bf4670750ecbc011294))
10
+ * implement Message::Tick with timestamp for timing-based updates ([982baec](https://github.com/elct9620/milktea/commit/982baeca567eed949ec82187fefb1f45078e97f5))
11
+ * implement Milktea::Text component with overflow handling ([5a0ef38](https://github.com/elct9620/milktea/commit/5a0ef38708e74f4eef6905c2f72813c4259fdfa0))
12
+ * refactor examples to use bundler/inline for self-contained dependencies ([2192a6b](https://github.com/elct9620/milktea/commit/2192a6b185e04d26fa258231c88f412f39254b1a))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * add rubocop-rubycw gem to resolve CI failure ([82b0dd7](https://github.com/elct9620/milktea/commit/82b0dd71bd535e8b2169401ca5e1f78e9f5dea79))
18
+ * properly configure RuboCop plugins to resolve CI failures ([d82e2c8](https://github.com/elct9620/milktea/commit/d82e2c83338eaca3841bfbb3cceec401bbdc6787))
19
+ * use clear_screen_down instead of clear_screen in renderer ([d41d431](https://github.com/elct9620/milktea/commit/d41d43115ca36b801fe5289d38996d4a38997e22))
data/CLAUDE.md CHANGED
@@ -202,7 +202,63 @@ This project follows Clean Architecture and Domain-Driven Design (DDD) principle
202
202
 
203
203
  Follow these conventions when writing RSpec tests:
204
204
 
205
- 1. **ALWAYS prefer one-line `it { ... }` syntax**:
205
+ ### Critical Anti-Patterns to Avoid
206
+
207
+ **NEVER use introspection methods to test private implementation details:**
208
+
209
+ ```ruby
210
+ # BAD - Never use these anti-patterns
211
+ it { expect(program.send(:check_resize)).to be_nil }
212
+ it { expect(subject.instance_variable_get(:@output)).to eq($stdout) }
213
+ it { expect(object.send(:private_method)).to eq(value) }
214
+
215
+ # GOOD - Test through public API and observable behavior
216
+ it { expect(runtime).to have_received(:enqueue) }
217
+ it { expect(output.string).to include("expected content") }
218
+ it { expect(program).to respond_to(:stop) }
219
+ ```
220
+
221
+ **Why these are anti-patterns:**
222
+ - Tests implementation details instead of behavior
223
+ - Makes tests brittle and coupled to internal structure
224
+ - Violates encapsulation principles
225
+ - Makes refactoring difficult
226
+
227
+ **Always test through public interfaces and observable outcomes.**
228
+
229
+ ### Core Testing Conventions
230
+
231
+ 1. **Use `describe` blocks to reference actual methods**:
232
+ ```ruby
233
+ # GOOD - Reference actual methods
234
+ describe "#initialize" do
235
+ describe "#update" do
236
+ describe "#with" do
237
+ describe ".configure" do # Class method
238
+ describe ".new" do # Class method
239
+
240
+ # BAD - Generic descriptions that don't map to methods
241
+ describe "initialization" do
242
+ describe "test creation" do
243
+ describe "updating behavior" do
244
+ describe "configuration setup" do
245
+ ```
246
+
247
+ 2. **Use `context` blocks with "when" for conditional scenarios**:
248
+ ```ruby
249
+ # GOOD - Always start context with "when" for conditions
250
+ context "when merging provided state with default state" do
251
+ context "when no config is provided" do
252
+ context "when runtime is running" do
253
+ context "when renderer detects resize" do
254
+
255
+ # BAD - Don't use context for non-conditional descriptions
256
+ context "merging state" do
257
+ context "configuration testing" do
258
+ context "with custom values" do
259
+ ```
260
+
261
+ 3. **ALWAYS prefer one-line `it { ... }` syntax**:
206
262
  ```ruby
207
263
  # Preferred - Always use this when possible
208
264
  it { expect(model.state[:count]).to eq(0) }
@@ -215,7 +271,7 @@ Follow these conventions when writing RSpec tests:
215
271
  end
216
272
  ```
217
273
 
218
- 2. **Use `subject` to define test targets**:
274
+ 4. **Use `subject` to define test targets**:
219
275
  ```ruby
220
276
  subject(:program) { described_class.new }
221
277
  subject(:new_model) { model.with(count: 5) }
@@ -226,7 +282,7 @@ Follow these conventions when writing RSpec tests:
226
282
  subject { described_class.env }
227
283
  ```
228
284
 
229
- 3. **Use `let` for test dependencies and lazy evaluation**:
285
+ 5. **Use `let` for test dependencies and lazy evaluation**:
230
286
  ```ruby
231
287
  let(:output) { StringIO.new }
232
288
  let(:new_model) { result.first }
@@ -234,7 +290,7 @@ Follow these conventions when writing RSpec tests:
234
290
  let(:original_children) { parent_model.children }
235
291
  ```
236
292
 
237
- 4. **Use `context` to group related scenarios and enable one-liners**:
293
+ 6. **Use `context` to group related scenarios and enable one-liners**:
238
294
  ```ruby
239
295
  # Good - Use context to set up scenarios for one-line tests
240
296
  context "when merging provided state with default state" do
@@ -251,7 +307,7 @@ Follow these conventions when writing RSpec tests:
251
307
  end
252
308
  ```
253
309
 
254
- 5. **Use `is_expected` when testing the subject directly**:
310
+ 7. **Use `is_expected` when testing the subject directly**:
255
311
  ```ruby
256
312
  it { is_expected.to be_running } # Preferred
257
313
  it { is_expected.not_to be(model) }
@@ -263,7 +319,7 @@ Follow these conventions when writing RSpec tests:
263
319
  it { expect(described_class.root).to be_a(Pathname) }
264
320
  ```
265
321
 
266
- 6. **Use `before` blocks for setup actions, not variable assignments**:
322
+ 8. **Use `before` blocks for setup actions, not variable assignments**:
267
323
  ```ruby
268
324
  # Good - Setup actions in before blocks
269
325
  context "when configuring with block" do
@@ -284,13 +340,13 @@ Follow these conventions when writing RSpec tests:
284
340
  end
285
341
  ```
286
342
 
287
- 7. **Use `.to change()` for testing immutability**:
343
+ 9. **Use `.to change()` for testing immutability**:
288
344
  ```ruby
289
345
  it { expect { model.update(:increment) }.not_to change(model, :state) }
290
346
  it { expect { model.with(count: 5) }.not_to change(model, :state) }
291
347
  ```
292
348
 
293
- 7. **Each `it` block should have only one expectation**:
349
+ 10. **Each `it` block should have only one expectation**:
294
350
  ```ruby
295
351
  # Good - Separate one-line tests
296
352
  it { expect(new_model).not_to be(model) }
@@ -304,7 +360,7 @@ Follow these conventions when writing RSpec tests:
304
360
  end
305
361
  ```
306
362
 
307
- 8. **Transform multi-line tests into context + one-liners**:
363
+ 11. **Transform multi-line tests into context + one-liners**:
308
364
  ```ruby
309
365
  # Good - Use context to enable one-liner
310
366
  context "when called on base class" do
@@ -328,7 +384,7 @@ Follow these conventions when writing RSpec tests:
328
384
  end
329
385
  ```
330
386
 
331
- 10. **PRIORITY: Transform ANY multi-line test into context + one-liner**:
387
+ 12. **PRIORITY: Transform ANY multi-line test into context + one-liner**:
332
388
  ```ruby
333
389
  # If you find yourself writing this:
334
390
  it "merges provided state with default state" do
@@ -344,24 +400,6 @@ Follow these conventions when writing RSpec tests:
344
400
  end
345
401
  ```
346
402
 
347
- 11. **Never test private instance variables directly**:
348
- ```ruby
349
- # Bad - Testing implementation details
350
- it { expect(subject.instance_variable_get(:@output)).to eq($stdout) }
351
-
352
- # Good - Testing public behavior with one-liner
353
- it { expect(output.string).to include("expected content") }
354
- ```
355
-
356
- 12. **Focus on observable behavior, not implementation**:
357
- ```ruby
358
- # Bad - Checking internal state
359
- it { expect(program.instance_variable_get(:@renderer)).to be_a(Milktea::Renderer) }
360
-
361
- # Good - Testing actual public behavior with one-liner
362
- it { expect(runtime).to have_received(:stop) }
363
- ```
364
-
365
403
  13. **Prefer `instance_double` over extensive `allow` calls**:
366
404
  ```ruby
367
405
  # Bad - Multiple allow calls
@@ -384,7 +422,7 @@ Follow these conventions when writing RSpec tests:
384
422
  end
385
423
  ```
386
424
 
387
- 13. **Use spies for testing delegation instead of expect().to receive()**:
425
+ 14. **Use spies for testing delegation instead of expect().to receive()**:
388
426
  ```ruby
389
427
  # Bad - Pre-setting expectations
390
428
  it "delegates to runtime stop" do
@@ -401,7 +439,7 @@ Follow these conventions when writing RSpec tests:
401
439
  end
402
440
  ```
403
441
 
404
- 14. **Use RSpec's `output` matcher for testing stdout/stderr**:
442
+ 15. **Use RSpec's `output` matcher for testing stdout/stderr**:
405
443
  ```ruby
406
444
  # Good - Using output matcher
407
445
  it "prints to stdout" do
@@ -422,7 +460,7 @@ Follow these conventions when writing RSpec tests:
422
460
  end
423
461
  ```
424
462
 
425
- 15. **Use `allow(ENV).to receive(:fetch)` for environment variable mocking**:
463
+ 16. **Use `allow(ENV).to receive(:fetch)` for environment variable mocking**:
426
464
  ```ruby
427
465
  # Good - Mock ENV.fetch calls
428
466
  before { allow(ENV).to receive(:fetch).with("MILKTEA_ENV", nil).and_return("test") }
@@ -432,7 +470,7 @@ Follow these conventions when writing RSpec tests:
432
470
  after { ENV.delete("MILKTEA_ENV") }
433
471
  ```
434
472
 
435
- 16. **Structure module/class method tests with clear subject definitions**:
473
+ 17. **Structure module/class method tests with clear subject definitions**:
436
474
  ```ruby
437
475
  describe ".root" do
438
476
  subject { described_class.root }
@@ -453,7 +491,7 @@ Follow these conventions when writing RSpec tests:
453
491
  end
454
492
  ```
455
493
 
456
- 17. **Use named subjects for configuration testing**:
494
+ 18. **Use named subjects for configuration testing**:
457
495
  ```ruby
458
496
  describe ".configure" do
459
497
  subject(:config) { described_class.config } # Named subject for clarity
@@ -474,7 +512,15 @@ Follow these conventions when writing RSpec tests:
474
512
 
475
513
  ### RSpec Style Summary
476
514
 
477
- **CRITICAL RULE**: Always prefer `it { ... }` one-line syntax. If you find yourself writing a multi-line `it` block, immediately refactor it into a `context` with a `subject` to enable one-line tests.
515
+ **CRITICAL ANTI-PATTERNS - NEVER DO THESE:**
516
+ - Never use `send(:private_method)` to test private methods
517
+ - Never use `instance_variable_get(:@var)` to test private variables
518
+ - Always test through public interfaces and observable behavior
519
+
520
+ **CRITICAL CONVENTIONS:**
521
+ - `describe` blocks must reference actual methods (`#initialize`, `.configure`)
522
+ - `context` blocks must start with "when" for conditional scenarios
523
+ - Always prefer `it { ... }` one-line syntax over multi-line blocks
478
524
 
479
525
  **The Golden Pattern**:
480
526
  ```ruby
@@ -517,7 +563,7 @@ end
517
563
 
518
564
  ## Important Notes
519
565
 
520
- - Ruby version requirement: >= 3.1.0
566
+ - Ruby version requirement: >= 3.2.0
521
567
  - Uses conventional commits format in English
522
568
  - The gemspec uses git to determine which files to include in the gem
523
569
  - Currently in early development (v0.1.0) with core architecture implemented
data/README.md CHANGED
@@ -12,6 +12,8 @@ A Terminal User Interface (TUI) framework for Ruby, inspired by [Bubble Tea](htt
12
12
  - 🔄 **Hot Reloading**: Instant feedback during development (similar to web frameworks)
13
13
  - 📱 **Responsive Design**: Automatic adaptation to terminal resize events
14
14
  - 🧩 **Composable Components**: Build complex UIs from simple, reusable models
15
+ - 📝 **Text Components**: Unicode-aware text rendering with wrapping and truncation
16
+ - ⏱️ **Timing System**: Built-in support for animations and time-based updates
15
17
  - 🎨 **Rich Terminal Support**: Leverage TTY gems for advanced terminal features
16
18
 
17
19
  ## Installation
@@ -224,14 +226,20 @@ end
224
226
 
225
227
  Explore the `examples/` directory for comprehensive demonstrations:
226
228
 
227
- - **[Container Layout](examples/container_layout.rb)**: Flexbox-style layouts with resize support
229
+ - **[Simple Counter](examples/simple.rb)**: Basic Elm Architecture patterns
230
+ - **[Container Layout](examples/container_layout.rb)**: Flexbox-style layouts with resize support
231
+ - **[Text Components](examples/text_demo.rb)**: Unicode text rendering with dual modes
232
+ - **[Animations](examples/tick_example.rb)**: Timing-based animations and dynamic content
228
233
  - **[Hot Reload Demo](examples/hot_reload_demo.rb)**: Development workflow with instant updates
234
+ - **[Dashboard](examples/dashboard.rb)**: Complex multi-component layout
229
235
 
230
236
  Run examples:
231
237
 
232
238
  ```bash
239
+ ruby examples/simple.rb
240
+ ruby examples/text_demo.rb
241
+ ruby examples/tick_example.rb
233
242
  ruby examples/container_layout.rb
234
- ruby examples/hot_reload_demo.rb
235
243
  ```
236
244
 
237
245
  ## Advanced Features
@@ -318,17 +326,19 @@ program.run
318
326
 
319
327
  - **`Milktea::Model`**: Base class for all UI components
320
328
  - **`Milktea::Container`**: Layout container with flexbox-style properties
329
+ - **`Milktea::Text`**: Unicode-aware text component with dual rendering modes
321
330
  - **`Milktea::Application`**: High-level application wrapper
322
331
  - **`Milktea::Program`**: Main application runtime
323
- - **`Milktea::Message`**: Standard message types (KeyPress, Exit, Resize, Reload)
332
+ - **`Milktea::Message`**: Standard message types for application events
324
333
 
325
334
  ### Message System
326
335
 
327
- - **`Message::KeyPress`**: Keyboard input events
336
+ - **`Message::KeyPress`**: Keyboard input events with key details
337
+ - **`Message::Tick`**: Timing events with timestamps for animations
328
338
  - **`Message::Exit`**: Application termination
329
- - **`Message::Resize`**: Terminal size changes
330
- - **`Message::Reload`**: Hot reload events
331
- - **`Message::None`**: No-operation message
339
+ - **`Message::Resize`**: Terminal size changes
340
+ - **`Message::Reload`**: Hot reload events (development)
341
+ - **`Message::None`**: No-operation message (no render)
332
342
 
333
343
  For detailed API documentation, see the [documentation website](https://rubydoc.info/gems/milktea).
334
344
 
@@ -20,6 +20,12 @@ Two new comprehensive examples demonstrate the framework's layout capabilities:
20
20
 
21
21
  These examples integrate with tty-box for professional terminal UI presentation and serve as practical references for developers building TUI applications.
22
22
 
23
+ #### Self-Contained Example Architecture
24
+ Examples have been completely refactored to use `bundler/inline` with inline gemfiles, making them truly self-contained. Each example now manages its own specific dependencies without affecting the main project's Gemfile. This architectural change eliminates the need for developers to modify project dependencies just to run examples and ensures examples remain functional regardless of the main project's development dependencies.
25
+
26
+ #### Enhanced Ruby Compatibility and CI Infrastructure
27
+ The project now enforces a minimum Ruby version of 3.2+ to ensure Zeitwerk compatibility and modern Ruby feature availability. The CI matrix has been expanded to test against Ruby 3.2, 3.3, and 3.4, providing comprehensive compatibility assurance across current Ruby versions while maintaining focus on supported versions.
28
+
23
29
  ## What's Fixed
24
30
 
25
31
  #### Hot Reloading and Dynamic Layout Integration
@@ -31,6 +37,12 @@ Fixed children_views joining behavior to eliminate unnecessary newlines between
31
37
  #### Test Organization and Coverage
32
38
  Reorganized test suites to properly reflect the architectural changes. Dynamic child resolution tests were moved from container_spec to model_spec, aligning test coverage with the new framework-wide availability of the feature. This ensures maintainability and prevents confusion about where functionality is implemented.
33
39
 
40
+ #### Dependency Management and Development Environment
41
+ Resolved dependency bloat by removing unnecessary development dependencies (ruby-lsp, logger, rbs, sorbet-runtime) from the main Gemfile. The ruby-lsp dependency was causing maintenance overhead without providing essential functionality for the framework itself. This cleanup reduces installation size and eliminates potential dependency conflicts for users integrating Milktea into their applications.
42
+
43
+ #### CI Configuration and RuboCop Integration
44
+ Fixed CI failures caused by RuboCop plugin configuration issues. Removed unnecessary RuboCop plugins (rubocop-rubycw, rubocop-on-rbs) that were added to resolve CI failures but weren't actually required for this project. The GitHub Actions now correctly use the project's .rubocop.yml configuration without additional plugin dependencies.
45
+
34
46
  ## Design Decisions
35
47
 
36
48
  #### Symbol-Based Dynamic Resolution Architecture
@@ -61,21 +73,59 @@ Reorganized test suites to properly reflect the architectural changes. Dynamic c
61
73
 
62
74
  **Rationale**: Terminal UI layout requires precise coordinate and dimension management. Breaking bounds propagation creates visual artifacts and layout inconsistencies. The solution maintains flexbox semantics while enabling dynamic behavior.
63
75
 
76
+ #### Ruby Version Requirements and Zeitwerk Compatibility
77
+ **Context**: The framework uses Zeitwerk for autoloading, which has specific Ruby version requirements for optimal functionality and stability.
78
+
79
+ **Decision**: Bump minimum Ruby version requirement from 3.1.0 to 3.2.0 across all project configurations.
80
+
81
+ **Rationale**: Ruby 3.2+ provides better Zeitwerk integration, improved performance, and access to modern Ruby features that enhance framework capabilities. This decision ensures developers have access to the most stable and feature-complete Ruby environment while maintaining reasonable compatibility with current Ruby versions.
82
+
83
+ #### Self-Contained Example Dependencies
84
+ **Context**: Examples previously relied on development dependencies in the main Gemfile, creating coupling between framework dependencies and example requirements.
85
+
86
+ **Decision**: Implement `bundler/inline` with inline gemfiles for all examples, making each example completely self-contained.
87
+
88
+ **Rationale**: This architectural pattern eliminates dependency pollution in the main project while ensuring examples remain functional and runnable without project setup. It demonstrates best practices for Ruby gem development and provides better developer experience for users exploring framework capabilities.
89
+
90
+ #### Development Dependency Minimization
91
+ **Context**: The main Gemfile contained development tools (ruby-lsp, tty-box, listen) that aren't essential for framework functionality but were needed for specific development workflows.
92
+
93
+ **Decision**: Remove non-essential development dependencies from the main Gemfile and move them to example-specific inline gemfiles where needed.
94
+
95
+ **Rationale**: This reduces the installation footprint for users integrating Milktea into their applications and eliminates potential dependency conflicts. Essential testing and code quality tools remain in the main Gemfile, while convenience tools are available where specifically needed.
96
+
64
97
  ## Impact
65
98
 
66
- These changes significantly enhance the framework's flexibility and developer experience. The dynamic child resolution system enables sophisticated component composition patterns previously impossible, while the enhanced Container system provides CSS-like layout capabilities for terminal interfaces.
99
+ These changes significantly enhance the framework's flexibility and developer experience while establishing a more robust foundation for long-term development. The dynamic child resolution system enables sophisticated component composition patterns previously impossible, while the enhanced Container system provides CSS-like layout capabilities for terminal interfaces.
100
+
101
+ The self-contained example architecture dramatically improves developer onboarding by eliminating setup friction and dependency conflicts. New users can now explore framework capabilities without modifying their development environment or dealing with complex dependency resolution.
67
102
 
68
- Developers can now build more maintainable TUI applications with less boilerplate code and greater architectural flexibility. The unified Symbol resolution pattern creates consistency across the framework, reducing learning curve and improving code predictability.
103
+ The Ruby 3.2+ requirement ensures developers have access to modern language features and optimal Zeitwerk performance, while the streamlined CI pipeline provides faster feedback and more reliable testing across supported Ruby versions.
104
+
105
+ Developers can now build more maintainable TUI applications with less boilerplate code, greater architectural flexibility, and confidence in cross-version compatibility. The unified Symbol resolution pattern creates consistency across the framework, reducing learning curve and improving code predictability.
69
106
 
70
107
  The improvements particularly benefit complex applications requiring dynamic layouts, state-driven component switching, and sophisticated terminal interface designs. Hot reloading reliability ensures smooth development experience even with complex dynamic component hierarchies.
71
108
 
72
109
  ## Files Modified
73
110
 
111
+ ### Core Framework
74
112
  - `lib/milktea/model.rb` - Added dynamic child resolution framework-wide
75
113
  - `lib/milktea/container.rb` - Added default view implementation, removed Container-specific resolution
114
+ - `lib/milktea/renderer.rb` - Enhanced cursor management for cleaner terminal experience
115
+
116
+ ### Testing and Quality
76
117
  - `spec/milktea/model_spec.rb` - Added comprehensive dynamic resolution tests
77
118
  - `spec/milktea/container_spec.rb` - Reorganized tests, removed duplicated dynamic tests
78
- - `examples/container_layout.rb` - Interactive layout demo with dynamic switching
79
- - `examples/container_simple.rb` - Basic layout demonstration
80
- - `Gemfile` - Added tty-box dependency for enhanced examples
81
- - `lib/milktea/renderer.rb` - Enhanced cursor management for cleaner terminal experience
119
+ - `.rubocop.yml` - Updated target Ruby version to 3.2, cleaned up plugin configuration
120
+
121
+ ### Examples and Documentation
122
+ - `examples/container_layout.rb` - Interactive layout demo with inline dependencies
123
+ - `examples/container_simple.rb` - Basic layout demonstration with inline dependencies
124
+ - `examples/hot_reload_demo.rb` - Updated to use inline gemfile with listen dependency
125
+ - `CLAUDE.md` - Updated Ruby version requirement documentation
126
+
127
+ ### Project Configuration
128
+ - `Gemfile` - Removed development dependencies (ruby-lsp, tty-box, listen)
129
+ - `Gemfile.lock` - Updated dependency tree reflecting removed packages
130
+ - `milktea.gemspec` - Updated minimum Ruby version to 3.2.0
131
+ - `.github/workflows/main.yml` - Updated CI matrix for Ruby 3.2, 3.3, 3.4 testing
@@ -0,0 +1,118 @@
1
+ # Development Log - 2025-07-06
2
+
3
+ ## What's New
4
+
5
+ #### Message::Tick for Timing-Based UI Updates
6
+ Introduced a new message type `Message::Tick` that provides timestamp information for time-dependent UI components. This enables developers to build animations, clocks, loading indicators, and other dynamic interfaces that update based on elapsed time. The Tick message includes a timestamp parameter (defaulting to `Time.now`) and integrates seamlessly into the Elm Architecture message flow.
7
+
8
+ The implementation automatically generates tick messages in the main event loop at 60 FPS, providing consistent timing information to all components. Unlike `Message::None`, tick messages trigger rendering to ensure time-based components update their display properly. This design enables smooth animations and responsive time-dependent interfaces while maintaining the framework's predictable message handling.
9
+
10
+ #### Enhanced Testing Convention Framework
11
+ Established comprehensive testing guidelines that eliminate anti-patterns and enforce consistent test structure across the codebase. The new conventions prioritize testing public behavior over implementation details, making tests more maintainable and robust against refactoring.
12
+
13
+ Key improvements include mandatory use of one-liner `it { ... }` syntax, proper `describe` blocks that reference actual methods, and `context` blocks that always start with "when" for conditional scenarios. These patterns create more readable and maintainable test suites while reducing cognitive overhead for developers working across different components.
14
+
15
+ #### Milktea::Text Component with Dual Mode Architecture
16
+ A comprehensive text display component has been introduced to the Milktea framework, providing developers with sophisticated text rendering capabilities for terminal interfaces. The Text component inherits from Container, enabling it to participate in the flexbox layout system while offering specialized text handling functionality.
17
+
18
+ The component supports two distinct operational modes: wrap mode for traditional line-based text display and truncation mode for space-constrained scenarios. This dual approach addresses the fundamental challenge of text display in terminal interfaces where developers need both readable text flow and precise space management.
19
+
20
+ #### Intelligent Text Wrapping and Overflow Management
21
+ The wrap mode leverages the `strings` gem to provide Unicode-aware text wrapping that respects word boundaries and handles complex character sets including emojis and multi-byte characters. When text exceeds the available height, the component intelligently truncates at line boundaries while preserving content readability.
22
+
23
+ The implementation handles edge cases in text measurement and display, including proper positioning with terminal cursor controls and accurate bounds calculation for nested layout scenarios. This ensures text components integrate seamlessly with the broader Container layout system.
24
+
25
+ ## What's Fixed
26
+
27
+ #### Testing Anti-Pattern Elimination
28
+ Systematically removed testing anti-patterns across all spec files, transforming 18+ multi-line tests into the preferred context + one-liner format. Fixed inappropriate use of `send(:private_method)` and `instance_variable_get(:@var)` that tested implementation details rather than public behavior.
29
+
30
+ The refactoring maintained 100% test coverage while improving readability and maintainability. Tests now focus on observable outcomes through public APIs, making them more resilient to internal refactoring and clearer about component expectations. This establishes a sustainable pattern for future test development.
31
+
32
+ #### Message::Tick Rendering Behavior
33
+ Corrected the initial design where Tick messages were excluded from triggering renders. The fix ensures that time-dependent components can update their display when tick events occur, enabling proper animation and dynamic UI behavior. This change resolved the fundamental issue that would have prevented time-based interfaces from working correctly.
34
+
35
+ The solution involved updating the runtime message handling to treat Tick messages like other render-triggering events while maintaining the optimization that `Message::None` doesn't cause unnecessary redraws. This balanced approach provides the timing functionality developers need while preserving performance for static content.
36
+
37
+ #### Text Positioning and Bounds Integration
38
+ Resolved initial implementation issues where text positioning used hardcoded coordinates instead of respecting the Container bounds system. The fix ensures text components correctly integrate with flexbox layouts and maintain proper positioning when used within complex Container hierarchies.
39
+
40
+ The solution involved implementing proper cursor positioning that respects both absolute coordinates (x, y) and relative positioning within allocated Container space. This enables text components to work correctly in nested layout scenarios and dynamic resizing situations.
41
+
42
+ #### Terminal Screen Clearing Behavior
43
+ Fixed the renderer's screen clearing behavior to use `clear_screen_down` instead of `clear_screen`, preventing the terminal from clearing the entire scrollback history. This change improves user experience by preserving terminal history while still properly clearing the application's display area.
44
+
45
+ The fix addresses the common terminal application issue where applications aggressively clear the entire screen, causing users to lose their command history and previous terminal output. The new approach only clears from the cursor position downward, maintaining the user's terminal session context while ensuring clean application rendering.
46
+
47
+ ## Design Decisions
48
+
49
+ #### Testing Architecture: Public API Focus
50
+ **Context**: The codebase had accumulated testing patterns that relied on accessing private methods and instance variables, creating brittle tests coupled to implementation details.
51
+
52
+ **Decision**: Mandate testing only through public interfaces and observable behavior, with strict prohibition of `send(:private_method)` and `instance_variable_get(:@var)` patterns.
53
+
54
+ **Rationale**: Tests should verify what components do, not how they do it internally. This approach makes tests more maintainable during refactoring, clearer about component contracts, and focused on user-observable behavior. The stricter guidelines prevent future accumulation of implementation-dependent tests while establishing sustainable patterns for the growing codebase.
55
+
56
+ #### Message::Tick Rendering Strategy
57
+ **Context**: Time-based UI components need regular updates to display current state, but the framework optimizes performance by avoiding unnecessary renders.
58
+
59
+ **Decision**: Enable Tick messages to trigger rendering while maintaining the optimization that `Message::None` events don't cause redraws.
60
+
61
+ **Rationale**: Animations, clocks, and loading indicators require visual updates when time progresses, even if no user interaction occurs. Excluding Tick from rendering would break time-dependent interfaces. The selective approach preserves performance optimizations for static content while enabling dynamic time-based behavior where needed. This balances responsiveness with resource efficiency.
62
+
63
+ #### Testing Convention Enforcement
64
+ **Context**: As the codebase grows, inconsistent testing patterns create maintenance overhead and cognitive load for developers working across different components.
65
+
66
+ **Decision**: Enforce standardized test structure with one-liner syntax, method-referencing describe blocks, and "when"-prefixed context blocks.
67
+
68
+ **Rationale**: Consistent patterns reduce the mental overhead of understanding tests and make it easier to identify what functionality is being verified. The one-liner preference encourages clear, focused test cases that verify single behaviors. Standardization enables developers to quickly navigate and understand test intent across the entire framework, improving development velocity and reducing onboarding time.
69
+
70
+ #### Dual Mode Architecture for Text Display
71
+ **Context**: Terminal interfaces require different text handling strategies depending on use case - some scenarios need readable text flow while others prioritize space efficiency and content density.
72
+
73
+ **Decision**: Implement a mode-based architecture with `wrap: false` (truncation) as default and `wrap: true` for traditional wrapping behavior.
74
+
75
+ **Rationale**: The truncation mode addresses the common terminal interface challenge of displaying maximum information in limited space, making it the more universally applicable default. The wrap mode preserves traditional text display expectations for content-focused scenarios. This approach provides developers with explicit control over text behavior while establishing sensible defaults for most TUI applications.
76
+
77
+ #### Documentation Integration Strategy
78
+ **Context**: Testing conventions needed to be preserved and communicated to prevent regression to anti-pattern usage.
79
+
80
+ **Decision**: Update CLAUDE.md with comprehensive anti-pattern prevention guidelines and reorganize existing rules for clarity.
81
+
82
+ **Rationale**: Embedding conventions in the project documentation ensures they persist beyond the current development session and guide future contributors. The prominent placement of anti-pattern warnings helps developers avoid common mistakes while the reorganized structure makes the guidelines more discoverable and actionable.
83
+
84
+ ## Impact
85
+
86
+ The Message::Tick implementation opens up entirely new categories of TUI applications that Milktea can support, including games, monitoring dashboards, and interactive animations. Time-based interfaces are now first-class citizens in the framework, with built-in support for smooth updates and consistent timing.
87
+
88
+ The testing convention improvements establish a sustainable foundation for the project's growth. With clear guidelines preventing anti-patterns and encouraging consistent structure, the codebase can scale without accumulating technical debt in the test suite. Future contributors have clear guidance for writing maintainable tests that focus on behavior rather than implementation.
89
+
90
+ The Text component introduction significantly expands Milktea's capabilities for building sophisticated terminal interfaces. Developers can now create information-dense applications with professional text rendering while maintaining the framework's layout system integration and hot reloading capabilities.
91
+
92
+ The combined effect of these improvements positions Milktea as a more mature and capable TUI framework. The timing system enables dynamic interfaces, the testing framework ensures long-term maintainability, and the text component provides essential UI building blocks. These foundational improvements support more ambitious application development while maintaining the framework's core strengths.
93
+
94
+ ## Files Modified
95
+
96
+ ### Core Framework
97
+ - `lib/milktea/message.rb` - Added timestamp parameter to Message::Tick for timing information
98
+ - `lib/milktea/program.rb` - Integrated tick message generation into main event loop
99
+ - `lib/milktea/runtime.rb` - Simplified side effect handling and enabled Tick rendering
100
+ - `lib/milktea/text.rb` - Complete Text component implementation with dual-mode architecture
101
+ - `lib/milktea/renderer.rb` - Fixed screen clearing behavior to preserve terminal history
102
+ - `milktea.gemspec` - Added strings gem dependency for text manipulation capabilities
103
+
104
+ ### Testing Framework
105
+ - `spec/milktea_spec.rb` - Converted multi-line tests to one-liner format
106
+ - `spec/milktea/application_spec.rb` - Transformed 8 multi-line tests to context + one-liner pattern
107
+ - `spec/milktea/program_spec.rb` - Fixed 4 multi-line tests and removed anti-patterns
108
+ - `spec/milktea/renderer_spec.rb` - Converted multi-line test to one-liner
109
+ - `spec/milktea/runtime_spec.rb` - Fixed instance variable usage and multi-line tests
110
+ - `spec/milktea/message_spec.rb` - Added comprehensive Message::Tick test coverage
111
+
112
+ ### Documentation and Examples
113
+ - `CLAUDE.md` - Enhanced RSpec testing guidelines with anti-pattern prevention
114
+ - `examples/tick_example.rb` - Demonstration of timing-based animation usage
115
+ - `examples/text_demo.rb` - Interactive demonstration of text component modes
116
+
117
+ ### Dependencies
118
+ - `Gemfile.lock` - Updated with strings gem and its dependencies for Unicode text handling
@@ -1,7 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "bundler/setup"
4
+ require "bundler/inline"
5
+
6
+ gemfile do
7
+ source "https://rubygems.org"
8
+ gem "milktea", path: "../"
9
+ gem "tty-box", "~> 0.7.0"
10
+ end
11
+
5
12
  require "milktea"
6
13
  require "tty-box"
7
14
 
@@ -1,7 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "bundler/setup"
4
+ require "bundler/inline"
5
+
6
+ gemfile do
7
+ source "https://rubygems.org"
8
+ gem "milktea", path: "../"
9
+ gem "tty-box", "~> 0.7.0"
10
+ end
11
+
5
12
  require "milktea"
6
13
  require "tty-box"
7
14
 
@@ -1,7 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "bundler/setup"
4
+ require "bundler/inline"
5
+
6
+ gemfile do
7
+ source "https://rubygems.org"
8
+ gem "milktea", path: "../"
9
+ gem "listen"
10
+ end
11
+
5
12
  require "milktea"
6
13
 
7
14
  # Hot Reload Demo using Milktea::Application - Simplified setup
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "milktea"
6
+
7
+ # TextDemo model demonstrating Text component usage
8
+ class TextDemo < Milktea::Container
9
+ direction :column
10
+ child :header, flex: 1
11
+ child :short_text, flex: 2
12
+ child :long_text, flex: 3
13
+ child :truncated_text, flex: 2
14
+ child :unicode_text, flex: 2
15
+ child :footer, flex: 1
16
+
17
+ def header
18
+ HeaderText
19
+ end
20
+
21
+ def short_text
22
+ ShortText
23
+ end
24
+
25
+ def long_text
26
+ LongText
27
+ end
28
+
29
+ def truncated_text
30
+ TruncatedText
31
+ end
32
+
33
+ def unicode_text
34
+ UnicodeText
35
+ end
36
+
37
+ def footer
38
+ FooterText
39
+ end
40
+
41
+ def update(message)
42
+ case message
43
+ when Milktea::Message::KeyPress
44
+ return [self, Milktea::Message::Exit.new] if ["q", "ctrl+c"].include?(message.key)
45
+ When Milktea::Message::Resize
46
+ return [with, Milktea::Message::None.new]
47
+ end
48
+
49
+ [self, Milktea::Message::None.new]
50
+ end
51
+ end
52
+
53
+ # Header text component
54
+ class HeaderText < Milktea::Text
55
+ private
56
+
57
+ def default_state
58
+ {
59
+ content: "Milktea Text Component Demo\nPress 'q' to quit",
60
+ wrap: true
61
+ }
62
+ end
63
+ end
64
+
65
+ # Short text that fits within bounds
66
+ class ShortText < Milktea::Text
67
+ private
68
+
69
+ def default_state
70
+ {
71
+ content: "This is a short text that fits comfortably within the bounds. " \
72
+ "It demonstrates basic text rendering."
73
+ }
74
+ end
75
+ end
76
+
77
+ # Long text that requires wrapping and truncation
78
+ class LongText < Milktea::Text
79
+ private
80
+
81
+ def default_state
82
+ {
83
+ content: "This is a much longer text that will demonstrate the wrapping " \
84
+ "and truncation capabilities of the Text component. When text " \
85
+ "is too long to fit within the specified width, it will " \
86
+ "automatically wrap to the next line. If there are too many " \
87
+ "lines to fit within the height bounds, the last visible line " \
88
+ "will be truncated with an ellipsis. This ensures that text " \
89
+ "always stays within its designated area and doesn't overflow " \
90
+ "into other components. The wrapping is word-aware, so it won't " \
91
+ "break words in the middle unless absolutely necessary.",
92
+ wrap: true
93
+ }
94
+ end
95
+ end
96
+
97
+ # Truncated text demonstrating the new truncation mode
98
+ class TruncatedText < Milktea::Text
99
+ private
100
+
101
+ def default_state
102
+ {
103
+ content: "This demonstrates the new truncation mode (wrap: false). " \
104
+ "Newlines are removed and text is truncated to fit within " \
105
+ "width * height characters. Custom trailing can be set.",
106
+ trailing: "..."
107
+ }
108
+ end
109
+ end
110
+
111
+ # Unicode and emoji text
112
+ class UnicodeText < Milktea::Text
113
+ private
114
+
115
+ def default_state
116
+ {
117
+ content: "Unicode support: 你好世界 🌍🌎🌏\n" \
118
+ "Japanese: ラドクリフ、マラソン五輪代表\n" \
119
+ "Emoji: 🚀 🎉 🔥 ✨ 💻 📱"
120
+ }
121
+ end
122
+ end
123
+
124
+ # Footer text
125
+ class FooterText < Milktea::Text
126
+ private
127
+
128
+ def default_state
129
+ { content: "Text components handle wrapping, truncation, and Unicode!" }
130
+ end
131
+ end
132
+
133
+ # Create and run the program
134
+ model = TextDemo.new
135
+ program = Milktea::Program.new(model)
136
+ program.run
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/milktea"
5
+
6
+ # Example model that uses Tick messages for animations
7
+ class TickModel < Milktea::Model
8
+ def view
9
+ elapsed = last_tick ? Time.now - last_tick : 0
10
+ dots = "." * ((elapsed * 2).to_i % 4)
11
+
12
+ <<~VIEW
13
+ ╭─────────────────────────────────────────────────────────╮
14
+ │ Tick Example │
15
+ ├─────────────────────────────────────────────────────────┤
16
+ │ │
17
+ │ Loading#{dots.ljust(3)} │
18
+ │ │
19
+ │ Last tick: #{last_tick&.strftime("%H:%M:%S.%L") || "none"} │
20
+ │ Elapsed: #{elapsed.round(3)}s │
21
+ │ │
22
+ │ Press 'q' to quit │
23
+ │ │
24
+ ╰─────────────────────────────────────────────────────────╯
25
+ VIEW
26
+ end
27
+
28
+ def update(message)
29
+ case message
30
+ when Milktea::Message::Tick
31
+ # Track the tick timestamp - this demonstrates that tick messages
32
+ # provide timing information that models can use for animations
33
+ [with(last_tick: message.timestamp), Milktea::Message::None.new]
34
+ when Milktea::Message::KeyPress
35
+ if message.key == "q"
36
+ [self, Milktea::Message::Exit.new]
37
+ else
38
+ [self, Milktea::Message::None.new]
39
+ end
40
+ else
41
+ [self, Milktea::Message::None.new]
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def default_state
48
+ { last_tick: nil }
49
+ end
50
+
51
+ def last_tick
52
+ state[:last_tick]
53
+ end
54
+ end
55
+
56
+ # Configure Milktea
57
+ Milktea.configure do |config|
58
+ config.hot_reloading = false
59
+ end
60
+
61
+ # Create and run the tick demonstration
62
+ program = Milktea::Program.new(TickModel.new)
63
+ program.run
@@ -9,8 +9,12 @@ module Milktea
9
9
  # Message to exit the program
10
10
  Exit = Data.define
11
11
 
12
- # Timer tick message
13
- Tick = Data.define
12
+ # Timer tick message with timestamp
13
+ Tick = Data.define(:timestamp) do
14
+ def initialize(timestamp: Time.now)
15
+ super
16
+ end
17
+ end
14
18
 
15
19
  # Keyboard event message
16
20
  KeyPress = Data.define(:key, :value, :ctrl, :alt, :shift) do
@@ -39,6 +39,7 @@ module Milktea
39
39
 
40
40
  def process_messages
41
41
  check_resize
42
+ enqueue(Message::Tick.new)
42
43
  @model = tick(@model)
43
44
  render(@model) if render?
44
45
  end
@@ -22,13 +22,13 @@ module Milktea
22
22
 
23
23
  def setup_screen
24
24
  @output.print @cursor.hide
25
- @output.print @cursor.clear_screen
25
+ @output.print @cursor.clear_screen_down
26
26
  @output.print @cursor.move_to(0, 0)
27
27
  @output.flush
28
28
  end
29
29
 
30
30
  def restore_screen
31
- @output.print @cursor.clear_screen
31
+ @output.print @cursor.clear_screen_down
32
32
  @output.print @cursor.show
33
33
  @output.flush
34
34
  end
@@ -53,18 +53,10 @@ module Milktea
53
53
 
54
54
  def execute_side_effect(side_effect)
55
55
  case side_effect
56
- when Message::None
57
- # Do nothing
58
56
  when Message::Exit
59
57
  stop
60
58
  when Message::Batch
61
59
  side_effect.messages.each { |msg| enqueue(msg) }
62
- when Message::Reload
63
- # Hot reload handled automatically by Zeitwerk
64
- # No additional action needed
65
- when Message::Resize
66
- # Terminal resize detected
67
- # No additional action needed at this level
68
60
  end
69
61
  end
70
62
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "strings"
4
+ require "tty-cursor"
5
+
6
+ module Milktea
7
+ # Text component for displaying text content with wrapping and truncation
8
+ class Text < Container
9
+ def view
10
+ return "" if content.empty?
11
+ return render(truncated_lines) if state[:wrap]
12
+
13
+ render_truncated
14
+ end
15
+
16
+ private
17
+
18
+ def render(lines)
19
+ lines.map.with_index do |line, index|
20
+ TTY::Cursor.move_to(bounds.x, bounds.y + index) + line
21
+ end.join
22
+ end
23
+
24
+ def render_truncated
25
+ cleaned_content = content.gsub("\n", "")
26
+ max_length = bounds.width * bounds.height
27
+ truncated_content = Strings.truncate(cleaned_content, max_length, trailing: state[:trailing])
28
+
29
+ TTY::Cursor.move_to(bounds.x, bounds.y) + truncated_content
30
+ end
31
+
32
+ def truncated_lines
33
+ lines = wrap_content
34
+ return lines unless lines.length > bounds.height
35
+
36
+ lines.take(bounds.height)
37
+ end
38
+
39
+ def wrap_content
40
+ Strings.wrap(content, bounds.width).split("\n")
41
+ end
42
+
43
+ def content
44
+ state[:content] || ""
45
+ end
46
+
47
+ def default_state
48
+ { content: "", wrap: false, trailing: "…" }.freeze
49
+ end
50
+ end
51
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Milktea
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -0,0 +1,10 @@
1
+ {
2
+ "release-type": "ruby",
3
+ "packages": {
4
+ ".": {
5
+ "package-name": "milktea",
6
+ "include-component-in-tag": false,
7
+ "release-type": "ruby"
8
+ }
9
+ }
10
+ }
metadata CHANGED
@@ -1,15 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: milktea
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
  - Aotokitsuruya
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-07-05 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: strings
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.2'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.2'
13
26
  - !ruby/object:Gem::Dependency
14
27
  name: timers
15
28
  requirement: !ruby/object:Gem::Requirement
@@ -89,9 +102,11 @@ extra_rdoc_files: []
89
102
  files:
90
103
  - ".claude/commands/devlog.md"
91
104
  - ".claude/settings.json"
105
+ - ".release-please-manifest.json"
92
106
  - ".rspec"
93
107
  - ".rubocop.yml"
94
108
  - ARCHITECTURE.md
109
+ - CHANGELOG.md
95
110
  - CLAUDE.md
96
111
  - LICENSE.txt
97
112
  - README.md
@@ -100,6 +115,7 @@ files:
100
115
  - docs/devlog/20250704-2.md
101
116
  - docs/devlog/20250704.md
102
117
  - docs/devlog/20250705.md
118
+ - docs/devlog/20250706.md
103
119
  - examples/container_layout.rb
104
120
  - examples/container_simple.rb
105
121
  - examples/counter.rb
@@ -108,6 +124,8 @@ files:
108
124
  - examples/hot_reload_demo/models/demo_model.rb
109
125
  - examples/hot_reload_demo/models/status_model.rb
110
126
  - examples/simple.rb
127
+ - examples/text_demo.rb
128
+ - examples/tick_example.rb
111
129
  - lib/milktea.rb
112
130
  - lib/milktea/application.rb
113
131
  - lib/milktea/bounds.rb
@@ -119,7 +137,9 @@ files:
119
137
  - lib/milktea/program.rb
120
138
  - lib/milktea/renderer.rb
121
139
  - lib/milktea/runtime.rb
140
+ - lib/milktea/text.rb
122
141
  - lib/milktea/version.rb
142
+ - release-please-config.json
123
143
  - sig/milktea.rbs
124
144
  homepage: https://github.com/elct9620/milktea
125
145
  licenses:
@@ -129,7 +149,6 @@ metadata:
129
149
  source_code_uri: https://github.com/elct9620/milktea
130
150
  changelog_uri: https://github.com/elct9620/milktea/blob/main/CHANGELOG.md
131
151
  rubygems_mfa_required: 'true'
132
- post_install_message:
133
152
  rdoc_options: []
134
153
  require_paths:
135
154
  - lib
@@ -137,15 +156,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
137
156
  requirements:
138
157
  - - ">="
139
158
  - !ruby/object:Gem::Version
140
- version: 3.1.0
159
+ version: 3.2.0
141
160
  required_rubygems_version: !ruby/object:Gem::Requirement
142
161
  requirements:
143
162
  - - ">="
144
163
  - !ruby/object:Gem::Version
145
164
  version: '0'
146
165
  requirements: []
147
- rubygems_version: 3.5.22
148
- signing_key:
166
+ rubygems_version: 3.6.7
149
167
  specification_version: 4
150
168
  summary: The TUI framework for Ruby, inspired by the bubbletea framework for Go.
151
169
  test_files: []