notionary 0.2.19__py3-none-any.whl → 0.2.21__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 (205) 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 +263 -0
  5. notionary/blocks/audio/__init__.py +8 -2
  6. notionary/blocks/audio/audio_element.py +42 -104
  7. notionary/blocks/audio/audio_markdown_node.py +3 -1
  8. notionary/blocks/audio/audio_models.py +6 -55
  9. notionary/blocks/base_block_element.py +30 -0
  10. notionary/blocks/bookmark/__init__.py +9 -2
  11. notionary/blocks/bookmark/bookmark_element.py +46 -139
  12. notionary/blocks/bookmark/bookmark_markdown_node.py +3 -1
  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 +40 -55
  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 +40 -89
  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 +7 -0
  27. notionary/blocks/child_database/child_database_models.py +19 -0
  28. notionary/blocks/child_page/__init__.py +9 -0
  29. notionary/blocks/child_page/child_page_models.py +12 -0
  30. notionary/blocks/{shared/block_client.py → client.py} +55 -54
  31. notionary/blocks/code/__init__.py +6 -2
  32. notionary/blocks/code/code_element.py +53 -187
  33. notionary/blocks/code/code_markdown_node.py +13 -13
  34. notionary/blocks/code/code_models.py +94 -0
  35. notionary/blocks/column/__init__.py +25 -1
  36. notionary/blocks/column/column_element.py +40 -314
  37. notionary/blocks/column/column_list_element.py +37 -0
  38. notionary/blocks/column/column_list_markdown_node.py +50 -0
  39. notionary/blocks/column/column_markdown_node.py +59 -0
  40. notionary/blocks/column/column_models.py +26 -0
  41. notionary/blocks/divider/__init__.py +9 -2
  42. notionary/blocks/divider/divider_element.py +26 -49
  43. notionary/blocks/divider/divider_markdown_node.py +2 -1
  44. notionary/blocks/divider/divider_models.py +12 -0
  45. notionary/blocks/embed/__init__.py +9 -2
  46. notionary/blocks/embed/embed_element.py +47 -114
  47. notionary/blocks/embed/embed_markdown_node.py +3 -1
  48. notionary/blocks/embed/embed_models.py +14 -0
  49. notionary/blocks/equation/__init__.py +14 -0
  50. notionary/blocks/equation/equation_element.py +80 -0
  51. notionary/blocks/equation/equation_element_markdown_node.py +36 -0
  52. notionary/blocks/equation/equation_models.py +11 -0
  53. notionary/blocks/file/__init__.py +25 -0
  54. notionary/blocks/file/file_element.py +93 -0
  55. notionary/blocks/file/file_element_markdown_node.py +35 -0
  56. notionary/blocks/file/file_element_models.py +39 -0
  57. notionary/blocks/heading/__init__.py +16 -2
  58. notionary/blocks/heading/heading_element.py +67 -72
  59. notionary/blocks/heading/heading_markdown_node.py +2 -1
  60. notionary/blocks/heading/heading_models.py +29 -0
  61. notionary/blocks/image_block/__init__.py +13 -0
  62. notionary/blocks/image_block/image_element.py +84 -0
  63. notionary/blocks/{image → image_block}/image_markdown_node.py +3 -1
  64. notionary/blocks/image_block/image_models.py +10 -0
  65. notionary/blocks/models.py +172 -0
  66. notionary/blocks/numbered_list/__init__.py +12 -2
  67. notionary/blocks/numbered_list/numbered_list_element.py +33 -58
  68. notionary/blocks/numbered_list/numbered_list_markdown_node.py +3 -1
  69. notionary/blocks/numbered_list/numbered_list_models.py +17 -0
  70. notionary/blocks/paragraph/__init__.py +12 -2
  71. notionary/blocks/paragraph/paragraph_element.py +27 -69
  72. notionary/blocks/paragraph/paragraph_markdown_node.py +2 -1
  73. notionary/blocks/paragraph/paragraph_models.py +16 -0
  74. notionary/blocks/pdf/__init__.py +13 -0
  75. notionary/blocks/pdf/pdf_element.py +91 -0
  76. notionary/blocks/pdf/pdf_markdown_node.py +35 -0
  77. notionary/blocks/pdf/pdf_models.py +11 -0
  78. notionary/blocks/quote/__init__.py +11 -2
  79. notionary/blocks/quote/quote_element.py +31 -65
  80. notionary/blocks/quote/quote_markdown_node.py +4 -1
  81. notionary/blocks/quote/quote_models.py +18 -0
  82. notionary/blocks/registry/__init__.py +4 -0
  83. notionary/blocks/registry/block_registry.py +75 -91
  84. notionary/blocks/registry/block_registry_builder.py +107 -59
  85. notionary/blocks/rich_text/__init__.py +33 -0
  86. notionary/blocks/rich_text/rich_text_models.py +188 -0
  87. notionary/blocks/rich_text/text_inline_formatter.py +125 -0
  88. notionary/blocks/table/__init__.py +16 -2
  89. notionary/blocks/table/table_element.py +48 -241
  90. notionary/blocks/table/table_markdown_node.py +2 -1
  91. notionary/blocks/table/table_models.py +28 -0
  92. notionary/blocks/table_of_contents/__init__.py +19 -0
  93. notionary/blocks/table_of_contents/table_of_contents_element.py +51 -0
  94. notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +35 -0
  95. notionary/blocks/table_of_contents/table_of_contents_models.py +18 -0
  96. notionary/blocks/todo/__init__.py +9 -2
  97. notionary/blocks/todo/todo_element.py +38 -95
  98. notionary/blocks/todo/todo_markdown_node.py +2 -1
  99. notionary/blocks/todo/todo_models.py +19 -0
  100. notionary/blocks/toggle/__init__.py +13 -3
  101. notionary/blocks/toggle/toggle_element.py +57 -264
  102. notionary/blocks/toggle/toggle_markdown_node.py +24 -14
  103. notionary/blocks/toggle/toggle_models.py +17 -0
  104. notionary/blocks/toggleable_heading/__init__.py +6 -2
  105. notionary/blocks/toggleable_heading/toggleable_heading_element.py +74 -244
  106. notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +26 -18
  107. notionary/blocks/types.py +61 -0
  108. notionary/blocks/video/__init__.py +8 -2
  109. notionary/blocks/video/video_element.py +67 -143
  110. notionary/blocks/video/video_element_models.py +10 -0
  111. notionary/blocks/video/video_markdown_node.py +3 -1
  112. notionary/database/client.py +3 -8
  113. notionary/database/database.py +13 -14
  114. notionary/database/database_filter_builder.py +2 -2
  115. notionary/database/database_provider.py +5 -4
  116. notionary/database/models.py +337 -0
  117. notionary/database/notion_database.py +6 -7
  118. notionary/file_upload/client.py +5 -7
  119. notionary/file_upload/models.py +2 -1
  120. notionary/file_upload/notion_file_upload.py +2 -3
  121. notionary/markdown/markdown_builder.py +722 -0
  122. notionary/markdown/markdown_document_model.py +228 -0
  123. notionary/{blocks → markdown}/markdown_node.py +1 -0
  124. notionary/models/notion_database_response.py +0 -338
  125. notionary/page/client.py +9 -10
  126. notionary/page/models.py +327 -0
  127. notionary/page/notion_page.py +99 -52
  128. notionary/page/notion_text_length_utils.py +119 -0
  129. notionary/page/{content/page_content_writer.py → page_content_writer.py} +88 -38
  130. notionary/page/reader/handler/__init__.py +17 -0
  131. notionary/page/reader/handler/base_block_renderer.py +44 -0
  132. notionary/page/reader/handler/block_processing_context.py +35 -0
  133. notionary/page/reader/handler/block_rendering_context.py +43 -0
  134. notionary/page/reader/handler/column_list_renderer.py +51 -0
  135. notionary/page/reader/handler/column_renderer.py +60 -0
  136. notionary/page/reader/handler/line_renderer.py +60 -0
  137. notionary/page/reader/handler/toggle_renderer.py +69 -0
  138. notionary/page/reader/handler/toggleable_heading_renderer.py +89 -0
  139. notionary/page/reader/page_content_retriever.py +69 -0
  140. notionary/page/search_filter_builder.py +2 -1
  141. notionary/page/writer/handler/__init__.py +22 -0
  142. notionary/page/writer/handler/code_handler.py +100 -0
  143. notionary/page/writer/handler/column_handler.py +141 -0
  144. notionary/page/writer/handler/column_list_handler.py +139 -0
  145. notionary/page/writer/handler/line_handler.py +35 -0
  146. notionary/page/writer/handler/line_processing_context.py +54 -0
  147. notionary/page/writer/handler/regular_line_handler.py +92 -0
  148. notionary/page/writer/handler/table_handler.py +130 -0
  149. notionary/page/writer/handler/toggle_handler.py +153 -0
  150. notionary/page/writer/handler/toggleable_heading_handler.py +167 -0
  151. notionary/page/writer/markdown_to_notion_converter.py +76 -0
  152. notionary/telemetry/__init__.py +2 -2
  153. notionary/telemetry/service.py +4 -3
  154. notionary/user/__init__.py +2 -2
  155. notionary/user/base_notion_user.py +2 -1
  156. notionary/user/client.py +2 -3
  157. notionary/user/models.py +1 -0
  158. notionary/user/notion_bot_user.py +4 -5
  159. notionary/user/notion_user.py +3 -4
  160. notionary/user/notion_user_manager.py +3 -2
  161. notionary/user/notion_user_provider.py +1 -1
  162. notionary/util/__init__.py +3 -2
  163. notionary/util/fuzzy.py +2 -1
  164. notionary/util/logging_mixin.py +2 -2
  165. notionary/util/singleton_metaclass.py +1 -1
  166. notionary/workspace.py +3 -2
  167. {notionary-0.2.19.dist-info → notionary-0.2.21.dist-info}/METADATA +12 -8
  168. notionary-0.2.21.dist-info/RECORD +185 -0
  169. notionary/blocks/document/__init__.py +0 -7
  170. notionary/blocks/document/document_element.py +0 -102
  171. notionary/blocks/document/document_markdown_node.py +0 -31
  172. notionary/blocks/image/__init__.py +0 -7
  173. notionary/blocks/image/image_element.py +0 -151
  174. notionary/blocks/markdown_builder.py +0 -356
  175. notionary/blocks/mention/__init__.py +0 -7
  176. notionary/blocks/mention/mention_element.py +0 -229
  177. notionary/blocks/mention/mention_markdown_node.py +0 -38
  178. notionary/blocks/prompts/element_prompt_builder.py +0 -83
  179. notionary/blocks/prompts/element_prompt_content.py +0 -41
  180. notionary/blocks/shared/__init__.py +0 -0
  181. notionary/blocks/shared/models.py +0 -713
  182. notionary/blocks/shared/notion_block_element.py +0 -37
  183. notionary/blocks/shared/text_inline_formatter.py +0 -262
  184. notionary/blocks/shared/text_inline_formatter_new.py +0 -139
  185. notionary/blocks/toggleable_heading/toggleable_heading_models.py +0 -0
  186. notionary/database/models/page_result.py +0 -10
  187. notionary/elements/__init__.py +0 -0
  188. notionary/models/notion_block_response.py +0 -264
  189. notionary/models/notion_page_response.py +0 -78
  190. notionary/models/search_response.py +0 -0
  191. notionary/page/__init__.py +0 -0
  192. notionary/page/content/notion_text_length_utils.py +0 -87
  193. notionary/page/content/page_content_retriever.py +0 -60
  194. notionary/page/formatting/line_processor.py +0 -153
  195. notionary/page/formatting/markdown_to_notion_converter.py +0 -153
  196. notionary/page/markdown_syntax_prompt_generator.py +0 -114
  197. notionary/page/notion_to_markdown_converter.py +0 -179
  198. notionary/page/properites/property_value_extractor.py +0 -0
  199. notionary-0.2.19.dist-info/RECORD +0 -150
  200. /notionary/{blocks/document/document_models.py → markdown/___init__.py} +0 -0
  201. /notionary/{blocks/image/image_models.py → markdown/makdown_document_model.py} +0 -0
  202. /notionary/page/{content/markdown_whitespace_processor.py → markdown_whitespace_processor.py} +0 -0
  203. /notionary/{blocks/mention/mention_models.py → page/reader/handler/context.py} +0 -0
  204. {notionary-0.2.19.dist-info → notionary-0.2.21.dist-info}/LICENSE +0 -0
  205. {notionary-0.2.19.dist-info → notionary-0.2.21.dist-info}/WHEEL +0 -0
