Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/spec-create.yml
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ jobs:
fi

# Reserved spec slugs collide with top-level frontend routes.
# Keep this list in sync with `RESERVED_TOP_LEVEL` in app/src/utils/paths.ts.
# Keep this list in sync with `RESERVED_TOP_LEVEL` in app/src/routes/paths.ts.
RESERVED_SLUGS=(plots specs libraries palette about legal mcp stats debug map api og sitemap.xml robots.txt)
for reserved in "${RESERVED_SLUGS[@]}"; do
if [[ "$SPEC_ID" == "$reserved" ]]; then
Expand Down
2 changes: 1 addition & 1 deletion app/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import CssBaseline from '@mui/material/CssBaseline';
import { ThemeProvider } from '@mui/material/styles';

import { AppRouter } from 'src/router';
import { AppRouter } from 'src/routes';
import { theme } from 'src/theme';

export function App() {
Expand Down
2 changes: 1 addition & 1 deletion app/src/components/FeedbackWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import Tooltip from '@mui/material/Tooltip';
import { useAnalytics } from 'src/hooks';
import { useLocalStorage } from 'src/hooks/useLocalStorage';
import { apiPost, endpoints } from 'src/lib/api';
import { RESERVED_TOP_LEVEL } from 'src/utils/paths';
import { RESERVED_TOP_LEVEL } from 'src/routes/paths';

const MAX_MESSAGE_LENGTH = 500;
const SESSION_KEY = 'anyplot_feedback_session';
Expand Down
5 changes: 3 additions & 2 deletions app/src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Box from '@mui/material/Box';
import Link from '@mui/material/Link';

import { GITHUB_URL } from 'src/constants';
import { paths } from 'src/routes/paths';
import { colors, fontSize, semanticColors, typography } from 'src/theme';

interface FooterProps {
Expand Down Expand Up @@ -92,11 +93,11 @@ export function Footer({ onTrackEvent, selectedSpec, selectedLibrary }: FooterPr
</Link>
</Box>
<span>·</span>
<Link component={RouterLink} to="/about" onClick={trackInternal('about')} sx={linkSx}>
<Link component={RouterLink} to={paths.about} onClick={trackInternal('about')} sx={linkSx}>
about
</Link>
<span>·</span>
<Link component={RouterLink} to="/legal" onClick={trackInternal('legal')} sx={linkSx}>
<Link component={RouterLink} to={paths.legal} onClick={trackInternal('legal')} sx={linkSx}>
legal
</Link>
</Box>
Expand Down
11 changes: 7 additions & 4 deletions app/src/components/HeroSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { PlotOfTheDayTerminal } from 'src/components/PlotOfTheDayTerminal';
import { TypewriterText } from 'src/components/TypewriterText';
import { useAnalytics } from 'src/hooks';
import type { PlotOfTheDayData } from 'src/hooks/usePlotOfTheDay';
import { paths } from 'src/routes/paths';
import { colors, typography } from 'src/theme';

interface HeroSectionProps {
Expand Down Expand Up @@ -170,11 +171,13 @@ export function HeroSection({ potd = null }: HeroSectionProps) {
}}
>
<PrimaryCta
to="/plots"
to={paths.plots}
subject="plots"
verb="browse"
ariaLabel="Browse plots"
onClick={() => trackEvent('nav_click', { source: 'hero_cta_browse', target: '/plots' })}
onClick={() =>
trackEvent('nav_click', { source: 'hero_cta_browse', target: paths.plots })
}
/>
<Box
aria-hidden="true"
Expand All @@ -189,11 +192,11 @@ export function HeroSection({ potd = null }: HeroSectionProps) {
</Box>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.25 }}>
<SecondaryLink
to="/mcp"
to={paths.mcp}
subject="mcp"
verb="connect"
ariaLabel="Connect via MCP"
onClick={() => trackEvent('nav_click', { source: 'hero_mcp', target: '/mcp' })}
onClick={() => trackEvent('nav_click', { source: 'hero_mcp', target: paths.mcp })}
/>
<SecondaryLink
href="https://github.com/MarkusNeusinger/anyplot"
Expand Down
6 changes: 3 additions & 3 deletions app/src/components/MastheadRule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import Box from '@mui/material/Box';
import { ThemeToggle } from 'src/components/ThemeToggle';
import { LANG_EXT, LIB_ABBREV } from 'src/constants';
import { useAnalytics, useLatestRelease, useTheme } from 'src/hooks';
import { paths, RESERVED_TOP_LEVEL } from 'src/routes/paths';
import { colors, typography } from 'src/theme';
import { RESERVED_TOP_LEVEL } from 'src/utils/paths';

// Symmetric block-comment delimiters used when no language context is in the URL.
// One is picked on mount so each page load reveals a different classic.
Expand Down Expand Up @@ -208,8 +208,8 @@ export function MastheadRule() {
keepRootMarkerOnXs above). */}
<Box
component={RouterLink}
to="/"
onClick={() => trackEvent('nav_click', { source: 'masthead_logo', target: '/' })}
to={paths.home}
onClick={() => trackEvent('nav_click', { source: 'masthead_logo', target: paths.home })}
sx={{ ...linkSx, display: rootMarkerDisplay }}
>
~/anyplot.ai
Expand Down
11 changes: 6 additions & 5 deletions app/src/components/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Link as RouterLink, useLocation, useNavigate } from 'react-router-dom';
import Box from '@mui/material/Box';

