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.
Files changed (381) hide show
  1. checksums.yaml +7 -0
  2. data/.beads/.gitignore +77 -0
  3. data/.beads/README.md +81 -0
  4. data/.beads/config.yaml +54 -0
  5. data/.beads/metadata.json +7 -0
  6. data/.rspec +3 -0
  7. data/AGENTS.md +91 -0
  8. data/CHANGELOG.md +294 -0
  9. data/CLAUDE.md +67 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +565 -0
  12. data/Rakefile +13 -0
  13. data/assets/streamweaver-hero.jpg +0 -0
  14. data/bench/baselines/ledger.rb +58 -0
  15. data/bench/baselines/warroom.rb +51 -0
  16. data/bench/fixtures/ledger.rb +76 -0
  17. data/bench/fixtures/warroom.rb +60 -0
  18. data/bench/results/6daf8b8.md +102 -0
  19. data/bench/results/c8d749b.md +102 -0
  20. data/bench/results/eb06ab9.md +102 -0
  21. data/bench/run.rb +151 -0
  22. data/bench/support.rb +84 -0
  23. data/copy_this/dto_run_sample_run_7631.html +220 -0
  24. data/docs/SERVICE_MODE.md +302 -0
  25. data/docs/architecture/how_streamweaver_works.md +772 -0
  26. data/docs/blog/2026-01-16-streamweaver-introduction.md +515 -0
  27. data/docs/bug-2026-08-31-canvas-bridge-socket-collision.md +83 -0
  28. data/docs/canvas-ipc-session-summary.md +127 -0
  29. data/docs/canvas-panel-workflow.md +172 -0
  30. data/docs/canvas-read.md +147 -0
  31. data/docs/canvas-roadmap.md +275 -0
  32. data/docs/case-studies/2026-09-03-didx-canvas-read-shelf.md +147 -0
  33. data/docs/claude-code-companion-skill-spec.md +567 -0
  34. data/docs/components_reference.md +1223 -0
  35. data/docs/crud-patterns.md +265 -0
  36. data/docs/endpoints.md +122 -0
  37. data/docs/for_llms.md +1 -0
  38. data/docs/form-patterns.md +165 -0
  39. data/docs/frontend-only.md +169 -0
  40. data/docs/html-artifact-audit.md +238 -0
  41. data/docs/ideas/2025-01-01-as-a-service.md +203 -0
  42. data/docs/ideas/2026-01-16-charm-tui-exploration.md +542 -0
  43. data/docs/ideas/2026-08-27-static-doc-shelf-export.md +153 -0
  44. data/docs/ideas/dhh-review-future-refactors.md +142 -0
  45. data/docs/opal-jamstack.md +47 -0
  46. data/docs/opal-npm-direction.md +184 -0
  47. data/docs/opal-spike-findings.md +194 -0
  48. data/docs/plans/2025-12-16-form-blocks-design.md +124 -0
  49. data/docs/plans/2025-12-30-examples-browser-design.md +152 -0
  50. data/docs/plans/2026-01-02-charts-design.md +288 -0
  51. data/docs/plans/2026-01-10-canvas-ipc-design.md +247 -0
  52. data/docs/plans/2026-01-19-cabinet-control-components.md +176 -0
  53. data/docs/plans/2026-01-26-gem-release-and-panel.md +140 -0
  54. data/docs/plans/canvas-claude-project-design.md +238 -0
  55. data/docs/plans/canvas-doc-location-and-discovery.md +32 -0
  56. data/docs/plans/org-doc-preview-surfaces.md +293 -0
  57. data/docs/plans/shadcn-polish-plan.md +417 -0
  58. data/docs/porting-artifacts.md +252 -0
  59. data/docs/reference/agent-skills-comparison.md +369 -0
  60. data/docs/reference/travel-state-prd.artifact.html +780 -0
  61. data/docs/research/2026-08-17-hotwire-alike-landscape.md +268 -0
  62. data/docs/research/2026-08-17-hotwire-concept-map.md +404 -0
  63. data/docs/research/2026-08-22-lazy-fragments-trigger-decision.md +246 -0
  64. data/docs/research/2026-08-22-learnhotwire-syllabus-coverage.md +239 -0
  65. data/docs/research/frontend-only-matrix.md +278 -0
  66. data/docs/research/streamweaver-way-spike-findings.md +529 -0
  67. data/docs/resource-dsl.md +342 -0
  68. data/docs/routing.md +302 -0
  69. data/docs/ruby-ui-comparison.md +319 -0
  70. data/docs/shared-dsl-fragments.md +356 -0
  71. data/docs/streamweaver-for-ai-agents.md +401 -0
  72. data/docs/streamweaver-frontend-vision.md +123 -0
  73. data/docs/streamweaver_canvas/glimmer_initial_final_layer.rb +77 -0
  74. data/docs/streamweaver_canvas/save_example.rb +9 -0
  75. data/docs/templates.md +213 -0
  76. data/docs/testing.md +155 -0
  77. data/docs/theming-hooks.md +281 -0
  78. data/docs/tutorials/the-streamweaver-way.md +882 -0
  79. data/docs/university/capability-inventory.md +137 -0
  80. data/docs/university/dependency-survey.md +114 -0
  81. data/docs/university/design-spec.md +217 -0
  82. data/docs/university/mockups/code-block-doc-theme-dark.png +0 -0
  83. data/docs/university/mockups/code-block-doc-theme-light.png +0 -0
  84. data/docs/university/mockups/course-list-dark.png +0 -0
  85. data/docs/university/mockups/course-list-light.png +0 -0
  86. data/docs/university/mockups/course_canvas_mockup.rb +741 -0
  87. data/docs/university/mockups/step-screen-dark.png +0 -0
  88. data/docs/university/mockups/step-screen-light.png +0 -0
  89. data/docs/university/roadmap.md +72 -0
  90. data/docs/university/send-to-coworker.md +77 -0
  91. data/docs/university/worker-session-mining.md +109 -0
  92. data/docs/visual/browser-rendering-spike.md +173 -0
  93. data/docs/visual/builder-visual-plan-analysis.md +378 -0
  94. data/docs/visual/sw-plan-format-exploration.md +511 -0
  95. data/docs/visual/sw-plan-rendering-deep-dive.md +453 -0
  96. data/docs/visual-skills/PROGRESS.md +60 -0
  97. data/docs/visual-skills/SESSION-CONTEXT.md +207 -0
  98. data/docs/visual-skills/analysis/components.md +317 -0
  99. data/docs/visual-skills/analysis/overlap.md +360 -0
  100. data/docs/visual-skills/analysis/pi-design-deck.md +1102 -0
  101. data/docs/visual-skills/analysis/streamweaver-inventory.md +93 -0
  102. data/docs/visual-skills/analysis/unified-specs.feature +1003 -0
  103. data/docs/visual-skills/analysis/visual-explainer.md +964 -0
  104. data/docs/visual-skills/blog/blog-series-outline.md +39 -0
  105. data/docs/visual-skills/blog/token-efficiency.md +73 -0
  106. data/docs/visual-skills/design/architecture.md +1725 -0
  107. data/docs/visual-skills/design/codex-review.md +340 -0
  108. data/docs/visual-skills/design/dhh-review.md +209 -0
  109. data/docs/visual-skills/design/evolution.md +100 -0
  110. data/docs/visual-skills/design/gemini-review.md +55 -0
  111. data/docs/visual-skills/design/review-synthesis.md +149 -0
  112. data/docs/visual-skills/implementation/STATE.md +22 -0
  113. data/docs/visual-skills/implementation/plan.md +347 -0
  114. data/docs/visual-skills/implementation/spike-findings.md +237 -0
  115. data/docs/visual-skills/implementation/tasks.md +702 -0
  116. data/docs/visual-skills/lessons-learned/process.md +145 -0
  117. data/examples/README.md +96 -0
  118. data/examples/advanced/all_components.rb +177 -0
  119. data/examples/advanced/examples_browser.rb +394 -0
  120. data/examples/advanced/teachables_browser.rb +261 -0
  121. data/examples/advanced/theme_tweaker.rb +366 -0
  122. data/examples/advanced/tutorial.rb +1528 -0
  123. data/examples/agentic/agentic_form.rb +30 -0
  124. data/examples/agentic/agentic_form_autoclose.rb +30 -0
  125. data/examples/agentic/cultivation_tracker.rb +69 -0
  126. data/examples/basic/hello_world.rb +22 -0
  127. data/examples/basic/opal_tabs_table.rb +44 -0
  128. data/examples/basic/todo_list.rb +39 -0
  129. data/examples/button_loading_test.rb +36 -0
  130. data/examples/canvas/mermaid_canvas_demo.sh +151 -0
  131. data/examples/charts/bar_chart_demo.rb +57 -0
  132. data/examples/charts/line_chart_demo.rb +73 -0
  133. data/examples/charts/pie_area_demo.rb +87 -0
  134. data/examples/charts/stacked_bar_chart_demo.rb +69 -0
  135. data/examples/claude_code/README.md +85 -0
  136. data/examples/claude_code/codebreaker/.claude/commands/infiltrate.md +632 -0
  137. data/examples/claude_code/codebreaker/.claude/settings.json +11 -0
  138. data/examples/claude_code/codebreaker/.claude/settings.local.json +17 -0
  139. data/examples/claude_code/codebreaker/.claude/skills/infiltrate.md +0 -0
  140. data/examples/claude_code/codebreaker/README.md +213 -0
  141. data/examples/claude_code/tutorial/.claude/commands/learn.md +453 -0
  142. data/examples/claude_code/tutorial/README.md +102 -0
  143. data/examples/claude_code/verification_flow/.claude/commands/verify.md +171 -0
  144. data/examples/claude_code/verification_flow/README.md +126 -0
  145. data/examples/components/annotated_code_demo.rb +54 -0
  146. data/examples/components/callout_demo.rb +33 -0
  147. data/examples/components/checkbox_group_demo.rb +46 -0
  148. data/examples/components/design_review.css +468 -0
  149. data/examples/components/design_review_demo.rb +44 -0
  150. data/examples/components/design_review_dsl.rb +300 -0
  151. data/examples/components/diff_block_demo.rb +112 -0
  152. data/examples/components/events_demo.rb +184 -0
  153. data/examples/components/form_demo.rb +62 -0
  154. data/examples/components/lesson_demo.rb +53 -0
  155. data/examples/components/markdown_demo.rb +110 -0
  156. data/examples/components/mermaid_demo.rb +166 -0
  157. data/examples/components/pareto_set.rb +64 -0
  158. data/examples/components/prd_demo.rb +21 -0
  159. data/examples/components/prd_dsl.rb +385 -0
  160. data/examples/components/quiz_demo.rb +47 -0
  161. data/examples/components/run_viewer_demo.rb +100 -0
  162. data/examples/components/score_and_collapsible_demo.rb +82 -0
  163. data/examples/components/select_stale_value_smoke_test.rb +39 -0
  164. data/examples/components/table_demo.rb +247 -0
  165. data/examples/components/timer_with_state_demo.rb +58 -0
  166. data/examples/components/todo_due_dates.rb +63 -0
  167. data/examples/components/uat_gaps_demo.rb +112 -0
  168. data/examples/dashboard/feed_simulator.rb +60 -0
  169. data/examples/dashboard/live_dashboard.rb +80 -0
  170. data/examples/dashboard_components.rb +108 -0
  171. data/examples/deferred_fragments_demo.rb +62 -0
  172. data/examples/generate_more_spike/README.md +63 -0
  173. data/examples/generate_more_spike/app.rb +441 -0
  174. data/examples/git_health.sh +401 -0
  175. data/examples/layout/layout_components_demo.rb +174 -0
  176. data/examples/layout/modal_demo.rb +215 -0
  177. data/examples/layout/navigation_demo.rb +227 -0
  178. data/examples/layout/route_tabs_demo.rb +52 -0
  179. data/examples/layout/routing_demo.rb +52 -0
  180. data/examples/layout/scroll_box_demo.rb +154 -0
  181. data/examples/lazy_fragments_demo.rb +151 -0
  182. data/examples/my_todos/README.md +29 -0
  183. data/examples/my_todos/my_todos.rb +312 -0
  184. data/examples/my_todos/store.rb +86 -0
  185. data/examples/opal/reactive_demo.rb +62 -0
  186. data/examples/opal/scenarios/s1_counter.rb +15 -0
  187. data/examples/opal/scenarios/s2_search_filter.rb +17 -0
  188. data/examples/opal/scenarios/s3_sibling_tabs.rb +25 -0
  189. data/examples/opal/scenarios/s4_shopping_cart.rb +34 -0
  190. data/examples/opal/scenarios/s5_watch.rb +39 -0
  191. data/examples/opal/scenarios/s6_on_start.rb +20 -0
  192. data/examples/opal/scenarios/s7_wizard.rb +33 -0
  193. data/examples/opal/scenarios/s8_loan_calculator.rb +21 -0
  194. data/examples/opal/scenarios/s9_dashboard.rb +27 -0
  195. data/examples/operations_dashboard_demo.rb +167 -0
  196. data/examples/panel_demo.sh +447 -0
  197. data/examples/parity/assets/tyrion_slice.css +86 -0
  198. data/examples/parity/rivet_people_slice.rb +182 -0
  199. data/examples/parity/tyrion_components.css +777 -0
  200. data/examples/parity/tyrion_warroom_components.rb +414 -0
  201. data/examples/parity/tyrion_warroom_slice.rb +549 -0
  202. data/examples/puma_dev/README.md +124 -0
  203. data/examples/puma_dev/config.ru +34 -0
  204. data/examples/puma_dev/standalone_app.rb +34 -0
  205. data/examples/scaffolding/blog.rb +49 -0
  206. data/examples/scaffolding/utf_lite.rb +105 -0
  207. data/examples/styling/feedback_demo.rb +229 -0
  208. data/examples/styling/style_showcase.rb +172 -0
  209. data/examples/styling/theme_demo.rb +508 -0
  210. data/examples/timer_health_checker.rb +111 -0
  211. data/examples/timer_showcase.rb +131 -0
  212. data/examples/tutorials/render_markdown.rb +112 -0
  213. data/examples/tutorials/streamweaver_way_tutorial.rb +44 -0
  214. data/examples/tutorials/tutorial_content.rb +1082 -0
  215. data/examples/visual_skills/design_deck_demo.rb +314 -0
  216. data/examples/visual_skills/explainer_demo.rb +499 -0
  217. data/exe/streamweaver +9 -0
  218. data/gsd/ROADMAP-1.0.md +167 -0
  219. data/gsd/STATE.md +13 -0
  220. data/gsd/research/market-positioning-research.md +139 -0
  221. data/gsd/research/production-patterns-research.md +270 -0
  222. data/gsd/research/repo-audit-1.0.md +274 -0
  223. data/lib/stream_weaver/action_token.rb +58 -0
  224. data/lib/stream_weaver/adapter/alpinejs.rb +8249 -0
  225. data/lib/stream_weaver/adapter/base.rb +591 -0
  226. data/lib/stream_weaver/adapter/opal.rb +334 -0
  227. data/lib/stream_weaver/adapter/static.rb +1118 -0
  228. data/lib/stream_weaver/admin.rb +176 -0
  229. data/lib/stream_weaver/app.rb +1821 -0
  230. data/lib/stream_weaver/assets/js/sw-copy.js +50 -0
  231. data/lib/stream_weaver/assets/js/sw-heredoc-rewrite.js +68 -0
  232. data/lib/stream_weaver/assets/js/sw-keyboard.js +165 -0
  233. data/lib/stream_weaver/assets/js/sw-mermaid-zoom.js +621 -0
  234. data/lib/stream_weaver/assets/js/sw-route-tabs.js +53 -0
  235. data/lib/stream_weaver/assets/js/sw-sidebar-toc.js +116 -0
  236. data/lib/stream_weaver/assets/js/sw-slide-nav.js +103 -0
  237. data/lib/stream_weaver/canvas/bridge.rb +239 -0
  238. data/lib/stream_weaver/canvas/bridge_server.rb +646 -0
  239. data/lib/stream_weaver/canvas/client.rb +298 -0
  240. data/lib/stream_weaver/canvas/doc_roots.rb +233 -0
  241. data/lib/stream_weaver/canvas/doc_store.rb +252 -0
  242. data/lib/stream_weaver/canvas/gist_publisher.rb +264 -0
  243. data/lib/stream_weaver/canvas/gist_save_handler.rb +89 -0
  244. data/lib/stream_weaver/canvas/gist_store.rb +135 -0
  245. data/lib/stream_weaver/canvas/helpers.rb +109 -0
  246. data/lib/stream_weaver/canvas/history.rb +90 -0
  247. data/lib/stream_weaver/canvas/protocol.rb +86 -0
  248. data/lib/stream_weaver/canvas/reader.rb +853 -0
  249. data/lib/stream_weaver/canvas/save_doc_widget.rb +457 -0
  250. data/lib/stream_weaver/canvas/scroll_top_hint.rb +21 -0
  251. data/lib/stream_weaver/canvas/session.rb +132 -0
  252. data/lib/stream_weaver/cli.rb +3235 -0
  253. data/lib/stream_weaver/component_assets.rb +70 -0
  254. data/lib/stream_weaver/component_registry.rb +67 -0
  255. data/lib/stream_weaver/component_renderer.rb +48 -0
  256. data/lib/stream_weaver/components/annotated_code.rb +53 -0
  257. data/lib/stream_weaver/components/api_endpoint.rb +42 -0
  258. data/lib/stream_weaver/components/callout.rb +59 -0
  259. data/lib/stream_weaver/components/chart.rb +84 -0
  260. data/lib/stream_weaver/components/code_block.rb +77 -0
  261. data/lib/stream_weaver/components/comparison.rb +39 -0
  262. data/lib/stream_weaver/components/decision.rb +38 -0
  263. data/lib/stream_weaver/components/deck/close_overlay.rb +84 -0
  264. data/lib/stream_weaver/components/deck/confirmation_bar.rb +60 -0
  265. data/lib/stream_weaver/components/deck/deck_option.rb +61 -0
  266. data/lib/stream_weaver/components/deck/deck_slide.rb +77 -0
  267. data/lib/stream_weaver/components/deck/deck_state.rb +469 -0
  268. data/lib/stream_weaver/components/deck/deck_summary.rb +73 -0
  269. data/lib/stream_weaver/components/deck/design_deck.rb +60 -0
  270. data/lib/stream_weaver/components/deck/generate_more_controls.rb +84 -0
  271. data/lib/stream_weaver/components/deck/model_selector.rb +84 -0
  272. data/lib/stream_weaver/components/deck/skeleton_placeholder.rb +36 -0
  273. data/lib/stream_weaver/components/diff_block.rb +125 -0
  274. data/lib/stream_weaver/components/doc_header.rb +57 -0
  275. data/lib/stream_weaver/components/image_block.rb +70 -0
  276. data/lib/stream_weaver/components/implementation_map.rb +33 -0
  277. data/lib/stream_weaver/components/keyboard_shortcuts.rb +94 -0
  278. data/lib/stream_weaver/components/kpi_dashboard.rb +78 -0
  279. data/lib/stream_weaver/components/mermaid.rb +79 -0
  280. data/lib/stream_weaver/components/pipeline.rb +63 -0
  281. data/lib/stream_weaver/components/sidebar_toc.rb +46 -0
  282. data/lib/stream_weaver/components/slide_container.rb +141 -0
  283. data/lib/stream_weaver/components/timeline_event.rb +58 -0
  284. data/lib/stream_weaver/components/wireframe.rb +29 -0
  285. data/lib/stream_weaver/components/wireframe_block.rb +34 -0
  286. data/lib/stream_weaver/components.rb +2711 -0
  287. data/lib/stream_weaver/css.rb +230 -0
  288. data/lib/stream_weaver/dev_fallback_overlay.rb +61 -0
  289. data/lib/stream_weaver/display_dsl.rb +1004 -0
  290. data/lib/stream_weaver/export/html_exporter.rb +478 -0
  291. data/lib/stream_weaver/feed.rb +34 -0
  292. data/lib/stream_weaver/feed_builder.rb +31 -0
  293. data/lib/stream_weaver/fonts.rb +33 -0
  294. data/lib/stream_weaver/interaction_runner.rb +487 -0
  295. data/lib/stream_weaver/iterm.rb +460 -0
  296. data/lib/stream_weaver/layout_registry.rb +92 -0
  297. data/lib/stream_weaver/opal/bridge.rb +52 -0
  298. data/lib/stream_weaver/opal/builder.rb +193 -0
  299. data/lib/stream_weaver/opal/env.rb +29 -0
  300. data/lib/stream_weaver/opal/reactive_state.rb +75 -0
  301. data/lib/stream_weaver/opal/regexp_anchor_patch.rb +62 -0
  302. data/lib/stream_weaver/opal/renderer.rb +77 -0
  303. data/lib/stream_weaver/opal/runtime.rb +250 -0
  304. data/lib/stream_weaver/opal/shell.rb +145 -0
  305. data/lib/stream_weaver/opal/string_bridge.rb +50 -0
  306. data/lib/stream_weaver/opal/stubs/diff.min.js +1 -0
  307. data/lib/stream_weaver/opal/stubs/digest.rb +15 -0
  308. data/lib/stream_weaver/opal/stubs/marked.umd.js +79 -0
  309. data/lib/stream_weaver/opal/stubs/md5.rb +3 -0
  310. data/lib/stream_weaver/opal/stubs/morphdom.min.js +775 -0
  311. data/lib/stream_weaver/opal/stubs/prism-tomorrow.min.css +1 -0
  312. data/lib/stream_weaver/opal/stubs/prism.min.js +1967 -0
  313. data/lib/stream_weaver/opal_entry.rb +136 -0
  314. data/lib/stream_weaver/org/inline.rb +95 -0
  315. data/lib/stream_weaver/org/reader.rb +561 -0
  316. data/lib/stream_weaver/org/recording_context.rb +86 -0
  317. data/lib/stream_weaver/org/source_splitter.rb +63 -0
  318. data/lib/stream_weaver/org/writer.rb +300 -0
  319. data/lib/stream_weaver/page_shell.rb +530 -0
  320. data/lib/stream_weaver/portfile.rb +79 -0
  321. data/lib/stream_weaver/pushable.rb +42 -0
  322. data/lib/stream_weaver/resource/default_views.rb +111 -0
  323. data/lib/stream_weaver/resource/field_input.rb +25 -0
  324. data/lib/stream_weaver/resource/state_keys.rb +16 -0
  325. data/lib/stream_weaver/resource/store.rb +18 -0
  326. data/lib/stream_weaver/resource.rb +104 -0
  327. data/lib/stream_weaver/server.rb +1471 -0
  328. data/lib/stream_weaver/service.rb +1240 -0
  329. data/lib/stream_weaver/service_client.rb +104 -0
  330. data/lib/stream_weaver/session_store.rb +186 -0
  331. data/lib/stream_weaver/skills/streamweaver-canvas-safe/SKILL.md +66 -0
  332. data/lib/stream_weaver/skills/streamweaver-canvas-safe/examples/canvas-safe-showcase.rb +98 -0
  333. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/actions-and-buttons.md +47 -0
  334. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/charts-and-diagrams.md +38 -0
  335. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/deck.md +32 -0
  336. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/inputs-and-forms.md +65 -0
  337. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/tabs-and-navigation.md +53 -0
  338. data/lib/stream_weaver/skills/streamweaver-doc-builder/SKILL.md +298 -0
  339. data/lib/stream_weaver/skills/streamweaver-visual-companion/SKILL.md +130 -0
  340. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.css +468 -0
  341. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.rb +39 -0
  342. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example_dsl.rb +300 -0
  343. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example.rb +24 -0
  344. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example_dsl.rb +385 -0
  345. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/checkpoints-and-forms.md +28 -0
  346. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/cleanup-and-panel.md +44 -0
  347. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/example-gallery.md +8 -0
  348. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/persistence.md +58 -0
  349. data/lib/stream_weaver/skills/streamweaver-way/SKILL.md +396 -0
  350. data/lib/stream_weaver/skills/visual-plan/SKILL.md +201 -0
  351. data/lib/stream_weaver/skills/visual-recap/SKILL.md +244 -0
  352. data/lib/stream_weaver/streamer.rb +63 -0
  353. data/lib/stream_weaver/templates/choices.rb +149 -0
  354. data/lib/stream_weaver/templates/code.rb +157 -0
  355. data/lib/stream_weaver/templates/confirm.rb +110 -0
  356. data/lib/stream_weaver/templates/diff.rb +195 -0
  357. data/lib/stream_weaver/templates/info.rb +142 -0
  358. data/lib/stream_weaver/templates/table.rb +169 -0
  359. data/lib/stream_weaver/templates/wizard.rb +271 -0
  360. data/lib/stream_weaver/theme/auto_mode.rb +116 -0
  361. data/lib/stream_weaver/theme/presets.rb +555 -0
  362. data/lib/stream_weaver/theme.rb +639 -0
  363. data/lib/stream_weaver/university/canvas.rb +924 -0
  364. data/lib/stream_weaver/university/course.rb +699 -0
  365. data/lib/stream_weaver/university/demos/counter.rb +33 -0
  366. data/lib/stream_weaver/university/demos/dashboard.rb +147 -0
  367. data/lib/stream_weaver/university/demos/decision_form.rb +143 -0
  368. data/lib/stream_weaver/university/demos.rb +50 -0
  369. data/lib/stream_weaver/university/listener.rb +467 -0
  370. data/lib/stream_weaver/university/progress.rb +201 -0
  371. data/lib/stream_weaver/university/runner.rb +138 -0
  372. data/lib/stream_weaver/university/scripts/growing_doc.rb +609 -0
  373. data/lib/stream_weaver/university/scripts/growing_doc_state.rb +91 -0
  374. data/lib/stream_weaver/utils.rb +31 -0
  375. data/lib/stream_weaver/version.rb +5 -0
  376. data/lib/stream_weaver/views/canvas/reader_layout.erb +800 -0
  377. data/lib/stream_weaver/views.rb +3940 -0
  378. data/lib/stream_weaver.rb +122 -0
  379. data/llms.txt +1269 -0
  380. data/sig/stream_weaver.rbs +4 -0
  381. metadata +598 -0