@@ -1,117 +1,74 @@
1
+ from __future__ import annotations
2
+
1
3
  import re
4
+ from typing import Optional
2
5
 
3
- from typing import Optional, Any
4
- from notionary.blocks import NotionBlockElement
5
- from notionary.blocks import (
6
- ElementPromptContent,
7
- ElementPromptBuilder,
8
- NotionBlockResult,
9
- )
10
- from notionary.blocks.shared.models import RichTextObject
6
+ from notionary.blocks.base_block_element import BaseBlockElement
7
+ from notionary.blocks.code.code_models import CodeBlock, CodeLanguage, CreateCodeBlock
8
+ from notionary.blocks.models import Block, BlockCreateResult, BlockType
9
+ from notionary.blocks.rich_text.rich_text_models import RichTextObject
11
10
 
12
11
 
13
- class CodeElement(NotionBlockElement):
12
+ class CodeElement(BaseBlockElement):
14
13
  """
15
14
  Handles conversion between Markdown code blocks and Notion code blocks.
15
+ Now integrated into the LineProcessor stack system.
16
16
 
17
17
  Markdown code block syntax:
18
18
  ```language
19
- code content
19
+ [code content as child lines]
20
20
  ```
21
- Caption: optional caption text
22
-
23
- Where:
24
- - language is optional and specifies the programming language
25
- - code content is the code to be displayed
26
- - Caption line is optional and must appear immediately after the closing ```
27
21
  """
