Technical documentation for LENR Academy developers and contributors.
- Architecture Overview
- Technology Stack
- Project Structure
- Database Architecture
- State Management
- Deployment
LENR Academy uses a three-layer architecture designed for client-side operation with a large SQLite database.
graph TB
UI["UI Layer (React)<br/>Components, Pages, Routes, Themes"]
State["State Layer (Context)<br/>DatabaseContext, ThemeContext"]
Data["Data Layer (Services)<br/>database.ts, dbCache.ts, queryService"]
UI --> State
State --> Data
style UI fill:#4a90e2,stroke:#2c5aa0,stroke-width:2px,color:#fff
style State fill:#f5a623,stroke:#c77d0a,stroke-width:2px,color:#fff
style Data fill:#7b68ee,stroke:#5a4bb5,stroke-width:2px,color:#fff
database.ts: sql.js initialization with streaming download and progress trackingdbCache.ts: IndexedDB caching with version management and persistent storagequeryService.ts: SQL query builders for fusion, fission, two-to-two, and nuclide queries
DatabaseContext.tsx: Global database state with metered connection detectionThemeContext.tsx: Dark/light theme with localStorage persistence
- Query pages: FusionQuery, FissionQuery, TwoToTwoQuery
- Data viewers: ShowElementData, TablesInDetail, AllTables
- Shared components: PeriodicTable, PeriodicTableSelector, ElementDetailsCard, NuclideDetailsCard
| Technology | Version | Purpose |
|---|---|---|
| React | 18.3 | UI framework |
| TypeScript | 5.5+ | Type-safe JavaScript |
| Vite | 5.4+ | Build tool and dev server |
| TailwindCSS | 3.4+ | Utility-first CSS |
| React Router | 6.26+ | Client-side routing |
| Technology | Version | Purpose |
|---|---|---|
| sql.js | 1.11+ | SQLite compiled to WebAssembly |
| IndexedDB | Native | Browser database caching |
| Technology | Version | Purpose |
|---|---|---|
| Sentry | 10.19+ | Privacy-focused error tracking (GDPR-compliant, EU hosting) |
| Tool | Purpose |
|---|---|
| ESLint | Code linting |
| Playwright | E2E testing and screenshot automation |
| TypeScript Compiler | Type checking |
{
"react": "^18.3.1",
"react-router-dom": "^6.26.2",
"sql.js": "^1.11.0",
"lucide-react": "^0.445.0"
}lenr.academy/
├── src/
│ ├── components/ # Reusable UI components
│ │ ├── Layout.tsx # Main app layout with sidebar
│ │ ├── PeriodicTable.tsx # Standalone periodic table (ShowElementData)
│ │ ├── PeriodicTableSelector.tsx # Multi-select dropdown with PT
│ │ ├── ElementDetailsCard.tsx # Element properties display
│ │ ├── NuclideDetailsCard.tsx # Nuclide/isotope details
│ │ ├── DatabaseLoadingCard.tsx # Download progress UI
│ │ ├── DatabaseUpdateBanner.tsx # Version update notifications
│ │ └── PrivacyBanner.tsx # Analytics consent banner
│ ├── pages/ # Route pages (one per navigation item)
│ │ ├── Home.tsx # Landing page
│ │ ├── FusionQuery.tsx # A + B → C reactions
│ │ ├── FissionQuery.tsx # A → B + C reactions
│ │ ├── TwoToTwoQuery.tsx # A + B → C + D reactions
│ │ ├── ShowElementData.tsx # Periodic table & element viewer
│ │ ├── TablesInDetail.tsx # Database schema browser
│ │ ├── AllTables.tsx # Advanced SQL query builder
│ │ └── CascadesAll.tsx # Cascade simulations
│ ├── services/ # Data layer
│ │ ├── database.ts # sql.js init with streaming download
│ │ ├── dbCache.ts # IndexedDB caching + version mgmt
│ │ ├── queryService.ts # SQL query execution engine
│ │ └── mockData.ts # Sample data for development
│ ├── contexts/ # React Context providers
│ │ ├── DatabaseContext.tsx # Global DB state
│ │ └── ThemeContext.tsx # Theme (dark/light) mgmt
│ ├── types/ # TypeScript type definitions
│ │ └── index.ts # All app types
│ ├── utils/ # Utility functions
│ │ └── version.ts # Version parsing utilities
│ ├── App.tsx # Root component with routing
│ ├── main.tsx # Application entry point
│ ├── index.css # Global styles with theme support
│ └── vite-env.d.ts # Vite environment type definitions
├── public/
│ ├── parkhomov.db # SQLite database (161MB, gitignored)
│ ├── parkhomov.db.meta.json # Version metadata for updates
│ └── sql-wasm.wasm # sql.js WebAssembly binary
├── e2e/ # Playwright E2E tests
│ ├── tests/ # Test files
│ │ ├── database.spec.ts # Database loading tests
│ │ ├── navigation.spec.ts # Navigation tests
│ │ └── version-display.spec.ts # Version display tests
│ └── fixtures/ # Test fixtures
├── scripts/
│ ├── generate-screenshots.ts # Automated screenshot generation
│ ├── rebuild_db.py # Database rebuild script
│ └── verify_db.py # Database verification
├── docs/ # Documentation
│ ├── DEVELOPMENT.md # This file
│ └── screenshots/ # Auto-generated screenshots
├── .github/
│ └── workflows/ # GitHub Actions CI/CD
└── Configuration files
├── vite.config.ts # Vite configuration
├── tailwind.config.js # TailwindCSS configuration
├── tsconfig.json # TypeScript configuration
├── playwright.config.ts # Playwright configuration
└── package.json # npm dependencies and scripts
Query Pages follow a consistent pattern:
- State management (selected elements, filters, results)
- Database query execution via
queryService - Results display with tables and detail cards
- CSV export functionality
Reusable Components:
- Use TypeScript interfaces for props
- Support dark mode via Tailwind
dark:classes - Responsive design with mobile-first approach
- Minimal external dependencies
The application uses sql.js (SQLite compiled to WebAssembly) to run queries entirely in the browser. The 161MB database (parkhomov.db) contains Dr. Alexander Parkhomov's nuclear reaction tables.
flowchart TD
Start([First Page Load])
CheckCache{Check IndexedDB<br/>for cached DB}
Found{Found?}
CheckMetered[Check metered<br/>connection]
ShowWarning[Show warning<br/>if metered]
Download[Download parkhomov.db<br/>with progress tracking]
StoreCache[Store in IndexedDB]
InitSQL[Initialize sql.js<br/>with database bytes]
Ready([App ready for queries])
Start --> CheckCache
CheckCache --> Found
Found -->|Yes| InitSQL
Found -->|No| CheckMetered
CheckMetered --> ShowWarning
ShowWarning --> Download
Download --> StoreCache
StoreCache --> InitSQL
InitSQL --> Ready
style Start fill:#50c878,stroke:#2d7a4d,stroke-width:2px,color:#fff
style Ready fill:#50c878,stroke:#2d7a4d,stroke-width:2px,color:#fff
style Found fill:#f5a623,stroke:#c77d0a,stroke-width:2px,color:#fff
style Download fill:#4a90e2,stroke:#2c5aa0,stroke-width:2px,color:#fff
style InitSQL fill:#7b68ee,stroke:#5a4bb5,stroke-width:2px,color:#fff
| Table | Rows | Description |
|---|---|---|
| TwoToTwoAll | 516,789 | A + B → C + D transmutation reactions (includes neutrino-involved) |
| NuclidesPlus | ~3,000 | Nuclide properties (Z, A, binding energy, half-life, boson/fermion flags) |
| FusionAll | 1,389 | A + B → C fusion reactions (includes neutrino-involved) |
| FissionAll | 817 | A → B + C fission reactions (includes neutrino-involved) |
| ElementPropertiesPlus | 118 | Chemical element properties (melting point, density, electronegativity) |
The database uses abbreviated column names:
Element table:
P→ PeriodG→ GroupMolarVol→ MolarVolumeVal→ ValenceElectG→ ElectConductThermG→ ThermConduct
Nuclide table:
Z→ Atomic numberA→ Mass numberBE/A→ Binding energy per nucleonLHL→ Log₁₀ of half-life in yearsnBorF→ Nuclear boson/fermion (based on A)aBorF→ Atomic boson/fermion (based on A-Z)
A nuclide is considered "stable" if LHL > 9 (half-life > 1 billion years).
LENR Academy uses React Context for global state instead of Redux or other state management libraries.
Purpose: Manage database loading state and provide query access
State:
interface DatabaseContextType {
db: Database | null // sql.js database instance
isLoading: boolean // Loading state
progress: number // Download progress (0-100)
error: string | null // Error message
showMeteredWarning: boolean // Metered connection warning
acceptMeteredDownload: () => void // User accepts download
}Usage:
const { db, isLoading } = useDatabase()
if (!db) return <DatabaseLoadingCard />
const results = queryFusionReactions(db, filters)Purpose: Manage dark/light theme preference
State:
interface ThemeContextType {
theme: 'light' | 'dark'
toggleTheme: () => void
}Persistence: Theme preference saved to localStorage as 'theme' key
The ShowElementData page uses useSearchParams from React Router for deep linking:
// URL: /element-data?Z=26&A=56
const [searchParams, setSearchParams] = useSearchParams()
const Z = searchParams.get('Z') // "26"
const A = searchParams.get('A') // "56"This enables:
- Shareable URLs for specific elements/isotopes
- Browser back/forward navigation
- Bookmarking specific views
The application is deployed to AWS S3 with CloudFront CDN.
npm run deploy # Full deployment (S3 sync + cache invalidation)
npm run deploy:s3 # Sync ./dist to S3 bucket
npm run deploy:uncache # Invalidate CloudFront cacheS3 Bucket:
- Bucket name:
lenr.academy - Static website hosting enabled
- Public read access (bucket policy)
CloudFront:
- Origin: S3 bucket website endpoint
- Custom domain:
lenr.academy - SSL certificate via ACM
- Default root object:
index.html - Error pages: Redirect 404 →
/index.html(SPA routing)
npm run buildOutputs to ./dist/:
index.html- Entry pointassets/- JS, CSS bundles (hashed filenames)parkhomov.db- Database fileparkhomov.db.meta.json- Version metadatasql-wasm.wasm- sql.js binary
Set in vite.config.ts via define:
define: {
'import.meta.env.VITE_APP_VERSION': JSON.stringify(getVersion()),
'import.meta.env.VITE_BUILD_TIME': JSON.stringify(getBuildTime()),
}Version String: Generated via git describe --tags --always --dirty
The application uses Umami Analytics (privacy-friendly, GDPR-compliant).
-
Create Umami Cloud Account: https://cloud.umami.is (free tier: 100k events/month)
-
Add Website:
- Domain:
lenr.academy - Get Website ID (UUID)
- Domain:
-
Update
index.html:<script defer src="https://cloud.umami.is/script.js" data-website-id="YOUR-WEBSITE-ID" ></script>
-
Deploy: Analytics will start tracking after deployment
Privacy Features:
- No cookies
- No personal data collection
- GDPR compliant
- User can opt out via PrivacyBanner
The application uses Sentry for privacy-focused error monitoring.
See detailed setup guide in docs/SENTRY_SETUP.md for complete instructions.
Quick Summary:
- Create Sentry account (EU region)
- Create React project
- Add GitHub secrets:
VITE_SENTRY_DSNSENTRY_ORGSENTRY_PROJECTSENTRY_AUTH_TOKEN
Privacy Features:
- No PII sent (
sendDefaultPii: false) - URL scrubbing (only safe query params kept)
- EU data hosting (GDPR-compliant)
- Source maps uploaded but not included in bundle
- Development mode disabled (console logging only)
Developer Guidelines: See .claude/sentry-guidelines.md for coding patterns and best practices.
Workflow: .github/workflows/e2e-main.yml
Runs on every push to main:
- Install dependencies
- Build application
- Start preview server
- Run Playwright E2E tests
- Upload test artifacts (traces, screenshots)
For contribution guidelines, see CONTRIBUTING.md.