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
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'stream_weaver'
|
|
2
|
+
|
|
3
|
+
app "Counter" do
|
|
4
|
+
callout "This whole app is the file your agent just ran -- nothing hidden. Click +1 -- the whole block re-runs on the server and the count updates. When you're done playing: come back to your Claude session and say \"done\".", variant: :info
|
|
5
|
+
state[:count] ||= 0
|
|
6
|
+
header1 "Six lines of Ruby"
|
|
7
|
+
text "Count: #{state[:count]}"
|
|
8
|
+
button("+1") { |s| s[:count] += 1 }
|
|
9
|
+
end.run!
|
|
10
|
+
|
|
11
|
+
# ^ The mechanism above is still the whole app: a six-line block (four
|
|
12
|
+
# statements wrapped by `app "Counter" do` and `end.run!`), in an eight-line
|
|
13
|
+
# file. Nothing has been elided, and nothing below this line runs -- `.run!`
|
|
14
|
+
# blocks. StreamWeaver University, step 2 (story: step-2-dsl-reexec). The
|
|
15
|
+
# worker runs THIS file, verbatim, rather than writing its own: two real
|
|
16
|
+
# worker sessions each lost a debug cycle to the "six-line app" framing, one
|
|
17
|
+
# by omitting `require 'stream_weaver'` (NoMethodError: undefined method
|
|
18
|
+
# 'app') and the other by omitting `.run!` (builds the app, starts no
|
|
19
|
+
# server, exits silently). Both lines are above, on purpose.
|
|
20
|
+
#
|
|
21
|
+
# ruby "$(streamweaver university-demo counter)"
|
|
22
|
+
#
|
|
23
|
+
# There is no event-handler code here and no route. The block on line 5 is
|
|
24
|
+
# re-executed on every click; `state[:count]` is one higher when it runs, so
|
|
25
|
+
# line 7 renders a different number. That single idea -- the block re-runs
|
|
26
|
+
# -- is every other thing StreamWeaver does, wearing different clothes.
|
|
27
|
+
#
|
|
28
|
+
# Round-6 UAT (2026-09-03): the app used to pop up with no explanation of
|
|
29
|
+
# its own, so anyone who tabbed over before the agent's narration caught up
|
|
30
|
+
# saw a bare counter and nothing else. The `callout` above is the file's
|
|
31
|
+
# ninth line -- deliberately not part of the six-line mechanism, and not one
|
|
32
|
+
# of the two bookend lines named above either. It is the one line meant to
|
|
33
|
+
# be read on screen, not walked through.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# StreamWeaver University -- step 1's dashboard demo (story:
|
|
4
|
+
# step-1-canvas-push).
|
|
5
|
+
#
|
|
6
|
+
# Three things a terminal cannot draw, in one push: a 2x2 grid of KPI tiles,
|
|
7
|
+
# a Chart.js bar chart, and a mermaid sequence diagram of the very
|
|
8
|
+
# canvas-push that put them there. Then the same layout again with different
|
|
9
|
+
# numbers, so the pane mutates in place while the worker narrates.
|
|
10
|
+
#
|
|
11
|
+
# streamweaver panel dashboard # open the pane first
|
|
12
|
+
# ruby "$(streamweaver university-demo dashboard)" dashboard # week 1
|
|
13
|
+
# ruby "$(streamweaver university-demo dashboard)" dashboard 2 # mutate
|
|
14
|
+
# ruby "$(streamweaver university-demo dashboard)" dashboard 3 # mutate
|
|
15
|
+
#
|
|
16
|
+
# ARGV: SESSION_NAME (default "dashboard"), SNAPSHOT (1-3, default 1).
|
|
17
|
+
#
|
|
18
|
+
# Why a push script rather than three DSL files piped through
|
|
19
|
+
# `canvas-push`: the DSL is rendered inside the bridge process, so a file
|
|
20
|
+
# cannot read the caller's environment, and three near-identical DSL files
|
|
21
|
+
# would be three places for the layout to drift. One parameterized script,
|
|
22
|
+
# three snapshots of the same layout.
|
|
23
|
+
#
|
|
24
|
+
# Design constraints baked in:
|
|
25
|
+
#
|
|
26
|
+
# - Pane width. This renders in a ~750-800px split pane
|
|
27
|
+
# (features/university-getting-started.context.md, "Pane-width rule"), so
|
|
28
|
+
# the tiles are two rows of two rather than one row of four -- `.sw-columns`
|
|
29
|
+
# only stacks below 640px, and four tiles across 750px is unreadable.
|
|
30
|
+
# - `chart type:` explicitly, never the `bar_chart` shorthand: the shorthand
|
|
31
|
+
# family renders through Alpine `x-init`, and disc-094 kills it on export.
|
|
32
|
+
# The explicit form is what commit 51beb6c fixed for bridge poll swaps.
|
|
33
|
+
|
|
34
|
+
require 'stream_weaver/canvas/client'
|
|
35
|
+
|
|
36
|
+
module StreamWeaver
|
|
37
|
+
module University
|
|
38
|
+
module Demos
|
|
39
|
+
module Dashboard
|
|
40
|
+
# Each snapshot is one frame of the same dashboard. Same tile
|
|
41
|
+
# labels, same chart series, different numbers -- so a re-push reads
|
|
42
|
+
# as "the data moved", not "a different page".
|
|
43
|
+
SNAPSHOTS = [
|
|
44
|
+
{
|
|
45
|
+
caption: 'Sprint 14 · Monday standup',
|
|
46
|
+
tiles: [
|
|
47
|
+
{ value: 128, label: 'PUSHES', color: :default },
|
|
48
|
+
{ value: 12, label: 'SESSIONS', color: :blue },
|
|
49
|
+
{ value: 3, label: 'BLOCKED', color: :red },
|
|
50
|
+
{ value: 41, label: 'DOCS SAVED', color: :purple }
|
|
51
|
+
],
|
|
52
|
+
bars: [18, 24, 31, 22, 33]
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
caption: 'Sprint 14 · Wednesday standup',
|
|
56
|
+
tiles: [
|
|
57
|
+
{ value: 164, label: 'PUSHES', color: :default },
|
|
58
|
+
{ value: 15, label: 'SESSIONS', color: :blue },
|
|
59
|
+
{ value: 1, label: 'BLOCKED', color: :red },
|
|
60
|
+
{ value: 52, label: 'DOCS SAVED', color: :purple }
|
|
61
|
+
],
|
|
62
|
+
bars: [18, 24, 31, 29, 47]
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
caption: 'Sprint 14 · Friday standup',
|
|
66
|
+
tiles: [
|
|
67
|
+
{ value: 203, label: 'PUSHES', color: :default },
|
|
68
|
+
{ value: 19, label: 'SESSIONS', color: :blue },
|
|
69
|
+
{ value: 0, label: 'BLOCKED', color: :red },
|
|
70
|
+
{ value: 68, label: 'DOCS SAVED', color: :purple }
|
|
71
|
+
],
|
|
72
|
+
bars: [18, 24, 31, 29, 61]
|
|
73
|
+
}
|
|
74
|
+
].freeze
|
|
75
|
+
|
|
76
|
+
DAYS = %w[Mon Tue Wed Thu Fri].freeze
|
|
77
|
+
|
|
78
|
+
# One flat DSL body for the given snapshot. Flat and
|
|
79
|
+
# one-statement-per-component on purpose (same rule the growing-doc
|
|
80
|
+
# script follows) so the text stays legible when a curious user
|
|
81
|
+
# reads the file this course just ran.
|
|
82
|
+
def self.dsl(index = 0)
|
|
83
|
+
snap = SNAPSHOTS[index]
|
|
84
|
+
top, bottom = snap[:tiles].each_slice(2).to_a
|
|
85
|
+
|
|
86
|
+
<<~RUBY
|
|
87
|
+
header1 "Canvas activity"
|
|
88
|
+
|
|
89
|
+
text #{snap[:caption].inspect}
|
|
90
|
+
|
|
91
|
+
columns 2 do
|
|
92
|
+
column { stat_display value: #{top[0][:value]}, label: #{top[0][:label].inspect}, color: :#{top[0][:color]}, size: :lg }
|
|
93
|
+
column { stat_display value: #{top[1][:value]}, label: #{top[1][:label].inspect}, color: :#{top[1][:color]}, size: :lg }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
columns 2 do
|
|
97
|
+
column { stat_display value: #{bottom[0][:value]}, label: #{bottom[0][:label].inspect}, color: :#{bottom[0][:color]}, size: :lg }
|
|
98
|
+
column { stat_display value: #{bottom[1][:value]}, label: #{bottom[1][:label].inspect}, color: :#{bottom[1][:color]}, size: :lg }
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
chart type: :bar, data: {
|
|
102
|
+
labels: #{DAYS.inspect},
|
|
103
|
+
datasets: [
|
|
104
|
+
{ label: "Pushes", data: #{snap[:bars].inspect}, backgroundColor: "#6366f1" }
|
|
105
|
+
]
|
|
106
|
+
}, height: 240
|
|
107
|
+
|
|
108
|
+
md "**How this got here.** Not a screenshot, not a file your agent opened in a browser -- one command, and the pane you are looking at redrew itself:"
|
|
109
|
+
|
|
110
|
+
mermaid <<~DIAGRAM
|
|
111
|
+
sequenceDiagram
|
|
112
|
+
participant A as Your agent
|
|
113
|
+
participant C as streamweaver CLI
|
|
114
|
+
participant B as canvas bridge
|
|
115
|
+
participant P as this pane
|
|
116
|
+
A->>C: ruby dashboard.rb dashboard #{index + 1}
|
|
117
|
+
C->>B: push(session, DSL)
|
|
118
|
+
B->>B: render DSL -> HTML
|
|
119
|
+
B-->>P: poll swap, in place
|
|
120
|
+
P-->>A: no reload, no flicker
|
|
121
|
+
DIAGRAM
|
|
122
|
+
RUBY
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def self.run!(session_name: ARGV[0] || 'dashboard',
|
|
126
|
+
snapshot: Integer(ARGV[1] || 1))
|
|
127
|
+
index = snapshot.clamp(1, SNAPSHOTS.length) - 1
|
|
128
|
+
|
|
129
|
+
::StreamWeaver::Canvas::Client.ensure_bridge_running
|
|
130
|
+
::StreamWeaver::Canvas::Client.send_message(
|
|
131
|
+
::StreamWeaver::Canvas::Protocol::Messages.create(session_name, layout: :fluid)
|
|
132
|
+
)
|
|
133
|
+
::StreamWeaver::Canvas::Client.send_message(
|
|
134
|
+
::StreamWeaver::Canvas::Protocol::Messages.push(session_name, dsl(index), source_dir: nil)
|
|
135
|
+
)
|
|
136
|
+
puts "Pushed snapshot #{index + 1} of #{SNAPSHOTS.length} (#{SNAPSHOTS[index][:caption]}) to canvas session '#{session_name}'."
|
|
137
|
+
rescue ::StreamWeaver::Canvas::Client::NotRunningError,
|
|
138
|
+
::StreamWeaver::Canvas::Client::ConnectionError => e
|
|
139
|
+
warn "dashboard: could not reach the canvas bridge (#{e.message}) -- " \
|
|
140
|
+
"run `streamweaver panel #{session_name}` first"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
StreamWeaver::University::Demos::Dashboard.run! if $PROGRAM_NAME == __FILE__
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# StreamWeaver University -- step 3's decision form (story:
|
|
4
|
+
# step-3-form-modes).
|
|
5
|
+
#
|
|
6
|
+
# ONE artifact, two surfaces. The same question -- a mermaid diagram of two
|
|
7
|
+
# candidate architectures, a comparison table, a radio_group and a rationale
|
|
8
|
+
# text_field -- is rendered either as a standalone blocking app or as a
|
|
9
|
+
# canvas push. Nothing is duplicated between the two modes; that identity is
|
|
10
|
+
# the lesson, and the worker is told to say so out loud.
|
|
11
|
+
#
|
|
12
|
+
# Mode 1, the founding loop (step 3's opening move):
|
|
13
|
+
#
|
|
14
|
+
# ruby "$(streamweaver university-demo decision-form)"
|
|
15
|
+
#
|
|
16
|
+
# `run_once!` finds a free port, OPENS the browser itself, and BLOCKS. The
|
|
17
|
+
# user answers in the page that just appeared; the process prints the
|
|
18
|
+
# submitted state as JSON and exits. The user never opens a page and never
|
|
19
|
+
# has to tell the agent "done" -- round-5 UAT found both of those to be
|
|
20
|
+
# homework the course was silently assigning.
|
|
21
|
+
#
|
|
22
|
+
# Mode 2, the same form on the canvas:
|
|
23
|
+
#
|
|
24
|
+
# streamweaver panel decision
|
|
25
|
+
# ruby "$(streamweaver university-demo decision-form)" canvas decision
|
|
26
|
+
# streamweaver canvas-wait decision
|
|
27
|
+
#
|
|
28
|
+
# Input components are radio_group / text_field only. disc-098 and disc-105:
|
|
29
|
+
# checkbox_group and multi chip_group harvest the wrong values back through
|
|
30
|
+
# canvas-wait.
|
|
31
|
+
|
|
32
|
+
require 'stream_weaver'
|
|
33
|
+
require 'stream_weaver/canvas/client'
|
|
34
|
+
|
|
35
|
+
module StreamWeaver
|
|
36
|
+
module University
|
|
37
|
+
module Demos
|
|
38
|
+
module DecisionForm
|
|
39
|
+
# The question and its evidence. Everything a terminal prompt cannot
|
|
40
|
+
# carry: a rendered diagram of both designs, and a table putting them
|
|
41
|
+
# side by side on three axes.
|
|
42
|
+
CONTEXT = <<~RUBY
|
|
43
|
+
header1 "Which way should the canvas bridge hold sessions?"
|
|
44
|
+
|
|
45
|
+
md "This is a real decision, not a demo prompt -- and it is the kind an agent normally has to ask you in prose, one option per paragraph, hoping you can hold both designs in your head at once."
|
|
46
|
+
|
|
47
|
+
mermaid <<~DIAGRAM
|
|
48
|
+
graph TB
|
|
49
|
+
subgraph A["A · One shared bridge"]
|
|
50
|
+
A1["streamweaver CLI"] --> A2["single bridge process"]
|
|
51
|
+
A2 --> A3["session: dashboard"]
|
|
52
|
+
A2 --> A4["session: decision"]
|
|
53
|
+
A2 --> A5["session: doc-demo"]
|
|
54
|
+
end
|
|
55
|
+
subgraph B["B · A process per session"]
|
|
56
|
+
B1["streamweaver CLI"] --> B2["bridge: dashboard"]
|
|
57
|
+
B1 --> B3["bridge: decision"]
|
|
58
|
+
B1 --> B4["bridge: doc-demo"]
|
|
59
|
+
end
|
|
60
|
+
DIAGRAM
|
|
61
|
+
|
|
62
|
+
table [
|
|
63
|
+
{ axis: "Memory", shared: "One process, one Puma", per_session: "N processes, N Pumas" },
|
|
64
|
+
{ axis: "Blast radius", shared: "A crash takes every pane", per_session: "A crash takes one pane" },
|
|
65
|
+
{ axis: "Cross-session reads", shared: "Free -- same object graph", per_session: "Needs IPC or a store" }
|
|
66
|
+
]
|
|
67
|
+
RUBY
|
|
68
|
+
|
|
69
|
+
# The inputs. Shared verbatim by both surfaces -- change a label here
|
|
70
|
+
# and both the standalone app and the canvas form change with it.
|
|
71
|
+
FORM = <<~RUBY
|
|
72
|
+
radio_group :architecture, [
|
|
73
|
+
"A -- one shared bridge, all sessions inside it",
|
|
74
|
+
"B -- one bridge process per session"
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
md "**Why?** One line. Your agent reads this back to you verbatim and reacts to it."
|
|
78
|
+
|
|
79
|
+
text_field :rationale, placeholder: "Because..."
|
|
80
|
+
RUBY
|
|
81
|
+
|
|
82
|
+
# The canvas surface says out loud what is happening to the worker's
|
|
83
|
+
# terminal, because on the canvas there is nothing else to tell you.
|
|
84
|
+
# The standalone surface does not need it: `run_once!` prints
|
|
85
|
+
# "Waiting for form submission" in the very terminal that is stuck.
|
|
86
|
+
BLOCKING_CALLOUT = <<~RUBY
|
|
87
|
+
callout(variant: :warning, title: "Your agent is frozen right now") do
|
|
88
|
+
md "This is a **blocking** form. The worker terminal is sitting inside `streamweaver canvas-wait decision` and will not print another line until you press Submit. Look over at it -- nothing is moving."
|
|
89
|
+
end
|
|
90
|
+
RUBY
|
|
91
|
+
|
|
92
|
+
SUBMIT = <<~RUBY
|
|
93
|
+
button "Submit decision"
|
|
94
|
+
RUBY
|
|
95
|
+
|
|
96
|
+
# Standalone gets no explicit button: agentic mode (`run_once!`)
|
|
97
|
+
# renders its own submit control and uses it to unblock.
|
|
98
|
+
def self.standalone_dsl
|
|
99
|
+
"#{CONTEXT}\n#{FORM}"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def self.canvas_dsl
|
|
103
|
+
"#{BLOCKING_CALLOUT}\n#{CONTEXT}\n#{FORM}\n#{SUBMIT}"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Mode 1. Blocks; opens the browser; prints the submitted state as
|
|
107
|
+
# JSON on stdout when the user submits.
|
|
108
|
+
def self.standalone!
|
|
109
|
+
dsl = standalone_dsl
|
|
110
|
+
::StreamWeaver.app("A real decision") { instance_eval(dsl) }.run_once!
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Mode 2. Pushes the same question into a canvas session, for
|
|
114
|
+
# `streamweaver canvas-wait <session>` to block on.
|
|
115
|
+
def self.push!(session_name)
|
|
116
|
+
::StreamWeaver::Canvas::Client.ensure_bridge_running
|
|
117
|
+
::StreamWeaver::Canvas::Client.send_message(
|
|
118
|
+
::StreamWeaver::Canvas::Protocol::Messages.create(session_name, layout: :fluid)
|
|
119
|
+
)
|
|
120
|
+
::StreamWeaver::Canvas::Client.send_message(
|
|
121
|
+
::StreamWeaver::Canvas::Protocol::Messages.push(session_name, canvas_dsl, source_dir: nil)
|
|
122
|
+
)
|
|
123
|
+
puts "Pushed the same decision form to canvas session '#{session_name}'. " \
|
|
124
|
+
"Block on it with: streamweaver canvas-wait #{session_name}"
|
|
125
|
+
rescue ::StreamWeaver::Canvas::Client::NotRunningError,
|
|
126
|
+
::StreamWeaver::Canvas::Client::ConnectionError => e
|
|
127
|
+
warn "decision_form: could not reach the canvas bridge (#{e.message}) -- " \
|
|
128
|
+
"run `streamweaver panel #{session_name}` first"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def self.run!(argv = ARGV)
|
|
132
|
+
if argv.first == 'canvas'
|
|
133
|
+
push!(argv[1] || 'decision')
|
|
134
|
+
else
|
|
135
|
+
standalone!
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
StreamWeaver::University::Demos::DecisionForm.run! if $PROGRAM_NAME == __FILE__
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StreamWeaver
|
|
4
|
+
module University
|
|
5
|
+
# The canned artifacts every Getting Started step demonstrates.
|
|
6
|
+
#
|
|
7
|
+
# Round-5 UAT (2026-09-03) took ~5 minutes to reach first paint because
|
|
8
|
+
# each worker session *composed* its demo live, and one of them went
|
|
9
|
+
# looking for the source repo to do it. Both are now designed out: every
|
|
10
|
+
# demo ships inside the gem as a finished, runnable file, so the worker's
|
|
11
|
+
# job is to RUN it and NARRATE it -- never to invent it, and never to
|
|
12
|
+
# need a checkout.
|
|
13
|
+
#
|
|
14
|
+
# `streamweaver university-demo <name>` prints the absolute path of one
|
|
15
|
+
# of these inside the installed gem, which is what the course prompts
|
|
16
|
+
# interpolate:
|
|
17
|
+
#
|
|
18
|
+
# ruby "$(streamweaver university-demo dashboard)" dashboard
|
|
19
|
+
#
|
|
20
|
+
# Names are normalized (`decision-form` and `decision_form` are the same
|
|
21
|
+
# demo) so a prompt, a spec and a human can each spell it the way that
|
|
22
|
+
# reads best where they are.
|
|
23
|
+
module Demos
|
|
24
|
+
ROOT = File.expand_path('demos', __dir__)
|
|
25
|
+
|
|
26
|
+
# name => path, relative to this file's directory. `doc` deliberately
|
|
27
|
+
# points at the growing-doc script that already shipped for step 4
|
|
28
|
+
# rather than a second copy of it -- one artifact, one place it can
|
|
29
|
+
# rot.
|
|
30
|
+
PATHS = {
|
|
31
|
+
'dashboard' => File.join(ROOT, 'dashboard.rb'),
|
|
32
|
+
'counter' => File.join(ROOT, 'counter.rb'),
|
|
33
|
+
'decision-form' => File.join(ROOT, 'decision_form.rb'),
|
|
34
|
+
'doc' => File.expand_path('scripts/growing_doc.rb', __dir__)
|
|
35
|
+
}.freeze
|
|
36
|
+
|
|
37
|
+
NAMES = PATHS.keys.freeze
|
|
38
|
+
|
|
39
|
+
# `decision_form`, `decision-form` and `DECISION FORM` all resolve.
|
|
40
|
+
def self.normalize(name)
|
|
41
|
+
name.to_s.strip.downcase.tr('_ ', '--')
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @return [String, nil] absolute path, or nil for an unknown name
|
|
45
|
+
def self.path(name)
|
|
46
|
+
PATHS[normalize(name)]
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|