hyperweave 0.2.1__tar.gz → 0.2.3__tar.gz

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 (330) hide show
  1. {hyperweave-0.2.1 → hyperweave-0.2.3}/.gitignore +2 -2
  2. {hyperweave-0.2.1 → hyperweave-0.2.3}/CHANGELOG.md +20 -0
  3. {hyperweave-0.2.1 → hyperweave-0.2.3}/PKG-INFO +1 -1
  4. {hyperweave-0.2.1 → hyperweave-0.2.3}/pyproject.toml +1 -0
  5. hyperweave-0.2.3/scripts/extract_glyphs.py +276 -0
  6. hyperweave-0.2.3/scripts/generate_hw_compliant.py +327 -0
  7. hyperweave-0.2.3/scripts/generate_proofset.py +613 -0
  8. hyperweave-0.2.3/scripts/stress_test.py +521 -0
  9. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/_version.py +2 -2
  10. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/compose/resolver.py +11 -4
  11. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/core/schema.py +13 -0
  12. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/genomes/chrome-horizon.json +4 -0
  13. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/badge/chrome-defs.j2 +8 -3
  14. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/stats/chrome-content.j2 +9 -15
  15. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/strip/chrome-defs.j2 +1 -0
  16. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/test_github_scrape.py +8 -8
  17. {hyperweave-0.2.1 → hyperweave-0.2.3}/.dockerignore +0 -0
  18. {hyperweave-0.2.1 → hyperweave-0.2.3}/.github/workflows/ci.yml +0 -0
  19. {hyperweave-0.2.1 → hyperweave-0.2.3}/.github/workflows/deploy.yml +0 -0
  20. {hyperweave-0.2.1 → hyperweave-0.2.3}/.github/workflows/publish.yml +0 -0
  21. {hyperweave-0.2.1 → hyperweave-0.2.3}/Dockerfile +0 -0
  22. {hyperweave-0.2.1 → hyperweave-0.2.3}/LICENSE +0 -0
  23. {hyperweave-0.2.1 → hyperweave-0.2.3}/README.md +0 -0
  24. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/buttons/button-liquid.svg +0 -0
  25. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/cards/card-butterfly.svg +0 -0
  26. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/cards/card-python.svg +0 -0
  27. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/cards/card-sunflower.svg +0 -0
  28. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/cards/card-waves.svg +0 -0
  29. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/brutalist-emerald/badge_critical.svg +0 -0
  30. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/brutalist-emerald/badge_passing.svg +0 -0
  31. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/brutalist-emerald/badge_warning.svg +0 -0
  32. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/brutalist-emerald/banner.svg +0 -0
  33. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/brutalist-emerald/icons/discord.svg +0 -0
  34. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/brutalist-emerald/icons/notion.svg +0 -0
  35. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/brutalist-emerald/icons/reddit.svg +0 -0
  36. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/brutalist-emerald/icons/spotify.svg +0 -0
  37. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/brutalist-emerald/icons/youtube.svg +0 -0
  38. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/brutalist-emerald/marquee_counter.svg +0 -0
  39. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/brutalist-emerald/marquee_horizontal.svg +0 -0
  40. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/brutalist-emerald/marquee_vertical.svg +0 -0
  41. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/brutalist-emerald/profile-cards/chart_stars_full.svg +0 -0
  42. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/brutalist-emerald/profile-cards/stats.svg +0 -0
  43. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/brutalist-emerald/strip.svg +0 -0
  44. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/chrome-horizon/badge_critical.svg +0 -0
  45. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/chrome-horizon/badge_passing.svg +0 -0
  46. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/chrome-horizon/badge_warning.svg +0 -0
  47. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/chrome-horizon/banner.svg +0 -0
  48. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/chrome-horizon/icons/bluesky.svg +0 -0
  49. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/chrome-horizon/icons/github.svg +0 -0
  50. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/chrome-horizon/icons/instagram.svg +0 -0
  51. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/chrome-horizon/icons/mastodon.svg +0 -0
  52. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/chrome-horizon/icons/x.svg +0 -0
  53. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/chrome-horizon/marquee_counter.svg +0 -0
  54. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/chrome-horizon/marquee_horizontal.svg +0 -0
  55. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/chrome-horizon/marquee_vertical.svg +0 -0
  56. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/chrome-horizon/profile-cards/chart_stars_full.svg +0 -0
  57. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/chrome-horizon/profile-cards/stats.svg +0 -0
  58. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/chrome-horizon/strip.svg +0 -0
  59. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/telemetry/master_card.svg +0 -0
  60. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/telemetry/receipt.svg +0 -0
  61. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/examples/telemetry/rhythm_strip.svg +0 -0
  62. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/footers/inneraura-footer-liquid.svg +0 -0
  63. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/footers/inneraura-footer-purple.svg +0 -0
  64. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/footers/inneraura-footer.svg +0 -0
  65. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/hyperweave-banner.svg +0 -0
  66. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/icons/cobalt-sapphire-discord.svg +0 -0
  67. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/icons/cobalt-sapphire-docs.svg +0 -0
  68. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/icons/cobalt-sapphire-github.svg +0 -0
  69. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/icons/cobalt-sapphire-instagram.svg +0 -0
  70. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/icons/cobalt-sapphire-linkedin.svg +0 -0
  71. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/icons/cobalt-sapphire-tiktok.svg +0 -0
  72. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/icons/cobalt-sapphire-x.svg +0 -0
  73. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/icons/cobalt-sapphire-youtube.svg +0 -0
  74. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/marquees/badge-showcase-triple.svg +0 -0
  75. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/marquees/genome-marquee-triple.svg +0 -0
  76. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/marquees/sample-badges.svg +0 -0
  77. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/mintlify-assets/404.svg +0 -0
  78. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/mintlify-assets/callout-icons.svg +0 -0
  79. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/mintlify-assets/divider.svg +0 -0
  80. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/mintlify-assets/favicon.svg +0 -0
  81. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/mintlify-assets/hero-banner.svg +0 -0
  82. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/mintlify-assets/hyperweave-banner-v2.svg +0 -0
  83. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/mintlify-assets/hyperweave-navbar-logo.svg +0 -0
  84. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/mintlify-assets/loader.svg +0 -0
  85. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/mintlify-assets/og-image.svg +0 -0
  86. {hyperweave-0.2.1 → hyperweave-0.2.3}/assets/timelines/hyperweave-roadmap.svg +0 -0
  87. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/assets/favicon.svg +0 -0
  88. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/assets/hyperweave-banner.svg +0 -0
  89. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/assets/hyperweave-navbar-logo.svg +0 -0
  90. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/assets/og-image.svg +0 -0
  91. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/explanation/architecture.mdx +0 -0
  92. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/explanation/camo-compatibility.mdx +0 -0
  93. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/explanation/cim-compliance.mdx +0 -0
  94. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/explanation/genome-profile-system.mdx +0 -0
  95. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/explanation/metadata-tiers.mdx +0 -0
  96. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/how-to/add-motion-to-badges.mdx +0 -0
  97. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/how-to/create-session-receipts.mdx +0 -0
  98. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/how-to/set-up-claude-code-hooks.mdx +0 -0
  99. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/how-to/use-live-data-badges.mdx +0 -0
  100. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/introduction.mdx +0 -0
  101. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/mint.json +0 -0
  102. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/quickstart.mdx +0 -0
  103. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/reference/cli.mdx +0 -0
  104. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/reference/compose-spec.mdx +0 -0
  105. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/reference/genomes.mdx +0 -0
  106. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/reference/http-api.mdx +0 -0
  107. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/reference/mcp-tools.mdx +0 -0
  108. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/reference/motions.mdx +0 -0
  109. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/reference/telemetry-contract.mdx +0 -0
  110. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/spec/hyperweave-protocol.mdx +0 -0
  111. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/tutorials/build-an-artifact-kit.mdx +0 -0
  112. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/tutorials/compose-your-first-badge.mdx +0 -0
  113. {hyperweave-0.2.1 → hyperweave-0.2.3}/docs/tutorials/install-session-telemetry.mdx +0 -0
  114. {hyperweave-0.2.1 → hyperweave-0.2.3}/fly.toml +0 -0
  115. {hyperweave-0.2.1 → hyperweave-0.2.3}/hooks/install.py +0 -0
  116. {hyperweave-0.2.1 → hyperweave-0.2.3}/hooks/session_end.sh +0 -0
  117. {hyperweave-0.2.1 → hyperweave-0.2.3}/justfile +0 -0
  118. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/__init__.py +0 -0
  119. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/__main__.py +0 -0
  120. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/cli.py +0 -0
  121. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/compose/__init__.py +0 -0
  122. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/compose/assembler.py +0 -0
  123. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/compose/context.py +0 -0
  124. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/compose/engine.py +0 -0
  125. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/compose/lanes.py +0 -0
  126. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/compose/resolvers/__init__.py +0 -0
  127. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/compose/resolvers/chart.py +0 -0
  128. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/compose/resolvers/stats.py +0 -0
  129. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/compose/resolvers/timeline.py +0 -0
  130. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/config/__init__.py +0 -0
  131. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/config/genome_validator.py +0 -0
  132. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/config/loader.py +0 -0
  133. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/config/registry.py +0 -0
  134. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/config/settings.py +0 -0
  135. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/connectors/__init__.py +0 -0
  136. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/connectors/arxiv.py +0 -0
  137. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/connectors/base.py +0 -0
  138. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/connectors/cache.py +0 -0
  139. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/connectors/github.py +0 -0
  140. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/connectors/rest.py +0 -0
  141. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/core/__init__.py +0 -0
  142. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/core/color.py +0 -0
  143. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/core/contracts.py +0 -0
  144. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/core/enums.py +0 -0
  145. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/core/models.py +0 -0
  146. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/core/text.py +0 -0
  147. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/core/thresholds.py +0 -0
  148. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/css/accessibility.css +0 -0
  149. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/css/bridge.css +0 -0
  150. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/css/expression.css +0 -0
  151. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/css/phase-colors.css +0 -0
  152. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/css/status.css +0 -0
  153. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/css/telemetry.css +0 -0
  154. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/font-metrics/inter.json +0 -0
  155. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/fonts/jetbrains-mono.b64 +0 -0
  156. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/fonts/jetbrains-mono.meta.json +0 -0
  157. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/fonts/orbitron.b64 +0 -0
  158. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/fonts/orbitron.meta.json +0 -0
  159. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/genomes/brutalist-emerald.json +0 -0
  160. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/genomes/telemetry-void.json +0 -0
  161. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/glyphs.json +0 -0
  162. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/motions/border/chromatic-pulse.yaml +0 -0
  163. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/motions/border/corner-trace.yaml +0 -0
  164. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/motions/border/dual-orbit.yaml +0 -0
  165. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/motions/border/entanglement.yaml +0 -0
  166. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/motions/border/rimrun.yaml +0 -0
  167. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/motions/kinetic/bars.yaml +0 -0
  168. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/motions/kinetic/breach.yaml +0 -0
  169. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/motions/kinetic/broadcast.yaml +0 -0
  170. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/motions/kinetic/cascade.yaml +0 -0
  171. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/motions/kinetic/collapse.yaml +0 -0
  172. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/motions/kinetic/converge.yaml +0 -0
  173. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/motions/kinetic/crash.yaml +0 -0
  174. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/motions/kinetic/drop.yaml +0 -0
  175. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/motions/kinetic/pulse.yaml +0 -0
  176. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/motions/static.yaml +0 -0
  177. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/policies/normal.json +0 -0
  178. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/policies/permissive.json +0 -0
  179. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/policies/ungoverned.json +0 -0
  180. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/profiles/brutalist.contract.json +0 -0
  181. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/profiles/brutalist.yaml +0 -0
  182. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/profiles/chrome.contract.json +0 -0
  183. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/profiles/chrome.yaml +0 -0
  184. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/specimens.yaml +0 -0
  185. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/telemetry/model-pricing.yaml +0 -0
  186. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/telemetry/stage-config.yaml +0 -0
  187. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/telemetry/stage-labels.yaml +0 -0
  188. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/telemetry/tool-classes.yaml +0 -0
  189. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/telemetry/tool-colors.yaml +0 -0
  190. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/terminals/arrow.json +0 -0
  191. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/terminals/aurora.json +0 -0
  192. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/terminals/beacon.json +0 -0
  193. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/terminals/bracket3d.json +0 -0
  194. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/terminals/chevron.json +0 -0
  195. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/terminals/crosshair.json +0 -0
  196. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/terminals/diamond.json +0 -0
  197. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/terminals/rocket.json +0 -0
  198. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/terminals/rules/bar.json +0 -0
  199. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/terminals/rules/dashed.json +0 -0
  200. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/terminals/rules/five-wave.json +0 -0
  201. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/terminals/rules/spectral.json +0 -0
  202. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/terminals/rules/straight.json +0 -0
  203. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/terminals/rules/wave.json +0 -0
  204. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/data/terminals/stijl.json +0 -0
  205. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/kit.py +0 -0
  206. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/mcp/__init__.py +0 -0
  207. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/mcp/__main__.py +0 -0
  208. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/mcp/server.py +0 -0
  209. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/py.typed +0 -0
  210. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/render/__init__.py +0 -0
  211. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/render/chart_engine.py +0 -0
  212. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/render/fonts.py +0 -0
  213. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/render/glyphs.py +0 -0
  214. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/render/motion.py +0 -0
  215. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/render/templates.py +0 -0
  216. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/serve/__init__.py +0 -0
  217. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/serve/app.py +0 -0
  218. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/telemetry/__init__.py +0 -0
  219. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/telemetry/capture.py +0 -0
  220. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/telemetry/contract.py +0 -0
  221. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/telemetry/corrections.py +0 -0
  222. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/telemetry/cost.py +0 -0
  223. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/telemetry/models.py +0 -0
  224. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/telemetry/parser.py +0 -0
  225. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/telemetry/stages.py +0 -0
  226. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/components/filter.svg.j2 +0 -0
  227. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/components/glyph-inline.svg.j2 +0 -0
  228. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/components/glyph.svg.j2 +0 -0
  229. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/components/gradient.svg.j2 +0 -0
  230. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/components/metadata.svg.j2 +0 -0
  231. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/components/metric.svg.j2 +0 -0
  232. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/components/phase-segment.svg.j2 +0 -0
  233. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/components/rule.svg.j2 +0 -0
  234. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/components/status.svg.j2 +0 -0
  235. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/components/terminal.svg.j2 +0 -0
  236. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/components/treemap.svg.j2 +0 -0
  237. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/document.svg.j2 +0 -0
  238. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/error-badge.svg.j2 +0 -0
  239. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/badge/brutalist-content.j2 +0 -0
  240. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/badge/brutalist-defs.j2 +0 -0
  241. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/badge/chrome-content.j2 +0 -0
  242. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/badge/default-content.j2 +0 -0
  243. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/badge/default-defs.j2 +0 -0
  244. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/badge.svg.j2 +0 -0
  245. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/banner/brutalist-defs.j2 +0 -0
  246. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/banner/chrome-defs.j2 +0 -0
  247. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/banner/default-defs.j2 +0 -0
  248. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/banner.svg.j2 +0 -0
  249. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/catalog.svg.j2 +0 -0
  250. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/chart/brutalist-content.j2 +0 -0
  251. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/chart/brutalist-defs.j2 +0 -0
  252. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/chart/chrome-content.j2 +0 -0
  253. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/chart/chrome-defs.j2 +0 -0
  254. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/chart.svg.j2 +0 -0
  255. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/divider.svg.j2 +0 -0
  256. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/icon/brutalist-content.j2 +0 -0
  257. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/icon/brutalist-defs.j2 +0 -0
  258. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/icon/chrome-content.j2 +0 -0
  259. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/icon/chrome-defs.j2 +0 -0
  260. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/icon/default-content.j2 +0 -0
  261. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/icon/default-defs.j2 +0 -0
  262. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/icon.svg.j2 +0 -0
  263. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/marquee-counter/brutalist-content.j2 +0 -0
  264. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/marquee-counter/brutalist-defs.j2 +0 -0
  265. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/marquee-counter/chrome-content.j2 +0 -0
  266. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/marquee-counter/chrome-defs.j2 +0 -0
  267. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/marquee-counter.svg.j2 +0 -0
  268. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-content.j2 +0 -0
  269. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-defs.j2 +0 -0
  270. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/marquee-horizontal/chrome-content.j2 +0 -0
  271. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/marquee-horizontal/chrome-defs.j2 +0 -0
  272. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/marquee-horizontal.svg.j2 +0 -0
  273. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/marquee-vertical/brutalist-content.j2 +0 -0
  274. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/marquee-vertical/brutalist-defs.j2 +0 -0
  275. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/marquee-vertical/chrome-content.j2 +0 -0
  276. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/marquee-vertical/chrome-defs.j2 +0 -0
  277. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/marquee-vertical.svg.j2 +0 -0
  278. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/master-card.svg.j2 +0 -0
  279. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/receipt.svg.j2 +0 -0
  280. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/rhythm-strip.svg.j2 +0 -0
  281. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/stats/brutalist-content.j2 +0 -0
  282. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/stats/brutalist-defs.j2 +0 -0
  283. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/stats/chrome-defs.j2 +0 -0
  284. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/stats.svg.j2 +0 -0
  285. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/strip/brutalist-content.j2 +0 -0
  286. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/strip/brutalist-defs.j2 +0 -0
  287. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/strip/brutalist-status.j2 +0 -0
  288. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/strip/chrome-content.j2 +0 -0
  289. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/strip/chrome-status.j2 +0 -0
  290. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/strip/default-content.j2 +0 -0
  291. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/strip/default-defs.j2 +0 -0
  292. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/strip/default-status.j2 +0 -0
  293. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/strip.svg.j2 +0 -0
  294. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/timeline/default-content.j2 +0 -0
  295. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/frames/timeline.svg.j2 +0 -0
  296. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/motions/border/chromatic-pulse.svg.j2 +0 -0
  297. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/motions/border/corner-trace.svg.j2 +0 -0
  298. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/motions/border/dual-orbit.svg.j2 +0 -0
  299. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/motions/border/entanglement.svg.j2 +0 -0
  300. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/motions/border/rimrun.svg.j2 +0 -0
  301. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/motions/kinetic/bars.svg.j2 +0 -0
  302. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/motions/kinetic/breach.svg.j2 +0 -0
  303. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/motions/kinetic/broadcast.svg.j2 +0 -0
  304. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/motions/kinetic/cascade.svg.j2 +0 -0
  305. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/motions/kinetic/collapse.svg.j2 +0 -0
  306. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/motions/kinetic/converge.svg.j2 +0 -0
  307. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/motions/kinetic/crash.svg.j2 +0 -0
  308. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/motions/kinetic/drop.svg.j2 +0 -0
  309. {hyperweave-0.2.1 → hyperweave-0.2.3}/src/hyperweave/templates/motions/kinetic/pulse.svg.j2 +0 -0
  310. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/__init__.py +0 -0
  311. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/conftest.py +0 -0
  312. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/fixtures/github_contributions/synthetic.html +0 -0
  313. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/fixtures/session.json +0 -0
  314. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/fixtures/session.jsonl +0 -0
  315. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/test_assembler.py +0 -0
  316. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/test_chart_engine.py +0 -0
  317. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/test_chart_frame.py +0 -0
  318. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/test_connectors.py +0 -0
  319. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/test_core.py +0 -0
  320. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/test_kit.py +0 -0
  321. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/test_mcp.py +0 -0
  322. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/test_paradigm_dispatch.py +0 -0
  323. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/test_proofset.py +0 -0
  324. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/test_render.py +0 -0
  325. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/test_serve.py +0 -0
  326. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/test_stats_card.py +0 -0
  327. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/test_telemetry.py +0 -0
  328. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/test_telemetry_integration.py +0 -0
  329. {hyperweave-0.2.1 → hyperweave-0.2.3}/tests/test_timeline.py +0 -0
  330. {hyperweave-0.2.1 → hyperweave-0.2.3}/uv.lock +0 -0
