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,639 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require_relative 'theme/presets'
|
|
5
|
+
require_relative 'theme/auto_mode'
|
|
6
|
+
|
|
7
|
+
module StreamWeaver
|
|
8
|
+
# Custom theme registration and management.
|
|
9
|
+
#
|
|
10
|
+
# == Visual Skills CSS Foundation (T2)
|
|
11
|
+
#
|
|
12
|
+
# This module provides the CSS custom property vocabulary used by ALL
|
|
13
|
+
# visual skills components (T3-T15). The convention is:
|
|
14
|
+
#
|
|
15
|
+
# === sw- CSS Prefix Convention
|
|
16
|
+
#
|
|
17
|
+
# All visual skills CSS classes MUST use the +sw-+ prefix to avoid
|
|
18
|
+
# collisions with user CSS and third-party libraries.
|
|
19
|
+
#
|
|
20
|
+
# BEM naming: sw-component--variant (e.g. sw-card--hero)
|
|
21
|
+
# State: sw-is-active, sw-is-selected
|
|
22
|
+
# Utility: sw-text-dim, sw-bg-surface
|
|
23
|
+
#
|
|
24
|
+
# === CSS Custom Properties
|
|
25
|
+
#
|
|
26
|
+
# Semantic tokens (set per light/dark mode):
|
|
27
|
+
# --sw-bg Page background
|
|
28
|
+
# --sw-surface Card / panel surface
|
|
29
|
+
# --sw-surface-elevated Elevated surface (dropdowns, popovers)
|
|
30
|
+
# --sw-border Default border color
|
|
31
|
+
# --sw-text Primary text color
|
|
32
|
+
# --sw-text-dim Secondary / muted text
|
|
33
|
+
# --sw-accent Primary accent color
|
|
34
|
+
# --sw-node-a/b/c Diagram node palette
|
|
35
|
+
# --sw-success/warning/error/info Status colors
|
|
36
|
+
#
|
|
37
|
+
# These are _additional_ to the existing --sw-color-* tokens.
|
|
38
|
+
# Components should prefer the short --sw-* names; the --sw-color-*
|
|
39
|
+
# variants remain for backward compatibility.
|
|
40
|
+
#
|
|
41
|
+
# @example Register a custom theme
|
|
42
|
+
# StreamWeaver.register_theme :corporate, {
|
|
43
|
+
# font_family: "'Inter', system-ui, sans-serif",
|
|
44
|
+
# color_primary: "#0066cc",
|
|
45
|
+
# spacing_md: "1rem"
|
|
46
|
+
# }, base: :dashboard
|
|
47
|
+
#
|
|
48
|
+
# @example Use in an app
|
|
49
|
+
# app "Corporate App", theme: :corporate do
|
|
50
|
+
# text "Hello, styled world!"
|
|
51
|
+
# end
|
|
52
|
+
class Theme
|
|
53
|
+
# Variable schema: maps Ruby keys to CSS variables with type info
|
|
54
|
+
# Types: :string, :size, :number, :color
|
|
55
|
+
VARIABLE_SCHEMA = {
|
|
56
|
+
# Typography
|
|
57
|
+
font_display: { css: "--sw-font-display", type: :string },
|
|
58
|
+
font_body: { css: "--sw-font-body", type: :string },
|
|
59
|
+
font_family: { css: "--sw-font-family", type: :string },
|
|
60
|
+
font_size_base: { css: "--sw-font-size-base", type: :size },
|
|
61
|
+
font_size_sm: { css: "--sw-font-size-sm", type: :size },
|
|
62
|
+
font_size_lg: { css: "--sw-font-size-lg", type: :size },
|
|
63
|
+
font_size_xl: { css: "--sw-font-size-xl", type: :size },
|
|
64
|
+
line_height: { css: "--sw-line-height", type: :number },
|
|
65
|
+
|
|
66
|
+
# Colors - Primary
|
|
67
|
+
color_primary: { css: "--sw-color-primary", type: :color },
|
|
68
|
+
color_primary_hover: { css: "--sw-color-primary-hover", type: :color },
|
|
69
|
+
color_primary_light: { css: "--sw-color-primary-light", type: :color },
|
|
70
|
+
color_primary_glow: { css: "--sw-color-primary-glow", type: :color },
|
|
71
|
+
|
|
72
|
+
# Colors - Neutrals
|
|
73
|
+
color_text: { css: "--sw-color-text", type: :color },
|
|
74
|
+
color_text_muted: { css: "--sw-color-text-muted", type: :color },
|
|
75
|
+
color_text_light: { css: "--sw-color-text-light", type: :color },
|
|
76
|
+
color_bg: { css: "--sw-color-bg", type: :color },
|
|
77
|
+
color_bg_card: { css: "--sw-color-bg-card", type: :color },
|
|
78
|
+
color_bg_elevated: { css: "--sw-color-bg-elevated", type: :color },
|
|
79
|
+
color_border: { css: "--sw-color-border", type: :color },
|
|
80
|
+
color_border_strong: { css: "--sw-color-border-strong", type: :color },
|
|
81
|
+
color_border_focus: { css: "--sw-color-border-focus", type: :color },
|
|
82
|
+
|
|
83
|
+
# Colors - Secondary
|
|
84
|
+
color_secondary: { css: "--sw-color-secondary", type: :color },
|
|
85
|
+
color_secondary_hover: { css: "--sw-color-secondary-hover", type: :color },
|
|
86
|
+
color_secondary_foreground: { css: "--sw-color-secondary-foreground", type: :color },
|
|
87
|
+
|
|
88
|
+
# Colors - Accent
|
|
89
|
+
color_accent: { css: "--sw-color-accent", type: :color },
|
|
90
|
+
color_accent_light: { css: "--sw-color-accent-light", type: :color },
|
|
91
|
+
|
|
92
|
+
# Spacing
|
|
93
|
+
spacing_xs: { css: "--sw-spacing-xs", type: :size },
|
|
94
|
+
spacing_sm: { css: "--sw-spacing-sm", type: :size },
|
|
95
|
+
spacing_md: { css: "--sw-spacing-md", type: :size },
|
|
96
|
+
spacing_lg: { css: "--sw-spacing-lg", type: :size },
|
|
97
|
+
spacing_xl: { css: "--sw-spacing-xl", type: :size },
|
|
98
|
+
spacing_2xl: { css: "--sw-spacing-2xl", type: :size },
|
|
99
|
+
|
|
100
|
+
# Border Radius
|
|
101
|
+
radius_sm: { css: "--sw-radius-sm", type: :size },
|
|
102
|
+
radius_md: { css: "--sw-radius-md", type: :size },
|
|
103
|
+
radius_lg: { css: "--sw-radius-lg", type: :size },
|
|
104
|
+
radius_xl: { css: "--sw-radius-xl", type: :size },
|
|
105
|
+
|
|
106
|
+
# Shadows
|
|
107
|
+
shadow_sm: { css: "--sw-shadow-sm", type: :string },
|
|
108
|
+
shadow_md: { css: "--sw-shadow-md", type: :string },
|
|
109
|
+
shadow_lg: { css: "--sw-shadow-lg", type: :string },
|
|
110
|
+
shadow_xl: { css: "--sw-shadow-xl", type: :string },
|
|
111
|
+
shadow_inner: { css: "--sw-shadow-inner", type: :string },
|
|
112
|
+
|
|
113
|
+
# Card styling
|
|
114
|
+
card_border_left: { css: "--sw-card-border-left", type: :string },
|
|
115
|
+
|
|
116
|
+
# Term highlighting
|
|
117
|
+
term_color: { css: "--sw-term-color", type: :color },
|
|
118
|
+
term_bg_hover: { css: "--sw-term-bg-hover", type: :color },
|
|
119
|
+
|
|
120
|
+
# ============================================
|
|
121
|
+
# Visual Skills Semantic Tokens (T2 foundation)
|
|
122
|
+
# These short-form --sw-* tokens are the primary
|
|
123
|
+
# vocabulary for visual skills components.
|
|
124
|
+
# ============================================
|
|
125
|
+
|
|
126
|
+
# Surfaces
|
|
127
|
+
vs_bg: { css: "--sw-bg", type: :color },
|
|
128
|
+
vs_surface: { css: "--sw-surface", type: :color },
|
|
129
|
+
vs_surface_elevated: { css: "--sw-surface-elevated", type: :color },
|
|
130
|
+
vs_border: { css: "--sw-border", type: :color },
|
|
131
|
+
|
|
132
|
+
# Text
|
|
133
|
+
vs_text: { css: "--sw-text", type: :color },
|
|
134
|
+
vs_text_dim: { css: "--sw-text-dim", type: :color },
|
|
135
|
+
vs_accent: { css: "--sw-accent", type: :color },
|
|
136
|
+
|
|
137
|
+
# Diagram node colors
|
|
138
|
+
vs_node_a: { css: "--sw-node-a", type: :color },
|
|
139
|
+
vs_node_b: { css: "--sw-node-b", type: :color },
|
|
140
|
+
vs_node_c: { css: "--sw-node-c", type: :color },
|
|
141
|
+
|
|
142
|
+
# Status colors
|
|
143
|
+
vs_success: { css: "--sw-success", type: :color },
|
|
144
|
+
vs_warning: { css: "--sw-warning", type: :color },
|
|
145
|
+
vs_error: { css: "--sw-error", type: :color },
|
|
146
|
+
vs_info: { css: "--sw-info", type: :color },
|
|
147
|
+
|
|
148
|
+
# Monospace font (for code blocks, dir trees)
|
|
149
|
+
font_mono: { css: "--sw-font-mono", type: :string }
|
|
150
|
+
}.freeze
|
|
151
|
+
|
|
152
|
+
# Built-in theme definitions (matches CSS in views.rb)
|
|
153
|
+
BUILT_IN_THEMES = {
|
|
154
|
+
default: {
|
|
155
|
+
name: "Default",
|
|
156
|
+
description: "Warm Industrial",
|
|
157
|
+
variables: {
|
|
158
|
+
font_display: "'Source Sans 3', system-ui, sans-serif",
|
|
159
|
+
font_body: "'Source Sans 3', system-ui, sans-serif",
|
|
160
|
+
font_size_base: "17px",
|
|
161
|
+
line_height: "1.7",
|
|
162
|
+
color_primary: "#a16207",
|
|
163
|
+
color_bg: "#f2ede4",
|
|
164
|
+
color_text: "#111111",
|
|
165
|
+
spacing_md: "1.25rem",
|
|
166
|
+
card_border_left: "3px solid var(--sw-color-primary)"
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
dashboard: {
|
|
170
|
+
name: "Dashboard",
|
|
171
|
+
description: "Data Dense",
|
|
172
|
+
variables: {
|
|
173
|
+
font_display: "'Source Sans 3', system-ui, sans-serif",
|
|
174
|
+
font_body: "'Source Sans 3', system-ui, sans-serif",
|
|
175
|
+
font_size_base: "15px",
|
|
176
|
+
line_height: "1.5",
|
|
177
|
+
color_primary: "#a16207",
|
|
178
|
+
color_bg: "#fafafa",
|
|
179
|
+
color_text: "#111111",
|
|
180
|
+
spacing_md: "0.75rem",
|
|
181
|
+
card_border_left: "1px solid var(--sw-color-border)"
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
document: {
|
|
185
|
+
name: "Document",
|
|
186
|
+
description: "Reading Mode",
|
|
187
|
+
variables: {
|
|
188
|
+
font_display: "'Source Sans 3', system-ui, sans-serif",
|
|
189
|
+
font_body: "'Crimson Pro', Georgia, 'Times New Roman', serif",
|
|
190
|
+
font_size_base: "19px",
|
|
191
|
+
line_height: "1.85",
|
|
192
|
+
color_primary: "#6b7280",
|
|
193
|
+
color_bg: "#faf8f5",
|
|
194
|
+
color_text: "#1a1a1a",
|
|
195
|
+
spacing_md: "1.5rem",
|
|
196
|
+
card_border_left: "none"
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
doc: {
|
|
200
|
+
name: "Doc",
|
|
201
|
+
description: "Compact Editorial",
|
|
202
|
+
variables: {
|
|
203
|
+
font_display: "Charter, 'Bitstream Charter', 'Sitka Text', Cambria, Georgia, serif",
|
|
204
|
+
font_body: "-apple-system, BlinkMacSystemFont, 'Helvetica Neue', Arial, sans-serif",
|
|
205
|
+
font_size_base: "15px",
|
|
206
|
+
line_height: "1.65",
|
|
207
|
+
color_primary: "#1E4ED8",
|
|
208
|
+
color_bg: "#F5F4EF",
|
|
209
|
+
color_text: "#181714",
|
|
210
|
+
spacing_md: "1rem",
|
|
211
|
+
card_border_left: "none"
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}.freeze
|
|
215
|
+
|
|
216
|
+
attr_reader :name, :variables, :base_theme, :label, :description
|
|
217
|
+
|
|
218
|
+
# @param name [Symbol] Theme identifier
|
|
219
|
+
# @param variables [Hash] Theme variable overrides
|
|
220
|
+
# @param base [Symbol] Base theme to inherit from (:default, :dashboard, :document)
|
|
221
|
+
# @param label [String] Human-readable name
|
|
222
|
+
# @param description [String] Short description
|
|
223
|
+
def initialize(name, variables = {}, base: :default, label: nil, description: nil)
|
|
224
|
+
@name = name.to_sym
|
|
225
|
+
@base_theme = base.to_sym
|
|
226
|
+
@variables = normalize_variables(variables)
|
|
227
|
+
@label = label || name.to_s.split('_').map(&:capitalize).join(' ')
|
|
228
|
+
@description = description || "Custom theme"
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Generate CSS variable declarations for this theme
|
|
232
|
+
#
|
|
233
|
+
# @return [String] CSS variable block
|
|
234
|
+
def to_css
|
|
235
|
+
css_lines = @variables.map do |key, value|
|
|
236
|
+
schema = VARIABLE_SCHEMA[key]
|
|
237
|
+
css_var = schema ? schema[:css] : "--sw-#{key.to_s.tr('_', '-')}"
|
|
238
|
+
" #{css_var}: #{value};"
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
"body.sw-theme-#{@name} {\n#{css_lines.join("\n")}\n}"
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Export as Ruby registration code
|
|
245
|
+
#
|
|
246
|
+
# @return [String] Ruby code to register this theme
|
|
247
|
+
def to_ruby
|
|
248
|
+
vars = @variables.map { |k, v| " #{k}: #{v.inspect}" }.join(",\n")
|
|
249
|
+
<<~RUBY
|
|
250
|
+
StreamWeaver.register_theme :#{@name}, {
|
|
251
|
+
#{vars}
|
|
252
|
+
}, base: :#{@base_theme}, label: #{@label.inspect}, description: #{@description.inspect}
|
|
253
|
+
RUBY
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Export as JSON
|
|
257
|
+
#
|
|
258
|
+
# @return [String] JSON representation
|
|
259
|
+
def to_json(*_args)
|
|
260
|
+
JSON.pretty_generate(
|
|
261
|
+
name: @name,
|
|
262
|
+
label: @label,
|
|
263
|
+
description: @description,
|
|
264
|
+
base_theme: @base_theme,
|
|
265
|
+
variables: @variables
|
|
266
|
+
)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Export as Hash
|
|
270
|
+
#
|
|
271
|
+
# @return [Hash] Hash representation
|
|
272
|
+
def to_h
|
|
273
|
+
{
|
|
274
|
+
name: @name,
|
|
275
|
+
label: @label,
|
|
276
|
+
description: @description,
|
|
277
|
+
base_theme: @base_theme,
|
|
278
|
+
variables: @variables
|
|
279
|
+
}
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# Load theme from JSON
|
|
283
|
+
#
|
|
284
|
+
# @param json_string [String] JSON theme definition
|
|
285
|
+
# @return [Theme] New theme instance
|
|
286
|
+
def self.from_json(json_string)
|
|
287
|
+
data = JSON.parse(json_string, symbolize_names: true)
|
|
288
|
+
new(
|
|
289
|
+
data[:name],
|
|
290
|
+
data[:variables],
|
|
291
|
+
base: data[:base_theme] || :default,
|
|
292
|
+
label: data[:label],
|
|
293
|
+
description: data[:description]
|
|
294
|
+
)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# Load theme from file
|
|
298
|
+
#
|
|
299
|
+
# @param path [String] Path to JSON file
|
|
300
|
+
# @return [Theme] New theme instance
|
|
301
|
+
def self.from_file(path)
|
|
302
|
+
from_json(File.read(path))
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# Get all available variable names
|
|
306
|
+
#
|
|
307
|
+
# @return [Array<Symbol>] Variable names
|
|
308
|
+
def self.variable_names
|
|
309
|
+
VARIABLE_SCHEMA.keys
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Get variables grouped by category
|
|
313
|
+
#
|
|
314
|
+
# @return [Hash] Variables grouped by category
|
|
315
|
+
def self.variables_by_category
|
|
316
|
+
{
|
|
317
|
+
typography: %i[font_display font_body font_family font_size_base font_size_sm font_size_lg font_size_xl line_height],
|
|
318
|
+
colors_primary: %i[color_primary color_primary_hover color_primary_light color_primary_glow],
|
|
319
|
+
colors_neutral: %i[color_text color_text_muted color_text_light color_bg color_bg_card color_bg_elevated color_border color_border_strong color_border_focus],
|
|
320
|
+
colors_secondary: %i[color_secondary color_secondary_hover color_secondary_foreground color_accent color_accent_light],
|
|
321
|
+
spacing: %i[spacing_xs spacing_sm spacing_md spacing_lg spacing_xl spacing_2xl],
|
|
322
|
+
border_radius: %i[radius_sm radius_md radius_lg radius_xl],
|
|
323
|
+
shadows: %i[shadow_sm shadow_md shadow_lg shadow_xl shadow_inner],
|
|
324
|
+
components: %i[card_border_left term_color term_bg_hover]
|
|
325
|
+
}
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# Generate the visual skills CSS custom properties block.
|
|
329
|
+
# This produces the --sw-* semantic tokens for both light and dark modes,
|
|
330
|
+
# using sensible defaults that bridge to existing --sw-color-* tokens.
|
|
331
|
+
#
|
|
332
|
+
# @return [String] CSS with :root and html.dark blocks
|
|
333
|
+
def self.visual_skills_css
|
|
334
|
+
<<~CSS
|
|
335
|
+
/* ===========================================
|
|
336
|
+
Visual Skills CSS Foundation (T2)
|
|
337
|
+
Semantic tokens for visual skills components.
|
|
338
|
+
All classes use sw- prefix with BEM modifiers:
|
|
339
|
+
sw-component--variant (e.g. sw-card--hero)
|
|
340
|
+
sw-is-active (state classes)
|
|
341
|
+
=========================================== */
|
|
342
|
+
|
|
343
|
+
/* Light mode defaults -- bridge to existing --sw-color-* tokens */
|
|
344
|
+
:root {
|
|
345
|
+
--sw-bg: var(--sw-color-bg, #f8f8f8);
|
|
346
|
+
--sw-surface: var(--sw-color-bg-card, #ffffff);
|
|
347
|
+
--sw-surface-elevated: var(--sw-color-bg-elevated, #f3f3f3);
|
|
348
|
+
--sw-border: var(--sw-color-border, #e0e0e0);
|
|
349
|
+
--sw-text: var(--sw-color-text, #111111);
|
|
350
|
+
--sw-text-dim: var(--sw-color-text-muted, #444444);
|
|
351
|
+
--sw-accent: var(--sw-color-accent, #0d9488);
|
|
352
|
+
--sw-font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
|
|
353
|
+
|
|
354
|
+
/* Diagram node palette */
|
|
355
|
+
--sw-node-a: var(--sw-color-primary, #c2410c);
|
|
356
|
+
--sw-node-b: var(--sw-color-accent, #0d9488);
|
|
357
|
+
--sw-node-c: #7c3aed;
|
|
358
|
+
|
|
359
|
+
/* Status colors */
|
|
360
|
+
--sw-success: #16a34a;
|
|
361
|
+
--sw-warning: #d97706;
|
|
362
|
+
--sw-error: #dc2626;
|
|
363
|
+
--sw-info: #2563eb;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/* Re-bridge on body: --sw-color-* (what these fall back to) is itself
|
|
367
|
+
declared on body (per-theme, or an app's theme_overrides:), a
|
|
368
|
+
descendant of :root -- so the :root block above can never see it and
|
|
369
|
+
is stuck on its own light-mode fallback. Redeclaring here lets these
|
|
370
|
+
short tokens track whatever theme body actually ends up with
|
|
371
|
+
(stream_weaver-9u2). */
|
|
372
|
+
body {
|
|
373
|
+
--sw-bg: var(--sw-color-bg, #f8f8f8);
|
|
374
|
+
--sw-surface: var(--sw-color-bg-card, #ffffff);
|
|
375
|
+
--sw-surface-elevated: var(--sw-color-bg-elevated, #f3f3f3);
|
|
376
|
+
--sw-border: var(--sw-color-border, #e0e0e0);
|
|
377
|
+
--sw-text: var(--sw-color-text, #111111);
|
|
378
|
+
--sw-text-dim: var(--sw-color-text-muted, #444444);
|
|
379
|
+
--sw-accent: var(--sw-color-accent, #0d9488);
|
|
380
|
+
--sw-node-a: var(--sw-color-primary, #c2410c);
|
|
381
|
+
--sw-node-b: var(--sw-color-accent, #0d9488);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/* Theme toggle button (sw- prefixed, BEM) */
|
|
385
|
+
.sw-theme-toggle {
|
|
386
|
+
display: inline-flex;
|
|
387
|
+
align-items: center;
|
|
388
|
+
gap: 0.375rem;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.sw-theme-toggle__btn {
|
|
392
|
+
background: transparent;
|
|
393
|
+
border: 1px solid var(--sw-border);
|
|
394
|
+
border-radius: var(--sw-radius-md, 6px);
|
|
395
|
+
padding: 0.375rem 0.5rem;
|
|
396
|
+
cursor: pointer;
|
|
397
|
+
font-size: 1.1rem;
|
|
398
|
+
line-height: 1;
|
|
399
|
+
color: var(--sw-text);
|
|
400
|
+
transition: background 200ms ease-out, border-color 200ms ease-out;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.sw-theme-toggle__btn:hover {
|
|
404
|
+
background: var(--sw-surface-elevated);
|
|
405
|
+
border-color: var(--sw-accent);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.sw-theme-toggle__label {
|
|
409
|
+
font-size: 0.75rem;
|
|
410
|
+
color: var(--sw-text-dim);
|
|
411
|
+
text-transform: uppercase;
|
|
412
|
+
letter-spacing: 0.05em;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/* Dark mode overrides */
|
|
416
|
+
html.dark {
|
|
417
|
+
--sw-bg: var(--sw-color-bg, oklch(0.145 0 0));
|
|
418
|
+
--sw-surface: var(--sw-color-bg-card, oklch(0.205 0 0));
|
|
419
|
+
--sw-surface-elevated: var(--sw-color-bg-elevated, oklch(0.269 0 0));
|
|
420
|
+
--sw-border: var(--sw-color-border, oklch(1 0 0 / 10%));
|
|
421
|
+
--sw-text: var(--sw-color-text, oklch(0.985 0 0));
|
|
422
|
+
--sw-text-dim: var(--sw-color-text-muted, oklch(0.708 0 0));
|
|
423
|
+
--sw-accent: var(--sw-color-accent, #2dd4bf);
|
|
424
|
+
|
|
425
|
+
/* Brighter node colors for dark backgrounds */
|
|
426
|
+
--sw-node-a: #f97316;
|
|
427
|
+
--sw-node-b: #2dd4bf;
|
|
428
|
+
--sw-node-c: #a78bfa;
|
|
429
|
+
|
|
430
|
+
/* Brighter status colors for dark backgrounds */
|
|
431
|
+
--sw-success: #22c55e;
|
|
432
|
+
--sw-warning: #fbbf24;
|
|
433
|
+
--sw-error: #f87171;
|
|
434
|
+
--sw-info: #60a5fa;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/* ===========================================
|
|
438
|
+
Card Depth Tiers (T6)
|
|
439
|
+
sw-card--{depth} classes for visual hierarchy.
|
|
440
|
+
=========================================== */
|
|
441
|
+
|
|
442
|
+
.sw-card--hero {
|
|
443
|
+
background: color-mix(in oklch, var(--sw-accent) 8%, var(--sw-surface));
|
|
444
|
+
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.08);
|
|
445
|
+
border-left: 4px solid var(--sw-accent);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.sw-card--elevated {
|
|
449
|
+
background: var(--sw-surface);
|
|
450
|
+
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.06);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.sw-card--default {
|
|
454
|
+
background: var(--sw-surface);
|
|
455
|
+
border: 1px solid var(--sw-border);
|
|
456
|
+
box-shadow: none;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.sw-card--recessed {
|
|
460
|
+
background: var(--sw-surface-elevated);
|
|
461
|
+
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
462
|
+
border: 1px solid var(--sw-border);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.sw-card--glass {
|
|
466
|
+
background: rgba(255, 255, 255, 0.08);
|
|
467
|
+
backdrop-filter: blur(12px);
|
|
468
|
+
-webkit-backdrop-filter: blur(12px);
|
|
469
|
+
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
470
|
+
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.06);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
html.dark .sw-card--glass {
|
|
474
|
+
background: rgba(255, 255, 255, 0.05);
|
|
475
|
+
border-color: rgba(255, 255, 255, 0.1);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/* Card accent borders (left border colored by node palette) */
|
|
479
|
+
.sw-card--accent-a { border-left: 4px solid var(--sw-node-a); }
|
|
480
|
+
.sw-card--accent-b { border-left: 4px solid var(--sw-node-b); }
|
|
481
|
+
.sw-card--accent-c { border-left: 4px solid var(--sw-node-c); }
|
|
482
|
+
.sw-card--accent { border-left: 4px solid; }
|
|
483
|
+
|
|
484
|
+
/* Card corner label */
|
|
485
|
+
.sw-card__label {
|
|
486
|
+
position: absolute;
|
|
487
|
+
top: 0.5rem;
|
|
488
|
+
right: 0.5rem;
|
|
489
|
+
font-size: 0.625rem;
|
|
490
|
+
font-weight: 700;
|
|
491
|
+
letter-spacing: 0.08em;
|
|
492
|
+
text-transform: uppercase;
|
|
493
|
+
padding: 0.125rem 0.5rem;
|
|
494
|
+
border-radius: var(--sw-radius-sm, 4px);
|
|
495
|
+
background: var(--sw-accent);
|
|
496
|
+
color: white;
|
|
497
|
+
line-height: 1.4;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/* Card with label: position: relative is applied inline by the component */
|
|
501
|
+
|
|
502
|
+
/* ===========================================
|
|
503
|
+
Table Enhancements (T6)
|
|
504
|
+
sw-table--{feature} classes for enhanced tables.
|
|
505
|
+
=========================================== */
|
|
506
|
+
|
|
507
|
+
/* Alternating row backgrounds */
|
|
508
|
+
.sw-table--alternating .sw-table__row--alt {
|
|
509
|
+
background: var(--sw-surface-elevated, rgba(0, 0, 0, 0.02));
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
html.dark .sw-table--alternating .sw-table__row--alt {
|
|
513
|
+
background: rgba(255, 255, 255, 0.03);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/* Row hover highlight */
|
|
517
|
+
.sw-table--hover .sw-table__row--hover:hover {
|
|
518
|
+
background: color-mix(in oklch, var(--sw-accent) 6%, transparent);
|
|
519
|
+
transition: background 150ms ease-out;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/* Scrollable wrapper */
|
|
523
|
+
.sw-table--scrollable {
|
|
524
|
+
border: 1px solid var(--sw-border, #e0e0e0);
|
|
525
|
+
border-radius: var(--sw-radius-md, 6px);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/* Sticky header styling */
|
|
529
|
+
.sw-table--sticky-header thead {
|
|
530
|
+
position: sticky;
|
|
531
|
+
top: 0;
|
|
532
|
+
z-index: 1;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
#{Presets.animations_css}
|
|
536
|
+
CSS
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
# Generate a Google Fonts <link> tag URL for the given font families.
|
|
540
|
+
#
|
|
541
|
+
# @param families [Array<String>] Google Fonts family strings
|
|
542
|
+
# e.g. ["Instrument+Serif:ital@0;1", "JetBrains+Mono:wght@400;500"]
|
|
543
|
+
# @return [String] Full Google Fonts CSS URL
|
|
544
|
+
def self.google_fonts_url(*families)
|
|
545
|
+
families = families.flatten
|
|
546
|
+
query = families.map { |f| "family=#{f}" }.join("&")
|
|
547
|
+
"https://fonts.googleapis.com/css2?#{query}&display=swap"
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
private
|
|
551
|
+
|
|
552
|
+
# Normalize variable keys to symbols
|
|
553
|
+
def normalize_variables(vars)
|
|
554
|
+
vars.transform_keys(&:to_sym)
|
|
555
|
+
end
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
# Theme registry - stores custom themes
|
|
559
|
+
@themes = {}
|
|
560
|
+
|
|
561
|
+
class << self
|
|
562
|
+
# Access the theme registry
|
|
563
|
+
#
|
|
564
|
+
# @return [Hash<Symbol, Theme>] Registered themes
|
|
565
|
+
def themes
|
|
566
|
+
@themes
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
# Register a custom theme
|
|
570
|
+
#
|
|
571
|
+
# @param name [Symbol] Theme identifier (used in theme: :name)
|
|
572
|
+
# @param variables [Hash] CSS variable overrides
|
|
573
|
+
# @param base [Symbol] Base theme to inherit from
|
|
574
|
+
# @param label [String] Human-readable name
|
|
575
|
+
# @param description [String] Short description
|
|
576
|
+
# @return [Theme] The registered theme
|
|
577
|
+
#
|
|
578
|
+
# @example Basic registration
|
|
579
|
+
# StreamWeaver.register_theme :corporate, {
|
|
580
|
+
# color_primary: "#0066cc",
|
|
581
|
+
# font_family: "'Inter', system-ui, sans-serif"
|
|
582
|
+
# }
|
|
583
|
+
#
|
|
584
|
+
# @example With base theme
|
|
585
|
+
# StreamWeaver.register_theme :compact_corporate, {
|
|
586
|
+
# color_primary: "#0066cc"
|
|
587
|
+
# }, base: :dashboard
|
|
588
|
+
def register_theme(name, variables = {}, base: :default, label: nil, description: nil)
|
|
589
|
+
theme = Theme.new(name, variables, base: base, label: label, description: description)
|
|
590
|
+
@themes[name.to_sym] = theme
|
|
591
|
+
theme
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
# Get a theme by name (custom or built-in)
|
|
595
|
+
#
|
|
596
|
+
# @param name [Symbol] Theme name
|
|
597
|
+
# @return [Theme, nil] The theme or nil if not found
|
|
598
|
+
def get_theme(name)
|
|
599
|
+
name = name.to_sym
|
|
600
|
+
@themes[name] || (Theme::BUILT_IN_THEMES[name] && Theme.new(
|
|
601
|
+
name,
|
|
602
|
+
Theme::BUILT_IN_THEMES[name][:variables],
|
|
603
|
+
label: Theme::BUILT_IN_THEMES[name][:name],
|
|
604
|
+
description: Theme::BUILT_IN_THEMES[name][:description]
|
|
605
|
+
))
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
# Check if a theme exists (custom or built-in)
|
|
609
|
+
#
|
|
610
|
+
# @param name [Symbol] Theme name
|
|
611
|
+
# @return [Boolean]
|
|
612
|
+
def theme_exists?(name)
|
|
613
|
+
name = name.to_sym
|
|
614
|
+
@themes.key?(name) || Theme::BUILT_IN_THEMES.key?(name)
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
# Get all available theme names
|
|
618
|
+
#
|
|
619
|
+
# @return [Array<Symbol>] Theme names
|
|
620
|
+
def available_themes
|
|
621
|
+
(Theme::BUILT_IN_THEMES.keys + @themes.keys).uniq
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
# Get all themes as an array suitable for theme_switcher
|
|
625
|
+
#
|
|
626
|
+
# @return [Array<Hash>] Array of theme info hashes
|
|
627
|
+
def all_themes_for_switcher
|
|
628
|
+
built_in = Theme::BUILT_IN_THEMES.map do |id, info|
|
|
629
|
+
{ id: id, label: info[:name], description: info[:description] }
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
custom = @themes.map do |id, theme|
|
|
633
|
+
{ id: id, label: theme.label, description: theme.description }
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
built_in + custom
|
|
637
|
+
end
|
|
638
|
+
end
|
|
639
|
+
end
|