notionary 0.2.19__py3-none-any.whl → 0.2.22__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 (220) hide show
  1. notionary/__init__.py +8 -4
  2. notionary/base_notion_client.py +3 -1
  3. notionary/blocks/__init__.py +2 -91
  4. notionary/blocks/_bootstrap.py +271 -0
  5. notionary/blocks/audio/__init__.py +8 -2
  6. notionary/blocks/audio/audio_element.py +69 -106
  7. notionary/blocks/audio/audio_markdown_node.py +13 -5
  8. notionary/blocks/audio/audio_models.py +6 -55
  9. notionary/blocks/base_block_element.py +42 -0
  10. notionary/blocks/bookmark/__init__.py +9 -2
  11. notionary/blocks/bookmark/bookmark_element.py +49 -139
  12. notionary/blocks/bookmark/bookmark_markdown_node.py +19 -18
  13. notionary/blocks/bookmark/bookmark_models.py +15 -0
  14. notionary/blocks/breadcrumbs/__init__.py +17 -0
  15. notionary/blocks/breadcrumbs/breadcrumb_element.py +39 -0
  16. notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +32 -0
  17. notionary/blocks/breadcrumbs/breadcrumb_models.py +12 -0
  18. notionary/blocks/bulleted_list/__init__.py +12 -2
  19. notionary/blocks/bulleted_list/bulleted_list_element.py +55 -53
  20. notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +2 -1
  21. notionary/blocks/bulleted_list/bulleted_list_models.py +18 -0
  22. notionary/blocks/callout/__init__.py +9 -2
  23. notionary/blocks/callout/callout_element.py +53 -86
  24. notionary/blocks/callout/callout_markdown_node.py +3 -1
  25. notionary/blocks/callout/callout_models.py +33 -0
  26. notionary/blocks/child_database/__init__.py +14 -0
  27. notionary/blocks/child_database/child_database_element.py +61 -0
  28. notionary/blocks/child_database/child_database_models.py +12 -0
  29. notionary/blocks/child_page/__init__.py +9 -0
  30. notionary/blocks/child_page/child_page_element.py +94 -0
  31. notionary/blocks/child_page/child_page_models.py +12 -0
  32. notionary/blocks/{shared/block_client.py → client.py} +54 -54
  33. notionary/blocks/code/__init__.py +6 -2
  34. notionary/blocks/code/code_element.py +96 -181
  35. notionary/blocks/code/code_markdown_node.py +64 -13
  36. notionary/blocks/code/code_models.py +94 -0
  37. notionary/blocks/column/__init__.py +25 -1
  38. notionary/blocks/column/column_element.py +44 -312
  39. notionary/blocks/column/column_list_element.py +52 -0
  40. notionary/blocks/column/column_list_markdown_node.py +50 -0
  41. notionary/blocks/column/column_markdown_node.py +59 -0
  42. notionary/blocks/column/column_models.py +26 -0
  43. notionary/blocks/divider/__init__.py +9 -2
  44. notionary/blocks/divider/divider_element.py +18 -49
  45. notionary/blocks/divider/divider_markdown_node.py +2 -1
  46. notionary/blocks/divider/divider_models.py +12 -0
  47. notionary/blocks/embed/__init__.py +9 -2
  48. notionary/blocks/embed/embed_element.py +65 -111
  49. notionary/blocks/embed/embed_markdown_node.py +3 -1
  50. notionary/blocks/embed/embed_models.py +14 -0
  51. notionary/blocks/equation/__init__.py +14 -0
  52. notionary/blocks/equation/equation_element.py +133 -0
  53. notionary/blocks/equation/equation_element_markdown_node.py +35 -0
  54. notionary/blocks/equation/equation_models.py +11 -0
  55. notionary/blocks/file/__init__.py +25 -0
  56. notionary/blocks/file/file_element.py +112 -0
  57. notionary/blocks/file/file_element_markdown_node.py +37 -0
  58. notionary/blocks/file/file_element_models.py +39 -0
  59. notionary/blocks/guards.py +22 -0
  60. notionary/blocks/heading/__init__.py +16 -2
  61. notionary/blocks/heading/heading_element.py +83 -69
  62. notionary/blocks/heading/heading_markdown_node.py +2 -1
  63. notionary/blocks/heading/heading_models.py +29 -0
  64. notionary/blocks/image_block/__init__.py +13 -0
  65. notionary/blocks/image_block/image_element.py +89 -0
  66. notionary/blocks/{image → image_block}/image_markdown_node.py +13 -6
  67. notionary/blocks/image_block/image_models.py +10 -0
  68. notionary/blocks/mixins/captions/__init__.py +4 -0
  69. notionary/blocks/mixins/captions/caption_markdown_node_mixin.py +31 -0
  70. notionary/blocks/mixins/captions/caption_mixin.py +92 -0
  71. notionary/blocks/models.py +174 -0
  72. notionary/blocks/numbered_list/__init__.py +12 -2
  73. notionary/blocks/numbered_list/numbered_list_element.py +48 -56
  74. notionary/blocks/numbered_list/numbered_list_markdown_node.py +3 -1
  75. notionary/blocks/numbered_list/numbered_list_models.py +17 -0
  76. notionary/blocks/paragraph/__init__.py +12 -2
  77. notionary/blocks/paragraph/paragraph_element.py +40 -66
  78. notionary/blocks/paragraph/paragraph_markdown_node.py +2 -1
  79. notionary/blocks/paragraph/paragraph_models.py +16 -0
  80. notionary/blocks/pdf/__init__.py +13 -0
  81. notionary/blocks/pdf/pdf_element.py +97 -0
  82. notionary/blocks/pdf/pdf_markdown_node.py +37 -0
  83. notionary/blocks/pdf/pdf_models.py +11 -0
  84. notionary/blocks/quote/__init__.py +11 -2
  85. notionary/blocks/quote/quote_element.py +45 -62
  86. notionary/blocks/quote/quote_markdown_node.py +6 -3
  87. notionary/blocks/quote/quote_models.py +18 -0
  88. notionary/blocks/registry/__init__.py +4 -0
  89. notionary/blocks/registry/block_registry.py +60 -121
  90. notionary/blocks/registry/block_registry_builder.py +115 -59
  91. notionary/blocks/rich_text/__init__.py +33 -0
  92. notionary/blocks/rich_text/name_to_id_resolver.py +205 -0
  93. notionary/blocks/rich_text/rich_text_models.py +221 -0
  94. notionary/blocks/rich_text/text_inline_formatter.py +456 -0
  95. notionary/blocks/syntax_prompt_builder.py +137 -0
  96. notionary/blocks/table/__init__.py +16 -2
  97. notionary/blocks/table/table_element.py +136 -228
  98. notionary/blocks/table/table_markdown_node.py +2 -1
  99. notionary/blocks/table/table_models.py +28 -0
  100. notionary/blocks/table_of_contents/__init__.py +19 -0
  101. notionary/blocks/table_of_contents/table_of_contents_element.py +68 -0
  102. notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +35 -0
  103. notionary/blocks/table_of_contents/table_of_contents_models.py +18 -0
  104. notionary/blocks/todo/__init__.py +9 -2
  105. notionary/blocks/todo/todo_element.py +52 -92
  106. notionary/blocks/todo/todo_markdown_node.py +2 -1
  107. notionary/blocks/todo/todo_models.py +19 -0
  108. notionary/blocks/toggle/__init__.py +13 -3
  109. notionary/blocks/toggle/toggle_element.py +69 -260
  110. notionary/blocks/toggle/toggle_markdown_node.py +25 -15
  111. notionary/blocks/toggle/toggle_models.py +17 -0
  112. notionary/blocks/toggleable_heading/__init__.py +6 -2
  113. notionary/blocks/toggleable_heading/toggleable_heading_element.py +86 -241
  114. notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +26 -18
  115. notionary/blocks/types.py +130 -0
  116. notionary/blocks/video/__init__.py +8 -2
  117. notionary/blocks/video/video_element.py +70 -141
  118. notionary/blocks/video/video_element_models.py +10 -0
  119. notionary/blocks/video/video_markdown_node.py +13 -6
  120. notionary/database/client.py +26 -8
  121. notionary/database/database.py +13 -14
  122. notionary/database/database_filter_builder.py +2 -2
  123. notionary/database/database_provider.py +5 -4
  124. notionary/database/models.py +337 -0
  125. notionary/database/notion_database.py +6 -7
  126. notionary/file_upload/client.py +5 -7
  127. notionary/file_upload/models.py +3 -2
  128. notionary/file_upload/notion_file_upload.py +2 -3
  129. notionary/markdown/markdown_builder.py +729 -0
  130. notionary/markdown/markdown_document_model.py +228 -0
  131. notionary/{blocks → markdown}/markdown_node.py +1 -0
  132. notionary/models/notion_database_response.py +0 -338
  133. notionary/page/client.py +34 -15
  134. notionary/page/models.py +327 -0
  135. notionary/page/notion_page.py +136 -58
  136. notionary/page/{content/page_content_writer.py → page_content_deleting_service.py} +25 -59
  137. notionary/page/page_content_writer.py +177 -0
  138. notionary/page/page_context.py +65 -0
  139. notionary/page/reader/handler/__init__.py +19 -0
  140. notionary/page/reader/handler/base_block_renderer.py +44 -0
  141. notionary/page/reader/handler/block_processing_context.py +35 -0
  142. notionary/page/reader/handler/block_rendering_context.py +48 -0
  143. notionary/page/reader/handler/column_list_renderer.py +51 -0
  144. notionary/page/reader/handler/column_renderer.py +60 -0
  145. notionary/page/reader/handler/line_renderer.py +73 -0
  146. notionary/page/reader/handler/numbered_list_renderer.py +85 -0
  147. notionary/page/reader/handler/toggle_renderer.py +69 -0
  148. notionary/page/reader/handler/toggleable_heading_renderer.py +89 -0
  149. notionary/page/reader/page_content_retriever.py +81 -0
  150. notionary/page/search_filter_builder.py +2 -1
  151. notionary/page/writer/handler/__init__.py +24 -0
  152. notionary/page/writer/handler/code_handler.py +72 -0
  153. notionary/page/writer/handler/column_handler.py +141 -0
  154. notionary/page/writer/handler/column_list_handler.py +139 -0
  155. notionary/page/writer/handler/equation_handler.py +74 -0
  156. notionary/page/writer/handler/line_handler.py +35 -0
  157. notionary/page/writer/handler/line_processing_context.py +54 -0
  158. notionary/page/writer/handler/regular_line_handler.py +86 -0
  159. notionary/page/writer/handler/table_handler.py +66 -0
  160. notionary/page/writer/handler/toggle_handler.py +155 -0
  161. notionary/page/writer/handler/toggleable_heading_handler.py +173 -0
  162. notionary/page/writer/markdown_to_notion_converter.py +95 -0
  163. notionary/page/writer/markdown_to_notion_converter_context.py +30 -0
  164. notionary/page/writer/markdown_to_notion_formatting_post_processor.py +73 -0
  165. notionary/page/writer/notion_text_length_processor.py +150 -0
  166. notionary/telemetry/__init__.py +2 -2
  167. notionary/telemetry/service.py +3 -3
  168. notionary/user/__init__.py +2 -2
  169. notionary/user/base_notion_user.py +2 -1
  170. notionary/user/client.py +2 -3
  171. notionary/user/models.py +1 -0
  172. notionary/user/notion_bot_user.py +4 -5
  173. notionary/user/notion_user.py +3 -4
  174. notionary/user/notion_user_manager.py +23 -95
  175. notionary/util/__init__.py +3 -2
  176. notionary/util/fuzzy.py +2 -1
  177. notionary/util/logging_mixin.py +2 -2
  178. notionary/util/singleton_metaclass.py +1 -1
  179. notionary/workspace.py +6 -5
  180. notionary-0.2.22.dist-info/METADATA +237 -0
  181. notionary-0.2.22.dist-info/RECORD +200 -0
  182. notionary/blocks/document/__init__.py +0 -7
  183. notionary/blocks/document/document_element.py +0 -102
  184. notionary/blocks/document/document_markdown_node.py +0 -31
  185. notionary/blocks/image/__init__.py +0 -7
  186. notionary/blocks/image/image_element.py +0 -151
  187. notionary/blocks/markdown_builder.py +0 -356
  188. notionary/blocks/mention/__init__.py +0 -7
  189. notionary/blocks/mention/mention_element.py +0 -229
  190. notionary/blocks/mention/mention_markdown_node.py +0 -38
  191. notionary/blocks/prompts/element_prompt_builder.py +0 -83
  192. notionary/blocks/prompts/element_prompt_content.py +0 -41
  193. notionary/blocks/shared/models.py +0 -713
  194. notionary/blocks/shared/notion_block_element.py +0 -37
  195. notionary/blocks/shared/text_inline_formatter.py +0 -262
  196. notionary/blocks/shared/text_inline_formatter_new.py +0 -139
  197. notionary/database/models/page_result.py +0 -10
  198. notionary/models/notion_block_response.py +0 -264
  199. notionary/models/notion_page_response.py +0 -78
  200. notionary/models/search_response.py +0 -0
  201. notionary/page/__init__.py +0 -0
  202. notionary/page/content/markdown_whitespace_processor.py +0 -80
  203. notionary/page/content/notion_text_length_utils.py +0 -87
  204. notionary/page/content/page_content_retriever.py +0 -60
  205. notionary/page/formatting/line_processor.py +0 -153
  206. notionary/page/formatting/markdown_to_notion_converter.py +0 -153
  207. notionary/page/markdown_syntax_prompt_generator.py +0 -114
  208. notionary/page/notion_to_markdown_converter.py +0 -179
  209. notionary/page/properites/property_value_extractor.py +0 -0
  210. notionary/user/notion_user_provider.py +0 -1
  211. notionary-0.2.19.dist-info/METADATA +0 -225
  212. notionary-0.2.19.dist-info/RECORD +0 -150
  213. /notionary/{blocks/document/document_models.py → markdown/___init__.py} +0 -0
  214. /notionary/{blocks/image/image_models.py → markdown/makdown_document_model.py} +0 -0
  215. /notionary/{blocks/mention/mention_models.py → page/reader/handler/equation_renderer.py} +0 -0
  216. /notionary/{blocks/shared/__init__.py → page/writer/markdown_to_notion_post_processor.py} +0 -0
  217. /notionary/{blocks/toggleable_heading/toggleable_heading_models.py → page/writer/markdown_to_notion_text_length_post_processor.py} +0 -0
  218. /notionary/{elements/__init__.py → util/concurrency_limiter.py} +0 -0
  219. {notionary-0.2.19.dist-info → notionary-0.2.22.dist-info}/LICENSE +0 -0
  220. {notionary-0.2.19.dist-info → notionary-0.2.22.dist-info}/WHEEL +0 -0
