mindmap.export module

mindmap.export.build_markdown_html(markdown_text: str) str
mindmap.export.build_markmap_data(markdown_text: str) str
mindmap.export.build_markmap_html(markmap_text: str) str
mindmap.export.build_mermaid_html(mermaid_text: str) str
mindmap.export.build_parser() ArgumentParser
mindmap.export.export_extension(export_type: str) str
mindmap.export.export_json(document: MindmapDocument) str
mindmap.export.export_mindmap(export_type: str, macos_access: str) Tuple[str, str]
mindmap.export.export_yaml(document: MindmapDocument) str
mindmap.export.main(argv: Sequence[str] | None = None, docs_dir: str | None = None) int
mindmap.export.markdown_data(document: MindmapDocument) str
mindmap.export.markdown_html(document: MindmapDocument, markdown_text: str | None = None) str
mindmap.export.markmap(document: MindmapDocument) str
mindmap.export.markmap_html(document: MindmapDocument, markmap_text: str | None = None) str
mindmap.export.mermaid(document: MindmapDocument) str
mindmap.export.mermaid_html(document: MindmapDocument, mermaid_text: str | None = None) str
mindmap.export.open_file(path: str) None
mindmap.export.resolve_output_path(output: str | None, export_type: str, docs_dir: str | None = None) str
mindmap.export.write_output(output_path: str, output: str, source: str, export_type: str) None
Source code for export.py
  1import argparse
  2import json
  3import os
  4import subprocess
  5import sys
  6import uuid
  7from typing import Optional, Sequence, Tuple
  8
  9import markdown
 10import mindmap.mindmap as mm
 11import mindmap.serialization as mms
 12
 13MARKMAP_DATA_TEMPLATE = """---
 14markmap:
 15colorFreezeLevel: {{colorFreezeLevel}}
 16initialExpandLevel: -1
 17---
 18{{markmap}}
 19"""
 20
 21MARKMAP_TEMPLATE = """
 22<div class="markmap">
 23<script type="text/template">
 24{{markmap}}
 25</script>
 26</div>
 27"""
 28
 29MERMAID_TEMPLATE = """
 30<div class="mermaid">
 31%%{init: {"theme": "light"}}%%
 32{{mermaid}}
 33</div>
 34"""
 35
 36MARKMAP_HTML_TEMPLATE = """
 37<!DOCTYPE html><html lang="en">
 38<head>
 39<meta charset="UTF-8" />
 40<meta name="viewport" content="width=device-width, initial-scale=1.0" />
 41<title>{{title}}</title>
 42<style>
 43svg.markmap{width:100%;height:100vh;}
 44</style>
 45<script src="https://cdn.jsdelivr.net/npm/markmap-autoloader@latest"></script>
 46</head><body>
 47{{body}}
 48</body></html>
 49"""
 50
 51MERMAID_HTML_TEMPLATE = """
 52<!DOCTYPE html><html lang="en">
 53<head>
 54<meta charset="UTF-8" />
 55<meta name="viewport" content="width=device-width, initial-scale=1.0" />
 56<title>{{title}}</title>
 57<style>
 58svg.mermaid{width:100%;height:100vh;}
 59</style>
 60<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
 61<script>
 62document.addEventListener('DOMContentLoaded', () => {
 63  mermaid.initialize({ startOnLoad: true });
 64});
 65</script>
 66</head><body>
 67{{body}}
 68</body></html>
 69"""
 70
 71MARKDOWN_HTML_TEMPLATE = """
 72<!DOCTYPE html><html lang="en">
 73<head>
 74<meta charset="UTF-8" />
 75<meta name="viewport" content="width=device-width, initial-scale=1.0" />
 76<title>{{title}}</title>
 77<style>
 78body{font-family:Helvetica,Arial,sans-serif;}
 79h2{margin-block-start:0;margin-block-end:0}
 80hr{margin-block-start:0;margin-block-end:0}
 81ul{margin-block-start:8px}
 82</style>
 83</head><body>
 84{{body}}
 85</body></html>
 86"""
 87
 88
 89def build_markmap_data(markdown_text: str) -> str:
 90    return MARKMAP_DATA_TEMPLATE.replace("{{colorFreezeLevel}}", "3").replace(
 91        "{{markmap}}", markdown_text
 92    )
 93
 94
 95def build_markmap_html(markmap_text: str) -> str:
 96    content = MARKMAP_TEMPLATE.replace("{{markmap}}", markmap_text)
 97    html = MARKMAP_HTML_TEMPLATE.replace("{{title}}", "Markmap")
 98    return html.replace("{{body}}", content)
 99
