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,215 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Modal Demo - demonstrates modal dialogs
4
+ #
5
+ # Run with: ruby examples/modal_demo.rb
6
+
7
+ require_relative '../../lib/stream_weaver'
8
+
9
+ app = StreamWeaver.app "Modal Demo" do
10
+ header1 "Modal Dialogs Demo"
11
+
12
+ md "This example demonstrates **modal dialogs** with different sizes and use cases."
13
+
14
+ # Initialize state
15
+ state[:items] ||= ["Item 1", "Item 2", "Item 3"]
16
+ state[:item_to_delete] ||= nil
17
+ state[:form_submitted] ||= false
18
+
19
+ # =========================================
20
+ # Basic Modal
21
+ # =========================================
22
+ header2 "Basic Modal"
23
+
24
+ button "Open Basic Modal" do |s|
25
+ s[:basic_modal_open] = true
26
+ end
27
+
28
+ modal :basic_modal, title: "Welcome!" do
29
+ text "This is a basic modal dialog."
30
+ md "Modals are great for:"
31
+ md "- Confirmations\n- Forms\n- Detailed information\n- Alerts"
32
+
33
+ modal_footer do
34
+ button "Got it!", style: :primary do |s|
35
+ s[:basic_modal_open] = false
36
+ end
37
+ end
38
+ end
39
+
40
+ # =========================================
41
+ # Confirmation Modal
42
+ # =========================================
43
+ header2 "Confirmation Modal"
44
+ md "Click an item to delete it (with confirmation):"
45
+
46
+ card do
47
+ state[:items].each_with_index do |item, idx|
48
+ hstack justify: :between, align: :center do
49
+ text item
50
+ button "Delete", style: :secondary do |s|
51
+ s[:item_to_delete] = idx
52
+ s[:confirm_delete_open] = true
53
+ end
54
+ end
55
+ end
56
+
57
+ if state[:items].empty?
58
+ text "No items. Add some below!"
59
+ end
60
+ end
61
+
62
+ button "Add Item" do |s|
63
+ s[:items] << "Item #{s[:items].length + 1}"
64
+ end
65
+
66
+ modal :confirm_delete, title: "Confirm Delete", size: :sm do
67
+ text "Are you sure you want to delete this item?"
68
+ text "This action cannot be undone."
69
+
70
+ modal_footer do
71
+ button "Cancel", style: :secondary do |s|
72
+ s[:confirm_delete_open] = false
73
+ s[:item_to_delete] = nil
74
+ end
75
+ button "Delete" do |s|
76
+ if s[:item_to_delete]
77
+ s[:items].delete_at(s[:item_to_delete])
78
+ end
79
+ s[:confirm_delete_open] = false
80
+ s[:item_to_delete] = nil
81
+ end
82
+ end
83
+ end
84
+
85
+ # =========================================
86
+ # Form Modal
87
+ # =========================================
88
+ header2 "Form in Modal"
89
+
90
+ button "Open Form Modal" do |s|
91
+ s[:form_modal_open] = true
92
+ s[:form_submitted] = false
93
+ end
94
+
95
+ if state[:form_submitted]
96
+ card do
97
+ text "Form submitted!"
98
+ text "Name: #{state[:modal_name]}"
99
+ text "Email: #{state[:modal_email]}"
100
+ end
101
+ end
102
+
103
+ modal :form_modal, title: "Contact Form", size: :md do
104
+ text "Fill out the form below:"
105
+
106
+ text_field :modal_name, placeholder: "Your name"
107
+ text_field :modal_email, placeholder: "your@email.com"
108
+ text_area :modal_message, placeholder: "Your message...", rows: 3
109
+
110
+ modal_footer do
111
+ button "Cancel", style: :secondary do |s|
112
+ s[:form_modal_open] = false
113
+ end
114
+ button "Submit" do |s|
115
+ s[:form_submitted] = true
116
+ s[:form_modal_open] = false
117
+ end
118
+ end
119
+ end
120
+
121
+ # =========================================
122
+ # Size Variations
123
+ # =========================================
124
+ header2 "Modal Sizes"
125
+ md "Modals come in four sizes: `:sm`, `:md` (default), `:lg`, and `:xl`"
126
+
127
+ hstack spacing: :md do
128
+ button "Small (sm)" do |s|
129
+ s[:size_sm_open] = true
130
+ end
131
+ button "Medium (md)" do |s|
132
+ s[:size_md_open] = true
133
+ end
134
+ button "Large (lg)" do |s|
135
+ s[:size_lg_open] = true
136
+ end
137
+ button "XL" do |s|
138
+ s[:size_xl_open] = true
139
+ end
140
+ end
141
+
142
+ modal :size_sm, title: "Small Modal", size: :sm do
143
+ text "This is a small modal (400px max width)."
144
+ text "Good for simple confirmations."
145
+ modal_footer do
146
+ button "Close" do |s|
147
+ s[:size_sm_open] = false
148
+ end
149
+ end
150
+ end
151
+
152
+ modal :size_md, title: "Medium Modal", size: :md do
153
+ text "This is a medium modal (560px max width)."
154
+ text "The default size, good for most use cases."
155
+ modal_footer do
156
+ button "Close" do |s|
157
+ s[:size_md_open] = false
158
+ end
159
+ end
160
+ end
161
+
162
+ modal :size_lg, title: "Large Modal", size: :lg do
163
+ text "This is a large modal (800px max width)."
164
+ text "Good for forms with more fields or detailed content."
165
+ md "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
166
+ modal_footer do
167
+ button "Close" do |s|
168
+ s[:size_lg_open] = false
169
+ end
170
+ end
171
+ end
172
+
173
+ modal :size_xl, title: "Extra Large Modal", size: :xl do
174
+ text "This is an extra large modal (1140px max width)."
175
+ text "Good for complex content like data tables or multi-column layouts."
176
+
177
+ columns widths: ['50%', '50%'] do
178
+ column do
179
+ header4 "Left Column"
180
+ text "Some content on the left side."
181
+ text_field :xl_field_1, placeholder: "Field 1"
182
+ end
183
+ column do
184
+ header4 "Right Column"
185
+ text "Some content on the right side."
186
+ text_field :xl_field_2, placeholder: "Field 2"
187
+ end
188
+ end
189
+
190
+ modal_footer do
191
+ button "Close" do |s|
192
+ s[:size_xl_open] = false
193
+ end
194
+ end
195
+ end
196
+
197
+ # =========================================
198
+ # Usage notes
199
+ # =========================================
200
+ md """
201
+ ---
202
+ **How it works:**
203
+ - Modals use state key `:{modal_key}_open` to control visibility
204
+ - Set to `true` to open, `false` to close
205
+ - Close by: clicking backdrop, pressing Escape, or button action
206
+ - Sizes: `:sm` (400px), `:md` (560px), `:lg` (800px), `:xl` (1140px)
207
+ """
208
+
209
+ md "---"
210
+ button "Reset Demo", style: :secondary do |s|
211
+ s.clear
212
+ end
213
+ end
214
+
215
+ app.run!
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Navigation Components Demo
4
+ # Demonstrates Tabs, Breadcrumbs, and Dropdown/Menu components
5
+ #
6
+ # Run with: ruby -r ./lib/stream_weaver examples/navigation_demo.rb
7
+
8
+ require_relative '../../lib/stream_weaver'
9
+
10
+ app "Navigation Components", layout: :wide do
11
+ header "Navigation Components Demo"
12
+ text "This demo showcases the Phase 2 navigation components: Tabs, Breadcrumbs, and Dropdown menus."
13
+
14
+ # =========================================
15
+ # Breadcrumbs
16
+ # =========================================
17
+ header2 "Breadcrumbs"
18
+ text "Navigate hierarchical content with breadcrumb trails."
19
+
20
+ breadcrumbs do
21
+ crumb "Home", href: "/"
22
+ crumb "Products", href: "/products"
23
+ crumb "Electronics", href: "/products/electronics"
24
+ crumb "Laptops" # Current page - no href
25
+ end
26
+
27
+ text "Custom separator:"
28
+ breadcrumbs separator: ">" do
29
+ crumb "Dashboard", href: "/dashboard"
30
+ crumb "Settings", href: "/settings"
31
+ crumb "Profile"
32
+ end
33
+
34
+ # =========================================
35
+ # Tabs - Line Variant (default)
36
+ # =========================================
37
+ header2 "Tabs - Line Variant"
38
+ text "Classic underline tabs - clean and minimal."
39
+
40
+ tabs :demo_tabs, variant: :line do
41
+ tab "Overview" do
42
+ card do
43
+ card_header "Overview Tab"
44
+ card_body do
45
+ text "This is the overview content. Tabs maintain state on the server, so switching tabs persists across page refreshes."
46
+ text_field :overview_input, placeholder: "Type something here..."
47
+ end
48
+ end
49
+ end
50
+
51
+ tab "Details" do
52
+ card do
53
+ card_header "Details Tab"
54
+ card_body do
55
+ text "Here are the details. Each tab can contain any StreamWeaver components."
56
+ vstack spacing: :sm do
57
+ checkbox :detail_option1, "Enable feature A"
58
+ checkbox :detail_option2, "Enable feature B"
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ tab "Settings" do
65
+ card do
66
+ card_header "Settings Tab"
67
+ card_body do
68
+ text "Configure your preferences here."
69
+ select :theme, ["Light", "Dark", "System"], default: "System"
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ # =========================================
76
+ # Tabs - Enclosed Variant
77
+ # =========================================
78
+ header2 "Tabs - Enclosed Variant"
79
+ text "Traditional boxed tabs with borders."
80
+
81
+ tabs :enclosed_tabs, variant: :enclosed do
82
+ tab "First" do
83
+ text "Content for the first enclosed tab."
84
+ end
85
+ tab "Second" do
86
+ text "Content for the second enclosed tab."
87
+ end
88
+ tab "Third" do
89
+ text "Content for the third enclosed tab."
90
+ end
91
+ end
92
+
93
+ # =========================================
94
+ # Tabs - Soft Rounded Variant
95
+ # =========================================
96
+ header2 "Tabs - Soft Rounded Variant"
97
+ text "Modern pill-style tabs with rounded backgrounds."
98
+
99
+ tabs :rounded_tabs, variant: :"soft-rounded" do
100
+ tab "All" do
101
+ text "Showing all items."
102
+ end
103
+ tab "Active" do
104
+ text "Showing only active items."
105
+ end
106
+ tab "Archived" do
107
+ text "Showing archived items."
108
+ end
109
+ end
110
+
111
+ # =========================================
112
+ # Dropdown Menu
113
+ # =========================================
114
+ header2 "Dropdown Menu"
115
+ text "Click the button to reveal a dropdown menu with actions."
116
+
117
+ hstack spacing: :md, align: :center do
118
+ dropdown do
119
+ trigger do
120
+ button "Actions", style: :primary
121
+ end
122
+
123
+ menu do
124
+ menu_item "Edit" do |s|
125
+ s[:last_action] = "Edit clicked"
126
+ end
127
+ menu_item "Duplicate" do |s|
128
+ s[:last_action] = "Duplicate clicked"
129
+ end
130
+ menu_divider
131
+ menu_item "Archive" do |s|
132
+ s[:last_action] = "Archive clicked"
133
+ end
134
+ menu_item "Delete", style: :destructive do |s|
135
+ s[:last_action] = "Delete clicked"
136
+ end
137
+ end
138
+ end
139
+
140
+ dropdown do
141
+ trigger do
142
+ button "More Options", style: :secondary
143
+ end
144
+
145
+ menu do
146
+ menu_item "View Profile" do |s|
147
+ s[:last_action] = "View Profile clicked"
148
+ end
149
+ menu_item "Account Settings" do |s|
150
+ s[:last_action] = "Account Settings clicked"
151
+ end
152
+ menu_divider
153
+ menu_item "Sign Out", style: :destructive do |s|
154
+ s[:last_action] = "Sign Out clicked"
155
+ end
156
+ end
157
+ end
158
+
159
+ text -> (s) { s[:last_action] ? "Last action: #{s[:last_action]}" : "Click a menu item..." }
160
+ end
161
+
162
+ # =========================================
163
+ # Combined Example
164
+ # =========================================
165
+ header2 "Combined Example"
166
+ text "Navigation components work together seamlessly."
167
+
168
+ breadcrumbs do
169
+ crumb "App", href: "/"
170
+ crumb "Users", href: "/users"
171
+ crumb "John Doe"
172
+ end
173
+
174
+ hstack spacing: :md, justify: :between, align: :center do
175
+ header3 "User Profile"
176
+
177
+ dropdown do
178
+ trigger do
179
+ button "User Actions", style: :secondary
180
+ end
181
+
182
+ menu do
183
+ menu_item "Edit Profile" do |s|
184
+ s[:profile_action] = "editing"
185
+ end
186
+ menu_item "Change Password" do |s|
187
+ s[:profile_action] = "password"
188
+ end
189
+ menu_divider
190
+ menu_item "Deactivate Account", style: :destructive do |s|
191
+ s[:profile_action] = "deactivate"
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ tabs :user_tabs do
198
+ tab "Info" do
199
+ vstack spacing: :sm do
200
+ text "Name: John Doe"
201
+ text "Email: john@example.com"
202
+ text "Role: Administrator"
203
+ end
204
+ end
205
+
206
+ tab "Activity" do
207
+ text "Recent activity will be shown here."
208
+ end
209
+
210
+ tab "Permissions" do
211
+ checkbox_group :permissions, select_all: "Select All", select_none: "Clear" do
212
+ item "read" do
213
+ text "Read - View content"
214
+ end
215
+ item "write" do
216
+ text "Write - Create and edit content"
217
+ end
218
+ item "delete" do
219
+ text "Delete - Remove content"
220
+ end
221
+ item "admin" do
222
+ text "Admin - Full access"
223
+ end
224
+ end
225
+ end
226
+ end
227
+ end.run!
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Route tabs demo: two url: true tab groups (bookmarkable, back/forward-aware)
4
+ # plus a deferred form to exercise the morph-does-not-revert-the-tab behavior.
5
+ #
6
+ # PORT=4573 ruby examples/layout/route_tabs_demo.rb
7
+ # open http://127.0.0.1:4573/?view=2&panel=1
8
+ require_relative "../../lib/stream_weaver"
9
+
10
+ app = StreamWeaver.app "Route Tabs Demo" do
11
+ header1 "Route Tabs Demo"
12
+
13
+ tabs :view, url: true do
14
+ tab "Overview" do
15
+ header3 "Overview"
16
+ text "The overview panel. Bookmark me with ?view=0."
17
+ end
18
+ tab "Findings" do
19
+ header3 "Findings"
20
+ text "The findings panel. Bookmark me with ?view=1."
21
+ end
22
+ tab "Details" do
23
+ header3 "Details"
24
+ text "The details panel. Bookmark me with ?view=2."
25
+ form :notes_form do
26
+ text_field :note, placeholder: "Add a note"
27
+ submit "Save note" do |values|
28
+ state[:last_note] = values[:note]
29
+ end
30
+ end
31
+ md "Last saved note: **#{state[:last_note] || '(none)'}**"
32
+ end
33
+ end
34
+
35
+ # Read BELOW the tabs declaration: above it, state[:view] still holds the raw
36
+ # pre-authority param (or stale session value) -- the resolved integer only
37
+ # exists once the group has applied URL authority.
38
+ md "Active view index (server-resolved): **#{state[:view].inspect}**"
39
+
40
+ md "---"
41
+
42
+ tabs :panel, url: true, variant: :enclosed do
43
+ tab "Alpha" do
44
+ text "Second group, panel Alpha (?panel=0)."
45
+ end
46
+ tab "Beta" do
47
+ text "Second group, panel Beta (?panel=1)."
48
+ end
49
+ end
50
+ end
51
+
52
+ app.run!(open_browser: false) if __FILE__ == $PROGRAM_NAME
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # URL Routing Demo
4
+ # Shows deep-linking, bookmarkable URLs, and browser back/forward
5
+ #
6
+ # Run with: ruby -r ./lib/stream_weaver examples/layout/routing_demo.rb
7
+ #
8
+ # Try:
9
+ # - Click nav items and watch the URL bar update
10
+ # - Visit /dashboard or /settings directly
11
+ # - Press back/forward in the browser
12
+
13
+ require_relative '../../lib/stream_weaver'
14
+
15
+ app "URL Routing Demo" do
16
+ route_by :page, home: "/", dashboard: "/dashboard", settings: "/settings"
17
+
18
+ state[:page] ||= :home
19
+
20
+ navbar do
21
+ nav_item "Home", href: "/", active: state[:page] == :home
22
+ nav_item "Dashboard", href: "/dashboard", active: state[:page] == :dashboard
23
+ nav_item "Settings", href: "/settings", active: state[:page] == :settings
24
+ end
25
+
26
+ case state[:page]
27
+ when :home
28
+ header "Home"
29
+ text "Welcome! Click the nav links above — the URL bar updates and each page is bookmarkable."
30
+ text "Try visiting /dashboard or /settings directly."
31
+ button "Go to Dashboard" do |s|
32
+ s[:page] = :dashboard
33
+ end
34
+
35
+ when :dashboard
36
+ header "Dashboard"
37
+ text "You navigated to /dashboard. Bookmark this URL and come back — it'll load right here."
38
+ text_field :dashboard_note, label: "Notes", placeholder: "Type something..."
39
+ button "Go to Settings" do |s|
40
+ s[:page] = :settings
41
+ end
42
+
43
+ when :settings
44
+ header "Settings"
45
+ text "You're at /settings. Press the browser Back button to go back."
46
+ checkbox :notifications, "Enable notifications"
47
+ checkbox :dark_mode, "Dark mode"
48
+ button "Back to Home" do |s|
49
+ s[:page] = :home
50
+ end
51
+ end
52
+ end.run!
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Demonstrates the scroll_box component for constraining vertical space.
5
+ #
6
+ # Use cases shown:
7
+ # 1. Static long content - Terms & conditions, documentation
8
+ # 2. Live log output - Auto-scrolling log tail via SSE timers
9
+ # 3. Chat/message history - Scrollable message list
10
+ # 4. Data list - Long lists constrained to a fixed height
11
+ #
12
+ # Run with: bundle exec ruby examples/layout/scroll_box_demo.rb
13
+
14
+ require_relative "../../lib/stream_weaver"
15
+
16
+ # Simulated log messages for the live demo
17
+ LOG_MESSAGES = [
18
+ "INFO -- : Request GET /api/v2/users completed in 42ms",
19
+ "INFO -- : Cache HIT for key session:abc123",
20
+ "DEBUG -- : SQL (1.2ms) SELECT * FROM users WHERE active = true",
21
+ "INFO -- : Request POST /api/v2/orders completed in 118ms",
22
+ "WARN -- : Slow query detected (>100ms) on orders#index",
23
+ "INFO -- : Background job ProcessEmailJob enqueued",
24
+ "INFO -- : WebSocket connection opened for channel:dashboard",
25
+ "ERROR -- : Redis connection timeout after 5000ms, retrying...",
26
+ "INFO -- : Redis reconnected successfully",
27
+ "DEBUG -- : Cache MISS for key metrics:daily, computing...",
28
+ "INFO -- : Request GET /health returned 200 in 2ms",
29
+ "WARN -- : Rate limit approaching for IP 192.168.1.42 (85/100)",
30
+ "INFO -- : Deploy webhook received, version v2.14.3",
31
+ "INFO -- : Asset compilation completed in 3.2s",
32
+ "ERROR -- : Failed to send notification email: SMTP timeout",
33
+ ].freeze
34
+
35
+ CHAT_MESSAGES = [
36
+ { user: "Alice", text: "Has anyone seen the latency spike on the API gateway?" },
37
+ { user: "Bob", text: "Yeah, I'm looking into it now. Seems like a connection pool issue." },
38
+ { user: "Carol", text: "We saw something similar last week. Check the pgbouncer config." },
39
+ { user: "Alice", text: "Good call. The max_client_conn was set too low." },
40
+ { user: "Bob", text: "Bumped it to 200, deploying now." },
41
+ { user: "Carol", text: "Keep an eye on memory usage after that change." },
42
+ { user: "Bob", text: "Will do. Monitoring dashboard is up." },
43
+ { user: "Alice", text: "Latency is back to normal. Nice work!" },
44
+ ].freeze
45
+
46
+ TERMS = <<~TERMS
47
+ 1. ACCEPTANCE OF TERMS. By accessing and using this service, you accept and agree to be bound by the terms and provision of this agreement.
48
+
49
+ 2. PROVISION OF SERVICE. The service is provided "as is" and on an "as available" basis. We make no warranties, expressed or implied, and hereby disclaim all warranties including without limitation, implied warranties of merchantability and fitness for a particular purpose.
50
+
51
+ 3. USER CONDUCT. You agree not to use the service for any unlawful purpose or in any way that could damage, disable, or impair the service. You agree not to attempt to gain unauthorized access to any part of the service.
52
+
53
+ 4. INTELLECTUAL PROPERTY. All content included in the service, such as text, graphics, logos, and software, is the property of the service provider and is protected by copyright and other intellectual property laws.
54
+
55
+ 5. PRIVACY POLICY. Your use of the service is also governed by our Privacy Policy, which is incorporated into these terms by reference. Please review our Privacy Policy to understand our practices.
56
+
57
+ 6. LIMITATION OF LIABILITY. In no event shall the service provider be liable for any indirect, incidental, special, consequential, or punitive damages resulting from your use of the service.
58
+
59
+ 7. MODIFICATIONS TO TERMS. We reserve the right to modify these terms at any time. Continued use of the service after changes constitutes acceptance of the new terms.
60
+
61
+ 8. GOVERNING LAW. These terms shall be governed by and construed in accordance with the laws of the applicable jurisdiction, without regard to conflict of law provisions.
62
+
63
+ 9. TERMINATION. We may terminate or suspend your access to the service at any time, without prior notice, for conduct that we believe violates these terms or is harmful to other users.
64
+
65
+ 10. CONTACT. If you have questions about these terms, please contact us through the appropriate channels provided on our website.
66
+ TERMS
67
+
68
+ App = app "ScrollBox Demo", layout: :wide do
69
+ hstack(justify: :between, align: :center) do
70
+ header1 "ScrollBox Component Demo"
71
+ theme_switcher
72
+ end
73
+ md "The `scroll_box` constrains content to a fixed height with vertical scrolling."
74
+
75
+ columns widths: ["50%", "50%"] do
76
+ # ---- Left column ----
77
+ column do
78
+ # Use case 1: Static long content
79
+ header2 "Terms & Conditions"
80
+ text "A classic scroll_box use case - long legal text in a constrained area:"
81
+
82
+ scroll_box max_height: "200px" do
83
+ md TERMS
84
+ end
85
+
86
+ # Use case 2: Data list
87
+ header2 "File Browser"
88
+ text "Long lists constrained to avoid page bloat:"
89
+
90
+ scroll_box max_height: "250px" do
91
+ vstack spacing: :xs, divider: true do
92
+ 50.times do |i|
93
+ ext = %w[.rb .js .yml .md .json].sample
94
+ size = rand(1..500)
95
+ hstack spacing: :sm, justify: :between, align: :center do
96
+ phrase "app/models/file_#{i + 1}#{ext}"
97
+ badge "#{size} KB", size: :sm
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ # ---- Right column ----
105
+ column do
106
+ # Use case 3: Chat messages
107
+ header2 "Team Chat"
108
+ text "Message history in a scrollable container:"
109
+
110
+ scroll_box max_height: "200px" do
111
+ vstack spacing: :sm do
112
+ CHAT_MESSAGES.each do |msg|
113
+ div do
114
+ md "**#{msg[:user]}**: #{msg[:text]}"
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ # Use case 4: Live log output (SSE-updated)
121
+ header2 "Live Application Log"
122
+ text "Log entries arrive via SSE and accumulate in the scroll_box:"
123
+
124
+ scroll_box max_height: "250px" do
125
+ div id: "log-output" do
126
+ text "[#{Time.now.strftime('%H:%M:%S')}] Log stream started..."
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ # --- Live log timer ---
133
+ every(2) do |streamer|
134
+ msg = LOG_MESSAGES.sample
135
+ timestamp = Time.now.strftime("%H:%M:%S.%L")
136
+ level_color = if msg.start_with?("ERROR")
137
+ "color: #ef4444"
138
+ elsif msg.start_with?("WARN")
139
+ "color: #eab308"
140
+ elsif msg.start_with?("DEBUG")
141
+ "color: #6b7280"
142
+ else
143
+ "color: #a3a3a3"
144
+ end
145
+
146
+ streamer.append("#log-output") do
147
+ div style: "font-family: monospace; font-size: 0.85rem; padding: 2px 0; #{level_color}" do
148
+ text "[#{timestamp}] #{msg}"
149
+ end
150
+ end
151
+ end
152
+ end
153
+
154
+ App.run! if __FILE__ == $0