notionary 0.1.19__py3-none-any.whl → 0.1.20__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.
- notionary/__init__.py +2 -2
- notionary/database/notion_database.py +3 -1
- notionary/elements/audio_element.py +7 -11
- notionary/elements/bookmark_element.py +17 -29
- notionary/elements/bulleted_list_element.py +69 -0
- notionary/elements/callout_element.py +18 -80
- notionary/elements/code_block_element.py +15 -10
- notionary/elements/column_element.py +39 -26
- notionary/elements/divider_element.py +8 -25
- notionary/elements/embed_element.py +10 -18
- notionary/elements/heading_element.py +10 -12
- notionary/elements/image_element.py +9 -15
- notionary/elements/mention_element.py +6 -15
- notionary/elements/notion_block_element.py +12 -11
- notionary/elements/numbered_list_element.py +68 -0
- notionary/elements/paragraph_element.py +11 -7
- notionary/elements/prompts/element_prompt_content.py +20 -0
- notionary/elements/prompts/synthax_prompt_builder.py +92 -0
- notionary/elements/qoute_element.py +20 -89
- notionary/elements/registry/block_element_registry.py +90 -0
- notionary/elements/{block_element_registry_builder.py → registry/block_element_registry_builder.py} +15 -124
- notionary/elements/table_element.py +4 -16
- notionary/elements/text_inline_formatter.py +15 -78
- notionary/elements/todo_lists.py +14 -18
- notionary/elements/toggle_element.py +19 -1
- notionary/elements/video_element.py +10 -19
- notionary/notion_client.py +2 -2
- notionary/page/content/page_content_manager.py +2 -3
- notionary/page/markdown_to_notion_converter.py +3 -3
- notionary/page/notion_page.py +3 -3
- notionary/page/notion_to_markdown_converter.py +3 -3
- notionary/page/relations/notion_page_relation_manager.py +24 -24
- notionary/page/relations/notion_page_title_resolver.py +1 -2
- {notionary-0.1.19.dist-info → notionary-0.1.20.dist-info}/METADATA +3 -3
- notionary-0.1.20.dist-info/RECORD +59 -0
- {notionary-0.1.19.dist-info → notionary-0.1.20.dist-info}/WHEEL +1 -1
- notionary/elements/block_element_registry.py +0 -233
- notionary/elements/list_element.py +0 -130
- notionary-0.1.19.dist-info/RECORD +0 -56
- {notionary-0.1.19.dist-info → notionary-0.1.20.dist-info}/licenses/LICENSE +0 -0
- {notionary-0.1.19.dist-info → notionary-0.1.20.dist-info}/top_level.txt +0 -0
@@ -1,28 +1,11 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional, List, Tuple
|
3
|
-
from typing_extensions import override
|
4
|
-
|
5
3
|
from notionary.elements.notion_block_element import NotionBlockElement
|
4
|
+
from notionary.elements.prompts.element_prompt_content import ElementPromptContent
|
6
5
|
|
7
6
|
|
8
7
|
class QuoteElement(NotionBlockElement):
|
9
|
-
"""Class for converting between Markdown blockquotes and Notion quote blocks
|
10
|
-
|
11
|
-
# Mapping von Markdown-Farbnamen zu Notion-Farbnamen
|
12
|
-
COLOR_MAPPING = {
|
13
|
-
"gray": "gray_background",
|
14
|
-
"brown": "brown_background",
|
15
|
-
"orange": "orange_background",
|
16
|
-
"yellow": "yellow_background",
|
17
|
-
"green": "green_background",
|
18
|
-
"blue": "blue_background",
|
19
|
-
"purple": "purple_background",
|
20
|
-
"pink": "pink_background",
|
21
|
-
"red": "red_background",
|
22
|
-
}
|
23
|
-
|
24
|
-
# Umgekehrtes Mapping für die Rückkonvertierung
|
25
|
-
REVERSE_COLOR_MAPPING = {v: k for k, v in COLOR_MAPPING.items()}
|
8
|
+
"""Class for converting between Markdown blockquotes and Notion quote blocks."""
|
26
9
|
|
27
10
|
@staticmethod
|
28
11
|
def find_matches(text: str) -> List[Tuple[int, int, Dict[str, Any]]]:
|
@@ -87,10 +70,9 @@ class QuoteElement(NotionBlockElement):
|
|
87
70
|
|
88
71
|
return matches
|
89
72
|
|
90
|
-
@override
|
91
73
|
@staticmethod
|
92
74
|
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
93
|
-
"""Convert markdown blockquote to Notion block
|
75
|
+
"""Convert markdown blockquote to Notion block."""
|
94
76
|
if not text:
|
95
77
|
return None
|
96
78
|
|
@@ -103,40 +85,12 @@ class QuoteElement(NotionBlockElement):
|
|
103
85
|
# Extract quote content
|
104
86
|
lines = text.split("\n")
|
105
87
|
quote_lines = []
|
106
|
-
color = "default" # Standardfarbe
|
107
88
|
|
108
|
-
#
|
109
|
-
first_line = None
|
110
|
-
for line in lines:
|
111
|
-
quote_match = quote_pattern.match(line)
|
112
|
-
if quote_match and quote_match.group(1).strip():
|
113
|
-
first_line = quote_match.group(1).strip()
|
114
|
-
break
|
115
|
-
|
116
|
-
# Farbangabe in eckigen Klammern prüfen
|
117
|
-
if first_line:
|
118
|
-
color_match = re.match(r"^\[background:(\w+)\]\s*(.*)", first_line)
|
119
|
-
if color_match:
|
120
|
-
potential_color = color_match.group(1).lower()
|
121
|
-
if potential_color in QuoteElement.COLOR_MAPPING:
|
122
|
-
color = QuoteElement.COLOR_MAPPING[potential_color]
|
123
|
-
# Erste Zeile ohne Farbangabe neu hinzufügen
|
124
|
-
first_line = color_match.group(2)
|
125
|
-
|
126
|
-
# Inhalte extrahieren
|
127
|
-
processing_first_color_line = True
|
89
|
+
# Extract content from each line
|
128
90
|
for line in lines:
|
129
91
|
quote_match = quote_pattern.match(line)
|
130
92
|
if quote_match:
|
131
93
|
content = quote_match.group(1)
|
132
|
-
# Farbangabe in der ersten Zeile entfernen
|
133
|
-
if (
|
134
|
-
processing_first_color_line
|
135
|
-
and content.strip()
|
136
|
-
and re.match(r"^\[background:(\w+)\]", content.strip())
|
137
|
-
):
|
138
|
-
content = re.sub(r"^\[background:(\w+)\]\s*", "", content)
|
139
|
-
processing_first_color_line = False
|
140
94
|
quote_lines.append(content)
|
141
95
|
elif not line.strip() and quote_lines:
|
142
96
|
# Allow empty lines within the quote
|
@@ -147,20 +101,17 @@ class QuoteElement(NotionBlockElement):
|
|
147
101
|
|
148
102
|
quote_content = "\n".join(quote_lines).strip()
|
149
103
|
|
150
|
-
# Create rich_text elements directly
|
151
104
|
rich_text = [{"type": "text", "text": {"content": quote_content}}]
|
152
105
|
|
153
|
-
return {"type": "quote", "quote": {"rich_text": rich_text, "color":
|
106
|
+
return {"type": "quote", "quote": {"rich_text": rich_text, "color": "default"}}
|
154
107
|
|
155
|
-
@override
|
156
108
|
@staticmethod
|
157
109
|
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
158
|
-
"""Convert Notion quote block to markdown
|
110
|
+
"""Convert Notion quote block to markdown."""
|
159
111
|
if block.get("type") != "quote":
|
160
112
|
return None
|
161
113
|
|
162
114
|
rich_text = block.get("quote", {}).get("rich_text", [])
|
163
|
-
color = block.get("quote", {}).get("color", "default")
|
164
115
|
|
165
116
|
# Extract the text content
|
166
117
|
content = QuoteElement._extract_text_content(rich_text)
|
@@ -169,33 +120,23 @@ class QuoteElement(NotionBlockElement):
|
|
169
120
|
lines = content.split("\n")
|
170
121
|
formatted_lines = []
|
171
122
|
|
172
|
-
#
|
173
|
-
if color != "default" and color in QuoteElement.REVERSE_COLOR_MAPPING:
|
174
|
-
markdown_color = QuoteElement.REVERSE_COLOR_MAPPING.get(color)
|
175
|
-
first_line = lines[0] if lines else ""
|
176
|
-
formatted_lines.append(f"> [background:{markdown_color}] {first_line}")
|
177
|
-
lines = lines[1:] if len(lines) > 1 else []
|
178
|
-
|
179
|
-
# Füge die restlichen Zeilen hinzu
|
123
|
+
# Add each line with blockquote prefix
|
180
124
|
for line in lines:
|
181
125
|
formatted_lines.append(f"> {line}")
|
182
126
|
|
183
127
|
return "\n".join(formatted_lines)
|
184
128
|
|
185
|
-
@override
|
186
129
|
@staticmethod
|
187
130
|
def match_markdown(text: str) -> bool:
|
188
131
|
"""Check if this element can handle the given markdown text."""
|
189
132
|
quote_pattern = re.compile(r"^\s*>\s?(.*)", re.MULTILINE)
|
190
133
|
return bool(quote_pattern.search(text))
|
191
134
|
|
192
|
-
@override
|
193
135
|
@staticmethod
|
194
136
|
def match_notion(block: Dict[str, Any]) -> bool:
|
195
137
|
"""Check if this element can handle the given Notion block."""
|
196
138
|
return block.get("type") == "quote"
|
197
139
|
|
198
|
-
@override
|
199
140
|
@staticmethod
|
200
141
|
def is_multiline() -> bool:
|
201
142
|
"""Blockquotes can span multiple lines."""
|
@@ -212,31 +153,21 @@ class QuoteElement(NotionBlockElement):
|
|
212
153
|
result += text_obj.get("plain_text", "")
|
213
154
|
return result
|
214
155
|
|
215
|
-
@override
|
216
156
|
@classmethod
|
217
|
-
def get_llm_prompt_content(cls) ->
|
218
|
-
"""
|
157
|
+
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
158
|
+
"""
|
159
|
+
Returns structured LLM prompt metadata for the quote element.
|
160
|
+
"""
|
219
161
|
return {
|
220
|
-
"description": "Creates blockquotes that visually distinguish quoted text
|
221
|
-
"when_to_use":
|
222
|
-
|
223
|
-
"
|
224
|
-
|
225
|
-
|
226
|
-
"color_options": [
|
227
|
-
"gray",
|
228
|
-
"brown",
|
229
|
-
"orange",
|
230
|
-
"yellow",
|
231
|
-
"green",
|
232
|
-
"blue",
|
233
|
-
"purple",
|
234
|
-
"pink",
|
235
|
-
"red",
|
236
|
-
],
|
162
|
+
"description": "Creates blockquotes that visually distinguish quoted text.",
|
163
|
+
"when_to_use": (
|
164
|
+
"Use blockquotes for quoting external sources, highlighting important statements, "
|
165
|
+
"or creating visual emphasis for key information."
|
166
|
+
),
|
167
|
+
"syntax": "> Quoted text",
|
237
168
|
"examples": [
|
238
|
-
"> This is a simple blockquote
|
239
|
-
">
|
240
|
-
">
|
169
|
+
"> This is a simple blockquote",
|
170
|
+
"> This is a multi-line quote\n> that continues on the next line",
|
171
|
+
"> Important note:\n> This quote spans\n> multiple lines.",
|
241
172
|
],
|
242
173
|
}
|
@@ -0,0 +1,90 @@
|
|
1
|
+
from typing import Dict, Any, Optional, List, Type
|
2
|
+
|
3
|
+
from notionary.elements.notion_block_element import NotionBlockElement
|
4
|
+
from notionary.elements.prompts.synthax_prompt_builder import (
|
5
|
+
MarkdownSyntaxPromptBuilder,
|
6
|
+
)
|
7
|
+
from notionary.elements.text_inline_formatter import TextInlineFormatter
|
8
|
+
|
9
|
+
|
10
|
+
class BlockElementRegistry:
|
11
|
+
"""Registry of elements that can convert between Markdown and Notion."""
|
12
|
+
|
13
|
+
def __init__(self, elements=None):
|
14
|
+
"""
|
15
|
+
Initialize a new registry instance.
|
16
|
+
"""
|
17
|
+
self._elements = []
|
18
|
+
|
19
|
+
if elements:
|
20
|
+
for element in elements:
|
21
|
+
self.register(element)
|
22
|
+
|
23
|
+
def register(self, element_class: Type[NotionBlockElement]):
|
24
|
+
"""Register an element class."""
|
25
|
+
self._elements.append(element_class)
|
26
|
+
return self
|
27
|
+
|
28
|
+
def deregister(self, element_class: Type[NotionBlockElement]) -> bool:
|
29
|
+
"""
|
30
|
+
Deregister an element class.
|
31
|
+
"""
|
32
|
+
if element_class in self._elements:
|
33
|
+
self._elements.remove(element_class)
|
34
|
+
return True
|
35
|
+
return False
|
36
|
+
|
37
|
+
def clear(self):
|
38
|
+
"""Clear the registry completely."""
|
39
|
+
self._elements.clear()
|
40
|
+
return self
|
41
|
+
|
42
|
+
def find_markdown_handler(self, text: str) -> Optional[Type[NotionBlockElement]]:
|
43
|
+
"""Find an element that can handle the given markdown text."""
|
44
|
+
for element in self._elements:
|
45
|
+
if element.match_markdown(text):
|
46
|
+
return element
|
47
|
+
return None
|
48
|
+
|
49
|
+
def find_notion_handler(
|
50
|
+
self, block: Dict[str, Any]
|
51
|
+
) -> Optional[Type[NotionBlockElement]]:
|
52
|
+
"""Find an element that can handle the given Notion block."""
|
53
|
+
for element in self._elements:
|
54
|
+
if element.match_notion(block):
|
55
|
+
return element
|
56
|
+
return None
|
57
|
+
|
58
|
+
def markdown_to_notion(self, text: str) -> Optional[Dict[str, Any]]:
|
59
|
+
"""Convert markdown to Notion block using registered elements."""
|
60
|
+
handler = self.find_markdown_handler(text)
|
61
|
+
if handler:
|
62
|
+
return handler.markdown_to_notion(text)
|
63
|
+
return None
|
64
|
+
|
65
|
+
def notion_to_markdown(self, block: Dict[str, Any]) -> Optional[str]:
|
66
|
+
"""Convert Notion block to markdown using registered elements."""
|
67
|
+
handler = self.find_notion_handler(block)
|
68
|
+
if handler:
|
69
|
+
return handler.notion_to_markdown(block)
|
70
|
+
return None
|
71
|
+
|
72
|
+
def get_multiline_elements(self) -> List[Type[NotionBlockElement]]:
|
73
|
+
"""Get all registered multiline elements."""
|
74
|
+
return [element for element in self._elements if element.is_multiline()]
|
75
|
+
|
76
|
+
def get_elements(self) -> List[Type[NotionBlockElement]]:
|
77
|
+
"""Get all registered elements."""
|
78
|
+
return self._elements.copy()
|
79
|
+
|
80
|
+
def generate_llm_prompt(self) -> str:
|
81
|
+
"""
|
82
|
+
Generates an LLM system prompt that describes the Markdown syntax of all registered elements.
|
83
|
+
"""
|
84
|
+
element_classes = self._elements.copy()
|
85
|
+
|
86
|
+
formatter_names = [e.__name__ for e in element_classes]
|
87
|
+
if "TextInlineFormatter" not in formatter_names:
|
88
|
+
element_classes = [TextInlineFormatter] + element_classes
|
89
|
+
|
90
|
+
return MarkdownSyntaxPromptBuilder.generate_system_prompt(element_classes)
|
notionary/elements/{block_element_registry_builder.py → registry/block_element_registry_builder.py}
RENAMED
@@ -3,10 +3,12 @@ from typing import List, Type
|
|
3
3
|
from collections import OrderedDict
|
4
4
|
|
5
5
|
from notionary.elements.audio_element import AudioElement
|
6
|
+
from notionary.elements.bulleted_list_element import BulletedListElement
|
6
7
|
from notionary.elements.embed_element import EmbedElement
|
7
8
|
from notionary.elements.mention_element import MentionElement
|
8
9
|
from notionary.elements.notion_block_element import NotionBlockElement
|
9
|
-
from notionary.elements.
|
10
|
+
from notionary.elements.numbered_list_element import NumberedListElement
|
11
|
+
from notionary.elements.registry.block_element_registry import (
|
10
12
|
BlockElementRegistry,
|
11
13
|
)
|
12
14
|
|
@@ -17,10 +19,6 @@ from notionary.elements.code_block_element import CodeBlockElement
|
|
17
19
|
from notionary.elements.divider_element import DividerElement
|
18
20
|
from notionary.elements.table_element import TableElement
|
19
21
|
from notionary.elements.todo_lists import TodoElement
|
20
|
-
from notionary.elements.list_element import (
|
21
|
-
BulletedListElement,
|
22
|
-
NumberedListElement,
|
23
|
-
)
|
24
22
|
from notionary.elements.qoute_element import QuoteElement
|
25
23
|
from notionary.elements.image_element import ImageElement
|
26
24
|
from notionary.elements.video_element import VideoElement
|
@@ -42,33 +40,9 @@ class BlockElementRegistryBuilder:
|
|
42
40
|
self._elements = OrderedDict()
|
43
41
|
|
44
42
|
@classmethod
|
45
|
-
def
|
46
|
-
"""
|
47
|
-
Start with a completely empty registry builder.
|
48
|
-
|
49
|
-
Returns:
|
50
|
-
A new builder instance with no elements
|
51
|
-
"""
|
52
|
-
return cls()
|
53
|
-
|
54
|
-
@classmethod
|
55
|
-
def start_minimal(cls) -> BlockElementRegistryBuilder:
|
56
|
-
"""
|
57
|
-
Start with a minimal set of essential elements.
|
58
|
-
|
59
|
-
Returns:
|
60
|
-
A new builder instance with basic elements
|
61
|
-
"""
|
62
|
-
builder = cls()
|
63
|
-
return builder.with_headings().with_lists().with_paragraphs()
|
64
|
-
|
65
|
-
@classmethod
|
66
|
-
def start_standard(cls) -> BlockElementRegistryBuilder:
|
43
|
+
def create_full_registry(cls) -> BlockElementRegistry:
|
67
44
|
"""
|
68
45
|
Start with all standard elements in recommended order.
|
69
|
-
|
70
|
-
Returns:
|
71
|
-
A new builder instance with all standard elements
|
72
46
|
"""
|
73
47
|
builder = cls()
|
74
48
|
return (
|
@@ -78,7 +52,8 @@ class BlockElementRegistryBuilder:
|
|
78
52
|
.with_dividers()
|
79
53
|
.with_tables()
|
80
54
|
.with_columns()
|
81
|
-
.
|
55
|
+
.with_bulleted_list()
|
56
|
+
.with_numberd_list()
|
82
57
|
.with_toggles()
|
83
58
|
.with_quotes()
|
84
59
|
.with_todos()
|
@@ -89,9 +64,7 @@ class BlockElementRegistryBuilder:
|
|
89
64
|
.with_audio()
|
90
65
|
.with_mention()
|
91
66
|
.with_paragraphs()
|
92
|
-
)
|
93
|
-
|
94
|
-
# Element manipulation methods
|
67
|
+
).build()
|
95
68
|
|
96
69
|
def add_element(
|
97
70
|
self, element_class: Type[NotionBlockElement]
|
@@ -168,153 +141,108 @@ class BlockElementRegistryBuilder:
|
|
168
141
|
def with_paragraphs(self) -> BlockElementRegistryBuilder:
|
169
142
|
"""
|
170
143
|
Add support for paragraph elements.
|
171
|
-
|
172
|
-
Returns:
|
173
|
-
Self for method chaining
|
174
144
|
"""
|
175
145
|
return self.add_element(ParagraphElement)
|
176
146
|
|
177
147
|
def with_headings(self) -> BlockElementRegistryBuilder:
|
178
148
|
"""
|
179
149
|
Add support for heading elements.
|
180
|
-
|
181
|
-
Returns:
|
182
|
-
Self for method chaining
|
183
150
|
"""
|
184
151
|
return self.add_element(HeadingElement)
|
185
152
|
|
186
153
|
def with_callouts(self) -> BlockElementRegistryBuilder:
|
187
154
|
"""
|
188
155
|
Add support for callout elements.
|
189
|
-
|
190
|
-
Returns:
|
191
|
-
Self for method chaining
|
192
156
|
"""
|
193
157
|
return self.add_element(CalloutElement)
|
194
158
|
|
195
159
|
def with_code(self) -> BlockElementRegistryBuilder:
|
196
160
|
"""
|
197
161
|
Add support for code blocks.
|
198
|
-
|
199
|
-
Returns:
|
200
|
-
Self for method chaining
|
201
162
|
"""
|
202
163
|
return self.add_element(CodeBlockElement)
|
203
164
|
|
204
165
|
def with_dividers(self) -> BlockElementRegistryBuilder:
|
205
166
|
"""
|
206
167
|
Add support for divider elements.
|
207
|
-
|
208
|
-
Returns:
|
209
|
-
Self for method chaining
|
210
168
|
"""
|
211
169
|
return self.add_element(DividerElement)
|
212
170
|
|
213
171
|
def with_tables(self) -> BlockElementRegistryBuilder:
|
214
172
|
"""
|
215
173
|
Add support for tables.
|
216
|
-
|
217
|
-
Returns:
|
218
|
-
Self for method chaining
|
219
174
|
"""
|
220
175
|
return self.add_element(TableElement)
|
221
176
|
|
222
177
|
def with_columns(self) -> BlockElementRegistryBuilder:
|
223
178
|
"""
|
224
179
|
Add support for column elements.
|
225
|
-
|
226
|
-
Returns:
|
227
|
-
Self for method chaining
|
228
180
|
"""
|
229
181
|
return self.add_element(ColumnElement)
|
230
182
|
|
231
|
-
def
|
183
|
+
def with_bulleted_list(self) -> BlockElementRegistryBuilder:
|
184
|
+
"""
|
185
|
+
Add support for bulleted list elements (unordered lists).
|
232
186
|
"""
|
233
|
-
|
187
|
+
return self.add_element(BulletedListElement)
|
234
188
|
|
235
|
-
|
236
|
-
|
189
|
+
def with_numberd_list(self) -> BlockElementRegistryBuilder:
|
190
|
+
"""
|
191
|
+
Add support for numbered list elements (ordered lists).
|
237
192
|
"""
|
238
|
-
return self.add_element(
|
193
|
+
return self.add_element(NumberedListElement)
|
239
194
|
|
240
195
|
def with_toggles(self) -> BlockElementRegistryBuilder:
|
241
196
|
"""
|
242
197
|
Add support for toggle elements.
|
243
|
-
|
244
|
-
Returns:
|
245
|
-
Self for method chaining
|
246
198
|
"""
|
247
199
|
return self.add_element(ToggleElement)
|
248
200
|
|
249
201
|
def with_quotes(self) -> BlockElementRegistryBuilder:
|
250
202
|
"""
|
251
203
|
Add support for quote elements.
|
252
|
-
|
253
|
-
Returns:
|
254
|
-
Self for method chaining
|
255
204
|
"""
|
256
205
|
return self.add_element(QuoteElement)
|
257
206
|
|
258
207
|
def with_todos(self) -> BlockElementRegistryBuilder:
|
259
208
|
"""
|
260
209
|
Add support for todo elements.
|
261
|
-
|
262
|
-
Returns:
|
263
|
-
Self for method chaining
|
264
210
|
"""
|
265
211
|
return self.add_element(TodoElement)
|
266
212
|
|
267
213
|
def with_bookmarks(self) -> BlockElementRegistryBuilder:
|
268
214
|
"""
|
269
215
|
Add support for bookmark elements.
|
270
|
-
|
271
|
-
Returns:
|
272
|
-
Self for method chaining
|
273
216
|
"""
|
274
217
|
return self.add_element(BookmarkElement)
|
275
218
|
|
276
219
|
def with_images(self) -> BlockElementRegistryBuilder:
|
277
220
|
"""
|
278
221
|
Add support for image elements.
|
279
|
-
|
280
|
-
Returns:
|
281
|
-
Self for method chaining
|
282
222
|
"""
|
283
223
|
return self.add_element(ImageElement)
|
284
224
|
|
285
225
|
def with_videos(self) -> BlockElementRegistryBuilder:
|
286
226
|
"""
|
287
227
|
Add support for video elements.
|
288
|
-
|
289
|
-
Returns:
|
290
|
-
Self for method chaining
|
291
228
|
"""
|
292
229
|
return self.add_element(VideoElement)
|
293
230
|
|
294
231
|
def with_embeds(self) -> BlockElementRegistryBuilder:
|
295
232
|
"""
|
296
233
|
Add support for embed elements.
|
297
|
-
|
298
|
-
Returns:
|
299
|
-
Self for method chaining
|
300
234
|
"""
|
301
235
|
return self.add_element(EmbedElement)
|
302
236
|
|
303
237
|
def with_audio(self) -> BlockElementRegistryBuilder:
|
304
238
|
"""
|
305
239
|
Add support for audio elements.
|
306
|
-
|
307
|
-
Returns:
|
308
|
-
Self for method chaining
|
309
240
|
"""
|
310
241
|
return self.add_element(AudioElement)
|
311
242
|
|
312
243
|
def with_media_support(self) -> BlockElementRegistryBuilder:
|
313
244
|
"""
|
314
245
|
Add support for media elements (images, videos, audio).
|
315
|
-
|
316
|
-
Returns:
|
317
|
-
Self for method chaining
|
318
246
|
"""
|
319
247
|
return self.with_images().with_videos().with_audio()
|
320
248
|
|
@@ -333,10 +261,8 @@ class BlockElementRegistryBuilder:
|
|
333
261
|
A configured BlockElementRegistry instance
|
334
262
|
"""
|
335
263
|
if ParagraphElement.__name__ not in self._elements:
|
336
|
-
# Add paragraph as fallback if not present
|
337
264
|
self.add_element(ParagraphElement)
|
338
265
|
else:
|
339
|
-
# Ensure it's at the end
|
340
266
|
self._ensure_paragraph_at_end()
|
341
267
|
|
342
268
|
registry = BlockElementRegistry()
|
@@ -346,38 +272,3 @@ class BlockElementRegistryBuilder:
|
|
346
272
|
registry.register(element_class)
|
347
273
|
|
348
274
|
return registry
|
349
|
-
|
350
|
-
@classmethod
|
351
|
-
def create_standard_registry(cls) -> BlockElementRegistry:
|
352
|
-
"""
|
353
|
-
Factory method to directly create a standard registry.
|
354
|
-
|
355
|
-
Returns:
|
356
|
-
A fully configured registry instance
|
357
|
-
"""
|
358
|
-
return cls.start_standard().build()
|
359
|
-
|
360
|
-
@classmethod
|
361
|
-
def create_minimal_registry(cls) -> BlockElementRegistry:
|
362
|
-
"""
|
363
|
-
Factory method to directly create a minimal registry.
|
364
|
-
|
365
|
-
Returns:
|
366
|
-
A minimal registry instance
|
367
|
-
"""
|
368
|
-
return cls.start_minimal().build()
|
369
|
-
|
370
|
-
@classmethod
|
371
|
-
def create_custom_registry(
|
372
|
-
cls, element_classes: List[Type[NotionBlockElement]]
|
373
|
-
) -> BlockElementRegistry:
|
374
|
-
"""
|
375
|
-
Factory method to directly create a custom registry.
|
376
|
-
|
377
|
-
Args:
|
378
|
-
element_classes: List of element classes to register
|
379
|
-
|
380
|
-
Returns:
|
381
|
-
A custom configured registry instance
|
382
|
-
"""
|
383
|
-
return cls().add_elements(element_classes).build()
|
@@ -1,10 +1,8 @@
|
|
1
|
-
# File: elements/tables.py
|
2
|
-
|
3
|
-
from typing import Dict, Any, Optional, List, Tuple
|
4
|
-
from typing_extensions import override
|
5
1
|
import re
|
2
|
+
from typing import Dict, Any, Optional, List, Tuple
|
6
3
|
from notionary.elements.notion_block_element import NotionBlockElement
|
7
4
|
from notionary.elements.text_inline_formatter import TextInlineFormatter
|
5
|
+
from notionary.elements.prompts.element_prompt_content import ElementPromptContent
|
8
6
|
|
9
7
|
|
10
8
|
class TableElement(NotionBlockElement):
|
@@ -20,11 +18,9 @@ class TableElement(NotionBlockElement):
|
|
20
18
|
The second line with dashes and optional colons defines column alignment.
|
21
19
|
"""
|
22
20
|
|
23
|
-
# Patterns for detecting Markdown tables
|
24
21
|
ROW_PATTERN = re.compile(r"^\s*\|(.+)\|\s*$")
|
25
22
|
SEPARATOR_PATTERN = re.compile(r"^\s*\|([\s\-:|]+)\|\s*$")
|
26
23
|
|
27
|
-
@override
|
28
24
|
@staticmethod
|
29
25
|
def match_markdown(text: str) -> bool:
|
30
26
|
"""Check if text contains a markdown table."""
|
@@ -43,13 +39,11 @@ class TableElement(NotionBlockElement):
|
|
43
39
|
|
44
40
|
return False
|
45
41
|
|
46
|
-
@override
|
47
42
|
@staticmethod
|
48
43
|
def match_notion(block: Dict[str, Any]) -> bool:
|
49
44
|
"""Check if block is a Notion table."""
|
50
45
|
return block.get("type") == "table"
|
51
46
|
|
52
|
-
@override
|
53
47
|
@staticmethod
|
54
48
|
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
55
49
|
"""Convert markdown table to Notion table block."""
|
@@ -82,7 +76,6 @@ class TableElement(NotionBlockElement):
|
|
82
76
|
},
|
83
77
|
}
|
84
78
|
|
85
|
-
@override
|
86
79
|
@staticmethod
|
87
80
|
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
88
81
|
"""Convert Notion table block to markdown table."""
|
@@ -145,7 +138,6 @@ class TableElement(NotionBlockElement):
|
|
145
138
|
|
146
139
|
return "\n".join(table_rows)
|
147
140
|
|
148
|
-
@override
|
149
141
|
@staticmethod
|
150
142
|
def is_multiline() -> bool:
|
151
143
|
"""Indicates if this element handles content that spans multiple lines."""
|
@@ -289,16 +281,12 @@ class TableElement(NotionBlockElement):
|
|
289
281
|
return position
|
290
282
|
|
291
283
|
@classmethod
|
292
|
-
def get_llm_prompt_content(cls) ->
|
284
|
+
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
293
285
|
"""Returns information for LLM prompts about this element."""
|
294
286
|
return {
|
295
287
|
"description": "Creates formatted tables with rows and columns for structured data.",
|
296
288
|
"when_to_use": "Use tables to organize and present structured data in a grid format, making information easier to compare and analyze. Tables are ideal for data sets, comparison charts, pricing information, or any content that benefits from columnar organization.",
|
297
|
-
"
|
298
|
-
"The header row is required and will be displayed differently in Notion",
|
299
|
-
"The separator row with dashes is required to define the table structure",
|
300
|
-
"Table cells support inline formatting such as **bold** and *italic*",
|
301
|
-
],
|
289
|
+
"syntax": "| Header 1 | Header 2 | Header 3 |\n| -------- | -------- | -------- |\n| Cell 1 | Cell 2 | Cell 3 |",
|
302
290
|
"examples": [
|
303
291
|
"| Product | Price | Stock |\n| ------- | ----- | ----- |\n| Widget A | $10.99 | 42 |\n| Widget B | $14.99 | 27 |",
|
304
292
|
"| Name | Role | Department |\n| ---- | ---- | ---------- |\n| John Smith | Manager | Marketing |\n| Jane Doe | Director | Sales |",
|