freeplane-and-yaml 0.1.1__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.
- freeplane_and_yaml/__init__.py +0 -0
- freeplane_and_yaml/cli.py +40 -0
- freeplane_and_yaml/convert.py +100 -0
- freeplane_and_yaml-0.1.1.dist-info/LICENSE +21 -0
- freeplane_and_yaml-0.1.1.dist-info/METADATA +133 -0
- freeplane_and_yaml-0.1.1.dist-info/RECORD +9 -0
- freeplane_and_yaml-0.1.1.dist-info/WHEEL +5 -0
- freeplane_and_yaml-0.1.1.dist-info/entry_points.txt +3 -0
- freeplane_and_yaml-0.1.1.dist-info/top_level.txt +1 -0
File without changes
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import sys
|
2
|
+
import os
|
3
|
+
from .convert import convert_yaml_file # Import your conversion logic
|
4
|
+
|
5
|
+
|
6
|
+
def main():
|
7
|
+
# Check for the correct number of arguments
|
8
|
+
if len(sys.argv) != 3:
|
9
|
+
print("Usage: convert input.yaml output_directory")
|
10
|
+
sys.exit(1)
|
11
|
+
|
12
|
+
# Parse arguments
|
13
|
+
input_file = sys.argv[1]
|
14
|
+
output_dir = sys.argv[2]
|
15
|
+
|
16
|
+
# Validate the input YAML file
|
17
|
+
if not input_file.endswith(".yaml"):
|
18
|
+
print(f"Error: {input_file} is not a valid YAML file.")
|
19
|
+
sys.exit(1)
|
20
|
+
|
21
|
+
if not os.path.isfile(input_file):
|
22
|
+
print(f"Error: Input file {input_file} does not exist.")
|
23
|
+
sys.exit(1)
|
24
|
+
|
25
|
+
# Validate the output directory
|
26
|
+
if not os.path.isdir(output_dir):
|
27
|
+
print(f"Error: Output directory {output_dir} does not exist.")
|
28
|
+
sys.exit(1)
|
29
|
+
|
30
|
+
# Generate the `.m` file name based on the input file
|
31
|
+
base_name = os.path.basename(input_file).replace(".yaml", ".mm")
|
32
|
+
output_file = os.path.join(output_dir, base_name)
|
33
|
+
|
34
|
+
# Perform the conversion
|
35
|
+
try:
|
36
|
+
convert_yaml_file(input_file, output_file)
|
37
|
+
print(f"Conversion complete: {output_file}")
|
38
|
+
except Exception as e:
|
39
|
+
print(f"Error during conversion: {e}")
|
40
|
+
sys.exit(1)
|
@@ -0,0 +1,100 @@
|
|
1
|
+
import yaml
|
2
|
+
from xml.etree.ElementTree import Element, SubElement, tostring
|
3
|
+
from xml.dom import minidom
|
4
|
+
import uuid
|
5
|
+
import argparse
|
6
|
+
import os
|
7
|
+
|
8
|
+
def generate_node_id():
|
9
|
+
"""Generate a unique ID for a node."""
|
10
|
+
return f"ID_{str(uuid.uuid4()).replace('-', '_')}"
|
11
|
+
|
12
|
+
def create_note_element(note_text):
|
13
|
+
"""Create a richcontent note element with the given text."""
|
14
|
+
richcontent = Element('richcontent', {
|
15
|
+
'TYPE': 'NOTE',
|
16
|
+
'CONTENT-TYPE': 'xml/'
|
17
|
+
})
|
18
|
+
html = SubElement(richcontent, 'html')
|
19
|
+
head = SubElement(html, 'head')
|
20
|
+
body = SubElement(html, 'body')
|
21
|
+
p = SubElement(body, 'p')
|
22
|
+
p.text = note_text
|
23
|
+
return richcontent
|
24
|
+
|
25
|
+
def add_node_recursive(parent_element, node_data, position=None):
|
26
|
+
"""Recursively add nodes to the mind map."""
|
27
|
+
# Create node element with basic attributes
|
28
|
+
node_attrs = {
|
29
|
+
'TEXT': node_data['title'],
|
30
|
+
'ID': generate_node_id()
|
31
|
+
}
|
32
|
+
if position:
|
33
|
+
node_attrs['POSITION'] = position
|
34
|
+
|
35
|
+
node = SubElement(parent_element, 'node', node_attrs)
|
36
|
+
|
37
|
+
# Add note if present
|
38
|
+
if 'note' in node_data:
|
39
|
+
node.append(create_note_element(node_data['note']))
|
40
|
+
|
41
|
+
# Process children if present
|
42
|
+
if 'children' in node_data:
|
43
|
+
# Alternate positions for children
|
44
|
+
positions = ['right', 'left']
|
45
|
+
position_index = 0
|
46
|
+
|
47
|
+
for child_key, child_data in node_data['children'].items():
|
48
|
+
# Alternate between right and left for top-level children
|
49
|
+
child_position = positions[position_index % 2] if parent_element.tag == 'map' else None
|
50
|
+
position_index += 1
|
51
|
+
add_node_recursive(node, child_data, child_position)
|
52
|
+
|
53
|
+
def converter(yaml_content):
|
54
|
+
"""Convert YAML content to Freeplane mind map format."""
|
55
|
+
# Parse YAML content
|
56
|
+
data = yaml.safe_load(yaml_content)
|
57
|
+
|
58
|
+
# Create the base map element
|
59
|
+
map_elem = Element('map', {'version': '1.9.13'})
|
60
|
+
|
61
|
+
# Add the root node and its children
|
62
|
+
add_node_recursive(map_elem, data['root'])
|
63
|
+
|
64
|
+
# Convert to string and pretty print
|
65
|
+
rough_string = tostring(map_elem, 'utf-8')
|
66
|
+
reparsed = minidom.parseString(rough_string)
|
67
|
+
pretty_xml = reparsed.toprettyxml(indent=" ")
|
68
|
+
|
69
|
+
# Add XML declaration
|
70
|
+
if not pretty_xml.startswith('<?xml'):
|
71
|
+
pretty_xml = '<?xml version="1.0" encoding="UTF-8"?>\n' + pretty_xml
|
72
|
+
|
73
|
+
return pretty_xml
|
74
|
+
|
75
|
+
def convert_yaml_file(yaml_file_path, output_file_path):
|
76
|
+
"""Convert a YAML file to a Freeplane mind map file."""
|
77
|
+
with open(yaml_file_path, 'r', encoding='utf-8') as yaml_file:
|
78
|
+
yaml_content = yaml_file.read()
|
79
|
+
|
80
|
+
mind_map_xml = converter(yaml_content)
|
81
|
+
|
82
|
+
with open(output_file_path, 'w', encoding='utf-8') as mm_file:
|
83
|
+
mm_file.write(mind_map_xml)
|
84
|
+
|
85
|
+
# Example usage:
|
86
|
+
if __name__ == "__main__":
|
87
|
+
parser = argparse.ArgumentParser(description='Convert YAML file to Freeplane mind map')
|
88
|
+
parser.add_argument('input_file', help='Input YAML file path')
|
89
|
+
parser.add_argument('output_dir', help='Output directory for the mind map file')
|
90
|
+
|
91
|
+
args = parser.parse_args()
|
92
|
+
|
93
|
+
# Create output directory if it doesn't exist
|
94
|
+
os.makedirs(args.output_dir, exist_ok=True)
|
95
|
+
|
96
|
+
# Generate output filename based on input filename
|
97
|
+
input_basename = os.path.splitext(os.path.basename(args.input_file))[0]
|
98
|
+
output_file = os.path.join(args.output_dir, f"{input_basename}.mm")
|
99
|
+
|
100
|
+
convert_yaml_file(args.input_file, output_file)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 Romilly Cocking
|
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.
|
@@ -0,0 +1,133 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: freeplane-and-yaml
|
3
|
+
Version: 0.1.1
|
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
|
+
First, clone this repository:
|
30
|
+
|
31
|
+
```bash
|
32
|
+
# clone the project from GitHub
|
33
|
+
git clone https://github.com/romilly/freeplane-and-yaml.git
|
34
|
+
cd freeplane-and-yaml
|
35
|
+
```
|
36
|
+
|
37
|
+
This project requires Python and should be run in a virtual environment:
|
38
|
+
|
39
|
+
```bash
|
40
|
+
# Create and activate virtual environment
|
41
|
+
python -m venv venv
|
42
|
+
source venv/bin/activate # On Windows use: venv\Scripts\activate
|
43
|
+
|
44
|
+
# Install required packages
|
45
|
+
pip install pyyaml
|
46
|
+
```
|
47
|
+
|
48
|
+
## Usage
|
49
|
+
|
50
|
+
The code should be run from the `src` directory after activating the virtual environment. Your YAML file should follow the schema defined in `schema/mindmap-schema.json`. Here's an example structure:
|
51
|
+
|
52
|
+
```yaml
|
53
|
+
root:
|
54
|
+
title: "Your Main Topic"
|
55
|
+
note: "Optional note for the main topic"
|
56
|
+
children:
|
57
|
+
subtopic1:
|
58
|
+
title: "Subtopic 1"
|
59
|
+
note: "Optional note for subtopic 1"
|
60
|
+
children:
|
61
|
+
# ... more nested topics
|
62
|
+
subtopic2:
|
63
|
+
title: "Subtopic 2"
|
64
|
+
# ... and so on
|
65
|
+
```
|
66
|
+
|
67
|
+
### YAML Schema Requirements
|
68
|
+
|
69
|
+
The YAML must conform to these rules:
|
70
|
+
- Must have a root node with a title and at least one child
|
71
|
+
- Each node requires a title
|
72
|
+
- Notes are optional
|
73
|
+
- Child node keys must be alphanumeric (including underscores)
|
74
|
+
- No additional properties are allowed beyond title, note, and children
|
75
|
+
|
76
|
+
For full schema details, see `schema/mindmap-schema.json`.
|
77
|
+
|
78
|
+
### Converting Documents to Mind Maps using Claude AI
|
79
|
+
|
80
|
+
You can use Claude Sonnet to automatically convert documents (PDFs, articles, specifications, etc.) into the required YAML format. Here's the workflow:
|
81
|
+
|
82
|
+
1. Share your document and the schema (from `schema/mindmap-schema.json`)with Claude Sonnet.
|
83
|
+
2. Use this prompt:
|
84
|
+
```
|
85
|
+
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.
|
86
|
+
```
|
87
|
+
3. Claude will generate a YAML file that follows the schema
|
88
|
+
4. Save Claude's output as a .yaml file
|
89
|
+
5. Convert it to a mind map using this tool
|
90
|
+
|
91
|
+
This workflow is useful for:
|
92
|
+
- Summarizing academic papers
|
93
|
+
- Converting product requirements documents (PRDs)
|
94
|
+
- Creating structured summaries of technical documentation
|
95
|
+
- Organizing research notes
|
96
|
+
|
97
|
+
### Converting YAML to Mind Map
|
98
|
+
|
99
|
+
To convert a YAML file to a Freeplane mind map:
|
100
|
+
|
101
|
+
```python
|
102
|
+
from freeplane_and_yaml.convert_yaml_to_freeplane import convert_yaml_file
|
103
|
+
|
104
|
+
# Convert YAML to mind map
|
105
|
+
convert_yaml_file('path/to/your/input.yaml', 'output.mm')
|
106
|
+
```
|
107
|
+
|
108
|
+
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:
|
109
|
+
|
110
|
+