@@ -27,10 +27,10 @@ htmlcov/
27
27
  archive/
28
28
  .claude/
29
29
  # CLAUDE.md
30
- docs/_notes/
30
+ docs/_notes/
31
31
  outputs/
32
- scripts/
33
32
  specs/
33
+ # scripts/ is version-controlled (tests/test_proofset.py imports generate_proofset.py)
34
34
 
35
35
  assets/cards/butterfly-mcp.svg
36
36
  assets/cards/card-moon.svg
@@ -5,6 +5,26 @@ All notable changes to HyperWeave are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.3] - 2026-04-13
9
+
10
+ ### Added
11
+
12
+ - **Genome-level `text_metrics` field.** Genomes can now declare per-zone text width multipliers (`badge_label_width_factor`, `badge_value_width_factor`) so the resolver sizes frames correctly for non-default display fonts. Defaults preserve pre-v0.2.3 behavior; new fonts (e.g. Orbitron on chrome-horizon) opt in without touching the resolver. Extensible to future zones without a schema change.
13
+ - chrome-horizon ships `badge_value_width_factor = 1.35` to match Orbitron 900 glyph advances at the compositor badge scale.
14
+
15
+ ### Fixed
16
+
17
+ - **Orbitron was not actually loading on badges and strips.** The v0.2.0 font bundler emits `@font-face` via a `{{ font_faces | safe }}` Jinja variable, but that variable was only rendered in `stats/chrome-defs.j2` and `chart/chrome-defs.j2` — badge and strip chrome-defs templates omitted it. As a result, v0.2.1's switch to `var(--dna-font-display)` fell through to the system-ui fallback instead of rendering Orbitron. `{{ font_faces | safe }}` is now emitted in `badge/chrome-defs.j2` and `strip/chrome-defs.j2` so the bundled WOFF2 actually loads.
18
+ - **chrome-horizon badge typography overflow.** Compounded by the font-loading bug above, v0.2.1's badge font sizes (11/17 label/value) matched the magazine's 200x52 showcase badge but the compositor badge is 125x22 (~40% of magazine scale). Scaled to 8/11 and combined with the new `text_metrics` width factor so the value text and status diamond no longer collide.
19
+ - **Activity bar vector halos produced visible "fat bar" artifacts on mobile.** The v0.2.1 fix replaced `feGaussianBlur` with 2-layer sibling-rect halos, but those expanded the visual width of each 7px bar by 4px total, which read as blurry on small viewports. The magazine specimen's light-cyan top highlight is carried by the `ch-bar` gradient's first stop (#C8DAE6) alone, not by any halo — so the halos are removed entirely. Bars render as crisp gradient rects at every scale.
20
+ - `tier2/` added to ruff `extend-exclude` so `just fmt` no longer trips on internal research files.
21
+
22
+ ## [0.2.2] - 2026-04-13
23
+
24
+ ### Fixed
25
+
26
+ - **CI test job** was red on the v0.2.1 push because `tests/test_proofset.py` imports `scripts/generate_proofset.py`, but `scripts/` was excluded from version control by `.gitignore`. The three test runners saw `FileNotFoundError: No such file or directory: scripts/generate_proofset.py`. `scripts/` is now tracked (it is a dev-tools directory, not a runtime dependency, and remains excluded from the PyPI wheel by `[tool.hatch.build.targets.wheel].packages`).
27
+
8
28
  ## [0.2.1] - 2026-04-13
9
29
 
10
30
  Post-v0.2.0 stabilization: typography alignment, mobile rendering fix, and a streak computation correction.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperweave
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: Headless visual output layer for AI agents. One API call → self-contained SVG.
5
5
  Project-URL: Repository, https://github.com/InnerAura/hyperweave
6
6
  Project-URL: Issues, https://github.com/InnerAura/hyperweave/issues
@@ -94,6 +94,7 @@ markers = [
94
94
  line-length = 120
95
95
  target-version = "py312"
96
96
  src = ["src"]
97
+ extend-exclude = ["tier2"]
97
98
 
98
99
  [tool.ruff.lint]
99
100
  select = ["E", "F", "I", "UP", "B", "SIM", "TCH", "RUF"]
@@ -0,0 +1,276 @@
1
+ #!/usr/bin/env python3
2
+ """Extract curated glyphs from Simple Icons and output data/glyphs.json.
3
+
4
+ Build-time only. Requires: npm install simple-icons (temp dev dependency).
5
+
6
+ Usage:
7
+ npm install simple-icons # one-time
8
+ python scripts/extract_glyphs.py
9
+
10
+ Output:
11
+ src/hyperweave/data/glyphs.json
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import json
17
+ import subprocess
18
+ import sys
19
+ from pathlib import Path
20
+
21
+ # Curated icon set per PRD SS10
22
+ # Keys = Simple Icons export names (camelCase with 'si' prefix)
23
+ # Values = (our slug, category)
24
+ CURATED_ICONS: dict[str, tuple[str, str]] = {
25
+ # Social (LinkedIn/Slack removed from SI v16 — trademark)
26
+ "siGithub": ("github", "social"),
27
+ "siX": ("x", "social"),
28
+ "siDiscord": ("discord", "social"),
29
+ "siYoutube": ("youtube", "social"),
30
+ "siSpotify": ("spotify", "social"),
31
+ "siInstagram": ("instagram", "social"),
32
+ "siTiktok": ("tiktok", "social"),
33
+ "siReddit": ("reddit", "social"),
34
+ "siMastodon": ("mastodon", "social"),
35
+ "siBluesky": ("bluesky", "social"),
36
+ # Dev
37
+ "siPython": ("python", "dev"),
38
+ "siNpm": ("npm", "dev"),
39
+ "siDocker": ("docker", "dev"),
40
+ "siRust": ("rust", "dev"),
41
+ "siGo": ("go", "dev"),
42
+ "siTypescript": ("typescript", "dev"),
43
+ "siJavascript": ("javascript", "dev"),
44
+ "siReact": ("react", "dev"),
45
+ "siVuedotjs": ("vue", "dev"),
46
+ "siSvelte": ("svelte", "dev"),
47
+ "siNextdotjs": ("nextjs", "dev"),
48
+ "siNodedotjs": ("nodejs", "dev"),
49
+ "siDeno": ("deno", "dev"),
50
+ "siSwift": ("swift", "dev"),
51
+ "siKotlin": ("kotlin", "dev"),
52
+ "siCplusplus": ("cplusplus", "dev"),
53
+ "siRuby": ("ruby", "dev"),
54
+ "siPhp": ("php", "dev"),
55
+ "siElixir": ("elixir", "dev"),
56
+ "siZig": ("zig", "dev"),
57
+ # Platforms (AWS/Azure/Heroku removed from SI v16 — trademark)
58
+ "siPypi": ("pypi", "platforms"),
59
+ "siHuggingface": ("huggingface", "platforms"),
60
+ "siArxiv": ("arxiv", "platforms"),
61
+ "siKaggle": ("kaggle", "platforms"),
62
+ "siVercel": ("vercel", "platforms"),
63
+ "siNetlify": ("netlify", "platforms"),
64
+ "siCloudflare": ("cloudflare", "platforms"),
65
+ "siGooglecloud": ("googlecloud", "platforms"),
66
+ "siDigitalocean": ("digitalocean", "platforms"),
67
+ "siSupabase": ("supabase", "platforms"),
68
+ "siFirebase": ("firebase", "platforms"),
69
+ "siGithubactions": ("githubactions", "platforms"),
70
+ "siGitlab": ("gitlab", "platforms"),
71
+ "siBitbucket": ("bitbucket", "platforms"),
72
+ # Tools (VS Code removed from SI v16 — trademark)
73
+ "siNeovim": ("neovim", "tools"),
74
+ "siFigma": ("figma", "tools"),
75
+ "siNotion": ("notion", "tools"),
76
+ "siObsidian": ("obsidian", "tools"),
77
+ "siLinear": ("linear", "tools"),
78
+ "siPostman": ("postman", "tools"),
79
+ "siGrafana": ("grafana", "tools"),
80
+ "siPrometheus": ("prometheus", "tools"),
81
+ "siJetbrains": ("jetbrains", "tools"),
82
+ "siSublimetext": ("sublimetext", "tools"),
83
+ "siStorybook": ("storybook", "tools"),
84
+ "siEslint": ("eslint", "tools"),
85
+ "siPrettier": ("prettier", "tools"),
86
+ # AI / ML (OpenAI removed from SI v16 — trademark)
87
+ "siAnthropic": ("anthropic", "ai"),
88
+ "siTensorflow": ("tensorflow", "ai"),
89
+ "siPytorch": ("pytorch", "ai"),
90
+ "siJupyter": ("jupyter", "ai"),
91
+ "siApachespark": ("spark", "ai"),
92
+ "siGithubcopilot": ("githubcopilot", "ai"),
93
+ # Infrastructure (AWS/Azure/Heroku removed from SI v16 — trademark)
94
+ "siKubernetes": ("kubernetes", "infra"),
95
+ "siTerraform": ("terraform", "infra"),
96
+ "siNginx": ("nginx", "infra"),
97
+ "siRedis": ("redis", "infra"),
98
+ "siPostgresql": ("postgresql", "infra"),
99
+ "siMongodb": ("mongodb", "infra"),
100
+ "siElasticsearch": ("elasticsearch", "infra"),
101
+ "siApachekafka": ("kafka", "infra"),
102
+ "siRabbitmq": ("rabbitmq", "infra"),
103
+ "siGnubash": ("bash", "infra"),
104
+ "siGit": ("git", "infra"),
105
+ # Web / Frameworks
106
+ "siTailwindcss": ("tailwindcss", "dev"),
107
+ "siVite": ("vite", "dev"),
108
+ "siBun": ("bun", "dev"),
109
+ "siPnpm": ("pnpm", "dev"),
110
+ "siAstro": ("astro", "dev"),
111
+ "siRemix": ("remix", "dev"),
112
+ "siFlutter": ("flutter", "dev"),
113
+ "siDart": ("dart", "dev"),
114
+ "siHtml5": ("html5", "dev"),
115
+ "siSass": ("sass", "dev"),
116
+ "siMarkdown": ("markdown", "dev"),
117
+ # Mobile / OS
118
+ "siAndroid": ("android", "platforms"),
119
+ "siApple": ("apple", "platforms"),
120
+ "siLinux": ("linux", "platforms"),
121
+ # Commerce / Services
122
+ "siStripe": ("stripe", "platforms"),
123
+ "siShopify": ("shopify", "platforms"),
124
+ "siAuth0": ("auth0", "platforms"),
125
+ }
126
+
127
+ # Geometric shapes (hand-authored, not from Simple Icons)
128
+ # Defined in 640x640 viewBox per PRD SS10
129
+ GEOMETRIC_SHAPES: dict[str, dict[str, str | None]] = {
130
+ "triangle": {
131
+ "path": "M320 53.3 586.6 533.3H53.4Z",
132
+ "viewBox": "0 0 640 640",
133
+ "brand_color": None,
134
+ "category": "geometric",
135
+ },
136
+ "diamond": {
137
+ "path": "M320 53.3 506.7 320 320 586.7 133.3 320Z",
138
+ "viewBox": "0 0 640 640",
139
+ "brand_color": None,
140
+ "category": "geometric",
141
+ },
142
+ "hexagon": {
143
+ "path": "M320 53.3 566.4 186.7V453.3L320 586.7 73.6 453.3V186.7Z",
144
+ "viewBox": "0 0 640 640",
145
+ "brand_color": None,
146
+ "category": "geometric",
147
+ },
148
+ "shield": {
149
+ "path": "M320 53.3 533.3 186.7V373.3C533.3 480 320 586.7 320 586.7S106.7 480 106.7 373.3V186.7Z",
150
+ "viewBox": "0 0 640 640",
151
+ "brand_color": None,
152
+ "category": "geometric",
153
+ },
154
+ "circle": {
155
+ "path": "M320 106.7A213.3 213.3 0 11106.7 320 213.3 213.3 0 01320 106.7Z",
156
+ "viewBox": "0 0 640 640",
157
+ "brand_color": None,
158
+ "category": "geometric",
159
+ },
160
+ "star": {
161
+ "path": (
162
+ "M320 53.3 396.4 237.8 586.7 253.3 440 381.5 483.3 566.7"
163
+ " 320 471.1 156.7 566.7 200 381.5 53.3 253.3 243.6 237.8Z"
164
+ ),
165
+ "viewBox": "0 0 640 640",
166
+ "brand_color": None,
167
+ "category": "geometric",
168
+ },
169
+ }
170
+
171
+
172
+ def extract_from_npm() -> dict[str, dict[str, object]]:
173
+ """Extract icons from the simple-icons npm package via Node.js subprocess.
174
+
175
+ Uses `node -e` to require simple-icons, extract all curated paths,
176
+ and return JSON to stdout. This avoids parsing JS manually.
177
+ """
178
+ # Build the Node.js extraction script
179
+ icon_keys = list(CURATED_ICONS.keys())
180
+ icon_keys_json = json.dumps(icon_keys)
181
+ slugs_json = json.dumps({k: v[0] for k, v in CURATED_ICONS.items()})
182
+ cats_json = json.dumps({k: v[1] for k, v in CURATED_ICONS.items()})
183
+
184
+ node_script = f"""
185
+ const si = require('simple-icons');
186
+ const keys = {icon_keys_json};
187
+ const slugs = {slugs_json};
188
+ const cats = {cats_json};
189
+ const result = {{}};
190
+ for (const key of keys) {{
191
+ const icon = si[key];
192
+ if (!icon) {{
193
+ process.stderr.write('WARN: ' + key + ' not found in simple-icons\\n');
194
+ continue;
195
+ }}
196
+ result[slugs[key]] = {{
197
+ path: icon.path,
198
+ viewBox: '0 0 24 24',
199
+ brand_color: '#' + icon.hex,
200
+ category: cats[key],
201
+ }};
202
+ }}
203
+ process.stdout.write(JSON.stringify(result));
204
+ """
205
+
206
+ try:
207
+ proc = subprocess.run(
208
+ ["node", "-e", node_script],
209
+ capture_output=True,
210
+ text=True,
211
+ timeout=30,
212
+ )
213
+ if proc.returncode != 0:
214
+ print(f"Node.js extraction failed: {proc.stderr}", file=sys.stderr)
215
+ return {}
216
+ if proc.stderr:
217
+ # Print warnings (missing icons)
218
+ for line in proc.stderr.strip().splitlines():
219
+ print(line, file=sys.stderr)
220
+
221
+ return json.loads(proc.stdout)
222
+
223
+ except FileNotFoundError:
224
+ print("Node.js not found. Install Node.js to extract Simple Icons.", file=sys.stderr)
225
+ return {}
226
+ except subprocess.TimeoutExpired:
227
+ print("Node.js extraction timed out.", file=sys.stderr)
228
+ return {}
229
+ except json.JSONDecodeError as e:
230
+ print(f"Invalid JSON from Node.js: {e}", file=sys.stderr)
231
+ return {}
232
+
233
+
234
+ def main() -> None:
235
+ """Run glyph extraction and write data/glyphs.json."""
236
+ output_path = Path(__file__).resolve().parent.parent / "src" / "hyperweave" / "data" / "glyphs.json"
237
+
238
+ # Extract from npm (via Node subprocess)
239
+ glyphs = extract_from_npm()
240
+
241
+ if not glyphs:
242
+ print("No SI icons extracted. Check that simple-icons is installed.", file=sys.stderr)
243
+ print("Run: npm install simple-icons", file=sys.stderr)
244
+ sys.exit(1)
245
+
246
+ # Add geometric shapes (always)
247
+ for glyph_id, definition in GEOMETRIC_SHAPES.items():
248
+ glyphs[glyph_id] = definition # type: ignore[assignment]
249
+
250
+ # Sort by category then name for readability
251
+ sorted_glyphs = dict(
252
+ sorted(
253
+ glyphs.items(),
254
+ key=lambda x: (x[1].get("category", "zzz"), x[0]),
255
+ )
256
+ )
257
+
258
+ # Write output
259
+ output_path.parent.mkdir(parents=True, exist_ok=True)
260
+ with output_path.open("w", encoding="utf-8") as f:
261
+ json.dump(sorted_glyphs, f, indent=2)
262
+ f.write("\n")
263
+
264
+ # Summary
265
+ by_cat: dict[str, int] = {}
266
+ for v in sorted_glyphs.values():
267
+ cat = v.get("category", "unknown")
268
+ by_cat[cat] = by_cat.get(cat, 0) + 1
269
+
270
+ print(f"Wrote {len(sorted_glyphs)} glyphs to {output_path}")
271
+ for cat, count in sorted(by_cat.items()):
272
+ print(f" {cat}: {count}")
273
+
274
+
275
+ if __name__ == "__main__":
276
+ main()
@@ -0,0 +1,327 @@
1
+ #!/usr/bin/env python3
2
+ """Generate HW-compliant versions of all specimen SVGs.
3
+
4
+ For each specimen SVG in specs/components/ and specs/motion/:
5
+ 1. Do NOT modify originals
6
+ 2. Create hw-compliant/ subfolder within each specimen's directory
7
+ 3. Regenerate with full HyperWeave v8.0 protocol scaffolding
8
+ 4. ZERO visual changes
9
+
10
+ Usage:
11
+ python3 scripts/generate_hw_compliant.py
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import re
17
+ import uuid
18
+ from datetime import UTC, datetime
19
+ from pathlib import Path
20
+
21
+ ROOT = Path(__file__).resolve().parent.parent
22
+ SPECS = ROOT / "specs"
23
+
24
+
25
+ def _has_hw_metadata(svg: str) -> bool:
26
+ """Check if SVG already has HW protocol metadata."""
27
+ return "hw:artifact" in svg and "xmlns:hw" in svg and 'role="img"' in svg
28
+
29
+
30
+ def _has_reduced_motion(svg: str) -> bool:
31
+ return "prefers-reduced-motion" in svg
32
+
33
+
34
+ def _has_color_scheme(svg: str) -> bool:
35
+ return "prefers-color-scheme" in svg
36
+
37
+
38
+ def _extract_viewbox(svg: str) -> str:
39
+ m = re.search(r'viewBox="([^"]+)"', svg, re.IGNORECASE)
40
+ return m.group(1) if m else "0 0 800 220"
41
+
42
+
43
+ def _extract_dims(svg: str) -> tuple[str, str]:
44
+ """Extract width/height from SVG, preserving original values exactly."""
45
+ w = re.search(r'width="([^"]+)"', svg)
46
+ h = re.search(r'height="([^"]+)"', svg)
47
+ return (w.group(1) if w else "800", h.group(1) if h else "220")
48
+
49
+
50
+ def _extract_svg_tag(svg: str) -> tuple[str, str]:
51
+ """Split into the opening <svg ...> tag and everything after."""
52
+ m = re.match(r"(<svg[^>]*>)(.*)", svg, re.DOTALL)
53
+ if m:
54
+ return m.group(1), m.group(2)
55
+ return "<svg>", svg
56
+
57
+
58
+ def _augment_svg_tag(tag: str, attrs: dict[str, str]) -> str:
59
+ """Add attributes to an existing <svg ...> tag without replacing originals.
60
+
61
+ Preserves ALL original attributes (xmlns, viewBox, width, height, etc.).
62
+ Only ADDS attributes that don't already exist in the tag.
63
+ """
64
+ for key, val in attrs.items():
65
+ # Skip if attribute already exists
66
+ if f"{key}=" in tag or f"{key} =" in tag:
67
+ continue
68
+ # Insert before the closing >
69
+ tag = tag.rstrip(">").rstrip() + f'\n {key}="{val}">'
70
+ # Fix: ensure single closing >
71
+ tag = tag.replace(">>", ">")
72
+ return tag
73
+
74
+
75
+ def _infer_type(path: Path) -> str:
76
+ """Infer artifact type from directory structure."""
77
+ parts = str(path).lower()
78
+ if "divider" in parts:
79
+ return "divider"
80
+ if "icon" in parts:
81
+ return "icon"
82
+ if "banner" in parts or "holofoil" in parts:
83
+ return "banner"
84
+ if "marquee" in parts:
85
+ return "marquee"
86
+ if "border-frame" in parts:
87
+ return "motion-primitive"
88
+ if "kinetic-typography" in parts:
89
+ return "kinetic-typography"
90
+ return "specimen"
91
+
92
+
93
+ def _infer_motion_id(path: Path) -> str:
94
+ """Infer motion ID from filename."""
95
+ name = path.stem.lower()
96
+ mapping = {
97
+ "002-chromatic-pulse": "chromatic-pulse",
98
+ "003-corner-trace": "corner-trace",
99
+ "004-dual-orbit": "dual-orbit",
100
+ "020-quantum-entanglement": "entanglement",
101
+ "rim-runners": "rimrun",
102
+ "banner_bars": "bars",
103
+ "banner_broadcast": "broadcast",
104
+ "banner_cascade": "cascade",
105
+ "banner_convergence": "converge",
106
+ "banner_drop": "drop",
107
+ "crash": "crash",
108
+ "chromatic_impact_physics": "impact",
109
+ "quantum_tunneling_breach": "breach",
110
+ "pulse": "pulse",
111
+ "z_axis_illusion_via_exponential_scale_blur_convergence": "collapse",
112
+ }
113
+ return mapping.get(name, name)
114
+
115
+
116
+ def _slug(path: Path) -> str:
117
+ return path.stem.lower().replace(" ", "-").replace("_", "-")
118
+
119
+
120
+ def _check_cim(svg: str) -> tuple[bool, str]:
121
+ """Check CIM compliance and return waiver if needed."""
122
+ non_cim_indicators = []
123
+ if "stop-color" in svg and "animate" in svg.lower():
124
+ non_cim_indicators.append("stop-color animation")
125
+ if "stroke-dashoffset" in svg and "animate" in svg.lower():
126
+ non_cim_indicators.append("stroke-dashoffset animation")
127
+ if "stroke-width" in svg and "<animate" in svg.lower() and "stroke-width" in svg.split("<animate")[1][:200]:
128
+ non_cim_indicators.append("stroke-width animation")
129
+ if "letter-spacing" in svg and "animate" in svg.lower():
130
+ non_cim_indicators.append("letter-spacing animation")
131
+ if "stdDeviation" in svg and "animate" in svg.lower():
132
+ non_cim_indicators.append("filter blur animation")
133
+ if "baseFrequency" in svg and "animate" in svg.lower():
134
+ non_cim_indicators.append("turbulence animation")
135
+ if "clip-path" in svg and "@keyframes" in svg:
136
+ non_cim_indicators.append("clip-path animation")
137
+ # Check for animated gradients
138
+ if re.search(r'<animate[^>]*attributeName="x[12]"', svg, re.IGNORECASE):
139
+ non_cim_indicators.append("gradient coordinate animation")
140
+
141
+ if non_cim_indicators:
142
+ return False, f"Non-CIM: {', '.join(non_cim_indicators)}"
143
+ return True, ""
144
+
145
+
146
+ def wrap_specimen(src: Path) -> str:
147
+ """Wrap a raw specimen SVG with HW v8.0 protocol scaffolding.
148
+
149
+ CRITICAL: The original <svg> tag's xmlns, viewBox, width, height are
150
+ preserved EXACTLY. We only ADD new attributes (xmlns:hw, role,
151
+ aria-labelledby, data-hw-*). Never replace existing ones.
152
+ """
153
+ svg = src.read_text(encoding="utf-8").strip()
154
+
155
+ # Already compliant — add only missing layers
156
+ if _has_hw_metadata(svg):
157
+ if not _has_reduced_motion(svg):
158
+ svg = _inject_reduced_motion(svg)
159
+ if not _has_color_scheme(svg):
160
+ svg = _inject_color_scheme(svg)
161
+ return svg
162
+
163
+ # Raw specimen — full protocol wrap
164
+ artifact_id = str(uuid.uuid4())
165
+ uid = f"hw-{artifact_id[:8]}"
166
+ now = datetime.now(UTC).isoformat()
167
+ w, h = _extract_dims(svg)
168
+ art_type = _infer_type(src)
169
+ slug = _slug(src)
170
+ motion_id = _infer_motion_id(src) if "motion" in str(src) else "static"
171
+ cim_ok, cim_waiver = _check_cim(svg)
172
+
173
+ # Extract the original <svg ...> tag and inner content
174
+ svg_tag, inner = _extract_svg_tag(svg)
175
+ if inner.rstrip().endswith("</svg>"):
176
+ inner = inner.rstrip()[:-6]
177
+
178
+ # Augment the ORIGINAL tag — preserve xmlns, viewBox, width, height
179
+ augmented = _augment_svg_tag(
180
+ svg_tag,
181
+ {
182
+ "xmlns:hw": "https://hyperweave.dev/hw/v8.0",
183
+ "role": "img",
184
+ "aria-labelledby": f"{uid}-title {uid}-desc",
185
+ "data-hw-id": artifact_id,
186
+ "data-hw-frame": art_type,
187
+ "data-hw-chromatic": "adaptive",
188
+ "data-hw-motion": motion_id,
189
+ },
190
+ )
191
+
192
+ perf = "composite-only" if cim_ok else "paint-ok"
193
+ cim_str = "true" if cim_ok else "false"
194
+ tradeoffs = "CIM-compliant: uses only compositor-friendly properties." if cim_ok else f"CIM waiver: {cim_waiver}"
195
+
196
+ return f'''{augmented}
197
+
198
+ <title id="{uid}-title">{slug} -- HyperWeave Specimen</title>
199
+ <desc id="{uid}-desc">HW-compliant version of {src.name}. Type: {art_type}. Zero visual changes from original.</desc>
200
+
201
+ <metadata>
202
+ <hw:artifact id="{artifact_id}" type="{art_type}" series="specimens" version="1.0.0"
203
+ xmlns:hw="https://hyperweave.dev/hw/v8.0">
204
+ <hw:provenance>
205
+ <hw:generator>HyperWeave Specimen Wrapper v0.1 (InnerAura Labs)</hw:generator>
206
+ <hw:created>{now}</hw:created>
207
+ <hw:human-directed>true</hw:human-directed>
208
+ <hw:source-file>{src.relative_to(ROOT)}</hw:source-file>
209
+ </hw:provenance>
210
+ <hw:spec size="{w}x{h}" performance="{perf}"
211
+ theme="adaptive" a11y="WCAG-AA"/>
212
+ <hw:composition>
213
+ <hw:frame>{art_type}</hw:frame>
214
+ <hw:environment motion="{motion_id}" status="active"/>
215
+ </hw:composition>
216
+ <hw:aesthetic>
217
+ <hw:motion vocabulary="{motion_id}" cim-compliant="{cim_str}"/>
218
+ </hw:aesthetic>
219
+ <hw:reasoning>
220
+ <hw:intent>Specimen artifact wrapped with HW v8.0 protocol for Layer 1 editorial publishing.</hw:intent>
221
+ <hw:approach>Zero-visual-change wrapping: original SVG preserved, HW metadata injected around it.</hw:approach>
222
+ <hw:tradeoffs>{tradeoffs}</hw:tradeoffs>
223
+ </hw:reasoning>
224
+ </hw:artifact>
225
+ </metadata>
226
+
227
+ <style>
228
+ @media (prefers-reduced-motion: reduce) {{
229
+ *, *::before, *::after {{
230
+ animation-duration: 0.01ms !important;
231
+ animation-iteration-count: 1 !important;
232
+ transition-duration: 0.01ms !important;
233
+ }}
234
+ animate, animateTransform, animateMotion, set {{
235
+ display: none !important;
236
+ }}
237
+ }}
238
+ @media (prefers-color-scheme: light) {{
239
+ svg {{ --hw-scheme: light; }}
240
+ }}
241
+ @media (prefers-color-scheme: dark) {{
242
+ svg {{ --hw-scheme: dark; }}
243
+ }}
244
+ </style>
245
+
246
+ <!-- Original specimen content (zero visual changes) -->
247
+ <g data-hw-zone="specimen-content">
248
+ {_indent(inner, 4)}
249
+ </g>
250
+
251
+ </svg>'''
252
+
253
+
254
+ def _inject_reduced_motion(svg: str) -> str:
255
+ """Inject prefers-reduced-motion into existing compliant SVG."""
256
+ rm_block = """
257
+ @media (prefers-reduced-motion: reduce) {
258
+ *, *::before, *::after {
259
+ animation-duration: 0.01ms !important;
260
+ animation-iteration-count: 1 !important;
261
+ transition-duration: 0.01ms !important;
262
+ }
263
+ animate, animateTransform, animateMotion, set {
264
+ display: none !important;
265
+ }
266
+ }"""
267
+ # Try to inject into existing <style> block
268
+ if "<style>" in svg:
269
+ return svg.replace("<style>", f"<style>{rm_block}", 1)
270
+ # Or add before </svg>
271
+ return svg.replace("</svg>", f"<style>{rm_block}</style>\n</svg>")
272
+
273
+
274
+ def _inject_color_scheme(svg: str) -> str:
275
+ """Inject prefers-color-scheme into existing compliant SVG."""
276
+ cs_block = """
277
+ @media (prefers-color-scheme: light) { svg { --hw-scheme: light; } }
278
+ @media (prefers-color-scheme: dark) { svg { --hw-scheme: dark; } }"""
279
+ if "</style>" in svg:
280
+ return svg.replace("</style>", f"{cs_block}\n</style>", 1)
281
+ return svg
282
+
283
+
284
+ def _indent(text: str, spaces: int) -> str:
285
+ """Indent every line by N spaces."""
286
+ pad = " " * spaces
287
+ return "\n".join(f"{pad}{line}" if line.strip() else line for line in text.splitlines())
288
+
289
+
290
+ def generate() -> None:
291
+ total = 0
292
+ dirs = [
293
+ SPECS / "components" / "dividers",
294
+ SPECS / "components" / "icons",
295
+ SPECS / "components" / "banners",
296
+ SPECS / "components" / "marquees",
297
+ SPECS / "motion" / "motion-border-frames",
298
+ SPECS / "motion" / "motion-kinetic-typography",
299
+ ]
300
+
301
+ for d in dirs:
302
+ if not d.exists():
303
+ continue
304
+ svgs = sorted(d.glob("*.svg"))
305
+ if not svgs:
306
+ continue
307
+
308
+ out_dir = d / "hw-compliant"
309
+ out_dir.mkdir(exist_ok=True)
310
+
311
+ for src in svgs:
312
+ wrapped = wrap_specimen(src)
313
+ out = out_dir / src.name
314
+ out.write_text(wrapped, encoding="utf-8")
315
+ total += 1
316
+
317
+ cim_ok, _ = _check_cim(wrapped)
318
+ already = _has_hw_metadata(src.read_text())
319
+ status = "copy" if already else "wrap"
320
+ cim_tag = "CIM" if cim_ok else "NON-CIM"
321
+ print(f" [{status:4s}] [{cim_tag:7s}] {src.relative_to(ROOT)}")
322
+
323
+ print(f"\nWrote {total} hw-compliant specimens.")
324
+
325
+
326
+ if __name__ == "__main__":
327
+ generate()