Technical-drawing SVG dashboards for GitHub repositories and profiles.
Project description
Cartouche
๐ฌ๐ง English (you are here) ยท ๐ซ๐ท Franรงais
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 barecartoucheslot belongs to an unrelated, abandoned Sphinx extension from 2013). Alwayspip install cartouche-svg.
[!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.
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
- What gets displayed
- Installation
- CLI usage
- Internationalization
- Themes
- Custom annotations
- Embedding in a README
- Auto-update via GitHub Actions
- Python API
- Architecture
- Known limitations
- Development
- License
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, ISOYYYY-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// PREFIXfor 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 closeststar_historyentry 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.profile-dashboard.ymlโ drop into your profile repo (<handle>/<handle>). Twice a day.
Both use secrets.GITHUB_TOKEN (already available in any Action) and
work with no further configuration. 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/.../stargazersfor 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-cacheto skip it,--cache-ttl 0to force a one-shot refresh, or--cache-dir PATHto 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. - 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cartouche_svg-0.3.0.tar.gz.
File metadata
- Download URL: cartouche_svg-0.3.0.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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c45c87897f1ac2649cfb8d951ecc1bbc71b4d150dc2cbdd893717291e917975
|
|
| MD5 |
56e35c6acec00b95bbd0648be9635afe
|
|
| BLAKE2b-256 |
4523fb2d20c479463da18bf0616dd1e2ea54fdd2aa0591393fd529de4f5b68f9
|
Provenance
The following attestation bundles were made for cartouche_svg-0.3.0.tar.gz:
Publisher:
release.yml on Sandjab/cartouche
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cartouche_svg-0.3.0.tar.gz -
Subject digest:
1c45c87897f1ac2649cfb8d951ecc1bbc71b4d150dc2cbdd893717291e917975 - Sigstore transparency entry: 1673044706
- Sigstore integration time:
-
Permalink:
Sandjab/cartouche@2aa66bcec438092a09d9d512c03a998310579922 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/Sandjab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2aa66bcec438092a09d9d512c03a998310579922 -
Trigger Event:
push
-
Statement type:
File details
Details for the file cartouche_svg-0.3.0-py3-none-any.whl.
File metadata
- Download URL: cartouche_svg-0.3.0-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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4a0fcc2ac83197ad7efb5f2febfb17ff4c5f5932c1a33a421e18439e842f1e39
|
|
| MD5 |
b62bcfc84e321c980dd111fa9c0ee011
|
|
| BLAKE2b-256 |
9d19bcc37573eda77dc56e99a369fbbbb7050ff6ed2a7e48cc3d0f760f98d56c
|
Provenance
The following attestation bundles were made for cartouche_svg-0.3.0-py3-none-any.whl:
Publisher:
release.yml on Sandjab/cartouche
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cartouche_svg-0.3.0-py3-none-any.whl -
Subject digest:
4a0fcc2ac83197ad7efb5f2febfb17ff4c5f5932c1a33a421e18439e842f1e39 - Sigstore transparency entry: 1673044754
- Sigstore integration time:
-
Permalink:
Sandjab/cartouche@2aa66bcec438092a09d9d512c03a998310579922 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/Sandjab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2aa66bcec438092a09d9d512c03a998310579922 -
Trigger Event:
push
-
Statement type: