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,271 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'net/http'
5
+ require 'uri'
6
+ require_relative '../cli'
7
+
8
+ module StreamWeaver
9
+ module Templates
10
+ # Multi-step wizard template with branching support
11
+ # Takes JSON config, handles full flow, returns collected data
12
+ #
13
+ # Linear usage (steps run in order):
14
+ # StreamWeaver::Templates::Wizard.run(session: "test", config: {
15
+ # "title" => "Project Setup",
16
+ # "steps" => [
17
+ # { "title" => "Info", "fields" => [...] },
18
+ # { "title" => "Features", "fields" => [...] }
19
+ # ]
20
+ # })
21
+ #
22
+ # Branching usage (steps have ids and next conditions):
23
+ # StreamWeaver::Templates::Wizard.run(session: "test", config: {
24
+ # "title" => "Project Setup",
25
+ # "steps" => [
26
+ # { "id" => "type", "title" => "Type", "fields" => [
27
+ # { "type" => "select", "key" => "project_type", "options" => ["Web", "CLI"] }
28
+ # ], "next" => { "branch_on" => "project_type", "Web" => "web_opts", "CLI" => "cli_opts" }},
29
+ # { "id" => "web_opts", "title" => "Web Options", "fields" => [...], "next" => "done" },
30
+ # { "id" => "cli_opts", "title" => "CLI Options", "fields" => [...], "next" => "done" },
31
+ # { "id" => "done", "title" => "Confirm", "fields" => [] }
32
+ # ]
33
+ # })
34
+ #
35
+ class Wizard
36
+ def initialize(session:, config:, port: nil)
37
+ @session = session
38
+ @config = config
39
+ @port = port || detect_port
40
+ @collected = {}
41
+ @steps = config['steps'] || []
42
+ @title = config['title'] || 'Wizard'
43
+ # Unique run ID to prevent browser autocomplete from previous runs
44
+ @run_id = Time.now.to_i.to_s(36)
45
+ # Build step lookup by id
46
+ @steps_by_id = @steps.each_with_object({}) do |step, hash|
47
+ hash[step['id']] = step if step['id']
48
+ end
49
+ # Track visited steps for "step X of Y" display
50
+ @visited_steps = []
51
+ end
52
+
53
+ def run
54
+ # Clear any stale submissions before starting
55
+ clear_submissions
56
+
57
+ # Start with first step
58
+ current_step = @steps.first
59
+ step_num = 0
60
+
61
+ while current_step
62
+ step_num += 1
63
+ @visited_steps << current_step
64
+
65
+ # Render step UI
66
+ push_step(current_step, step_num)
67
+
68
+ # Wait for submission
69
+ data = wait_for_submission
70
+
71
+ # Collect data (skip _button key, strip run_id suffix from keys)
72
+ data.each do |key, value|
73
+ next if key == '_button'
74
+ # Strip the run_id suffix to get original key name
75
+ original_key = key.to_s.sub(/_#{@run_id}$/, '')
76
+ @collected[original_key] = value
77
+ end
78
+
79
+ # Determine next step
80
+ current_step = resolve_next_step(current_step)
81
+ end
82
+
83
+ # Return collected data as JSON
84
+ @collected
85
+ end
86
+
87
+ private
88
+
89
+ def resolve_next_step(current_step)
90
+ next_config = current_step['next']
91
+
92
+ # No next config - try next step in array (linear mode)
93
+ if next_config.nil?
94
+ current_index = @steps.index(current_step)
95
+ return @steps[current_index + 1] if current_index && current_index < @steps.length - 1
96
+ return nil
97
+ end
98
+
99
+ # String - go to step by id
100
+ if next_config.is_a?(String)
101
+ return nil if next_config == 'done' || next_config == 'end'
102
+ return @steps_by_id[next_config]
103
+ end
104
+
105
+ # Hash - branch based on collected value
106
+ if next_config.is_a?(Hash)
107
+ branch_key = next_config['branch_on']
108
+ if branch_key
109
+ value = @collected[branch_key]
110
+ next_id = next_config[value] || next_config['_default']
111
+ return nil if next_id.nil? || next_id == 'done' || next_id == 'end'
112
+ return @steps_by_id[next_id]
113
+ end
114
+ end
115
+
116
+ nil
117
+ end
118
+
119
+ def detect_port
120
+ pid_file = File.expand_path('~/.streamweaver/service.pid')
121
+ return 4576 unless File.exist?(pid_file)
122
+
123
+ data = JSON.parse(File.read(pid_file))
124
+ data['port'] || 4576
125
+ rescue
126
+ 4576
127
+ end
128
+
129
+ def push_step(step, step_num)
130
+ dsl = build_step_dsl(step, step_num)
131
+ push_dsl(dsl)
132
+ end
133
+
134
+ def build_step_dsl(step, step_num)
135
+ title = step['title'] || "Step #{step_num}"
136
+ description = step['description']
137
+ fields = step['fields'] || []
138
+ # Check if this step has a next (not final)
139
+ has_next = step['next'] != 'done' && step['next'] != 'end' && step['next'] != nil
140
+ # Also check if linear mode and not last step
141
+ if step['next'].nil?
142
+ current_index = @steps.index(step)
143
+ has_next = current_index && current_index < @steps.length - 1
144
+ end
145
+
146
+ lines = []
147
+ # Show wizard title at top
148
+ lines << "header2 \"#{@title}\""
149
+ lines << "card title: \"Step #{step_num}: #{title}\" do"
150
+ lines << " text \"#{description}\"" if description
151
+
152
+ fields.each do |field|
153
+ lines << build_field_dsl(field)
154
+ end
155
+
156
+ # Show collected data so far if not first step
157
+ if step_num > 1 && @collected.any?
158
+ lines << " text \"\""
159
+ lines << " text \"Your selections:\""
160
+ @collected.each do |key, value|
161
+ display_value = value.is_a?(Array) ? value.join(', ') : value.to_s
162
+ lines << " text \" #{key}: #{display_value}\""
163
+ end
164
+ end
165
+
166
+ lines << " button \"#{has_next ? 'Next' : 'Finish'}\""
167
+ lines << "end"
168
+
169
+ lines.join("\n")
170
+ end
171
+
172
+ def build_field_dsl(field)
173
+ type = field['type']
174
+ key = field['key']
175
+ # Use run_id in field name to prevent browser autocomplete from previous runs
176
+ unique_key = "#{key}_#{@run_id}"
177
+ label = field['label'] || key.to_s.capitalize
178
+
179
+ lines = []
180
+ case type
181
+ when 'text'
182
+ placeholder = field['placeholder'] || label
183
+ lines << " text \"#{label}:\""
184
+ lines << " text_field :#{unique_key}, placeholder: \"#{placeholder}\""
185
+ when 'select'
186
+ options = field['options'] || []
187
+ options_str = options.map { |o| "\"#{o}\"" }.join(', ')
188
+ lines << " text \"#{label}:\""
189
+ lines << " select :#{unique_key}, [#{options_str}]"
190
+ when 'checkbox'
191
+ lines << " checkbox :#{unique_key}, \"#{label}\""
192
+ when 'radio'
193
+ options = field['options'] || []
194
+ options_str = options.map { |o| "\"#{o}\"" }.join(', ')
195
+ lines << " text \"#{label}:\""
196
+ lines << " radio_group :#{unique_key}, [#{options_str}]"
197
+ else
198
+ lines << " text \"Unknown field type: #{type}\""
199
+ end
200
+ lines.join("\n")
201
+ end
202
+
203
+ def push_dsl(dsl)
204
+ # Render DSL to HTML first
205
+ html = StreamWeaver::CLI.render_dsl_to_html(dsl, session_name: @session)
206
+
207
+ uri = URI("http://localhost:#{@port}/live/#{URI.encode_www_form_component(@session)}/push")
208
+
209
+ request = Net::HTTP::Post.new(uri)
210
+ request.set_form_data(
211
+ 'content' => html,
212
+ 'target' => '#main',
213
+ 'action' => 'replace'
214
+ )
215
+
216
+ response = Net::HTTP.start(uri.hostname, uri.port) do |http|
217
+ http.request(request)
218
+ end
219
+
220
+ unless response.is_a?(Net::HTTPSuccess)
221
+ raise "Push failed: #{response.code} - #{response.body}"
222
+ end
223
+ end
224
+
225
+ def clear_submissions
226
+ # Drain any stale submissions from the queue
227
+ uri = URI("http://localhost:#{@port}/live/#{URI.encode_www_form_component(@session)}/submissions")
228
+ loop do
229
+ response = Net::HTTP.get_response(uri)
230
+ break unless response.is_a?(Net::HTTPSuccess)
231
+
232
+ data = JSON.parse(response.body)
233
+ submissions = data['submissions'] || []
234
+ break if submissions.empty?
235
+ # The GET consumes the submission, so just loop until empty
236
+ end
237
+ rescue
238
+ # Ignore errors during cleanup
239
+ end
240
+
241
+ def wait_for_submission(timeout: 300)
242
+ start_time = Time.now
243
+
244
+ loop do
245
+ if Time.now - start_time > timeout
246
+ raise "Timeout waiting for submission"
247
+ end
248
+
249
+ uri = URI("http://localhost:#{@port}/live/#{URI.encode_www_form_component(@session)}/submissions")
250
+
251
+ response = Net::HTTP.get_response(uri)
252
+ if response.is_a?(Net::HTTPSuccess)
253
+ data = JSON.parse(response.body)
254
+ submissions = data['submissions'] || []
255
+
256
+ if submissions.any?
257
+ return submissions.first['data']
258
+ end
259
+ end
260
+
261
+ sleep 0.3
262
+ end
263
+ end
264
+
265
+ # Class method for easy invocation
266
+ def self.run(session:, config:, port: nil)
267
+ new(session: session, config: config, port: port).run
268
+ end
269
+ end
270
+ end
271
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StreamWeaver
4
+ class Theme
5
+ # JavaScript generation for auto-mode theme toggle.
6
+ # Manages data-theme attribute on <html>, meta theme-color,
7
+ # and localStorage persistence.
8
+ #
9
+ # Auto-mode is opt-in: it does not activate unless
10
+ # theme_toggle(mode: :auto) is called in the app.
11
+ #
12
+ # sw- CSS prefix convention:
13
+ # Toggle button: sw-theme-toggle
14
+ # State attribute: data-sw-theme="dark|light|auto"
15
+ module AutoMode
16
+ # Generate the inline JavaScript for auto-mode detection and toggling.
17
+ # This script runs immediately (no DOMContentLoaded wait) to prevent
18
+ # flash of wrong theme.
19
+ #
20
+ # @param meta_colors [Hash] theme-color values for light/dark
21
+ # @return [String] JavaScript code
22
+ def self.inline_script(meta_colors: { light: "#f8f8f8", dark: "#1a1a1a" }, default_mode: :auto)
23
+ <<~JS
24
+ (function() {
25
+ // sw-theme auto-mode: follows OS prefers-color-scheme with localStorage override
26
+ var STORAGE_KEY = 'sw-theme-preference';
27
+ var META_COLORS = #{meta_colors.to_json};
28
+ var DEFAULT_MODE = #{default_mode.to_s.to_json};
29
+
30
+ function getEffectiveTheme() {
31
+ var pref = localStorage.getItem(STORAGE_KEY) || DEFAULT_MODE;
32
+ if (pref === 'dark' || pref === 'light') return pref;
33
+ // auto or no preference: follow OS
34
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
35
+ }
36
+
37
+ function getStoredPreference() {
38
+ return localStorage.getItem(STORAGE_KEY) || DEFAULT_MODE;
39
+ }
40
+
41
+ function applyTheme(effective) {
42
+ var html = document.documentElement;
43
+ html.classList.toggle('dark', effective === 'dark');
44
+ html.setAttribute('data-sw-theme', effective);
45
+
46
+ // Update meta theme-color
47
+ var meta = document.querySelector('meta[name="theme-color"]');
48
+ if (!meta) {
49
+ meta = document.createElement('meta');
50
+ meta.name = 'theme-color';
51
+ document.head.appendChild(meta);
52
+ }
53
+ meta.content = META_COLORS[effective] || META_COLORS.light;
54
+ }
55
+
56
+ // Apply immediately to prevent FOUC
57
+ applyTheme(getEffectiveTheme());
58
+
59
+ // Listen for OS preference changes (only matters in auto mode)
60
+ var mql = window.matchMedia('(prefers-color-scheme: dark)');
61
+ mql.addEventListener('change', function() {
62
+ if (getStoredPreference() === 'auto') {
63
+ applyTheme(getEffectiveTheme());
64
+ }
65
+ });
66
+
67
+ // Global toggle function, callable from Alpine.js or anywhere
68
+ window.swToggleTheme = function(mode) {
69
+ if (mode === 'auto') {
70
+ localStorage.setItem(STORAGE_KEY, 'auto');
71
+ } else if (mode === 'dark' || mode === 'light') {
72
+ localStorage.setItem(STORAGE_KEY, mode);
73
+ } else {
74
+ // Cycle: auto -> light -> dark -> auto
75
+ var current = getStoredPreference();
76
+ var next = current === 'auto' ? 'light' : current === 'light' ? 'dark' : 'auto';
77
+ localStorage.setItem(STORAGE_KEY, next);
78
+ }
79
+ applyTheme(getEffectiveTheme());
80
+ return getStoredPreference();
81
+ };
82
+
83
+ // Query current state
84
+ window.swGetTheme = function() {
85
+ return {
86
+ preference: getStoredPreference(),
87
+ effective: getEffectiveTheme()
88
+ };
89
+ };
90
+ })();
91
+ JS
92
+ end
93
+
94
+ # Generate the Alpine.js x-data for the theme toggle button
95
+ #
96
+ # @return [String] Alpine.js data expression
97
+ def self.alpine_data(default_mode: :auto)
98
+ <<~JS.gsub(/\s+/, " ").strip
99
+ {
100
+ preference: (localStorage.getItem('sw-theme-preference') || '#{default_mode}'),
101
+ get effective() {
102
+ if (this.preference === 'dark' || this.preference === 'light') return this.preference;
103
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
104
+ },
105
+ toggle() {
106
+ this.preference = swToggleTheme();
107
+ },
108
+ setMode(mode) {
109
+ this.preference = swToggleTheme(mode);
110
+ }
111
+ }
112
+ JS
113
+ end
114
+ end
115
+ end
116
+ end