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,116 @@
1
+ /**
2
+ * StreamWeaver SidebarToc Scroll Spy
3
+ *
4
+ * Uses IntersectionObserver to highlight the active section in the
5
+ * sidebar table-of-contents. Works with both desktop (sticky sidebar)
6
+ * and mobile (horizontal scrollable bar) layouts.
7
+ */
8
+ (function() {
9
+ 'use strict';
10
+
11
+ function initSidebarToc() {
12
+ var navEl = document.querySelector('.sw-sidebar-toc__nav');
13
+ // A real htmx swap replaces navEl outright, so this flag is naturally
14
+ // fresh there. A host that re-invokes init without a real swap (the
15
+ // browser extension's sandbox, which has no htmx at all) would
16
+ // otherwise attach a second click listener to every link and leak a
17
+ // second IntersectionObserver watching the first one's now-detached
18
+ // targets -- not hypothetical, this exact host now calls init directly.
19
+ if (!navEl || navEl.dataset.swTocReady) return;
20
+ navEl.dataset.swTocReady = 'true';
21
+
22
+ var links = navEl.querySelectorAll('.sw-sidebar-toc__link');
23
+ if (links.length === 0) return;
24
+
25
+ // Collect target section IDs
26
+ var sectionIds = [];
27
+ links.forEach(function(link) {
28
+ var id = link.getAttribute('data-sw-toc-target');
29
+ if (id) sectionIds.push(id);
30
+ });
31
+
32
+ // Track which sections are intersecting
33
+ var visibleSections = {};
34
+
35
+ // Create IntersectionObserver
36
+ var observer = new IntersectionObserver(function(entries) {
37
+ entries.forEach(function(entry) {
38
+ visibleSections[entry.target.id] = entry.isIntersecting;
39
+ });
40
+
41
+ // Find the first visible section (in DOM order)
42
+ var activeId = null;
43
+ for (var i = 0; i < sectionIds.length; i++) {
44
+ if (visibleSections[sectionIds[i]]) {
45
+ activeId = sectionIds[i];
46
+ break;
47
+ }
48
+ }
49
+
50
+ if (activeId) {
51
+ setActiveLink(activeId, links);
52
+ }
53
+ }, {
54
+ rootMargin: '-80px 0px -60% 0px',
55
+ threshold: 0
56
+ });
57
+
58
+ // Observe each section target
59
+ sectionIds.forEach(function(id) {
60
+ var el = document.getElementById(id);
61
+ if (el) observer.observe(el);
62
+ });
63
+
64
+ // Smooth-scroll click handler
65
+ links.forEach(function(link) {
66
+ link.addEventListener('click', function(e) {
67
+ e.preventDefault();
68
+ var targetId = link.getAttribute('data-sw-toc-target');
69
+ var target = document.getElementById(targetId);
70
+ if (target) {
71
+ target.scrollIntoView({ behavior: 'smooth', block: 'start' });
72
+ setActiveLink(targetId, links);
73
+ }
74
+ });
75
+ });
76
+
77
+ // Activate the first link by default
78
+ if (links.length > 0 && sectionIds.length > 0) {
79
+ setActiveLink(sectionIds[0], links);
80
+ }
81
+ }
82
+
83
+ function setActiveLink(activeId, links) {
84
+ links.forEach(function(link) {
85
+ var isActive = link.getAttribute('data-sw-toc-target') === activeId;
86
+ if (isActive) {
87
+ link.classList.add('sw-is-active');
88
+ } else {
89
+ link.classList.remove('sw-is-active');
90
+ }
91
+ });
92
+
93
+ // On mobile, scroll the active link into view in the horizontal bar
94
+ var activeLink = document.querySelector('.sw-sidebar-toc__link.sw-is-active');
95
+ if (activeLink) {
96
+ var nav = activeLink.closest('.sw-sidebar-toc__nav');
97
+ if (nav && nav.scrollWidth > nav.clientWidth) {
98
+ activeLink.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' });
99
+ }
100
+ }
101
+ }
102
+
103
+ // Hosts with no htmx at all (the browser extension's sandbox, a one-shot
104
+ // static render) call this directly instead of relying on the htmx
105
+ // listener below -- a named, greppable hook rather than that host faking
106
+ // an htmx event it never actually loads.
107
+ window.swInitSidebarToc = initSidebarToc;
108
+
109
+ // Initialize on DOMContentLoaded and after HTMX swaps
110
+ if (document.readyState === 'loading') {
111
+ document.addEventListener('DOMContentLoaded', initSidebarToc);
112
+ } else {
113
+ initSidebarToc();
114
+ }
115
+ document.addEventListener('htmx:afterSwap', initSidebarToc);
116
+ })();
@@ -0,0 +1,103 @@
1
+ /**
2
+ * StreamWeaver Slide Navigation (sw-slide-nav.js)
3
+ *
4
+ * Alpine.js data component for slide container navigation.
5
+ * Handles both :swap and :scroll_snap modes.
6
+ *
7
+ * Usage: x-data="swSlideNav(totalSlides, mode)"
8
+ *
9
+ * Registers keyboard shortcuts via swKeyboard (if available).
10
+ */
11
+ (function() {
12
+ 'use strict';
13
+
14
+ /**
15
+ * Alpine.js data factory for slide navigation.
16
+ * @param {number} total - Total number of slides
17
+ * @param {string} mode - 'swap' or 'scroll_snap'
18
+ * @param {boolean} keyboardNav - Whether to register keyboard shortcuts
19
+ * @returns {object} Alpine.js data object
20
+ */
21
+ window.swSlideNav = function(total, mode, keyboardNav, initialSlide) {
22
+ return {
23
+ current: initialSlide || 0,
24
+ total: total,
25
+ mode: mode || 'swap',
26
+
27
+ init: function() {
28
+ if (keyboardNav !== false && typeof swKeyboard !== 'undefined') {
29
+ var self = this;
30
+
31
+ swKeyboard.register('arrowright', 'navigation', function() {
32
+ self.next();
33
+ });
34
+ swKeyboard.register('arrowleft', 'navigation', function() {
35
+ self.prev();
36
+ });
37
+ swKeyboard.register('arrowdown', 'navigation', function() {
38
+ self.next();
39
+ });
40
+ swKeyboard.register('arrowup', 'navigation', function() {
41
+ self.prev();
42
+ });
43
+ swKeyboard.register(' ', 'navigation', function() {
44
+ self.next();
45
+ });
46
+ }
47
+ },
48
+
49
+ next: function() {
50
+ if (this.current < this.total - 1) {
51
+ this.current++;
52
+ this._onNavigate();
53
+ }
54
+ },
55
+
56
+ prev: function() {
57
+ if (this.current > 0) {
58
+ this.current--;
59
+ this._onNavigate();
60
+ }
61
+ },
62
+
63
+ goTo: function(index) {
64
+ if (index >= 0 && index < this.total) {
65
+ this.current = index;
66
+ this._onNavigate();
67
+ }
68
+ },
69
+
70
+ /** Progress as a percentage (0-100) */
71
+ progress: function() {
72
+ if (this.total <= 1) return 100;
73
+ return Math.round((this.current / (this.total - 1)) * 100);
74
+ },
75
+
76
+ /** Whether the Back button should be enabled */
77
+ canPrev: function() {
78
+ return this.current > 0;
79
+ },
80
+
81
+ /** Whether the Next button should be enabled */
82
+ canNext: function() {
83
+ return this.current < this.total - 1;
84
+ },
85
+
86
+ /** Handle post-navigation actions (scroll-snap scroll, scroll-to-top for swap) */
87
+ _onNavigate: function() {
88
+ if (this.mode === 'scroll_snap') {
89
+ var slideEl = document.getElementById('sw-slide-' + this.current);
90
+ if (slideEl) {
91
+ slideEl.scrollIntoView({ behavior: 'smooth' });
92
+ }
93
+ } else if (this.mode === 'swap') {
94
+ // Scroll the slide container into view so the new slide is visible
95
+ var container = this.$el;
96
+ if (container) {
97
+ container.scrollIntoView({ behavior: 'smooth', block: 'start' });
98
+ }
99
+ }
100
+ }
101
+ };
102
+ };
103
+ })();
@@ -0,0 +1,239 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'protocol'
4
+ require_relative 'session'
5
+
6
+ module StreamWeaver
7
+ module Canvas
8
+ # Bridge between Claude Code (Unix socket) and browsers (WebSocket).
9
+ # Manages sessions and routes messages between the two.
10
+ class Bridge
11
+ DEFAULT_PORT = 4568
12
+
13
+ # Result of rendering DSL to HTML
14
+ RenderResult = Struct.new(:html, :error, :stylesheets, keyword_init: true)
15
+
16
+ attr_reader :sessions, :port
17
+
18
+ def initialize(port: DEFAULT_PORT)
19
+ @sessions = {}
20
+ @port = port
21
+ end
22
+
23
+ # Create or get an existing session
24
+ # @param name [String] Session name
25
+ # @return [Session]
26
+ def create_session(name, layout: :fluid, theme: :default)
27
+ @sessions[name] ||= Session.new(name, layout: layout, theme: theme)
28
+ end
29
+
30
+ # Get a session by name
31
+ # @param name [String] Session name
32
+ # @return [Session, nil]
33
+ def get_session(name)
34
+ @sessions[name]
35
+ end
36
+
37
+ # Close and remove a session
38
+ # @param name [String] Session name
39
+ # @return [Boolean] true if session existed
40
+ def close_session(name)
41
+ !!@sessions.delete(name)
42
+ end
43
+
44
+ # List all sessions
45
+ # @return [Array<Hash>]
46
+ def list_sessions
47
+ @sessions.values.map(&:to_h)
48
+ end
49
+
50
+ # Handle a message from Claude Code (via Unix socket)
51
+ # @param message [Hash] Message from Claude
52
+ # @return [Hash] Response message
53
+ def handle_claude_message(message)
54
+ case message[:type]
55
+ when 'create'
56
+ handle_create(message[:name], layout: (message[:layout] || :fluid).to_sym, theme: (message[:theme] || :default).to_sym)
57
+ when 'push'
58
+ handle_push(message[:name], message[:dsl], message[:source_dir])
59
+ when 'toast'
60
+ handle_toast(message[:name], message[:message], message[:variant], message[:duration])
61
+ when 'close'
62
+ handle_close(message[:name])
63
+ when 'get_dsl'
64
+ handle_get_dsl(message[:name])
65
+ when 'set_pane_id'
66
+ handle_set_pane_id(message[:name], message[:pane_id])
67
+ when 'reset'
68
+ handle_reset(message[:name], message[:all])
69
+ when 'list'
70
+ handle_list
71
+ when 'get_state'
72
+ handle_get_state(message[:name])
73
+ else
74
+ { type: 'error', message: "Unknown message type: #{message[:type]}" }
75
+ end
76
+ end
77
+
78
+ # Handle a message from browser (via WebSocket)
79
+ # @param session_name [String] Session name
80
+ # @param message [Hash] Message from browser
81
+ # @return [Hash] Event message to forward to Claude
82
+ def handle_browser_message(session_name, message)
83
+ session = get_session(session_name)
84
+ return nil unless session
85
+
86
+ # Update session state from browser
87
+ if message[:state]
88
+ session.update_state(message[:state])
89
+ end
90
+
91
+ # Return event for Claude
92
+ {
93
+ type: 'event',
94
+ name: session_name,
95
+ event: message[:type],
96
+ data: {
97
+ button: message[:button],
98
+ state: message[:state]
99
+ }
100
+ }
101
+ end
102
+
103
+ private
104
+
105
+ def handle_create(name, layout: :fluid, theme: :default)
106
+ create_session(name, layout: layout, theme: theme)
107
+ {
108
+ type: 'ready',
109
+ name: name,
110
+ url: "http://localhost:#{@port}/canvas/#{name}"
111
+ }
112
+ end
113
+
114
+ def handle_push(name, dsl, source_dir = nil)
115
+ session = get_session(name)
116
+ return { type: 'error', message: "Session not found: #{name}" } unless session
117
+
118
+ # Render DSL to HTML
119
+ result = render_dsl(dsl, session_name: name)
120
+
121
+ # Store in session for polling clients
122
+ session.set_html(result.html)
123
+
124
+ # Broadcast to all websockets (if any connected)
125
+ session.broadcast({ type: 'update', html: result.html })
126
+
127
+ # Return error info if rendering failed
128
+ if result.error
129
+ { type: 'push_error', message: result.error }
130
+ else
131
+ # Persist raw DSL, its inline stylesheets, and the directory it was
132
+ # pushed from only on successful render so a later failed push
133
+ # doesn't clobber the user's last-good content, re-skin
134
+ # (stream_weaver-9uk), or desync source_dir from the DSL it's
135
+ # paired with (canvas-doc-location-and-discovery.md).
136
+ session.set_dsl(dsl)
137
+ session.set_stylesheets(result.stylesheets)
138
+ session.set_source_dir(source_dir)
139
+ { type: 'push_ok' }
140
+ end
141
+ end
142
+
143
+ def handle_toast(name, message, variant, duration)
144
+ session = get_session(name)
145
+ return { type: 'error', message: "Session not found: #{name}" } unless session
146
+
147
+ session.queue_toast(
148
+ message: message,
149
+ variant: variant || 'warning',
150
+ duration: duration || 0
151
+ )
152
+
153
+ nil # No response to Claude for toast
154
+ end
155
+
156
+ def handle_close(name)
157
+ session = get_session(name)
158
+ pane_id = session&.pane_id
159
+ session&.broadcast(type: 'closed')
160
+ close_session(name)
161
+ { type: 'closed', name: name, pane_id: pane_id }
162
+ end
163
+
164
+ # canvas-snapshot's DSL/body source (stream_weaver-ps84): returns the
165
+ # session's current in-memory dsl plus the theme/layout it was created
166
+ # with, since the bridge only sets those at Session.new and neither
167
+ # can drift afterward (Session has no theme=/layout= setter).
168
+ def handle_get_dsl(name)
169
+ session = get_session(name)
170
+ return { type: 'error', message: "Session not found: #{name}" } unless session
171
+
172
+ { type: 'dsl', name: name, dsl: session.dsl, theme: session.theme, layout: session.layout }
173
+ end
174
+
175
+ def handle_set_pane_id(name, pane_id)
176
+ session = get_session(name)
177
+ return { type: 'error', message: "Session not found: #{name}" } unless session
178
+
179
+ session.pane_id = pane_id
180
+ { type: 'pane_id_set', name: name }
181
+ end
182
+
183
+ def handle_get_state(name)
184
+ session = get_session(name)
185
+ return { type: 'error', message: "Session not found: #{name}" } unless session
186
+
187
+ {
188
+ type: 'state',
189
+ name: name,
190
+ data: session.state
191
+ }
192
+ end
193
+
194
+ def handle_reset(name, reset_all = false)
195
+ return reset_all_sessions if reset_all
196
+
197
+ session = get_session(name) or return { type: 'error', message: "Session not found: #{name}" }
198
+ session.reset!
199
+ { type: 'reset_ok', name: name }
200
+ end
201
+
202
+ def reset_all_sessions
203
+ @sessions.each_value(&:reset!)
204
+ { type: 'reset_ok', count: @sessions.size }
205
+ end
206
+
207
+ def handle_list
208
+ { type: 'list', sessions: list_sessions }
209
+ end
210
+
211
+ # Render StreamWeaver DSL to HTML
212
+ # @param dsl [String] DSL code
213
+ # @param session_name [String] Session name for routing
214
+ # @return [RenderResult] Result with html and optional error
215
+ def render_dsl(dsl, session_name:)
216
+ # Create a mini app to evaluate the DSL
217
+ mini_app = StreamWeaver::App.new("Canvas")
218
+ mini_app.instance_eval(dsl)
219
+
220
+ # Create adapter in websocket mode
221
+ adapter = StreamWeaver::Adapter::AlpineJS.new(
222
+ url_prefix: "/canvas/#{session_name}",
223
+ mode: :websocket
224
+ )
225
+
226
+ # Render to HTML
227
+ state = {}
228
+ html = StreamWeaver::Views::AppContentView.new(mini_app, state, adapter, false).call
229
+ RenderResult.new(html: html, error: nil, stylesheets: mini_app.inline_stylesheets)
230
+ rescue => e
231
+ RenderResult.new(
232
+ html: "<div class='error'>Error: #{e.message}</div>",
233
+ error: e.message,
234
+ stylesheets: []
235
+ )
236
+ end
237
+ end
238
+ end
239
+ end