Readme

Bloggy

A lightweight, elegant blogging platform built with FastHTML that renders Markdown files into beautiful web pages with advanced features.

Architecture Overview

graph TB
    subgraph "User Interface"
        Browser[Web Browser]
        Theme[Light/Dark Theme Toggle]
    end
    
    subgraph "FastHTML Application"
        App[FastHTML App<br/>core.py]
        Router[URL Router]
        Layout[Layout Engine]
        
        subgraph "Route Handlers"
            Index[Index Route<br/>/]
            PostDetail[Post Detail Route<br/>/posts/path]
        end
    end
    
    subgraph "Markdown Processing"
        MDParser[Mistletoe Parser]
        Renderer[ContentRenderer]
        
        subgraph "Custom Renderers"
            Footnotes[Footnote Renderer<br/>Sidenotes]
            Mermaid[Mermaid Diagram Renderer<br/>Zoom/Pan Controls]
            Links[Link Renderer<br/>HTMX Integration]
        end
    end
    
    subgraph "File System"
        MDFiles[Markdown Files<br/>.md]
        Tree[Folder Tree Builder<br/>build_post_tree]
    end
    
    subgraph "Frontend Assets"
        Static[Static Files]
        JS[scripts.js<br/>Mermaid + Interactions]
        CSS[Styles<br/>TailwindCSS + MonsterUI]
    end
    
    Browser -->|HTTP Request| Router
    Theme -->|Toggle Dark Mode| JS
    
    Router --> Index
    Router --> PostDetail
    
    Index --> Tree
    Tree --> MDFiles
    Index --> Layout
    
    PostDetail --> MDFiles
    PostDetail --> MDParser
    
    MDParser --> Renderer
    Renderer --> Footnotes
    Renderer --> Mermaid
    Renderer --> Links
    
    Footnotes -->|Marginal Notes| Layout
    Mermaid -->|Interactive Diagrams| Layout
    Links -->|HTMX Navigation| Layout
    
    Layout --> Browser
    
    JS -->|Theme Change| Mermaid
    JS -->|Zoom/Pan/Reset| Mermaid
    
    Static --> CSS
    Static --> JS
    
    App -.->|Serves| Static
    
    style Browser fill:#e1f5ff
    style App fill:#fff3cd
    style MDParser fill:#d4edda
    style Static fill:#f8d7da
    style Mermaid fill:#cce5ff
    style Footnotes fill:#cce5ff

How Bloggy Works

1. Request Flow

