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,591 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StreamWeaver
|
|
4
|
+
module Adapter
|
|
5
|
+
# Base adapter class that all adapters must inherit from
|
|
6
|
+
# Defines the interface that components use to render themselves
|
|
7
|
+
#
|
|
8
|
+
# This adapter pattern decouples components from specific frontend frameworks
|
|
9
|
+
# (Alpine.js, HTMX, React, Opal, etc.), allowing pluggable implementations.
|
|
10
|
+
#
|
|
11
|
+
# @abstract Subclass and override all methods to implement a new adapter
|
|
12
|
+
#
|
|
13
|
+
# @example Creating a custom adapter
|
|
14
|
+
# class MyAdapter < StreamWeaver::Adapter::Base
|
|
15
|
+
# def render_text_field(view, key, options, state)
|
|
16
|
+
# view.input(type: "text", "my-binding" => key.to_s)
|
|
17
|
+
# end
|
|
18
|
+
# # ... implement all other methods
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# @example Using an adapter
|
|
22
|
+
# app = StreamWeaver::App.new("My App", adapter: MyAdapter.new) do
|
|
23
|
+
# text_field :name
|
|
24
|
+
# end
|
|
25
|
+
class Base
|
|
26
|
+
# Render a single-line text input field
|
|
27
|
+
#
|
|
28
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
29
|
+
# @param key [Symbol] The state key for this input
|
|
30
|
+
# @param options [Hash] Component options
|
|
31
|
+
# @option options [String] :placeholder Placeholder text
|
|
32
|
+
# @param state [Hash] Current state hash (symbol keys)
|
|
33
|
+
# @return [void] Renders to view
|
|
34
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
35
|
+
#
|
|
36
|
+
# @example
|
|
37
|
+
# adapter.render_text_field(view, :email, { placeholder: "Email" }, state)
|
|
38
|
+
def render_text_field(view, key, options, state)
|
|
39
|
+
raise NotImplementedError, "#{self.class} must implement #render_text_field"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Render a multi-line text area
|
|
43
|
+
#
|
|
44
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
45
|
+
# @param key [Symbol] The state key for this textarea
|
|
46
|
+
# @param options [Hash] Component options
|
|
47
|
+
# @option options [String] :placeholder Placeholder text
|
|
48
|
+
# @option options [Integer] :rows Number of rows (default: 3)
|
|
49
|
+
# @param state [Hash] Current state hash (symbol keys)
|
|
50
|
+
# @return [void] Renders to view
|
|
51
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
52
|
+
#
|
|
53
|
+
# @example
|
|
54
|
+
# adapter.render_text_area(view, :bio, { rows: 5 }, state)
|
|
55
|
+
def render_text_area(view, key, options, state)
|
|
56
|
+
raise NotImplementedError, "#{self.class} must implement #render_text_area"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Render a native date input, state-bound like a text field. Value is
|
|
60
|
+
# stored/read as an ISO 8601 string ("YYYY-MM-DD").
|
|
61
|
+
#
|
|
62
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
63
|
+
# @param key [Symbol] The state key for this input
|
|
64
|
+
# @param options [Hash] Component options
|
|
65
|
+
# @option options [String] :label Optional label rendered above the input
|
|
66
|
+
# @option options [String] :min Minimum selectable date (ISO 8601)
|
|
67
|
+
# @option options [String] :max Maximum selectable date (ISO 8601)
|
|
68
|
+
# @param state [Hash] Current state hash (symbol keys)
|
|
69
|
+
# @return [void] Renders to view
|
|
70
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
71
|
+
#
|
|
72
|
+
# @example
|
|
73
|
+
# adapter.render_date_field(view, :due_on, { label: "Due" }, state)
|
|
74
|
+
def render_date_field(view, key, options, state)
|
|
75
|
+
raise NotImplementedError, "#{self.class} must implement #render_date_field"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Render a checkbox input
|
|
79
|
+
#
|
|
80
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
81
|
+
# @param key [Symbol] The state key for this checkbox
|
|
82
|
+
# @param label [String] The label text
|
|
83
|
+
# @param options [Hash] Component options
|
|
84
|
+
# @param state [Hash] Current state hash (symbol keys)
|
|
85
|
+
# @return [void] Renders to view
|
|
86
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
87
|
+
#
|
|
88
|
+
# @example
|
|
89
|
+
# adapter.render_checkbox(view, :agree, "I agree", {}, state)
|
|
90
|
+
def render_checkbox(view, key, label, options, state)
|
|
91
|
+
raise NotImplementedError, "#{self.class} must implement #render_checkbox"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Render a select dropdown
|
|
95
|
+
#
|
|
96
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
97
|
+
# @param key [Symbol] The state key for this select
|
|
98
|
+
# @param choices [Array<String>] The available choices
|
|
99
|
+
# @param options [Hash] Component options
|
|
100
|
+
# @param state [Hash] Current state hash (symbol keys)
|
|
101
|
+
# @return [void] Renders to view
|
|
102
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
103
|
+
#
|
|
104
|
+
# @example
|
|
105
|
+
# adapter.render_select(view, :color, ["Red", "Green", "Blue"], {}, state)
|
|
106
|
+
def render_select(view, key, choices, options, state)
|
|
107
|
+
raise NotImplementedError, "#{self.class} must implement #render_select"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Render a radio button group for single-choice selection
|
|
111
|
+
#
|
|
112
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
113
|
+
# @param key [Symbol] The state key for this radio group
|
|
114
|
+
# @param choices [Array<String>] The available choices
|
|
115
|
+
# @param options [Hash] Component options
|
|
116
|
+
# @param state [Hash] Current state hash (symbol keys)
|
|
117
|
+
# @return [void] Renders to view
|
|
118
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
119
|
+
#
|
|
120
|
+
# @example
|
|
121
|
+
# adapter.render_radio_group(view, :answer, ["Option A", "Option B"], {}, state)
|
|
122
|
+
def render_radio_group(view, key, choices, options, state)
|
|
123
|
+
raise NotImplementedError, "#{self.class} must implement #render_radio_group"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Render a button that executes an action
|
|
127
|
+
#
|
|
128
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
129
|
+
# @param button_id [String] The deterministic button ID (e.g., "btn_submit_1")
|
|
130
|
+
# @param label [String] The button label text
|
|
131
|
+
# @param options [Hash] Component options
|
|
132
|
+
# @option options [Symbol] :style Button style (:primary or :secondary)
|
|
133
|
+
# @return [void] Renders to view
|
|
134
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
135
|
+
#
|
|
136
|
+
# @example
|
|
137
|
+
# adapter.render_button(view, "btn_submit_1", "Submit", { style: :primary })
|
|
138
|
+
def render_button(view, button_id, label, options)
|
|
139
|
+
raise NotImplementedError, "#{self.class} must implement #render_button"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Render a clipboard-copy button (client-side only; Alpine.js only --
|
|
143
|
+
# not implemented for the Opal adapter by design)
|
|
144
|
+
#
|
|
145
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
146
|
+
# @param component [Components::CopyButton] The copy button component
|
|
147
|
+
# @param state [Hash] Current state hash
|
|
148
|
+
# @return [void] Renders to view
|
|
149
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
150
|
+
#
|
|
151
|
+
# @example
|
|
152
|
+
# adapter.render_copy_button(view, component, state)
|
|
153
|
+
def render_copy_button(view, component, state)
|
|
154
|
+
raise NotImplementedError, "#{self.class} must implement #render_copy_button"
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Get HTML attributes for the app container (e.g., x-data for Alpine.js)
|
|
158
|
+
#
|
|
159
|
+
# @param state [Hash] Current state hash (symbol keys)
|
|
160
|
+
# @return [Hash] HTML attributes to apply to container div
|
|
161
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
162
|
+
#
|
|
163
|
+
# @example
|
|
164
|
+
# attrs = adapter.container_attributes({ name: "Alice" })
|
|
165
|
+
# # => { "x-data" => '{"name":"Alice"}' }
|
|
166
|
+
def container_attributes(state)
|
|
167
|
+
raise NotImplementedError, "#{self.class} must implement #container_attributes"
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Get CDN script tags or inline scripts needed by this adapter
|
|
171
|
+
#
|
|
172
|
+
# @return [Array<String>] Array of HTML script tags
|
|
173
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
174
|
+
#
|
|
175
|
+
# @example
|
|
176
|
+
# adapter.cdn_scripts
|
|
177
|
+
# # => ['<script src="..."></script>', '<script>...</script>']
|
|
178
|
+
def cdn_scripts
|
|
179
|
+
raise NotImplementedError, "#{self.class} must implement #cdn_scripts"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Render CDN scripts directly to the view using Phlex methods
|
|
183
|
+
#
|
|
184
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
185
|
+
# @return [void] Renders script tags to the view
|
|
186
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
187
|
+
#
|
|
188
|
+
# @example
|
|
189
|
+
# adapter.render_cdn_scripts(view)
|
|
190
|
+
def render_cdn_scripts(view)
|
|
191
|
+
raise NotImplementedError, "#{self.class} must implement #render_cdn_scripts"
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Get the input selector for including form data (e.g., "[x-model]" for Alpine.js)
|
|
195
|
+
# Used by buttons to know which inputs to include when submitting
|
|
196
|
+
#
|
|
197
|
+
# @return [String] CSS selector for input elements
|
|
198
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
199
|
+
#
|
|
200
|
+
# @example
|
|
201
|
+
# adapter.input_selector
|
|
202
|
+
# # => "[x-model]"
|
|
203
|
+
def input_selector
|
|
204
|
+
raise NotImplementedError, "#{self.class} must implement #input_selector"
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Render a checkbox group with select all/none functionality
|
|
208
|
+
# State is stored as an array of selected values
|
|
209
|
+
#
|
|
210
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
211
|
+
# @param key [Symbol] The state key for this group (stores array)
|
|
212
|
+
# @param children [Array<CheckboxItem>] The checkbox items
|
|
213
|
+
# @param options [Hash] Component options (select_all, select_none labels)
|
|
214
|
+
# @param state [Hash] Current state hash (symbol keys)
|
|
215
|
+
# @return [void] Renders to view
|
|
216
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
217
|
+
def render_checkbox_group(view, key, children, options, state)
|
|
218
|
+
raise NotImplementedError, "#{self.class} must implement #render_checkbox_group"
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Render a vertical stack container
|
|
222
|
+
def render_vstack(view, component, state)
|
|
223
|
+
raise NotImplementedError, "#{self.class} must implement #render_vstack"
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Render a horizontal stack container
|
|
227
|
+
def render_hstack(view, component, state)
|
|
228
|
+
raise NotImplementedError, "#{self.class} must implement #render_hstack"
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Render a responsive grid container
|
|
232
|
+
def render_grid(view, component, state)
|
|
233
|
+
raise NotImplementedError, "#{self.class} must implement #render_grid"
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Render a scrollable container with max-height
|
|
237
|
+
def render_scroll_box(view, component, state)
|
|
238
|
+
raise NotImplementedError, "#{self.class} must implement #render_scroll_box"
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Render a collapsible/expandable section
|
|
242
|
+
#
|
|
243
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
244
|
+
# @param component [Components::Collapsible] The collapsible component
|
|
245
|
+
# @param state [Hash] Current state hash (symbol keys)
|
|
246
|
+
# @return [void] Renders to view
|
|
247
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
248
|
+
def render_collapsible(view, component, state)
|
|
249
|
+
raise NotImplementedError, "#{self.class} must implement #render_collapsible"
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Render an expandable card with title, subtitle, badge, and status
|
|
253
|
+
#
|
|
254
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
255
|
+
# @param component [Components::ExpandableCard] The expandable card component
|
|
256
|
+
# @param state [Hash] Current state hash (symbol keys)
|
|
257
|
+
# @return [void] Renders to view
|
|
258
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
259
|
+
def render_expandable_card(view, component, state)
|
|
260
|
+
raise NotImplementedError, "#{self.class} must implement #render_expandable_card"
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# =========================================
|
|
264
|
+
# Navigation component rendering
|
|
265
|
+
# =========================================
|
|
266
|
+
|
|
267
|
+
# Render a tabbed navigation container
|
|
268
|
+
#
|
|
269
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
270
|
+
# @param component [Tabs] The tabs component with children
|
|
271
|
+
# @param state [Hash] Current state hash (symbol keys)
|
|
272
|
+
# @return [void] Renders to view
|
|
273
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
274
|
+
def render_tabs(view, component, state)
|
|
275
|
+
raise NotImplementedError, "#{self.class} must implement #render_tabs"
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# Render a breadcrumbs navigation trail
|
|
279
|
+
#
|
|
280
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
281
|
+
# @param component [Breadcrumbs] The breadcrumbs component with children
|
|
282
|
+
# @param state [Hash] Current state hash (symbol keys)
|
|
283
|
+
# @return [void] Renders to view
|
|
284
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
285
|
+
def render_breadcrumbs(view, component, state)
|
|
286
|
+
raise NotImplementedError, "#{self.class} must implement #render_breadcrumbs"
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Render a dropdown menu container
|
|
290
|
+
#
|
|
291
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
292
|
+
# @param component [Dropdown] The dropdown component with trigger and menu
|
|
293
|
+
# @param state [Hash] Current state hash (symbol keys)
|
|
294
|
+
# @return [void] Renders to view
|
|
295
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
296
|
+
def render_dropdown(view, component, state)
|
|
297
|
+
raise NotImplementedError, "#{self.class} must implement #render_dropdown"
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# Render a theme toggle button (dark/light/auto mode)
|
|
301
|
+
#
|
|
302
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
303
|
+
# @param component [ThemeToggle] The theme toggle component
|
|
304
|
+
# @param state [Hash] Current state hash
|
|
305
|
+
# @return [void] Renders to view
|
|
306
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
307
|
+
def render_theme_toggle(view, component, state)
|
|
308
|
+
raise NotImplementedError, "#{self.class} must implement #render_theme_toggle"
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# Render a theme preset (Google Fonts link + CSS custom properties)
|
|
312
|
+
#
|
|
313
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
314
|
+
# @param component [ThemePreset] The theme preset component
|
|
315
|
+
# @param state [Hash] Current state hash
|
|
316
|
+
# @return [void] Renders to view
|
|
317
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
318
|
+
def render_theme_preset(view, component, state)
|
|
319
|
+
raise NotImplementedError, "#{self.class} must implement #render_theme_preset"
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# Render a syntax-highlighted code block with Prism.js
|
|
323
|
+
#
|
|
324
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
325
|
+
# @param component [CodeBlock] The code block component
|
|
326
|
+
# @param state [Hash] Current state hash
|
|
327
|
+
# @return [void] Renders to view
|
|
328
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
329
|
+
def render_code_block(view, component, state)
|
|
330
|
+
raise NotImplementedError, "#{self.class} must implement #render_code_block"
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# Render an image with optional caption
|
|
334
|
+
#
|
|
335
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
336
|
+
# @param component [ImageBlock] The image block component
|
|
337
|
+
# @param state [Hash] Current state hash
|
|
338
|
+
# @return [void] Renders to view
|
|
339
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
340
|
+
def render_image_block(view, component, state)
|
|
341
|
+
raise NotImplementedError, "#{self.class} must implement #render_image_block"
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# Render a Mermaid diagram with optional zoom/pan
|
|
345
|
+
#
|
|
346
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
347
|
+
# @param component [Mermaid] The mermaid component
|
|
348
|
+
# @param state [Hash] Current state hash
|
|
349
|
+
# @return [void] Renders to view
|
|
350
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
351
|
+
def render_mermaid(view, component, state)
|
|
352
|
+
raise NotImplementedError, "#{self.class} must implement #render_mermaid"
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# Render a pipeline step flow visualization
|
|
356
|
+
#
|
|
357
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
358
|
+
# @param component [Pipeline] The pipeline component
|
|
359
|
+
# @param state [Hash] Current state hash
|
|
360
|
+
# @return [void] Renders to view
|
|
361
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
362
|
+
def render_pipeline(view, component, state)
|
|
363
|
+
raise NotImplementedError, "#{self.class} must implement #render_pipeline"
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# Render a KPI dashboard grid of metric cards
|
|
367
|
+
#
|
|
368
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
369
|
+
# @param component [KpiDashboard] The KPI dashboard component
|
|
370
|
+
# @param state [Hash] Current state hash
|
|
371
|
+
# @return [void] Renders to view
|
|
372
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
373
|
+
def render_kpi_dashboard(view, component, state)
|
|
374
|
+
raise NotImplementedError, "#{self.class} must implement #render_kpi_dashboard"
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# Render a Chart.js chart (visual skills T12 wrapper)
|
|
378
|
+
# Named render_chartjs to avoid collision with existing render_chart
|
|
379
|
+
# which uses config_class: keyword for legacy BarChart/LineChart/PieChart.
|
|
380
|
+
#
|
|
381
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
382
|
+
# @param component [Chart] The chart component
|
|
383
|
+
# @param state [Hash] Current state hash
|
|
384
|
+
# @return [void] Renders to view
|
|
385
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
386
|
+
def render_chartjs(view, component, state)
|
|
387
|
+
raise NotImplementedError, "#{self.class} must implement #render_chartjs"
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
# Render keyboard shortcuts (non-visual -- emits <script> block)
|
|
391
|
+
#
|
|
392
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
393
|
+
# @param component [KeyboardShortcuts] The keyboard shortcuts component
|
|
394
|
+
# @param state [Hash] Current state hash
|
|
395
|
+
# @return [void] Renders to view
|
|
396
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
397
|
+
def render_keyboard_shortcuts(view, component, state)
|
|
398
|
+
raise NotImplementedError, "#{self.class} must implement #render_keyboard_shortcuts"
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
# Render a slide container with navigation
|
|
402
|
+
#
|
|
403
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
404
|
+
# @param component [SlideContainer] The slide container component
|
|
405
|
+
# @param state [Hash] Current state hash
|
|
406
|
+
# @return [void] Renders to view
|
|
407
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
408
|
+
def render_slide_container(view, component, state)
|
|
409
|
+
raise NotImplementedError, "#{self.class} must implement #render_slide_container"
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# Render an individual slide within a container
|
|
413
|
+
#
|
|
414
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
415
|
+
# @param component [Slide] The slide component
|
|
416
|
+
# @param state [Hash] Current state hash
|
|
417
|
+
# @return [void] Renders to view
|
|
418
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
419
|
+
def render_slide(view, component, state)
|
|
420
|
+
raise NotImplementedError, "#{self.class} must implement #render_slide"
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
# =========================================
|
|
424
|
+
# Explainer component rendering (T11)
|
|
425
|
+
# =========================================
|
|
426
|
+
|
|
427
|
+
# Render a sticky sidebar table-of-contents with scroll spy
|
|
428
|
+
def render_sidebar_toc(view, component, state)
|
|
429
|
+
raise NotImplementedError, "#{self.class} must implement #render_sidebar_toc"
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
# Render a non-dismissible callout box with colored left border
|
|
433
|
+
def render_callout(view, component, state)
|
|
434
|
+
raise NotImplementedError, "#{self.class} must implement #render_callout"
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
# Render side-by-side comparison panels
|
|
438
|
+
def render_comparison(view, component, state)
|
|
439
|
+
raise NotImplementedError, "#{self.class} must implement #render_comparison"
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
# Render a document-level header (eyebrow + serif title + meta pills)
|
|
443
|
+
def render_doc_header(view, component, state)
|
|
444
|
+
raise NotImplementedError, "#{self.class} must implement #render_doc_header"
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
# Render a numbered section eyebrow + h2 heading pair
|
|
448
|
+
def render_doc_section_header(view, component, state)
|
|
449
|
+
raise NotImplementedError, "#{self.class} must implement #render_doc_section_header"
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def render_implementation_map(view, component, state)
|
|
453
|
+
raise NotImplementedError, "#{self.class} must implement #render_implementation_map"
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def render_decision(view, component, state)
|
|
457
|
+
raise NotImplementedError, "#{self.class} must implement #render_decision"
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
def render_annotated_code(view, component, state)
|
|
461
|
+
raise NotImplementedError, "#{self.class} must implement #render_annotated_code"
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
# =========================================
|
|
465
|
+
# Design Deck component rendering (T7)
|
|
466
|
+
# =========================================
|
|
467
|
+
|
|
468
|
+
# Render a design deck (top-level orchestrator)
|
|
469
|
+
#
|
|
470
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
471
|
+
# @param component [Deck::DesignDeck] The deck component
|
|
472
|
+
# @param state [Hash] Current state hash
|
|
473
|
+
# @return [void] Renders to view
|
|
474
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
475
|
+
def render_design_deck(view, component, state)
|
|
476
|
+
raise NotImplementedError, "#{self.class} must implement #render_design_deck"
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
# Render a deck slide with options grid
|
|
480
|
+
#
|
|
481
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
482
|
+
# @param component [Deck::DeckSlide] The slide component
|
|
483
|
+
# @param state [Hash] Current state hash
|
|
484
|
+
# @return [void] Renders to view
|
|
485
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
486
|
+
def render_deck_slide(view, component, state)
|
|
487
|
+
raise NotImplementedError, "#{self.class} must implement #render_deck_slide"
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
# Render a deck option card
|
|
491
|
+
#
|
|
492
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
493
|
+
# @param component [Deck::DeckOption] The option component
|
|
494
|
+
# @param state [Hash] Current state hash
|
|
495
|
+
# @return [void] Renders to view
|
|
496
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
497
|
+
def render_deck_option(view, component, state)
|
|
498
|
+
raise NotImplementedError, "#{self.class} must implement #render_deck_option"
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
# Render the auto-generated deck summary slide
|
|
502
|
+
#
|
|
503
|
+
# @param view [Phlex::HTML] The Phlex view instance
|
|
504
|
+
# @param component [Deck::DeckSummary] The summary component
|
|
505
|
+
# @param state [Hash] Current state hash
|
|
506
|
+
# @return [void] Renders to view
|
|
507
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
508
|
+
def render_deck_summary(view, component, state)
|
|
509
|
+
raise NotImplementedError, "#{self.class} must implement #render_deck_summary"
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
# =========================================
|
|
513
|
+
# CSS-Only Helpers (T13)
|
|
514
|
+
# =========================================
|
|
515
|
+
|
|
516
|
+
# Render a hero section
|
|
517
|
+
def render_hero(view, component, state)
|
|
518
|
+
raise NotImplementedError, "#{self.class} must implement #render_hero"
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
# Render a prose container
|
|
522
|
+
def render_prose(view, component, state)
|
|
523
|
+
raise NotImplementedError, "#{self.class} must implement #render_prose"
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
# Render a pullquote
|
|
527
|
+
def render_pullquote(view, component, state)
|
|
528
|
+
raise NotImplementedError, "#{self.class} must implement #render_pullquote"
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
# Render a directory tree display
|
|
532
|
+
def render_dir_tree(view, component, state)
|
|
533
|
+
raise NotImplementedError, "#{self.class} must implement #render_dir_tree"
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
# Render a color legend
|
|
537
|
+
def render_legend(view, component, state)
|
|
538
|
+
raise NotImplementedError, "#{self.class} must implement #render_legend"
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
# Render a flow arrow connector
|
|
542
|
+
def render_flow_arrow(view, component, state)
|
|
543
|
+
raise NotImplementedError, "#{self.class} must implement #render_flow_arrow"
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
# Render layout toggle buttons
|
|
547
|
+
def render_layout_toggle(view, component, state)
|
|
548
|
+
raise NotImplementedError, "#{self.class} must implement #render_layout_toggle"
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
# Render a timeline event row with expandable details
|
|
552
|
+
def render_timeline_event(view, component, state)
|
|
553
|
+
raise NotImplementedError, "#{self.class} must implement #render_timeline_event"
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
# Render a wireframe surface block with raw HTML content
|
|
557
|
+
def render_wireframe_block(view, component, state)
|
|
558
|
+
raise NotImplementedError, "#{self.class} must implement #render_wireframe_block"
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
def render_wireframe(view, component, state)
|
|
562
|
+
raise NotImplementedError, "#{self.class} must implement #render_wireframe"
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
def render_diff_block(view, component, state)
|
|
566
|
+
raise NotImplementedError, "#{self.class} must implement #render_diff_block"
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
def render_api_endpoint(view, component, state)
|
|
570
|
+
raise NotImplementedError, "#{self.class} must implement #render_api_endpoint"
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
protected
|
|
574
|
+
|
|
575
|
+
# Convert spacing symbol to CSS value
|
|
576
|
+
#
|
|
577
|
+
# @param spacing [Symbol, String] Spacing value (:xs, :sm, :md, :lg, :xl or raw CSS)
|
|
578
|
+
# @return [String] CSS value
|
|
579
|
+
def spacing_to_css(spacing)
|
|
580
|
+
case spacing
|
|
581
|
+
when :xs then "var(--sw-spacing-xs)"
|
|
582
|
+
when :sm then "var(--sw-spacing-sm)"
|
|
583
|
+
when :md then "var(--sw-spacing-md)"
|
|
584
|
+
when :lg then "var(--sw-spacing-lg)"
|
|
585
|
+
when :xl then "var(--sw-spacing-xl)"
|
|
586
|
+
else spacing.to_s
|
|
587
|
+
end
|
|
588
|
+
end
|
|
589
|
+
end
|
|
590
|
+
end
|
|
591
|
+
end
|