Skip to content

petertzy/markdown-reader

Repository files navigation

Markdown Reader

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.


Features

  • 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.

Editor Overview

Image

Preview Overview

Image

Installation & Usage

1. Clone the repository

git clone https://github.com/petertzy/markdown-reader.git
cd markdown-reader

2. Create a virtual environment (recommended)

python -m venv venv
source venv/bin/activate  # macOS/Linux
# .\venv\Scripts\activate  # Windows (cmd/powershell)

3. Install dependencies

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 sync

If you update dependencies in pyproject.toml, regenerate and commit the lockfile:

uv lock

CI uses uv sync --locked --extra dev, so uv.lock must stay in sync with pyproject.toml.

Or with pip:

pip install .

4. Install pre-commit hooks (recommended)

This project uses pre-commit to automatically fix import sorting and code formatting on every commit. Run this once after cloning:

pre-commit install

After this, ruff --fix and ruff-format will run automatically on staged files whenever you git commit.


Running the Application

python app.py

How to Use

  • Open File: Choose .md, .markdown, .html, .htm, or .pdf from the "File → Open File" menu.
  • Open with Double-Click: Double-clicking a .md file opens it directly with the app and displays the document with a real-time preview.
  • Editor-Only Mode: To open a .md file 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 summary
      • generate table of contents
      • format this section
      • format 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"

Packaging as a macOS App (Optional)

To bundle this app as a .app, use the included setup.py script:

Build the App

rm -rf build dist
python setup.py py2app

The 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.


Exit the Virtual Environment

deactivate

Code Formatting (Ruff)

This project uses Ruff to ensure consistent code formatting across all contributions.

Before submitting changes, please check and format your code.

Check formatting

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.

Fix formatting

To automatically format all files:

uv run ruff format .

Running Tests

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 tests

Or with pip:

python -m unittest discover -s tests

Run a single test file:

uv run python -m unittest tests/test_ai_automation_logic.py

Test coverage overview

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.py and test_ai_agent_selection_only_integration.py import markdown_reader.ui, which loads WeasyPrint at import time. WeasyPrint requires native system libraries (pango, cairo, gdk-pixbuf). If you see an OSError: cannot load library 'libgobject-2.0-0' error, follow the steps in PrepareForMacUser.md first. test_ai_automation_logic.py and test_ai_provider_config_logic.py have no system library dependencies and can always run.


Development Workflow: uv vs. pip / requirements.txt

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.

The old approach (requirements.txt + pip)

python -m venv venv
source venv/bin/activate
pip install -r requirements.txt

requirements.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.
  • pip resolves the full dependency graph from scratch on every install, which is slow.

The new approach (pyproject.toml + uv)

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 under docs. You only install what you need.
  • Lockfile (uv.lock): records exact versions of every direct and transitive package. uv sync --locked guarantees identical environments across all machines and in CI — no more "works on my machine" surprises.
  • Speed: uv is written in Rust and resolves and installs packages significantly faster than pip.
  • Single source of truth: tool configuration for Ruff, ty, and isort also lives in pyproject.toml, eliminating separate config files.

When to update the lockfile

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.


Submit Changes to Git

git add .
git commit -m "Update"  # Replace "Update" with a meaningful commit message
git push

Technical Details

  • GUI: tkinter, ttkbootstrap
  • Markdown Engine: markdown2
  • HTML Preview: Dynamically generated HTML opened in the default browser
  • File Conversion: html2text for HTML, PyMuPDF and docling for PDF import (pypdf is used as a fallback when PyMuPDF is unavailable)
  • PDF Export: weasyprint
  • AI Translation: requests for 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

System Requirements:

Python >= 3.10


AI-powered translation:

To enable AI-powered translation features, you need to set up API keys:

  1. Open Settings -> AI Provider & API Keys....
  2. Choose provider and model.
  3. Enter API key and save.
  4. If you choose OpenAI Compatible, you can also choose a Base URL option (Navidia or Groq).

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:

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.


License

This project is licensed under the MIT License.
See the LICENSE file for the full text.


Contributing

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.


Contributors

We are grateful to all our contributors who have made this project better! Special thanks to:

Major Contributors:

All Contributors: See CONTRIBUTORS.md for the complete list of all contributors including:

For a detailed breakdown, please visit CONTRIBUTORS.md.

About

Cross-platform Markdown editor with tabbed editing, real-time preview, AI-powered translation (OpenAI Compatible/OpenRouter/OpenAI/Anthropic), dockable AI chat with audit trail, automation templates, advanced table editor, dual PDF conversion modes, dark mode, and smart provider failover—built with Python and Tkinter.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages