Markdown Reader is a clean and intuitive Markdown editor/reader with real-time preview support and dark mode toggle. It is compatible with macOS and Windows desktop environments and built with pure Python and Tkinter.
- Tabbed Markdown editing with real-time HTML preview.
- AI-Powered Translation: Translate Markdown documents while preserving formatting (supports OpenAI Compatible, OpenRouter, OpenAI, and Anthropic).
- Built-in AI Agent Chat: A dockable in-app chat panel can read current document context and suggest/apply edits.
- AI Task Automation Templates: Run reusable templates for formatting, TOC generation, summaries, and code-block cleanup.
- Approval/Reject Workflow: Every AI-proposed change can be explicitly applied or rejected from the panel.
- AI Audit Trail: Proposed/applied/rejected/undone AI actions are logged for auditing and rollback tracking.
- API Key Management: Missing or rejected provider keys trigger an in-app dialog, and keys are saved in the OS credential store.
- Progressive Translation Rendering: Long translations are split into smaller chunks, inserted into the editor as they complete, auto-scrolled into view, and tracked with a progress bar.
- Dark mode toggle.
- Advanced Table Editor: Interactive table insertion with customizable rows and columns.
- Dual PDF Conversion: Fast PyMuPDF mode or advanced Docling mode for complex documents.
- Built with pure Python, Tkinter, and ttkbootstrap — cross-platform.
- Can be bundled as a macOS app using
py2app. - Opens preview automatically and avoids multiple browser tabs for a smoother experience.
- Multi-provider AI failover: Automatically switches to fallback providers on rate-limit/auth/server errors.
git clone https://github.com/petertzy/markdown-reader.git
cd markdown-readerpython -m venv venv
source venv/bin/activate # macOS/Linux
# .\venv\Scripts\activate # Windows (cmd/powershell)For Mac users, you should first complete the preparation steps described in the PrepareForMacUser file.
For Windows users, WeasyPrint requires additional system libraries. Complete the preparation steps described in the PrepareForWindowsUser file before installing dependencies.
With uv (recommended):
uv syncIf you update dependencies in pyproject.toml, regenerate and commit the lockfile:
uv lockCI uses uv sync --locked --extra dev, so uv.lock must stay in sync with pyproject.toml.
Or with pip:
pip install .This project uses pre-commit to automatically fix import sorting and code formatting on every commit. Run this once after cloning:
pre-commit installAfter this, ruff --fix and ruff-format will run automatically on staged files whenever you git commit.
python app.py- Open File: Choose
.md,.markdown,.html,.htm, or.pdffrom the "File → Open File" menu. - Open with Double-Click: Double-clicking a
.mdfile opens it directly with the app and displays the document with a real-time preview. - Editor-Only Mode: To open a
.mdfile in the editor without automatically generating a web preview, configure the double-click behavior via "Settings → Open Behavior → Editor Only". - Dark Mode: Toggle via "View → Toggle Dark Mode".
- Preview: Automatically opens in your web browser, only one tab is opened per session.
- AI Translation:
- Translate selected text: "Edit → Translate with AI → Translate Selected Text with AI"
- Translate full document: "Edit → Translate with AI → Translate Full Document with AI"
- Select source and target languages from dropdown menus
- Configure provider/model/API keys: "Settings → AI Provider & API Keys..."
- If a key is missing or rejected, the app prompts for the correct provider key and stores it in the OS credential store
- Large translations appear progressively in the editor with automatic scrolling and a visible progress bar
- AI Agent Chat Panel:
- Show/hide: "View → Show AI Agent Panel"
- You can type natural-language requests directly in the chat box; no special command syntax is required
- The AI agent can automatically prepare edit suggestions for tasks such as summaries, formatting, TOC generation, and code-block fixes
- Typical chat requests:
generate summarygenerate table of contentsformat this sectionformat code blocks and correct syntax
- Supports commands like "format this section", "generate summary", and "generate table of contents"
- Built-in automation templates can trigger repetitive editor tasks quickly
- Can format and fix Markdown code blocks (including unclosed fences)
- Context scope toggle: "Full document" or "Selection only"
- Uses current document and/or selected text as context based on the selected scope
- Workflow: send a natural-language request -> review AI preview -> click "Apply Suggestion" to apply
- AI suggestions can be applied or rejected with confirmation and validation checks
- "Undo AI Task" allows one-click rollback of the latest applied AI change
- "Audit Log" shows recent AI automation events for traceability
- Chat history is stored per document and restored automatically
- Table Insertion: Use "Table → Insert Table" to create custom tables with interactive cell editing.
- PDF Conversion Modes:
- Fast mode (default): Uses PyMuPDF for quick extraction
- Advanced mode: Enable "Tools → Use Advanced PDF Conversion (Docling)" for complex documents
- View mode info: "Tools → PDF Converter Info"
To bundle this app as a .app, use the included setup.py script:
rm -rf build dist
python setup.py py2appThe generated app will be located in the dist/ folder. You can launch it by double-clicking. To use it like a regular app, move it to your Applications folder.
deactivateThis project uses Ruff to ensure consistent code formatting across all contributions.
Before submitting changes, please check and format your code.
uv run ruff format --check .If any files are not properly formatted, you will see output like:
Would reformat: some_file.py
This means the file does not match the required formatting style.
To automatically format all files:
uv run ruff format .The project uses Python's built-in unittest framework. Tests live in the tests/ directory and cover AI automation logic, UI helper methods, provider configuration, and agent interaction workflows.
Run the full test suite:
uv run python -m unittest discover -s testsOr with pip:
python -m unittest discover -s testsRun a single test file:
uv run python -m unittest tests/test_ai_automation_logic.py| Test file | What it covers |
|---|---|
test_ai_automation_logic.py |
Offline fallback logic for TOC generation, summarization, code-block formatting, and task template listing |
test_ai_agent_ui_helpers.py |
UI helper methods: payload validation, chat history migration, audit log capping, suggestion apply/reject |
test_ai_agent_selection_only_integration.py |
Integration path for selection-only mode — verifies correct error handling when no text is selected |
test_ai_provider_config_logic.py |
Provider configuration and API key management logic |
Tests run automatically on every push and pull request via GitHub Actions (see .github/workflows/python-ci.yml).
macOS note:
test_ai_agent_ui_helpers.pyandtest_ai_agent_selection_only_integration.pyimportmarkdown_reader.ui, which loads WeasyPrint at import time. WeasyPrint requires native system libraries (pango, cairo, gdk-pixbuf). If you see anOSError: cannot load library 'libgobject-2.0-0'error, follow the steps in PrepareForMacUser.md first.test_ai_automation_logic.pyandtest_ai_provider_config_logic.pyhave no system library dependencies and can always run.
This project previously used a requirements.txt file and standard pip commands. It has since migrated to uv with pyproject.toml. Here is what changed and why.
python -m venv venv
source venv/bin/activate
pip install -r requirements.txtrequirements.txt is a flat list of pinned packages. It works, but has limitations:
- No distinction between direct dependencies and transitive ones — everything is listed together.
- No separation of dev-only dependencies (linters, test runners) from runtime dependencies.
- No built-in lockfile mechanism — the file must be manually regenerated and tends to drift.
pipresolves the full dependency graph from scratch on every install, which is slow.
uv sync # install all runtime dependencies from the lockfile
uv sync --locked --extra dev # also install dev tools (ruff, ty, pre-commit)pyproject.toml is the modern Python packaging standard (PEP 517/518/621). It consolidates project metadata, runtime dependencies, and tool configuration in one file. Key benefits over requirements.txt:
- Dependency groups: runtime deps go under
[project.dependencies]; dev-only tools (Ruff, ty, pre-commit) go under[project.optional-dependencies] dev; docs tools underdocs. You only install what you need. - Lockfile (
uv.lock): records exact versions of every direct and transitive package.uv sync --lockedguarantees identical environments across all machines and in CI — no more "works on my machine" surprises. - Speed:
uvis written in Rust and resolves and installs packages significantly faster thanpip. - Single source of truth: tool configuration for Ruff, ty, and isort also lives in
pyproject.toml, eliminating separate config files.
If you add or change a dependency in pyproject.toml, regenerate the lockfile and commit it:
uv lock
git add pyproject.toml uv.lock
git commit -m "chore: update dependencies"CI runs uv sync --locked, so a stale uv.lock will fail the build — this is intentional and keeps the lockfile honest.
git add .
git commit -m "Update" # Replace "Update" with a meaningful commit message
git push- GUI:
tkinter,ttkbootstrap - Markdown Engine:
markdown2 - HTML Preview: Dynamically generated HTML opened in the default browser
- File Conversion:
html2textfor HTML,PyMuPDFanddoclingfor PDF import (pypdfis used as a fallback when PyMuPDF is unavailable) - PDF Export:
weasyprint - AI Translation:
requestsfor API communication with OpenAI Compatible, OpenRouter, OpenAI, and Anthropic - Configuration: Per-user settings JSON for provider/model (macOS
~/Library/Application Support/MarkdownReader/settings.json; Windows%APPDATA%/MarkdownReader/settings.json; Linux~/.config/markdown-reader/settings.json) plus OS credential storage for API keys (keyring) - Auto-failover: Automatically switches AI providers when the primary provider returns rate-limit/auth/server errors
Python >= 3.10
To enable AI-powered translation features, you need to set up API keys:
- Open
Settings -> AI Provider & API Keys.... - Choose provider and model.
- Enter API key and save.
- If you choose
OpenAI Compatible, you can also choose a Base URL option (NavidiaorGroq).
The app stores provider and model in a per-user JSON file:
- macOS:
~/Library/Application Support/MarkdownReader/settings.json - Windows:
%APPDATA%/MarkdownReader/settings.json - Linux:
~/.config/markdown-reader/settings.json
How to get API keys:
- OpenRouter (recommended for free tier): openrouter.ai
- OpenAI: platform.openai.com
- Anthropic: console.anthropic.com
- OpenAI Compatible: Create an API key from your chosen compatible provider console, then select the matching Base URL preset in this app:
- Navidia preset: NVIDIA Integrate
- Groq preset: Groq Console
API keys are saved in the OS credential store (Keychain on macOS, Credential Manager on Windows, Secret Service/KWallet on Linux when available).
The app will automatically switch to a fallback provider when the primary provider returns rate-limit/auth/server errors.
This project is licensed under the MIT License.
See the LICENSE file for the full text.
All contributions are welcome, including:
- Bug reports
- Feature suggestions
- Pull requests
- Documentation improvements
Please see our CONTRIBUTING guide for more details on how to get started, submit changes, or report issues.
We are grateful to all our contributors who have made this project better! Special thanks to:
Major Contributors:
- @MatthewEmer - 22 commits
- @Dazaa1 - 10 commits
- @PMaruthiPrasad - Help menu feature
- @lwu1822 - MkDocs documentation setup
All Contributors: See CONTRIBUTORS.md for the complete list of all contributors including:
- @larikardoso
- @metergames
- @gustavo-szesz
- @ferrandj
- @nwankwolinus
- @Xavi1
- @CarlosHCS001
- @jonaebel
- @Poorna-Raj
- @NalinKaushik
- @apoorvdarshan
- @raza-khan0108
- @O-sama12
- And many more!
For a detailed breakdown, please visit CONTRIBUTORS.md.