mirror of
https://github.com/kennethreitz/kennethreitz.org.git
synced 2026-06-05 06:46:13 +00:00
Rewrite README for current Responder-based architecture
Was still describing Flask blueprints, gunicorn, and old project structure. Now reflects the single-file Responder app, Fly.io deployment, cached search, bot detection, and actual feature set. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,320 +1,93 @@
|
||||
# TufteCMS
|
||||
# kennethreitz.org
|
||||
|
||||
A Flask-based content management system designed for thoughtful writing, inspired by Edward Tufte's design principles. Built for digital gardens, essay collections, and content-rich websites that prioritize readability and deep engagement.
|
||||
Kenneth Reitz's personal website and digital garden. A living exploration of how technology can serve human consciousness rather than exploit it.
|
||||
|
||||
> **Note**: This repository is a hybrid containing both the TufteCMS framework (in `tuftecms/`) and Kenneth Reitz's personal website content (in `data/`). The CMS powers kennethreitz.org and serves as a living example of the system in production.
|
||||
Built with [Responder](https://github.com/kennethreitz/responder), powered by markdown content, deployed on [Fly.io](https://fly.io).
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Option 1: Docker Compose (Recommended)
|
||||
|
||||
The easiest way to get started with all dependencies included:
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/kennethreitz/tuftecms.git
|
||||
cd tuftecms
|
||||
|
||||
# Start with Docker Compose
|
||||
docker compose up
|
||||
```
|
||||
|
||||
The site will be available at `http://localhost:8000` with hot-reloading enabled. All dependencies, including PDF generation, are pre-configured.
|
||||
|
||||
### Option 2: Local Development with uv
|
||||
|
||||
For local development without Docker:
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/kennethreitz/tuftecms.git
|
||||
cd tuftecms
|
||||
|
||||
# Install dependencies with uv (recommended)
|
||||
# Install dependencies
|
||||
uv sync
|
||||
|
||||
# Run development server
|
||||
uv run python engine.py
|
||||
```
|
||||
|
||||
The site will be available at `http://localhost:8000`
|
||||
|
||||
**Note**: PDF generation requires additional system libraries (see below). The site works fully without them - PDFs just won't be available.
|
||||
|
||||
### Installing uv
|
||||
|
||||
If you don't have `uv` installed:
|
||||
|
||||
```bash
|
||||
# macOS/Linux
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
|
||||
# Windows
|
||||
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
|
||||
|
||||
# Or via pip
|
||||
pip install uv
|
||||
```
|
||||
|
||||
## Philosophy
|
||||
|
||||
TufteCMS embraces **human-first design** - content presentation that serves readers' mental models rather than forcing adaptation to machine logic. It provides powerful features like sidenotes, content indexing, and cross-referencing while maintaining simplicity and elegance.
|
||||
|
||||
## Features
|
||||
|
||||
### Content & Typography
|
||||
- **Tufte-style sidenotes** - Margin commentary without disrupting reading flow
|
||||
- **Responsive typography** - Optimized for reading across all devices
|
||||
- **Markdown rendering** - Custom Mistune renderer with philosophical code blocks
|
||||
- **File-based content** - Simple directory structure mirrors URL routing
|
||||
|
||||
### Discovery & Navigation
|
||||
- **Full-text search** - Relevance-scored search with contextual snippets
|
||||
- **Content indexes** - Automatic extraction of sidenotes, quotes, outlines, connections, terms
|
||||
- **Cross-referencing** - Track incoming/outgoing links between content
|
||||
- **Theme detection** - Automatic categorization by content patterns
|
||||
- **HTML & XML sitemaps** - Both human and machine-readable navigation
|
||||
|
||||
### Visual Identity
|
||||
- **Generated SVG icons** - Unique algorithmic icons for every piece of content
|
||||
- **Deterministic design** - Same title always produces the same icon
|
||||
- **Dynamic folder icons** - Color variations based on content titles
|
||||
- **Reading progress** - Visual indicator for longer essays
|
||||
|
||||
### Performance
|
||||
- **Intelligent caching** - LRU caches for blog posts, indexes, and metadata
|
||||
- **Background cache warming** - Async startup loading for instant response
|
||||
- **Lazy loading** - Images load via IntersectionObserver
|
||||
- **HTTP caching headers** - Aggressive caching for static assets
|
||||
- **Cache debugging** - API endpoint to monitor cache performance
|
||||
|
||||
### Developer Experience
|
||||
- **Flask blueprints** - Modular architecture with clear separation
|
||||
- **API endpoints** - JSON APIs for search, icons, and debugging
|
||||
- **RSS/Atom feeds** - Standard feed formats for syndication
|
||||
- **Template system** - Jinja2 templates with extensible blocks
|
||||
|
||||
## PDF Generation Setup (Optional)
|
||||
|
||||
TufteCMS includes server-side PDF generation using WeasyPrint. This requires system libraries:
|
||||
|
||||
**macOS (via Homebrew):**
|
||||
```bash
|
||||
brew install cairo pango gdk-pixbuf libffi
|
||||
```
|
||||
|
||||
**Ubuntu/Debian:**
|
||||
```bash
|
||||
sudo apt-get install libcairo2 libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf2.0-0 libffi-dev shared-mime-info
|
||||
```
|
||||
|
||||
**Docker:**
|
||||
The provided Dockerfile includes these dependencies automatically.
|
||||
|
||||
**Note:** If you skip this step, the site will work fine but `.pdf` URLs will not function. All other features work without these libraries.
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
tuftecms/
|
||||
├── tuftecms/
|
||||
│ ├── app.py # Application factory
|
||||
│ ├── config.py # Configuration management
|
||||
│ ├── blueprints/ # Route handlers
|
||||
│ │ ├── main.py # Homepage and core routes
|
||||
│ │ ├── content.py # Content rendering
|
||||
│ │ ├── api.py # JSON API endpoints
|
||||
│ │ └── feeds.py # RSS/sitemap generation
|
||||
│ ├── core/ # Core functionality
|
||||
│ │ ├── cache.py # Caching system
|
||||
│ │ ├── content.py # Content processing
|
||||
│ │ └── markdown.py # Markdown rendering
|
||||
│ ├── utils/ # Utilities
|
||||
│ │ ├── content.py # Content helpers
|
||||
│ │ └── svg_icons.py # Icon generation
|
||||
│ ├── templates/ # Jinja2 templates
|
||||
│ └── static/ # CSS, images, fonts
|
||||
├── data/ # Content directory
|
||||
│ └── essays/ # Your content here
|
||||
├── engine.py # Development server
|
||||
└── pyproject.toml # Dependencies
|
||||
```
|
||||
|
||||
## Content Organization
|
||||
|
||||
### Directory Structure
|
||||
|
||||
Content is organized in a simple directory structure under `data/`:
|
||||
|
||||
```
|
||||
data/
|
||||
├── essays/
|
||||
│ ├── 2025-01-15-example-essay.md
|
||||
│ └── 2025-02-20-another-essay.md
|
||||
├── notes/
|
||||
└── index.md
|
||||
```
|
||||
|
||||
URL paths mirror the file system: `/essays/2025-01-15-example-essay`
|
||||
|
||||
### Writing with Sidenotes
|
||||
|
||||
Sidenotes provide commentary without disrupting reading flow:
|
||||
|
||||
```markdown
|
||||
This is the main text.<label for="sn-example" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-example" class="margin-toggle"/><span class="sidenote">This is a sidenote that adds depth without breaking the narrative.</span> The text continues naturally.
|
||||
```
|
||||
|
||||
**Critical formatting rules:**
|
||||
- Sidenotes must be inline (attached to sentence end with NO line breaks)
|
||||
- Use descriptive IDs like `sn-consciousness`, `sn-recursion`
|
||||
- Keep away from code blocks to prevent layout issues
|
||||
|
||||
### Markdown Features
|
||||
|
||||
```markdown
|
||||
# Essay Title
|
||||
|
||||
*Published: January 2025*
|
||||
|
||||
Standard markdown with [links](/other-essay) and **emphasis**.
|
||||
|
||||
Code blocks with philosophy:
|
||||
|
||||
\`\`\`python
|
||||
def consciousness():
|
||||
# TODO: Model the unmappable
|
||||
pass
|
||||
\`\`\`
|
||||
|
||||
> Blockquotes for memorable passages
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Edit `tuftecms/config.py`:
|
||||
|
||||
```python
|
||||
class Config:
|
||||
DISABLE_ANALYTICS = True # Privacy-first by default
|
||||
SEARCH_CACHE_TIMEOUT = 60
|
||||
DATA_DIR = "data"
|
||||
MIN_SEARCH_QUERY_LENGTH = 2
|
||||
MAX_SEARCH_RESULTS = 50
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Search
|
||||
```
|
||||
GET /api/search?q=query
|
||||
```
|
||||
|
||||
Returns JSON with relevance-scored results, snippets, and match locations.
|
||||
|
||||
### Cache Debugging
|
||||
```
|
||||
GET /api/debug-cache
|
||||
```
|
||||
|
||||
Returns cache statistics and LRU cache performance metrics.
|
||||
|
||||
### Icon Generation
|
||||
```
|
||||
GET /api/icon/<path:article_path>
|
||||
```
|
||||
|
||||
Returns generated SVG icon data for any content path.
|
||||
|
||||
## Content Indexes
|
||||
|
||||
TufteCMS automatically builds indexes:
|
||||
|
||||
- **Sidenotes** - All margin notes with context
|
||||
- **Outlines** - Heading structure across content
|
||||
- **Quotes** - Blockquoted passages
|
||||
- **Connections** - Internal link graph
|
||||
- **Terms** - Key terms appearing across multiple articles
|
||||
- **Themes** - Pattern-based content categorization
|
||||
|
||||
Access via routes: `/sidenotes`, `/outlines`, `/quotes`, `/connections`, `/terms`
|
||||
|
||||
## Deployment
|
||||
The site will be available at `http://localhost:8000`.
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
docker build -t tuftecms .
|
||||
docker run -p 8000:8000 tuftecms
|
||||
docker compose up
|
||||
```
|
||||
|
||||
### Production with Gunicorn
|
||||
## Architecture
|
||||
|
||||
This is a single-file Responder application (`engine.py`) serving markdown content from `data/`.
|
||||
|
||||
```
|
||||
kennethreitz.org/
|
||||
├── engine.py # The whole application
|
||||
├── data/ # All content (markdown)
|
||||
│ ├── essays/ # 250+ essays (2008-2026)
|
||||
│ ├── software/ # Project pages
|
||||
│ ├── themes/ # Thematic collections
|
||||
│ ├── artificial-intelligence/
|
||||
│ ├── poetry/
|
||||
│ ├── photography/
|
||||
│ ├── music/
|
||||
│ └── *.md # Standalone pages
|
||||
├── tuftecms/
|
||||
│ ├── templates/ # Jinja2 templates
|
||||
│ ├── static/ # CSS, fonts, images
|
||||
│ ├── core/ # Cache, markdown rendering
|
||||
│ └── utils/ # Content helpers, SVG icons
|
||||
├── Dockerfile
|
||||
├── fly.toml # Fly.io deployment config
|
||||
└── pyproject.toml
|
||||
```
|
||||
|
||||
URL paths mirror the filesystem: `data/essays/2025-08-26-example.md` → `/essays/2025-08-26-example`
|
||||
|
||||
## Features
|
||||
|
||||
- **Tufte-style sidenotes** — margin commentary without disrupting flow.
|
||||
- **Full-site search** — cached index built at startup, server-side autocomplete.
|
||||
- **Content indexes** — automatic extraction of sidenotes, quotes, outlines, connections, terms, themes.
|
||||
- **Legacy URL redirects** — intelligent matching for old URL patterns (301 permanent).
|
||||
- **Bot detection** — logs scraper activity with User-Agent identification.
|
||||
- **Structured logging** — request-scoped context (ID, method, path, client IP) on every log line.
|
||||
- **PDF generation** — server-side via WeasyPrint.
|
||||
- **Generated SVG icons** — unique algorithmic icons for every piece of content.
|
||||
- **RSS feed** — at `/feed.xml`.
|
||||
- **Knowledge graph** — interactive D3 visualization of content connections.
|
||||
- **Dark mode** — system-preference and manual toggle.
|
||||
|
||||
## API Endpoints
|
||||
|
||||
```
|
||||
GET /api/search?q=query # Full-site search with scoring
|
||||
GET /api/search/autocomplete?q=q # Title-based autocomplete (8 results)
|
||||
GET /api/blog # Essay listing
|
||||
GET /api/themes # Theme listing with icons
|
||||
GET /api/directory-tree # Site directory structure
|
||||
GET /api/icon/<path> # Generated SVG icon
|
||||
GET /api/cache-stats # Cache performance metrics
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
Deployed on Fly.io with 2 shared CPUs, 2GB RAM, 2 uvicorn workers.
|
||||
|
||||
```bash
|
||||
uv run gunicorn -w 4 -b 0.0.0.0:8000 'tuftecms.app:create_app()'
|
||||
fly deploy
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
## Content
|
||||
|
||||
```bash
|
||||
DISABLE_ANALYTICS=true # Disable analytics
|
||||
DEBUG=1 # Enable debug mode
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
### Debug Mode
|
||||
|
||||
```bash
|
||||
DEBUG=1 uv run python engine.py
|
||||
```
|
||||
|
||||
Enables verbose logging and development features.
|
||||
|
||||
### Cache Management
|
||||
|
||||
Clear all caches:
|
||||
|
||||
```python
|
||||
from tuftecms.core.cache import clear_all_caches
|
||||
clear_all_caches()
|
||||
```
|
||||
|
||||
### Adding New Features
|
||||
|
||||
1. Create new blueprint in `tuftecms/blueprints/`
|
||||
2. Register in `app.py`
|
||||
3. Add templates to `tuftecms/templates/`
|
||||
4. Update cache logic if needed in `core/cache.py`
|
||||
|
||||
## Philosophy & Design
|
||||
|
||||
TufteCMS is built on principles of **contemplative pragmatism**:
|
||||
|
||||
- **Human-first design** - Optimize for people having difficult days
|
||||
- **Simple over clever** - Tools should feel natural to use
|
||||
- **Content-focused** - Design serves the writing, not the ego
|
||||
- **Respectful reading** - No dark patterns, no engagement manipulation
|
||||
- **Open and inspectable** - Clean code, clear architecture
|
||||
|
||||
The name honors Edward Tufte's work on information design - presenting complex information with clarity and respect for the reader's intelligence.
|
||||
|
||||
## Use Cases
|
||||
|
||||
- **Digital gardens** - Personal knowledge bases that grow organically
|
||||
- **Essay collections** - Long-form writing with depth and cross-references
|
||||
- **Academic writing** - Papers with extensive sidenotes and citations
|
||||
- **Documentation sites** - Technical docs with commentary
|
||||
- **Philosophy blogs** - Contemplative writing with layered meaning
|
||||
The `data/` directory contains 15+ years of writing — essays, software documentation, poetry, photography, AI explorations, and personal pages. Content is plain markdown with optional YAML frontmatter and Tufte-style HTML sidenotes.
|
||||
|
||||
## License
|
||||
|
||||
MIT License - see LICENSE file for details.
|
||||
|
||||
## Credits
|
||||
|
||||
Created by Kenneth Reitz as part of kennethreitz.org. Built with Flask, Mistune, and care for human consciousness.
|
||||
|
||||
---
|
||||
|
||||
*"Technology should serve human mental models, not force humans to adapt to machine logic."*
|
||||
MIT
|
||||
|
||||
Reference in New Issue
Block a user