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,401 @@
|
|
|
1
|
+
# StreamWeaver for AI Agents
|
|
2
|
+
|
|
3
|
+
A guide to building visual UI for AI agent workflows with minimal token overhead.
|
|
4
|
+
|
|
5
|
+
## The Problem
|
|
6
|
+
|
|
7
|
+
AI agents need visual UI, but the options are frustrating:
|
|
8
|
+
|
|
9
|
+
**Terminal output** is hard to parse. Complex choices become walls of text. Multi-step workflows turn into scrolling nightmares. Users lose context.
|
|
10
|
+
|
|
11
|
+
**HTML/React** is verbose. A simple form is 50+ lines. AI generation is slow and expensive. Token costs matter—slower, more expensive, less context available for the actual problem.
|
|
12
|
+
|
|
13
|
+
**What agents actually need:**
|
|
14
|
+
- Quick visual feedback for complex choices
|
|
15
|
+
- Forms that don't require parsing natural language
|
|
16
|
+
- Progress indicators and status displays
|
|
17
|
+
- Charts and data visualization
|
|
18
|
+
- All without burning context on boilerplate
|
|
19
|
+
|
|
20
|
+
## The Solution: Concise DSL
|
|
21
|
+
|
|
22
|
+
StreamWeaver is a Ruby DSL that renders to HTML. Many components, clear syntax, fast to build real apps.
|
|
23
|
+
|
|
24
|
+
### Token Comparison
|
|
25
|
+
|
|
26
|
+
A form with 3 fields and a button:
|
|
27
|
+
|
|
28
|
+
**StreamWeaver DSL** (~45 tokens):
|
|
29
|
+
```ruby
|
|
30
|
+
card do
|
|
31
|
+
header1 "Sign Up"
|
|
32
|
+
text_field :email, placeholder: "you@example.com", label: "Email"
|
|
33
|
+
text_field :name, placeholder: "Your Name", label: "Name"
|
|
34
|
+
text_field :company, label: "Company"
|
|
35
|
+
button "Submit", id: "btn_submit", style: :primary
|
|
36
|
+
end
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**HTML** (~200+ tokens):
|
|
40
|
+
```html
|
|
41
|
+
<div class="card">
|
|
42
|
+
<h1>Sign Up</h1>
|
|
43
|
+
<div class="form-group">
|
|
44
|
+
<label for="email">Email</label>
|
|
45
|
+
<input type="text" id="email" name="email" placeholder="you@example.com" class="form-control">
|
|
46
|
+
</div>
|
|
47
|
+
<div class="form-group">
|
|
48
|
+
<label for="name">Name</label>
|
|
49
|
+
<input type="text" id="name" name="name" placeholder="Your Name" class="form-control">
|
|
50
|
+
</div>
|
|
51
|
+
<div class="form-group">
|
|
52
|
+
<label for="company">Company</label>
|
|
53
|
+
<input type="text" id="company" name="company" class="form-control">
|
|
54
|
+
</div>
|
|
55
|
+
<button type="submit" class="btn btn-primary">Submit</button>
|
|
56
|
+
</div>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**React** (~300+ tokens):
|
|
60
|
+
```jsx
|
|
61
|
+
function SignUpForm() {
|
|
62
|
+
const [email, setEmail] = useState('');
|
|
63
|
+
const [name, setName] = useState('');
|
|
64
|
+
const [company, setCompany] = useState('');
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<div className="card">
|
|
68
|
+
<h1>Sign Up</h1>
|
|
69
|
+
<div className="form-group">
|
|
70
|
+
<label htmlFor="email">Email</label>
|
|
71
|
+
<input
|
|
72
|
+
type="text"
|
|
73
|
+
id="email"
|
|
74
|
+
value={email}
|
|
75
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
76
|
+
placeholder="you@example.com"
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
{/* ... more fields ... */}
|
|
80
|
+
<button type="submit" className="btn btn-primary">Submit</button>
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
That's **5-7x fewer tokens** for the same UI. For AI generation, this means:
|
|
87
|
+
- Faster responses
|
|
88
|
+
- Lower cost
|
|
89
|
+
- More context available for actual work
|
|
90
|
+
|
|
91
|
+
### Component Variety
|
|
92
|
+
|
|
93
|
+
StreamWeaver includes components for common agent needs:
|
|
94
|
+
|
|
95
|
+
| Category | Components |
|
|
96
|
+
|----------|------------|
|
|
97
|
+
| Layout | `card`, `columns`, `hstack`, `vstack`, `collapsible` |
|
|
98
|
+
| Text | `header1`-`header4`, `text`, `md` (markdown) |
|
|
99
|
+
| Forms | `text_field`, `radio_group`, `checkbox`, `select`, `button` |
|
|
100
|
+
| Data | `table`, `bar_chart`, `pie_chart`, `stat_display` |
|
|
101
|
+
| Status | `status_dot`, `progress_bar`, `spinner`, `badge`, `alert` |
|
|
102
|
+
| Media | `image`, `video`, `audio` |
|
|
103
|
+
|
|
104
|
+
### CRITICAL: Never Put Markdown Inside `text`
|
|
105
|
+
|
|
106
|
+
`text` is a literal renderer — markdown syntax appears as raw characters on screen.
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
# WRONG — renders as: **Select the article PDF:** (asterisks visible to user)
|
|
110
|
+
text "**Select the article PDF:**"
|
|
111
|
+
|
|
112
|
+
# CORRECT — use md for markdown formatting
|
|
113
|
+
md "**Select the article PDF:**"
|
|
114
|
+
|
|
115
|
+
# BETTER — use semantic headers for labels and section titles
|
|
116
|
+
header3 "Select the article PDF:"
|
|
117
|
+
header4 "What do you want to know?"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Use `text` only for plain prose. Use `md` when you need inline markdown (bold, italic, links, code). Use `header1`–`header6` for structural headings and form-section labels.
|
|
121
|
+
|
|
122
|
+
## The Modes Gamut
|
|
123
|
+
|
|
124
|
+
StreamWeaver supports multiple modes, from full standalone apps to persistent canvas sessions with Claude Code.
|
|
125
|
+
|
|
126
|
+
| Mode | Use Case | Process Model | Example |
|
|
127
|
+
|------|----------|---------------|---------|
|
|
128
|
+
| **Standalone** | Full applications | `streamweaver app.rb` | Rivet, tutorial.rb, theme_tweaker |
|
|
129
|
+
| **Agentic** (`run_once!`) | Mid-task UI from scripts | Generate → wait → parse JSON | Quick prompts, confirmations |
|
|
130
|
+
| **Service** | Multi-app hosting | Long-running server, reuse apps | Dashboard with multiple widgets |
|
|
131
|
+
| **Canvas/Panel** | Claude Code companion | Persistent session, push/wait | codebreaker, verification_flow |
|
|
132
|
+
|
|
133
|
+
### Standalone Mode
|
|
134
|
+
|
|
135
|
+
Full applications that run independently.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
streamweaver my_app.rb
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Good for:
|
|
142
|
+
- Reusable tools and utilities
|
|
143
|
+
- Self-documenting tutorials
|
|
144
|
+
- Applications that run outside agent workflows
|
|
145
|
+
|
|
146
|
+
Example: `examples/advanced/tutorial.rb` teaches StreamWeaver using StreamWeaver itself.
|
|
147
|
+
|
|
148
|
+
### Agentic Mode (`run_once!`)
|
|
149
|
+
|
|
150
|
+
Quick UI mid-script. Generate DSL, render, wait for input, parse JSON response.
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
require 'stream_weaver'
|
|
154
|
+
|
|
155
|
+
result = StreamWeaver.run_once! do
|
|
156
|
+
card do
|
|
157
|
+
header1 "Quick Question"
|
|
158
|
+
radio_group :choice, ["Option A", "Option B", "Option C"]
|
|
159
|
+
button "Continue", id: "btn_continue", style: :primary
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
puts "User chose: #{result['choice']}"
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Good for:
|
|
167
|
+
- Confirmation dialogs
|
|
168
|
+
- Quick selections
|
|
169
|
+
- Mid-workflow decisions
|
|
170
|
+
|
|
171
|
+
### Service Mode
|
|
172
|
+
|
|
173
|
+
Long-running server that hosts multiple apps. Avoids startup overhead when switching between apps.
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
streamweaver service start
|
|
177
|
+
streamweaver service add my_app.rb
|
|
178
|
+
streamweaver service status
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Good for:
|
|
182
|
+
- Dashboard with multiple widgets
|
|
183
|
+
- Frequently-used utilities
|
|
184
|
+
- Development workflows
|
|
185
|
+
|
|
186
|
+
### Canvas/Panel Mode
|
|
187
|
+
|
|
188
|
+
Persistent session with Claude Code. The agent opens a panel, pushes updates, waits for input, and continues the conversation.
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
# Open a panel (browser in iTerm split pane)
|
|
192
|
+
streamweaver panel my_session
|
|
193
|
+
|
|
194
|
+
# Push DSL content (via stdin)
|
|
195
|
+
streamweaver canvas-push my_session <<'DSL'
|
|
196
|
+
card do
|
|
197
|
+
header1 "Working..."
|
|
198
|
+
spinner label: "Processing files..."
|
|
199
|
+
end
|
|
200
|
+
DSL
|
|
201
|
+
|
|
202
|
+
# Wait for user interaction (returns JSON)
|
|
203
|
+
streamweaver canvas-wait my_session
|
|
204
|
+
|
|
205
|
+
# Show toast notification
|
|
206
|
+
streamweaver canvas-toast my_session "Check terminal for input"
|
|
207
|
+
|
|
208
|
+
# Close when done
|
|
209
|
+
streamweaver canvas-close my_session
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Good for:
|
|
213
|
+
- Multi-phase Claude Code workflows
|
|
214
|
+
- Real-time status updates
|
|
215
|
+
- Interactive analysis tools
|
|
216
|
+
|
|
217
|
+
## Remote & Mobile Access
|
|
218
|
+
|
|
219
|
+
StreamWeaver apps aren't limited to localhost. With Tailscale or LAN access, the same app you build for local use becomes a mobile dashboard:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
# Access from your phone via Tailscale
|
|
223
|
+
STREAMWEAVER_HOST=0.0.0.0 STREAMWEAVER_PORT=4580 ruby dashboard.rb
|
|
224
|
+
# → http://your-machine:4580 from any Tailscale device
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Or in code:
|
|
228
|
+
```ruby
|
|
229
|
+
app "Ops Dashboard", theme: :dark do
|
|
230
|
+
# ... status displays, charts, priority items
|
|
231
|
+
end.run!(host: '0.0.0.0', port: 4580)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
This opens up use cases like:
|
|
235
|
+
- **Mobile monitoring** — check build status, deployment health from your phone
|
|
236
|
+
- **Team dashboards** — share a StreamWeaver app across Tailscale for team visibility
|
|
237
|
+
- **Remote agentic UI** — trigger agent workflows from a tablet while away from your desk
|
|
238
|
+
|
|
239
|
+
The fixed port ensures bookmarks and home screen shortcuts stay stable across restarts.
|
|
240
|
+
|
|
241
|
+
## When to Use What
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
┌─────────────────────┐
|
|
245
|
+
│ What are you │
|
|
246
|
+
│ building? │
|
|
247
|
+
└──────────┬──────────┘
|
|
248
|
+
│
|
|
249
|
+
┌──────────────────────────┼──────────────────────────┐
|
|
250
|
+
│ │ │
|
|
251
|
+
▼ ▼ ▼
|
|
252
|
+
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
|
|
253
|
+
│ Reusable app │ │ Quick UI in │ │ Claude Code │
|
|
254
|
+
│ or tool? │ │ a script? │ │ integration? │
|
|
255
|
+
└───────┬────────┘ └───────┬────────┘ └───────┬────────┘
|
|
256
|
+
│ │ │
|
|
257
|
+
▼ ▼ │
|
|
258
|
+
┌────────────────┐ ┌────────────────┐ │
|
|
259
|
+
│ STANDALONE │ │ AGENTIC │ │
|
|
260
|
+
│ streamweaver │ │ run_once! │ │
|
|
261
|
+
│ app.rb │ │ │ │
|
|
262
|
+
└────────────────┘ └────────────────┘ │
|
|
263
|
+
│
|
|
264
|
+
┌───────────────────────┴───────────────────────┐
|
|
265
|
+
│ │
|
|
266
|
+
▼ ▼
|
|
267
|
+
┌────────────────┐ ┌────────────────┐
|
|
268
|
+
│ Single │ │ Multi-app │
|
|
269
|
+
│ workflow? │ │ dashboard? │
|
|
270
|
+
└───────┬────────┘ └───────┬────────┘
|
|
271
|
+
│ │
|
|
272
|
+
▼ ▼
|
|
273
|
+
┌────────────────┐ ┌────────────────┐
|
|
274
|
+
│ CANVAS/PANEL │ │ SERVICE │
|
|
275
|
+
│ slash command │ │ multi-app │
|
|
276
|
+
│ + heredocs │ │ hosting │
|
|
277
|
+
└────────────────┘ └────────────────┘
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Canvas/Panel Patterns
|
|
281
|
+
|
|
282
|
+
The canvas/panel mode is the most common for Claude Code integration. Key patterns:
|
|
283
|
+
|
|
284
|
+
### Pattern A: Progress While Working
|
|
285
|
+
|
|
286
|
+
Show a spinner while Claude does actual work:
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
# Push spinner (with canvas_continue)
|
|
290
|
+
streamweaver canvas-push session <<'DSL'
|
|
291
|
+
canvas_continue message: "Analyzing..."
|
|
292
|
+
card do
|
|
293
|
+
header1 "Working"
|
|
294
|
+
spinner label: "Processing files..."
|
|
295
|
+
end
|
|
296
|
+
DSL
|
|
297
|
+
|
|
298
|
+
# DO THE ACTUAL WORK (Glob, Grep, Read, etc.)
|
|
299
|
+
# ...
|
|
300
|
+
|
|
301
|
+
# Push results (NO canvas_continue)
|
|
302
|
+
streamweaver canvas-push session <<'DSL'
|
|
303
|
+
card do
|
|
304
|
+
header1 "Results"
|
|
305
|
+
table headers: ["File", "Lines"], rows: [["main.rb", "150"]]
|
|
306
|
+
button "Continue", id: "btn_continue", style: :primary
|
|
307
|
+
end
|
|
308
|
+
DSL
|
|
309
|
+
|
|
310
|
+
# Wait for user
|
|
311
|
+
streamweaver canvas-wait session
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Pattern B: Form Interaction
|
|
315
|
+
|
|
316
|
+
Show a form and wait for user input:
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
# Push form (NO canvas_continue)
|
|
320
|
+
streamweaver canvas-push session <<'DSL'
|
|
321
|
+
card do
|
|
322
|
+
header1 "Choose Options"
|
|
323
|
+
radio_group :choice, ["Option A", "Option B"]
|
|
324
|
+
button "Submit", id: "btn_submit", style: :primary
|
|
325
|
+
end
|
|
326
|
+
DSL
|
|
327
|
+
|
|
328
|
+
# Wait for user
|
|
329
|
+
streamweaver canvas-wait session
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**Critical:** Never combine `canvas_continue` with `canvas-wait`. That's contradictory:
|
|
333
|
+
- `canvas_continue` says "I'm working, here's a spinner"
|
|
334
|
+
- `canvas-wait` says "I'm done, waiting for you"
|
|
335
|
+
|
|
336
|
+
### Toast for Terminal Prompts
|
|
337
|
+
|
|
338
|
+
When Claude needs permission for tools, toast alerts the user:
|
|
339
|
+
|
|
340
|
+
```bash
|
|
341
|
+
streamweaver canvas-toast session "Check terminal for authorization" --variant warning
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## Real Examples
|
|
345
|
+
|
|
346
|
+
### codebreaker
|
|
347
|
+
|
|
348
|
+
Spy-themed code analysis tool. Demonstrates:
|
|
349
|
+
- Multi-phase workflow (briefing → reconnaissance → report → deep analysis)
|
|
350
|
+
- Component variety (charts, tables, status dots, progress bars)
|
|
351
|
+
- Dynamic DSL generation based on analysis results
|
|
352
|
+
- `canvas_continue` for progress, `canvas-wait` for interaction
|
|
353
|
+
|
|
354
|
+
### verification_flow
|
|
355
|
+
|
|
356
|
+
Realistic 2FA-style account verification. Demonstrates:
|
|
357
|
+
- Form collection across multiple steps
|
|
358
|
+
- Status page updates during provisioning
|
|
359
|
+
- Toast usage for terminal prompts
|
|
360
|
+
- Clean phase transitions
|
|
361
|
+
|
|
362
|
+
### tutorial (Claude Code)
|
|
363
|
+
|
|
364
|
+
Conversational canvas tutorial. Demonstrates:
|
|
365
|
+
- Freeform exploration (no fixed order)
|
|
366
|
+
- Dynamic DSL generation based on questions
|
|
367
|
+
- Code + rendered output in same view
|
|
368
|
+
- Iterative refinement ("make it blue")
|
|
369
|
+
|
|
370
|
+
## Creating Your Own
|
|
371
|
+
|
|
372
|
+
1. Create directory with `.claude/commands/yourcommand.md`
|
|
373
|
+
2. Write instructions with canvas-push heredocs and canvas-wait
|
|
374
|
+
3. Add `.claude/settings.local.json` for permissions:
|
|
375
|
+
|
|
376
|
+
```json
|
|
377
|
+
{
|
|
378
|
+
"permissions": {
|
|
379
|
+
"allow": [
|
|
380
|
+
"Bash(streamweaver panel:*)",
|
|
381
|
+
"Bash(streamweaver canvas-push:*)",
|
|
382
|
+
"Bash(streamweaver canvas-wait:*)",
|
|
383
|
+
"Bash(streamweaver canvas-toast:*)",
|
|
384
|
+
"Bash(streamweaver canvas-close:*)"
|
|
385
|
+
]
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
4. Run with `/yourcommand` in Claude Code
|
|
391
|
+
|
|
392
|
+
## Summary
|
|
393
|
+
|
|
394
|
+
StreamWeaver makes AI agent UI practical:
|
|
395
|
+
|
|
396
|
+
- **Concise DSL**: 5-7x fewer tokens than HTML/React
|
|
397
|
+
- **Rich components**: Forms, charts, tables, status indicators
|
|
398
|
+
- **Multiple modes**: Standalone apps to Claude Code canvases
|
|
399
|
+
- **Simple patterns**: Push DSL, wait for JSON, repeat
|
|
400
|
+
|
|
401
|
+
Build what you need to show, not the infrastructure to show it.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# StreamWeaver Frontend Vision
|
|
2
|
+
|
|
3
|
+
## The Thesis
|
|
4
|
+
|
|
5
|
+
A **Hotwire-equivalent frontend stack for Ruby/Rails developers**, built on lighter infrastructure than Hotwire and with no build step:
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Ruby DSL + htmx + Alpine.js + Idiomorph
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Same capabilities as Turbo + Stimulus, but cheaper to load, easier to read for Rails developers, and aligned with the way StreamWeaver already ships UI: a Ruby DSL renders complete server-side pages, the browser swaps regions intelligently, and nobody runs `npm install`.
|
|
12
|
+
|
|
13
|
+
The goal is to make StreamWeaver the obvious choice when a Ruby developer wants to build a browser UI without leaving Ruby — and to make those apps **look and feel like well-built Rails apps** so anyone with Rails muscle memory can read them.
|
|
14
|
+
|
|
15
|
+
## Why Not Hotwire Directly
|
|
16
|
+
|
|
17
|
+
Hotwire is excellent. The reasons we're not adopting it wholesale:
|
|
18
|
+
|
|
19
|
+
- **Asset pipeline assumptions.** Turbo and Stimulus expect a Rails-flavored build pipeline (importmaps, esbuild, propshaft). StreamWeaver apps are a single Ruby file plus a CDN script tag — bringing in Hotwire's tooling violates that promise.
|
|
20
|
+
- **Bundle size.** Turbo (~30kb) + Stimulus (~10kb) is fine in absolute terms but heavier than htmx (~10kb) + Alpine (~15kb) + Idiomorph (~5kb).
|
|
21
|
+
- **Tied to a worldview.** Stimulus controllers are JavaScript classes registered to DOM elements. Alpine declarations live inline in the markup, closer to the data they affect. For Ruby developers used to ERB partials with embedded behavior, Alpine is a smaller mental jump.
|
|
22
|
+
- **Token efficiency.** Alpine inline declarations and htmx attributes are token-cheap when an LLM is generating views — relevant given how much of StreamWeaver gets written by Claude.
|
|
23
|
+
|
|
24
|
+
We are not anti-Hotwire. We are choosing the same shape with lighter materials.
|
|
25
|
+
|
|
26
|
+
## What We Keep From Hotwire
|
|
27
|
+
|
|
28
|
+
- **Server renders complete pages.** No virtual DOM in the browser, no client-side router, no JSON-and-render-on-client pattern. The server is the source of truth for what the page looks like.
|
|
29
|
+
- **Diff-based morph for navigation.** Idiomorph is literally the same algorithm Turbo 8 uses; we adopt it directly. State preservation across navigation comes from the morph algorithm, not from explicit per-region swap configuration.
|
|
30
|
+
- **Bookmarkable resource URLs.** Every meaningful view has its own URL. Users can share links, back-button works, refresh works.
|
|
31
|
+
- **Forms POST to controllers, return updated views.** No client-side form state machines. The server says what the page should look like after a submission.
|
|
32
|
+
|
|
33
|
+
## What We Use Instead
|
|
34
|
+
|
|
35
|
+
| Hotwire | StreamWeaver convention | Why |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| Turbo Drive | htmx `hx-boost` | Lighter, no build step, same UX |
|
|
38
|
+
| Turbo Frames | htmx `hx-target`/`hx-swap` | More explicit, less opinionated, fits htmx idioms |
|
|
39
|
+
| Turbo Streams | htmx out-of-band swaps + Alpine | OOB covers the partial-update case; Alpine covers the live-update case |
|
|
40
|
+
| Stimulus controllers | Alpine `x-data` declarations | Closer to the markup, no separate JS file per component |
|
|
41
|
+
| Idiomorph (extracted from Turbo) | Idiomorph (used directly) | Same library |
|
|
42
|
+
|
|
43
|
+
## Rails-isms We Are Adopting
|
|
44
|
+
|
|
45
|
+
This is the part of the vision still being filled in. Confirmed directions:
|
|
46
|
+
|
|
47
|
+
### Resource URLs
|
|
48
|
+
Every meaningful view gets a Rails-style resource URL. No positional indexes that depend on local server state.
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
/docs/:name # show
|
|
52
|
+
/docs # index
|
|
53
|
+
/history/:session # session's history
|
|
54
|
+
/history/:session/:timestamp # specific snapshot
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Filed as bd `stream_weaver-9ei`.
|
|
58
|
+
|
|
59
|
+
### Routes-as-conventions
|
|
60
|
+
Sidebar navigation, Prev/Next links, all link emission produces these resource URLs. No hand-rolled query-string indexes.
|
|
61
|
+
|
|
62
|
+
### Layouts and Partials (in progress)
|
|
63
|
+
Templates compose from a layout + content + nav region structure. Partials would be a future addition once the layout convention stabilizes.
|
|
64
|
+
|
|
65
|
+
### Forms (open question)
|
|
66
|
+
Rails form helpers (`form_with`, `form_for`, `f.text_field`) translate naturally to a StreamWeaver DSL. The user has signaled interest in stealing the patterns:
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
form_with(url: "/docs", method: :post) do |f|
|
|
70
|
+
f.text_field :name
|
|
71
|
+
f.submit "Save"
|
|
72
|
+
end
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This is **not yet implemented**. It is on the roadmap. The current DSL has `text_field :name, placeholder: "..."` which is closer to Rails' `text_field_tag`. Migrating to a `form_with`-style block is plausible.
|
|
76
|
+
|
|
77
|
+
### Scaffolding (long-term)
|
|
78
|
+
A `streamweaver new <name>` generator that scaffolds a resource (model + canvas templates + URLs) following these conventions. This is well downstream — the conventions need to stabilize first.
|
|
79
|
+
|
|
80
|
+
## Naming
|
|
81
|
+
|
|
82
|
+
The combined frontend stack ("htmx + Alpine + Idiomorph + StreamWeaver DSL with resource URL conventions") deserves a name so it can be referred to as a unit. Working title: **streamwire** (StreamWeaver + Hotwire-shape). Open to alternatives. The naming matters because once a thing is named, conventions can be enforced ("this should follow streamwire patterns") instead of restated each time.
|
|
83
|
+
|
|
84
|
+
## State Conventions
|
|
85
|
+
|
|
86
|
+
Where each kind of state lives:
|
|
87
|
+
|
|
88
|
+
| State type | Lives in | Examples |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| What is being viewed | URL | which doc, which session, which tab |
|
|
91
|
+
| Persistent data | Server (file/DB) | saved docs, user preferences |
|
|
92
|
+
| Ephemeral UI state | Client (Alpine) | dialog open/closed, dropdown expanded |
|
|
93
|
+
| In-progress input | Client (Alpine) | text being typed before submit |
|
|
94
|
+
| Bookmarkable filters | URL query string | search query, sort order |
|
|
95
|
+
|
|
96
|
+
The principle: **state is in the URL when sharing/bookmarking the URL should restore it.** Otherwise it's client-only.
|
|
97
|
+
|
|
98
|
+
## What Solves What
|
|
99
|
+
|
|
100
|
+
The recurring bug pattern Forrest has hit ("my apps reload the whole page and lose state") is solved structurally by Idiomorph. Once the bridge_server and reader templates use morph as the default swap strategy, future features inherit state preservation without thinking about it. No new feature can accidentally regress to "click → full reload → lose accordion state" because that path stops existing in the layouts.
|
|
101
|
+
|
|
102
|
+
## Path Forward
|
|
103
|
+
|
|
104
|
+
1. **Adopt Idiomorph in StreamWeaver layouts** (bd `stream_weaver-2ds`, P2). Replaces hand-rolled OOB swaps and JS active-class fixups with one extension.
|
|
105
|
+
2. **Adopt resource URLs in canvas-read** (bd `stream_weaver-9ei`, P2). Establishes the URL convention.
|
|
106
|
+
3. **Document everything in `llms.txt` (linked as `docs/for_llms.md`)** so LLMs build new apps correctly without rediscovering the patterns. This is the most important deliverable — Forrest reports significant churn from unclear conventions.
|
|
107
|
+
4. **Migrate existing examples** to the conventions once the patterns are documented. Existing apps under `examples/` become reference material.
|
|
108
|
+
5. **(Later)** Forms — translate Rails form helpers to the DSL.
|
|
109
|
+
6. **(Later)** Scaffolding generator.
|
|
110
|
+
|
|
111
|
+
## Open Questions
|
|
112
|
+
|
|
113
|
+
- Naming: streamwire? streamweave? something else?
|
|
114
|
+
- Forms: how much Rails form helper API to mirror exactly?
|
|
115
|
+
- Multi-page apps: routing conventions for apps with several resources
|
|
116
|
+
- Asset handling: how a single-file Ruby app declares its own CSS/JS without a build step
|
|
117
|
+
- Testing: convention for testing StreamWeaver views (currently spec-by-rendered-HTML — fine for now)
|
|
118
|
+
|
|
119
|
+
These get answered as the conventions get exercised by real apps.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
*This document captures the strategic direction. For the practical "how to build a StreamWeaver app correctly" guide, see `llms.txt` (linked as `docs/for_llms.md`).*
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# streamweaver-doc: v1
|
|
2
|
+
header1 "Glimmer: Inspired By, Not Dependent On"
|
|
3
|
+
|
|
4
|
+
div(style: "height:8px")
|
|
5
|
+
|
|
6
|
+
columns widths: ["50%", "50%"] do
|
|
7
|
+
column do
|
|
8
|
+
card do
|
|
9
|
+
header3 "Why we skip the Glimmer gem"
|
|
10
|
+
badge "Too heavy for Opal", color: :red
|
|
11
|
+
div(style: "height:8px")
|
|
12
|
+
md "Glimmer depends on **facets** — a massive Ruby utility library. Compiled to Opal, facets alone balloons the JS bundle."
|
|
13
|
+
div(style: "height:8px")
|
|
14
|
+
md "Also: the `<=>` / `<=` binding syntax is confusing — it reuses comparison operators for a completely different meaning."
|
|
15
|
+
div(style: "height:8px")
|
|
16
|
+
md "We take the **ideas**, not the code."
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
column do
|
|
20
|
+
card do
|
|
21
|
+
header3 "What we take from Glimmer"
|
|
22
|
+
badge "Conceptual only", color: :green
|
|
23
|
+
div(style: "height:8px")
|
|
24
|
+
md "**Observer pattern** — the right model for reactive state"
|
|
25
|
+
md "**Direct DOM** (no virtual DOM) — proven to be sufficient"
|
|
26
|
+
md "**Observable model enhancement** — state auto-notifies observers when keys change"
|
|
27
|
+
div(style: "height:8px")
|
|
28
|
+
md "We implement this in ~70 lines of pure Ruby/Opal. No dependencies."
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
div(style: "height:24px")
|
|
34
|
+
header2 "ReactiveState: ~70 Lines, Zero Deps"
|
|
35
|
+
div(style: "height:8px")
|
|
36
|
+
|
|
37
|
+
card do
|
|
38
|
+
header3 "Design"
|
|
39
|
+
columns widths: ["50%", "50%"] do
|
|
40
|
+
column do
|
|
41
|
+
md "**ReactiveState** is a Ruby hash wrapper:"
|
|
42
|
+
md "- `[]` tracks which block regions read which keys during render"
|
|
43
|
+
md "- `[]=` notifies observers when a key changes"
|
|
44
|
+
md "- Observers are DOM updater lambdas registered at render time"
|
|
45
|
+
div(style: "height:8px")
|
|
46
|
+
md "Explicit watch for edge cases:"
|
|
47
|
+
md "- `watch(:search) { |val| ... }` — readable, obvious"
|
|
48
|
+
md "- No operator overloading confusion"
|
|
49
|
+
end
|
|
50
|
+
column do
|
|
51
|
+
md "**Invisible to the user — no API change:**"
|
|
52
|
+
md "Phase 1: plain hash, whole block re-runs"
|
|
53
|
+
md "Phase 2: ReactiveState proxy, only affected DOM nodes update"
|
|
54
|
+
div(style: "height:8px")
|
|
55
|
+
md "Same DSL code works in both phases. Upgrade is internal to the adapter."
|
|
56
|
+
div(style: "height:8px")
|
|
57
|
+
badge "Zero learning curve for app authors", color: :green
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
div(style: "height:24px")
|
|
63
|
+
header2 "Final Layer Map"
|
|
64
|
+
div(style: "height:8px")
|
|
65
|
+
|
|
66
|
+
table(
|
|
67
|
+
headers: ["Layer", "What it is", "Phase"],
|
|
68
|
+
rows: [
|
|
69
|
+
["User DSL", "Unchanged StreamWeaver DSL — same app/card/text_field/button", "Now"],
|
|
70
|
+
["Adapter::Opal", "Renders DSL to HTML string, wires event listeners, holds state", "1"],
|
|
71
|
+
["OpalBuilder", "opal-build command → dist/index.html + dist/app.js", "1"],
|
|
72
|
+
["morphdom.js", "Client-side DOM patching after block re-execution", "1"],
|
|
73
|
+
["ReactiveState", "~70-line Observable hash: auto-tracks reads, notifies on write", "2"],
|
|
74
|
+
["History API wrapper", "route DSL via pushState/popstate — same calls as Sinatra routes", "2"],
|
|
75
|
+
["Supabase client", "Thin Ruby/Opal wrapper — sync state to Supabase on demand", "3 (MMA app)"]
|
|
76
|
+
]
|
|
77
|
+
)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# streamweaver-doc: v1
|
|
2
|
+
header1 "🎯 Save as doc button — fresh bridge"
|
|
3
|
+
md "Hard-refresh this page (cmd-shift-R or ctrl-shift-R) — old bridge had stale code."
|
|
4
|
+
md ""
|
|
5
|
+
md "**Button is at the bottom-right corner.** If you don't see it:"
|
|
6
|
+
md "1. Try hard-refresh"
|
|
7
|
+
md "2. Check that this URL is `http://localhost:4700/canvas/canvas-arch`"
|
|
8
|
+
md "3. Open dev tools (cmd-opt-I) → Console — any JS errors?"
|
|
9
|
+
md "4. Inspect element on the page → look for `<button class=\"sw-save-doc-btn\">`"
|