Skip to content

Commit 0acd51b

Browse files
feat: MarkItDown Desktop — cross-platform GUI for markitdown
- CustomTkinter GUI with file picker, URL input, batch conversion - Background threading for responsive UI during conversion - Copy / Save / Save All output actions - Dark / Light / System theme toggle - Keyboard shortcuts: Ctrl+O, Ctrl+S, Ctrl+Shift+S - GitHub Actions: auto-build .exe (Win), binary (Linux/macOS) on tag push - Supports all markitdown formats: PDF, Word, Excel, PPT, images, audio, HTML, etc. Co-Authored-By: rohandevin5@outlook.com <bossincrypto@gmail.com>
1 parent 407628a commit 0acd51b

11 files changed

Lines changed: 683 additions & 1 deletion

File tree

.github/workflows/release.yml

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
name: Build & Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
build:
13+
strategy:
14+
matrix:
15+
include:
16+
- os: windows-latest
17+
name: Windows
18+
ext: .exe
19+
- os: ubuntu-22.04
20+
name: Linux
21+
ext: ""
22+
- os: macos-latest
23+
name: macOS
24+
ext: ""
25+
runs-on: ${{ matrix.os }}
26+
27+
steps:
28+
- uses: actions/checkout@v4
29+
30+
- uses: actions/setup-python@v5
31+
with:
32+
python-version: "3.12"
33+
34+
- name: Install dependencies
35+
run: |
36+
pip install -r requirements.txt
37+
pip install pyinstaller
38+
39+
- name: Build executable
40+
run: |
41+
pyinstaller --onefile --windowed \
42+
--collect-data customtkinter \
43+
--collect-data magika \
44+
--name MarkItDown \
45+
run.py
46+
shell: bash
47+
48+
- name: Rename artifact
49+
run: |
50+
cd dist
51+
if [ "${{ matrix.ext }}" = ".exe" ]; then
52+
mv MarkItDown.exe MarkItDown-${{ matrix.name }}.exe
53+
else
54+
mv MarkItDown MarkItDown-${{ matrix.name }}
55+
chmod +x MarkItDown-${{ matrix.name }}
56+
fi
57+
shell: bash
58+
59+
- name: Upload artifact
60+
uses: actions/upload-artifact@v4
61+
with:
62+
name: MarkItDown-${{ matrix.name }}
63+
path: dist/MarkItDown-${{ matrix.name }}*
64+
65+
release:
66+
needs: build
67+
runs-on: ubuntu-latest
68+
steps:
69+
- uses: actions/download-artifact@v4
70+
with:
71+
path: artifacts
72+
73+
- name: Create GitHub Release
74+
uses: softprops/action-gh-release@v2
75+
with:
76+
generate_release_notes: true
77+
files: artifacts/**/*

