@caddis/cli 0.3.0 → 0.4.1

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 (244) hide show
  1. package/bundles/antigravity-plugin/agents/knowledge-transfer.md +17 -32
  2. package/bundles/antigravity-plugin/caddis_statusline.py +25 -3
  3. package/bundles/antigravity-plugin/guard_agy.py +41 -9
  4. package/bundles/antigravity-plugin/plugin.json +1 -1
  5. package/bundles/antigravity-plugin/scripts/caddis_inventory.py +73 -3
  6. package/bundles/antigravity-plugin/scripts/caddis_statusline.py +25 -3
  7. package/bundles/antigravity-plugin/scripts/check_doc_coverage.py +1 -1
  8. package/bundles/antigravity-plugin/scripts/claudster_config.py +0 -5
  9. package/bundles/antigravity-plugin/scripts/hook_log.py +1 -1
  10. package/bundles/antigravity-plugin/scripts/new-machine.ps1 +170 -0
  11. package/bundles/antigravity-plugin/scripts/oss_ask.py +1 -1
  12. package/bundles/antigravity-plugin/scripts/oss_model.py +10 -2
  13. package/bundles/antigravity-plugin/scripts/oss_review.py +1 -1
  14. package/bundles/antigravity-plugin/scripts/setup_project_ai.py +42 -10
  15. package/bundles/antigravity-plugin/session_end_agy.py +31 -6
  16. package/bundles/antigravity-plugin/skills/_registry.md +1 -0
  17. package/bundles/antigravity-plugin/skills/advisory-hub/SKILL.md +44 -0
  18. package/bundles/antigravity-plugin/skills/code-review/SKILL.md +11 -0
  19. package/bundles/antigravity-plugin/skills/feature-plan/SKILL.md +21 -0
  20. package/bundles/antigravity-plugin/skills/gate-review/SKILL.md +11 -0
  21. package/bundles/antigravity-plugin/skills/implement/SKILL.md +36 -0
  22. package/bundles/antigravity-plugin/skills/safe-cleanup/SKILL.md +186 -0
  23. package/bundles/antigravity-plugin/skills/security-review/SKILL.md +9 -0
  24. package/bundles/antigravity-plugin/skills/validate-phase/SKILL.md +31 -0
  25. package/bundles/antigravity-plugin/warm_start_agy.py +96 -9
  26. package/bundles/antigravity-plugin-extras/plugin.json +1 -1
  27. package/bundles/antigravity-plugin-extras/skills/_registry.md +1 -0
  28. package/bundles/antigravity-plugin-extras/skills/safe-cleanup/SKILL.md +186 -0
  29. package/bundles/codex/.codex/config.toml.example +36 -0
  30. package/bundles/codex/.codex/skills/coding/api-design/SKILL.md +249 -0
  31. package/bundles/codex/.codex/skills/coding/backend-development/SKILL.md +305 -0
  32. package/bundles/codex/.codex/skills/coding/code-review/SKILL.md +140 -0
  33. package/bundles/codex/.codex/skills/coding/codebase-audit/SKILL.md +204 -0
  34. package/bundles/codex/.codex/skills/coding/dashboard-perf-sweep/SKILL.md +132 -0
  35. package/bundles/codex/.codex/skills/coding/dashboard-perf-sweep/scripts/perf_sweep.py +249 -0
  36. package/bundles/codex/.codex/skills/coding/error-handling/SKILL.md +260 -0
  37. package/bundles/codex/.codex/skills/coding/fastapi-dev/SKILL.md +300 -0
  38. package/bundles/codex/.codex/skills/coding/javascript-typescript/SKILL.md +142 -0
  39. package/bundles/codex/.codex/skills/coding/python/SKILL.md +388 -0
  40. package/bundles/codex/.codex/skills/coding/refactoring/SKILL.md +486 -0
  41. package/bundles/codex/.codex/skills/coding/security-review/SKILL.md +205 -0
  42. package/bundles/codex/.codex/skills/coding/sql/SKILL.md +321 -0
  43. package/bundles/codex/.codex/skills/data/database-design/SKILL.md +177 -0
  44. package/bundles/codex/.codex/skills/data/db-diagram/SKILL.md +148 -0
  45. package/bundles/codex/.codex/skills/data/db-diagram/scripts/sql_to_graph.py +1212 -0
  46. package/bundles/codex/.codex/skills/devops/ci-cd-pipeline/SKILL.md +315 -0
  47. package/bundles/codex/.codex/skills/devops/gh-cli/SKILL.md +195 -0
  48. package/bundles/codex/.codex/skills/devops/git-commit/SKILL.md +285 -0
  49. package/bundles/codex/.codex/skills/devops/safe-cleanup/SKILL.md +186 -0
  50. package/bundles/codex/.codex/skills/devops/using-git-worktrees/SKILL.md +217 -0
  51. package/bundles/codex/.codex/skills/devops/windows-deployment/SKILL.md +880 -0
  52. package/bundles/codex/.codex/skills/docs/code-documentation/SKILL.md +271 -0
  53. package/bundles/codex/.codex/skills/docs/explainer-doc/SKILL.md +97 -0
  54. package/bundles/codex/.codex/skills/docs/technical-writing/SKILL.md +286 -0
  55. package/bundles/codex/.codex/skills/docs/writing-plans/SKILL.md +387 -0
  56. package/bundles/codex/.codex/skills/frontend/css-architecture/SKILL.md +305 -0
  57. package/bundles/codex/.codex/skills/frontend/css-architecture/references/RESPONSIVE-DESIGN.md +604 -0
  58. package/bundles/codex/.codex/skills/frontend/frontend-design/SKILL.md +193 -0
  59. package/bundles/codex/.codex/skills/frontend/frontend-design/references/my-tech-stack.md +127 -0
  60. package/bundles/codex/.codex/skills/frontend/mockup/SKILL.md +242 -0
  61. package/bundles/codex/.codex/skills/frontend/react-best-practices/SKILL.md +269 -0
  62. package/bundles/codex/.codex/skills/frontend/react-dev/README.md +404 -0
  63. package/bundles/codex/.codex/skills/frontend/react-dev/SKILL.md +459 -0
  64. package/bundles/codex/.codex/skills/frontend/react-dev/examples/generic-components.md +579 -0
  65. package/bundles/codex/.codex/skills/frontend/react-dev/examples/server-components.md +579 -0
  66. package/bundles/codex/.codex/skills/frontend/react-dev/references/event-handlers.md +574 -0
  67. package/bundles/codex/.codex/skills/frontend/react-dev/references/hooks.md +456 -0
  68. package/bundles/codex/.codex/skills/frontend/react-dev/references/react-19-patterns.md +638 -0
  69. package/bundles/codex/.codex/skills/frontend/react-dev/references/react-router.md +1002 -0
  70. package/bundles/codex/.codex/skills/frontend/react-dev/references/tanstack-router.md +587 -0
  71. package/bundles/codex/.codex/skills/frontend/ui-review/SKILL.md +176 -0
  72. package/bundles/codex/.codex/skills/frontend/ui-review/references/framework-fixes.md +471 -0
  73. package/bundles/codex/.codex/skills/frontend/ui-review/references/visual-checklist.md +236 -0
  74. package/bundles/codex/.codex/skills/frontend/warm-editorial-ui/DESIGN_TOKENS.md +487 -0
  75. package/bundles/codex/.codex/skills/frontend/warm-editorial-ui/IMPLEMENTATION_GUIDE.md +177 -0
  76. package/bundles/codex/.codex/skills/frontend/warm-editorial-ui/SKILL.md +732 -0
  77. package/bundles/codex/.codex/skills/media/draw-io/SKILL.md +162 -0
  78. package/bundles/codex/.codex/skills/media/draw-io/references/aws-icons.md +677 -0
  79. package/bundles/codex/.codex/skills/media/draw-io/references/layout-guidelines.md +142 -0
  80. package/bundles/codex/.codex/skills/media/draw-io/references/troubleshooting.md +118 -0
  81. package/bundles/codex/.codex/skills/media/draw-io/references/workflows.md +103 -0
  82. package/bundles/codex/.codex/skills/media/draw-io/scripts/convert-drawio-to-png.sh +25 -0
  83. package/bundles/codex/.codex/skills/media/draw-io/scripts/find_aws_icon.py +79 -0
  84. package/bundles/codex/.codex/skills/media/mermaid-diagrams/SKILL.md +292 -0
  85. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/ENHANCEMENTS.md +264 -0
  86. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/MERMAID-SUMMARY.md +137 -0
  87. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/advanced-features.md +556 -0
  88. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/architecture-diagrams.md +192 -0
  89. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/c4-diagrams.md +410 -0
  90. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/class-diagrams.md +361 -0
  91. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/erd-diagrams.md +510 -0
  92. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/flowcharts.md +450 -0
  93. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/sequence-diagrams.md +394 -0
  94. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/troubleshooting.md +335 -0
  95. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/workflows.md +418 -0
  96. package/bundles/codex/.codex/skills/media/particle-art/SKILL.md +243 -0
  97. package/bundles/codex/.codex/skills/media/particle-art/references/canvas-utils.ts +171 -0
  98. package/bundles/codex/.codex/skills/media/particle-art/references/dot-field.template.tsx +203 -0
  99. package/bundles/codex/.codex/skills/media/particle-art/references/flow-field.template.tsx +263 -0
  100. package/bundles/codex/.codex/skills/media/particle-art/references/node-shape.template.tsx +261 -0
  101. package/bundles/codex/.codex/skills/media/particle-art/references/shape-sampler.ts +281 -0
  102. package/bundles/codex/.codex/skills/media/particle-art/references/stipple-morph.template.tsx +167 -0
  103. package/bundles/codex/.codex/skills/media/particle-art/references/stipple.template.tsx +175 -0
  104. package/bundles/codex/.codex/skills/media/particle-art/references/trail-ghost.template.tsx +266 -0
  105. package/bundles/codex/.codex/skills/media/particle-art/references/usage-examples.md +320 -0
  106. package/bundles/codex/.codex/skills/testing/playwright/API_REFERENCE.md +653 -0
  107. package/bundles/codex/.codex/skills/testing/playwright/SKILL.md +454 -0
  108. package/bundles/codex/.codex/skills/testing/playwright/lib/helpers.js +441 -0
  109. package/bundles/codex/.codex/skills/testing/playwright/package.json +26 -0
  110. package/bundles/codex/.codex/skills/testing/playwright/run.js +228 -0
  111. package/bundles/codex/.codex/skills/testing/tdd-workflow/SKILL.md +188 -0
  112. package/bundles/codex/.codex/skills/testing/test-strategy/SKILL.md +155 -0
  113. package/bundles/codex/.codex/skills/testing/webapp-testing/LICENSE.txt +202 -0
  114. package/bundles/codex/.codex/skills/testing/webapp-testing/SKILL.md +97 -0
  115. package/bundles/codex/.codex/skills/testing/webapp-testing/examples/console_logging.py +35 -0
  116. package/bundles/codex/.codex/skills/testing/webapp-testing/examples/element_discovery.py +40 -0
  117. package/bundles/codex/.codex/skills/testing/webapp-testing/examples/static_html_automation.py +33 -0
  118. package/bundles/codex/.codex/skills/testing/webapp-testing/scripts/with_server.py +106 -0
  119. package/bundles/codex/.codex/skills/workflow/advisory-hub/SKILL.md +227 -0
  120. package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/context-template.md +214 -0
  121. package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/hub-handbook.md +233 -0
  122. package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/report-contract.md +94 -0
  123. package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/succession.md +118 -0
  124. package/bundles/codex/.codex/skills/workflow/best-practices/SKILL.md +500 -0
  125. package/bundles/codex/.codex/skills/workflow/best-practices/agents/best-practices-referencer.md +263 -0
  126. package/bundles/codex/.codex/skills/workflow/best-practices/agents/codebase-context-builder.md +326 -0
  127. package/bundles/codex/.codex/skills/workflow/best-practices/agents/task-intent-analyzer.md +245 -0
  128. package/bundles/codex/.codex/skills/workflow/best-practices/references/anti-patterns.md +571 -0
  129. package/bundles/codex/.codex/skills/workflow/best-practices/references/before-after-examples.md +1114 -0
  130. package/bundles/codex/.codex/skills/workflow/best-practices/references/best-practices-guide.md +513 -0
  131. package/bundles/codex/.codex/skills/workflow/best-practices/references/common-workflows.md +692 -0
  132. package/bundles/codex/.codex/skills/workflow/best-practices/references/prompt-patterns.md +547 -0
  133. package/bundles/codex/.codex/skills/workflow/brainstorming/SKILL.md +57 -0
  134. package/bundles/codex/.codex/skills/workflow/context-curator/SKILL.md +157 -0
  135. package/bundles/codex/.codex/skills/workflow/golden-plan/SKILL.md +586 -0
  136. package/bundles/codex/.codex/skills/workflow/preflight/SKILL.md +440 -0
  137. package/bundles/codex/.codex/skills/workflow/skill-creator/LICENSE.txt +202 -0
  138. package/bundles/codex/.codex/skills/workflow/skill-creator/SKILL.md +491 -0
  139. package/bundles/codex/.codex/skills/workflow/skill-creator/agents/analyzer.md +274 -0
  140. package/bundles/codex/.codex/skills/workflow/skill-creator/agents/comparator.md +202 -0
  141. package/bundles/codex/.codex/skills/workflow/skill-creator/agents/grader.md +223 -0
  142. package/bundles/codex/.codex/skills/workflow/skill-creator/assets/eval_review.html +146 -0
  143. package/bundles/codex/.codex/skills/workflow/skill-creator/eval-viewer/generate_review.py +471 -0
  144. package/bundles/codex/.codex/skills/workflow/skill-creator/eval-viewer/viewer.html +1325 -0
  145. package/bundles/codex/.codex/skills/workflow/skill-creator/references/schemas.md +430 -0
  146. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/__init__.py +0 -0
  147. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/aggregate_benchmark.py +401 -0
  148. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/generate_report.py +326 -0
  149. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/improve_description.py +247 -0
  150. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/package_skill.py +136 -0
  151. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/quick_validate.py +103 -0
  152. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/run_eval.py +310 -0
  153. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/run_loop.py +328 -0
  154. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/utils.py +47 -0
  155. package/bundles/codex/AGENTS.md +146 -0
  156. package/bundles/manifest.json +4 -3
  157. package/dist/claude-deepseek.js +2 -2
  158. package/dist/claude-glm.js +2 -2
  159. package/dist/claude-oss.js +2 -2
  160. package/dist/cli.js +187 -56
  161. package/package.json +1 -1
  162. package/bundles/antigravity-plugin/scripts/dream_capture.py +0 -249
  163. package/bundles/antigravity-plugin/scripts/dream_memory.py +0 -438
  164. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +0 -93
  165. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  166. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  167. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +0 -93
  168. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  169. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +0 -93
  170. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  171. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  172. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -93
  173. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  174. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  175. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  176. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +0 -93
  177. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  178. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +0 -93
  179. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  180. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +0 -94
  181. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  182. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  183. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +0 -93
  184. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  185. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +0 -93
  186. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  187. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  188. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +0 -93
  189. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  190. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  191. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  192. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  193. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  194. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  195. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  196. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  197. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +0 -93
  198. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  199. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  200. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  201. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +0 -93
  202. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  203. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  204. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +0 -93
  205. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  206. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  207. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  208. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Jura-OFL.txt +0 -93
  209. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +0 -93
  210. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  211. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  212. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  213. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  214. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-OFL.txt +0 -93
  215. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  216. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  217. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +0 -93
  218. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  219. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -93
  220. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  221. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  222. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +0 -93
  223. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  224. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  225. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +0 -93
  226. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +0 -93
  227. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  228. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  229. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +0 -93
  230. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  231. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +0 -93
  232. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  233. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  234. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +0 -93
  235. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  236. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +0 -93
  237. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  238. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  239. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  240. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  241. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +0 -93
  242. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  243. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +0 -93
  244. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
