Bidirectional converter between GitHub Flavored Markdown and Atlassian Document Format
Project description
Marklas
Lossless bidirectional converter between Markdown and Atlassian Document Format (ADF).
Why Marklas?
Confluence and Jira store documents in ADF — a rich JSON structure with panels, layouts, mentions, colored text, and more. Standard Markdown can only represent a subset of these features.
Marklas defines a union AST that covers both specs, then converts in both directions through it:
Markdown ⇄ Union AST ⇄ ADF
Nodes shared by both formats (paragraphs, headings, lists, tables, etc.) map directly. ADF-only nodes (panels, mentions, colored text, etc.) are embedded as invisible HTML comment annotations in the Markdown output, so the full structure survives a roundtrip:
ADF → Markdown (with annotations) → ADF ✅ lossless
Without annotations, standard Markdown elements still convert to valid ADF — just without the ADF-specific extras:
Plain Markdown → ADF ✅ works (standard elements only)
How Annotations Work
When ADF contains features that Markdown can't express natively (e.g., panels, mentions, colored text), Marklas wraps a readable Markdown fallback in HTML comment annotations:
<!-- adf:panel {"panelType": "info"} -->
This is an info panel — readable as plain Markdown.
<!-- /adf:panel -->
User <!-- adf:mention {"id": "abc123", "text": "@John"} -->`@John`<!-- /adf:mention --> approved this.
These annotations are invisible when rendered as Markdown (GitHub, editors, etc.), but Marklas can parse them back to reconstruct the original ADF structure exactly.
Installation
pip install marklas
Usage
from marklas import to_adf, to_md
Markdown → ADF
Any standard Markdown converts to valid ADF:
adf = to_adf("""
## Project Update
The release is **on track**. Key changes:
- Refactored auth module
- Fixed 3 critical bugs
| Component | Status |
| --------- | ------ |
| Backend | Done |
| Frontend | WIP |
""")
ADF → Markdown
ADF-only features (panels, mentions, colored text, etc.) are preserved as HTML comment annotations — invisible in rendered Markdown, but fully restorable:
md = to_md(adf_with_panel)
<!-- adf:panel {"panelType": "warning"} -->
Do **not** deploy on Fridays.
<!-- /adf:panel -->
Roundtrip
original_adf = fetch_confluence_page() # complex ADF
markdown = to_md(original_adf) # edit in any Markdown editor
restored_adf = to_adf(markdown) # push back — structure preserved
Token Efficiency
Markdown is significantly more compact than ADF JSON — critical for LLM-based workflows where every token counts.
| Format | Tokens | Bytes |
|---|---|---|
| ADF JSON | 89,374 | 523 KB |
| Markdown | 21,798 | 49 KB |
| Reduction | 4.1x | 10.6x |
Measured on a real Confluence page using GPT-4o tokenizer (tiktoken).
Notes
- Table cells: Non-paragraph content inside table cells (lists, code blocks, etc.) is converted to inline HTML (
<ul>,<code>,<br>) to fit within GFM table syntax. - Markdown-only features: Raw HTML blocks/inlines and other Markdown-specific constructs that have no ADF equivalent are silently dropped during conversion.
Development
uv sync --extra dev
uv run pytest -v
uv run black src/ tests/
Project details
Release history Release notifications | RSS feed
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 marklas-0.4.1.tar.gz.
File metadata
- Download URL: marklas-0.4.1.tar.gz
- Upload date:
- Size: 21.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
08364c9e8110ebf74d765417840552266afd3e888e2d98b1cf0a8f4515ade1f9
|
|
| MD5 |
01abe7a0eb90d2072b9aa93c65e98996
|
|
| BLAKE2b-256 |
1bc37f592dc29c2bc136e7870a5f3024ee16062889b9b1dae4f1e0365164a0ab
|
Provenance
The following attestation bundles were made for marklas-0.4.1.tar.gz:
Publisher:
publish.yml on byExist/marklas
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
marklas-0.4.1.tar.gz -
Subject digest:
08364c9e8110ebf74d765417840552266afd3e888e2d98b1cf0a8f4515ade1f9 - Sigstore transparency entry: 1059145569
- Sigstore integration time:
-
Permalink:
byExist/marklas@cfe487f7fafa85181afbee9bd90764bc192eb4f6 -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/byExist
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cfe487f7fafa85181afbee9bd90764bc192eb4f6 -
Trigger Event:
release
-
Statement type:
File details
Details for the file marklas-0.4.1-py3-none-any.whl.
File metadata
- Download URL: marklas-0.4.1-py3-none-any.whl
- Upload date:
- Size: 26.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
624024992056413e699d0cd8603fdef455330f3d868d7fa0aeeb03f0787ea63f
|
|
| MD5 |
6fa03b0f711ffd902278c09225da9b5c
|
|
| BLAKE2b-256 |
d56523af0ae6fe62edb254dc1e7deee3a7e2215e6ed3b1af02b843b8981a54f7
|
Provenance
The following attestation bundles were made for marklas-0.4.1-py3-none-any.whl:
Publisher:
publish.yml on byExist/marklas
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
marklas-0.4.1-py3-none-any.whl -
Subject digest:
624024992056413e699d0cd8603fdef455330f3d868d7fa0aeeb03f0787ea63f - Sigstore transparency entry: 1059145577
- Sigstore integration time:
-
Permalink:
byExist/marklas@cfe487f7fafa85181afbee9bd90764bc192eb4f6 -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/byExist
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cfe487f7fafa85181afbee9bd90764bc192eb4f6 -
Trigger Event:
release
-
Statement type: