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.
Files changed (34) hide show
  1. notionary/elements/audio_element.py +41 -38
  2. notionary/elements/bookmark_element.py +36 -27
  3. notionary/elements/bulleted_list_element.py +28 -21
  4. notionary/elements/callout_element.py +39 -31
  5. notionary/elements/code_block_element.py +38 -26
  6. notionary/elements/divider_element.py +29 -18
  7. notionary/elements/embed_element.py +37 -28
  8. notionary/elements/heading_element.py +39 -24
  9. notionary/elements/image_element.py +33 -24
  10. notionary/elements/mention_element.py +40 -29
  11. notionary/elements/notion_block_element.py +13 -31
  12. notionary/elements/numbered_list_element.py +29 -20
  13. notionary/elements/paragraph_element.py +37 -31
  14. notionary/elements/prompts/element_prompt_content.py +91 -7
  15. notionary/elements/prompts/synthax_prompt_builder.py +63 -16
  16. notionary/elements/qoute_element.py +72 -74
  17. notionary/elements/registry/block_element_registry_builder.py +6 -9
  18. notionary/elements/table_element.py +49 -36
  19. notionary/elements/text_inline_formatter.py +23 -15
  20. notionary/elements/{todo_lists.py → todo_element.py} +34 -25
  21. notionary/elements/toggle_element.py +184 -108
  22. notionary/elements/toggleable_heading_element.py +269 -0
  23. notionary/elements/video_element.py +37 -28
  24. notionary/page/content/page_content_manager.py +3 -8
  25. notionary/page/markdown_to_notion_converter.py +269 -274
  26. notionary/page/notion_page.py +2 -4
  27. notionary/page/notion_to_markdown_converter.py +20 -95
  28. {notionary-0.1.25.dist-info → notionary-0.1.26.dist-info}/METADATA +1 -1
  29. notionary-0.1.26.dist-info/RECORD +58 -0
  30. {notionary-0.1.25.dist-info → notionary-0.1.26.dist-info}/WHEEL +1 -1
  31. notionary/elements/column_element.py +0 -307
  32. notionary-0.1.25.dist-info/RECORD +0 -58
  33. {notionary-0.1.25.dist-info → notionary-0.1.26.dist-info}/licenses/LICENSE +0 -0
  34. {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 ElementPromptContent
4
+ from notionary.elements.prompts.element_prompt_content import (
5
+ ElementPromptBuilder,
6
+ ElementPromptContent,
7
+ )
5
8
 
6
9
 
7
10
  class VideoElement(NotionBlockElement):
@@ -30,19 +33,19 @@ class VideoElement(NotionBlockElement):
30
33
  re.compile(r"(?:https?://)?(?:www\.)?youtu\.be/([a-zA-Z0-9_-]{11})"),
31
34
  ]
32
35
 
33
- @staticmethod
34
- def match_markdown(text: str) -> bool:
36
+ @classmethod
37
+ def match_markdown(cls, text: str) -> bool:
35
38
  """Check if text is a markdown video embed."""
36
39
  text = text.strip()
37
40
  return text.startswith("@[") and bool(VideoElement.PATTERN.match(text))
38
41
 
39
- @staticmethod
40
- def match_notion(block: Dict[str, Any]) -> bool:
42
+ @classmethod
43
+ def match_notion(cls, block: Dict[str, Any]) -> bool:
41
44
  """Check if block is a Notion video."""
42
45
  return block.get("type") == "video"
43
46
 
44
- @staticmethod
45
- def is_youtube_url(url: str) -> bool:
47
+ @classmethod
48
+ def is_youtube_url(cls, url: str) -> bool:
46
49
  """Check if URL is a YouTube video and return video ID if it is."""
47
50
  for pattern in VideoElement.YOUTUBE_PATTERNS:
48
51
  match = pattern.match(url)
@@ -50,8 +53,8 @@ class VideoElement(NotionBlockElement):
50
53
  return True
51
54
  return False
52
55
 
53
- @staticmethod
54
- def get_youtube_id(url: str) -> Optional[str]:
56
+ @classmethod
57
+ def get_youtube_id(cls, url: str) -> Optional[str]:
55
58
  """Extract YouTube video ID from URL."""
56
59
  for pattern in VideoElement.YOUTUBE_PATTERNS:
57
60
  match = pattern.match(url)
@@ -59,8 +62,8 @@ class VideoElement(NotionBlockElement):
59
62
  return match.group(1)
60
63
  return None
61
64
 
