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,137 @@
|
|
|
1
|
+
# StreamWeaver Capability Inventory
|
|
2
|
+
|
|
3
|
+
Read-only inventory of what StreamWeaver (the Ruby gem at the root of this repo) can do, compiled to design a "getting started" tutorial for a newcomer. Sourced from `README.md`, `docs/for_llms.md`, `exe/streamweaver`, `lib/stream_weaver/cli.rb`, `docs/*.md`, `lib/stream_weaver/skills/*/SKILL.md`, `.claude/skills/`, and `examples/`.
|
|
4
|
+
|
|
5
|
+
## 1. CLI Subcommands
|
|
6
|
+
|
|
7
|
+
All subcommands dispatch through `StreamWeaver::CLI.run` in `lib/stream_weaver/cli.rb`. `streamweaver <file.rb>` (bare path) is shorthand for `run`.
|
|
8
|
+
|
|
9
|
+
| Command | Purpose | Tutorial-worthy? |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| `streamweaver <file.rb>` / `run` | Run an app file (standalone, auto-port, auto-browser) | Y — the entry point everyone needs first |
|
|
12
|
+
| `ruby app.rb` | Same DSL, but you own the process (no service) | Y — the "vs `streamweaver app.rb`" distinction is a common first confusion |
|
|
13
|
+
| `eval` | Evaluate DSL from stdin, print result as JSON | N for a first pass — power-user/scripting path |
|
|
14
|
+
| `prompt` | One-shot flag-driven UI (`--radio`, `--select`, etc.), returns JSON | N — niche flag-based alternative to writing DSL |
|
|
15
|
+
| `list` | List all apps loaded into the background service | N — housekeeping |
|
|
16
|
+
| `remove <app_id>` | Remove one app from the service | N — housekeeping |
|
|
17
|
+
| `clear` | Remove all apps from the service | N — housekeeping |
|
|
18
|
+
| `admin` | Open the admin dashboard for the running service | N — operational, not a first-day need |
|
|
19
|
+
| `tutorial` | Launches the built-in interactive tutorial app (`examples/advanced/tutorial.rb`) | Y — literally the guided onboarding path, mention immediately |
|
|
20
|
+
| `showcase` | Browse all bundled examples in a live examples browser | Y — best "see what's possible" discovery step |
|
|
21
|
+
| `serve` | Start the background service in the foreground (dev mode) | N — internals |
|
|
22
|
+
| `stop` | Stop the background service | N — housekeeping, but worth one line |
|
|
23
|
+
| `status` | Show service status (port, pid) | N — troubleshooting reference |
|
|
24
|
+
| `llm` | Print `docs/for_llms.md` to stdout (LLM quick reference) | Y — the one-liner every AI-agent user needs on day one |
|
|
25
|
+
| `opal-build <app.rb>` | Compile an app to a static Opal (client-side Ruby→JS) bundle in `dist/` | N — advanced/deployment topic |
|
|
26
|
+
| `live <name>` | Open a persistent one-way live session (SSE push, no round-trip IPC) | N — superseded by `canvas` for most agent use, but worth a mention as the simpler ancestor |
|
|
27
|
+
| `push <name>` | Push content into a `live` session | N — pairs with `live` |
|
|
28
|
+
| `live-list` / `live-close <name>` | List / close live sessions | N — housekeeping |
|
|
29
|
+
| `template <name>` | Run one of the bundled templates | N — not explored in depth this pass; see Gaps |
|
|
30
|
+
| `canvas <name>` | Create/connect to a two-way canvas session (opens browser) | Y — the flagship "Claude Code talks to a UI" capability |
|
|
31
|
+
| `canvas-push <name>` | Push DSL content (from stdin) into a canvas session | Y — paired with `canvas`, this is the core loop |
|
|
32
|
+
| `canvas-wait <name>` | Block until the user interacts, return form state as JSON | Y — completes the two-way loop |
|
|
33
|
+
| `canvas-toast <name> <msg>` | Show a toast overlay without replacing page content | N — nice-to-know, not core |
|
|
34
|
+
| `canvas-close <name>` | Close a canvas session | N — one line in passing |
|
|
35
|
+
| `canvas-reset <name>` / `--all` | Reset session state, keep the connection/pane open | N — troubleshooting reference |
|
|
36
|
+
| `canvas-list` | List active canvas sessions | N — housekeeping |
|
|
37
|
+
| `canvas-stop` | Stop the canvas bridge server entirely | N — troubleshooting reference |
|
|
38
|
+
| `canvas-read <file\|dir>` | Browse canvas DSL docs (and history) in a read-only local viewer, no app session needed | Y — how you view a doc after the fact / share it |
|
|
39
|
+
| `export <file.rb>` | Write a canvas DSL doc out as standalone static HTML | Y — the "hand this to someone with no StreamWeaver installed" capability |
|
|
40
|
+
| `org-export <file.rb>` | Convert a saved DSL doc to a human-readable `.org` sibling file | N for first tutorial — but core to the org-mode story, see section 3 |
|
|
41
|
+
| `org-render <file.org>` | Convert an `.org` doc back into DSL body text (stdout) | N for first tutorial — round-trip companion to `org-export` |
|
|
42
|
+
| `pick` | High-level canvas helper wrapping a common choice pattern | N — not explored deeply this pass |
|
|
43
|
+
| `confirm` | High-level canvas helper wrapping a common confirm-dialog pattern | N — not explored deeply this pass |
|
|
44
|
+
| `panel [name]` | Split the current iTerm2 pane and open a canvas session in the new pane | Y — the single best "wow" demo for a terminal-based coworker |
|
|
45
|
+
| `install-skill [--global]` | Install StreamWeaver's Claude Code / cross-tool skills | Y — a one-time setup step worth doing in the first session |
|
|
46
|
+
| `setup` | One-command setup: bash permissions + globally install the panel skill for Claude Code | Y — alternative/superset of `install-skill`, good "step 0" |
|
|
47
|
+
| `--help` / `-h` / `help` | Show usage | N |
|
|
48
|
+
| `--version` / `-v` | Show gem version | N |
|
|
49
|
+
|
|
50
|
+
## 2. Canvas Capabilities
|
|
51
|
+
|
|
52
|
+
The canvas bridge is a persistent background server (separate from the app service) that gives Claude Code a two-way, addressable, named UI surface.
|
|
53
|
+
|
|
54
|
+
| Capability | Exact command |
|
|
55
|
+
|---|---|
|
|
56
|
+
| Create/open a session | `streamweaver canvas <name>` |
|
|
57
|
+
| Push DSL content into it | `streamweaver canvas-push <name> <<'RUBY' ... RUBY` (stdin) |
|
|
58
|
+
| Block for user interaction, get JSON back | `streamweaver canvas-wait <name>` |
|
|
59
|
+
| Multi-step flow without a final "close" message | end a pushed page with `canvas_continue message: "..."` DSL call instead of nothing |
|
|
60
|
+
| Show a non-destructive toast | `streamweaver canvas-toast <name> "message"` |
|
|
61
|
+
| Reset session state (keep the pane/connection) | `streamweaver canvas-reset <name>` (or `--all` for every session) |
|
|
62
|
+
| Close a session (and its iTerm pane, if any) | `streamweaver canvas-close <name>` |
|
|
63
|
+
| List all sessions | `streamweaver canvas-list` |
|
|
64
|
+
| Stop the whole bridge | `streamweaver canvas-stop` |
|
|
65
|
+
| Browse a saved/pushed DSL doc read-only (no live app) | `streamweaver canvas-read <file\|dir> [--theme=NAME] [--layout=NAME]` |
|
|
66
|
+
| Read history of everything pushed to a session | Tier-1 (off-repo) snapshots at `~/.streamweaver/history/<session>/<timestamp>.rb`, written automatically on every `canvas-push`, pruned after 7 days (`lib/stream_weaver/canvas/history.rb`). `canvas-read` with no args discovers these automatically. |
|
|
67
|
+
| Save the current canvas content as a permanent doc | In-browser: click the floating **💾 Save as doc** button rendered by `lib/stream_weaver/canvas/save_doc_widget.rb` (posts to the bridge; `scope: :repo` default writes near the project, `scope: :global` writes to `~/.streamweaver/canvas`) |
|
|
68
|
+
| Session naming | Free-form string arg to `canvas`/`canvas-push`/etc.; sanitized to `[A-Za-z0-9._-]` for on-disk paths; `panel` auto-generates `panel-<hex>` if omitted |
|
|
69
|
+
| Port / PID discovery | Canvas bridge finds its own free port (same auto-scan behavior as app service); the **app service** (separate from canvas) writes `~/.streamweaver/server.pid` (`port=`, `pid=`) via `lib/stream_weaver/service.rb`; individual apps write portfiles to `~/.streamweaver/apps/<sanitized_name>.port` (`url=`, `pid=`, `name=`) via `lib/stream_weaver/portfile.rb` |
|
|
70
|
+
| iTerm split-pane helper | `streamweaver panel [name]` — implemented in `lib/stream_weaver/iterm.rb` via the optional `iterm2_ruby` gem, **not** a standalone `bin/` script (see Gaps §7) |
|
|
71
|
+
|
|
72
|
+
## 3. Docs Capabilities
|
|
73
|
+
|
|
74
|
+
| Capability | Exact command |
|
|
75
|
+
|---|---|
|
|
76
|
+
| Render org-mode or DSL docs live in a viewer | `streamweaver canvas-read <path>` (accepts a file or directory; also auto-discovers Tier-1 history + registered doc roots when called with no args) |
|
|
77
|
+
| Force a fallback theme/layout for docs with no `use_theme`/`use_layout` declared | `streamweaver canvas-read <path> --theme=NAME --layout=NAME` |
|
|
78
|
+
| Apply the `:doc` theme in code (artifact-parity editorial styling, dark mode via `data-sw-theme="dark"`) | `theme: :doc` option to `app`, or `--theme=doc` flag to `streamweaver panel` |
|
|
79
|
+
| Export a live/saved DSL doc to standalone static HTML (no server, no Ruby needed to view) | `streamweaver export <file.rb> [-o out.html] [--inline-images] [--offline]` |
|
|
80
|
+
| Convert a saved DSL doc to human-readable `.org` | `streamweaver org-export <file.rb>` (writes `<name>.org` next to the source) |
|
|
81
|
+
| Convert an `.org` doc back into DSL body text | `streamweaver org-render <file.org>` (prints to stdout) |
|
|
82
|
+
| Render a StreamWeaver doc on GitHub/Gist as it would look running, client-side | Chrome extension at `extension/` (`StreamWeaver Doc Viewer`) — content script matches `github.com/*` and `gist.github.com/*`, adds a "View rendered" button. Build: `bin/vendor_browser_assets` then `bin/build_extension`, then load unpacked in `chrome://extensions`. **Shipped and verified** (status per `docs/plans/org-doc-preview-surfaces.md`: S1–S3 shipped, S4–S6 not started). |
|
|
83
|
+
| Save the currently-displayed canvas content permanently as a doc | Floating "Save as doc" button in the browser (see §2) |
|
|
84
|
+
| Doc history (append-only local snapshots) | `~/.streamweaver/history/<session>/` for canvas pushes; separate doc store at `~/.streamweaver/canvas` (`scope: :global`) or repo-local (`scope: :repo`, default) for explicit saves — `lib/stream_weaver/canvas/doc_store.rb` |
|
|
85
|
+
|
|
86
|
+
## 4. Interaction Modes Worth Demonstrating
|
|
87
|
+
|
|
88
|
+
| Mode | What it does | Example file |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| **Standalone / stateful app** | `app { ... }.run!` — persistent server, DSL block re-executes on every interaction, state persists across the session | `examples/basic/hello_world.rb`, `examples/basic/todo_list.rb` |
|
|
91
|
+
| **Agentic / blocking form** | `app { ... }.run_once!` — opens a popup UI, blocks the calling Ruby process until Submit, returns collected state as JSON, then exits | `examples/agentic/agentic_form.rb` |
|
|
92
|
+
| **Agentic with auto-close** | Same as above, plus the browser window auto-closes ~1s after submit | `examples/agentic/agentic_form_autoclose.rb` |
|
|
93
|
+
| **Canvas two-way IPC** | Claude Code pushes DSL, blocks on `canvas-wait`, reacts, pushes again — the terminal-agent-drives-a-UI loop | `docs/canvas-panel-workflow.md` walkthrough; `examples/claude_code/` |
|
|
94
|
+
| **Multi-step canvas flow** | `canvas_continue message: "..."` keeps a "processing" spinner between pushes instead of ending the session | documented in `docs/canvas-panel-workflow.md`; no single dedicated example file found (see Gaps) |
|
|
95
|
+
| **Traditional form block** | `form :name do ... submit "Save" { ... }; cancel "Cancel" end` — deferred submission, all fields collected together | `docs/form-patterns.md` (canonical pattern reference with all four form patterns side by side) |
|
|
96
|
+
| **Reactive auto-submit inputs** | Default behavior: every input triggers a server round-trip + re-render immediately (filters, toggles) | `docs/form-patterns.md`, `examples/basic/todo_list.rb` |
|
|
97
|
+
| **Editorial long-form doc (`:doc` theme)** | `doc_header`, `doc_section_header`, `sidebar_toc` — PRDs/reports delivered as standalone app or canvas from one shared DSL body | `examples/tutorials/streamweaver_way_tutorial.rb` (`SW_NO_OPEN=1 ruby examples/tutorials/streamweaver_way_tutorial.rb`); pattern documented in `lib/stream_weaver/skills/streamweaver-doc-builder/SKILL.md` |
|
|
98
|
+
| **Live one-way SSE session** | `streamweaver live <name>` + `streamweaver push <name>` — simpler ancestor of canvas, push-only, no wait/reply loop | referenced in CLI help; no dedicated `examples/` file found for `live`/`push` specifically (`examples/canvas/` only has a mermaid demo script) |
|
|
99
|
+
|
|
100
|
+
Note: the task brief's phrase "growing document" does not appear anywhere in the docs or code searched — likely means the multi-step canvas flow (`canvas_continue`) or the doc-builder pattern above; flagged rather than guessed at further.
|
|
101
|
+
|
|
102
|
+
## 5. Skills Shipped in This Repo
|
|
103
|
+
|
|
104
|
+
Two install paths exist, both driven by the CLI:
|
|
105
|
+
|
|
106
|
+
- **`streamweaver install-skill [--global]`** — installs the panel skill (inline, Claude-Code-only, not spec-compliant `SKILL.md`) plus four gem-sourced skills, symlinked (not copied) so gem updates propagate. Installs to `.claude/skills/` (Claude Code) **and** `.agents/skills/` (the cross-tool alias Codex CLI, Gemini CLI, and GitHub Copilot all discover natively) — `--global` targets `~/.claude/skills/` / `~/.agents/skills/` instead of the project-local paths.
|
|
107
|
+
- **`streamweaver setup`** — one-command setup: adds bash permissions for Claude Code and installs the panel skill globally.
|
|
108
|
+
|
|
109
|
+
| Skill | Source | Installed by `install-skill`? | Triggers on |
|
|
110
|
+
|---|---|---|---|
|
|
111
|
+
| `streamweaver-panel` | `.claude/skills/streamweaver-panel.md` (repo-committed, loose-file legacy format) | Y | Presenting results/status/rich choices visually via `streamweaver panel` |
|
|
112
|
+
| `streamweaver-visual-companion` | `lib/stream_weaver/skills/streamweaver-visual-companion/` | Y | Any "show me / mock it up / push to canvas" moment — replaces Artifact tool and Chrome-screenshot workflows |
|
|
113
|
+
| `streamweaver-doc-builder` | `lib/stream_weaver/skills/streamweaver-doc-builder/` | Y | Building editorial long-form docs with the `:doc` theme |
|
|
114
|
+
| `streamweaver-way` | `lib/stream_weaver/skills/streamweaver-way/` | Y | Building/changing interactive app features (fragments, defer/lazy, keying, dev-loud/prod-self-heal conventions) |
|
|
115
|
+
| `streamweaver-canvas-safe` | `lib/stream_weaver/skills/streamweaver-canvas-safe/` | Y | Before building a canvas doc / running `export` — which components need a live backend vs. render standalone |
|
|
116
|
+
| `visual-plan` | `lib/stream_weaver/skills/visual-plan/` | **N — not in the `gem_skills` install list** (see Gaps) | Pre-flight implementation planning via canvas, before code is written |
|
|
117
|
+
| `visual-recap` | `lib/stream_weaver/skills/visual-recap/` | **N — not in the `gem_skills` install list** (see Gaps) | Post-implementation visual summary of what changed |
|
|
118
|
+
|
|
119
|
+
## 6. The Representative 5 (first 20-minute tutorial)
|
|
120
|
+
|
|
121
|
+
1. **`streamweaver tutorial`** — the built-in guided app. Zero-setup, self-teaching first move; sets the "this is real code, not slides" tone immediately.
|
|
122
|
+
2. **`ruby app.rb` on the minimal example from `docs/for_llms.md`** (a 6-line `text_field` + reactive `text` app) — proves the "your DSL block re-executes on every interaction" mental model in under a minute, which is the single idea everything else builds on.
|
|
123
|
+
3. **`streamweaver panel demo`** + `canvas-push` from another terminal — the "wow" moment: an agent (or the coworker's own hand-typed command) drives a live split-pane UI with no HTML/JS. This is StreamWeaver's actual differentiator versus every other quick-UI tool, and it's the one capability a coworker cannot get anywhere else.
|
|
124
|
+
4. **`streamweaver showcase`** — self-serve exploration of the example library once the core loop clicks, so the tutorial doesn't have to enumerate every component; Matt's Law (find it, digest it) via a browsable index instead of a wall of text.
|
|
125
|
+
5. **`streamweaver export`** on a pushed canvas doc — closes the loop by showing the output is a real, shareable artifact (static HTML, no server needed), which matters for a coworker evaluating whether this is a toy or something they can actually hand to someone else.
|
|
126
|
+
|
|
127
|
+
Rationale: this set covers all four modes (standalone, agentic-adjacent via canvas-wait, canvas two-way, static export) with the minimum number of new concepts, and front-loads the one feature (`panel`) that no generic "Streamlit for Ruby" pitch conveys in words alone — it has to be seen running.
|
|
128
|
+
|
|
129
|
+
## 7. Gaps (things a newcomer would trip on)
|
|
130
|
+
|
|
131
|
+
1. **`visual-plan` and `visual-recap` skills exist in `lib/stream_weaver/skills/` but are absent from the `gem_skills` hash in `CLI.install_skill`** (`lib/stream_weaver/cli.rb:2056-2061`) — running `streamweaver install-skill` does not install them, and the command's own printed summary doesn't mention them either. A newcomer who reads the skill source directly and expects `install-skill` to wire it up will be quietly wrong.
|
|
132
|
+
2. **No `bin/iterm_split_browser.py` in this repo.** The iTerm split-pane behavior referenced by that name (from prior session memory) doesn't exist here — the actual mechanism is `lib/stream_weaver/iterm.rb`, a Ruby wrapper around the optional `iterm2_ruby` gem, invoked via `streamweaver panel`. Worth correcting in any tutorial material that inherited the old filename.
|
|
133
|
+
3. **`streamweaver template <name>`** is a documented top-level command (`lib/stream_weaver/cli.rb:916`) but this pass didn't find example template names or a `docs/` page describing what templates ship or how to list them — a newcomer typing `streamweaver template` with no args has no discoverable next step from the CLI help text alone (the main `help` output doesn't mention `template` at all, only `live`/`push`/canvas commands are documented there).
|
|
134
|
+
4. **`streamweaver pick` and `streamweaver confirm`** are listed in the dispatch table and called "high-level canvas helpers" in the main help text's Panel section, but neither appears in the `help` command's own examples, and this pass didn't trace their DSL surface — a newcomer has no worked example to copy.
|
|
135
|
+
5. **`examples/canvas/`** contains only `mermaid_canvas_demo.sh` — there's no example file demonstrating the basic `live`/`push` one-way session commands that the CLI help documents at length, so a newcomer following the help text's "Live Session Examples" has to hand-type them from scratch rather than running something first.
|
|
136
|
+
|
|
137
|
+
These are reported, not fixed — this was a read-only inventory pass.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Dependency & Setup Surface Survey
|
|
2
|
+
|
|
3
|
+
Read-only survey for a future `streamweaver get-started` command that verifies/installs
|
|
4
|
+
dependencies for a newcomer on a fresh Mac, and degrades gracefully on Linux/Windows.
|
|
5
|
+
Companion to `docs/university/roadmap.md` and `docs/university/capability-inventory.md`.
|
|
6
|
+
|
|
7
|
+
## 1. Dependency summary table
|
|
8
|
+
|
|
9
|
+
| Dependency | Required / Optional | Platform | Where declared / used | Notes |
|
|
10
|
+
|---|---|---|---|---|
|
|
11
|
+
| sinatra, sinatra-contrib | required | any | `stream_weaver.gemspec:38-39` | web framework |
|
|
12
|
+
| phlex | required | any | `stream_weaver.gemspec:40` | view layer |
|
|
13
|
+
| puma | required | any | `stream_weaver.gemspec:41` | server |
|
|
14
|
+
| rackup | required | any | `stream_weaver.gemspec:42` | server |
|
|
15
|
+
| kramdown, kramdown-parser-gfm | required | any | `stream_weaver.gemspec:43-44` | markdown |
|
|
16
|
+
| ostruct | required | any | `stream_weaver.gemspec:45` | Ruby 3.5+ compat |
|
|
17
|
+
| diffy | required | any | `stream_weaver.gemspec:52` | DiffBlock |
|
|
18
|
+
| **iterm2_ruby** | **optional** | **macOS only** | not in gemspec (deliberately) | see [1] below |
|
|
19
|
+
| `open` (BSD) | required for browser auto-launch | macOS | `iterm.rb:39`, `service_client.rb:35`, `cli.rb:1060`, `server.rb:1064` | |
|
|
20
|
+
| `xdg-open` | required for browser auto-launch | Linux | `service_client.rb:37`, `cli.rb:1062`, `server.rb:1066` | |
|
|
21
|
+
| `start` | required for browser auto-launch | Windows | `service_client.rb:39`, `cli.rb:1064`, `server.rb:1068` | via `system('start', url)` |
|
|
22
|
+
| `osascript` | optional (terminal-focus convenience) | macOS only | `cli.rb:2159` | see [2] below |
|
|
23
|
+
| `iterm2ctl` (bin from iterm2_ruby gem) | optional | macOS only | not called from this repo's `lib/`; only from `~/work/claude_code_history` worker-session tooling | see [4] below — outside this gem |
|
|
24
|
+
| `herdr` socket/CLI | optional | any (Forrest's personal tool) | not in this repo; used by `/worker-session` | see [4] below |
|
|
25
|
+
|
|
26
|
+
**[1] `iterm2_ruby`** — Not a gemspec runtime dependency by design (`stream_weaver.gemspec:46-50`, comment explicitly states it is optional so `StreamWeaver::ITerm` degrades gracefully). Required by `lib/stream_weaver/iterm.rb:85` (`require "iterm2"` inside `check_availability`, rescued as `LoadError` → `false`). Availability gating in `iterm.rb:78-89`: only even attempts the require when host OS matches `darwin` (`iterm.rb:79`) and `ENV['ITERM_SESSION_ID']` is set (`iterm.rb:83`), i.e. only inside a live iTerm2 session on macOS — this also avoids spawning the gem's Python-API subprocess unnecessarily. Setup needed for `iterm2_ruby` itself (per `~/work/iterm2_ruby/README.md:67-69`): macOS + iTerm2 installed, Ruby >= 3.1, and iTerm2's own **Preferences > General > Magic > Enable Python API** toggle switched on — no separate Python venv needed (the gem's own README says "No external WebSocket gems required" and does not mention a Python dependency for the Ruby side; iTerm2's built-in Python API server is what it talks to over its own socket). No install script/launcher is provided by this repo; the only guidance is `gem install iterm2_ruby`.
|
|
27
|
+
|
|
28
|
+
**[2] `osascript`** — `lib/stream_weaver/cli.rb:2159`, inside `self.focus_terminal`, used to bring the terminal back to front after the browser auto-closes; runs an AppleScript block checking `System Events`/iTerm2/Terminal.app. Gated behind `RbConfig::CONFIG['host_os']` matching `/darwin|mac os/` (`cli.rb` case statement just above `2148`, see `2143-2165` range) — no-ops elsewhere.
|
|
29
|
+
|
|
30
|
+
## 2. `iterm2_ruby` — error handling / messaging when missing
|
|
31
|
+
|
|
32
|
+
- `ITerm.available?` (`iterm.rb:11-14`) memoizes `check_availability`, which returns `false` on `LoadError` (`iterm.rb:87-88`) — every downstream `ITerm` call (`split_vertical_with_url`, `close_pane`, `split_vertical_with_command`, `navigate_browser`) short-circuits to a `false`/`nil`/no-op return, so nothing raises.
|
|
33
|
+
- `split_vertical_with_url` (`iterm.rb:31-44`) falls back to `system("open", url)` (the plain macOS opener) when the pane split isn't available and `open_browser: true` — this is the "degrade to system browser" path referenced in the gemspec comment.
|
|
34
|
+
- The one place StreamWeaver actively *tells the user* the gem would help: `ITerm.gem_missing?` (`iterm.rb:20-27`) is true only when **not** available, **is** macOS, and **is** inside an iTerm2 session (`ENV["ITERM_SESSION_ID"]` present) — i.e. the one case where installing the gem is actually actionable. Consumed at `lib/stream_weaver/cli.rb:1921-1922`:
|
|
35
|
+
```
|
|
36
|
+
if ITerm.gem_missing?
|
|
37
|
+
puts "(Tip: `gem install iterm2_ruby` to open canvases in an iTerm split pane)"
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
This hint was added in commit `a5c5571` ("feat(cli): hint 'gem install iterm2_ruby' when split-pane would help"); the gem was made optional (not a runtime dep) in `12d76b5` ("fix(gem): make iterm2_ruby an optional enhancement, not a runtime dep"), then pinned to the published RubyGems version in `c3dfaf7`. Full history: `git log -S 'iterm2_ruby' --oneline` shows 12 touching commits from the original hand-rolled-AppleScript replacement (`58866e3`) through the current optional-gem state.
|
|
41
|
+
- Docs mentioning setup gotchas: `docs/claude-code-companion-skill-spec.md:24` ("StreamWeaver's `iterm.rb` uses the `iterm2_ruby` gem to: split pane, open browser pane, keep it persistent"); `docs/university/capability-inventory.md:70` and `:132` (corrects stale session-memory that referenced a nonexistent `bin/iterm_split_browser.py` — the real mechanism is `lib/stream_weaver/iterm.rb` + the optional gem, invoked via `streamweaver panel`); `docs/university/roadmap.md:36` proposes (not yet built — status `[loose]`) a future `streamweaver university` command that "checks `iterm2_ruby` gem (prompts if absent)."
|
|
42
|
+
|
|
43
|
+
## 3. `streamweaver setup` subcommand (`lib/stream_weaver/cli.rb:2095-2133`, `self.setup`)
|
|
44
|
+
|
|
45
|
+
Currently a two-step, Claude-Code-specific bootstrap — **it does not touch `iterm2_ruby` at all**:
|
|
46
|
+
|
|
47
|
+
1. **Bash permission**: reads (or creates) `~/.claude/settings.json`, ensures `permissions.allow` contains `'Bash(streamweaver *)'`, writes it back pretty-printed (`cli.rb:2098-2118`).
|
|
48
|
+
2. **Skill install**: calls `install_skill(['--global'])` (`cli.rb:2121`) — see §3 below for what that installs and where.
|
|
49
|
+
|
|
50
|
+
No dependency checks (no Ruby version check, no `iterm2_ruby` check, no browser-opener check) and no cross-platform branching — it unconditionally writes to `~/.claude/settings.json`, which only makes sense for a Claude Code user. This is the gap a `get-started`/`streamweaver university` command would need to fill; `docs/university/roadmap.md:36` sketches exactly that (story `university-door-command`).
|
|
51
|
+
|
|
52
|
+
**Update (get-started-door-command, shipped):** the gap above is now closed — `streamweaver get-started` (`CLI.get_started` in `cli.rb`) wraps `setup`, adds the Ruby-version/canvas-bridge/agent-skill/premier-iTerm2 dependency report this section describes, and pushes the `university` canvas session.
|
|
53
|
+
|
|
54
|
+
## 4. `streamweaver install-skill` (`lib/stream_weaver/cli.rb:2039-2091`, `self.install_skill`)
|
|
55
|
+
|
|
56
|
+
Yes — **Codex is supported today**, alongside Claude Code and (per the printed help text) Gemini CLI and GitHub Copilot, via a single cross-tool alias directory. Two target roots, chosen by `--global`/`-g`:
|
|
57
|
+
|
|
58
|
+
| Tool | Global path | Project path |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| Claude Code | `~/.claude/skills` (`cli.rb:2043`) | `.claude/skills` (cwd) |
|
|
61
|
+
| Codex / Gemini CLI / Copilot alias | `~/.agents/skills` (`cli.rb:2044`) | `.agents/skills` (cwd) |
|
|
62
|
+
|
|
63
|
+
Behavior:
|
|
64
|
+
- `streamweaver-panel.md` (inline `SKILL_CONTENT`, a flat `.md` with no frontmatter) is written **only** to the Claude-Code path (`cli.rb:2049-2052`) — explicitly noted as "not SKILL.md-spec-compliant, so Claude Code only."
|
|
65
|
+
- Four gem-sourced, spec-compliant skills (`streamweaver-visual-companion`, `streamweaver-doc-builder`, `streamweaver-way`, `streamweaver-canvas-safe`; `cli.rb:2057-2062`) are symlinked — **whole directory, not just SKILL.md** (fixed in the commit tagged `stream_weaver-5fyf` per the comment at `cli.rb:2064-2067`, so sibling `examples/`/`references/` content stays reachable) — into **both** `claude_dir` and `agents_dir` (`cli.rb:2069-2076`).
|
|
66
|
+
- Printed summary explicitly documents the Codex path (`cli.rb:2088-2090`): "Also installed to `#{agents_location}` — the cross-tool alias Codex CLI, Gemini CLI, and GitHub Copilot all discover natively (Claude Code uses its own path above instead)."
|
|
67
|
+
|
|
68
|
+
**Tested?** Yes, partially — `spec/cli_install_skill_spec.rb:38-39` has a test asserting the `.agents/skills` symlink is created for `streamweaver-canvas-safe`. No test found asserting Codex itself discovers/loads from that path (that would require a real Codex CLI invocation, out of scope for this repo's spec suite).
|
|
69
|
+
|
|
70
|
+
**Caveat vs. Codex's own conventions**: `docs/reference/agent-skills-comparison.md:16` documents that Codex actually looks in `~/.agents/skills/` (user), `$REPO_ROOT/.agents/skills/` (repo-root), `$CWD/.agents/skills/` (working dir), **and** historically `~/.codex/skills/`, plus an admin-level `/etc/codex/skills/`. StreamWeaver's installer only ever writes to `~/.agents/skills` or `<cwd>/.agents/skills` — it does not also mirror into the legacy `~/.codex/skills/` path, so a Codex install expecting the legacy path specifically would miss it (the modern `.agents/skills` convention is covered).
|
|
71
|
+
|
|
72
|
+
## 5. `/worker-session` skill — dependency chain
|
|
73
|
+
|
|
74
|
+
Location: `~/.claude/skills/worker-session/SKILL.md` (single file, no sibling `references/`/`examples/`).
|
|
75
|
+
|
|
76
|
+
The skill itself is a thin dispatcher: it resolves `--dir`/`--task`/`--model`/`--session-name`/`--mode` and shells out to:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
~/work/claude_code_history/bin/worker-session --dir <dir> --task-file <path> [--model][--name][--mode]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
That script (`bin/worker-session`) requires `../lib/worker_session_launcher` (`ClaudeCodeAnalyzer::WorkerSessionLauncher`), which chains to:
|
|
83
|
+
|
|
84
|
+
| Dependency | Type | Used for |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `ENV['HERDR_PANE_ID']` / `ENV['TERM_PROGRAM']` | env inspection | detects calling terminal (`:herdr` vs `:iterm` vs `:unknown`) — `worker_session_launcher.rb caller_terminal` |
|
|
87
|
+
| `TerminalAdapter::HerdrAdapter` (`herdr_adapter.rb`) | Ruby class, shells to `herdr` CLI | when caller is inside a herdr pane: `herdr api snapshot`, `herdr tab create --cwd ... --label ... --focus`, `herdr pane run <pane_id> <command>`, `herdr tab focus <tab_id>` — all via `IO.popen(['herdr', *args], ...)` |
|
|
88
|
+
| `herdr` socket | file existence check | `HerdrAdapter.detected?` checks `~/.config/herdr/herdr.sock` exists before considering herdr "running" |
|
|
89
|
+
| `iterm2ctl` (from the `iterm2_ruby` gem's `bin/`) | external binary, `IO.popen` | when caller is plain iTerm (not herdr): `iterm2ctl create tab`, `iterm2ctl send-text <session_id> <text>` — `worker_session_launcher.rb run_iterm2ctl` |
|
|
90
|
+
| `claude` CLI | shelled-out command | the actual command sent into the new pane/tab (`build_command`, prefixes with `--model`/`-n`/`--permission-mode` as given) |
|
|
91
|
+
|
|
92
|
+
So the full external chain for `/worker-session` is: **herdr** (socket + CLI, optional — only when inside a herdr pane) **or** **iterm2ctl** (binary shipped by the `iterm2_ruby` gem, optional — only when inside plain iTerm and not herdr) → **claude** CLI itself. None of this lives in the `stream_weaver` repo — it's entirely in `~/work/claude_code_history` (personal tooling), invoked by a global skill, not a repo-local one. Not a dependency of the `stream_weaver` gem or its `get-started` surface, but flagged since it was in scope of the ask.
|
|
93
|
+
|
|
94
|
+
## 6. Platform-specific shell-outs in `lib/` and `exe/`
|
|
95
|
+
|
|
96
|
+
All `osascript` / `system(...)` / backtick invocations found (excludes comments and non-shell backtick usage in prose, e.g. inline-code markdown in comments):
|
|
97
|
+
|
|
98
|
+
| Call | File:line | Platform gate | Purpose |
|
|
99
|
+
|---|---|---|---|
|
|
100
|
+
| `system("open", url)` | `lib/stream_weaver/iterm.rb:39` | none explicit (only reached after `available?` macOS/iTerm gate upstream) | fallback browser open when pane split unavailable |
|
|
101
|
+
| `system('open', url)` | `lib/stream_weaver/service_client.rb:35` | `when /darwin/` | browser open |
|
|
102
|
+
| `system('xdg-open', url)` | `lib/stream_weaver/service_client.rb:37` | `when /linux/` | browser open |
|
|
103
|
+
| `system('start', url)` | `lib/stream_weaver/service_client.rb:39` | `when /mswin\|mingw\|cygwin/` (Windows) | browser open |
|
|
104
|
+
| `system('open', url)` | `lib/stream_weaver/cli.rb:1060` | `when /darwin\|mac os/` | browser open |
|
|
105
|
+
| `system('xdg-open', url)` | `lib/stream_weaver/cli.rb:1062` | `when /linux/` | browser open |
|
|
106
|
+
| `system('start', url)` | `lib/stream_weaver/cli.rb:1064` | `when /mswin\|mingw\|cygwin/` | browser open |
|
|
107
|
+
| `system('osascript', '-e', script)` | `lib/stream_weaver/cli.rb:2159` | `when /darwin\|mac os/` (`self.focus_terminal`) | refocus terminal after browser auto-close |
|
|
108
|
+
| `system("open", url)` / `system("xdg-open", url)` / `system("start", url)` | `lib/stream_weaver/server.rb:1064,1066,1068` | same three-way OS case | browser open (server-side variant) |
|
|
109
|
+
|
|
110
|
+
All three OS-specific browser-open call sites (`service_client.rb`, `cli.rb`, `server.rb`) implement the same `open`/`xdg-open`/`start` fallback triad independently — i.e. this logic is duplicated three times rather than factored into one shared helper. No `Process.spawn`/`Open3`/other shell-out patterns found for anything beyond these two purposes (browser-open, terminal-refocus) and the `iterm2ctl`/`herdr` calls covered in §5 (which live outside this repo).
|
|
111
|
+
|
|
112
|
+
## 7. Skill packaging levels (private/team/world) — not found
|
|
113
|
+
|
|
114
|
+
Grepped `docs/` and `~/work/cultiv-ai/wiki` for "world" near "skill"/"packag": no hits describing a private/team/world skill-packaging tier scheme. The only adjacent material is `docs/reference/agent-skills-comparison.md:112`, which notes Claude Code has no equivalent to Codex's "plugin packaging layer (distributable bundles of multiple skills + MCP config)" — a different axis (bundling, not visibility/audience tiers). If a private/team/world scheme exists, it isn't documented in this repo or in the cultiv-ai wiki index as of this survey.
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# StreamWeaver University — course canvas design spec
|
|
2
|
+
|
|
3
|
+
Story: `course-canvas-design` (epic `university-getting-started`). Binding input for `course-list-canvas`, `progress-ledger`, `driver-worker-runner`, and every `step-N-*` story.
|
|
4
|
+
|
|
5
|
+
Mockup: `docs/university/mockups/course_canvas_mockup.rb` (runs standalone with `ruby`, and pushes to a canvas with `canvas-push`).
|
|
6
|
+
Screenshots: `course-list-light.png`, `course-list-dark.png`, `step-screen-light.png`, `step-screen-dark.png` in the same directory.
|
|
7
|
+
|
|
8
|
+
**Revision note (v2).** v1 was reviewed and rejected as "a decent first start" that read as a *document* rather than an app: the course state sat at page level above everything, sections were divided by hairlines and 11px small-caps labels that reviewers missed entirely, and the single-blue palette left every state looking alike. v2 rebuilds the information architecture around course panels, replaces hairlines with real surfaces, raises the type scale, and introduces a three-colour state language. Sections 1–4 describe v2; §5 records what v1 taught us that still binds the build.
|
|
9
|
+
|
|
10
|
+
**Revision note (single-mode, 2026-09-03).** Live UAT (round 5) cut the separate "step screen" this doc's §2/§6 describe: "Details" on a course-list row now expands that row's content inline on the SAME course-list render (why it matters, prompt + Run/Copy, payoffs, Mark done + a next-step hint) instead of navigating to a second screen. Expanding one row collapses any other, and there is nothing to get "back" from — no `← All steps`, no `Next: step N+1` link. The content and CSS classes §2's "Step screen" table describes (`.uni-label`, `.uni-prose`, `.uni-promptbox`, `.uni-payoff`, the footer) carry over unchanged as the expansion body; only the App band / Context row (wordmark+breadcrumbs, `← All steps`, `Step N of 5`) are gone, since there is no second screen to orient within. See `lib/stream_weaver/university/canvas.rb` (`.uni-step__expansion`) and `Progress#expanded_step`/`#expand_step!`/`#collapse!`.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. Visual direction
|
|
15
|
+
|
|
16
|
+
**A warm workbench.** The `:doc` theme's warm paper stays, but it is now the *room*, not the object. Courses sit on it as raised instrument panels — near-white in light, warm charcoal in dark — each with a title bar, a body, and its own state. The reader is someone with a terminal open beside this pane, at roughly 500–900px wide, whose attention is already spent. They must be able to tell in under a second: what is this, which course am I in, and what do I do next.
|
|
17
|
+
|
|
18
|
+
Five decisions carry the design:
|
|
19
|
+
|
|
20
|
+
**a. The course owns its state; the page owns nothing.** Nothing course-specific appears above the course list. The resume line ("Pick up at step 3."), the primary Run button, and the 5-segment rail all live *inside* the Getting Started panel, under its own title bar. This is what makes a second course additive rather than a rewrite: a course is a self-contained unit with a header, a state, and a list. The page above it carries only identity — the app name and one line saying what the app is for.
|
|
21
|
+
|
|
22
|
+
**b. Courses are accordion panels, open or dormant.** Getting Started renders open (`card depth: :elevated`) with a filled title bar, a status chip, and its full body. Future courses render closed (`card depth: :recessed`): transparent ground, quiet border, hollow dot, name and one-line blurb, a `Soon` chip with a drawn lock — and no controls at all. A dormant course is a promise on a shelf, not a wall of dead buttons.
|
|
23
|
+
|
|
24
|
+
**c. Surfaces, not hairlines.** v1 separated everything with 1px rules, and reviewers scrolled straight past the boundaries. v2 gives every region a real ground: page `#E7E4D9`, panel `#FDFCF9`, title bar `#F3F1E8`, recessed regions (resume band, prompt block, footer) `#F1EFE6`, plus a two-layer neutral shadow on the open panel. The panel edge is now the loudest line on the page, which is correct — it is the strongest structural fact.
|
|
25
|
+
|
|
26
|
+
**d. A three-colour state language, used identically everywhere.** One triad, no confetti:
|
|
27
|
+
|
|
28
|
+
| State | Light | Dark | Where it appears |
|
|
29
|
+
|---|---|---|---|
|
|
30
|
+
| Done | `#17754A` green | `#57C98A` | step mark (filled disc + check), rail segment, "What you should see" checks |
|
|
31
|
+
| Now | `#1E4ED8` blue (`--sw-color-accent`) | `#6699FF` | step mark (filled disc + number + ring), current-row band, rail segment, every primary button, course status dot |
|
|
32
|
+
| Not yet | `#6E6959` stone | `#9A9285` | step mark (hollow ring + number), `Soon` chip |
|
|
33
|
+
|
|
34
|
+
The rail's empty segments use a separate `--uni-rail-track` value rather than the stone. They are a *track*, not a state marker: the rail's meaning is carried by the filled done/now segments and by the always-present `N of 5 done` label beside it, so the track is not required to clear 3:1 and darkening it to that level would read as "filled."
|
|
35
|
+
|
|
36
|
+
Because the same three colours drive the mark, the rail, and the checks, the rail becomes readable without its label and the current step is findable at a glance from anywhere on the page. Colour is never the *only* signal: done also carries a check glyph, current also carries a filled disc and a tinted row band, dormant also carries a lock glyph and the word `Soon`.
|
|
37
|
+
|
|
38
|
+
**e. One primary action, three button weights.** Every step keeps a working Run/Repeat control — locking steps would be pure friction — but hierarchy carries the guidance: filled accent (the one thing to do now) → outlined (the current step's own Run, and Copy prompt) → quiet borderless (repeat a done step, jump ahead). Exactly one filled button exists per screen.
|
|
39
|
+
|
|
40
|
+
**Type.** Base is 16px, up from 15px. The serif display face (`--sw-font-display`, Charter) is used exactly twice per screen and only for voice, never for structure: the app name (27px) and the resume line / step title (~1.55rem / ~1.95rem). Everything structural is the body sans, at sizes that survive a skim: course name 20px/700, step title 17px/650, step payoff 14.5px, section labels 15px/650, prose 16px. **v1's 11px uppercase micro-labels are gone from the content flow.** Small caps survive only as *chrome* — the `IN PROGRESS` / `SOON` chips and the prompt block's title bar — where the enclosing shape, not the type size, does the work of being noticed.
|
|
41
|
+
|
|
42
|
+
**Motion.** Deliberately none on load. The canvas is re-pushed by an agent many times per session; a re-entrance animation on every push is an attention tax, not a delight. The budget is spent on 130ms state transitions (hover, focus) behind a `prefers-reduced-motion` guard.
|
|
43
|
+
|
|
44
|
+
**Light and dark.** v1 rode `--sw-*` tokens exclusively and claimed zero dark-mode rules as a win. That constraint is what produced the blandness: a control panel needs the page and the panel to be *different grounds*, and that is a two-value decision per theme, not one token. v2 therefore carries one explicit dark block, hung off the same selector the `:doc` theme uses for its own dark palette (`html[data-sw-theme="dark"] body`), so the two flip on exactly the same signal and can never disagree. Semantic tokens (`--sw-color-text`, `--sw-color-accent`, `--sw-font-*`) are still consumed directly.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 2. What each screen must assert
|
|
49
|
+
|
|
50
|
+
### Course list
|
|
51
|
+
|
|
52
|
+
| Region | Content | Why it's there |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| App band | `StreamWeaver University` (serif, 27px), one-line tagline, theme toggle | Says what this is in under a second — the fix for "I thought it was a document" |
|
|
55
|
+
| Course panel — title bar | status dot, `Getting Started` (20px/700), `IN PROGRESS` chip | The course names itself at a size nobody scrolls past |
|
|
56
|
+
| Course panel — resume band | "Pick up at step N." + step title + `Run step N` (primary) + `Repeat step N-1` (quiet) + 5-segment rail + "N of 5 done" | The single answer to "what do I do now" — scoped to this course |
|
|
57
|
+
| Course panel — run report band | Only after a Run/Repeat click: one line saying what the driver did. On a clean send, green and nothing else. On a refusal (worker session closed, none recorded) or in degraded mode, stone, plus the step's prompt in a code block with a Copy button and a paste instruction | The click has to say what it did — a silent Run is indistinguishable from a broken one, and a refusal must hand back the prompt rather than strand the user (`driver-worker-runner` criteria 4-5). Superseded by the next click and cleared by Mark done, so it never goes stale |
|
|
58
|
+
| Course panel — step rows | 5 rows: state mark, title, one-line payoff, Run/Repeat | The map; every step re-runnable |
|
|
59
|
+
| Divider | `In the works` (15px/650) + rule to the right edge | A section label at reading size, not a micro-label |
|
|
60
|
+
| Dormant panels | 3 closed cards: hollow dot, name, blurb, `Soon` chip. **No controls.** | Promise, not denial |
|
|
61
|
+
| Footer note | `streamweaver tutorial` in a code chip, on its own recessed panel | The escape hatch to the old tour |
|
|
62
|
+
|
|
63
|
+
### Step screen
|
|
64
|
+
|
|
65
|
+
| Region | Content | Why it's there |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| App band | wordmark + breadcrumbs `Getting Started · Step 3` + theme toggle | Where am I |
|
|
68
|
+
| Context row | `← All steps` button, the same 5-segment rail, `Step 3 of 5` | Back out, and position — the rail is the same instrument as on the list |
|
|
69
|
+
| Panel title bar | blue step-number badge + serif h1 title | The step, marked with the same "now" badge the list used |
|
|
70
|
+
| Why this matters | 2 short paragraphs, ≤66ch | Motivation before mechanics |
|
|
71
|
+
| Prompt block | labelled title bar + verbatim monospace body on a sunk ground | **Transparency.** No hidden prompt, no paraphrase — the user sees exactly what gets sent |
|
|
72
|
+
| Actions | `Run in worker session` (primary) + `Copy prompt` (outlined) | Premier path and degraded path, side by side |
|
|
73
|
+
| What you should see | 3 lines with green done-checks | The payoff, stated before they run it |
|
|
74
|
+
| Footer band | `Mark step N done` (outlined) + "Unlocks step 4." | The exit, on its own ground, not competing with Run |
|
|
75
|
+
|
|
76
|
+
Monospace appears exactly once per screen, on the prompt block, where it is doing its real job: showing a verbatim payload character-for-character.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 3. Component and token inventory
|
|
81
|
+
|
|
82
|
+
Existing components used as-is (prefer these in the build; do not hand-roll replacements):
|
|
83
|
+
|
|
84
|
+
| Component | Used for | Hook restyled |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `card(depth:, class:)` | every course panel — `:elevated` open, `:recessed` dormant | `.sw-card` |
|
|
87
|
+
| `card_header(class:)` | course title bar, step title bar | `.sw-card-header` |
|
|
88
|
+
| `card_body(class:)` | course body | `.sw-card-body` |
|
|
89
|
+
| `topbar(wordmark:, breadcrumbs:)` | app band on both screens | `.sw-topbar`, `.sw-topbar-wordmark`, `.sw-topbar-crumb`, `.sw-topbar-crumb--active` |
|
|
90
|
+
| `theme_toggle mode: :auto` | light/dark control | `.sw-theme-toggle__btn`, `.sw-theme-toggle__icon`, `.sw-theme-toggle__label` |
|
|
91
|
+
| `button(label, submit: false, key:, class:)` | every Run / Repeat / Mark done | `.sw-button` |
|
|
92
|
+
| `copy_button(label, text:, copied_label:)` | Copy prompt (degraded path) | `.sw-button` (it emits the same hook) |
|
|
93
|
+
| `header1` / `header2` / `header3` | step-screen title, course names, step-row titles | none — styled via own class |
|
|
94
|
+
| `phrase` / `div` / `md` | rows, chips, labels, prose, the prompt block | `.markdown-content` for `md` bodies |
|
|
95
|
+
| `tabs :screen, variant: :line` | **review chrome only** — see §6 | none |
|
|
96
|
+
| `use_theme` / `use_layout` / `use_stylesheet` | in-DSL theme + stylesheet, so one file serves standalone and canvas | — |
|
|
97
|
+
|
|
98
|
+
Theme tokens consumed directly:
|
|
99
|
+
|
|
100
|
+
| Token | Used for |
|
|
101
|
+
|---|---|
|
|
102
|
+
| `--sw-color-text` | all primary text |
|
|
103
|
+
| `--sw-color-text-muted` | payoffs, blurbs, quiet buttons, hints |
|
|
104
|
+
| `--sw-color-accent` | the "now" colour, in every state surface and primary button |
|
|
105
|
+
| `--sw-color-primary-hover` | primary button hover |
|
|
106
|
+
| `--sw-font-display` / `--sw-font-body` / `--sw-font-mono` | the three voices |
|
|
107
|
+
|
|
108
|
+
Values defined per-theme by this design rather than taken from a token, because no token expresses them: the four grounds (page / panel / bar / sunk), the two line weights, and the done / now-tint / not-yet triad. All are declared once on `body` and once on `html[data-sw-theme="dark"] body`.
|
|
109
|
+
|
|
110
|
+
`--sw-color-text-light` is deliberately **not** used for any text: at `#A09D96` on paper it is 2.4:1 and fails WCAG AA. Anything that needs to recede drops *weight* rather than contrast.
|
|
111
|
+
|
|
112
|
+
`--sw-color-text-muted` is also **overridden in light mode** (`#5F5C54` instead of `#6B6860`). The theme value measures 4.37:1 on this design's page ground, and the tagline and the dormant-course blurbs sit exactly there. The override clears 4.5:1 on all four grounds. Dark mode keeps the theme value (6.5:1+ everywhere).
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 4. Bespoke CSS
|
|
117
|
+
|
|
118
|
+
One stylesheet, carried inline via `use_stylesheet` (see §5 for why inline rather than a sibling `.css`). All of it lives in the `uni-` namespace except the `sw-` hook overrides listed in §3.
|
|
119
|
+
|
|
120
|
+
| Class | What it is |
|
|
121
|
+
|---|---|
|
|
122
|
+
| `.uni-i`, `--check/play/repeat/back/lock` | the drawn icon set — masked inline SVG at one stroke weight, `background-color: currentColor` so icons inherit text colour and theme automatically. No emoji, no icon library. |
|
|
123
|
+
| `.uni-course`, `--dormant`, `__bar`, `__dot`, `__name`, `__blurb`, `__body` | the course panel: open and dormant variants, title bar, status dot |
|
|
124
|
+
| `.uni-chip`, `--soon` | the `IN PROGRESS` and `Soon` status chips |
|
|
125
|
+
| `.uni-resume`, `__lead`, `__sub` | the resume band inside a course panel |
|
|
126
|
+
| `.uni-actions` | the primary/secondary button row |
|
|
127
|
+
| `.uni-btn`, `--run`, `--outline`, `--quiet` | the three-weight button ladder, applied via `class:` on `button`/`copy_button` |
|
|
128
|
+
| `.uni-rail`, `__track`, `__seg`, `--done`, `--current`, `__label` | the five-segment progress rail |
|
|
129
|
+
| `.uni-step`, `--done/--current/--todo`, `__mark`, `--hero`, `__title`, `__payoff` | the step rows and their state marks |
|
|
130
|
+
| `.uni-divider`, `__label`, `__rule` | the `In the works` section divider |
|
|
131
|
+
| `.uni-note` | the `streamweaver tutorial` footer panel |
|
|
132
|
+
| `.uni-context`, `.uni-back`, `.uni-count`, `.uni-title`, `.uni-section`, `.uni-label`, `.uni-prose`, `.uni-promptbox`, `.uni-prompt`, `.uni-payoff`, `.uni-foot` | the step screen |
|
|
133
|
+
| chrome neutralization on `body[class*="sw-layout-"]` + `#app-container` | holds one 860px column whether the host set `sw-layout-fluid` (canvas) or `sw-layout-default` (standalone) |
|
|
134
|
+
| `::selection`, `:focus-visible`, scrollbar rules | browser surfaces themed from the palette instead of left at browser defaults |
|
|
135
|
+
|
|
136
|
+
Four structural notes for the build:
|
|
137
|
+
|
|
138
|
+
- The icon set is data-URI masks, so it costs no network request and needs no asset route.
|
|
139
|
+
- Step titles are `h3` under the course's `h2` name, and the `:doc` theme's own `h2`/`h3` treatment is explicitly reset, so the outline nests correctly instead of running flat.
|
|
140
|
+
- Selectors that fight a theme rule are written `body.sw-theme-doc h2.uni-…` to win specificity outright rather than relying on source order.
|
|
141
|
+
- The card component's own `--elevated` / `--recessed` backgrounds are overridden by `body .sw-card.uni-course`; the depth argument is kept because it carries the semantic and any future theme work should key off it.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 5. Gaps found
|
|
146
|
+
|
|
147
|
+
| Gap | Impact on the build | Suggested fix |
|
|
148
|
+
|---|---|---|
|
|
149
|
+
| **`use_theme` in a pushed DSL body does not reach the live canvas page.** `Canvas::Session` fixes `theme`/`layout` at create time (`bridge.rb` `handle_create`); `render_canvas_page` emits `sw-theme-#{session.theme}`. A body declaring `use_theme :doc` still renders under `sw-theme-default`. | `course-list-canvas` **must** create its session with the doc theme — the canvas will silently look wrong otherwise. | Have the push path adopt the body's `use_theme`/`use_layout` (canvas-read already does), or at minimum let `streamweaver canvas` accept `--theme=`. |
|
|
150
|
+
| **`streamweaver canvas <name>` has no `--theme=` flag** (only `panel` does). | Any headless canvas setup for the course app needs `panel`, which also opens a pane. | Add `--theme=` to `canvas_session`, matching `panel`. |
|
|
151
|
+
| **`use_stylesheet` must be handed literal CSS, not a path, for canvas.** Path resolution is relative to the *evaluating* script dir, which canvas-push does not set. | The mockup inlines its CSS in a heredoc. Same constraint applies to the built app. | Either keep CSS inline in the body, or teach `canvas-push` to resolve `use_stylesheet` paths against the pushed file's directory. |
|
|
152
|
+
| **`topbar`'s wordmark renders as a `div`, so a page whose title is the wordmark has no `h1`.** | The course list has no `h1` — its outline starts at the `h2` course name. Harmless visually, wrong for screen readers and for `export`. | Give `topbar` a `wordmark_level:` option (default `div`, opt into `h1`), or let the app pass its own heading into the wordmark slot. |
|
|
153
|
+
| **`card_header(content)` renders the title as `h4`,** which cannot sit under an `h2`/`h1` without skipping levels. | The mockup passes no content string and nests its own `header2`/`header1` as a child instead. | Add a `level:` option to `card_header`. |
|
|
154
|
+
| **`theme_toggle` ships an emoji icon plus a `System/Dark/Light` word label** with no option to suppress either. | The mockup overrides both in CSS (hides the label, re-masks the two icon spans by `:nth-of-type`). Brittle if the component's markup order changes. | Add `compact: true` (icon only) and/or an `icon:` option. Worth an `sw-` hook per icon variant so `nth-of-type` isn't the selector. |
|
|
155
|
+
| **`Header` drops unknown options.** `header2 "x", "data-foo": 1` silently loses the attribute (only `:class`/`:style` forward). | Step numbers live inside the mark element rather than as a `data-*` on the title. Fine, but worth knowing. | Forward arbitrary `data-*` through `render_header`. |
|
|
156
|
+
| **No component for a "list row with state mark + title + detail + action".** This is the step row, and it will recur (course lists, checklists, run logs). | Built from `div` + grid CSS here. | Candidate component after `course-list-canvas` proves the shape: `step_row(number:, title:, detail:, state:, &action)` emitting `.sw-step-row`, `__mark/__title/__detail`, `--done/--current/--todo`. |
|
|
157
|
+
| **No progress-rail component.** `progress_bar` is a percentage bar, which is the wrong instrument. | Built from five `div`s, and now used on *both* screens. | Candidate: `step_rail(total:, done:, current:)` emitting `.sw-step-rail`, `__seg`, `--done`, `--current`. |
|
|
158
|
+
| **No status-chip component.** `badge` exists but carries its own colour vocabulary. | `IN PROGRESS` / `Soon` are hand-rolled `.uni-chip`. | Either widen `badge` to accept a semantic state, or leave chips bespoke. |
|
|
159
|
+
| Canvas WebSocket handshake 404s and falls back to polling (`ws://…/canvas/<name>/ws` → 404 in the console). | Pre-existing; not caused by this design. Content still updates. | Out of scope here — worth a separate mark if it isn't already tracked. |
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 6. How to build `course-list-canvas` from this
|
|
164
|
+
|
|
165
|
+
1. **Start from the mockup file, not from scratch.** `docs/university/mockups/course_canvas_mockup.rb` already holds the finished stylesheet and both screens' markup. Lift the `_css` heredoc verbatim into the real app; it is the design.
|
|
166
|
+
2. **Drop the tabs.** `tabs :screen` in the mockup is review chrome so both screens are visible in one push. The real app has no tabs: the course list and the step screen are two renders of the same app, chosen by state (`state[:step]`, nil = list).
|
|
167
|
+
3. **Model a course, not a page.** The course panel is the unit of composition: `card(depth:) > card_header(title bar) > card_body(resume band + step rows)`. Adding a second live course means appending another panel with its own state — no page-level change. Keep the rule that **nothing course-specific renders above the course list.**
|
|
168
|
+
4. **Replace the literal arrays with the curriculum.** The `steps` array (number, title, payoff, state) is the shape the curriculum layer must supply. `state` comes from the progress ledger (`progress-ledger`), so it is `:done` / `:current` / `:todo` — the CSS keys off exactly those three, as do the rail segments.
|
|
169
|
+
5. **Wire the buttons.** Every `button` in the mockup is `submit: false` and inert. In the build: the resume-band Run and each row's Run/Repeat call the driver (`driver-worker-runner`); `Mark step N done` writes the ledger; `← All steps` clears `state[:step]`. Keep `key:` on every button in the loop — without it, loop-derived ids collide.
|
|
170
|
+
|
|
171
|
+
Two non-obvious requirements, both found the hard way in the 2026-08-29 UAT, where every button blanked the canvas to "✓ Submitted — You can close this window" and nothing happened:
|
|
172
|
+
|
|
173
|
+
- **The DSL must emit `canvas_continue`.** Buttons already dispatch non-terminal `action` events; the blanking comes from the adapter's `showFeedback()`, which falls back to that terminal screen when the pushed page carries no `#sw-canvas-continue` marker. Right for a one-shot form, fatal for a control panel. With the marker a click shows a brief spinner instead.
|
|
174
|
+
- **Something has to be listening.** A click only reaches the ledger if `University::Listener` is running, and only returns to a real page because the listener re-pushes. `get-started` starts it as a detached background process (pid and log under `~/.streamweaver/university/`); `streamweaver university-listener [start|stop|status]` is the manual control. Without it the canvas is a screen of buttons that silently do nothing — and, with the marker in place, one that sits on "Working…" forever.
|
|
175
|
+
6. **Keep `use_theme :doc` + `use_stylesheet` in the DSL body** so the app renders identically standalone, on a canvas, through `canvas-read`, and through `export`. And per §5, create the canvas session itself with the doc theme — `use_theme` alone will not colour the live page.
|
|
176
|
+
7. **Degraded mode is a hierarchy swap, not a different screen.** With no worker session, drop the Run buttons and promote `Copy prompt` from `.uni-btn--outline` to `.uni-btn--run`. The resume band's `Run step N` becomes `Copy step N prompt`. Nothing else changes.
|
|
177
|
+
8. **States the mockup does not show, which the build owes:**
|
|
178
|
+
- *First run* (0 of 5): resume line reads "Start with step 1.", rail all not-yet, no Repeat button, and the course chip reads `NOT STARTED` rather than `IN PROGRESS`.
|
|
179
|
+
- *All done* (5 of 5): the chip reads `COMPLETE` in the done green, the resume band congratulates and points at the shelf / `streamweaver tutorial`, every row shows Repeat, and there is no primary button (the course has no "next").
|
|
180
|
+
- *Running*: the row whose step was just dispatched needs a "sent to your worker session" acknowledgement — the canvas cannot see the worker's progress, so say only what is true.
|
|
181
|
+
9. **Do not add:** a percentage progress bar, per-step time estimates, step locking, a fourth accent colour, a nested card inside a course panel, or an entrance animation. Each was considered and rejected above.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 7. Verification done
|
|
186
|
+
|
|
187
|
+
- Rendered on the live canvas bridge (session `university-design`, doc theme) and screenshotted full-page at 1000px, light and dark, both screens. Also checked at 620px — the real narrow iTerm pane — where step rows reflow to put the action under the text.
|
|
188
|
+
- Booted standalone (`SW_NO_OPEN=1 STREAMWEAVER_PORT=… ruby docs/university/mockups/course_canvas_mockup.rb`) and confirmed HTTP 200 with `<body class="sw-layout-default sw-theme-doc">` — the same file drives both delivery modes.
|
|
189
|
+
- Ran the Impeccable mechanical detector over the rendered page, then ran a two-assessment critique (independent design review + independent detector/contrast evidence) over the result. Findings and their attribution are in §8.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## 8. Critique findings (v2)
|
|
194
|
+
|
|
195
|
+
Recorded so the build inherits them rather than rediscovering them.
|
|
196
|
+
|
|
197
|
+
**Attribution rule.** The canvas bridge injects its own framework CSS and its own "Save canvas as doc" control into every page it serves. A detector finding belongs to this design only if it traces to markup or CSS authored in the mockup file.
|
|
198
|
+
|
|
199
|
+
| Finding | Attribution | Disposition |
|
|
200
|
+
|---|---|---|
|
|
201
|
+
| `skipped-heading`: `h1` "One form, two modes" followed by `h3` "Save canvas as doc" | Host — the Save-as-doc control is bridge chrome appended after page content | Not fixable from the DSL; ignore |
|
|
202
|
+
| `pulsing-dot`: `.sw-pulse-dot` with an infinite `sw-pulse` animation | Framework CSS — this page has no pulsing element (`.uni-course__dot` is static) | Ignore |
|
|
203
|
+
| `dark-glow`: zero-offset `#ef4444` box-shadow | Framework CSS — not a colour this design uses | Ignore |
|
|
204
|
+
| Course list has no `h1` | Ours, but caused by `topbar`'s wordmark rendering as a `div` | Recorded as a component gap in §5; the build should fix it there, not with a stray heading |
|
|
205
|
+
| Step titles were `h2` under an `h2` course name (flat outline) | Ours | **Fixed** — step titles are now `h3` |
|
|
206
|
+
|
|
207
|
+
Contrast was computed, not estimated, for every state colour against every ground it lands on in both themes. The first pass had three genuine failures, all now fixed:
|
|
208
|
+
|
|
209
|
+
| Pair | Was | Now | Fix |
|
|
210
|
+
|---|---|---|---|
|
|
211
|
+
| `not yet` stone on panel (todo step numerals) | 3.53 light / 4.43 dark | 5.35 / 5.34 | darkened light stone to `#6E6959`, lightened dark stone to `#9A9285` |
|
|
212
|
+
| `not yet` stone on the `Soon` chip ground | 2.49 light / 3.95 dark | 4.64 / 4.76 | chip ground split off from the rail track and lightened to `#EFECE1` — no stone value could pass against the old ground |
|
|
213
|
+
| muted text on the page ground (tagline, dormant blurbs) | 4.37 light | 5.24 | light-mode `--uni-muted` override, §3 |
|
|
214
|
+
|
|
215
|
+
Also fixed from the design review: the todo mark's ring was a 50% tint (2.07:1 as a boundary) and is now solid stone; the done mark, which replaces its number with a glyph, now carries visually-hidden state text (`.uni-sr`) so a screen reader gets "Step 1, done" rather than silence; body bottom padding became a real safe area (120px, 150px under 700px) because the host's floating "Save as doc" control was landing on the last payoff line in a narrow pane; the step screen's rail gained the same `N of 5 done` label the course list uses.
|
|
216
|
+
|
|
217
|
+
One review suggestion was implemented with a deliberate change: it asked for a `Next: step N →` **action** in the footer. Two equally-weighted exits there would have been ambiguous — only one of them writes the ledger — so `Mark step N done` keeps the outlined weight and `Next: step 4` sits at the quiet weight on the far side of the footer. The one-primary-action rule still holds; the screen's primary remains `Run in worker session`.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|