notionary 0.2.23__py3-none-any.whl → 0.2.25__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 (101) hide show
  1. notionary/__init__.py +1 -1
  2. notionary/blocks/__init__.py +3 -1
  3. notionary/blocks/audio/__init__.py +0 -2
  4. notionary/blocks/audio/audio_element.py +92 -49
  5. notionary/blocks/audio/audio_markdown_node.py +4 -17
  6. notionary/blocks/bookmark/__init__.py +0 -2
  7. notionary/blocks/bookmark/bookmark_markdown_node.py +5 -21
  8. notionary/blocks/breadcrumbs/__init__.py +0 -2
  9. notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +2 -21
  10. notionary/blocks/bulleted_list/__init__.py +0 -2
  11. notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +3 -17
  12. notionary/blocks/bulleted_list/bulleted_list_models.py +0 -1
  13. notionary/blocks/callout/__init__.py +0 -2
  14. notionary/blocks/callout/callout_markdown_node.py +4 -18
  15. notionary/blocks/callout/callout_models.py +3 -4
  16. notionary/blocks/code/code_markdown_node.py +5 -19
  17. notionary/blocks/column/__init__.py +0 -4
  18. notionary/blocks/column/column_list_markdown_node.py +3 -19
  19. notionary/blocks/column/column_markdown_node.py +4 -21
  20. notionary/blocks/divider/__init__.py +0 -2
  21. notionary/blocks/divider/divider_markdown_node.py +2 -16
  22. notionary/blocks/embed/__init__.py +0 -2
  23. notionary/blocks/embed/embed_markdown_node.py +4 -17
  24. notionary/blocks/equation/__init__.py +0 -1
  25. notionary/blocks/equation/equation_element_markdown_node.py +3 -15
  26. notionary/blocks/file/__init__.py +0 -2
  27. notionary/blocks/file/file_element.py +67 -46
  28. notionary/blocks/file/file_element_markdown_node.py +4 -17
  29. notionary/blocks/heading/__init__.py +0 -2
  30. notionary/blocks/heading/heading_markdown_node.py +5 -19
  31. notionary/blocks/heading/heading_models.py +3 -3
  32. notionary/blocks/image_block/__init__.py +0 -2
  33. notionary/blocks/image_block/image_element.py +66 -25
  34. notionary/blocks/image_block/image_markdown_node.py +5 -20
  35. notionary/{markdown → blocks/markdown}/markdown_builder.py +29 -233
  36. notionary/blocks/markdown/markdown_node.py +25 -0
  37. notionary/blocks/mixins/file_upload/__init__.py +3 -0
  38. notionary/blocks/mixins/file_upload/file_upload_mixin.py +320 -0
  39. notionary/blocks/numbered_list/__init__.py +0 -1
  40. notionary/blocks/numbered_list/numbered_list_markdown_node.py +3 -17
  41. notionary/blocks/numbered_list/numbered_list_models.py +3 -3
  42. notionary/blocks/paragraph/__init__.py +0 -2
  43. notionary/blocks/paragraph/paragraph_markdown_node.py +3 -13
  44. notionary/blocks/pdf/__init__.py +0 -2
  45. notionary/blocks/pdf/pdf_element.py +81 -32
  46. notionary/blocks/pdf/pdf_markdown_node.py +5 -18
  47. notionary/blocks/quote/__init__.py +0 -2
  48. notionary/blocks/quote/quote_markdown_node.py +3 -13
  49. notionary/blocks/registry/__init__.py +1 -2
  50. notionary/blocks/registry/block_registry.py +116 -61
  51. notionary/blocks/table/__init__.py +0 -2
  52. notionary/blocks/table/table_markdown_node.py +17 -16
  53. notionary/blocks/table_of_contents/__init__.py +0 -2
  54. notionary/blocks/table_of_contents/table_of_contents_element.py +27 -15
  55. notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +3 -17
  56. notionary/blocks/table_of_contents/table_of_contents_models.py +2 -2
  57. notionary/blocks/todo/__init__.py +0 -2
  58. notionary/blocks/todo/todo_markdown_node.py +9 -20
  59. notionary/blocks/todo/todo_models.py +2 -3
  60. notionary/blocks/toggle/__init__.py +0 -2
  61. notionary/blocks/toggle/toggle_markdown_node.py +5 -19
  62. notionary/blocks/toggleable_heading/__init__.py +0 -2
  63. notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +6 -23
  64. notionary/blocks/video/__init__.py +0 -2
  65. notionary/blocks/video/video_element.py +110 -34
  66. notionary/blocks/video/video_markdown_node.py +4 -15
  67. notionary/comments/client.py +1 -1
  68. notionary/file_upload/client.py +3 -2
  69. notionary/file_upload/models.py +10 -1
  70. notionary/file_upload/notion_file_upload.py +5 -5
  71. notionary/page/markdown_whitespace_processor.py +129 -0
  72. notionary/page/notion_page.py +35 -40
  73. notionary/page/page_content_deleting_service.py +1 -1
  74. notionary/page/page_content_writer.py +32 -129
  75. notionary/page/page_context.py +0 -5
  76. notionary/page/reader/handler/column_list_renderer.py +2 -2
  77. notionary/page/reader/handler/column_renderer.py +2 -2
  78. notionary/page/reader/handler/line_renderer.py +2 -2
  79. notionary/page/reader/handler/toggle_renderer.py +2 -2
  80. notionary/page/reader/handler/toggleable_heading_renderer.py +2 -2
  81. notionary/page/writer/handler/equation_handler.py +1 -1
  82. notionary/page/writer/handler/toggle_handler.py +8 -4
  83. notionary/page/writer/handler/toggleable_heading_handler.py +3 -2
  84. notionary/page/writer/markdown_to_notion_converter.py +74 -30
  85. notionary/schemas/__init__.py +3 -0
  86. notionary/schemas/base.py +73 -0
  87. notionary/shared/__init__.py +1 -3
  88. notionary-0.2.25.dist-info/METADATA +270 -0
  89. {notionary-0.2.23.dist-info → notionary-0.2.25.dist-info}/RECORD +92 -94
  90. notionary/blocks/guards.py +0 -22
  91. notionary/blocks/registry/block_registry_builder.py +0 -264
  92. notionary/markdown/makdown_document_model.py +0 -0
  93. notionary/markdown/markdown_document_model.py +0 -228
  94. notionary/markdown/markdown_node.py +0 -30
  95. notionary/models/notion_database_response.py +0 -0
  96. notionary/page/writer/markdown_to_notion_formatting_post_processor.py +0 -73
  97. notionary/page/writer/markdown_to_notion_post_processor.py +0 -0
  98. notionary-0.2.23.dist-info/METADATA +0 -235
  99. /notionary/{markdown/___init__.py → blocks/markdown/markdown_document_model.py} +0 -0
  100. {notionary-0.2.23.dist-info → notionary-0.2.25.dist-info}/LICENSE +0 -0
  101. {notionary-0.2.23.dist-info → notionary-0.2.25.dist-info}/WHEEL +0 -0