sequenceDiagram
    participant User
    participant Browser
    participant FastHTML
    participant Router
    participant FileSystem
    participant Renderer
    participant HTMX
    
    User->>Browser: Visit /
    Browser->>FastHTML: GET /
    FastHTML->>Router: Route to index()
    Router->>FileSystem: Scan for .md files
    FileSystem-->>Router: Return file tree
    Router->>Browser: Render post list + layout
    
    User->>Browser: Click post link
    Browser->>HTMX: hx-get="/posts/demo"
    HTMX->>FastHTML: GET /posts/demo
    FastHTML->>Router: Route to post_detail()
    Router->>FileSystem: Read demo.md
    FileSystem-->>Router: Markdown content
    Router->>Renderer: Parse & render markdown
    
    rect rgb(200, 220, 250)
        Note over Renderer: Process custom syntax:<br/>- Footnotes [^1]<br/>- Mermaid diagrams<br/>- Internal links
    end
    
    Renderer-->>Router: Rendered HTML
    Router->>HTMX: Return HTML fragment
    HTMX->>Browser: Swap content (#main-content)
    Browser->>User: Display post

2. Markdown Processing Pipeline

flowchart LR
    A[Raw Markdown] --> B{Extract Footnotes}
    B -->|Content| C[Mistletoe Parser]
    B -->|Footnote Defs| D[Store in Dict]
    
    C --> E[Token Stream]
    
    E --> F{ContentRenderer}
    
    F -->|BlockCode + 'mermaid'| G[Mermaid Renderer]
    F -->|Link| H[Link Renderer]
    F -->|FootnoteRef| I[Footnote Renderer]
    F -->|Other| J[Standard HTML]
    
    G --> K[Diagram with Controls]
    H --> L{Internal Link?}
    L -->|Yes| M[Add HTMX attrs]
    L -->|No| N[Add target=_blank]
    
    I --> O[Sidenote Component]
    D --> O
    
    K --> P[Apply CSS Classes]
    M --> P
    N --> P
    O --> P
    J --> P
    
    P --> Q[Final HTML]
    
    style G fill:#ffe6cc
    style I fill:#d1ecf1
    style L fill:#fff3cd

3. Mermaid Diagram Lifecycle

stateDiagram-v2
    [*] --> Rendered: Page Load
    
    state Rendered {
        [*] --> Initialize
        Initialize --> AddControls: Create buttons
        AddControls --> StoreCode: Save original code
        StoreCode --> EnableInteraction: Mouse events
    }
    
    state EnableInteraction {
        [*] --> Idle
        Idle --> Panning: Mouse drag
        Idle --> Zooming: Mouse wheel
        Idle --> ButtonZoom: +/- buttons
        ButtonZoom --> Idle
        Zooming --> Idle
        Panning --> Idle
        
        state "Reset Button" as Reset
        Idle --> Reset: Click reset
        Reset --> Idle: Restore defaults
    }
    
    Rendered --> ThemeChange: Dark/Light toggle
    
    state ThemeChange {
        [*] --> DetectTheme
        DetectTheme --> GetOriginalCode: Read data attribute
        GetOriginalCode --> ClearWrapper
        ClearWrapper --> ReinitMermaid: New theme
        ReinitMermaid --> ReRender: mermaid.run()
    }
    
    ThemeChange --> Rendered: Re-rendered
    
    note right of ThemeChange
        MutationObserver watches
        HTML class changes
    end note
    
    note right of EnableInteraction
        Transform state stored
        per diagram ID
    end note

Key Features

✨ Advanced Markdown Features

  • Footnotes as Sidenotes: [^1] references become elegant margin notes on desktop, expandable on mobile
  • Mermaid Diagrams: Full support for flowcharts, sequence diagrams, state diagrams, etc.
  • Interactive Diagrams: Built-in zoom, pan, and reset controls for all mermaid diagrams
  • Theme-aware Rendering: Diagrams automatically re-render when switching light/dark mode

🎨 Modern UI

  • Responsive Design: Works beautifully on all screen sizes
  • Dark Mode: Automatic theme switching with localStorage persistence
  • HTMX Navigation: Fast, SPA-like navigation without full page reloads
  • Collapsible Folders: Organize posts in nested directories

🚀 Technical Highlights

  • Built on FastHTML for modern Python web development
  • Uses Mistletoe for extensible Markdown parsing
  • TailwindCSS + MonsterUI for styling
  • Hyperscript for interactive behaviors
  • Mermaid.js v11 for diagram rendering

Project Structure

graph LR
    subgraph bloggy/
        A[__init__.py]
        B[core.py<br/>Main App Logic]
        C[main.py<br/>Entry Point]
        
        subgraph static/
            D[scripts.js<br/>Mermaid + Interactions]
            E[sidenote.css<br/>Footnote Styles]
            F[favicon.png]
        end
    end
    
    subgraph demo/
        G[*.md Files<br/>Your Blog Posts]
        
        subgraph guides/
            H[*.md Files<br/>Nested Content]
        end
    end
    
    B --> D
    B --> E
    B --> F
    B -.reads.-> G
    B -.reads.-> H
    
    style B fill:#ffe6cc
    style D fill:#d1ecf1
    style G fill:#d4edda

Installation

pip install bloggy

From source

git clone https://github.com/yeshwanth/bloggy.git
cd bloggy
pip install -e .

Quick Start

  1. Create a directory with your markdown files:

    mkdir my-blog
    cd my-blog
    echo "# Hello World" > hello.md
    
  2. Run Bloggy:

    bloggy .
    
  3. Open your browser at http://127.0.0.1:5001

Configuration

Bloggy supports three ways to configure your blog (in priority order):

  1. .bloggy configuration file (TOML format)
  2. Environment variables
  3. Default values

Using a .bloggy Configuration File

Create a .bloggy file in your blog directory or current directory:

# Blog title (default: derives from root folder name)
title = "My Awesome Blog"

# Root folder containing markdown files (default: current directory)
root = "."

# Server host (default: 127.0.0.1)
# Use "0.0.0.0" to make the server accessible from network
host = "127.0.0.1"

# Server port (default: 5001)
port = 5001

All settings in the .bloggy file are optional. See .bloggy.example for a full example.

Environment Variables

You can also use environment variables as a fallback:

  • BLOGGY_ROOT: Path to your markdown files (default: current directory)
  • BLOGGY_TITLE: Your blog's title (default: folder name)
  • BLOGGY_HOST: Server host (default: 127.0.0.1)
  • BLOGGY_PORT: Server port (default: 5001)

Examples

Using a .bloggy file:

# Create a .bloggy file in your blog directory
cd /path/to/your/blog
cat > .bloggy << EOF
title = "My Tech Blog"
port = 8000
EOF

bloggy

Using environment variables:

export BLOGGY_ROOT=/path/to/your/markdown/files
export BLOGGY_TITLE="My Awesome Blog"
export BLOGGY_PORT=8000
bloggy

Passing directory as argument:

bloggy /path/to/your/markdown/files

Configuration priority example:

If you have both a .bloggy file with port = 8000 and an environment variable BLOGGY_PORT=9000, the .bloggy file takes precedence and port 8000 will be used.