anima-core 0.3.0 → 1.0.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 +4 -4
- data/.reek.yml +27 -1
- data/CHANGELOG.md +4 -0
- data/README.md +219 -25
- data/agents/codebase-analyzer.md +88 -0
- data/agents/codebase-pattern-finder.md +83 -0
- data/agents/documentation-researcher.md +59 -0
- data/agents/thoughts-analyzer.md +102 -0
- data/agents/web-search-researcher.md +71 -0
- data/anima-core.gemspec +3 -0
- data/app/channels/session_channel.rb +76 -28
- data/app/jobs/agent_request_job.rb +24 -0
- data/app/jobs/analytical_brain_job.rb +33 -0
- data/app/jobs/count_event_tokens_job.rb +1 -1
- data/app/models/concerns/event/broadcasting.rb +20 -2
- data/app/models/event.rb +1 -1
- data/app/models/goal.rb +91 -0
- data/app/models/session.rb +347 -22
- data/config/application.rb +2 -0
- data/db/migrate/20260314075248_add_subagent_support_to_sessions.rb +6 -0
- data/db/migrate/20260314112417_add_granted_tools_to_sessions.rb +5 -0
- data/db/migrate/20260314140000_add_name_to_sessions.rb +7 -0
- data/db/migrate/20260314150000_add_viewport_event_ids_to_sessions.rb +7 -0
- data/db/migrate/20260315100000_add_active_skills_to_sessions.rb +7 -0
- data/db/migrate/20260315140843_create_goals.rb +16 -0
- data/db/migrate/20260315144837_add_completed_at_to_goals.rb +5 -0
- data/db/migrate/20260315191105_add_active_workflow_to_sessions.rb +5 -0
- data/lib/agent_loop.rb +65 -9
- data/lib/agents/definition.rb +116 -0
- data/lib/agents/registry.rb +106 -0
- data/lib/analytical_brain/runner.rb +276 -0
- data/lib/analytical_brain/tools/activate_skill.rb +52 -0
- data/lib/analytical_brain/tools/deactivate_skill.rb +43 -0
- data/lib/analytical_brain/tools/deactivate_workflow.rb +34 -0
- data/lib/analytical_brain/tools/everything_is_ready.rb +28 -0
- data/lib/analytical_brain/tools/finish_goal.rb +62 -0
- data/lib/analytical_brain/tools/read_workflow.rb +58 -0
- data/lib/analytical_brain/tools/rename_session.rb +63 -0
- data/lib/analytical_brain/tools/set_goal.rb +60 -0
- data/lib/analytical_brain/tools/update_goal.rb +60 -0
- data/lib/analytical_brain.rb +23 -0
- data/lib/anima/cli/mcp/secrets.rb +76 -0
- data/lib/anima/cli/mcp.rb +197 -0
- data/lib/anima/cli.rb +4 -0
- data/lib/anima/installer.rb +168 -0
- data/lib/anima/settings.rb +226 -0
- data/lib/anima/version.rb +1 -1
- data/lib/anima.rb +9 -0
- data/lib/credential_store.rb +103 -0
- data/lib/environment_probe.rb +232 -0
- data/lib/llm/client.rb +29 -10
- data/lib/mcp/client_manager.rb +86 -0
- data/lib/mcp/config.rb +213 -0
- data/lib/mcp/health_check.rb +77 -0
- data/lib/mcp/secrets.rb +73 -0
- data/lib/mcp/stdio_transport.rb +206 -0
- data/lib/providers/anthropic.rb +8 -7
- data/lib/shell_session.rb +11 -10
- data/lib/skills/definition.rb +97 -0
- data/lib/skills/registry.rb +105 -0
- data/lib/tools/edit.rb +3 -4
- data/lib/tools/mcp_tool.rb +114 -0
- data/lib/tools/read.rb +15 -16
- data/lib/tools/registry.rb +14 -12
- data/lib/tools/request_feature.rb +121 -0
- data/lib/tools/return_result.rb +81 -0
- data/lib/tools/spawn_specialist.rb +109 -0
- data/lib/tools/spawn_subagent.rb +111 -0
- data/lib/tools/subagent_prompts.rb +12 -0
- data/lib/tools/web_get.rb +8 -9
- data/lib/tui/app.rb +332 -43
- data/lib/tui/message_store.rb +20 -0
- data/lib/tui/screens/chat.rb +207 -20
- data/lib/workflows/definition.rb +97 -0
- data/lib/workflows/registry.rb +89 -0
- data/skills/activerecord/SKILL.md +255 -0
- data/skills/activerecord/examples/associations/association_extensions.rb +298 -0
- data/skills/activerecord/examples/associations/basic_associations.rb +118 -0
- data/skills/activerecord/examples/associations/counter_caches.rb +215 -0
- data/skills/activerecord/examples/associations/polymorphic_associations.rb +217 -0
- data/skills/activerecord/examples/associations/self_referential.rb +302 -0
- data/skills/activerecord/examples/associations/through_associations.rb +203 -0
- data/skills/activerecord/examples/basics/crud_operations.rb +209 -0
- data/skills/activerecord/examples/basics/dirty_tracking.rb +218 -0
- data/skills/activerecord/examples/basics/inheritance.rb +377 -0
- data/skills/activerecord/examples/basics/type_casting.rb +317 -0
- data/skills/activerecord/examples/callbacks/alternatives_to_callbacks.rb +447 -0
- data/skills/activerecord/examples/callbacks/conditional_callbacks.rb +353 -0
- data/skills/activerecord/examples/callbacks/lifecycle_callbacks.rb +280 -0
- data/skills/activerecord/examples/callbacks/transaction_callbacks.rb +340 -0
- data/skills/activerecord/examples/migrations/indexes_and_constraints.rb +337 -0
- data/skills/activerecord/examples/migrations/reversible_patterns.rb +403 -0
- data/skills/activerecord/examples/migrations/safe_patterns.rb +420 -0
- data/skills/activerecord/examples/migrations/schema_changes.rb +277 -0
- data/skills/activerecord/examples/querying/batch_processing.rb +226 -0
- data/skills/activerecord/examples/querying/eager_loading.rb +259 -0
- data/skills/activerecord/examples/querying/finder_methods.rb +170 -0
- data/skills/activerecord/examples/querying/optimization.rb +275 -0
- data/skills/activerecord/examples/querying/scopes.rb +260 -0
- data/skills/activerecord/examples/validations/built_in_validators.rb +277 -0
- data/skills/activerecord/examples/validations/conditional_validations.rb +288 -0
- data/skills/activerecord/examples/validations/custom_validators.rb +381 -0
- data/skills/activerecord/examples/validations/database_constraints.rb +432 -0
- data/skills/activerecord/examples/validations/validation_contexts.rb +367 -0
- data/skills/activerecord/references/associations.md +709 -0
- data/skills/activerecord/references/basics.md +622 -0
- data/skills/activerecord/references/callbacks.md +738 -0
- data/skills/activerecord/references/migrations.md +657 -0
- data/skills/activerecord/references/querying.md +655 -0
- data/skills/activerecord/references/validations.md +596 -0
- data/skills/dragonruby/SKILL.md +250 -0
- data/skills/dragonruby/examples/audio/audio_events.rb +55 -0
- data/skills/dragonruby/examples/audio/background_music.rb +29 -0
- data/skills/dragonruby/examples/audio/crossfade.rb +51 -0
- data/skills/dragonruby/examples/audio/music_controls.rb +51 -0
- data/skills/dragonruby/examples/audio/sound_effects.rb +30 -0
- data/skills/dragonruby/examples/core/coordinate_system.rb +27 -0
- data/skills/dragonruby/examples/core/hello_world.rb +24 -0
- data/skills/dragonruby/examples/core/labels.rb +22 -0
- data/skills/dragonruby/examples/core/sprites.rb +35 -0
- data/skills/dragonruby/examples/core/state_management.rb +29 -0
- data/skills/dragonruby/examples/distribution/background_pause.rb +42 -0
- data/skills/dragonruby/examples/distribution/build_workflow.sh +26 -0
- data/skills/dragonruby/examples/distribution/cvars_production.txt +16 -0
- data/skills/dragonruby/examples/distribution/game_metadata_hd.txt +23 -0
- data/skills/dragonruby/examples/distribution/game_metadata_minimal.txt +9 -0
- data/skills/dragonruby/examples/distribution/game_metadata_mobile.txt +31 -0
- data/skills/dragonruby/examples/distribution/platform_detection.rb +36 -0
- data/skills/dragonruby/examples/distribution/steam_metadata.txt +19 -0
- data/skills/dragonruby/examples/entities/collision_detection.rb +43 -0
- data/skills/dragonruby/examples/entities/entity_lifecycle.rb +68 -0
- data/skills/dragonruby/examples/entities/entity_storage.rb +38 -0
- data/skills/dragonruby/examples/entities/factory_methods.rb +45 -0
- data/skills/dragonruby/examples/entities/random_spawning.rb +50 -0
- data/skills/dragonruby/examples/game-logic/reset_patterns.rb +98 -0
- data/skills/dragonruby/examples/game-logic/save_load.rb +101 -0
- data/skills/dragonruby/examples/game-logic/scoring.rb +104 -0
- data/skills/dragonruby/examples/game-logic/state_transitions.rb +103 -0
- data/skills/dragonruby/examples/game-logic/timers.rb +87 -0
- data/skills/dragonruby/examples/input/action_triggers.rb +36 -0
- data/skills/dragonruby/examples/input/analog_movement.rb +28 -0
- data/skills/dragonruby/examples/input/controller_input.rb +28 -0
- data/skills/dragonruby/examples/input/directional_input.rb +24 -0
- data/skills/dragonruby/examples/input/keyboard_input.rb +28 -0
- data/skills/dragonruby/examples/input/mouse_click.rb +26 -0
- data/skills/dragonruby/examples/input/movement_with_bounds.rb +22 -0
- data/skills/dragonruby/examples/input/normalized_movement.rb +32 -0
- data/skills/dragonruby/examples/rendering/frame_animation.rb +32 -0
- data/skills/dragonruby/examples/rendering/labels.rb +32 -0
- data/skills/dragonruby/examples/rendering/layering.rb +51 -0
- data/skills/dragonruby/examples/rendering/solids.rb +61 -0
- data/skills/dragonruby/examples/rendering/sprites.rb +33 -0
- data/skills/dragonruby/examples/rendering/spritesheet_animation.rb +39 -0
- data/skills/dragonruby/examples/scenes/case_dispatch.rb +60 -0
- data/skills/dragonruby/examples/scenes/class_based.rb +150 -0
- data/skills/dragonruby/examples/scenes/pause_overlay.rb +100 -0
- data/skills/dragonruby/examples/scenes/safe_transitions.rb +68 -0
- data/skills/dragonruby/examples/scenes/scene_transitions.rb +98 -0
- data/skills/dragonruby/examples/scenes/send_dispatch.rb +88 -0
- data/skills/dragonruby/references/audio.md +396 -0
- data/skills/dragonruby/references/core.md +385 -0
- data/skills/dragonruby/references/distribution.md +434 -0
- data/skills/dragonruby/references/entities.md +516 -0
- data/skills/dragonruby/references/game-logic/persistence.md +386 -0
- data/skills/dragonruby/references/game-logic/state.md +389 -0
- data/skills/dragonruby/references/input.md +414 -0
- data/skills/dragonruby/references/rendering/animation.md +467 -0
- data/skills/dragonruby/references/rendering/primitives.md +403 -0
- data/skills/dragonruby/references/scenes.md +443 -0
- data/skills/draper-decorators/SKILL.md +344 -0
- data/skills/draper-decorators/examples/application_decorator.rb +61 -0
- data/skills/draper-decorators/examples/decorator_spec.rb +253 -0
- data/skills/draper-decorators/examples/model_decorator.rb +152 -0
- data/skills/draper-decorators/references/anti-patterns.md +640 -0
- data/skills/draper-decorators/references/patterns.md +507 -0
- data/skills/draper-decorators/references/testing.md +559 -0
- data/skills/gh-issue.md +182 -0
- data/skills/mcp-server/SKILL.md +177 -0
- data/skills/mcp-server/examples/dynamic_tools.rb +36 -0
- data/skills/mcp-server/examples/file_manager_tool.rb +85 -0
- data/skills/mcp-server/examples/http_client.rb +48 -0
- data/skills/mcp-server/examples/http_server.rb +97 -0
- data/skills/mcp-server/examples/rails_integration.rb +88 -0
- data/skills/mcp-server/examples/stdio_server.rb +108 -0
- data/skills/mcp-server/examples/streaming_client.rb +95 -0
- data/skills/mcp-server/references/gotchas.md +183 -0
- data/skills/mcp-server/references/prompts.md +98 -0
- data/skills/mcp-server/references/resources.md +53 -0
- data/skills/mcp-server/references/server.md +140 -0
- data/skills/mcp-server/references/tools.md +146 -0
- data/skills/mcp-server/references/transport.md +104 -0
- data/skills/ratatui-ruby/SKILL.md +315 -0
- data/skills/ratatui-ruby/references/core-concepts.md +340 -0
- data/skills/ratatui-ruby/references/events.md +387 -0
- data/skills/ratatui-ruby/references/frameworks.md +522 -0
- data/skills/ratatui-ruby/references/layout.md +423 -0
- data/skills/ratatui-ruby/references/styling.md +268 -0
- data/skills/ratatui-ruby/references/testing.md +433 -0
- data/skills/ratatui-ruby/references/widgets.md +532 -0
- data/skills/rspec/SKILL.md +340 -0
- data/skills/rspec/examples/core/basic_structure.rb +69 -0
- data/skills/rspec/examples/core/configuration.rb +126 -0
- data/skills/rspec/examples/core/hooks.rb +126 -0
- data/skills/rspec/examples/core/memoized_helpers.rb +139 -0
- data/skills/rspec/examples/core/metadata_filtering.rb +144 -0
- data/skills/rspec/examples/core/shared_examples.rb +145 -0
- data/skills/rspec/examples/factory_bot/associations.rb +314 -0
- data/skills/rspec/examples/factory_bot/build_strategies.rb +272 -0
- data/skills/rspec/examples/factory_bot/callbacks.rb +320 -0
- data/skills/rspec/examples/factory_bot/custom_construction.rb +328 -0
- data/skills/rspec/examples/factory_bot/factory_definition.rb +191 -0
- data/skills/rspec/examples/factory_bot/inheritance.rb +314 -0
- data/skills/rspec/examples/factory_bot/traits.rb +293 -0
- data/skills/rspec/examples/factory_bot/transients.rb +229 -0
- data/skills/rspec/examples/matchers/change.rb +115 -0
- data/skills/rspec/examples/matchers/collections.rb +154 -0
- data/skills/rspec/examples/matchers/comparisons.rb +79 -0
- data/skills/rspec/examples/matchers/composing.rb +155 -0
- data/skills/rspec/examples/matchers/custom_matchers.rb +197 -0
- data/skills/rspec/examples/matchers/equality.rb +58 -0
- data/skills/rspec/examples/matchers/errors.rb +136 -0
- data/skills/rspec/examples/matchers/output.rb +103 -0
- data/skills/rspec/examples/matchers/predicates.rb +87 -0
- data/skills/rspec/examples/matchers/truthiness.rb +101 -0
- data/skills/rspec/examples/matchers/types.rb +82 -0
- data/skills/rspec/examples/matchers/yield.rb +147 -0
- data/skills/rspec/examples/mocks/any_instance.rb +172 -0
- data/skills/rspec/examples/mocks/argument_matchers.rb +206 -0
- data/skills/rspec/examples/mocks/constants.rb +177 -0
- data/skills/rspec/examples/mocks/doubles.rb +139 -0
- data/skills/rspec/examples/mocks/expectations.rb +137 -0
- data/skills/rspec/examples/mocks/message_chains.rb +173 -0
- data/skills/rspec/examples/mocks/ordering.rb +144 -0
- data/skills/rspec/examples/mocks/receive_counts.rb +181 -0
- data/skills/rspec/examples/mocks/responses.rb +223 -0
- data/skills/rspec/examples/mocks/spies.rb +149 -0
- data/skills/rspec/examples/mocks/stubbing.rb +133 -0
- data/skills/rspec/examples/rails/channels.rb +250 -0
- data/skills/rspec/examples/rails/controller_specs.rb +302 -0
- data/skills/rspec/examples/rails/helper_specs.rb +245 -0
- data/skills/rspec/examples/rails/job_specs.rb +256 -0
- data/skills/rspec/examples/rails/mailer_specs.rb +228 -0
- data/skills/rspec/examples/rails/matchers.rb +374 -0
- data/skills/rspec/examples/rails/model_specs.rb +193 -0
- data/skills/rspec/examples/rails/request_specs.rb +275 -0
- data/skills/rspec/examples/rails/routing_specs.rb +276 -0
- data/skills/rspec/examples/rails/system_specs.rb +294 -0
- data/skills/rspec/examples/rails/transactions.rb +254 -0
- data/skills/rspec/examples/rails/view_specs.rb +252 -0
- data/skills/rspec/references/core.md +816 -0
- data/skills/rspec/references/factory_bot.md +641 -0
- data/skills/rspec/references/matchers.md +516 -0
- data/skills/rspec/references/mocks.md +381 -0
- data/skills/rspec/references/rails.md +528 -0
- data/templates/soul.md +40 -0
- data/workflows/commit.md +45 -0
- data/workflows/create_handoff.md +98 -0
- data/workflows/create_note.md +82 -0
- data/workflows/create_plan.md +457 -0
- data/workflows/decompose_ticket.md +109 -0
- data/workflows/feature.md +91 -0
- data/workflows/implement_plan.md +87 -0
- data/workflows/iterate_plan.md +247 -0
- data/workflows/research_codebase.md +210 -0
- data/workflows/resume_handoff.md +217 -0
- data/workflows/review_pr.md +320 -0
- data/workflows/thoughts_init.md +71 -0
- data/workflows/validate_plan.md +166 -0
- metadata +284 -1
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
# Widgets
|
|
2
|
+
|
|
3
|
+
RatatuiRuby provides 20+ widgets for building terminal interfaces. This reference covers the complete widget catalog, composition patterns, and state management.
|
|
4
|
+
|
|
5
|
+
## Widget Architecture
|
|
6
|
+
|
|
7
|
+
Widgets follow structural typing: any object implementing `render(area)` functions as a widget. The method receives a `Rect` and returns drawing commands.
|
|
8
|
+
|
|
9
|
+
### Stateless vs Stateful
|
|
10
|
+
|
|
11
|
+
**Stateless widgets** render based on initialization parameters:
|
|
12
|
+
- Paragraph, Block, Gauge, LineGauge, BarChart, Sparkline, Tabs, Calendar, Canvas
|
|
13
|
+
|
|
14
|
+
**Stateful widgets** require external state objects:
|
|
15
|
+
- List → `ListState`
|
|
16
|
+
- Table → `TableState`
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
# Stateless
|
|
20
|
+
frame.render_widget(tui.paragraph(text: "Hello"), area)
|
|
21
|
+
|
|
22
|
+
# Stateful
|
|
23
|
+
@list_state = tui.list_state(0)
|
|
24
|
+
frame.render_stateful_widget(tui.list(items:), area, @list_state)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Block (Universal Container)
|
|
28
|
+
|
|
29
|
+
Foundation for composition. Provides borders, titles, and padding.
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
Block.new(
|
|
33
|
+
title: nil, # Main title
|
|
34
|
+
titles: [], # Additional titles [{content:, position:, alignment:}]
|
|
35
|
+
title_alignment: nil, # :left, :center, :right (nil = :left)
|
|
36
|
+
title_style: nil,
|
|
37
|
+
borders: [:all], # :top, :bottom, :left, :right, :all
|
|
38
|
+
border_style: nil,
|
|
39
|
+
border_type: nil, # :plain, :rounded, :double, :thick, :quadrant
|
|
40
|
+
border_set: nil, # Custom characters
|
|
41
|
+
style: nil, # Content area style
|
|
42
|
+
padding: 0, # Integer or [left, right, top, bottom]
|
|
43
|
+
children: [] # Child widgets to render inside
|
|
44
|
+
)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Border Options
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
Block.new(borders: [:top, :bottom]) # Horizontal only
|
|
51
|
+
Block.new(borders: [:left, :right]) # Vertical only
|
|
52
|
+
Block.new(borders: [:all]) # All sides
|
|
53
|
+
|
|
54
|
+
# Border types
|
|
55
|
+
Block.new(border_type: :rounded) # Rounded corners
|
|
56
|
+
Block.new(border_type: :double) # Double lines
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Inner Area
|
|
60
|
+
|
|
61
|
+
Calculate content area after borders/padding:
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
block = Block.new(borders: [:all], padding: 1)
|
|
65
|
+
inner_rect = block.inner(outer_rect)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Multiple Titles
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
tui.block(
|
|
72
|
+
title: "Main",
|
|
73
|
+
titles: [
|
|
74
|
+
{content: "Help: ?", position: :bottom, alignment: :right},
|
|
75
|
+
{content: "v1.0", position: :top, alignment: :right}
|
|
76
|
+
],
|
|
77
|
+
borders: [:all]
|
|
78
|
+
)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Text Display
|
|
82
|
+
|
|
83
|
+
### Paragraph
|
|
84
|
+
|
|
85
|
+
Text display with wrapping, alignment, and scrolling.
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
Paragraph.new(
|
|
89
|
+
text:, # String or Text::Line array
|
|
90
|
+
style: nil,
|
|
91
|
+
block: nil,
|
|
92
|
+
wrap: false, # Enable wrapping
|
|
93
|
+
alignment: :left, # :left, :center, :right
|
|
94
|
+
scroll: [0, 0] # [vertical, horizontal] offsets
|
|
95
|
+
)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Methods:**
|
|
99
|
+
- `line_count(width)` — Lines needed at width
|
|
100
|
+
- `line_width()` — Minimum width without wrapping
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
tui.paragraph(
|
|
104
|
+
text: "Long content...",
|
|
105
|
+
wrap: true,
|
|
106
|
+
scroll: [@scroll_y, 0],
|
|
107
|
+
block: tui.block(title: "Output", borders: [:all])
|
|
108
|
+
)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## List Widgets
|
|
112
|
+
|
|
113
|
+
### List
|
|
114
|
+
|
|
115
|
+
Selectable item list with automatic scrolling.
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
List.new(
|
|
119
|
+
items: [], # Strings, Spans, Lines, or ListItems
|
|
120
|
+
selected_index: nil, # Current selection
|
|
121
|
+
offset: nil, # Manual scroll (nil = auto)
|
|
122
|
+
style: nil,
|
|
123
|
+
highlight_style: nil, # Selected item style
|
|
124
|
+
highlight_symbol: "> ", # Selection prefix
|
|
125
|
+
repeat_highlight_symbol: false,
|
|
126
|
+
highlight_spacing: :when_selected, # :always, :when_selected, :never
|
|
127
|
+
direction: :top_to_bottom, # :top_to_bottom, :bottom_to_top
|
|
128
|
+
scroll_padding: nil, # Margin around selection
|
|
129
|
+
block: nil
|
|
130
|
+
)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Methods:**
|
|
134
|
+
- `empty?()` — No items
|
|
135
|
+
- `len()` / `length()` / `size()` — Item count
|
|
136
|
+
- `selected?()` — Item selected
|
|
137
|
+
- `selected_item()` — Current item or nil
|
|
138
|
+
|
|
139
|
+
**Auto-scroll vs Manual:**
|
|
140
|
+
- `offset: nil` — Auto-scroll to keep selection visible
|
|
141
|
+
- `offset: N` — Manual scroll (for log viewers)
|
|
142
|
+
|
|
143
|
+
### ListState
|
|
144
|
+
|
|
145
|
+
External state for complex applications:
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
@list_state = tui.list_state(0) # positional: initial selected index
|
|
149
|
+
|
|
150
|
+
# In draw
|
|
151
|
+
frame.render_stateful_widget(list, area, @list_state)
|
|
152
|
+
|
|
153
|
+
# Navigation
|
|
154
|
+
@list_state.select_next
|
|
155
|
+
@list_state.select_previous
|
|
156
|
+
@list_state.select_first
|
|
157
|
+
@list_state.select_last
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Table
|
|
161
|
+
|
|
162
|
+
Structured data with row/column selection.
|
|
163
|
+
|
|
164
|
+
```ruby
|
|
165
|
+
Table.new(
|
|
166
|
+
header: nil, # Array of strings/Text/Paragraphs
|
|
167
|
+
rows: [], # 2D array of cells
|
|
168
|
+
widths: [], # Constraints or Integers
|
|
169
|
+
row_highlight_style: nil,
|
|
170
|
+
highlight_symbol: "> ",
|
|
171
|
+
highlight_spacing: :when_selected,
|
|
172
|
+
column_highlight_style: nil,
|
|
173
|
+
cell_highlight_style: nil, # When row AND column selected
|
|
174
|
+
selected_row: nil,
|
|
175
|
+
selected_column: nil,
|
|
176
|
+
offset: nil,
|
|
177
|
+
block: nil,
|
|
178
|
+
footer: nil,
|
|
179
|
+
flex: :legacy, # :start, :center, :end, :space_between
|
|
180
|
+
style: nil,
|
|
181
|
+
column_spacing: 1
|
|
182
|
+
)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Methods:**
|
|
186
|
+
- `row_selected?()` — Row selected
|
|
187
|
+
- `column_selected?()` — Column selected
|
|
188
|
+
- `cell_selected?()` — Both selected
|
|
189
|
+
- `empty?()` — No rows
|
|
190
|
+
|
|
191
|
+
**Cell Types:** String, Text::Span, Text::Line, Paragraph, or Cell objects.
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
tui.table(
|
|
195
|
+
header: ["Name", "Status", "CPU"],
|
|
196
|
+
rows: [
|
|
197
|
+
["web-1", "Running", "45%"],
|
|
198
|
+
["db-1", "Running", "22%"]
|
|
199
|
+
],
|
|
200
|
+
widths: [
|
|
201
|
+
tui.constraint(:length, 20),
|
|
202
|
+
tui.constraint(:length, 10),
|
|
203
|
+
tui.constraint(:min, 5)
|
|
204
|
+
],
|
|
205
|
+
block: tui.block(title: "Services", borders: [:all])
|
|
206
|
+
)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Progress Widgets
|
|
210
|
+
|
|
211
|
+
### Gauge
|
|
212
|
+
|
|
213
|
+
Progress bar with percentage/ratio display.
|
|
214
|
+
|
|
215
|
+
```ruby
|
|
216
|
+
Gauge.new(
|
|
217
|
+
ratio: nil, # Float 0.0-1.0 (exclusive with percent)
|
|
218
|
+
percent: nil, # Integer 0-100
|
|
219
|
+
label: nil, # Overlay text
|
|
220
|
+
style: nil, # Background
|
|
221
|
+
gauge_style: nil, # Filled bar
|
|
222
|
+
block: nil,
|
|
223
|
+
use_unicode: true
|
|
224
|
+
)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Methods:**
|
|
228
|
+
- `filled?()` — ratio > 0
|
|
229
|
+
- `complete?()` — ratio >= 1.0
|
|
230
|
+
- `percent()` — Integer percentage
|
|
231
|
+
|
|
232
|
+
```ruby
|
|
233
|
+
tui.gauge(
|
|
234
|
+
ratio: 0.65,
|
|
235
|
+
label: "Downloading...",
|
|
236
|
+
gauge_style: {fg: "green"},
|
|
237
|
+
block: tui.block(title: "Progress", borders: [:all])
|
|
238
|
+
)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### LineGauge
|
|
242
|
+
|
|
243
|
+
Compact single-line progress.
|
|
244
|
+
|
|
245
|
+
```ruby
|
|
246
|
+
LineGauge.new(
|
|
247
|
+
ratio: nil, # Float (0.0 - 1.0)
|
|
248
|
+
percent: nil, # Integer (0 - 100), alternative to ratio
|
|
249
|
+
label: nil, # Overlay text
|
|
250
|
+
style: nil,
|
|
251
|
+
filled_style: nil,
|
|
252
|
+
unfilled_style: nil,
|
|
253
|
+
block: nil,
|
|
254
|
+
filled_symbol: "█",
|
|
255
|
+
unfilled_symbol: "░"
|
|
256
|
+
)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Chart Widgets
|
|
260
|
+
|
|
261
|
+
### Chart
|
|
262
|
+
|
|
263
|
+
Cartesian plotting with datasets and axes.
|
|
264
|
+
|
|
265
|
+
```ruby
|
|
266
|
+
Chart.new(
|
|
267
|
+
datasets: [], # Array of Dataset objects
|
|
268
|
+
x_axis: nil, # Axis configuration
|
|
269
|
+
y_axis: nil,
|
|
270
|
+
legend_position: nil, # Corner or nil to hide
|
|
271
|
+
marker: :braille, # :braille, :dot, :block, :bar
|
|
272
|
+
style: nil,
|
|
273
|
+
block: nil
|
|
274
|
+
)
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### BarChart
|
|
278
|
+
|
|
279
|
+
Categorical data as bars.
|
|
280
|
+
|
|
281
|
+
```ruby
|
|
282
|
+
BarChart.new(
|
|
283
|
+
data:, # Hash, Array, or BarGroup list
|
|
284
|
+
bar_width: 3,
|
|
285
|
+
bar_gap: 1,
|
|
286
|
+
group_gap: 0,
|
|
287
|
+
max: nil, # Y-axis max (auto if nil)
|
|
288
|
+
style: nil,
|
|
289
|
+
block: nil,
|
|
290
|
+
direction: :vertical, # :vertical, :horizontal
|
|
291
|
+
label_style: nil,
|
|
292
|
+
value_style: nil,
|
|
293
|
+
bar_set: nil
|
|
294
|
+
)
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Data Formats:**
|
|
298
|
+
|
|
299
|
+
```ruby
|
|
300
|
+
# Hash
|
|
301
|
+
{"Apples" => 10, "Oranges" => 15}
|
|
302
|
+
|
|
303
|
+
# Array of tuples
|
|
304
|
+
[["Mon", 20], ["Tue", 30]]
|
|
305
|
+
|
|
306
|
+
# With styling
|
|
307
|
+
[["Label", 42, {fg: "red"}]]
|
|
308
|
+
|
|
309
|
+
# Grouped
|
|
310
|
+
[BarGroup.new(label: "Q1", bars: [Bar.new(10), Bar.new(15)])]
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Sparkline
|
|
314
|
+
|
|
315
|
+
Compact trend visualization.
|
|
316
|
+
|
|
317
|
+
```ruby
|
|
318
|
+
Sparkline.new(
|
|
319
|
+
data:, # Array of Integers or nil
|
|
320
|
+
max: nil,
|
|
321
|
+
style: nil,
|
|
322
|
+
block: nil,
|
|
323
|
+
direction: :left_to_right,
|
|
324
|
+
absent_value_symbol: nil, # For nil values
|
|
325
|
+
absent_value_style: nil,
|
|
326
|
+
bar_set: nil
|
|
327
|
+
)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
`nil` in data marks absent values (rendered differently from `0`).
|
|
331
|
+
|
|
332
|
+
## Navigation Widgets
|
|
333
|
+
|
|
334
|
+
### Tabs
|
|
335
|
+
|
|
336
|
+
Tab bar for multi-view navigation.
|
|
337
|
+
|
|
338
|
+
```ruby
|
|
339
|
+
Tabs.new(
|
|
340
|
+
titles: [], # Tab titles
|
|
341
|
+
selected_index: 0,
|
|
342
|
+
block: nil,
|
|
343
|
+
divider: nil, # Separator between tabs
|
|
344
|
+
highlight_style: nil, # Selected tab
|
|
345
|
+
style: nil,
|
|
346
|
+
padding_left: 0,
|
|
347
|
+
padding_right: 0
|
|
348
|
+
)
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**Methods:**
|
|
352
|
+
- `width()` — Total width including dividers
|
|
353
|
+
|
|
354
|
+
```ruby
|
|
355
|
+
tui.tabs(
|
|
356
|
+
titles: ["General", "Network", "Display"],
|
|
357
|
+
selected_index: @tab_index,
|
|
358
|
+
highlight_style: {fg: "yellow", bold: true}
|
|
359
|
+
)
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Scrollbar
|
|
363
|
+
|
|
364
|
+
Scroll position indicator. Stateless widget that takes position directly.
|
|
365
|
+
|
|
366
|
+
```ruby
|
|
367
|
+
Scrollbar.new(
|
|
368
|
+
content_length:, # Total scrollable content length (required)
|
|
369
|
+
position:, # Current scroll position (required)
|
|
370
|
+
orientation: :vertical, # :vertical, :horizontal, :vertical_left, :vertical_right, :horizontal_top, :horizontal_bottom
|
|
371
|
+
thumb_symbol: "█",
|
|
372
|
+
thumb_style: nil,
|
|
373
|
+
track_symbol: nil,
|
|
374
|
+
track_style: nil,
|
|
375
|
+
begin_symbol: nil, # Arrow at start
|
|
376
|
+
begin_style: nil,
|
|
377
|
+
end_symbol: nil, # Arrow at end
|
|
378
|
+
end_style: nil,
|
|
379
|
+
style: nil,
|
|
380
|
+
block: nil
|
|
381
|
+
)
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## Layout Widgets
|
|
385
|
+
|
|
386
|
+
### Center
|
|
387
|
+
|
|
388
|
+
Centers child widget.
|
|
389
|
+
|
|
390
|
+
```ruby
|
|
391
|
+
Center.new(
|
|
392
|
+
child:, # Widget to center
|
|
393
|
+
width_percent: 100,
|
|
394
|
+
height_percent: 100
|
|
395
|
+
)
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Clear
|
|
399
|
+
|
|
400
|
+
Clears area (fills with spaces).
|
|
401
|
+
|
|
402
|
+
## Canvas & Drawing
|
|
403
|
+
|
|
404
|
+
### Canvas
|
|
405
|
+
|
|
406
|
+
High-resolution drawing surface using Braille characters.
|
|
407
|
+
|
|
408
|
+
```ruby
|
|
409
|
+
Canvas.new(
|
|
410
|
+
shapes: [], # Array of Shape objects
|
|
411
|
+
x_bounds: [0.0, 100.0], # [min, max] X range
|
|
412
|
+
y_bounds: [0.0, 100.0], # [min, max] Y range
|
|
413
|
+
marker: :braille, # :braille, :half_block, :dot, :block, :bar
|
|
414
|
+
block: nil,
|
|
415
|
+
background_color: nil
|
|
416
|
+
)
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
**Shapes:** Point, Line, Circle, Rectangle, Map, Label
|
|
420
|
+
|
|
421
|
+
**Coordinate System:** Float-based, independent of terminal cells.
|
|
422
|
+
|
|
423
|
+
**Methods:**
|
|
424
|
+
- `get_point(x, y)` — Convert to normalized grid coordinates [0.0-1.0]
|
|
425
|
+
|
|
426
|
+
```ruby
|
|
427
|
+
tui.canvas(
|
|
428
|
+
shapes: [
|
|
429
|
+
tui.shape_line(x1: 0, y1: 0, x2: 100, y2: 100, color: "red"),
|
|
430
|
+
tui.shape_point(x: 50, y: 50) # Point only has x: and y:
|
|
431
|
+
],
|
|
432
|
+
x_bounds: [0.0, 100.0],
|
|
433
|
+
y_bounds: [0.0, 100.0],
|
|
434
|
+
marker: :braille
|
|
435
|
+
)
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### Calendar
|
|
439
|
+
|
|
440
|
+
Monthly calendar display.
|
|
441
|
+
|
|
442
|
+
```ruby
|
|
443
|
+
Calendar.new(
|
|
444
|
+
year:, # Integer year (required)
|
|
445
|
+
month:, # Integer month 1-12 (required)
|
|
446
|
+
events: {}, # Hash<Date, Style> to highlight
|
|
447
|
+
default_style: nil, # Style for days
|
|
448
|
+
header_style: nil, # Style for month name header
|
|
449
|
+
show_month_header: false,
|
|
450
|
+
show_weekdays_header: true,
|
|
451
|
+
show_surrounding: nil, # Style or nil for adjacent month days
|
|
452
|
+
block: nil
|
|
453
|
+
)
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
## Composition Patterns
|
|
457
|
+
|
|
458
|
+
### Block Wrapping
|
|
459
|
+
|
|
460
|
+
Most widgets accept `block:` parameter:
|
|
461
|
+
|
|
462
|
+
```ruby
|
|
463
|
+
tui.list(
|
|
464
|
+
items: ["A", "B", "C"],
|
|
465
|
+
block: tui.block(
|
|
466
|
+
title: "Menu",
|
|
467
|
+
borders: [:all],
|
|
468
|
+
border_type: :rounded
|
|
469
|
+
)
|
|
470
|
+
)
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### Coordinate Offsetting (Custom Widgets)
|
|
474
|
+
|
|
475
|
+
Always add area origin to drawing coordinates:
|
|
476
|
+
|
|
477
|
+
```ruby
|
|
478
|
+
def render(area)
|
|
479
|
+
# CORRECT
|
|
480
|
+
Draw.string(area.x + 5, area.y + 2, "Text", style)
|
|
481
|
+
|
|
482
|
+
# WRONG - assumes origin at (0,0)
|
|
483
|
+
Draw.string(5, 2, "Text", style)
|
|
484
|
+
end
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Bounds Checking
|
|
488
|
+
|
|
489
|
+
Validate dimensions before rendering:
|
|
490
|
+
|
|
491
|
+
```ruby
|
|
492
|
+
def render(area)
|
|
493
|
+
return [] if area.width < 10 || area.height < 3
|
|
494
|
+
# ... render
|
|
495
|
+
end
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
## Factory Methods Quick Reference
|
|
499
|
+
|
|
500
|
+
| Widget | Factory | Key Parameters |
|
|
501
|
+
|--------|---------|----------------|
|
|
502
|
+
| Paragraph | `tui.paragraph()` | `text:`, `wrap:`, `alignment:` |
|
|
503
|
+
| Block | `tui.block()` | `title:`, `borders:`, `border_type:` |
|
|
504
|
+
| List | `tui.list()` | `items:`, `highlight_style:` |
|
|
505
|
+
| Table | `tui.table()` | `header:`, `rows:`, `widths:` |
|
|
506
|
+
| Gauge | `tui.gauge()` | `ratio:` or `percent:`, `label:` |
|
|
507
|
+
| LineGauge | `tui.line_gauge()` | `data:`, `max:` |
|
|
508
|
+
| BarChart | `tui.bar_chart()` | `data:`, `direction:` |
|
|
509
|
+
| Sparkline | `tui.sparkline()` | `data:`, `max:` |
|
|
510
|
+
| Chart | `tui.chart()` | `datasets:`, `x_axis:`, `y_axis:` |
|
|
511
|
+
| Tabs | `tui.tabs()` | `titles:`, `selected_index:` |
|
|
512
|
+
| Canvas | `tui.canvas()` | `shapes:`, `x_bounds:`, `y_bounds:` |
|
|
513
|
+
| Calendar | `tui.calendar()` | `date:`, `events:` |
|
|
514
|
+
| Scrollbar | `tui.scrollbar()` | `content_length:`, `position:`, `orientation:` |
|
|
515
|
+
| Center | `tui.center()` | `child:` |
|
|
516
|
+
|
|
517
|
+
## State Objects
|
|
518
|
+
|
|
519
|
+
| State | Widget | Key Methods |
|
|
520
|
+
|-------|--------|-------------|
|
|
521
|
+
| `ListState` | List | `select_next`, `select_previous`, `selected` |
|
|
522
|
+
| `TableState` | Table | `select_next`, `select_previous`, `select_next_column`, `select_previous_column` |
|
|
523
|
+
| `ScrollbarState` | (deprecated) | `position=`, `content_length` |
|
|
524
|
+
|
|
525
|
+
Create via factory methods:
|
|
526
|
+
|
|
527
|
+
```ruby
|
|
528
|
+
@list_state = tui.list_state(0) # positional: initial selected index
|
|
529
|
+
@table_state = tui.table_state # optional positional: selected index
|
|
530
|
+
@scrollbar_state = tui.scrollbar_state(100) # positional: content_length
|
|
531
|
+
@scrollbar_state.position = 0 # set position via setter
|
|
532
|
+
```
|