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
data/README.md ADDED
@@ -0,0 +1,565 @@
1
+ # StreamWeaver
2
+
3
+ ![HTML/CSS/JS chaos weaving into a clean StreamWeaver doc](assets/streamweaver-hero.jpg)
4
+
5
+ **Express intention, get interface. The joy of Ruby applied to UI.**
6
+
7
+ ```ruby
8
+ app "Meeting Notes" do
9
+ header "1:1 with #{manager}"
10
+ md notes
11
+ end.run!
12
+ ```
13
+
14
+ That's it. No HTML. No CSS. No JavaScript. No webpack.
15
+
16
+ ---
17
+
18
+ ## Platform support
19
+
20
+ | Tier | Platforms | What you get |
21
+ |---|---|---|
22
+ | Tested & supported | macOS + iTerm2 + Chrome + gh | The full `get-started` course: a controller canvas window, an agent worker tab, live demo panes, one-click sharing |
23
+ | Expected to work, untested | macOS plain terminal, Linux, WSL | The degraded path: `canvas-read` plus a browser-tab canvas, no panel/worker automation |
24
+ | Known degraded path | Any of the above | `streamweaver get-started --degraded` |
25
+ | Future | Windows native, other terminals/multiplexers | Not yet built |
26
+
27
+ A few things worth being explicit about:
28
+
29
+ - `streamweaver panel` and the worker-tab automation it drives are **macOS + iTerm2 only** — they script iTerm2's Python API.
30
+ - `canvas-read` is a plain local web server. It's the most portable piece of StreamWeaver and doesn't need iTerm2, Chrome, or `gh` — see [docs/canvas-read.md](docs/canvas-read.md).
31
+ - The Chrome extension and the `gh` CLI are enhancers for *sharing* docs (Gist links that render nicely), not requirements for reading them locally.
32
+ - Never assume a port. Every command that starts a server prints the URL it actually bound — StreamWeaver auto-increments past busy ports.
33
+
34
+ ---
35
+
36
+ ## Why StreamWeaver?
37
+
38
+ **TL;DR:** I want a quick UI. What do I need? Some text, a few inputs, a button. Why isn't *that* the interface? Instead: HTML, CSS, JavaScript, backend wiring... Streamlit showed me the interface *can* just be "text, inputs, button." StreamWeaver brings that to Ruby - and it turns out this minimal approach is perfect for AI agents too.
39
+
40
+ [Skip to Quick Start →](#quick-start)
41
+
42
+ <details>
43
+ <summary><b>The Longer Story</b></summary>
44
+
45
+ ### The Interface Should Be What You Need
46
+
47
+ Think about what a simple UI actually requires: some text, a few inputs, maybe a dropdown, a button. That's it. That's what you're trying to build. But to get there you're dealing with HTML structure, CSS styling, JavaScript (or a backend framework), controllers, state management...
48
+
49
+ Streamlit's brilliance was recognizing that the DSL *can* just be the interface. You describe what you need - text, inputs, button - and you're done. StreamWeaver brings that philosophy to Ruby.
50
+
51
+ ### Why This Matters for AI Agents
52
+
53
+ When you're building with Claude Code (or other AI coding assistants), this "what matters" approach pays off even more:
54
+
55
+ 1. **Token efficiency** - The LLM generates a concise DSL instead of verbose HTML/React. **5-10x fewer tokens** means faster responses and lower costs.
56
+
57
+ 2. **Rich interactions** - Instead of walls of terminal text for complex decisions, spin up an actual UI. What would be 5 pages of back-and-forth becomes one well-designed form.
58
+
59
+ 3. **Persistent output** - Claude generates content (meeting notes, analysis, reports) but terminal output scrolls away. Canvas mode gives Claude a persistent display that stays visible.
60
+
61
+ 4. **Data-only generation** - Pre-build your StreamWeaver app once, then have the LLM just generate the *data* to feed it. Minimal tokens, maximum speed.
62
+
63
+ ```ruby
64
+ # Agent generates just this data:
65
+ meetings = [{ title: "1:1 with Sarah", notes: "..." }, ...]
66
+
67
+ # Pre-built app renders it:
68
+ MeetingNotesApp.new(meetings: meetings).run!
69
+ ```
70
+
71
+ </details>
72
+
73
+ ---
74
+
75
+ ## vs. "I can show you a mockup, but it's token-intensive"
76
+
77
+ Ask Claude for a visual and you'll often get something like this:
78
+
79
+ > Some of what we're working on might be easier to explain if I can show it to you in a web browser. I can put together mockups, layout comparisons, and other visuals as we go. This feature is still new and can be token-intensive. Want to try it? (Requires opening a local URL)
80
+
81
+ That's an honest warning, not a bug - hand-written HTML/React artifacts *are* token-intensive, so tools built that way have to gate visual explanation behind an expensive, novel, opt-in mode. TypeScript-flavored tooling doesn't have Ruby's sensibilities about generating a lot of interface from a little code.
82
+
83
+ StreamWeaver's answer: mockups, layout comparisons, and diagrams aren't a special mode you opt into - they're the default, at 5-10x fewer tokens than hand-written markup (see below), and they're **two-way interactive** while a static artifact isn't. Same local-browser mechanism, none of the token tax.
84
+
85
+ ---
86
+
87
+ ## The Modes
88
+
89
+ StreamWeaver evolved through real needs, resulting in four modes:
90
+
91
+ | Mode | Command | What It Does | Use Case |
92
+ |------|---------|--------------|----------|
93
+ | **Standalone** | `ruby app.rb` | Auto-port, auto-browser, persistent server | Quick apps, utilities, prototypes |
94
+ | **Agentic** | `app.run_once!` | Popup UI → collect input → return JSON → quit | Claude Code needs structured input |
95
+ | **Canvas** | `streamweaver live SESSION` | Persistent display that Claude updates | Output that doesn't scroll away |
96
+ | **Service** | `streamweaver app.rb` | Single server, multiple apps | Development, showcase, tutorial |
97
+
98
+ ### `ruby app.rb` vs `streamweaver app.rb`
99
+
100
+ | Command | Process Model | When to Use |
101
+ |---------|---------------|-------------|
102
+ | `ruby app.rb` | You own the process, runs until you kill it | Quick one-off scripts, standalone apps |
103
+ | `streamweaver app.rb` | Managed by background service, multi-app routing | Multiple apps side-by-side, development |
104
+
105
+ The service mode runs one Sinatra server for all apps instead of one process per app.
106
+
107
+ ### From Local Script to Mobile Dashboard
108
+
109
+ StreamWeaver apps cover a wide range — the same DSL works whether you're hacking a quick one-off or running a persistent dashboard you check from your phone:
110
+
111
+ | Scenario | Host | Port | How |
112
+ |----------|------|------|-----|
113
+ | **Quick one-off** | localhost | auto-detect | `ruby app.rb` — browser opens, use it, Ctrl+C |
114
+ | **Agentic popup** | localhost | auto-detect | `app.run_once!` — collect input, return JSON, exit |
115
+ | **Puma-dev** | localhost | from Puma-dev | `config.ru` + `puma-dev link` — access at `http://myapp.test` |
116
+ | **Mobile/Tailscale** | `0.0.0.0` | fixed | `STREAMWEAVER_HOST=0.0.0.0 STREAMWEAVER_PORT=4580 ruby app.rb` |
117
+ | **LAN access** | `0.0.0.0` | fixed | Same — any device on your network can reach it |
118
+ | **Always-on dashboard** | `0.0.0.0` | fixed | Bookmark `http://your-machine:4580` on your phone |
119
+
120
+ **Environment variables** (overridden by code options if set):
121
+ - `STREAMWEAVER_HOST` — bind address (default: `127.0.0.1`)
122
+ - `STREAMWEAVER_PORT` — fixed port (default: auto-detect from 4567)
123
+ - `PORT` — standard port variable (used by Puma-dev, Heroku, etc.)
124
+
125
+ For quick local work, the defaults are perfect — auto-find a port, open the browser, done. For mobile or remote access (Tailscale, LAN), set a fixed host and port so your URL stays stable across restarts. For Puma-dev, see [examples/puma_dev](examples/puma_dev).
126
+
127
+ ---
128
+
129
+ ## Quick Start
130
+
131
+ Three steps to awesome:
132
+
133
+ ```bash
134
+ gem install stream_weaver
135
+ streamweaver install
136
+ streamweaver get-started
137
+ ```
138
+
139
+ `install` wires StreamWeaver into Claude Code (permissions + skills); `get-started` walks you
140
+ through a short interactive course, next to your own terminal. On macOS + iTerm2 you get the full
141
+ experience; anywhere else it falls back to a browser tab automatically (or jump straight there with
142
+ `streamweaver get-started --degraded`). See [Platform support](#platform-support) above.
143
+
144
+ Prefer to explore on your own first?
145
+
146
+ ```bash
147
+ # Interactive tutorial
148
+ streamweaver tutorial
149
+
150
+ # Browse examples
151
+ streamweaver showcase
152
+
153
+ # Run any example
154
+ ruby examples/basic/hello_world.rb
155
+ ```
156
+
157
+ ---
158
+
159
+ ## Standalone Mode
160
+
161
+ The simplest path - one Ruby file, one command:
162
+
163
+ ```ruby
164
+ # todo.rb
165
+ require 'stream_weaver'
166
+
167
+ app "Todo List" do
168
+ text_field :new_todo, placeholder: "What needs doing?"
169
+
170
+ button "Add" do |state|
171
+ state[:todos] ||= []
172
+ state[:todos] << state[:new_todo]
173
+ state[:new_todo] = ""
174
+ end
175
+
176
+ state[:todos]&.each do |todo|
177
+ div { text "• #{todo}" }
178
+ end
179
+ end.run!
180
+ ```
181
+
182
+ ```bash
183
+ ruby todo.rb
184
+ # Browser opens at http://localhost:4567
185
+ ```
186
+
187
+ ---
188
+
189
+ ## Puma-dev Mode
190
+
191
+ Run StreamWeaver apps with [Puma-dev](https://github.com/puma/puma-dev) for memorable local URLs like `http://myapp.test` that are always available without manually starting the server:
192
+
193
+ ```ruby
194
+ # config.ru
195
+ require 'bundler/setup'
196
+ require 'stream_weaver'
197
+
198
+ App = app "My App" do
199
+ header1 "Hello from Puma-dev!"
200
+ text_field :name, placeholder: "Your name"
201
+ end
202
+
203
+ run App
204
+ ```
205
+
206
+ ```bash
207
+ # Link to Puma-dev
208
+ puma-dev link
209
+
210
+ # Access at http://[directory-name].test
211
+ # Browser won't auto-open - perfect for on-demand access
212
+ ```
213
+
214
+ **Key differences from standalone mode:**
215
+ - Uses the `PORT` environment variable set by Puma-dev
216
+ - Browser doesn't auto-open (you access the URL when you need it)
217
+ - App starts automatically on first request
218
+
219
+ See [examples/puma_dev](examples/puma_dev) for a complete example.
220
+
221
+ ---
222
+
223
+ ## Agentic Mode
224
+
225
+ When Claude Code needs structured input, not terminal menus:
226
+
227
+ ```ruby
228
+ result = app "Project Setup" do
229
+ header "Configure New Project"
230
+ text_field :name, placeholder: "Project name"
231
+ select :database, ["PostgreSQL", "SQLite", "MySQL"]
232
+ checkbox :docker, "Include Docker setup"
233
+ end.run_once!
234
+
235
+ # Browser opens, user fills form, returns:
236
+ # { "name" => "myapp", "database" => "PostgreSQL", "docker" => true }
237
+ ```
238
+
239
+ The browser opens, user fills the form, JSON returns to the calling script. Perfect for AI workflows that need human input.
240
+
241
+ ---
242
+
243
+ ## Canvas Mode
244
+
245
+ A persistent browser display that Claude Code can update. Content stays visible instead of scrolling away in the terminal.
246
+
247
+ ```bash
248
+ # Start the canvas (keeps running)
249
+ streamweaver live mynotes
250
+
251
+ # Claude Code pushes content as it works
252
+ streamweaver push mynotes --dsl 'md "# Analysis Results\n\n## Key Findings\n- ..."'
253
+
254
+ # Canvas updates in real-time
255
+ ```
256
+
257
+ **Pro tip:** iTerm2 has a [built-in browser](https://iterm2.com/documentation-web.html) that fits into split panes - run Claude Code on the left, canvas on the right, same window.
258
+
259
+ ### Saving & Sharing Docs
260
+
261
+ A canvas session's "Save as doc" button writes it to disk in two possible
262
+ formats:
263
+
264
+ - **`.rb`** — the DSL source, canonical. Always lossless.
265
+ - **`.org`** — a human-readable, roundtrippable export (`streamweaver
266
+ org-export <file.rb>` / `org-render <file.org>` from the CLI, or the
267
+ Save-as-Org button in the UI). `:doc`-vocabulary content (`doc_header`,
268
+ `callout`, `card`, `table`, etc.) round-trips cleanly and reads like a real
269
+ document in GitHub's own file view or any generic org-mode viewer, with no
270
+ StreamWeaver tooling required. Content outside that vocabulary still
271
+ round-trips (a verbatim-recovered raw block), just without the readability
272
+ payoff. Saving as org shows a coverage notice when a doc isn't a good fit
273
+ for the format — never blocks the save.
274
+
275
+ Where a doc saves is automatic today: repo-local
276
+ (`<repo>/docs/streamweaver_canvas/`) if you're inside a git repo, `~/.streamweaver/canvas`
277
+ otherwise — no way to choose yet. An explicit "global vs. this repo" toggle
278
+ is designed but not built (`docs/plans/canvas-doc-location-and-discovery.md`);
279
+ expect this UX to change.
280
+
281
+ Saved `.rb`/`.org` docs checked into a GitHub repo render with full
282
+ StreamWeaver styling via the [browser extension](extension/README.md) — see
283
+ below.
284
+
285
+ ### Templates for Common Patterns
286
+
287
+ ```bash
288
+ # Quick selection
289
+ streamweaver template choices mysession '{"title": "Pick DB", "options": ["PostgreSQL", "SQLite"]}'
290
+ # Returns: {"choice": "PostgreSQL"}
291
+
292
+ # Yes/No confirmation
293
+ streamweaver template confirm mysession '{"title": "Delete?", "message": "Cannot be undone"}'
294
+ # Returns: {"confirmed": true}
295
+
296
+ # Multi-step wizard
297
+ streamweaver template wizard mysession '{"steps": [...]}'
298
+
299
+ # Data table with selection
300
+ streamweaver template table mysession '{"headers": ["File", "Size"], "rows": [...]}'
301
+ ```
302
+
303
+ ---
304
+
305
+ ## Browser Extension
306
+
307
+ Push a `.rb` or `.org` doc to GitHub or a Gist, and even without this
308
+ extension it's still legible — `.org` already reads close to markdown there.
309
+ With this extension, either format renders exactly as designed: sidebar nav,
310
+ callouts, cards, tables, live Mermaid diagrams, all compiled and displayed
311
+ entirely inside the browser. No StreamWeaver install, no Ruby, no server, no
312
+ vendor lock-in — just a "View rendered" button next to the file. Works on
313
+ public and private repo blob pages, GitHub Gists (including multi-file
314
+ gists), and local files dropped straight into the viewer with no GitHub at
315
+ all.
316
+
317
+ That's the actual point of shipping this publicly: docs like this only get to
318
+ move freely between a work team and outside collaborators once anyone with a
319
+ browser can open one.
320
+
321
+ ### Install
322
+
323
+ **[Chrome Web Store](https://chromewebstore.google.com/detail/streamweaver-doc-viewer/odjjednfpfiagefgpcfdlelldphmpcgj)**
324
+ — the primary path. One click, no build step, works right away.
325
+
326
+ **Dev path** — building from source, for contributors working on the
327
+ extension itself:
328
+
329
+ ```bash
330
+ bin/vendor_browser_assets # once
331
+ bin/build_extension
332
+ ```
333
+
334
+ Then `chrome://extensions` → Developer mode → Load unpacked → select
335
+ `extension/`. See [`extension/README.md`](extension/README.md) for how it
336
+ works, the full architecture, and current known gaps.
337
+
338
+ ### Share a doc
339
+
340
+ Two ways to hand someone a doc, depending on how much staying power it needs:
341
+
342
+ - **Quick collab** — push the doc to a Gist, then send the Gist link plus
343
+ the [extension link](https://chromewebstore.google.com/detail/streamweaver-doc-viewer/odjjednfpfiagefgpcfdlelldphmpcgj).
344
+ No install for you, one click for them: after installing, they open the
345
+ Gist link and a **View rendered** button appears in that file's header
346
+ bar, next to its Raw/Copy buttons — click it. (If the Gist tab was
347
+ already open before installing, refresh the page first; a newly
348
+ installed extension only runs in tabs loaded after install.) Good for a
349
+ one-off review or looping in someone outside the team.
350
+ - **Level up** — commit the same `.org` file to the team repo. It renders
351
+ identically in the repo's own file view, no format change required. The
352
+ doc graduates from shared-once to living with the code it documents.
353
+
354
+ ---
355
+
356
+ ## Components
357
+
358
+ ### Text & Headers
359
+
360
+ ```ruby
361
+ text "Plain text"
362
+ md "**Markdown** with *formatting* and [links](url)"
363
+ header "Section" # <h2> default
364
+ header1 "Page Title" # through header6
365
+ ```
366
+
367
+ ### Form Inputs
368
+
369
+ ```ruby
370
+ text_field :name, placeholder: "Name", default: "Alice"
371
+ text_area :bio, rows: 5
372
+ select :color, ["Red", "Green", "Blue"], default: "Green"
373
+ checkbox :agree, "I accept"
374
+ radio_group :size, ["S", "M", "L"]
375
+ ```
376
+
377
+ ### Buttons
378
+
379
+ ```ruby
380
+ button "Primary" do |state|
381
+ state[:clicked] = true
382
+ end
383
+
384
+ button "Secondary", style: :secondary do |state|
385
+ # ...
386
+ end
387
+ ```
388
+
389
+ ### Scoped Fragments
390
+
391
+ Fragments keep the normal single full DSL rerun while limiting the HTML swapped into
392
+ the page. Interactive controls target their enclosing fragment automatically.
393
+
394
+ ```ruby
395
+ action :refresh, updates: :sidebar_count do |state, account_id|
396
+ state[:account_id] = account_id
397
+ end
398
+
399
+ fragment :results do
400
+ text "Account: #{state[:account_id]}"
401
+ button "Refresh", action: :refresh, key: 42
402
+ end
403
+
404
+ fragment :sidebar_count do
405
+ text "Selected: #{state[:account_id] ? 1 : 0}"
406
+ end
407
+ ```
408
+
409
+ Use `updates:` on an action or button to refresh additional named fragments with
410
+ out-of-band swaps. Server-rendered content in other fragments is intentionally left
411
+ unchanged; omit fragments when an interaction must refresh the whole app. If a target
412
+ disappears or routing changes, StreamWeaver automatically falls back to a full swap.
413
+
414
+ ### Layout
415
+
416
+ ```ruby
417
+ columns widths: ['30%', '70%'] do
418
+ column { text "Sidebar" }
419
+ column { text "Main" }
420
+ end
421
+
422
+ vstack spacing: :md do
423
+ text "Item 1"
424
+ text "Item 2"
425
+ end
426
+
427
+ hstack justify: :between do
428
+ button "Cancel", style: :secondary
429
+ button "Save"
430
+ end
431
+
432
+ card do
433
+ header3 "Title"
434
+ text "Content"
435
+ end
436
+
437
+ collapsible "Show Details" do
438
+ text "Hidden until clicked"
439
+ end
440
+ ```
441
+
442
+ ### Tables
443
+
444
+ ```ruby
445
+ # Simple
446
+ table headers: ["Name", "Age"], rows: [["Alice", 30], ["Bob", 25]]
447
+
448
+ # From hashes (headers inferred)
449
+ table [{ name: "Alice", age: 30 }, { name: "Bob", age: 25 }]
450
+
451
+ # With formatters
452
+ table users do
453
+ column :name
454
+ column :balance, format: :currency
455
+ column :joined, format: :date
456
+ end
457
+
458
+ # Interactive
459
+ table data, sortable: true, sticky_header: true, striped: true, markdown: true
460
+ ```
461
+
462
+ ### Charts
463
+
464
+ ```ruby
465
+ bar_chart data: { sales: 100, costs: 60, profit: 40 }
466
+ line_chart data: [12, 19, 8, 15, 22], fill: true
467
+ pie_chart data: { frontend: 40, backend: 35, devops: 25 }
468
+ sparkline data: [45, 52, 48, 61, 55, 67, 72]
469
+ ```
470
+
471
+ ### Navigation & Modals
472
+
473
+ ```ruby
474
+ tabs :settings do
475
+ tab("General") { text_field :name }
476
+ tab("Advanced") { checkbox :debug, "Debug" }
477
+ end
478
+
479
+ modal :confirm, title: "Are you sure?" do
480
+ text "This cannot be undone."
481
+ modal_footer do
482
+ button "Cancel", style: :secondary do |s| s[:confirm_open] = false end
483
+ button "Delete" do |s| do_delete; s[:confirm_open] = false end
484
+ end
485
+ end
486
+ ```
487
+
488
+ ### URL Routing
489
+
490
+ Deep links, bookmarkable URLs, and browser back/forward — no router library, just a
491
+ bidirectional map between a URL path and a slice of state:
492
+
493
+ ```ruby
494
+ route_with(
495
+ parser: ->(path) { path == '/goals' ? { main_nav: 2 } : nil },
496
+ builder: ->(state) { state[:main_nav] == 2 ? '/goals' : nil }
497
+ )
498
+ ```
499
+
500
+ `route_by` covers the simple single-key case. See [`docs/routing.md`](docs/routing.md) for the
501
+ full contract — including a **Common Pitfalls** section worth reading before an app grows past a
502
+ handful of routes (state merges rather than replaces on every GET, so an incomplete route table
503
+ fails silently rather than loudly).
504
+
505
+ ### Feedback
506
+
507
+ ```ruby
508
+ alert(variant: :success) { text "Saved!" }
509
+ progress_bar value: 75, variant: :success
510
+ spinner label: "Loading..."
511
+ toast_container position: :top_right
512
+ ```
513
+
514
+ ### Dashboard Components
515
+
516
+ For operations dashboards (best with `theme: :dark`):
517
+
518
+ ```ruby
519
+ status_dot status: :green, pulse: true
520
+ badge "5", variant: :danger
521
+ stat_display value: 42, label: "TASKS"
522
+ priority_item priority: :critical, title: "Server down"
523
+
524
+ app_shell sidebar_width: "320px" do
525
+ main { header "Dashboard" }
526
+ sidebar(header: "Alerts") { priority_item priority: :high, title: "CPU 90%" }
527
+ end
528
+ ```
529
+
530
+ ---
531
+
532
+ ## Theming
533
+
534
+ ```ruby
535
+ app "My App", theme: :dark do
536
+ # Dark mode with glow effects
537
+ end
538
+
539
+ app "Report", theme: :document do
540
+ # Reading-optimized (serif, paper background)
541
+ end
542
+ ```
543
+
544
+ **Built-in themes:** `:default`, `:dashboard`, `:document`, `:dark`
545
+
546
+ ---
547
+
548
+ ## More Resources
549
+
550
+ - [Canvas Mode Documentation](docs/canvas-roadmap.md)
551
+ - [canvas-read: the document shelf](docs/canvas-read.md) — the most portable way to read StreamWeaver docs, no iTerm2 required
552
+ - [Templates Reference](docs/templates.md)
553
+ - [Components Reference](docs/components_reference.md)
554
+ - [Service Mode](docs/SERVICE_MODE.md)
555
+ - [URL Routing](docs/routing.md) — `route_by`/`route_with`, plus Common Pitfalls for larger apps
556
+
557
+ ---
558
+
559
+ ## Contributing
560
+
561
+ Contributions welcome! [GitHub repository](https://github.com/fkchang/stream_weaver)
562
+
563
+ ## License
564
+
565
+ MIT License - see [LICENSE.txt](LICENSE.txt)
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ desc "Run the manual Phase-1 dispatch benchmark"
9
+ task :bench do
10
+ ruby File.expand_path("bench/run.rb", __dir__)
11
+ end
12
+
13
+ task default: :spec
Binary file
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sinatra/base"
4
+ require "phlex"
5
+ require_relative "../support"
6
+
7
+ module StreamWeaverBench
8
+ module Baselines
9
+ class LedgerRows < Phlex::HTML
10
+ def initialize(people) = @people = people
11
+ def view_template
12
+ tbody(id: "people-rows") do
13
+ @people.each do |person|
14
+ tr(id: "person-#{person[:id]}") do
15
+ td { plain person[:name] }; td { plain person[:email] }; td { plain person[:touched] }
16
+ td { button { plain "Edit" }; button { plain "Delete" } }
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ class LedgerRow < Phlex::HTML
24
+ def initialize(person) = @person = person
25
+ def view_template
26
+ tr(id: "person-#{@person[:id]}") do
27
+ td { plain @person[:name] }; td { plain @person[:email] }; td { plain @person[:touched] }
28
+ td { button { plain "Edit" }; button { plain "Delete" } }
29
+ end
30
+ end
31
+ end
32
+
33
+ class LedgerToolbar < Phlex::HTML
34
+ def initialize(error) = @error = error
35
+ def view_template = div(id: "toolbar") { button { plain "Create" }; button { plain "Invalid" }; span { plain @error } }
36
+ end
37
+
38
+ module Ledger
39
+ module_function
40
+
41
+ def build
42
+ metrics = Metrics.new
43
+ people = StreamWeaverBench.deep_copy(PEOPLE)
44
+ error = ""
45
+ app = Class.new(Sinatra::Base)
46
+ app.set :environment, :test
47
+ app.set :show_exceptions, false
48
+ app.get("/") { LedgerToolbar.new(error).call + "<table>#{LedgerRows.new(people).call}</table>" }
49
+ app.post("/create") { metrics.callback!; people << { id: 51, name: "New Person", email: "new@example.test", touched: "2026-07-01" }; LedgerRow.new(people.last).call }
50
+ app.post("/edit") { metrics.callback!; people.first[:name] = "Edited Person"; LedgerRow.new(people.first).call }
51
+ app.post("/invalid") { metrics.callback!; error = "Name is required"; LedgerToolbar.new(error).call }
52
+ app.post("/filter") { metrics.callback!; LedgerRows.new(people.select { |p| p[:name].downcase.include?(params[:query].to_s.downcase) }).call }
53
+ app.post("/delete") { metrics.callback!; people.shift; "" }
54
+ [app, metrics]
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sinatra/base"
4
+ require "phlex"
5
+ require_relative "../support"
6
+
7
+ module StreamWeaverBench
8
+ module Baselines
9
+ class StoryDetail < Phlex::HTML
10
+ def initialize(story) = @story = story
11
+ def view_template
12
+ section(id: "story-detail") do
13
+ h2 { plain @story[:title] }
14
+ @story[:notes].each { |note| p { plain note } }
15
+ button { plain "Append note" }; button { plain "Move story" }
16
+ end
17
+ end
18
+ end
19
+
20
+ class BoardColumn < Phlex::HTML
21
+ def initialize(stories, column) = (@stories, @column = stories, column)
22
+ def view_template
23
+ section(id: "column-#{@column.downcase}") do
24
+ h2 { plain @column }
25
+ @stories.select { |story| story[:column] == @column }.each { |story| article { plain "#{story[:id]} — #{story[:title]}" } }
26
+ end
27
+ end
28
+ end
29
+
30
+ module Warroom
31
+ module_function
32
+
33
+ def build
34
+ metrics = Metrics.new
35
+ stories = StreamWeaverBench.deep_copy(STORIES)
36
+ app = Class.new(Sinatra::Base)
37
+ app.set :environment, :test
38
+ app.set :show_exceptions, false
39
+ app.get("/") do
40
+ %w[Ready Active Done].map { |column| BoardColumn.new(stories, column).call }.join + StoryDetail.new(stories.first).call
41
+ end
42
+ app.post("/note") { metrics.callback!; stories.first[:notes] << "Appended note"; StoryDetail.new(stories.first).call }
43
+ app.post("/move") do
44
+ metrics.callback!; stories.first[:column] = "Active"
45
+ BoardColumn.new(stories, "Ready").call + BoardColumn.new(stories, "Active").call
46
+ end
47
+ [app, metrics]
48
+ end
49
+ end
50
+ end
51
+ end