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,591 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StreamWeaver
4
+ module Adapter
5
+ # Base adapter class that all adapters must inherit from
6
+ # Defines the interface that components use to render themselves
7
+ #
8
+ # This adapter pattern decouples components from specific frontend frameworks
9
+ # (Alpine.js, HTMX, React, Opal, etc.), allowing pluggable implementations.
10
+ #
11
+ # @abstract Subclass and override all methods to implement a new adapter
12
+ #
13
+ # @example Creating a custom adapter
14
+ # class MyAdapter < StreamWeaver::Adapter::Base
15
+ # def render_text_field(view, key, options, state)
16
+ # view.input(type: "text", "my-binding" => key.to_s)
17
+ # end
18
+ # # ... implement all other methods
19
+ # end
20
+ #
21
+ # @example Using an adapter
22
+ # app = StreamWeaver::App.new("My App", adapter: MyAdapter.new) do
23
+ # text_field :name
24
+ # end
25
+ class Base
26
+ # Render a single-line text input field
27
+ #
28
+ # @param view [Phlex::HTML] The Phlex view instance
29
+ # @param key [Symbol] The state key for this input
30
+ # @param options [Hash] Component options
31
+ # @option options [String] :placeholder Placeholder text
32
+ # @param state [Hash] Current state hash (symbol keys)
33
+ # @return [void] Renders to view
34
+ # @raise [NotImplementedError] if not implemented by subclass
35
+ #
36
+ # @example
37
+ # adapter.render_text_field(view, :email, { placeholder: "Email" }, state)
38
+ def render_text_field(view, key, options, state)
39
+ raise NotImplementedError, "#{self.class} must implement #render_text_field"
40
+ end
41
+
42
+ # Render a multi-line text area
43
+ #
44
+ # @param view [Phlex::HTML] The Phlex view instance
45
+ # @param key [Symbol] The state key for this textarea
46
+ # @param options [Hash] Component options
47
+ # @option options [String] :placeholder Placeholder text
48
+ # @option options [Integer] :rows Number of rows (default: 3)
49
+ # @param state [Hash] Current state hash (symbol keys)
50
+ # @return [void] Renders to view
51
+ # @raise [NotImplementedError] if not implemented by subclass
52
+ #
53
+ # @example
54
+ # adapter.render_text_area(view, :bio, { rows: 5 }, state)
55
+ def render_text_area(view, key, options, state)
56
+ raise NotImplementedError, "#{self.class} must implement #render_text_area"
57
+ end
58
+
59
+ # Render a native date input, state-bound like a text field. Value is
60
+ # stored/read as an ISO 8601 string ("YYYY-MM-DD").
61
+ #
62
+ # @param view [Phlex::HTML] The Phlex view instance
63
+ # @param key [Symbol] The state key for this input
64
+ # @param options [Hash] Component options
65
+ # @option options [String] :label Optional label rendered above the input
66
+ # @option options [String] :min Minimum selectable date (ISO 8601)
67
+ # @option options [String] :max Maximum selectable date (ISO 8601)
68
+ # @param state [Hash] Current state hash (symbol keys)
69
+ # @return [void] Renders to view
70
+ # @raise [NotImplementedError] if not implemented by subclass
71
+ #
72
+ # @example
73
+ # adapter.render_date_field(view, :due_on, { label: "Due" }, state)
74
+ def render_date_field(view, key, options, state)
75
+ raise NotImplementedError, "#{self.class} must implement #render_date_field"
76
+ end
77
+
78
+ # Render a checkbox input
79
+ #
80
+ # @param view [Phlex::HTML] The Phlex view instance
81
+ # @param key [Symbol] The state key for this checkbox
82
+ # @param label [String] The label text
83
+ # @param options [Hash] Component options
84
+ # @param state [Hash] Current state hash (symbol keys)
85
+ # @return [void] Renders to view
86
+ # @raise [NotImplementedError] if not implemented by subclass
87
+ #
88
+ # @example
89
+ # adapter.render_checkbox(view, :agree, "I agree", {}, state)
90
+ def render_checkbox(view, key, label, options, state)
91
+ raise NotImplementedError, "#{self.class} must implement #render_checkbox"
92
+ end
93
+
94
+ # Render a select dropdown
95
+ #
96
+ # @param view [Phlex::HTML] The Phlex view instance
97
+ # @param key [Symbol] The state key for this select
98
+ # @param choices [Array<String>] The available choices
99
+ # @param options [Hash] Component options
100
+ # @param state [Hash] Current state hash (symbol keys)
101
+ # @return [void] Renders to view
102
+ # @raise [NotImplementedError] if not implemented by subclass
103
+ #
104
+ # @example
105
+ # adapter.render_select(view, :color, ["Red", "Green", "Blue"], {}, state)
106
+ def render_select(view, key, choices, options, state)
107
+ raise NotImplementedError, "#{self.class} must implement #render_select"
108
+ end
109
+
110
+ # Render a radio button group for single-choice selection
111
+ #
112
+ # @param view [Phlex::HTML] The Phlex view instance
113
+ # @param key [Symbol] The state key for this radio group
114
+ # @param choices [Array<String>] The available choices
115
+ # @param options [Hash] Component options
116
+ # @param state [Hash] Current state hash (symbol keys)
117
+ # @return [void] Renders to view
118
+ # @raise [NotImplementedError] if not implemented by subclass
119
+ #
120
+ # @example
121
+ # adapter.render_radio_group(view, :answer, ["Option A", "Option B"], {}, state)
122
+ def render_radio_group(view, key, choices, options, state)
123
+ raise NotImplementedError, "#{self.class} must implement #render_radio_group"
124
+ end
125
+
126
+ # Render a button that executes an action
127
+ #
128
+ # @param view [Phlex::HTML] The Phlex view instance
129
+ # @param button_id [String] The deterministic button ID (e.g., "btn_submit_1")
130
+ # @param label [String] The button label text
131
+ # @param options [Hash] Component options
132
+ # @option options [Symbol] :style Button style (:primary or :secondary)
133
+ # @return [void] Renders to view
134
+ # @raise [NotImplementedError] if not implemented by subclass
135
+ #
136
+ # @example
137
+ # adapter.render_button(view, "btn_submit_1", "Submit", { style: :primary })
138
+ def render_button(view, button_id, label, options)
139
+ raise NotImplementedError, "#{self.class} must implement #render_button"
140
+ end
141
+
142
+ # Render a clipboard-copy button (client-side only; Alpine.js only --
143
+ # not implemented for the Opal adapter by design)
144
+ #
145
+ # @param view [Phlex::HTML] The Phlex view instance
146
+ # @param component [Components::CopyButton] The copy button component
147
+ # @param state [Hash] Current state hash
148
+ # @return [void] Renders to view
149
+ # @raise [NotImplementedError] if not implemented by subclass
150
+ #
151
+ # @example
152
+ # adapter.render_copy_button(view, component, state)
153
+ def render_copy_button(view, component, state)
154
+ raise NotImplementedError, "#{self.class} must implement #render_copy_button"
155
+ end
156
+
157
+ # Get HTML attributes for the app container (e.g., x-data for Alpine.js)
158
+ #
159
+ # @param state [Hash] Current state hash (symbol keys)
160
+ # @return [Hash] HTML attributes to apply to container div
161
+ # @raise [NotImplementedError] if not implemented by subclass
162
+ #
163
+ # @example
164
+ # attrs = adapter.container_attributes({ name: "Alice" })
165
+ # # => { "x-data" => '{"name":"Alice"}' }
166
+ def container_attributes(state)
167
+ raise NotImplementedError, "#{self.class} must implement #container_attributes"
168
+ end
169
+
170
+ # Get CDN script tags or inline scripts needed by this adapter
171
+ #
172
+ # @return [Array<String>] Array of HTML script tags
173
+ # @raise [NotImplementedError] if not implemented by subclass
174
+ #
175
+ # @example
176
+ # adapter.cdn_scripts
177
+ # # => ['<script src="..."></script>', '<script>...</script>']
178
+ def cdn_scripts
179
+ raise NotImplementedError, "#{self.class} must implement #cdn_scripts"
180
+ end
181
+
182
+ # Render CDN scripts directly to the view using Phlex methods
183
+ #
184
+ # @param view [Phlex::HTML] The Phlex view instance
185
+ # @return [void] Renders script tags to the view
186
+ # @raise [NotImplementedError] if not implemented by subclass
187
+ #
188
+ # @example
189
+ # adapter.render_cdn_scripts(view)
190
+ def render_cdn_scripts(view)
191
+ raise NotImplementedError, "#{self.class} must implement #render_cdn_scripts"
192
+ end
193
+
194
+ # Get the input selector for including form data (e.g., "[x-model]" for Alpine.js)
195
+ # Used by buttons to know which inputs to include when submitting
196
+ #
197
+ # @return [String] CSS selector for input elements
198
+ # @raise [NotImplementedError] if not implemented by subclass
199
+ #
200
+ # @example
201
+ # adapter.input_selector
202
+ # # => "[x-model]"
203
+ def input_selector
204
+ raise NotImplementedError, "#{self.class} must implement #input_selector"
205
+ end
206
+
207
+ # Render a checkbox group with select all/none functionality
208
+ # State is stored as an array of selected values
209
+ #
210
+ # @param view [Phlex::HTML] The Phlex view instance
211
+ # @param key [Symbol] The state key for this group (stores array)
212
+ # @param children [Array<CheckboxItem>] The checkbox items
213
+ # @param options [Hash] Component options (select_all, select_none labels)
214
+ # @param state [Hash] Current state hash (symbol keys)
215
+ # @return [void] Renders to view
216
+ # @raise [NotImplementedError] if not implemented by subclass
217
+ def render_checkbox_group(view, key, children, options, state)
218
+ raise NotImplementedError, "#{self.class} must implement #render_checkbox_group"
219
+ end
220
+
221
+ # Render a vertical stack container
222
+ def render_vstack(view, component, state)
223
+ raise NotImplementedError, "#{self.class} must implement #render_vstack"
224
+ end
225
+
226
+ # Render a horizontal stack container
227
+ def render_hstack(view, component, state)
228
+ raise NotImplementedError, "#{self.class} must implement #render_hstack"
229
+ end
230
+
231
+ # Render a responsive grid container
232
+ def render_grid(view, component, state)
233
+ raise NotImplementedError, "#{self.class} must implement #render_grid"
234
+ end
235
+
236
+ # Render a scrollable container with max-height
237
+ def render_scroll_box(view, component, state)
238
+ raise NotImplementedError, "#{self.class} must implement #render_scroll_box"
239
+ end
240
+
241
+ # Render a collapsible/expandable section
242
+ #
243
+ # @param view [Phlex::HTML] The Phlex view instance
244
+ # @param component [Components::Collapsible] The collapsible component
245
+ # @param state [Hash] Current state hash (symbol keys)
246
+ # @return [void] Renders to view
247
+ # @raise [NotImplementedError] if not implemented by subclass
248
+ def render_collapsible(view, component, state)
249
+ raise NotImplementedError, "#{self.class} must implement #render_collapsible"
250
+ end
251
+
252
+ # Render an expandable card with title, subtitle, badge, and status
253
+ #
254
+ # @param view [Phlex::HTML] The Phlex view instance
255
+ # @param component [Components::ExpandableCard] The expandable card component
256
+ # @param state [Hash] Current state hash (symbol keys)
257
+ # @return [void] Renders to view
258
+ # @raise [NotImplementedError] if not implemented by subclass
259
+ def render_expandable_card(view, component, state)
260
+ raise NotImplementedError, "#{self.class} must implement #render_expandable_card"
261
+ end
262
+
263
+ # =========================================
264
+ # Navigation component rendering
265
+ # =========================================
266
+
267
+ # Render a tabbed navigation container
268
+ #
269
+ # @param view [Phlex::HTML] The Phlex view instance
270
+ # @param component [Tabs] The tabs component with children
271
+ # @param state [Hash] Current state hash (symbol keys)
272
+ # @return [void] Renders to view
273
+ # @raise [NotImplementedError] if not implemented by subclass
274
+ def render_tabs(view, component, state)
275
+ raise NotImplementedError, "#{self.class} must implement #render_tabs"
276
+ end
277
+
278
+ # Render a breadcrumbs navigation trail
279
+ #
280
+ # @param view [Phlex::HTML] The Phlex view instance
281
+ # @param component [Breadcrumbs] The breadcrumbs component with children
282
+ # @param state [Hash] Current state hash (symbol keys)
283
+ # @return [void] Renders to view
284
+ # @raise [NotImplementedError] if not implemented by subclass
285
+ def render_breadcrumbs(view, component, state)
286
+ raise NotImplementedError, "#{self.class} must implement #render_breadcrumbs"
287
+ end
288
+
289
+ # Render a dropdown menu container
290
+ #
291
+ # @param view [Phlex::HTML] The Phlex view instance
292
+ # @param component [Dropdown] The dropdown component with trigger and menu
293
+ # @param state [Hash] Current state hash (symbol keys)
294
+ # @return [void] Renders to view
295
+ # @raise [NotImplementedError] if not implemented by subclass
296
+ def render_dropdown(view, component, state)
297
+ raise NotImplementedError, "#{self.class} must implement #render_dropdown"
298
+ end
299
+
300
+ # Render a theme toggle button (dark/light/auto mode)
301
+ #
302
+ # @param view [Phlex::HTML] The Phlex view instance
303
+ # @param component [ThemeToggle] The theme toggle component
304
+ # @param state [Hash] Current state hash
305
+ # @return [void] Renders to view
306
+ # @raise [NotImplementedError] if not implemented by subclass
307
+ def render_theme_toggle(view, component, state)
308
+ raise NotImplementedError, "#{self.class} must implement #render_theme_toggle"
309
+ end
310
+
311
+ # Render a theme preset (Google Fonts link + CSS custom properties)
312
+ #
313
+ # @param view [Phlex::HTML] The Phlex view instance
314
+ # @param component [ThemePreset] The theme preset component
315
+ # @param state [Hash] Current state hash
316
+ # @return [void] Renders to view
317
+ # @raise [NotImplementedError] if not implemented by subclass
318
+ def render_theme_preset(view, component, state)
319
+ raise NotImplementedError, "#{self.class} must implement #render_theme_preset"
320
+ end
321
+
322
+ # Render a syntax-highlighted code block with Prism.js
323
+ #
324
+ # @param view [Phlex::HTML] The Phlex view instance
325
+ # @param component [CodeBlock] The code block component
326
+ # @param state [Hash] Current state hash
327
+ # @return [void] Renders to view
328
+ # @raise [NotImplementedError] if not implemented by subclass
329
+ def render_code_block(view, component, state)
330
+ raise NotImplementedError, "#{self.class} must implement #render_code_block"
331
+ end
332
+
333
+ # Render an image with optional caption
334
+ #
335
+ # @param view [Phlex::HTML] The Phlex view instance
336
+ # @param component [ImageBlock] The image block component
337
+ # @param state [Hash] Current state hash
338
+ # @return [void] Renders to view
339
+ # @raise [NotImplementedError] if not implemented by subclass
340
+ def render_image_block(view, component, state)
341
+ raise NotImplementedError, "#{self.class} must implement #render_image_block"
342
+ end
343
+
344
+ # Render a Mermaid diagram with optional zoom/pan
345
+ #
346
+ # @param view [Phlex::HTML] The Phlex view instance
347
+ # @param component [Mermaid] The mermaid component
348
+ # @param state [Hash] Current state hash
349
+ # @return [void] Renders to view
350
+ # @raise [NotImplementedError] if not implemented by subclass
351
+ def render_mermaid(view, component, state)
352
+ raise NotImplementedError, "#{self.class} must implement #render_mermaid"
353
+ end
354
+
355
+ # Render a pipeline step flow visualization
356
+ #
357
+ # @param view [Phlex::HTML] The Phlex view instance
358
+ # @param component [Pipeline] The pipeline component
359
+ # @param state [Hash] Current state hash
360
+ # @return [void] Renders to view
361
+ # @raise [NotImplementedError] if not implemented by subclass
362
+ def render_pipeline(view, component, state)
363
+ raise NotImplementedError, "#{self.class} must implement #render_pipeline"
364
+ end
365
+
366
+ # Render a KPI dashboard grid of metric cards
367
+ #
368
+ # @param view [Phlex::HTML] The Phlex view instance
369
+ # @param component [KpiDashboard] The KPI dashboard component
370
+ # @param state [Hash] Current state hash
371
+ # @return [void] Renders to view
372
+ # @raise [NotImplementedError] if not implemented by subclass
373
+ def render_kpi_dashboard(view, component, state)
374
+ raise NotImplementedError, "#{self.class} must implement #render_kpi_dashboard"
375
+ end
376
+
377
+ # Render a Chart.js chart (visual skills T12 wrapper)
378
+ # Named render_chartjs to avoid collision with existing render_chart
379
+ # which uses config_class: keyword for legacy BarChart/LineChart/PieChart.
380
+ #
381
+ # @param view [Phlex::HTML] The Phlex view instance
382
+ # @param component [Chart] The chart component
383
+ # @param state [Hash] Current state hash
384
+ # @return [void] Renders to view
385
+ # @raise [NotImplementedError] if not implemented by subclass
386
+ def render_chartjs(view, component, state)
387
+ raise NotImplementedError, "#{self.class} must implement #render_chartjs"
388
+ end
389
+
390
+ # Render keyboard shortcuts (non-visual -- emits <script> block)
391
+ #
392
+ # @param view [Phlex::HTML] The Phlex view instance
393
+ # @param component [KeyboardShortcuts] The keyboard shortcuts component
394
+ # @param state [Hash] Current state hash
395
+ # @return [void] Renders to view
396
+ # @raise [NotImplementedError] if not implemented by subclass
397
+ def render_keyboard_shortcuts(view, component, state)
398
+ raise NotImplementedError, "#{self.class} must implement #render_keyboard_shortcuts"
399
+ end
400
+
401
+ # Render a slide container with navigation
402
+ #
403
+ # @param view [Phlex::HTML] The Phlex view instance
404
+ # @param component [SlideContainer] The slide container component
405
+ # @param state [Hash] Current state hash
406
+ # @return [void] Renders to view
407
+ # @raise [NotImplementedError] if not implemented by subclass
408
+ def render_slide_container(view, component, state)
409
+ raise NotImplementedError, "#{self.class} must implement #render_slide_container"
410
+ end
411
+
412
+ # Render an individual slide within a container
413
+ #
414
+ # @param view [Phlex::HTML] The Phlex view instance
415
+ # @param component [Slide] The slide component
416
+ # @param state [Hash] Current state hash
417
+ # @return [void] Renders to view
418
+ # @raise [NotImplementedError] if not implemented by subclass
419
+ def render_slide(view, component, state)
420
+ raise NotImplementedError, "#{self.class} must implement #render_slide"
421
+ end
422
+
423
+ # =========================================
424
+ # Explainer component rendering (T11)
425
+ # =========================================
426
+
427
+ # Render a sticky sidebar table-of-contents with scroll spy
428
+ def render_sidebar_toc(view, component, state)
429
+ raise NotImplementedError, "#{self.class} must implement #render_sidebar_toc"
430
+ end
431
+
432
+ # Render a non-dismissible callout box with colored left border
433
+ def render_callout(view, component, state)
434
+ raise NotImplementedError, "#{self.class} must implement #render_callout"
435
+ end
436
+
437
+ # Render side-by-side comparison panels
438
+ def render_comparison(view, component, state)
439
+ raise NotImplementedError, "#{self.class} must implement #render_comparison"
440
+ end
441
+
442
+ # Render a document-level header (eyebrow + serif title + meta pills)
443
+ def render_doc_header(view, component, state)
444
+ raise NotImplementedError, "#{self.class} must implement #render_doc_header"
445
+ end
446
+
447
+ # Render a numbered section eyebrow + h2 heading pair
448
+ def render_doc_section_header(view, component, state)
449
+ raise NotImplementedError, "#{self.class} must implement #render_doc_section_header"
450
+ end
451
+
452
+ def render_implementation_map(view, component, state)
453
+ raise NotImplementedError, "#{self.class} must implement #render_implementation_map"
454
+ end
455
+
456
+ def render_decision(view, component, state)
457
+ raise NotImplementedError, "#{self.class} must implement #render_decision"
458
+ end
459
+
460
+ def render_annotated_code(view, component, state)
461
+ raise NotImplementedError, "#{self.class} must implement #render_annotated_code"
462
+ end
463
+
464
+ # =========================================
465
+ # Design Deck component rendering (T7)
466
+ # =========================================
467
+
468
+ # Render a design deck (top-level orchestrator)
469
+ #
470
+ # @param view [Phlex::HTML] The Phlex view instance
471
+ # @param component [Deck::DesignDeck] The deck component
472
+ # @param state [Hash] Current state hash
473
+ # @return [void] Renders to view
474
+ # @raise [NotImplementedError] if not implemented by subclass
475
+ def render_design_deck(view, component, state)
476
+ raise NotImplementedError, "#{self.class} must implement #render_design_deck"
477
+ end
478
+
479
+ # Render a deck slide with options grid
480
+ #
481
+ # @param view [Phlex::HTML] The Phlex view instance
482
+ # @param component [Deck::DeckSlide] The slide component
483
+ # @param state [Hash] Current state hash
484
+ # @return [void] Renders to view
485
+ # @raise [NotImplementedError] if not implemented by subclass
486
+ def render_deck_slide(view, component, state)
487
+ raise NotImplementedError, "#{self.class} must implement #render_deck_slide"
488
+ end
489
+
490
+ # Render a deck option card
491
+ #
492
+ # @param view [Phlex::HTML] The Phlex view instance
493
+ # @param component [Deck::DeckOption] The option component
494
+ # @param state [Hash] Current state hash
495
+ # @return [void] Renders to view
496
+ # @raise [NotImplementedError] if not implemented by subclass
497
+ def render_deck_option(view, component, state)
498
+ raise NotImplementedError, "#{self.class} must implement #render_deck_option"
499
+ end
500
+
501
+ # Render the auto-generated deck summary slide
502
+ #
503
+ # @param view [Phlex::HTML] The Phlex view instance
504
+ # @param component [Deck::DeckSummary] The summary component
505
+ # @param state [Hash] Current state hash
506
+ # @return [void] Renders to view
507
+ # @raise [NotImplementedError] if not implemented by subclass
508
+ def render_deck_summary(view, component, state)
509
+ raise NotImplementedError, "#{self.class} must implement #render_deck_summary"
510
+ end
511
+
512
+ # =========================================
513
+ # CSS-Only Helpers (T13)
514
+ # =========================================
515
+
516
+ # Render a hero section
517
+ def render_hero(view, component, state)
518
+ raise NotImplementedError, "#{self.class} must implement #render_hero"
519
+ end
520
+
521
+ # Render a prose container
522
+ def render_prose(view, component, state)
523
+ raise NotImplementedError, "#{self.class} must implement #render_prose"
524
+ end
525
+
526
+ # Render a pullquote
527
+ def render_pullquote(view, component, state)
528
+ raise NotImplementedError, "#{self.class} must implement #render_pullquote"
529
+ end
530
+
531
+ # Render a directory tree display
532
+ def render_dir_tree(view, component, state)
533
+ raise NotImplementedError, "#{self.class} must implement #render_dir_tree"
534
+ end
535
+
536
+ # Render a color legend
537
+ def render_legend(view, component, state)
538
+ raise NotImplementedError, "#{self.class} must implement #render_legend"
539
+ end
540
+
541
+ # Render a flow arrow connector
542
+ def render_flow_arrow(view, component, state)
543
+ raise NotImplementedError, "#{self.class} must implement #render_flow_arrow"
544
+ end
545
+
546
+ # Render layout toggle buttons
547
+ def render_layout_toggle(view, component, state)
548
+ raise NotImplementedError, "#{self.class} must implement #render_layout_toggle"
549
+ end
550
+
551
+ # Render a timeline event row with expandable details
552
+ def render_timeline_event(view, component, state)
553
+ raise NotImplementedError, "#{self.class} must implement #render_timeline_event"
554
+ end
555
+
556
+ # Render a wireframe surface block with raw HTML content
557
+ def render_wireframe_block(view, component, state)
558
+ raise NotImplementedError, "#{self.class} must implement #render_wireframe_block"
559
+ end
560
+
561
+ def render_wireframe(view, component, state)
562
+ raise NotImplementedError, "#{self.class} must implement #render_wireframe"
563
+ end
564
+
565
+ def render_diff_block(view, component, state)
566
+ raise NotImplementedError, "#{self.class} must implement #render_diff_block"
567
+ end
568
+
569
+ def render_api_endpoint(view, component, state)
570
+ raise NotImplementedError, "#{self.class} must implement #render_api_endpoint"
571
+ end
572
+
573
+ protected
574
+
575
+ # Convert spacing symbol to CSS value
576
+ #
577
+ # @param spacing [Symbol, String] Spacing value (:xs, :sm, :md, :lg, :xl or raw CSS)
578
+ # @return [String] CSS value
579
+ def spacing_to_css(spacing)
580
+ case spacing
581
+ when :xs then "var(--sw-spacing-xs)"
582
+ when :sm then "var(--sw-spacing-sm)"
583
+ when :md then "var(--sw-spacing-md)"
584
+ when :lg then "var(--sw-spacing-lg)"
585
+ when :xl then "var(--sw-spacing-xl)"
586
+ else spacing.to_s
587
+ end
588
+ end
589
+ end
590
+ end
591
+ end