100
101def build_mermaid_html(mermaid_text: str) -> str:
102    content = MERMAID_TEMPLATE.replace("{{mermaid}}", mermaid_text)
103    html = MERMAID_HTML_TEMPLATE.replace("{{title}}", "Mermaid")
104    return html.replace("{{body}}", content)
105
106
107def build_markdown_html(markdown_text: str) -> str:
108    body_html = markdown.markdown(markdown_text)
109    body_html = body_html.replace("</h2>", "</h2><hr/>")
110    html = MARKDOWN_HTML_TEMPLATE.replace("{{title}}", "Mindmap")
111    return html.replace("{{body}}", body_html)
112
113
114def open_file(path: str) -> None:
115    if sys.platform.startswith("darwin"):
116        subprocess.Popen(["open", path])
117    elif sys.platform.startswith("win"):
118        os.startfile(path)  # type: ignore[attr-defined]
119    else:
120        subprocess.Popen(["xdg-open", path])
121
122
123def export_extension(export_type: str) -> str:
124    if export_type.endswith("_html"):
125        return ".htm"
126    if export_type == "json":
127        return ".json"
128    if export_type == "yaml":
129        return ".yaml"
130    if export_type == "mermaid":
131        return ".mmd"
132    if export_type in ("markmap", "markdown"):
133        return ".md"
134    return ".txt"
135
136
137def resolve_output_path(
138    output: Optional[str], export_type: str, docs_dir: Optional[str] = None
139) -> str:
140    extension = export_extension(export_type)
141    if output:
142        if os.path.isdir(output):
143            os.makedirs(output, exist_ok=True)
144            return os.path.join(os.path.abspath(output), f"{uuid.uuid4()}{extension}")
145
146        output_path = os.path.abspath(output)
147        output_dir = os.path.dirname(output_path)
148        if output_dir:
149            os.makedirs(output_dir, exist_ok=True)
150        return output_path
151
152    if docs_dir is None:
153        docs_dir = os.path.join(os.getcwd(), "docs")
154    os.makedirs(docs_dir, exist_ok=True)
155    return os.path.join(docs_dir, f"{uuid.uuid4()}{extension}")
156
157
158def mermaid(document: mm.MindmapDocument) -> str:
159    return mms.serialize_mindmap_simple(document.mindmap)
160
161
162def markmap(document: mm.MindmapDocument) -> str:
163    markdown_text = mms.serialize_mindmap_markdown(
164        document.mindmap, include_notes=False
165    )
166    return build_markmap_data(markdown_text)
167
168
169def markdown_data(document: mm.MindmapDocument) -> str:
170    return mms.serialize_mindmap_markdown(document.mindmap, include_notes=True)
171
172
173def export_json(document: mm.MindmapDocument) -> str:
174    guid_mapping = {}
175    mms.build_mapping(document.mindmap, guid_mapping)
176    data = mms.serialize_object(document.mindmap, guid_mapping)
177    return json.dumps(data, indent=1)
178
179
180def export_yaml(document: mm.MindmapDocument) -> str:
181    import yaml
182
183    guid_mapping = {}
184    mms.build_mapping(document.mindmap, guid_mapping)
185    data = mms.serialize_object(document.mindmap, guid_mapping)
186    return yaml.dump(data, sort_keys=False)
187
188
189def mermaid_html(
190    document: mm.MindmapDocument, mermaid_text: Optional[str] = None
191) -> str:
192    if mermaid_text is None:
193        mermaid_text = mermaid(document)
194    return build_mermaid_html(mermaid_text)
195
196
197def markmap_html(
198    document: mm.MindmapDocument, markmap_text: Optional[str] = None
199) -> str:
200    if markmap_text is None:
201        markmap_text = markmap(document)
202    return build_markmap_html(markmap_text)
203
204
205def markdown_html(
206    document: mm.MindmapDocument, markdown_text: Optional[str] = None
207) -> str:
208    if markdown_text is None:
209        markdown_text = markdown_data(document)
210    return build_markdown_html(markdown_text)
211
212
213def export_mindmap(export_type: str, macos_access: str) -> Tuple[str, str]:
214    document = mm.MindmapDocument(macos_access=macos_access)
215    document.get_mindmap(mode="content")
216
217    if export_type == "mermaid_html":
218        data = mermaid(document)
219        return data, mermaid_html(document, data)
220
221    if export_type == "markmap_html":
222        data = markmap(document)
223        return data, markmap_html(document, data)
224
225    if export_type == "markdown_html":
226        data = markdown_data(document)
227        return data, markdown_html(document, data)
228
229    if export_type == "json":
230        data = export_json(document)
231        return data, data
232
233    if export_type == "yaml":
234        data = export_yaml(document)
235        return data, data
236
237    if export_type == "mermaid":
238        data = mermaid(document)
239        return data, data
240
241    if export_type == "markmap":
242        data = markmap(document)
243        return data, data
244
245    data = markdown_data(document)
246    return data, data
247
248
249def write_output(output_path: str, output: str, source: str, export_type: str) -> None:
250    with open(output_path, "w", encoding="utf-8") as f:
251        f.write(output)
252
253    if export_type == "markdown_html":
254        markdown_path = os.path.splitext(output_path)[0] + ".md"
255        with open(markdown_path, "w", encoding="utf-8") as f:
256            f.write(source)
257
258
259def build_parser() -> argparse.ArgumentParser:
260    parser = argparse.ArgumentParser(
261        description=(
262            "Export the current MindManager mindmap to HTML or data-only output "
263            "for mermaid, markmap, markdown, JSON, or YAML."
264        )
265    )
266    parser.add_argument(
267        "--type",
268        required=True,
269        choices=(
270            "mermaid_html",
271            "markmap_html",
272            "markdown_html",
273            "json",
274            "yaml",
275            "mermaid",
276            "markmap",
277            "markdown",
278        ),
279        help="Export type to generate (HTML or data-only).",
280    )
281    parser.add_argument(
282        "--output",
283        help=(
284            "Output file path. Defaults to docs/<uuid> plus an extension based on "
285            "--type."
286        ),
287    )
288    mode_group = parser.add_mutually_exclusive_group()
289    mode_group.add_argument(
290        "--open",
291        action="store_true",
292        help="Open the generated output file after export.",
293    )
294    mode_group.add_argument(
295        "--stream",
296        action="store_true",
297        help="Write the generated output to stdout instead of writing a file.",
298    )
299    parser.add_argument(
300        "--macos-access",
301        default="applescript",
302        choices=("applescript", "appscript"),
303        help="macOS MindManager access method.",
304    )
305    return parser
306
307
308def main(argv: Optional[Sequence[str]] = None, docs_dir: Optional[str] = None) -> int:
309    parser = build_parser()
310    args = parser.parse_args(argv)
311
312    source, output = export_mindmap(args.type, args.macos_access)
313
314    if args.stream:
315        sys.stdout.write(output)
316        return 0
317
318    output_path = resolve_output_path(args.output, args.type, docs_dir=docs_dir)
319    write_output(output_path, output, source, args.type)
320    print(output_path)
321
322    if args.open:
323        open_file(output_path)
324
325    return 0
326
327
328if __name__ == "__main__":
329    raise SystemExit(main())