|
111
|
+
|
112
|
+
This is normal and expected — click OK to load the mind map.
|
113
|
+
|
114
|
+
Here's an example of how the output looks:
|
115
|
+
|
116
|
+

|
117
|
+
|
118
|
+
## Features
|
119
|
+
|
120
|
+
- Converts YAML structured data to Freeplane mind map format
|
121
|
+
- Supports hierarchical node structure
|
122
|
+
- Includes node titles and optional notes
|
123
|
+
- Automatically alternates between right and left positions for top-level nodes
|
124
|
+
- Generates unique IDs for each node
|
125
|
+
- Validates input against JSON schema
|
126
|
+
|
127
|
+
## License
|
128
|
+
|
129
|
+
Apologiresd to readers from the USA. This README uses UK spelling.
|
130
|
+
|
131
|
+
This project is licensed under the MIT Licence — see the [LICENCE](LICENSE) file for details.
|
132
|
+
|
133
|
+
|
@@ -0,0 +1,9 @@
|
|
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.1.dist-info/LICENSE,sha256=gCmvBRHqGZibjt4wyMG81SeYWMKuhoGFVQh_Kn1wZ98,1072
|
5
|
+
freeplane_and_yaml-0.1.1.dist-info/METADATA,sha256=rK9H4NH6Z7rx89SzQs8c-ZtccsPrztOl6ukMyJ0CO78,4420
|
6
|
+
freeplane_and_yaml-0.1.1.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
7
|
+
freeplane_and_yaml-0.1.1.dist-info/entry_points.txt,sha256=kWuCk_RZlzcO9h7yk7uJ4dtJiccQxBocEuQPVt6qzxo,57
|
8
|
+
freeplane_and_yaml-0.1.1.dist-info/top_level.txt,sha256=UY6T4vy985r4DAfWpM1D_n6t0cEis5SxtfkPPd-xDhQ,19
|
9
|
+
freeplane_and_yaml-0.1.1.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
freeplane_and_yaml
|