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,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StreamWeaver
|
|
4
|
+
module Components
|
|
5
|
+
# Centralized keyboard shortcut registry.
|
|
6
|
+
# Non-visual component -- emits a <script> block that registers keyboard handlers
|
|
7
|
+
# with context awareness (suppress in text inputs, textareas, code editors).
|
|
8
|
+
#
|
|
9
|
+
# "mod" maps to Meta (Cmd) on Mac, Control elsewhere -- handled in JS.
|
|
10
|
+
#
|
|
11
|
+
# sw- CSS classes: none (non-visual)
|
|
12
|
+
#
|
|
13
|
+
# @example Basic usage
|
|
14
|
+
# keyboard_shortcuts do |kb|
|
|
15
|
+
# kb.on "mod+s", context: :global do |state|
|
|
16
|
+
# # save action
|
|
17
|
+
# end
|
|
18
|
+
# kb.on "ArrowRight", context: :navigation do |state|
|
|
19
|
+
# # next slide
|
|
20
|
+
# end
|
|
21
|
+
# end
|
|
22
|
+
class KeyboardShortcuts < Base
|
|
23
|
+
attr_reader :bindings
|
|
24
|
+
|
|
25
|
+
# Suppression selectors -- keyboard shortcuts are ignored when
|
|
26
|
+
# the active element matches any of these.
|
|
27
|
+
SUPPRESSION_SELECTORS = %w[
|
|
28
|
+
input[type=text]
|
|
29
|
+
input[type=search]
|
|
30
|
+
input[type=email]
|
|
31
|
+
input[type=url]
|
|
32
|
+
input[type=number]
|
|
33
|
+
input:not([type])
|
|
34
|
+
textarea
|
|
35
|
+
[contenteditable=true]
|
|
36
|
+
.sw-mermaid--zoom
|
|
37
|
+
.sw-code-scroll
|
|
38
|
+
].freeze
|
|
39
|
+
|
|
40
|
+
# @param options [Hash] Additional options
|
|
41
|
+
def initialize(**options)
|
|
42
|
+
@bindings = []
|
|
43
|
+
super(**options)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Register a keyboard shortcut binding.
|
|
47
|
+
#
|
|
48
|
+
# @param key_combo [String] Key combination (e.g. "mod+s", "ArrowRight", "1..9")
|
|
49
|
+
# @param context [Symbol] Context for suppression (:global, :navigation, :selection)
|
|
50
|
+
# @param js_action [String, nil] JavaScript code to execute (alternative to block)
|
|
51
|
+
# @param block [Proc, nil] Server-side callback (stored but not executed client-side)
|
|
52
|
+
def on(key_combo, context: :global, js_action: nil, &block)
|
|
53
|
+
@bindings << {
|
|
54
|
+
key: key_combo,
|
|
55
|
+
context: context,
|
|
56
|
+
js_action: js_action,
|
|
57
|
+
callback: block
|
|
58
|
+
}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def render(view, state)
|
|
62
|
+
view.adapter.render_keyboard_shortcuts(view, self, state)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Generate the JS registration calls for all bindings.
|
|
66
|
+
# Each binding becomes a call to swKeyboard.register(...)
|
|
67
|
+
#
|
|
68
|
+
# @return [String] JavaScript code
|
|
69
|
+
def to_js
|
|
70
|
+
lines = @bindings.map do |binding|
|
|
71
|
+
key = binding[:key]
|
|
72
|
+
context = binding[:context]
|
|
73
|
+
action = binding[:js_action] || "console.log('shortcut: #{key}')"
|
|
74
|
+
|
|
75
|
+
if key.include?("..")
|
|
76
|
+
# Range shortcut (e.g. "1..9")
|
|
77
|
+
range_start, range_end = key.split("..").map(&:to_i)
|
|
78
|
+
(range_start..range_end).map do |n|
|
|
79
|
+
"swKeyboard.register('#{n}', '#{context}', function(e) { #{action.gsub('KEY', n.to_s)} });"
|
|
80
|
+
end.join("\n")
|
|
81
|
+
else
|
|
82
|
+
"swKeyboard.register('#{key}', '#{context}', function(e) { #{action} });"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
lines.join("\n")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# CSS class list (empty -- non-visual component)
|
|
89
|
+
def css_classes
|
|
90
|
+
""
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StreamWeaver
|
|
4
|
+
module Components
|
|
5
|
+
# KPI Dashboard component.
|
|
6
|
+
# Renders an auto-fit grid of metric cards, each showing a large value,
|
|
7
|
+
# label, optional trend arrow, and optional color accent.
|
|
8
|
+
# Entry animation via CSS fadeIn.
|
|
9
|
+
#
|
|
10
|
+
# sw- CSS classes:
|
|
11
|
+
# sw-kpi-dashboard - outer grid container (auto-fit)
|
|
12
|
+
# sw-kpi-card - individual metric card
|
|
13
|
+
# sw-kpi-card--{color} - color accent variant
|
|
14
|
+
# sw-kpi-card__value - large value display
|
|
15
|
+
# sw-kpi-card__label - label below value
|
|
16
|
+
# sw-kpi-card__trend - trend arrow indicator
|
|
17
|
+
# sw-kpi-card__trend--up - green up arrow
|
|
18
|
+
# sw-kpi-card__trend--down - red down arrow
|
|
19
|
+
# sw-kpi-card__trend--flat - gray flat arrow
|
|
20
|
+
#
|
|
21
|
+
# @example
|
|
22
|
+
# kpi_dashboard metrics: [
|
|
23
|
+
# { value: "99.9%", label: "Uptime", color: :green, trend: :up },
|
|
24
|
+
# { value: "42ms", label: "Latency", trend: :down },
|
|
25
|
+
# { value: "1.2M", label: "Requests", trend: :flat }
|
|
26
|
+
# ]
|
|
27
|
+
class KpiDashboard < Base
|
|
28
|
+
attr_reader :metrics
|
|
29
|
+
|
|
30
|
+
# @param metrics [Array<Hash>] Array of metric hashes with :value, :label, :color (optional), :trend (optional)
|
|
31
|
+
# trend values: :up, :down, :flat, nil
|
|
32
|
+
# color values: :green, :blue, :red, :orange, :purple, nil
|
|
33
|
+
# @param options [Hash] Additional options
|
|
34
|
+
def initialize(metrics:, **options)
|
|
35
|
+
@metrics = metrics.map { |m| normalize_metric(m) }
|
|
36
|
+
super(**options)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def render(view, state)
|
|
40
|
+
view.adapter.render_kpi_dashboard(view, self, state)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Trend arrow character for a metric
|
|
44
|
+
def trend_arrow(metric)
|
|
45
|
+
case metric[:trend]
|
|
46
|
+
when :up then "\u2191" # up arrow
|
|
47
|
+
when :down then "\u2193" # down arrow
|
|
48
|
+
when :flat then "\u2192" # right arrow
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# CSS class for trend indicator
|
|
53
|
+
def trend_css_class(metric)
|
|
54
|
+
base = "sw-kpi-card__trend"
|
|
55
|
+
return base unless metric[:trend]
|
|
56
|
+
"#{base} sw-kpi-card__trend--#{metric[:trend]}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# CSS class for the card (with optional color)
|
|
60
|
+
def card_css_class(metric)
|
|
61
|
+
classes = ["sw-kpi-card"]
|
|
62
|
+
classes << "sw-kpi-card--#{metric[:color]}" if metric[:color]
|
|
63
|
+
classes.join(" ")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def normalize_metric(m)
|
|
69
|
+
{
|
|
70
|
+
value: m[:value].to_s,
|
|
71
|
+
label: m[:label] || "",
|
|
72
|
+
color: m[:color] ? m[:color].to_sym : nil,
|
|
73
|
+
trend: m[:trend] ? m[:trend].to_sym : nil
|
|
74
|
+
}
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StreamWeaver
|
|
4
|
+
module Components
|
|
5
|
+
# Mermaid diagram component.
|
|
6
|
+
# Renders Mermaid.js diagrams with optional zoom/pan support,
|
|
7
|
+
# compact mode for card embedding, and theme-aware styling.
|
|
8
|
+
#
|
|
9
|
+
# CDN loading is lazy: Mermaid.js 11 (ESM) is only loaded when
|
|
10
|
+
# this component is present on the page.
|
|
11
|
+
#
|
|
12
|
+
# sw- CSS classes:
|
|
13
|
+
# sw-mermaid - outer container
|
|
14
|
+
# sw-mermaid--compact - compact mode for card embedding
|
|
15
|
+
# sw-mermaid--zoom - zoom/pan enabled
|
|
16
|
+
# sw-mermaid__diagram - the diagram rendering area
|
|
17
|
+
# sw-mermaid__controls - zoom control buttons
|
|
18
|
+
# sw-mermaid__btn - individual zoom button
|
|
19
|
+
#
|
|
20
|
+
# @example Basic usage
|
|
21
|
+
# mermaid("graph LR; A-->B")
|
|
22
|
+
#
|
|
23
|
+
# @example With zoom
|
|
24
|
+
# mermaid("graph TD; A-->B-->C", zoom: true)
|
|
25
|
+
#
|
|
26
|
+
# @example Compact for cards
|
|
27
|
+
# mermaid("graph LR; A-->B", compact: true)
|
|
28
|
+
#
|
|
29
|
+
# @example ELK layout
|
|
30
|
+
# mermaid("graph TD; A-->B", layout: :elk)
|
|
31
|
+
class Mermaid < Base
|
|
32
|
+
attr_reader :code, :zoom, :compact, :layout, :theme_vars
|
|
33
|
+
|
|
34
|
+
# @param code [String] Mermaid diagram definition
|
|
35
|
+
# @param zoom [Boolean] Enable zoom/pan controls (default: false)
|
|
36
|
+
# @param compact [Boolean] Compact mode for card embedding (default: false)
|
|
37
|
+
# @param layout [Symbol] Layout engine (:default or :elk)
|
|
38
|
+
# @param theme_vars [Hash, nil] Per-block Mermaid themeVariables overrides
|
|
39
|
+
# @param options [Hash] Additional HTML options
|
|
40
|
+
def initialize(code, zoom: false, compact: false, layout: :default, theme_vars: nil, **options)
|
|
41
|
+
@code = code
|
|
42
|
+
@zoom = zoom
|
|
43
|
+
@compact = compact
|
|
44
|
+
@layout = layout.to_sym
|
|
45
|
+
@theme_vars = theme_vars
|
|
46
|
+
super(**options)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def render(view, state)
|
|
50
|
+
view.adapter.render_mermaid(view, self, state)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Whether ELK layout engine is requested
|
|
54
|
+
def elk?
|
|
55
|
+
@layout == :elk
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Generate a unique ID for this diagram instance
|
|
59
|
+
def diagram_id
|
|
60
|
+
@diagram_id ||= "sw-mermaid-#{object_id}"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Serialize theme_vars to JSON for the JS initializer
|
|
64
|
+
def theme_vars_json
|
|
65
|
+
return "null" unless @theme_vars
|
|
66
|
+
require "json"
|
|
67
|
+
JSON.generate(@theme_vars)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# CSS class list for the outer container
|
|
71
|
+
def css_classes
|
|
72
|
+
classes = ["sw-mermaid"]
|
|
73
|
+
classes << "sw-mermaid--compact" if @compact
|
|
74
|
+
classes << "sw-mermaid--zoom" if @zoom
|
|
75
|
+
classes.join(" ")
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StreamWeaver
|
|
4
|
+
module Components
|
|
5
|
+
# Pipeline step flow visualization.
|
|
6
|
+
# Renders a horizontal sequence of steps with arrow connectors,
|
|
7
|
+
# color-coded by status: complete (green), active (blue), pending (gray).
|
|
8
|
+
# Responsive: collapses to vertical layout on narrow screens.
|
|
9
|
+
#
|
|
10
|
+
# sw- CSS classes:
|
|
11
|
+
# sw-pipeline - outer flexbox container
|
|
12
|
+
# sw-pipeline__step - individual step box
|
|
13
|
+
# sw-pipeline__step--complete - green (done)
|
|
14
|
+
# sw-pipeline__step--active - blue (in progress)
|
|
15
|
+
# sw-pipeline__step--pending - gray (not started)
|
|
16
|
+
# sw-pipeline__label - step label text
|
|
17
|
+
# sw-pipeline__desc - step description text
|
|
18
|
+
# sw-pipeline__arrow - CSS triangle arrow connector
|
|
19
|
+
#
|
|
20
|
+
# @example Basic
|
|
21
|
+
# pipeline steps: [
|
|
22
|
+
# { label: "Build", status: :complete },
|
|
23
|
+
# { label: "Test", status: :active },
|
|
24
|
+
# { label: "Deploy", status: :pending }
|
|
25
|
+
# ]
|
|
26
|
+
class Pipeline < Base
|
|
27
|
+
attr_reader :steps
|
|
28
|
+
|
|
29
|
+
# @param steps [Array<Hash>] Array of step hashes with :label, :description (optional), :status
|
|
30
|
+
# status values: :complete, :active, :pending
|
|
31
|
+
# @param options [Hash] Additional options
|
|
32
|
+
def initialize(steps:, **options)
|
|
33
|
+
@steps = steps.map { |s| normalize_step(s) }
|
|
34
|
+
super(**options)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def render(view, state)
|
|
38
|
+
view.adapter.render_pipeline(view, self, state)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# CSS class for a step based on its status
|
|
42
|
+
def step_css_class(step)
|
|
43
|
+
base = "sw-pipeline__step"
|
|
44
|
+
modifier = case step[:status]
|
|
45
|
+
when :complete then "sw-pipeline__step--complete"
|
|
46
|
+
when :active then "sw-pipeline__step--active"
|
|
47
|
+
else "sw-pipeline__step--pending"
|
|
48
|
+
end
|
|
49
|
+
"#{base} #{modifier}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def normalize_step(step)
|
|
55
|
+
{
|
|
56
|
+
label: step[:label] || "Step",
|
|
57
|
+
description: step[:description],
|
|
58
|
+
status: (step[:status] || :pending).to_sym
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StreamWeaver
|
|
4
|
+
module Components
|
|
5
|
+
# Sticky sidebar table-of-contents with IntersectionObserver scroll spy.
|
|
6
|
+
#
|
|
7
|
+
# Desktop (>=1000px): sticky 170px sidebar with active-section highlighting.
|
|
8
|
+
# Mobile (<1000px): horizontal scrollable sticky bar at the top.
|
|
9
|
+
#
|
|
10
|
+
# Sections are an array of hashes, each with :id and :label keys.
|
|
11
|
+
# The :id corresponds to the DOM id of the target section element.
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
# sidebar_toc sections: [
|
|
15
|
+
# { id: "summary", label: "Executive Summary" },
|
|
16
|
+
# { id: "architecture", label: "Architecture" },
|
|
17
|
+
# { id: "risks", label: "Risks" }
|
|
18
|
+
# ]
|
|
19
|
+
class SidebarToc < Base
|
|
20
|
+
attr_reader :sections
|
|
21
|
+
|
|
22
|
+
# @param sections [Array<Hash>] Array of { id:, label: } hashes
|
|
23
|
+
# @param options [Hash] Additional options
|
|
24
|
+
def initialize(sections:, **options)
|
|
25
|
+
@sections = normalize_sections(sections)
|
|
26
|
+
@options = options
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def render(view, state)
|
|
30
|
+
view.adapter.render_sidebar_toc(view, self, state)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
# Normalize sections to ensure string keys become symbols
|
|
36
|
+
def normalize_sections(sections)
|
|
37
|
+
sections.map do |s|
|
|
38
|
+
{
|
|
39
|
+
id: (s[:id] || s["id"]).to_s,
|
|
40
|
+
label: (s[:label] || s["label"]).to_s
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StreamWeaver
|
|
4
|
+
module Components
|
|
5
|
+
# Slide within a SlideContainer.
|
|
6
|
+
# Contains child components rendered as slide content.
|
|
7
|
+
#
|
|
8
|
+
# sw- CSS classes:
|
|
9
|
+
# sw-slide - individual slide wrapper
|
|
10
|
+
# sw-slide--active - currently visible slide (swap mode)
|
|
11
|
+
# sw-slide--title - title slide type
|
|
12
|
+
# sw-slide--content - content slide type
|
|
13
|
+
#
|
|
14
|
+
# @example
|
|
15
|
+
# slide "intro", "Introduction" do
|
|
16
|
+
# text "Welcome to the presentation"
|
|
17
|
+
# end
|
|
18
|
+
class Slide < Base
|
|
19
|
+
attr_reader :id, :title, :type
|
|
20
|
+
attr_accessor :children
|
|
21
|
+
|
|
22
|
+
# @param id [String] Unique identifier for this slide
|
|
23
|
+
# @param title [String, nil] Optional slide title
|
|
24
|
+
# @param type [Symbol] Slide type (:content, :title)
|
|
25
|
+
# @param options [Hash] Additional HTML options
|
|
26
|
+
def initialize(id, title = nil, type: :content, **options)
|
|
27
|
+
@id = id.to_s
|
|
28
|
+
@title = title
|
|
29
|
+
@type = type.to_sym
|
|
30
|
+
@children = []
|
|
31
|
+
super(**options)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def render(view, state)
|
|
35
|
+
view.adapter.render_slide(view, self, state)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# CSS class list
|
|
39
|
+
def css_classes
|
|
40
|
+
classes = ["sw-slide"]
|
|
41
|
+
classes << "sw-slide--#{@type}"
|
|
42
|
+
classes.join(" ")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Container for navigable slides.
|
|
47
|
+
# Supports two modes:
|
|
48
|
+
# :swap -- one slide visible at a time, Back/Next buttons, fade transition (deck)
|
|
49
|
+
# :scroll_snap -- all slides rendered, CSS scroll-snap-type: y mandatory (explainer)
|
|
50
|
+
#
|
|
51
|
+
# Both modes share: keyboard navigation, progress tracking, Alpine.js x-data for current index.
|
|
52
|
+
#
|
|
53
|
+
# sw- CSS classes:
|
|
54
|
+
# sw-slide-container - outer container
|
|
55
|
+
# sw-slide-container--swap - swap mode
|
|
56
|
+
# sw-slide-container--scroll-snap - scroll-snap mode
|
|
57
|
+
# sw-slide-nav - navigation buttons wrapper
|
|
58
|
+
# sw-slide-nav__btn - Back/Next buttons
|
|
59
|
+
# sw-slide-nav__btn--prev - Back button modifier
|
|
60
|
+
# sw-slide-nav__btn--next - Next button modifier
|
|
61
|
+
# sw-slide-progress - progress bar
|
|
62
|
+
# sw-slide-progress--fixed - fixed at top of viewport
|
|
63
|
+
# sw-slide-progress__bar - progress bar fill
|
|
64
|
+
# sw-slide-dots - navigation dots container
|
|
65
|
+
# sw-slide-dots__dot - individual dot
|
|
66
|
+
# sw-slide-dots__dot--active - active dot
|
|
67
|
+
# sw-slide-counter - slide counter text (e.g. "2 / 5")
|
|
68
|
+
#
|
|
69
|
+
# @example Swap mode (deck)
|
|
70
|
+
# slide_container mode: :swap, progress_bar: true do
|
|
71
|
+
# slide "intro", "Introduction" do
|
|
72
|
+
# text "Welcome"
|
|
73
|
+
# end
|
|
74
|
+
# slide "arch", "Architecture" do
|
|
75
|
+
# text "System design"
|
|
76
|
+
# end
|
|
77
|
+
# end
|
|
78
|
+
#
|
|
79
|
+
# @example Scroll-snap mode (explainer)
|
|
80
|
+
# slide_container mode: :scroll_snap, nav_dots: true, counter: true do
|
|
81
|
+
# slide "title", type: :title do
|
|
82
|
+
# header1 "Title"
|
|
83
|
+
# end
|
|
84
|
+
# slide "content1" do
|
|
85
|
+
# text "Content..."
|
|
86
|
+
# end
|
|
87
|
+
# end
|
|
88
|
+
class SlideContainer < Base
|
|
89
|
+
attr_reader :mode, :progress_bar, :nav_dots, :counter, :keyboard_nav
|
|
90
|
+
attr_accessor :children
|
|
91
|
+
|
|
92
|
+
# @param mode [Symbol] Display mode (:swap or :scroll_snap)
|
|
93
|
+
# @param progress_bar [Boolean] Show progress bar (default: true)
|
|
94
|
+
# @param keyboard_nav [Boolean] Enable arrow key navigation (default: true)
|
|
95
|
+
# @param nav_dots [Boolean] Show navigation dots (default: false)
|
|
96
|
+
# @param counter [Boolean] Show slide counter (default: false)
|
|
97
|
+
# @param options [Hash] Additional options
|
|
98
|
+
def initialize(mode: :swap, progress_bar: true, keyboard_nav: true,
|
|
99
|
+
nav_dots: false, counter: false, **options)
|
|
100
|
+
@mode = mode.to_sym
|
|
101
|
+
@progress_bar = progress_bar
|
|
102
|
+
@keyboard_nav = keyboard_nav
|
|
103
|
+
@nav_dots = nav_dots
|
|
104
|
+
@counter = counter
|
|
105
|
+
@children = []
|
|
106
|
+
super(**options)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def render(view, state)
|
|
110
|
+
view.adapter.render_slide_container(view, self, state)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Number of slides
|
|
114
|
+
def slide_count
|
|
115
|
+
@children.length
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Whether swap mode
|
|
119
|
+
def swap?
|
|
120
|
+
@mode == :swap
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Whether scroll-snap mode
|
|
124
|
+
def scroll_snap?
|
|
125
|
+
@mode == :scroll_snap
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Generate a unique container ID
|
|
129
|
+
def container_id
|
|
130
|
+
@container_id ||= "sw-slides-#{object_id}"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# CSS class list
|
|
134
|
+
def css_classes
|
|
135
|
+
classes = ["sw-slide-container"]
|
|
136
|
+
classes << "sw-slide-container--#{@mode.to_s.tr('_', '-')}"
|
|
137
|
+
classes.join(" ")
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StreamWeaver
|
|
4
|
+
module Components
|
|
5
|
+
# Timeline event row with colored type badge and expandable detail fields.
|
|
6
|
+
# Designed to replicate the DTO Run Viewer event log style:
|
|
7
|
+
# compact row with index, type badge, timestamp, label — click to expand key-value details.
|
|
8
|
+
#
|
|
9
|
+
# Event types map to distinct colors:
|
|
10
|
+
# phase → cyan (#00bcd4)
|
|
11
|
+
# snapshot → yellow (#ffc107)
|
|
12
|
+
# intervention → purple (#ce93d8)
|
|
13
|
+
# timeout → red (#ef5350)
|
|
14
|
+
# guard → red (#ef5350)
|
|
15
|
+
# final → green (#66bb6a)
|
|
16
|
+
#
|
|
17
|
+
# sw- CSS classes:
|
|
18
|
+
# sw-timeline-event - outer row container
|
|
19
|
+
# sw-timeline-event--{type} - type-specific border + badge colors
|
|
20
|
+
# sw-timeline-event__idx - index number (right-aligned)
|
|
21
|
+
# sw-timeline-event__badge - type badge pill
|
|
22
|
+
# sw-timeline-event__ts - timestamp
|
|
23
|
+
# sw-timeline-event__label - summary label
|
|
24
|
+
# sw-timeline-event__detail - expandable detail section
|
|
25
|
+
# sw-timeline-event__field - single key-value pair
|
|
26
|
+
# sw-timeline-event__field-key - field label (bold)
|
|
27
|
+
# sw-timeline-event__field-value - field value (may contain pre block)
|
|
28
|
+
#
|
|
29
|
+
# @example
|
|
30
|
+
# timeline_event index: 0, event_type: :phase, timestamp: "10:00:00",
|
|
31
|
+
# label: "launch", fields: { run_id: "abc-123", phase: "launch" }
|
|
32
|
+
class TimelineEvent < Base
|
|
33
|
+
attr_reader :index, :event_type, :timestamp, :label, :fields, :expanded
|
|
34
|
+
|
|
35
|
+
TYPES = %i[phase snapshot intervention timeout guard final].freeze
|
|
36
|
+
|
|
37
|
+
# @param index [Integer] Event sequence number
|
|
38
|
+
# @param event_type [Symbol] One of :phase, :snapshot, :intervention, :timeout, :guard, :final
|
|
39
|
+
# @param timestamp [String] Time display (e.g., "10:00:00")
|
|
40
|
+
# @param label [String] Summary label for the event row
|
|
41
|
+
# @param fields [Hash] Key-value pairs shown in the expanded detail section
|
|
42
|
+
# @param expanded [Boolean] Whether to start expanded (default: false)
|
|
43
|
+
def initialize(index:, event_type:, timestamp:, label:, fields: {}, expanded: false, **options)
|
|
44
|
+
@index = index
|
|
45
|
+
@event_type = TYPES.include?(event_type.to_sym) ? event_type.to_sym : :phase
|
|
46
|
+
@timestamp = timestamp
|
|
47
|
+
@label = label
|
|
48
|
+
@fields = fields
|
|
49
|
+
@expanded = expanded
|
|
50
|
+
super(**options)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def render(view, state)
|
|
54
|
+
view.adapter.render_timeline_event(view, self, state)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StreamWeaver
|
|
4
|
+
module Components
|
|
5
|
+
# HTML mockup wrapped in device chrome matching the surface type.
|
|
6
|
+
# Renders the author's HTML fragment inside a visual device frame —
|
|
7
|
+
# browser window, phone bezel, desktop window, or compact floating frame.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# wireframe(surface: :browser) do
|
|
11
|
+
# "<h1>Login</h1><button class=\"primary\">Sign in</button>"
|
|
12
|
+
# end
|
|
13
|
+
class Wireframe < Base
|
|
14
|
+
SURFACES = %w[browser desktop mobile phone tablet popover card widget panel].freeze
|
|
15
|
+
|
|
16
|
+
attr_reader :html, :surface
|
|
17
|
+
|
|
18
|
+
def initialize(html: "", surface: :browser, **options)
|
|
19
|
+
@html = html
|
|
20
|
+
@surface = SURFACES.include?(surface.to_s) ? surface.to_s : "browser"
|
|
21
|
+
@options = options
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def render(view, state)
|
|
25
|
+
view.adapter.render_wireframe(view, self, state)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StreamWeaver
|
|
4
|
+
module Components
|
|
5
|
+
# A wireframe surface block that renders raw HTML as a hand-drawn mockup.
|
|
6
|
+
# Provides scoped --wf-* CSS tokens and helper classes within .sw-wireframe-surface.
|
|
7
|
+
#
|
|
8
|
+
# All styling is intentionally scoped to .sw-wireframe-surface so wireframe
|
|
9
|
+
# aesthetics cannot leak into the surrounding app UI.
|
|
10
|
+
#
|
|
11
|
+
# CSS tokens and helper classes are injected inline by the adapter (same pattern
|
|
12
|
+
# as other canvas components). See AlpineJS#wireframe_css for the full token set.
|
|
13
|
+
#
|
|
14
|
+
# @example
|
|
15
|
+
# wireframe_block(html: "<h1>Login</h1><button class=\"primary\">Sign in</button>")
|
|
16
|
+
class WireframeBlock < Base
|
|
17
|
+
SURFACES = %w[browser desktop mobile popover panel].freeze
|
|
18
|
+
|
|
19
|
+
attr_reader :html, :surface
|
|
20
|
+
|
|
21
|
+
# @param html [String] Raw HTML fragment to display inside the wireframe surface
|
|
22
|
+
# @param surface [String] Surface type: browser, desktop, mobile, popover, panel
|
|
23
|
+
def initialize(html: "", surface: "browser", **options)
|
|
24
|
+
@html = html
|
|
25
|
+
@surface = SURFACES.include?(surface) ? surface : "browser"
|
|
26
|
+
@options = options
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def render(view, state)
|
|
30
|
+
view.adapter.render_wireframe_block(view, self, state)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|