A minimalistic, self-hosted, statically rendered, micro-blogging engine.
Project description
readthememo
A minimalistic, self-hosted, statically rendered, micro-blogging engine.
Overview
readthememo (command: memos) is a simple tool that transforms memos into static HTML pages.
It's designed for quick, low-friction publishing of short-form content without the complexity of traditional blogging platforms.
Features
- Minimal dependencies: Built with Python 3.11+ using Jinja2, marko, and tyro
- Static output: Generates standalone HTML files
- CommonMark (markdown) input: Write your content in a simple, readable format
- LogSeq-ready: Directory format works seamlessly with LogSeq
- Responsive design: Uses Pico CSS for clean, semantic styling
- CLI interface: Simple command-line tool for building your site
- Self-contained: No database or server requirements
Installation
pipx install readthememo
Usage
Basic Usage
Build HTML from a single CommonMark file:
memos path/to/your/memos.md output.html
Build HTML from a directory with CommonMark files (e.g. LogSeq journals):
memos path/to/your/memos/ output.html
Standard Input/Output
Read from stdin and write to stdout:
cat memos.md | memos - > output.html
CommonMark Format
site.yaml:
---
title: "My Blog Title"
meta:
author: "Your Name"
header:
headline: "My Blog"
motto:
- "A description of your blog"
- "by Your Name"
---
2025-01-01.md:
## My First Memo #InterstingTopic
This is the content of my first memo.
It can contain <strong>HTML tags</strong> and multiple paragraphs.
2025-01-02.md:
- ## Hello from LogSeq
- This memo is bullet point heavy, as can be seen when reading raw content from a LogSeq journal.
- The idea is to strip the first two levels of bullet points to "flatten" the structure.
Key features:
- Natural prose: Write directly in markdown (CommonMark)
- LogSeq compatible: Use
YYYY-MM-DD.mdorYYYY_MM_DD.mdnaming; strip the first two levels of bullet-points - Tag extraction: Add tags to titles using
#tagformat
Alternative: Single File with YAML Frontmatter
You can also use a single markdown file with YAML frontmatter:
---
title: "My Blog Title"
meta:
author: "Your Name"
header:
headline: "My Blog"
motto:
- "A description of your blog"
- "by Your Name"
---
# 2025-01-01
## My First Memo
This is the content of my first memo.
It can contain <strong>HTML tags</strong> and multiple paragraphs.
# 2025-01-02
## Another Memo #tech #thoughts
This memo has tags listed after the title.
This format is useful for smaller blogs or when you prefer to keep everything in one file.
LogSeq Integration
The directory structure is designed to work seamlessly with LogSeq:
Setup:
- No special care needed for LogSeq
- Create a
site.yamlfile at the base of your LogSeq graph
title: "My Blog Title"
meta:
author: "Your Name"
header:
headline: "My Blog"
motto:
- "A description of your blog"
- "by Your Name"
LogSeq workflow:
- Open your memos directory as a LogSeq graph
- Create daily notes using LogSeq's journal feature
- Use
## Title #tagformat for your memo titles - Write content underneath (nested)
- Repeat for other articles on the same day
Publishing workflow:
memos path/to/logseq-graph > output.html
File naming compatibility:
- LogSeq uses
YYYY_MM_DD.mdformat by default (underscores), but will happily use files named using hyphens (YYYY-MM-DD.md) readthememosupports bothYYYY-MM-DD.mdandYYYY_MM_DD.md
LogSeq features that work:
- Daily notes: Perfect for memo-style content
- Tags: Use
#tagin titles for automatic tag extraction - Block references: Can be used within memo content
- Linked references: Work normally within LogSeq
What gets published:
- Only files matching
YYYY-MM-DD.mdorYYYY_MM_DD.mdpatterns - Other LogSeq files (config, assets, etc.) are ignored
- Content is processed as standard markdown with HTML passthrough
Project Structure
├── src/readthememo/ # Main application code
│ ├── templates/ # Jinja2 HTML templates
│ ├── cli.py # Command-line interface
│ ├── core.py # Core parsing and rendering logic
│ └── templates.py # Template environment setup
├── static/ # CSS and static assets
├── tests/ # Test suite
├── docs/decisions/ # Architecture Decision Records
└── memos/ # Example memos documenting this project itself
Development
You will need uv, install it first through your method of choice.
For example with pipx:
pipx install uv
Running Tests
uv run pytest
Code Quality
The project uses ruff for linting and formatting:
uv run ruff check
uv run ruff format
Pre-commit Hooks
Set up pre-commit hooks:
uv run pre-commit install
Architecture
This project follows a functional core, imperative shell architecture:
- Core: Pure functions for parsing TOML and rendering HTML
- Shell: CLI interface and I/O operations
- Templates: Jinja2 templates for HTML generation
See docs/decisions/ for detailed architecture decisions.
Versioning and Publishing
Version Management
This project follows Calendar Versioning using the YYYY.PATCH format:
- Year: Current calendar year (e.g.,
2025) - Patch: Incremental release number within the year (e.g.,
8) - Example:
2025.8
To create a new release:
-
Update the version:
uv version --frozen --bump patch uv lock
-
Update the changelog in
CHANGELOG.md:- The information about the should be there already
- What should be left is to add the release information:
sed -i --posix -e "/## \[Unreleased]/ a \\\n## [$(uv version | awk '{print $2}')] - $(date --iso=date)" CHANGELOG.md
-
Commit the changes:
git commit -m "build: Release version $(uv version | awk '{print $2}')" pyproject.toml uv.lock CHANGELOG.md
Publishing to PyPI
-
Build the package using
uv:# Clean-up previous builds git clean -xdf dist/ # Build now uv build
-
Ensure the
keyringtool is available for the tokens, and define the tokens (only once per machine or after the tokens are refreshed):uv tool install keyring keyring set https://test.pypi.org/legacy/ __token__ keyring set https://upload.pypi.org/legacy/ __token__
-
Publish to PyPI (for test releases):
uv publish --index testpypi --username __token__
-
Publish to PyPI:
uv publish --username __token__
Automated CI/CD
The project uses SourceHut Builds for continuous integration:
- Test Matrix: Runs tests across Python versions (3.11, 3.12, 3.13, and 3.14)
- Package Testing: Validates both source distributions and wheels
- Site Publishing: Automatically publishes to readthememo.app when changes are pushed to
main
All CI configuration is in the .builds/ directory.
License
Contributing
- Check existing Architecture Decision Records in
docs/decisions/ - Follow the functional core, imperative shell pattern
- Write tests for new functionality
- Ensure code passes ruff linting
- Update documentation as needed
Examples
See the memos/ directory for this very repository memos; with site.toml configuration and date-based .md files.
Also take a look at the test files (.md) in tests/cases/ for single-file examples.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file readthememo-2025.8.1.tar.gz.
File metadata
- Download URL: readthememo-2025.8.1.tar.gz
- Upload date:
- Size: 9.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eebdee344de251deb18f0e693bcaa6bf089bdbf978a2fae49f3d71c0e048448a
|
|
| MD5 |
785d0bebe5aa513201da46a4a37d3eff
|
|
| BLAKE2b-256 |
66676981a5b727c7dba4fea2d2ad3c69ad40d1ae457805640828ab75b6683ba0
|
File details
Details for the file readthememo-2025.8.1-py3-none-any.whl.
File metadata
- Download URL: readthememo-2025.8.1-py3-none-any.whl
- Upload date:
- Size: 10.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9e669c59f00cd4bb1169f7e3ad860d0b565aaee080e67fc2e0ee5abf4b2b3e5b
|
|
| MD5 |
6ee7d14876d7993dd220ea8e267f01d0
|
|
| BLAKE2b-256 |
f74ba3e785d9ebb3785842cf5063a65c00c38c9378208a6ac509ec646ace5a57
|