stream_weaver 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. checksums.yaml +7 -0
  2. data/.beads/.gitignore +77 -0
  3. data/.beads/README.md +81 -0
  4. data/.beads/config.yaml +54 -0
  5. data/.beads/metadata.json +7 -0
  6. data/.rspec +3 -0
  7. data/AGENTS.md +91 -0
  8. data/CHANGELOG.md +294 -0
  9. data/CLAUDE.md +67 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +565 -0
  12. data/Rakefile +13 -0
  13. data/assets/streamweaver-hero.jpg +0 -0
  14. data/bench/baselines/ledger.rb +58 -0
  15. data/bench/baselines/warroom.rb +51 -0
  16. data/bench/fixtures/ledger.rb +76 -0
  17. data/bench/fixtures/warroom.rb +60 -0
  18. data/bench/results/6daf8b8.md +102 -0
  19. data/bench/results/c8d749b.md +102 -0
  20. data/bench/results/eb06ab9.md +102 -0
  21. data/bench/run.rb +151 -0
  22. data/bench/support.rb +84 -0
  23. data/copy_this/dto_run_sample_run_7631.html +220 -0
  24. data/docs/SERVICE_MODE.md +302 -0
  25. data/docs/architecture/how_streamweaver_works.md +772 -0
  26. data/docs/blog/2026-01-16-streamweaver-introduction.md +515 -0
  27. data/docs/bug-2026-08-31-canvas-bridge-socket-collision.md +83 -0
  28. data/docs/canvas-ipc-session-summary.md +127 -0
  29. data/docs/canvas-panel-workflow.md +172 -0
  30. data/docs/canvas-read.md +147 -0
  31. data/docs/canvas-roadmap.md +275 -0
  32. data/docs/case-studies/2026-09-03-didx-canvas-read-shelf.md +147 -0
  33. data/docs/claude-code-companion-skill-spec.md +567 -0
  34. data/docs/components_reference.md +1223 -0
  35. data/docs/crud-patterns.md +265 -0
  36. data/docs/endpoints.md +122 -0
  37. data/docs/for_llms.md +1 -0
  38. data/docs/form-patterns.md +165 -0
  39. data/docs/frontend-only.md +169 -0
  40. data/docs/html-artifact-audit.md +238 -0
  41. data/docs/ideas/2025-01-01-as-a-service.md +203 -0
  42. data/docs/ideas/2026-01-16-charm-tui-exploration.md +542 -0
  43. data/docs/ideas/2026-08-27-static-doc-shelf-export.md +153 -0
  44. data/docs/ideas/dhh-review-future-refactors.md +142 -0
  45. data/docs/opal-jamstack.md +47 -0
  46. data/docs/opal-npm-direction.md +184 -0
  47. data/docs/opal-spike-findings.md +194 -0
  48. data/docs/plans/2025-12-16-form-blocks-design.md +124 -0
  49. data/docs/plans/2025-12-30-examples-browser-design.md +152 -0
  50. data/docs/plans/2026-01-02-charts-design.md +288 -0
  51. data/docs/plans/2026-01-10-canvas-ipc-design.md +247 -0
  52. data/docs/plans/2026-01-19-cabinet-control-components.md +176 -0
  53. data/docs/plans/2026-01-26-gem-release-and-panel.md +140 -0
  54. data/docs/plans/canvas-claude-project-design.md +238 -0
  55. data/docs/plans/canvas-doc-location-and-discovery.md +32 -0
  56. data/docs/plans/org-doc-preview-surfaces.md +293 -0
  57. data/docs/plans/shadcn-polish-plan.md +417 -0
  58. data/docs/porting-artifacts.md +252 -0
  59. data/docs/reference/agent-skills-comparison.md +369 -0
  60. data/docs/reference/travel-state-prd.artifact.html +780 -0
  61. data/docs/research/2026-08-17-hotwire-alike-landscape.md +268 -0
  62. data/docs/research/2026-08-17-hotwire-concept-map.md +404 -0
  63. data/docs/research/2026-08-22-lazy-fragments-trigger-decision.md +246 -0
  64. data/docs/research/2026-08-22-learnhotwire-syllabus-coverage.md +239 -0
  65. data/docs/research/frontend-only-matrix.md +278 -0
  66. data/docs/research/streamweaver-way-spike-findings.md +529 -0
  67. data/docs/resource-dsl.md +342 -0
  68. data/docs/routing.md +302 -0
  69. data/docs/ruby-ui-comparison.md +319 -0
  70. data/docs/shared-dsl-fragments.md +356 -0
  71. data/docs/streamweaver-for-ai-agents.md +401 -0
  72. data/docs/streamweaver-frontend-vision.md +123 -0
  73. data/docs/streamweaver_canvas/glimmer_initial_final_layer.rb +77 -0
  74. data/docs/streamweaver_canvas/save_example.rb +9 -0
  75. data/docs/templates.md +213 -0
  76. data/docs/testing.md +155 -0
  77. data/docs/theming-hooks.md +281 -0
  78. data/docs/tutorials/the-streamweaver-way.md +882 -0
  79. data/docs/university/capability-inventory.md +137 -0
  80. data/docs/university/dependency-survey.md +114 -0
  81. data/docs/university/design-spec.md +217 -0
  82. data/docs/university/mockups/code-block-doc-theme-dark.png +0 -0
  83. data/docs/university/mockups/code-block-doc-theme-light.png +0 -0
  84. data/docs/university/mockups/course-list-dark.png +0 -0
  85. data/docs/university/mockups/course-list-light.png +0 -0
  86. data/docs/university/mockups/course_canvas_mockup.rb +741 -0
  87. data/docs/university/mockups/step-screen-dark.png +0 -0
  88. data/docs/university/mockups/step-screen-light.png +0 -0
  89. data/docs/university/roadmap.md +72 -0
  90. data/docs/university/send-to-coworker.md +77 -0
  91. data/docs/university/worker-session-mining.md +109 -0
  92. data/docs/visual/browser-rendering-spike.md +173 -0
  93. data/docs/visual/builder-visual-plan-analysis.md +378 -0
  94. data/docs/visual/sw-plan-format-exploration.md +511 -0
  95. data/docs/visual/sw-plan-rendering-deep-dive.md +453 -0
  96. data/docs/visual-skills/PROGRESS.md +60 -0
  97. data/docs/visual-skills/SESSION-CONTEXT.md +207 -0
  98. data/docs/visual-skills/analysis/components.md +317 -0
  99. data/docs/visual-skills/analysis/overlap.md +360 -0
  100. data/docs/visual-skills/analysis/pi-design-deck.md +1102 -0
  101. data/docs/visual-skills/analysis/streamweaver-inventory.md +93 -0
  102. data/docs/visual-skills/analysis/unified-specs.feature +1003 -0
  103. data/docs/visual-skills/analysis/visual-explainer.md +964 -0
  104. data/docs/visual-skills/blog/blog-series-outline.md +39 -0
  105. data/docs/visual-skills/blog/token-efficiency.md +73 -0
  106. data/docs/visual-skills/design/architecture.md +1725 -0
  107. data/docs/visual-skills/design/codex-review.md +340 -0
  108. data/docs/visual-skills/design/dhh-review.md +209 -0
  109. data/docs/visual-skills/design/evolution.md +100 -0
  110. data/docs/visual-skills/design/gemini-review.md +55 -0
  111. data/docs/visual-skills/design/review-synthesis.md +149 -0
  112. data/docs/visual-skills/implementation/STATE.md +22 -0
  113. data/docs/visual-skills/implementation/plan.md +347 -0
  114. data/docs/visual-skills/implementation/spike-findings.md +237 -0
  115. data/docs/visual-skills/implementation/tasks.md +702 -0
  116. data/docs/visual-skills/lessons-learned/process.md +145 -0
  117. data/examples/README.md +96 -0
  118. data/examples/advanced/all_components.rb +177 -0
  119. data/examples/advanced/examples_browser.rb +394 -0
  120. data/examples/advanced/teachables_browser.rb +261 -0
  121. data/examples/advanced/theme_tweaker.rb +366 -0
  122. data/examples/advanced/tutorial.rb +1528 -0
  123. data/examples/agentic/agentic_form.rb +30 -0
  124. data/examples/agentic/agentic_form_autoclose.rb +30 -0
  125. data/examples/agentic/cultivation_tracker.rb +69 -0
  126. data/examples/basic/hello_world.rb +22 -0
  127. data/examples/basic/opal_tabs_table.rb +44 -0
  128. data/examples/basic/todo_list.rb +39 -0
  129. data/examples/button_loading_test.rb +36 -0
  130. data/examples/canvas/mermaid_canvas_demo.sh +151 -0
  131. data/examples/charts/bar_chart_demo.rb +57 -0
  132. data/examples/charts/line_chart_demo.rb +73 -0
  133. data/examples/charts/pie_area_demo.rb +87 -0
  134. data/examples/charts/stacked_bar_chart_demo.rb +69 -0
  135. data/examples/claude_code/README.md +85 -0
  136. data/examples/claude_code/codebreaker/.claude/commands/infiltrate.md +632 -0
  137. data/examples/claude_code/codebreaker/.claude/settings.json +11 -0
  138. data/examples/claude_code/codebreaker/.claude/settings.local.json +17 -0
  139. data/examples/claude_code/codebreaker/.claude/skills/infiltrate.md +0 -0
  140. data/examples/claude_code/codebreaker/README.md +213 -0
  141. data/examples/claude_code/tutorial/.claude/commands/learn.md +453 -0
  142. data/examples/claude_code/tutorial/README.md +102 -0
  143. data/examples/claude_code/verification_flow/.claude/commands/verify.md +171 -0
  144. data/examples/claude_code/verification_flow/README.md +126 -0
  145. data/examples/components/annotated_code_demo.rb +54 -0
  146. data/examples/components/callout_demo.rb +33 -0
  147. data/examples/components/checkbox_group_demo.rb +46 -0
  148. data/examples/components/design_review.css +468 -0
  149. data/examples/components/design_review_demo.rb +44 -0
  150. data/examples/components/design_review_dsl.rb +300 -0
  151. data/examples/components/diff_block_demo.rb +112 -0
  152. data/examples/components/events_demo.rb +184 -0
  153. data/examples/components/form_demo.rb +62 -0
  154. data/examples/components/lesson_demo.rb +53 -0
  155. data/examples/components/markdown_demo.rb +110 -0
  156. data/examples/components/mermaid_demo.rb +166 -0
  157. data/examples/components/pareto_set.rb +64 -0
  158. data/examples/components/prd_demo.rb +21 -0
  159. data/examples/components/prd_dsl.rb +385 -0
  160. data/examples/components/quiz_demo.rb +47 -0
  161. data/examples/components/run_viewer_demo.rb +100 -0
  162. data/examples/components/score_and_collapsible_demo.rb +82 -0
  163. data/examples/components/select_stale_value_smoke_test.rb +39 -0
  164. data/examples/components/table_demo.rb +247 -0
  165. data/examples/components/timer_with_state_demo.rb +58 -0
  166. data/examples/components/todo_due_dates.rb +63 -0
  167. data/examples/components/uat_gaps_demo.rb +112 -0
  168. data/examples/dashboard/feed_simulator.rb +60 -0
  169. data/examples/dashboard/live_dashboard.rb +80 -0
  170. data/examples/dashboard_components.rb +108 -0
  171. data/examples/deferred_fragments_demo.rb +62 -0
  172. data/examples/generate_more_spike/README.md +63 -0
  173. data/examples/generate_more_spike/app.rb +441 -0
  174. data/examples/git_health.sh +401 -0
  175. data/examples/layout/layout_components_demo.rb +174 -0
  176. data/examples/layout/modal_demo.rb +215 -0
  177. data/examples/layout/navigation_demo.rb +227 -0
  178. data/examples/layout/route_tabs_demo.rb +52 -0
  179. data/examples/layout/routing_demo.rb +52 -0
  180. data/examples/layout/scroll_box_demo.rb +154 -0
  181. data/examples/lazy_fragments_demo.rb +151 -0
  182. data/examples/my_todos/README.md +29 -0
  183. data/examples/my_todos/my_todos.rb +312 -0
  184. data/examples/my_todos/store.rb +86 -0
  185. data/examples/opal/reactive_demo.rb +62 -0
  186. data/examples/opal/scenarios/s1_counter.rb +15 -0
  187. data/examples/opal/scenarios/s2_search_filter.rb +17 -0
  188. data/examples/opal/scenarios/s3_sibling_tabs.rb +25 -0
  189. data/examples/opal/scenarios/s4_shopping_cart.rb +34 -0
  190. data/examples/opal/scenarios/s5_watch.rb +39 -0
  191. data/examples/opal/scenarios/s6_on_start.rb +20 -0
  192. data/examples/opal/scenarios/s7_wizard.rb +33 -0
  193. data/examples/opal/scenarios/s8_loan_calculator.rb +21 -0
  194. data/examples/opal/scenarios/s9_dashboard.rb +27 -0
  195. data/examples/operations_dashboard_demo.rb +167 -0
  196. data/examples/panel_demo.sh +447 -0
  197. data/examples/parity/assets/tyrion_slice.css +86 -0
  198. data/examples/parity/rivet_people_slice.rb +182 -0
  199. data/examples/parity/tyrion_components.css +777 -0
  200. data/examples/parity/tyrion_warroom_components.rb +414 -0
  201. data/examples/parity/tyrion_warroom_slice.rb +549 -0
  202. data/examples/puma_dev/README.md +124 -0
  203. data/examples/puma_dev/config.ru +34 -0
  204. data/examples/puma_dev/standalone_app.rb +34 -0
  205. data/examples/scaffolding/blog.rb +49 -0
  206. data/examples/scaffolding/utf_lite.rb +105 -0
  207. data/examples/styling/feedback_demo.rb +229 -0
  208. data/examples/styling/style_showcase.rb +172 -0
  209. data/examples/styling/theme_demo.rb +508 -0
  210. data/examples/timer_health_checker.rb +111 -0
  211. data/examples/timer_showcase.rb +131 -0
  212. data/examples/tutorials/render_markdown.rb +112 -0
  213. data/examples/tutorials/streamweaver_way_tutorial.rb +44 -0
  214. data/examples/tutorials/tutorial_content.rb +1082 -0
  215. data/examples/visual_skills/design_deck_demo.rb +314 -0
  216. data/examples/visual_skills/explainer_demo.rb +499 -0
  217. data/exe/streamweaver +9 -0
  218. data/gsd/ROADMAP-1.0.md +167 -0
  219. data/gsd/STATE.md +13 -0
  220. data/gsd/research/market-positioning-research.md +139 -0
  221. data/gsd/research/production-patterns-research.md +270 -0
  222. data/gsd/research/repo-audit-1.0.md +274 -0
  223. data/lib/stream_weaver/action_token.rb +58 -0
  224. data/lib/stream_weaver/adapter/alpinejs.rb +8249 -0
  225. data/lib/stream_weaver/adapter/base.rb +591 -0
  226. data/lib/stream_weaver/adapter/opal.rb +334 -0
  227. data/lib/stream_weaver/adapter/static.rb +1118 -0
  228. data/lib/stream_weaver/admin.rb +176 -0
  229. data/lib/stream_weaver/app.rb +1821 -0
  230. data/lib/stream_weaver/assets/js/sw-copy.js +50 -0
  231. data/lib/stream_weaver/assets/js/sw-heredoc-rewrite.js +68 -0
  232. data/lib/stream_weaver/assets/js/sw-keyboard.js +165 -0
  233. data/lib/stream_weaver/assets/js/sw-mermaid-zoom.js +621 -0
  234. data/lib/stream_weaver/assets/js/sw-route-tabs.js +53 -0
  235. data/lib/stream_weaver/assets/js/sw-sidebar-toc.js +116 -0
  236. data/lib/stream_weaver/assets/js/sw-slide-nav.js +103 -0
  237. data/lib/stream_weaver/canvas/bridge.rb +239 -0
  238. data/lib/stream_weaver/canvas/bridge_server.rb +646 -0
  239. data/lib/stream_weaver/canvas/client.rb +298 -0
  240. data/lib/stream_weaver/canvas/doc_roots.rb +233 -0
  241. data/lib/stream_weaver/canvas/doc_store.rb +252 -0
  242. data/lib/stream_weaver/canvas/gist_publisher.rb +264 -0
  243. data/lib/stream_weaver/canvas/gist_save_handler.rb +89 -0
  244. data/lib/stream_weaver/canvas/gist_store.rb +135 -0
  245. data/lib/stream_weaver/canvas/helpers.rb +109 -0
  246. data/lib/stream_weaver/canvas/history.rb +90 -0
  247. data/lib/stream_weaver/canvas/protocol.rb +86 -0
  248. data/lib/stream_weaver/canvas/reader.rb +853 -0
  249. data/lib/stream_weaver/canvas/save_doc_widget.rb +457 -0
  250. data/lib/stream_weaver/canvas/scroll_top_hint.rb +21 -0
  251. data/lib/stream_weaver/canvas/session.rb +132 -0
  252. data/lib/stream_weaver/cli.rb +3235 -0
  253. data/lib/stream_weaver/component_assets.rb +70 -0
  254. data/lib/stream_weaver/component_registry.rb +67 -0
  255. data/lib/stream_weaver/component_renderer.rb +48 -0
  256. data/lib/stream_weaver/components/annotated_code.rb +53 -0
  257. data/lib/stream_weaver/components/api_endpoint.rb +42 -0
  258. data/lib/stream_weaver/components/callout.rb +59 -0
  259. data/lib/stream_weaver/components/chart.rb +84 -0
  260. data/lib/stream_weaver/components/code_block.rb +77 -0
  261. data/lib/stream_weaver/components/comparison.rb +39 -0
  262. data/lib/stream_weaver/components/decision.rb +38 -0
  263. data/lib/stream_weaver/components/deck/close_overlay.rb +84 -0
  264. data/lib/stream_weaver/components/deck/confirmation_bar.rb +60 -0
  265. data/lib/stream_weaver/components/deck/deck_option.rb +61 -0
  266. data/lib/stream_weaver/components/deck/deck_slide.rb +77 -0
  267. data/lib/stream_weaver/components/deck/deck_state.rb +469 -0
  268. data/lib/stream_weaver/components/deck/deck_summary.rb +73 -0
  269. data/lib/stream_weaver/components/deck/design_deck.rb +60 -0
  270. data/lib/stream_weaver/components/deck/generate_more_controls.rb +84 -0
  271. data/lib/stream_weaver/components/deck/model_selector.rb +84 -0
  272. data/lib/stream_weaver/components/deck/skeleton_placeholder.rb +36 -0
  273. data/lib/stream_weaver/components/diff_block.rb +125 -0
  274. data/lib/stream_weaver/components/doc_header.rb +57 -0
  275. data/lib/stream_weaver/components/image_block.rb +70 -0
  276. data/lib/stream_weaver/components/implementation_map.rb +33 -0
  277. data/lib/stream_weaver/components/keyboard_shortcuts.rb +94 -0
  278. data/lib/stream_weaver/components/kpi_dashboard.rb +78 -0
  279. data/lib/stream_weaver/components/mermaid.rb +79 -0
  280. data/lib/stream_weaver/components/pipeline.rb +63 -0
  281. data/lib/stream_weaver/components/sidebar_toc.rb +46 -0
  282. data/lib/stream_weaver/components/slide_container.rb +141 -0
  283. data/lib/stream_weaver/components/timeline_event.rb +58 -0
  284. data/lib/stream_weaver/components/wireframe.rb +29 -0
  285. data/lib/stream_weaver/components/wireframe_block.rb +34 -0
  286. data/lib/stream_weaver/components.rb +2711 -0
  287. data/lib/stream_weaver/css.rb +230 -0
  288. data/lib/stream_weaver/dev_fallback_overlay.rb +61 -0
  289. data/lib/stream_weaver/display_dsl.rb +1004 -0
  290. data/lib/stream_weaver/export/html_exporter.rb +478 -0
  291. data/lib/stream_weaver/feed.rb +34 -0
  292. data/lib/stream_weaver/feed_builder.rb +31 -0
  293. data/lib/stream_weaver/fonts.rb +33 -0
  294. data/lib/stream_weaver/interaction_runner.rb +487 -0
  295. data/lib/stream_weaver/iterm.rb +460 -0
  296. data/lib/stream_weaver/layout_registry.rb +92 -0
  297. data/lib/stream_weaver/opal/bridge.rb +52 -0
  298. data/lib/stream_weaver/opal/builder.rb +193 -0
  299. data/lib/stream_weaver/opal/env.rb +29 -0
  300. data/lib/stream_weaver/opal/reactive_state.rb +75 -0
  301. data/lib/stream_weaver/opal/regexp_anchor_patch.rb +62 -0
  302. data/lib/stream_weaver/opal/renderer.rb +77 -0
  303. data/lib/stream_weaver/opal/runtime.rb +250 -0
  304. data/lib/stream_weaver/opal/shell.rb +145 -0
  305. data/lib/stream_weaver/opal/string_bridge.rb +50 -0
  306. data/lib/stream_weaver/opal/stubs/diff.min.js +1 -0
  307. data/lib/stream_weaver/opal/stubs/digest.rb +15 -0
  308. data/lib/stream_weaver/opal/stubs/marked.umd.js +79 -0
  309. data/lib/stream_weaver/opal/stubs/md5.rb +3 -0
  310. data/lib/stream_weaver/opal/stubs/morphdom.min.js +775 -0
  311. data/lib/stream_weaver/opal/stubs/prism-tomorrow.min.css +1 -0
  312. data/lib/stream_weaver/opal/stubs/prism.min.js +1967 -0
  313. data/lib/stream_weaver/opal_entry.rb +136 -0
  314. data/lib/stream_weaver/org/inline.rb +95 -0
  315. data/lib/stream_weaver/org/reader.rb +561 -0
  316. data/lib/stream_weaver/org/recording_context.rb +86 -0
  317. data/lib/stream_weaver/org/source_splitter.rb +63 -0
  318. data/lib/stream_weaver/org/writer.rb +300 -0
  319. data/lib/stream_weaver/page_shell.rb +530 -0
  320. data/lib/stream_weaver/portfile.rb +79 -0
  321. data/lib/stream_weaver/pushable.rb +42 -0
  322. data/lib/stream_weaver/resource/default_views.rb +111 -0
  323. data/lib/stream_weaver/resource/field_input.rb +25 -0
  324. data/lib/stream_weaver/resource/state_keys.rb +16 -0
  325. data/lib/stream_weaver/resource/store.rb +18 -0
  326. data/lib/stream_weaver/resource.rb +104 -0
  327. data/lib/stream_weaver/server.rb +1471 -0
  328. data/lib/stream_weaver/service.rb +1240 -0
  329. data/lib/stream_weaver/service_client.rb +104 -0
  330. data/lib/stream_weaver/session_store.rb +186 -0
  331. data/lib/stream_weaver/skills/streamweaver-canvas-safe/SKILL.md +66 -0
  332. data/lib/stream_weaver/skills/streamweaver-canvas-safe/examples/canvas-safe-showcase.rb +98 -0
  333. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/actions-and-buttons.md +47 -0
  334. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/charts-and-diagrams.md +38 -0
  335. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/deck.md +32 -0
  336. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/inputs-and-forms.md +65 -0
  337. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/tabs-and-navigation.md +53 -0
  338. data/lib/stream_weaver/skills/streamweaver-doc-builder/SKILL.md +298 -0
  339. data/lib/stream_weaver/skills/streamweaver-visual-companion/SKILL.md +130 -0
  340. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.css +468 -0
  341. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.rb +39 -0
  342. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example_dsl.rb +300 -0
  343. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example.rb +24 -0
  344. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example_dsl.rb +385 -0
  345. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/checkpoints-and-forms.md +28 -0
  346. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/cleanup-and-panel.md +44 -0
  347. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/example-gallery.md +8 -0
  348. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/persistence.md +58 -0
  349. data/lib/stream_weaver/skills/streamweaver-way/SKILL.md +396 -0
  350. data/lib/stream_weaver/skills/visual-plan/SKILL.md +201 -0
  351. data/lib/stream_weaver/skills/visual-recap/SKILL.md +244 -0
  352. data/lib/stream_weaver/streamer.rb +63 -0
  353. data/lib/stream_weaver/templates/choices.rb +149 -0
  354. data/lib/stream_weaver/templates/code.rb +157 -0
  355. data/lib/stream_weaver/templates/confirm.rb +110 -0
  356. data/lib/stream_weaver/templates/diff.rb +195 -0
  357. data/lib/stream_weaver/templates/info.rb +142 -0
  358. data/lib/stream_weaver/templates/table.rb +169 -0
  359. data/lib/stream_weaver/templates/wizard.rb +271 -0
  360. data/lib/stream_weaver/theme/auto_mode.rb +116 -0
  361. data/lib/stream_weaver/theme/presets.rb +555 -0
  362. data/lib/stream_weaver/theme.rb +639 -0
  363. data/lib/stream_weaver/university/canvas.rb +924 -0
  364. data/lib/stream_weaver/university/course.rb +699 -0
  365. data/lib/stream_weaver/university/demos/counter.rb +33 -0
  366. data/lib/stream_weaver/university/demos/dashboard.rb +147 -0
  367. data/lib/stream_weaver/university/demos/decision_form.rb +143 -0
  368. data/lib/stream_weaver/university/demos.rb +50 -0
  369. data/lib/stream_weaver/university/listener.rb +467 -0
  370. data/lib/stream_weaver/university/progress.rb +201 -0
  371. data/lib/stream_weaver/university/runner.rb +138 -0
  372. data/lib/stream_weaver/university/scripts/growing_doc.rb +609 -0
  373. data/lib/stream_weaver/university/scripts/growing_doc_state.rb +91 -0
  374. data/lib/stream_weaver/utils.rb +31 -0
  375. data/lib/stream_weaver/version.rb +5 -0
  376. data/lib/stream_weaver/views/canvas/reader_layout.erb +800 -0
  377. data/lib/stream_weaver/views.rb +3940 -0
  378. data/lib/stream_weaver.rb +122 -0
  379. data/llms.txt +1269 -0
  380. data/sig/stream_weaver.rbs +4 -0
  381. metadata +598 -0
data/docs/templates.md ADDED
@@ -0,0 +1,213 @@
1
+ # StreamWeaver Templates
2
+
3
+ Templates provide pre-built UI patterns for common interactions. Each template is a single command that handles the full interaction flow and returns JSON.
4
+
5
+ ## Quick Reference
6
+
7
+ | Template | Purpose | Returns |
8
+ |----------|---------|---------|
9
+ | `wizard` | Multi-step forms with branching | All collected fields |
10
+ | `choices` | Pick one from options | `{"choice": "Selected"}` |
11
+ | `confirm` | Yes/No decision | `{"confirmed": true/false}` |
12
+ | `info` | Show message + pick action | `{"action": "Clicked"}` |
13
+ | `table` | Display data, optional selection | `{"action": "OK"}` or `{"selected_row": N}` |
14
+ | `code` | Show code with line numbers | `{"action": "Apply/Cancel"}` |
15
+ | `diff` | Show changes with highlighting | `{"action": "Apply/Reject"}` |
16
+
17
+ ## For LLMs
18
+
19
+ ### Basic Usage Pattern
20
+
21
+ ```bash
22
+ # All templates follow this pattern:
23
+ ./exe/streamweaver template <name> <session> '<json-config>'
24
+
25
+ # Returns JSON on stdout when user completes interaction
26
+ ```
27
+
28
+ ### Wizard - Multi-step Forms
29
+
30
+ ```bash
31
+ # Linear wizard (steps in order)
32
+ ./exe/streamweaver template wizard test '{
33
+ "title": "Project Setup",
34
+ "steps": [
35
+ {"title": "Info", "fields": [
36
+ {"type": "text", "key": "name", "label": "Project name"},
37
+ {"type": "select", "key": "type", "label": "Type", "options": ["Web", "CLI", "API"]}
38
+ ]},
39
+ {"title": "Options", "fields": [
40
+ {"type": "checkbox", "key": "tests", "label": "Include tests"},
41
+ {"type": "checkbox", "key": "ci", "label": "CI/CD"}
42
+ ]}
43
+ ]
44
+ }'
45
+ # Returns: {"name": "MyApp", "type": "Web", "tests": true, "ci": false}
46
+ ```
47
+
48
+ ```bash
49
+ # Branching wizard (different paths based on choices)
50
+ ./exe/streamweaver template wizard test '{
51
+ "title": "Setup",
52
+ "steps": [
53
+ {"id": "type", "title": "Type", "fields": [
54
+ {"type": "select", "key": "project_type", "options": ["Web", "CLI"]}
55
+ ], "next": {"branch_on": "project_type", "Web": "web_opts", "CLI": "cli_opts"}},
56
+ {"id": "web_opts", "title": "Web Options", "fields": [
57
+ {"type": "select", "key": "framework", "options": ["React", "Vue"]}
58
+ ], "next": "done"},
59
+ {"id": "cli_opts", "title": "CLI Options", "fields": [
60
+ {"type": "select", "key": "cli_lib", "options": ["Thor", "Commander"]}
61
+ ], "next": "done"}
62
+ ]
63
+ }'
64
+ ```
65
+
66
+ **Field types:**
67
+ - `text` - Text input
68
+ - `select` - Dropdown selection
69
+ - `checkbox` - Boolean toggle
70
+
71
+ ### Choices - Quick Selection
72
+
73
+ ```bash
74
+ ./exe/streamweaver template choices test '{
75
+ "title": "Select Database",
76
+ "description": "Which database for this project?",
77
+ "options": ["PostgreSQL", "SQLite", "MySQL"]
78
+ }'
79
+ # Returns: {"choice": "PostgreSQL"}
80
+ ```
81
+
82
+ ### Confirm - Yes/No
83
+
84
+ ```bash
85
+ ./exe/streamweaver template confirm test '{
86
+ "title": "Delete files?",
87
+ "message": "This will permanently remove 3 files.",
88
+ "yes": "Delete",
89
+ "no": "Cancel"
90
+ }'
91
+ # Returns: {"confirmed": true} or {"confirmed": false}
92
+ ```
93
+
94
+ ### Info - Message with Actions
95
+
96
+ ```bash
97
+ ./exe/streamweaver template info test '{
98
+ "title": "Build Complete",
99
+ "message": "All tests passed",
100
+ "details": ["12 specs", "Coverage: 94%"],
101
+ "actions": ["Deploy", "View Report", "Close"]
102
+ }'
103
+ # Returns: {"action": "Deploy"}
104
+ ```
105
+
106
+ ### Table - Data Display
107
+
108
+ ```bash
109
+ ./exe/streamweaver template table test '{
110
+ "title": "Recent Files",
111
+ "headers": ["File", "Size", "Modified"],
112
+ "rows": [
113
+ ["app.rb", "12kb", "2 hours ago"],
114
+ ["cli.rb", "8kb", "yesterday"]
115
+ ]
116
+ }'
117
+ # Returns: {"action": "OK"}
118
+
119
+ # With row selection:
120
+ ./exe/streamweaver template table test '{
121
+ "title": "Select File",
122
+ "headers": ["File", "Size"],
123
+ "rows": [["app.rb", "12kb"], ["cli.rb", "8kb"]],
124
+ "selectable": true
125
+ }'
126
+ # Returns: {"selected_row": 0, "selected_data": ["app.rb", "12kb"]}
127
+ ```
128
+
129
+ ### Code - Code Display
130
+
131
+ ```bash
132
+ ./exe/streamweaver template code test '{
133
+ "title": "Generated Code",
134
+ "filename": "hello.rb",
135
+ "code": "def hello\n puts \"Hello\"\nend",
136
+ "actions": ["Apply", "Edit", "Cancel"]
137
+ }'
138
+ # Returns: {"action": "Apply"}
139
+ ```
140
+
141
+ ### Diff - Change Display
142
+
143
+ ```bash
144
+ ./exe/streamweaver template diff test '{
145
+ "title": "Proposed Changes",
146
+ "filename": "app.rb",
147
+ "diff": "- old line\n+ new line\n context",
148
+ "actions": ["Apply", "Reject"]
149
+ }'
150
+ # Returns: {"action": "Apply"}
151
+ ```
152
+
153
+ ## For Humans
154
+
155
+ ### Architecture
156
+
157
+ Templates are Ruby classes in `lib/stream_weaver/templates/` that:
158
+ 1. Push UI to a live session
159
+ 2. Wait for user interaction
160
+ 3. Return collected data as JSON
161
+
162
+ Each template clears stale submissions on start and uses unique field IDs to prevent browser autocomplete issues.
163
+
164
+ ### Adding New Templates
165
+
166
+ 1. Create `lib/stream_weaver/templates/your_template.rb`
167
+ 2. Inherit common patterns from existing templates
168
+ 3. Add to CLI in `lib/stream_weaver/cli.rb` (search for `when 'wizard'`)
169
+
170
+ ### Native DSL Components
171
+
172
+ Templates can use StreamWeaver's DSL directly:
173
+
174
+ ```ruby
175
+ # In template build_dsl method:
176
+ lines << "header2 \"Title\""
177
+ lines << "table headers: [\"A\", \"B\"], rows: [[\"1\", \"2\"]], striped: true"
178
+ lines << "button \"Submit\""
179
+ ```
180
+
181
+ ### Available Table Options
182
+
183
+ ```ruby
184
+ table headers: [...], rows: [...],
185
+ striped: true, # Zebra stripes
186
+ bordered: true, # Cell borders
187
+ hoverable: true, # Highlight on hover
188
+ compact: true, # Reduced padding
189
+ caption: "Title" # Table caption
190
+ ```
191
+
192
+ ## Session Management
193
+
194
+ ```bash
195
+ # Start a live session
196
+ ./exe/streamweaver live mysession
197
+
198
+ # Push content
199
+ ./exe/streamweaver push mysession --dsl 'text "Hello"'
200
+
201
+ # Wait for submission (blocks)
202
+ ./exe/streamweaver wait mysession --timeout 60
203
+
204
+ # Run template (combines push + wait)
205
+ ./exe/streamweaver template wizard mysession '{...}'
206
+ ```
207
+
208
+ ## Tips
209
+
210
+ 1. **Single command = no permission prompts** - Templates handle the full flow in one bash call
211
+ 2. **JSON escaping** - Use single quotes around JSON to avoid shell escaping issues
212
+ 3. **Timeout** - Templates default to 300s timeout; user must interact within this window
213
+ 4. **Browser** - User needs the live session open at `http://localhost:PORT/live/SESSION`
data/docs/testing.md ADDED
@@ -0,0 +1,155 @@
1
+ # Testing StreamWeaver Apps
2
+
3
+ This guide covers testing strategies for StreamWeaver applications.
4
+
5
+ ## Unit Testing with `rebuild_with_state`
6
+
7
+ Test component logic without running a server:
8
+
9
+ ```ruby
10
+ require 'spec_helper'
11
+
12
+ RSpec.describe "My App" do
13
+ let(:my_app) do
14
+ StreamWeaver::App.new("Test") do
15
+ text_field :name
16
+ if state[:name] == "Alice"
17
+ text "Hello Alice!"
18
+ end
19
+ end
20
+ end
21
+
22
+ it "shows greeting when name is Alice" do
23
+ my_app.rebuild_with_state({ name: "Alice" })
24
+ expect(my_app.components.length).to eq(2) # text_field + text
25
+ end
26
+
27
+ it "hides greeting when name is empty" do
28
+ my_app.rebuild_with_state({ name: "" })
29
+ expect(my_app.components.length).to eq(1) # just text_field
30
+ end
31
+ end
32
+ ```
33
+
34
+ ## Integration Testing with Rack::Test
35
+
36
+ Test HTTP endpoints and button actions without opening a browser:
37
+
38
+ ```ruby
39
+ require 'spec_helper'
40
+ require 'rack/test'
41
+
42
+ RSpec.describe "My App Integration" do
43
+ include Rack::Test::Methods
44
+
45
+ let(:stream_app) do
46
+ StreamWeaver.app "Test" do
47
+ text_field :name
48
+ button "Greet" do |s|
49
+ s[:greeted] = true
50
+ s[:greeting] = "Hello, #{s[:name]}!"
51
+ end
52
+ end
53
+ end
54
+ let(:app) { stream_app.generate } # Get Sinatra app for Rack::Test
55
+
56
+ it "renders initial form" do
57
+ get '/'
58
+ expect(last_response).to be_ok
59
+ expect(last_response.body).to include('name')
60
+ end
61
+
62
+ it "handles button click with session state" do
63
+ # Inject state before request
64
+ env 'rack.session', { streamlit_state: { name: "Alice" } }
65
+
66
+ # Simulate button click (button names normalized: lowercase, underscores)
67
+ post '/action/btn_greet_1', { name: "Alice" }
68
+
69
+ # Verify state was updated
70
+ state = last_request.session[:streamlit_state]
71
+ expect(state[:greeted]).to be true
72
+ expect(state[:greeting]).to eq("Hello, Alice!")
73
+ end
74
+
75
+ it "handles form field update" do
76
+ env 'rack.session', { streamlit_state: { name: "" } }
77
+ post '/update', { name: "Bob" }
78
+
79
+ state = last_request.session[:streamlit_state]
80
+ expect(state[:name]).to eq("Bob")
81
+ end
82
+ end
83
+ ```
84
+
85
+ ## Key Testing Patterns
86
+
87
+ | Pattern | Purpose |
88
+ |---------|---------|
89
+ | `rebuild_with_state(hash)` | Re-evaluates DSL block with given state, for testing conditional rendering |
90
+ | `app.generate` | Returns the Sinatra app for Rack::Test |
91
+ | `env 'rack.session', { streamlit_state: {...} }` | Inject state before requests |
92
+ | `last_request.session[:streamlit_state]` | Read state after requests |
93
+ | `POST /action/btn_<name>_<id>` | Button actions (names normalized: lowercase, underscores) |
94
+ | `POST /update` | Form field updates |
95
+
96
+ ## Testing `route_with`
97
+
98
+ Covered in [`docs/routing.md`](routing.md) § Testing route_with, not here: extract the parser
99
+ and builder into plain functions and test them with a `build(parse(path)) == path` round-trip
100
+ check over every route — no Rack::Test or session harness required. See that doc's Common
101
+ Pitfalls section for the two bug classes this catches.
102
+
103
+ ## Testing Agentic Mode (`run_once!`)
104
+
105
+ The `/submit` endpoint can be tested directly:
106
+
107
+ ```ruby
108
+ it "handles form submission" do
109
+ env 'rack.session', { streamlit_state: { name: "Test", priority: "High" } }
110
+ post '/submit', { name: "Test", priority: "High" }
111
+ expect(last_response).to be_ok
112
+ end
113
+ ```
114
+
115
+ Note: Testing the full `run_once!` flow (with browser wait loop) requires manual testing or browser automation.
116
+
117
+ ## Session State Notes
118
+
119
+ State persists in browser cookies across server restarts. For testing:
120
+ - Clear browser cookies for localhost
121
+ - Use incognito mode for fresh sessions
122
+
123
+ ## Smoke test / UAT
124
+
125
+ `bin/smoke` is an executable UAT battery, not an rspec file. It boots real
126
+ StreamWeaver servers on ephemeral ports and drives them over real HTTP
127
+ (`Net::HTTP`), covering things unit/integration specs don't: the actual
128
+ process boot sequence, the standalone-vs-service seam, and slug/hex URL
129
+ resolution across multiple loaded apps.
130
+
131
+ It covers, using a small fixture app (`bin/support/smoke_fixture.rb`):
132
+
133
+ - **Standalone mode** (`ruby app.rb`, driven via `run!`): custom `endpoint`
134
+ JSON/webhook/CSV responses, the reserved-path boot warning and internal
135
+ route always winning for `/update`, and 404s for unmapped verbs/paths.
136
+ - **Service mode** (`streamweaver serve`): loading an app via `POST
137
+ /load-app`, rendering it at both its slug and hex `/apps/:id` URLs,
138
+ endpoint dispatch scoped under `/apps/:id/...` for both forms, the
139
+ collision-suffix behavior when two different files derive the same slug,
140
+ and slug reuse when the same file is reloaded.
141
+
142
+ Run it locally:
143
+
144
+ ```bash
145
+ bin/smoke
146
+ ```
147
+
148
+ It prints one check/x check line per assertion plus a final `N/M passed`
149
+ summary, and exits non-zero if anything fails (all remaining checks still
150
+ run so a single miss doesn't hide others). Spawned processes are tracked and
151
+ killed (by process group) in an `at_exit` handler, and ports are picked by
152
+ binding to port 0 so it never collides with a `streamweaver` instance you
153
+ already have running. It's wired into CI as the `smoke` job alongside
154
+ `rspec`.
155
+ - Add a reset button: `button "Reset" do |s| s.clear end`
@@ -0,0 +1,281 @@
1
+ # Theming hooks
2
+
3
+ StreamWeaver's structural components emit a stable, `sw-` prefixed class on
4
+ every element you'd want to skin. The contract:
5
+
6
+ - **`sw-` prefixed classes are a stable public API.** They will not be
7
+ renamed or removed without a deprecation cycle. Write your CSS against
8
+ them.
9
+ - **Any other class (internal names, Alpine `x-` attributes, `data-sw-*`
10
+ attributes) is an implementation detail.** It can change between
11
+ releases without notice. Don't select on it in production CSS.
12
+ - **`class:`/`style:` on any structural component appends to the element,
13
+ it never replaces the framework's own classes.** Your class always
14
+ lands after the framework's, so at equal specificity your rule wins on
15
+ document order too -- and since stream_weaver-oeo, every framework rule
16
+ also lives in `@layer stream-weaver`, so your unlayered CSS wins
17
+ regardless of specificity or order. One stylesheet targeting these
18
+ hooks is enough to fully re-skin an app; you never need to fight the
19
+ framework's own styles.
20
+ - **A few components still emit a second, unprefixed legacy class**
21
+ (`card`, `card-header`, `card-body`, `card-footer`, `status-badge` and
22
+ its `status-badge-*` siblings, and the `.sidebar-section` utility class)
23
+ **alongside** the `sw-` hook, for backward compatibility. These generic
24
+ names are exactly what stream_weaver-lyb was filed over -- they can
25
+ collide with an app's own class names by coincidence (a real app's
26
+ `.card` or `.status-badge` is a completely reasonable thing to
27
+ independently invent). They're deprecated and will be removed at 1.0;
28
+ new code should target the `sw-` hook only.
29
+
30
+ ## Component -> hooks -> minimal CSS
31
+
32
+ ### Board / Lane / BoardCard
33
+
34
+ | Element | Hook |
35
+ |---|---|
36
+ | Board container | `.sw-board` |
37
+ | Board with pinned lane headers | `.sw-board--pinned-headers` |
38
+ | Lane container | `.sw-board__lane` |
39
+ | Lane header band | `.sw-board__lane-header`, `.sw-board__lane-header--{tone}` |
40
+ | Lane heading wrapper | `.sw-board__lane-heading` |
41
+ | Lane title | `.sw-board__lane-title` |
42
+ | Lane subtitle | `.sw-board__lane-subtitle` |
43
+ | Lane card count | `.sw-board__lane-count` |
44
+ | Lane body (card stack) | `.sw-board__lane-body` |
45
+ | Lane icon (before the title) | `.sw-board__lane-icon` |
46
+ | Board card | `.sw-board__card`, `.sw-board__card--{tone}` |
47
+
48
+ ```css
49
+ .sw-board { gap: 1.5rem; }
50
+ .sw-board__lane-header--success { background: #16a34a; }
51
+ .sw-board__card { border-radius: 12px; backdrop-filter: blur(4px); }
52
+ ```
53
+
54
+ `tone:` on `lane`/`board_card` is one of `%i[neutral success warning error info]`.
55
+ `board`, `lane`, and `board_card` all accept `class:`/`style:`.
56
+ `board(pinned_headers: true)` adds `.sw-board--pinned-headers` and
57
+ `data-sw-pinned-headers="true"`; its lane header bands remain sticky while the
58
+ board scrolls vertically.
59
+
60
+ `lane` accepts `icon:` (stream_weaver-oeo) -- an emoji/glyph string, or a
61
+ URL/path (an `App#local_asset` result, `/sw-asset/...`, or any http(s)/data
62
+ URI), rendered before the title. A value is treated as an image when it
63
+ starts with `http://`, `https://`, `/`, or `data:`; anything else renders
64
+ as plain text/emoji. Replaces the `title_prefix` string-concatenation
65
+ workaround (design-parity-fights.md) that both tyrion parity slices used.
66
+
67
+ ### Sidebar
68
+
69
+ | Element | Hook |
70
+ |---|---|
71
+ | Sidebar container | `.sw-sidebar`, `.sw-sidebar-sticky` (when `sticky: true`) |
72
+ | Header wrapper | `.sw-sidebar-header` |
73
+ | Header title | `.sw-sidebar-title` |
74
+ | Content wrapper | `.sw-sidebar-content` |
75
+
76
+ ```css
77
+ .sw-sidebar { width: 260px; background: #1a1714; }
78
+ .sw-sidebar-title { font-family: 'Cinzel', serif; }
79
+ ```
80
+
81
+ `sidebar` accepts `class:`/`style:`.
82
+
83
+ ### Navbar / NavItem
84
+
85
+ | Element | Hook |
86
+ |---|---|
87
+ | Navbar container | `.sw-navbar` |
88
+ | Nav item | `.sw-navbar-item` |
89
+ | Active nav item | `.sw-navbar-item-active` |
90
+ | Nav item label (when close chrome is present) | `.sw-navbar-item__label` |
91
+ | Decorative close chrome | `.sw-navbar-item__close` |
92
+
93
+ ```css
94
+ .sw-navbar { border-bottom: 2px solid gold; }
95
+ .sw-navbar-item-active { text-decoration: underline; }
96
+ ```
97
+
98
+ Both `navbar` and `nav_item` accept `class:`/`style:` (nav_item forwarding
99
+ added in stream_weaver-oeo).
100
+ `nav_item(..., close: true)` adds a decorative `×`; pass a string to `close:`
101
+ for a different glyph. This option is visual tab chrome, not an interactive
102
+ close action. Apps that close tabs should wire that behavior through their own
103
+ action component.
104
+
105
+ ### Topbar
106
+
107
+ App-chrome header bar: brand (icon/glyph + wordmark), a breadcrumb trail,
108
+ and trailing block content (badges/pills/whatever you render in the
109
+ block). Added in stream_weaver-oeo to replace the hand-rolled
110
+ div/phrase topbar both tyrion parity slices needed (design-parity-
111
+ fights.md finding #6).
112
+
113
+ | Element | Hook |
114
+ |---|---|
115
+ | Container | `.sw-topbar` |
116
+ | Brand wrapper | `.sw-topbar-brand` |
117
+ | Brand icon | `.sw-topbar-icon` |
118
+ | Wordmark | `.sw-topbar-wordmark` |
119
+ | Breadcrumb trail | `.sw-topbar-breadcrumbs` |
120
+ | Breadcrumb item | `.sw-topbar-crumb`, `.sw-topbar-crumb--active` (last item) |
121
+ | Breadcrumb separator | `.sw-topbar-separator` |
122
+ | Trailing content | `.sw-topbar-trailing` |
123
+
124
+ ```css
125
+ .sw-topbar { background: #1a1208; border-bottom: 1px solid #4a3520; }
126
+ .sw-topbar-wordmark { font-family: 'Cinzel', serif; letter-spacing: 0.1em; }
127
+ ```
128
+
129
+ ```ruby
130
+ topbar(icon: "🦁", wordmark: "TYRION", breadcrumbs: ["field-ops", "warroom"]) do
131
+ badge("main")
132
+ end
133
+ ```
134
+
135
+ `icon:` follows the same URL-vs-glyph detection as `lane`'s `icon:`.
136
+ `breadcrumbs:` is a plain array of strings; the last one gets
137
+ `.sw-topbar-crumb--active`. The brand wrapper, breadcrumb trail, and
138
+ trailing wrapper are each omitted from the DOM when they have nothing to
139
+ render (no icon/wordmark, empty breadcrumbs, or an empty block).
140
+ `topbar` accepts `class:`/`style:`.
141
+
142
+ ### AppShell
143
+
144
+ | Element | Hook |
145
+ |---|---|
146
+ | Shell container | `.sw-app-shell`, `.sw-app-shell-sidebar-{position}` |
147
+ | Main region | `.sw-app-shell-main` |
148
+ | Sidebar region | `.sw-app-shell-sidebar` |
149
+
150
+ ```css
151
+ .sw-app-shell { --sw-shell-sidebar-width: 280px; }
152
+ ```
153
+
154
+ `app_shell` accepts `class:`/`style:` (merged with the framework's own
155
+ `--sw-shell-sidebar-width`/`--sw-shell-gap` custom properties).
156
+
157
+ ### Card
158
+
159
+ | Element | Hook | Legacy (deprecated) |
160
+ |---|---|---|
161
+ | Card container | `.sw-card`, `.sw-card--{depth}`, `.sw-card--accent-{a\|b\|c}` | `.card` |
162
+ | Corner label | `.sw-card__label` | -- |
163
+ | Header | `.sw-card-header` | `.card-header`, `.card-header--badged` |
164
+ | Header badge | `.card-header__badge` | -- |
165
+ | Header title | `.card-header__title` | -- |
166
+ | Header meta | `.card-header__meta` | -- |
167
+ | Body | `.sw-card-body` | `.card-body` |
168
+ | Footer | `.sw-card-footer` | `.card-footer` |
169
+
170
+ ```css
171
+ .sw-card { border-radius: 16px; }
172
+ .sw-card--hero { box-shadow: 0 20px 40px rgba(0,0,0,0.3); }
173
+ ```
174
+
175
+ `card`, `card_header`, `card_body`, and `card_footer` all accept
176
+ `class:`/`style:` (`card_header`/`card_body`/`card_footer` forwarding
177
+ added in stream_weaver-oeo).
178
+
179
+ ### Table
180
+
181
+ | Element | Hook |
182
+ |---|---|
183
+ | Table element | `.sw-table` |
184
+ | Variants | `.sw-table-striped`, `.sw-table-bordered`, `.sw-table-hoverable`, `.sw-table-compact`, `.sw-table-sortable`, `.sw-table--alternating`, `.sw-table--hover`, `.sw-table--sticky-header` |
185
+ | Row action cell | `.sw-table__actions` |
186
+
187
+ ```css
188
+ .sw-table { font-family: 'IBM Plex Mono', monospace; }
189
+ .sw-table-striped tr:nth-child(even) { background: #f5f5f0; }
190
+ ```
191
+
192
+ `table` accepts `class:`/`style:` on the `<table>` element itself
193
+ (forwarding added in stream_weaver-oeo; `style:` is appended after the
194
+ framework's own required `width`/`border-collapse` base style).
195
+
196
+ If the first column's inline accent styling beats your own CSS on cells (not
197
+ the `<table>` element), that's a cell-level concern -- `class:`/`style:`
198
+ above only forwards to the `<table>` tag. Use per-column `style:` (String or
199
+ Proc) for arbitrary cell CSS, per-column `id_style:` to opt a column in or
200
+ out of the accent styling regardless of position, or table-level
201
+ `id_column:` to redirect (or disable) which column gets it on raw
202
+ `headers:`/`rows:` tables. See Table in `docs/components_reference.md` for
203
+ examples.
204
+
205
+ ### Button
206
+
207
+ | Element | Hook | Legacy (deprecated) |
208
+ |---|---|---|
209
+ | Button | `.sw-button` | `.btn`, `.btn-primary`/`.btn-secondary`, `.btn-quiet`, `.btn-outline`, `.btn-sm` |
210
+
211
+ ```css
212
+ .sw-button { text-transform: uppercase; letter-spacing: 0.05em; }
213
+ ```
214
+
215
+ `.sw-button` is an identifying hook only -- no framework CSS rule targets
216
+ it directly (styling stays keyed off `.btn`/`.btn-primary`/etc. so
217
+ `style: :none`'s "no framework look" contract is unaffected by its
218
+ presence). It's always emitted, including under `style: :none`.
219
+
220
+ ### Modal
221
+
222
+ | Element | Hook |
223
+ |---|---|
224
+ | Outer wrapper | `.sw-modal-wrapper` |
225
+ | Backdrop | `.sw-modal-backdrop` |
226
+ | Dialog | `.sw-modal`, `.sw-modal-{sm\|md\|lg\|xl}` |
227
+ | Header | `.sw-modal-header` |
228
+ | Title | `.sw-modal-title` |
229
+ | Body | `.sw-modal-body` |
230
+
231
+ ```css
232
+ .sw-modal { border-radius: 8px; border: 2px solid gold; }
233
+ ```
234
+
235
+ `modal` accepts `class:`/`style:` on the dialog element (forwarding added
236
+ in stream_weaver-oeo).
237
+
238
+ ### Badge / Status
239
+
240
+ | Component | Element | Hook | Legacy (deprecated) |
241
+ |---|---|---|---|
242
+ | `badge` | Pill | `.sw-badge`, `.sw-badge-{variant}`, `.sw-badge-{size}` | -- |
243
+ | `status_dot` | Dot | `.sw-status-dot`, `.sw-status-dot-{status}`, `.sw-status-dot-{size}`, `.sw-status-dot-pulse` | -- |
244
+ | `status_dot` | Label | `.sw-status-dot-label` | -- |
245
+ | `status_badge` | Badge | `.sw-status-badge`, `.sw-status-badge--{strong\|maybe\|skip\|unknown}` | `.status-badge`, `.status-badge-{strong\|maybe\|skip\|unknown}` |
246
+ | `status_badge` | Icon/label/reasoning | `.sw-status-badge__icon`, `.sw-status-badge__label`, `.sw-status-badge__reasoning` | `.status-badge-icon`, `.status-badge-label`, `.status-badge-reasoning` |
247
+
248
+ ```css
249
+ .sw-badge-success { background: #16a34a; }
250
+ .sw-status-badge--strong { background: rgba(16,185,129,0.15); }
251
+ ```
252
+
253
+ ### Sidebar-section utility class
254
+
255
+ `.sidebar-section` is a plain CSS utility (no dedicated component --
256
+ apply it with `class: "sw-sidebar-section"` on any `div`/container). The
257
+ `sw-sidebar-section` hook styles identically; the unprefixed name is kept
258
+ for back-compat only.
259
+
260
+ ```css
261
+ .sw-sidebar-section { background: transparent; }
262
+ ```
263
+
264
+ ## Why this exists
265
+
266
+ Before stream_weaver-oeo, a bespoke skin had two options: fight the
267
+ framework's CSS with specificity bumps (see
268
+ `gsd/analysis/08-design-parity-fights.md` finding #1, and
269
+ `examples/parity/tyrion_warroom_slice.rb`'s header comment for the
270
+ pixel-parity slice that had to invent its own class names entirely to
271
+ avoid the fight), or reverse-engineer whichever internal class happened
272
+ to be attached to the element you wanted to style. Neither scales past a
273
+ one-off demo.
274
+
275
+ Now: every framework style lives in `@layer stream-weaver`, so any
276
+ unlayered app stylesheet always wins regardless of specificity or
277
+ document order, and every structural component emits one of the `sw-`
278
+ hooks documented above. A bespoke look is "pretty DSL calls + one CSS
279
+ file targeting these hooks" -- see
280
+ `examples/parity/tyrion_warroom_components.rb` +
281
+ `examples/parity/tyrion_components.css` for the proof.