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,142 @@
1
+ # DHH Review: Future Refactors
2
+
3
+ Notes from DHH-style code review of service mode implementation (2026-01-02).
4
+
5
+ ## Route Duplication Between Service and SinatraApp
6
+
7
+ The `/apps/:app_id/update`, `/apps/:app_id/action/:button_id`, `/apps/:app_id/event/:key`, and `/apps/:app_id/form/:form_name` routes in `service.rb` are nearly identical to their counterparts in `server.rb`. Extract shared logic to `RouteHelpers` module.
8
+
9
+ ```ruby
10
+ # lib/stream_weaver/route_helpers.rb
11
+ module StreamWeaver
12
+ module RouteHelpers
13
+ def coerce_param_value(value, current_value)
14
+ case
15
+ when value.is_a?(Array) then value
16
+ when %w[on true].include?(value) then true
17
+ when value == "false" then false
18
+ when current_value.is_a?(Array) then Array(value)
19
+ else value
20
+ end
21
+ end
22
+
23
+ def sync_params_to_state(state, excluded_keys: [])
24
+ excluded = %w[splat captures app_id button_id] + excluded_keys.map(&:to_s)
25
+ params.each do |key, value|
26
+ next if excluded.include?(key)
27
+ state[key.to_sym] = coerce_param_value(value, state[key.to_sym])
28
+ end
29
+ end
30
+ end
31
+ end
32
+ ```
33
+
34
+ ## PID File Management Should Be Its Own Class
35
+
36
+ Extract PID file logic from Service (lines 96-129) to dedicated class:
37
+
38
+ ```ruby
39
+ # lib/stream_weaver/pid_file.rb
40
+ module StreamWeaver
41
+ class PidFile
42
+ PATH = File.expand_path('~/.streamweaver/server.pid').freeze
43
+
44
+ class << self
45
+ def write(port:)
46
+ FileUtils.mkdir_p(File.dirname(PATH))
47
+ File.write(PATH, "port=#{port}\npid=#{Process.pid}\n")
48
+ end
49
+
50
+ def delete
51
+ File.delete(PATH) if File.exist?(PATH)
52
+ end
53
+
54
+ def read
55
+ return unless File.exist?(PATH)
56
+ content = File.read(PATH)
57
+ { port: content[/port=(\d+)/, 1]&.to_i,
58
+ pid: content[/pid=(\d+)/, 1]&.to_i }
59
+ end
60
+
61
+ def process_alive?
62
+ info = read or return false
63
+ Process.kill(0, info[:pid])
64
+ true
65
+ rescue Errno::ESRCH
66
+ delete
67
+ false
68
+ end
69
+ end
70
+ end
71
+ end
72
+ ```
73
+
74
+ ## Admin Inline Styles Should Use Semantic CSS Classes
75
+
76
+ The Admin class has inline styles everywhere. Should use semantic classes that reference the theme system:
77
+
78
+ ```ruby
79
+ # Before
80
+ div style: "display: inline-flex; align-items: center; gap: 0.5rem; ..." do
81
+
82
+ # After
83
+ div class: "admin-status-bar" do
84
+ status_indicator :running, "Port #{Service.settings.port}"
85
+ end
86
+ ```
87
+
88
+ ## CLI Repetitive Error Handling
89
+
90
+ Every CLI method that contacts the service has identical error handling. Extract to helper:
91
+
92
+ ```ruby
93
+ # Before: Repeated 5+ times
94
+ rescue Errno::ECONNREFUSED
95
+ puts "Error: Could not connect to StreamWeaver service"
96
+ exit 1
97
+ end
98
+
99
+ # After
100
+ def self.with_service_connection
101
+ yield
102
+ rescue Errno::ECONNREFUSED
103
+ puts "Error: Could not connect to StreamWeaver service"
104
+ exit 1
105
+ end
106
+ ```
107
+
108
+ ## CLI Case Statement Could Be More Declarative
109
+
110
+ ```ruby
111
+ # After: Declarative command mapping
112
+ COMMANDS = {
113
+ 'serve' => :serve,
114
+ 'run' => :run_app,
115
+ 'list' => :list_apps,
116
+ 'remove' => ->(args) { remove_app(args.first) },
117
+ # ...
118
+ }.freeze
119
+
120
+ def self.run(args)
121
+ return help if args.empty?
122
+ command = args.shift
123
+ handler = COMMANDS[command]
124
+
125
+ case handler
126
+ when Symbol then send(handler, args)
127
+ when Proc then handler.call(args)
128
+ when nil then handle_unknown_command(command, args)
129
+ end
130
+ end
131
+ ```
132
+
133
+ ## Remove Comment Banners
134
+
135
+ The Service class has banner comments like:
136
+ ```ruby
137
+ # =========================================
138
+ # Service Management Routes
139
+ # =========================================
140
+ ```
141
+
142
+ Well-organized code documents itself through structure. If you need a banner to find routes, the class is too large.
@@ -0,0 +1,47 @@
1
+ # StreamWeaver Goes Static
2
+
3
+ You've always been able to write reactive, server-rendered UIs without touching JavaScript. Now the same Ruby DSL runs entirely in the browser — no server, no round-trips, no ops.
4
+
5
+ Here's a complete interactive app:
6
+
7
+ ```ruby
8
+ app "Hello World" do
9
+ header1 "Welcome to StreamWeaver!"
10
+
11
+ text_field :name, placeholder: "Enter your name"
12
+
13
+ if state[:name] && state[:name].strip != ""
14
+ text "Hello, #{state[:name]}!"
15
+
16
+ checkbox :subscribe, "Subscribe to newsletter"
17
+
18
+ if state[:subscribe]
19
+ text "You're subscribed!"
20
+ end
21
+ end
22
+ end
23
+ ```
24
+
25
+ That's not a server app stripped of its `require` lines. That is the whole thing — identical DSL, running in-browser via Opal. State lives in the page. Interactions re-execute the block and patch the DOM with morphdom. No server involved.
26
+
27
+ ## One command to deploy
28
+
29
+ ```bash
30
+ streamweaver opal-build app.rb
31
+ ```
32
+
33
+ That produces a `dist/` folder: `index.html`, `app.js`, `morphdom.min.js`. Drag it to GitHub Pages, Netlify, S3 — anywhere that serves files. Done.
34
+
35
+ ## What this is not
36
+
37
+ It's not React. It's not Next.js. There's no webpack config, no `package.json`, no `node_modules`, no TypeScript compilation step, no context switch between your "frontend language" and your "backend language" because there is only one language. You write Ruby. You ship Ruby. The toolchain is one gem.
38
+
39
+ The mental model stays the same whether you're building a server-rendered dashboard or a static page: declare what the UI looks like given the current state, let StreamWeaver figure out the rest.
40
+
41
+ ## Phase 1 — honest about scope
42
+
43
+ The core components are covered: `app`, `header`, `text_field`, `checkbox`, `button`, `md`, `div`. State is fully reactive in-browser.
44
+
45
+ What's not there yet: `feed`, `streamer`, and `service_client` (they require a server by definition), plus `mermaid`, `chartjs`, `tabs`, and `table` (coming in Phase 2). If your app uses those, keep running it server-side — that path isn't going anywhere.
46
+
47
+ Full component parity is the goal. Phase 1 gets you surprisingly far.
@@ -0,0 +1,184 @@
1
+ # StreamWeaver → Opal → NPM: Direction Document
2
+
3
+ **Author:** Forrest Chang
4
+ **Date:** May 2026
5
+ **Status:** Vision / Future Work — Do Not Pursue Until Prerequisites Met
6
+ **Purpose:** Preserve this architectural vision so it's not lost. This is a north star, not a sprint.
7
+
8
+ ---
9
+
10
+ ## The Vision
11
+
12
+ StreamWeaver is currently a Ruby gem with a Sinatra server backend. The vision: **package StreamWeaver as an NPM module via Opal's "Sole" engine**, making it installable as:
13
+
14
+ ```bash
15
+ npm install stream-weaver
16
+ ```
17
+
18
+ This would allow JavaScript developers to use a Ruby-authored reactive UI framework **without knowing Ruby**. They import it, write component trees in JS (which would be Opal-transpiled Ruby API), and get all the same reactive rendering, component library, and DSL expressiveness — in their existing JS stack.
19
+
20
+ This is a compelling story for reaching the much larger JavaScript ecosystem while keeping the canonical source of truth in Ruby.
21
+
22
+ ---
23
+
24
+ ## What Already Exists
25
+
26
+ ### Opal Adapter
27
+ `lib/stream_weaver/adapter/opal.rb` — an Opal-compatible adapter layer already exists. This was written with browser execution in mind.
28
+
29
+ ### Opal Runtime
30
+ `lib/stream_weaver/opal/runtime.rb` contains:
31
+ - `OpalRuntime` — Opal-clean entry point
32
+ - `OpalBridge` — browser ↔ Ruby communication bridge
33
+ - `ReactiveState` — client-side reactive state, already Opal-clean
34
+
35
+ These three modules are **already ready** for Opal compilation. They have no server-side dependencies.
36
+
37
+ ### JS Stubs
38
+ `lib/stream_weaver/opal/stubs/` contains stubs for:
39
+ - `marked.umd.js` — markdown rendering (browser equivalent of kramdown)
40
+ - `morphdom.min.js` — DOM diffing
41
+
42
+ ### Opal Compatibility Spike
43
+ `bin/opal_spike.rb` — an existing spike script that tests which modules compile cleanly through Opal. Run this to audit current compilation status.
44
+
45
+ ---
46
+
47
+ ## What "Sole" Does
48
+
49
+ Opal's **Sole** engine is the build tool that makes Ruby→NPM publishing practical:
50
+
51
+ 1. Reads your `.gemspec`
52
+ 2. Generates a `package.json` from gem metadata
53
+ 3. Compiles all Ruby source files to JavaScript via Opal
54
+ 4. Creates an `index.js` entry point that exports your Ruby classes/modules as JS
55
+ 5. Enables `npm publish` — the package is now installable like any JS library
56
+
57
+ The result: `npm install stream-weaver` gives a JS developer access to `StreamWeaver.canvas()`, reactive state, and all components, backed by Opal's Ruby runtime in the browser.
58
+
59
+ ---
60
+
61
+ ## The Technical Gap
62
+
63
+ ### What Can't Run in a Browser
64
+
65
+ The current server layer has components that fundamentally cannot run in a browser:
66
+
67
+ | Server Concern | Why It Can't Go to Browser |
68
+ |---------------|---------------------------|
69
+ | Sinatra HTTP server | No server socket in browser |
70
+ | Canvas Bridge WebSocket server | No TCP listener in browser |
71
+ | `kramdown` gem (Markdown) | Server-side Ruby gem, no browser equivalent |
72
+ | File I/O for table loading | No filesystem access in browser |
73
+ | Prism.js CDN loading via server | Needs to be bundled or loaded differently |
74
+
75
+ ### The Replacement Model
76
+
77
+ Instead of a server → client push model, the Opal version needs a **pure client-side reactive model**:
78
+
79
+ ```
80
+ Ruby DSL (compiled by Opal) → OpalRuntime → OpalBridge → DOM
81
+
82
+ ReactiveState
83
+ (in-memory, browser)
84
+ ```
85
+
86
+ The server is replaced by in-browser reactive state. Component updates are DOM mutations, not WebSocket pushes.
87
+
88
+ **The good news:** `OpalRuntime`, `OpalBridge`, and `ReactiveState` already implement this model. They were written with Opal in mind.
89
+
90
+ ### Remaining Gaps to Close
91
+
92
+ Before a full Opal build would succeed:
93
+
94
+ #### 1. `components.rb` — Server-Side Concerns
95
+ - `kramdown` calls need to be replaced with `marked.umd.js` (stub already exists)
96
+ - File I/O for table data loading needs an Opal stub or in-memory equivalent
97
+ - Any `require 'json'` or `require 'tempfile'` needs Opal stubs
98
+
99
+ #### 2. `display_dsl.rb` — Server-Side Concerns
100
+ - Prism.js loading pattern needs to be browser-native (script tag or bundled)
101
+ - Any server-side rendering helpers need client-side equivalents
102
+
103
+ #### 3. WebSocket → In-Memory Events
104
+ `canvas_bridge.rb` uses WebSockets. The Opal version needs to replace this with an in-browser event bus (custom events or a simple pub/sub).
105
+
106
+ #### 4. Sole Configuration
107
+ A `stream-weaver.gemspec` needs Sole-compatible metadata and the Opal build needs to be configured to exclude the server layer files entirely.
108
+
109
+ ---
110
+
111
+ ## Minimal Viable NPM Path
112
+
113
+ Rather than porting the entire feature set at once, there's a pragmatic MVP:
114
+
115
+ **MVP Scope:** Just the reactive core
116
+
117
+ ```
118
+ OpalRuntime ← Opal-clean ✅
119
+ OpalBridge ← Opal-clean ✅
120
+ Opal adapter ← Opal-clean ✅
121
+ Basic components ← ~20 most-used components, stubs for server deps
122
+ ReactiveState ← Opal-clean ✅
123
+ ```
124
+
125
+ **What's excluded from MVP:**
126
+ - Mermaid (external library dependency)
127
+ - Chart.js integration (manageable but deferred)
128
+ - Prism.js code blocks (deferred)
129
+ - Slide container (deferred)
130
+ - All navigation/server-rendered concerns
131
+
132
+ The MVP would demonstrate:
133
+ - `npm install stream-weaver` works
134
+ - `import StreamWeaver from 'stream-weaver'` gives you a working reactive component tree
135
+ - Basic layout, cards, text, stat displays, progress bars all render
136
+ - Reactive state updates propagate to DOM
137
+
138
+ This is enough to be interesting to JS developers and validate the concept.
139
+
140
+ ---
141
+
142
+ ## Strategic Framing
143
+
144
+ ### Do This AFTER Traction
145
+
146
+ The NPM story is most compelling when there's proven Ruby-side value to port. The sequence should be:
147
+
148
+ 1. ✅ **Build the Ruby gem to be feature-rich** — fill the audit gaps, get to parity with artifact aesthetic
149
+ 2. ✅ **Publish the gem publicly** — RubyGems, documentation, README with demos
150
+ 3. ✅ **See community adoption** — GitHub stars, issues filed by others, blog posts, use in production
151
+ 4. 🔜 **Then pursue NPM** — "The Ruby reactive UI framework that JS developers can also use"
152
+
153
+ Pursuing NPM too early means spending engineering time on a complex cross-compilation build before there's proven demand. The JS community will be more receptive to "port this popular Ruby thing" than "try this new thing that happens to be Ruby inside."
154
+
155
+ ### The Narrative When the Time Is Right
156
+
157
+ > "StreamWeaver is Streamlit for Ruby — and now you can use it from JavaScript too, without ever touching Ruby. All the expressiveness of a Ruby DSL, compiled to JavaScript by Opal, available on NPM."
158
+
159
+ This is a genuinely novel story. Opal's NPM publishing via Sole is underutilized. StreamWeaver would be a showcase of what's possible.
160
+
161
+ ---
162
+
163
+ ## Technical Checklist (When Ready to Execute)
164
+
165
+ - [ ] Run `bin/opal_spike.rb` — document which modules compile cleanly and which fail
166
+ - [ ] Create Opal stubs for `kramdown`, file I/O, and WebSocket dependencies
167
+ - [ ] Audit `components.rb` for every `require` and server assumption
168
+ - [ ] Audit `display_dsl.rb` same
169
+ - [ ] Implement in-browser event bus to replace WebSocket canvas bridge
170
+ - [ ] Configure Sole build with gemspec metadata
171
+ - [ ] Build and test MVP component set in a browser via Opal
172
+ - [ ] Publish `stream-weaver` to NPM (scoped or unscoped)
173
+ - [ ] Write JS-facing documentation (different audience from Ruby docs)
174
+
175
+ ---
176
+
177
+ ## References
178
+
179
+ - Opal documentation: https://opalrb.com
180
+ - Sole (Opal NPM bridge): https://github.com/opal/opal (see `sole` tooling)
181
+ - Existing Opal adapter: `lib/stream_weaver/adapter/opal.rb`
182
+ - Existing Opal runtime: `lib/stream_weaver/opal/runtime.rb`
183
+ - Opal spike: `bin/opal_spike.rb`
184
+ - JS stubs: `lib/stream_weaver/opal/stubs/`
@@ -0,0 +1,194 @@
1
+ # Opal Compatibility Spike Findings
2
+
3
+ **Spike date:** 2026-04-29
4
+ **Opal version:** 1.8.2
5
+ **Ruby version:** 3.3.5
6
+
7
+ ## Summary
8
+
9
+ All 7 candidate files compile cleanly under Opal when missing-require errors are suppressed (syntax-only pass). Two files have `require` statements for standard library modules unavailable in Opal. These are fixable by conditionally guarding the requires or replacing the calls. There are no fundamental syntax incompatibilities in the target files.
10
+
11
+ ---
12
+
13
+ ## Files and Results
14
+
15
+ ### Files that compiled cleanly (no issues at all)
16
+
17
+ | File | Result |
18
+ |---|---|
19
+ | `lib/stream_weaver/version.rb` | OK |
20
+ | `lib/stream_weaver/utils.rb` | OK |
21
+ | `lib/stream_weaver/theme.rb` | OK |
22
+ | `lib/stream_weaver/components.rb` | OK |
23
+ | `lib/stream_weaver/adapter/base.rb` | OK |
24
+
25
+ ### Files with missing-require issues (fixable)
26
+
27
+ | File | Missing require | Impact |
28
+ |---|---|---|
29
+ | `lib/stream_weaver/display_dsl.rb` | `require 'digest/md5'` | Inside `DisplayDSL#button` at runtime — for generating stable button IDs |
30
+ | `lib/stream_weaver/app.rb` | `require 'digest'`, `require 'set'` | `digest` for `Digest::MD5` in `App#button`; `set` for `@transient_keys = Set.new` |
31
+
32
+ **Note:** `set`, `json`, `cgi`, `base64`, and `ostruct` ARE available in Opal's stdlib. Only `digest` and `digest/md5` are missing from Opal.
33
+
34
+ **All 7 files pass Opal syntax compilation when `missing_require_severity: :ignore` is used.**
35
+
36
+ ---
37
+
38
+ ## Missing Requires Analysis
39
+
40
+ ### `digest` / `digest/md5`
41
+
42
+ Used for: generating stable 8-character hex IDs for buttons from source location or label+id pair.
43
+
44
+ **Where:** `display_dsl.rb:580` (in `DisplayDSL#button`) and `app.rb:3` (top-level require) + `app.rb:326` (in `App#button`).
45
+
46
+ **Workarounds:**
47
+ 1. **Best for Opal:** Replace `Digest::MD5.hexdigest(id_input)[0..7]` with a pure-Ruby FNV or djb2 hash (no stdlib needed). This is the recommended approach for the Opal adapter.
48
+ 2. **Conditional guard:** `require 'digest/md5' unless RUBY_PLATFORM == 'opal'` — but this leaks server/client coupling into shared files.
49
+ 3. **OpalRenderer handles button IDs independently** — the Opal runtime can use JS `Date.now()` or a counter; the Ruby-side `stable_id` is server-only.
50
+
51
+ **Recommended resolution:** The Opal runtime/renderer does not need stable IDs from Ruby — buttons in the browser are handled via JS event listeners, not HTMX post-backs. The `DisplayDSL#button` and `App#button` methods can be overridden in `adapter/opal.rb` without the `digest` dependency.
52
+
53
+ ### `require 'set'` in `app.rb`
54
+
55
+ `Set` IS available in Opal's stdlib (`stdlib/set.rb`). However, the `require 'set'` at the top of `app.rb` will trigger a missing-require warning in strict mode — but since Opal includes `set.rb` in its stdlib, this resolves correctly at runtime. **No action needed.**
56
+
57
+ Wait — re-checking: Opal's stdlib has `set.rb` but it needs to be added to the Opal path. In practice, when using `missing_require_severity: :ignore`, `Set` will be undefined at runtime unless explicitly included. The `opal_entry.rb` build step should add `require 'set'` before bundling, or stub it.
58
+
59
+ **Resolution:** Add `require 'set'` to the Opal bundle entrypoint build configuration.
60
+
61
+ ---
62
+
63
+ ## Opal Standard Library Availability
64
+
65
+ | Library | In Opal | Notes |
66
+ |---|---|---|
67
+ | `set` | YES (`stdlib/set.rb`) | Must be explicitly included in bundle |
68
+ | `json` | YES (`stdlib/json.rb`) | Must be explicitly included in bundle |
69
+ | `cgi` | YES (`stdlib/cgi.rb`) | Must be explicitly included in bundle |
70
+ | `base64` | YES (`stdlib/base64.rb`) | Must be explicitly included in bundle |
71
+ | `ostruct` | YES (`stdlib/ostruct.rb`) | Must be explicitly included in bundle |
72
+ | `digest` | NO | Not in Opal core or stdlib — replace with pure-Ruby hash |
73
+ | `digest/md5` | NO | Same as above |
74
+
75
+ ---
76
+
77
+ ## kramdown Opal Compatibility
78
+
79
+ **Result: NOT Opal-safe for production use.**
80
+
81
+ When kramdown's gem `lib/` is added to Opal's load path:
82
+ - Compiles with `missing_require_severity: :ignore` but produces warnings:
83
+ - "Cannot handle dynamic require" — `kramdown/document.rb:130`, `kramdown/converter.rb:39`, `kramdown/converter.rb:55`
84
+ - "Skipping the 'o' Regexp flag as it's not widely supported by JavaScript vendors" — multiple occurrences in `kramdown/parser/html.rb`
85
+ - Fails strict compilation with `MissingRequire` on `kramdown/converter/math_engine/`
86
+ - kramdown uses `require` with dynamic strings (string interpolation), which Opal cannot resolve
87
+
88
+ **Conclusion:** `render_markdown` in the Opal renderer MUST use `marked.js` via JS interop (or another browser-native Markdown library) rather than kramdown. This is a firm constraint.
89
+
90
+ **Implementation note:** `marked.js` is already a common CDN dependency for Markdown rendering in browser contexts. The Opal adapter can call `JS.global[:marked].call(content)` after loading marked.js.
91
+
92
+ ---
93
+
94
+ ## Server-Only Constructs in Candidate Files
95
+
96
+ ### `app.rb`
97
+
98
+ | Construct | Impact |
99
+ |---|---|
100
+ | `SinatraApp.create(self)` in `App#generate` | Only called from server entrypoint — not in browser path |
101
+ | `File.exist?` / `File.binread` in `build_favicon_href` | File I/O — not callable in browser. `build_favicon_href` only reached if `favicon` DSL is used. Override in Opal adapter. |
102
+ | `CGI.escape` in `define_path_helpers` | CGI is available in Opal stdlib — works in browser |
103
+ | `Time.now.to_f` in `show_toast` | `Time` is available in Opal core — works in browser |
104
+ | `require 'resource'` at top of `app.rb` | `resource.rb` requires `cgi` (available) but also integrates with Sinatra routing. Must stub or exclude. |
105
+
106
+ ### `display_dsl.rb`
107
+
108
+ | Construct | Impact |
109
+ |---|---|
110
+ | `require 'digest/md5'` in `#button` | See above — override button in Opal adapter |
111
+ | `block.source_location` | Available in Opal — OK |
112
+
113
+ ### `components.rb`
114
+
115
+ | Construct | Impact |
116
+ |---|---|
117
+ | `require "yaml"` / `require "json"` (inside Table/ScoreTable) | `json` available in Opal stdlib; `yaml` not needed for browser rendering |
118
+ | Direct `view.div`, `view.span`, `view.h4`, `view.p`, `view.hr` calls | These 5 HTML methods must exist on the OpalRenderer's view object |
119
+ | `view.adapter.*` delegation | All rendering goes through adapter — clean separation |
120
+
121
+ ---
122
+
123
+ ## OpalRenderer Must-Implement Method List
124
+
125
+ The `OpalRenderer` (the `view` object passed to `component.render(view, state)`) must support:
126
+
127
+ ### Direct HTML element methods (called directly on view in components.rb)
128
+
129
+ - `div(**attrs, &block)` — Card, CardHeader, CardBody, CardFooter, Div
130
+ - `span(&block)` — Phrase, Card label
131
+ - `h4(&block)` — CardHeader
132
+ - `p(&block)` — Text component
133
+ - `hr(**attrs)` — MenuDivider
134
+
135
+ ### Adapter methods (via `view.adapter.*`)
136
+
137
+ From `components.rb` (64 adapter calls):
138
+ - `render_text_field`, `render_text_area`, `render_checkbox`, `render_select`, `render_radio_group`
139
+ - `render_button`, `render_app_header`, `render_div`, `render_term`, `render_lesson_text`
140
+ - `render_vstack`, `render_hstack`, `render_grid`, `render_scroll_box`
141
+ - `render_collapsible`, `render_alert`, `render_tabs`, `render_breadcrumbs`, `render_dropdown`
142
+ - `render_modal`, `render_toast_container`, `render_theme_toggle`, `render_theme_preset`, `render_theme_switcher`
143
+ - `render_header`, `render_markdown`, `render_stat_display`, `render_badge`, `render_status_dot`
144
+ - `render_type_tag`, `render_pulse_indicator`, `render_activity_item`, `render_timeline_event`
145
+ - `render_priority_item`, `render_progress_bar`, `render_spinner`, `render_score_table`, `render_table`
146
+ - `render_status_badge`, `render_external_link_button`, `render_link`, `render_navbar`, `render_nav_item`
147
+ - `render_checkbox_group`, `render_code_editor`, `render_tag_buttons`, `render_form`
148
+ - `render_expandable_card`, `render_canvas_continue`, `render_bar_chart`, `render_line_chart`
149
+ - `render_pie_chart`, `render_stacked_bar_chart`, `render_column`, `render_columns`
150
+ - `render_app_shell`, `render_main_content`, `render_sidebar`
151
+
152
+ From visual skills component sub-files:
153
+ - `render_code_block`, `render_image_block`, `render_mermaid`, `render_keyboard_shortcuts`
154
+ - `render_slide_container`, `render_slide`, `render_sidebar_toc`, `render_callout`, `render_comparison`
155
+ - `render_pipeline`, `render_kpi_dashboard`, `render_chartjs`, `render_timeline_event`
156
+ - `render_design_deck`, `render_deck_slide`, `render_deck_option`, `render_deck_summary`
157
+ - `render_generate_more_controls`, `render_skeleton_placeholder`, `render_model_selector`
158
+ - `render_close_overlay`, `render_confirmation_bar`
159
+ - `render_hero`, `render_prose`, `render_pullquote`, `render_dir_tree`, `render_legend`
160
+ - `render_flow_arrow`, `render_layout_toggle`
161
+
162
+ **Phase 1 OpalRenderer priority (minimal viable set for basic text/container rendering):**
163
+ - `div`, `span`, `h4`, `p`, `hr`
164
+ - `render_text`, `render_markdown` (via marked.js), `render_header`, `render_button`
165
+ - `render_div`, `render_vstack`, `render_hstack`, `render_card`
166
+
167
+ ---
168
+
169
+ ## Adjustments to Phase 1 Plan
170
+
171
+ 1. **Opal version locked:** Use `opal ~> 1.8` (installed 1.8.2). API confirmed working.
172
+
173
+ 2. **Builder pattern:** Use `Opal::Builder.new(missing_require_severity: :ignore)` for the entrypoint build. The 7 candidate files all compile under this mode.
174
+
175
+ 3. **digest replacement:** The `App#button` and `DisplayDSL#button` use `Digest::MD5` for stable IDs. In the Opal adapter, override the button logic to use a pure counter or JS `Math.random()`. No digest dependency needed in the browser.
176
+
177
+ 4. **stdlib requires in Opal bundle:** The opal build step must explicitly `require` stdlib modules: `set`, `json`, `cgi` before bundling `stream_weaver/app`.
178
+
179
+ 5. **kramdown is out:** `render_markdown` in `OpalAdapter` must use `marked.js` via `JS.global[:marked].call(content)`. Add marked.js to the CDN list alongside the Opal runtime bundle.
180
+
181
+ 6. **`require 'resource'` in app.rb:** `resource.rb` is a server-only routing concern. The `opal_entry.rb` entrypoint does NOT require `app.rb` directly — it requires only the safe subset. Phase 1 OpalRenderer can stub or skip resource/routing machinery.
182
+
183
+ 7. **Direct view HTML calls are minimal:** Only `div`, `span`, `h4`, `p`, `hr` — the OpalRenderer's view interface is very simple.
184
+
185
+ 8. **Adapter delegation is the primary pattern:** 64 of 73 `view.` calls go through `view.adapter.*` — the clean separation makes OpalAdapter straightforward to implement incrementally.
186
+
187
+ ---
188
+
189
+ ## Files Created/Modified This Spike
190
+
191
+ - `Gemfile` — added `group :development { gem "opal", "~> 1.8" }`
192
+ - `lib/stream_weaver/opal_entry.rb` — browser-only require tree (created, placeholder for non-existent files)
193
+ - `bin/opal_spike.rb` — Opal compatibility test script
194
+ - `docs/opal-spike-findings.md` — this document