.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
__pycache__/
2+
*.py[cod]
3+
*.egg-info/
4+
dist/
5+
build/
6+
*.spec
7+
.venv/
8+
venv/
9+
.env
10+
*.exe
11+
plan.md

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 BOSSincrypto
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,87 @@
1-
# markitdown-app
1+
# MarkItDown Desktop
2+
3+
Cross-platform desktop GUI for [microsoft/markitdown](https://github.com/microsoft/markitdown) — convert PDF, Word, Excel, PowerPoint, images, audio, and more to Markdown.
4+
5+
## Features
6+
7+
- **Batch conversion** — add multiple files and convert them all at once
8+
- **URL support** — paste YouTube or webpage URLs for conversion
9+
- **Live preview** — view converted Markdown output instantly
10+
- **Copy / Save / Save All** — export results with one click
11+
- **Dark & Light themes** — toggle between dark, light, and system themes
12+
- **Cross-platform** — runs on Windows, Linux, and macOS
13+
- **Lightweight** — built with CustomTkinter, no browser engine
14+
15+
## Supported Formats
16+
17+
| Category | Extensions |
18+
|----------|-----------|
19+
| Documents | PDF, DOCX, PPTX, XLSX, XLS, EPUB |
20+
| Images | JPG, PNG, GIF, BMP, TIFF |
21+
| Audio | WAV, MP3 |
22+
| Web | HTML, RSS, YouTube URLs |
23+
| Data | CSV, JSON, XML |
24+
| Other | ZIP, Outlook MSG |
25+
26+
## Download
27+
28+
Go to [Releases](../../releases) and download the binary for your platform:
29+
30+
| Platform | File |
31+
|----------|------|
32+
| Windows | `MarkItDown-Windows.exe` |
33+
| Linux | `MarkItDown-Linux` |
34+
| macOS | `MarkItDown-macOS` |
35+
36+
## Run from Source
37+
38+
Requires Python 3.10+.
39+
40+
```bash
41+
pip install -r requirements.txt
42+
python run.py
43+
```
44+
45+
## Keyboard Shortcuts
46+
47+
| Shortcut | Action |
48+
|----------|--------|
49+
| Ctrl+O | Add files |
50+
| Ctrl+S | Save current output |
51+
| Ctrl+Shift+S | Save all outputs |
52+
53+
## How It Works
54+
55+
1. Click **Add Files** (or Ctrl+O) to select files for conversion
56+
2. Optionally click **Add URL** to add YouTube or webpage URLs
57+
3. Click **Convert** to process all files in background threads
58+
4. Click on any file in the list to view its output
59+
5. Use **Copy**, **Save**, or **Save All** to export
60+
61+
## Build Executable
62+
63+
```bash
64+
pip install pyinstaller
65+
pyinstaller --onefile --windowed \
66+
--collect-data customtkinter \
67+
--collect-data magika \
68+
--name MarkItDown \
69+
run.py
70+
```
71+
72+
The executable will be in the `dist/` folder.
73+
74+
## Auto-Release
75+
76+
Push a version tag to trigger automated builds for all platforms:
77+
78+
```bash
79+
git tag v1.0.0
80+
git push origin v1.0.0
81+
```
82+
83+
GitHub Actions will build executables for Windows, Linux, and macOS and attach them to a GitHub Release.
84+
85+
## License
86+
87+
MIT

app/__init__.py

Whitespace-only changes.

app/converter.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import threading
2+
import queue
3+
from dataclasses import dataclass, field
4+
from enum import Enum
5+
from pathlib import Path
6+
from typing import Callable
7+
8+
from markitdown import MarkItDown
9+
10+
11+
class Status(Enum):
12+
PENDING = "pending"
13+
CONVERTING = "converting"
14+
DONE = "done"
15+
ERROR = "error"
16+
17+
18+
@dataclass
19+
class ConversionResult:
20+
file_path: str
21+
status: Status = Status.PENDING
22+
markdown: str = ""
23+
title: str = ""
24+
error: str = ""
25+
26+
27+
class ConverterEngine:
28+
def __init__(self):
29+
self._md = MarkItDown(enable_plugins=False)
30+
31+
def convert_file(self, file_path: str) -> ConversionResult:
32+
result = ConversionResult(file_path=file_path, status=Status.CONVERTING)
33+
try:
34+
doc = self._md.convert(file_path)
35+
result.markdown = doc.markdown
36+
result.title = doc.title or Path(file_path).stem
37+
result.status = Status.DONE
38+
except Exception as e:
39+
result.error = str(e)
40+
result.status = Status.ERROR
41+
return result
42+
43+
def convert_async(
44+
self, file_path: str, result_queue: queue.Queue
45+
) -> None:
46+
def _worker():
47+
result = self.convert_file(file_path)
48+
result_queue.put(result)
49+
50+
thread = threading.Thread(target=_worker, daemon=True)
51+
thread.start()

app/main.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import sys
2+
import os
3+
4+
5+
def main():
6+
# Magika ONNX: prefer pure-Python backend in frozen builds
7+
if getattr(sys, "frozen", False):
8+
os.environ.setdefault("MAGIKA_USE_PYTHON", "1")
9+
10+
from .ui import MainWindow
11+
12+
window = MainWindow()
13+
window.mainloop()
14+
15+
16+
if __name__ == "__main__":
17+
main()

0 commit comments

Comments
 (0)