28
22
 
29
- PATTERN = re.compile(
30
- r"```(\w*)\n([\s\S]+?)```(?:\n(?:Caption|caption):\s*(.+))?", re.MULTILINE
31
- )
23
+ DEFAULT_LANGUAGE = "plain text"
24
+ CODE_START_PATTERN = re.compile(r"^```(\w*)\s*$")
32
25
 
33
26
  @classmethod
34
- def match_markdown(cls, text: str) -> bool:
35
- """Check if text contains a markdown code block."""
36
- return bool(cls.PATTERN.search(text))
37
-
38
- @classmethod
39
- def match_notion(cls, block: dict[str, any]) -> bool:
27
+ def match_notion(cls, block: Block) -> bool:
40
28
  """Check if block is a Notion code block."""
41
- return block.get("type") == "code"
29
+ return block.type == BlockType.CODE and block.code
42
30
 
43
31
  @classmethod
44
- def markdown_to_notion(cls, text: str) -> NotionBlockResult:
45
- """Convert markdown code block to Notion code block."""
46
- match = cls.PATTERN.search(text)
47
- if not match:
32
+ def markdown_to_notion(cls, text: str) -> BlockCreateResult:
33
+ """Convert opening ```language to Notion code block."""
34
+ if not (match := cls.CODE_START_PATTERN.match(text.strip())):
48
35
  return None
