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.
Files changed (269) hide show
  1. checksums.yaml +4 -4
  2. data/.reek.yml +27 -1
  3. data/CHANGELOG.md +4 -0
  4. data/README.md +219 -25
  5. data/agents/codebase-analyzer.md +88 -0
  6. data/agents/codebase-pattern-finder.md +83 -0
  7. data/agents/documentation-researcher.md +59 -0
  8. data/agents/thoughts-analyzer.md +102 -0
  9. data/agents/web-search-researcher.md +71 -0
  10. data/anima-core.gemspec +3 -0
  11. data/app/channels/session_channel.rb +76 -28
  12. data/app/jobs/agent_request_job.rb +24 -0
  13. data/app/jobs/analytical_brain_job.rb +33 -0
  14. data/app/jobs/count_event_tokens_job.rb +1 -1
  15. data/app/models/concerns/event/broadcasting.rb +20 -2
  16. data/app/models/event.rb +1 -1
  17. data/app/models/goal.rb +91 -0
  18. data/app/models/session.rb +347 -22
  19. data/config/application.rb +2 -0
  20. data/db/migrate/20260314075248_add_subagent_support_to_sessions.rb +6 -0
  21. data/db/migrate/20260314112417_add_granted_tools_to_sessions.rb +5 -0
  22. data/db/migrate/20260314140000_add_name_to_sessions.rb +7 -0
  23. data/db/migrate/20260314150000_add_viewport_event_ids_to_sessions.rb +7 -0
  24. data/db/migrate/20260315100000_add_active_skills_to_sessions.rb +7 -0
  25. data/db/migrate/20260315140843_create_goals.rb +16 -0
  26. data/db/migrate/20260315144837_add_completed_at_to_goals.rb +5 -0
  27. data/db/migrate/20260315191105_add_active_workflow_to_sessions.rb +5 -0
  28. data/lib/agent_loop.rb +65 -9
  29. data/lib/agents/definition.rb +116 -0
  30. data/lib/agents/registry.rb +106 -0
  31. data/lib/analytical_brain/runner.rb +276 -0
  32. data/lib/analytical_brain/tools/activate_skill.rb +52 -0
  33. data/lib/analytical_brain/tools/deactivate_skill.rb +43 -0
  34. data/lib/analytical_brain/tools/deactivate_workflow.rb +34 -0
  35. data/lib/analytical_brain/tools/everything_is_ready.rb +28 -0
  36. data/lib/analytical_brain/tools/finish_goal.rb +62 -0
  37. data/lib/analytical_brain/tools/read_workflow.rb +58 -0
  38. data/lib/analytical_brain/tools/rename_session.rb +63 -0
  39. data/lib/analytical_brain/tools/set_goal.rb +60 -0
  40. data/lib/analytical_brain/tools/update_goal.rb +60 -0
  41. data/lib/analytical_brain.rb +23 -0
  42. data/lib/anima/cli/mcp/secrets.rb +76 -0
  43. data/lib/anima/cli/mcp.rb +197 -0
  44. data/lib/anima/cli.rb +4 -0
  45. data/lib/anima/installer.rb +168 -0
  46. data/lib/anima/settings.rb +226 -0
  47. data/lib/anima/version.rb +1 -1
  48. data/lib/anima.rb +9 -0
  49. data/lib/credential_store.rb +103 -0
  50. data/lib/environment_probe.rb +232 -0
  51. data/lib/llm/client.rb +29 -10
  52. data/lib/mcp/client_manager.rb +86 -0
  53. data/lib/mcp/config.rb +213 -0
  54. data/lib/mcp/health_check.rb +77 -0
  55. data/lib/mcp/secrets.rb +73 -0
  56. data/lib/mcp/stdio_transport.rb +206 -0
  57. data/lib/providers/anthropic.rb +8 -7
  58. data/lib/shell_session.rb +11 -10
  59. data/lib/skills/definition.rb +97 -0
  60. data/lib/skills/registry.rb +105 -0
  61. data/lib/tools/edit.rb +3 -4
  62. data/lib/tools/mcp_tool.rb +114 -0
  63. data/lib/tools/read.rb +15 -16
  64. data/lib/tools/registry.rb +14 -12
  65. data/lib/tools/request_feature.rb +121 -0
  66. data/lib/tools/return_result.rb +81 -0
  67. data/lib/tools/spawn_specialist.rb +109 -0
  68. data/lib/tools/spawn_subagent.rb +111 -0
  69. data/lib/tools/subagent_prompts.rb +12 -0
  70. data/lib/tools/web_get.rb +8 -9
  71. data/lib/tui/app.rb +332 -43
  72. data/lib/tui/message_store.rb +20 -0
  73. data/lib/tui/screens/chat.rb +207 -20
  74. data/lib/workflows/definition.rb +97 -0
  75. data/lib/workflows/registry.rb +89 -0
  76. data/skills/activerecord/SKILL.md +255 -0
  77. data/skills/activerecord/examples/associations/association_extensions.rb +298 -0
  78. data/skills/activerecord/examples/associations/basic_associations.rb +118 -0
  79. data/skills/activerecord/examples/associations/counter_caches.rb +215 -0
  80. data/skills/activerecord/examples/associations/polymorphic_associations.rb +217 -0
  81. data/skills/activerecord/examples/associations/self_referential.rb +302 -0
  82. data/skills/activerecord/examples/associations/through_associations.rb +203 -0
  83. data/skills/activerecord/examples/basics/crud_operations.rb +209 -0
  84. data/skills/activerecord/examples/basics/dirty_tracking.rb +218 -0
  85. data/skills/activerecord/examples/basics/inheritance.rb +377 -0
  86. data/skills/activerecord/examples/basics/type_casting.rb +317 -0
  87. data/skills/activerecord/examples/callbacks/alternatives_to_callbacks.rb +447 -0
  88. data/skills/activerecord/examples/callbacks/conditional_callbacks.rb +353 -0
  89. data/skills/activerecord/examples/callbacks/lifecycle_callbacks.rb +280 -0
  90. data/skills/activerecord/examples/callbacks/transaction_callbacks.rb +340 -0
  91. data/skills/activerecord/examples/migrations/indexes_and_constraints.rb +337 -0
  92. data/skills/activerecord/examples/migrations/reversible_patterns.rb +403 -0
  93. data/skills/activerecord/examples/migrations/safe_patterns.rb +420 -0
  94. data/skills/activerecord/examples/migrations/schema_changes.rb +277 -0
  95. data/skills/activerecord/examples/querying/batch_processing.rb +226 -0
  96. data/skills/activerecord/examples/querying/eager_loading.rb +259 -0
  97. data/skills/activerecord/examples/querying/finder_methods.rb +170 -0
  98. data/skills/activerecord/examples/querying/optimization.rb +275 -0
  99. data/skills/activerecord/examples/querying/scopes.rb +260 -0
  100. data/skills/activerecord/examples/validations/built_in_validators.rb +277 -0
  101. data/skills/activerecord/examples/validations/conditional_validations.rb +288 -0
  102. data/skills/activerecord/examples/validations/custom_validators.rb +381 -0
  103. data/skills/activerecord/examples/validations/database_constraints.rb +432 -0
  104. data/skills/activerecord/examples/validations/validation_contexts.rb +367 -0
  105. data/skills/activerecord/references/associations.md +709 -0
  106. data/skills/activerecord/references/basics.md +622 -0
  107. data/skills/activerecord/references/callbacks.md +738 -0
  108. data/skills/activerecord/references/migrations.md +657 -0
  109. data/skills/activerecord/references/querying.md +655 -0
  110. data/skills/activerecord/references/validations.md +596 -0
  111. data/skills/dragonruby/SKILL.md +250 -0
  112. data/skills/dragonruby/examples/audio/audio_events.rb +55 -0
  113. data/skills/dragonruby/examples/audio/background_music.rb +29 -0
  114. data/skills/dragonruby/examples/audio/crossfade.rb +51 -0
  115. data/skills/dragonruby/examples/audio/music_controls.rb +51 -0
  116. data/skills/dragonruby/examples/audio/sound_effects.rb +30 -0
  117. data/skills/dragonruby/examples/core/coordinate_system.rb +27 -0
  118. data/skills/dragonruby/examples/core/hello_world.rb +24 -0
  119. data/skills/dragonruby/examples/core/labels.rb +22 -0
  120. data/skills/dragonruby/examples/core/sprites.rb +35 -0
  121. data/skills/dragonruby/examples/core/state_management.rb +29 -0
  122. data/skills/dragonruby/examples/distribution/background_pause.rb +42 -0
  123. data/skills/dragonruby/examples/distribution/build_workflow.sh +26 -0
  124. data/skills/dragonruby/examples/distribution/cvars_production.txt +16 -0
  125. data/skills/dragonruby/examples/distribution/game_metadata_hd.txt +23 -0
  126. data/skills/dragonruby/examples/distribution/game_metadata_minimal.txt +9 -0
  127. data/skills/dragonruby/examples/distribution/game_metadata_mobile.txt +31 -0
  128. data/skills/dragonruby/examples/distribution/platform_detection.rb +36 -0
  129. data/skills/dragonruby/examples/distribution/steam_metadata.txt +19 -0
  130. data/skills/dragonruby/examples/entities/collision_detection.rb +43 -0
  131. data/skills/dragonruby/examples/entities/entity_lifecycle.rb +68 -0
  132. data/skills/dragonruby/examples/entities/entity_storage.rb +38 -0
  133. data/skills/dragonruby/examples/entities/factory_methods.rb +45 -0
  134. data/skills/dragonruby/examples/entities/random_spawning.rb +50 -0
  135. data/skills/dragonruby/examples/game-logic/reset_patterns.rb +98 -0
  136. data/skills/dragonruby/examples/game-logic/save_load.rb +101 -0
  137. data/skills/dragonruby/examples/game-logic/scoring.rb +104 -0
  138. data/skills/dragonruby/examples/game-logic/state_transitions.rb +103 -0
  139. data/skills/dragonruby/examples/game-logic/timers.rb +87 -0
  140. data/skills/dragonruby/examples/input/action_triggers.rb +36 -0
  141. data/skills/dragonruby/examples/input/analog_movement.rb +28 -0
  142. data/skills/dragonruby/examples/input/controller_input.rb +28 -0
  143. data/skills/dragonruby/examples/input/directional_input.rb +24 -0
  144. data/skills/dragonruby/examples/input/keyboard_input.rb +28 -0
  145. data/skills/dragonruby/examples/input/mouse_click.rb +26 -0
  146. data/skills/dragonruby/examples/input/movement_with_bounds.rb +22 -0
  147. data/skills/dragonruby/examples/input/normalized_movement.rb +32 -0
  148. data/skills/dragonruby/examples/rendering/frame_animation.rb +32 -0
  149. data/skills/dragonruby/examples/rendering/labels.rb +32 -0
  150. data/skills/dragonruby/examples/rendering/layering.rb +51 -0
  151. data/skills/dragonruby/examples/rendering/solids.rb +61 -0
  152. data/skills/dragonruby/examples/rendering/sprites.rb +33 -0
  153. data/skills/dragonruby/examples/rendering/spritesheet_animation.rb +39 -0
  154. data/skills/dragonruby/examples/scenes/case_dispatch.rb +60 -0
  155. data/skills/dragonruby/examples/scenes/class_based.rb +150 -0
  156. data/skills/dragonruby/examples/scenes/pause_overlay.rb +100 -0
  157. data/skills/dragonruby/examples/scenes/safe_transitions.rb +68 -0
  158. data/skills/dragonruby/examples/scenes/scene_transitions.rb +98 -0
  159. data/skills/dragonruby/examples/scenes/send_dispatch.rb +88 -0
  160. data/skills/dragonruby/references/audio.md +396 -0
  161. data/skills/dragonruby/references/core.md +385 -0
  162. data/skills/dragonruby/references/distribution.md +434 -0
  163. data/skills/dragonruby/references/entities.md +516 -0
  164. data/skills/dragonruby/references/game-logic/persistence.md +386 -0
  165. data/skills/dragonruby/references/game-logic/state.md +389 -0
  166. data/skills/dragonruby/references/input.md +414 -0
  167. data/skills/dragonruby/references/rendering/animation.md +467 -0
  168. data/skills/dragonruby/references/rendering/primitives.md +403 -0
  169. data/skills/dragonruby/references/scenes.md +443 -0
  170. data/skills/draper-decorators/SKILL.md +344 -0
  171. data/skills/draper-decorators/examples/application_decorator.rb +61 -0
  172. data/skills/draper-decorators/examples/decorator_spec.rb +253 -0
  173. data/skills/draper-decorators/examples/model_decorator.rb +152 -0
  174. data/skills/draper-decorators/references/anti-patterns.md +640 -0
  175. data/skills/draper-decorators/references/patterns.md +507 -0
  176. data/skills/draper-decorators/references/testing.md +559 -0
  177. data/skills/gh-issue.md +182 -0
  178. data/skills/mcp-server/SKILL.md +177 -0
  179. data/skills/mcp-server/examples/dynamic_tools.rb +36 -0
  180. data/skills/mcp-server/examples/file_manager_tool.rb +85 -0
  181. data/skills/mcp-server/examples/http_client.rb +48 -0
  182. data/skills/mcp-server/examples/http_server.rb +97 -0
  183. data/skills/mcp-server/examples/rails_integration.rb +88 -0
  184. data/skills/mcp-server/examples/stdio_server.rb +108 -0
  185. data/skills/mcp-server/examples/streaming_client.rb +95 -0
  186. data/skills/mcp-server/references/gotchas.md +183 -0
  187. data/skills/mcp-server/references/prompts.md +98 -0
  188. data/skills/mcp-server/references/resources.md +53 -0
  189. data/skills/mcp-server/references/server.md +140 -0
  190. data/skills/mcp-server/references/tools.md +146 -0
  191. data/skills/mcp-server/references/transport.md +104 -0
  192. data/skills/ratatui-ruby/SKILL.md +315 -0
  193. data/skills/ratatui-ruby/references/core-concepts.md +340 -0
  194. data/skills/ratatui-ruby/references/events.md +387 -0
  195. data/skills/ratatui-ruby/references/frameworks.md +522 -0
  196. data/skills/ratatui-ruby/references/layout.md +423 -0
  197. data/skills/ratatui-ruby/references/styling.md +268 -0
  198. data/skills/ratatui-ruby/references/testing.md +433 -0
  199. data/skills/ratatui-ruby/references/widgets.md +532 -0
  200. data/skills/rspec/SKILL.md +340 -0
  201. data/skills/rspec/examples/core/basic_structure.rb +69 -0
  202. data/skills/rspec/examples/core/configuration.rb +126 -0
  203. data/skills/rspec/examples/core/hooks.rb +126 -0
  204. data/skills/rspec/examples/core/memoized_helpers.rb +139 -0
  205. data/skills/rspec/examples/core/metadata_filtering.rb +144 -0
  206. data/skills/rspec/examples/core/shared_examples.rb +145 -0
  207. data/skills/rspec/examples/factory_bot/associations.rb +314 -0
  208. data/skills/rspec/examples/factory_bot/build_strategies.rb +272 -0
  209. data/skills/rspec/examples/factory_bot/callbacks.rb +320 -0
  210. data/skills/rspec/examples/factory_bot/custom_construction.rb +328 -0
  211. data/skills/rspec/examples/factory_bot/factory_definition.rb +191 -0
  212. data/skills/rspec/examples/factory_bot/inheritance.rb +314 -0
  213. data/skills/rspec/examples/factory_bot/traits.rb +293 -0
  214. data/skills/rspec/examples/factory_bot/transients.rb +229 -0
  215. data/skills/rspec/examples/matchers/change.rb +115 -0
  216. data/skills/rspec/examples/matchers/collections.rb +154 -0
  217. data/skills/rspec/examples/matchers/comparisons.rb +79 -0
  218. data/skills/rspec/examples/matchers/composing.rb +155 -0
  219. data/skills/rspec/examples/matchers/custom_matchers.rb +197 -0
  220. data/skills/rspec/examples/matchers/equality.rb +58 -0
  221. data/skills/rspec/examples/matchers/errors.rb +136 -0
  222. data/skills/rspec/examples/matchers/output.rb +103 -0
  223. data/skills/rspec/examples/matchers/predicates.rb +87 -0
  224. data/skills/rspec/examples/matchers/truthiness.rb +101 -0
  225. data/skills/rspec/examples/matchers/types.rb +82 -0
  226. data/skills/rspec/examples/matchers/yield.rb +147 -0
  227. data/skills/rspec/examples/mocks/any_instance.rb +172 -0
  228. data/skills/rspec/examples/mocks/argument_matchers.rb +206 -0
  229. data/skills/rspec/examples/mocks/constants.rb +177 -0
  230. data/skills/rspec/examples/mocks/doubles.rb +139 -0
  231. data/skills/rspec/examples/mocks/expectations.rb +137 -0
  232. data/skills/rspec/examples/mocks/message_chains.rb +173 -0
  233. data/skills/rspec/examples/mocks/ordering.rb +144 -0
  234. data/skills/rspec/examples/mocks/receive_counts.rb +181 -0
  235. data/skills/rspec/examples/mocks/responses.rb +223 -0
  236. data/skills/rspec/examples/mocks/spies.rb +149 -0
  237. data/skills/rspec/examples/mocks/stubbing.rb +133 -0
  238. data/skills/rspec/examples/rails/channels.rb +250 -0
  239. data/skills/rspec/examples/rails/controller_specs.rb +302 -0
  240. data/skills/rspec/examples/rails/helper_specs.rb +245 -0
  241. data/skills/rspec/examples/rails/job_specs.rb +256 -0
  242. data/skills/rspec/examples/rails/mailer_specs.rb +228 -0
  243. data/skills/rspec/examples/rails/matchers.rb +374 -0
  244. data/skills/rspec/examples/rails/model_specs.rb +193 -0
  245. data/skills/rspec/examples/rails/request_specs.rb +275 -0
  246. data/skills/rspec/examples/rails/routing_specs.rb +276 -0
  247. data/skills/rspec/examples/rails/system_specs.rb +294 -0
  248. data/skills/rspec/examples/rails/transactions.rb +254 -0
  249. data/skills/rspec/examples/rails/view_specs.rb +252 -0
  250. data/skills/rspec/references/core.md +816 -0
  251. data/skills/rspec/references/factory_bot.md +641 -0
  252. data/skills/rspec/references/matchers.md +516 -0
  253. data/skills/rspec/references/mocks.md +381 -0
  254. data/skills/rspec/references/rails.md +528 -0
  255. data/templates/soul.md +40 -0
  256. data/workflows/commit.md +45 -0
  257. data/workflows/create_handoff.md +98 -0
  258. data/workflows/create_note.md +82 -0
  259. data/workflows/create_plan.md +457 -0
  260. data/workflows/decompose_ticket.md +109 -0
  261. data/workflows/feature.md +91 -0
  262. data/workflows/implement_plan.md +87 -0
  263. data/workflows/iterate_plan.md +247 -0
  264. data/workflows/research_codebase.md +210 -0
  265. data/workflows/resume_handoff.md +217 -0
  266. data/workflows/review_pr.md +320 -0
  267. data/workflows/thoughts_init.md +71 -0
  268. data/workflows/validate_plan.md +166 -0
  269. 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
+ ```