freeplane-and-yaml 0.1.3__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ from .convert import convert_yaml_file, converter
2
+ from .llm_adapter import LLMAdapter
3
+ from .text_to_mindmap import TextToMindMap
4
+
5
+ __all__ = [
6
+ "convert_yaml_file",
7
+ "converter",
8
+ "LLMAdapter",
9
+ "TextToMindMap",
10
+ ]
freeplane_and_yaml/cli.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import sys
2
2
  import os
3
- from .convert import convert_yaml_file # Import your conversion logic
3
+ from freeplane_and_yaml.convert import convert_yaml_file # Import your conversion logic
4
4
 
5
5
 
6
6
  def main():
@@ -31,10 +31,13 @@ def main():
31
31
  base_name = os.path.basename(input_file).replace(".yaml", ".mm")
32
32
  output_file = os.path.join(output_dir, base_name)
33
33
 
34
- # Perform the conversion
34
+ # Perform the conversio
35
35
  try:
36
36
  convert_yaml_file(input_file, output_file)
37
37
  print(f"Conversion complete: {output_file}")
38
38
  except Exception as e:
39
39
  print(f"Error during conversion: {e}")
40
40
  sys.exit(1)
41
+
42
+ if __name__ == "__main__":
43
+ main()
@@ -0,0 +1,75 @@
1
+ import os
2
+ import sys
3
+ import tempfile
4
+ import pymupdf4llm
5
+ from .text_to_mindmap import TextToMindMap
6
+ from .openai_adapter import OpenAIAdapter
7
+
8
+ def convert_pdf_to_mindmap(base_name, input_file, mm_output, model, text_output, yaml_output):
9
+ # Step 1: Extract text from the PDF
10
+ print(f"Extracting text from {input_file}...")
11
+ # Ensure the file exists
12
+ if not os.path.isfile(input_file):
13
+ raise FileNotFoundError(f"PDF file not found: {input_file}")
14
+ # Extract text using PyMuPDF4LLM directly
15
+ extracted_text = pymupdf4llm.to_markdown(input_file)
16
+ # Save the extracted text if requested
17
+ if text_output:
18
+ with open(text_output, 'w', encoding='utf-8') as f:
19
+ f.write(extracted_text)
20
+ print(f"Saved extracted text to: {text_output}")
21
+ # If the text is empty, report an error
22
+ if not extracted_text.strip():
23
+ print("Error: No text could be extracted from the PDF", file=sys.stderr)
24
+ sys.exit(1)
25
+ # Step 2: Create the OpenAI adapter
26
+ # Create the adapter with specified model or default
27
+ adapter = OpenAIAdapter(model=model)
28
+ # Step 3: Create a temporary text file for the extracted content
29
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as temp_file:
30
+ temp_file.write(extracted_text)
31
+ temp_file_path = temp_file.name
32
+ try:
33
+ # Step 4: Process the text with the text-to-mindmap service
34
+ print("Converting extracted text to mind map format...")
35
+ service = TextToMindMap(adapter)
36
+
37
+ # First try to get the YAML directly to inspect it
38
+ with open(temp_file_path, 'r', encoding='utf-8') as f:
39
+ text_content = f.read()
40
+
41
+ # Limit the text size to avoid timeouts
42
+ MAX_CHARS = 40000 # Large documents may cause timeouts
43
+ if len(text_content) > MAX_CHARS:
44
+ print(f"Document is large ({len(text_content)} chars). Truncating to {MAX_CHARS} chars to avoid timeouts.")
45
+ text_content = text_content[:MAX_CHARS] + "\n\n[Content truncated due to length...]"
46
+
47
+ try:
48
+ print(f"Sending document for processing...")
49
+ yaml_content = adapter.generate_mind_map_yaml(text_content)
50
+
51
+ # Save raw output for debugging
52
+ output_dir = os.path.dirname(mm_output)
53
+ debug_yaml_path = os.path.join(output_dir, f"{base_name}.raw.yaml")
54
+ with open(debug_yaml_path, 'w', encoding='utf-8') as f:
55
+ f.write(yaml_content)
56
+ print(f"Saved raw YAML for debugging to: {debug_yaml_path}")
57
+
58
+ # Now continue with the regular process
59
+ service.convert_text_file(temp_file_path, yaml_output, mm_output)
60
+ except Exception as e:
61
+ print(f"YAML conversion error: {e}")
62
+ print("Saving debug information and continuing...")
63
+ # Continue with original code path anyway
64
+ service.convert_text_file(temp_file_path, yaml_output, mm_output)
65
+
66
+ print("\nConversion successful!")
67
+ print(f"YAML output: {yaml_output}")
68
+ print(f"Mind map: {mm_output}")
69
+ if text_output:
70
+ print(f"Text output: {text_output}")
71
+
72
+ finally:
73
+ # Clean up the temporary file
74
+ if os.path.exists(temp_file_path):
75
+ os.unlink(temp_file_path)
@@ -0,0 +1,37 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Dict, Any
3
+ import os
4
+
5
+ # Default schema path - using a path that works both in development and when installed
6
+ # First try the package-relative path (for installed package)
7
+ DEFAULT_SCHEMA_PATH = os.path.join(os.path.dirname(__file__), 'schema', 'mindmap-schema.json')
8
+
9
+ # If that doesn't exist, try the development path
10
+ if not os.path.exists(DEFAULT_SCHEMA_PATH):
11
+ DEFAULT_SCHEMA_PATH = os.path.abspath(
12
+ os.path.join(
13
+ os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
14
+ "src", "schema", "mindmap-schema.json"
15
+ )
16
+ )
17
+
18
+
19
+ class LLMAdapter(ABC):
20
+ """
21
+ Abstract base class for LLM adapters.
22
+ This follows the hexagonal architecture pattern to isolate the LLM service from our core logic.
23
+ """
24
+
25
+ @abstractmethod
26
+ def generate_mind_map_yaml(self, text: str) -> str:
27
+ """
28
+ Generate a mind map in YAML format from the provided text,
29
+ following the schema defined in DEFAULT_SCHEMA_PATH.
30
+
31
+ Args:
32
+ text: The text content to convert to a mind map
33
+
34
+ Returns:
35
+ A string containing the YAML representation of the mind map
36
+ """
37
+ pass
@@ -0,0 +1,110 @@
1
+ import os
2
+ from openai import OpenAI
3
+ from typing import Dict, Any
4
+ from dotenv import load_dotenv
5
+ from .llm_adapter import LLMAdapter, DEFAULT_SCHEMA_PATH
6
+ from .yaml_utils import extract_yaml_from_markdown
7
+
8
+ # Load environment variables from .env file
9
+ load_dotenv()
10
+
11
+ class OpenAIAdapter(LLMAdapter):
12
+ """
13
+ Adapter for OpenAI API.
14
+
15
+ This class provides an implementation of the LLMAdapter for OpenAI models.
16
+ It makes API calls to OpenAI to generate mind maps from text.
17
+ """
18
+
19
+ DEFAULT_MODEL = "gpt-4o" # Default OpenAI model to use
20
+
21
+ def __init__(self, api_key: str = None, model: str = None):
22
+ """
23
+ Initialize the adapter with API credentials.
24
+
25
+ Args:
26
+ api_key: OpenAI API key, defaults to reading from OPENAI_API_KEY environment variable
27
+ model: OpenAI model to use, defaults to DEFAULT_MODEL
28
+ """
29
+ self.api_key = api_key or os.environ.get("OPENAI_API_KEY")
30
+ if not self.api_key:
31
+ raise ValueError(
32
+ "No API key provided. Pass an API key to the constructor or "
33
+ "set the OPENAI_API_KEY environment variable in the .env file."
34
+ )
35
+
36
+ self.model = model or self.DEFAULT_MODEL
37
+ self.client = OpenAI(api_key=self.api_key)
38
+
39
+ def generate_mind_map_yaml(self, text: str) -> str:
40
+ """
41
+ Generate a mind map in YAML format using OpenAI.
42
+
43
+ Args:
44
+ text: The text content to convert to a mind map
45
+
46
+ Returns:
47
+ A string containing the YAML representation of the mind map
48
+ """
49
+ # Read the schema content
50
+ with open(DEFAULT_SCHEMA_PATH, 'r') as f:
51
+ schema = f.read()
52
+
53
+ # Make the API call to OpenAI
54
+ yaml_content = self._call_openai_api(text, schema)
55
+
56
+ return yaml_content
57
+
58
+ def _call_openai_api(self, text: str, schema: str) -> str:
59
+ """
60
+ Call the OpenAI API with the text and schema.
61
+
62
+ Args:
63
+ text: The text content to convert to a mind map
64
+ schema: The JSON schema content
65
+
66
+ Returns:
67
+ The generated YAML content
68
+ """
69
+ try:
70
+ # Build system prompt and user prompt
71
+ system_prompt = "You are an expert at creating structured mind maps in YAML format. Your task is to organize the provided text into a hierarchical structure that follows the schema exactly. Every node must include both a title and a descriptive note providing context or details for that node. Only output valid YAML without any explanations, markdown code blocks, or extra text."
72
+
73
+ user_prompt = f"""I'd like you to summarise a document as a yaml file following a schema.
74
+
75
+ Please respond with ONLY the YAML content that follows the schema, without any additional text or explanations.
76
+ The YAML should have a root node with an appropriate title and organized children nodes that capture
77
+ the key points and structure of the text. Aim to have one node per 20 lines of input text.
78
+
79
+ IMPORTANT: Every node (including the root and ALL child nodes) should include both a title AND a note.
80
+ The note should provide additional context, explanation, or details about that specific node.
81
+
82
+ Here is the schema to follow:
83
+
84
+ {schema}
85
+
86
+ Here is the document to summarize:
87
+
88
+ {text}"""
89
+
90
+ # Call the OpenAI API using the new client interface
91
+ response = self.client.chat.completions.create(
92
+ model=self.model,
93
+ messages=[
94
+ {"role": "system", "content": system_prompt},
95
+ {"role": "user", "content": user_prompt}
96
+ ],
97
+ max_tokens=16000, # Maximum allowed for this model
98
+ temperature=0.2, # Low temperature for more deterministic output
99
+ )
100
+
101
+ # Extract the response content
102
+ content = response.choices[0].message.content
103
+
104
+ # Use the utility function to extract YAML from markdown
105
+ content = extract_yaml_from_markdown(content)
106
+
107
+ return content
108
+
109
+ except Exception as e:
110
+ raise RuntimeError(f"Error calling OpenAI API: {str(e)}") from e
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Convert PDF files to mind maps using PyMuPDF4LLM and AI.
4
+ """
5
+
6
+ import os
7
+ import sys
8
+ import argparse
9
+ import tempfile
10
+ import pymupdf4llm
11
+ from dotenv import load_dotenv
12
+
13
+ from freeplane_and_yaml.convert_pdf_to_mindmap import convert_pdf_to_mindmap
14
+ from .text_to_mindmap import TextToMindMap
15
+ from .openai_adapter import OpenAIAdapter
16
+
17
+ # Load environment variables
18
+ load_dotenv()
19
+
20
+
21
+ def main():
22
+ """Main entry point for the pdf2mindmap command-line tool."""
23
+ parser = argparse.ArgumentParser(
24
+ description="Convert a PDF file to a mind map using PyMuPDF4LLM and OpenAI"
25
+ )
26
+ parser.add_argument(
27
+ "input_file",
28
+ help="Path to the input PDF file"
29
+ )
30
+ parser.add_argument(
31
+ "output_dir",
32
+ help="Directory to save the output files"
33
+ )
34
+ parser.add_argument(
35
+ "--model",
36
+ help="OpenAI model to use (defaults to gpt-4o)"
37
+ )
38
+ parser.add_argument(
39
+ "--save-text",
40
+ action="store_true",
41
+ help="Save the extracted text to a file"
42
+ )
43
+
44
+ args = parser.parse_args()
45
+
46
+ # Validate input file
47
+ if not os.path.isfile(args.input_file):
48
+ print(f"Error: Input PDF file '{args.input_file}' does not exist", file=sys.stderr)
49
+ sys.exit(1)
50
+
51
+ # Make sure it's a PDF file
52
+ if not args.input_file.lower().endswith('.pdf'):
53
+ print(f"Error: Input file must be a PDF file: {args.input_file}", file=sys.stderr)
54
+ sys.exit(1)
55
+
56
+ # Validate output directory
57
+ if not os.path.isdir(args.output_dir):
58
+ try:
59
+ os.makedirs(args.output_dir, exist_ok=True)
60
+ print(f"Created output directory: {args.output_dir}")
61
+ except Exception as e:
62
+ print(f"Error: Could not create output directory '{args.output_dir}': {e}", file=sys.stderr)
63
+ sys.exit(1)
64
+ input_file = args.input_file
65
+ output_dir = args.output_dir
66
+ save_text = args.save_text
67
+ # Generate output file paths
68
+ base_name = os.path.splitext(os.path.basename(input_file))[0]
69
+ yaml_output = os.path.join(output_dir, f"{base_name}.yaml")
70
+ mm_output = os.path.join(output_dir, f"{base_name}.mm")
71
+ text_output = os.path.join(output_dir, f"{base_name}.txt") if save_text else None
72
+ model = args.model
73
+
74
+ try:
75
+ convert_pdf_to_mindmap(base_name, input_file, mm_output, model, text_output, yaml_output)
76
+
77
+ except Exception as e:
78
+ print(f"Error during conversion: {e}", file=sys.stderr)
79
+ sys.exit(1)
80
+
81
+
82
+
83
+
84
+ if __name__ == "__main__":
85
+ main()
@@ -0,0 +1,73 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "Mind Map Schema",
4
+ "description": "Schema for Freeplane-compatible mind map YAML format",
5
+
6
+ "definitions": {
7
+ "node": {
8
+ "type": "object",
9
+ "required": ["title"],
10
+ "properties": {
11
+ "title": {
12
+ "type": "string",
13
+ "description": "The display text for the node"
14
+ },
15
+ "note": {
16
+ "type": "string",
17
+ "description": "Rich text note attached to the node"
18
+ },
19
+ "children": {
20
+ "type": "object",
21
+ "description": "Child nodes of this node",
22
+ "patternProperties": {
23
+ "^[a-zA-Z0-9_]+$": {
24
+ "$ref": "#/definitions/node"
25
+ }
26
+ },
27
+ "additionalProperties": false
28
+ }
29
+ },
30
+ "additionalProperties": false
31
+ }
32
+ },
33
+
34
+ "type": "object",
35
+ "required": ["root"],
36
+ "properties": {
37
+ "root": {
38
+ "allOf": [
39
+ { "$ref": "#/definitions/node" },
40
+ {
41
+ "required": ["children"],
42
+ "description": "The root node must have at least one child"
43
+ }
44
+ ]
45
+ }
46
+ },
47
+ "additionalProperties": false,
48
+
49
+ "examples": [
50
+ {
51
+ "root": {
52
+ "title": "Example Mind Map",
53
+ "note": "This is the root node",
54
+ "children": {
55
+ "topic1": {
56
+ "title": "First Topic",
57
+ "note": "Note for first topic",
58
+ "children": {
59
+ "subtopic1": {
60
+ "title": "Subtopic 1",
61
+ "note": "Note for subtopic"
62
+ }
63
+ }
64
+ },
65
+ "topic2": {
66
+ "title": "Second Topic",
67
+ "note": "Note for second topic"
68
+ }
69
+ }
70
+ }
71
+ }
72
+ ]
73
+ }
@@ -0,0 +1,54 @@
1
+ import os
2
+ from typing import Optional
3
+ from .llm_adapter import LLMAdapter
4
+ from .convert import converter
5
+
6
+
7
+ class TextToMindMap:
8
+ """
9
+ Service for converting text files to mind maps via LLM-generated YAML.
10
+
11
+ This class follows the hexagonal architecture pattern, with the LLM adapter
12
+ injected through the constructor, allowing for easy testing and flexibility
13
+ in switching between different LLM providers.
14
+ """
15
+
16
+ def __init__(self, llm_adapter: LLMAdapter):
17
+ """
18
+ Initialize the service with an LLM adapter.
19
+
20
+ Args:
21
+ llm_adapter: An implementation of LLMAdapter
22
+ """
23
+ self.llm_adapter = llm_adapter
24
+
25
+ def convert_text_file(self, input_file_path: str, output_yaml_path: str, output_mm_path: str = None) -> str:
26
+ """
27
+ Convert a text file to a mind map.
28
+
29
+ Args:
30
+ input_file_path: Path to the text file to convert
31
+ output_yaml_path: Path where the YAML file should be saved
32
+ output_mm_path: Optional path where the Freeplane .mm file should be saved
33
+
34
+ Returns:
35
+ The YAML content generated from the text
36
+ """
37
+ # Read the text file
38
+ with open(input_file_path, 'r', encoding='utf-8') as f:
39
+ text_content = f.read()
40
+
41
+ # Convert text to YAML using the adapter
42
+ yaml_content = self.llm_adapter.generate_mind_map_yaml(text_content)
43
+
44
+ # Save the YAML output
45
+ with open(output_yaml_path, 'w', encoding='utf-8') as f:
46
+ f.write(yaml_content)
47
+
48
+ # Optionally convert to Freeplane .mm format
49
+ if output_mm_path:
50
+ mm_content = converter(yaml_content)
51
+ with open(output_mm_path, 'w', encoding='utf-8') as f:
52
+ f.write(mm_content)
53
+
54
+ return yaml_content
@@ -0,0 +1,26 @@
1
+ """
2
+ Utility functions for YAML handling.
3
+ """
4
+
5
+ def extract_yaml_from_markdown(content):
6
+ """
7
+ Extract YAML content from markdown-formatted text.
8
+
9
+ Args:
10
+ content: The markdown text containing YAML code blocks
11
+
12
+ Returns:
13
+ The extracted YAML content
14
+ """
15
+ # Remove any initial text before the YAML content
16
+ if "```yaml" in content:
17
+ content = content.split("```yaml", 1)[1]
18
+ if "```" in content:
19
+ content = content.split("```", 1)[0]
20
+ elif "```" in content:
21
+ content = content.split("```", 1)[1]
22
+ if "```" in content:
23
+ content = content.split("```", 1)[0]
24
+
25
+ # Clean up and return
26
+ return content.strip()
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.1
2
+ Name: freeplane-and-yaml
3
+ Version: 0.3.0
4
+ Summary: A tool to convert PDF files to Freeplane mind maps using OpenAI
5
+ Home-page: https://github.com/romilly/freeplane-and-yaml
6
+ Author: Romilly Cocking
7
+ Author-email: romilly.cocking@gmail.com
8
+ License: MIT
9
+ Platform: UNKNOWN
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: PyYAML
17
+ Requires-Dist: openai
18
+ Requires-Dist: pymupdf4llm
19
+ Requires-Dist: python-dotenv
20
+
21
+ # PDF to Freeplane Mind Map Converter
22
+
23
+ A Python tool that converts PDF documents into Freeplane mind maps using OpenAI. This allows you to automatically generate structured mind maps from PDF documents.
24
+
25
+ You can read about how it got written using AI on [medium](https://medium.com/@romillyc/build-your-own-mind-map-tools-with-ai-b193564f2464?sk=b353aa7d16d6412e4aae8f3eab0ec554).
26
+ That's a _friend link_ so you can read it even if you're not a subscriber.
27
+
28
+ ## Installation
29
+
30
+ This project requires Python and should be run in a virtual environment:
31
+
32
+ ```bash
33
+ # Create and activate virtual environment in the directory of your choice
34
+ python -m venv venv
35
+ source venv/bin/activate # On Windows use: venv\Scripts\activate
36
+ pip install freeplane-and-yaml
37
+ ```
38
+
39
+ You'll need to set up your OpenAI API key in a `.env` file or as an environment variable:
40
+
41
+ ```bash
42
+ # Create a .env file in your project directory
43
+ echo "OPENAI_API_KEY=your_api_key_here" > .env
44
+ ```
45
+
46
+
47
+ ## Usage
48
+
49
+ ### Converting PDF Documents to Mind Maps
50
+
51
+ To convert a PDF document to a Freeplane mind map:
52
+
53
+ ```bash
54
+ # Convert a PDF file to a mind map
55
+ pdf2mindmap path/to/document.pdf output_directory
56
+
57
+ # Use a specific OpenAI model (default is gpt-4o-mini)
58
+ pdf2mindmap path/to/document.pdf output_directory --model gpt-4-turbo
59
+
60
+ # Also save the extracted text
61
+ pdf2mindmap path/to/document.pdf output_directory --save-text
62
+ ```
63
+
64
+ The tool performs these steps:
65
+ 1. Extracts text from the PDF using PyMuPDF4LLM (preserving structure as markdown)
66
+ 2. Processes the text with OpenAI to generate a structured mind map in YAML format
67
+ 3. Converts the YAML to a Freeplane mind map (.mm file)
68
+ 4. Saves all files in the output directory
69
+
70
+ This is useful for:
71
+ - Summarizing academic papers
72
+ - Converting product requirements documents (PRDs)
73
+ - Creating structured summaries of technical documentation
74
+ - Organizing research notes or book content
75
+
76
+
77
+ The generated `.mm` file can be opened in Freeplane. When you first open the file, Freeplane will show this warning dialog because the file wasn't created by Freeplane itself:
78
+
79
+ ![Freeplane Warning Dialog](images/warning-dialog.png)
80
+
81
+ This is normal and expected — click OK to load the mind map.
82
+
83
+ Here's an example of how the output looks:
84
+
85
+ ![Example Mind Map](images/Screenshot%20at%202025-02-12%2010-43-23.png)
86
+
87
+ ## Features
88
+
89
+ - Converts PDF documents to Freeplane mind maps using OpenAI
90
+ - Uses PyMuPDF4LLM for high-quality text extraction from PDFs
91
+ - Creates hierarchical mind maps with node titles and notes
92
+ - Automatically alternates between right and left positions for top-level nodes
93
+ - Generates unique IDs for each node
94
+ - Produces clean, well-structured YAML as an intermediate format
95
+ - Supports different OpenAI models (default is gpt-4o-mini)
96
+
97
+ ## License
98
+
99
+ _Apologies to readers from the USA. This README uses UK spelling._
100
+
101
+ This project is licensed under the MIT Licence — see the [LICENCE](LICENSE) file for details.
102
+
103
+
@@ -0,0 +1,16 @@
1
+ freeplane_and_yaml/__init__.py,sha256=x8BtsOuvatRJqbtZkqU93yf-h4OQJK2tkvi3WnIB5YM,226
2
+ freeplane_and_yaml/cli.py,sha256=ETgW_67QglX7QhDJ4NQANiV134_V4vT3I70o--zwFNw,1271
3
+ freeplane_and_yaml/convert.py,sha256=dyQLt2FlmCgsQOnutnp1DyLXzqk-guazL8TZwTH0vHI,3440
4
+ freeplane_and_yaml/convert_pdf_to_mindmap.py,sha256=TR620sdvw9-4jKIeoK0eQko3kAD9LPH83TKndKJHYY4,3294
5
+ freeplane_and_yaml/llm_adapter.py,sha256=Sd-ibim34Nunj1A45i4om0JkwhldjC_eFvkX1jhDhPg,1250
6
+ freeplane_and_yaml/openai_adapter.py,sha256=1RKv_6Hb_9s9xqvLq3Vmu4g9We2vdbY8YfcTqiMuOLA,4312
7
+ freeplane_and_yaml/pdf2mindmap.py,sha256=17WnRxP_w5eqI40IX7y9JIRsQEONGNrSm00PQKok7uA,2543
8
+ freeplane_and_yaml/text_to_mindmap.py,sha256=f46jQFAvOb4u1tKRSROGdFAyqG62Smww-mBWS1ke1fM,1898
9
+ freeplane_and_yaml/yaml_utils.py,sha256=W_K3kstknOFibZbMDnkxuTqVQO1hKGfNBOLLa9_GErM,718
10
+ freeplane_and_yaml/schema/mindmap-schema.json,sha256=vCFcGjWcZTeTNozGChsRjEw-jYsYDiPxr-qVML8M_XE,1734
11
+ freeplane_and_yaml-0.3.0.dist-info/LICENSE,sha256=gCmvBRHqGZibjt4wyMG81SeYWMKuhoGFVQh_Kn1wZ98,1072
12
+ freeplane_and_yaml-0.3.0.dist-info/METADATA,sha256=ZV85Aw2fyntEKziT2E-RjvzV6PcTG99Afaz4oW2XQCw,3527
13
+ freeplane_and_yaml-0.3.0.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
14
+ freeplane_and_yaml-0.3.0.dist-info/entry_points.txt,sha256=-k40S1cR7VRZViC202mFFI2OsUxmPPBzBiRh0wsMmHE,69
15
+ freeplane_and_yaml-0.3.0.dist-info/top_level.txt,sha256=UY6T4vy985r4DAfWpM1D_n6t0cEis5SxtfkPPd-xDhQ,19
16
+ freeplane_and_yaml-0.3.0.dist-info/RECORD,,
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ pdf2mindmap = freeplane_and_yaml.pdf2mindmap:main
3
+
@@ -1,203 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: freeplane-and-yaml
3
- Version: 0.1.3
4
- Summary: A tool to convert YAML files to Freeplane MM format
5
- Home-page: https://github.com/romilly/freeplane-and-yaml
6
- Author: Romilly Cocking
7
- Author-email: romilly.cocking@gmail.com
8
- License: MIT
9
- Platform: UNKNOWN
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: License :: OSI Approved :: MIT License
12
- Classifier: Operating System :: OS Independent
13
- Requires-Python: >=3.9
14
- Description-Content-Type: text/markdown
15
- License-File: LICENSE
16
- Requires-Dist: PyYAML
17
-
18
- # YAML to Freeplane Converter
19
-
20
- A Python tool that converts YAML files into Freeplane mind maps. This allows you to generate mind maps programmatically from structured YAML data.
21
-
22
- The YAML file can be created using [Claude AI](https://claude.ai/chat/). A suitable prompt is given [below](#converting-documents-to-mind-maps-using-claude-ai).
23
-
24
- You can read about how it got written using AI on [medium](https://medium.com/@romillyc/build-your-own-mind-map-tools-with-ai-b193564f2464?sk=b353aa7d16d6412e4aae8f3eab0ec554).
25
- That's a friend link so you can read it even if you're not a subscriber.
26
-
27
- ## Installation
28
-
29
- This project requires Python and should be run in a virtual environment:
30
-
31
- ```bash
32
- # Create and activate virtual environment in the direcoty of your choice
33
- python -m venv venv
34
- source venv/bin/activate # On Windows use: venv\Scripts\activate
35
- pip install freeplane-and-yaml
36
- ```
37
-
38
-
39
- ## Usage
40
-
41
- Your YAML file should follow this schema:
42
-
43
- ```json
44
- {
45
- "$schema": "http://json-schema.org/draft-07/schema#",
46
- "title": "Mind Map Schema",
47
- "description": "Schema for Freeplane-compatible mind map YAML format",
48
-
49
- "definitions": {
50
- "node": {
51
- "type": "object",
52
- "required": ["title"],
53
- "properties": {
54
- "title": {
55
- "type": "string",
56
- "description": "The display text for the node"
57
- },
58
- "note": {
59
- "type": "string",
60
- "description": "Rich text note attached to the node"
61
- },
62
- "children": {
63
- "type": "object",
64
- "description": "Child nodes of this node",
65
- "patternProperties": {
66
- "^[a-zA-Z0-9_]+$": {
67
- "$ref": "#/definitions/node"
68
- }
69
- },
70
- "additionalProperties": false
71
- }
72
- },
73
- "additionalProperties": false
74
- }
75
- },
76
-
77
- "type": "object",
78
- "required": ["root"],
79
- "properties": {
80
- "root": {
81
- "allOf": [
82
- { "$ref": "#/definitions/node" },
83
- {
84
- "required": ["children"],
85
- "description": "The root node must have at least one child"
86
- }
87
- ]
88
- }
89
- },
90
- "additionalProperties": false,
91
-
92
- "examples": [
93
- {
94
- "root": {
95
- "title": "Example Mind Map",
96
- "note": "This is the root node",
97
- "children": {
98
- "topic1": {
99
- "title": "First Topic",
100
- "note": "Note for first topic",
101
- "children": {
102
- "subtopic1": {
103
- "title": "Subtopic 1",
104
- "note": "Note for subtopic"
105
- }
106
- }
107
- },
108
- "topic2": {
109
- "title": "Second Topic",
110
- "note": "Note for second topic"
111
- }
112
- }
113
- }
114
- }
115
- ]
116
- }
117
-
118
- ```
119
-
120
- Here's an example structure:
121
-
122
- ```yaml
123
- root:
124
- title: "Your Main Topic"
125
- note: "Optional note for the main topic"
126
- children:
127
- subtopic1:
128
- title: "Subtopic 1"
129
- note: "Optional note for subtopic 1"
130
- children:
131
- # ... more nested topics
132
- subtopic2:
133
- title: "Subtopic 2"
134
- # ... and so on
135
- ```
136
-
137
- ### Converting YAML to Mind Map
138
-
139
- To convert a YAML file to a Freeplane mind map:
140
-
141
- ```bash
142
-
143
- # Convert YAML and store mind map in temp
144
- convert data/marr.yaml temp
145
- ```
146
-
147
- ### YAML Schema Requirements
148
-
149
- The YAML must conform to these rules:
150
- - Must have a root node with a title and at least one child
151
- - Each node requires a title
152
- - Notes are optional
153
- - Child node keys must be alphanumeric (including underscores)
154
- - No additional properties are allowed beyond title, note, and children
155
-
156
- For full schema details, see above.
157
-
158
- ### Converting Documents to YAML using Claude AI
159
-
160
- You can use Claude Sonnet to automatically convert documents (PDFs, articles, specifications, etc.) into the required YAML format. Here's the workflow:
161
-
162
- 1. Share your document and the schema (above) with Claude Sonnet.
163
- 2. Use this prompt:
164
- ```
165
- I've uploaded a document and a schema file. I'd like you to summarise the document as a yaml file following the schema that I uploaded.
166
- ```
167
- 3. Claude will generate a YAML file that follows the schema
168
- 4. Save Claude's output as a .yaml file
169
- 5. Convert it to a mind map using this tool
170
-
171
- This workflow is useful for:
172
- - Summarizing academic papers
173
- - Converting product requirements documents (PRDs)
174
- - Creating structured summaries of technical documentation
175
- - Organizing research notes
176
-
177
-
178
- The generated `.mm` file can be opened in Freeplane. When you first open the file, Freeplane will show this warning dialog because the file wasn't created by Freeplane itself:
179
-
180
- ![Freeplane Warning Dialog](images/warning-dialog.png)
181
-
182
- This is normal and expected — click OK to load the mind map.
183
-
184
- Here's an example of how the output looks:
185
-
186
- ![Example Mind Map](images/Screenshot%20at%202025-02-12%2010-43-23.png)
187
-
188
- ## Features
189
-
190
- - Converts YAML structured data to Freeplane mind map format
191
- - Supports hierarchical node structure
192
- - Includes node titles and optional notes
193
- - Automatically alternates between right and left positions for top-level nodes
194
- - Generates unique IDs for each node
195
- - Validates input against JSON schema
196
-
197
- ## License
198
-
199
- _Apologies to readers from the USA. This README uses UK spelling._
200
-
201
- This project is licensed under the MIT Licence — see the [LICENCE](LICENSE) file for details.
202
-
203
-
@@ -1,9 +0,0 @@
1
- freeplane_and_yaml/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- freeplane_and_yaml/cli.py,sha256=qeUJXw2MqCf6Kv7c-K63-srGj339beL4JTrAXi5_-3c,1213
3
- freeplane_and_yaml/convert.py,sha256=dyQLt2FlmCgsQOnutnp1DyLXzqk-guazL8TZwTH0vHI,3440
4
- freeplane_and_yaml-0.1.3.dist-info/LICENSE,sha256=gCmvBRHqGZibjt4wyMG81SeYWMKuhoGFVQh_Kn1wZ98,1072
5
- freeplane_and_yaml-0.1.3.dist-info/METADATA,sha256=MCf8NWb6YiHUf6K22xJy_sGOOoSI9TNiyXdw5Mb_rEc,5751
6
- freeplane_and_yaml-0.1.3.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
7
- freeplane_and_yaml-0.1.3.dist-info/entry_points.txt,sha256=kWuCk_RZlzcO9h7yk7uJ4dtJiccQxBocEuQPVt6qzxo,57
8
- freeplane_and_yaml-0.1.3.dist-info/top_level.txt,sha256=UY6T4vy985r4DAfWpM1D_n6t0cEis5SxtfkPPd-xDhQ,19
9
- freeplane_and_yaml-0.1.3.dist-info/RECORD,,
@@ -1,3 +0,0 @@
1
- [console_scripts]
2
- convert = freeplane_and_yaml.cli:main
3
-