49
36
 
50
- language = match.group(1) or "plain text"
51
- content = match.group(2)
52
- caption = match.group(3)
53
-
54
- if content.endswith("\n"):
55
- content = content[:-1]
37
+ language = (match.group(1) or cls.DEFAULT_LANGUAGE).lower()
38
+ language = cls._normalize_language(language)
56
39
 
57
- # Create code block with rich text
58
- content_rich_text = RichTextObject.from_plain_text(content)
59
-
60
- block = {
61
- "type": "code",
62
- "code": {
63
- "rich_text": [content_rich_text.model_dump()],
64
- "language": language,
65
- },
66
- }
67
-
68
- # Add caption if provided
69
- if caption and caption.strip():
70
- caption_rich_text = RichTextObject.from_plain_text(caption.strip())
71
- block["code"]["caption"] = [caption_rich_text.model_dump()]
72
-
73
- # Leerer Paragraph nach dem Code-Block
74
- empty_paragraph = {"type": "paragraph", "paragraph": {"rich_text": []}}
75
-
76
- return [block, empty_paragraph]
40
+ # Create empty CodeBlock - content will be added by stack processor
41
+ code_block = CodeBlock(rich_text=[], language=language, caption=[])
42
+ return CreateCodeBlock(code=code_block)
77
43
 
