Skip to main content

Technical-drawing SVG dashboards for GitHub repositories and profiles.

Project description

Cartouche

๐Ÿ‡ฌ๐Ÿ‡ง English (you are here) ยท ๐Ÿ‡ซ๐Ÿ‡ท Franรงais

CI Last commit License: MIT Python PyPI Visitors

Technical-drawing SVG dashboards for GitHub repositories and profiles. Pure SVG primitives, sixteen themes (incl. watermarked variants), two languages, embeddable in any README via <picture>.

PyPI name: cartouche-svg (the bare cartouche slot belongs to an unrelated, abandoned Sphinx extension from 2013). Always pip install cartouche-svg.

Cartouche dashboard for the cartouche repo โ€” refreshed every 6 hours by GitHub Actions

[!TIP] The dashboard at the top of this README pulls real data and refreshes every 6 hours. A โ˜… on the repo means you'll show up as a tick on its own star-history curve before tonight. It's the most self-referential growth hack we could come up with. Yes, we already starred it ourselves โ€” the experiment lacks a control group.

drafting-light blueprint-light vellum-davinci-light botanical-floral-light blossom-kawai-light

Light theme sampler โ€” click any thumbnail for its section in THEMES.md, where every variant lives at full size in light + dark.

Cartouche takes a GitHub repo (or a whole profile) and renders it as a technical-drawing SVG: grid, double-line frame, annotated star history, health radar, key metrics, and a cartouche โ€” the architectural title block โ€” in the bottom-right corner. Sixteen themes (light + dark, plus watermarked variants for Vellum + Davinci, Botanical + Floral, Blossom + Kawai), two built-in languages (English + French) with extensible custom packs via JSON, all served through the <picture> tag for both color modes.

Contents

Why

Existing solutions (star-history.com, GitHub Charts, etc.) serve images through GitHub's Camo proxy, which means aggressive caching, occasional refresh failures, and zero control over the rendering. Cartouche makes the opposite tradeoff: generate the SVG locally via a GitHub Action, commit it to your repo, and serve it as a versioned file โ€” so it refreshes on the cron cadence you set, is readable by anyone, and is fully styleable to taste.

What gets displayed

Repo dashboard

  • FIG. 01 โ€” Star history with peak annotations and endpoint marker
  • FIG. 02 โ€” 6-axis radar: stars, forks, commits, code, tests, docs
  • FIG. 03 โ€” Cards: stargazers, forks, issues, commits/30d + language breakdown bar

Profile dashboard

  • FIG. 01 โ€” Cumulative stars across all the account's public repos
  • FIG. 02 โ€” Top 5 repos by stars, with language and commits/30d (long names are truncated with โ€ฆ to keep them off the bars)
  • FIG. 03 โ€” 6-axis profile radar: reach, activity, breadth, depth, polyglot, engagement
  • FIG. 04 โ€” 53-week trailing contribution heatmap (via GraphQL โ€” requires a token)
  • FIG. 05 โ€” Indicators: total stars, total forks, commits/12 months, account age

Both dashboards carry a small Proudly Clauded by @<handle> watermark just below the frame, in the bottom-right corner. The handle is pulled from the data, so anyone running the lib gets their own credit line automatically. To remove or rephrase it, override the proudly_clauded template via --lang-file.

Installation

pip install cartouche-svg

Zero runtime dependencies โ€” Cartouche uses only the standard library (urllib for API calls, json, datetime, math, importlib.resources).

CLI usage

# Repo dashboard (English by default)
cartouche repo Sandjab/Athanor --theme blueprint-light --out dashboard.svg

# Same dashboard in French
cartouche repo Sandjab/Athanor --theme blueprint-light --lang fr --out dashboard.svg

# Profile dashboard
cartouche profile Sandjab --theme drafting-dark --out profile.svg

# List available themes and languages
cartouche themes
cartouche langs

