stream_weaver 0.3.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 (381) hide show
  1. checksums.yaml +7 -0
  2. data/.beads/.gitignore +77 -0
  3. data/.beads/README.md +81 -0
  4. data/.beads/config.yaml +54 -0
  5. data/.beads/metadata.json +7 -0
  6. data/.rspec +3 -0
  7. data/AGENTS.md +91 -0
  8. data/CHANGELOG.md +294 -0
  9. data/CLAUDE.md +67 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +565 -0
  12. data/Rakefile +13 -0
  13. data/assets/streamweaver-hero.jpg +0 -0
  14. data/bench/baselines/ledger.rb +58 -0
  15. data/bench/baselines/warroom.rb +51 -0
  16. data/bench/fixtures/ledger.rb +76 -0
  17. data/bench/fixtures/warroom.rb +60 -0
  18. data/bench/results/6daf8b8.md +102 -0
  19. data/bench/results/c8d749b.md +102 -0
  20. data/bench/results/eb06ab9.md +102 -0
  21. data/bench/run.rb +151 -0
  22. data/bench/support.rb +84 -0
  23. data/copy_this/dto_run_sample_run_7631.html +220 -0
  24. data/docs/SERVICE_MODE.md +302 -0
  25. data/docs/architecture/how_streamweaver_works.md +772 -0
  26. data/docs/blog/2026-01-16-streamweaver-introduction.md +515 -0
  27. data/docs/bug-2026-08-31-canvas-bridge-socket-collision.md +83 -0
  28. data/docs/canvas-ipc-session-summary.md +127 -0
  29. data/docs/canvas-panel-workflow.md +172 -0
  30. data/docs/canvas-read.md +147 -0
  31. data/docs/canvas-roadmap.md +275 -0
  32. data/docs/case-studies/2026-09-03-didx-canvas-read-shelf.md +147 -0
  33. data/docs/claude-code-companion-skill-spec.md +567 -0
  34. data/docs/components_reference.md +1223 -0
  35. data/docs/crud-patterns.md +265 -0
  36. data/docs/endpoints.md +122 -0
  37. data/docs/for_llms.md +1 -0
  38. data/docs/form-patterns.md +165 -0
  39. data/docs/frontend-only.md +169 -0
  40. data/docs/html-artifact-audit.md +238 -0
  41. data/docs/ideas/2025-01-01-as-a-service.md +203 -0
  42. data/docs/ideas/2026-01-16-charm-tui-exploration.md +542 -0
  43. data/docs/ideas/2026-08-27-static-doc-shelf-export.md +153 -0
  44. data/docs/ideas/dhh-review-future-refactors.md +142 -0
  45. data/docs/opal-jamstack.md +47 -0
  46. data/docs/opal-npm-direction.md +184 -0
  47. data/docs/opal-spike-findings.md +194 -0
  48. data/docs/plans/2025-12-16-form-blocks-design.md +124 -0
  49. data/docs/plans/2025-12-30-examples-browser-design.md +152 -0
  50. data/docs/plans/2026-01-02-charts-design.md +288 -0
  51. data/docs/plans/2026-01-10-canvas-ipc-design.md +247 -0
  52. data/docs/plans/2026-01-19-cabinet-control-components.md +176 -0
  53. data/docs/plans/2026-01-26-gem-release-and-panel.md +140 -0
  54. data/docs/plans/canvas-claude-project-design.md +238 -0
  55. data/docs/plans/canvas-doc-location-and-discovery.md +32 -0
  56. data/docs/plans/org-doc-preview-surfaces.md +293 -0
  57. data/docs/plans/shadcn-polish-plan.md +417 -0
  58. data/docs/porting-artifacts.md +252 -0
  59. data/docs/reference/agent-skills-comparison.md +369 -0
  60. data/docs/reference/travel-state-prd.artifact.html +780 -0
  61. data/docs/research/2026-08-17-hotwire-alike-landscape.md +268 -0
  62. data/docs/research/2026-08-17-hotwire-concept-map.md +404 -0
  63. data/docs/research/2026-08-22-lazy-fragments-trigger-decision.md +246 -0
  64. data/docs/research/2026-08-22-learnhotwire-syllabus-coverage.md +239 -0
  65. data/docs/research/frontend-only-matrix.md +278 -0
  66. data/docs/research/streamweaver-way-spike-findings.md +529 -0
  67. data/docs/resource-dsl.md +342 -0
  68. data/docs/routing.md +302 -0
  69. data/docs/ruby-ui-comparison.md +319 -0
  70. data/docs/shared-dsl-fragments.md +356 -0
  71. data/docs/streamweaver-for-ai-agents.md +401 -0
  72. data/docs/streamweaver-frontend-vision.md +123 -0
  73. data/docs/streamweaver_canvas/glimmer_initial_final_layer.rb +77 -0
  74. data/docs/streamweaver_canvas/save_example.rb +9 -0
  75. data/docs/templates.md +213 -0
  76. data/docs/testing.md +155 -0
  77. data/docs/theming-hooks.md +281 -0
  78. data/docs/tutorials/the-streamweaver-way.md +882 -0
  79. data/docs/university/capability-inventory.md +137 -0
  80. data/docs/university/dependency-survey.md +114 -0
  81. data/docs/university/design-spec.md +217 -0
  82. data/docs/university/mockups/code-block-doc-theme-dark.png +0 -0
  83. data/docs/university/mockups/code-block-doc-theme-light.png +0 -0
  84. data/docs/university/mockups/course-list-dark.png +0 -0
  85. data/docs/university/mockups/course-list-light.png +0 -0
  86. data/docs/university/mockups/course_canvas_mockup.rb +741 -0
  87. data/docs/university/mockups/step-screen-dark.png +0 -0
  88. data/docs/university/mockups/step-screen-light.png +0 -0
  89. data/docs/university/roadmap.md +72 -0
  90. data/docs/university/send-to-coworker.md +77 -0
  91. data/docs/university/worker-session-mining.md +109 -0
  92. data/docs/visual/browser-rendering-spike.md +173 -0
  93. data/docs/visual/builder-visual-plan-analysis.md +378 -0
  94. data/docs/visual/sw-plan-format-exploration.md +511 -0
  95. data/docs/visual/sw-plan-rendering-deep-dive.md +453 -0
  96. data/docs/visual-skills/PROGRESS.md +60 -0
  97. data/docs/visual-skills/SESSION-CONTEXT.md +207 -0
  98. data/docs/visual-skills/analysis/components.md +317 -0
  99. data/docs/visual-skills/analysis/overlap.md +360 -0
  100. data/docs/visual-skills/analysis/pi-design-deck.md +1102 -0
  101. data/docs/visual-skills/analysis/streamweaver-inventory.md +93 -0
  102. data/docs/visual-skills/analysis/unified-specs.feature +1003 -0
  103. data/docs/visual-skills/analysis/visual-explainer.md +964 -0
  104. data/docs/visual-skills/blog/blog-series-outline.md +39 -0
  105. data/docs/visual-skills/blog/token-efficiency.md +73 -0
  106. data/docs/visual-skills/design/architecture.md +1725 -0
  107. data/docs/visual-skills/design/codex-review.md +340 -0
  108. data/docs/visual-skills/design/dhh-review.md +209 -0
  109. data/docs/visual-skills/design/evolution.md +100 -0
  110. data/docs/visual-skills/design/gemini-review.md +55 -0
  111. data/docs/visual-skills/design/review-synthesis.md +149 -0
  112. data/docs/visual-skills/implementation/STATE.md +22 -0
  113. data/docs/visual-skills/implementation/plan.md +347 -0
  114. data/docs/visual-skills/implementation/spike-findings.md +237 -0
  115. data/docs/visual-skills/implementation/tasks.md +702 -0
  116. data/docs/visual-skills/lessons-learned/process.md +145 -0
  117. data/examples/README.md +96 -0
  118. data/examples/advanced/all_components.rb +177 -0
  119. data/examples/advanced/examples_browser.rb +394 -0
  120. data/examples/advanced/teachables_browser.rb +261 -0
  121. data/examples/advanced/theme_tweaker.rb +366 -0
  122. data/examples/advanced/tutorial.rb +1528 -0
  123. data/examples/agentic/agentic_form.rb +30 -0
  124. data/examples/agentic/agentic_form_autoclose.rb +30 -0
  125. data/examples/agentic/cultivation_tracker.rb +69 -0
  126. data/examples/basic/hello_world.rb +22 -0
  127. data/examples/basic/opal_tabs_table.rb +44 -0
  128. data/examples/basic/todo_list.rb +39 -0
  129. data/examples/button_loading_test.rb +36 -0
  130. data/examples/canvas/mermaid_canvas_demo.sh +151 -0
  131. data/examples/charts/bar_chart_demo.rb +57 -0
  132. data/examples/charts/line_chart_demo.rb +73 -0
  133. data/examples/charts/pie_area_demo.rb +87 -0
  134. data/examples/charts/stacked_bar_chart_demo.rb +69 -0
  135. data/examples/claude_code/README.md +85 -0
  136. data/examples/claude_code/codebreaker/.claude/commands/infiltrate.md +632 -0
  137. data/examples/claude_code/codebreaker/.claude/settings.json +11 -0
  138. data/examples/claude_code/codebreaker/.claude/settings.local.json +17 -0
  139. data/examples/claude_code/codebreaker/.claude/skills/infiltrate.md +0 -0
  140. data/examples/claude_code/codebreaker/README.md +213 -0
  141. data/examples/claude_code/tutorial/.claude/commands/learn.md +453 -0
  142. data/examples/claude_code/tutorial/README.md +102 -0
  143. data/examples/claude_code/verification_flow/.claude/commands/verify.md +171 -0
  144. data/examples/claude_code/verification_flow/README.md +126 -0
  145. data/examples/components/annotated_code_demo.rb +54 -0
  146. data/examples/components/callout_demo.rb +33 -0
  147. data/examples/components/checkbox_group_demo.rb +46 -0
  148. data/examples/components/design_review.css +468 -0
  149. data/examples/components/design_review_demo.rb +44 -0
  150. data/examples/components/design_review_dsl.rb +300 -0
  151. data/examples/components/diff_block_demo.rb +112 -0
  152. data/examples/components/events_demo.rb +184 -0
  153. data/examples/components/form_demo.rb +62 -0
  154. data/examples/components/lesson_demo.rb +53 -0
  155. data/examples/components/markdown_demo.rb +110 -0
  156. data/examples/components/mermaid_demo.rb +166 -0
  157. data/examples/components/pareto_set.rb +64 -0
  158. data/examples/components/prd_demo.rb +21 -0
  159. data/examples/components/prd_dsl.rb +385 -0
  160. data/examples/components/quiz_demo.rb +47 -0
  161. data/examples/components/run_viewer_demo.rb +100 -0
  162. data/examples/components/score_and_collapsible_demo.rb +82 -0
  163. data/examples/components/select_stale_value_smoke_test.rb +39 -0
  164. data/examples/components/table_demo.rb +247 -0
  165. data/examples/components/timer_with_state_demo.rb +58 -0
  166. data/examples/components/todo_due_dates.rb +63 -0
  167. data/examples/components/uat_gaps_demo.rb +112 -0
  168. data/examples/dashboard/feed_simulator.rb +60 -0
  169. data/examples/dashboard/live_dashboard.rb +80 -0
  170. data/examples/dashboard_components.rb +108 -0
  171. data/examples/deferred_fragments_demo.rb +62 -0
  172. data/examples/generate_more_spike/README.md +63 -0
  173. data/examples/generate_more_spike/app.rb +441 -0
  174. data/examples/git_health.sh +401 -0
  175. data/examples/layout/layout_components_demo.rb +174 -0
  176. data/examples/layout/modal_demo.rb +215 -0
  177. data/examples/layout/navigation_demo.rb +227 -0
  178. data/examples/layout/route_tabs_demo.rb +52 -0
  179. data/examples/layout/routing_demo.rb +52 -0
  180. data/examples/layout/scroll_box_demo.rb +154 -0
  181. data/examples/lazy_fragments_demo.rb +151 -0
  182. data/examples/my_todos/README.md +29 -0
  183. data/examples/my_todos/my_todos.rb +312 -0
  184. data/examples/my_todos/store.rb +86 -0
  185. data/examples/opal/reactive_demo.rb +62 -0
  186. data/examples/opal/scenarios/s1_counter.rb +15 -0
  187. data/examples/opal/scenarios/s2_search_filter.rb +17 -0
  188. data/examples/opal/scenarios/s3_sibling_tabs.rb +25 -0
  189. data/examples/opal/scenarios/s4_shopping_cart.rb +34 -0
  190. data/examples/opal/scenarios/s5_watch.rb +39 -0
  191. data/examples/opal/scenarios/s6_on_start.rb +20 -0
  192. data/examples/opal/scenarios/s7_wizard.rb +33 -0
  193. data/examples/opal/scenarios/s8_loan_calculator.rb +21 -0
  194. data/examples/opal/scenarios/s9_dashboard.rb +27 -0
  195. data/examples/operations_dashboard_demo.rb +167 -0
  196. data/examples/panel_demo.sh +447 -0
  197. data/examples/parity/assets/tyrion_slice.css +86 -0
  198. data/examples/parity/rivet_people_slice.rb +182 -0
  199. data/examples/parity/tyrion_components.css +777 -0
  200. data/examples/parity/tyrion_warroom_components.rb +414 -0
  201. data/examples/parity/tyrion_warroom_slice.rb +549 -0
  202. data/examples/puma_dev/README.md +124 -0
  203. data/examples/puma_dev/config.ru +34 -0
  204. data/examples/puma_dev/standalone_app.rb +34 -0
  205. data/examples/scaffolding/blog.rb +49 -0
  206. data/examples/scaffolding/utf_lite.rb +105 -0
  207. data/examples/styling/feedback_demo.rb +229 -0
  208. data/examples/styling/style_showcase.rb +172 -0
  209. data/examples/styling/theme_demo.rb +508 -0
  210. data/examples/timer_health_checker.rb +111 -0
  211. data/examples/timer_showcase.rb +131 -0
  212. data/examples/tutorials/render_markdown.rb +112 -0
  213. data/examples/tutorials/streamweaver_way_tutorial.rb +44 -0
  214. data/examples/tutorials/tutorial_content.rb +1082 -0
  215. data/examples/visual_skills/design_deck_demo.rb +314 -0
  216. data/examples/visual_skills/explainer_demo.rb +499 -0
  217. data/exe/streamweaver +9 -0
  218. data/gsd/ROADMAP-1.0.md +167 -0
  219. data/gsd/STATE.md +13 -0
  220. data/gsd/research/market-positioning-research.md +139 -0
  221. data/gsd/research/production-patterns-research.md +270 -0
  222. data/gsd/research/repo-audit-1.0.md +274 -0
  223. data/lib/stream_weaver/action_token.rb +58 -0
  224. data/lib/stream_weaver/adapter/alpinejs.rb +8249 -0
  225. data/lib/stream_weaver/adapter/base.rb +591 -0
  226. data/lib/stream_weaver/adapter/opal.rb +334 -0
  227. data/lib/stream_weaver/adapter/static.rb +1118 -0
  228. data/lib/stream_weaver/admin.rb +176 -0
  229. data/lib/stream_weaver/app.rb +1821 -0
  230. data/lib/stream_weaver/assets/js/sw-copy.js +50 -0
  231. data/lib/stream_weaver/assets/js/sw-heredoc-rewrite.js +68 -0
  232. data/lib/stream_weaver/assets/js/sw-keyboard.js +165 -0
  233. data/lib/stream_weaver/assets/js/sw-mermaid-zoom.js +621 -0
  234. data/lib/stream_weaver/assets/js/sw-route-tabs.js +53 -0
  235. data/lib/stream_weaver/assets/js/sw-sidebar-toc.js +116 -0
  236. data/lib/stream_weaver/assets/js/sw-slide-nav.js +103 -0
  237. data/lib/stream_weaver/canvas/bridge.rb +239 -0
  238. data/lib/stream_weaver/canvas/bridge_server.rb +646 -0
  239. data/lib/stream_weaver/canvas/client.rb +298 -0
  240. data/lib/stream_weaver/canvas/doc_roots.rb +233 -0
  241. data/lib/stream_weaver/canvas/doc_store.rb +252 -0
  242. data/lib/stream_weaver/canvas/gist_publisher.rb +264 -0
  243. data/lib/stream_weaver/canvas/gist_save_handler.rb +89 -0
  244. data/lib/stream_weaver/canvas/gist_store.rb +135 -0
  245. data/lib/stream_weaver/canvas/helpers.rb +109 -0
  246. data/lib/stream_weaver/canvas/history.rb +90 -0
  247. data/lib/stream_weaver/canvas/protocol.rb +86 -0
  248. data/lib/stream_weaver/canvas/reader.rb +853 -0
  249. data/lib/stream_weaver/canvas/save_doc_widget.rb +457 -0
  250. data/lib/stream_weaver/canvas/scroll_top_hint.rb +21 -0
  251. data/lib/stream_weaver/canvas/session.rb +132 -0
  252. data/lib/stream_weaver/cli.rb +3235 -0
  253. data/lib/stream_weaver/component_assets.rb +70 -0
  254. data/lib/stream_weaver/component_registry.rb +67 -0
  255. data/lib/stream_weaver/component_renderer.rb +48 -0
  256. data/lib/stream_weaver/components/annotated_code.rb +53 -0
  257. data/lib/stream_weaver/components/api_endpoint.rb +42 -0
  258. data/lib/stream_weaver/components/callout.rb +59 -0
  259. data/lib/stream_weaver/components/chart.rb +84 -0
  260. data/lib/stream_weaver/components/code_block.rb +77 -0
  261. data/lib/stream_weaver/components/comparison.rb +39 -0
  262. data/lib/stream_weaver/components/decision.rb +38 -0
  263. data/lib/stream_weaver/components/deck/close_overlay.rb +84 -0
  264. data/lib/stream_weaver/components/deck/confirmation_bar.rb +60 -0
  265. data/lib/stream_weaver/components/deck/deck_option.rb +61 -0
  266. data/lib/stream_weaver/components/deck/deck_slide.rb +77 -0
  267. data/lib/stream_weaver/components/deck/deck_state.rb +469 -0
  268. data/lib/stream_weaver/components/deck/deck_summary.rb +73 -0
  269. data/lib/stream_weaver/components/deck/design_deck.rb +60 -0
  270. data/lib/stream_weaver/components/deck/generate_more_controls.rb +84 -0
  271. data/lib/stream_weaver/components/deck/model_selector.rb +84 -0
  272. data/lib/stream_weaver/components/deck/skeleton_placeholder.rb +36 -0
  273. data/lib/stream_weaver/components/diff_block.rb +125 -0
  274. data/lib/stream_weaver/components/doc_header.rb +57 -0
  275. data/lib/stream_weaver/components/image_block.rb +70 -0
  276. data/lib/stream_weaver/components/implementation_map.rb +33 -0
  277. data/lib/stream_weaver/components/keyboard_shortcuts.rb +94 -0
  278. data/lib/stream_weaver/components/kpi_dashboard.rb +78 -0
  279. data/lib/stream_weaver/components/mermaid.rb +79 -0
  280. data/lib/stream_weaver/components/pipeline.rb +63 -0
  281. data/lib/stream_weaver/components/sidebar_toc.rb +46 -0
  282. data/lib/stream_weaver/components/slide_container.rb +141 -0
  283. data/lib/stream_weaver/components/timeline_event.rb +58 -0
  284. data/lib/stream_weaver/components/wireframe.rb +29 -0
  285. data/lib/stream_weaver/components/wireframe_block.rb +34 -0
  286. data/lib/stream_weaver/components.rb +2711 -0
  287. data/lib/stream_weaver/css.rb +230 -0
  288. data/lib/stream_weaver/dev_fallback_overlay.rb +61 -0
  289. data/lib/stream_weaver/display_dsl.rb +1004 -0
  290. data/lib/stream_weaver/export/html_exporter.rb +478 -0
  291. data/lib/stream_weaver/feed.rb +34 -0
  292. data/lib/stream_weaver/feed_builder.rb +31 -0
  293. data/lib/stream_weaver/fonts.rb +33 -0
  294. data/lib/stream_weaver/interaction_runner.rb +487 -0
  295. data/lib/stream_weaver/iterm.rb +460 -0
  296. data/lib/stream_weaver/layout_registry.rb +92 -0
  297. data/lib/stream_weaver/opal/bridge.rb +52 -0
  298. data/lib/stream_weaver/opal/builder.rb +193 -0
  299. data/lib/stream_weaver/opal/env.rb +29 -0
  300. data/lib/stream_weaver/opal/reactive_state.rb +75 -0
  301. data/lib/stream_weaver/opal/regexp_anchor_patch.rb +62 -0
  302. data/lib/stream_weaver/opal/renderer.rb +77 -0
  303. data/lib/stream_weaver/opal/runtime.rb +250 -0
  304. data/lib/stream_weaver/opal/shell.rb +145 -0
  305. data/lib/stream_weaver/opal/string_bridge.rb +50 -0
  306. data/lib/stream_weaver/opal/stubs/diff.min.js +1 -0
  307. data/lib/stream_weaver/opal/stubs/digest.rb +15 -0
  308. data/lib/stream_weaver/opal/stubs/marked.umd.js +79 -0
  309. data/lib/stream_weaver/opal/stubs/md5.rb +3 -0
  310. data/lib/stream_weaver/opal/stubs/morphdom.min.js +775 -0
  311. data/lib/stream_weaver/opal/stubs/prism-tomorrow.min.css +1 -0
  312. data/lib/stream_weaver/opal/stubs/prism.min.js +1967 -0
  313. data/lib/stream_weaver/opal_entry.rb +136 -0
  314. data/lib/stream_weaver/org/inline.rb +95 -0
  315. data/lib/stream_weaver/org/reader.rb +561 -0
  316. data/lib/stream_weaver/org/recording_context.rb +86 -0
  317. data/lib/stream_weaver/org/source_splitter.rb +63 -0
  318. data/lib/stream_weaver/org/writer.rb +300 -0
  319. data/lib/stream_weaver/page_shell.rb +530 -0
  320. data/lib/stream_weaver/portfile.rb +79 -0
  321. data/lib/stream_weaver/pushable.rb +42 -0
  322. data/lib/stream_weaver/resource/default_views.rb +111 -0
  323. data/lib/stream_weaver/resource/field_input.rb +25 -0
  324. data/lib/stream_weaver/resource/state_keys.rb +16 -0
  325. data/lib/stream_weaver/resource/store.rb +18 -0
  326. data/lib/stream_weaver/resource.rb +104 -0
  327. data/lib/stream_weaver/server.rb +1471 -0
  328. data/lib/stream_weaver/service.rb +1240 -0
  329. data/lib/stream_weaver/service_client.rb +104 -0
  330. data/lib/stream_weaver/session_store.rb +186 -0
  331. data/lib/stream_weaver/skills/streamweaver-canvas-safe/SKILL.md +66 -0
  332. data/lib/stream_weaver/skills/streamweaver-canvas-safe/examples/canvas-safe-showcase.rb +98 -0
  333. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/actions-and-buttons.md +47 -0
  334. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/charts-and-diagrams.md +38 -0
  335. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/deck.md +32 -0
  336. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/inputs-and-forms.md +65 -0
  337. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/tabs-and-navigation.md +53 -0
  338. data/lib/stream_weaver/skills/streamweaver-doc-builder/SKILL.md +298 -0
  339. data/lib/stream_weaver/skills/streamweaver-visual-companion/SKILL.md +130 -0
  340. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.css +468 -0
  341. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.rb +39 -0
  342. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example_dsl.rb +300 -0
  343. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example.rb +24 -0
  344. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example_dsl.rb +385 -0
  345. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/checkpoints-and-forms.md +28 -0
  346. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/cleanup-and-panel.md +44 -0
  347. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/example-gallery.md +8 -0
  348. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/persistence.md +58 -0
  349. data/lib/stream_weaver/skills/streamweaver-way/SKILL.md +396 -0
  350. data/lib/stream_weaver/skills/visual-plan/SKILL.md +201 -0
  351. data/lib/stream_weaver/skills/visual-recap/SKILL.md +244 -0
  352. data/lib/stream_weaver/streamer.rb +63 -0
  353. data/lib/stream_weaver/templates/choices.rb +149 -0
  354. data/lib/stream_weaver/templates/code.rb +157 -0
  355. data/lib/stream_weaver/templates/confirm.rb +110 -0
  356. data/lib/stream_weaver/templates/diff.rb +195 -0
  357. data/lib/stream_weaver/templates/info.rb +142 -0
  358. data/lib/stream_weaver/templates/table.rb +169 -0
  359. data/lib/stream_weaver/templates/wizard.rb +271 -0
  360. data/lib/stream_weaver/theme/auto_mode.rb +116 -0
  361. data/lib/stream_weaver/theme/presets.rb +555 -0
  362. data/lib/stream_weaver/theme.rb +639 -0
  363. data/lib/stream_weaver/university/canvas.rb +924 -0
  364. data/lib/stream_weaver/university/course.rb +699 -0
  365. data/lib/stream_weaver/university/demos/counter.rb +33 -0
  366. data/lib/stream_weaver/university/demos/dashboard.rb +147 -0
  367. data/lib/stream_weaver/university/demos/decision_form.rb +143 -0
  368. data/lib/stream_weaver/university/demos.rb +50 -0
  369. data/lib/stream_weaver/university/listener.rb +467 -0
  370. data/lib/stream_weaver/university/progress.rb +201 -0
  371. data/lib/stream_weaver/university/runner.rb +138 -0
  372. data/lib/stream_weaver/university/scripts/growing_doc.rb +609 -0
  373. data/lib/stream_weaver/university/scripts/growing_doc_state.rb +91 -0
  374. data/lib/stream_weaver/utils.rb +31 -0
  375. data/lib/stream_weaver/version.rb +5 -0
  376. data/lib/stream_weaver/views/canvas/reader_layout.erb +800 -0
  377. data/lib/stream_weaver/views.rb +3940 -0
  378. data/lib/stream_weaver.rb +122 -0
  379. data/llms.txt +1269 -0
  380. data/sig/stream_weaver.rbs +4 -0
  381. metadata +598 -0