@@ -19,8 +19,9 @@ class ToggleableHeadingHandler(LineHandler):
19
19
 
20
20
  def __init__(self):
21
21
  super().__init__()
22
+ # Updated: Support both "+++# title" and "+++#title"
22
23
  self._start_pattern = re.compile(
23
- r"^[+]{3}(?P<level>#{1,3})\s+(.+)$", re.IGNORECASE
24
+ r"^[+]{3}\s*(?P<level>#{1,3})\s*(.+)$", re.IGNORECASE
24
25
  )
25
26
  # +++
26
27
  self._end_pattern = re.compile(r"^[+]{3}\s*$")
@@ -49,7 +50,7 @@ class ToggleableHeadingHandler(LineHandler):
49
50
  return await _handle(self._add_toggleable_heading_content)
50
51
 
51
52
  def _is_toggleable_heading_start(self, context: LineProcessingContext) -> bool:
52
- """Check if line starts a toggleable heading (+++# "Title")."""
53
+ """Check if line starts a toggleable heading (+++# "Title" or +++#"Title")."""
53
54
  return self._start_pattern.match(context.line.strip()) is not None
54
55
 
55
56
  def _is_toggleable_heading_end(self, context: LineProcessingContext) -> bool:
@@ -12,54 +12,32 @@ from notionary.page.writer.handler import (
12
12
  ToggleableHeadingHandler,
13
13
  ToggleHandler,
14
14
  )