78
44
  @classmethod
79
- def notion_to_markdown(cls, block: dict[str, Any]) -> Optional[str]:
45
+ def notion_to_markdown(cls, block: Block) -> Optional[str]:
80
46
  """Convert Notion code block to Markdown."""
81
- if block.get("type") != "code":
47
+ if block.type != BlockType.CODE:
48
+ return None
49
+
50
+ if not block.code:
82
51
  return None
83
52
 
84
- code_data = block.get("code", {})
85
- language = code_data.get("language", "")
86
- rich_text = code_data.get("rich_text", [])
87
- caption = code_data.get("caption", [])
88
-
89
- def extract_content(rich_text_list):
90
- """Extract code content from rich_text array."""
91
- return "".join(
92
- text.get("text", {}).get("content", "")
93
- if text.get("type") == "text"
94
- else text.get("plain_text", "")
95
- for text in rich_text_list
96
- )
97
-
98
- def extract_caption(caption_list):
99
- """Extract caption text from caption array."""
100
- return "".join(
101
- c.get("text", {}).get("content", "")
102
- for c in caption_list
103
- if c.get("type") == "text"
104
- )
105
-
106
- code_content = extract_content(rich_text)
107
- caption_text = extract_caption(caption)
53
+ language_enum = block.code.language
54
+ rich_text = block.code.rich_text or []
55
+ caption = block.code.caption or []
56
+
57
+ code_content = cls.extract_content(rich_text)
58
+ caption_text = cls.extract_caption(caption)
59
+
60
+ # Convert enum to string value
61
+ language = language_enum.value if language_enum else ""
108
62
 
109
63
  # Handle language - convert "plain text" back to empty string for markdown
110
- if language == "plain text":
64
+ if language == cls.DEFAULT_LANGUAGE:
111
65
  language = ""
112
66
 
113
67
  # Build markdown code block
114
- result = f"```{language}\n{code_content}\n```" if language else f"```\n{code_content}\n```"
68
+ if language:
69
+ result = f"```{language}\n{code_content}\n```"
70
+ else:
71
+ result = f"```\n{code_content}\n```"
115
72
 
116
73
  # Add caption if present
117
74
  if caption_text:
@@ -120,115 +77,24 @@ class CodeElement(NotionBlockElement):
120
77
  return result
121
78
 
122
79
  @classmethod