62
- @staticmethod
63
- def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
65
+ @classmethod
66
+ def markdown_to_notion(cls, text: str) -> Optional[Dict[str, Any]]:
64
67
  """Convert markdown video embed to Notion video block."""
65
68
  video_match = VideoElement.PATTERN.match(text.strip())
66
69
  if not video_match:
@@ -88,8 +91,8 @@ class VideoElement(NotionBlockElement):
88
91
 
89
92
  return video_block
90
93
 
91
- @staticmethod
92
- def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
94
+ @classmethod
95
+ def notion_to_markdown(cls, block: Dict[str, Any]) -> Optional[str]:
93
96
  """Convert Notion video block to markdown video embed."""
94
97
  if block.get("type") != "video":
95
98
  return None
@@ -114,13 +117,13 @@ class VideoElement(NotionBlockElement):
114
117
 
115
118
  return f"@[{caption}]({url})"
116
119
 
117
- @staticmethod
118
- def is_multiline() -> bool:
120
+ @classmethod
121
+ def is_multiline(cls) -> bool:
119
122
  """Videos are single-line elements."""
120
123
  return False
121
124
 
122
- @staticmethod
123
- def _extract_text_content(rich_text: List[Dict[str, Any]]) -> str:
125
+ @classmethod
126
+ def _extract_text_content(cls, rich_text: List[Dict[str, Any]]) -> str:
124
127
  """Extract plain text content from Notion rich_text elements."""
125
128
  result = ""
126
129
  for text_obj in rich_text:
@@ -135,16 +138,22 @@ class VideoElement(NotionBlockElement):
135
138
  """
136
139
  Returns structured LLM prompt metadata for the video element.
137
140
  """
138
- return {
139
- "description": "Embeds video content from external sources like YouTube or direct video URLs.",
140
- "when_to_use": (
141
+ return (
142
+ ElementPromptBuilder()
143
+ .with_description(
144
+ "Embeds video content from external sources like YouTube or direct video URLs."
145
+ )
146
+ .with_usage_guidelines(
141
147
  "Use video embeds when you want to include multimedia content directly in your document. "
142
148
  "Videos are useful for tutorials, demonstrations, presentations, or any content that benefits from visual explanation."
143
- ),
144
- "syntax": "@[Caption](https://example.com/video.mp4)",
145
- "examples": [
146
- "@[How to use this feature](https://www.youtube.com/watch?v=dQw4w9WgXcQ)",
147
- "@[Product demo](https://example.com/videos/demo.mp4)",
148
- "@[](https://youtu.be/dQw4w9WgXcQ)",
149
- ],
150
- }
149
+ )
150
+ .with_syntax("@[Caption](https://example.com/video.mp4)")
151
+ .with_examples(
152
+ [
153
+ "@[How to use this feature](https://www.youtube.com/watch?v=dQw4w9WgXcQ)",
154
+ "@[Product demo](https://example.com/videos/demo.mp4)",
155
+ "@[](https://youtu.be/dQw4w9WgXcQ)",
156
+ ]
157
+ )
158
+ .build()
159
+ )
@@ -1,3 +1,4 @@
1
+ import json
1
2
  from typing import Any, Dict, List, Optional
2
3
 
3
4
  from notionary.elements.registry.block_element_registry import BlockElementRegistry
@@ -32,9 +33,7 @@ class PageContentManager(LoggingMixin):
32
33
  )
33
34
  self._chunker = NotionPageContentChunker()
34
35
 
35
- async def append_markdown(
36
- self, markdown_text: str, append_divider: bool = False
37
- ) -> str:
36
+ async def append_markdown(self, markdown_text: str) -> str:
38
37
  """
39
38
  Append markdown text to a Notion page, automatically handling content length limits.
40
39
  First strips out triple backtick markdown fences if they wrap the entire content.
@@ -45,11 +44,6 @@ class PageContentManager(LoggingMixin):
45
44
  """
46
45
  try:
47
46
  blocks = self._markdown_to_notion_converter.convert(markdown_text)
48
-
49
- if append_divider:
50
- divider_block = {"type": "divider", "divider": {}}
51
- blocks.append(divider_block)
52
-
53
47
  fixed_blocks = self._chunker.fix_blocks_content_length(blocks)
54
48
 
55
49
  result = await self._client.patch(
@@ -162,6 +156,7 @@ class PageContentManager(LoggingMixin):
162
156
  if parent_id is None
163
157
  else await self.get_block_children(parent_id)
164
158
  )
159
+
165
160
  if not blocks:
166
161
  return []
167
162