15
- from notionary.page.writer.markdown_to_notion_formatting_post_processor import (
16
- MarkdownToNotionFormattingPostProcessor,
17
- )
18
15
  from notionary.page.writer.notion_text_length_processor import (
19
16
  NotionTextLengthProcessor,
20
17
  )
18
+ from notionary.util.logging_mixin import LoggingMixin
19
+
20
+
21
+ class HandlerOrderValidationError(RuntimeError):
22
+ """Raised when handler chain order is incorrect."""
21
23
 
24
+ pass
22
25
 
23
- class MarkdownToNotionConverter:
26
+
27
+ class MarkdownToNotionConverter(LoggingMixin):
24
28
  """Converts Markdown text to Notion API block format with unified stack-based processing."""
25
29
 
26
30
  def __init__(self, block_registry: BlockRegistry) -> None:
27
31
  self._block_registry = block_registry
28
- self._formatting_post_processor = MarkdownToNotionFormattingPostProcessor()
29
32
  self._text_length_post_processor = NotionTextLengthProcessor()
30
-
31
33
  self._setup_handler_chain()
32
34
 
33
- def _setup_handler_chain(self) -> None:
34
- code_handler = CodeHandler()
35
- equation_handler = EquationHandler()
36
- table_handler = TableHandler()
37
- column_list_handler = ColumnListHandler()
38
- column_handler = ColumnHandler()
39
- toggle_handler = ToggleHandler()
40
- toggleable_heading_handler = ToggleableHeadingHandler()
41
- regular_handler = RegularLineHandler()
42
-
43
- # register more specific elements first
44
- code_handler.set_next(equation_handler).set_next(table_handler).set_next(
45
- column_list_handler
46
- ).set_next(column_handler).set_next(toggleable_heading_handler).set_next(
47
- toggle_handler
48
- ).set_next(
49
- regular_handler
50
- )
51
-
52
- self._handler_chain = code_handler
53
-
54
35
  async def convert(self, markdown_text: str) -> list[BlockCreateRequest]:
55
36
  if not markdown_text.strip():
56
37
  return []
57
38
 
58
39
  all_blocks = await self.process_lines(markdown_text)
59
40
 
60
- # Apply formatting post-processing (empty paragraphs)
61
- all_blocks = self._formatting_post_processor.process(all_blocks)
62
-
63
41
  # Apply text length post-processing (truncation)
64
42
  all_blocks = self._text_length_post_processor.process(all_blocks)
65
43
 
@@ -93,3 +71,69 @@ class MarkdownToNotionConverter:
93
71
  continue
94
72
 
95
73
  return result_blocks
