notionary 0.2.21__tar.gz → 0.2.23__tar.gz

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 (229) hide show
  1. notionary-0.2.23/PKG-INFO +235 -0
  2. notionary-0.2.23/README.md +212 -0
  3. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/_bootstrap.py +9 -1
  4. notionary-0.2.23/notionary/blocks/audio/audio_element.py +115 -0
  5. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/audio/audio_markdown_node.py +10 -4
  6. notionary-0.2.23/notionary/blocks/base_block_element.py +42 -0
  7. notionary-0.2.23/notionary/blocks/bookmark/bookmark_element.py +83 -0
  8. notionary-0.2.23/notionary/blocks/bookmark/bookmark_markdown_node.py +44 -0
  9. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/breadcrumbs/breadcrumb_element.py +2 -2
  10. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/bulleted_list/bulleted_list_element.py +21 -4
  11. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/callout/callout_element.py +20 -4
  12. notionary-0.2.23/notionary/blocks/child_database/__init__.py +14 -0
  13. notionary-0.2.23/notionary/blocks/child_database/child_database_element.py +59 -0
  14. notionary-0.2.23/notionary/blocks/child_database/child_database_models.py +12 -0
  15. notionary-0.2.23/notionary/blocks/child_page/child_page_element.py +94 -0
  16. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/client.py +0 -1
  17. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/code/code_element.py +51 -2
  18. notionary-0.2.23/notionary/blocks/code/code_markdown_node.py +94 -0
  19. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/column/column_element.py +9 -3
  20. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/column/column_list_element.py +18 -3
  21. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/divider/divider_element.py +3 -11
  22. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/embed/embed_element.py +27 -6
  23. notionary-0.2.23/notionary/blocks/equation/equation_element.py +133 -0
  24. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/equation/equation_element_markdown_node.py +8 -9
  25. notionary-0.2.23/notionary/blocks/file/file_element.py +112 -0
  26. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/file/file_element_markdown_node.py +9 -7
  27. notionary-0.2.23/notionary/blocks/guards.py +22 -0
  28. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/heading/heading_element.py +23 -4
  29. notionary-0.2.23/notionary/blocks/image_block/image_element.py +89 -0
  30. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/image_block/image_markdown_node.py +10 -5
  31. notionary-0.2.23/notionary/blocks/mixins/captions/__init__.py +4 -0
  32. notionary-0.2.23/notionary/blocks/mixins/captions/caption_markdown_node_mixin.py +31 -0
  33. notionary-0.2.23/notionary/blocks/mixins/captions/caption_mixin.py +92 -0
  34. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/models.py +3 -1
  35. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/numbered_list/numbered_list_element.py +21 -4
  36. notionary-0.2.23/notionary/blocks/paragraph/paragraph_element.py +58 -0
  37. notionary-0.2.23/notionary/blocks/pdf/pdf_element.py +97 -0
  38. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/pdf/pdf_markdown_node.py +9 -7
  39. notionary-0.2.23/notionary/blocks/quote/quote_element.py +75 -0
  40. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/quote/quote_markdown_node.py +2 -2
  41. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/registry/block_registry.py +1 -46
  42. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/registry/block_registry_builder.py +8 -0
  43. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/rich_text/rich_text_models.py +62 -29
  44. notionary-0.2.23/notionary/blocks/rich_text/text_inline_formatter.py +456 -0
  45. notionary-0.2.23/notionary/blocks/syntax_prompt_builder.py +137 -0
  46. notionary-0.2.23/notionary/blocks/table/table_element.py +225 -0
  47. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/table_of_contents/table_of_contents_element.py +19 -2
  48. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/todo/todo_element.py +21 -4
  49. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/toggle/toggle_element.py +19 -3
  50. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/toggle/toggle_markdown_node.py +1 -1
  51. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/toggleable_heading/toggleable_heading_element.py +19 -4
  52. notionary-0.2.23/notionary/blocks/types.py +130 -0
  53. notionary-0.2.23/notionary/blocks/video/video_element.py +111 -0
  54. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/video/video_markdown_node.py +10 -5
  55. notionary-0.2.23/notionary/comments/__init__.py +26 -0
  56. notionary-0.2.23/notionary/comments/client.py +211 -0
  57. notionary-0.2.23/notionary/comments/models.py +129 -0
  58. {notionary-0.2.21 → notionary-0.2.23}/notionary/database/client.py +23 -0
  59. {notionary-0.2.21 → notionary-0.2.23}/notionary/file_upload/models.py +2 -2
  60. {notionary-0.2.21 → notionary-0.2.23}/notionary/markdown/markdown_builder.py +34 -27
  61. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/client.py +21 -6
  62. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/notion_page.py +77 -2
  63. notionary-0.2.21/notionary/page/page_content_writer.py → notionary-0.2.23/notionary/page/page_content_deleting_service.py +4 -88
  64. notionary-0.2.23/notionary/page/page_content_writer.py +177 -0
  65. notionary-0.2.23/notionary/page/page_context.py +64 -0
  66. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/__init__.py +2 -0
  67. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/base_block_renderer.py +4 -4
  68. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/block_rendering_context.py +5 -0
  69. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/line_renderer.py +16 -3
  70. notionary-0.2.23/notionary/page/reader/handler/numbered_list_renderer.py +85 -0
  71. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/page_content_retriever.py +17 -5
  72. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/handler/__init__.py +2 -0
  73. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/handler/code_handler.py +12 -40
  74. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/handler/column_handler.py +12 -12
  75. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/handler/column_list_handler.py +13 -13
  76. notionary-0.2.23/notionary/page/writer/handler/equation_handler.py +74 -0
  77. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/handler/line_handler.py +4 -4
  78. notionary-0.2.23/notionary/page/writer/handler/regular_line_handler.py +86 -0
  79. notionary-0.2.23/notionary/page/writer/handler/table_handler.py +66 -0
  80. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/handler/toggle_handler.py +14 -12
  81. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/handler/toggleable_heading_handler.py +22 -16
  82. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/markdown_to_notion_converter.py +28 -9
  83. notionary-0.2.23/notionary/page/writer/markdown_to_notion_converter_context.py +30 -0
  84. notionary-0.2.23/notionary/page/writer/markdown_to_notion_formatting_post_processor.py +73 -0
  85. notionary-0.2.23/notionary/page/writer/markdown_to_notion_text_length_post_processor.py +0 -0
  86. notionary-0.2.23/notionary/page/writer/notion_text_length_processor.py +150 -0
  87. notionary-0.2.23/notionary/shared/__init__.py +5 -0
  88. notionary-0.2.23/notionary/shared/name_to_id_resolver.py +203 -0
  89. {notionary-0.2.21 → notionary-0.2.23}/notionary/telemetry/service.py +0 -1
  90. notionary-0.2.23/notionary/user/notion_user_manager.py +101 -0
  91. notionary-0.2.23/notionary/util/concurrency_limiter.py +0 -0
  92. notionary-0.2.23/notionary/util/factory_decorator.py +0 -0
  93. {notionary-0.2.21 → notionary-0.2.23}/notionary/workspace.py +4 -4
  94. {notionary-0.2.21 → notionary-0.2.23}/pyproject.toml +2 -3
  95. notionary-0.2.21/PKG-INFO +0 -229
  96. notionary-0.2.21/README.md +0 -204
  97. notionary-0.2.21/notionary/blocks/audio/audio_element.py +0 -90
  98. notionary-0.2.21/notionary/blocks/base_block_element.py +0 -30
  99. notionary-0.2.21/notionary/blocks/bookmark/bookmark_element.py +0 -80
  100. notionary-0.2.21/notionary/blocks/bookmark/bookmark_markdown_node.py +0 -45
  101. notionary-0.2.21/notionary/blocks/child_database/__init__.py +0 -7
  102. notionary-0.2.21/notionary/blocks/child_database/child_database_models.py +0 -19
  103. notionary-0.2.21/notionary/blocks/code/code_markdown_node.py +0 -43
  104. notionary-0.2.21/notionary/blocks/equation/equation_element.py +0 -80
  105. notionary-0.2.21/notionary/blocks/file/file_element.py +0 -93
  106. notionary-0.2.21/notionary/blocks/image_block/image_element.py +0 -84
  107. notionary-0.2.21/notionary/blocks/paragraph/paragraph_element.py +0 -42
  108. notionary-0.2.21/notionary/blocks/pdf/pdf_element.py +0 -91
  109. notionary-0.2.21/notionary/blocks/quote/quote_element.py +0 -58
  110. notionary-0.2.21/notionary/blocks/rich_text/text_inline_formatter.py +0 -125
  111. notionary-0.2.21/notionary/blocks/table/table_element.py +0 -124
  112. notionary-0.2.21/notionary/blocks/types.py +0 -61
  113. notionary-0.2.21/notionary/blocks/video/video_element.py +0 -106
  114. notionary-0.2.21/notionary/page/markdown_whitespace_processor.py +0 -80
  115. notionary-0.2.21/notionary/page/notion_text_length_utils.py +0 -119
  116. notionary-0.2.21/notionary/page/writer/handler/regular_line_handler.py +0 -92
  117. notionary-0.2.21/notionary/page/writer/handler/table_handler.py +0 -130
  118. notionary-0.2.21/notionary/user/notion_user_manager.py +0 -174
  119. notionary-0.2.21/notionary/user/notion_user_provider.py +0 -1
  120. {notionary-0.2.21 → notionary-0.2.23}/LICENSE +0 -0
  121. {notionary-0.2.21 → notionary-0.2.23}/notionary/__init__.py +0 -0
  122. {notionary-0.2.21 → notionary-0.2.23}/notionary/base_notion_client.py +0 -0
  123. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/__init__.py +0 -0
  124. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/audio/__init__.py +0 -0
  125. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/audio/audio_models.py +0 -0
  126. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/bookmark/__init__.py +0 -0
  127. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/bookmark/bookmark_models.py +0 -0
  128. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/breadcrumbs/__init__.py +0 -0
  129. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +0 -0
  130. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/breadcrumbs/breadcrumb_models.py +0 -0
  131. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/bulleted_list/__init__.py +0 -0
  132. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +0 -0
  133. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/bulleted_list/bulleted_list_models.py +0 -0
  134. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/callout/__init__.py +0 -0
  135. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/callout/callout_markdown_node.py +0 -0
  136. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/callout/callout_models.py +0 -0
  137. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/child_page/__init__.py +0 -0
  138. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/child_page/child_page_models.py +0 -0
  139. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/code/__init__.py +0 -0
  140. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/code/code_models.py +0 -0
  141. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/column/__init__.py +0 -0
  142. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/column/column_list_markdown_node.py +0 -0
  143. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/column/column_markdown_node.py +0 -0
  144. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/column/column_models.py +0 -0
  145. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/divider/__init__.py +0 -0
  146. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/divider/divider_markdown_node.py +0 -0
  147. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/divider/divider_models.py +0 -0
  148. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/embed/__init__.py +0 -0
  149. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/embed/embed_markdown_node.py +0 -0
  150. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/embed/embed_models.py +0 -0
  151. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/equation/__init__.py +0 -0
  152. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/equation/equation_models.py +0 -0
  153. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/file/__init__.py +0 -0
  154. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/file/file_element_models.py +0 -0
  155. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/heading/__init__.py +0 -0
  156. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/heading/heading_markdown_node.py +0 -0
  157. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/heading/heading_models.py +0 -0
  158. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/image_block/__init__.py +0 -0
  159. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/image_block/image_models.py +0 -0
  160. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/numbered_list/__init__.py +0 -0
  161. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/numbered_list/numbered_list_markdown_node.py +0 -0
  162. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/numbered_list/numbered_list_models.py +0 -0
  163. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/paragraph/__init__.py +0 -0
  164. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/paragraph/paragraph_markdown_node.py +0 -0
  165. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/paragraph/paragraph_models.py +0 -0
  166. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/pdf/__init__.py +0 -0
  167. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/pdf/pdf_models.py +0 -0
  168. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/quote/__init__.py +0 -0
  169. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/quote/quote_models.py +0 -0
  170. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/registry/__init__.py +0 -0
  171. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/rich_text/__init__.py +0 -0
  172. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/table/__init__.py +0 -0
  173. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/table/table_markdown_node.py +0 -0
  174. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/table/table_models.py +0 -0
  175. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/table_of_contents/__init__.py +0 -0
  176. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +0 -0
  177. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/table_of_contents/table_of_contents_models.py +0 -0
  178. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/todo/__init__.py +0 -0
  179. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/todo/todo_markdown_node.py +0 -0
  180. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/todo/todo_models.py +0 -0
  181. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/toggle/__init__.py +0 -0
  182. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/toggle/toggle_models.py +0 -0
  183. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/toggleable_heading/__init__.py +0 -0
  184. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +0 -0
  185. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/video/__init__.py +0 -0
  186. {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/video/video_element_models.py +0 -0
  187. {notionary-0.2.21 → notionary-0.2.23}/notionary/database/__init__.py +0 -0
  188. {notionary-0.2.21 → notionary-0.2.23}/notionary/database/database.py +0 -0
  189. {notionary-0.2.21 → notionary-0.2.23}/notionary/database/database_filter_builder.py +0 -0
  190. {notionary-0.2.21 → notionary-0.2.23}/notionary/database/database_provider.py +0 -0
  191. {notionary-0.2.21 → notionary-0.2.23}/notionary/database/exceptions.py +0 -0
  192. {notionary-0.2.21 → notionary-0.2.23}/notionary/database/factory.py +0 -0
  193. {notionary-0.2.21 → notionary-0.2.23}/notionary/database/models.py +0 -0
  194. {notionary-0.2.21 → notionary-0.2.23}/notionary/database/notion_database.py +0 -0
  195. {notionary-0.2.21 → notionary-0.2.23}/notionary/file_upload/__init__.py +0 -0
  196. {notionary-0.2.21 → notionary-0.2.23}/notionary/file_upload/client.py +0 -0
  197. {notionary-0.2.21 → notionary-0.2.23}/notionary/file_upload/notion_file_upload.py +0 -0
  198. {notionary-0.2.21 → notionary-0.2.23}/notionary/markdown/___init__.py +0 -0
  199. {notionary-0.2.21 → notionary-0.2.23}/notionary/markdown/makdown_document_model.py +0 -0
  200. {notionary-0.2.21 → notionary-0.2.23}/notionary/markdown/markdown_document_model.py +0 -0
  201. {notionary-0.2.21 → notionary-0.2.23}/notionary/markdown/markdown_node.py +0 -0
  202. {notionary-0.2.21 → notionary-0.2.23}/notionary/models/notion_database_response.py +0 -0
  203. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/models.py +0 -0
  204. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/property_formatter.py +0 -0
  205. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/block_processing_context.py +0 -0
  206. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/column_list_renderer.py +0 -0
  207. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/column_renderer.py +0 -0
  208. /notionary-0.2.21/notionary/page/reader/handler/context.py → /notionary-0.2.23/notionary/page/reader/handler/equation_renderer.py +0 -0
  209. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/toggle_renderer.py +0 -0
  210. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/toggleable_heading_renderer.py +0 -0
  211. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/search_filter_builder.py +0 -0
  212. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/utils.py +0 -0
  213. {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/handler/line_processing_context.py +0 -0
  214. /notionary-0.2.21/notionary/util/factory_decorator.py → /notionary-0.2.23/notionary/page/writer/markdown_to_notion_post_processor.py +0 -0
  215. {notionary-0.2.21 → notionary-0.2.23}/notionary/telemetry/__init__.py +0 -0
  216. {notionary-0.2.21 → notionary-0.2.23}/notionary/telemetry/views.py +0 -0
  217. {notionary-0.2.21 → notionary-0.2.23}/notionary/user/__init__.py +0 -0
  218. {notionary-0.2.21 → notionary-0.2.23}/notionary/user/base_notion_user.py +0 -0
  219. {notionary-0.2.21 → notionary-0.2.23}/notionary/user/client.py +0 -0
  220. {notionary-0.2.21 → notionary-0.2.23}/notionary/user/models.py +0 -0
  221. {notionary-0.2.21 → notionary-0.2.23}/notionary/user/notion_bot_user.py +0 -0
  222. {notionary-0.2.21 → notionary-0.2.23}/notionary/user/notion_user.py +0 -0
  223. {notionary-0.2.21 → notionary-0.2.23}/notionary/util/__init__.py +0 -0
  224. {notionary-0.2.21 → notionary-0.2.23}/notionary/util/factory_only.py +0 -0
  225. {notionary-0.2.21 → notionary-0.2.23}/notionary/util/fuzzy.py +0 -0
  226. {notionary-0.2.21 → notionary-0.2.23}/notionary/util/logging_mixin.py +0 -0
  227. {notionary-0.2.21 → notionary-0.2.23}/notionary/util/page_id_utils.py +0 -0
  228. {notionary-0.2.21 → notionary-0.2.23}/notionary/util/singleton.py +0 -0
  229. {notionary-0.2.21 → notionary-0.2.23}/notionary/util/singleton_metaclass.py +0 -0
@@ -0,0 +1,235 @@
1
+ Metadata-Version: 2.3
2
+ Name: notionary
3
+ Version: 0.2.23
4
+ Summary: Python library for programmatic Notion workspace management - databases, pages, and content with advanced Markdown support
5
+ License: MIT
6
+ Author: Mathis Arends
7
+ Author-email: mathisarends27@gmail.com
8
+ Requires-Python: >=3.9
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Requires-Dist: httpx (>=0.28.0)
17
+ Requires-Dist: posthog (>=6.3.1,<7.0.0)
18
+ Requires-Dist: pydantic (>=2.11.4)
19
+ Requires-Dist: python-dotenv (>=1.1.0)
20
+ Project-URL: Homepage, https://github.com/mathisarends/notionary
21
+ Description-Content-Type: text/markdown
22
+
23
+ <picture>
24
+ <source media="(prefers-color-scheme: dark)" srcset="./static/notionary-dark.png">
25
+ <source media="(prefers-color-scheme: light)" srcset="./static/notionary-light.png">
26
+ <img alt="Notionary logo: dark mode shows a white logo, light mode shows a black logo." src="./static/browser-use.png" width="full">
27
+ </picture>
28
+
29
+ <h1 align="center">Notion API simplified for Python developers 🐍</h1>
30
+
31
+ <div align="center">
32
+
33
+ [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue.svg)](https://www.python.org/downloads/)
34
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
35
+ [![Documentation](https://img.shields.io/badge/docs-mathisarends.github.io-blue.svg)](https://mathisarends.github.io/notionary/)
36
+
37
+ Transform complex Notion API interactions into simple, Pythonic code. Build AI agents, automate workflows, and create dynamic content with ease.
38
+
39
+ </div>
40
+
41
+ ---
42
+
43
+ ## Why Notionary?
44
+
45
+ - **Smart Discovery**: Find pages and databases by name—no more hunting for URLs or IDs
46
+ - **Rich Markdown**: Convert extended Markdown (callouts, toggles, columns) directly into beautiful Notion blocks
47
+ - **Async-First**: Built for modern Python with full async/await support and high performance
48
+ - **AI-Ready**: Perfect foundation for AI agents that generate and manage Notion content
49
+ - **Round-Trip**: Read existing content, modify it, and write it back while preserving formatting
50
+
51
+ ---
52
+
53
+ ## Quick Start
54
+
55
+ ```bash
56
+ pip install notionary
57
+ ```
58
+
59
+ Set up your [Notion integration](https://www.notion.so/profile/integrations) and add your token:
60
+
61
+ ```bash
62
+ NOTION_SECRET=your_integration_key
63
+ ```
64
+
65
+ ### Simple Flow: Find → Create → Update
66
+
67
+ ```python
68
+ import asyncio
69
+ from notionary import NotionPage, NotionDatabase
70
+
71
+ async def main():
72
+ # Work with pages - find by name, no exact match needed!
73
+ page = await NotionPage.from_page_name("Meeting Notes")
74
+
75
+ # Direct Markdown - quick & intuitive
76
+ await page.append_markdown("""
77
+ ## Action Items
78
+ - Review project proposal
79
+ - Schedule team meeting
80
+ - Update documentation
81
+
82
+ [callout](Important meeting decisions require follow-up "💡")
83
+ """)
84
+
85
+ # Builder Pattern - type-safe & powerful for complex layouts
86
+ await page.append_markdown(lambda builder: (
87
+ builder
88
+ .h2("Project Status")
89
+ .callout("Project milestone reached!", "🎉")
90
+ .columns(
91
+ lambda col: (col
92
+ .h3("Completed")
93
+ .bulleted_list(["API design", "Database setup", "Authentication"])
94
+ ),
95
+ lambda col: (col
96
+ .h3("In Progress")
97
+ .bulleted_list(["Frontend UI", "Testing", "Documentation"])
98
+ )
99
+ )
100
+ .table(
101
+ headers=["Task", "Owner", "Due Date"],
102
+ rows=[
103
+ ["Launch prep", "Alice", "2024-03-15"],
104
+ ["Marketing", "Bob", "2024-03-20"]
105
+ ]
106
+ )
107
+ ))
108
+
109
+ asyncio.run(main())
110
+ ```
111
+
112
+ ### Create Rich Database Entries
113
+
114
+ ```python
115
+ # Work with databases - connect and create styled entries
116
+ db = await NotionDatabase.from_database_name("Projects")
117
+
118
+ # Create new project with full styling
119
+ project = await db.create_blank_page()
120
+ await project.set_title("New Marketing Campaign")
121
+ await project.set_emoji_icon("🚀")
122
+ await project.set_random_gradient_cover()
123
+
124
+ # Set database properties
125
+ await project.set_property_value_by_name("Status", "Planning")
126
+ await project.set_property_value_by_name("Priority", "High")
127
+ await project.set_property_value_by_name("Team Lead", "sarah@company.com")
128
+
129
+ # Add rich content to the new page
130
+ await project.replace_content(lambda builder: (
131
+ builder
132
+ .h1("Campaign Overview")
133
+ .callout("New marketing initiative targeting Q2 growth", "🎯")
134
+ .h2("Goals & Objectives")
135
+ .numbered_list([
136
+ "Increase brand awareness by 25%",
137
+ "Generate 500 qualified leads",
138
+ "Launch in 3 target markets"
139
+ ])
140
+ .h2("Budget Breakdown")
141
+ .table(
142
+ headers=["Category", "Allocated", "Spent", "Remaining"],
143
+ rows=[
144
+ ["Digital Ads", "$15,000", "$3,200", "$11,800"],
145
+ ["Content Creation", "$8,000", "$1,500", "$6,500"],
146
+ ["Events", "$12,000", "$0", "$12,000"]
147
+ ]
148
+ )
149
+ .divider()
150
+ .toggle("Technical Requirements", lambda toggle: (
151
+ toggle
152
+ .paragraph("Platform specifications and integration details.")
153
+ .bulleted_list([
154
+ "CRM integration with Salesforce",
155
+ "Analytics tracking setup",
156
+ "Landing page development"
157
+ ])
158
+ ))
159
+ ))
160
+
161
+ print(f"✅ Created styled project: {project.url}")
162
+ ```
163
+
164
+ ### Extended Markdown Syntax
165
+
166
+ Notionary supports rich formatting with callouts, toggles, multi-column layouts, tables, media embeds, and more. Use either direct markdown syntax or the type-safe builder pattern.
167
+
168
+ See the complete [Block Types documentation](https://mathisarends.github.io/notionary/blocks/) for all available formatting options and syntax examples.
169
+
170
+ ## What You Can Build
171
+
172
+ - **AI Content Generation** - Perfect for AI agents that create structured reports and documentation
173
+ - **Workflow Automation** - Update project status, sync data between databases, generate reports
174
+ - **Dynamic Documentation** - Auto-generate team docs, API references, and knowledge bases
175
+ - **Content Management** - Bulk page updates, template generation, and content migration
176
+
177
+ ## Core Features
178
+
179
+ | Feature | Description |
180
+ | -------------------- | ------------------------------------------------------ |
181
+ | **Smart Discovery** | Find pages/databases by name with fuzzy matching |
182
+ | **Rich Markdown** | Extended syntax for callouts, toggles, columns, tables |
183
+ | **Async-First** | Modern Python with full async/await support |
184
+ | **Round-Trip** | Read content as markdown, edit, and write back |
185
+ | **AI-Ready** | Generate system prompts for AI content creation |
186
+ | **All Block Types** | Support for every Notion block type |
187
+ | **Type Safety** | Full type hints for better IDE support |
188
+ | **High Performance** | Efficient batch operations and caching |
189
+
190
+ ## Examples & Documentation
191
+
192
+ Explore comprehensive guides and real-world examples:
193
+
194
+ - **[📖 Full Documentation](https://mathisarends.github.io/notionary/)** - Complete API reference and guides
195
+ - **[Getting Started](https://mathisarends.github.io/notionary/get-started/)** - Quick setup and first steps
196
+ - **[Page Management](https://mathisarends.github.io/notionary/page/)** - Work with page content and properties
197
+ - **[Database Operations](https://mathisarends.github.io/notionary/database/)** - Query and manage databases
198
+ - **[Block Types](https://mathisarends.github.io/notionary/blocks/)** - Complete formatting reference
199
+
200
+ Check out the `examples/` directory for hands-on tutorials:
201
+
202
+ ### Core Examples
203
+
204
+ - **[Page Management](examples/page_example.py)** - Create, update, and manage pages
205
+ - **[Database Operations](examples/database.py)** - Connect to and query databases
206
+ - **[Workspace Discovery](examples/workspace_discovery.py)** - Explore your workspace
207
+
208
+ ### Markdown Examples
209
+
210
+ - **[Basic Formatting](examples/markdown/basic.py)** - Text, lists, and links
211
+ - **[Callouts](examples/markdown/callout.py)** - Eye-catching information boxes
212
+ - **[Toggles](examples/markdown/toggle.py)** - Collapsible content sections
213
+ - **[Multi-Column](examples/markdown/columns.py)** - Side-by-side layouts
214
+ - **[Tables](examples/markdown/table.py)** - Structured data presentation
215
+
216
+ ## Contributing
217
+
218
+ We'd love your help making Notionary even better!
219
+
220
+ Whether it's fixing bugs, adding features, improving docs, or sharing examples - all contributions are welcome.
221
+
222
+ See our [Contributing Guide](https://mathisarends.github.io/notionary/contributing/) to get started.
223
+
224
+ ---
225
+
226
+ <div align="center">
227
+
228
+ **Ready to transform your Notion workflow?**
229
+
230
+ [📖 Read the Docs](https://mathisarends.github.io/notionary/) • [Getting Started](https://mathisarends.github.io/notionary/get-started/) • [Examples](examples/)
231
+
232
+ Built with ❤️ for the Python community
233
+
234
+ </div>
235
+
@@ -0,0 +1,212 @@
1
+ <picture>
2
+ <source media="(prefers-color-scheme: dark)" srcset="./static/notionary-dark.png">
3
+ <source media="(prefers-color-scheme: light)" srcset="./static/notionary-light.png">
4
+ <img alt="Notionary logo: dark mode shows a white logo, light mode shows a black logo." src="./static/browser-use.png" width="full">
5
+ </picture>
6
+
7
+ <h1 align="center">Notion API simplified for Python developers 🐍</h1>
8
+
9
+ <div align="center">
10
+
11
+ [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue.svg)](https://www.python.org/downloads/)
12
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
13
+ [![Documentation](https://img.shields.io/badge/docs-mathisarends.github.io-blue.svg)](https://mathisarends.github.io/notionary/)
14
+
15
+ Transform complex Notion API interactions into simple, Pythonic code. Build AI agents, automate workflows, and create dynamic content with ease.
16
+
17
+ </div>
18
+
19
+ ---
20
+
21
+ ## Why Notionary?
22
+
23
+ - **Smart Discovery**: Find pages and databases by name—no more hunting for URLs or IDs
24
+ - **Rich Markdown**: Convert extended Markdown (callouts, toggles, columns) directly into beautiful Notion blocks
25
+ - **Async-First**: Built for modern Python with full async/await support and high performance
26
+ - **AI-Ready**: Perfect foundation for AI agents that generate and manage Notion content
27
+ - **Round-Trip**: Read existing content, modify it, and write it back while preserving formatting
28
+
29
+ ---
30
+
31
+ ## Quick Start
32
+
33
+ ```bash
34
+ pip install notionary
35
+ ```
36
+
37
+ Set up your [Notion integration](https://www.notion.so/profile/integrations) and add your token:
38
+
39
+ ```bash
40
+ NOTION_SECRET=your_integration_key
41
+ ```
42
+
43
+ ### Simple Flow: Find → Create → Update
44
+
45
+ ```python
46
+ import asyncio
47
+ from notionary import NotionPage, NotionDatabase
48
+
49
+ async def main():
50
+ # Work with pages - find by name, no exact match needed!
51
+ page = await NotionPage.from_page_name("Meeting Notes")
52
+
53
+ # Direct Markdown - quick & intuitive
54
+ await page.append_markdown("""
55
+ ## Action Items
56
+ - Review project proposal
57
+ - Schedule team meeting
58
+ - Update documentation
59
+
60
+ [callout](Important meeting decisions require follow-up "💡")
61
+ """)
62
+
63
+ # Builder Pattern - type-safe & powerful for complex layouts
64
+ await page.append_markdown(lambda builder: (
65
+ builder
66
+ .h2("Project Status")
67
+ .callout("Project milestone reached!", "🎉")
68
+ .columns(
69
+ lambda col: (col
70
+ .h3("Completed")
71
+ .bulleted_list(["API design", "Database setup", "Authentication"])
72
+ ),
73
+ lambda col: (col
74
+ .h3("In Progress")
75
+ .bulleted_list(["Frontend UI", "Testing", "Documentation"])
76
+ )
77
+ )
78
+ .table(
79
+ headers=["Task", "Owner", "Due Date"],
80
+ rows=[
81
+ ["Launch prep", "Alice", "2024-03-15"],
82
+ ["Marketing", "Bob", "2024-03-20"]
83
+ ]
84
+ )
85
+ ))
86
+
87
+ asyncio.run(main())
88
+ ```
89
+
90
+ ### Create Rich Database Entries
91
+
92
+ ```python
93
+ # Work with databases - connect and create styled entries
94
+ db = await NotionDatabase.from_database_name("Projects")
95
+
96
+ # Create new project with full styling
97
+ project = await db.create_blank_page()
98
+ await project.set_title("New Marketing Campaign")
99
+ await project.set_emoji_icon("🚀")
100
+ await project.set_random_gradient_cover()
101
+
102
+ # Set database properties
103
+ await project.set_property_value_by_name("Status", "Planning")
104
+ await project.set_property_value_by_name("Priority", "High")
105
+ await project.set_property_value_by_name("Team Lead", "sarah@company.com")
106
+
107
+ # Add rich content to the new page
108
+ await project.replace_content(lambda builder: (
109
+ builder
110
+ .h1("Campaign Overview")
111
+ .callout("New marketing initiative targeting Q2 growth", "🎯")
112
+ .h2("Goals & Objectives")
113
+ .numbered_list([
114
+ "Increase brand awareness by 25%",
115
+ "Generate 500 qualified leads",
116
+ "Launch in 3 target markets"
117
+ ])
118
+ .h2("Budget Breakdown")
119
+ .table(
120
+ headers=["Category", "Allocated", "Spent", "Remaining"],
121
+ rows=[
122
+ ["Digital Ads", "$15,000", "$3,200", "$11,800"],
123
+ ["Content Creation", "$8,000", "$1,500", "$6,500"],
124
+ ["Events", "$12,000", "$0", "$12,000"]
125
+ ]
126
+ )
127
+ .divider()
128
+ .toggle("Technical Requirements", lambda toggle: (
129
+ toggle
130
+ .paragraph("Platform specifications and integration details.")
131
+ .bulleted_list([
132
+ "CRM integration with Salesforce",
133
+ "Analytics tracking setup",
134
+ "Landing page development"
135
+ ])
136
+ ))
137
+ ))
138
+
139
+ print(f"✅ Created styled project: {project.url}")
140
+ ```
141
+
142
+ ### Extended Markdown Syntax
143
+
144
+ Notionary supports rich formatting with callouts, toggles, multi-column layouts, tables, media embeds, and more. Use either direct markdown syntax or the type-safe builder pattern.
145
+
146
+ See the complete [Block Types documentation](https://mathisarends.github.io/notionary/blocks/) for all available formatting options and syntax examples.
147
+
148
+ ## What You Can Build
149
+
150
+ - **AI Content Generation** - Perfect for AI agents that create structured reports and documentation
151
+ - **Workflow Automation** - Update project status, sync data between databases, generate reports
152
+ - **Dynamic Documentation** - Auto-generate team docs, API references, and knowledge bases
153
+ - **Content Management** - Bulk page updates, template generation, and content migration
154
+
155
+ ## Core Features
156
+
157
+ | Feature | Description |
158
+ | -------------------- | ------------------------------------------------------ |
159
+ | **Smart Discovery** | Find pages/databases by name with fuzzy matching |
160
+ | **Rich Markdown** | Extended syntax for callouts, toggles, columns, tables |
161
+ | **Async-First** | Modern Python with full async/await support |
162
+ | **Round-Trip** | Read content as markdown, edit, and write back |
163
+ | **AI-Ready** | Generate system prompts for AI content creation |
164
+ | **All Block Types** | Support for every Notion block type |
165
+ | **Type Safety** | Full type hints for better IDE support |
166
+ | **High Performance** | Efficient batch operations and caching |
167
+
168
+ ## Examples & Documentation
169
+
170
+ Explore comprehensive guides and real-world examples:
171
+
172
+ - **[📖 Full Documentation](https://mathisarends.github.io/notionary/)** - Complete API reference and guides
173
+ - **[Getting Started](https://mathisarends.github.io/notionary/get-started/)** - Quick setup and first steps
174
+ - **[Page Management](https://mathisarends.github.io/notionary/page/)** - Work with page content and properties
175
+ - **[Database Operations](https://mathisarends.github.io/notionary/database/)** - Query and manage databases
176
+ - **[Block Types](https://mathisarends.github.io/notionary/blocks/)** - Complete formatting reference
177
+
178
+ Check out the `examples/` directory for hands-on tutorials:
179
+
180
+ ### Core Examples
181
+
182
+ - **[Page Management](examples/page_example.py)** - Create, update, and manage pages
183
+ - **[Database Operations](examples/database.py)** - Connect to and query databases
184
+ - **[Workspace Discovery](examples/workspace_discovery.py)** - Explore your workspace
185
+
186
+ ### Markdown Examples
187
+
188
+ - **[Basic Formatting](examples/markdown/basic.py)** - Text, lists, and links
189
+ - **[Callouts](examples/markdown/callout.py)** - Eye-catching information boxes
190
+ - **[Toggles](examples/markdown/toggle.py)** - Collapsible content sections
191
+ - **[Multi-Column](examples/markdown/columns.py)** - Side-by-side layouts
192
+ - **[Tables](examples/markdown/table.py)** - Structured data presentation
193
+
194
+ ## Contributing
195
+
196
+ We'd love your help making Notionary even better!
197
+
198
+ Whether it's fixing bugs, adding features, improving docs, or sharing examples - all contributions are welcome.
199
+
200
+ See our [Contributing Guide](https://mathisarends.github.io/notionary/contributing/) to get started.
201
+
202
+ ---
203
+
204
+ <div align="center">
205
+
206
+ **Ready to transform your Notion workflow?**
207
+
208
+ [📖 Read the Docs](https://mathisarends.github.io/notionary/) • [Getting Started](https://mathisarends.github.io/notionary/get-started/) • [Examples](examples/)
209
+
210
+ Built with ❤️ for the Python community
211
+
212
+ </div>
@@ -34,6 +34,7 @@ def bootstrap_blocks() -> None:
34
34
  toggle,
35
35
  toggleable_heading,
36
36
  video,
37
+ child_database,
37
38
  )
38
39
 
39
40
  # Collect all exports from modules
@@ -61,6 +62,7 @@ def bootstrap_blocks() -> None:
61
62
  video,
62
63
  toggleable_heading,
63
64
  table_of_contents,
65
+ child_database,
64
66
  ):
65
67
  ns.update(vars(m))
66
68
 
@@ -123,6 +125,10 @@ def bootstrap_blocks() -> None:
123
125
  from notionary.blocks.toggle.toggle_models import CreateToggleBlock, ToggleBlock
124
126
  from notionary.blocks.types import BlockType
125
127
  from notionary.blocks.video.video_element_models import CreateVideoBlock
128
+ from notionary.blocks.child_database.child_database_models import (
129
+ CreateChildDatabaseBlock,
130
+ ChildDatabaseBlock,
131
+ )
126
132
 
127
133
  # Define the Union types that are needed for model rebuilding
128
134
  BlockCreateRequest = Union[
@@ -150,9 +156,10 @@ def bootstrap_blocks() -> None:
150
156
  CreateVideoBlock,
151
157
  CreateTableOfContentsBlock,
152
158
  CreatePdfBlock,
159
+ CreateChildDatabaseBlock,
153
160
  ]
154
161
 
155
- BlockCreateResult = Optional[Union[list[BlockCreateRequest], BlockCreateRequest]]
162
+ BlockCreateResult = Optional[BlockCreateRequest]
156
163
 
157
164
  # Add all block types to namespace
158
165
  ns.update(
@@ -202,6 +209,7 @@ def bootstrap_blocks() -> None:
202
209
  "CreateVideoBlock": CreateVideoBlock,
203
210
  "TableOfContentsBlock": TableOfContentsBlock,
204
211
  "CreateTableOfContentsBlock": CreateTableOfContentsBlock,
212
+ "ChildDatabaseBlock": ChildDatabaseBlock,
205
213
  # Add the Union types
206
214
  "BlockCreateRequest": BlockCreateRequest,
207
215
  "BlockCreateResult": BlockCreateResult,
@@ -0,0 +1,115 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from typing import Optional
5
+
6
+ from notionary.blocks.audio.audio_models import CreateAudioBlock
7
+ from notionary.blocks.base_block_element import BaseBlockElement
8
+ from notionary.blocks.file.file_element_models import ExternalFile, FileBlock, FileType
9
+ from notionary.blocks.mixins.captions import CaptionMixin
10
+ from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformation
11
+ from notionary.blocks.models import Block, BlockCreateResult, BlockType
12
+
13
+
14
+ class AudioElement(BaseBlockElement, CaptionMixin):
15
+ """
16
+ Handles conversion between Markdown audio embeds and Notion audio blocks.
17
+
18
+ Markdown audio syntax:
19
+ - [audio](https://example.com/audio.mp3) - Simple audio embed
20
+ - [audio](https://example.com/audio.mp3)(caption:Episode 1) - Audio with caption
21
+ - (caption:Background music)[audio](https://example.com/song.mp3) - caption before URL
22
+
23
+ Where:
24
+ - URL is the required audio file URL
25
+ - Caption supports rich text formatting and is optional
26
+ """
27
+
28
+ # Simple pattern that matches just the audio link, CaptionMixin handles caption separately
29
+ AUDIO_PATTERN = re.compile(r"\[audio\]\((https?://[^\s\"]+)\)")
30
+
31
+ @classmethod
32
+ def _extract_audio_url(cls, text: str) -> Optional[str]:
33
+ """Extract audio URL from text, handling caption patterns."""
34
+ # First remove any captions to get clean text for URL extraction
35
+ clean_text = cls.remove_caption(text)
36
+
37
+ # Now extract the URL from clean text
38
+ match = cls.AUDIO_PATTERN.search(clean_text)
39
+ if match:
40
+ return match.group(1)
41
+
42
+ return None
43
+
44
+ SUPPORTED_EXTENSIONS = {".mp3", ".wav", ".ogg", ".oga", ".m4a"}
45
+
46
+ @classmethod
47
+ def match_notion(cls, block: Block) -> bool:
48
+ """Check if this element can handle the given Notion block."""
49
+ return block.type == BlockType.AUDIO
50
+
51
+ @classmethod
52
+ async def markdown_to_notion(cls, text: str) -> BlockCreateResult:
53
+ """Convert markdown audio embed to Notion audio block."""
54
+ # Use our helper method to extract the URL
55
+ url = cls._extract_audio_url(text.strip())
56
+ if not url:
57
+ return None
58
+
59
+ if not cls._is_likely_audio_url(url):
60
+ return None
61
+
62
+ # Use mixin to extract caption (if present anywhere in text)
63
+ caption_text = cls.extract_caption(text.strip())
64
+ caption_rich_text = cls.build_caption_rich_text(caption_text or "")
65
+
66
+ audio_content = FileBlock(
67
+ type=FileType.EXTERNAL,
68
+ external=ExternalFile(url=url),
69
+ caption=caption_rich_text,
70
+ )
71
+
72
+ return CreateAudioBlock(audio=audio_content)
73
+
74
+ @classmethod
75
+ async def notion_to_markdown(cls, block: Block) -> Optional[str]:
76
+ """Convert Notion audio block to markdown audio embed."""
77
+ if block.type != BlockType.AUDIO or block.audio is None:
78
+ return None
79
+
80
+ audio = block.audio
81
+
82
+ # Only handle external audio
83
+ if audio.type != FileType.EXTERNAL or audio.external is None:
84
+ return None
85
+ url = audio.external.url
86
+ if not url:
87
+ return None
88
+
89
+ result = f"[audio]({url})"
90
+
91
+ # Add caption if present
92
+ caption_markdown = await cls.format_caption_for_markdown(audio.caption or [])
93
+ if caption_markdown:
94
+ result += caption_markdown
95
+
96
+ return result
97
+
98
+ @classmethod
99
+ def get_system_prompt_information(cls) -> Optional[BlockElementMarkdownInformation]:
100
+ """Get system prompt information for audio blocks."""
101
+ return BlockElementMarkdownInformation(
102
+ block_type=cls.__name__,
103
+ description="Audio blocks embed audio files from external URLs with optional captions",
104
+ syntax_examples=[
105
+ "[audio](https://example.com/song.mp3)",
106
+ "[audio](https://example.com/podcast.wav)(caption:Episode 1)",
107
+ "(caption:Background music)[audio](https://soundcloud.com/track/123)",
108
+ "[audio](https://example.com/interview.mp3)(caption:**Live** interview)",
109
+ ],
110
+ usage_guidelines="Use for embedding audio files like music, podcasts, or sound effects. Supports common audio formats (mp3, wav, ogg, m4a). Caption supports rich text formatting and is optional.",
111
+ )
112
+
113
+ @classmethod
114
+ def _is_likely_audio_url(cls, url: str) -> bool:
115
+ return any(url.lower().endswith(ext) for ext in cls.SUPPORTED_EXTENSIONS)
@@ -5,6 +5,7 @@ from typing import Optional
5
5
  from pydantic import BaseModel
6
6
 
7
7
  from notionary.markdown.markdown_node import MarkdownNode
8
+ from notionary.blocks.mixins.captions import CaptionMarkdownNodeMixin
8
9
 
9
10
 
10
11
  class AudioMarkdownBlockParams(BaseModel):
@@ -12,7 +13,7 @@ class AudioMarkdownBlockParams(BaseModel):
12
13
  caption: Optional[str] = None
13
14
 
14
15
 
15
- class AudioMarkdownNode(MarkdownNode):
16
+ class AudioMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
16
17
  """
17
18
  Programmatic interface for creating Notion-style audio blocks.
18
19
  """
@@ -26,6 +27,11 @@ class AudioMarkdownNode(MarkdownNode):
26
27
  return cls(url=params.url, caption=params.caption)
27
28
 
28
29
  def to_markdown(self) -> str:
29
- if self.caption:
30
- return f'[audio]({self.url} "{self.caption}")'
31
- return f"[audio]({self.url})"
30
+ """Return the Markdown representation.
31
+
32
+ Examples:
33
+ - [audio](https://example.com/song.mp3)
34
+ - [audio](https://example.com/song.mp3)(caption:Background music)
35
+ """
36
+ base_markdown = f"[audio]({self.url})"
37
+ return self.append_caption_to_markdown(base_markdown, self.caption)
@@ -0,0 +1,42 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC
4
+ from typing import Optional
5
+
6
+ from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformation
7
+ from notionary.blocks.models import Block, BlockCreateResult
8
+
9
+
10
+ class BaseBlockElement(ABC):
11
+ """Base class for elements that can be converted between Markdown and Notion."""
12
+
13
+ @classmethod
14
+ async def markdown_to_notion(cls, text: str) -> BlockCreateResult:
15
+ """
16
+ Convert markdown to Notion block content.
17
+
18
+ Returns:
19
+ - BlockContent: Single block content (e.g., ToDoBlock, ParagraphBlock)
20
+ - list[BlockContent]: Multiple block contents
21
+ - None: Cannot convert this markdown
22
+ """
23
+
24
+ @classmethod
25
+ async def notion_to_markdown(cls, block: Block) -> Optional[str]:
26
+ """Convert Notion block to markdown."""
27
+
28
+ @classmethod
29
+ def match_notion(cls, block: Block) -> bool:
30
+ """Check if this element can handle the given Notion block."""
31
+ # Default implementation - subclasses should override this method
32
+ # Cannot call async notion_to_markdown here
33
+ return False
34
+
35
+ @classmethod
36
+ def get_system_prompt_information(cls) -> Optional[BlockElementMarkdownInformation]:
37
+ """Get system prompt information for this block element.
38
+
39
+ Subclasses should override this method to provide their specific information.
40
+ Return None if the element should not be included in documentation.
41
+ """
42
+ return None