notionary 0.1.25__py3-none-any.whl → 0.1.26__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/elements/audio_element.py +41 -38
- notionary/elements/bookmark_element.py +36 -27
- notionary/elements/bulleted_list_element.py +28 -21
- notionary/elements/callout_element.py +39 -31
- notionary/elements/code_block_element.py +38 -26
- notionary/elements/divider_element.py +29 -18
- notionary/elements/embed_element.py +37 -28
- notionary/elements/heading_element.py +39 -24
- notionary/elements/image_element.py +33 -24
- notionary/elements/mention_element.py +40 -29
- notionary/elements/notion_block_element.py +13 -31
- notionary/elements/numbered_list_element.py +29 -20
- notionary/elements/paragraph_element.py +37 -31
- notionary/elements/prompts/element_prompt_content.py +91 -7
- notionary/elements/prompts/synthax_prompt_builder.py +63 -16
- notionary/elements/qoute_element.py +72 -74
- notionary/elements/registry/block_element_registry_builder.py +6 -9
- notionary/elements/table_element.py +49 -36
- notionary/elements/text_inline_formatter.py +23 -15
- notionary/elements/{todo_lists.py → todo_element.py} +34 -25
- notionary/elements/toggle_element.py +184 -108
- notionary/elements/toggleable_heading_element.py +269 -0
- notionary/elements/video_element.py +37 -28
- notionary/page/content/page_content_manager.py +3 -8
- notionary/page/markdown_to_notion_converter.py +269 -274
- notionary/page/notion_page.py +2 -4
- notionary/page/notion_to_markdown_converter.py +20 -95
- {notionary-0.1.25.dist-info → notionary-0.1.26.dist-info}/METADATA +1 -1
- notionary-0.1.26.dist-info/RECORD +58 -0
- {notionary-0.1.25.dist-info → notionary-0.1.26.dist-info}/WHEEL +1 -1
- notionary/elements/column_element.py +0 -307
- notionary-0.1.25.dist-info/RECORD +0 -58
- {notionary-0.1.25.dist-info → notionary-0.1.26.dist-info}/licenses/LICENSE +0 -0
- {notionary-0.1.25.dist-info → notionary-0.1.26.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,10 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional, List
|
3
3
|
from notionary.elements.notion_block_element import NotionBlockElement
|
4
|
-
from notionary.elements.prompts.element_prompt_content import
|
4
|
+
from notionary.elements.prompts.element_prompt_content import (
|
5
|
+
ElementPromptBuilder,
|
6
|
+
ElementPromptContent,
|
7
|
+
)
|
5
8
|
|
6
9
|
|
7
10
|
class EmbedElement(NotionBlockElement):
|
@@ -20,19 +23,19 @@ class EmbedElement(NotionBlockElement):
|
|
20
23
|
r"^<embed(?:\:(.*?))?>(?:\s*)" + r'\((https?://[^\s"]+)' + r"\)$"
|
21
24
|
)
|
22
25
|
|
23
|
-
@
|
24
|
-
def match_markdown(text: str) -> bool:
|
26
|
+
@classmethod
|
27
|
+
def match_markdown(cls, text: str) -> bool:
|
25
28
|
"""Check if text is a markdown embed."""
|
26
29
|
text = text.strip()
|
27
30
|
return text.startswith("<embed") and bool(EmbedElement.PATTERN.match(text))
|
28
31
|
|
29
|
-
@
|
30
|
-
def match_notion(block: Dict[str, Any]) -> bool:
|
32
|
+
@classmethod
|
33
|
+
def match_notion(cls, block: Dict[str, Any]) -> bool:
|
31
34
|
"""Check if block is a Notion embed."""
|
32
35
|
return block.get("type") == "embed"
|
33
36
|
|
34
|
-
@
|
35
|
-
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
37
|
+
@classmethod
|
38
|
+
def markdown_to_notion(cls, text: str) -> Optional[Dict[str, Any]]:
|
36
39
|
"""Convert markdown embed to Notion embed block."""
|
37
40
|
embed_match = EmbedElement.PATTERN.match(text.strip())
|
38
41
|
if not embed_match:
|
@@ -58,8 +61,8 @@ class EmbedElement(NotionBlockElement):
|
|
58
61
|
|
59
62
|
return embed_block
|
60
63
|
|
61
|
-
@
|
62
|
-
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
64
|
+
@classmethod
|
65
|
+
def notion_to_markdown(cls, block: Dict[str, Any]) -> Optional[str]:
|
63
66
|
"""Convert Notion embed block to markdown embed."""
|
64
67
|
if block.get("type") != "embed":
|
65
68
|
return None
|
@@ -78,16 +81,16 @@ class EmbedElement(NotionBlockElement):
|
|
78
81
|
|
79
82
|
if caption:
|
80
83
|
return f"<embed:{caption}>({url})"
|
81
|
-
else:
|
82
|
-
return f"<embed>({url})"
|
83
84
|
|
84
|
-
|
85
|
-
|
85
|
+
return f"<embed>({url})"
|
86
|
+
|
87
|
+
@classmethod
|
88
|
+
def is_multiline(cls) -> bool:
|
86
89
|
"""Embeds are single-line elements."""
|
87
90
|
return False
|
88
91
|
|
89
|
-
@
|
90
|
-
def _extract_text_content(rich_text: List[Dict[str, Any]]) -> str:
|
92
|
+
@classmethod
|
93
|
+
def _extract_text_content(cls, rich_text: List[Dict[str, Any]]) -> str:
|
91
94
|
"""Extract plain text content from Notion rich_text elements."""
|
92
95
|
result = ""
|
93
96
|
for text_obj in rich_text:
|
@@ -102,18 +105,24 @@ class EmbedElement(NotionBlockElement):
|
|
102
105
|
"""
|
103
106
|
Returns structured LLM prompt metadata for the embed element.
|
104
107
|
"""
|
105
|
-
return
|
106
|
-
|
107
|
-
|
108
|
+
return (
|
109
|
+
ElementPromptBuilder()
|
110
|
+
.with_description(
|
111
|
+
"Embeds external content from websites, PDFs, Google Maps, and other sources directly in your document."
|
112
|
+
)
|
113
|
+
.with_usage_guidelines(
|
108
114
|
"Use embeds when you want to include external content that isn't just a video or image. "
|
109
115
|
"Embeds are great for interactive content, reference materials, or live data sources."
|
110
|
-
)
|
111
|
-
"
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
116
|
+
)
|
117
|
+
.with_syntax("<embed:Caption>(https://example.com)")
|
118
|
+
.with_examples(
|
119
|
+
[
|
120
|
+
"<embed:Course materials>(https://drive.google.com/file/d/123456/view)",
|
121
|
+
"<embed:Our office location>(https://www.google.com/maps?q=San+Francisco)",
|
122
|
+
"<embed:Latest announcement>(https://twitter.com/NotionHQ/status/1234567890)",
|
123
|
+
"<embed:Project documentation>(https://github.com/username/repo)",
|
124
|
+
"<embed>(https://example.com/important-reference.pdf)",
|
125
|
+
]
|
126
|
+
)
|
127
|
+
.build()
|
128
|
+
)
|
@@ -2,34 +2,40 @@ import re
|
|
2
2
|
from typing import Dict, Any, Optional
|
3
3
|
|
4
4
|
from notionary.elements.notion_block_element import NotionBlockElement
|
5
|
-
from notionary.elements.prompts.element_prompt_content import
|
5
|
+
from notionary.elements.prompts.element_prompt_content import (
|
6
|
+
ElementPromptBuilder,
|
7
|
+
ElementPromptContent,
|
8
|
+
)
|
6
9
|
from notionary.elements.text_inline_formatter import TextInlineFormatter
|
7
10
|
|
8
11
|
|
9
12
|
class HeadingElement(NotionBlockElement):
|
10
13
|
"""Handles conversion between Markdown headings and Notion heading blocks."""
|
11
14
|
|
12
|
-
PATTERN = re.compile(r"^(#{1,
|
15
|
+
PATTERN = re.compile(r"^(#{1,3})\s(.+)$")
|
13
16
|
|
14
|
-
@
|
15
|
-
def match_markdown(text: str) -> bool:
|
17
|
+
@classmethod
|
18
|
+
def match_markdown(cls, text: str) -> bool:
|
16
19
|
"""Check if text is a markdown heading."""
|
17
20
|
return bool(HeadingElement.PATTERN.match(text))
|
18
21
|
|
19
|
-
@
|
20
|
-
def match_notion(block: Dict[str, Any]) -> bool:
|
22
|
+
@classmethod
|
23
|
+
def match_notion(cls, block: Dict[str, Any]) -> bool:
|
21
24
|
"""Check if block is a Notion heading."""
|
22
25
|
block_type: str = block.get("type", "")
|
23
|
-
return block_type.startswith("heading_") and block_type[-1] in "
|
26
|
+
return block_type.startswith("heading_") and block_type[-1] in "123"
|
24
27
|
|
25
|
-
@
|
26
|
-
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
28
|
+
@classmethod
|
29
|
+
def markdown_to_notion(cls, text: str) -> Optional[Dict[str, Any]]:
|
27
30
|
"""Convert markdown heading to Notion heading block."""
|
28
31
|
header_match = HeadingElement.PATTERN.match(text)
|
29
32
|
if not header_match:
|
30
33
|
return None
|
31
34
|
|
32
35
|
level = len(header_match.group(1))
|
36
|
+
if not 1 <= level <= 3:
|
37
|
+
return None
|
38
|
+
|
33
39
|
content = header_match.group(2)
|
34
40
|
|
35
41
|
return {
|
@@ -39,8 +45,8 @@ class HeadingElement(NotionBlockElement):
|
|
39
45
|
},
|
40
46
|
}
|
41
47
|
|
42
|
-
@
|
43
|
-
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
48
|
+
@classmethod
|
49
|
+
def notion_to_markdown(cls, block: Dict[str, Any]) -> Optional[str]:
|
44
50
|
"""Convert Notion heading block to markdown heading."""
|
45
51
|
block_type = block.get("type", "")
|
46
52
|
|
@@ -49,7 +55,8 @@ class HeadingElement(NotionBlockElement):
|
|
49
55
|
|
50
56
|
try:
|
51
57
|
level = int(block_type[-1])
|
52
|
-
|
58
|
+
# Only allow levels 1-3
|
59
|
+
if not 1 <= level <= 3:
|
53
60
|
return None
|
54
61
|
except ValueError:
|
55
62
|
return None
|
@@ -61,8 +68,8 @@ class HeadingElement(NotionBlockElement):
|
|
61
68
|
prefix = "#" * level
|
62
69
|
return f"{prefix} {text or ''}"
|
63
70
|
|
64
|
-
@
|
65
|
-
def is_multiline() -> bool:
|
71
|
+
@classmethod
|
72
|
+
def is_multiline(cls) -> bool:
|
66
73
|
return False
|
67
74
|
|
68
75
|
@classmethod
|
@@ -70,13 +77,21 @@ class HeadingElement(NotionBlockElement):
|
|
70
77
|
"""
|
71
78
|
Returns structured LLM prompt metadata for the heading element.
|
72
79
|
"""
|
73
|
-
return
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
"
|
80
|
-
|
81
|
-
|
82
|
-
|
80
|
+
return (
|
81
|
+
ElementPromptBuilder()
|
82
|
+
.with_description(
|
83
|
+
"Use Markdown headings (#, ##, ###) to structure content hierarchically."
|
84
|
+
)
|
85
|
+
.with_usage_guidelines(
|
86
|
+
"Use to group content into sections and define a visual hierarchy."
|
87
|
+
)
|
88
|
+
.with_syntax("## Your Heading Text")
|
89
|
+
.with_examples(
|
90
|
+
[
|
91
|
+
"# Main Title",
|
92
|
+
"## Section Title",
|
93
|
+
"### Subsection Title",
|
94
|
+
]
|
95
|
+
)
|
96
|
+
.build()
|
97
|
+
)
|
@@ -1,7 +1,10 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional, List
|
3
3
|
from notionary.elements.notion_block_element import NotionBlockElement
|
4
|
-
from notionary.elements.prompts.element_prompt_content import
|
4
|
+
from notionary.elements.prompts.element_prompt_content import (
|
5
|
+
ElementPromptBuilder,
|
6
|
+
ElementPromptContent,
|
7
|
+
)
|
5
8
|
|
6
9
|
|
7
10
|
class ImageElement(NotionBlockElement):
|
@@ -22,20 +25,20 @@ class ImageElement(NotionBlockElement):
|
|
22
25
|
+ r"\)$" # closing parenthesis
|
23
26
|
)
|
24
27
|
|
25
|
-
@
|
26
|
-
def match_markdown(text: str) -> bool:
|
28
|
+
@classmethod
|
29
|
+
def match_markdown(cls, text: str) -> bool:
|
27
30
|
"""Check if text is a markdown image."""
|
28
31
|
return text.strip().startswith("![") and bool(
|
29
32
|
ImageElement.PATTERN.match(text.strip())
|
30
33
|
)
|
31
34
|
|
32
|
-
@
|
33
|
-
def match_notion(block: Dict[str, Any]) -> bool:
|
35
|
+
@classmethod
|
36
|
+
def match_notion(cls, block: Dict[str, Any]) -> bool:
|
34
37
|
"""Check if block is a Notion image."""
|
35
38
|
return block.get("type") == "image"
|
36
39
|
|
37
|
-
@
|
38
|
-
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
40
|
+
@classmethod
|
41
|
+
def markdown_to_notion(cls, text: str) -> Optional[Dict[str, Any]]:
|
39
42
|
"""Convert markdown image to Notion image block."""
|
40
43
|
image_match = ImageElement.PATTERN.match(text.strip())
|
41
44
|
if not image_match:
|
@@ -61,8 +64,8 @@ class ImageElement(NotionBlockElement):
|
|
61
64
|
|
62
65
|
return image_block
|
63
66
|
|
64
|
-
@
|
65
|
-
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
67
|
+
@classmethod
|
68
|
+
def notion_to_markdown(cls, block: Dict[str, Any]) -> Optional[str]:
|
66
69
|
"""Convert Notion image block to markdown image."""
|
67
70
|
if block.get("type") != "image":
|
68
71
|
return None
|
@@ -88,8 +91,8 @@ class ImageElement(NotionBlockElement):
|
|
88
91
|
|
89
92
|
return f""
|
90
93
|
|
91
|
-
@
|
92
|
-
def _extract_text_content(rich_text: List[Dict[str, Any]]) -> str:
|
94
|
+
@classmethod
|
95
|
+
def _extract_text_content(cls, rich_text: List[Dict[str, Any]]) -> str:
|
93
96
|
"""Extract plain text content from Notion rich_text elements."""
|
94
97
|
result = ""
|
95
98
|
for text_obj in rich_text:
|
@@ -99,8 +102,8 @@ class ImageElement(NotionBlockElement):
|
|
99
102
|
result += text_obj.get("plain_text", "")
|
100
103
|
return result
|
101
104
|
|
102
|
-
@
|
103
|
-
def is_multiline() -> bool:
|
105
|
+
@classmethod
|
106
|
+
def is_multiline(cls) -> bool:
|
104
107
|
return False
|
105
108
|
|
106
109
|
@classmethod
|
@@ -108,17 +111,23 @@ class ImageElement(NotionBlockElement):
|
|
108
111
|
"""
|
109
112
|
Returns structured LLM prompt metadata for the image element.
|
110
113
|
"""
|
111
|
-
return
|
112
|
-
|
113
|
-
|
114
|
+
return (
|
115
|
+
ElementPromptBuilder()
|
116
|
+
.with_description(
|
117
|
+
"Embeds an image from an external URL into your document."
|
118
|
+
)
|
119
|
+
.with_usage_guidelines(
|
114
120
|
"Use images to include visual content such as diagrams, screenshots, charts, photos, or illustrations "
|
115
121
|
"that enhance your document. Images can make complex information easier to understand, create visual interest, "
|
116
122
|
"or provide evidence for your points."
|
117
|
-
)
|
118
|
-
"
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
123
|
+
)
|
124
|
+
.with_syntax("")
|
125
|
+
.with_examples(
|
126
|
+
[
|
127
|
+
"",
|
128
|
+
"",
|
129
|
+
'',
|
130
|
+
]
|
131
|
+
)
|
132
|
+
.build()
|
133
|
+
)
|
@@ -3,7 +3,10 @@ from typing import Dict, Any, Optional, List
|
|
3
3
|
from typing_extensions import override
|
4
4
|
|
5
5
|
from notionary.elements.notion_block_element import NotionBlockElement
|
6
|
-
from notionary.elements.prompts.element_prompt_content import
|
6
|
+
from notionary.elements.prompts.element_prompt_content import (
|
7
|
+
ElementPromptBuilder,
|
8
|
+
ElementPromptContent,
|
9
|
+
)
|
7
10
|
|
8
11
|
|
9
12
|
class MentionElement(NotionBlockElement):
|
@@ -46,16 +49,16 @@ class MentionElement(NotionBlockElement):
|
|
46
49
|
},
|
47
50
|
}
|
48
51
|
|
49
|
-
@
|
50
|
-
def match_markdown(text: str) -> bool:
|
52
|
+
@classmethod
|
53
|
+
def match_markdown(cls, text: str) -> bool:
|
51
54
|
"""Check if text contains a markdown mention."""
|
52
55
|
for mention_type in MentionElement.MENTION_TYPES.values():
|
53
56
|
if re.search(mention_type["pattern"], text):
|
54
57
|
return True
|
55
58
|
return False
|
56
59
|
|
57
|
-
@
|
58
|
-
def match_notion(block: Dict[str, Any]) -> bool:
|
60
|
+
@classmethod
|
61
|
+
def match_notion(cls, block: Dict[str, Any]) -> bool:
|
59
62
|
"""Check if block contains a mention."""
|
60
63
|
supported_block_types = [
|
61
64
|
"paragraph",
|
@@ -74,8 +77,8 @@ class MentionElement(NotionBlockElement):
|
|
74
77
|
|
75
78
|
return any(text_item.get("type") == "mention" for text_item in rich_text)
|
76
79
|
|
77
|
-
@
|
78
|
-
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
80
|
+
@classmethod
|
81
|
+
def markdown_to_notion(cls, text: str) -> Optional[Dict[str, Any]]:
|
79
82
|
"""Convert markdown text with mentions to a Notion paragraph block."""
|
80
83
|
if not MentionElement.match_markdown(text):
|
81
84
|
return None
|
@@ -87,8 +90,8 @@ class MentionElement(NotionBlockElement):
|
|
87
90
|
"paragraph": {"rich_text": rich_text, "color": "default"},
|
88
91
|
}
|
89
92
|
|
90
|
-
@
|
91
|
-
def _process_markdown_with_mentions(text: str) -> List[Dict[str, Any]]:
|
93
|
+
@classmethod
|
94
|
+
def _process_markdown_with_mentions(cls, text: str) -> List[Dict[str, Any]]:
|
92
95
|
"""Convert markdown mentions to Notion rich_text format."""
|
93
96
|
mentions = []
|
94
97
|
|
@@ -136,8 +139,8 @@ class MentionElement(NotionBlockElement):
|
|
136
139
|
|
137
140
|
return rich_text
|
138
141
|
|
139
|
-
@
|
140
|
-
def _create_text_item(content: str) -> Dict[str, Any]:
|
142
|
+
@classmethod
|
143
|
+
def _create_text_item(cls, content: str) -> Dict[str, Any]:
|
141
144
|
"""Create a text item with default annotations."""
|
142
145
|
text_item = {
|
143
146
|
"type": "text",
|
@@ -147,8 +150,8 @@ class MentionElement(NotionBlockElement):
|
|
147
150
|
}
|
148
151
|
return text_item
|
149
152
|
|
150
|
-
@
|
151
|
-
def _default_annotations() -> Dict[str, Any]:
|
153
|
+
@classmethod
|
154
|
+
def _default_annotations(cls) -> Dict[str, Any]:
|
152
155
|
"""Return default annotations for rich text."""
|
153
156
|
return {
|
154
157
|
"bold": False,
|
@@ -159,8 +162,8 @@ class MentionElement(NotionBlockElement):
|
|
159
162
|
"color": "default",
|
160
163
|
}
|
161
164
|
|
162
|
-
@
|
163
|
-
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
165
|
+
@classmethod
|
166
|
+
def notion_to_markdown(cls, block: Dict[str, Any]) -> Optional[str]:
|
164
167
|
"""Extract mentions from Notion block and convert to markdown format."""
|
165
168
|
block_type = block.get("type")
|
166
169
|
if not block_type or block_type not in block:
|
@@ -176,8 +179,8 @@ class MentionElement(NotionBlockElement):
|
|
176
179
|
|
177
180
|
return None
|
178
181
|
|
179
|
-
@
|
180
|
-
def _process_rich_text_with_mentions(rich_text: List[Dict[str, Any]]) -> str:
|
182
|
+
@classmethod
|
183
|
+
def _process_rich_text_with_mentions(cls, rich_text: List[Dict[str, Any]]) -> str:
|
181
184
|
"""Convert rich text with mentions to markdown string."""
|
182
185
|
result = []
|
183
186
|
|
@@ -197,8 +200,8 @@ class MentionElement(NotionBlockElement):
|
|
197
200
|
|
198
201
|
return "".join(result)
|
199
202
|
|
200
|
-
@
|
201
|
-
def is_multiline() -> bool:
|
203
|
+
@classmethod
|
204
|
+
def is_multiline(cls) -> bool:
|
202
205
|
return False
|
203
206
|
|
204
207
|
@classmethod
|
@@ -206,13 +209,21 @@ class MentionElement(NotionBlockElement):
|
|
206
209
|
"""
|
207
210
|
Returns structured LLM prompt metadata for the mention element.
|
208
211
|
"""
|
209
|
-
return
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
"
|
216
|
-
|
217
|
-
]
|
218
|
-
|
212
|
+
return (
|
213
|
+
ElementPromptBuilder()
|
214
|
+
.with_description(
|
215
|
+
"References to Notion pages, databases, or dates within text content."
|
216
|
+
)
|
217
|
+
.with_usage_guidelines(
|
218
|
+
"When you want to link to other Notion content within your text."
|
219
|
+
)
|
220
|
+
.with_syntax("@[page-id]")
|
221
|
+
.with_examples(
|
222
|
+
[
|
223
|
+
"Check the meeting notes at @[1a6389d5-7bd3-80c5-9a87-e90b034989d0]",
|
224
|
+
"Deadline is @date[2023-12-31]",
|
225
|
+
"Use the structure in @db[1a6389d5-7bd3-80e9-b199-000cfb3fa0b3]",
|
226
|
+
]
|
227
|
+
)
|
228
|
+
.build()
|
229
|
+
)
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import inspect
|
2
1
|
from typing import Dict, Any, Optional
|
3
2
|
from abc import ABC
|
4
3
|
|
@@ -8,45 +7,28 @@ from notionary.elements.prompts.element_prompt_content import ElementPromptConte
|
|
8
7
|
class NotionBlockElement(ABC):
|
9
8
|
"""Base class for elements that can be converted between Markdown and Notion."""
|
10
9
|
|
11
|
-
@
|
12
|
-
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
10
|
+
@classmethod
|
11
|
+
def markdown_to_notion(cls, text: str) -> Optional[Dict[str, Any]]:
|
13
12
|
"""Convert markdown to Notion block."""
|
14
13
|
|
15
|
-
@
|
16
|
-
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
14
|
+
@classmethod
|
15
|
+
def notion_to_markdown(cls, block: Dict[str, Any]) -> Optional[str]:
|
17
16
|
"""Convert Notion block to markdown."""
|
18
17
|
|
19
|
-
@
|
20
|
-
def match_markdown(text: str) -> bool:
|
18
|
+
@classmethod
|
19
|
+
def match_markdown(cls, text: str) -> bool:
|
21
20
|
"""Check if this element can handle the given markdown text."""
|
22
|
-
return bool(
|
21
|
+
return bool(cls.markdown_to_notion(text)) # Now calls the class's version
|
23
22
|
|
24
|
-
@
|
25
|
-
def match_notion(block: Dict[str, Any]) -> bool:
|
23
|
+
@classmethod
|
24
|
+
def match_notion(cls, block: Dict[str, Any]) -> bool:
|
26
25
|
"""Check if this element can handle the given Notion block."""
|
27
|
-
return bool(
|
28
|
-
|
29
|
-
@staticmethod
|
30
|
-
def is_multiline() -> bool:
|
31
|
-
return False
|
26
|
+
return bool(cls.notion_to_markdown(block)) # Now calls the class's version
|
32
27
|
|
33
28
|
@classmethod
|
34
|
-
def
|
35
|
-
|
36
|
-
Returns documentation specifically formatted for LLM system prompts.
|
37
|
-
Can be overridden by subclasses to provide custom LLM-friendly documentation.
|
38
|
-
|
39
|
-
By default, returns the class docstring.
|
40
|
-
"""
|
29
|
+
def is_multiline(cls) -> bool:
|
30
|
+
return False
|
41
31
|
|
42
32
|
@classmethod
|
43
33
|
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
44
|
-
"""
|
45
|
-
Returns a dictionary with information for LLM prompts about this element.
|
46
|
-
This default implementation extracts information from the class docstring.
|
47
|
-
Subclasses should override this method to provide more structured information.
|
48
|
-
|
49
|
-
Returns:
|
50
|
-
Dictionary with documentation information
|
51
|
-
"""
|
52
|
-
return {"description": inspect.cleandoc(cls.__doc__ or ""), "examples": []}
|
34
|
+
"""Returns a dictionary with information for LLM prompts about this element."""
|
@@ -1,15 +1,18 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional
|
3
3
|
from notionary.elements.notion_block_element import NotionBlockElement
|
4
|
-
from notionary.elements.prompts.element_prompt_content import
|
4
|
+
from notionary.elements.prompts.element_prompt_content import (
|
5
|
+
ElementPromptBuilder,
|
6
|
+
ElementPromptContent,
|
7
|
+
)
|
5
8
|
from notionary.elements.text_inline_formatter import TextInlineFormatter
|
6
9
|
|
7
10
|
|
8
11
|
class NumberedListElement(NotionBlockElement):
|
9
12
|
"""Class for converting between Markdown numbered lists and Notion numbered list items."""
|
10
13
|
|
11
|
-
@
|
12
|
-
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
14
|
+
@classmethod
|
15
|
+
def markdown_to_notion(cls, text: str) -> Optional[Dict[str, Any]]:
|
13
16
|
"""Convert markdown numbered list item to Notion block."""
|
14
17
|
pattern = re.compile(r"^\s*(\d+)\.\s+(.+)$")
|
15
18
|
numbered_match = pattern.match(text)
|
@@ -26,8 +29,8 @@ class NumberedListElement(NotionBlockElement):
|
|
26
29
|
"numbered_list_item": {"rich_text": rich_text, "color": "default"},
|
27
30
|
}
|
28
31
|
|
29
|
-
@
|
30
|
-
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
32
|
+
@classmethod
|
33
|
+
def notion_to_markdown(cls, block: Dict[str, Any]) -> Optional[str]:
|
31
34
|
"""Convert Notion numbered list item block to markdown."""
|
32
35
|
if block.get("type") != "numbered_list_item":
|
33
36
|
return None
|
@@ -37,19 +40,19 @@ class NumberedListElement(NotionBlockElement):
|
|
37
40
|
|
38
41
|
return f"1. {content}"
|
39
42
|
|
40
|
-
@
|
41
|
-
def match_markdown(text: str) -> bool:
|
43
|
+
@classmethod
|
44
|
+
def match_markdown(cls, text: str) -> bool:
|
42
45
|
"""Check if this element can handle the given markdown text."""
|
43
46
|
pattern = re.compile(r"^\s*\d+\.\s+(.+)$")
|
44
47
|
return bool(pattern.match(text))
|
45
48
|
|
46
|
-
@
|
47
|
-
def match_notion(block: Dict[str, Any]) -> bool:
|
49
|
+
@classmethod
|
50
|
+
def match_notion(cls, block: Dict[str, Any]) -> bool:
|
48
51
|
"""Check if this element can handle the given Notion block."""
|
49
52
|
return block.get("type") == "numbered_list_item"
|
50
53
|
|
51
|
-
@
|
52
|
-
def is_multiline() -> bool:
|
54
|
+
@classmethod
|
55
|
+
def is_multiline(cls) -> bool:
|
53
56
|
return False
|
54
57
|
|
55
58
|
@classmethod
|
@@ -57,12 +60,18 @@ class NumberedListElement(NotionBlockElement):
|
|
57
60
|
"""
|
58
61
|
Returns structured LLM prompt metadata for the numbered list element.
|
59
62
|
"""
|
60
|
-
return
|
61
|
-
|
62
|
-
"
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
63
|
+
return (
|
64
|
+
ElementPromptBuilder()
|
65
|
+
.with_description("Creates numbered list items for ordered sequences.")
|
66
|
+
.with_usage_guidelines(
|
67
|
+
"Use for lists where order matters, such as steps, rankings, or sequential items."
|
68
|
+
)
|
69
|
+
.with_syntax("1. Item text")
|
70
|
+
.with_examples(
|
71
|
+
[
|
72
|
+
"1. First step\n2. Second step\n3. Third step",
|
73
|
+
"1. Gather materials\n2. Assemble parts\n3. Test the result",
|
74
|
+
]
|
75
|
+
)
|
76
|
+
.build()
|
77
|
+
)
|