@@ -0,0 +1,171 @@
1
+ /**
2
+ * canvas-utils.ts — Shared canvas helpers for all particle-art components.
3
+ *
4
+ * Copy the functions you need directly into your component file.
5
+ * These are NOT meant to be imported as a module — they are reference implementations.
6
+ */
7
+
8
+ // ─── DPR-Correct Canvas Setup ────────────────────────────────────────────────
9
+ //
10
+ // Must be called in useEffect, AFTER the canvas ref is attached.
11
+ // Returns the 2D context; the canvas is scaled so all drawing coordinates
12
+ // are in CSS pixels (no need to multiply by dpr elsewhere).
13
+ //
14
+ // Usage:
15
+ // const ctx = setupCanvas(ref.current!, size);
16
+ //
17
+ export function setupCanvas(
18
+ el: HTMLCanvasElement,
19
+ size: number,
20
+ ): CanvasRenderingContext2D {
21
+ const ctx = el.getContext("2d")!;
22
+ const dpr = window.devicePixelRatio || 1;
23
+ el.width = size * dpr;
24
+ el.height = size * dpr;
25
+ ctx.scale(dpr, dpr);
26
+ return ctx;
27
+ }
28
+
29
+ // ─── Theme-Aware Colour Reading ───────────────────────────────────────────────
30
+ //
31
+ // Reads a CSS custom property (e.g. "--accent-strong") and returns [R, G, B].
32
+ // The value must be space-separated channels: "79 209 197".
33
+ // Call this INSIDE the RAF loop so theme changes (light/dark toggle) auto-apply.
34
+ //
35
+ // Standard variable names for projects using CSS-variable design tokens:
36
+ // "--accent-strong" → primary colour (brightest nodes, tittle glow)
37
+ // "--accent-soft" → secondary colour (lines, ambient nodes)
38
+ // "--fg" → foreground / text colour
39
+ // "--bg" → page background
40
+ //
41
+ export function themeRgb(varName: string): [number, number, number] {
42
+ const raw = getComputedStyle(document.documentElement)
43
+ .getPropertyValue(varName)
44
+ .trim()
45
+ .split(/\s+/)
46
+ .map(Number);
47
+ return [raw[0] ?? 100, raw[1] ?? 100, raw[2] ?? 100];
48
+ }
49
+
50
+ // Convenience: build a CSS rgba() string from a themeRgb result + alpha
51
+ export function rgba(
52
+ [r, g, b]: [number, number, number],
53
+ alpha: number,
54
+ ): string {
55
+ return `rgba(${r},${g},${b},${alpha.toFixed(3)})`;
56
+ }
57
+
58
+ // ─── Hex Colour Fallback ──────────────────────────────────────────────────────
59
+ //
60
+ // Use when the project does NOT have CSS custom properties.
61
+ // Pass hex strings in the component's props and convert once.
62
+ //
63
+ export function hexToRgb(hex: string): [number, number, number] {
64
+ const n = parseInt(hex.replace("#", ""), 16);
65
+ return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
66
+ }
67
+
68
+ // ─── Mouse Tracking ───────────────────────────────────────────────────────────
69
+ //
70
+ // Returns a ref-compatible object and attaches listeners to the canvas.
71
+ // The position is in CSS pixels (matches drawing coordinates).
72
+ // Returns a cleanup function — call it in the useEffect return.
73
+ //
74
+ // Usage:
75
+ // const mouse = { x: -9999, y: -9999 };
76
+ // const cleanupMouse = attachMouseTracking(el, mouse);
77
+ // return () => { cancelAnimationFrame(raf); cleanupMouse(); };
78
+ //
79
+ export function attachMouseTracking(
80
+ el: HTMLCanvasElement,
81
+ mouse: { x: number; y: number },
82
+ ): () => void {
83
+ const onMove = (e: MouseEvent) => {
84
+ const r = el.getBoundingClientRect();
85
+ mouse.x = e.clientX - r.left;
86
+ mouse.y = e.clientY - r.top;
87
+ };
88
+ const onLeave = () => {
89
+ mouse.x = -9999;
90
+ mouse.y = -9999;
91
+ };
92
+ el.addEventListener("mousemove", onMove);
93
+ el.addEventListener("mouseleave", onLeave);
94
+ return () => {
95
+ el.removeEventListener("mousemove", onMove);
96
+ el.removeEventListener("mouseleave", onLeave);
97
+ };
98
+ }
99
+
100
+ // ─── Velocity Cap ─────────────────────────────────────────────────────────────
101
+ //
102
+ // Prevents particles from flying off-screen after strong mouse interactions.
103
+ // Apply AFTER adding forces, BEFORE updating position.
104
+ //
105
+ export function capVelocity(
106
+ p: { vx: number; vy: number },
107
+ maxSpeed: number,
108
+ ): void {
109
+ const spd = Math.sqrt(p.vx * p.vx + p.vy * p.vy);
110
+ if (spd > maxSpeed) {
111
+ p.vx *= maxSpeed / spd;
112
+ p.vy *= maxSpeed / spd;
113
+ }
114
+ }
115
+
116
+ // ─── Reduced-Motion Guard ─────────────────────────────────────────────────────
117
+ //
118
+ // ⚠️ DO NOT use this as an early `return` before drawing — that leaves the
119
+ // canvas completely blank, which is WORSE than showing animation.
120
+ //
121
+ // Correct pattern for canvas components:
122
+ // draw one static frame (nodes at target positions), then skip the RAF loop.
123
+ //
124
+ // const reduced = prefersReducedMotion();
125
+ // // ... build pts, set up canvas ...
126
+ // if (reduced) {
127
+ // pts.forEach(p => { p.x = p.tx; p.y = p.ty; }); // snap to targets
128
+ // drawOneFrame(ctx, pts, size); // render static
129
+ // return; // no RAF
130
+ // }
131
+ // tick(); // full animation
132
+ //
133
+ // For HTML/stipple components the early return IS fine because HTML content
134
+ // is already visible in the DOM without JavaScript.
135
+ //
136
+ export function prefersReducedMotion(): boolean {
137
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
138
+ }
139
+
140
+ // ─── Connection Lines ─────────────────────────────────────────────────────────
141
+ //
142
+ // Draw faded lines between all particle pairs within linkDist.
143
+ // Call this BEFORE drawing nodes so lines appear underneath.
144
+ //
145
+ // pts must have { x, y } properties.
146
+ //
147
+ export function drawConnections(
148
+ ctx: CanvasRenderingContext2D,
149
+ pts: { x: number; y: number }[],
150
+ linkDist: number,
151
+ lineColor: [number, number, number],
152
+ maxAlpha = 0.32,
153
+ ): void {
154
+ const [r, g, b] = lineColor;
155
+ for (let i = 0; i < pts.length; i++) {
156
+ for (let j = i + 1; j < pts.length; j++) {
157
+ const dx = pts[i].x - pts[j].x;
158
+ const dy = pts[i].y - pts[j].y;
159
+ const d = Math.sqrt(dx * dx + dy * dy);
160
+ if (d < linkDist) {
161
+ const a = (1 - d / linkDist) * maxAlpha;
162
+ ctx.beginPath();
163
+ ctx.moveTo(pts[i].x, pts[i].y);
164
+ ctx.lineTo(pts[j].x, pts[j].y);
165
+ ctx.strokeStyle = `rgba(${r},${g},${b},${a.toFixed(3)})`;
166
+ ctx.lineWidth = 0.65;
167
+ ctx.stroke();
168
+ }
169
+ }
170
+ }
171
+ }
@@ -0,0 +1,203 @@
1
+ "use client";
2
+ /**
3
+ * dot-field.template.tsx — Gaussian canvas dot cloud with sparks template.
4
+ *
5
+ * USAGE:
6
+ * 1. Copy and rename (e.g. HeroDotField.tsx, CosmicField.tsx)
7
+ * 2. Adjust DOT_COUNT, SIGMA (spread), spark parameters to taste
8
+ * 3. Mouse moves dots away; they spring back slowly to their base positions
9
+ *
10
+ * THEMING: --accent-strong (inner/bright), --accent-soft (outer/dim) CSS vars
11
+ * DEPENDENCIES: none
12
+ */
13
+ import { useEffect, useRef } from "react";
14
+
15
+ // ── Constants ──────────────────────────────────────────────────────────────────
16
+ const DOT_COUNT = 120; // total dot count (lower for fine dot feel)
17
+ const SIGMA = 0.28; // Gaussian standard deviation [0–0.5]; lower = tighter cluster
18
+ const SPRING_BACK = 0.08; // how fast dots return to base position after repulsion
19
+ const REPEL_MULT = 0.2; // repulsion radius = size * REPEL_MULT
20
+ const SPARK_PROB = 0.008; // probability per frame to spawn a spark
21
+ const MAX_SPARKS = 8;
22
+
23
+ // ── Helpers ────────────────────────────────────────────────────────────────────
24
+ function themeRgb(v: string): [number, number, number] {
25
+ const ch = getComputedStyle(document.documentElement)
26
+ .getPropertyValue(v).trim().split(/\s+/).map(Number);
27
+ return [ch[0] ?? 100, ch[1] ?? 100, ch[2] ?? 100];
28
+ }
29
+
30
+ // Box-Muller: Gaussian sample with mean=0, std=sigma
31
+ function gaussian(sigma: number): number {
32
+ let u = 0, v = 0;
33
+ while (u === 0) u = Math.random();
34
+ while (v === 0) v = Math.random();
35
+ return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v) * sigma;
36
+ }
37
+
38
+ interface Dot {
39
+ bx: number; by: number; // base position (centre of gravity)
40
+ x: number; y: number; // current position
41
+ r: number; // radius
42
+ distRatio: number; // normalised distance from centre [0, 1]
43
+ }
44
+
45
+ interface Spark {
46
+ x: number; y: number;
47
+ vx: number; vy: number;
48
+ life: number; // [1, 0] — fades and shrinks as it dies
49
+ }
50
+
51
+ function buildDots(size: number): Dot[] {
52
+ const cx = size / 2, cy = size / 2;
53
+ const dots: Dot[] = [];
54
+ for (let i = 0; i < DOT_COUNT; i++) {
55
+ const bx = cx + gaussian(SIGMA) * size;
56
+ const by = cy + gaussian(SIGMA) * size;
57
+ const dx = (bx - cx) / (size * 0.5);
58
+ const dy = (by - cy) / (size * 0.5);
59
+ dots.push({
60
+ bx, by, x: bx, y: by,
61
+ r: Math.random() * 1.8 + 0.8,
62
+ distRatio: Math.sqrt(dx * dx + dy * dy),
63
+ });
64
+ }
65
+ return dots;
66
+ }
67
+
68
+ // ── Component ──────────────────────────────────────────────────────────────────
69
+ export function DotFieldArt({ size = 280 }: { size?: number }) {
70
+ const ref = useRef<HTMLCanvasElement>(null);
71
+
72
+ useEffect(() => {
73
+ const el = ref.current;
74
+ if (!el) return;
75
+
76
+ const ctx = el.getContext("2d")!;
77
+ const dpr = window.devicePixelRatio || 1;
78
+ el.width = size * dpr;
79
+ el.height = size * dpr;
80
+ ctx.scale(dpr, dpr);
81
+
82
+ const dots = buildDots(size);
83
+ const sparks: Spark[] = [];
84
+ const REPEL = size * REPEL_MULT;
85
+
86
+ // Mouse tracking
87
+ const mouse = { x: -9999, y: -9999 };
88
+ const onMove = (e: MouseEvent) => {
89
+ const r = el.getBoundingClientRect();
90
+ mouse.x = e.clientX - r.left;
91
+ mouse.y = e.clientY - r.top;
92
+ };
93
+ const onLeave = () => { mouse.x = -9999; mouse.y = -9999; };
94
+ el.addEventListener("mousemove", onMove);
95
+ el.addEventListener("mouseleave", onLeave);
96
+
97
+ let raf: number;
98
+
99
+ const tick = () => {
100
+ ctx.clearRect(0, 0, size, size);
101
+ const [sr, sg, sb] = themeRgb("--accent-strong");
102
+ const [lr, lg, lb] = themeRgb("--accent-soft");
103
+ const { x: mx, y: my } = mouse;
104
+
105
+ // ── Update dots ────────────────────────────────────────────────────
106
+ dots.forEach(d => {
107
+ // Repulsion
108
+ const dx = d.x - mx, dy = d.y - my;
109
+ const dist = Math.sqrt(dx * dx + dy * dy);
110
+ if (dist < REPEL && dist > 0) {
111
+ const f = ((REPEL - dist) / REPEL) * 2.5;
112
+ d.x += (dx / dist) * f;
113
+ d.y += (dy / dist) * f;
114
+ }
115
+ // Spring back to base
116
+ d.x += (d.bx - d.x) * SPRING_BACK;
117
+ d.y += (d.by - d.y) * SPRING_BACK;
118
+
119
+ // Colour blend: inner → --accent-strong, outer → --accent-soft
120
+ const t = Math.min(1, d.distRatio * 1.6);
121
+ const r2 = sr + (lr - sr) * t;
122
+ const g2 = sg + (lg - sg) * t;
123
+ const b2 = sb + (lb - sb) * t;
124
+ const alpha = Math.max(0.12, 0.85 - d.distRatio * 0.7);
125
+
126
+ ctx.beginPath();
127
+ ctx.arc(d.x, d.y, d.r, 0, Math.PI * 2);
128
+ ctx.fillStyle = `rgba(${Math.round(r2)},${Math.round(g2)},${Math.round(b2)},${alpha.toFixed(3)})`;
129
+ ctx.fill();
130
+ });
131
+
132
+ // ── Sparks ────────────────────────────────────────────────────────
133
+ if (Math.random() < SPARK_PROB && sparks.length < MAX_SPARKS) {
134
+ const angle = Math.random() * Math.PI * 2;
135
+ const spawnR = size * 0.08 + Math.random() * size * 0.15;
136
+ sparks.push({
137
+ x: size / 2 + Math.cos(angle) * spawnR,
138
+ y: size / 2 + Math.sin(angle) * spawnR,
139
+ vx: (Math.random() - 0.5) * 2.2,
140
+ vy: (Math.random() - 0.5) * 2.2,
141
+ life: 1,
142
+ });
143
+ }
144
+
145
+ for (let i = sparks.length - 1; i >= 0; i--) {
146
+ const s = sparks[i];
147
+ s.x += s.vx;
148
+ s.y += s.vy;
149
+ s.vx *= 0.96;
150
+ s.vy *= 0.96;
151
+ s.life -= 0.025;
152
+ if (s.life <= 0) { sparks.splice(i, 1); continue; }
153
+
154
+ ctx.beginPath();
155
+ ctx.arc(s.x, s.y, s.life * 1.8, 0, Math.PI * 2);
156
+ ctx.fillStyle = `rgba(${sr},${sg},${sb},${(s.life * 0.9).toFixed(3)})`;
157
+ ctx.fill();
158
+ }
159
+
160
+ raf = requestAnimationFrame(tick);
161
+ };
162
+
163
+ // Reduced-motion: draw one static frame of dots at base positions, skip the loop.
164
+ // Never return early before drawing — that leaves the canvas blank.
165
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
166
+ tick();
167
+ cancelAnimationFrame(raf);
168
+ return;
169
+ }
170
+
171
+ tick();
172
+ return () => {
173
+ cancelAnimationFrame(raf);
174
+ el.removeEventListener("mousemove", onMove);
175
+ el.removeEventListener("mouseleave", onLeave);
176
+ };
177
+ }, [size]);
178
+
179
+ return (
180
+ <div style={{ position: "relative", width: size, height: size }} aria-hidden="true">
181
+ <canvas
182
+ ref={ref}
183
+ style={{ width: size, height: size }}
184
+ />
185
+ {/* Centre ✦ as HTML so it scales with the canvas and stays crisp */}
186
+ <span
187
+ style={{
188
+ position: "absolute",
189
+ top: "50%",
190
+ left: "50%",
191
+ transform: "translate(-50%, -50%)",
192
+ fontSize: "1.4rem",
193
+ color: "rgb(var(--accent-strong))",
194
+ opacity: 0.72,
195
+ pointerEvents: "none",
196
+ userSelect: "none",
197
+ }}
198
+ >
199
+ ✦
200
+ </span>
201
+ </div>
202
+ );
203
+ }
@@ -0,0 +1,263 @@
1
+ "use client";
2
+ /**
3
+ * flow-field.template.tsx — Particles driven by 2D value noise; morph into shape on hover.
4
+ *
5
+ * USAGE:
6
+ * 1. Copy and rename (e.g. FlowField.tsx, HeroFlow.tsx)
7
+ * 2. Replace `buildTargets` with any shape from shape-sampler.ts
8
+ * 3. Tune FLOW_FORCE (current strength), FRICTION, and MORPH_SPEED
9
+ * 4. Ambient-only mode: remove letter targets entirely; particles just drift with noise
10
+ *
11
+ * KEY TECHNIQUE: every particle follows a `flowAngle` derived from two octaves of value noise
12
+ * (vnoise). On hover, spring forces toward letter targets compete with the noise field; morph
13
+ * controls the blend (0 = pure noise, 1 = 25% noise + 75% spring). Particles are drawn as
14
+ * velocity-aligned streaks to show flow direction.
15
+ *
16
+ * INTERACTION: mousemove → letter assembles from flowing particles; mouseleave → disperses back
17
+ * THEMING: --accent-strong (streak/node), --accent-soft (connection lines)
18
+ */
19
+ import { useEffect, useRef } from "react";
20
+
21
+ // ── 2D value noise ─────────────────────────────────────────────────────────────
22
+ //
23
+ // Coherent noise — same sin-hash base as shape-sampler.ts.
24
+ // Input: continuous (x, y). Output: 0..1, smoothly varying.
25
+ //
26
+ function vnoise(x: number, y: number): number {
27
+ const ix = Math.floor(x), iy = Math.floor(y);
28
+ const fx = x - ix, fy = y - iy;
29
+ const ux = fx * fx * (3 - 2 * fx); // smoothstep
30
+ const uy = fy * fy * (3 - 2 * fy);
31
+ const h = (a: number, b: number) =>
32
+ (Math.abs(Math.sin(a * 127.1 + b * 311.7) * 43758.5453)) % 1;
33
+ const aa = h(ix, iy), ba = h(ix+1, iy);
34
+ const ab = h(ix, iy+1), bb = h(ix+1, iy+1);
35
+ return aa + (ba-aa)*ux + (ab-aa)*uy + (aa-ba-ab+bb)*ux*uy;
36
+ }
37
+
38
+ // Two-octave flow angle — animates over time via `t`
39
+ // CUSTOMISE: adjust multipliers for scale (0.008 = large cells) and speed (0.00035 = slow drift)
40
+ function flowAngle(x: number, y: number, t: number): number {
41
+ const n1 = vnoise(x * 0.008 + t * 0.00035, y * 0.008);
42
+ const n2 = vnoise(x * 0.015 + t * 0.0005 + 100, y * 0.015 + 50);
43
+ return (n1 * 0.65 + n2 * 0.35) * Math.PI * 4;
44
+ }
45
+
46
+ // ── Target shape — replace or import from shape-sampler.ts ───────────────────
47
+ function cubicBezier(
48
+ t: number,
49
+ p0: [number,number], p1: [number,number],
50
+ p2: [number,number], p3: [number,number],
51
+ ): [number,number] {
52
+ const m = 1 - t;
53
+ return [
54
+ m**3*p0[0]+3*m**2*t*p1[0]+3*m*t**2*p2[0]+t**3*p3[0],
55
+ m**3*p0[1]+3*m**2*t*p1[1]+3*m*t**2*p2[1]+t**3*p3[1],
56
+ ];
57
+ }
58
+
59
+ interface Target { tx: number; ty: number; spring: number; r: number; alpha: number; isDot: boolean }
60
+
61
+ function buildTargets(size: number): Target[] {
62
+ const cx = size * 0.50;
63
+ const stemTop = size * 0.210, stemBot = size * 0.660;
64
+ const out: Target[] = [];
65
+
66
+ // Tittle
67
+ out.push({ tx: cx, ty: size * 0.130, spring: 0.030, r: 4.5, alpha: 1.0, isDot: true });
68
+
69
+ // Stem
70
+ for (let i = 0; i <= 12; i++)
71
+ out.push({ tx: cx, ty: stemTop + (i/12) * (stemBot-stemTop), spring: 0.028, r: 2.2, alpha: 0.92, isDot: false });
72
+
73
+ // Hook
74
+ const P0: [number,number] = [cx, stemBot];
75
+ const P1: [number,number] = [cx, stemBot + size*0.115];
76
+ const P2: [number,number] = [cx - size*0.115, stemBot + size*0.145];
77
+ const P3: [number,number] = [cx - size*0.155, stemBot + size*0.040];
78
+ for (let i = 1; i <= 9; i++) {
79
+ const [bx,by] = cubicBezier(i/9, P0, P1, P2, P3);
80
+ out.push({ tx: bx, ty: by, spring: 0.026, r: 2.2, alpha: 0.88, isDot: false });
81
+ }
82
+ return out;
83
+ }
84
+
85
+ // ── Types & theme helper ──────────────────────────────────────────────────────
86
+ interface Pt {
87
+ x: number; y: number; vx: number; vy: number;
88
+ tx: number; ty: number; spring: number;
89
+ r: number; alpha: number; isDot: boolean; isLetter: boolean;
90
+ }
91
+
92
+ function themeRgb(v: string): [number, number, number] {
93
+ const ch = getComputedStyle(document.documentElement)
94
+ .getPropertyValue(v).trim().split(/\s+/).map(Number);
95
+ return [ch[0], ch[1], ch[2]];
96
+ }
97
+
98
+ // ── Constants ─────────────────────────────────────────────────────────────────
99
+ const FLOW_FORCE = 0.06; // noise field push strength; raise for wilder currents
100
+ const FRICTION = 0.965; // less damping than spring components — particles glide
101
+ const MAX_SPEED = 1.8;
102
+ const MORPH_SPEED = 0.048; // lerp speed 0→1; lower = longer, dreamier transition
103
+ const LINK = 80; // connection line distance threshold (px)
104
+
105
+ // ── Component ─────────────────────────────────────────────────────────────────
106
+ export function FlowFieldArt({ size = 300 }: { size?: number }) {
107
+ const ref = useRef<HTMLCanvasElement>(null);
108
+ // useRef — never stale in the RAF loop
109
+ const active = useRef(false);
110
+ const morph = useRef(0);
111
+
112
+ useEffect(() => {
113
+ const el = ref.current;
114
+ if (!el) return;
115
+
116
+ const ctx = el.getContext("2d")!;
117
+ const dpr = window.devicePixelRatio || 1;
118
+ el.width = size * dpr;
119
+ el.height = size * dpr;
120
+ ctx.scale(dpr, dpr);
121
+
122
+ const cx = size * 0.50, cy = size * 0.46;
123
+
124
+ // Letter particles — scattered initially; attracted to letter on hover
125
+ const pts: Pt[] = buildTargets(size).map(t => ({
126
+ x: cx + (Math.random() - 0.5) * size * 0.6,
127
+ y: cy + (Math.random() - 0.5) * size * 0.6,
128
+ vx: (Math.random() - 0.5) * 0.5,
129
+ vy: (Math.random() - 0.5) * 0.5,
130
+ tx: t.tx, ty: t.ty, spring: t.spring,
131
+ r: t.r, alpha: t.alpha, isDot: t.isDot, isLetter: true,
132
+ }));
133
+
134
+ // Ambient flow-only particles — no letter target; fill the field
135
+ // CUSTOMISE: increase count for denser streams
136
+ for (let i = 0; i < 22; i++) {
137
+ pts.push({
138
+ x: Math.random() * size, y: Math.random() * size,
139
+ vx: (Math.random() - 0.5) * 0.4,
140
+ vy: (Math.random() - 0.5) * 0.4,
141
+ tx: cx, ty: cy, spring: 0,
142
+ r: Math.random() * 1.2 + 0.5,
143
+ alpha: 0.28 + Math.random() * 0.22,
144
+ isDot: false, isLetter: false,
145
+ });
146
+ }
147
+
148
+ const onMove = () => { active.current = true; };
149
+ const onLeave = () => { active.current = false; };
150
+ el.addEventListener("mousemove", onMove);
151
+ el.addEventListener("mouseleave", onLeave);
152
+
153
+ let raf: number;
154
+ let frame = 0;
155
+
156
+ const tick = () => {
157
+ ctx.clearRect(0, 0, size, size);
158
+ const [nr, ng, nb] = themeRgb("--accent-strong");
159
+ const [lr, lg, lb] = themeRgb("--accent-soft");
160
+
161
+ const m = morph.current + ((active.current ? 1 : 0) - morph.current) * MORPH_SPEED;
162
+ morph.current = m;
163
+
164
+ // Connection lines — grow more visible as letter forms
165
+ for (let i = 0; i < pts.length; i++) {
166
+ for (let j = i + 1; j < pts.length; j++) {
167
+ const dx = pts[i].x - pts[j].x, dy = pts[i].y - pts[j].y;
168
+ const d = Math.sqrt(dx*dx + dy*dy);
169
+ if (d < LINK) {
170
+ const a = (1 - d/LINK) * 0.14 * (0.4 + m*0.6);
171
+ ctx.beginPath();
172
+ ctx.moveTo(pts[i].x, pts[i].y);
173
+ ctx.lineTo(pts[j].x, pts[j].y);
174
+ ctx.strokeStyle = `rgba(${lr},${lg},${lb},${a.toFixed(3)})`;
175
+ ctx.lineWidth = 0.6;
176
+ ctx.stroke();
177
+ }
178
+ }
179
+ }
180
+
181
+ pts.forEach(p => {
182
+ // 1. Flow field force — attenuates as morph strengthens (25% remains at m=1)
183
+ const angle = flowAngle(p.x, p.y, frame);
184
+ const flowScale = 1 - m * 0.75;
185
+ p.vx += Math.cos(angle) * FLOW_FORCE * flowScale;
186
+ p.vy += Math.sin(angle) * FLOW_FORCE * flowScale;
187
+
188
+ // 2. Spring toward letter target (only for letter particles; scales with morph)
189
+ if (p.isLetter && m > 0.01) {
190
+ p.vx += (p.tx - p.x) * p.spring * m;
191
+ p.vy += (p.ty - p.y) * p.spring * m;
192
+ }
193
+
194
+ // 3. Edge wrap in idle (disable during morph to let letter form cleanly)
195
+ if (m < 0.3) {
196
+ if (p.x < 0) p.x = size;
197
+ if (p.x > size) p.x = 0;
198
+ if (p.y < 0) p.y = size;
199
+ if (p.y > size) p.y = 0;
200
+ }
201
+
202
+ p.vx *= FRICTION; p.vy *= FRICTION;
203
+ const spd = Math.sqrt(p.vx*p.vx + p.vy*p.vy);
204
+ if (spd > MAX_SPEED) { p.vx *= MAX_SPEED/spd; p.vy *= MAX_SPEED/spd; }
205
+ p.x += p.vx; p.y += p.vy;
206
+
207
+ // 4. Velocity-aligned streak — shows flow direction
208
+ // Length proportional to speed (particles moving faster cast longer streaks)
209
+ const streakLen = Math.sqrt(p.vx*p.vx + p.vy*p.vy) * 5;
210
+ const alpha = p.isLetter
211
+ ? p.alpha * (0.55 + m * 0.45) // letter particles brighten as form solidifies
212
+ : p.alpha;
213
+
214
+ ctx.beginPath();
215
+ ctx.moveTo(p.x - p.vx * streakLen, p.y - p.vy * streakLen);
216
+ ctx.lineTo(p.x, p.y);
217
+ ctx.strokeStyle = `rgba(${nr},${ng},${nb},${alpha.toFixed(3)})`;
218
+ ctx.lineWidth = p.r * 1.4;
219
+ ctx.lineCap = "round";
220
+ ctx.stroke();
221
+
222
+ // 5. Solid node dot at head — visible only once letter starts forming
223
+ if (m > 0.2 && p.isLetter) {
224
+ let nodeR = p.r;
225
+ if (p.isDot) {
226
+ nodeR = p.r + Math.sin(frame * 0.04) * 0.8;
227
+ // Pulsing glow ring for tittle
228
+ ctx.beginPath();
229
+ ctx.arc(p.x, p.y, nodeR + 4 + Math.sin(frame * 0.04) * 2, 0, Math.PI * 2);
230
+ ctx.strokeStyle = `rgba(${nr},${ng},${nb},${(0.18 * m).toFixed(3)})`;
231
+ ctx.lineWidth = 1;
232
+ ctx.stroke();
233
+ }
234
+ ctx.beginPath();
235
+ ctx.arc(p.x, p.y, nodeR * m, 0, Math.PI * 2);
236
+ ctx.fillStyle = `rgba(${nr},${ng},${nb},${(alpha * m).toFixed(3)})`;
237
+ ctx.fill();
238
+ }
239
+ });
240
+
241
+ frame++;
242
+ raf = requestAnimationFrame(tick);
243
+ };
244
+ tick();
245
+
246
+ // ⚠️ prefers-reduced-motion: do NOT early-return before drawing (leaves canvas blank).
247
+ // To support reduced-motion: snap pts to targets, call tick() once, then cancelRAF.
248
+
249
+ return () => {
250
+ cancelAnimationFrame(raf);
251
+ el.removeEventListener("mousemove", onMove);
252
+ el.removeEventListener("mouseleave", onLeave);
253
+ };
254
+ }, [size]);
255
+
256
+ return (
257
+ <canvas
258
+ ref={ref}
259
+ style={{ width: size, height: size, cursor: "crosshair" }}
260
+ aria-hidden="true"
261
+ />
262
+ );
263
+ }