Skip to content

chore(palette): migrate matplotlib impls from Okabe-Ito to imprint#7776

Merged
MarkusNeusinger merged 1 commit into
mainfrom
chore/migrate-imprint-matplotlib
May 27, 2026
Merged

chore(palette): migrate matplotlib impls from Okabe-Ito to imprint#7776
MarkusNeusinger merged 1 commit into
mainfrom
chore/migrate-imprint-matplotlib

Conversation

@MarkusNeusinger
Copy link
Copy Markdown
Owner

Summary

  • Positional hex replacement across 137 matplotlib impls that already ship light+dark renders, moving them from the legacy Okabe-Ito palette (and the short-lived variant-D palette) to the canonical imprint palette defined in core/palette.py.
  • Code-only change. Metadata YAMLs are intentionally untouched — they refresh on the next regular daily-regen cycle. Rendered images are not in this PR; the homepage will pick up the new colors immediately after merge once Stage B (local render + GCS upload) finishes.
  • Drive-by fix to pyproject.toml — removes a duplicate per-file-ignores entry that was breaking ruff parsing on these files.
  • Adds the two scripts that drive this migration so the remaining ~1.2k impls (other libraries) can run through the same pipeline.

Scope filter

Per metadata YAML, only impls with both preview_url_light and preview_url_dark set are migrated. Single-theme impls are left to the regular daily-regen rotation (they're the oldest specs anyway). Specs without an existing matplotlib impl are not given a new one — that's still the job of the regular spec workflow.

Stage-A dry-run distribution:

Library Migrated Already imprint Skip (single-theme)
matplotlib 137 87 98

Color mapping (positional, deterministic)

Both pre-imprint palettes share #009E73 (brand green) with imprint, so series-index-0 is unchanged. The remaining slots collapse:

Slot Okabe-Ito variant-D → imprint
0 #009E73 #009E73 #009E73 (green, unchanged)
1 #D55E00 #9418DB #C475FD (lavender)
2 #0072B2 #B71D27 #4467A3 / #AE3030
3 #CC79A7 #16B8F3 #BD8233 / #4467A3
4 #E69F00 #99B314 #AE3030 / #99B314
5 #56B4E9 #D359A7 #2ABCCD / #954477
6 #F0E442 #BA843E #954477 / #BD8233

OKABE_ITO / ANYPLOT_PALETTE constants are renamed to IMPRINT in the same file when present.

Why this PR (and not the daily-regen pipeline)

The daily-regen runs 10× per day and rotates over 327 specs — at the current cadence it would take weeks to unify all impls on imprint. Each regen also burns tokens on spec-polish + impl-generate + AI-review × 11 libraries, even though here we only swap hex codes. This is a one-shot fix that bypasses all of that.

Test plan

  • Stage A dry-run + idempotency check on voronoi-basic / matplotlib
  • Stage B (render light+dark) smoke test produced both PNGs with imprint colors
  • Local ruff check . + ruff format --check . green (matches CI)
  • CI green (lint + tests + frontend-tests)
  • Copilot review triage
  • After merge: Stage B for all 137 matplotlib impls → GCS production overwrite
  • Sample 3–5 specs on anyplot.ai post-merge to confirm imprint shows

🤖 Generated with Claude Code

Positional hex replacement across 137 matplotlib impls that already
ship light+dark renders. Code-only — metadata YAMLs are untouched and
will refresh on the next regular daily-regen cycle. Images are not
regenerated in this PR; they will be re-rendered locally and pushed
to GCS production immediately after merge via the companion script.

Scope filter (per metadata YAML):
- include: preview_url_light AND preview_url_dark both populated
- skip: single-theme impls (handled by daily-regen rotation)
- skip: specs without an existing matplotlib impl (no new ones created)

Mapping is positional slot-to-slot, deterministic, idempotent:
- Okabe-Ito #D55E00 #0072B2 #CC79A7 #E69F00 #56B4E9 #F0E442
- variant-D  #9418DB #B71D27 #16B8F3 #99B314 #D359A7 #BA843E
both collapse into the imprint 8-hue pool defined in core/palette.py.
OKABE_ITO / ANYPLOT_PALETTE constants are renamed to IMPRINT in the
same file when present.

Drive-by: removes a duplicate per-file-ignores entry in pyproject.toml
that was breaking ruff parsing on these files.

New scripts (used to drive this and the remaining ~1.2k impls):
- scripts/migrate_to_imprint.py        deterministic hex replace
- scripts/migrate_render_and_upload.py local render + GCS upload

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 27, 2026 19:58
@MarkusNeusinger MarkusNeusinger merged commit 6c0c420 into main May 27, 2026
8 checks passed
@MarkusNeusinger MarkusNeusinger deleted the chore/migrate-imprint-matplotlib branch May 27, 2026 20:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR accelerates the palette unification effort by mechanically migrating existing dual-theme matplotlib implementations from legacy Okabe-Ito / variant-D hex literals to the canonical imprint palette (as defined in core/palette.py), without going through full daily regen. It also adds two standalone scripts to drive the migration and fixes a Ruff config parsing issue.

Changes:

  • Add Stage A/Stage B migration scripts to (A) rewrite palette hex literals in-place and (B) re-render + upload light/dark assets to GCS.
  • Replace legacy palette hex codes/constants with imprint equivalents across the touched matplotlib implementations.
  • Remove a duplicate Ruff per-file-ignores entry in pyproject.toml.

Reviewed changes

Copilot reviewed 140 out of 140 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
scripts/migrate_to_imprint.py Stage A script to rewrite legacy palette hex literals/constants to imprint using a deterministic mapping.
scripts/migrate_render_and_upload.py Stage B script to re-render light/dark previews and upload/promote artifacts in GCS.
pyproject.toml Removes duplicate Ruff per-file-ignores entry (fixes config parsing).
plots/wordcloud-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/windrose-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/waterfall-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/waffle-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/voronoi-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/volcano-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/violin-swarm/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/violin-split/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/violin-grouped-swarm/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/venn-labeled-items/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/venn-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/upset-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/treemap-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/timeseries-forecast-uncertainty/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/timeseries-decomposition/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/timeline-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/swarm-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/sunburst-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/subplot-mosaic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/strip-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/streamgraph-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/spectrum-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/span-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/sn-curve-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/smith-chart-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/slope-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/skewt-logp-atmospheric/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/silhouette-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/shap-waterfall/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/scatter-text/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/scatter-regression-polynomial/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/scatter-regression-lowess/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/scatter-regression-linear/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/scatter-matrix/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/scatter-matrix-interactive/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/scatter-map-geographic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/scatter-embedding/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/scatter-categorical/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/scatter-animated-controls/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/roc-curve/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/renko-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/radar-multi/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/radar-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/qq-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/polar-scatter/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/polar-line/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/polar-bar/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/point-and-figure-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/phase-diagram/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/pdp-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/parliament-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/parallel-categories-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/ohlc-bar/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/network-weighted/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/network-transport-static/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/network-hierarchical/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/network-force-directed/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/network-directed/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/network-bipartite/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/network-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/mosaic-categorical/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/maze-circular/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/marimekko-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/map-route-path/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/map-projections/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/map-marker-clustered/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/map-drilldown-geographic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/manhattan-gwas/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/logistic-regression/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/linked-views-selection/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/line-timeseries-rolling/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/line-styled/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/line-stock-comparison/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/line-stepwise/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/line-multi/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/line-loss-training/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/line-confidence/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/line-annotated-events/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/lift-curve/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/learning-curve-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/kagi-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/indicator-sma/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/indicator-rsi/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/indicator-macd/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/indicator-ema/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/indicator-bollinger/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/ice-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/horizon-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/hive-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/histogram-stepwise/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/histogram-stacked/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/histogram-returns-distribution/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/histogram-overlapping/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/histogram-kde/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/histogram-density/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/histogram-cumulative/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/hierarchy-toggle-view/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/heatmap-geographic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/heatmap-adjacency/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/gauge-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint (note: semantic zone colors need adjustment).
plots/gantt-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/gain-curve/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/funnel-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/frequency-polygon-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/forest-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/facet-grid/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/dumbbell-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/drawdown-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/dot-matrix-proportional/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/donut-nested/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/donut-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/dendrogram-radial/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/datamatrix-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/dashboard-synchronized-crosshair/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/dashboard-metrics-tiles/implementations/python/matplotlib.py Replace legacy palette usage with imprint (note: semantic severity colors need adjustment).
plots/contour-decision-boundary/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/circos-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/circlepacking-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/cat-box-strip/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/candlestick-volume/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/calibration-curve/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/box-notched/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/box-grouped/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/bland-altman-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/bar-stacked/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/bar-stacked-percent/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/bar-stacked-labeled/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/bar-spine/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/bar-race-animated/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/bar-grouped/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/bar-diverging/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/area-stacked/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/area-stacked-percent/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/area-stacked-confidence/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/area-cumulative-flow/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/andrews-curves/implementations/python/matplotlib.py Replace legacy palette usage with imprint.
plots/alluvial-basic/implementations/python/matplotlib.py Replace legacy palette usage with imprint.

Comment on lines +115 to +121
def _rename_palette_constant(text: str) -> str:
"""Rename common old palette variable names to IMPRINT (Python, R, Julia).

Only renames identifiers that look like a standalone palette constant
(whole-word match, all-caps name). Conservative — does not touch
inline comments or string literals.
"""
Comment on lines +10 to +29
"""Stage B of the imprint migration: re-render light+dark images locally and
overwrite the GCS production bucket.

For every (spec, language, library) listed in ``.migration-list.json`` (or
discovered from a freshly-applied Stage A), this script:

1. Renders ``plot-light.png`` and ``plot-dark.png`` in ``.regen-preview/{library}/``
by running the impl with ``ANYPLOT_THEME`` set to each theme.
2. Optimizes each PNG in place and emits responsive variants (400/800/1200 px,
PNG + WebP) via ``core.images``.
3. Uploads the bundle to ``gs://anyplot-images/staging/{spec}/{language}/{library}/``.
4. Promotes staging → ``gs://anyplot-images/plots/{spec}/{language}/{library}/``
with public-read ACL, then deletes staging.

Resumable via ``.migration-progress.json`` — Ctrl-C and re-run to continue from
where you stopped. Skips items already marked ``done``.

Self-contained: does NOT depend on the regen module, so it works for R and
Julia impls too (the regen module is Python-only).
"""
Comment on lines +154 to +156
gcloud_ok = _check_command(["gcloud", "auth", "list", "--format=value(account)"])
if not gcloud_ok:
notes.append("gcloud not authenticated → gcloud auth login")
Comment on lines 21 to +24
# Okabe-Ito palette for waterfall categories
INCREASE_COLOR = "#009E73" # Brand green (position 1)
DECREASE_COLOR = "#D55E00" # Vermillion (position 2)
TOTAL_COLOR = "#0072B2" # Blue (position 3)
DECREASE_COLOR = "#C475FD" # Vermillion (position 2)
TOTAL_COLOR = "#4467A3" # Blue (position 3)
Comment on lines 18 to 21
# Okabe-Ito base colors for level-1 departments (positions 1–3)
OKABE_ITO = ["#009E73", "#D55E00", "#0072B2"]
OKABE_RGB = [tuple(int(c[j : j + 2], 16) / 255 for j in (1, 3, 5)) for c in OKABE_ITO]
IMPRINT = ["#009E73", "#C475FD", "#4467A3"]
OKABE_RGB = [tuple(int(c[j : j + 2], 16) / 255 for j in (1, 3, 5)) for c in IMPRINT]

ax.set_facecolor(PAGE_BG)

BRIDGE_COLOR = "#E69F00" # Okabe-Ito amber — distinct cross-department highlight
BRIDGE_COLOR = "#AE3030" # Okabe-Ito amber — distinct cross-department highlight
Comment on lines 20 to 22
# Status colors — semantic alarm indicators using Okabe-Ito positions 1, 5, 2
STATUS_COLORS = {"good": "#009E73", "warning": "#E69F00", "critical": "#D55E00"}
STATUS_COLORS = {"good": "#009E73", "warning": "#AE3030", "critical": "#C475FD"}
STATUS_LABELS = {"good": "GOOD", "warning": "WARNING", "critical": "CRITICAL"}
Comment on lines 22 to 25
# Okabe-Ito zone colors (colorblind-safe red/yellow/green)
ZONE_BAD = "#D55E00" # vermillion
ZONE_WARN = "#E69F00" # orange
ZONE_BAD = "#C475FD" # vermillion
ZONE_WARN = "#AE3030" # orange
ZONE_GOOD = "#009E73" # bluish green (brand)
@codecov
Copy link
Copy Markdown

codecov Bot commented May 27, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

MarkusNeusinger added a commit that referenced this pull request May 27, 2026
## Summary

Follow-up to #7776. The positional Okabe-Ito → imprint swap broke charts
where the old hex carried **semantic** meaning (red = bad, green = good,
amber = warning), not just categorical separation. Slot 1 became
lavender, which silently turned "bearish" / "negative" / "critical" /
"decrease" markers into purple.

Reaches for imprint **semantic anchors** (red `#AE3030`, amber
`#DDCC77`, green `#009E73`) on the 12 matplotlib impls where the
variable name or comment made the semantic intent explicit. Categorical,
non-semantic uses of `#C475FD` (lavender) are left alone.

## Files touched

| File | Fix |
|---|---|
| gauge-basic | `ZONE_BAD` → red, `ZONE_WARN` → amber |
| dashboard-metrics-tiles | `STATUS_COLORS` warning → amber, critical →
red |
| kagi-basic | `COLOR_YIN` (bearish) → red |
| point-and-figure-basic | `O_COLOR` (falling) → red |
| horizon-basic | `neg_base` (negative) → red |
| bar-diverging | negative bars + legend → red |
| renko-basic | `BEARISH` → red |
| slope-basic | `COLOR_DEC` → red |
| waterfall-basic | `DECREASE_COLOR` → red |
| shap-waterfall | `COLOR_POS` (positive SHAP) → red (against blue) |
| indicator-macd | `NEGATIVE_COLOR` → red; `SIGNAL` → ochre
(re-establishes contrast vs the red NEGATIVE bars) |
| indicator-ema | `GOLDEN_COLOR` → green, `DEATH_COLOR` → red;
short/long EMA → blue/ochre categorical |

Also addresses three nits on the helper scripts from the same review
round:

- `migrate_to_imprint.py` — docstring for `_rename_palette_constant` no
longer overstates the conservatism of the plain-regex rename
- `migrate_render_and_upload.py` — module docstring matches the actual
target-discovery flow (working tree or `--from-commit`), drops a stale
reference to a never-implemented `.migration-list.json`
- `migrate_render_and_upload.py` — pre-flight now also checks `gsutil
version`, not just `gcloud auth list`

## What this PR doesn't do

Comment rot in the other ~125 untouched matplotlib impls (e.g. comments
still saying "Okabe-Ito vermillion" next to imprint hex codes) is
**deferred** — those refresh naturally on the next daily-regen cycle.

The image regeneration for **all** matplotlib impls (the 137 from #7776
+ the 12 fixed here) hasn't run yet against GCS. That happens locally
via `scripts/migrate_render_and_upload.py --from-commit <merge-sha>
--library matplotlib` immediately after this PR merges.

## Test plan

- [x] `ruff check .` + `ruff format --check .` green (matches CI)
- [ ] CI green (lint + tests + frontend-tests)
- [ ] Copilot triage
- [ ] After merge: local Stage B for all 137+12 matplotlib impls → GCS
production overwrite
- [ ] Spot-check gauge-basic, dashboard-metrics-tiles, kagi-basic on
anyplot.ai

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MarkusNeusinger added a commit that referenced this pull request May 27, 2026
## Summary

Two small follow-ups uncovered while running Stage B of the imprint
migration locally on the 137 matplotlib impls (PRs #7776 / #7777).

**1. `scripts/migrate_render_and_upload.py` — savefig fallback**

A handful of impls call `plt.savefig(os.path.join(script_dir, ...))`
instead of plain `plt.savefig("plot-{theme}.png")`, so the PNG lands
next to the impl file instead of inside `.regen-preview/{library}/`.
Stage B now moves stray PNGs into the preview dir before checking for
completeness — keeps the script compatible with these impls without
rewriting them.

Affected on the matplotlib pass: `area-cumulative-flow`, `bar-spine`,
`histogram-stepwise`, `indicator-macd`, `line-stepwise`,
`linked-views-selection`. Likely a few more across other Python
libraries once we run those.

**2. `pyproject.toml` — add cartopy / wordcloud / matplotlib-venn to
`plotting` extra**

Three matplotlib add-ons that some specs depend on (`map-projections` →
cartopy, `wordcloud-basic` → wordcloud, `venn-basic` → matplotlib-venn).
Without them, `uv sync --extra plotting` produces a venv that can't
render those three. CI paths are unaffected — the per-library
`lib-matplotlib` extra is intentionally minimal.

## Result on the 137 matplotlib impls

After both fixes + a `--resume` rerun: **137 / 137 ✓** in GCS
production. Without them the first run was 128 / 137 (the 9 failures
above).

## Test plan

- [x] `ruff check .` + `ruff format --check .` green (matches CI)
- [ ] CI green (lint + tests + frontend-tests)
- [ ] Copilot triage

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MarkusNeusinger added a commit that referenced this pull request May 27, 2026
## Summary

- Positional hex replacement across **138 seaborn impls** that already
ship light+dark renders, moving them from Okabe-Ito + variant-D to the
canonical **imprint** palette in
[core/palette.py](https://github.com/MarkusNeusinger/anyplot/blob/main/core/palette.py).
Mirrors #7776 for matplotlib.
- Includes proactive **semantic-anchor fixes for 11 seaborn impls**
where slot-1 (now lavender) carried explicit meaning (red =
bad/loss/bearish, amber = warning, green = good). Same pattern as #7777
but applied preemptively before Copilot's first review.
- Picks up the `uv.lock` entries for cartopy / wordcloud /
matplotlib-venn that were missing from #7778.
- Adds `.migration-progress.json` to `.gitignore` (local-only Stage-B
state file).

## Scope filter

Per metadata YAML, only impls with **both** `preview_url_light` and
`preview_url_dark` set are migrated. Single-theme impls are left to the
regular daily-regen rotation. Specs without an existing seaborn impl
don't get a new one.

| Library | Migrated | Already imprint | Skip (single-theme) |
|---|---:|---:|---:|
| seaborn | **138** | 86 | 96 |

## Semantic fixes (proactive, 11 files)

| File | Fix |
|---|---|
| bar-diverging | `LOSS_COLOR` → red |
| candlestick-volume | `SECONDARY` (bear) → red |
| dashboard-metrics-tiles | warning → amber, critical → red |
| gauge-basic | `ZONE_LOW`/`MED` → red/amber traffic-light |
| indicator-macd | `HIST_NEGATIVE` → red, `SIGNAL` → ochre |
| kagi-basic | `color_yin` → red |
| point-and-figure-basic | `BEAR_COLOR` → red |
| renko-basic | `BEARISH` → red |
| shap-waterfall | `COLOR_POS` → red (vs blue NEG) |
| slope-basic | `COLOR_DECREASE` → red |
| waterfall-basic | `ACCENT_RED` → red (was lavender despite the name) |

## What this PR doesn't do

Image regen for the 138 seaborn impls hasn't run yet against GCS. That
happens locally via `scripts/migrate_render_and_upload.py --from-commit
<merge-sha> --library seaborn` immediately after this PR merges.

## Test plan

- [x] `ruff check .` + `ruff format --check .` green
- [x] Idempotency check (second Stage-A run on a sample spec = 0
changes)
- [ ] CI green (lint + tests + frontend-tests)
- [ ] Copilot triage (should be minimal — semantic bugs pre-fixed)
- [ ] After merge: local Stage B for all 138 seaborn impls → GCS
production overwrite
- [ ] Spot-check gauge-basic, dashboard-metrics-tiles,
candlestick-volume on anyplot.ai

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MarkusNeusinger added a commit that referenced this pull request May 27, 2026
## Summary

- Positional hex replacement across **132 plotnine impls** with
light+dark renders, moving them from Okabe-Ito + variant-D to
**imprint**. Same pattern as #7776 (matplotlib) and #7779 (seaborn).
- Includes proactive **semantic-anchor fixes for 11 plotnine impls**
where slot-1 (now lavender) carried explicit meaning. Variable `ORANGE`
in indicator-macd renamed to `OCHRE` so the name matches the actual
imprint hex.

## Semantic fixes (proactive)

| File | Fix |
|---|---|
| bar-diverging | "Negative" → red |
| candlestick-volume | DOWN_COLOR → red |
| dashboard-metrics-tiles | status warn → amber, critical → red |
| gauge-basic | ZONE_BAD/WARN → red/amber traffic-light |
| indicator-macd | ORANGE → OCHRE (var rename + #BD8233) |
| kagi-basic | YIN_COLOR → red |
| logistic-regression | FAIL_COLOR → red |
| ohlc-bar | DOWN_COLOR → red |
| point-and-figure-basic | O_COLOR → red |
| renko-basic | BEARISH → red |
| shap-waterfall | COLOR_POS → red (vs blue NEG) |

## Test plan

- [x] `ruff check .` + `ruff format --check .` green
- [ ] CI green
- [ ] Copilot triage
- [ ] After merge: Stage B for 132 plotnine impls → GCS production

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MarkusNeusinger added a commit that referenced this pull request May 27, 2026
…basic

Extends scripts/migrate_to_imprint.py with an rgba(r,g,b,a) substitution
pass: same positional Okabe-Ito / variant-D → imprint mapping, but
matching ints inside `rgba(r, g, b, ...)` literals instead of `#RRGGBB`.
Whitespace inside the tuple is tolerated.

This is what we should have had from the start — the hex-only regex
in PR #7776/#7779 missed every rgba() fill across plotly (and likely
across bokeh + altair in upcoming passes). Without this, every plotly
PR turned into "ship outline migration, Copilot finds rgba fill
mismatches, manual cleanup". With it, the next library passes catch
them on the first Stage-A run.

The new pass also catches one plotly file the manual sweep missed —
drawdown-basic uses `rgba(183,29,39,...)` which is variant-D's
#B71D27 (not Okabe-Ito); now correctly migrated to imprint red rgb
174/48/48.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MarkusNeusinger added a commit that referenced this pull request May 27, 2026
## Summary

- Positional hex replacement across **139 plotly impls** with light+dark
renders → imprint. Mirrors #7776/#7779/#7780.
- Proactive semantic-anchor fixes for **13 plotly impls**
(red/amber/green where slot-1 lavender broke the meaning).

## Semantic fixes (proactive)

bar-diverging, candlestick-volume, gauge-basic (traffic-light),
indicator-ema (golden cross → amber), indicator-macd (signal → ochre for
contrast), indicator-rsi (overbought 70 → red), kagi-basic, ohlc-bar,
point-and-figure-basic, renko-basic, shap-waterfall, slope-basic,
waterfall-basic.

## Test plan

- [x] `ruff check .` + format green
- [ ] CI green
- [ ] Copilot triage
- [ ] After merge: Stage B for 139 plotly impls → GCS

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MarkusNeusinger added a commit that referenced this pull request May 27, 2026
Positional hex + rgba replacement across 135 bokeh impls that already
ship light+dark renders. Mirrors PRs #7776/#7779/#7780/#7781.

Stage A now handles rgba(r,g,b,a) literals natively (added in #7781),
so the outline/fill mismatch class that bit the plotly PR doesn't
recur here.

Proactive semantic-anchor fixes for 13 bokeh impls where slot-1
(now lavender) or slot-5 (now red) carried explicit meaning:

- bar-diverging          BRAND_NEG       → red
- candlestick-volume     COLOR_DOWN      → red
- dashboard-metrics-tiles status warn→amber, critical→red, UNFAVORABLE→red
- gain-curve             SECONDARY       → blue (perfect-model reference)
- gauge-basic            ZONE_LOW/MID    → red/amber traffic-light
- horizon-basic          neg_colors      → warm-red ramp (light→mid→dark red)
- indicator-macd         HIST_NEGATIVE   → red
- kagi-basic             YIN_COLOR       → red
- point-and-figure-basic O_COLOR         → red
- renko-basic            BEARISH_COLOR   → red
- shap-waterfall         POS_COLOR       → red (vs blue NEG)
- slope-basic            DECREASE_COLOR  → red
- waterfall-basic        NEGATIVE        → red

Drive-by: add squarify to the [plotting] extra. It's required by
`plots/treemap-basic/implementations/python/seaborn.py` and was the
last spec to fail Stage B during the seaborn pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants