convoviz 0.2.4__py3-none-any.whl → 0.2.6__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.
- convoviz/analysis/graphs.py +610 -242
- convoviz/config.py +3 -1
- convoviz/interactive.py +113 -73
- convoviz/models/conversation.py +20 -9
- convoviz/renderers/markdown.py +27 -7
- {convoviz-0.2.4.dist-info → convoviz-0.2.6.dist-info}/METADATA +15 -3
- {convoviz-0.2.4.dist-info → convoviz-0.2.6.dist-info}/RECORD +9 -9
- {convoviz-0.2.4.dist-info → convoviz-0.2.6.dist-info}/WHEEL +0 -0
- {convoviz-0.2.4.dist-info → convoviz-0.2.6.dist-info}/entry_points.txt +0 -0
convoviz/config.py
CHANGED
|
@@ -19,7 +19,7 @@ class MarkdownConfig(BaseModel):
|
|
|
19
19
|
"""Configuration for markdown output."""
|
|
20
20
|
|
|
21
21
|
latex_delimiters: Literal["default", "dollars"] = "default"
|
|
22
|
-
flavor: Literal["obsidian", "standard"] = "
|
|
22
|
+
flavor: Literal["obsidian", "standard"] = "standard"
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class YAMLConfig(BaseModel):
|
|
@@ -74,6 +74,8 @@ class GraphConfig(BaseModel):
|
|
|
74
74
|
figsize: tuple[int, int] = (10, 6)
|
|
75
75
|
dpi: int = 300
|
|
76
76
|
timezone: Literal["utc", "local"] = "local"
|
|
77
|
+
generate_monthly_breakdowns: bool = False
|
|
78
|
+
generate_yearly_breakdowns: bool = False
|
|
77
79
|
|
|
78
80
|
|
|
79
81
|
class ConvovizConfig(BaseModel):
|
convoviz/interactive.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Interactive configuration prompts using questionary."""
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
|
+
from typing import Literal, Protocol, cast
|
|
4
5
|
|
|
5
6
|
from questionary import Choice, Style, checkbox, select
|
|
6
7
|
from questionary import path as qst_path
|
|
@@ -25,6 +26,23 @@ CUSTOM_STYLE = Style(
|
|
|
25
26
|
]
|
|
26
27
|
)
|
|
27
28
|
|
|
29
|
+
class _QuestionaryPrompt[T](Protocol):
|
|
30
|
+
def ask(self) -> T | None: ...
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _ask_or_cancel[T](prompt: _QuestionaryPrompt[T]) -> T:
|
|
34
|
+
"""Ask a questionary prompt; treat Ctrl+C/Ctrl+D as cancelling the run.
|
|
35
|
+
|
|
36
|
+
questionary's `.ask()` returns `None` on cancellation (Ctrl+C / Ctrl+D). We
|
|
37
|
+
convert that to `KeyboardInterrupt` so callers can abort the whole
|
|
38
|
+
interactive session with a single Ctrl+C.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
result = prompt.ask()
|
|
42
|
+
if result is None:
|
|
43
|
+
raise KeyboardInterrupt
|
|
44
|
+
return result
|
|
45
|
+
|
|
28
46
|
|
|
29
47
|
def _validate_input_path(raw: str) -> bool | str:
|
|
30
48
|
path = Path(raw)
|
|
@@ -67,22 +85,26 @@ def run_interactive_config(initial_config: ConvovizConfig | None = None) -> Conv
|
|
|
67
85
|
|
|
68
86
|
# Prompt for input path
|
|
69
87
|
input_default = str(config.input_path) if config.input_path else ""
|
|
70
|
-
input_result =
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
88
|
+
input_result: str = _ask_or_cancel(
|
|
89
|
+
qst_path(
|
|
90
|
+
"Enter the path to the export ZIP, conversations JSON, or extracted directory:",
|
|
91
|
+
default=input_default,
|
|
92
|
+
validate=_validate_input_path,
|
|
93
|
+
style=CUSTOM_STYLE,
|
|
94
|
+
)
|
|
95
|
+
)
|
|
76
96
|
|
|
77
97
|
if input_result:
|
|
78
98
|
config.input_path = Path(input_result)
|
|
79
99
|
|
|
80
100
|
# Prompt for output folder
|
|
81
|
-
output_result =
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
101
|
+
output_result: str = _ask_or_cancel(
|
|
102
|
+
qst_path(
|
|
103
|
+
"Enter the path to the output folder:",
|
|
104
|
+
default=str(config.output_folder),
|
|
105
|
+
style=CUSTOM_STYLE,
|
|
106
|
+
)
|
|
107
|
+
)
|
|
86
108
|
|
|
87
109
|
if output_result:
|
|
88
110
|
config.output_folder = Path(output_result)
|
|
@@ -91,34 +113,46 @@ def run_interactive_config(initial_config: ConvovizConfig | None = None) -> Conv
|
|
|
91
113
|
headers = config.message.author_headers
|
|
92
114
|
for role in ["system", "user", "assistant", "tool"]:
|
|
93
115
|
current = getattr(headers, role)
|
|
94
|
-
result =
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
116
|
+
result: str = _ask_or_cancel(
|
|
117
|
+
qst_text(
|
|
118
|
+
f"Enter the message header for '{role}':",
|
|
119
|
+
default=current,
|
|
120
|
+
validate=lambda t: validate_header(t)
|
|
121
|
+
or "Must be a valid markdown header (e.g., # Title)",
|
|
122
|
+
style=CUSTOM_STYLE,
|
|
123
|
+
)
|
|
124
|
+
)
|
|
101
125
|
if result:
|
|
102
126
|
setattr(headers, role, result)
|
|
103
127
|
|
|
104
128
|
# Prompt for LaTeX delimiters
|
|
105
|
-
latex_result =
|
|
106
|
-
"
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
129
|
+
latex_result = cast(
|
|
130
|
+
Literal["default", "dollars"],
|
|
131
|
+
_ask_or_cancel(
|
|
132
|
+
select(
|
|
133
|
+
"Select the LaTeX math delimiters:",
|
|
134
|
+
choices=["default", "dollars"],
|
|
135
|
+
default=config.conversation.markdown.latex_delimiters,
|
|
136
|
+
style=CUSTOM_STYLE,
|
|
137
|
+
)
|
|
138
|
+
),
|
|
139
|
+
)
|
|
111
140
|
|
|
112
141
|
if latex_result:
|
|
113
142
|
config.conversation.markdown.latex_delimiters = latex_result
|
|
114
143
|
|
|
115
144
|
# Prompt for markdown flavor
|
|
116
|
-
flavor_result =
|
|
117
|
-
"
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
145
|
+
flavor_result = cast(
|
|
146
|
+
Literal["obsidian", "standard"],
|
|
147
|
+
_ask_or_cancel(
|
|
148
|
+
select(
|
|
149
|
+
"Select the markdown flavor:",
|
|
150
|
+
choices=["obsidian", "standard"],
|
|
151
|
+
default=config.conversation.markdown.flavor,
|
|
152
|
+
style=CUSTOM_STYLE,
|
|
153
|
+
)
|
|
154
|
+
),
|
|
155
|
+
)
|
|
122
156
|
|
|
123
157
|
if flavor_result:
|
|
124
158
|
config.conversation.markdown.flavor = flavor_result
|
|
@@ -141,27 +175,28 @@ def run_interactive_config(initial_config: ConvovizConfig | None = None) -> Conv
|
|
|
141
175
|
]
|
|
142
176
|
]
|
|
143
177
|
|
|
144
|
-
selected =
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
178
|
+
selected: list[str] = _ask_or_cancel(
|
|
179
|
+
checkbox(
|
|
180
|
+
"Select YAML metadata headers to include:",
|
|
181
|
+
choices=yaml_choices,
|
|
182
|
+
style=CUSTOM_STYLE,
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
selected_set = set(selected)
|
|
187
|
+
for field_name in [
|
|
188
|
+
"title",
|
|
189
|
+
"tags",
|
|
190
|
+
"chat_link",
|
|
191
|
+
"create_time",
|
|
192
|
+
"update_time",
|
|
193
|
+
"model",
|
|
194
|
+
"used_plugins",
|
|
195
|
+
"message_count",
|
|
196
|
+
"content_types",
|
|
197
|
+
"custom_instructions",
|
|
198
|
+
]:
|
|
199
|
+
setattr(yaml_config, field_name, field_name in selected_set)
|
|
165
200
|
|
|
166
201
|
# Prompt for font
|
|
167
202
|
available_fonts = font_names()
|
|
@@ -169,12 +204,14 @@ def run_interactive_config(initial_config: ConvovizConfig | None = None) -> Conv
|
|
|
169
204
|
current_font = (
|
|
170
205
|
config.wordcloud.font_path.stem if config.wordcloud.font_path else available_fonts[0]
|
|
171
206
|
)
|
|
172
|
-
font_result =
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
207
|
+
font_result: str = _ask_or_cancel(
|
|
208
|
+
select(
|
|
209
|
+
"Select the font for word clouds:",
|
|
210
|
+
choices=available_fonts,
|
|
211
|
+
default=current_font if current_font in available_fonts else available_fonts[0],
|
|
212
|
+
style=CUSTOM_STYLE,
|
|
213
|
+
)
|
|
214
|
+
)
|
|
178
215
|
|
|
179
216
|
if font_result:
|
|
180
217
|
config.wordcloud.font_path = font_path(font_result)
|
|
@@ -182,26 +219,29 @@ def run_interactive_config(initial_config: ConvovizConfig | None = None) -> Conv
|
|
|
182
219
|
# Prompt for colormap
|
|
183
220
|
available_colormaps = colormaps()
|
|
184
221
|
if available_colormaps:
|
|
185
|
-
colormap_result =
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
222
|
+
colormap_result: str = _ask_or_cancel(
|
|
223
|
+
select(
|
|
224
|
+
"Select the color theme for word clouds:",
|
|
225
|
+
choices=available_colormaps,
|
|
226
|
+
default=config.wordcloud.colormap
|
|
227
|
+
if config.wordcloud.colormap in available_colormaps
|
|
228
|
+
else available_colormaps[0],
|
|
229
|
+
style=CUSTOM_STYLE,
|
|
230
|
+
)
|
|
231
|
+
)
|
|
193
232
|
|
|
194
233
|
if colormap_result:
|
|
195
234
|
config.wordcloud.colormap = colormap_result
|
|
196
235
|
|
|
197
236
|
# Prompt for custom stopwords
|
|
198
|
-
stopwords_result =
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
237
|
+
stopwords_result: str = _ask_or_cancel(
|
|
238
|
+
qst_text(
|
|
239
|
+
"Enter custom stopwords (comma-separated):",
|
|
240
|
+
default=config.wordcloud.custom_stopwords,
|
|
241
|
+
style=CUSTOM_STYLE,
|
|
242
|
+
)
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
config.wordcloud.custom_stopwords = stopwords_result
|
|
206
246
|
|
|
207
247
|
return config
|
convoviz/models/conversation.py
CHANGED
|
@@ -36,22 +36,29 @@ class Conversation(BaseModel):
|
|
|
36
36
|
|
|
37
37
|
@property
|
|
38
38
|
def all_message_nodes(self) -> list[Node]:
|
|
39
|
-
"""Get all nodes that have messages (including
|
|
39
|
+
"""Get all nodes that have messages (including hidden/internal ones)."""
|
|
40
40
|
return [node for node in self.node_mapping.values() if node.has_message]
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
@property
|
|
43
|
+
def visible_message_nodes(self) -> list[Node]:
|
|
44
|
+
"""Get all nodes that have *visible* (non-hidden) messages."""
|
|
45
|
+
return [
|
|
46
|
+
node
|
|
47
|
+
for node in self.node_mapping.values()
|
|
48
|
+
if node.has_message and node.message is not None and not node.message.is_hidden
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
def nodes_by_author(self, *authors: AuthorRole, include_hidden: bool = False) -> list[Node]:
|
|
43
52
|
"""Get nodes with messages from specified authors.
|
|
44
53
|
|
|
45
54
|
Args:
|
|
46
55
|
*authors: Author roles to filter by. Defaults to ("user",) if empty.
|
|
56
|
+
include_hidden: Whether to include hidden/internal messages.
|
|
47
57
|
"""
|
|
48
58
|
if not authors:
|
|
49
59
|
authors = ("user",)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
for node in self.all_message_nodes
|
|
53
|
-
if node.message and node.message.author.role in authors
|
|
54
|
-
]
|
|
60
|
+
nodes = self.all_message_nodes if include_hidden else self.visible_message_nodes
|
|
61
|
+
return [node for node in nodes if node.message and node.message.author.role in authors]
|
|
55
62
|
|
|
56
63
|
@property
|
|
57
64
|
def leaf_count(self) -> int:
|
|
@@ -65,9 +72,13 @@ class Conversation(BaseModel):
|
|
|
65
72
|
|
|
66
73
|
@property
|
|
67
74
|
def content_types(self) -> list[str]:
|
|
68
|
-
"""Get all unique content types in the conversation."""
|
|
75
|
+
"""Get all unique content types in the conversation (excluding hidden messages)."""
|
|
69
76
|
return list(
|
|
70
|
-
{
|
|
77
|
+
{
|
|
78
|
+
node.message.content.content_type
|
|
79
|
+
for node in self.visible_message_nodes
|
|
80
|
+
if node.message
|
|
81
|
+
}
|
|
71
82
|
)
|
|
72
83
|
|
|
73
84
|
def message_count(self, *authors: AuthorRole) -> int:
|
convoviz/renderers/markdown.py
CHANGED
|
@@ -8,6 +8,24 @@ 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
|
+
|
|
11
29
|
|
|
12
30
|
def close_code_blocks(text: str) -> str:
|
|
13
31
|
"""Ensure all code blocks in the text are properly closed.
|
|
@@ -81,7 +99,7 @@ def render_message_header(role: str, headers: AuthorHeaders) -> str:
|
|
|
81
99
|
return header_map.get(role, f"### {role.title()}")
|
|
82
100
|
|
|
83
101
|
|
|
84
|
-
def render_node_header(node: Node, headers: AuthorHeaders, flavor: str = "
|
|
102
|
+
def render_node_header(node: Node, headers: AuthorHeaders, flavor: str = "standard") -> str:
|
|
85
103
|
"""Render the header section of a node.
|
|
86
104
|
|
|
87
105
|
Includes the node ID, parent link, and message author header.
|
|
@@ -105,15 +123,15 @@ def render_node_header(node: Node, headers: AuthorHeaders, flavor: str = "obsidi
|
|
|
105
123
|
|
|
106
124
|
# Add parent link if parent has a message
|
|
107
125
|
if node.parent_node and node.parent_node.message:
|
|
108
|
-
parts.append(f"[⬆️](#^{node.parent_node.id})")
|
|
126
|
+
parts.append(f"[⬆️](#^{shorten_id(node.parent_node.id)})")
|
|
109
127
|
|
|
110
128
|
author_header = render_message_header(node.message.author.role, headers)
|
|
111
|
-
parts.append(f"{author_header} ^{node.id}")
|
|
129
|
+
parts.append(f"{author_header} ^{shorten_id(node.id)}")
|
|
112
130
|
|
|
113
131
|
return "\n".join(parts) + "\n"
|
|
114
132
|
|
|
115
133
|
|
|
116
|
-
def render_node_footer(node: Node, flavor: str = "
|
|
134
|
+
def render_node_footer(node: Node, flavor: str = "standard") -> str:
|
|
117
135
|
"""Render the footer section of a node with child links.
|
|
118
136
|
|
|
119
137
|
Args:
|
|
@@ -127,9 +145,11 @@ def render_node_footer(node: Node, flavor: str = "obsidian") -> str:
|
|
|
127
145
|
return ""
|
|
128
146
|
|
|
129
147
|
if len(node.children_nodes) == 1:
|
|
130
|
-
return f"\n[⬇️](#^{node.children_nodes[0].id})\n"
|
|
148
|
+
return f"\n[⬇️](#^{shorten_id(node.children_nodes[0].id)})\n"
|
|
131
149
|
|
|
132
|
-
links = " | ".join(
|
|
150
|
+
links = " | ".join(
|
|
151
|
+
f"[{i + 1} ⬇️](#^{shorten_id(child.id)})" for i, child in enumerate(node.children_nodes)
|
|
152
|
+
)
|
|
133
153
|
return f"\n{links}\n"
|
|
134
154
|
|
|
135
155
|
|
|
@@ -138,7 +158,7 @@ def render_node(
|
|
|
138
158
|
headers: AuthorHeaders,
|
|
139
159
|
use_dollar_latex: bool = False,
|
|
140
160
|
asset_resolver: Callable[[str], str | None] | None = None,
|
|
141
|
-
flavor: str = "
|
|
161
|
+
flavor: str = "standard",
|
|
142
162
|
) -> str:
|
|
143
163
|
"""Render a complete node as markdown.
|
|
144
164
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: convoviz
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
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
|
|
@@ -101,7 +101,19 @@ convoviz --help
|
|
|
101
101
|
|
|
102
102
|
### 4. Check the Output 🎉
|
|
103
103
|
|
|
104
|
-
And that's it! After running the script, head over to the output folder to see your
|
|
104
|
+
And that's it! After running the script, head over to the output folder to see your neatly formatted Markdown files and visualizations.
|
|
105
|
+
|
|
106
|
+
The main outputs are:
|
|
107
|
+
|
|
108
|
+
- **`Markdown/`**: one `.md` file per conversation
|
|
109
|
+
- **`Graphs/`**: a small set of high-signal plots, including:
|
|
110
|
+
- `overview.png` (dashboard)
|
|
111
|
+
- `activity_heatmap.png` (weekday × hour)
|
|
112
|
+
- `daily_activity.png` / `monthly_activity.png`
|
|
113
|
+
- `model_usage.png`, `conversation_lengths.png`
|
|
114
|
+
- `weekday_pattern.png`, `hourly_pattern.png`, `conversation_lifetimes.png`
|
|
115
|
+
- **`Word-Clouds/`**: weekly/monthly/yearly word clouds
|
|
116
|
+
- **`custom_instructions.json`**: extracted custom instructions
|
|
105
117
|
|
|
106
118
|
## Share Your Feedback! 💌
|
|
107
119
|
|
|
@@ -119,7 +131,7 @@ And if you've had a great experience, consider giving the project a star ⭐. It
|
|
|
119
131
|
|
|
120
132
|
## Notes
|
|
121
133
|
|
|
122
|
-
This is just a small thing I coded to help me see my convos in beautiful markdown
|
|
134
|
+
This is just a small thing I coded to help me see my convos in beautiful markdown. It was originally built with [Obsidian](https://obsidian.md/) (my go-to note-taking app) in mind, but the default output is standard Markdown (and you can choose an Obsidian-flavored mode in the interactive config if you want block IDs / navigation links).
|
|
123
135
|
|
|
124
136
|
I wasn't a fan of the clunky, and sometimes paid, browser extensions.
|
|
125
137
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
convoviz/__init__.py,sha256=bQLCHO2U9EyMTGqNgsYiCtBQKTKNj4iIM3-TwIkrnRY,612
|
|
2
2
|
convoviz/__main__.py,sha256=1qiGW13_SgL7wJi8iioIN-AAHGkNGnEl5q_RcPUrI0s,143
|
|
3
3
|
convoviz/analysis/__init__.py,sha256=FxgH5JJpyypiLJpMQn_HlM51jnb8lQdP63_C_W3Dlx4,241
|
|
4
|
-
convoviz/analysis/graphs.py,sha256=
|
|
4
|
+
convoviz/analysis/graphs.py,sha256=gt056UkgGcy9vCkupQmW_HjOLy-W6j4Ekxr315BXPgA,29457
|
|
5
5
|
convoviz/analysis/wordcloud.py,sha256=ZnbA_-rcXHwXIny_xbudfJDQbIuPT7urNFfHcx6QWxQ,4673
|
|
6
6
|
convoviz/assets/colormaps.txt,sha256=59TSGz428AxY14AEvymAH2IJ2RT9Mlp7Sy0N12NEdXQ,108
|
|
7
7
|
convoviz/assets/fonts/AmaticSC-Regular.ttf,sha256=83clh7a3urnTLud0_yZofuIb6BdyC2LMI9jhE6G2LvU,142696
|
|
@@ -37,25 +37,25 @@ convoviz/assets/fonts/YsabeauSC-Thin.ttf,sha256=hZGOZNTRrxbiUPE2VDeLbtnaRwkMOBaV
|
|
|
37
37
|
convoviz/assets/fonts/Zeyada-Regular.ttf,sha256=fKhkrp9VHt_3Aw8JfkfkPeC2j3CilLWuPUudzBeawPQ,57468
|
|
38
38
|
convoviz/assets/stopwords.txt,sha256=7_ywpxsKYOj3U5CZTh9lP4GqbbkZLMabSOjKAXFk6Wc,539
|
|
39
39
|
convoviz/cli.py,sha256=8HNn-6kmDN8ECb0BspvjeGa_636SQPDffpM0yINgNII,3463
|
|
40
|
-
convoviz/config.py,sha256=
|
|
40
|
+
convoviz/config.py,sha256=UUynLSD22e5_fdm2zXx_bjRNOPRov_UjMBIY8u-76vg,2815
|
|
41
41
|
convoviz/exceptions.py,sha256=bQpIKls48uOQpagEJAxpXf5LF7QoagRRfbD0MjWC7Ak,1476
|
|
42
|
-
convoviz/interactive.py,sha256=
|
|
42
|
+
convoviz/interactive.py,sha256=VXtKgYo9tZGtsoj7zThdnbTrbjSNP5MzAZbdOs3icW4,7424
|
|
43
43
|
convoviz/io/__init__.py,sha256=y70TYypJ36_kaEA04E2wa1EDaKQVjprKItoKR6MMs4M,471
|
|
44
44
|
convoviz/io/assets.py,sha256=BykidWJG6OQAgbVfUByQ3RLTrldzpZ_NeM7HV3a5Tig,2333
|
|
45
45
|
convoviz/io/loaders.py,sha256=RuGiGzpyNcgwTxOM-m2ehhyh2mP1-k1YamK8-VynR3g,5713
|
|
46
46
|
convoviz/io/writers.py,sha256=KaLr0f2F2Pw5XOoQKMA75IeQYXUTT4WbS-HAqRxsp3c,3494
|
|
47
47
|
convoviz/models/__init__.py,sha256=6gAfrk6KJT2QxdvX_v15mUdfIqEw1xKxwQlKSfyA5eI,532
|
|
48
48
|
convoviz/models/collection.py,sha256=L658yKMNC6IZrfxYxZBe-oO9COP_bzVfRznnNon7tfU,4467
|
|
49
|
-
convoviz/models/conversation.py,sha256=
|
|
49
|
+
convoviz/models/conversation.py,sha256=ssx1Z6LM9kJIx3OucQW8JVoAc8zCdxj1iOLtie2B3ak,5678
|
|
50
50
|
convoviz/models/message.py,sha256=mVnaUG6hypz92Oz3OgFAK1uuTgH3ZOJAWsFiCpLYneY,5459
|
|
51
51
|
convoviz/models/node.py,sha256=1vBAtKVscYsUBDnKAOyLxuZaK9KoVF1dFXiKXRHxUnY,1946
|
|
52
52
|
convoviz/pipeline.py,sha256=Mwg3Xqazk5PrsIHxhVajtWbfq4PgFlIGVHWq8BsW0U0,5750
|
|
53
53
|
convoviz/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
54
|
convoviz/renderers/__init__.py,sha256=IQgwD9NqtUgbS9zwyPBNZbBIZcFrbZ9C7WMAV-X3Xdg,261
|
|
55
|
-
convoviz/renderers/markdown.py,sha256=
|
|
55
|
+
convoviz/renderers/markdown.py,sha256=mpDt-xrjsPX_wt9URCDk2wicesaVv_VTWWxTHCMKiLM,7765
|
|
56
56
|
convoviz/renderers/yaml.py,sha256=XG1s4VhDdx-TiqekTkgED87RZ1lVQ7IwrbA-sZHrs7k,4056
|
|
57
57
|
convoviz/utils.py,sha256=IQEKYHhWOnYxlr4GwAHoquG0BXTlVRkORL80oUSaIeQ,3417
|
|
58
|
-
convoviz-0.2.
|
|
59
|
-
convoviz-0.2.
|
|
60
|
-
convoviz-0.2.
|
|
61
|
-
convoviz-0.2.
|
|
58
|
+
convoviz-0.2.6.dist-info/WHEEL,sha256=eycQt0QpYmJMLKpE3X9iDk8R04v2ZF0x82ogq-zP6bQ,79
|
|
59
|
+
convoviz-0.2.6.dist-info/entry_points.txt,sha256=HYsmsw5vt36yYHB05uVU48AK2WLkcwshly7m7KKuZMY,54
|
|
60
|
+
convoviz-0.2.6.dist-info/METADATA,sha256=8bNTXriUg1k45-hM3NlxNrk01HM1Hu8xZPLhtk8uYgI,5994
|
|
61
|
+
convoviz-0.2.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|