convoviz 0.2.8__tar.gz → 0.2.10__tar.gz
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.
- {convoviz-0.2.8 → convoviz-0.2.10}/PKG-INFO +1 -2
- {convoviz-0.2.8 → convoviz-0.2.10}/README.md +0 -1
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/analysis/wordcloud.py +5 -2
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/config.py +3 -3
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/interactive.py +2 -2
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/io/writers.py +69 -9
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/renderers/markdown.py +6 -66
- {convoviz-0.2.8 → convoviz-0.2.10}/pyproject.toml +1 -1
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/__init__.py +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/__main__.py +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/analysis/__init__.py +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/analysis/graphs.py +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/colormaps.txt +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/AmaticSC-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/ArchitectsDaughter-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/BebasNeue-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/Borel-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/Courgette-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/CroissantOne-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/Handjet-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/IndieFlower-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/Kalam-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/Lobster-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/MartianMono-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/MartianMono-Thin.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/Montserrat-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/Mooli-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/Pacifico-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/PlayfairDisplay-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/Raleway-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/RobotoMono-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/RobotoMono-Thin.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/RobotoSlab-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/RobotoSlab-Thin.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/Ruwudu-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/Sacramento-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/SedgwickAveDisplay-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/ShadowsIntoLight-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/TitilliumWeb-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/Yellowtail-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/YsabeauOffice-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/YsabeauSC-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/YsabeauSC-Thin.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/fonts/Zeyada-Regular.ttf +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/assets/stopwords.txt +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/cli.py +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/exceptions.py +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/io/__init__.py +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/io/assets.py +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/io/loaders.py +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/models/__init__.py +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/models/collection.py +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/models/conversation.py +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/models/message.py +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/models/node.py +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/pipeline.py +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/py.typed +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/renderers/__init__.py +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/renderers/yaml.py +0 -0
- {convoviz-0.2.8 → convoviz-0.2.10}/convoviz/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: convoviz
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.10
|
|
4
4
|
Summary: Get analytics and visualizations on your ChatGPT data!
|
|
5
5
|
Keywords: markdown,chatgpt,openai,visualization,analytics,json,export,data-analysis,obsidian
|
|
6
6
|
Author: Mohamed Cheikh Sidiya
|
|
@@ -36,7 +36,6 @@ Convert your ChatGPT history into well-formatted Markdown files. Additionally, v
|
|
|
36
36
|
- **YAML Headers**: Optional and included by default.
|
|
37
37
|
- **Inline Images**: Media attachments rendered directly in Markdown.
|
|
38
38
|
- **Data Visualizations**: Word clouds, graphs, and more.
|
|
39
|
-
- **Custom Instructions**: All your custom instructions in one JSON file.
|
|
40
39
|
|
|
41
40
|
See examples [here](demo).
|
|
42
41
|
|
|
@@ -10,7 +10,6 @@ Convert your ChatGPT history into well-formatted Markdown files. Additionally, v
|
|
|
10
10
|
- **YAML Headers**: Optional and included by default.
|
|
11
11
|
- **Inline Images**: Media attachments rendered directly in Markdown.
|
|
12
12
|
- **Data Visualizations**: Word clouds, graphs, and more.
|
|
13
|
-
- **Custom Instructions**: All your custom instructions in one JSON file.
|
|
14
13
|
|
|
15
14
|
See examples [here](demo).
|
|
16
15
|
|
|
@@ -139,7 +139,8 @@ def generate_wordclouds(
|
|
|
139
139
|
text = group.plaintext("user", "assistant")
|
|
140
140
|
if text.strip():
|
|
141
141
|
img = generate_wordcloud(text, config)
|
|
142
|
-
|
|
142
|
+
# Format: 2024-W15.png (ISO week format)
|
|
143
|
+
img.save(output_dir / f"{week.strftime('%Y-W%W')}.png", optimize=True)
|
|
143
144
|
|
|
144
145
|
for month, group in tqdm(
|
|
145
146
|
month_groups.items(),
|
|
@@ -149,7 +150,8 @@ def generate_wordclouds(
|
|
|
149
150
|
text = group.plaintext("user", "assistant")
|
|
150
151
|
if text.strip():
|
|
151
152
|
img = generate_wordcloud(text, config)
|
|
152
|
-
|
|
153
|
+
# Format: 2024-03-March.png (consistent with folder naming)
|
|
154
|
+
img.save(output_dir / f"{month.strftime('%Y-%m-%B')}.png", optimize=True)
|
|
153
155
|
|
|
154
156
|
for year, group in tqdm(
|
|
155
157
|
year_groups.items(),
|
|
@@ -159,4 +161,5 @@ def generate_wordclouds(
|
|
|
159
161
|
text = group.plaintext("user", "assistant")
|
|
160
162
|
if text.strip():
|
|
161
163
|
img = generate_wordcloud(text, config)
|
|
164
|
+
# Format: 2024.png
|
|
162
165
|
img.save(output_dir / f"{year.strftime('%Y')}.png", optimize=True)
|
|
@@ -10,8 +10,8 @@ from pydantic import BaseModel, Field
|
|
|
10
10
|
class FolderOrganization(str, Enum):
|
|
11
11
|
"""How to organize markdown output files in folders."""
|
|
12
12
|
|
|
13
|
-
FLAT = "flat" # All files in one directory
|
|
14
|
-
DATE = "date" # Nested by year/month
|
|
13
|
+
FLAT = "flat" # All files in one directory
|
|
14
|
+
DATE = "date" # Nested by year/month (default)
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class AuthorHeaders(BaseModel):
|
|
@@ -27,7 +27,7 @@ class MarkdownConfig(BaseModel):
|
|
|
27
27
|
"""Configuration for markdown output."""
|
|
28
28
|
|
|
29
29
|
latex_delimiters: Literal["default", "dollars"] = "default"
|
|
30
|
-
flavor: Literal["
|
|
30
|
+
flavor: Literal["standard", "obsidian"] = "standard"
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class YAMLConfig(BaseModel):
|
|
@@ -143,11 +143,11 @@ def run_interactive_config(initial_config: ConvovizConfig | None = None) -> Conv
|
|
|
143
143
|
|
|
144
144
|
# Prompt for markdown flavor
|
|
145
145
|
flavor_result = cast(
|
|
146
|
-
Literal["
|
|
146
|
+
Literal["standard", "obsidian"],
|
|
147
147
|
_ask_or_cancel(
|
|
148
148
|
select(
|
|
149
149
|
"Select the markdown flavor:",
|
|
150
|
-
choices=["
|
|
150
|
+
choices=["standard", "obsidian"],
|
|
151
151
|
default=config.conversation.markdown.flavor,
|
|
152
152
|
style=CUSTOM_STYLE,
|
|
153
153
|
)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from os import utime as os_utime
|
|
4
4
|
from pathlib import Path
|
|
5
|
+
from urllib.parse import quote
|
|
5
6
|
|
|
6
7
|
from orjson import OPT_INDENT_2, dumps
|
|
7
8
|
from tqdm import tqdm
|
|
@@ -32,8 +33,8 @@ _MONTH_NAMES = [
|
|
|
32
33
|
def get_date_folder_path(conversation: Conversation) -> Path:
|
|
33
34
|
"""Get the date-based folder path for a conversation.
|
|
34
35
|
|
|
35
|
-
Creates a nested structure: year/month
|
|
36
|
-
Example: 2024/03-March/
|
|
36
|
+
Creates a nested structure: year/month
|
|
37
|
+
Example: 2024/03-March/
|
|
37
38
|
|
|
38
39
|
Args:
|
|
39
40
|
conversation: The conversation to get the path for
|
|
@@ -51,13 +52,7 @@ def get_date_folder_path(conversation: Conversation) -> Path:
|
|
|
51
52
|
month_name = _MONTH_NAMES[month_num - 1]
|
|
52
53
|
month = f"{month_num:02d}-{month_name}"
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
# Calculate which week of the month this date falls into
|
|
56
|
-
day = create_time.day
|
|
57
|
-
week_of_month = (day - 1) // 7 + 1
|
|
58
|
-
week = f"Week-{week_of_month:02d}"
|
|
59
|
-
|
|
60
|
-
return Path(year) / month / week
|
|
55
|
+
return Path(year) / month
|
|
61
56
|
|
|
62
57
|
|
|
63
58
|
def save_conversation(
|
|
@@ -115,6 +110,62 @@ def save_conversation(
|
|
|
115
110
|
return final_path
|
|
116
111
|
|
|
117
112
|
|
|
113
|
+
def _generate_year_index(year_dir: Path, year: str) -> None:
|
|
114
|
+
"""Generate a _index.md file for a year folder.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
year_dir: Path to the year directory
|
|
118
|
+
year: The year string (e.g., "2024")
|
|
119
|
+
"""
|
|
120
|
+
months = sorted(
|
|
121
|
+
[d.name for d in year_dir.iterdir() if d.is_dir()],
|
|
122
|
+
key=lambda m: int(m.split("-")[0]),
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
lines = [
|
|
126
|
+
f"# {year}",
|
|
127
|
+
"",
|
|
128
|
+
"## Months",
|
|
129
|
+
"",
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
for month in months:
|
|
133
|
+
month_name = month.split("-", 1)[1] if "-" in month else month
|
|
134
|
+
lines.append(f"- [{month_name}]({month}/_index.md)")
|
|
135
|
+
|
|
136
|
+
index_path = year_dir / "_index.md"
|
|
137
|
+
index_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _generate_month_index(month_dir: Path, year: str, month: str) -> None:
|
|
141
|
+
"""Generate a _index.md file for a month folder.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
month_dir: Path to the month directory
|
|
145
|
+
year: The year string (e.g., "2024")
|
|
146
|
+
month: The month folder name (e.g., "03-March")
|
|
147
|
+
"""
|
|
148
|
+
month_name = month.split("-", 1)[1] if "-" in month else month
|
|
149
|
+
files = sorted(
|
|
150
|
+
[f.name for f in month_dir.glob("*.md") if f.name != "_index.md"]
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
lines = [
|
|
154
|
+
f"# {month_name} {year}",
|
|
155
|
+
"",
|
|
156
|
+
"## Conversations",
|
|
157
|
+
"",
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
for file in files:
|
|
161
|
+
title = file[:-3] # Remove .md extension
|
|
162
|
+
encoded_file = quote(file)
|
|
163
|
+
lines.append(f"- [{title}]({encoded_file})")
|
|
164
|
+
|
|
165
|
+
index_path = month_dir / "_index.md"
|
|
166
|
+
index_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
167
|
+
|
|
168
|
+
|
|
118
169
|
def save_collection(
|
|
119
170
|
collection: ConversationCollection,
|
|
120
171
|
directory: Path,
|
|
@@ -151,6 +202,15 @@ def save_collection(
|
|
|
151
202
|
filepath = target_dir / f"{sanitize(conv.title)}.md"
|
|
152
203
|
save_conversation(conv, filepath, config, headers, source_path=collection.source_path)
|
|
153
204
|
|
|
205
|
+
# Generate index files for date organization
|
|
206
|
+
if folder_organization == FolderOrganization.DATE:
|
|
207
|
+
for year_dir in directory.iterdir():
|
|
208
|
+
if year_dir.is_dir() and year_dir.name.isdigit():
|
|
209
|
+
for month_dir in year_dir.iterdir():
|
|
210
|
+
if month_dir.is_dir():
|
|
211
|
+
_generate_month_index(month_dir, year_dir.name, month_dir.name)
|
|
212
|
+
_generate_year_index(year_dir, year_dir.name)
|
|
213
|
+
|
|
154
214
|
|
|
155
215
|
def save_custom_instructions(
|
|
156
216
|
collection: ConversationCollection,
|
|
@@ -8,24 +8,6 @@ from convoviz.exceptions import MessageContentError
|
|
|
8
8
|
from convoviz.models import Conversation, Node
|
|
9
9
|
from convoviz.renderers.yaml import render_yaml_header
|
|
10
10
|
|
|
11
|
-
# Length for shortened node IDs in markdown output (similar to Git short hashes)
|
|
12
|
-
SHORT_ID_LENGTH = 8
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def shorten_id(node_id: str) -> str:
|
|
16
|
-
"""Shorten a node ID for display in markdown.
|
|
17
|
-
|
|
18
|
-
Takes the first 8 characters of the ID, which is typically the first
|
|
19
|
-
segment of a UUID and provides sufficient uniqueness within a conversation.
|
|
20
|
-
|
|
21
|
-
Args:
|
|
22
|
-
node_id: The full node ID (often a UUID)
|
|
23
|
-
|
|
24
|
-
Returns:
|
|
25
|
-
Shortened ID string
|
|
26
|
-
"""
|
|
27
|
-
return node_id[:SHORT_ID_LENGTH]
|
|
28
|
-
|
|
29
11
|
|
|
30
12
|
def close_code_blocks(text: str) -> str:
|
|
31
13
|
"""Ensure all code blocks in the text are properly closed.
|
|
@@ -99,15 +81,12 @@ def render_message_header(role: str, headers: AuthorHeaders) -> str:
|
|
|
99
81
|
return header_map.get(role, f"### {role.title()}")
|
|
100
82
|
|
|
101
83
|
|
|
102
|
-
def render_node_header(node: Node, headers: AuthorHeaders
|
|
84
|
+
def render_node_header(node: Node, headers: AuthorHeaders) -> str:
|
|
103
85
|
"""Render the header section of a node.
|
|
104
86
|
|
|
105
|
-
Includes the node ID, parent link, and message author header.
|
|
106
|
-
|
|
107
87
|
Args:
|
|
108
88
|
node: The node to render
|
|
109
89
|
headers: Configuration for author headers
|
|
110
|
-
flavor: Markdown flavor (obsidian, standard)
|
|
111
90
|
|
|
112
91
|
Returns:
|
|
113
92
|
The header markdown string
|
|
@@ -115,42 +94,7 @@ def render_node_header(node: Node, headers: AuthorHeaders, flavor: str = "standa
|
|
|
115
94
|
if node.message is None:
|
|
116
95
|
return ""
|
|
117
96
|
|
|
118
|
-
|
|
119
|
-
return render_message_header(node.message.author.role, headers) + "\n"
|
|
120
|
-
|
|
121
|
-
# Obsidian flavor
|
|
122
|
-
parts = []
|
|
123
|
-
|
|
124
|
-
# Add parent link if parent has a message
|
|
125
|
-
if node.parent_node and node.parent_node.message:
|
|
126
|
-
parts.append(f"[⬆️](#^{shorten_id(node.parent_node.id)})")
|
|
127
|
-
|
|
128
|
-
author_header = render_message_header(node.message.author.role, headers)
|
|
129
|
-
parts.append(f"{author_header} ^{shorten_id(node.id)}")
|
|
130
|
-
|
|
131
|
-
return "\n".join(parts) + "\n"
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def render_node_footer(node: Node, flavor: str = "standard") -> str:
|
|
135
|
-
"""Render the footer section of a node with child links.
|
|
136
|
-
|
|
137
|
-
Args:
|
|
138
|
-
node: The node to render
|
|
139
|
-
flavor: Markdown flavor (obsidian, standard)
|
|
140
|
-
|
|
141
|
-
Returns:
|
|
142
|
-
The footer markdown string with child navigation links
|
|
143
|
-
"""
|
|
144
|
-
if flavor == "standard" or not node.children_nodes:
|
|
145
|
-
return ""
|
|
146
|
-
|
|
147
|
-
if len(node.children_nodes) == 1:
|
|
148
|
-
return f"\n[⬇️](#^{shorten_id(node.children_nodes[0].id)})\n"
|
|
149
|
-
|
|
150
|
-
links = " | ".join(
|
|
151
|
-
f"[{i + 1} ⬇️](#^{shorten_id(child.id)})" for i, child in enumerate(node.children_nodes)
|
|
152
|
-
)
|
|
153
|
-
return f"\n{links}\n"
|
|
97
|
+
return render_message_header(node.message.author.role, headers) + "\n"
|
|
154
98
|
|
|
155
99
|
|
|
156
100
|
def render_node(
|
|
@@ -158,7 +102,6 @@ def render_node(
|
|
|
158
102
|
headers: AuthorHeaders,
|
|
159
103
|
use_dollar_latex: bool = False,
|
|
160
104
|
asset_resolver: Callable[[str], str | None] | None = None,
|
|
161
|
-
flavor: str = "standard",
|
|
162
105
|
) -> str:
|
|
163
106
|
"""Render a complete node as markdown.
|
|
164
107
|
|
|
@@ -167,7 +110,6 @@ def render_node(
|
|
|
167
110
|
headers: Configuration for author headers
|
|
168
111
|
use_dollar_latex: Whether to convert LaTeX delimiters to dollars
|
|
169
112
|
asset_resolver: Function to resolve asset IDs to paths
|
|
170
|
-
flavor: Markdown flavor (obsidian, standard)
|
|
171
113
|
|
|
172
114
|
Returns:
|
|
173
115
|
Complete markdown string for the node
|
|
@@ -178,7 +120,7 @@ def render_node(
|
|
|
178
120
|
if node.message.is_hidden:
|
|
179
121
|
return ""
|
|
180
122
|
|
|
181
|
-
header = render_node_header(node, headers
|
|
123
|
+
header = render_node_header(node, headers)
|
|
182
124
|
|
|
183
125
|
# Get and process content
|
|
184
126
|
try:
|
|
@@ -201,9 +143,7 @@ def render_node(
|
|
|
201
143
|
# Obsidian handles this well.
|
|
202
144
|
content += f"\n\n"
|
|
203
145
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
return f"\n{header}{content}{footer}\n---\n"
|
|
146
|
+
return f"\n{header}{content}\n---\n"
|
|
207
147
|
|
|
208
148
|
|
|
209
149
|
def _ordered_nodes(conversation: Conversation) -> list[Node]:
|
|
@@ -254,7 +194,7 @@ def render_conversation(
|
|
|
254
194
|
Complete markdown document string
|
|
255
195
|
"""
|
|
256
196
|
use_dollar_latex = config.markdown.latex_delimiters == "dollars"
|
|
257
|
-
|
|
197
|
+
# Note: config.markdown.flavor is available for future obsidian-specific features
|
|
258
198
|
|
|
259
199
|
# Start with YAML header
|
|
260
200
|
markdown = render_yaml_header(conversation, config.yaml)
|
|
@@ -263,7 +203,7 @@ def render_conversation(
|
|
|
263
203
|
for node in _ordered_nodes(conversation):
|
|
264
204
|
if node.message:
|
|
265
205
|
markdown += render_node(
|
|
266
|
-
node, headers, use_dollar_latex, asset_resolver=asset_resolver
|
|
206
|
+
node, headers, use_dollar_latex, asset_resolver=asset_resolver
|
|
267
207
|
)
|
|
268
208
|
|
|
269
209
|
return markdown
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|