From da84cfd4d715cc8653f9047833212776406f02a4 Mon Sep 17 00:00:00 2001 From: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com> Date: Wed, 10 Jun 2026 01:04:17 +0200 Subject: [PATCH 1/2] refactor(app): central routes/ module with paths registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - router.tsx moves to src/routes/index.tsx (git mv, route config unchanged) - utils/paths.ts moves to src/routes/paths.ts and gains a paths registry — the single source of truth for app URLs (static routes, plotsFiltered builder, spec detail via specPath) - ~25 hardcoded route literals across 16 files replaced with paths.* - routes/paths.test.ts covers builders, reserved slugs, and the registry; routes/ added to coverage - Updated path references in spec-create.yml and docs/reference/seo.md Part 6 of the frontend modernization roadmap. Co-Authored-By: Claude Fable 5 --- .github/workflows/spec-create.yml | 2 +- app/src/app.tsx | 2 +- app/src/components/FeedbackWidget.tsx | 2 +- app/src/components/Footer.tsx | 5 +- app/src/components/HeroSection.tsx | 11 +-- app/src/components/MastheadRule.tsx | 4 +- app/src/components/NavBar.tsx | 7 +- app/src/components/PlotOfTheDay.tsx | 2 +- app/src/components/PlotOfTheDayTerminal.tsx | 2 +- app/src/components/RelatedSpecs.tsx | 2 +- app/src/components/RootLayout.tsx | 3 +- app/src/components/RouteErrorBoundary.tsx | 3 +- app/src/components/ScienceNote.tsx | 3 +- app/src/components/SpecOverview.tsx | 2 +- app/src/components/SpecTabs.tsx | 4 +- app/src/components/ToolbarActions.tsx | 3 +- app/src/hooks/useAnalytics.ts | 2 +- app/src/pages/AboutPage.tsx | 7 +- app/src/pages/DebugPage.tsx | 2 +- app/src/pages/LandingPage.tsx | 8 +-- app/src/pages/LibrariesPage.tsx | 5 +- app/src/pages/MapPage.tsx | 2 +- app/src/pages/NotFoundPage.tsx | 3 +- app/src/pages/PalettePage.tsx | 3 +- app/src/pages/PlotsPage.tsx | 2 +- app/src/pages/SpecPage.tsx | 4 +- app/src/pages/SpecsListPage.tsx | 4 +- app/src/pages/StatsPage.tsx | 6 +- app/src/{router.tsx => routes/index.tsx} | 0 app/src/routes/paths.test.ts | 80 +++++++++++++++++++++ app/src/{utils => routes}/paths.ts | 22 ++++++ app/vitest.config.ts | 1 + docs/reference/seo.md | 4 +- 33 files changed, 165 insertions(+), 47 deletions(-) rename app/src/{router.tsx => routes/index.tsx} (100%) create mode 100644 app/src/routes/paths.test.ts rename app/src/{utils => routes}/paths.ts (66%) diff --git a/.github/workflows/spec-create.yml b/.github/workflows/spec-create.yml index 8a9d11055b..77804d4488 100644 --- a/.github/workflows/spec-create.yml +++ b/.github/workflows/spec-create.yml @@ -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 diff --git a/app/src/app.tsx b/app/src/app.tsx index f2fc46313a..0c4bb9c84f 100644 --- a/app/src/app.tsx +++ b/app/src/app.tsx @@ -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() { diff --git a/app/src/components/FeedbackWidget.tsx b/app/src/components/FeedbackWidget.tsx index f093cf6c27..e21ec8b249 100644 --- a/app/src/components/FeedbackWidget.tsx +++ b/app/src/components/FeedbackWidget.tsx @@ -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'; diff --git a/app/src/components/Footer.tsx b/app/src/components/Footer.tsx index 12397d6eba..0f5d0ceba0 100644 --- a/app/src/components/Footer.tsx +++ b/app/src/components/Footer.tsx @@ -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 { @@ -92,11 +93,11 @@ export function Footer({ onTrackEvent, selectedSpec, selectedLibrary }: FooterPr · - + about · - + legal diff --git a/app/src/components/HeroSection.tsx b/app/src/components/HeroSection.tsx index 2585bd9074..c3a185de08 100644 --- a/app/src/components/HeroSection.tsx +++ b/app/src/components/HeroSection.tsx @@ -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 { @@ -170,11 +171,13 @@ export function HeroSection({ potd = null }: HeroSectionProps) { }} > trackEvent('nav_click', { source: 'hero_cta_browse', target: '/plots' })} + onClick={() => + trackEvent('nav_click', { source: 'hero_cta_browse', target: paths.plots }) + } />