123
- def find_matches(cls, text: str) -> list[tuple[int, int, dict[str, any]]]:
80
+ def _normalize_language(cls, language: str) -> CodeLanguage:
124
81
  """
125
- Find all code block matches in the text and return their positions.
126
-
127
- Args:
128
- text: The text to search in
129
-
130
- Returns:
131
- List of tuples with (start_pos, end_pos, block)
82
+ Normalize the language string to a valid CodeLanguage enum or default.
132
83
  """
133
- matches = []
134
- for match in CodeElement.PATTERN.finditer(text):
135
- language = match.group(1) or "plain text"
136
- content = match.group(2)
137
- caption = match.group(3)
138
-
139
- # Remove trailing newline if present
140
- if content.endswith("\n"):
141
- content = content[:-1]
142
-
143
- block = {
144
- "type": "code",
145
- "code": {
146
- "rich_text": [
147
- {
148
- "type": "text",
149
- "text": {"content": content},
150
- "plain_text": content,
151
- }
152
- ],
153
- "language": language,
154
- },
155
- }
156
-
157
- # Add caption if provided
158
- if caption and caption.strip():
159
- block["code"]["caption"] = [
160
- {
161
- "type": "text",
162
- "text": {"content": caption.strip()},
163
- "plain_text": caption.strip(),
164
- }
165
- ]
166
-
167
- matches.append((match.start(), match.end(), block))
168
-
169
- return matches
84
+ # Try to find matching enum by value
85
+ for lang_enum in CodeLanguage:
86
+ if lang_enum.value.lower() == language.lower():
87
+ return lang_enum
170
88
 
171
- @classmethod
172
- def is_multiline(cls) -> bool:
173
- return True
174
-
175
- @classmethod
176
- def get_llm_prompt_content(cls) -> ElementPromptContent:
177
- """
178
- Returns structured LLM prompt metadata for the code block element.
179
- """
180
- return (
181
- ElementPromptBuilder()
182
- .with_description(
183
- "Use fenced code blocks to format content as code. Supports language annotations like "
184
- "'python', 'json', or 'mermaid'. Useful for displaying code, configurations, command-line "
185
- "examples, or diagram syntax. Also suitable for explaining or visualizing systems with diagram languages. "
186
- "Code blocks can include optional captions for better documentation."
187
- )
188
- .with_usage_guidelines(
189
- "Use code blocks when you want to present technical content like code snippets, terminal commands, "
190
- "JSON structures, or system diagrams. Especially helpful when structure and formatting are essential. "
191
- "Add captions to provide context, explanations, or titles for your code blocks."
192
- )
193
- .with_syntax(
194
- "```language\ncode content\n```\nCaption: optional caption text\n\n"
195
- "OR\n\n"
196
- "```language\ncode content\n```"
197
- )
198
- .with_examples(
199
- [
200
- "```python\nprint('Hello, world!')\n```\nCaption: Basic Python greeting example",
201
- '```json\n{"name": "Alice", "age": 30}\n```\nCaption: User data structure',
202
- "```mermaid\nflowchart TD\n A --> B\n```\nCaption: Simple flow diagram",
203
- '```bash\ngit commit -m "Initial commit"\n```',
204
- ]
205
- )
206
- .with_avoidance_guidelines(
207
- "NEVER EVER wrap markdown content with ```markdown. Markdown should be written directly without code block formatting. "
208
- "NEVER use ```markdown under any circumstances. "
209
- "For Mermaid diagrams, use ONLY the default styling without colors, backgrounds, or custom styling attributes. "
210
- "Keep Mermaid diagrams simple and minimal without any styling or color modifications. "
211
- "Captions must appear immediately after the closing ``` on a new line starting with 'Caption:' - "
212
- "no empty lines between the code block and the caption."
213
- )
214
- .build()
215
- )
89
+ # Return default if not found
90
+ return CodeLanguage.PLAIN_TEXT
216
91
 
217
92
  @staticmethod
