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,385 @@
1
+ # frozen_string_literal: true
2
+ # Inner DSL for the Calendar-Driven Travel State PRD.
3
+ # Intended for canvas-push: streamweaver canvas-push <session> < examples/components/prd_dsl.rb
4
+ # For standalone app with theme: ruby examples/components/prd_demo.rb
5
+
6
+ sidebar_toc sections: [
7
+ { id: "problem", label: "Problem Statement" },
8
+ { id: "principle", label: "Design Principle" },
9
+ { id: "architecture", label: "Architecture" },
10
+ { id: "c1", label: "Component 1: Enforcement" },
11
+ { id: "c2", label: "Component 2: Sync Script" },
12
+ { id: "c3", label: "Component 3: Checkin Gate" },
13
+ { id: "data-model", label: "Data Model" },
14
+ { id: "integrations", label: "Integration Points" },
15
+ { id: "scope", label: "Scope" },
16
+ { id: "success", label: "Success Criteria" },
17
+ { id: "open", label: "Open Questions" }
18
+ ]
19
+
20
+ doc_header(
21
+ eyebrow: "aria · Personal OS",
22
+ title: "Calendar-Driven Travel State",
23
+ pills: [
24
+ { text: "Draft" },
25
+ "June 25, 2026",
26
+ "Author: Maya Chen",
27
+ "Owner: scheduler secretary + aria_dev"
28
+ ]
29
+ )
30
+
31
+ # 01 — Problem Statement
32
+ doc_section_header "01", "Problem Statement", id: "problem"
33
+ md <<~MD
34
+ During the June 24 daily checkin, the grandmaster analysis fabricated "Austin, Texas"
35
+ as Maya's location throughout — she was in Frederick, Maryland. The cause was a
36
+ two-part failure:
37
+
38
+ 1. `current-state.yaml` didn't exist at checkin time, so Claude had no ground truth
39
+ 2. Claude pattern-matched "the Rio" (a Maryland venue) against prior journal context
40
+ that had mentioned Austin, and confabulated a coherent but entirely wrong narrative
41
+ MD
42
+ callout(variant: :warning, title: "Root cause:") do
43
+ text "Stale or absent location context causes Claude to infer location from indirect signals. " \
44
+ "Confident wrong inferences propagate silently through analysis — they don't look uncertain, " \
45
+ "they look authoritative."
46
+ end
47
+ md "The immediate fix (creating `current-state.yaml` and adding the Ask-Don't-Fabricate directive) " \
48
+ "addresses the symptom. This PRD addresses the cause: `current-state.yaml` was created as a " \
49
+ "manually-maintained file, which will inevitably go stale when juggling 20+ concurrent " \
50
+ "sessions with no bandwidth to remember to update a doc."
51
+
52
+ # 02 — Design Principle
53
+ doc_section_header "02", "Design Principle: Calendar Is Truth", id: "principle"
54
+ md "Maya already maintains two calendars — Outlook (work PTO, flights, meetings) and Google " \
55
+ "(family events). These are the existing ground truth for where she is and what she's doing. " \
56
+ "Rather than maintaining a parallel file, the system should read from what already exists."
57
+ mermaid <<~MERMAID
58
+ graph LR
59
+ A["Calendar<br/>Outlook + Google<br/><i>ground truth</i>"]
60
+ B["bin/sync-travel-state<br/>daily derivation"]
61
+ C["current-state.yaml<br/>read-only cache"]
62
+ D["Claude sessions<br/>consume, never write"]
63
+ A --> B --> C --> D
64
+ style A fill:#EEF2FF,stroke:#1E4ED8,color:#1E4ED8
65
+ MERMAID
66
+ callout(variant: :info, title: "Process change required:") do
67
+ text "Driving trips (e.g., Austin) have no flights to detect. The convention is: all travel — " \
68
+ "personal or work, driving or flying — must have an Outlook PTO block before the system " \
69
+ "will track it. The cabinet enforces this rather than relying on memory."
70
+ end
71
+
72
+ # 03 — Architecture
73
+ doc_section_header "03", "Architecture Overview", id: "architecture"
74
+ md "Three components work together. Each has a single responsibility."
75
+ table(
76
+ headers: ["Component", "Responsibility", "Owner", "Trigger"],
77
+ rows: [
78
+ ["Enforcement", "Require calendar entry before travel is acknowledged", "scheduler secretary", "Any travel mention"],
79
+ ["bin/sync-travel-state", "Read calendars → write current-state.yaml", "launchd / morning brief","Daily 6am + post-checkin"],
80
+ ["Checkin gate", "Human confirms derived state is accurate", "aria-checkin Step 0", "Every daily checkin"]
81
+ ]
82
+ )
83
+
84
+ # 04 — Component 1
85
+ doc_section_header "04", "Component 1: Calendar Entry Enforcement", id: "c1"
86
+ card do
87
+ card_header "Scheduler Secretary Behavior", badge: "C1", meta: "secretary work cycle + direct requests"
88
+ card_body do
89
+ md <<~MD
90
+ When travel is mentioned in any context — daily checkin narrative, voicenote, direct
91
+ message to scheduler — the secretary checks for a corresponding calendar entry before
92
+ acknowledging or routing the trip.
93
+
94
+ ### Detection Triggers
95
+
96
+ - User says "I'm going to X" / "we're traveling to X" / "trip to X"
97
+ - Voicenote mentions destination + date range
98
+ - Checkin narrative references upcoming travel not in calendar
99
+
100
+ ### Behavior: entry exists
101
+
102
+ Acknowledge and flag for sync script to pick up at next run. No further action.
103
+
104
+ ### Behavior: no entry found
105
+
106
+ Create the calendar entry via `aria-outlook` or `aria-google`, confirm with user,
107
+ then acknowledge. Secretary does _not_ write to `current-state.yaml` — that is
108
+ exclusively the sync script's domain.
109
+
110
+ #### Entry format (Outlook PTO)
111
+ MD
112
+ code_block(<<~TXT, lang: "text")
113
+ Title: Maya – PTO [Destination]
114
+ e.g., "Maya – PTO Maryland"
115
+ "Maya – PTO Austin"
116
+ Type: All-day event, multi-day
117
+ Calendar: Outlook personal
118
+ TXT
119
+ md <<~MD
120
+ #### Entry format (work travel)
121
+ MD
122
+ code_block(<<~TXT, lang: "text")
123
+ Title: [Trip name] e.g., "Meridian Analytics summer trip 2026"
124
+ Flight events are detected separately; PTO block still required
125
+ Type: All-day event
126
+ Calendar: Outlook work
127
+ TXT
128
+ md "### Type Inference Rules"
129
+ table(
130
+ headers: ["Title pattern", "Inferred type"],
131
+ rows: [
132
+ ["contains \"Meridian\" / \"work\" / trip name from work context", "work"],
133
+ ["\"Maya – PTO [Destination]\" with no work keywords", "vacation"],
134
+ ["Flight pair without PTO block (day trip)", "work"],
135
+ ["Google family calendar multi-day event", "vacation"]
136
+ ]
137
+ )
138
+ end
139
+ end
140
+
141
+ # 05 — Component 2
142
+ doc_section_header "05", "Component 2: bin/sync-travel-state", id: "c2"
143
+ card do
144
+ card_header "Calendar Sync Script", badge: "C2", meta: "bin/sync-travel-state · Ruby"
145
+ card_body do
146
+ md <<~MD
147
+ Reads both calendars, derives travel state, overwrites `current-state.yaml`.
148
+ Idempotent — safe to run multiple times per day.
149
+
150
+ ### Calendar Sources
151
+
152
+ - **Outlook** — via `aria-outlook calendar list`. Reads 60-day window. Detects: PTO
153
+ all-day events, flight events (title contains "flight" / airline names / airport codes).
154
+ - **Google** — via `aria-google calendar list`. Reads Maya's primary calendar + Sam's
155
+ shared calendar. Detects: multi-day events (family trips, shared PTO).
156
+
157
+ ### Detection Logic
158
+
159
+ - **Active travel:** any PTO event (Outlook) or multi-day non-recurring family event
160
+ (Google) that spans today's date
161
+ - **Upcoming trips:** PTO events or flight pairs within the next 60 days, grouped by
162
+ date proximity (±2 days = same trip)
163
+ - **Trip end date:** last day of PTO block or last flight's arrival date, whichever is later
164
+ - **Destination:** parsed from PTO event title suffix or location field, falling back to
165
+ flight arrival airport city
166
+
167
+ ### Schedule
168
+
169
+ - Daily at 6:00am via launchd (runs before any sessions likely to start)
170
+ - After daily checkin completes (hooked into checkin flow)
171
+ - Manual: `bin/sync-travel-state --force`
172
+
173
+ ### Staleness guard
174
+
175
+ If `aria-outlook` or `aria-google` returns an auth error (OAuth expired), the script
176
+ logs the failure and exits without overwriting the existing file. It emits a warning to
177
+ the COS inbox. It never writes a stale derivation.
178
+ MD
179
+ code_block(<<~SH, lang: "bash")
180
+ bin/sync-travel-state # normal run
181
+ bin/sync-travel-state --dry-run # print derived state, don't write
182
+ bin/sync-travel-state --force # run even if synced_at is recent
183
+ SH
184
+ end
185
+ end
186
+
187
+ # 06 — Component 3
188
+ doc_section_header "06", "Component 3: Checkin Verification Gate", id: "c3"
189
+ card do
190
+ card_header "Daily Checkin — Step 0", badge: "C3", meta: "bin/aria-checkin · before form opens"
191
+ card_body do
192
+ md <<~MD
193
+ A brief confirmation step added before the StreamWeaver form opens. Shows derived state
194
+ and asks for a binary confirm. Catches calendar errors before they flow into analysis.
195
+
196
+ ### When to show
197
+
198
+ - `travel.active: true` — confirm travel is still ongoing
199
+ - `travel.ends` is today or yesterday — confirm return status
200
+ - `synced_at` is >24h old — warn and offer to re-sync
201
+ - Upcoming trip starts within 3 days — surface it for awareness
202
+
203
+ ### When to skip
204
+
205
+ - `travel.active: false` and no trip starting within 3 days — no confirmation needed,
206
+ proceed directly to form
207
+
208
+ ### UI behavior
209
+
210
+ Single yes/no question added to the StreamWeaver form as a pre-flight card. Displayed
211
+ above the main form sections. Estimated time: 5 seconds.
212
+ MD
213
+ code_block(<<~TXT, lang: "text")
214
+ "Derived from calendar: traveling in Maryland through Jun 28.
215
+ Still accurate?"
216
+ → [Yes, continue] [No, something's wrong]
217
+ TXT
218
+ md "If the user selects \"No\": display a correction prompt, update the calendar entry " \
219
+ "(not the yaml directly), trigger `bin/sync-travel-state --force`, then proceed to the " \
220
+ "main form with updated context."
221
+ end
222
+ end
223
+
224
+ # 07 — Data Model
225
+ doc_section_header "07", "Data Model", id: "data-model"
226
+ callout(variant: :warning, title: "Do not hand-edit this file.") do
227
+ text "It is written exclusively by bin/sync-travel-state. Manual edits will be overwritten " \
228
+ "at next sync. For urgent corrections, update the source calendar entry and run " \
229
+ "bin/sync-travel-state --force."
230
+ end
231
+ md "### current-state.yaml schema"
232
+ code_block(<<~YAML, lang: "yaml")
233
+ # ~/aria-os/current-state.yaml
234
+ # DERIVED FROM CALENDAR — do not hand-edit.
235
+ # Run bin/sync-travel-state to refresh.
236
+
237
+ synced_at: "2026-06-25T06:00:00-07:00" # ISO8601, written by sync script
238
+
239
+ location:
240
+ current: "Frederick, MD" # from PTO event location or title suffix
241
+ home: "Ashland, OR" # static — set once in script config
242
+
243
+ # travel.active is the primary flag all Claude sessions check.
244
+ # true = away from home, any type. Repeating calendar events do not occur.
245
+ travel:
246
+ active: true
247
+ type: vacation # vacation | work | mixed
248
+ destination: "Maryland" # parsed from calendar
249
+ ends: "2026-06-28" # PTO end date
250
+ source_event_id: "AAMkADk1..." # Outlook/Google event ID for traceability
251
+ source_calendar: outlook # outlook | google
252
+
253
+ mode: vacation # normal | vacation | work-travel | deadline-crunch
254
+ # Note: deadline-crunch is the only mode not derivable from calendar.
255
+ # It must be set manually via: aria-cabinet cos-state set mode deadline-crunch
256
+
257
+ mode_implications:
258
+ hobby_teaching: skip # auto: true when travel.active is true
259
+ hobby_training: skip # auto: true when travel.type is vacation
260
+ calendar_note: >
261
+ Repeating events (volunteer tutoring, weekend pottery class, etc.) appear
262
+ on calendar but do not occur during travel. Do not log absences.
263
+
264
+ travel_schedule:
265
+ - destination: "Austin, TX"
266
+ start: "2026-07-11"
267
+ end: "2026-07-18"
268
+ type: vacation
269
+ source: outlook_pto
270
+ source_event_id: "BBMkADk2..."
271
+
272
+ - destination: "Denver, CO"
273
+ start: "2026-07-20"
274
+ end: null # return flight not yet booked
275
+ type: work
276
+ source: flight_pair
277
+ flight_departure: "2026-07-20T06:00-04:00"
278
+ flight_return: null
279
+ YAML
280
+ md <<~MD
281
+ ### Non-calendar context (separate file)
282
+
283
+ Data that cannot be derived from calendar — active work deadlines, upcoming notes —
284
+ lives in a separate file read by relevant secretaries:
285
+ MD
286
+ code_block(<<~YAML, lang: "yaml")
287
+ # ~/aria-os/cabinet/secretaries/cos/context.yaml
288
+ # Hand-maintained by COS secretary. Not overwritten by sync.
289
+
290
+ deadline_crunch_status:
291
+ status: "Board deck due Friday — deprioritizing non-essential secretary work"
292
+ review_meeting_planned: "2026-06-27"
293
+ YAML
294
+
295
+ # 08 — Integrations
296
+ doc_section_header "08", "Integration Points", id: "integrations"
297
+ md "### Systems that read current-state.yaml"
298
+ table(
299
+ headers: ["Consumer", "What it reads", "Purpose"],
300
+ rows: [
301
+ ["bin/aria-checkin", "travel, mode", "Step 0 verification gate; grandmaster analysis context"],
302
+ ["SessionStart hook", "synced_at, travel.ends", "Staleness warning before any session"],
303
+ ["CLAUDE.md directive", "location.current, travel.active", "Ground truth for location-sensitive reasoning"],
304
+ ["Hobby schedule reasoning", "travel.active, travel.type","Skip training/teaching when traveling"],
305
+ ["Scheduler secretary", "travel_schedule", "Plan-ahead awareness, avoid scheduling conflicts"]
306
+ ]
307
+ )
308
+ md <<~MD
309
+ ### SessionStart staleness check
310
+
311
+ Add to the SessionStart hook (alongside `bin/aria-session-nudge`):
312
+ MD
313
+ code_block(<<~'RUBY', lang: "ruby")
314
+ ruby -ryaml -rdate -e '
315
+ f = File.expand_path("~/aria-os/current-state.yaml")
316
+ exit unless File.exist?(f)
317
+ s = YAML.load_file(f)
318
+ synced = Date.parse(s["synced_at"].to_s.split("T").first) rescue nil
319
+ if synced && Date.today - synced > 1
320
+ puts "⚠️ current-state.yaml is stale (#{Date.today - synced}d) — run bin/sync-travel-state"
321
+ end
322
+ ends_str = s.dig("travel", "ends")
323
+ if ends_str && s.dig("travel", "active") && Date.parse(ends_str) < Date.today
324
+ puts "⚠️ travel.ends (#{ends_str}) is past but travel.active is true — run bin/sync-travel-state"
325
+ end
326
+ '
327
+ RUBY
328
+
329
+ # 09 — Scope
330
+ doc_section_header "09", "Scope", id: "scope"
331
+ comparison(before_label: "In Scope", after_label: "Out of Scope") do
332
+ before do
333
+ md <<~MD
334
+ - Outlook PTO event detection
335
+ - Google family calendar detection
336
+ - Outlook flight event detection
337
+ - Trip type inference from event titles
338
+ - Destination extraction from title/location field
339
+ - Checkin verification step
340
+ - Scheduler enforcement (require calendar entry)
341
+ - SessionStart staleness warning
342
+ - launchd schedule (6am daily)
343
+ MD
344
+ end
345
+ after do
346
+ md <<~MD
347
+ - Sub-day location tracking
348
+ - Multi-timezone handling
349
+ - Historical travel reconstruction
350
+ - Automatic deadline-crunch mode detection
351
+ - Seattle trip (not yet booked — tracks once calendar entry exists)
352
+ - Drive trip detection without PTO block (process rule: add PTO)
353
+ MD
354
+ end
355
+ end
356
+
357
+ # 10 — Success Criteria
358
+ doc_section_header "10", "Success Criteria", id: "success"
359
+ table(
360
+ headers: ["#", "Criterion"],
361
+ rows: [
362
+ ["SC1", "Zero location fabrications in grandmaster analysis after build — Claude reads ground truth or asks, never infers from indirect signals"],
363
+ ["SC2", "`current-state.yaml` reflects accurate travel state without manual updates on ≥90% of travel days"],
364
+ ["SC3", "Checkin verification gate catches any remaining discrepancies within 24h"],
365
+ ["SC4", "New travel appears in `current-state.yaml` within 24h of calendar entry creation"],
366
+ ["SC5", "Auth failures (OAuth expired) emit a COS inbox warning rather than silently writing stale data"],
367
+ ["SC6", "Hobby absence during travel is not logged — no false 'missed training' entries accumulate"]
368
+ ]
369
+ )
370
+
371
+ # 11 — Open Questions
372
+ doc_section_header "11", "Open Questions", id: "open"
373
+ table(
374
+ headers: ["#", "Question", "Context"],
375
+ rows: [
376
+ ["Q1", "aria-google headless auth",
377
+ "The sync script runs via launchd (non-interactive). Confirm that aria-google's OAuth token refresh works headlessly. If not, the Google calendar read will silently fail — the script must detect this and fall back to Outlook-only with a warning rather than writing incomplete state."],
378
+ ["Q2", "Destination parsing from event title",
379
+ "PTO events may not have a location field set. Title parsing needs a convention: \"Maya – PTO Maryland\" works; \"Maya – PTO\" alone does not yield a destination. Worth establishing the naming convention and adding it to the scheduler secretary's entry template."],
380
+ ["Q3", "Sam's Google calendar access",
381
+ "The script needs read access to Sam's shared family calendar to detect joint trips. Confirm aria-google has the correct scope and that the calendar is shared with Maya's Google account."],
382
+ ["Q4", "Work trip hobby implications",
383
+ "Work travel (Denver) is different from vacation — Maya may or may not train depending on the trip. Currently the model sets hobby_training: skip only for vacation type. Confirm whether work trips should also skip or leave as ambiguous (requiring checkin verification to clarify)."]
384
+ ]
385
+ )
@@ -0,0 +1,28 @@
1
+ # Checkpoints and Forms
2
+
3
+ ## Single choice
4
+
5
+ Add a `radio_group` + `button`, then use `canvas-wait <session>` to block until they click. Returns JSON with the selection.
6
+
7
+ ## Multiple questions — prefer a form
8
+
9
+ When a push ends with several questions — a mix of choices and open-ended asks — bundle them into one blocking form instead of making the user read the canvas and type each answer back in the terminal. Combine `radio_group` per choice question and `text_field` per open question, one `button` to submit, and a single `canvas-wait` call to collect everything at once:
10
+
11
+ ```ruby
12
+ streamweaver canvas-push brainstorm <<'RUBY'
13
+ header1 "A few quick questions"
14
+ radio_group :layout, ["Sidebar nav", "Top nav", "Tabs"]
15
+ radio_group :theme, ["Light", "Dark", "Match system"]
16
+ text_field :notes, label: "Anything else to flag?"
17
+ button "Submit"
18
+ RUBY
19
+ ```
20
+
21
+ ```bash
22
+ streamweaver canvas-wait brainstorm
23
+ # {"button":"Submit","state":{"layout":"Top nav","theme":"Dark","notes":"keep it minimal"}}
24
+ ```
25
+
26
+ One click, one `canvas-wait`, every answer back at once in `.state` — no round-tripping to the terminal per question.
27
+
28
+ **Boundary case:** only bundle questions that are independent of each other. If a later question depends on an earlier answer (e.g. "which of these three do you like?" then, based on that pick, "what should we change about it?"), keep those sequential — push the first, wait for the answer, then push the second.
@@ -0,0 +1,44 @@
1
+ # Cleanup and How `panel` Opens the Browser
2
+
3
+ ## Kill orphaned servers
4
+
5
+ If a previous session launched orphaned processes (canvas or standalone), clean them up:
6
+
7
+ ```bash
8
+ # List active canvas sessions
9
+ streamweaver canvas-list
10
+
11
+ # Close a specific canvas session
12
+ streamweaver canvas-close brainstorm
13
+
14
+ # Stop the entire canvas bridge
15
+ streamweaver canvas-stop
16
+
17
+ # List all loaded StreamWeaver apps
18
+ streamweaver list
19
+
20
+ # Remove all apps from the service
21
+ streamweaver clear
22
+
23
+ # Find orphaned StreamWeaver processes by port range
24
+ lsof -i :4567-4600 -sTCP:LISTEN
25
+
26
+ # Kill a specific port (e.g., 4570)
27
+ lsof -ti :4570 | xargs kill -9
28
+ ```
29
+
30
+ ## How `streamweaver panel` opens the browser
31
+
32
+ **You don't need to detect the terminal or call any helper scripts.** `streamweaver panel <session>` figures out the best experience automatically:
33
+
34
+ - **In iTerm2:** opens as a vertical split pane next to the terminal so the canvas lives alongside the conversation. This is the ideal UX — the user sees diagrams without leaving the terminal context.
35
+ - **Anywhere else (Terminal.app, VSCode terminal, kitty, alacritty, tmux, SSH, Linux):** opens in the default system browser (a new tab/window) and prints the URL.
36
+
37
+ Either way the URL is printed in stdout so the user has a fallback.
38
+
39
+ **Anti-patterns to avoid:**
40
+ - Don't run `python` scripts to drive iTerm — the `iterm2_ruby` gem (on RubyGems) drives iTerm natively and the CLI handles invocation.
41
+ - Don't try to `osascript`/AppleScript the iTerm split yourself — `streamweaver panel` already does this through the iTerm2 Ruby API.
42
+ - Don't open the browser with a shell `open` / `xdg-open` after `streamweaver panel` — it already opened one (or split into one). Doing so creates duplicates.
43
+
44
+ If you specifically need the browser opened externally even when iTerm is available (e.g., for screen sharing on a separate display), that's a feature request — file a bd issue rather than working around it in the skill.
@@ -0,0 +1,8 @@
1
+ # Example Gallery
2
+
3
+ Bundled proof examples, each demonstrating 1:1 visual parity with a real claude.ai Artifact for a different document genre. Read the closer-matching one before generating a new port from scratch, and see `docs/porting-artifacts.md` for the general process behind both (and for adding a new gallery entry).
4
+
5
+ - **`doc-parity-example.rb` + `_dsl.rb`** — long-form PRD/report genre. Techniques proven: `:doc` theme reaching Artifact parity with almost no extra CSS, `doc_header`/`doc_section_header`/`sidebar_toc`, `callout`, `mermaid`, `comparison` (before/after scope grid), `code_block`, `card`/`card_header`/`card_body`.
6
+ - **`design-review-example.rb` + `_dsl.rb` + `.css`** — editorial "design options" review genre (numbered sections, option cards, a comparison matrix, a verified/unverified checklist split). Techniques proven: status-dot chips (`div` + `phrase` + `status_dot`), a "picked/winning" state reused across both a card and a table row, checklist tiles built from `div`/`phrase` (no dedicated component), and a full bespoke re-skin via **one** unlayered CSS file targeting only stable `sw-` hooks — no framework CSS fights (structural hooks documented in `docs/theming-hooks.md`; doc-component hook docs tracked in stream_weaver-d11).
7
+
8
+ Don't assume canvas-push is limited to throwaway mockups — Tier 2 persistence (Save-as-doc, see `persistence.md`) can grow a quick brainstorm into a fully polished, long-form document. `doc-parity-example.rb` is proof: it reached 1:1 visual parity with a real claude.ai Artifact. For building documents like it, hand off to the `streamweaver-doc-builder` skill, which owns the `:doc` theme and component vocabulary in depth.
@@ -0,0 +1,58 @@
1
+ # Persisting Visual Docs
2
+
3
+ Every canvas session has two tiers of persistence — both work without any action from you.
4
+
5
+ ## Tier 1: Auto-saved history (always-on)
6
+
7
+ Every `streamweaver canvas-push` call automatically writes the DSL to `~/.streamweaver/history/<session>/<YYYYMMDD_HHMMSS>.rb`. The CLI prints the saved path on stderr:
8
+
9
+ ```
10
+ $ streamweaver canvas-push brainstorm <<'RUBY' ... RUBY
11
+ saved: /Users/.../streamweaver/history/brainstorm/20260428_153012.rb
12
+ Pushed to brainstorm
13
+ ```
14
+
15
+ You don't have to ask the user, configure anything, or run a separate save command. The history is the project's safety net — entries older than 7 days are auto-cleaned. **Never in git, never noisy.** It's the user's "I forgot to save that good diagram from yesterday" insurance.
16
+
17
+ ## Tier 2: Persistent project docs (user-driven)
18
+
19
+ Each canvas page has a floating **💾 Save as doc** button in the bottom-right. The user clicks it, names the doc (pre-filled with `<session>-YYYYMMDD-HHMM`), and the DSL is written to:
20
+
21
+ - `<git_root>/docs/streamweaver_canvas/<name>.rb` if invoked inside a git repo
22
+ - `~/.streamweaver/canvas/<name>.rb` otherwise
23
+
24
+ These are the *intentional* keep-forever artifacts that get committed to the repo and shared with teammates. `examples/doc-parity-example.rb` in this skill folder shows what this tier can grow into — a fully polished document, not just a saved sketch.
25
+
26
+ A doc saved this way gets reopened later with no live bridge behind it — the same file also gets browsed via `canvas-read` and can be run through `streamweaver export`. Not every component behaves the same way once the bridge is gone; see the `streamweaver-canvas-safe` skill before building interactivity into anything you expect to Save-as-doc.
27
+
28
+ When the same material ships to two audiences (a decision memo plus its engineering companion), don't keep two docs that each restate the same tables. Put the shared tables in one `shared/*.rb` fragment and `instance_eval` it from both bodies, so they cannot drift. Push with `cat shared/frag.rb my-doc.rb | streamweaver canvas-push my-doc` — the bridge evaluates pushed text with no filename, so `__dir__` is `nil` there and the doc cannot find the fragment on its own. Pattern, per-mode resolution rules, and org-mode caveats: `docs/shared-dsl-fragments.md` in the stream_weaver repo.
29
+
30
+ **Important:** Saving is a user action, not yours. Don't try to "save the canvas" yourself unless the user explicitly asks. If the user says "save this as X" and the button isn't easy to reach, you can fall back to:
31
+
32
+ ```bash
33
+ curl -sX POST "http://localhost:<bridge-port>/canvas/<session>/save-doc" \
34
+ -H 'Content-Type: application/json' \
35
+ -d '{"name":"<doc-name>"}'
36
+ ```
37
+
38
+ The bridge port is shown in `streamweaver canvas-list` output.
39
+
40
+ ## Browsing saved docs
41
+
42
+ `streamweaver canvas-read` with no arguments opens the project's `docs/streamweaver_canvas/` directory in a local viewer. The user just runs:
43
+
44
+ ```bash
45
+ streamweaver canvas-read
46
+ # canvas-read using default: <git_root>/docs/streamweaver_canvas
47
+ # → opens browser with sidebar listing every saved doc
48
+ ```
49
+
50
+ If the user asks "show me what we saved," that's the command. No paths needed.
51
+
52
+ To browse the *history* tier (auto-saved snapshots), pass it explicitly:
53
+
54
+ ```bash
55
+ streamweaver canvas-read ~/.streamweaver/history/brainstorm/
56
+ ```
57
+
58
+ A combined view (history + docs visible together in one viewer) is on the roadmap (`6m8`, `q4y` in beads).