notionary 0.1.18__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.18.dist-info → notionary-0.1.20.dist-info}/METADATA +3 -8
- notionary-0.1.20.dist-info/RECORD +59 -0
- {notionary-0.1.18.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.18.dist-info/RECORD +0 -56
- {notionary-0.1.18.dist-info → notionary-0.1.20.dist-info}/licenses/LICENSE +0 -0
- {notionary-0.1.18.dist-info → notionary-0.1.20.dist-info}/top_level.txt +0 -0
notionary/__init__.py
CHANGED
@@ -7,8 +7,8 @@ from .database.database_discovery import DatabaseDiscovery
|
|
7
7
|
from .page.notion_page import NotionPage
|
8
8
|
from .page.notion_page_factory import NotionPageFactory
|
9
9
|
|
10
|
-
from .elements.block_element_registry import BlockElementRegistry
|
11
|
-
from .elements.block_element_registry_builder import (
|
10
|
+
from .elements.registry.block_element_registry import BlockElementRegistry
|
11
|
+
from .elements.registry.block_element_registry_builder import (
|
12
12
|
BlockElementRegistryBuilder,
|
13
13
|
)
|
14
14
|
|
@@ -230,7 +230,9 @@ class NotionDatabase(LoggingMixin):
|
|
230
230
|
if response and "last_edited_time" in response:
|
231
231
|
return response["last_edited_time"]
|
232
232
|
|
233
|
-
self.logger.warning(
|
233
|
+
self.logger.warning(
|
234
|
+
"Could not retrieve last_edited_time for database %s", self.database_id
|
235
|
+
)
|
234
236
|
return None
|
235
237
|
|
236
238
|
except Exception as e:
|
@@ -1,6 +1,7 @@
|
|
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 ElementPromptContent
|
4
5
|
|
5
6
|
|
6
7
|
class AudioElement(NotionBlockElement):
|
@@ -121,23 +122,18 @@ class AudioElement(NotionBlockElement):
|
|
121
122
|
return result
|
122
123
|
|
123
124
|
@classmethod
|
124
|
-
def get_llm_prompt_content(cls) ->
|
125
|
+
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
125
126
|
"""Returns information for LLM prompts about this element."""
|
126
127
|
return {
|
127
128
|
"description": "Embeds audio content from external sources like CDNs or direct audio URLs.",
|
128
|
-
"
|
129
|
-
"syntax": [
|
130
|
-
"$[](https://example.com/audio.mp3) - Audio without caption",
|
131
|
-
"$[Caption text](https://example.com/audio.mp3) - Audio with caption",
|
132
|
-
],
|
133
|
-
"supported_sources": [
|
134
|
-
"Direct links to audio files (.mp3, .wav, .ogg, etc.)",
|
135
|
-
"Google Cloud Storage links (storage.googleapis.com)",
|
136
|
-
"Other audio hosting platforms supported by Notion",
|
137
|
-
],
|
129
|
+
"syntax": "$[Caption](https://example.com/audio.mp3)",
|
138
130
|
"examples": [
|
139
131
|
"$[Podcast Episode](https://storage.googleapis.com/audio_summaries/ep_ai_summary_127d02ec-ca12-4312-a5ed-cb14b185480c.mp3)",
|
140
132
|
"$[Voice recording](https://example.com/audio/recording.mp3)",
|
141
133
|
"$[](https://storage.googleapis.com/audio_summaries/example.mp3)",
|
142
134
|
],
|
135
|
+
"when_to_use": (
|
136
|
+
"Use audio embeds when you want to include audio content directly in your document. "
|
137
|
+
"Audio embeds are useful for podcasts, music, voice recordings, or any content that benefits from audio explanation."
|
138
|
+
),
|
143
139
|
}
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional, List, Tuple
|
3
|
-
from typing_extensions import override
|
4
3
|
|
5
4
|
from notionary.elements.notion_block_element import NotionBlockElement
|
5
|
+
from notionary.elements.prompts.element_prompt_content import ElementPromptContent
|
6
6
|
|
7
7
|
|
8
8
|
class BookmarkElement(NotionBlockElement):
|
@@ -29,7 +29,6 @@ class BookmarkElement(NotionBlockElement):
|
|
29
29
|
+ r"\)$" # closing parenthesis
|
30
30
|
)
|
31
31
|
|
32
|
-
@override
|
33
32
|
@staticmethod
|
34
33
|
def match_markdown(text: str) -> bool:
|
35
34
|
"""Check if text is a markdown bookmark."""
|
@@ -37,14 +36,11 @@ class BookmarkElement(NotionBlockElement):
|
|
37
36
|
BookmarkElement.PATTERN.match(text.strip())
|
38
37
|
)
|
39
38
|
|
40
|
-
@override
|
41
39
|
@staticmethod
|
42
40
|
def match_notion(block: Dict[str, Any]) -> bool:
|
43
41
|
"""Check if block is a Notion bookmark."""
|
44
|
-
# Handle both standard "bookmark" type and "external-bookmark" type
|
45
42
|
return block.get("type") in ["bookmark", "external-bookmark"]
|
46
43
|
|
47
|
-
@override
|
48
44
|
@staticmethod
|
49
45
|
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
50
46
|
"""Convert markdown bookmark to Notion bookmark block."""
|
@@ -124,7 +120,6 @@ class BookmarkElement(NotionBlockElement):
|
|
124
120
|
|
125
121
|
return {"type": "bookmark", "bookmark": bookmark_data}
|
126
122
|
|
127
|
-
@override
|
128
123
|
@staticmethod
|
129
124
|
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
130
125
|
"""Convert Notion bookmark block to markdown bookmark."""
|
@@ -133,13 +128,10 @@ class BookmarkElement(NotionBlockElement):
|
|
133
128
|
if block_type == "bookmark":
|
134
129
|
bookmark_data = block.get("bookmark", {})
|
135
130
|
elif block_type == "external-bookmark":
|
136
|
-
# Handle external-bookmark type
|
137
|
-
# Extract URL from the external-bookmark structure
|
138
131
|
url = block.get("url", "")
|
139
132
|
if not url:
|
140
133
|
return None
|
141
134
|
|
142
|
-
# For external bookmarks, create a simple bookmark format
|
143
135
|
return f"[bookmark]({url})"
|
144
136
|
else:
|
145
137
|
return None
|
@@ -160,12 +152,12 @@ class BookmarkElement(NotionBlockElement):
|
|
160
152
|
|
161
153
|
if title and description:
|
162
154
|
return f'[bookmark]({url} "{title}" "{description}")'
|
163
|
-
|
155
|
+
|
156
|
+
if title:
|
164
157
|
return f'[bookmark]({url} "{title}")'
|
165
|
-
else:
|
166
|
-
return f"[bookmark]({url})"
|
167
158
|
|
168
|
-
|
159
|
+
return f"[bookmark]({url})"
|
160
|
+
|
169
161
|
@staticmethod
|
170
162
|
def is_multiline() -> bool:
|
171
163
|
"""Bookmarks are single-line elements."""
|
@@ -191,34 +183,30 @@ class BookmarkElement(NotionBlockElement):
|
|
191
183
|
if not caption:
|
192
184
|
return "", ""
|
193
185
|
|
194
|
-
# Extract the full text content from caption
|
195
186
|
full_text = BookmarkElement._extract_text_content(caption)
|
196
187
|
|
197
|
-
# Check if the text contains a separator
|
198
188
|
if " - " in full_text:
|
199
189
|
parts = full_text.split(" - ", 1)
|
200
190
|
return parts[0].strip(), parts[1].strip()
|
201
|
-
else:
|
202
|
-
# If no separator, assume the whole content is the title
|
203
|
-
return full_text.strip(), ""
|
204
191
|
|
205
|
-
|
192
|
+
return full_text.strip(), ""
|
193
|
+
|
206
194
|
@classmethod
|
207
|
-
def get_llm_prompt_content(cls) ->
|
195
|
+
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
208
196
|
"""
|
209
|
-
Returns
|
210
|
-
Includes description, usage guidance, syntax options, and examples.
|
197
|
+
Returns structured LLM prompt metadata for the bookmark element.
|
211
198
|
"""
|
212
199
|
return {
|
213
200
|
"description": "Creates a bookmark that links to an external website.",
|
214
|
-
"when_to_use":
|
215
|
-
|
216
|
-
"
|
217
|
-
|
218
|
-
|
219
|
-
],
|
201
|
+
"when_to_use": (
|
202
|
+
"Use bookmarks when you want to reference external content while keeping the page clean and organized. "
|
203
|
+
"Bookmarks display a preview card for the linked content."
|
204
|
+
),
|
205
|
+
"syntax": '[bookmark](https://example.com "Optional Title" "Optional Description")',
|
220
206
|
"examples": [
|
221
|
-
|
207
|
+
"[bookmark](https://example.com)",
|
208
|
+
'[bookmark](https://example.com "Example Title")',
|
209
|
+
'[bookmark](https://example.com "Example Title" "Example description of the site")',
|
222
210
|
'[bookmark](https://github.com "GitHub" "Where the world builds software")',
|
223
211
|
],
|
224
212
|
}
|
@@ -0,0 +1,69 @@
|
|
1
|
+
import re
|
2
|
+
from typing import Dict, Any, Optional
|
3
|
+
from typing_extensions import override
|
4
|
+
from notionary.elements.notion_block_element import NotionBlockElement
|
5
|
+
from notionary.elements.prompts.element_prompt_content import ElementPromptContent
|
6
|
+
from notionary.elements.text_inline_formatter import TextInlineFormatter
|
7
|
+
|
8
|
+
|
9
|
+
class BulletedListElement(NotionBlockElement):
|
10
|
+
"""Class for converting between Markdown bullet lists and Notion bulleted list items."""
|
11
|
+
|
12
|
+
@override
|
13
|
+
@staticmethod
|
14
|
+
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
15
|
+
"""Convert markdown bulleted list item to Notion block."""
|
16
|
+
pattern = re.compile(
|
17
|
+
r"^(\s*)[*\-+]\s+(?!\[[ x]\])(.+)$"
|
18
|
+
) # Avoid matching todo items
|
19
|
+
list_match = pattern.match(text)
|
20
|
+
if not list_match:
|
21
|
+
return None
|
22
|
+
|
23
|
+
content = list_match.group(2)
|
24
|
+
|
25
|
+
# Use parse_inline_formatting to handle rich text
|
26
|
+
rich_text = TextInlineFormatter.parse_inline_formatting(content)
|
27
|
+
|
28
|
+
return {
|
29
|
+
"type": "bulleted_list_item",
|
30
|
+
"bulleted_list_item": {"rich_text": rich_text, "color": "default"},
|
31
|
+
}
|
32
|
+
|
33
|
+
@staticmethod
|
34
|
+
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
35
|
+
"""Convert Notion bulleted list item block to markdown."""
|
36
|
+
if block.get("type") != "bulleted_list_item":
|
37
|
+
return None
|
38
|
+
|
39
|
+
rich_text = block.get("bulleted_list_item", {}).get("rich_text", [])
|
40
|
+
content = TextInlineFormatter.extract_text_with_formatting(rich_text)
|
41
|
+
|
42
|
+
return f"- {content}"
|
43
|
+
|
44
|
+
@staticmethod
|
45
|
+
def match_markdown(text: str) -> bool:
|
46
|
+
"""Check if this element can handle the given markdown text."""
|
47
|
+
pattern = re.compile(r"^(\s*)[*\-+]\s+(?!\[[ x]\])(.+)$")
|
48
|
+
return bool(pattern.match(text))
|
49
|
+
|
50
|
+
@staticmethod
|
51
|
+
def match_notion(block: Dict[str, Any]) -> bool:
|
52
|
+
"""Check if this element can handle the given Notion block."""
|
53
|
+
return block.get("type") == "bulleted_list_item"
|
54
|
+
|
55
|
+
@classmethod
|
56
|
+
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
57
|
+
"""
|
58
|
+
Returns structured LLM prompt metadata for the bulleted list element.
|
59
|
+
"""
|
60
|
+
return {
|
61
|
+
"description": "Creates bulleted list items for unordered lists.",
|
62
|
+
"when_to_use": "Use for lists where order doesn't matter, such as features, options, or items without hierarchy.",
|
63
|
+
"syntax": "- Item text",
|
64
|
+
"examples": [
|
65
|
+
"- First item\n- Second item\n- Third item",
|
66
|
+
"* Apple\n* Banana\n* Cherry",
|
67
|
+
"+ Task A\n+ Task B",
|
68
|
+
],
|
69
|
+
}
|
@@ -1,7 +1,8 @@
|
|
1
|
+
import re
|
1
2
|
from typing import Dict, Any, Optional
|
2
3
|
from typing_extensions import override
|
3
|
-
import re
|
4
4
|
|
5
|
+
from notionary.elements.prompts.element_prompt_content import ElementPromptContent
|
5
6
|
from notionary.elements.text_inline_formatter import TextInlineFormatter
|
6
7
|
from notionary.elements.notion_block_element import NotionBlockElement
|
7
8
|
|
@@ -12,53 +13,21 @@ class CalloutElement(NotionBlockElement):
|
|
12
13
|
|
13
14
|
Markdown callout syntax:
|
14
15
|
- !> [emoji] Text - Callout with custom emoji
|
15
|
-
- !>
|
16
|
-
- !> Text - Simple callout with default emoji and color
|
16
|
+
- !> Text - Simple callout with default emoji
|
17
17
|
|
18
18
|
Where:
|
19
|
-
- {color} can be one of Notion's color options (e.g., "blue_background")
|
20
19
|
- [emoji] is any emoji character
|
21
20
|
- Text is the callout content with optional inline formatting
|
22
21
|
"""
|
23
22
|
|
24
|
-
COLOR_PATTERN = r"(?:(?:{([a-z_]+)})?\s*)?"
|
25
23
|
EMOJI_PATTERN = r"(?:\[([^\]]+)\])?\s*"
|
26
24
|
TEXT_PATTERN = r"(.+)"
|
27
25
|
|
28
|
-
|
29
|
-
PATTERN = re.compile(
|
30
|
-
r"^!>\s+" # Callout prefix
|
31
|
-
+ COLOR_PATTERN
|
32
|
-
+ EMOJI_PATTERN
|
33
|
-
+ TEXT_PATTERN
|
34
|
-
+ r"$" # End of line
|
35
|
-
)
|
26
|
+
PATTERN = re.compile(r"^!>\s+" + EMOJI_PATTERN + TEXT_PATTERN + r"$")
|
36
27
|
|
37
28
|
DEFAULT_EMOJI = "💡"
|
38
29
|
DEFAULT_COLOR = "gray_background"
|
39
30
|
|
40
|
-
VALID_COLORS = [
|
41
|
-
"default",
|
42
|
-
"gray",
|
43
|
-
"brown",
|
44
|
-
"orange",
|
45
|
-
"yellow",
|
46
|
-
"green",
|
47
|
-
"blue",
|
48
|
-
"purple",
|
49
|
-
"pink",
|
50
|
-
"red",
|
51
|
-
"gray_background",
|
52
|
-
"brown_background",
|
53
|
-
"orange_background",
|
54
|
-
"yellow_background",
|
55
|
-
"green_background",
|
56
|
-
"blue_background",
|
57
|
-
"purple_background",
|
58
|
-
"pink_background",
|
59
|
-
"red_background",
|
60
|
-
]
|
61
|
-
|
62
31
|
@override
|
63
32
|
@staticmethod
|
64
33
|
def match_markdown(text: str) -> bool:
|
@@ -81,22 +50,18 @@ class CalloutElement(NotionBlockElement):
|
|
81
50
|
if not callout_match:
|
82
51
|
return None
|
83
52
|
|
84
|
-
|
85
|
-
|
86
|
-
content = callout_match.group(3)
|
53
|
+
emoji = callout_match.group(1)
|
54
|
+
content = callout_match.group(2)
|
87
55
|
|
88
56
|
if not emoji:
|
89
57
|
emoji = CalloutElement.DEFAULT_EMOJI
|
90
58
|
|
91
|
-
if not color or color not in CalloutElement.VALID_COLORS:
|
92
|
-
color = CalloutElement.DEFAULT_COLOR
|
93
|
-
|
94
59
|
return {
|
95
60
|
"type": "callout",
|
96
61
|
"callout": {
|
97
62
|
"rich_text": TextInlineFormatter.parse_inline_formatting(content),
|
98
63
|
"icon": {"type": "emoji", "emoji": emoji},
|
99
|
-
"color":
|
64
|
+
"color": CalloutElement.DEFAULT_COLOR,
|
100
65
|
},
|
101
66
|
}
|
102
67
|
|
@@ -110,7 +75,6 @@ class CalloutElement(NotionBlockElement):
|
|
110
75
|
callout_data = block.get("callout", {})
|
111
76
|
rich_text = callout_data.get("rich_text", [])
|
112
77
|
icon = callout_data.get("icon", {})
|
113
|
-
color = callout_data.get("color", CalloutElement.DEFAULT_COLOR)
|
114
78
|
|
115
79
|
text = TextInlineFormatter.extract_text_with_formatting(rich_text)
|
116
80
|
if not text:
|
@@ -120,15 +84,11 @@ class CalloutElement(NotionBlockElement):
|
|
120
84
|
if icon and icon.get("type") == "emoji":
|
121
85
|
emoji = icon.get("emoji", "")
|
122
86
|
|
123
|
-
color_str = ""
|
124
|
-
if color and color != CalloutElement.DEFAULT_COLOR:
|
125
|
-
color_str = f"{{{color}}} "
|
126
|
-
|
127
87
|
emoji_str = ""
|
128
|
-
if emoji:
|
88
|
+
if emoji and emoji != CalloutElement.DEFAULT_EMOJI:
|
129
89
|
emoji_str = f"[{emoji}] "
|
130
90
|
|
131
|
-
return f"!> {
|
91
|
+
return f"!> {emoji_str}{text}"
|
132
92
|
|
133
93
|
@override
|
134
94
|
@staticmethod
|
@@ -136,44 +96,22 @@ class CalloutElement(NotionBlockElement):
|
|
136
96
|
return False
|
137
97
|
|
138
98
|
@classmethod
|
139
|
-
def get_llm_prompt_content(cls) ->
|
99
|
+
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
140
100
|
"""
|
141
101
|
Returns a dictionary with all information needed for LLM prompts about this element.
|
142
102
|
Includes description, usage guidance, syntax options, and examples.
|
143
103
|
"""
|
144
104
|
return {
|
145
|
-
"description": "Creates a callout block to highlight important information with an icon
|
146
|
-
"when_to_use":
|
147
|
-
|
148
|
-
"
|
149
|
-
|
150
|
-
|
151
|
-
],
|
152
|
-
"color_options": [
|
153
|
-
"default",
|
154
|
-
"gray",
|
155
|
-
"brown",
|
156
|
-
"orange",
|
157
|
-
"yellow",
|
158
|
-
"green",
|
159
|
-
"blue",
|
160
|
-
"purple",
|
161
|
-
"pink",
|
162
|
-
"red",
|
163
|
-
"gray_background",
|
164
|
-
"brown_background",
|
165
|
-
"orange_background",
|
166
|
-
"yellow_background",
|
167
|
-
"green_background",
|
168
|
-
"blue_background",
|
169
|
-
"purple_background",
|
170
|
-
"pink_background",
|
171
|
-
"red_background",
|
172
|
-
],
|
105
|
+
"description": "Creates a callout block to highlight important information with an icon.",
|
106
|
+
"when_to_use": (
|
107
|
+
"Use callouts when you want to draw attention to important information, "
|
108
|
+
"tips, warnings, or notes that stand out from the main content."
|
109
|
+
),
|
110
|
+
"syntax": "!> [emoji] Text",
|
173
111
|
"examples": [
|
174
112
|
"!> This is a default callout with the light bulb emoji",
|
175
113
|
"!> [🔔] This is a callout with a bell emoji",
|
176
|
-
"!>
|
177
|
-
"!>
|
114
|
+
"!> [⚠️] Warning: This is an important note to pay attention to",
|
115
|
+
"!> [💡] Tip: Add emoji that matches your content's purpose",
|
178
116
|
],
|
179
117
|
}
|
@@ -1,7 +1,7 @@
|
|
1
|
-
from typing import Dict, Any, Optional, List, Tuple
|
2
|
-
from typing_extensions import override
|
3
1
|
import re
|
2
|
+
from typing import Dict, Any, Optional, List, Tuple
|
4
3
|
from notionary.elements.notion_block_element import NotionBlockElement
|
4
|
+
from notionary.elements.prompts.element_prompt_content import ElementPromptContent
|
5
5
|
|
6
6
|
|
7
7
|
class CodeBlockElement(NotionBlockElement):
|
@@ -20,19 +20,16 @@ class CodeBlockElement(NotionBlockElement):
|
|
20
20
|
|
21
21
|
PATTERN = re.compile(r"```(\w*)\n([\s\S]+?)```", re.MULTILINE)
|
22
22
|
|
23
|
-
@override
|
24
23
|
@staticmethod
|
25
24
|
def match_markdown(text: str) -> bool:
|
26
25
|
"""Check if text contains a markdown code block."""
|
27
26
|
return bool(CodeBlockElement.PATTERN.search(text))
|
28
27
|
|
29
|
-
@override
|
30
28
|
@staticmethod
|
31
29
|
def match_notion(block: Dict[str, Any]) -> bool:
|
32
30
|
"""Check if block is a Notion code block."""
|
33
31
|
return block.get("type") == "code"
|
34
32
|
|
35
|
-
@override
|
36
33
|
@staticmethod
|
37
34
|
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
38
35
|
"""Convert markdown code block to Notion code block."""
|
@@ -68,7 +65,6 @@ class CodeBlockElement(NotionBlockElement):
|
|
68
65
|
},
|
69
66
|
}
|
70
67
|
|
71
|
-
@override
|
72
68
|
@staticmethod
|
73
69
|
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
74
70
|
"""Convert Notion code block to markdown code block."""
|
@@ -134,20 +130,29 @@ class CodeBlockElement(NotionBlockElement):
|
|
134
130
|
|
135
131
|
return matches
|
136
132
|
|
137
|
-
@override
|
138
133
|
@staticmethod
|
139
134
|
def is_multiline() -> bool:
|
140
135
|
return True
|
141
136
|
|
142
137
|
@classmethod
|
143
|
-
def get_llm_prompt_content(cls) ->
|
138
|
+
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
144
139
|
"""
|
145
|
-
Returns
|
140
|
+
Returns structured LLM prompt metadata for the code block element.
|
146
141
|
"""
|
147
142
|
return {
|
148
|
-
"description":
|
143
|
+
"description": (
|
144
|
+
"Use fenced code blocks to format content as code. Supports language annotations like "
|
145
|
+
"'python', 'json', or 'mermaid'. Useful for displaying code, configurations, command-line "
|
146
|
+
"examples, or diagram syntax. Also suitable for explaining or visualizing systems with diagram languages."
|
147
|
+
),
|
148
|
+
"when_to_use": (
|
149
|
+
"Use code blocks when you want to present technical content like code snippets, terminal commands, "
|
150
|
+
"JSON structures, or system diagrams. Especially helpful when structure and formatting are essential."
|
151
|
+
),
|
152
|
+
"syntax": "```language\ncode content\n```",
|
149
153
|
"examples": [
|
150
154
|
"```python\nprint('Hello, world!')\n```",
|
155
|
+
'```json\n{"name": "Alice", "age": 30}\n```',
|
151
156
|
"```mermaid\nflowchart TD\n A --> B\n```",
|
152
157
|
],
|
153
158
|
}
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional, List, Tuple, Callable
|
3
|
-
from typing_extensions import override
|
4
3
|
|
5
4
|
from notionary.elements.notion_block_element import NotionBlockElement
|
5
|
+
from notionary.elements.prompts.element_prompt_content import ElementPromptContent
|
6
6
|
|
7
7
|
|
8
8
|
class ColumnElement(NotionBlockElement):
|
@@ -40,19 +40,16 @@ class ColumnElement(NotionBlockElement):
|
|
40
40
|
"""
|
41
41
|
cls._converter_callback = callback
|
42
42
|
|
43
|
-
@override
|
44
43
|
@staticmethod
|
45
44
|
def match_markdown(text: str) -> bool:
|
46
45
|
"""Check if text starts a columns block."""
|
47
46
|
return bool(ColumnElement.COLUMNS_START.match(text.strip()))
|
48
47
|
|
49
|
-
@override
|
50
48
|
@staticmethod
|
51
49
|
def match_notion(block: Dict[str, Any]) -> bool:
|
52
50
|
"""Check if block is a Notion column_list."""
|
53
51
|
return block.get("type") == "column_list"
|
54
52
|
|
55
|
-
@override
|
56
53
|
@staticmethod
|
57
54
|
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
58
55
|
"""
|
@@ -68,7 +65,6 @@ class ColumnElement(NotionBlockElement):
|
|
68
65
|
# Child columns will be added by the column processor
|
69
66
|
return {"type": "column_list", "column_list": {"children": []}}
|
70
67
|
|
71
|
-
@override
|
72
68
|
@staticmethod
|
73
69
|
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
74
70
|
"""Convert Notion column_list block to markdown column syntax."""
|
@@ -95,7 +91,6 @@ class ColumnElement(NotionBlockElement):
|
|
95
91
|
|
96
92
|
return "\n".join(result)
|
97
93
|
|
98
|
-
@override
|
99
94
|
@staticmethod
|
100
95
|
def is_multiline() -> bool:
|
101
96
|
"""Column blocks span multiple lines."""
|
@@ -264,31 +259,49 @@ class ColumnElement(NotionBlockElement):
|
|
264
259
|
columns_children.append(column_block)
|
265
260
|
|
266
261
|
@classmethod
|
267
|
-
def get_llm_prompt_content(cls) ->
|
262
|
+
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
268
263
|
"""
|
269
|
-
Returns
|
270
|
-
Includes description, usage guidance, syntax options, and examples.
|
264
|
+
Returns structured LLM prompt metadata for the column layout element.
|
271
265
|
"""
|
272
266
|
return {
|
273
267
|
"description": "Creates a multi-column layout that displays content side by side.",
|
274
|
-
"when_to_use":
|
275
|
-
|
276
|
-
"
|
277
|
-
"
|
278
|
-
|
279
|
-
|
280
|
-
":::
|
281
|
-
"
|
268
|
+
"when_to_use": (
|
269
|
+
"Use columns sparingly, only for direct comparisons or when parallel presentation significantly improves readability. "
|
270
|
+
"Best for pros/cons lists, feature comparisons, or pairing images with descriptions. "
|
271
|
+
"Avoid overusing as it can complicate document structure."
|
272
|
+
),
|
273
|
+
"syntax": (
|
274
|
+
"::: columns\n"
|
275
|
+
"::: column\n"
|
276
|
+
"Content for first column\n"
|
277
|
+
":::\n"
|
278
|
+
"::: column\n"
|
279
|
+
"Content for second column\n"
|
280
|
+
":::\n"
|
281
|
+
":::"
|
282
|
+
),
|
283
|
+
"examples": [
|
284
|
+
"::: columns\n"
|
285
|
+
"::: column\n"
|
286
|
+
"## Features\n"
|
287
|
+
"- Fast response time\n"
|
288
|
+
"- Intuitive interface\n"
|
289
|
+
"- Regular updates\n"
|
290
|
+
":::\n"
|
291
|
+
"::: column\n"
|
292
|
+
"## Benefits\n"
|
293
|
+
"- Increased productivity\n"
|
294
|
+
"- Better collaboration\n"
|
295
|
+
"- Simplified workflows\n"
|
296
|
+
":::\n"
|
282
297
|
":::",
|
298
|
+
"::: columns\n"
|
299
|
+
"::: column\n"
|
300
|
+
"\n"
|
301
|
+
":::\n"
|
302
|
+
"::: column\n"
|
303
|
+
"This text appears next to the image, creating a media-with-caption style layout that's perfect for documentation or articles.\n"
|
304
|
+
":::\n"
|
283
305
|
":::",
|
284
306
|
],
|
285
|
-
"notes": [
|
286
|
-
"Any Notion block can be placed within columns",
|
287
|
-
"Add more columns with additional '::: column' sections",
|
288
|
-
"Each column must close with ':::' and the entire columns section with another ':::'",
|
289
|
-
],
|
290
|
-
"examples": [
|
291
|
-
"::: columns\n::: column\n## Features\n- Fast response time\n- Intuitive interface\n- Regular updates\n:::\n::: column\n## Benefits\n- Increased productivity\n- Better collaboration\n- Simplified workflows\n:::\n:::",
|
292
|
-
"::: columns\n::: column\n\n:::\n::: column\nThis text appears next to the image, creating a media-with-caption style layout that's perfect for documentation or articles.\n:::\n:::",
|
293
|
-
],
|
294
307
|
}
|