import { useAnalytics } from 'src/hooks';
import { paths } from 'src/routes/paths';
import { colors, typography } from 'src/theme';

const DEBUG_CLICK_COUNT = 5;
Expand Down Expand Up @@ -72,15 +73,15 @@ export function NavBar() {
}, []);

const handleSearch = () => {
trackEvent('nav_click', { source: 'nav_search', target: '/plots?focus=search' });
navigate('/plots?focus=search');
trackEvent('nav_click', { source: 'nav_search', target: paths.plotsSearch });
navigate(paths.plotsSearch);
};

// 5 rapid clicks on the logo opens /debug.
// Non-triggering clicks fall through to RouterLink's normal `/` navigation.
const handleLogoClick = useCallback(
(e: React.MouseEvent) => {
trackEvent('nav_click', { source: 'nav_logo', target: '/' });
trackEvent('nav_click', { source: 'nav_logo', target: paths.home });
if (e.ctrlKey || e.metaKey || e.shiftKey || e.button !== 0) return;
clickCountRef.current += 1;
if (clickTimerRef.current) clearTimeout(clickTimerRef.current);
Expand All @@ -91,7 +92,7 @@ export function NavBar() {
e.preventDefault();
clickCountRef.current = 0;
if (clickTimerRef.current) clearTimeout(clickTimerRef.current);
navigate('/debug');
navigate(paths.debug);
}
},
[navigate, trackEvent]
Expand Down Expand Up @@ -119,7 +120,7 @@ export function NavBar() {
{/* Logo */}
<Box
component={RouterLink}
to="/"
to={paths.home}
onClick={handleLogoClick}
sx={{
gridArea: 'logo',
Expand Down
2 changes: 1 addition & 1 deletion app/src/components/PlotOfTheDay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import { GITHUB_URL } from 'src/constants';
import { useAnalytics } from 'src/hooks';
import { useTheme } from 'src/hooks/useLayoutContext';
import { apiGet, endpoints } from 'src/lib/api';
import { specPath } from 'src/routes/paths';
import { colors, fontSize, semanticColors, typography } from 'src/theme';
import { specPath } from 'src/utils/paths';
import { buildSrcSet, getFallbackSrc } from 'src/utils/responsiveImage';
import { selectPreviewUrl } from 'src/utils/themedPreview';

Expand Down
2 changes: 1 addition & 1 deletion app/src/components/PlotOfTheDayTerminal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { GITHUB_URL } from 'src/constants';
import { useAnalytics } from 'src/hooks';
import { useTheme } from 'src/hooks/useLayoutContext';
import type { PlotOfTheDayData } from 'src/hooks/usePlotOfTheDay';
import { specPath } from 'src/routes/paths';
import { colors, typography } from 'src/theme';
import { specPath } from 'src/utils/paths';
import { buildSrcSet, getFallbackSrc } from 'src/utils/responsiveImage';
import { selectPreviewUrl } from 'src/utils/themedPreview';

Expand Down
2 changes: 1 addition & 1 deletion app/src/components/RelatedSpecs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import Typography from '@mui/material/Typography';
import { LIB_ABBREV } from 'src/constants';
import { useTheme } from 'src/hooks/useLayoutContext';
import { apiGet, endpoints } from 'src/lib/api';
import { specPath } from 'src/routes/paths';
import { colors, fontSize, semanticColors, typography } from 'src/theme';
import { specPath } from 'src/utils/paths';
import { buildSrcSet, getFallbackSrc } from 'src/utils/responsiveImage';
import { selectPreviewUrl } from 'src/utils/themedPreview';

Expand Down
3 changes: 2 additions & 1 deletion app/src/components/RootLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { NavBar } from 'src/components/NavBar';
import { useAnalytics } from 'src/hooks';
import { setAnalyticsAmbientProps } from 'src/hooks/useAnalytics';
import { useTheme } from 'src/hooks/useLayoutContext';
import { paths } from 'src/routes/paths';

const containerSx = {
px: { xs: 2, sm: 4, md: 8, lg: 12 },
Expand All @@ -31,7 +32,7 @@ export function RootLayout() {
const { pathname, hash } = useLocation();
const navigationType = useNavigationType();
const { effective } = useTheme();
const mastheadSticks = pathname !== '/plots';
const mastheadSticks = pathname !== paths.plots;

// Set synchronously during render so the first pageview from a child page's
// useEffect (which runs before the parent's useEffect) carries the theme prop.
Expand Down
3 changes: 2 additions & 1 deletion app/src/components/RouteErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Typography from '@mui/material/Typography';

import { CONFIG } from 'src/global-config';
import { NotFoundPage } from 'src/pages/NotFoundPage';
import { paths } from 'src/routes/paths';

const RELOAD_ATTEMPT_KEY = 'anyplot:chunk-reload-attempt';

Expand Down Expand Up @@ -175,7 +176,7 @@ export function RouteErrorBoundary() {
</Button>
<Button
component={RouterLink}
to="/"
to={paths.home}
variant="outlined"
startIcon={<HomeIcon />}
sx={{ textTransform: 'none' }}
Expand Down
3 changes: 2 additions & 1 deletion app/src/components/ScienceNote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Link as RouterLink } from 'react-router-dom';
import Box from '@mui/material/Box';

import { PaletteStrip } from 'src/components/PaletteStrip';
import { paths } from 'src/routes/paths';
import { colors, typography } from 'src/theme';

export function ScienceNote() {
Expand Down Expand Up @@ -94,7 +95,7 @@ export function ScienceNote() {

<Box
component={RouterLink}
to="/palette"
to={paths.palette}
sx={{
display: 'inline-block',
mt: 3,
Expand Down
2 changes: 1 addition & 1 deletion app/src/components/SpecOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import Tooltip from '@mui/material/Tooltip';
import Typography from '@mui/material/Typography';

import { useTheme } from 'src/hooks/useLayoutContext';
import { specPath } from 'src/routes/paths';
import { colors, fontSize, semanticColors, typography } from 'src/theme';
import type { Implementation } from 'src/types';
import { specPath } from 'src/utils/paths';
import { buildSrcSet, OVERVIEW_SIZES } from 'src/utils/responsiveImage';
import { selectPreviewUrl } from 'src/utils/themedPreview';

Expand Down
4 changes: 3 additions & 1 deletion app/src/components/SpecTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import Tabs from '@mui/material/Tabs';
import Tooltip from '@mui/material/Tooltip';
import Typography from '@mui/material/Typography';

import { paths } from 'src/routes/paths';

const CodeHighlighter = lazy(() => import('src/components/CodeHighlighter'));
import { apiGet, endpoints } from 'src/lib/api';
import { colors, fontSize, semanticColors, typography } from 'src/theme';
Expand Down Expand Up @@ -231,7 +233,7 @@ export function SpecTabs({
const handleTagClick = useCallback(
(paramName: string, value: string) => {
onTrackEvent?.('tag_click', { param: paramName, value, source: 'spec_detail' });
navigate(`/plots?${paramName}=${encodeURIComponent(value)}`);
navigate(paths.plotsFiltered(paramName, value));
},
[navigate, onTrackEvent]
);
Expand Down
3 changes: 2 additions & 1 deletion app/src/components/ToolbarActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Box from '@mui/material/Box';
import Tooltip from '@mui/material/Tooltip';

import type { ImageSize } from 'src/constants';
import { paths } from 'src/routes/paths';
import { colors, semanticColors } from 'src/theme';

interface ToolbarActionsProps {
Expand All @@ -28,7 +29,7 @@ export function PlotsLink() {
<Tooltip title="plots.list()">
<Box
component={Link}
to="/plots"
to={paths.plots}
aria-label="Browse plots"
sx={{
display: 'flex',
Expand Down
2 changes: 1 addition & 1 deletion app/src/hooks/useAnalytics.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useMemo, useRef } from 'react';

import { RESERVED_TOP_LEVEL } from 'src/utils/paths';
import { RESERVED_TOP_LEVEL } from 'src/routes/paths';

interface EventProps {
[key: string]: string | undefined;
Expand Down
7 changes: 4 additions & 3 deletions app/src/pages/AboutPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Link from '@mui/material/Link';
import { SectionHeader } from 'src/components/SectionHeader';
import { GITHUB_URL } from 'src/constants';
import { useAnalytics } from 'src/hooks';
import { paths } from 'src/routes/paths';
import { codeBlockStyle, colors, proseLinkStyle, textStyle, typography } from 'src/theme';

const PIPELINE_BLOCK = `// pipeline
Expand Down Expand Up @@ -44,7 +45,7 @@ export function AboutPage() {
const { trackPageview, trackEvent } = useAnalytics();

useEffect(() => {
trackPageview('/about');
trackPageview(paths.about);
}, [trackPageview]);

return (
Expand Down Expand Up @@ -108,7 +109,7 @@ export function AboutPage() {
<Box sx={{ ...textStyle, mt: 1 }}>
see the{' '}
<Link
href="/palette"
href={paths.palette}
onClick={() =>
trackEvent('internal_link', { destination: 'palette', source: 'about' })
}
Expand Down Expand Up @@ -153,7 +154,7 @@ export function AboutPage() {
curious about the stack, costs, or analytics? see{' '}
<Link
component={RouterLink}
to="/legal#transparency"
to={`${paths.legal}#transparency`}
onClick={() =>
trackEvent('internal_link', {
destination: 'legal_transparency',
Expand Down
2 changes: 1 addition & 1 deletion app/src/pages/DebugPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import { SectionHeader } from 'src/components/SectionHeader';
import { DEBUG_API_URL, LIB_ABBREV, LIB_TO_LANG, LIBRARIES } from 'src/constants';
import { useCopyCode } from 'src/hooks';
import { fetchWithAuth } from 'src/lib/api';
import { specPath } from 'src/routes/paths';
import { colors, fontSize, semanticColors, typography } from 'src/theme';
import { buildClaudePrompt } from 'src/utils/claudePrompt';
import { specPath } from 'src/utils/paths';

// ============================================================================
// Types
Expand Down
18 changes: 11 additions & 7 deletions app/src/pages/LandingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import { useAnalytics, useAppData } from 'src/hooks';
import { type FeaturedImpl, useFeaturedSpecs } from 'src/hooks/useFeaturedSpecs';
import { useTheme } from 'src/hooks/useLayoutContext';
import { usePlotOfTheDay } from 'src/hooks/usePlotOfTheDay';
import { paths, specPath } from 'src/routes/paths';
import { colors, semanticColors, typography } from 'src/theme';
import { specPath } from 'src/utils/paths';
import { buildSrcSet, getFallbackSrc } from 'src/utils/responsiveImage';
import { selectPreviewUrl } from 'src/utils/themedPreview';

Expand All @@ -31,8 +31,8 @@ export function LandingPage() {
}, [trackPageview]);

const handleLibraryClick = (lib: string) => {
trackEvent('nav_click', { source: 'library_card', target: '/plots', value: lib });
navigate(`/plots?lib=${encodeURIComponent(lib)}`);
trackEvent('nav_click', { source: 'library_card', target: paths.plots, value: lib });
navigate(paths.plotsFiltered('lib', lib));
};
Comment on lines 33 to 36

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Applied — all six hardcoded trackEvent targets (incl. two 'target: /' cases beyond the four flagged) now use the paths registry. See latest commit.


return (
Expand Down Expand Up @@ -117,8 +117,10 @@ function MapSection({ specCount }: { specCount?: number }) {

<Box
component={RouterLink}
to="/map"
onClick={() => trackEvent('nav_click', { source: 'map_teaser_preview', target: '/map' })}
to={paths.map}
onClick={() =>
trackEvent('nav_click', { source: 'map_teaser_preview', target: paths.map })
}
sx={{
display: 'block',
textDecoration: 'none',
Expand Down Expand Up @@ -475,8 +477,10 @@ function SpecsSection({
{specCount != null && featured && featured.length < specCount && (
<Box
component={RouterLink}
to="/specs"
onClick={() => trackEvent('nav_click', { source: 'specs_more_link', target: '/specs' })}
to={paths.specs}
onClick={() =>
trackEvent('nav_click', { source: 'specs_more_link', target: paths.specs })
}
sx={{
display: 'inline-block',
mt: 2.5,
Expand Down
Loading
Loading