Cleaned up repo with old files, and ammended logo size in sidebar/mobile overview
This commit is contained in:
@@ -1,75 +1,84 @@
|
||||
# Andy Charlwood - Interactive CV
|
||||
|
||||
A distinctive interactive portfolio website featuring a three-phase cinematic loading experience: terminal boot sequence → ECG heartbeat animation → main content. Built with React, TypeScript, and Vite.
|
||||
An interactive portfolio styled as a PMR (patient medical record) system — the kind of GP clinical interface NHS staff use daily. Features a cinematic boot sequence, D3 career constellation, semantic search, and an LLM-powered chat widget. Built with React, TypeScript, and Vite.
|
||||
|
||||
## Features
|
||||
|
||||
- **Three-Phase Loading Experience**: Terminal boot (~4s) → ECG animation (~5-6s) → content reveal
|
||||
- **Interactive Sections**: Hero, Skills, Experience, Education, Projects, Contact
|
||||
- **Smooth Animations**: Framer Motion for scroll reveals and staggered transitions
|
||||
- **SVG Skill Visualization**: Circular progress indicators for skill levels
|
||||
- **Floating Navigation**: Active section tracking as you scroll
|
||||
- **Responsive Design**: Tailwind CSS with custom breakpoints
|
||||
- **Four-Phase Loading**: Terminal boot → login screen → PMR dashboard (skippable; session-cached for returning visitors)
|
||||
- **Career Constellation**: D3 force simulation mapping roles as clusters and skills as nodes — interactive via hover, click, tap, and keyboard
|
||||
- **Semantic Search**: Pre-computed embeddings + local Xenova transformer model running in-browser
|
||||
- **Command Palette**: `Ctrl+K` hybrid search (Fuse.js fuzzy + semantic)
|
||||
- **Chat Widget**: Gemini/OpenRouter LLM integration for conversational Q&A about career history
|
||||
- **Detail Panel**: Context-aware slide-out panel for deep-diving into any entity
|
||||
- **Responsive Design**: Tailwind CSS with mobile-specific navigation and layout
|
||||
- **Accessibility**: Focus management, reduced motion support, ARIA throughout
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- **Framework**: React 18 + TypeScript
|
||||
- **Build Tool**: Vite 6
|
||||
- **Styling**: Tailwind CSS 3
|
||||
- **Animations**: Framer Motion + Canvas API
|
||||
- **Icons**: Lucide React
|
||||
- **Linting**: ESLint 9
|
||||
| Category | Technologies |
|
||||
|----------|-------------|
|
||||
| **Framework** | React 18 + TypeScript (strict mode) |
|
||||
| **Build** | Vite 6 |
|
||||
| **Styling** | Tailwind CSS 3 + CSS custom properties |
|
||||
| **Animations** | Framer Motion + Canvas API |
|
||||
| **Visualisation** | D3 v7 (force simulation) |
|
||||
| **Search** | Fuse.js (fuzzy) + @xenova/transformers (semantic) |
|
||||
| **Backend** | Express + Nodemailer (contact form, chat proxy) |
|
||||
| **UI** | Lucide React, Embla Carousel, react-markdown |
|
||||
| **Linting** | ESLint 9 |
|
||||
|
||||
## Getting Started
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
# Start development server
|
||||
npm run dev
|
||||
|
||||
# Build for production
|
||||
npm run build
|
||||
|
||||
# Preview production build
|
||||
npm run preview
|
||||
npm run dev # Starts Vite + Express backend concurrently
|
||||
```
|
||||
|
||||
The chat widget and contact form require API keys in a `.env` file — see `.env.example` if available.
|
||||
|
||||
## Available Scripts
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `npm run dev` | Start dev server (localhost:5173) |
|
||||
| `npm run dev` | Vite dev server + Express backend (concurrently) |
|
||||
| `npm run dev:frontend` | Vite only (no backend) |
|
||||
| `npm run build` | TypeScript compile + Vite production build |
|
||||
| `npm run start` | Run production server |
|
||||
| `npm run typecheck` | TypeScript type checking only |
|
||||
| `npm run lint` | Run ESLint |
|
||||
| `npm run preview` | Preview production build |
|
||||
| `npm run generate-embeddings` | Regenerate semantic search embeddings |
|
||||
| `npm run benchmark` | Run performance benchmarks |
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── components/ # React components (PascalCase)
|
||||
├── hooks/ # Custom hooks (use* prefix)
|
||||
├── lib/ # Utility functions
|
||||
├── types/ # TypeScript interfaces
|
||||
├── App.tsx # Phase manager (root component)
|
||||
└── index.css # Global styles + Tailwind
|
||||
├── components/ # React components (PascalCase)
|
||||
│ ├── constellation/ # D3 career constellation + legend
|
||||
│ ├── detail/ # Detail panel views per entity type
|
||||
│ └── tiles/ # Dashboard tile components
|
||||
├── contexts/ # React contexts (DetailPanel, Accessibility)
|
||||
├── data/ # Canonical data sources (timeline, skills, kpis, etc.)
|
||||
├── hooks/ # Custom hooks (use* prefix)
|
||||
├── lib/ # Utilities (semantic-search, embedding-model, llm)
|
||||
├── types/ # TypeScript interfaces (pmr.ts)
|
||||
├── App.tsx # Phase orchestrator (boot → login → dashboard)
|
||||
└── index.css # Global styles + Tailwind
|
||||
```
|
||||
|
||||
## Editing Profile Copy In One Place
|
||||
### Data architecture
|
||||
|
||||
- Canonical shared descriptive/profile text lives in `src/data/profile-content.ts`.
|
||||
- Typed selectors for all consumers live in `src/lib/profile-content.ts`.
|
||||
- Rule of thumb: if copy is shared across UI/search/chat/timeline surfaces, edit it once in `src/data/profile-content.ts` and let consumers read it via selectors.
|
||||
- **Canonical source**: `src/data/timeline.ts` — all career and education entities
|
||||
- **Derived**: `constellation.ts` (D3 graph), `tags.ts` (from skills), `kpis.ts` (standalone)
|
||||
- **Profile copy**: `src/data/profile-content.ts` with typed selectors in `src/lib/profile-content.ts`
|
||||
|
||||
## Design Tokens
|
||||
|
||||
- **Primary**: Teal `#00897B`
|
||||
- **Accent**: Coral `#FF6B6B`
|
||||
- **Fonts**: Plus Jakarta Sans (primary), Inter Tight (secondary), Fira Code (mono)
|
||||
- **Breakpoints**: xs 480px, sm 640px, md 768px, lg 1024px, xl 1280px
|
||||
- **Primary**: Teal `#00897B` / **Accent**: Coral `#FF6B6B`
|
||||
- **Palette**: GP system-inspired greens, teals, and greys
|
||||
- **Fonts**: Elvaro Grotesque (UI), Geist Mono / Fira Code (mono), Plus Jakarta Sans / Inter Tight (fallback)
|
||||
- **Breakpoints**: xxs 360px, xs 480px, sm 640px, md 768px, lg 1024px, xl 1280px
|
||||
|
||||
## License
|
||||
|
||||
|
||||
Reference in New Issue
Block a user