Add routing

This commit is contained in:
Peter Stockings
2026-01-10 10:33:43 +11:00
parent 0c53496e9b
commit eb78a1b098
5 changed files with 35 additions and 29 deletions

View File

@@ -7,6 +7,7 @@
"dependencies": {
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.12.0",
},
"devDependencies": {
"@eslint/js": "^9.39.1",
@@ -275,6 +276,8 @@
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
"cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
@@ -415,6 +418,10 @@
"react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="],
"react-router": ["react-router@7.12.0", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-kTPDYPFzDVGIIGNLS5VJykK0HfHLY5MF3b+xj0/tTyNYL1gF1qs7u67Z9jEhQk2sQ98SUaHxlG31g1JtF7IfVw=="],
"react-router-dom": ["react-router-dom@7.12.0", "", { "dependencies": { "react-router": "7.12.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-pfO9fiBcpEfX4Tx+iTYKDtPbrSLLCbwJ5EqP+SPYQu1VYCXdy79GSj0wttR0U4cikVdlImZuEZ/9ZNCgoaxwBA=="],
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
"rollup": ["rollup@4.55.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.55.1", "@rollup/rollup-android-arm64": "4.55.1", "@rollup/rollup-darwin-arm64": "4.55.1", "@rollup/rollup-darwin-x64": "4.55.1", "@rollup/rollup-freebsd-arm64": "4.55.1", "@rollup/rollup-freebsd-x64": "4.55.1", "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", "@rollup/rollup-linux-arm-musleabihf": "4.55.1", "@rollup/rollup-linux-arm64-gnu": "4.55.1", "@rollup/rollup-linux-arm64-musl": "4.55.1", "@rollup/rollup-linux-loong64-gnu": "4.55.1", "@rollup/rollup-linux-loong64-musl": "4.55.1", "@rollup/rollup-linux-ppc64-gnu": "4.55.1", "@rollup/rollup-linux-ppc64-musl": "4.55.1", "@rollup/rollup-linux-riscv64-gnu": "4.55.1", "@rollup/rollup-linux-riscv64-musl": "4.55.1", "@rollup/rollup-linux-s390x-gnu": "4.55.1", "@rollup/rollup-linux-x64-gnu": "4.55.1", "@rollup/rollup-linux-x64-musl": "4.55.1", "@rollup/rollup-openbsd-x64": "4.55.1", "@rollup/rollup-openharmony-arm64": "4.55.1", "@rollup/rollup-win32-arm64-msvc": "4.55.1", "@rollup/rollup-win32-ia32-msvc": "4.55.1", "@rollup/rollup-win32-x64-gnu": "4.55.1", "@rollup/rollup-win32-x64-msvc": "4.55.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A=="],
@@ -423,6 +430,8 @@
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],

View File

@@ -11,7 +11,8 @@
},
"dependencies": {
"react": "^19.2.0",
"react-dom": "^19.2.0"
"react-dom": "^19.2.0",
"react-router-dom": "^7.12.0"
},
"devDependencies": {
"@eslint/js": "^9.39.1",

View File

@@ -1,27 +1,21 @@
import { useState } from 'react';
import Sidebar, { type AppId } from './components/Sidebar';
import { Routes, Route, Navigate } from 'react-router-dom';
import Sidebar from './components/Sidebar';
import ImageApprox from './apps/ImageApprox/ImageApprox';
import SnakeAI from './apps/SnakeAI/SnakeAI';
import './App.css';
function App() {
const [currentApp, setCurrentApp] = useState<AppId>('image-approx');
const renderApp = () => {
switch (currentApp) {
case 'image-approx':
return <ImageApprox />;
case 'snake-ai':
return <SnakeAI />;
default:
return <div>App not found</div>;
}
};
return (
<div className="app-layout">
<Sidebar currentApp={currentApp} onAppChange={setCurrentApp} />
<main className="app-main">{renderApp()}</main>
<Sidebar />
<main className="app-main">
<Routes>
<Route path="/" element={<Navigate to="/image-approx" replace />} />
<Route path="/image-approx" element={<ImageApprox />} />
<Route path="/snake-ai" element={<SnakeAI />} />
<Route path="*" element={<div>App not found</div>} />
</Routes>
</main>
</div>
);
}

View File

@@ -1,9 +1,11 @@
import { NavLink } from 'react-router-dom';
import './Sidebar.css';
export type AppId = 'image-approx' | 'snake-ai';
export interface AppInfo {
id: AppId;
path: string;
name: string;
icon: string;
description: string;
@@ -12,24 +14,21 @@ export interface AppInfo {
export const APPS: AppInfo[] = [
{
id: 'image-approx',
path: '/image-approx',
name: 'Image Approximation',
icon: '🎨',
description: 'Evolve triangles to approximate images',
},
{
id: 'snake-ai',
path: '/snake-ai',
name: 'Neural Network Snake',
icon: '🐍',
description: 'Evolve neural networks to play Snake',
},
];
interface SidebarProps {
currentApp: AppId;
onAppChange: (appId: AppId) => void;
}
export default function Sidebar({ currentApp, onAppChange }: SidebarProps) {
export default function Sidebar() {
return (
<aside className="sidebar">
<div className="sidebar-header">
@@ -39,15 +38,15 @@ export default function Sidebar({ currentApp, onAppChange }: SidebarProps) {
<nav className="sidebar-nav">
{APPS.map((app) => (
<button
<NavLink
key={app.id}
className={`nav-item ${currentApp === app.id ? 'active' : ''}`}
onClick={() => onAppChange(app.id)}
to={app.path}
className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}
title={app.description}
>
<span className="nav-icon">{app.icon}</span>
<span className="nav-name">{app.name}</span>
</button>
</NavLink>
))}
</nav>

View File

@@ -1,10 +1,13 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>,
)