# Test layouts without hitting the API (canned data)
cartouche repo Sandjab/Athanor --mock --theme vellum-light

# Override individual labels with your own JSON pack
cartouche repo Sandjab/Athanor --lang fr --lang-file ./my-overrides.json

# Replace auto-detected callouts on the star line with your own events
cartouche repo Sandjab/Athanor --annotations-file ./events.json

Token resolution: --token > $GITHUB_TOKEN > $GH_TOKEN > anonymous (60 req/h, rarely enough for a profile).

Internationalization

English by default. Switch to French with --lang fr.

Built-in packs

cartouche langs   # โ†’ en, fr

Adding a language

Drop a <code>.json file into src/cartouche/lang/, mirroring the schema of en.json (keys: labels, templates, months_short, months_long). The test_lang_has_all_required_keys test will tell you which keys are mandatory. Once dropped and the wheel rebuilt, --lang <code> works out of the box.

Overriding without recompiling

To tweak a few strings ad-hoc without publishing a new pack, write a JSON overlay and pass it via --lang-file:

{
  "labels": {
    "fig_radar_health": "FIG. 02 โ€” VITAL SIGNS",
    "drawn_by": "BY"
  },
  "templates": {
    "n_years": "{n} years"
  }
}
cartouche repo Sandjab/Athanor --lang en --lang-file my-overrides.json --out dashboard.svg

The overlay is deep-merged on top of the base pack: only the keys you specify are replaced, everything else stays intact.

Language pack schema

{
  "code": "xx",
  "name": "My language",
  "labels": {
    "drawn_by": "...",
    "fig_radar_health": "...",
    "card_stargazers": "...",
    ...
  },
  "templates": {
    "fig_star_history": "FIG. 01 โ€” STAR HISTORY ยท {start} โ†’ {end}",
    "first_star_top": "// FIRST STAR โ€” {date}",
    ...
  },
  "months_short": ["JAN", "FEB", ...],
  "months_long":  ["Jan", "Feb", ...]
}

See src/cartouche/lang/en.json for the full key list.

Themes

Sixteen themes in eight families, each with a light and a dark counterpart. The first five families are clean palettes; the last three are watermarked variants that ghost a bundled PNG (Da Vinci plate, floral motif, kawaii character) behind the data layer at low opacity. See THEMES.md for side-by-side previews of every variant.

Family Light Dark
Drafting drafting-light drafting-dark
Blueprint blueprint-light blueprint-dark
Vellum vellum-light vellum-dark
Botanical botanical-light botanical-dark
Blossom blossom-light blossom-dark
Vellum + Davinci vellum-davinci-light vellum-davinci-dark
Botanical + Floral botanical-floral-light botanical-floral-dark
Blossom + Kawai blossom-kawai-light blossom-kawai-dark
  • Drafting โ€” pure grayscale. White paper, black ink โ€” no hue at all, series and accent separated by lightness only.
  • Blueprint โ€” cyanotype lineage. Pale faded blue or a deep nighttime Prussian blue dive.
  • Vellum โ€” cream parchment / sepia. Dark variant: aged leather and gold. For those who want a Beaux-Arts feel rather than engineering.
  • Botanical โ€” 19th-century herbarium plate. Sage-and-fern ink on ivory, deep forest at night with candle-pollen accents.
  • Blossom โ€” sakura kawaii. Powder-rose on pearl-grey ivory, deep aubergine boudoir at night, neon-soft pink data with mint accents.
  • Vellum + Davinci โ€” Vellum palette + Da Vinci plate watermark. Reads as the surface of an old codex with the master draftsman's notebook bleeding through.
  • Botanical + Floral โ€” Botanical palette + floral motif watermark. The herbarium feel pushed further into a wallpapered field of flowers.
  • Blossom + Kawai โ€” Blossom palette + kawaii character watermark. Sakura stationery, soft mascot ghosted under the cartouche grid.

Custom annotations

