diff --git a/CLAUDE.md b/CLAUDE.md index 1f17204..ae54bd2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -64,7 +64,7 @@ Simple React Router setup in App.tsx: - `/assets/projects/advanced-retrieval` - AdvancedRetrieval - `*` - NotFoundPage (404) -No lazy loading is used - all components are directly imported. +HomePage and NotFoundPage are imported directly; every other route is loaded with `React.lazy` inside one `Suspense` boundary (fallback keeps the Layout visible). This keeps ECharts, Cytoscape and Leaflet out of the homepage bundle - add new project routes the same way. ### Project Data Management @@ -79,7 +79,7 @@ The ProjectCard component is also defined inline in ProjectsPage.tsx (lines 15-1 ### Layout Component The Layout component (`src/components/layout/Layout.tsx`) wraps all pages and provides: -- Navigation bar with links to sections (About, Expertise, Publications, Projects, Concepts, Teaching, Certifications, Contact, Graph Demos) +- Navigation bar with links to sections (About, Expertise, Publications, Projects, Teaching, Certifications, Contact, Graph Demos) - Active link highlighting using hash-based navigation for sections and pathname for Projects - Footer with social links (GitHub, LinkedIn, Graph Visualizations) @@ -151,5 +151,6 @@ Located in `src/data/`: - No testing framework is currently configured - No formatter (like Prettier) is configured in package.json -- Project uses direct imports (no code splitting/lazy loading) +- Routes other than Home and 404 are code-split with `React.lazy` +- Each page renders its own `` inside `<Layout>` (React 19 hoists it into `<head>`); format: `Page Name | Don Branson` - Some project components include their own types (e.g., situational-awareness/types.ts) diff --git a/src/App.tsx b/src/App.tsx index 95bb349..9804632 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,39 +1,51 @@ -import React from 'react'; +import React, { lazy, Suspense } from 'react'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import Layout from '@/components/layout/Layout'; -// Page Components +// Page Components (HomePage and NotFoundPage are small, so they stay in the main bundle) import HomePage from '@/pages/HomePage'; -import ProjectsPage from '@/pages/ProjectsPage'; -import ConceptsPage from '@/pages/ConceptsPage'; import NotFoundPage from '@/pages/NotFoundPage'; +// Lazy-loaded routes: keeps ECharts, Cytoscape and Leaflet out of the homepage bundle +const ProjectsPage = lazy(() => import('@/pages/ProjectsPage')); + // Project Components -import GdeltRecordViewer from '@/components/projects/gdelt/GdeltRecordViewer'; // Add .jsx extension -import CytoscapeViewer from '@/components/projects/cytoscape/CytoscapeViewer'; -import EventAnalyzer from '@/components/projects/event-analyzer/EventAnalyzer'; -import SituationalAwareness from '@/components/projects/situational-awareness/SituationalAwareness'; -import GdeltKnowledgeBase from '@/components/projects/gdelt-kb/GdeltKnowledgeBase'; -import AdvancedRetrieval from '@/components/projects/advanced-retrieval/AdvancedRetrieval'; +const GdeltRecordViewer = lazy(() => import('@/components/projects/gdelt/GdeltRecordViewer')); +const CytoscapeViewer = lazy(() => import('@/components/projects/cytoscape/CytoscapeViewer')); +const EventAnalyzer = lazy(() => import('@/components/projects/event-analyzer/EventAnalyzer')); +const SituationalAwareness = lazy(() => import('@/components/projects/situational-awareness/SituationalAwareness')); +const GdeltKnowledgeBase = lazy(() => import('@/components/projects/gdelt-kb/GdeltKnowledgeBase')); +const AdvancedRetrieval = lazy(() => import('@/components/projects/advanced-retrieval/AdvancedRetrieval')); + +// Shown while a lazy route's chunk loads; keeps the nav and footer in place +const RouteFallback: React.FC = () => ( + <Layout> + <div className="max-w-6xl mx-auto px-4 py-16 text-gray-500" role="status"> + Loading… + </div> + </Layout> +); const App: React.FC = () => { return ( <BrowserRouter> - <Routes> - <Route path="/" element={<HomePage />} /> - <Route path="/assets/projects" element={<ProjectsPage />} /> - <Route path="/concepts" element={<ConceptsPage />} /> - - {/* Project routes */} - <Route path="/assets/projects/gdelt" element={<GdeltRecordViewer />} /> - <Route path="/assets/projects/cytoscape" element={<CytoscapeViewer />} /> - <Route path="/assets/projects/event-analyzer" element={<EventAnalyzer />} /> - <Route path="/assets/projects/situational-awareness" element={<SituationalAwareness />} /> - <Route path="/assets/projects/gdelt-knowledge-base" element={<GdeltKnowledgeBase />} /> - <Route path="/assets/projects/advanced-retrieval" element={<AdvancedRetrieval />} /> - - {/* Catch-all route for 404s */} - <Route path="*" element={<NotFoundPage />} /> - </Routes> + <Suspense fallback={<RouteFallback />}> + <Routes> + <Route path="/" element={<HomePage />} /> + <Route path="/assets/projects" element={<ProjectsPage />} /> + + {/* Project routes */} + <Route path="/assets/projects/gdelt" element={<GdeltRecordViewer />} /> + <Route path="/assets/projects/cytoscape" element={<CytoscapeViewer />} /> + <Route path="/assets/projects/event-analyzer" element={<EventAnalyzer />} /> + <Route path="/assets/projects/situational-awareness" element={<SituationalAwareness />} /> + <Route path="/assets/projects/gdelt-knowledge-base" element={<GdeltKnowledgeBase />} /> + <Route path="/assets/projects/advanced-retrieval" element={<AdvancedRetrieval />} /> + + {/* Catch-all route for 404s */} + <Route path="*" element={<NotFoundPage />} /> + </Routes> + </Suspense> </BrowserRouter> ); }; diff --git a/src/components/projects/advanced-retrieval/AdvancedRetrieval.tsx b/src/components/projects/advanced-retrieval/AdvancedRetrieval.tsx index d5916c9..20c65cf 100644 --- a/src/components/projects/advanced-retrieval/AdvancedRetrieval.tsx +++ b/src/components/projects/advanced-retrieval/AdvancedRetrieval.tsx @@ -5,6 +5,7 @@ import { Github, BookOpen } from 'lucide-react'; const AdvancedRetrieval: React.FC = () => { return ( <Layout> + <title>Advanced Retrieval Strategies for RAG | Don Branson {/* Hero Section */}
diff --git a/src/components/projects/cytoscape/CytoscapeViewer.tsx b/src/components/projects/cytoscape/CytoscapeViewer.tsx index a3aa5ed..48bf76a 100644 --- a/src/components/projects/cytoscape/CytoscapeViewer.tsx +++ b/src/components/projects/cytoscape/CytoscapeViewer.tsx @@ -205,6 +205,7 @@ const CytoscapeViewer: React.FC = () => { return ( + STRING Network Viewer | Don Branson

STRING Network Viewer

diff --git a/src/components/projects/event-analyzer/EventAnalyzer.tsx b/src/components/projects/event-analyzer/EventAnalyzer.tsx index 1ac6f5c..3b35e42 100644 --- a/src/components/projects/event-analyzer/EventAnalyzer.tsx +++ b/src/components/projects/event-analyzer/EventAnalyzer.tsx @@ -235,6 +235,7 @@ const EventAnalyzer: React.FC = () => { return ( + Event Analysis System | Don Branson

Event Analysis System

diff --git a/src/components/projects/gdelt-kb/GdeltKnowledgeBase.tsx b/src/components/projects/gdelt-kb/GdeltKnowledgeBase.tsx index 44e6367..912532a 100644 --- a/src/components/projects/gdelt-kb/GdeltKnowledgeBase.tsx +++ b/src/components/projects/gdelt-kb/GdeltKnowledgeBase.tsx @@ -5,6 +5,7 @@ import { ExternalLink, Github, Database } from 'lucide-react'; const GdeltKnowledgeBase: React.FC = () => { return ( + GDELT Knowledge Base | Don Branson {/* Hero Section */}

diff --git a/src/components/projects/gdelt/GdeltRecordViewer.tsx b/src/components/projects/gdelt/GdeltRecordViewer.tsx index 6779918..617da77 100644 --- a/src/components/projects/gdelt/GdeltRecordViewer.tsx +++ b/src/components/projects/gdelt/GdeltRecordViewer.tsx @@ -355,6 +355,7 @@ const GdeltRecordViewer: React.FC = () => { return ( + GDELT GKG Viewer | Don Branson

GDELT GKG Viewer

diff --git a/src/components/projects/situational-awareness/SituationalAwareness.tsx b/src/components/projects/situational-awareness/SituationalAwareness.tsx index 639de5f..df22c7f 100644 --- a/src/components/projects/situational-awareness/SituationalAwareness.tsx +++ b/src/components/projects/situational-awareness/SituationalAwareness.tsx @@ -106,6 +106,7 @@ const SituationalAwareness: React.FC = () => { return ( + Situational Awareness Graph | Don Branson

Situational Awareness Graph

diff --git a/src/pages/ConceptsPage.tsx b/src/pages/ConceptsPage.tsx deleted file mode 100644 index 822051c..0000000 --- a/src/pages/ConceptsPage.tsx +++ /dev/null @@ -1,348 +0,0 @@ -import React from 'react'; -import Layout from '@/components/layout/Layout'; - -const ConceptsPage: React.FC = () => { - return ( - - {/* Hero Section */} -
-
-

- MCP Architecture: Concepts Before Code -

-

- Frame the problem first. Understand the trade-offs. Then write the code. -

-
-
- - {/* Introduction */} -
-
-
-

- A student once asked me: "Should I refactor my FastAPI RAG system to use FastMCP?" -

-

- My answer? "It depends. Let's talk about what problem you're actually solving." -

-
-

- This page isn't about code syntax. It's about architectural decisions β€” - when to use MCP tools vs resources, when FastAPI still wins, and how CQRS patterns help you think clearly about these choices. -

-
-
- - {/* MCP Decision Framework */} -
-
-

- The MCP Decision Framework -

- -
- {/* MCP Tools */} -
-

MCP Tools πŸ”§

-

Use when you need to change state

- -
-

Good For:

-
    -
  • β€’ Running searches (query changes each time)
  • -
  • β€’ Creating/updating records
  • -
  • β€’ Executing actions with side effects
  • -
  • β€’ Real-time operations that need parameters
  • -
-
- -
-

Ask yourself:

-

"Does this operation need unique input each time it runs?"

-

If yes β†’ Tool

-
-
- - {/* MCP Resources */} -
-

MCP Resources πŸ“š

-

Use when you need to read state

- -
-

Good For:

-
    -
  • β€’ Static or semi-static data
  • -
  • β€’ Configuration files
  • -
  • β€’ Documentation that doesn't change often
  • -
  • β€’ Cacheable content
  • -
-
- -
-

Ask yourself:

-

"Can this data be cached and reused?"

-

If yes β†’ Resource

-
-
-
- - {/* CQRS Connection */} -
-

The CQRS Connection

-

- If you're familiar with CQRS (Command Query Responsibility Segregation), this framework should feel natural: -

-
-
-

Commands (write)

-

Change state, have side effects

-

β†’ MCP Tools

-
-
-

Queries (read)

-

Read state, no side effects

-

β†’ MCP Resources

-
-
-
-
-
- - {/* FastAPI vs FastMCP */} -
-
-

- FastAPI β†’ FastMCP: The Migration Pattern -

- -
-

- The question isn't "Should I migrate?" -

-

- The question is: "What integration point matters most to me?" -

-
- -
- {/* FastAPI Strengths */} -
-

FastAPI Strengths πŸš€

-
    -
  • - βœ“ - Simple setup β€” pip install, run server, done -
  • -
  • - βœ“ - HTTP everywhere β€” works with any client -
  • -
  • - βœ“ - Mature ecosystem β€” tons of middleware, auth, etc. -
  • -
  • - βœ“ - Flexibility β€” full control over implementation -
  • -
- -
-

Choose FastAPI when:

-

- You need a general-purpose API that non-AI clients will also use, or when you need full control over auth, rate limiting, and middleware. -

-
-
- - {/* FastMCP Strengths */} -
-

FastMCP Strengths ⚑

-
    -
  • - βœ“ - Native IDE integration β€” Claude Desktop, Cursor, Zed -
  • -
  • - βœ“ - Resource caching β€” built into the protocol -
  • -
  • - βœ“ - Standard discovery β€” servers are auto-discoverable -
  • -
  • - βœ“ - Context-aware β€” designed for AI workflows -
  • -
- -
-

Choose FastMCP when:

-

- Your primary users are AI applications/IDEs, you want standardized tool/resource patterns, or you're building for the MCP ecosystem. -

-
-
-
- - {/* The Hybrid Approach */} -
-

πŸ’‘ The Hybrid Approach

-

- You don't have to choose! Many production systems run both: -

-
    -
  • β€’ FastAPI for your main application and web clients
  • -
  • β€’ FastMCP server for IDE/AI integration
  • -
  • β€’ Both calling the same business logic layer
  • -
-

- This is often the right answer for production systems that need broad compatibility. -

-
-
-
- - {/* Real-World Scenarios */} -
-
-

- Real-World Decision Scenarios -

- -
- {/* Scenario 1 */} -
-

- Scenario 1: RAG System with Vector Search -

-

- "I have a RAG system where users search documentation. Should the search endpoint be FastAPI or FastMCP?" -

-
-

Recommendation: FastMCP Tool

-

- Why: Each search query is unique (dynamic input), and if you're using Claude/IDEs, - MCP gives native integration. The search results aren't cacheable since the query changes. -

-

- But consider FastAPI if: You need the API for a web frontend too, or you have complex authentication needs. -

-
-
- - {/* Scenario 2 */} -
-

- Scenario 2: LangSmith Trace Analysis -

-

- "I want to query my LangSmith traces from Claude Desktop. Tool or Resource?" -

-
-

Recommendation: MCP Resource

-

- Why: Traces are historical data (read-only), they don't change frequently, - and caching can significantly improve performance. Resources are perfect for this. -

-

- Use a Tool for: Creating new traces, updating metadata, or running analytics that need parameters. -

-
-
- - {/* Scenario 3 */} -
-

- Scenario 3: Multi-Agent Orchestration -

-

- "I'm building a multi-agent system with LangGraph. How do I expose agent capabilities?" -

-
-

Recommendation: Hybrid Approach

-

- MCP Tools for invoking agents (dynamic, stateful operations) -

-

- MCP Resources for agent configurations, schemas, available tools -

-

- Consider FastAPI if you need a REST API for monitoring dashboards or external integrations -

-
-
-
-
-
- - {/* Code Examples Link */} -
-
-

- Ready to See the Code? -

-

- Now that you understand the concepts and trade-offs, check out working implementations: -

- -
-
- - {/* Questions Section */} -
-
-

- Still Have Questions? -

-
-

- These architectural decisions depend heavily on your specific use case. If you're a student in one of my bootcamp cohorts, - bring your scenario to office hours. If not, feel free to reach out: -

- -
-
-
-
- ); -}; - -export default ConceptsPage; diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index a07b56b..cbac4b3 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -24,6 +24,7 @@ const HomePage: React.FC = () => { return ( + Don Branson | Generative AI Architect & Systems Engineer {/* Hero Section */}
diff --git a/src/pages/NotFoundPage.tsx b/src/pages/NotFoundPage.tsx index e17c7e0..85a6f18 100644 --- a/src/pages/NotFoundPage.tsx +++ b/src/pages/NotFoundPage.tsx @@ -5,6 +5,7 @@ import Layout from '@/components/layout/Layout'; const NotFoundPage: React.FC = () => { return ( + Page Not Found | Don Branson

404 - Page Not Found

The page you are looking for doesn't exist or has been moved.

diff --git a/src/pages/ProjectsPage.tsx b/src/pages/ProjectsPage.tsx index f7f0e76..e0721f8 100644 --- a/src/pages/ProjectsPage.tsx +++ b/src/pages/ProjectsPage.tsx @@ -243,6 +243,7 @@ const ProjectsPage: React.FC = () => { return ( + Projects | Don Branson