Skip to content

fix(frontend+api): wire R/ggplot2 into code viewer, libraries page, languages count#6961

Merged
MarkusNeusinger merged 2 commits into
mainfrom
fix/wire-r-ggplot2-frontend
May 16, 2026
Merged

fix(frontend+api): wire R/ggplot2 into code viewer, libraries page, languages count#6961
MarkusNeusinger merged 2 commits into
mainfrom
fix/wire-r-ggplot2-frontend

Conversation

@MarkusNeusinger
Copy link
Copy Markdown
Owner

Three R/ggplot2 rollout gaps surfaced after PR #6951 merged. All show wrong data despite the DB row being correct — they were always going to need a follow-up after the first non-Python impl landed.

1. Code viewer empty on /scatter-basic/r/ggplot2

`ImplRepository.get_code()` has signature `get_code(spec_id, library_id, language_id="python")`. The `/specs/{spec_id}/{library}/code` endpoint never accepted a language and never passed one through — so R rows (`language_id="r"`) failed the WHERE clause, the endpoint returned 404, and the frontend showed a blank code panel.

```bash
$ curl -s -o /dev/null -w "%{http_code}\n" https://anyplot.ai/api/specs/scatter-basic/matplotlib/code
200
$ curl -s -o /dev/null -w "%{http_code}\n" https://anyplot.ai/api/specs/scatter-basic/ggplot2/code
404
```

Fix:

  • API: `get_impl_code` accepts `?language=` (default `"python"` for backwards compat). `_build_impl_code` and the Redis cache key both include language so Python and R impls under the same library_id can't collide.
  • Frontend: `useCodeFetch` accepts an optional `language` arg, includes it in the in-memory cache key, and only appends `?language=` to the URL when it diverges from python — Python URLs and existing tests stay unchanged.
  • SpecPage: passes `urlLanguage` (already destructured from `useParams`) to `fetchCode`/`getCode`, and `impl.language` to the copy-to-clipboard path.

2. Landing strip shows "languages: 1"

`NumbersStrip.tsx` line 16 was `{ value: '1', label: 'languages' }` — literally hardcoded, never wired to any stat.

Fix:

  • API: add `languages: int = 0` to `StatsResponse`. `_refresh_stats` and the cached `_fetch` factory derive it from `{lib.language_id for lib in libraries}` (with a `len(LANGUAGES_METADATA)` fallback when libraries are unavailable, mirroring how libraries count already worked).
  • Frontend: bind to `stats.languages` with an em-dash fallback while loading.

3. ggplot2 missing from /libraries

`app/src/constants/index.ts` had:
```ts
export const LIBRARIES = ['altair', 'bokeh', 'highcharts', 'letsplot', 'matplotlib', 'plotly', 'plotnine', 'pygal', 'seaborn'];
```

LibrariesPage iterates this list to render cards, so ggplot2 was silently skipped even though `/api/libraries` already returned it.

Fix: add `'ggplot2'` to `LIBRARIES` (alpha order, between bokeh and highcharts) and `'gg'` to `LIB_ABBREV` for compact display.