218
- def extract_content(rich_text_list: list[dict[str, Any]]) -> str:
93
+ def extract_content(rich_text_list: list[RichTextObject]) -> str:
219
94
  """Extract code content from rich_text array."""
220
- return "".join(
221
- text.get("text", {}).get("content", "")
222
- if text.get("type") == "text"
223
- else text.get("plain_text", "")
224
- for text in rich_text_list
225
- )
95
+ return "".join(rt.plain_text for rt in rich_text_list if rt.plain_text)
226
96
 
227
97
  @staticmethod
228
- def extract_caption(caption_list: list[dict[str, Any]]) -> str:
98
+ def extract_caption(caption_list: list[RichTextObject]) -> str:
229
99
  """Extract caption text from caption array."""
230
- return "".join(
231
- c.get("text", {}).get("content", "")
232
- for c in caption_list
233
- if c.get("type") == "text"
234
- )
100
+ return "".join(rt.plain_text for rt in caption_list if rt.plain_text)
@@ -1,24 +1,18 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from typing import Optional
4
- from pydantic import BaseModel
5
- from notionary.blocks.markdown_node import MarkdownNode
6
4
 
7
-
8
- class CodeMarkdownBlockParams(BaseModel):
9
- code: str
10
- language: Optional[str] = None
11
- caption: Optional[str] = None
5
+ from notionary.blocks.code.code_models import CodeBlock
6
+ from notionary.markdown.markdown_node import MarkdownNode
12
7
 
13
8
 
14
9
  class CodeMarkdownNode(MarkdownNode):
15
10
  """
16
11
  Programmatic interface for creating Notion-style Markdown code blocks.
17
12
  Example:
18
- ```python
13
+ ```python "Basic usage"
19
14
  print("Hello, world!")
20
15
  ```
21
- Caption: Basic usage
22
16
  """
23
17
 