By default the repo dashboard auto-detects two callouts on the star history: the first star and the largest single-step spike. Pass --annotations-file path/to/events.json to replace them with your own list of events:

[
  {"date": "2025-12-15", "label_top": "// HACKER NEWS", "label_bottom": "// front page"},
  {"date": "2026-04-01", "label_top": "// SHIPPED v1", "label_bottom": "// public release"},
  {"date": "2026-04-15", "count": 100, "label_top": "// MILESTONE", "label_bottom": "// 100 stars"}
]
  • date (required, ISO YYYY-MM-DD) is where the callout anchors on the star-history axis.
  • label_top / label_bottom (required) are the two text lines drawn next to the leader line. Convention is // PREFIX for the top line and a more descriptive bottom line, but anything goes.
  • count (optional) is the cumulative star count to anchor on the curve. If omitted, Cartouche derives it from the closest star_history entry on or before that date.

Custom annotations replace the auto-detected pair โ€” copy the auto- detected ones into your file if you want to keep them. Only available on the repo dashboard (the profile star line does not have annotations).

Embedding in a README

Serve the right variant based on the visitor's prefers-color-scheme:

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="assets/dashboard-dark.svg">
  <img src="assets/dashboard-light.svg" alt="Cartouche dashboard">
</picture>

Important: use a relative path (assets/...), not an absolute URL. GitHub rewrites external images through its Camo proxy, which breaks the <picture> light/dark mechanism; relative paths are served as-is.

Auto-update via GitHub Actions

Two ready-to-use workflows live in examples/workflows/:

  • repo-dashboard.yml โ€” drop into .github/workflows/ of the repo whose dashboard you want. Regenerates and commits every 6 hours. Runs on the built-in secrets.GITHUB_TOKEN with no configuration, but renders FIG. 01 (star history) empty unless you also provide a PAT โ€” see Known limitations.
  • profile-dashboard.yml โ€” drop into your profile repo (<handle>/<handle>). Twice a day.

Both templates read the token from secrets.CARTOUCHE_PAT and fall back to secrets.GITHUB_TOKEN. The profile dashboard requires the PAT; the repo dashboard works without it, minus the star curve. Create a classic PAT with public_repo + read:user (or repo to include private repos) and store it as a repository secret named CARTOUCHE_PAT.

To serve a French dashboard, add --lang fr to the cartouche commands in the workflow.

Python API

from cartouche import lang
from cartouche.render import repo
from cartouche.themes import get_theme

# Load a language pack (with optional overlay)
fr = lang.load("fr", overlay_path="my-overrides.json")  # overlay optional

# Load data (mock or via fetch.repo_data())
from cartouche.mock import mock_repo
data = mock_repo("Sandjab", "Athanor", lang=fr)

# Render the SVG
svg = repo.render(data, theme=get_theme("vellum-light"), lang=fr)

Architecture

src/cartouche/
โ”œโ”€โ”€ themes.py            # 16-theme registry (dict-of-dicts)
โ”œโ”€โ”€ lang/
โ”‚   โ”œโ”€โ”€ __init__.py      # load(), list_builtin(), t(), tmpl()
โ”‚   โ”œโ”€โ”€ en.json          # default language
โ”‚   โ””โ”€โ”€ fr.json          # French
โ”œโ”€โ”€ fetch.py             # GitHub REST + GraphQL wrappers, stdlib only
โ”œโ”€โ”€ mock.py              # canned fixtures for development without API
โ”œโ”€โ”€ cli.py               # argparse entry point
โ””โ”€โ”€ render/
    โ”œโ”€โ”€ primitives.py    # frame, grid, cartouche, axes, radar, text
    โ”œโ”€โ”€ repo.py          # repo dashboard composer
    โ””โ”€โ”€ profile.py       # profile dashboard composer

