Skip to content

Commit 3da97f0

Browse files
Merge branch 'claude/plots-page-retro-branding-emimK'
Renames the catalog URL/page to plots across frontend, backend, and docs. Retro UI experiment was reverted, only the clean rename was kept.
2 parents 74d9841 + 2ad0d72 commit 3da97f0

31 files changed

+159
-140
lines changed

api/analytics.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def track_og_image(
148148
149149
Args:
150150
request: FastAPI request for headers
151-
page: Page type ('home', 'catalog', 'spec_overview', 'spec_detail')
151+
page: Page type ('home', 'plots', 'spec_overview', 'spec_detail')
152152
spec: Spec ID (optional)
153153
library: Library ID (optional)
154154
filters: Query params for filtered home page (e.g., {'lib': 'plotly', 'dom': 'statistics'})
@@ -160,8 +160,8 @@ def track_og_image(
160160
# Build URL based on page type
161161
if page == "home":
162162
url = "https://anyplot.ai/"
163-
elif page == "catalog":
164-
url = "https://anyplot.ai/catalog"
163+
elif page == "plots":
164+
url = "https://anyplot.ai/plots"
165165
elif spec is not None and library:
166166
url = f"https://anyplot.ai/python/{spec}/{library}"
167167
elif spec is not None:

api/routers/og_images.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ async def get_home_og_image(request: Request) -> Response:
5050
)
5151

5252

53-
@router.get("/catalog.png")
54-
async def get_catalog_og_image(request: Request) -> Response:
55-
"""OG image for catalog page with tracking."""
56-
track_og_image(request, page="catalog")
53+
@router.get("/plots.png")
54+
async def get_plots_og_image(request: Request) -> Response:
55+
"""OG image for plots page with tracking."""
56+
track_og_image(request, page="plots")
5757

5858
return Response(
5959
content=_get_static_og_image(), media_type="image/png", headers={"Cache-Control": "public, max-age=86400"}

api/routers/seo.py

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ def _build_sitemap_xml(specs: list) -> str:
2828
'<?xml version="1.0" encoding="UTF-8"?>',
2929
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
3030
" <url><loc>https://anyplot.ai/</loc></url>",
31-
" <url><loc>https://anyplot.ai/catalog</loc></url>",
31+
" <url><loc>https://anyplot.ai/plots</loc></url>",
32+
" <url><loc>https://anyplot.ai/specs</loc></url>",
3233
" <url><loc>https://anyplot.ai/mcp</loc></url>",
3334
" <url><loc>https://anyplot.ai/legal</loc></url>",
3435
" <url><loc>https://anyplot.ai/stats</loc></url>",
@@ -83,7 +84,7 @@ async def _refresh_sitemap() -> str:
8384

8485
# Route through API for tracking (was: anyplot.ai/og-image.png)
8586
DEFAULT_HOME_IMAGE = "https://api.anyplot.ai/og/home.png"
86-
DEFAULT_CATALOG_IMAGE = "https://api.anyplot.ai/og/catalog.png"
87+
DEFAULT_PLOTS_IMAGE = "https://api.anyplot.ai/og/plots.png"
8788
DEFAULT_DESCRIPTION = "library-agnostic, ai-powered plotting."
8889

8990

@@ -103,7 +104,7 @@ async def get_sitemap(db: AsyncSession | None = Depends(optional_db)):
103104
"""
104105
Generate dynamic XML sitemap for SEO.
105106
106-
Includes root, catalog page, and all specs with implementations.
107+
Includes root, plots/specs pages, and all specs with implementations.
107108
"""
108109
if db is None:
109110
return Response(content=_STATIC_SITEMAP, media_type="application/xml")
@@ -143,15 +144,28 @@ async def seo_home(request: Request):
143144
)
144145

145146

146-
@router.get("/seo-proxy/catalog")
147-
async def seo_catalog():
148-
"""Bot-optimized catalog page with correct og:tags."""
147+
@router.get("/seo-proxy/plots")
148+
async def seo_plots():
149+
"""Bot-optimized plots page with correct og:tags."""
149150
return HTMLResponse(
150151
BOT_HTML_TEMPLATE.format(
151-
title="Catalog | anyplot.ai",
152-
description="Browse all Python plotting specifications alphabetically. Find matplotlib, seaborn, plotly, bokeh, altair examples.",
153-
image=DEFAULT_CATALOG_IMAGE,
154-
url="https://anyplot.ai/catalog",
152+
title="plots | anyplot.ai",
153+
description="Browse and filter Python visualization examples across 9 libraries: matplotlib, seaborn, plotly, bokeh, altair, plotnine, pygal, highcharts, lets-plot.",
154+
image=DEFAULT_PLOTS_IMAGE,
155+
url="https://anyplot.ai/plots",
156+
)
157+
)
158+
159+
160+
@router.get("/seo-proxy/specs")
161+
async def seo_specs():
162+
"""Bot-optimized specs page with correct og:tags."""
163+
return HTMLResponse(
164+
BOT_HTML_TEMPLATE.format(
165+
title="specs | anyplot.ai",
166+
description="Browse all Python plotting specifications alphabetically.",
167+
image=DEFAULT_PLOTS_IMAGE,
168+
url="https://anyplot.ai/specs",
155169
)
156170
)
157171

app/src/components/Breadcrumb.test.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,28 @@ import { Breadcrumb } from './Breadcrumb';
44

55
describe('Breadcrumb', () => {
66
it('renders breadcrumb items', () => {
7-
render(<Breadcrumb items={[{ label: 'anyplot.ai', to: '/' }, { label: 'catalog' }]} />);
7+
render(<Breadcrumb items={[{ label: 'anyplot.ai', to: '/' }, { label: 'plots' }]} />);
88

99
expect(screen.getByText('anyplot.ai')).toBeInTheDocument();
10-
expect(screen.getByText('catalog')).toBeInTheDocument();
10+
expect(screen.getByText('plots')).toBeInTheDocument();
1111
});
1212

1313
it('renders linked items as links', () => {
14-
render(<Breadcrumb items={[{ label: 'anyplot.ai', to: '/' }, { label: 'catalog' }]} />);
14+
render(<Breadcrumb items={[{ label: 'anyplot.ai', to: '/' }, { label: 'plots' }]} />);
1515

1616
const link = screen.getByText('anyplot.ai');
1717
expect(link.closest('a')).toHaveAttribute('href', '/');
1818
});
1919

2020
it('renders current page as plain text', () => {
21-
render(<Breadcrumb items={[{ label: 'anyplot.ai', to: '/' }, { label: 'catalog' }]} />);
21+
render(<Breadcrumb items={[{ label: 'anyplot.ai', to: '/' }, { label: 'plots' }]} />);
2222

23-
const current = screen.getByText('catalog');
23+
const current = screen.getByText('plots');
2424
expect(current.closest('a')).toBeNull();
2525
});
2626

2727
it('renders separator between items', () => {
28-
render(<Breadcrumb items={[{ label: 'anyplot.ai', to: '/' }, { label: 'catalog' }]} />);
28+
render(<Breadcrumb items={[{ label: 'anyplot.ai', to: '/' }, { label: 'plots' }]} />);
2929

3030
expect(screen.getByText('›')).toBeInTheDocument();
3131
});

app/src/components/Breadcrumb.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export interface BreadcrumbProps {
2424
* Breadcrumb navigation component.
2525
*
2626
* @example
27-
* // Simple: anyplot.ai > catalog
28-
* <Breadcrumb items={[{ label: 'anyplot.ai', to: '/' }, { label: 'catalog' }]} />
27+
* // Simple: anyplot.ai > plots
28+
* <Breadcrumb items={[{ label: 'anyplot.ai', to: '/' }, { label: 'plots' }]} />
2929
*
3030
* @example
3131
* // With short labels for mobile

app/src/components/Footer.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ export function Footer({ onTrackEvent, selectedSpec, selectedLibrary }: FooterPr
5959
<span>·</span>
6060
<Link
6161
component={RouterLink}
62-
to="/catalog"
63-
onClick={() => onTrackEvent?.('internal_link', { destination: 'catalog', spec: selectedSpec, library: selectedLibrary })}
62+
to="/plots"
63+
onClick={() => onTrackEvent?.('internal_link', { destination: 'plots', spec: selectedSpec, library: selectedLibrary })}
6464
sx={linkSx}
6565
>
66-
catalog
66+
plots
6767
</Link>
6868
<span>·</span>
6969
<Link

app/src/components/HeroSection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ export function HeroSection({ stats }: HeroSectionProps) {
118118
<Box sx={{ display: 'flex', gap: 1.5, flexWrap: 'wrap', animation: 'rise 0.8s cubic-bezier(0.2, 0.8, 0.2, 1) 0.3s backwards' }}>
119119
<Box
120120
component={RouterLink}
121-
to="/catalog"
121+
to="/plots"
122122
sx={{
123123
textDecoration: 'none',
124124
boxSizing: 'border-box',

app/src/components/LibrariesSection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export function LibrariesSection({ libraries, onLibraryClick }: LibrariesSection
2222
number="§ 01"
2323
title={<>The <em>libraries</em></>}
2424
linkText="view all"
25-
linkTo="/catalog"
25+
linkTo="/plots"
2626
/>
2727

2828
<Box sx={{

app/src/components/NavBar.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import Box from '@mui/material/Box';
44
import { colors, typography } from '../theme';
55

66
interface NavBarProps {
7-
/** Ref to FilterBar search input -- when on /catalog, focuses it; otherwise navigates to /catalog */
7+
/** Ref to FilterBar search input -- when on /plots, focuses it; otherwise navigates to /plots */
88
searchInputRef?: React.RefObject<HTMLInputElement | null>;
99
}
1010

1111
const NAV_LINKS = [
12-
{ label: 'catalog', to: '/catalog' },
12+
{ label: 'plots', to: '/plots' },
1313
{ label: 'specs', to: '/specs' },
1414
{ label: 'palette', to: '/palette' },
1515
{ label: 'mcp', to: '/mcp' },
@@ -58,10 +58,10 @@ export function NavBar({ searchInputRef }: NavBarProps) {
5858
const location = useLocation();
5959

6060
const handleSearch = useCallback(() => {
61-
if (location.pathname === '/catalog' && searchInputRef?.current) {
61+
if (location.pathname === '/plots' && searchInputRef?.current) {
6262
searchInputRef.current.focus();
6363
} else {
64-
navigate('/catalog');
64+
navigate('/plots');
6565
}
6666
}, [location.pathname, searchInputRef, navigate]);
6767

app/src/components/SpecTabs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ export function SpecTabs({
210210
return tagCounts[paramName]?.[value] ?? null;
211211
}, [tagCounts]);
212212

213-
// Handle tag click - navigate to filtered catalog (full page navigation)
213+
// Handle tag click - navigate to filtered plots page (full page navigation)
214214
const handleTagClick = useCallback(
215215
(paramName: string, value: string) => {
216216
onTrackEvent?.('tag_click', { param: paramName, value, source: 'spec_detail' });

0 commit comments

Comments
 (0)