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,741 @@
|
|
|
1
|
+
# streamweaver-doc: v1
|
|
2
|
+
#
|
|
3
|
+
# StreamWeaver University — course canvas mockup (story: course-canvas-design)
|
|
4
|
+
#
|
|
5
|
+
# Two screens of the app `streamweaver get-started` lands on:
|
|
6
|
+
# 1. Course list — a shelf of course panels. Getting Started is open and holds
|
|
7
|
+
# its own state (resume line, rail, 5 steps). Future courses
|
|
8
|
+
# are closed, dormant panels with no controls.
|
|
9
|
+
# 2. Step screen — one step: why it matters, the exact prompt, Run / Copy,
|
|
10
|
+
# payoff, done.
|
|
11
|
+
#
|
|
12
|
+
# Dual-mode single file (shared-DSL pattern):
|
|
13
|
+
# canvas : streamweaver canvas-push university-design < docs/university/mockups/course_canvas_mockup.rb
|
|
14
|
+
# standalone: ruby docs/university/mockups/course_canvas_mockup.rb
|
|
15
|
+
#
|
|
16
|
+
# v2 (revision after UAT): the resume state moved INSIDE the Getting Started
|
|
17
|
+
# panel, surfaces replaced hairlines, the type scale went up, and a three-colour
|
|
18
|
+
# state language (done / now / next) replaced the single blue. Buttons are
|
|
19
|
+
# `submit: false` (display-only) — this is a mockup, not the built app.
|
|
20
|
+
|
|
21
|
+
# ---------------------------------------------------------------------------
|
|
22
|
+
# One stylesheet. Targets sw- hooks (docs/theming-hooks.md) plus `uni-` classes
|
|
23
|
+
# for the handful of elements that have no component. Inlined as literal CSS
|
|
24
|
+
# rather than a sibling .css file because canvas-push has no route to serve a
|
|
25
|
+
# referenced asset across processes (see App#use_stylesheet, stream_weaver-9uk).
|
|
26
|
+
#
|
|
27
|
+
# Unlike v1, this sheet carries an explicit dark block. Riding only --sw-* meant
|
|
28
|
+
# every surface collapsed onto one flat paper tone; a control panel needs the
|
|
29
|
+
# page and the panel to be different grounds, and that is a two-value decision
|
|
30
|
+
# per theme. The dark block hangs off the same selector the :doc theme uses for
|
|
31
|
+
# its own dark palette, so the two flip on exactly the same signal.
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
_css = <<~CSS
|
|
34
|
+
body {
|
|
35
|
+
/* ---- Grounds: the page is the room, panels are the instruments ------- */
|
|
36
|
+
--uni-page: #E7E4D9;
|
|
37
|
+
--uni-panel: #FDFCF9;
|
|
38
|
+
--uni-bar: #F3F1E8;
|
|
39
|
+
--uni-sunk: #F1EFE6;
|
|
40
|
+
--uni-line: #D6D2C4;
|
|
41
|
+
--uni-line-soft: #E6E3D7;
|
|
42
|
+
|
|
43
|
+
/* ---- State language: one triad, used identically everywhere ---------- */
|
|
44
|
+
--uni-done: #17754A;
|
|
45
|
+
--uni-done-bg: #E2F0E8;
|
|
46
|
+
--uni-now: var(--sw-color-accent);
|
|
47
|
+
--uni-now-bg: #E5ECFC;
|
|
48
|
+
--uni-next: #6E6959;
|
|
49
|
+
/* Chip ground is separate from the rail track: the chip carries text and
|
|
50
|
+
must clear 4.5:1, the track is a container behind the filled segments. */
|
|
51
|
+
--uni-next-bg: #EFECE1;
|
|
52
|
+
--uni-rail-track: #D6D1BF;
|
|
53
|
+
|
|
54
|
+
--uni-ink: var(--sw-color-text);
|
|
55
|
+
/* Darker than --sw-color-text-muted (#6B6860), which is only 4.37:1 on the
|
|
56
|
+
page ground — the tagline and dormant blurbs sit there. */
|
|
57
|
+
--uni-muted: #5F5C54;
|
|
58
|
+
--uni-on-solid: #ffffff;
|
|
59
|
+
--uni-step: 130ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
html[data-sw-theme="dark"] body {
|
|
63
|
+
--uni-page: #131110;
|
|
64
|
+
--uni-panel: #221F1A;
|
|
65
|
+
--uni-bar: #2A2620;
|
|
66
|
+
--uni-sunk: #1B1815;
|
|
67
|
+
--uni-line: #383327;
|
|
68
|
+
--uni-line-soft: #2C2821;
|
|
69
|
+
|
|
70
|
+
--uni-done: #57C98A;
|
|
71
|
+
--uni-done-bg: #17301F;
|
|
72
|
+
--uni-now-bg: #1C2946;
|
|
73
|
+
--uni-next: #9A9285;
|
|
74
|
+
--uni-next-bg: #2C2821;
|
|
75
|
+
--uni-rail-track: #322D25;
|
|
76
|
+
--uni-muted: var(--sw-color-text-muted);
|
|
77
|
+
--uni-on-solid: #131110;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Chrome neutralization — the course canvas IS the page, not a card inside
|
|
81
|
+
one. Holds the same column whether the host set sw-layout-fluid (canvas)
|
|
82
|
+
or sw-layout-default (standalone). */
|
|
83
|
+
/* Bottom padding is a safe area, not slack: the canvas host floats its own
|
|
84
|
+
"Save as doc" control over the bottom-right corner, and at narrow widths it
|
|
85
|
+
otherwise lands on the last line of content. */
|
|
86
|
+
body[class*="sw-layout-"] {
|
|
87
|
+
max-width: 860px; margin: 0 auto;
|
|
88
|
+
padding: 26px 24px 120px;
|
|
89
|
+
background: var(--uni-page);
|
|
90
|
+
font-size: 16px;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* Visually hidden, still announced — the done mark replaces its number with a
|
|
94
|
+
glyph, so without this a screen reader gets nothing for a completed step. */
|
|
95
|
+
.uni-sr {
|
|
96
|
+
position: absolute; width: 1px; height: 1px;
|
|
97
|
+
margin: -1px; padding: 0; overflow: hidden;
|
|
98
|
+
clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
|
|
99
|
+
}
|
|
100
|
+
#app-container {
|
|
101
|
+
background: transparent; border: none; box-shadow: none;
|
|
102
|
+
padding: 0; margin: 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Browser surfaces the design still owns */
|
|
106
|
+
body ::selection {
|
|
107
|
+
background: color-mix(in srgb, var(--uni-now) 24%, transparent);
|
|
108
|
+
color: var(--uni-ink);
|
|
109
|
+
}
|
|
110
|
+
body *:focus-visible {
|
|
111
|
+
outline: 2px solid var(--uni-now);
|
|
112
|
+
outline-offset: 2px;
|
|
113
|
+
border-radius: 4px;
|
|
114
|
+
}
|
|
115
|
+
body { scrollbar-width: thin; scrollbar-color: var(--uni-line) transparent; }
|
|
116
|
+
body ::-webkit-scrollbar { width: 11px; height: 11px; }
|
|
117
|
+
body ::-webkit-scrollbar-thumb {
|
|
118
|
+
background: var(--uni-line); border-radius: 6px;
|
|
119
|
+
border: 3px solid var(--uni-page);
|
|
120
|
+
}
|
|
121
|
+
body ::-webkit-scrollbar-track { background: transparent; }
|
|
122
|
+
|
|
123
|
+
/* Drawn icon set — one family, masked so it inherits currentColor */
|
|
124
|
+
.uni-i {
|
|
125
|
+
display: inline-block; width: 1em; height: 1em;
|
|
126
|
+
background-color: currentColor; flex: none;
|
|
127
|
+
-webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
|
|
128
|
+
-webkit-mask-position: center; mask-position: center;
|
|
129
|
+
-webkit-mask-size: contain; mask-size: contain;
|
|
130
|
+
}
|
|
131
|
+
.uni-i--check {
|
|
132
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.2 8.4l3.1 3.1 6.5-7'/%3E%3C/svg%3E");
|
|
133
|
+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.2 8.4l3.1 3.1 6.5-7'/%3E%3C/svg%3E");
|
|
134
|
+
}
|
|
135
|
+
.uni-i--play {
|
|
136
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M5.1 3.3l7.4 4.35a.4.4 0 0 1 0 .7L5.1 12.7A.4.4 0 0 1 4.5 12.35V3.65a.4.4 0 0 1 .6-.35z' fill='%23000'/%3E%3C/svg%3E");
|
|
137
|
+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M5.1 3.3l7.4 4.35a.4.4 0 0 1 0 .7L5.1 12.7A.4.4 0 0 1 4.5 12.35V3.65a.4.4 0 0 1 .6-.35z' fill='%23000'/%3E%3C/svg%3E");
|
|
138
|
+
}
|
|
139
|
+
.uni-i--repeat {
|
|
140
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M13.1 8a5.1 5.1 0 1 1-1.68-3.78'/%3E%3Cpath d='M13.3 2.9v2.6h-2.6'/%3E%3C/svg%3E");
|
|
141
|
+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M13.1 8a5.1 5.1 0 1 1-1.68-3.78'/%3E%3Cpath d='M13.3 2.9v2.6h-2.6'/%3E%3C/svg%3E");
|
|
142
|
+
}
|
|
143
|
+
.uni-i--back {
|
|
144
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12.4 8H4'/%3E%3Cpath d='M7.6 4.4L4 8l3.6 3.6'/%3E%3C/svg%3E");
|
|
145
|
+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12.4 8H4'/%3E%3Cpath d='M7.6 4.4L4 8l3.6 3.6'/%3E%3C/svg%3E");
|
|
146
|
+
}
|
|
147
|
+
.uni-i--lock {
|
|
148
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.3' y='6.9' width='9.4' height='6.6' rx='1.4'/%3E%3Cpath d='M5.6 6.9V5.1a2.4 2.4 0 0 1 4.8 0v1.8'/%3E%3C/svg%3E");
|
|
149
|
+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.3' y='6.9' width='9.4' height='6.6' rx='1.4'/%3E%3Cpath d='M5.6 6.9V5.1a2.4 2.4 0 0 1 4.8 0v1.8'/%3E%3C/svg%3E");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* ---- App band: says what this is, in one glance ----------------------- */
|
|
153
|
+
body .sw-topbar {
|
|
154
|
+
border-bottom: none;
|
|
155
|
+
padding: 0; margin: 0 0 8px;
|
|
156
|
+
background: transparent;
|
|
157
|
+
}
|
|
158
|
+
body .sw-topbar-wordmark {
|
|
159
|
+
font-family: var(--sw-font-display);
|
|
160
|
+
font-size: 27px; font-weight: 600;
|
|
161
|
+
letter-spacing: -0.015em; text-transform: none;
|
|
162
|
+
color: var(--uni-ink);
|
|
163
|
+
}
|
|
164
|
+
body .sw-topbar-crumb,
|
|
165
|
+
body .sw-topbar-separator { font-size: 14px; color: var(--uni-muted); }
|
|
166
|
+
body .sw-topbar-crumb--active { color: var(--uni-ink); font-weight: 650; }
|
|
167
|
+
.uni-tagline {
|
|
168
|
+
display: block; max-width: 62ch;
|
|
169
|
+
font-size: 15px; line-height: 1.55; color: var(--uni-muted);
|
|
170
|
+
margin: 0 0 30px;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* theme_toggle ships an emoji glyph + a "System/Dark/Light" word. The panel
|
|
174
|
+
wants a quiet 30px control, so the label is dropped and the two x-show icon
|
|
175
|
+
spans are re-drawn from the same masked-SVG family as the rest of the page
|
|
176
|
+
(span 1 = dark/moon, span 2 = light/sun). */
|
|
177
|
+
body .sw-theme-toggle__label { display: none; }
|
|
178
|
+
body .sw-theme-toggle__btn {
|
|
179
|
+
background: var(--uni-panel); border: 1px solid var(--uni-line);
|
|
180
|
+
border-radius: 7px; padding: 7px; box-shadow: none;
|
|
181
|
+
color: var(--uni-muted); line-height: 0;
|
|
182
|
+
transition: border-color var(--uni-step), color var(--uni-step);
|
|
183
|
+
}
|
|
184
|
+
body .sw-theme-toggle__btn:hover { border-color: var(--uni-now); color: var(--uni-now); }
|
|
185
|
+
body .sw-theme-toggle__icon {
|
|
186
|
+
display: inline-block; width: 16px; height: 16px; font-size: 0;
|
|
187
|
+
background-color: currentColor;
|
|
188
|
+
-webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
|
|
189
|
+
-webkit-mask-position: center; mask-position: center;
|
|
190
|
+
-webkit-mask-size: contain; mask-size: contain;
|
|
191
|
+
}
|
|
192
|
+
body .sw-theme-toggle__icon:nth-of-type(1) {
|
|
193
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M13.4 9.6A5.8 5.8 0 0 1 6.4 2.6a5.8 5.8 0 1 0 7 7z'/%3E%3C/svg%3E");
|
|
194
|
+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M13.4 9.6A5.8 5.8 0 0 1 6.4 2.6a5.8 5.8 0 1 0 7 7z'/%3E%3C/svg%3E");
|
|
195
|
+
}
|
|
196
|
+
body .sw-theme-toggle__icon:nth-of-type(2) {
|
|
197
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='8' cy='8' r='3.1'/%3E%3Cpath d='M8 1.2v1.6M8 13.2v1.6M1.2 8h1.6M13.2 8h1.6M3.2 3.2l1.1 1.1M11.7 11.7l1.1 1.1M12.8 3.2l-1.1 1.1M4.3 11.7l-1.1 1.1'/%3E%3C/svg%3E");
|
|
198
|
+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='8' cy='8' r='3.1'/%3E%3Cpath d='M8 1.2v1.6M8 13.2v1.6M1.2 8h1.6M13.2 8h1.6M3.2 3.2l1.1 1.1M11.7 11.7l1.1 1.1M12.8 3.2l-1.1 1.1M4.3 11.7l-1.1 1.1'/%3E%3C/svg%3E");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* ---- Course panel ----------------------------------------------------- */
|
|
202
|
+
body .sw-card.uni-course {
|
|
203
|
+
background: var(--uni-panel);
|
|
204
|
+
border: 1px solid var(--uni-line);
|
|
205
|
+
border-left: 1px solid var(--uni-line);
|
|
206
|
+
border-radius: 12px;
|
|
207
|
+
padding: 0; margin: 0 0 14px;
|
|
208
|
+
box-shadow: 0 1px 2px rgba(24, 23, 20, 0.05), 0 6px 18px -10px rgba(24, 23, 20, 0.16);
|
|
209
|
+
overflow: hidden;
|
|
210
|
+
}
|
|
211
|
+
html[data-sw-theme="dark"] body .sw-card.uni-course {
|
|
212
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35), 0 8px 22px -12px rgba(0, 0, 0, 0.6);
|
|
213
|
+
}
|
|
214
|
+
body .sw-card.uni-course--dormant {
|
|
215
|
+
background: transparent;
|
|
216
|
+
border-color: var(--uni-line);
|
|
217
|
+
box-shadow: none;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* Title bar — the panel names itself at a size nobody scrolls past */
|
|
221
|
+
body .sw-card-header.uni-course__bar {
|
|
222
|
+
display: flex; align-items: center; gap: 12px;
|
|
223
|
+
background: var(--uni-bar);
|
|
224
|
+
border-bottom: 1px solid var(--uni-line);
|
|
225
|
+
padding: 16px 22px; margin: 0;
|
|
226
|
+
}
|
|
227
|
+
body .uni-course--dormant .sw-card-header.uni-course__bar {
|
|
228
|
+
background: transparent; border-bottom: none;
|
|
229
|
+
padding: 13px 20px 0;
|
|
230
|
+
}
|
|
231
|
+
body .sw-card.uni-course--dormant { margin-bottom: 10px; }
|
|
232
|
+
.uni-course__dot {
|
|
233
|
+
width: 9px; height: 9px; border-radius: 50%; flex: none;
|
|
234
|
+
background: var(--uni-now);
|
|
235
|
+
box-shadow: 0 0 0 3.5px color-mix(in srgb, var(--uni-now) 20%, transparent);
|
|
236
|
+
}
|
|
237
|
+
.uni-course--dormant .uni-course__dot {
|
|
238
|
+
background: transparent; border: 1.5px solid var(--uni-next);
|
|
239
|
+
box-shadow: none;
|
|
240
|
+
}
|
|
241
|
+
body.sw-theme-doc h2.uni-course__name {
|
|
242
|
+
font-family: var(--sw-font-body);
|
|
243
|
+
font-size: 20px; font-weight: 700; line-height: 1.2;
|
|
244
|
+
letter-spacing: -0.012em; color: var(--uni-ink);
|
|
245
|
+
margin: 0; padding: 0; border-bottom: none; flex: 1 1 auto;
|
|
246
|
+
}
|
|
247
|
+
body.sw-theme-doc .uni-course--dormant h2.uni-course__name {
|
|
248
|
+
font-size: 18px; font-weight: 650; color: var(--uni-muted);
|
|
249
|
+
}
|
|
250
|
+
.uni-chip {
|
|
251
|
+
font-size: 11.5px; font-weight: 700; letter-spacing: 0.07em;
|
|
252
|
+
text-transform: uppercase; padding: 5px 9px; border-radius: 5px;
|
|
253
|
+
flex: none; white-space: nowrap;
|
|
254
|
+
background: var(--uni-now-bg); color: var(--uni-now);
|
|
255
|
+
}
|
|
256
|
+
.uni-chip--soon {
|
|
257
|
+
display: inline-flex; align-items: center; gap: 5px;
|
|
258
|
+
background: var(--uni-next-bg); color: var(--uni-next);
|
|
259
|
+
}
|
|
260
|
+
.uni-chip--soon .uni-i { font-size: 12px; }
|
|
261
|
+
/* Aligned to the course name, not the card edge — the dot column is a gutter */
|
|
262
|
+
.uni-course__blurb {
|
|
263
|
+
display: block; font-size: 14.5px; line-height: 1.5;
|
|
264
|
+
color: var(--uni-muted); padding: 3px 20px 14px 41px;
|
|
265
|
+
max-width: 70ch;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
body .sw-card-body.uni-course__body { padding: 0; }
|
|
269
|
+
|
|
270
|
+
/* ---- Resume band: course state, inside the course --------------------- */
|
|
271
|
+
.uni-resume {
|
|
272
|
+
background: var(--uni-sunk);
|
|
273
|
+
border-bottom: 1px solid var(--uni-line);
|
|
274
|
+
padding: 22px 22px 20px;
|
|
275
|
+
}
|
|
276
|
+
.uni-resume__lead {
|
|
277
|
+
font-family: var(--sw-font-display);
|
|
278
|
+
font-size: clamp(1.35rem, 3.4vw, 1.55rem);
|
|
279
|
+
font-weight: 600; line-height: 1.16; letter-spacing: -0.016em;
|
|
280
|
+
text-wrap: balance; color: var(--uni-ink); margin: 0 0 5px;
|
|
281
|
+
}
|
|
282
|
+
.uni-resume__sub {
|
|
283
|
+
display: block; max-width: 58ch;
|
|
284
|
+
font-size: 15px; line-height: 1.5; color: var(--uni-muted);
|
|
285
|
+
margin-bottom: 18px;
|
|
286
|
+
}
|
|
287
|
+
.uni-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
|
|
288
|
+
.uni-resume .uni-actions { margin-bottom: 20px; }
|
|
289
|
+
|
|
290
|
+
/* ---- Buttons: three weights, one ladder ------------------------------- */
|
|
291
|
+
body .sw-button.uni-btn {
|
|
292
|
+
display: inline-flex; align-items: center; gap: 7px;
|
|
293
|
+
font-family: var(--sw-font-body);
|
|
294
|
+
font-size: 14px; font-weight: 650; line-height: 1;
|
|
295
|
+
padding: 10px 15px; border-radius: 7px;
|
|
296
|
+
border: 1px solid transparent; background: transparent;
|
|
297
|
+
box-shadow: none; text-transform: none;
|
|
298
|
+
color: var(--uni-ink); cursor: pointer;
|
|
299
|
+
transition: background var(--uni-step), color var(--uni-step),
|
|
300
|
+
border-color var(--uni-step), transform var(--uni-step);
|
|
301
|
+
}
|
|
302
|
+
body .sw-button.uni-btn .uni-i { font-size: 14px; }
|
|
303
|
+
body .sw-button.uni-btn--run {
|
|
304
|
+
background: var(--uni-now); color: var(--uni-on-solid);
|
|
305
|
+
padding: 12px 20px; font-size: 15px; font-weight: 650;
|
|
306
|
+
box-shadow: 0 1px 2px color-mix(in srgb, var(--uni-now) 30%, transparent);
|
|
307
|
+
}
|
|
308
|
+
body .sw-button.uni-btn--run:hover {
|
|
309
|
+
background: var(--sw-color-primary-hover); transform: translateY(-1px);
|
|
310
|
+
}
|
|
311
|
+
body .sw-button.uni-btn--outline {
|
|
312
|
+
border-color: var(--uni-line); background: var(--uni-panel);
|
|
313
|
+
color: var(--uni-ink);
|
|
314
|
+
}
|
|
315
|
+
body .sw-button.uni-btn--outline:hover { border-color: var(--uni-now); color: var(--uni-now); }
|
|
316
|
+
body .sw-button.uni-btn--quiet {
|
|
317
|
+
color: var(--uni-muted); font-weight: 600;
|
|
318
|
+
padding: 9px 12px; font-size: 13.5px;
|
|
319
|
+
}
|
|
320
|
+
body .sw-button.uni-btn--quiet:hover { color: var(--uni-now); background: var(--uni-now-bg); }
|
|
321
|
+
|
|
322
|
+
/* ---- Progress rail: position in a sequence, not a percentage ---------- */
|
|
323
|
+
.uni-rail { display: flex; align-items: center; gap: 13px; }
|
|
324
|
+
.uni-rail__track { display: flex; align-items: center; gap: 5px; }
|
|
325
|
+
.uni-rail__seg { width: 30px; height: 6px; border-radius: 3px; background: var(--uni-rail-track); }
|
|
326
|
+
.uni-rail__seg--done { background: var(--uni-done); }
|
|
327
|
+
.uni-rail__seg--current { background: var(--uni-now); width: 40px; }
|
|
328
|
+
.uni-rail__label {
|
|
329
|
+
font-size: 13px; font-weight: 600; color: var(--uni-muted);
|
|
330
|
+
font-variant-numeric: tabular-nums;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* ---- Step rows -------------------------------------------------------- */
|
|
334
|
+
.uni-step {
|
|
335
|
+
display: grid;
|
|
336
|
+
grid-template-columns: 28px 1fr auto;
|
|
337
|
+
align-items: center;
|
|
338
|
+
gap: 0 15px;
|
|
339
|
+
padding: 15px 22px;
|
|
340
|
+
border-bottom: 1px solid var(--uni-line-soft);
|
|
341
|
+
transition: background var(--uni-step);
|
|
342
|
+
}
|
|
343
|
+
.uni-step:last-child { border-bottom: none; }
|
|
344
|
+
.uni-step__mark {
|
|
345
|
+
width: 28px; height: 28px; border-radius: 50%;
|
|
346
|
+
display: flex; align-items: center; justify-content: center;
|
|
347
|
+
font-size: 13.5px; font-weight: 700;
|
|
348
|
+
font-variant-numeric: tabular-nums;
|
|
349
|
+
}
|
|
350
|
+
.uni-step--done .uni-step__mark {
|
|
351
|
+
background: var(--uni-done); color: var(--uni-on-solid);
|
|
352
|
+
}
|
|
353
|
+
.uni-step--done .uni-step__mark .uni-i { font-size: 16px; }
|
|
354
|
+
.uni-step--current .uni-step__mark {
|
|
355
|
+
background: var(--uni-now); color: var(--uni-on-solid);
|
|
356
|
+
box-shadow: 0 0 0 4px color-mix(in srgb, var(--uni-now) 18%, transparent);
|
|
357
|
+
}
|
|
358
|
+
/* Solid ring, not a tint: at 50% the boundary fell under 3:1 */
|
|
359
|
+
.uni-step--todo .uni-step__mark {
|
|
360
|
+
background: transparent; color: var(--uni-next);
|
|
361
|
+
border: 1.5px solid var(--uni-next);
|
|
362
|
+
}
|
|
363
|
+
/* h3, not h2: a step is nested under its course's h2 name */
|
|
364
|
+
body.sw-theme-doc h3.uni-step__title {
|
|
365
|
+
font-family: var(--sw-font-body);
|
|
366
|
+
font-size: 17px; font-weight: 650; line-height: 1.28;
|
|
367
|
+
letter-spacing: -0.008em; color: var(--uni-ink);
|
|
368
|
+
margin: 0 0 3px; padding: 0; border-bottom: none;
|
|
369
|
+
}
|
|
370
|
+
.uni-step__payoff {
|
|
371
|
+
display: block; font-size: 14.5px; line-height: 1.45;
|
|
372
|
+
color: var(--uni-muted); max-width: 54ch;
|
|
373
|
+
}
|
|
374
|
+
.uni-step--current { background: var(--uni-now-bg); }
|
|
375
|
+
body.sw-theme-doc .uni-step--done h3.uni-step__title {
|
|
376
|
+
color: var(--uni-muted); font-weight: 600;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/* ---- Shelf divider ---------------------------------------------------- */
|
|
380
|
+
.uni-divider {
|
|
381
|
+
display: flex; align-items: center; gap: 14px;
|
|
382
|
+
margin: 38px 0 16px;
|
|
383
|
+
}
|
|
384
|
+
.uni-divider__label {
|
|
385
|
+
font-size: 15px; font-weight: 650; color: var(--uni-ink);
|
|
386
|
+
letter-spacing: -0.005em; flex: none;
|
|
387
|
+
}
|
|
388
|
+
.uni-divider__rule { height: 1px; background: var(--uni-line); flex: 1 1 auto; }
|
|
389
|
+
|
|
390
|
+
/* ---- Tutorial pointer ------------------------------------------------- */
|
|
391
|
+
.uni-note {
|
|
392
|
+
margin-top: 30px; padding: 16px 20px;
|
|
393
|
+
background: var(--uni-sunk);
|
|
394
|
+
border: 1px solid var(--uni-line-soft);
|
|
395
|
+
border-radius: 10px;
|
|
396
|
+
font-size: 14.5px; line-height: 1.6; color: var(--uni-muted);
|
|
397
|
+
max-width: 74ch;
|
|
398
|
+
}
|
|
399
|
+
.uni-note code {
|
|
400
|
+
font-family: var(--sw-font-mono); font-size: 13.5px;
|
|
401
|
+
background: var(--uni-panel); border: 1px solid var(--uni-line);
|
|
402
|
+
border-radius: 4px; padding: 2px 6px; color: var(--uni-ink);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/* ---- Step screen ------------------------------------------------------ */
|
|
406
|
+
.uni-context {
|
|
407
|
+
display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
|
|
408
|
+
margin-bottom: 16px;
|
|
409
|
+
}
|
|
410
|
+
.uni-back {
|
|
411
|
+
display: inline-flex; align-items: center; gap: 7px;
|
|
412
|
+
font-size: 14px; font-weight: 600; color: var(--uni-muted);
|
|
413
|
+
background: var(--uni-panel); border: 1px solid var(--uni-line);
|
|
414
|
+
border-radius: 7px; padding: 8px 13px; text-decoration: none;
|
|
415
|
+
transition: color var(--uni-step), border-color var(--uni-step);
|
|
416
|
+
}
|
|
417
|
+
.uni-back:hover { color: var(--uni-now); border-color: var(--uni-now); }
|
|
418
|
+
.uni-context__spacer { flex: 1 1 auto; }
|
|
419
|
+
.uni-count {
|
|
420
|
+
font-size: 13px; font-weight: 600; color: var(--uni-muted);
|
|
421
|
+
font-variant-numeric: tabular-nums;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
body .sw-card-header.uni-step__bar { gap: 14px; align-items: center; }
|
|
425
|
+
.uni-step__mark--hero {
|
|
426
|
+
background: var(--uni-now); color: var(--uni-on-solid);
|
|
427
|
+
width: 34px; height: 34px; font-size: 15px;
|
|
428
|
+
box-shadow: 0 0 0 4px color-mix(in srgb, var(--uni-now) 18%, transparent);
|
|
429
|
+
}
|
|
430
|
+
body.sw-theme-doc h1.uni-title {
|
|
431
|
+
font-family: var(--sw-font-display);
|
|
432
|
+
font-size: clamp(1.6rem, 4vw, 1.95rem);
|
|
433
|
+
font-weight: 600; line-height: 1.15; letter-spacing: -0.018em;
|
|
434
|
+
text-wrap: balance; color: var(--uni-ink);
|
|
435
|
+
margin: 0; padding: 0; border-bottom: none; flex: 1 1 auto;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.uni-section { padding: 0 22px; }
|
|
439
|
+
.uni-section:first-child { padding-top: 24px; }
|
|
440
|
+
.uni-section:last-of-type { padding-bottom: 8px; }
|
|
441
|
+
/* Section rhythm: a label owns the space above it, not below it */
|
|
442
|
+
.uni-label {
|
|
443
|
+
display: block;
|
|
444
|
+
font-size: 15px; font-weight: 650; color: var(--uni-ink);
|
|
445
|
+
letter-spacing: -0.005em; margin: 32px 0 11px;
|
|
446
|
+
}
|
|
447
|
+
.uni-section:first-child > .uni-label { margin-top: 0; }
|
|
448
|
+
.uni-prose { max-width: 66ch; font-size: 16px; line-height: 1.6; color: var(--uni-ink); }
|
|
449
|
+
.uni-prose p { margin: 0 0 0.8em; }
|
|
450
|
+
.uni-prose p:last-child { margin-bottom: 0; }
|
|
451
|
+
|
|
452
|
+
/* The prompt is a payload, so it gets a payload's chrome: labelled bar,
|
|
453
|
+
sunk ground, monospace only where it is doing verbatim work. */
|
|
454
|
+
.uni-promptbox {
|
|
455
|
+
border: 1px solid var(--uni-line); border-radius: 9px;
|
|
456
|
+
background: var(--uni-sunk); overflow: hidden; margin: 30px 0 18px;
|
|
457
|
+
}
|
|
458
|
+
.uni-promptbox__bar {
|
|
459
|
+
display: flex; align-items: center; justify-content: space-between; gap: 12px;
|
|
460
|
+
background: var(--uni-bar); border-bottom: 1px solid var(--uni-line);
|
|
461
|
+
padding: 9px 15px;
|
|
462
|
+
font-size: 12px; font-weight: 700; letter-spacing: 0.06em;
|
|
463
|
+
text-transform: uppercase; color: var(--uni-muted);
|
|
464
|
+
}
|
|
465
|
+
.uni-prompt {
|
|
466
|
+
display: block;
|
|
467
|
+
font-family: var(--sw-font-mono);
|
|
468
|
+
font-size: 13px; line-height: 1.65;
|
|
469
|
+
white-space: pre-wrap; color: var(--uni-ink);
|
|
470
|
+
padding: 16px 17px; overflow-x: auto;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.uni-payoff ul { list-style: none; padding: 0; margin: 0; max-width: 64ch; }
|
|
474
|
+
.uni-payoff li {
|
|
475
|
+
display: grid; grid-template-columns: 20px 1fr; gap: 11px;
|
|
476
|
+
font-size: 15px; line-height: 1.5; color: var(--uni-ink);
|
|
477
|
+
padding: 7px 0;
|
|
478
|
+
}
|
|
479
|
+
.uni-payoff li::before {
|
|
480
|
+
content: ""; margin-top: 4px;
|
|
481
|
+
width: 15px; height: 15px;
|
|
482
|
+
background-color: var(--uni-done);
|
|
483
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.2 8.4l3.1 3.1 6.5-7'/%3E%3C/svg%3E");
|
|
484
|
+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.2 8.4l3.1 3.1 6.5-7'/%3E%3C/svg%3E");
|
|
485
|
+
-webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
|
|
486
|
+
-webkit-mask-size: contain; mask-size: contain;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.uni-foot {
|
|
490
|
+
display: flex; align-items: center; gap: 14px;
|
|
491
|
+
background: var(--uni-sunk); border-top: 1px solid var(--uni-line);
|
|
492
|
+
padding: 16px 22px; margin-top: 22px;
|
|
493
|
+
}
|
|
494
|
+
.uni-foot__hint { font-size: 14px; color: var(--uni-muted); }
|
|
495
|
+
.uni-foot__spacer { flex: 1 1 auto; }
|
|
496
|
+
@media (max-width: 620px) {
|
|
497
|
+
.uni-foot { flex-wrap: wrap; }
|
|
498
|
+
.uni-foot__spacer { display: none; }
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/* ---- Review chrome: the two screens live behind tabs ------------------ */
|
|
502
|
+
body .sw-tabs-list, body .sw-tab-list { margin-bottom: 28px; }
|
|
503
|
+
|
|
504
|
+
@media (max-width: 700px) {
|
|
505
|
+
body[class*="sw-layout-"] { padding-bottom: 150px; }
|
|
506
|
+
}
|
|
507
|
+
@media (max-width: 620px) {
|
|
508
|
+
.uni-step { grid-template-columns: 28px 1fr; row-gap: 10px; }
|
|
509
|
+
.uni-step > .sw-button { grid-column: 2; justify-self: start; }
|
|
510
|
+
.uni-resume__lead { font-size: 1.3rem; }
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
@media (prefers-reduced-motion: reduce) {
|
|
514
|
+
body * { transition-duration: 0.01ms !important; }
|
|
515
|
+
}
|
|
516
|
+
CSS
|
|
517
|
+
|
|
518
|
+
# The literal payload the canvas sends to the worker session. Shown verbatim on
|
|
519
|
+
# the step screen — no hidden prompt, no paraphrase.
|
|
520
|
+
_step3_prompt = <<~PROMPT.strip
|
|
521
|
+
Using stream_weaver, write a small app with a radio_group and a button
|
|
522
|
+
that stores the choice in state and keeps rendering after each click.
|
|
523
|
+
Run it and click through it.
|
|
524
|
+
|
|
525
|
+
Then show me the same form driven by `canvas-wait`, so the script blocks
|
|
526
|
+
until I answer and prints my answer as JSON in the terminal.
|
|
527
|
+
PROMPT
|
|
528
|
+
|
|
529
|
+
_body = proc do
|
|
530
|
+
use_theme :doc
|
|
531
|
+
use_layout :default
|
|
532
|
+
use_stylesheet _css
|
|
533
|
+
|
|
534
|
+
tabs :screen, variant: :line do
|
|
535
|
+
# =====================================================================
|
|
536
|
+
# Screen 1 — Course list. Every course is a panel; course state lives
|
|
537
|
+
# inside its own panel, never above the shelf.
|
|
538
|
+
# =====================================================================
|
|
539
|
+
tab "Course list" do
|
|
540
|
+
topbar(wordmark: "StreamWeaver University") do
|
|
541
|
+
theme_toggle mode: :auto
|
|
542
|
+
end
|
|
543
|
+
phrase "Learn StreamWeaver by running it. Every step is a real app that appears " \
|
|
544
|
+
"in this pane while you stay in your terminal.",
|
|
545
|
+
class: "uni-tagline"
|
|
546
|
+
|
|
547
|
+
# ---- Getting Started: open, and holding its own state --------------
|
|
548
|
+
card(depth: :elevated, class: "uni-course") do
|
|
549
|
+
card_header(class: "uni-course__bar") do
|
|
550
|
+
div(class: "uni-course__dot") {}
|
|
551
|
+
header2 "Getting Started", class: "uni-course__name"
|
|
552
|
+
phrase "In progress", class: "uni-chip"
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
card_body(class: "uni-course__body") do
|
|
556
|
+
div(class: "uni-resume") do
|
|
557
|
+
phrase "Pick up at step 3.", class: "uni-resume__lead"
|
|
558
|
+
phrase "One form, two modes — the same six lines of DSL, running live and blocking.",
|
|
559
|
+
class: "uni-resume__sub"
|
|
560
|
+
|
|
561
|
+
div(class: "uni-actions") do
|
|
562
|
+
button "Run step 3", submit: false, key: "hero-run", class: "uni-btn uni-btn--run"
|
|
563
|
+
button "Repeat step 2", submit: false, key: "hero-repeat", class: "uni-btn uni-btn--quiet"
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
div(class: "uni-rail") do
|
|
567
|
+
div(class: "uni-rail__track") do
|
|
568
|
+
div(class: "uni-rail__seg uni-rail__seg--done") {}
|
|
569
|
+
div(class: "uni-rail__seg uni-rail__seg--done") {}
|
|
570
|
+
div(class: "uni-rail__seg uni-rail__seg--current") {}
|
|
571
|
+
div(class: "uni-rail__seg") {}
|
|
572
|
+
div(class: "uni-rail__seg") {}
|
|
573
|
+
end
|
|
574
|
+
phrase "2 of 5 done", class: "uni-rail__label"
|
|
575
|
+
end
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
steps = [
|
|
579
|
+
["1", "A card in your pane",
|
|
580
|
+
"Push a card to the canvas beside your terminal without writing any HTML.", :done],
|
|
581
|
+
["2", "Six lines of Ruby",
|
|
582
|
+
"Run a real app and watch the block re-run on every click.", :done],
|
|
583
|
+
["3", "One form, two modes",
|
|
584
|
+
"The same form stays live, or blocks and waits for one answer.", :current],
|
|
585
|
+
["4", "A doc that writes itself",
|
|
586
|
+
"Watch a script append sections, then save the result as a document.", :todo],
|
|
587
|
+
["5", "Take the doc with you",
|
|
588
|
+
"Export to org, drop it in a gist, read it anywhere.", :todo]
|
|
589
|
+
]
|
|
590
|
+
|
|
591
|
+
steps.each do |num, title, payoff, state|
|
|
592
|
+
div(class: "uni-step uni-step--#{state}") do
|
|
593
|
+
div(class: "uni-step__mark") do
|
|
594
|
+
if state == :done
|
|
595
|
+
phrase "", class: "uni-i uni-i--check"
|
|
596
|
+
phrase "Step #{num}, done", class: "uni-sr"
|
|
597
|
+
else
|
|
598
|
+
phrase num
|
|
599
|
+
phrase(state == :current ? ", current step" : ", not started", class: "uni-sr")
|
|
600
|
+
end
|
|
601
|
+
end
|
|
602
|
+
div do
|
|
603
|
+
header3 title, class: "uni-step__title"
|
|
604
|
+
phrase payoff, class: "uni-step__payoff"
|
|
605
|
+
end
|
|
606
|
+
case state
|
|
607
|
+
when :done
|
|
608
|
+
button "Repeat", submit: false, key: "rep-#{num}", class: "uni-btn uni-btn--quiet"
|
|
609
|
+
when :current
|
|
610
|
+
button "Run", submit: false, key: "run-#{num}", class: "uni-btn uni-btn--outline"
|
|
611
|
+
else
|
|
612
|
+
button "Run", submit: false, key: "run-#{num}", class: "uni-btn uni-btn--quiet"
|
|
613
|
+
end
|
|
614
|
+
end
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
# ---- The shelf: closed, dormant, no controls -----------------------
|
|
620
|
+
div(class: "uni-divider") do
|
|
621
|
+
phrase "In the works", class: "uni-divider__label"
|
|
622
|
+
div(class: "uni-divider__rule") {}
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
[
|
|
626
|
+
["Docs deep dive", "Org export, gists, and the reader extension, end to end."],
|
|
627
|
+
["Canvas modes", "Stateful, blocking, and streaming — when to reach for each."],
|
|
628
|
+
["Skills and panels", "Teach your agent to drive the canvas without you."]
|
|
629
|
+
].each do |name, blurb|
|
|
630
|
+
card(depth: :recessed, class: "uni-course uni-course--dormant") do
|
|
631
|
+
card_header(class: "uni-course__bar") do
|
|
632
|
+
div(class: "uni-course__dot") {}
|
|
633
|
+
header2 name, class: "uni-course__name"
|
|
634
|
+
div(class: "uni-chip uni-chip--soon") do
|
|
635
|
+
phrase "", class: "uni-i uni-i--lock"
|
|
636
|
+
phrase "Soon"
|
|
637
|
+
end
|
|
638
|
+
end
|
|
639
|
+
phrase blurb, class: "uni-course__blurb"
|
|
640
|
+
end
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
md "Looking for the old component tour? Run `streamweaver tutorial` — the classic " \
|
|
644
|
+
"walkthrough of every component. Older than these courses, and being refreshed.",
|
|
645
|
+
class: "uni-note"
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
# =====================================================================
|
|
649
|
+
# Screen 2 — Step screen
|
|
650
|
+
# =====================================================================
|
|
651
|
+
tab "Step screen" do
|
|
652
|
+
topbar(wordmark: "StreamWeaver University", breadcrumbs: ["Getting Started", "Step 3"]) do
|
|
653
|
+
theme_toggle mode: :auto
|
|
654
|
+
end
|
|
655
|
+
phrase "Learn StreamWeaver by running it. Every step is a real app that appears " \
|
|
656
|
+
"in this pane while you stay in your terminal.",
|
|
657
|
+
class: "uni-tagline"
|
|
658
|
+
|
|
659
|
+
div(class: "uni-context") do
|
|
660
|
+
div(class: "uni-back") do
|
|
661
|
+
phrase "", class: "uni-i uni-i--back"
|
|
662
|
+
phrase "All steps"
|
|
663
|
+
end
|
|
664
|
+
div(class: "uni-rail") do
|
|
665
|
+
div(class: "uni-rail__track") do
|
|
666
|
+
div(class: "uni-rail__seg uni-rail__seg--done") {}
|
|
667
|
+
div(class: "uni-rail__seg uni-rail__seg--done") {}
|
|
668
|
+
div(class: "uni-rail__seg uni-rail__seg--current") {}
|
|
669
|
+
div(class: "uni-rail__seg") {}
|
|
670
|
+
div(class: "uni-rail__seg") {}
|
|
671
|
+
end
|
|
672
|
+
phrase "2 of 5 done", class: "uni-rail__label"
|
|
673
|
+
end
|
|
674
|
+
div(class: "uni-context__spacer") {}
|
|
675
|
+
phrase "Step 3 of 5", class: "uni-count"
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
card(depth: :elevated, class: "uni-course") do
|
|
679
|
+
card_header(class: "uni-course__bar uni-step__bar") do
|
|
680
|
+
div(class: "uni-step__mark uni-step__mark--hero") { phrase "3" }
|
|
681
|
+
header1 "One form, two modes", class: "uni-title"
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
card_body(class: "uni-course__body") do
|
|
685
|
+
div(class: "uni-section") do
|
|
686
|
+
phrase "Why this matters", class: "uni-label"
|
|
687
|
+
md "Terminal tools make you pick one: a form that stays live, or a prompt that " \
|
|
688
|
+
"blocks until you answer. StreamWeaver runs the same form both ways.\n\n" \
|
|
689
|
+
"You will write it once, change the call that consumes it, and watch the " \
|
|
690
|
+
"behaviour flip. That is the thing a TUI cannot do.",
|
|
691
|
+
class: "uni-prose"
|
|
692
|
+
end
|
|
693
|
+
|
|
694
|
+
div(class: "uni-section") do
|
|
695
|
+
div(class: "uni-promptbox") do
|
|
696
|
+
div(class: "uni-promptbox__bar") do
|
|
697
|
+
phrase "The prompt your worker session receives"
|
|
698
|
+
end
|
|
699
|
+
phrase _step3_prompt, class: "uni-prompt"
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
div(class: "uni-actions") do
|
|
703
|
+
button "Run in worker session", submit: false, key: "step-run",
|
|
704
|
+
class: "uni-btn uni-btn--run"
|
|
705
|
+
copy_button "Copy prompt", text: _step3_prompt,
|
|
706
|
+
copied_label: "Copied", class: "uni-btn uni-btn--outline"
|
|
707
|
+
end
|
|
708
|
+
end
|
|
709
|
+
|
|
710
|
+
div(class: "uni-section") do
|
|
711
|
+
phrase "What you should see", class: "uni-label"
|
|
712
|
+
md "- Click a different option and the page re-renders in place. Nothing blocks.\n" \
|
|
713
|
+
"- The second version freezes your terminal until you click, then prints your answer as JSON.\n" \
|
|
714
|
+
"- Same six lines of DSL. Only the call that consumes the form changed.",
|
|
715
|
+
class: "uni-payoff"
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
# Two exits, deliberately unequal: marking done is the one that writes
|
|
719
|
+
# the ledger, so it keeps the outlined weight; moving on without
|
|
720
|
+
# finishing is allowed but quiet.
|
|
721
|
+
div(class: "uni-foot") do
|
|
722
|
+
button "Mark step 3 done", submit: false, key: "step-done",
|
|
723
|
+
class: "uni-btn uni-btn--outline"
|
|
724
|
+
phrase "Unlocks step 4.", class: "uni-foot__hint"
|
|
725
|
+
div(class: "uni-foot__spacer") {}
|
|
726
|
+
button "Next: step 4", submit: false, key: "step-next",
|
|
727
|
+
class: "uni-btn uni-btn--quiet"
|
|
728
|
+
end
|
|
729
|
+
end
|
|
730
|
+
end
|
|
731
|
+
end
|
|
732
|
+
end
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
if respond_to?(:use_theme)
|
|
736
|
+
# canvas-push / canvas-read: self is already a StreamWeaver::App
|
|
737
|
+
instance_eval(&_body)
|
|
738
|
+
elsif __FILE__ == $PROGRAM_NAME
|
|
739
|
+
require_relative "../../../lib/stream_weaver"
|
|
740
|
+
app("StreamWeaver University") { instance_eval(&_body) }.run!
|
|
741
|
+
end
|