@@ -1,356 +0,0 @@
1
- """
2
- Clean Fluent Markdown Builder
3
- ============================
4
-
5
- A direct, chainable builder for all MarkdownNode types without overengineering.
6
- Maps 1:1 to the available blocks with clear, expressive method names.
7
- """
8
-
9
- from __future__ import annotations
10
- from typing import Optional, Self, Union
11
-
12
- from notionary.blocks import (
13
- HeadingMarkdownNode,
14
- ImageMarkdownNode,
15
- ParagraphMarkdownNode,
16
- AudioMarkdownNode,
17
- BookmarkMarkdownNode,
18
- CalloutMarkdownNode,
19
- CodeMarkdownNode,
20
- DividerMarkdownNode,
21
- DocumentMarkdownNode,
22
- EmbedMarkdownNode,
23
- MentionMarkdownNode,
24
- NumberedListMarkdownNode,
25
- BulletedListMarkdownNode,
26
- QuoteMarkdownNode,
27
- TableMarkdownNode,
28
- TodoMarkdownNode,
29
- ToggleMarkdownNode,
30
- ToggleableHeadingMarkdownNode,
31
- VideoMarkdownNode,
32
- MarkdownNode,
33
- )
34
-
35
-
36
- class MarkdownBuilder:
37
- """
38
- Fluent interface builder for creating Notion content with clean, direct methods.
39
- """
40
-
41
- def __init__(self) -> None:
42
- self.children: list[MarkdownNode] = []
43
-
44
- def h1(self, text: str) -> Self:
45
- """
46
- Add an H1 heading.
47
-
48
- Args:
49
- text: The heading text content
50
- """
51
- self.children.append(HeadingMarkdownNode(text=text, level=1))
52
- return self
53
-
54
- def h2(self, text: str) -> Self:
55
- """
56
- Add an H2 heading.
57
-
58
- Args:
59
- text: The heading text content
60
- """
61
- self.children.append(HeadingMarkdownNode(text=text, level=2))
62
- return self
63
-
64
- def h3(self, text: str) -> Self:
65
- """
66
- Add an H3 heading.
67
-
68
- Args:
69
- text: The heading text content
70
- """
71
- self.children.append(HeadingMarkdownNode(text=text, level=3))
72
- return self
73
-
74
- def heading(self, text: str, level: int = 2) -> Self:
75
- """
76
- Add a heading with specified level.
77
-
78
- Args:
79
- text: The heading text content
80
- level: Heading level (1-3), defaults to 2
81
- """
82
- self.children.append(HeadingMarkdownNode(text=text, level=level))
83
- return self
84
-
85
- def paragraph(self, text: str) -> Self:
86
- """
87
- Add a paragraph block.
88
-
89
- Args:
90
- text: The paragraph text content
91
- """
92
- self.children.append(ParagraphMarkdownNode(text=text))
93
- return self
94
-
95
- def text(self, content: str) -> Self:
96
- """
97
- Add a text paragraph (alias for paragraph).
98
-
99
- Args:
100
- content: The text content
101
- """
102
- return self.paragraph(content)
103
-
104
- def quote(self, text: str, author: Optional[str] = None) -> Self:
105
- """
106
- Add a blockquote.
107
-
108
- Args:
109
- text: Quote text content
110
- author: Optional quote author/attribution
111
- """
112
- self.children.append(QuoteMarkdownNode(text=text, author=author))
113
- return self
114
-
115
- def divider(self) -> Self:
116
- """Add a horizontal divider."""
117
- self.children.append(DividerMarkdownNode())
118
- return self
119
-
120
- def numbered_list(self, items: list[str]) -> Self:
121
- """
122
- Add a numbered list.
123
-
124
- Args:
125
- items: List of text items for the numbered list
126
- """
127
- self.children.append(NumberedListMarkdownNode(texts=items))
128
- return self
129
-
130
- def bulleted_list(self, items: list[str]) -> Self:
131
- """
132
- Add a bulleted list.
133
-
134
- Args:
135
- items: List of text items for the bulleted list
136
- """
137
- self.children.append(BulletedListMarkdownNode(texts=items))
138
- return self
139
-
140
- def todo(self, text: str, checked: bool = False) -> Self:
141
- """
142
- Add a single todo item.
143
-
144
- Args:
145
- text: The todo item text
146
- checked: Whether the todo item is completed, defaults to False
147
- """
148
- self.children.append(TodoMarkdownNode(text=text, checked=checked))
149
- return self
150
-
151
- def todo_list(
152
- self, items: list[str], completed: Optional[list[bool]] = None
153
- ) -> Self:
154
- """
155
- Add multiple todo items.
156
-
157
- Args:
158
- items: List of todo item texts
159
- completed: List of completion states for each item, defaults to all False
160
- """
161
- if completed is None:
162
- completed = [False] * len(items)
163
-
164
- for i, item in enumerate(items):
165
- is_done = completed[i] if i < len(completed) else False
166
- self.children.append(TodoMarkdownNode(text=item, checked=is_done))
167
- return self
168
-
169
- def callout(self, text: str, emoji: Optional[str] = None) -> Self:
170
- """
171
- Add a callout block.
172
-
173
- Args:
174
- text: The callout text content
175
- emoji: Optional emoji for the callout icon
176
- """
177
- self.children.append(CalloutMarkdownNode(text=text, emoji=emoji))
178
- return self
179
-
180
- def toggle(self, title: str, content: Optional[list[str]] = None) -> Self:
181
- """
182
- Add a toggle block.
183
-
184
- Args:
185
- title: The toggle title/header text
186
- content: Optional list of content items inside the toggle
187
- """
188
- self.children.append(ToggleMarkdownNode(title=title, content=content))
189
- return self
190
-
191
- def toggleable_heading(
192
- self, text: str, level: int = 2, content: Optional[list[str]] = None
193
- ) -> Self:
194
- """
195
- Add a toggleable heading.
196
-
197
- Args:
198
- text: The heading text content
199
- level: Heading level (1-3), defaults to 2
200
- content: Optional list of content items inside the toggleable heading
201
- """
202
- self.children.append(
203
- ToggleableHeadingMarkdownNode(text=text, level=level, content=content)
204
- )
205
- return self
206
-
207
- def image(
208
- self, url: str, caption: Optional[str] = None, alt: Optional[str] = None
209
- ) -> Self:
210
- """
211
- Add an image.
212
-
213
- Args:
214
- url: Image URL or file path
215
- caption: Optional image caption text
216
- alt: Optional alternative text for accessibility
217
- """
218
- self.children.append(ImageMarkdownNode(url=url, caption=caption, alt=alt))
219
- return self
220
-
221
- def video(self, url: str, caption: Optional[str] = None) -> Self:
222
- """
223
- Add a video.
224
-
225
- Args:
226
- url: Video URL or file path
227
- caption: Optional video caption text
228
- """
229
- self.children.append(VideoMarkdownNode(url=url, caption=caption))
230
- return self
231
-
232
- def audio(self, url: str, caption: Optional[str] = None) -> Self:
233
- """
234
- Add audio content.
235
-
236
- Args:
237
- url: Audio file URL or path
238
- caption: Optional audio caption text
239
- """
240
- self.children.append(AudioMarkdownNode(url=url, caption=caption))
241
- return self
242
-
243
- def document(self, url: str, caption: Optional[str] = None) -> Self:
244
- """
245
- Add a document file.
246
-
247
- Args:
248
- url: Document file URL or path
249
- caption: Optional document caption text
250
- """
251
- self.children.append(DocumentMarkdownNode(url=url, caption=caption))
252
- return self
253
-
254
- def bookmark(
255
- self, url: str, title: Optional[str] = None, description: Optional[str] = None
256
- ) -> Self:
257
- """
258
- Add a bookmark.
259
-
260
- Args:
261
- url: Bookmark URL
262
- title: Optional bookmark title
263
- description: Optional bookmark description text
264
- """
265
- self.children.append(
266
- BookmarkMarkdownNode(url=url, title=title, description=description)
267
- )
268
- return self
269
-
270
- def embed(self, url: str, caption: Optional[str] = None) -> Self:
271
- """
272
- Add an embed.
273
-
274
- Args:
275
- url: URL to embed (e.g., YouTube, Twitter, etc.)
276
- caption: Optional embed caption text
277
- """
278
- self.children.append(EmbedMarkdownNode(url=url, caption=caption))
279
- return self
280
-
281
- def code(
282
- self, code: str, language: Optional[str] = None, caption: Optional[str] = None
283
- ) -> Self:
284
- """
285
- Add a code block.
286
-
287
- Args:
288
- code: The source code content
289
- language: Optional programming language for syntax highlighting
290
- caption: Optional code block caption text
291
- """
292
- self.children.append(
293
- CodeMarkdownNode(code=code, language=language, caption=caption)
294
- )
295
- return self
296
-
297
- def table(self, headers: list[str], rows: list[list[str]]) -> Self:
298
- """
299
- Add a table.
300
-
301
- Args:
302
- headers: List of column header texts
303
- rows: List of rows, where each row is a list of cell texts
304
- """
305
- self.children.append(TableMarkdownNode(headers=headers, rows=rows))
306
- return self
307
-
308
- def mention_page(self, page_id: str) -> Self:
309
- """
310
- Add a page mention.
311
-
312
- Args:
313
- page_id: The ID of the page to mention
314
- """
315
- self.children.append(MentionMarkdownNode("page", page_id))
316
- return self
317
-
318
- def mention_database(self, database_id: str) -> Self:
319
- """
320
- Add a database mention.
321
-
322
- Args:
323
- database_id: The ID of the database to mention
324
- """
325
- self.children.append(MentionMarkdownNode("database", database_id))
326
- return self
327
-
328
- def mention_date(self, date: str) -> Self:
329
- """
330
- Add a date mention.
331
-
332
- Args:
333
- date: Date in YYYY-MM-DD format
334
- """
335
- self.children.append(MentionMarkdownNode("date", date))
336
- return self
337
-
338
- def add_custom(self, node: MarkdownNode) -> Self:
339
- """
340
- Add a custom MarkdownNode.
341
-
342
- Args:
343
- node: A custom MarkdownNode instance
344
- """
345
- self.children.append(node)
346
- return self
347
-
348
- def space(self) -> Self:
349
- """Add vertical spacing."""
350
- return self.paragraph("")
351
-
352
- def build(self) -> str:
353
- """Build and return the final markdown string."""
354
- return "\n\n".join(
355
- child.to_markdown() for child in self.children if child is not None
356
- )
@@ -1,7 +0,0 @@
1
- from .mention_element import MentionElement
2
- from .mention_markdown_node import MentionMarkdownNode
3
-
4
- __all__ = [
5
- "MentionElement",
6
- "MentionMarkdownNode",
7
- ]
@@ -1,229 +0,0 @@
1
- import re
2
- from typing import Dict, Any, Optional, List
3
-
4
- from notionary.blocks import NotionBlockElement
5
- from notionary.blocks import (
6
- ElementPromptContent,
7
- ElementPromptBuilder,
8
- NotionBlockResult,
9
- )
10
-
11
-
12
- class MentionElement(NotionBlockElement):
13
- """
14
- Handles conversion between Markdown mentions and Notion mention elements.
15
-
16
- Markdown mention syntax:
17
- - @[page-id] - Mention a page by its ID
18
- - @date[YYYY-MM-DD] - Mention a date
19
- - @db[database-id] - Mention a database by its ID
20
- """
21
-
22
- MENTION_TYPES = {
23
- "page": {
24
- "pattern": r"@\[([0-9a-f-]+)\]",
25
- "create_mention": lambda id_value: {
26
- "type": "mention",
27
- "mention": {"type": "page", "page": {"id": id_value}},
28
- },
29
- "get_plain_text": lambda mention: f"Page {mention['mention']['page']['id']}",
30
- "to_markdown": lambda mention: f"@[{mention['mention']['page']['id']}]",
31
- },
32
- "date": {
33
- "pattern": r"@date\[(\d{4}-\d{2}-\d{2})\]",
34
- "create_mention": lambda date_value: {
35
- "type": "mention",
36
- "mention": {"type": "date", "date": {"start": date_value, "end": None}},
37
- },
38
- "get_plain_text": lambda mention: mention["mention"]["date"]["start"],
39
- "to_markdown": lambda mention: f"@date[{mention['mention']['date']['start']}]",
40
- },
41
- "database": {
42
- "pattern": r"@db\[([0-9a-f-]+)\]",
43
- "create_mention": lambda db_id: {
44
- "type": "mention",
45
- "mention": {"type": "database", "database": {"id": db_id}},
46
- },
47
- "get_plain_text": lambda mention: f"Database {mention['mention']['database']['id']}",
48
- "to_markdown": lambda mention: f"@db[{mention['mention']['database']['id']}]",
49
- },
50
- }
51
-
52
- @classmethod
53
- def match_markdown(cls, text: str) -> bool:
54
- """Check if text contains a markdown mention."""
55
- for mention_type in MentionElement.MENTION_TYPES.values():
56
- if re.search(mention_type["pattern"], text):
57
- return True
58
- return False
59
-
60
- @classmethod
61
- def match_notion(cls, block: Dict[str, Any]) -> bool:
62
- """Check if block contains a mention."""
63
- supported_block_types = [
64
- "paragraph",
65
- "heading_1",
66
- "heading_2",
67
- "heading_3",
68
- "bulleted_list_item",
69
- "numbered_list_item",
70
- ]
71
-
72
- if block.get("type") not in supported_block_types:
73
- return False
74
-
75
- block_content = block.get(block.get("type"), {})
76
- rich_text = block_content.get("rich_text", [])
77
-
78
- return any(text_item.get("type") == "mention" for text_item in rich_text)
79
-
80
- @classmethod
81
- def markdown_to_notion(cls, text: str) -> NotionBlockResult:
82
- """Convert markdown text with mentions to a Notion paragraph block."""
83
- if not MentionElement.match_markdown(text):
84
- return None
85
-
86
- rich_text = MentionElement._process_markdown_with_mentions(text)
87
-
88
- return {
89
- "type": "paragraph",
90
- "paragraph": {"rich_text": rich_text, "color": "default"},
91
- }
92
-
93
- @classmethod
94
- def _process_markdown_with_mentions(cls, text: str) -> List[Dict[str, Any]]:
95
- """Convert markdown mentions to Notion rich_text format."""
96
- mentions = []
97
-
98
- for mention_type, config in MentionElement.MENTION_TYPES.items():
99
- for match in re.finditer(config["pattern"], text):
100
- mentions.append(
101
- {
102
- "start": match.start(),
103
- "end": match.end(),
104
- "type": mention_type,
105
- "value": match.group(1),
106
- "original": match.group(0),
107
- }
108
- )
109
-
110
- mentions.sort(key=lambda m: m["start"])
111
-
112
- # Build rich_text list
113
- rich_text = []
114
- position = 0
115
-
116
- for mention in mentions:
117
- if mention["start"] > position:
118
- rich_text.append(
119
- MentionElement._create_text_item(text[position : mention["start"]])
120
- )
121
-
122
- # Add the mention
123
- mention_obj = MentionElement.MENTION_TYPES[mention["type"]][
124
- "create_mention"
125
- ](mention["value"])
126
-
127
- # Add annotations and plain text
128
- mention_obj["annotations"] = MentionElement._default_annotations()
129
- mention_obj["plain_text"] = MentionElement.MENTION_TYPES[mention["type"]][
130
- "get_plain_text"
131
- ](mention_obj)
132
-
133
- rich_text.append(mention_obj)
134
- position = mention["end"]
135
-
136
- # Add remaining text if any
137
- if position < len(text):
138
- rich_text.append(MentionElement._create_text_item(text[position:]))
139
-
140
- return rich_text
141
-
142
- @classmethod
143
- def _create_text_item(cls, content: str) -> Dict[str, Any]:
144
- """Create a text item with default annotations."""
145
- text_item = {
146
- "type": "text",
147
- "text": {"content": content, "link": None},
148
- "annotations": MentionElement._default_annotations(),
149
- "plain_text": content,
150
- }
151
- return text_item
152
-
153
- @classmethod
154
- def _default_annotations(cls) -> Dict[str, Any]:
155
- """Return default annotations for rich text."""
156
- return {
157
- "bold": False,
158
- "italic": False,
159
- "strikethrough": False,
160
- "underline": False,
161
- "code": False,
162
- "color": "default",
163
- }
164
-
165
- @classmethod
166
- def notion_to_markdown(cls, block: Dict[str, Any]) -> Optional[str]:
167
- """Extract mentions from Notion block and convert to markdown format."""
168
- block_type = block.get("type")
169
- if not block_type or block_type not in block:
170
- return None
171
-
172
- block_content = block.get(block_type, {})
173
- rich_text = block_content.get("rich_text", [])
174
-
175
- processed_text = MentionElement._process_rich_text_with_mentions(rich_text)
176
-
177
- if processed_text:
178
- return processed_text
179
-
180
- return None
181
-
182
- @classmethod
183
- def _process_rich_text_with_mentions(cls, rich_text: List[Dict[str, Any]]) -> str:
184
- """Convert rich text with mentions to markdown string."""
185
- result = []
186
-
187
- for item in rich_text:
188
- if item.get("type") == "mention":
189
- mention = item.get("mention", {})
190
- mention_type = mention.get("type")
191
-
192
- if mention_type in MentionElement.MENTION_TYPES:
193
- result.append(
194
- MentionElement.MENTION_TYPES[mention_type]["to_markdown"](item)
195
- )
196
- else:
197
- result.append(item.get("plain_text", "@[unknown]"))
198
- else:
199
- result.append(item.get("plain_text", ""))
200
-
201
- return "".join(result)
202
-
203
- @classmethod
204
- def is_multiline(cls) -> bool:
205
- return False
206
-
207
- @classmethod
208
- def get_llm_prompt_content(cls) -> ElementPromptContent:
209
- """
210
- Returns structured LLM prompt metadata for the mention element.
211
- """
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,38 +0,0 @@
1
- from __future__ import annotations
2
- from typing import Literal
3
- from pydantic import BaseModel
4
- from notionary.blocks.markdown_node import MarkdownNode
5
-
6
-
7
- class MentionMarkdownBlockParams(BaseModel):
8
- mention_type: Literal["page", "database", "date"]
9
- value: str
10
-
11
-
12
- class MentionMarkdownNode(MarkdownNode):
13
- """
14
- Programmatic interface for creating Notion-style Markdown mentions.
15
- Supports: page, database, date.
16
- Examples: @[page-id], @db[database-id], @date[YYYY-MM-DD]
17
- """
18
-
19
- def __init__(self, mention_type: str, value: str):
20
- allowed = {"page", "database", "date"}
21
- if mention_type not in allowed:
22
- raise ValueError(f"mention_type must be one of {allowed}")
23
- self.mention_type = mention_type
24
- self.value = value
25
-
26
- @classmethod
27
- def from_params(cls, params: MentionMarkdownBlockParams) -> MentionMarkdownNode:
28
- return cls(mention_type=params.mention_type, value=params.value)
29
-
30
- def to_markdown(self) -> str:
31
- if self.mention_type == "page":
32
- return f"@[{self.value}]"
33
- elif self.mention_type == "database":
34
- return f"@db[{self.value}]"
35
- elif self.mention_type == "date":
36
- return f"@date[{self.value}]"
37
- else:
38
- return f"@[{self.value}]"