@@ -0,0 +1,401 @@
1
+ #!/bin/bash
2
+ # Git Project Health Analyzer
3
+ #
4
+ # A dynamic canvas workflow that analyzes a git repository and generates
5
+ # a custom health dashboard. Each run produces different results based on
6
+ # the actual project state.
7
+ #
8
+ # Features demonstrated:
9
+ # - Real data analysis (git commands)
10
+ # - Dynamic UI generation based on findings
11
+ # - Charts, tables, progress indicators
12
+ # - Multi-step interactive workflow
13
+ #
14
+ # Usage: ./examples/git_health.sh [path-to-repo]
15
+
16
+ set -e
17
+
18
+ REPO_PATH="${1:-.}"
19
+ SESSION="git-health-$$"
20
+
21
+ # Validate it's a git repo
22
+ if ! git -C "$REPO_PATH" rev-parse --git-dir > /dev/null 2>&1; then
23
+ echo "Error: $REPO_PATH is not a git repository"
24
+ exit 1
25
+ fi
26
+
27
+ cd "$REPO_PATH"
28
+ REPO_NAME=$(basename "$(git rev-parse --show-toplevel)")
29
+
30
+ echo "=== Git Project Health Analyzer ==="
31
+ echo "Repository: $REPO_NAME"
32
+ echo ""
33
+
34
+ # Start the panel
35
+ streamweaver panel "$SESSION" 2>/dev/null
36
+ sleep 1
37
+
38
+ # ============================================================================
39
+ # STEP 1: Discovery - Scan the repository
40
+ # ============================================================================
41
+ echo "Step 1: Scanning repository..."
42
+ echo " Features: spinner, progress feedback"
43
+
44
+ streamweaver canvas-push "$SESSION" <<RUBY
45
+ header1 "Git Project Health"
46
+ md "Analyzing **${REPO_NAME}**..."
47
+
48
+ hstack spacing: :md, align: :center do
49
+ spinner size: :md
50
+ md "Scanning repository structure..."
51
+ end
52
+
53
+ progress_bar value: 10, show_label: true, animated: true
54
+ RUBY
55
+
56
+ sleep 0.5
57
+
58
+ # Gather repository metrics
59
+ TOTAL_COMMITS=$(git rev-list --count HEAD 2>/dev/null || echo "0")
60
+ TOTAL_FILES=$(git ls-files | wc -l | tr -d ' ')
61
+ TOTAL_AUTHORS=$(git shortlog -sn --all | wc -l | tr -d ' ')
62
+ TOTAL_BRANCHES=$(git branch -a | wc -l | tr -d ' ')
63
+ REPO_AGE_DAYS=$(( ($(date +%s) - $(git log --reverse --format=%ct | head -1)) / 86400 ))
64
+
65
+ # Recent activity
66
+ COMMITS_LAST_30=$(git rev-list --count --since="30 days ago" HEAD 2>/dev/null || echo "0")
67
+ COMMITS_LAST_7=$(git rev-list --count --since="7 days ago" HEAD 2>/dev/null || echo "0")
68
+
69
+ # File types
70
+ TOP_EXTENSIONS=$(git ls-files | grep -E '\.[a-zA-Z0-9]+$' | sed 's/.*\.//' | sort | uniq -c | sort -rn | head -5)
71
+
72
+ streamweaver canvas-push "$SESSION" <<RUBY
73
+ header1 "Git Project Health"
74
+ md "Analyzing **${REPO_NAME}**..."
75
+
76
+ hstack spacing: :md, align: :center do
77
+ spinner size: :md
78
+ md "Analyzing commit history..."
79
+ end
80
+
81
+ progress_bar value: 40, show_label: true, animated: true
82
+ RUBY
83
+
84
+ sleep 0.5
85
+
86
+ # ============================================================================
87
+ # STEP 2: Selection - Choose what to analyze
88
+ # ============================================================================
89
+ echo ""
90
+ echo "Step 2: Showing discovery results, getting user selection..."
91
+ echo " Features: cards, badges, stats, checkboxes, radio_group"
92
+
93
+ # Determine health indicators
94
+ if [ "$COMMITS_LAST_7" -gt 5 ]; then
95
+ ACTIVITY_STATUS="green"
96
+ ACTIVITY_LABEL="Active"
97
+ elif [ "$COMMITS_LAST_30" -gt 0 ]; then
98
+ ACTIVITY_STATUS="yellow"
99
+ ACTIVITY_LABEL="Moderate"
100
+ else
101
+ ACTIVITY_STATUS="red"
102
+ ACTIVITY_LABEL="Stale"
103
+ fi
104
+
105
+ streamweaver canvas-push "$SESSION" <<RUBY
106
+ header1 "Git Project Health"
107
+ md "Repository: **${REPO_NAME}** (${REPO_AGE_DAYS} days old)"
108
+
109
+ # Quick stats
110
+ hstack spacing: :lg do
111
+ card do
112
+ hstack spacing: :sm, align: :center do
113
+ badge "${TOTAL_COMMITS}", variant: :info
114
+ md "**Commits**"
115
+ end
116
+ end
117
+ card do
118
+ hstack spacing: :sm, align: :center do
119
+ badge "${TOTAL_FILES}", variant: :info
120
+ md "**Files**"
121
+ end
122
+ end
123
+ card do
124
+ hstack spacing: :sm, align: :center do
125
+ badge "${TOTAL_AUTHORS}", variant: :info
126
+ md "**Authors**"
127
+ end
128
+ end
129
+ card do
130
+ hstack spacing: :sm, align: :center do
131
+ status_dot status: :${ACTIVITY_STATUS}, label: "${ACTIVITY_LABEL}"
132
+ end
133
+ end
134
+ end
135
+
136
+ md "---"
137
+
138
+ # Analysis options
139
+ header3 "Select Analysis Areas"
140
+ card do
141
+ checkbox :commits, "**Commit Activity** - Commits over time, busiest days"
142
+ checkbox :authors, "**Author Contributions** - Who contributes most"
143
+ checkbox :files, "**File Analysis** - Largest files, most changed"
144
+ checkbox :branches, "**Branch Health** - Stale branches, merge status"
145
+ end
146
+
147
+ md "---"
148
+
149
+ header3 "Report Detail"
150
+ radio_group :detail, ["Summary only", "Detailed with charts", "Full analysis"]
151
+
152
+ md "---"
153
+
154
+ hstack spacing: :md do
155
+ button "Cancel", id: "btn_cancel", style: :secondary
156
+ button "Generate Report", id: "btn_generate", variant: "primary"
157
+ end
158
+ RUBY
159
+
160
+ SELECTION=$(streamweaver canvas-wait "$SESSION" 2>/dev/null)
161
+ echo "User selection: $SELECTION"
162
+
163
+ # Check if cancelled
164
+ if echo "$SELECTION" | grep -q "btn_cancel"; then
165
+ echo "User cancelled."
166
+ streamweaver canvas-close "$SESSION" 2>/dev/null
167
+ exit 0
168
+ fi
169
+
170
+ # ============================================================================
171
+ # STEP 3: Analysis - Generate the report
172
+ # ============================================================================
173
+ echo ""
174
+ echo "Step 3: Generating analysis with progress..."
175
+ echo " Features: progress_bar, status_dot, activity_item"
176
+
177
+ # Parse selections (simplified - check if keys are true)
178
+ ANALYZE_COMMITS=$(echo "$SELECTION" | python3 -c "import sys,json; d=json.load(sys.stdin); print('1' if d.get('state',{}).get('commits') else '0')" 2>/dev/null || echo "1")
179
+ ANALYZE_AUTHORS=$(echo "$SELECTION" | python3 -c "import sys,json; d=json.load(sys.stdin); print('1' if d.get('state',{}).get('authors') else '0')" 2>/dev/null || echo "1")
180
+
181
+ # Show progress
182
+ streamweaver canvas-push "$SESSION" <<RUBY
183
+ header1 "Generating Report"
184
+
185
+ hstack spacing: :md, align: :center do
186
+ spinner size: :md
187
+ md "**Analyzing commit patterns...**"
188
+ end
189
+
190
+ progress_bar value: 20, show_label: true, animated: true, variant: :success
191
+
192
+ hstack spacing: :lg, align: :center do
193
+ status_dot status: :green, pulse: true, label: "Commits"
194
+ status_dot status: :gray, label: "Authors"
195
+ status_dot status: :gray, label: "Files"
196
+ end
197
+ RUBY
198
+
199
+ sleep 0.3
200
+
201
+ # Gather detailed metrics
202
+ # Commits by day of week
203
+ COMMITS_BY_DAY=$(git log --format='%ad' --date=format:'%A' | sort | uniq -c | sort -rn)
204
+ MON=$(echo "$COMMITS_BY_DAY" | grep Monday | awk '{print $1}' || echo "0")
205
+ TUE=$(echo "$COMMITS_BY_DAY" | grep Tuesday | awk '{print $1}' || echo "0")
206
+ WED=$(echo "$COMMITS_BY_DAY" | grep Wednesday | awk '{print $1}' || echo "0")
207
+ THU=$(echo "$COMMITS_BY_DAY" | grep Thursday | awk '{print $1}' || echo "0")
208
+ FRI=$(echo "$COMMITS_BY_DAY" | grep Friday | awk '{print $1}' || echo "0")
209
+
210
+ # Top authors
211
+ TOP_AUTHORS=$(git shortlog -sn --all | head -5)
212
+
213
+ streamweaver canvas-push "$SESSION" <<RUBY
214
+ header1 "Generating Report"
215
+
216
+ hstack spacing: :md, align: :center do
217
+ spinner size: :md
218
+ md "**Analyzing author contributions...**"
219
+ end
220
+
221
+ progress_bar value: 60, show_label: true, animated: true, variant: :success
222
+
223
+ hstack spacing: :lg, align: :center do
224
+ status_dot status: :green, label: "Commits"
225
+ status_dot status: :green, pulse: true, label: "Authors"
226
+ status_dot status: :gray, label: "Files"
227
+ end
228
+ RUBY
229
+
230
+ sleep 0.3
231
+
232
+ # Recent files changed
233
+ RECENT_FILES=$(git diff --stat HEAD~10 HEAD 2>/dev/null | head -10 || echo "No recent changes")
234
+
235
+ streamweaver canvas-push "$SESSION" <<RUBY
236
+ header1 "Generating Report"
237
+
238
+ hstack spacing: :md, align: :center do
239
+ spinner size: :md
240
+ md "**Finalizing report...**"
241
+ end
242
+
243
+ progress_bar value: 90, show_label: true, animated: true, variant: :success
244
+
245
+ hstack spacing: :lg, align: :center do
246
+ status_dot status: :green, label: "Commits"
247
+ status_dot status: :green, label: "Authors"
248
+ status_dot status: :green, pulse: true, label: "Files"
249
+ end
250
+ RUBY
251
+
252
+ sleep 0.3
253
+
254
+ # ============================================================================
255
+ # STEP 4: Results - Display the dashboard
256
+ # ============================================================================
257
+ echo ""
258
+ echo "Step 4: Displaying results dashboard..."
259
+ echo " Features: charts, tables, collapsible sections"
260
+
261
+ # Build author table data
262
+ AUTHOR_TABLE=""
263
+ while IFS= read -r line; do
264
+ if [ -n "$line" ]; then
265
+ COUNT=$(echo "$line" | awk '{print $1}')
266
+ NAME=$(echo "$line" | sed 's/^[[:space:]]*[0-9]*[[:space:]]*//')
267
+ AUTHOR_TABLE="${AUTHOR_TABLE}{ author: \"${NAME}\", commits: ${COUNT} },"
268
+ fi
269
+ done <<< "$TOP_AUTHORS"
270
+ AUTHOR_TABLE="[${AUTHOR_TABLE%,}]"
271
+
272
+ streamweaver canvas-push "$SESSION" <<RUBY
273
+ header1 "Project Health Report"
274
+ md "**${REPO_NAME}** - Generated $(date '+%Y-%m-%d %H:%M')"
275
+
276
+ # Summary stats
277
+ hstack spacing: :lg do
278
+ card do
279
+ hstack spacing: :sm, align: :center do
280
+ badge "${TOTAL_COMMITS}", variant: :success
281
+ md "**Total Commits**"
282
+ end
283
+ end
284
+ card do
285
+ hstack spacing: :sm, align: :center do
286
+ badge "${COMMITS_LAST_30}", variant: :info
287
+ md "**Last 30 Days**"
288
+ end
289
+ end
290
+ card do
291
+ hstack spacing: :sm, align: :center do
292
+ badge "${TOTAL_AUTHORS}", variant: :info
293
+ md "**Contributors**"
294
+ end
295
+ end
296
+ end
297
+
298
+ md "---"
299
+
300
+ # Commit activity chart
301
+ header3 "Commit Activity by Day"
302
+ bar_chart data: {
303
+ "Mon" => ${MON:-0},
304
+ "Tue" => ${TUE:-0},
305
+ "Wed" => ${WED:-0},
306
+ "Thu" => ${THU:-0},
307
+ "Fri" => ${FRI:-0}
308
+ }, title: "Commits by Day of Week", height: "200px"
309
+
310
+ md "---"
311
+
312
+ # Top contributors
313
+ header3 "Top Contributors"
314
+ table ${AUTHOR_TABLE}, striped: true, sortable: true
315
+
316
+ md "---"
317
+
318
+ # Recommendations
319
+ header3 "Recommendations"
320
+ RUBY
321
+
322
+ # Add dynamic recommendations based on findings
323
+ RECS=""
324
+ if [ "$COMMITS_LAST_7" -eq 0 ]; then
325
+ RECS="${RECS}alert variant: :warning, title: \"Low Recent Activity\" do\n md \"No commits in the last 7 days. Consider reviewing project status.\"\nend\n"
326
+ fi
327
+
328
+ if [ "$TOTAL_BRANCHES" -gt 20 ]; then
329
+ RECS="${RECS}alert variant: :info, title: \"Many Branches\" do\n md \"${TOTAL_BRANCHES} branches detected. Consider cleaning up stale branches.\"\nend\n"
330
+ fi
331
+
332
+ if [ -z "$RECS" ]; then
333
+ RECS="alert variant: :success, title: \"Looking Good!\" do\n md \"No major issues detected. Keep up the good work!\"\nend\n"
334
+ fi
335
+
336
+ streamweaver canvas-push "$SESSION" <<RUBY
337
+ header1 "Project Health Report"
338
+ md "**${REPO_NAME}** - Generated $(date '+%Y-%m-%d %H:%M')"
339
+
340
+ hstack spacing: :lg do
341
+ card do
342
+ hstack spacing: :sm, align: :center do
343
+ badge "${TOTAL_COMMITS}", variant: :success
344
+ md "**Total Commits**"
345
+ end
346
+ end
347
+ card do
348
+ hstack spacing: :sm, align: :center do
349
+ badge "${COMMITS_LAST_30}", variant: :info
350
+ md "**Last 30 Days**"
351
+ end
352
+ end
353
+ card do
354
+ hstack spacing: :sm, align: :center do
355
+ badge "${TOTAL_AUTHORS}", variant: :info
356
+ md "**Contributors**"
357
+ end
358
+ end
359
+ end
360
+
361
+ md "---"
362
+
363
+ header3 "Commit Activity by Day"
364
+ bar_chart data: {
365
+ "Mon" => ${MON:-0},
366
+ "Tue" => ${TUE:-0},
367
+ "Wed" => ${WED:-0},
368
+ "Thu" => ${THU:-0},
369
+ "Fri" => ${FRI:-0}
370
+ }, title: "Commits by Day of Week", height: "200px"
371
+
372
+ md "---"
373
+
374
+ header3 "Top Contributors"
375
+ table ${AUTHOR_TABLE}, striped: true, sortable: true
376
+
377
+ md "---"
378
+
379
+ header3 "Recommendations"
380
+ ${RECS}
381
+
382
+ md "---"
383
+
384
+ button "Done", id: "btn_done", variant: "primary"
385
+ RUBY
386
+
387
+ streamweaver canvas-wait "$SESSION" 2>/dev/null
388
+
389
+ # Cleanup
390
+ streamweaver canvas-close "$SESSION" 2>/dev/null
391
+
392
+ echo ""
393
+ echo "=============================================="
394
+ echo "Report complete!"
395
+ echo ""
396
+ echo "This workflow demonstrated:"
397
+ echo " - Real git data analysis (${TOTAL_COMMITS} commits analyzed)"
398
+ echo " - Dynamic charts based on actual commit patterns"
399
+ echo " - Contextual recommendations based on findings"
400
+ echo " - Different results each time based on repo state"
401
+ echo ""
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Demonstrates the new layout components and custom component mechanism:
5
+ # - Custom component modules (components: option)
6
+ # - Card sub-components (card_header, card_body, card_footer)
7
+ # - VStack and HStack for stacking layouts
8
+ # - Grid for responsive grid layouts
9
+
10
+ require_relative '../../lib/stream_weaver'
11
+
12
+ # Custom component module - defines reusable components
13
+ module UserComponents
14
+ def user_card(user)
15
+ card do
16
+ card_header do
17
+ header4 user[:name]
18
+ status_badge user[:status], user[:role]
19
+ end
20
+ card_body do
21
+ text user[:bio]
22
+ text "Email: #{user[:email]}"
23
+ end
24
+ card_footer do
25
+ button "View Profile", style: :secondary
26
+ button "Message"
27
+ end
28
+ end
29
+ end
30
+
31
+ def stat_card(label, value, trend: nil)
32
+ card do
33
+ card_header label
34
+ card_body do
35
+ header2 value
36
+ if trend
37
+ text trend
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ # Example data
45
+ USERS = [
46
+ { name: "Alice Chen", email: "alice@example.com", bio: "Full-stack developer", status: :strong, role: "Engineering" },
47
+ { name: "Bob Smith", email: "bob@example.com", bio: "Product designer", status: :maybe, role: "Design" },
48
+ { name: "Carol Davis", email: "carol@example.com", bio: "Data scientist", status: :strong, role: "Analytics" },
49
+ { name: "Dan Wilson", email: "dan@example.com", bio: "DevOps engineer", status: :skip, role: "Infrastructure" },
50
+ { name: "Eve Johnson", email: "eve@example.com", bio: "Frontend specialist", status: :strong, role: "Engineering" },
51
+ { name: "Frank Brown", email: "frank@example.com", bio: "Backend architect", status: :maybe, role: "Engineering" }
52
+ ].freeze
53
+
54
+ App = app "Layout Components Demo", layout: :wide, components: [UserComponents] do
55
+ header1 "StreamWeaver Layout Components"
56
+
57
+ md "This demo showcases the new layout components: **vstack**, **hstack**, **grid**, and **card sub-components**."
58
+
59
+ # Section 1: Card Sub-Components
60
+ header2 "Card Sub-Components"
61
+
62
+ card do
63
+ card_header "Structured Card"
64
+ card_body do
65
+ text "Cards can now have distinct header, body, and footer sections."
66
+ text "Headers get a bottom border, footers get a top border and right-aligned content."
67
+ end
68
+ card_footer do
69
+ button "Cancel", style: :secondary
70
+ button "Save Changes"
71
+ end
72
+ end
73
+
74
+ # Section 2: VStack - Vertical Stacking
75
+ header2 "VStack - Vertical Stacking"
76
+
77
+ vstack spacing: :lg do
78
+ text "Items stacked vertically with large spacing"
79
+ text "Second item"
80
+ text "Third item"
81
+ end
82
+
83
+ header3 "VStack with Dividers"
84
+
85
+ vstack spacing: :md, divider: true do
86
+ text "Item with divider below"
87
+ text "Another item with divider"
88
+ text "Last item (no divider)"
89
+ end
90
+
91
+ # Section 3: HStack - Horizontal Stacking
92
+ header2 "HStack - Horizontal Stacking"
93
+
94
+ hstack spacing: :md, align: :center do
95
+ button "Action 1"
96
+ button "Action 2", style: :secondary
97
+ button "Action 3", style: :secondary
98
+ end
99
+
100
+ header3 "HStack with Justify"
101
+
102
+ hstack spacing: :sm, justify: :between do
103
+ text "Left content"
104
+ text "Right content"
105
+ end
106
+
107
+ # Section 4: Grid - Responsive Layouts
108
+ header2 "Grid - Responsive Layouts"
109
+
110
+ header3 "Fixed 3-Column Grid"
111
+
112
+ grid columns: 3, gap: :md do
113
+ stat_card("Total Users", "1,234", trend: "+12% this month")
114
+ stat_card("Active Sessions", "567", trend: "+8% today")
115
+ stat_card("Revenue", "$45.2K", trend: "+23% YoY")
116
+ end
117
+
118
+ header3 "Responsive Grid (resize browser to see)"
119
+ text "1 column on mobile, 2 on tablet, 3 on desktop"
120
+
121
+ grid columns: [1, 2, 3], gap: :lg do
122
+ USERS.each do |user|
123
+ user_card(user)
124
+ end
125
+ end
126
+
127
+ # Section 5: Custom Components
128
+ header2 "Custom Components via Modules"
129
+
130
+ md <<~MD
131
+ The `user_card` and `stat_card` used above are defined in a `UserComponents` module
132
+ and included via the `components:` option:
133
+
134
+ ```ruby
135
+ module UserComponents
136
+ def user_card(user)
137
+ card do
138
+ card_header { header4 user[:name] }
139
+ card_body { text user[:bio] }
140
+ card_footer { button "View" }
141
+ end
142
+ end
143
+ end
144
+
145
+ app "My App", components: [UserComponents] do
146
+ user_card(current_user)
147
+ end
148
+ ```
149
+ MD
150
+
151
+ # Section 6: Nested Layouts
152
+ header2 "Nested Layouts"
153
+
154
+ columns widths: ['30%', '70%'] do
155
+ column do
156
+ vstack spacing: :sm do
157
+ header3 "Sidebar"
158
+ text "VStack inside a Column"
159
+ text "Another stacked item"
160
+ end
161
+ end
162
+
163
+ column do
164
+ grid columns: 2, gap: :sm do
165
+ card { text "Grid item 1" }
166
+ card { text "Grid item 2" }
167
+ card { text "Grid item 3" }
168
+ card { text "Grid item 4" }
169
+ end
170
+ end
171
+ end
172
+ end
173
+
174
+ App.run! if __FILE__ == $0