24
18
  def __init__(
@@ -32,12 +26,18 @@ class CodeMarkdownNode(MarkdownNode):
32
26
  self.caption = caption
33
27
 
34
28
  @classmethod
35
- def from_params(cls, params: CodeMarkdownBlockParams) -> CodeMarkdownNode:
36
- return cls(code=params.code, language=params.language, caption=params.caption)
29
+ def from_params(cls, params: CodeBlock) -> CodeMarkdownNode:
30
+ return cls(
31
+ code=params.rich_text, language=params.language, caption=params.caption
32
+ )
37
33
 
38
34
  def to_markdown(self) -> str:
39
35
  lang = self.language or ""
40
- content = f"```{lang}\n{self.code}\n```"
36
+
37
+ # Build the opening fence with optional caption
38
+ opening_fence = f"```{lang}"
41
39
  if self.caption:
42
- content += f"\nCaption: {self.caption}"
40
+ opening_fence += f' "{self.caption}"'
41
+
42
+ content = f"{opening_fence}\n{self.code}\n```"
43
43
  return content
@@ -0,0 +1,94 @@
1
+ from enum import Enum
2
+ from typing import Literal
3
+
4
+ from pydantic import BaseModel, ConfigDict, Field
5
+
6
+ from notionary.blocks.rich_text.rich_text_models import RichTextObject
7
+
8
+
9
+ class CodeLanguage(str, Enum):
10
+ ABAP = "abap"
11
+ ARDUINO = "arduino"
12
+ BASH = "bash"
13
+ BASIC = "basic"
14
+ C = "c"
15
+ CLOJURE = "clojure"
16
+ COFFEESCRIPT = "coffeescript"
17
+ CPP = "c++"
18
+ CSHARP = "c#"
19
+ CSS = "css"
20
+ DART = "dart"
21
+ DIFF = "diff"
22
+ DOCKER = "docker"
23
+ ELIXIR = "elixir"
24
+ ELM = "elm"
25
+ ERLANG = "erlang"
26
+ FLOW = "flow"
27
+ FORTRAN = "fortran"
28
+ FSHARP = "f#"
29
+ GHERKIN = "gherkin"
30
+ GLSL = "glsl"
31
+ GO = "go"
32
+ GRAPHQL = "graphql"
33
+ GROOVY = "groovy"
34
+ HASKELL = "haskell"
35
+ HTML = "html"
36
+ JAVA = "java"
37
+ JAVASCRIPT = "javascript"
38
+ JSON = "json"
39
+ JULIA = "julia"
40
+ KOTLIN = "kotlin"
41
+ LATEX = "latex"
42
+ LESS = "less"
43
+ LISP = "lisp"
44
+ LIVESCRIPT = "livescript"
45
+ LUA = "lua"
46
+ MAKEFILE = "makefile"
47
+ MARKDOWN = "markdown"
48
+ MARKUP = "markup"
49
+ MATLAB = "matlab"
50
+ MERMAID = "mermaid"
51
+ NIX = "nix"
52
+ OBJECTIVE_C = "objective-c"
53
+ OCAML = "ocaml"
54
+ PASCAL = "pascal"
55
+ PERL = "perl"
56
+ PHP = "php"
57
+ PLAIN_TEXT = "plain text"
58
+ POWERSHELL = "powershell"
59
+ PROLOG = "prolog"
60
+ PROTOBUF = "protobuf"
61
+ PYTHON = "python"
62
+ R = "r"
63
+ REASON = "reason"
64
+ RUBY = "ruby"
65
+ RUST = "rust"
66
+ SASS = "sass"
67
+ SCALA = "scala"
68
+ SCHEME = "scheme"
69
+ SCSS = "scss"
70
+ SHELL = "shell"
71
+ SQL = "sql"
72
+ SWIFT = "swift"
73
+ TYPESCRIPT = "typescript"
74
+ VB_NET = "vb.net"
75
+ VERILOG = "verilog"
76
+ VHDL = "vhdl"
77
+ VISUAL_BASIC = "visual basic"
78
+ WEBASSEMBLY = "webassembly"
79
+ XML = "xml"
80
+ YAML = "yaml"
81
+ JAVA_C_CPP_CSHARP = "java/c/c++/c#"
82
+
83
+
84
+ class CodeBlock(BaseModel):
85
+ caption: list[RichTextObject] = Field(default_factory=list)
86
+ rich_text: list[RichTextObject]
87
+ language: CodeLanguage = CodeLanguage.PLAIN_TEXT
88
+
89
+ model_config = ConfigDict(arbitrary_types_allowed=True)
90
+
91
+
92
+ class CreateCodeBlock(BaseModel):
93
+ type: Literal["code"] = "code"
94
+ code: CodeBlock
@@ -1,5 +1,29 @@
1
- from .column_element import ColumnElement
1
+ from notionary.blocks.column.column_element import ColumnElement
2
+ from notionary.blocks.column.column_list_element import ColumnListElement
3
+ from notionary.blocks.column.column_list_markdown_node import (
4
+ ColumnListMarkdownBlockParams,
5
+ ColumnListMarkdownNode,
6
+ )
7
+ from notionary.blocks.column.column_markdown_node import (
8
+ ColumnMarkdownBlockParams,
9
+ ColumnMarkdownNode,
10
+ )
11
+ from notionary.blocks.column.column_models import (
12
+ ColumnBlock,
13
+ ColumnListBlock,
14
+ CreateColumnBlock,
15
+ CreateColumnListBlock,
16
+ )
2
17
 
3
18
  __all__ = [
4
19
  "ColumnElement",
20
+ "ColumnListElement",
21
+ "ColumnBlock",
22
+ "CreateColumnBlock",
23
+ "ColumnListBlock",
24
+ "CreateColumnListBlock",
25
+ "ColumnMarkdownNode",
26
+ "ColumnMarkdownBlockParams",
27
+ "ColumnListMarkdownNode",
28
+ "ColumnListMarkdownBlockParams",
5
29
  ]