This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Mathnotes is a static site generator that renders mathematics notes with interactive demonstrations into static HTML files served by nginx. The application features:
- Structured mathematical content with semantic markup (theorems, proofs, definitions)
- TypeScript interactive demos using p5.js for 2D/WebGL graphics and Plotly.js for 3D scientific visualizations
- Wiki-style cross-references using
[[slug]]syntax - Dark mode support with automatic detection
- Comprehensive security headers with Content Security Policy
- Modern CSS system with PostCSS, CSS custom properties, and hot module replacement
Demo Crawler Guidelines:
- do not every read the screenshots from the demo crawler directly, only use --ask
- Hey - you're not supposed to look at screenshots yourself. Only use --ask to do that.
- Don't check types after every single change
- For styling guidelines, see STYLE.md
- we never put styles directly in typescript
- You don't need to manually rebuild everytime we change code in dev mode, unless you're changing the docker stuff itself.
# Run all tests
./run_tests.sh# Development mode (for local testing only)
docker-compose -f docker-compose.dev.yml up# Generate static site (the ONLY deployment method)
docker-compose up --build
# Production Docker build process:
# 1. Generator crawls all markdown content
# 2. Jinja2 templates render each page to static HTML
# 3. All assets copied to static output directory
# 4. Sitemap.xml generated for SEO
# 5. nginx serves the static files# Test a specific page for JavaScript errors
./scripts/crawl-dev.sh --single-page "http://web-dev:5000/mathnotes/page-slug"
# Test framework changes by crawling entire site
./scripts/crawl-dev.sh "http://web-dev:5000"
# Check TypeScript type errors
npm run type-check
# IMPORTANT: Check for TypeScript build errors in Docker
# When demos don't appear or features don't work, ALWAYS check:
docker ps -a | grep static-builder # Check if container exited
docker logs mathnotes-static-builder # See TypeScript errors and build logs
# If the static-builder container has exited with errors:
# 1. Fix the TypeScript errors
# 2. Restart the builder: docker restart mathnotes-static-builder
# 3. Watch the logs: docker logs -f mathnotes-static-builderYou have to use the venv to run ./scripts/crawl-demos.py
# Capture screenshots of all demos (both desktop and mobile viewports)
./scripts/crawl-demos.py
# Capture a specific demo
./scripts/crawl-demos.py -d electric-field
./scripts/crawl-demos.py -d game-of-life
# Capture with specific viewport
./scripts/crawl-demos.py --viewport desktop # Desktop only
./scripts/crawl-demos.py --viewport mobile # Mobile onlyFor advanced demo testing commands (AI analysis, standards checking, scaling verification, etc.), see DEBUGGING.md.
# Move files
./scripts/move_file.sh old/path.md new/path.md- Core Generator (
generator/core.py): Manages Jinja2 environment and template rendering - URL Router (
generator/router.py): Simple pattern matching for URL generation - Page Renderer (
generator/renderer.py): Orchestrates page rendering from markdown to HTML - Site Builder (
generator/builder.py): Main orchestration for the entire build process - Build Script (
scripts/build_static_simple.py): Minimal entry point (~45 lines)
- URL Resolution (
url_mapper.py): Maps slugs to file paths - Markdown Processing (
markdown_processor.py):- Protects math expressions from markdown parsing
- Processes wiki-style links
[[slug]] - Handles
{% include_demo %}tags
- Structured Math (
structured_math.py):- Parses
:::typeblocks (definition, theorem, proof, etc.) - Builds global index for cross-references
- Handles
@labelreferences and@@labelembeds
- Parses
- Security (
security.py): Embeds CSP and security headers in HTML - Rendering: Jinja2 templates generate static HTML with MathJax for LaTeX
- Output: Complete static site ready for nginx serving
-
URL System: Content uses slug-based canonical URLs (
/mathnotes/section/slug). This allows content reorganization without breaking links. -
Math Processing Pipeline:
- Math expressions are protected before markdown parsing
- Structured blocks are parsed and indexed globally
- Cross-references resolved using the global index
- MathJax handles final LaTeX rendering client-side
- For detailed explanation of the parsing pipeline, see PARSING.md
-
Demo System: TypeScript demos are registered in
demos-framework/src/main.tsand loaded dynamically with code splitting. Demos can use:- p5.js (
P5DemoBaseclass): For 2D canvas graphics, WebGL, and creative coding - Plotly.js (direct
DemoInstanceimplementation): For 3D scientific visualizations with built-in camera controls
- p5.js (
-
CSP Implementation: No inline JavaScript or inline event handlers are permitted. All JavaScript must be in external files to keep content static and cacheable.
-
Development Mode Detection: Development server runs only locally for testing. Production always uses pre-generated static files served by nginx.
-
Modern CSS System: See STYLE.md for detailed CSS and styling guidelines
The markdown processor uses a two-phase approach:
- Protection Phase: Replace
$...$and$$...$$with placeholders - Restoration Phase: Restore math after markdown conversion This prevents markdown from interfering with LaTeX syntax.
@label- Links to a block with auto-generated text@type:label- Links with type validation (e.g.,@theorem:ftc)@{Custom text|label}- Custom link text@@label- Embeds the entire block content inline
{% include_demo "demo-name" %}- Demo must be registered in
demos-framework/src/main.ts - TypeScript source in
mathnotes/demos/ - Automatic dark mode support
- CSP-compliant (no inline scripts or event handlers)
Demo Viewer: To test any demo in isolation, use the demo viewer:
- Dev:
http://localhost:5000/demos/#demo-name - Prod:
https://lacunary.org/demos/#demo-name - Supports keyboard navigation (arrow keys) and URL hash for direct linking
Visualization Library Choices:
- p5.js: Best for 2D canvas graphics, particle systems, cellular automata, WebGL custom shaders
- Extend
P5DemoBaseclass for automatic canvas management and responsive sizing - Built-in draw loop and event handling
- Examples:
game-of-life,electric-field,pendulum
- Extend
- Plotly.js: Best for 3D scientific visualizations, vector fields, data plots
- Implement
DemoInstancedirectly - Built-in 3D camera controls (rotate, zoom, pan)
- Native support for 3D arrows (cone traces), meshes, scatter plots
- Examples:
cross-product
- Implement
When moving/renaming content files:
- Note the current canonical URL
- Update any internal links that reference the old URL
- Or use:
./scripts/move_file.sh old/path.md new/path.md
- No "Generated with Claude Code" or AI attribution
- Technical, concise commit messages
- Only commit when explicitly requested ("ship it")
- Pre-commit hook warns about moved/deleted files
- Always test in Docker for consistency
- Use
docker-compose -f docker-compose.dev.ymlfor development - Verify both light and dark modes
- Check browser console for CSP violations
- Test demos on mobile devices
- NEVER use inline JavaScript - all JavaScript must be in external files
- NEVER generate nonces - we don't need them since we don't use inline scripts
- No inline event handlers (onclick, onload, etc.)
- Use
addEventListenerand data attributes instead - All demos must be CSP-compliant
- This policy keeps our content static and cacheable without per-request processing
- For CSS-specific guidelines, see STYLE.md
- Choose stable, descriptive slugs
- Update internal links when moving files
- Use wiki-style links for resilience:
[[slug]] - Canonical URLs adapt automatically for dev/prod
Development mode auto-detected via:
localhostor127.0.0.1in URL- Development environment variables
- Assets served from
/static/dist/in production - TypeScript/JavaScript bundled with code splitting
- CSS processed with PostCSS
- For CSS configuration and guidelines, see STYLE.md
Python:
- Python 3.11+ required
- Key packages: Jinja2, Markdown 3.5.1, python-frontmatter 1.0.1
- Dev tools: pytest, black, flake8, mypy, tox
JavaScript/TypeScript:
- Node.js 24.x
- TypeScript 5.3+
- Visualization: p5.js, Plotly.js (plotly.js-dist-min)
- Build tools: esbuild, PostCSS
-
Build Phase: Docker multi-stage build
- Stage 1: Python generator crawls and renders all content to static HTML using Jinja2 directly
- Stage 2: Static HTML and assets copied to nginx image (no Python runtime)
- URL structure preserved (e.g.,
/mathnotes/algebra/groups→mathnotes/algebra/groups/index.html) - All assets included with proper cache headers
- sitemap.xml generated for SEO
-
Production Serving: nginx only
- Pure static file serving
- No Python runtime in production container
- Pre-rendered HTML for instant page loads
- Full CDN compatibility
- Maximum security (no dynamic code execution)
-
Generated File Structure:
/usr/share/nginx/html/ # Inside nginx container ├── index.html # Homepage ├── mathnotes/ # Content pages │ ├── algebra/ │ │ └── groups/ │ │ └── index.html │ └── ... ├── static/ # Static assets │ ├── dist/ # Bundled JS/CSS │ ├── css/ │ └── images/ └── sitemap.xml # Generated sitemap
- Push to main triggers build
- Docker build runs static site generation
- Multi-platform Docker images (amd64, arm64) with nginx
- Push to ghcr.io registry
- Automatic deployment to Fly.io
- Primary: https://lacunary.org
For comprehensive debugging techniques, troubleshooting tips, and testing workflows, see DEBUGGING.md.
Key debugging tools available:
- Console probe debugging for JavaScript/CSS/DOM issues
- Demo screenshot analysis with AI-powered visual testing
- Mobile-specific testing and responsive scaling
- JavaScript policy compliance and CSP debugging
- Docker container debugging
- Don't restart/rebuild docker everytime you change typescript, css, python, etc. We have autorebuild built in.