Listsome Documentation
A static site generator for sharing DIY project guides. Write standard Markdown with YAML frontmatter, get a deployable static site.
What It Does Today
| Command | What It Does |
|---|---|
listsome init | Scaffolds a new site with config, projects directory, and .gitignore |
listsome new | Creates a new project with index.md, frontmatter template, and assets directory |
listsome build | Parses projects, renders HTML from templates, copies assets, generates index page and Atom feed |
listsome serve | Dev server with live reload via WebSocket, auto-rebuilds on file changes |
listsome check | Validates project structure and frontmatter |
listsome list | Lists projects with filtering by category, difficulty, and draft status |
Every project lives in projects/<slug>/index.md. Step headers (## Step N: Title, ## Part N: Title) are automatically extracted. Tables with Item/Qty/Cost/Source columns are parsed as bill-of-materials.
Philosophy
- Standard Markdown: No proprietary syntax. Your files render everywhere.
- Single Binary: One Rust binary, no runtime, no database.
- Opinionated: Sensible defaults for DIY project sites.
- Portable Output: Deploy anywhere that serves static files.
Project Structure
my-site/
├── listsome.toml # Site configuration
├── projects/ # Your project directories
│ └── my-project/
│ ├── index.md # Project content with frontmatter
│ └── assets/ # Images, downloads, etc.
├── output/ # Generated site (gitignored)
└── README.md # Your site's README (generated by init)
Quick Links
- Quickstart: Install, create a site, build, deploy
- Project Format: How to write project files
- Roadmap: Current state and planned features
- Contributors: Architecture, building, testing
Quickstart
Prerequisites
- Rust 1.75 or later
Install
cargo install listsome
Create a Site
listsome init my-projects
cd my-projects
This creates listsome.toml, a projects/ directory, and a .gitignore. Edit listsome.toml to set your site name, author, URL, and webmention endpoint. See the configuration reference for details.
Add a Project
listsome new smart-thermostat --title "Smart Thermostat" --difficulty intermediate
Edit projects/smart-thermostat/index.md with your content.
Build
listsome build
Generates output/index.html, output/<slug>/index.html for each project, and output/rss.xml.
Preview
listsome serve --open
Opens a browser at http://127.0.0.1:8080 with live reload. Edit your project files and the browser refreshes automatically.
Deploy
Copy the output/ directory to any static host (Netlify, Codeberg Pages, S3, nginx, etc.).
Command-Line Tool Reference
| Command | Description |
|---|---|
listsome init [PATH] | Initialize a new site |
listsome new <SLUG> | Create a new project |
listsome build | Build the static site |
listsome serve | Start development server |
listsome check [PROJECT] | Validate projects |
listsome list | List projects with filters |
listsome webmention fetch|show|test | Webmention management (coming soon) |
Global flags: -v (verbose), -q (quiet), -C <FILE> (config path), --color <WHEN>.
Project Format Reference
Philosophy
Write standard Markdown with YAML frontmatter. Listsome recognizes patterns through convention, not custom syntax. Your files render anywhere Markdown does.
Folder Structure
projects/
└── smart-thermostat/
├── index.md # Project content (required)
└── assets/ # Images, downloads (optional)
├── hero.jpg
├── wiring-diagram.png
└── firmware.ino
index.mdis required, everything else is optional- Metadata goes in frontmatter, not separate config files
- Assets referenced by relative path:
./assets/hero.jpg
Example Project
---
title: DIY Smart Thermostat
description: A $20 ESP32-based smart thermostat for Home Assistant
difficulty: intermediate
time: 4 hours
cost: 23.50
categories: [home-automation, electronics]
author: Your Name
date: 2026-03-08
---
# DIY Smart Thermostat

Replace your old thermostat with a smart, WiFi-enabled version
that integrates with Home Assistant.
## What You'll Need
| Item | Qty | Cost | Source |
|------|-----|------|--------|
| ESP32 DevKit | 1 | $6.00 | [Example](https://example.com) |
| DHT22 Sensor | 1 | $4.50 | [Adafruit](https://adafruit.com) |
| 5V Relay Module | 1 | $3.00 | [Example](https://example.com) |
## Step 1: Test Your Components
Connect the DHT22 to the ESP32 (VCC → 3.3V, GND → GND, Data → GPIO4)
and upload the test sketch.
## Step 2: Wire the Relay
Connect 24VAC control: R to relay COM, W to relay NO.
**Warning:** Turn off power at the breaker before proceeding.
## Step 3: Install the Software
Flash the firmware with PlatformIO, then configure WiFi and MQTT.
Frontmatter Schema
Required fields:
title: "Project Name" # Display title (3-100 chars)
description: "Short summary" # For listings, SEO (10-200 chars)
Optional:
difficulty: beginner # beginner | intermediate | advanced
time: 4 hours # Estimated duration
cost: 23.50 # Estimated total cost
currency: USD # ISO 4217 code
categories: [electronics] # Primary categories
tags: [arduino, sensor] # Searchable tags
author: Your Name # Display name
date: 2026-03-08 # ISO 8601
Advanced:
featured: true # Highlight on index page
hidden: false # Exclude from listings
draft: true # Work in progress
related: [other-project-slug] # Related projects
dangerous: true # Shows warning banner
hero: ./assets/hero.jpg # Image for index listings
Section Conventions
Listsome recognizes these H2 headers and extracts structured data:
| Header | Extracted as |
|---|---|
## What You'll Need / ## Materials / ## BOM | Bill of Materials |
## Tools / ## Equipment | Tool list |
## Step N: / ## Part N: / ## Phase N: | Numbered steps |
## Troubleshooting / ## FAQ / ## Common Issues | Collapsible sections |
## Downloads / ## Files / ## Assets | Download links with metadata |
BOM Tables
Tables under a materials header are parsed as structured BOM data. Recognized column names (case-insensitive):
- Item: Item, Part, Component, Name, Material
- Qty: Qty, Quantity, Count, Amount
- Cost: Cost, Price, Unit Cost, Each
- Source: Source, Link, Buy, Vendor, Supplier
- Notes: Notes, Description, Comment, Note
| Item | Qty | Cost | Source | Notes |
|------|-----|------|--------|-------|
| ESP32 | 1 | $6.00 | [Buy](link) | Any ESP32 works |
Step Headers
Headers matching ## Step N: Title, ## Part N: Title, or
## Phase N: Title are extracted as numbered steps. A table of
contents is generated from the step titles.
Emoji Callouts
Bold text starting with a recognized prefix renders as a styled callout box:
Warning: High voltage present Tip: Buy in bulk to save 40% Note: App requires iOS 14+
Recognized prefixes include: warning, hot, tip, note, pro tip, caution.
Asset Conventions
Naming
assets/
├── hero.jpg # Main image for listings
├── thumbnail.jpg # Optional listing thumbnail override
├── step-01-breadboard.jpg # Step images
├── step-02-wiring.jpg
├── diagram.svg # Vector graphics
├── firmware.ino # Code downloads
└── schematic.pdf # PDF documents
Referencing Assets
# Standard image

# Video
<video src="./assets/demo.mp4" poster="./assets/demo-thumb.jpg"></video>
# Download link (renders as button)
[Download STL](./assets/model.stl)
Minimal Example
---
title: Paperclip Antenna
description: Free TV antenna from a paperclip
difficulty: beginner
time: 5 minutes
cost: 0
---
# Paperclip Antenna
Need free over-the-air TV? Unbend a paperclip.
## What You Need
| Item | Qty | Cost |
|------|-----|------|
| Paperclip | 1 | $0 |
| Coax adapter | 1 | $5 (optional) |
## Steps
1. Unbend paperclip into "L" shape
2. Insert into TV's coax input center pin
3. Auto-scan for channels
Done. No amplifier needed if you're close to broadcast towers.
Under 50 lines including frontmatter. Listsome makes it look professional.
Site Configuration
Listsome is configured via listsome.toml in your site root. Run listsome init to generate a default config, then edit it to match your setup.
Full Example
[site]
name = "My DIY Projects"
url = "https://projects.example.com"
description = "Step-by-step guides for things I built"
language = "en"
author = "Your Name"
about = "https://yourdomain.com/about"
email = "you@example.com"
source_repo = "https://codeberg.org/username/my-projects"
[site.fediverse]
mastodon = "@username@mastodon.social"
[build]
output_dir = "./output"
theme = "default"
drafts = false
parallel_jobs = 8
incremental = true
[webmention]
enabled = true
endpoint = "https://webmention.io/yourdomain.com/webmention"
require_approval = true
notify = false
[dev]
base_url = "http://localhost:8080"
port = 8080
[deploy]
host = "your-server.local"
user = "deploy"
path = "/var/www/site"
[fediverse]
enabled = false
mastodon_instance = "https://mastodon.social"
mastodon_token = ""
auto_syndicate = false
default_hashtags = ["#DIY", "#makers"]
[site]: Site Metadata
| Field | Type | Description |
|---|---|---|
name | string | Site title, shown in header and page titles |
url | string (optional) | Production URL. Used for absolute links in feeds and sitemaps |
description | string | Short site description, shown below the site name in the header |
language | string | Language code for the HTML lang attribute (default: en) |
author | string | Default author name for projects without an explicit author in frontmatter. Also used in the footer copyright |
about | string (optional) | URL to your about page. Sets the u-url on your author h-card for IndieWeb identity verification |
email | string (optional) | Contact email. Shown as a “Reply via email” link on project pages |
source_repo | string (optional) | Repository URL. Enables “Edit This Page” links that point to the source markdown |
Fediverse Handles (under [site.fediverse])
These live under [site.fediverse] and are optional: they enable sharing features on project pages.
| Field | Type | Description |
|---|---|---|
mastodon | string (optional) | Your full Mastodon handle (for example, @username@mastodon.social). Enables a “Share on Mastodon” link on project pages |
pixelfed | string (optional) | Your Pixelfed username (for photo gallery integration) |
peertube | string (optional) | Your PeerTube channel |
bluesky | string (optional) | Your Bluesky handle (secondary) |
[build]: Build Settings
| Field | Type | Default | Description |
|---|---|---|---|
output_dir | string | ./output | Where the generated site is written |
theme | string | default | Theme name (currently unused, single built-in theme) |
drafts | boolean | false | Include projects with status: draft in builds |
parallel_jobs | integer | number of CPU cores | How many projects to build concurrently |
incremental | boolean | true | Skip projects whose source has not changed since last build |
base_url | string (optional) | none | Set this for production deployments where the site lives at a subpath |
[webmention]: Webmention Settings
Webmentions let other sites notify you when they link to your projects. Requires endpoint to be configured.
| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | Enable webmention sending |
endpoint | string | https://webmention.io/yourdomain.com/webmention | Your webmention endpoint (get one at webmention.io) |
require_approval | boolean | true | Hold incoming webmentions for review instead of publishing automatically |
notify | boolean | false | Send email notifications for new webmentions (requires your endpoint to support this) |
[dev]: Development Settings
Configure the local development server.
| Field | Type | Default | Description |
|---|---|---|---|
base_url | string (optional) | none | Base URL for local development (e.g., http://localhost:8080) |
port | integer | 8080 | Port for the local development server |
[deploy]: Deployment Settings
Configure remote deployment via listsome deploy.
| Field | Type | Default | Description |
|---|---|---|---|
host | string (optional) | none | Remote host (e.g., your-server.local) |
user | string (optional) | none | SSH user for remote deployment |
path | string (optional) | none | Remote path where the site will be deployed |
port | string (optional) | none | Port for accessing the deployed site (used in output messages) |
[fediverse]: Fediverse Syndication
Configure automatic posting to Mastodon when you publish or update a project.
| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Enable auto-syndication |
mastodon_instance | string | https://mastodon.social | Your Mastodon instance URL |
mastodon_token | string | (empty) | Your Mastodon API token |
auto_syndicate | boolean | false | Automatically post when a project is published or updated |
default_hashtags | array | ["#DIY", "#makers"] | Hashtags to include in syndicated posts |
To get a Mastodon API token: settings → Development → New Application, then copy the access token.
Author Fallback
When creating a project, the author is determined in this order:
--authorflag onlistsome newauthorfield in the project’s frontmattersite.authorfromlistsome.tomlUSERorUSERNAMEenvironment variable"Anonymous"as final fallback
Command-line Tool Flags
These fields can also be set via command-line tool flags on listsome init:
| Flag | Config Field |
|---|---|
--name | site.name |
--author | site.author |
--url | site.url |
--about | site.about |
Roadmap
Current State (v0.1.0)
Listsome is functional for creating and publishing DIY project sites. See overview for what works today.
Planned
The following features are documented in design specs but not yet implemented:
Short Term
- Webmention support: Fetch, display, and send webmentions
- Image optimization: Automatic resizing and WebP/AVIF conversion
- Syntax highlighting: Code blocks with syntect-based highlighting
- Search index: Full-text search without external services
- Category and tag pages: Auto-generated listing pages per category/tag
Longer Term
- POSSE syndication: Publish to Mastodon, Pixelfed, PeerTube from the command-line tool
- Print stylesheets: Built-in print-friendly CSS for generated sites
- Import tools: Import from Instructables, Hackaday, and other platforms
- Video support: Transcoding and thumbnail generation
- Multi-language sites: i18n support for international makers
- Plugin system: Extend Listsome with custom generators and themes
- 3D model viewer: Inline STEP/STL viewing in project pages
Design Status
- IndieWeb integration: Documented; microformats2 markup is generated by default templates, webmention command-line tool is stubbed
- Fediverse syndication: Configuration model exists in
listsome.toml, no implementation - Interactivity: Research done on webmentions, email replies, and Giscus integrations for adding comments/reactions to project pages
Contributors Guide
Architecture
Listsome follows a pipeline architecture: source content → parse → render → output.
projects/*/index.md ──► Parser ──► Renderer (MiniJinja) ──► output/
(frontmatter + MD) (pulldown-cmark) + (static HTML)
templates/
Modules
| Module | Responsibility |
|---|---|
src/cli.rs | Command-line argument parsing with clap |
src/commands.rs | Command implementations that wire up the pipeline |
src/config.rs | listsome.toml loading, default config creation |
src/parser/mod.rs | Frontmatter extraction, Markdown→HTML, step/BOM parsing |
src/render/mod.rs | MiniJinja template rendering, context builders |
src/generator/mod.rs | Site generation orchestration (discover, parse, render, copy assets, generate index + feed) |
src/server/mod.rs | Axum-based dev server with WebSocket live reload and file watching |
src/model/project.rs | Project, Step, ProjectMetadata, Difficulty, ProjectStatus |
src/model/site.rs | SiteConfig, BuildConfig, WebmentionConfig, FediverseConfig |
src/error.rs | ListsomeError enum with typed error variants |
src/templates/ | Built-in MiniJinja templates (base.html, index.html, project.html, feed.atom) |
Data Flow
listsome buildloadslistsome.tomlviaconfig::find_and_load_config()generator::SiteGenerator::discover_projects()walksprojects/looking forindex.md- Each file is parsed by
parser::parse_project()which extracts frontmatter and renders Markdown to HTML - Headers matching
## Step N: TitlebecomeStepstructs; BOM tables becomeMaterialItems render::TemplateRendererrenders each project into HTML using MiniJinja templates- Assets from
projects/<slug>/assets/are copied tooutput/<slug>/assets/ output/index.html(project listing) andoutput/feed.atomare generated
Design Patterns
- Pure functions with no shared mutable state
Result<T, ListsomeError>throughout for error handling- Immutable
ProjectandProjectMetadatastructs SiteConfigwith sensible defaults viaDefaulttrait- Template renderer is created once and reused across all projects
Building
cargo build
cargo build --release # Optimized binary
Testing
cargo test # Run all tests
cargo test -- --nocapture # Show test output
Tests are in #[cfg(test)] modules within each source file (parser, render, generator, config, server).
Code Style
cargo clippy: Pedantic and nursery lints enabled (warnings during dev, not errors)cargo fmt: 100-character line width, Unix line endings- Conventional commit messages for pull requests
How to Contribute
- Open an issue to discuss your proposed change
- Fork the repository
- Make your changes with tests
- Run
cargo testandcargo clippy - Submit a pull request