@@ -0,0 +1,124 @@
1
+ # Form Blocks with Deferred Submission
2
+
3
+ ## Overview
4
+
5
+ Add a `form` block to StreamWeaver that groups multiple form elements together, uses client-side only state until submission, and sends all values in a single HTMX POST on submit.
6
+
7
+ ## Problem
8
+
9
+ Currently, all form widgets trigger immediate HTMX requests on change, syncing state with the server on every keystroke/selection. This causes issues when:
10
+ 1. Multiple fields need to be edited before saving
11
+ 2. Navigation state gets reset during re-renders
12
+ 3. Unnecessary server round-trips for uncommitted changes
13
+
14
+ ## API
15
+
16
+ ```ruby
17
+ form :edit_person do
18
+ text_field :name, placeholder: 'Name'
19
+ select :status, %w[active paused archived]
20
+ text_area :notes, placeholder: 'Notes...', rows: 3
21
+
22
+ submit 'Save' do |form_values|
23
+ # form_values = { name: "...", status: "...", notes: "..." }
24
+ # state[:edit_person] already updated at this point
25
+ api.save_person(form_values) # optional side effects
26
+ end
27
+
28
+ cancel 'Cancel' # resets to original values, no server request
29
+ end
30
+ ```
31
+
32
+ ## Design Decisions
33
+
34
+ ### State Namespace: Nested Hash
35
+ Form values stored as `state[:edit_person][:name]` - nested hash structure matching Rails conventions (strong params, form_for).
36
+
37
+ ### Initial Values: From State
38
+ Form reads initial values from `state[:form_name]`. App code is responsible for populating state before rendering (like Rails controller setting `@person = Person.find(id)`).
39
+
40
+ ### Submit Behavior: Auto-Update State
41
+ On submit, state is automatically updated with form values, then the submit block runs. This keeps UI consistent - other components using `state[:edit_person][:name]` show current values.
42
+
43
+ ### Context Flag for Widgets
44
+ Same DSL methods (`text_field`, `select`, etc.) work inside and outside forms. A context flag tells the adapter to render differently (no HTMX, form-scoped x-model).
45
+
46
+ ## Client-Side Behavior
47
+
48
+ Form block wraps contents in Alpine.js scope with local state:
49
+
50
+ ```html
51
+ <div x-data="{
52
+ _form: { name: 'Alice', status: 'active', notes: '' },
53
+ _original: { name: 'Alice', status: 'active', notes: '' }
54
+ }">
55
+ <input type="text" x-model="_form.name" name="edit_person[name]">
56
+ <select x-model="_form.status" name="edit_person[status]">...</select>
57
+
58
+ <button hx-post="/form/edit_person" hx-include="[name^='edit_person']">Save</button>
59
+ <button type="button" @click="_form = JSON.parse(JSON.stringify(_original))">Cancel</button>
60
+ </div>
61
+ ```
62
+
63
+ ### Widget Rendering Differences
64
+
65
+ | Aspect | Standalone widget | Widget inside form |
66
+ |--------|-------------------|-------------------|
67
+ | `x-model` | `x-model="name"` (root scope) | `x-model="_form.name"` (form-local) |
68
+ | `hx-post` | On every change (`/update`) | None |
69
+ | `hx-trigger` | `keyup changed delay:500ms` | None |
70
+ | `name` attr | `name="name"` | `name="form_name[field]"` (Rails nested) |
71
+
72
+ ### Cancel Button
73
+ Pure Alpine - resets `_form` to `_original` snapshot, no server request.
74
+
75
+ ### Submit Button
76
+ Single HTMX POST to `/form/:form_name` with all form values.
77
+
78
+ ## Server-Side Handling
79
+
80
+ New endpoint `POST /form/:form_name`:
81
+
82
+ 1. Parse Rails-style nested params: `edit_person[name]` -> `{ name: "..." }`
83
+ 2. Auto-update state: `state[:edit_person] = form_values`
84
+ 3. Execute submit block if defined
85
+ 4. Re-render page with updated state
86
+
87
+ ## Implementation Files
88
+
89
+ | File | Changes |
90
+ |------|---------|
91
+ | `lib/stream_weaver/components.rb` | Add `Form` component class |
92
+ | `lib/stream_weaver/app.rb` | Add `form`, `submit`, `cancel` DSL methods; add `@form_context` flag |
93
+ | `lib/stream_weaver/adapter/alpinejs.rb` | Add `render_form`, modify widget renderers to check form context |
94
+ | `lib/stream_weaver/server.rb` | Add `POST /form/:form_name` endpoint |
95
+ | `docs/for_llms.md` | Document form block usage and auto-update behavior |
96
+
97
+ ## Form Component Structure
98
+
99
+ ```ruby
100
+ class Form < Base
101
+ attr_reader :name, :submit_label, :cancel_label, :submit_action
102
+ attr_accessor :children
103
+
104
+ def initialize(name, **options)
105
+ @name = name
106
+ @children = []
107
+ @submit_label = nil
108
+ @cancel_label = nil
109
+ @submit_action = nil
110
+ end
111
+
112
+ def execute_submit(state, form_values)
113
+ @submit_action&.call(form_values)
114
+ end
115
+ end
116
+ ```
117
+
118
+ ## Documentation Notes
119
+
120
+ For `docs/for_llms.md`, emphasize:
121
+ - Form values auto-update to state on submit (state[:form_name] = form_values)
122
+ - Submit block is for side effects (API calls), not state management
123
+ - Cancel resets to original values without server request
124
+ - Same DSL methods work inside/outside forms - context determines behavior
@@ -0,0 +1,152 @@
1
+ # Examples Browser Design
2
+
3
+ ## Overview
4
+
5
+ An interactive Examples Browser app that showcases StreamWeaver examples, built with StreamWeaver itself. Features directory navigation, syntax-highlighted code display, and ability to run examples.
6
+
7
+ ## Goals
8
+
9
+ 1. **Phase 1 (MVP):** Browse and run examples with read-only code display
10
+ 2. **Phase 2:** Edit code and run modified versions
11
+
12
+ ## Architecture
13
+
14
+ ```
15
+ ┌─────────────────────────────────────────────────────────┐
16
+ │ Examples Browser │
17
+ ├──────────────┬──────────────────────────────────────────┤
18
+ │ Sidebar │ Main Area │
19
+ │ │ ┌────────────────────────────────────┐ │
20
+ │ 📁 basic/ │ │ Code Panel (CodeMirror 5) │ │
21
+ │ hello_wo │ │ ┌──────────────────────────────┐ │ │
22
+ │ todo_lis │ │ │ require 'stream_weaver' │ │ │
23
+ │ 📁 agentic/ │ │ │ │ │ │
24
+ │ ... │ │ │ app "Hello" do │ │ │
25
+ │ │ │ │ text_field :name │ │ │
26
+ │ │ │ └──────────────────────────────┘ │ │
27
+ │ │ │ [▶ Run] [✓ Check Syntax] │ │
28
+ │ │ └────────────────────────────────────┘ │
29
+ └──────────────┴──────────────────────────────────────────┘
30
+ ```
31
+
32
+ ## State Structure
33
+
34
+ ```ruby
35
+ state[:selected_dir] # Currently expanded directory
36
+ state[:selected_file] # Full path to selected file
37
+ state[:code_content] # File content (or edited content in phase 2)
38
+ state[:error_message] # Syntax error from ruby -c
39
+ state[:error_modal_open] # Show/hide error modal
40
+ state[:running_pid] # Track spawned process for cleanup
41
+ ```
42
+
43
+ ## New Component: `code_editor`
44
+
45
+ Added to core StreamWeaver for reusability.
46
+
47
+ ### Usage
48
+
49
+ ```ruby
50
+ # Read-only (phase 1)
51
+ code_editor :code_content, language: :ruby, readonly: true, height: "500px"
52
+
53
+ # Editable (phase 2)
54
+ code_editor :code_content, language: :ruby, readonly: false
55
+ ```
56
+
57
+ ### Implementation Details
58
+
59
+ - Renders `<div>` with `hx-preserve="true"` to survive HTMX swaps
60
+ - Contains hidden `<textarea>` bound to state key
61
+ - Emits initialization JS for CodeMirror instance
62
+ - Single instance pattern: `window.cmEditor` persists across navigation
63
+
64
+ ### CDN Dependencies (CodeMirror 5)
65
+
66
+ ```ruby
67
+ stylesheets: [
68
+ "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.css"
69
+ ]
70
+ scripts: [
71
+ "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.js",
72
+ "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/ruby/ruby.min.js"
73
+ ]
74
+ ```
75
+
76
+ ### HTMX Integration Strategy
77
+
78
+ **Challenge:** CodeMirror creates complex DOM that HTMX can orphan/duplicate.
79
+
80
+ **Solution:**
81
+ 1. Use `hx-preserve="true"` on editor container
82
+ 2. Update content via JS: `window.cmEditor.setValue(newContent)`
83
+ 3. Initialize once on DOMContentLoaded, not on HTMX swaps
84
+
85
+ ## Interactions
86
+
87
+ ### Run Flow
88
+
89
+ 1. User clicks "Run"
90
+ 2. System runs `ruby -c` on content for syntax check
91
+ 3. If error: Parse message, show error modal
92
+ 4. If OK: Write temp file to `/tmp/sw_example_*.rb`
93
+ 5. Spawn process: `ruby /tmp/sw_example_*.rb`
94
+ 6. Track PID for cleanup
95
+ 7. Show success toast
96
+
97
+ ### Error Modal
98
+
99
+ ```
100
+ ┌─────────────────────────────────────┐
101
+ │ ❌ Syntax Error │
102
+ ├─────────────────────────────────────┤
103
+ │ Line 5: syntax error, unexpected │
104
+ │ end-of-input, expecting `end' │
105
+ │ [Close] │
106
+ └─────────────────────────────────────┘
107
+ ```
108
+
109
+ ### Cleanup
110
+
111
+ `at_exit` hook kills spawned PIDs (matches tutorial.rb pattern).
112
+
113
+ ## File Structure
114
+
115
+ ```
116
+ lib/stream_weaver/
117
+ ├── components.rb # Add CodeEditor class
118
+ ├── adapter/
119
+ │ └── alpinejs.rb # Add render_code_editor method
120
+ └── views.rb # Add CodeMirror CSS
121
+
122
+ examples/
123
+ └── advanced/
124
+ └── examples_browser.rb
125
+ ```
126
+
127
+ ## Implementation Plan
128
+
129
+ ### Phase 1: Core Component + MVP Browser
130
+
131
+ 1. Add `CodeEditor` component to `components.rb`
132
+ 2. Add `render_code_editor` to AlpineJS adapter
133
+ 3. Add CodeMirror initialization JS
134
+ 4. Build `examples_browser.rb`:
135
+ - Directory discovery
136
+ - Sidebar navigation
137
+ - Code display (read-only)
138
+ - Run button with syntax check
139
+ - Error modal
140
+
141
+ ### Phase 2: Editing Support
142
+
143
+ 1. Enable `readonly: false` mode
144
+ 2. Sync edited content back to state
145
+ 3. Track "modified" state
146
+ 4. "Run Modified" writes temp file
147
+ 5. "Reset" button restores original
148
+
149
+ ## Inspiration
150
+
151
+ - [Glimmer meta_example.rb](https://github.com/AndyObtiva/glimmer-dsl-libui/blob/master/examples/meta_example.rb) - Example discovery, categorization, launch pattern
152
+ - StreamWeaver tutorial.rb - Process spawning, PID tracking, temp file approach
@@ -0,0 +1,288 @@
1
+ # StreamWeaver Charts Design
2
+
3
+ **Date**: 2026-01-02
4
+ **Status**: Approved
5
+ **Context**: Spin-off from cultiv-os/cultiv-ai daily-brief timing instrumentation work
6
+
7
+ ## Overview
8
+
9
+ Add chart visualization components to StreamWeaver, following Streamlit's data-first philosophy while maintaining StreamWeaver's minimal-dependency ethos.
10
+
11
+ ### Goals
12
+
13
+ 1. Composable chart primitives (not a pre-built dashboard)
14
+ 2. External data pattern (charts read files, don't bloat session)
15
+ 3. Chart.js via CDN, lazy-loaded only when charts are used
16
+ 4. Path toward Streamlit-like API as Ruby DataFrame ecosystem matures
17
+
18
+ ## Components
19
+
20
+ | Component | Purpose | Primary Use |
21
+ |-----------|---------|-------------|
22
+ | `bar_chart` | Horizontal/vertical bars | Phase breakdown, comparisons |
23
+ | `line_chart` | Trend lines | Time series, progress |
24
+ | `stacked_bar_chart` | Segmented bars | Distribution across runs |
25
+ | `sparkline` | Minimal line (no axes) | Compact trends |
26
+ | `hbar_chart` | Horizontal bar shorthand | Quick horizontal bars |
27
+
28
+ ## Architecture
29
+
30
+ ```
31
+ ┌─────────────────────────────────────────────────────────┐
32
+ │ StreamWeaver App │
33
+ │ ┌─────────────────────────────────────────────────┐ │
34
+ │ │ bar_chart file: "timing.yaml", path: "..." │ │
35
+ │ └─────────────────────────────────────────────────┘ │
36
+ │ │ │
37
+ │ ▼ │
38
+ │ ┌─────────────────────────────────────────────────┐ │
39
+ │ │ Components::BarChart │ │
40
+ │ │ - Loads data (file or inline) │ │
41
+ │ │ - Normalizes to labels/values │ │
42
+ │ └─────────────────────────────────────────────────┘ │
43
+ │ │ │
44
+ │ ▼ │
45
+ │ ┌─────────────────────────────────────────────────┐ │
46
+ │ │ AlpineJS Adapter │ │
47
+ │ │ - Renders <canvas> with Chart.js config │ │
48
+ │ │ - Lazy-loads Chart.js CDN on first chart │ │
49
+ │ └─────────────────────────────────────────────────┘ │
50
+ └─────────────────────────────────────────────────────────┘
51
+ ```
52
+
53
+ ## API Design
54
+
55
+ ### Data Input Options (Priority Order)
56
+
57
+ ```ruby
58
+ # 1. File with dot-path (recommended for external data)
59
+ bar_chart file: "~/metrics/timing.yaml", path: "entries.-1.phases"
60
+
61
+ # 2. File with block transform (complex extraction)
62
+ bar_chart file: "timing.yaml" do |data|
63
+ data[:entries].last[:phases]
64
+ end
65
+
66
+ # 3. Inline hash (quick/simple)
67
+ bar_chart data: { calendar: 45, news: 120, tasks: 30 }
68
+
69
+ # 4. Explicit labels/values (full control)
70
+ bar_chart labels: %w[Calendar News Tasks],
71
+ values: [45, 120, 30]
72
+
73
+ # 5. State-bound (when data is already in state)
74
+ bar_chart data: :timing_phases # reads state[:timing_phases]
75
+ ```
76
+
77
+ ### Common Options (All Chart Types)
78
+
79
+ ```ruby
80
+ bar_chart data: timing,
81
+ title: "Phase Breakdown", # Optional header
82
+ height: "300px", # CSS height (default: 250px)
83
+ horizontal: true, # Bar direction (default: false)
84
+ colors: ["#c45b28", "#4a90d9"], # Custom palette
85
+ show_legend: false, # Hide legend (default: auto)
86
+ show_values: true # Display values on bars
87
+ ```
88
+
89
+ ### Chart-Specific Options
90
+
91
+ ```ruby
92
+ # Line chart
93
+ line_chart data: trends,
94
+ fill: true, # Area fill under line
95
+ smooth: true, # Curved vs straight lines
96
+ points: false # Hide data points
97
+
98
+ # Stacked bar
99
+ stacked_bar_chart data: runs,
100
+ stack: true, # true (stacked) or false (grouped)
101
+ normalize: false # Percentage mode (100% bars)
102
+ ```
103
+
104
+ ### DSL Methods
105
+
106
+ ```ruby
107
+ app "Dashboard" do
108
+ bar_chart data: { a: 1, b: 2 } # Vertical bars
109
+ hbar_chart data: { a: 1, b: 2 } # Horizontal bars (shorthand)
110
+ line_chart data: [1, 2, 3, 4] # Simple array = sequential x
111
+ sparkline data: [1, 2, 3, 4] # Minimal line (no axes/labels)
112
+ stacked_bar_chart data: complex_data
113
+ end
114
+ ```
115
+
116
+ ## Implementation Details
117
+
118
+ ### Chart.js Integration (Lazy Loading)
119
+
120
+ ```ruby
121
+ # In views.rb - only include when charts are present
122
+ def chart_js_assets
123
+ return unless @app.has_charts?
124
+
125
+ script(src: "https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js",
126
+ defer: true)
127
+ end
128
+ ```
129
+
130
+ ### Component Class Pattern
131
+
132
+ ```ruby
133
+ class BarChart < Base
134
+ attr_reader :labels, :values, :options
135
+
136
+ def initialize(data: nil, file: nil, path: nil, labels: nil, values: nil, **options, &block)
137
+ @data = data
138
+ @file = file
139
+ @path = path
140
+ @labels = labels
141
+ @values = values
142
+ @transform_block = block
143
+ @options = options
144
+ end
145
+
146
+ def resolve_data(state)
147
+ if @file
148
+ raw = load_file(@file)
149
+ raw = @transform_block ? @transform_block.call(raw) : extract_path(raw, @path)
150
+ normalize_hash(raw)
151
+ elsif @data.is_a?(Symbol)
152
+ normalize_hash(state[@data])
153
+ elsif @data.is_a?(Hash)
154
+ normalize_hash(@data)
155
+ else
156
+ { labels: @labels, values: @values }
157
+ end
158
+ end
159
+
160
+ def render(view, state)
161
+ view.adapter.render_bar_chart(view, self, state)
162
+ end
163
+ end
164
+ ```
165
+
166
+ ### File Loading & Path Extraction
167
+
168
+ ```ruby
169
+ # Supports YAML, JSON, expanding ~
170
+ def load_file(path)
171
+ expanded = File.expand_path(path)
172
+ case File.extname(expanded)
173
+ when '.yaml', '.yml' then YAML.safe_load_file(expanded, symbolize_names: true)
174
+ when '.json' then JSON.parse(File.read(expanded), symbolize_keys: true)
175
+ else raise "Unsupported file type: #{path}"
176
+ end
177
+ end
178
+
179
+ # Dot-path extraction: "entries.-1.phases"
180
+ def extract_path(data, path)
181
+ return data unless path
182
+ path.split('.').reduce(data) do |obj, key|
183
+ case key
184
+ when /^-?\d+$/ then obj[key.to_i] # Array index
185
+ else obj[key.to_sym] rescue obj[key] # Hash key
186
+ end
187
+ end
188
+ end
189
+ ```
190
+
191
+ ## Example: Timing Dashboard
192
+
193
+ ```ruby
194
+ # examples/charts/timing_dashboard.rb
195
+ require 'stream_weaver'
196
+
197
+ TIMING_FILE = File.expand_path("~/cultiv-os/metrics/brief-timing.yaml")
198
+
199
+ app "Daily Brief Timing", layout: :wide do
200
+ header2 "Performance Dashboard"
201
+
202
+ # Latest run breakdown
203
+ card do
204
+ header3 "Latest Run"
205
+ hbar_chart file: TIMING_FILE,
206
+ path: "entries.-1.phases",
207
+ title: "Phase Breakdown (seconds)",
208
+ show_values: true,
209
+ colors: ["#c45b28"]
210
+ end
211
+
212
+ columns widths: ['50%', '50%'] do
213
+ column do
214
+ card do
215
+ header3 "Total Duration Trend"
216
+ line_chart file: TIMING_FILE do |data|
217
+ data[:entries].last(14).map { |e| e[:wall_clock_seconds] }
218
+ end
219
+ end
220
+ end
221
+
222
+ column do
223
+ card do
224
+ header3 "News-Wait Over Time"
225
+ sparkline file: TIMING_FILE do |data|
226
+ data[:entries].last(14).map { |e| e[:phases][:"news-wait"] }
227
+ end
228
+ end
229
+ end
230
+ end
231
+
232
+ # Historical comparison
233
+ card do
234
+ header3 "Last 7 Runs by Phase"
235
+ stacked_bar_chart file: TIMING_FILE do |data|
236
+ data[:entries].last(7).map do |entry|
237
+ { label: entry[:date], **entry[:phases] }
238
+ end
239
+ end
240
+ end
241
+ end.run!
242
+ ```
243
+
244
+ ## Implementation Plan
245
+
246
+ | Phase | What | Why First |
247
+ |-------|------|-----------|
248
+ | **1** | `BarChart` component + adapter | Most useful, validates pattern |
249
+ | **2** | Chart.js CDN lazy-loading in views.rb | Required for any chart |
250
+ | **3** | File loading + dot-path extraction | Core data pattern |
251
+ | **4** | `LineChart` + `sparkline` | Second most useful |
252
+ | **5** | `StackedBarChart` | More complex data shape |
253
+ | **6** | Example dashboard | Proves composition works |
254
+ | **7** | Tests | Ensure stability |
255
+
256
+ ## Files to Create/Modify
257
+
258
+ | File | Action |
259
+ |------|--------|
260
+ | `lib/stream_weaver/components.rb` | Add BarChart, LineChart, StackedBarChart classes |
261
+ | `lib/stream_weaver/adapter/alpinejs.rb` | Add render methods for each chart type |
262
+ | `lib/stream_weaver/views.rb` | Add conditional Chart.js CDN include |
263
+ | `lib/stream_weaver/app.rb` | Add DSL methods |
264
+ | `examples/charts/timing_dashboard.rb` | New example |
265
+ | `spec/components/charts_spec.rb` | New test file |
266
+
267
+ ## Design Decisions
268
+
269
+ ### Why Chart.js via CDN?
270
+ - Zero build step (fits StreamWeaver's philosophy)
271
+ - Apps without charts pay no cost
272
+ - Well-documented, handles edge cases (scaling, tooltips, animation)
273
+
274
+ ### Why file-based data loading?
275
+ - Session storage has limits (~4KB cookies, memory for server-side)
276
+ - Timing data already lives in external YAML
277
+ - Pattern: session for UI state, external for data
278
+
279
+ ### Why explicit API first?
280
+ - Ruby hashes lack Pandas-style metadata
281
+ - Can evolve toward Streamlit-like inference later
282
+ - Optional DataFrame support (ruby-polars, Rover) when gems present
283
+
284
+ ## Future Considerations
285
+
286
+ - **DataFrame support**: Detect ruby-polars/Rover, enable richer data inference
287
+ - **Auto-refresh**: `refresh: 30` option to re-read files periodically
288
+ - **Dashboard creator UI**: Visual tool for composing charts (ultrathink later)