74
+
75
+ def _setup_handler_chain(self) -> None:
76
+ code_handler = CodeHandler()
77
+ equation_handler = EquationHandler()
78
+ table_handler = TableHandler()
79
+ column_list_handler = ColumnListHandler()
80
+ column_handler = ColumnHandler()
81
+ toggle_handler = ToggleHandler()
82
+ toggleable_heading_handler = ToggleableHeadingHandler()
83
+ regular_handler = RegularLineHandler()
84
+
85
+ # Create handler chain
86
+ code_handler.set_next(equation_handler).set_next(table_handler).set_next(
87
+ column_handler
88
+ ).set_next(column_list_handler).set_next(toggleable_heading_handler).set_next(
89
+ toggle_handler
90
+ ).set_next(
91
+ regular_handler
92
+ )
93
+
94
+ self._handler_chain = code_handler
95
+
96
+ # Validate critical order - only log/error if something is wrong
97
+ self._validate_handler_order(
98
+ [
99
+ code_handler,
100
+ equation_handler,
101
+ table_handler,
102
+ column_handler,
103
+ column_list_handler,
104
+ toggleable_heading_handler,
105
+ toggle_handler,
106
+ regular_handler,
107
+ ]
108
+ )
109
+
110
+ def _validate_handler_order(self, handlers) -> None:
111
+ """Validate critical handler positioning rules - only warns/errors when needed."""
112
+ handler_classes = [handler.__class__ for handler in handlers]
113
+
114
+ # Critical: ColumnHandler MUST come before ColumnListHandler
115
+ try:
116
+ column_handler_pos = handler_classes.index(ColumnHandler)
117
+ column_list_handler_pos = handler_classes.index(ColumnListHandler)
118
+
119
+ if column_handler_pos >= column_list_handler_pos:
120
+ error_msg = (
121
+ f"CRITICAL: ColumnHandler must come BEFORE ColumnListHandler. "
122
+ f"Current order: ColumnHandler at {column_handler_pos}, ColumnListHandler at {column_list_handler_pos}. "
123
+ f"Fix: Move ColumnHandler before ColumnListHandler in _setup_handler_chain()"
124
+ )
125
+ self.logger.error(error_msg)
126
+ raise HandlerOrderValidationError(error_msg)
127
+
128
+ except ValueError as e:
129
+ error_msg = f"Missing required handlers in chain: {e}"
130
+ self.logger.error(error_msg)
131
+ raise HandlerOrderValidationError(error_msg)
132
+
133
+ # Critical: RegularLineHandler should be last (fallback)
134
+ if handler_classes[-1] != RegularLineHandler:
135
+ error_msg = (
136
+ f"WARNING: RegularLineHandler should be last handler (fallback), "
137
+ f"but {handler_classes[-1].__name__} is at the end"
138
+ )
139
+ self.logger.warning(error_msg)
@@ -0,0 +1,3 @@
1
+ from .base import NotionContentSchema
2
+
3
+ __all__ = ["NotionContentSchema"]
@@ -0,0 +1,73 @@
1
+ """
2
+ NotionContentSchema Base Class
3
+ =============================
4
+
5
+ Base class for all Notion structured output schemas with injected MarkdownBuilder
6
+ """
7
+
8
+ from pydantic import BaseModel
9
+ from notionary.blocks.markdown.markdown_builder import MarkdownBuilder
10
+
11
+
12
+ class NotionContentSchema(BaseModel):
13
+ """
14
+ Base class for all Notion content schemas.
15
+
16
+ Inherit from this and implement to_notion_content() to create
17
+ schemas that work with LLM structured output.
18
+
19
+ Example usage:
20
+
21
+ class BlogPost(NotionContentSchema):
22
+ title: str = Field(description="Catchy blog post title")
23
+ introduction: str = Field(description="Engaging opening paragraph")
24
+ main_points: List[str] = Field(description="3-5 key takeaways")
25
+ conclusion: str = Field(description="Summary and call-to-action")
26
+
27
+ def to_notion_content(self, builder: MarkdownBuilder) -> str:
28
+ return (builder
29
+ .h1(self.title)
30
+ .paragraph(self.introduction)
31
+ .h2("Key Points")
32
+ .bulleted_list(self.main_points)
33
+ .h2("Conclusion")
34
+ .paragraph(self.conclusion)
35
+ .build()
36
+ )
37
+
38
+ # Usage with LLM:
39
+ llm = ChatOpenAI(model="gpt-4o")
40
+ structured_llm = llm.with_structured_output(BlogPost)
41
+ blog = structured_llm.invoke("Write about Python async/await")
42
+
43
+ # Upload to Notion:
44
+ await blog.append_to_page("My Blog")
45
+ """
46
+
47
+ def to_notion_content(self, builder: MarkdownBuilder) -> str:
48
+ """
49
+ Build Notion content using the provided MarkdownBuilder.
50
+
51
+ Args:
52
+ builder: Empty MarkdownBuilder instance to build content with
53
+
54
+ Returns:
55
+ str: The final markdown string (user should call build() on the builder)
56
+ """
57
+ raise NotImplementedError("Subclasses must implement to_notion_content()")
58
+
59
+ async def append_to_page(self, page_name: str):
60
+ """
61
+ Upload this content directly to a Notion page.
62
+
63
+ Args:
64
+ page_name: Name of the target Notion page
65
+ """
66
+ from notionary import NotionPage
67
+
68
+ # Create fresh builder and let subclass build content
69
+ builder = MarkdownBuilder()
70
+ markdown = self.to_notion_content(builder)
71
+
72
+ page = await NotionPage.from_page_name(page_name)
73
+ await page.append_markdown(markdown)
@@ -1,5 +1,3 @@
1
1
  from .name_to_id_resolver import NameIdResolver
