A lightweight local utility that converts .docx and .pdf files to Markdown via a drag-and-drop web interface.
╔══════════╗
║ 10110100 ║
║ 01001011 ╠═~~> # Heading
║ 11010110 ║ **bold** _italic_ `code`
╚══════════╝ ─ list · > blockquote
To reduce token usage and processing time when using AI.
AI chat and agent interfaces — Claude, ChatGPT, Copilot, etc. — work natively with text. Uploading a Word document or PDF requires the model to use tools, consume extra tokens, and spend time parsing a binary format before it can reason about the content. Markdown is plain text: the model reads it directly, with no overhead.
This tool sits in your local browser. Drop a file in, get a .md file back. The resulting Markdown can be pasted into a chat, attached as a text file, or fed to an agent — all with lower token cost and faster response times than the original binary.
You can set the container to autostart with Docker Desktop, open it in a browser tab. It's a handy utility to have as you work.
- Drag-and-drop (or click-to-browse) file selection
- Converts
.docxand.pdfto Markdown - Preserves headings, bold, italic, lists, and tables where possible
- Favors content integrity over formatting fidelity
- Warns if a PDF appears to contain only scanned images (no text layer)
- Download filename matches the original file, with an
.mdextension - Runs entirely locally — no files leave your machine
- Docker Desktop (Windows or Mac)
No other local dependencies are required to run the app.
1. Clone the repository
git clone https://github.com/TechIsSoCool/decant.git
cd decant2. Build and start the container
docker compose up --build3. Open the app
Navigate to http://localhost:8000 in your browser.
4. Stop the container
docker compose downIf port 8000 is already in use, set the PORT environment variable before starting:
PORT=8080 docker compose up --buildThe app will be available at http://localhost:8080.
Alternatively, change the PORT value in the .env file before building and running docker compose up --build.
- Open the app in your browser
- Drag a
.docxor.pdffile onto the drop zone, or click the zone to browse for a file - Wait for conversion (a spinner appears during processing)
- The converted
.mdfile downloads automatically with the same base filename as the original - Drop another file at any time — the app is ready immediately after each conversion
File size limit: 100 MB per file. (Can be changed in main.py by changing MAX_FILE_UPLOAD_SIZE_MB)
Unsupported files: Any file that is not a .docx or .pdf is rejected with an error message.
No content is sent to a server — everything runs locally in Docker.
If you want to modify this code, you can use the existing tests as a starting point. Unit and API tests run against the Python code directly and do not require Docker or a running server.
Install dependencies
pip install -r requirements.txt -r requirements-test.txtRun unit and API tests
pytest tests/test_converter.py tests/test_api.py -vBrowser integration tests
Integration tests drive a real browser against the running app. They require Playwright and a running container.
Install Playwright:
pip install playwright
playwright install chromiumStart the app (if not already running):
docker compose up --buildRun the integration tests:
pytest tests/test_integration.py -vIntegration tests are skipped automatically if Playwright is not installed or the app is not reachable at
localhost:8000.
Run all tests at once
pytest -v- Images embedded in
.docxor.pdffiles are not included in the Markdown output.- They don't provide additional value in most use cases, and handling the image file locations in markdown is additional scope that wasn't justifiable for me.
- Scanned PDFs (image-only, no text layer) produce empty or minimal output — a warning is shown
- Not hardened for public deployment; intended for local, single-user use only
