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.
- checksums.yaml +7 -0
- data/.beads/.gitignore +77 -0
- data/.beads/README.md +81 -0
- data/.beads/config.yaml +54 -0
- data/.beads/metadata.json +7 -0
- data/.rspec +3 -0
- data/AGENTS.md +91 -0
- data/CHANGELOG.md +294 -0
- data/CLAUDE.md +67 -0
- data/LICENSE.txt +21 -0
- data/README.md +565 -0
- data/Rakefile +13 -0
- data/assets/streamweaver-hero.jpg +0 -0
- data/bench/baselines/ledger.rb +58 -0
- data/bench/baselines/warroom.rb +51 -0
- data/bench/fixtures/ledger.rb +76 -0
- data/bench/fixtures/warroom.rb +60 -0
- data/bench/results/6daf8b8.md +102 -0
- data/bench/results/c8d749b.md +102 -0
- data/bench/results/eb06ab9.md +102 -0
- data/bench/run.rb +151 -0
- data/bench/support.rb +84 -0
- data/copy_this/dto_run_sample_run_7631.html +220 -0
- data/docs/SERVICE_MODE.md +302 -0
- data/docs/architecture/how_streamweaver_works.md +772 -0
- data/docs/blog/2026-01-16-streamweaver-introduction.md +515 -0
- data/docs/bug-2026-08-31-canvas-bridge-socket-collision.md +83 -0
- data/docs/canvas-ipc-session-summary.md +127 -0
- data/docs/canvas-panel-workflow.md +172 -0
- data/docs/canvas-read.md +147 -0
- data/docs/canvas-roadmap.md +275 -0
- data/docs/case-studies/2026-09-03-didx-canvas-read-shelf.md +147 -0
- data/docs/claude-code-companion-skill-spec.md +567 -0
- data/docs/components_reference.md +1223 -0
- data/docs/crud-patterns.md +265 -0
- data/docs/endpoints.md +122 -0
- data/docs/for_llms.md +1 -0
- data/docs/form-patterns.md +165 -0
- data/docs/frontend-only.md +169 -0
- data/docs/html-artifact-audit.md +238 -0
- data/docs/ideas/2025-01-01-as-a-service.md +203 -0
- data/docs/ideas/2026-01-16-charm-tui-exploration.md +542 -0
- data/docs/ideas/2026-08-27-static-doc-shelf-export.md +153 -0
- data/docs/ideas/dhh-review-future-refactors.md +142 -0
- data/docs/opal-jamstack.md +47 -0
- data/docs/opal-npm-direction.md +184 -0
- data/docs/opal-spike-findings.md +194 -0
- data/docs/plans/2025-12-16-form-blocks-design.md +124 -0
- data/docs/plans/2025-12-30-examples-browser-design.md +152 -0
- data/docs/plans/2026-01-02-charts-design.md +288 -0
- data/docs/plans/2026-01-10-canvas-ipc-design.md +247 -0
- data/docs/plans/2026-01-19-cabinet-control-components.md +176 -0
- data/docs/plans/2026-01-26-gem-release-and-panel.md +140 -0
- data/docs/plans/canvas-claude-project-design.md +238 -0
- data/docs/plans/canvas-doc-location-and-discovery.md +32 -0
- data/docs/plans/org-doc-preview-surfaces.md +293 -0
- data/docs/plans/shadcn-polish-plan.md +417 -0
- data/docs/porting-artifacts.md +252 -0
- data/docs/reference/agent-skills-comparison.md +369 -0
- data/docs/reference/travel-state-prd.artifact.html +780 -0
- data/docs/research/2026-08-17-hotwire-alike-landscape.md +268 -0
- data/docs/research/2026-08-17-hotwire-concept-map.md +404 -0
- data/docs/research/2026-08-22-lazy-fragments-trigger-decision.md +246 -0
- data/docs/research/2026-08-22-learnhotwire-syllabus-coverage.md +239 -0
- data/docs/research/frontend-only-matrix.md +278 -0
- data/docs/research/streamweaver-way-spike-findings.md +529 -0
- data/docs/resource-dsl.md +342 -0
- data/docs/routing.md +302 -0
- data/docs/ruby-ui-comparison.md +319 -0
- data/docs/shared-dsl-fragments.md +356 -0
- data/docs/streamweaver-for-ai-agents.md +401 -0
- data/docs/streamweaver-frontend-vision.md +123 -0
- data/docs/streamweaver_canvas/glimmer_initial_final_layer.rb +77 -0
- data/docs/streamweaver_canvas/save_example.rb +9 -0
- data/docs/templates.md +213 -0
- data/docs/testing.md +155 -0
- data/docs/theming-hooks.md +281 -0
- data/docs/tutorials/the-streamweaver-way.md +882 -0
- data/docs/university/capability-inventory.md +137 -0
- data/docs/university/dependency-survey.md +114 -0
- data/docs/university/design-spec.md +217 -0
- data/docs/university/mockups/code-block-doc-theme-dark.png +0 -0
- data/docs/university/mockups/code-block-doc-theme-light.png +0 -0
- data/docs/university/mockups/course-list-dark.png +0 -0
- data/docs/university/mockups/course-list-light.png +0 -0
- data/docs/university/mockups/course_canvas_mockup.rb +741 -0
- data/docs/university/mockups/step-screen-dark.png +0 -0
- data/docs/university/mockups/step-screen-light.png +0 -0
- data/docs/university/roadmap.md +72 -0
- data/docs/university/send-to-coworker.md +77 -0
- data/docs/university/worker-session-mining.md +109 -0
- data/docs/visual/browser-rendering-spike.md +173 -0
- data/docs/visual/builder-visual-plan-analysis.md +378 -0
- data/docs/visual/sw-plan-format-exploration.md +511 -0
- data/docs/visual/sw-plan-rendering-deep-dive.md +453 -0
- data/docs/visual-skills/PROGRESS.md +60 -0
- data/docs/visual-skills/SESSION-CONTEXT.md +207 -0
- data/docs/visual-skills/analysis/components.md +317 -0
- data/docs/visual-skills/analysis/overlap.md +360 -0
- data/docs/visual-skills/analysis/pi-design-deck.md +1102 -0
- data/docs/visual-skills/analysis/streamweaver-inventory.md +93 -0
- data/docs/visual-skills/analysis/unified-specs.feature +1003 -0
- data/docs/visual-skills/analysis/visual-explainer.md +964 -0
- data/docs/visual-skills/blog/blog-series-outline.md +39 -0
- data/docs/visual-skills/blog/token-efficiency.md +73 -0
- data/docs/visual-skills/design/architecture.md +1725 -0
- data/docs/visual-skills/design/codex-review.md +340 -0
- data/docs/visual-skills/design/dhh-review.md +209 -0
- data/docs/visual-skills/design/evolution.md +100 -0
- data/docs/visual-skills/design/gemini-review.md +55 -0
- data/docs/visual-skills/design/review-synthesis.md +149 -0
- data/docs/visual-skills/implementation/STATE.md +22 -0
- data/docs/visual-skills/implementation/plan.md +347 -0
- data/docs/visual-skills/implementation/spike-findings.md +237 -0
- data/docs/visual-skills/implementation/tasks.md +702 -0
- data/docs/visual-skills/lessons-learned/process.md +145 -0
- data/examples/README.md +96 -0
- data/examples/advanced/all_components.rb +177 -0
- data/examples/advanced/examples_browser.rb +394 -0
- data/examples/advanced/teachables_browser.rb +261 -0
- data/examples/advanced/theme_tweaker.rb +366 -0
- data/examples/advanced/tutorial.rb +1528 -0
- data/examples/agentic/agentic_form.rb +30 -0
- data/examples/agentic/agentic_form_autoclose.rb +30 -0
- data/examples/agentic/cultivation_tracker.rb +69 -0
- data/examples/basic/hello_world.rb +22 -0
- data/examples/basic/opal_tabs_table.rb +44 -0
- data/examples/basic/todo_list.rb +39 -0
- data/examples/button_loading_test.rb +36 -0
- data/examples/canvas/mermaid_canvas_demo.sh +151 -0
- data/examples/charts/bar_chart_demo.rb +57 -0
- data/examples/charts/line_chart_demo.rb +73 -0
- data/examples/charts/pie_area_demo.rb +87 -0
- data/examples/charts/stacked_bar_chart_demo.rb +69 -0
- data/examples/claude_code/README.md +85 -0
- data/examples/claude_code/codebreaker/.claude/commands/infiltrate.md +632 -0
- data/examples/claude_code/codebreaker/.claude/settings.json +11 -0
- data/examples/claude_code/codebreaker/.claude/settings.local.json +17 -0
- data/examples/claude_code/codebreaker/.claude/skills/infiltrate.md +0 -0
- data/examples/claude_code/codebreaker/README.md +213 -0
- data/examples/claude_code/tutorial/.claude/commands/learn.md +453 -0
- data/examples/claude_code/tutorial/README.md +102 -0
- data/examples/claude_code/verification_flow/.claude/commands/verify.md +171 -0
- data/examples/claude_code/verification_flow/README.md +126 -0
- data/examples/components/annotated_code_demo.rb +54 -0
- data/examples/components/callout_demo.rb +33 -0
- data/examples/components/checkbox_group_demo.rb +46 -0
- data/examples/components/design_review.css +468 -0
- data/examples/components/design_review_demo.rb +44 -0
- data/examples/components/design_review_dsl.rb +300 -0
- data/examples/components/diff_block_demo.rb +112 -0
- data/examples/components/events_demo.rb +184 -0
- data/examples/components/form_demo.rb +62 -0
- data/examples/components/lesson_demo.rb +53 -0
- data/examples/components/markdown_demo.rb +110 -0
- data/examples/components/mermaid_demo.rb +166 -0
- data/examples/components/pareto_set.rb +64 -0
- data/examples/components/prd_demo.rb +21 -0
- data/examples/components/prd_dsl.rb +385 -0
- data/examples/components/quiz_demo.rb +47 -0
- data/examples/components/run_viewer_demo.rb +100 -0
- data/examples/components/score_and_collapsible_demo.rb +82 -0
- data/examples/components/select_stale_value_smoke_test.rb +39 -0
- data/examples/components/table_demo.rb +247 -0
- data/examples/components/timer_with_state_demo.rb +58 -0
- data/examples/components/todo_due_dates.rb +63 -0
- data/examples/components/uat_gaps_demo.rb +112 -0
- data/examples/dashboard/feed_simulator.rb +60 -0
- data/examples/dashboard/live_dashboard.rb +80 -0
- data/examples/dashboard_components.rb +108 -0
- data/examples/deferred_fragments_demo.rb +62 -0
- data/examples/generate_more_spike/README.md +63 -0
- data/examples/generate_more_spike/app.rb +441 -0
- data/examples/git_health.sh +401 -0
- data/examples/layout/layout_components_demo.rb +174 -0
- data/examples/layout/modal_demo.rb +215 -0
- data/examples/layout/navigation_demo.rb +227 -0
- data/examples/layout/route_tabs_demo.rb +52 -0
- data/examples/layout/routing_demo.rb +52 -0
- data/examples/layout/scroll_box_demo.rb +154 -0
- data/examples/lazy_fragments_demo.rb +151 -0
- data/examples/my_todos/README.md +29 -0
- data/examples/my_todos/my_todos.rb +312 -0
- data/examples/my_todos/store.rb +86 -0
- data/examples/opal/reactive_demo.rb +62 -0
- data/examples/opal/scenarios/s1_counter.rb +15 -0
- data/examples/opal/scenarios/s2_search_filter.rb +17 -0
- data/examples/opal/scenarios/s3_sibling_tabs.rb +25 -0
- data/examples/opal/scenarios/s4_shopping_cart.rb +34 -0
- data/examples/opal/scenarios/s5_watch.rb +39 -0
- data/examples/opal/scenarios/s6_on_start.rb +20 -0
- data/examples/opal/scenarios/s7_wizard.rb +33 -0
- data/examples/opal/scenarios/s8_loan_calculator.rb +21 -0
- data/examples/opal/scenarios/s9_dashboard.rb +27 -0
- data/examples/operations_dashboard_demo.rb +167 -0
- data/examples/panel_demo.sh +447 -0
- data/examples/parity/assets/tyrion_slice.css +86 -0
- data/examples/parity/rivet_people_slice.rb +182 -0
- data/examples/parity/tyrion_components.css +777 -0
- data/examples/parity/tyrion_warroom_components.rb +414 -0
- data/examples/parity/tyrion_warroom_slice.rb +549 -0
- data/examples/puma_dev/README.md +124 -0
- data/examples/puma_dev/config.ru +34 -0
- data/examples/puma_dev/standalone_app.rb +34 -0
- data/examples/scaffolding/blog.rb +49 -0
- data/examples/scaffolding/utf_lite.rb +105 -0
- data/examples/styling/feedback_demo.rb +229 -0
- data/examples/styling/style_showcase.rb +172 -0
- data/examples/styling/theme_demo.rb +508 -0
- data/examples/timer_health_checker.rb +111 -0
- data/examples/timer_showcase.rb +131 -0
- data/examples/tutorials/render_markdown.rb +112 -0
- data/examples/tutorials/streamweaver_way_tutorial.rb +44 -0
- data/examples/tutorials/tutorial_content.rb +1082 -0
- data/examples/visual_skills/design_deck_demo.rb +314 -0
- data/examples/visual_skills/explainer_demo.rb +499 -0
- data/exe/streamweaver +9 -0
- data/gsd/ROADMAP-1.0.md +167 -0
- data/gsd/STATE.md +13 -0
- data/gsd/research/market-positioning-research.md +139 -0
- data/gsd/research/production-patterns-research.md +270 -0
- data/gsd/research/repo-audit-1.0.md +274 -0
- data/lib/stream_weaver/action_token.rb +58 -0
- data/lib/stream_weaver/adapter/alpinejs.rb +8249 -0
- data/lib/stream_weaver/adapter/base.rb +591 -0
- data/lib/stream_weaver/adapter/opal.rb +334 -0
- data/lib/stream_weaver/adapter/static.rb +1118 -0
- data/lib/stream_weaver/admin.rb +176 -0
- data/lib/stream_weaver/app.rb +1821 -0
- data/lib/stream_weaver/assets/js/sw-copy.js +50 -0
- data/lib/stream_weaver/assets/js/sw-heredoc-rewrite.js +68 -0
- data/lib/stream_weaver/assets/js/sw-keyboard.js +165 -0
- data/lib/stream_weaver/assets/js/sw-mermaid-zoom.js +621 -0
- data/lib/stream_weaver/assets/js/sw-route-tabs.js +53 -0
- data/lib/stream_weaver/assets/js/sw-sidebar-toc.js +116 -0
- data/lib/stream_weaver/assets/js/sw-slide-nav.js +103 -0
- data/lib/stream_weaver/canvas/bridge.rb +239 -0
- data/lib/stream_weaver/canvas/bridge_server.rb +646 -0
- data/lib/stream_weaver/canvas/client.rb +298 -0
- data/lib/stream_weaver/canvas/doc_roots.rb +233 -0
- data/lib/stream_weaver/canvas/doc_store.rb +252 -0
- data/lib/stream_weaver/canvas/gist_publisher.rb +264 -0
- data/lib/stream_weaver/canvas/gist_save_handler.rb +89 -0
- data/lib/stream_weaver/canvas/gist_store.rb +135 -0
- data/lib/stream_weaver/canvas/helpers.rb +109 -0
- data/lib/stream_weaver/canvas/history.rb +90 -0
- data/lib/stream_weaver/canvas/protocol.rb +86 -0
- data/lib/stream_weaver/canvas/reader.rb +853 -0
- data/lib/stream_weaver/canvas/save_doc_widget.rb +457 -0
- data/lib/stream_weaver/canvas/scroll_top_hint.rb +21 -0
- data/lib/stream_weaver/canvas/session.rb +132 -0
- data/lib/stream_weaver/cli.rb +3235 -0
- data/lib/stream_weaver/component_assets.rb +70 -0
- data/lib/stream_weaver/component_registry.rb +67 -0
- data/lib/stream_weaver/component_renderer.rb +48 -0
- data/lib/stream_weaver/components/annotated_code.rb +53 -0
- data/lib/stream_weaver/components/api_endpoint.rb +42 -0
- data/lib/stream_weaver/components/callout.rb +59 -0
- data/lib/stream_weaver/components/chart.rb +84 -0
- data/lib/stream_weaver/components/code_block.rb +77 -0
- data/lib/stream_weaver/components/comparison.rb +39 -0
- data/lib/stream_weaver/components/decision.rb +38 -0
- data/lib/stream_weaver/components/deck/close_overlay.rb +84 -0
- data/lib/stream_weaver/components/deck/confirmation_bar.rb +60 -0
- data/lib/stream_weaver/components/deck/deck_option.rb +61 -0
- data/lib/stream_weaver/components/deck/deck_slide.rb +77 -0
- data/lib/stream_weaver/components/deck/deck_state.rb +469 -0
- data/lib/stream_weaver/components/deck/deck_summary.rb +73 -0
- data/lib/stream_weaver/components/deck/design_deck.rb +60 -0
- data/lib/stream_weaver/components/deck/generate_more_controls.rb +84 -0
- data/lib/stream_weaver/components/deck/model_selector.rb +84 -0
- data/lib/stream_weaver/components/deck/skeleton_placeholder.rb +36 -0
- data/lib/stream_weaver/components/diff_block.rb +125 -0
- data/lib/stream_weaver/components/doc_header.rb +57 -0
- data/lib/stream_weaver/components/image_block.rb +70 -0
- data/lib/stream_weaver/components/implementation_map.rb +33 -0
- data/lib/stream_weaver/components/keyboard_shortcuts.rb +94 -0
- data/lib/stream_weaver/components/kpi_dashboard.rb +78 -0
- data/lib/stream_weaver/components/mermaid.rb +79 -0
- data/lib/stream_weaver/components/pipeline.rb +63 -0
- data/lib/stream_weaver/components/sidebar_toc.rb +46 -0
- data/lib/stream_weaver/components/slide_container.rb +141 -0
- data/lib/stream_weaver/components/timeline_event.rb +58 -0
- data/lib/stream_weaver/components/wireframe.rb +29 -0
- data/lib/stream_weaver/components/wireframe_block.rb +34 -0
- data/lib/stream_weaver/components.rb +2711 -0
- data/lib/stream_weaver/css.rb +230 -0
- data/lib/stream_weaver/dev_fallback_overlay.rb +61 -0
- data/lib/stream_weaver/display_dsl.rb +1004 -0
- data/lib/stream_weaver/export/html_exporter.rb +478 -0
- data/lib/stream_weaver/feed.rb +34 -0
- data/lib/stream_weaver/feed_builder.rb +31 -0
- data/lib/stream_weaver/fonts.rb +33 -0
- data/lib/stream_weaver/interaction_runner.rb +487 -0
- data/lib/stream_weaver/iterm.rb +460 -0
- data/lib/stream_weaver/layout_registry.rb +92 -0
- data/lib/stream_weaver/opal/bridge.rb +52 -0
- data/lib/stream_weaver/opal/builder.rb +193 -0
- data/lib/stream_weaver/opal/env.rb +29 -0
- data/lib/stream_weaver/opal/reactive_state.rb +75 -0
- data/lib/stream_weaver/opal/regexp_anchor_patch.rb +62 -0
- data/lib/stream_weaver/opal/renderer.rb +77 -0
- data/lib/stream_weaver/opal/runtime.rb +250 -0
- data/lib/stream_weaver/opal/shell.rb +145 -0
- data/lib/stream_weaver/opal/string_bridge.rb +50 -0
- data/lib/stream_weaver/opal/stubs/diff.min.js +1 -0
- data/lib/stream_weaver/opal/stubs/digest.rb +15 -0
- data/lib/stream_weaver/opal/stubs/marked.umd.js +79 -0
- data/lib/stream_weaver/opal/stubs/md5.rb +3 -0
- data/lib/stream_weaver/opal/stubs/morphdom.min.js +775 -0
- data/lib/stream_weaver/opal/stubs/prism-tomorrow.min.css +1 -0
- data/lib/stream_weaver/opal/stubs/prism.min.js +1967 -0
- data/lib/stream_weaver/opal_entry.rb +136 -0
- data/lib/stream_weaver/org/inline.rb +95 -0
- data/lib/stream_weaver/org/reader.rb +561 -0
- data/lib/stream_weaver/org/recording_context.rb +86 -0
- data/lib/stream_weaver/org/source_splitter.rb +63 -0
- data/lib/stream_weaver/org/writer.rb +300 -0
- data/lib/stream_weaver/page_shell.rb +530 -0
- data/lib/stream_weaver/portfile.rb +79 -0
- data/lib/stream_weaver/pushable.rb +42 -0
- data/lib/stream_weaver/resource/default_views.rb +111 -0
- data/lib/stream_weaver/resource/field_input.rb +25 -0
- data/lib/stream_weaver/resource/state_keys.rb +16 -0
- data/lib/stream_weaver/resource/store.rb +18 -0
- data/lib/stream_weaver/resource.rb +104 -0
- data/lib/stream_weaver/server.rb +1471 -0
- data/lib/stream_weaver/service.rb +1240 -0
- data/lib/stream_weaver/service_client.rb +104 -0
- data/lib/stream_weaver/session_store.rb +186 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/SKILL.md +66 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/examples/canvas-safe-showcase.rb +98 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/actions-and-buttons.md +47 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/charts-and-diagrams.md +38 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/deck.md +32 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/inputs-and-forms.md +65 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/tabs-and-navigation.md +53 -0
- data/lib/stream_weaver/skills/streamweaver-doc-builder/SKILL.md +298 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/SKILL.md +130 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.css +468 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.rb +39 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example_dsl.rb +300 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example.rb +24 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example_dsl.rb +385 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/references/checkpoints-and-forms.md +28 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/references/cleanup-and-panel.md +44 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/references/example-gallery.md +8 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/references/persistence.md +58 -0
- data/lib/stream_weaver/skills/streamweaver-way/SKILL.md +396 -0
- data/lib/stream_weaver/skills/visual-plan/SKILL.md +201 -0
- data/lib/stream_weaver/skills/visual-recap/SKILL.md +244 -0
- data/lib/stream_weaver/streamer.rb +63 -0
- data/lib/stream_weaver/templates/choices.rb +149 -0
- data/lib/stream_weaver/templates/code.rb +157 -0
- data/lib/stream_weaver/templates/confirm.rb +110 -0
- data/lib/stream_weaver/templates/diff.rb +195 -0
- data/lib/stream_weaver/templates/info.rb +142 -0
- data/lib/stream_weaver/templates/table.rb +169 -0
- data/lib/stream_weaver/templates/wizard.rb +271 -0
- data/lib/stream_weaver/theme/auto_mode.rb +116 -0
- data/lib/stream_weaver/theme/presets.rb +555 -0
- data/lib/stream_weaver/theme.rb +639 -0
- data/lib/stream_weaver/university/canvas.rb +924 -0
- data/lib/stream_weaver/university/course.rb +699 -0
- data/lib/stream_weaver/university/demos/counter.rb +33 -0
- data/lib/stream_weaver/university/demos/dashboard.rb +147 -0
- data/lib/stream_weaver/university/demos/decision_form.rb +143 -0
- data/lib/stream_weaver/university/demos.rb +50 -0
- data/lib/stream_weaver/university/listener.rb +467 -0
- data/lib/stream_weaver/university/progress.rb +201 -0
- data/lib/stream_weaver/university/runner.rb +138 -0
- data/lib/stream_weaver/university/scripts/growing_doc.rb +609 -0
- data/lib/stream_weaver/university/scripts/growing_doc_state.rb +91 -0
- data/lib/stream_weaver/utils.rb +31 -0
- data/lib/stream_weaver/version.rb +5 -0
- data/lib/stream_weaver/views/canvas/reader_layout.erb +800 -0
- data/lib/stream_weaver/views.rb +3940 -0
- data/lib/stream_weaver.rb +122 -0
- data/llms.txt +1269 -0
- data/sig/stream_weaver.rbs +4 -0
- 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.
|