The render engine is token-agnostic (colors come from themes) and literal-free (strings come from lang). Adding a seventh theme = ~12 lines in THEMES. Adding a language = drop a JSON in lang/. See CLAUDE.md for the architectural invariants in detail.

Known limitations

  • The profile dashboard hits /repos/.../stargazers for each public repo; the first run on a viral account can still take a minute, but subsequent runs are near-instant thanks to a 24h disk cache ($XDG_CACHE_HOME/cartouche/). Pass --no-cache to skip it, --cache-ttl 0 to force a one-shot refresh, or --cache-dir PATH to relocate. True incremental refresh (only fetch stars added since last run) isn't implemented yet โ€” when the cache is stale, the timeline is refetched in full.

  • Star history needs a personal access token โ€” for repo dashboards too. On 2026-06-30 GitHub announced that the stargazer list endpoint (/repos/{owner}/{repo}/stargazers, the only source of star timestamps) would be restricted to a repo's admins and collaborators; the restriction reached Actions on 2026-07-13. A GitHub App installation token โ€” which is what the default GITHUB_TOKEN is โ€” is neither an admin nor a collaborator, so it now gets a 403 there even on its own repo. A user PAT belonging to an admin still reads it fine.

    Without a PAT, both dashboards render with an empty star chart and emit a RuntimeWarning; every other figure, including the star count (which comes from a different endpoint), is unaffected. Cartouche never fails the build over this.

  • Forks are excluded from profile aggregates (filtered out). The dashboard for an individual fork still works normally.
  • Web fonts are not embedded โ€” GitHub strips them when rendering SVGs in READMEs. The fallback is a system monospace stack.
  • The two auto-detected annotations (first โ˜…, biggest spike) replace each other if you pass --annotations-file; there's no way to augment the auto-detected pair without listing them manually in your overlay.

Development

git clone https://github.com/Sandjab/cartouche
cd cartouche
pip install -e ".[dev]"
pytest                                                 # 128 tests, ~0.3s
python -m cartouche repo Sandjab/Athanor --mock        # smoke test without API
python -m cartouche profile Sandjab --mock --lang fr   # FR variant

For Claude Code CLI: see CLAUDE.md for architecture, invariants, and common tasks.

License

MIT โ€” see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cartouche_svg-0.3.3.tar.gz (1.8 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cartouche_svg-0.3.3-py3-none-any.whl (1.8 MB view details)

Uploaded Python 3

File details

Details for the file cartouche_svg-0.3.3.tar.gz.

File metadata

  • Download URL: cartouche_svg-0.3.3.tar.gz
  • Upload date:
  • Size: 1.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for cartouche_svg-0.3.3.tar.gz
Algorithm Hash digest
SHA256 1c49f6af6465f83cea68bdd0ffe83a7462a54900eb66af2301f46b3c25be4d9f
MD5 235afc70699999b4b9b7cc0a93f1fc14
BLAKE2b-256 dcd522a656a497eeb6dcddbfd057abc84d45d8b94012b860f67a5aabf0c1f140

See more details on using hashes here.

Provenance

The following attestation bundles were made for cartouche_svg-0.3.3.tar.gz:

Publisher: release.yml on Sandjab/cartouche

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cartouche_svg-0.3.3-py3-none-any.whl.

File metadata

  • Download URL: cartouche_svg-0.3.3-py3-none-any.whl
  • Upload date:
  • Size: 1.8 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for cartouche_svg-0.3.3-py3-none-any.whl
Algorithm Hash digest
SHA256 5e0dd2fb08354b83385a440755193c439f681d9dfd7e9ca806df9104396eea4c
MD5 0f83d00e11cfc65543a91c3d8c069196
BLAKE2b-256 65ff485e84836aa4815fc04a217262999351a4d06d69a9acbcfc5569f4383966

See more details on using hashes here.

Provenance

The following attestation bundles were made for cartouche_svg-0.3.3-py3-none-any.whl:

Publisher: release.yml on Sandjab/cartouche

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page