2
2
 
3
- __all__ = [
4
- "NameIdResolver"
5
- ]
3
+ __all__ = ["NameIdResolver"]
@@ -0,0 +1,270 @@
1
+ Metadata-Version: 2.3
2
+ Name: notionary
3
+ Version: 0.2.25
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: aiofiles (>=24.1.0,<25.0.0)
17
+ Requires-Dist: httpx (>=0.28.0)
18
+ Requires-Dist: posthog (>=6.3.1,<7.0.0)
19
+ Requires-Dist: pydantic (>=2.11.4)
20
+ Requires-Dist: python-dotenv (>=1.1.0)
21
+ Project-URL: Homepage, https://github.com/mathisarends/notionary
22
+ Description-Content-Type: text/markdown
23
+
24
+ <picture>
25
+ <source media="(prefers-color-scheme: dark)" srcset="./static/notionary-dark.png">
26
+ <source media="(prefers-color-scheme: light)" srcset="./static/notionary-light.png">
27
+ <img alt="Notionary logo: dark mode shows a white logo, light mode shows a black logo." src="./static/browser-use.png" width="full">
28
+ </picture>
29
+
30
+ <h1 align="center">The Modern Notion API for Python & AI Agents</h1>
31
+
32
+ <div align="center">
33
+
34
+ [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue.svg)](https://www.python.org/downloads/)
35
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
36
+ [![Documentation](https://img.shields.io/badge/docs-mathisarends.github.io-blue.svg)](https://mathisarends.github.io/notionary/)
37
+
38
+ **Transform complex Notion API interactions into simple, Pythonic code.**
39
+ Perfect for developers building AI agents, automation workflows, and dynamic content systems.
40
+
41
+ </div>
42
+
43
+ ---
44
+
45
+ ## Why Notionary?
46
+
47
+ - **AI-Native Design** - Built specifically for AI agents with schema-driven markdown syntax
48
+ - **Smart Discovery** - Find pages and databases by name with fuzzy matching—no more hunting for IDs
49
+ - **Extended Markdown** - Rich syntax for toggles, columns, callouts, and media uploads
50
+ - **Async-First** - Modern Python with full async/await support and high performance
51
+ - **Round-Trip** - Read existing content, modify it, and write it back while preserving formatting
52
+ - **Complete Coverage** - Every Notion block type supported with type safety
53
+
54
+ ---
55
+
56
+ ## Installation
57
+
58
+ ```bash
59
+ pip install notionary
60
+ ```
61
+
62
+ Set up your [Notion integration](https://www.notion.so/profile/integrations) and configure your token:
63
+
64
+ ```bash
65
+ export NOTION_SECRET=your_integration_key
66
+ ```
67
+
68
+ ---
69
+
70
+ ## See It in Action
71
+
72
+ ### Creating Rich Database Entries
73
+
74
+ https://github.com/user-attachments/assets/da8b4691-bee4-4b0f-801e-dccacb630398
75
+
76
+ *Create styled project pages with properties, content, and rich formatting*
77
+
78
+ ### Local File Uploads (Videos & Images)
79
+
80
+ https://github.com/user-attachments/assets/a079ec01-bb56-4c65-8260-7b1fca42ac68
81
+
82
+ *Upload videos and images using simple markdown syntax - files are automatically uploaded to Notion*
83
+
84
+ ---
85
+
86
+ ## Quick Start
87
+
88
+ ### Find → Create → Update Flow
89
+
90
+ ```python
91
+ import asyncio
92
+ from notionary import NotionPage, NotionDatabase
93
+
94
+ async def main():
95
+ # Find pages by name - fuzzy matching included!
96
+ page = await NotionPage.from_page_name("Meeting Notes")
97
+
98
+ # Option 1: Direct Extended Markdown
99
+ await page.append_markdown("""
100
+ ## Action Items
101
+ - [x] Review project proposal
102
+ - [ ] Schedule team meeting
103
+ - [ ] Update documentation
104
+
105
+ [callout](Meeting decisions require follow-up "💡")
106
+
107
+ +++ Details
108
+ Additional context and next steps...
109
+ +++
110
+ """)
111
+
112
+ # Option 2: Type-Safe Builder (maps to same markdown internally)
113
+ await page.append_markdown(lambda builder: (
114
+ builder
115
+ .h2("Project Status")
116
+ .callout("Milestone reached!", "🎉")
117
+ .columns(
118
+ lambda col: col.h3("Completed").bulleted_list([
119
+ "API design", "Database setup", "Authentication"
120
+ ]),
121
+ lambda col: col.h3("In Progress").bulleted_list([
122
+ "Frontend UI", "Testing", "Documentation"
123
+ ]),
124
+ width_ratios=[0.6, 0.4]
125
+ )
126
+ .toggle("Budget Details", lambda t: t
127
+ .table(["Item", "Cost", "Status"], [
128
+ ["Development", "$15,000", "Paid"],
129
+ ["Design", "$8,000", "Pending"]
130
+ ])
131
+ )
132
+ ))
133
+
134
+ asyncio.run(main())
135
+ ```
136
+
137
+ ### Complete Block Support
138
+
139
+ Every Notion block type with extended syntax:
140
+
141
+ | Block Type | Markdown Syntax | Use Case |
142
+ |------------|-----------------|----------|
143
+ | **Callouts** | `[callout](Text "🔥")` | Highlighting key information |
144
+ | **Toggles** | `+++ Title\nContent\n+++` | Collapsible sections |
145
+ | **Columns** | `::: columns\n::: column\nContent\n:::\n:::` | Side-by-side layouts |
146
+ | **Tables** | Standard markdown tables | Structured data |
147
+ | **Media** | `[video](./file.mp4)(caption:Description)` | Auto-uploading files |
148
+ | **Code** | Standard code fences with captions | Code snippets |
149
+ | **Equations** | `$LaTeX$` | Mathematical expressions |
150
+ | **TOC** | `[toc](blue_background)` | Auto-generated navigation |
151
+
152
+ ---
153
+
154
+ ## What You Can Build 💡
155
+
156
+ ### **AI Content Systems**
157
+ - **Report Generation**: AI agents that create structured reports, documentation, and analysis
158
+ - **Content Pipelines**: Automated workflows that process data and generate Notion pages
159
+ - **Knowledge Management**: AI-powered documentation systems with smart categorization
160
+
161
+ ### **Workflow Automation**
162
+ - **Project Management**: Sync project status, update timelines, generate progress reports
163
+ - **Data Integration**: Connect external APIs and databases to Notion workspaces
164
+ - **Template Systems**: Dynamic page generation from templates and data sources
165
+
166
+ ### **Content Management**
167
+ - **Bulk Operations**: Mass page updates, content migration, and database management
168
+ - **Media Handling**: Automated image/video uploads with proper organization
169
+ - **Cross-Platform**: Sync content between Notion and other platforms
170
+
171
+ ---
172
+
173
+ ## Key Features
174
+
175
+ <table>
176
+ <tr>
177
+ <td width="50%">
178
+
179
+ ### Smart Discovery
180
+ - Find pages/databases by name
181
+ - Fuzzy matching for approximate searches
182
+ - No more hunting for IDs or URLs
183
+
184
+ ### Extended Markdown
185
+ - Rich syntax beyond standard markdown
186
+ - Callouts, toggles, columns, media uploads
187
+ - Schema provided for AI agent integration
188
+
189
+ ### Modern Python
190
+ - Full async/await support
191
+ - Type hints throughout
192
+ - High-performance batch operations
193
+
194
+ </td>
195
+ <td width="50%">
196
+
197
+ ### Round-Trip Editing
198
+ - Read existing content as markdown
199
+ - Edit and modify preserving formatting
200
+ - Write back to Notion seamlessly
201
+
202
+ ### AI-Ready Architecture
203
+ - Schema-driven syntax for LLM prompts
204
+ - Perfect for AI content generation
205
+ - Handles complex nested structures
206
+
207
+ ### Complete Coverage
208
+ - Every Notion block type supported
209
+ - File uploads with automatic handling
210
+ - Database operations and properties
211
+
212
+ </td>
213
+ </tr>
214
+ </table>
215
+
216
+ ---
217
+
218
+ ## Examples & Documentation
219
+
220
+ ### Full Documentation
221
+ [**mathisarends.github.io/notionary**](https://mathisarends.github.io/notionary/) - Complete API reference, guides, and tutorials
222
+
223
+ ### Quick Links
224
+ - [**Getting Started**](https://mathisarends.github.io/notionary/get-started/) - Setup and first steps
225
+ - [**Page Management**](https://mathisarends.github.io/notionary/page/) - Content and properties
226
+ - [**Database Operations**](https://mathisarends.github.io/notionary/database/) - Queries and management
227
+ - [**Block Types Reference**](https://mathisarends.github.io/notionary/blocks/) - Complete syntax guide
228
+
229
+ ### Hands-On Examples
230
+
231
+ **Core Functionality:**
232
+ - [Page Management](examples/page_example.py) - Create, update, and manage pages
233
+ - [Database Operations](examples/database.py) - Connect and query databases
234
+ - [Workspace Discovery](examples/workspace_discovery.py) - Explore your workspace
235
+
236
+ **Extended Markdown:**
237
+ - [Basic Formatting](examples/markdown/basic.py) - Text, lists, and links
238
+ - [Callouts & Highlights](examples/markdown/callout.py) - Information boxes
239
+ - [Toggle Sections](examples/markdown/toggle.py) - Collapsible content
240
+ - [Multi-Column Layouts](examples/markdown/columns.py) - Side-by-side design
241
+ - [Tables & Data](examples/markdown/table.py) - Structured presentations
242
+
243
+ ---
244
+
245
+ ## Contributing
246
+
247
+ We welcome contributions from the community! Whether you're:
248
+ - **Fixing bugs** - Help improve stability and reliability
249
+ - **Adding features** - Extend functionality for new use cases
250
+ - **Improving docs** - Make the library more accessible
251
+ - **Sharing examples** - Show creative applications and patterns
252
+
253
+ Check our [**Contributing Guide**](https://mathisarends.github.io/notionary/contributing/) to get started.
254
+
255
+ ---
256
+
257
+ <div align="center">
258
+
259
+ **Ready to revolutionize your Notion workflows?**
260
+
261
+ [📖 **Read the Docs**](https://mathisarends.github.io/notionary/) • [🚀 **Getting Started**](https://mathisarends.github.io/notionary/get-started/) • [💻 **Browse Examples**](examples/)
262
+
263
+ *Built with ❤️ for Python developers and AI agents*
264
+
265
+ ---
266
+
267
+ **Transform complex Notion API interactions into simple, powerful code.**
268
+
269
+ </div>
270
+