hyperweave 0.2.3__tar.gz → 0.2.4__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.
- {hyperweave-0.2.3 → hyperweave-0.2.4}/CHANGELOG.md +30 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/PKG-INFO +3 -3
- {hyperweave-0.2.3 → hyperweave-0.2.4}/README.md +2 -2
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/_version.py +2 -2
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/compose/context.py +3 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/compose/engine.py +12 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/compose/resolver.py +38 -3
- hyperweave-0.2.4/src/hyperweave/compose/resolvers/chart.py +139 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/compose/resolvers/stats.py +12 -5
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/connectors/base.py +3 -1
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/connectors/github.py +31 -0
- hyperweave-0.2.4/src/hyperweave/core/state.py +45 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/profiles/chrome.yaml +2 -2
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/kit.py +2 -30
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/render/chart_engine.py +220 -15
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/components/metadata.svg.j2 +4 -4
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/chart/brutalist-content.j2 +10 -14
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/chart/chrome-content.j2 +10 -13
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/stats/chrome-content.j2 +12 -9
- hyperweave-0.2.4/src/hyperweave/templates/frames/strip/chrome-content.j2 +29 -0
- hyperweave-0.2.4/src/hyperweave/templates/frames/strip/chrome-defs.j2 +67 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/strip.svg.j2 +12 -5
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/test_chart_engine.py +145 -1
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/test_chart_frame.py +111 -3
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/test_kit.py +23 -22
- hyperweave-0.2.4/tests/test_serve_live_state.py +82 -0
- hyperweave-0.2.4/tests/test_state_inference.py +79 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/test_stats_card.py +27 -0
- hyperweave-0.2.3/src/hyperweave/compose/resolvers/chart.py +0 -125
- hyperweave-0.2.3/src/hyperweave/templates/frames/strip/chrome-content.j2 +0 -20
- hyperweave-0.2.3/src/hyperweave/templates/frames/strip/chrome-defs.j2 +0 -86
- {hyperweave-0.2.3 → hyperweave-0.2.4}/.dockerignore +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/.github/workflows/ci.yml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/.github/workflows/deploy.yml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/.github/workflows/publish.yml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/.gitignore +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/Dockerfile +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/LICENSE +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/buttons/button-liquid.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/cards/card-butterfly.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/cards/card-python.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/cards/card-sunflower.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/cards/card-waves.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/badge_critical.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/badge_passing.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/badge_warning.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/banner.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/icons/discord.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/icons/notion.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/icons/reddit.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/icons/spotify.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/icons/youtube.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/marquee_counter.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/marquee_horizontal.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/marquee_vertical.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/profile-cards/chart_stars_full.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/profile-cards/stats.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/strip.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/chrome-horizon/badge_critical.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/chrome-horizon/badge_passing.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/chrome-horizon/badge_warning.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/chrome-horizon/banner.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/chrome-horizon/icons/bluesky.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/chrome-horizon/icons/github.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/chrome-horizon/icons/instagram.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/chrome-horizon/icons/mastodon.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/chrome-horizon/icons/x.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/chrome-horizon/marquee_counter.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/chrome-horizon/marquee_horizontal.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/chrome-horizon/marquee_vertical.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/chrome-horizon/profile-cards/chart_stars_full.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/chrome-horizon/profile-cards/stats.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/chrome-horizon/strip.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/telemetry/master_card.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/telemetry/receipt.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/examples/telemetry/rhythm_strip.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/footers/inneraura-footer-liquid.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/footers/inneraura-footer-purple.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/footers/inneraura-footer.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/hyperweave-banner.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/icons/cobalt-sapphire-discord.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/icons/cobalt-sapphire-docs.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/icons/cobalt-sapphire-github.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/icons/cobalt-sapphire-instagram.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/icons/cobalt-sapphire-linkedin.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/icons/cobalt-sapphire-tiktok.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/icons/cobalt-sapphire-x.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/icons/cobalt-sapphire-youtube.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/marquees/badge-showcase-triple.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/marquees/genome-marquee-triple.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/marquees/sample-badges.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/mintlify-assets/404.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/mintlify-assets/callout-icons.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/mintlify-assets/divider.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/mintlify-assets/favicon.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/mintlify-assets/hero-banner.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/mintlify-assets/hyperweave-banner-v2.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/mintlify-assets/hyperweave-navbar-logo.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/mintlify-assets/loader.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/mintlify-assets/og-image.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/assets/timelines/hyperweave-roadmap.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/assets/favicon.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/assets/hyperweave-banner.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/assets/hyperweave-navbar-logo.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/assets/og-image.svg +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/explanation/architecture.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/explanation/camo-compatibility.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/explanation/cim-compliance.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/explanation/genome-profile-system.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/explanation/metadata-tiers.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/how-to/add-motion-to-badges.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/how-to/create-session-receipts.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/how-to/set-up-claude-code-hooks.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/how-to/use-live-data-badges.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/introduction.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/mint.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/quickstart.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/reference/cli.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/reference/compose-spec.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/reference/genomes.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/reference/http-api.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/reference/mcp-tools.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/reference/motions.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/reference/telemetry-contract.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/spec/hyperweave-protocol.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/tutorials/build-an-artifact-kit.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/tutorials/compose-your-first-badge.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/docs/tutorials/install-session-telemetry.mdx +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/fly.toml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/hooks/install.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/hooks/session_end.sh +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/justfile +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/pyproject.toml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/scripts/extract_glyphs.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/scripts/generate_hw_compliant.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/scripts/generate_proofset.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/scripts/stress_test.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/__init__.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/__main__.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/cli.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/compose/__init__.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/compose/assembler.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/compose/lanes.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/compose/resolvers/__init__.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/compose/resolvers/timeline.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/config/__init__.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/config/genome_validator.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/config/loader.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/config/registry.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/config/settings.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/connectors/__init__.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/connectors/arxiv.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/connectors/cache.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/connectors/rest.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/core/__init__.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/core/color.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/core/contracts.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/core/enums.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/core/models.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/core/schema.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/core/text.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/core/thresholds.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/css/accessibility.css +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/css/bridge.css +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/css/expression.css +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/css/phase-colors.css +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/css/status.css +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/css/telemetry.css +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/font-metrics/inter.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/fonts/jetbrains-mono.b64 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/fonts/jetbrains-mono.meta.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/fonts/orbitron.b64 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/fonts/orbitron.meta.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/genomes/brutalist-emerald.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/genomes/chrome-horizon.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/genomes/telemetry-void.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/glyphs.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/motions/border/chromatic-pulse.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/motions/border/corner-trace.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/motions/border/dual-orbit.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/motions/border/entanglement.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/motions/border/rimrun.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/bars.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/breach.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/broadcast.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/cascade.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/collapse.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/converge.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/crash.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/drop.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/pulse.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/motions/static.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/policies/normal.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/policies/permissive.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/policies/ungoverned.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/profiles/brutalist.contract.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/profiles/brutalist.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/profiles/chrome.contract.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/specimens.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/telemetry/model-pricing.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/telemetry/stage-config.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/telemetry/stage-labels.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/telemetry/tool-classes.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/telemetry/tool-colors.yaml +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/terminals/arrow.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/terminals/aurora.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/terminals/beacon.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/terminals/bracket3d.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/terminals/chevron.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/terminals/crosshair.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/terminals/diamond.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/terminals/rocket.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/terminals/rules/bar.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/terminals/rules/dashed.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/terminals/rules/five-wave.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/terminals/rules/spectral.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/terminals/rules/straight.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/terminals/rules/wave.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/data/terminals/stijl.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/mcp/__init__.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/mcp/__main__.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/mcp/server.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/py.typed +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/render/__init__.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/render/fonts.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/render/glyphs.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/render/motion.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/render/templates.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/serve/__init__.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/serve/app.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/telemetry/__init__.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/telemetry/capture.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/telemetry/contract.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/telemetry/corrections.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/telemetry/cost.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/telemetry/models.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/telemetry/parser.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/telemetry/stages.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/components/filter.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/components/glyph-inline.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/components/glyph.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/components/gradient.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/components/metric.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/components/phase-segment.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/components/rule.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/components/status.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/components/terminal.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/components/treemap.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/document.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/error-badge.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/badge/brutalist-content.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/badge/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/badge/chrome-content.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/badge/chrome-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/badge/default-content.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/badge/default-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/badge.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/banner/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/banner/chrome-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/banner/default-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/banner.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/catalog.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/chart/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/chart/chrome-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/chart.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/divider.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/icon/brutalist-content.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/icon/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/icon/chrome-content.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/icon/chrome-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/icon/default-content.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/icon/default-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/icon.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-counter/brutalist-content.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-counter/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-counter/chrome-content.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-counter/chrome-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-counter.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-content.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-horizontal/chrome-content.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-horizontal/chrome-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-horizontal.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-vertical/brutalist-content.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-vertical/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-vertical/chrome-content.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-vertical/chrome-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-vertical.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/master-card.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/receipt.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/rhythm-strip.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/stats/brutalist-content.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/stats/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/stats/chrome-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/stats.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/strip/brutalist-content.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/strip/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/strip/brutalist-status.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/strip/chrome-status.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/strip/default-content.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/strip/default-defs.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/strip/default-status.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/timeline/default-content.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/frames/timeline.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/motions/border/chromatic-pulse.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/motions/border/corner-trace.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/motions/border/dual-orbit.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/motions/border/entanglement.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/motions/border/rimrun.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/bars.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/breach.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/broadcast.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/cascade.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/collapse.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/converge.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/crash.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/drop.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/pulse.svg.j2 +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/__init__.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/conftest.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/fixtures/github_contributions/synthetic.html +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/fixtures/session.json +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/fixtures/session.jsonl +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/test_assembler.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/test_connectors.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/test_core.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/test_github_scrape.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/test_mcp.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/test_paradigm_dispatch.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/test_proofset.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/test_render.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/test_serve.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/test_telemetry.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/test_telemetry_integration.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/tests/test_timeline.py +0 -0
- {hyperweave-0.2.3 → hyperweave-0.2.4}/uv.lock +0 -0
|
@@ -5,6 +5,36 @@ 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.4] - 2026-04-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Truthful chart rendering.** Charts now reflect what the data actually says. When a GitHub fetch fails, the chart renders a clear `DATA UNAVAILABLE` overlay instead of a synthesized placeholder curve. Brand-new repos with zero stars render `NEW REPO · NO STARS YET`. Every chart carries a status attribute (`fresh`, `stale`, `empty`) that downstream consumers can read.
|
|
13
|
+
- **Adaptive chart axes.** Y-tick values auto-generate from the data range using round numbers (1, 2, 5 × powers of 10), so labels agree with the curve at any scale. X-axis year labels come from actual point timestamps — the old hardcoded `EARLY '24` / `LATE '24` placeholders are gone.
|
|
14
|
+
- **Single-page stargazer granularity.** Repos with ≤30 stars now render with individual stargazer timestamps instead of collapsed samples, so early-stage projects produce visible curves instead of flat lines.
|
|
15
|
+
- **Adaptive strip metric cell pitch.** Strip cells size themselves to fit the widest metric, then propagate that pitch uniformly. Long values no longer overflow; the grid stays balanced.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **Live state now reflects live data.** Badges, strips, and live routes across HTTP, CLI, and MCP auto-detect pass/fail/warning/critical state from fetched values. Previously this inference ran only inside `hw kit readme`, so a live badge fetching `build=failing` rendered as green ("active") instead of red. Explicit overrides (`?state=passing`, `--state failing`, MCP `state` argument) continue to win.
|
|
20
|
+
- **Generated SVGs advertised the wrong version in their embedded metadata.** The `version` variable that feeds `<hw:artifact version="...">`, `<hw:generator>`, `<hw:genome>`, and `<dc:creator>` was never populated, so every SVG from v0.2.0–v0.2.3 declared itself as `0.1.0` regardless of installed version. The metadata now reflects the real release.
|
|
21
|
+
- **Stats card activity bars blurred on mobile.** Bars now render with a solid fill and pixel-crisp edges rather than a multi-stop vertical gradient. The icy highlight lives in the horizon shelf-glow above the bars, so small-viewport rendering stays sharp.
|
|
22
|
+
- **Strip chrome typography drifted from badge.** Chrome strip metric values render in Orbitron 17px upright, matching the chrome badge — replacing the previous Impact-italic treatment. Chrome strip and chrome badge now share one typographic system.
|
|
23
|
+
- **Strip chrome filter stack caused mobile blur.** Removed specular lighting and the text-shadow filter, both of which rasterized poorly on small frames. Replaced the sheen with vector hairlines so highlights stay pixel-perfect at every size.
|
|
24
|
+
- **Chart axis labels no longer hardcoded.** Both brutalist and chrome chart templates read axis labels from the chart engine; changing the data range updates the labels automatically.
|
|
25
|
+
- **User-Agent header reported the wrong version.** Outbound HTTP requests identify as the installed HyperWeave version rather than a stale `0.1.0`.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- **Chart resolver is a three-state machine.** `stale` (fetch failed), `empty` (zero-value repo), or `fresh` (real data). The previous behavior of synthesizing a placeholder curve on failure is removed — data truthfulness is now a rendering contract.
|
|
30
|
+
- **Strip skew is profile-declared.** The hardcoded italic skew on chrome strips has been removed; profiles that want a skew opt in explicitly via a profile field.
|
|
31
|
+
|
|
32
|
+
### Known follow-ups (not blocking v0.2.4)
|
|
33
|
+
|
|
34
|
+
- **Chart hero strip placeholders.** The static repo slug and date-range label on charts are not yet data-driven; only the curve and axes are.
|
|
35
|
+
- **Chart/stats data-provenance states lack dedicated styling.** Charts with failed fetches render a `stale` status, but no CSS rule matches it — the visual is covered by the text overlay rather than a distinct chrome color. Same for `empty`.
|
|
36
|
+
- **`loop` artifact status is declared but unused.** The status enum includes `loop`, exposed via MCP schema, but no inference logic produces it and no CSS animation exists for it.
|
|
37
|
+
|
|
8
38
|
## [0.2.3] - 2026-04-13
|
|
9
39
|
|
|
10
40
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyperweave
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
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
|
|
@@ -325,8 +325,8 @@ Every artifact ships with:
|
|
|
325
325
|
|---|---|
|
|
326
326
|
| Frame types | 15 (badge, strip, banner, icon, divider, marquee-h/v/counter, receipt, rhythm-strip, master-card, catalog, stats, chart, timeline) |
|
|
327
327
|
| Genomes | 2 (brutalist-emerald, chrome-horizon) |
|
|
328
|
-
| Motion configs |
|
|
329
|
-
| Glyphs |
|
|
328
|
+
| Motion configs | 15 (1 static + 5 border SMIL + 9 kinetic CSS) |
|
|
329
|
+
| Glyphs | 99 (93 Simple Icons + 6 geometric) |
|
|
330
330
|
| Divider variants | 5 (block, current, takeoff, void, zeropoint) |
|
|
331
331
|
| Metadata tiers | 5 (Tier 0 silent → Tier 4 reasoning) |
|
|
332
332
|
| Paradigms | 3 per frame (default, brutalist, chrome) — per-frame dispatch from genome |
|
|
@@ -293,8 +293,8 @@ Every artifact ships with:
|
|
|
293
293
|
|---|---|
|
|
294
294
|
| Frame types | 15 (badge, strip, banner, icon, divider, marquee-h/v/counter, receipt, rhythm-strip, master-card, catalog, stats, chart, timeline) |
|
|
295
295
|
| Genomes | 2 (brutalist-emerald, chrome-horizon) |
|
|
296
|
-
| Motion configs |
|
|
297
|
-
| Glyphs |
|
|
296
|
+
| Motion configs | 15 (1 static + 5 border SMIL + 9 kinetic CSS) |
|
|
297
|
+
| Glyphs | 99 (93 Simple Icons + 6 geometric) |
|
|
298
298
|
| Divider variants | 5 (block, current, takeoff, void, zeropoint) |
|
|
299
299
|
| Metadata tiers | 5 (Tier 0 silent → Tier 4 reasoning) |
|
|
300
300
|
| Paradigms | 3 per frame (default, brutalist, chrome) — per-frame dispatch from genome |
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 2,
|
|
21
|
+
__version__ = version = '0.2.4'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 4)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -14,6 +14,7 @@ from collections.abc import Callable
|
|
|
14
14
|
from datetime import UTC, datetime
|
|
15
15
|
from typing import TYPE_CHECKING, Any
|
|
16
16
|
|
|
17
|
+
from hyperweave import __version__
|
|
17
18
|
from hyperweave.core.enums import ArtifactStatus, FrameType, MotionId
|
|
18
19
|
|
|
19
20
|
if TYPE_CHECKING:
|
|
@@ -152,6 +153,8 @@ def _base_context(
|
|
|
152
153
|
# Timestamp
|
|
153
154
|
"created": datetime.now(UTC).isoformat(),
|
|
154
155
|
"created_at": datetime.now(UTC).isoformat(),
|
|
156
|
+
# Version -- read by templates/components/metadata.svg.j2
|
|
157
|
+
"version": __version__,
|
|
155
158
|
# Embedded fonts (base64 @font-face CSS)
|
|
156
159
|
"font_faces": _load_font_faces(resolved.genome),
|
|
157
160
|
}
|
|
@@ -11,6 +11,18 @@ def compose(spec: ComposeSpec) -> ComposeResult:
|
|
|
11
11
|
"""Compose an artifact from a ComposeSpec."""
|
|
12
12
|
start = time.monotonic()
|
|
13
13
|
|
|
14
|
+
# ── 0. Infer state from value when the caller left the default ──
|
|
15
|
+
# Any explicit override (?state=failing, --state passing, MCP state arg,
|
|
16
|
+
# etc.) sets spec.state to something other than "active" and survives
|
|
17
|
+
# this step untouched. This is the single chokepoint covering HTTP,
|
|
18
|
+
# CLI, MCP, and kit.
|
|
19
|
+
if spec.state == "active" and spec.value:
|
|
20
|
+
from hyperweave.core.state import infer_state
|
|
21
|
+
|
|
22
|
+
inferred = infer_state(spec.title, spec.value)
|
|
23
|
+
if inferred != "active":
|
|
24
|
+
spec = spec.model_copy(update={"state": inferred})
|
|
25
|
+
|
|
14
26
|
# ── 1. Resolve genome, profile, frame ──
|
|
15
27
|
from hyperweave.compose.resolver import resolve
|
|
16
28
|
|
|
@@ -249,9 +249,8 @@ def resolve_strip(
|
|
|
249
249
|
height = 52
|
|
250
250
|
|
|
251
251
|
metrics = _parse_metrics(spec)
|
|
252
|
-
|
|
252
|
+
min_metric_pitch = profile.get("strip_metric_pitch", 106)
|
|
253
253
|
cell_offset = 12 # gap between first divider and first metric cell
|
|
254
|
-
status_zone = 56 # 14px indicator + padding
|
|
255
254
|
|
|
256
255
|
# Glyph zone is ~36px (12 pad + 24 glyph + gap). Collapse when absent.
|
|
257
256
|
has_glyph = bool(glyph_data and glyph_data.get("path"))
|
|
@@ -267,6 +266,42 @@ def resolve_strip(
|
|
|
267
266
|
# Account for letter-spacing 0.18em on identity text
|
|
268
267
|
id_text_w += len(identity) * 11 * 0.18
|
|
269
268
|
first_divider_x = max(int(identity_x + id_text_w + 14), 80) # 14px right padding, min 80
|
|
269
|
+
|
|
270
|
+
# ── Adaptive cell pitch (preserves the slot grid) ──
|
|
271
|
+
# Uniform pitch adopts the widest metric's measured width so the slot
|
|
272
|
+
# grid stays consistent (every cell is the same size), but no metric's
|
|
273
|
+
# value overflows its cell. This is where adaptivity lives: `for metric
|
|
274
|
+
# in metrics` varies by payload, and each metric's value width drives
|
|
275
|
+
# the shared pitch upward when values are long. The status diamond
|
|
276
|
+
# lives in the dedicated right-edge ``status_zone``, not per-cell.
|
|
277
|
+
cell_pad = 20 # horizontal breathing room inside each cell
|
|
278
|
+
|
|
279
|
+
# Typography params for width measurement. For chrome paradigm these
|
|
280
|
+
# should match the CSS declared in strip/chrome-defs.j2 (Orbitron 17px
|
|
281
|
+
# 900 for value, JetBrains Mono 9px 600 for label). Other paradigms
|
|
282
|
+
# fall back to profile-declared sizes.
|
|
283
|
+
paradigm = (genome.get("paradigms") or {}).get("strip", "default")
|
|
284
|
+
if paradigm == "chrome":
|
|
285
|
+
value_size = 17
|
|
286
|
+
label_size = 9
|
|
287
|
+
else:
|
|
288
|
+
value_size = profile.get("strip_metric_value_size", 18)
|
|
289
|
+
label_size = profile.get("strip_metric_label_size", 7)
|
|
290
|
+
|
|
291
|
+
widest_cell = min_metric_pitch
|
|
292
|
+
for metric in metrics:
|
|
293
|
+
raw_value = str(metric.get("value", ""))
|
|
294
|
+
raw_label = str(metric.get("label", "")).upper()
|
|
295
|
+
value_w = measure_text(raw_value, font_size=value_size, bold=True, monospace=False)
|
|
296
|
+
label_w = measure_text(raw_label, font_size=label_size, bold=False, monospace=True)
|
|
297
|
+
cell_content_w = max(label_w, value_w)
|
|
298
|
+
needed = int(cell_content_w + cell_pad)
|
|
299
|
+
if needed > widest_cell:
|
|
300
|
+
widest_cell = needed
|
|
301
|
+
|
|
302
|
+
metric_pitch = widest_cell
|
|
303
|
+
status_zone = 56 # reserved for right-edge status diamond + padding
|
|
304
|
+
|
|
270
305
|
n = max(len(metrics), 1)
|
|
271
306
|
width = first_divider_x + cell_offset + n * metric_pitch + status_zone
|
|
272
307
|
|
|
@@ -1629,7 +1664,7 @@ def _resolve_motion(spec: ComposeSpec, genome: dict[str, Any]) -> str:
|
|
|
1629
1664
|
return MotionId.STATIC
|
|
1630
1665
|
|
|
1631
1666
|
|
|
1632
|
-
def _parse_metrics(spec: ComposeSpec) -> list[dict[str,
|
|
1667
|
+
def _parse_metrics(spec: ComposeSpec) -> list[dict[str, Any]]:
|
|
1633
1668
|
metrics: list[dict[str, str]] = []
|
|
1634
1669
|
|
|
1635
1670
|
# Try slots first
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""Chart frame resolver — star history / time-series visualization.
|
|
2
|
+
|
|
3
|
+
Reads pre-fetched connector data from ``spec.connector_data`` and delegates
|
|
4
|
+
the actual SVG math to :mod:`hyperweave.render.chart_engine`.
|
|
5
|
+
|
|
6
|
+
Three-state truthfulness contract:
|
|
7
|
+
- ``connector_data is None`` → ``data-hw-status="stale"``, "DATA UNAVAILABLE" overlay
|
|
8
|
+
- ``current_stars == 0`` (new repo) → ``data-hw-status="empty"``, "NEW REPO · NO STARS YET" overlay
|
|
9
|
+
- real points + current_stars > 0 → ``data-hw-status="fresh"``, live chart
|
|
10
|
+
|
|
11
|
+
The chart never fabricates data. There is no placeholder series — a zero-star
|
|
12
|
+
repo is a legitimate state, and upstream failure is rendered truthfully as
|
|
13
|
+
unavailable rather than masked with demo data.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import TYPE_CHECKING, Any
|
|
19
|
+
|
|
20
|
+
from hyperweave.render.chart_engine import Viewport, build_chart_svg
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from hyperweave.core.models import ComposeSpec
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Default milestones for star charts (shown when values cross these thresholds).
|
|
27
|
+
_DEFAULT_MILESTONES: list[int] = [500, 1000, 2000, 5000, 10000]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def resolve_chart(
|
|
31
|
+
spec: ComposeSpec,
|
|
32
|
+
genome: dict[str, Any],
|
|
33
|
+
profile: dict[str, Any],
|
|
34
|
+
**_kw: Any,
|
|
35
|
+
) -> dict[str, Any]:
|
|
36
|
+
"""Resolve the ``chart`` frame into width/height/template/context."""
|
|
37
|
+
width, height = 900, 500
|
|
38
|
+
|
|
39
|
+
# Viewport insets: leave room for hero title at top, axis labels on left,
|
|
40
|
+
# date labels at bottom, and hero value at right.
|
|
41
|
+
# Dimensions match the target SVGs in tier2/genomes/.
|
|
42
|
+
paradigm = genome.get("paradigms", {}).get("chart", "brutalist")
|
|
43
|
+
vp = (
|
|
44
|
+
Viewport(x=80, y=160, w=750, h=250)
|
|
45
|
+
if paradigm == "chrome"
|
|
46
|
+
else Viewport(x=80, y=150, w=760, h=245)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Three-state machine. "fresh" preserved (not renamed to "live") for
|
|
50
|
+
# backward compat with the existing data-hw-status contract; "empty" is
|
|
51
|
+
# new and specifically marks a truthful zero-star state.
|
|
52
|
+
connector = spec.connector_data
|
|
53
|
+
raw_points: list[Any]
|
|
54
|
+
empty_message: str | None
|
|
55
|
+
if connector is None:
|
|
56
|
+
# Upstream API failure — no data to trust.
|
|
57
|
+
status = "stale"
|
|
58
|
+
raw_points = []
|
|
59
|
+
current_stars = 0
|
|
60
|
+
empty_message = "DATA UNAVAILABLE"
|
|
61
|
+
else:
|
|
62
|
+
current_stars = int(
|
|
63
|
+
connector.get("current_stars") or connector.get("stars_total") or 0
|
|
64
|
+
)
|
|
65
|
+
raw_points = list(
|
|
66
|
+
connector.get("points") or connector.get("star_history") or []
|
|
67
|
+
)
|
|
68
|
+
if current_stars == 0:
|
|
69
|
+
# Truthful zero-star state (brand-new repo) — render empty, don't fabricate.
|
|
70
|
+
status = "empty"
|
|
71
|
+
raw_points = []
|
|
72
|
+
empty_message = "NEW REPO · NO STARS YET"
|
|
73
|
+
elif not raw_points:
|
|
74
|
+
# Has stars but no history — shouldn't happen after the connector
|
|
75
|
+
# fix, but degrade truthfully rather than synthesize.
|
|
76
|
+
status = "stale"
|
|
77
|
+
empty_message = "HISTORY UNAVAILABLE"
|
|
78
|
+
else:
|
|
79
|
+
status = "fresh"
|
|
80
|
+
empty_message = None
|
|
81
|
+
|
|
82
|
+
# Structural hints come from the resolver injection in compose/resolver.py,
|
|
83
|
+
# but we also read directly from the genome here because this file is
|
|
84
|
+
# imported before _resolve_paradigm has run (resolvers run INSIDE resolve()).
|
|
85
|
+
structural = genome.get("structural") or {}
|
|
86
|
+
|
|
87
|
+
chart_fragments = build_chart_svg(
|
|
88
|
+
raw_points,
|
|
89
|
+
vp,
|
|
90
|
+
structural,
|
|
91
|
+
milestones=_DEFAULT_MILESTONES,
|
|
92
|
+
empty_message=empty_message,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
repo = connector.get("repo") if connector else None
|
|
96
|
+
repo = repo or f"{spec.chart_owner}/{spec.chart_repo}".strip("/")
|
|
97
|
+
|
|
98
|
+
# Hero identity strings shown at top + right of the standalone chart.
|
|
99
|
+
title_upper = (repo or "star history").upper()
|
|
100
|
+
current_display = _format_compact(int(current_stars))
|
|
101
|
+
|
|
102
|
+
ctx: dict[str, Any] = {
|
|
103
|
+
"chart_repo": repo,
|
|
104
|
+
"chart_title": title_upper,
|
|
105
|
+
"chart_current_stars": current_display,
|
|
106
|
+
"chart_viewport_x": vp.x,
|
|
107
|
+
"chart_viewport_y": vp.y,
|
|
108
|
+
"chart_viewport_w": vp.w,
|
|
109
|
+
"chart_viewport_h": vp.h,
|
|
110
|
+
"chart_defs": chart_fragments["defs"],
|
|
111
|
+
"chart_axes": chart_fragments["axes"],
|
|
112
|
+
"chart_gridlines": chart_fragments["gridlines"],
|
|
113
|
+
"chart_area": chart_fragments["area"],
|
|
114
|
+
"chart_polyline": chart_fragments["polyline"],
|
|
115
|
+
"chart_markers": chart_fragments["markers"],
|
|
116
|
+
"chart_milestones": chart_fragments["milestones"],
|
|
117
|
+
"chart_y_labels": chart_fragments["y_labels"],
|
|
118
|
+
"chart_x_labels": chart_fragments["x_labels"],
|
|
119
|
+
"chart_empty_state": chart_fragments["empty_state"],
|
|
120
|
+
"data_hw_status": status,
|
|
121
|
+
}
|
|
122
|
+
# Surface non-fresh states via the document-level data-hw-status attribute.
|
|
123
|
+
# "fresh" stays implicit (live data is the default, no status marker needed).
|
|
124
|
+
if status != "fresh":
|
|
125
|
+
ctx["status"] = status
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
"width": width,
|
|
129
|
+
"height": height,
|
|
130
|
+
"template": "frames/chart.svg.j2",
|
|
131
|
+
"context": ctx,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _format_compact(n: int) -> str:
|
|
136
|
+
"""Render an integer as a compact string (2850 → '2,850', 12847 → '12.8K')."""
|
|
137
|
+
if n >= 10000:
|
|
138
|
+
return f"{n / 1000:.1f}K".rstrip("0").rstrip(".")
|
|
139
|
+
return f"{n:,}"
|
|
@@ -124,11 +124,18 @@ def resolve_stats(
|
|
|
124
124
|
paradigm = genome.get("paradigms", {}).get("stats", "brutalist")
|
|
125
125
|
if paradigm == "chrome":
|
|
126
126
|
embed_vp = Viewport(x=240, y=170, w=220, h=70)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
127
|
+
# Zero-guard: never default to a 1200-star synthetic curve. When
|
|
128
|
+
# stars_total is zero, the truthful state is an empty embedded chart.
|
|
129
|
+
stars_int = int(stars_total or 0)
|
|
130
|
+
real_points = connector.get("points") or connector.get("star_history")
|
|
131
|
+
if real_points:
|
|
132
|
+
chart_points: list[dict[str, Any]] = list(real_points)
|
|
133
|
+
elif stars_int > 0:
|
|
134
|
+
# Only synthesize when we know the total — this approximates a
|
|
135
|
+
# plausible growth curve rather than fabricating it from nothing.
|
|
136
|
+
chart_points = _synthetic_series_from_total(stars_int)
|
|
137
|
+
else:
|
|
138
|
+
chart_points = []
|
|
132
139
|
embed = build_chart_svg(
|
|
133
140
|
chart_points,
|
|
134
141
|
embed_vp,
|
|
@@ -10,6 +10,8 @@ from urllib.parse import urlparse
|
|
|
10
10
|
|
|
11
11
|
import httpx
|
|
12
12
|
|
|
13
|
+
from hyperweave import __version__
|
|
14
|
+
|
|
13
15
|
# SSRF Protection
|
|
14
16
|
|
|
15
17
|
ALLOWED_HOSTS: frozenset[str] = frozenset(
|
|
@@ -161,7 +163,7 @@ async def fetch(
|
|
|
161
163
|
)
|
|
162
164
|
|
|
163
165
|
merged_headers: dict[str, str] = {
|
|
164
|
-
"User-Agent": "HyperWeave/
|
|
166
|
+
"User-Agent": f"HyperWeave/{__version__} (https://hyperweave.app)",
|
|
165
167
|
"Accept": "application/json",
|
|
166
168
|
}
|
|
167
169
|
if headers:
|
|
@@ -185,6 +185,37 @@ async def fetch_stargazer_history(
|
|
|
185
185
|
|
|
186
186
|
# Step 2: compute total pages
|
|
187
187
|
total_pages = max(1, math.ceil(total_stars / _STARGAZER_PAGE_SIZE))
|
|
188
|
+
|
|
189
|
+
# Single-page case: repo has ≤ 30 stars. The "first starred_at of the page"
|
|
190
|
+
# sampling trick would otherwise collapse to a single aggregated point plus
|
|
191
|
+
# a duplicate-date "now" point, producing a zero time-range polyline. Use
|
|
192
|
+
# each stargazer's own timestamp instead — the whole page fits in one call.
|
|
193
|
+
if total_pages == 1:
|
|
194
|
+
single_page_url = (
|
|
195
|
+
f"https://api.github.com/repos/{identifier}/stargazers"
|
|
196
|
+
f"?per_page={_STARGAZER_PAGE_SIZE}&page=1"
|
|
197
|
+
)
|
|
198
|
+
page_payload = await fetch_json(
|
|
199
|
+
single_page_url,
|
|
200
|
+
provider=PROVIDER,
|
|
201
|
+
headers={"Accept": _STARGAZER_ACCEPT_HEADER},
|
|
202
|
+
)
|
|
203
|
+
single_page_points: list[dict[str, Any]] = []
|
|
204
|
+
if isinstance(page_payload, list):
|
|
205
|
+
for idx, entry in enumerate(page_payload):
|
|
206
|
+
if isinstance(entry, dict) and entry.get("starred_at"):
|
|
207
|
+
single_page_points.append(
|
|
208
|
+
{"date": entry["starred_at"], "count": idx + 1}
|
|
209
|
+
)
|
|
210
|
+
single_page_result: dict[str, Any] = {
|
|
211
|
+
"points": single_page_points,
|
|
212
|
+
"current_stars": total_stars,
|
|
213
|
+
"repo": identifier,
|
|
214
|
+
"ttl": STARGAZER_HISTORY_TTL,
|
|
215
|
+
}
|
|
216
|
+
cache.set(cache_key, single_page_result, STARGAZER_HISTORY_TTL)
|
|
217
|
+
return single_page_result
|
|
218
|
+
|
|
188
219
|
# Cap the sample count at the number of available pages.
|
|
189
220
|
sample_count = min(sample_pages, total_pages)
|
|
190
221
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""State inference from label + value strings.
|
|
2
|
+
|
|
3
|
+
Maps a (label, value) pair to one of the ArtifactStatus member values. Called
|
|
4
|
+
by compose() to auto-populate spec.state when the caller left it at the
|
|
5
|
+
default "active", so that a live badge whose fetched value is "failing"
|
|
6
|
+
renders as failing without every route having to remember to call this.
|
|
7
|
+
|
|
8
|
+
Inference is deliberately conservative: it returns "active" whenever no rule
|
|
9
|
+
fires, which compose() treats as "caller did not override, leave alone".
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def infer_state(label: str, value: str) -> str:
|
|
16
|
+
"""Infer a semantic state from a (label, value) pair.
|
|
17
|
+
|
|
18
|
+
Returns one of: passing, failing, warning, building, critical, active.
|
|
19
|
+
"active" means "could not infer" — callers should treat it as unchanged.
|
|
20
|
+
"""
|
|
21
|
+
label_lower = label.lower()
|
|
22
|
+
value_lower = value.lower()
|
|
23
|
+
|
|
24
|
+
if "pass" in value_lower or "success" in value_lower:
|
|
25
|
+
return "passing"
|
|
26
|
+
if "fail" in value_lower or "error" in value_lower:
|
|
27
|
+
return "failing"
|
|
28
|
+
if "warn" in value_lower:
|
|
29
|
+
return "warning"
|
|
30
|
+
if "build" in label_lower and "run" in value_lower:
|
|
31
|
+
return "building"
|
|
32
|
+
|
|
33
|
+
# Percentage-based threshold (e.g. "95%", "72.5%")
|
|
34
|
+
if value.rstrip("%").replace(".", "").isdigit():
|
|
35
|
+
try:
|
|
36
|
+
num = float(value.rstrip("%"))
|
|
37
|
+
if num >= 90:
|
|
38
|
+
return "passing"
|
|
39
|
+
if num >= 70:
|
|
40
|
+
return "warning"
|
|
41
|
+
return "critical"
|
|
42
|
+
except ValueError:
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
return "active"
|
|
@@ -16,7 +16,7 @@ badge_value_size: 11
|
|
|
16
16
|
badge_value_weight: 700
|
|
17
17
|
strip_corner: 0
|
|
18
18
|
badge_corner: 0
|
|
19
|
-
strip_accent_width:
|
|
19
|
+
strip_accent_width: 4
|
|
20
20
|
strip_metric_pitch: 107
|
|
21
21
|
strip_divider_mode: minimal
|
|
22
22
|
badge_frame_height: 22
|
|
@@ -51,7 +51,7 @@ strip_metric_label_y: 17
|
|
|
51
51
|
strip_metric_value_weight: 800
|
|
52
52
|
strip_metric_value_fill: "var(--dna-ink-primary)"
|
|
53
53
|
strip_metric_value_y: 38
|
|
54
|
-
strip_metric_value_skew:
|
|
54
|
+
strip_metric_value_skew: 0
|
|
55
55
|
strip_identity_font: "var(--dna-font-mono, 'Courier New', monospace)"
|
|
56
56
|
strip_metric_label_font: "var(--dna-font-mono, 'Courier New', monospace)"
|
|
57
57
|
|
|
@@ -31,14 +31,14 @@ def _readme_kit(
|
|
|
31
31
|
# Parse badges: "build:passing,version:v0.6.3,coverage:92%"
|
|
32
32
|
badge_pairs = _parse_badge_string(badges)
|
|
33
33
|
|
|
34
|
-
# Generate badges
|
|
34
|
+
# Generate badges -- compose() infers state from value at the chokepoint,
|
|
35
|
+
# so we leave it at the default and let that logic fire.
|
|
35
36
|
for label, value in badge_pairs:
|
|
36
37
|
spec = ComposeSpec(
|
|
37
38
|
type="badge",
|
|
38
39
|
genome_id=genome,
|
|
39
40
|
title=label,
|
|
40
41
|
value=value,
|
|
41
|
-
state=_infer_state(label, value),
|
|
42
42
|
)
|
|
43
43
|
results[f"badge-{label.lower()}"] = compose(spec)
|
|
44
44
|
|
|
@@ -97,31 +97,3 @@ def _parse_badge_string(badges: str) -> list[tuple[str, str]]:
|
|
|
97
97
|
k, v = pair.split(":", 1)
|
|
98
98
|
pairs.append((k.strip(), v.strip()))
|
|
99
99
|
return pairs
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def _infer_state(label: str, value: str) -> str:
|
|
103
|
-
label_lower = label.lower()
|
|
104
|
-
value_lower = value.lower()
|
|
105
|
-
|
|
106
|
-
if "pass" in value_lower or "success" in value_lower:
|
|
107
|
-
return "passing"
|
|
108
|
-
if "fail" in value_lower or "error" in value_lower:
|
|
109
|
-
return "failing"
|
|
110
|
-
if "warn" in value_lower:
|
|
111
|
-
return "warning"
|
|
112
|
-
if "build" in label_lower and "run" in value_lower:
|
|
113
|
-
return "building"
|
|
114
|
-
|
|
115
|
-
# Percentage-based threshold
|
|
116
|
-
if value.rstrip("%").replace(".", "").isdigit():
|
|
117
|
-
try:
|
|
118
|
-
num = float(value.rstrip("%"))
|
|
119
|
-
if num >= 90:
|
|
120
|
-
return "passing"
|
|
121
|
-
if num >= 70:
|
|
122
|
-
return "warning"
|
|
123
|
-
return "critical"
|
|
124
|
-
except ValueError:
|
|
125
|
-
pass
|
|
126
|
-
|
|
127
|
-
return "active"
|