Test plan

  • `useCodeFetch.test.ts` — 12/12 green (10 existing + 2 new: `?language=` is appended for non-python, separate cache keys per language for same library_id)
  • `tests/unit/api/test_stats.py` — green, asserts `languages == 1` for the mocked single-library case
  • `tests/unit/api/test_schemas.py` — green, `languages` field present in `model_dump()`
  • `tests/unit/api/test_routers.py` — 8/8 code-endpoint tests still pass (default-python path unchanged)
  • After deploy: `curl https://anyplot.ai/api/specs/scatter-basic/ggplot2/code?language=r\` returns 200 with the R source
  • After deploy: `/libraries` shows ggplot2 card, landing strip shows `languages: 2`

Out of scope (separate issues)

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings May 16, 2026 22:36
…anguages count

Three R/ggplot2 rollout gaps reported by users after PR #6951 merged.

## 1. Code viewer 404 on /scatter-basic/r/ggplot2

`ImplRepository.get_code()` accepts a language_id but defaults to
"python", and the `/specs/{spec_id}/{library}/code` endpoint never
passed one through. R rows were filtered out at the WHERE clause and
the endpoint returned 404 → frontend showed an empty code panel.

- API: add optional `?language=` query param (defaults to "python" for
  backwards compat). Include language in the cache key so Python and
  R impls under the same library_id can't collide.
- Frontend: `useCodeFetch` accepts an optional `language` arg, includes
  it in the cache key, and only appends `?language=` when it diverges
  from python so Python URLs and tests remain unchanged.
- `SpecPage` passes `urlLanguage` (from the route) to fetchCode/getCode
  and `impl.language` to the copy-to-clipboard path.

## 2. "languages: 1" on the landing strip

`NumbersStrip.tsx` hardcoded `'1'` for the languages tile.
- API: add `languages` to `StatsResponse`, computed from the distinct
  set of `lib.language_id` (falls back to `len(LANGUAGES_METADATA)`
  when libraries are unavailable).
- Frontend: bind to `stats.languages` with a "—" fallback during load.

## 3. ggplot2 missing from /libraries

`app/src/constants/index.ts:LIBRARIES` was a hardcoded 9-item list
(matplotlib, seaborn, ...) without ggplot2, so the LibrariesPage grid
silently skipped it. Added ggplot2 to the list (alpha order, between
bokeh and highcharts) and to `LIB_ABBREV` as `gg`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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

Follow-up to PR #6951 fixing three rollout gaps after the first non-Python (R/ggplot2) implementation merged: the code viewer 404'd for R impls, the landing strip hard-coded languages: 1, and ggplot2 was missing from /libraries.

Changes:

  • API: /specs/{spec_id}/{library}/code accepts ?language= (default python), threaded through _build_impl_code and the Redis cache key; StatsResponse gains a languages field derived from distinct library.language_ids.
  • Frontend: useCodeFetch accepts an optional language, includes it in cache key, and appends ?language= to the URL only for non-Python; SpecPage passes through urlLanguage/impl.language; NumbersStrip binds to stats.languages.
  • Constants: ggplot2 added to LIBRARIES (alpha order) and LIB_ABBREV (gg).

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
api/routers/specs.py Adds language query param to code endpoint and includes it in cache key + repo lookup
api/routers/stats.py Computes distinct-language count from libraries, with LANGUAGES_METADATA fallback
api/schemas.py Adds languages: int = 0 to StatsResponse
app/src/hooks/useCodeFetch.ts Optional language param; cache keyed by specId:language:library; query param only for non-python
app/src/hooks/useCodeFetch.test.ts New tests for ?language=r URL and per-language cache separation
app/src/pages/SpecPage.tsx Passes urlLanguage to fetchCode/getCode, and impl.language to copy-to-clipboard
app/src/components/NumbersStrip.tsx Binds languages value to stats.languages with em-dash fallback
app/src/constants/index.ts Adds ggplot2 to LIBRARIES and gg to LIB_ABBREV
tests/unit/api/test_stats.py Asserts languages == 1 for single mocked library
tests/unit/api/test_schemas.py Updates serialization assertion and adds languages field test

@codecov
Copy link
Copy Markdown

codecov Bot commented May 16, 2026

Codecov Report

❌ Patch coverage is 88.88889% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
api/routers/specs.py 85.71% 1 Missing ⚠️
app/src/components/NumbersStrip.tsx 0.00% 1 Missing ⚠️
app/src/pages/SpecPage.tsx 66.66% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Ruff format fold the multi-line function signatures back onto single
lines after the language/languages additions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@MarkusNeusinger MarkusNeusinger merged commit 489a531 into main May 16, 2026
9 checks passed
@MarkusNeusinger MarkusNeusinger deleted the fix/wire-r-ggplot2-frontend branch May 16, 2026 22:43
MarkusNeusinger pushed a commit that referenced this pull request May 17, 2026
PR #6961 deferred a known `tsc` error under "Out of scope" and that
deferral broke every subsequent frontend Cloud Build — the language
count, ggplot2 card, and R code viewer fixes never reached anyplot.ai
even though the PR merged green. Codify the rule so the next latent
blocker gets fixed in the same PR instead of parked.
MarkusNeusinger added a commit that referenced this pull request May 17, 2026
## Why

The frontend changes from [PR
#6961](#6961) —
`languages` count on the landing strip, ggplot2 card on `/libraries`, R
code viewer on `/scatter-basic/r/ggplot2` — are merged on `main`, but
production still shows the pre-#6961 state:

```bash
$ curl -s https://anyplot.ai/assets/constants-Cv1rI8LA.js | head -c 200
…LIBRARIES=['altair','bokeh','highcharts',…]  # no ggplot2
$ curl -s https://anyplot.ai/assets/LandingPage-Bluw1eOY.js | grep -oE '.{20}languages.{20}'
…children:[{value:`1`,label:`languages`}…    # hardcoded '1'
```

All served assets carry `last-modified: Sat, 16 May 2026 20:04:15 GMT` —
about 2.5 h **before** #6961 merged. The API was redeployed (`/stats`
now returns `"languages":2,"libraries":10`), but the frontend Cloud Run
revision is stale.

## Root cause

The frontend Cloud Build (`app/cloudbuild.yaml` → `yarn build` = `tsc &&
vite build`) fails at the `tsc` step:

```
src/components/CodeHighlighter.tsx(3,15): error TS7016: Could not find a declaration
file for module 'react-syntax-highlighter/dist/esm/languages/prism/r'.
'…/react-syntax-highlighter/dist/esm/languages/prism/r.js' implicitly has an 'any' type.
```

`@types/react-syntax-highlighter` actually does ship an ambient
declaration for this exact path (line 2768 of its `index.d.ts`), but
with `moduleResolution: "bundler"` tsc resolves the import to the
sibling `.js` file first and never reaches the @types index — only `r`
trips, `python` from the line above happens to resolve through the
cached package types. Net effect: `tsc` exits non-zero, `vite build`
never runs, no new Docker layer, Cloud Run keeps serving the previous
revision.

Docker build log confirmed this:

```
ERROR: build step 0 "gcr.io/cloud-builders/docker" failed: step exited with non-zero status: 2
The command '/bin/sh -c yarn build' returned a non-zero code: 2
```

PR #6961 listed this exact TS error under "Out of scope" — it had been
latent since #6944 added the R import, and only became a deploy blocker
because #6961 was the first frontend-touching PR after the lib bumps
that affect tsc behavior.

## Fix

Add `app/src/types/react-syntax-highlighter.d.ts` declaring the missing
module locally. A project-local shim wins over `@types`, which is enough
to unblock `tsc`. The runtime import is unchanged — Prism still
registers the real R grammar via `registerLanguage('r', r)`, and a grep
over the rebuilt `CodeHighlighter-*.js` confirms the R keyword set is
bundled:

```
break|else|for|function|if|in|next|repeat|while
```

So R syntax highlighting on `/scatter-basic/r/ggplot2` works as
intended.

## Test plan

- [x] `cd app && yarn build` — green (was failing on TS7016 before)
- [x] `cd app && yarn test` — 459/459 green
- [x] New bundle hash differs from the deployed one
(`LandingPage-e58y3raq.js` vs prod `LandingPage-Bluw1eOY.js`),
confirming the redeploy will publish fresh assets
- [ ] After merge: Cloud Build for `anyplot-app` finishes green;
`https://anyplot.ai/assets/LandingPage-*.js` no longer contains the
hardcoded `{value:'1',label:'languages'}`
- [ ] After deploy: landing strip shows `languages: 2`, `/libraries`
shows the ggplot2 card, `/scatter-basic/r/ggplot2` code panel renders R
code with highlighting

## Notes

- This PR does **not** re-introduce the language count / ggplot2 /
R-code-viewer logic — those landed in #6961 and are already on main.
This only removes the build blocker so the existing fixes can actually
reach production.
- The Cloud Build "deploy-app (anyplot) ✅ Passed" check that appeared on
#6961's merge commit was from `.github/workflows/notify-deployment.yml`,
which is a no-op `echo` step that records a GitHub Deployment object —
it does not actually report the GCP Cloud Build outcome. Worth a
follow-up to either wire the real Cloud Build status back to the commit
or rename the workflow so the green check doesn't mislead.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01DdvT6aZxMm5QKU4jd8afUr)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
MarkusNeusinger added a commit that referenced this pull request May 18, 2026
Version bump for the v2.4.0 release. Release notes will be attached to
the tag once this lands.

## Highlights since v2.3.0

- **R / ggplot2 added as the 10th library** + multi-language pipeline
(#6944, #6961, #7052). 30 ggplot2 implementations landed across
foundational plot types.
- **In-app feedback widget** (#7143).
- **Stats page** with Plausible visitors chart + daily-impl timeline
(#6608).
- **Language across the site**: `/plots?lang=` filtering, cross-language
carousel, language in URLs and titles (#7141, #7142, #7144).
- **UI polish**: pseudo-function styling for 404 / footer / empty state
/ library card (#6436); mobile fixes for `/stats`, `/mcp`, breadcrumb +
FAB (#6902, #7283).
- **Pipeline**: review-retry listener + stuck-jobs watchdog (#6084);
daily-regen 2h → hourly (#6943).
- **Dependencies**: mypy 1.20→2.1, urllib3 2.6→2.7, authlib bump,
react/mui/python-minor groups.
- ~1200 implementation regenerations across all 10 libraries.

No SemVer-breaking changes.

**Full Changelog:**
v2.3.0...main

🤖 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 22, 2026
)

Closes #7612.

Adds **Julia** as anyplot's third language with **Makie.jl** (CairoMakie
backend) as its first library entry. Generalizes the multi-language
plumbing introduced for R/ggplot2 in #6944 + follow-ups (#6947, #6961,
#7066) so every `python | r` dispatch now handles `python | r | julia`
uniformly.

## Phase ordering — heads-up

This ships **Phase 5 (Julia) before Phases 1+2 (JavaScript family)**,
which the issue called out explicitly. The multi-language pipeline is
already proven on R; shipping Julia next provides a cheap second
non-Python validation that surfaces multi-language gaps the JS work
would otherwise hit cold. The JavaScript phases remain planned in
unchanged order. See `docs/concepts/library-expansion.md` §9.

## Why Makie over Plots.jl

Distinct scientific stack (not a wrapper), ~45 % share within Julia,
MIT-licensed. CairoMakie is the right backend for CI — pure-Cairo,
headless, no GPU / GLFW window. `PlotlyJS.jl` is a plotly.js wrapper
(already covered via Python Plotly). Plots.jl is the alternative;
revisit only if Plausible data flips. `GLMakie` / `WGLMakie` interactive
backends are out of scope; `INTERACTIVE_LIBRARIES` intentionally
excludes Makie.

## What's in the PR

### Constants + registry

- `core/constants.py`: add `julia` language, `makie` library
- `tests/unit/core/test_constants.py`: assert `julia` + `makie`

### CI runtime

- `.github/actions/setup-julia/action.yml`: install Julia 1.11, restore
from `Project.toml` / `Manifest.toml`, smoke-test a CairoMakie render
- `Project.toml`: pin CairoMakie + Makie + dataset packages (CSV,
Colors, ColorSchemes, DataFrames, PalmerPenguins, RDatasets, Random,
Statistics)

**`Manifest.toml` is intentionally not committed** — it gets resolved by
the first CI run; the action falls back to `Pkg.add(...)` when
`Manifest.toml` is absent so first-run doesn't hard-fail. (Same model as
`renv.lock` had pre-bump on the R rollout.)

### Workflows

Each workflow derives `LANGUAGE` + `EXT` from `LIBRARY` via a case
statement. Every case now has `matplotlib→python`, `ggplot2→r`,
`makie→julia`:

- `impl-generate.yml`, `impl-repair.yml`, `impl-review.yml`,
`impl-merge.yml`: add the `makie→julia` arm; install `setup-julia` when
`language==julia`; detect Julia version via `julia -e 'print(VERSION)'`;
detect Makie version via `Pkg.dependencies()`
- `impl-review.yml`:
- extend canvas-gate library-cause map with a Makie entry
(`Figure(resolution=(1600,900)) + px_per_unit=2` → 3200×1800;
`resolution=(1200,1200)` → 2400×2400)
- extend header-rewrite step to handle `#`-prefixed Julia comment
blocks, with prepend fallback when missing (same fix as #6947 did for
Python / R)
- `bulk-generate.yml`: add `makie` to `library` choices and
`ALL_LIBRARIES`
- `impl-merge.yml`: completion total now derives from the actual library
list rather than hardcoding 9

### Prompts

- **`prompts/library/makie.md`** (new): no-workarounds policy,
NOT_FEASIBLE for primary-interactivity specs, canvas hard rule,
`ANYPLOT_THEME` mapping, forbidden patterns (no `Plots.jl`, no
`display(fig)`, no `GLMakie`/`WGLMakie`), `# `-comment header style
- `plot-generator.md`: extend lead, available environment, forbidden
patterns with Julia/Makie section
- `quality-evaluator.md`, `workflow-prompts/ai-quality-review.md`:
extend the AR-08 static-libraries list with `makie`; extend the language
allow-list with `julia`
- `workflow-prompts/impl-generate-claude.md`, `impl-repair-claude.md`:
add the `julia | .jl | julia --project=.` row to the (language, ext,
runner) table; add Julia run + format sections
- `workflow-prompts/impl-similarity-claude.md`: third arm for `julia` in
the `{language}` mapping

### Frontend (avoids the two-follow-up-PR pattern from #6961 / #7066)

- `app/src/constants/index.ts`: add `makie` to `LIBRARIES`, `LIB_ABBREV:
'mk'`, `LIB_TO_LANG: 'julia'`, `LANG_DISPLAY: 'Julia'`, `LANG_EXT: 'jl'`
- `app/src/components/CodeHighlighter.tsx`: register `julia` Prism
grammar
- `app/src/types/react-syntax-highlighter.d.ts`: declare the
`prism/julia` module locally — **TS7016 fix in same PR, not deferred**
(the issue specifically called this out as a gap not to repeat)
- `app/src/components/LibraryCard.tsx`: description for makie
- `app/src/components/PlotOfTheDay.tsx`, `PlotOfTheDayTerminal.tsx`:
`julia --project=.` runner token + `.jl` extension when language is
julia
- `app/src/pages/LibrariesPage.tsx`, `AboutPage.tsx`, `PlotsPage.tsx`:
meta-description copy bumped to "Eleven libraries across Python, R, and
Julia"
- `app/src/pages/DebugPage.tsx`: `makie` column in `SpecStatus`
interface (the grid template uses `LIBRARIES.length` so the matrix grows
automatically)

### Backend

- `api/routers/debug.py`: `SpecStatusItem` and `library_names` entries
for makie
- `api/routers/specs.py`: doc comment updated to note `makie is Julia`

### Tests

- `app/src/components/CodeHighlighter.test.tsx`: julia-grammar case
- `app/src/hooks/useCodeFetch.test.ts`: julia `?language=` case +
three-language cache-key test
- `app/src/pages/DebugPage.test.tsx`: `makie` field on fixture

### Docs

- `docs/concepts/library-expansion.md`: §1 current-state table grows to
11 entries; §8 Phase 5 marked **shipped**; §9 records the phase-skip
rationale, Makie-over-Plots.jl decision, and CairoMakie-only scope
- `docs/reference/repository.md`: `implementations/` layout shows
`python/` + `r/` + `julia/` siblings
- `docs/concepts/vision.md`, `docs/reference/style-guide.md`:
pipeline-story copy ("eleven libraries in three languages")
- `docs/reference/plausible.md`: language slug list now includes `julia`
- `prompts/README.md`, `agentic/docs/project-guide.md`: Makie added

## Verified

- ✅ `core/constants.py` loads: `len(SUPPORTED_LIBRARIES)=11`,
`SUPPORTED_LANGUAGES={'python','r','julia'}`
- ✅ `pytest tests/unit/core tests/unit/api/test_routers.py
tests/unit/api/test_debug.py tests/unit/api/test_schemas.py` — **213
passed**, no regressions
- ✅ `Project.toml` valid TOML, deps load
- Frontend tests are wired but I couldn't run `yarn test` in this
environment (no node_modules). Code follows the existing patterns from
the R rollout.

## Post-merge follow-ups (called out in the issue)

1. Run `uv run python -m automation.scripts.label_manager sync` once so
`library:makie`, `generate:makie`, `impl:makie:{pending,done,failed}`
labels appear.
2. First CI run of `setup-julia` will resolve `Manifest.toml` — commit
that lockfile in a tiny follow-up so subsequent runs use
`Pkg.instantiate()` instead of the `Pkg.add` fallback.
3. Trigger `bulk-generate.yml -f library=makie` to fill the catalog
(separate from this infra PR — the issue called this out as "Out of
scope" / handled by the regular flow).
4. Optional `julia.anyplot.ai` marketing subdomain (mentioned in the
issue's "Out of scope" section; small follow-up modeled on
`python.anyplot.ai`).


---
_Generated by [Claude
Code](https://claude.ai/code/session_01AzjPDEE5aY2LYR5tcK3faj)_

---------

Co-authored-by: Claude <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