notionary 0.2.18__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.
- notionary/__init__.py +8 -4
- notionary/base_notion_client.py +3 -1
- notionary/blocks/__init__.py +2 -91
- notionary/blocks/_bootstrap.py +263 -0
- notionary/blocks/audio/__init__.py +8 -2
- notionary/blocks/audio/audio_element.py +42 -104
- notionary/blocks/audio/audio_markdown_node.py +3 -1
- notionary/blocks/audio/audio_models.py +6 -55
- notionary/blocks/base_block_element.py +30 -0
- notionary/blocks/bookmark/__init__.py +9 -2
- notionary/blocks/bookmark/bookmark_element.py +46 -139
- notionary/blocks/bookmark/bookmark_markdown_node.py +3 -1
- notionary/blocks/bookmark/bookmark_models.py +15 -0
- notionary/blocks/breadcrumbs/__init__.py +17 -0
- notionary/blocks/breadcrumbs/breadcrumb_element.py +39 -0
- notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +32 -0
- notionary/blocks/breadcrumbs/breadcrumb_models.py +12 -0
- notionary/blocks/bulleted_list/__init__.py +12 -2
- notionary/blocks/bulleted_list/bulleted_list_element.py +40 -55
- notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +2 -1
- notionary/blocks/bulleted_list/bulleted_list_models.py +18 -0
- notionary/blocks/callout/__init__.py +9 -2
- notionary/blocks/callout/callout_element.py +40 -89
- notionary/blocks/callout/callout_markdown_node.py +3 -1
- notionary/blocks/callout/callout_models.py +33 -0
- notionary/blocks/child_database/__init__.py +7 -0
- notionary/blocks/child_database/child_database_models.py +19 -0
- notionary/blocks/child_page/__init__.py +9 -0
- notionary/blocks/child_page/child_page_models.py +12 -0
- notionary/blocks/{shared/block_client.py → client.py} +55 -54
- notionary/blocks/code/__init__.py +6 -2
- notionary/blocks/code/code_element.py +53 -187
- notionary/blocks/code/code_markdown_node.py +13 -13
- notionary/blocks/code/code_models.py +94 -0
- notionary/blocks/column/__init__.py +25 -1
- notionary/blocks/column/column_element.py +40 -314
- notionary/blocks/column/column_list_element.py +37 -0
- notionary/blocks/column/column_list_markdown_node.py +50 -0
- notionary/blocks/column/column_markdown_node.py +59 -0
- notionary/blocks/column/column_models.py +26 -0
- notionary/blocks/divider/__init__.py +9 -2
- notionary/blocks/divider/divider_element.py +26 -49
- notionary/blocks/divider/divider_markdown_node.py +2 -1
- notionary/blocks/divider/divider_models.py +12 -0
- notionary/blocks/embed/__init__.py +9 -2
- notionary/blocks/embed/embed_element.py +47 -114
- notionary/blocks/embed/embed_markdown_node.py +3 -1
- notionary/blocks/embed/embed_models.py +14 -0
- notionary/blocks/equation/__init__.py +14 -0
- notionary/blocks/equation/equation_element.py +80 -0
- notionary/blocks/equation/equation_element_markdown_node.py +36 -0
- notionary/blocks/equation/equation_models.py +11 -0
- notionary/blocks/file/__init__.py +25 -0
- notionary/blocks/file/file_element.py +93 -0
- notionary/blocks/file/file_element_markdown_node.py +35 -0
- notionary/blocks/file/file_element_models.py +39 -0
- notionary/blocks/heading/__init__.py +16 -2
- notionary/blocks/heading/heading_element.py +67 -72
- notionary/blocks/heading/heading_markdown_node.py +2 -1
- notionary/blocks/heading/heading_models.py +29 -0
- notionary/blocks/image_block/__init__.py +13 -0
- notionary/blocks/image_block/image_element.py +84 -0
- notionary/blocks/{image → image_block}/image_markdown_node.py +3 -1
- notionary/blocks/image_block/image_models.py +10 -0
- notionary/blocks/models.py +172 -0
- notionary/blocks/numbered_list/__init__.py +12 -2
- notionary/blocks/numbered_list/numbered_list_element.py +33 -58
- notionary/blocks/numbered_list/numbered_list_markdown_node.py +3 -1
- notionary/blocks/numbered_list/numbered_list_models.py +17 -0
- notionary/blocks/paragraph/__init__.py +12 -2
- notionary/blocks/paragraph/paragraph_element.py +27 -69
- notionary/blocks/paragraph/paragraph_markdown_node.py +2 -1
- notionary/blocks/paragraph/paragraph_models.py +16 -0
- notionary/blocks/pdf/__init__.py +13 -0
- notionary/blocks/pdf/pdf_element.py +91 -0
- notionary/blocks/pdf/pdf_markdown_node.py +35 -0
- notionary/blocks/pdf/pdf_models.py +11 -0
- notionary/blocks/quote/__init__.py +11 -2
- notionary/blocks/quote/quote_element.py +31 -65
- notionary/blocks/quote/quote_markdown_node.py +4 -1
- notionary/blocks/quote/quote_models.py +18 -0
- notionary/blocks/registry/__init__.py +4 -0
- notionary/blocks/registry/block_registry.py +75 -91
- notionary/blocks/registry/block_registry_builder.py +107 -59
- notionary/blocks/rich_text/__init__.py +33 -0
- notionary/blocks/rich_text/rich_text_models.py +188 -0
- notionary/blocks/rich_text/text_inline_formatter.py +125 -0
- notionary/blocks/table/__init__.py +16 -2
- notionary/blocks/table/table_element.py +48 -241
- notionary/blocks/table/table_markdown_node.py +2 -1
- notionary/blocks/table/table_models.py +28 -0
- notionary/blocks/table_of_contents/__init__.py +19 -0
- notionary/blocks/table_of_contents/table_of_contents_element.py +51 -0
- notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +35 -0
- notionary/blocks/table_of_contents/table_of_contents_models.py +18 -0
- notionary/blocks/todo/__init__.py +9 -2
- notionary/blocks/todo/todo_element.py +38 -95
- notionary/blocks/todo/todo_markdown_node.py +2 -1
- notionary/blocks/todo/todo_models.py +19 -0
- notionary/blocks/toggle/__init__.py +13 -3
- notionary/blocks/toggle/toggle_element.py +57 -264
- notionary/blocks/toggle/toggle_markdown_node.py +24 -14
- notionary/blocks/toggle/toggle_models.py +17 -0
- notionary/blocks/toggleable_heading/__init__.py +6 -2
- notionary/blocks/toggleable_heading/toggleable_heading_element.py +74 -244
- notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +26 -18
- notionary/blocks/types.py +61 -0
- notionary/blocks/video/__init__.py +8 -2
- notionary/blocks/video/video_element.py +67 -143
- notionary/blocks/video/video_element_models.py +10 -0
- notionary/blocks/video/video_markdown_node.py +3 -1
- notionary/database/client.py +3 -8
- notionary/database/database.py +13 -14
- notionary/database/database_filter_builder.py +2 -2
- notionary/database/database_provider.py +5 -4
- notionary/database/models.py +337 -0
- notionary/database/notion_database.py +6 -7
- notionary/file_upload/client.py +5 -7
- notionary/file_upload/models.py +2 -1
- notionary/file_upload/notion_file_upload.py +2 -3
- notionary/markdown/markdown_builder.py +722 -0
- notionary/markdown/markdown_document_model.py +228 -0
- notionary/{blocks → markdown}/markdown_node.py +1 -0
- notionary/models/notion_database_response.py +0 -338
- notionary/page/client.py +9 -10
- notionary/page/models.py +327 -0
- notionary/page/notion_page.py +99 -52
- notionary/page/notion_text_length_utils.py +119 -0
- notionary/page/{content/page_content_writer.py → page_content_writer.py} +88 -38
- notionary/page/reader/handler/__init__.py +17 -0
- notionary/page/reader/handler/base_block_renderer.py +44 -0
- notionary/page/reader/handler/block_processing_context.py +35 -0
- notionary/page/reader/handler/block_rendering_context.py +43 -0
- notionary/page/reader/handler/column_list_renderer.py +51 -0
- notionary/page/reader/handler/column_renderer.py +60 -0
- notionary/page/reader/handler/line_renderer.py +60 -0
- notionary/page/reader/handler/toggle_renderer.py +69 -0
- notionary/page/reader/handler/toggleable_heading_renderer.py +89 -0
- notionary/page/reader/page_content_retriever.py +69 -0
- notionary/page/search_filter_builder.py +2 -1
- notionary/page/writer/handler/__init__.py +22 -0
- notionary/page/writer/handler/code_handler.py +100 -0
- notionary/page/writer/handler/column_handler.py +141 -0
- notionary/page/writer/handler/column_list_handler.py +139 -0
- notionary/page/writer/handler/line_handler.py +35 -0
- notionary/page/writer/handler/line_processing_context.py +54 -0
- notionary/page/writer/handler/regular_line_handler.py +92 -0
- notionary/page/writer/handler/table_handler.py +130 -0
- notionary/page/writer/handler/toggle_handler.py +153 -0
- notionary/page/writer/handler/toggleable_heading_handler.py +167 -0
- notionary/page/writer/markdown_to_notion_converter.py +76 -0
- notionary/telemetry/__init__.py +2 -2
- notionary/telemetry/service.py +4 -3
- notionary/user/__init__.py +2 -2
- notionary/user/base_notion_user.py +2 -1
- notionary/user/client.py +2 -3
- notionary/user/models.py +1 -0
- notionary/user/notion_bot_user.py +4 -5
- notionary/user/notion_user.py +3 -4
- notionary/user/notion_user_manager.py +3 -2
- notionary/user/notion_user_provider.py +1 -1
- notionary/util/__init__.py +3 -2
- notionary/util/fuzzy.py +2 -1
- notionary/util/logging_mixin.py +2 -2
- notionary/util/singleton_metaclass.py +1 -1
- notionary/workspace.py +3 -2
- {notionary-0.2.18.dist-info → notionary-0.2.21.dist-info}/METADATA +12 -8
- notionary-0.2.21.dist-info/RECORD +185 -0
- notionary/blocks/document/__init__.py +0 -7
- notionary/blocks/document/document_element.py +0 -102
- notionary/blocks/document/document_markdown_node.py +0 -31
- notionary/blocks/image/__init__.py +0 -7
- notionary/blocks/image/image_element.py +0 -151
- notionary/blocks/markdown_builder.py +0 -356
- notionary/blocks/mention/__init__.py +0 -7
- notionary/blocks/mention/mention_element.py +0 -229
- notionary/blocks/mention/mention_markdown_node.py +0 -38
- notionary/blocks/prompts/element_prompt_builder.py +0 -83
- notionary/blocks/prompts/element_prompt_content.py +0 -41
- notionary/blocks/shared/__init__.py +0 -0
- notionary/blocks/shared/models.py +0 -710
- notionary/blocks/shared/notion_block_element.py +0 -37
- notionary/blocks/shared/text_inline_formatter.py +0 -262
- notionary/blocks/shared/text_inline_formatter_new.py +0 -139
- notionary/blocks/toggleable_heading/toggleable_heading_models.py +0 -0
- notionary/database/models/page_result.py +0 -10
- notionary/models/notion_block_response.py +0 -264
- notionary/models/notion_page_response.py +0 -78
- notionary/models/search_response.py +0 -0
- notionary/page/__init__.py +0 -0
- notionary/page/content/notion_text_length_utils.py +0 -87
- notionary/page/content/page_content_retriever.py +0 -52
- notionary/page/formatting/line_processor.py +0 -153
- notionary/page/formatting/markdown_to_notion_converter.py +0 -153
- notionary/page/markdown_syntax_prompt_generator.py +0 -114
- notionary/page/notion_to_markdown_converter.py +0 -179
- notionary/page/properites/property_value_extractor.py +0 -0
- notionary-0.2.18.dist-info/RECORD +0 -149
- /notionary/{blocks/document/document_models.py → markdown/___init__.py} +0 -0
- /notionary/{blocks/image/image_models.py → markdown/makdown_document_model.py} +0 -0
- /notionary/page/{content/markdown_whitespace_processor.py → markdown_whitespace_processor.py} +0 -0
- /notionary/{blocks/mention/mention_models.py → page/reader/handler/context.py} +0 -0
- {notionary-0.2.18.dist-info → notionary-0.2.21.dist-info}/LICENSE +0 -0
- {notionary-0.2.18.dist-info → notionary-0.2.21.dist-info}/WHEEL +0 -0
@@ -1,151 +0,0 @@
|
|
1
|
-
import re
|
2
|
-
from typing import Dict, Any, Optional, List
|
3
|
-
|
4
|
-
from notionary.blocks import NotionBlockElement
|
5
|
-
from notionary.blocks import (
|
6
|
-
ElementPromptContent,
|
7
|
-
ElementPromptBuilder,
|
8
|
-
NotionBlockResult,
|
9
|
-
)
|
10
|
-
|
11
|
-
|
12
|
-
class ImageElement(NotionBlockElement):
|
13
|
-
"""
|
14
|
-
Handles conversion between Markdown images and Notion image blocks.
|
15
|
-
|
16
|
-
Markdown image syntax:
|
17
|
-
- [image](https://example.com/image.jpg) - Simple image with URL only
|
18
|
-
- [image](https://example.com/image.jpg "Caption") - Image with URL and caption
|
19
|
-
|
20
|
-
Where:
|
21
|
-
- URL is the required image URL
|
22
|
-
- Caption is an optional descriptive text (enclosed in quotes)
|
23
|
-
"""
|
24
|
-
|
25
|
-
# Regex pattern for image syntax with optional caption
|
26
|
-
PATTERN = re.compile(
|
27
|
-
r"^\[image\]\(" # [image]( prefix
|
28
|
-
+ r'(https?://[^\s"]+)' # URL (required)
|
29
|
-
+ r'(?:\s+"([^"]+)")?' # Optional caption in quotes
|
30
|
-
+ r"\)$" # closing parenthesis
|
31
|
-
)
|
32
|
-
|
33
|
-
@classmethod
|
34
|
-
def match_markdown(cls, text: str) -> bool:
|
35
|
-
"""Check if text is a markdown image."""
|
36
|
-
return text.strip().startswith("[image]") and bool(
|
37
|
-
ImageElement.PATTERN.match(text.strip())
|
38
|
-
)
|
39
|
-
|
40
|
-
@classmethod
|
41
|
-
def match_notion(cls, block: Dict[str, Any]) -> bool:
|
42
|
-
"""Check if block is a Notion image."""
|
43
|
-
return block.get("type") == "image"
|
44
|
-
|
45
|
-
@classmethod
|
46
|
-
def markdown_to_notion(cls, text: str) -> NotionBlockResult:
|
47
|
-
"""Convert markdown image to Notion image block."""
|
48
|
-
image_match = ImageElement.PATTERN.match(text.strip())
|
49
|
-
if not image_match:
|
50
|
-
return None
|
51
|
-
|
52
|
-
url = image_match.group(1)
|
53
|
-
caption = image_match.group(2)
|
54
|
-
|
55
|
-
if not url:
|
56
|
-
return None
|
57
|
-
|
58
|
-
image_data = {"type": "external", "external": {"url": url}}
|
59
|
-
|
60
|
-
# Add caption if provided
|
61
|
-
if caption:
|
62
|
-
image_data["caption"] = [{"type": "text", "text": {"content": caption}}]
|
63
|
-
else:
|
64
|
-
image_data["caption"] = []
|
65
|
-
|
66
|
-
# Prepare the image block
|
67
|
-
image_block = {"type": "image", "image": image_data}
|
68
|
-
|
69
|
-
# Add empty paragraph after image
|
70
|
-
empty_paragraph = {"type": "paragraph", "paragraph": {"rich_text": []}}
|
71
|
-
|
72
|
-
return [image_block, empty_paragraph]
|
73
|
-
|
74
|
-
@classmethod
|
75
|
-
def notion_to_markdown(cls, block: Dict[str, Any]) -> Optional[str]:
|
76
|
-
"""Convert Notion image block to markdown image."""
|
77
|
-
if block.get("type") != "image":
|
78
|
-
return None
|
79
|
-
|
80
|
-
image_data = block.get("image", {})
|
81
|
-
|
82
|
-
# Handle both external and file (uploaded) images
|
83
|
-
url = ImageElement._extract_image_url(image_data)
|
84
|
-
if not url:
|
85
|
-
return None
|
86
|
-
|
87
|
-
caption_rich_text = image_data.get("caption", [])
|
88
|
-
|
89
|
-
if not caption_rich_text:
|
90
|
-
# Simple image with URL only
|
91
|
-
return f"[image]({url})"
|
92
|
-
|
93
|
-
# Extract caption text
|
94
|
-
caption = ImageElement._extract_text_content(caption_rich_text)
|
95
|
-
|
96
|
-
if caption:
|
97
|
-
return f'[image]({url} "{caption}")'
|
98
|
-
|
99
|
-
return f"[image]({url})"
|
100
|
-
|
101
|
-
@classmethod
|
102
|
-
def is_multiline(cls) -> bool:
|
103
|
-
"""Images are single-line elements."""
|
104
|
-
return False
|
105
|
-
|
106
|
-
@classmethod
|
107
|
-
def _extract_image_url(cls, image_data: Dict[str, Any]) -> str:
|
108
|
-
"""Extract URL from image data, handling both external and uploaded images."""
|
109
|
-
if image_data.get("type") == "external":
|
110
|
-
return image_data.get("external", {}).get("url", "")
|
111
|
-
elif image_data.get("type") == "file":
|
112
|
-
return image_data.get("file", {}).get("url", "")
|
113
|
-
return ""
|
114
|
-
|
115
|
-
@classmethod
|
116
|
-
def _extract_text_content(cls, rich_text: List[Dict[str, Any]]) -> str:
|
117
|
-
"""Extract plain text content from Notion rich_text elements."""
|
118
|
-
result = ""
|
119
|
-
for text_obj in rich_text:
|
120
|
-
if text_obj.get("type") == "text":
|
121
|
-
result += text_obj.get("text", {}).get("content", "")
|
122
|
-
elif "plain_text" in text_obj:
|
123
|
-
result += text_obj.get("plain_text", "")
|
124
|
-
return result
|
125
|
-
|
126
|
-
@classmethod
|
127
|
-
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
128
|
-
"""
|
129
|
-
Returns structured LLM prompt metadata for the image element.
|
130
|
-
"""
|
131
|
-
return (
|
132
|
-
ElementPromptBuilder()
|
133
|
-
.with_description(
|
134
|
-
"Embeds an image from an external URL into your document."
|
135
|
-
)
|
136
|
-
.with_usage_guidelines(
|
137
|
-
"Use images to include visual content such as diagrams, screenshots, charts, photos, or illustrations "
|
138
|
-
"that enhance your document. Images can make complex information easier to understand, create visual interest, "
|
139
|
-
"or provide evidence for your points."
|
140
|
-
)
|
141
|
-
.with_syntax('[image](https://example.com/image.jpg "Optional caption")')
|
142
|
-
.with_examples(
|
143
|
-
[
|
144
|
-
"[image](https://example.com/chart.png)",
|
145
|
-
'[image](https://example.com/screenshot.jpg "Data visualization showing monthly trends")',
|
146
|
-
'[image](https://company.com/logo.png "Company Inc. logo")',
|
147
|
-
'[image](https://example.com/diagram.jpg "System architecture overview")',
|
148
|
-
]
|
149
|
-
)
|
150
|
-
.build()
|
151
|
-
)
|
@@ -1,356 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Clean Fluent Markdown Builder
|
3
|
-
============================
|
4
|
-
|
5
|
-
A direct, chainable builder for all MarkdownNode types without overengineering.
|
6
|
-
Maps 1:1 to the available blocks with clear, expressive method names.
|
7
|
-
"""
|
8
|
-
|
9
|
-
from __future__ import annotations
|
10
|
-
from typing import Optional, Self, Union
|
11
|
-
|
12
|
-
from notionary.blocks import (
|
13
|
-
HeadingMarkdownNode,
|
14
|
-
ImageMarkdownNode,
|
15
|
-
ParagraphMarkdownNode,
|
16
|
-
AudioMarkdownNode,
|
17
|
-
BookmarkMarkdownNode,
|
18
|
-
CalloutMarkdownNode,
|
19
|
-
CodeMarkdownNode,
|
20
|
-
DividerMarkdownNode,
|
21
|
-
DocumentMarkdownNode,
|
22
|
-
EmbedMarkdownNode,
|
23
|
-
MentionMarkdownNode,
|
24
|
-
NumberedListMarkdownNode,
|
25
|
-
BulletedListMarkdownNode,
|
26
|
-
QuoteMarkdownNode,
|
27
|
-
TableMarkdownNode,
|
28
|
-
TodoMarkdownNode,
|
29
|
-
ToggleMarkdownNode,
|
30
|
-
ToggleableHeadingMarkdownNode,
|
31
|
-
VideoMarkdownNode,
|
32
|
-
MarkdownNode,
|
33
|
-
)
|
34
|
-
|
35
|
-
|
36
|
-
class MarkdownBuilder:
|
37
|
-
"""
|
38
|
-
Fluent interface builder for creating Notion content with clean, direct methods.
|
39
|
-
"""
|
40
|
-
|
41
|
-
def __init__(self) -> None:
|
42
|
-
self.children: list[MarkdownNode] = []
|
43
|
-
|
44
|
-
def h1(self, text: str) -> Self:
|
45
|
-
"""
|
46
|
-
Add an H1 heading.
|
47
|
-
|
48
|
-
Args:
|
49
|
-
text: The heading text content
|
50
|
-
"""
|
51
|
-
self.children.append(HeadingMarkdownNode(text=text, level=1))
|
52
|
-
return self
|
53
|
-
|
54
|
-
def h2(self, text: str) -> Self:
|
55
|
-
"""
|
56
|
-
Add an H2 heading.
|
57
|
-
|
58
|
-
Args:
|
59
|
-
text: The heading text content
|
60
|
-
"""
|
61
|
-
self.children.append(HeadingMarkdownNode(text=text, level=2))
|
62
|
-
return self
|
63
|
-
|
64
|
-
def h3(self, text: str) -> Self:
|
65
|
-
"""
|
66
|
-
Add an H3 heading.
|
67
|
-
|
68
|
-
Args:
|
69
|
-
text: The heading text content
|
70
|
-
"""
|
71
|
-
self.children.append(HeadingMarkdownNode(text=text, level=3))
|
72
|
-
return self
|
73
|
-
|
74
|
-
def heading(self, text: str, level: int = 2) -> Self:
|
75
|
-
"""
|
76
|
-
Add a heading with specified level.
|
77
|
-
|
78
|
-
Args:
|
79
|
-
text: The heading text content
|
80
|
-
level: Heading level (1-3), defaults to 2
|
81
|
-
"""
|
82
|
-
self.children.append(HeadingMarkdownNode(text=text, level=level))
|
83
|
-
return self
|
84
|
-
|
85
|
-
def paragraph(self, text: str) -> Self:
|
86
|
-
"""
|
87
|
-
Add a paragraph block.
|
88
|
-
|
89
|
-
Args:
|
90
|
-
text: The paragraph text content
|
91
|
-
"""
|
92
|
-
self.children.append(ParagraphMarkdownNode(text=text))
|
93
|
-
return self
|
94
|
-
|
95
|
-
def text(self, content: str) -> Self:
|
96
|
-
"""
|
97
|
-
Add a text paragraph (alias for paragraph).
|
98
|
-
|
99
|
-
Args:
|
100
|
-
content: The text content
|
101
|
-
"""
|
102
|
-
return self.paragraph(content)
|
103
|
-
|
104
|
-
def quote(self, text: str, author: Optional[str] = None) -> Self:
|
105
|
-
"""
|
106
|
-
Add a blockquote.
|
107
|
-
|
108
|
-
Args:
|
109
|
-
text: Quote text content
|
110
|
-
author: Optional quote author/attribution
|
111
|
-
"""
|
112
|
-
self.children.append(QuoteMarkdownNode(text=text, author=author))
|
113
|
-
return self
|
114
|
-
|
115
|
-
def divider(self) -> Self:
|
116
|
-
"""Add a horizontal divider."""
|
117
|
-
self.children.append(DividerMarkdownNode())
|
118
|
-
return self
|
119
|
-
|
120
|
-
def numbered_list(self, items: list[str]) -> Self:
|
121
|
-
"""
|
122
|
-
Add a numbered list.
|
123
|
-
|
124
|
-
Args:
|
125
|
-
items: List of text items for the numbered list
|
126
|
-
"""
|
127
|
-
self.children.append(NumberedListMarkdownNode(texts=items))
|
128
|
-
return self
|
129
|
-
|
130
|
-
def bulleted_list(self, items: list[str]) -> Self:
|
131
|
-
"""
|
132
|
-
Add a bulleted list.
|
133
|
-
|
134
|
-
Args:
|
135
|
-
items: List of text items for the bulleted list
|
136
|
-
"""
|
137
|
-
self.children.append(BulletedListMarkdownNode(texts=items))
|
138
|
-
return self
|
139
|
-
|
140
|
-
def todo(self, text: str, checked: bool = False) -> Self:
|
141
|
-
"""
|
142
|
-
Add a single todo item.
|
143
|
-
|
144
|
-
Args:
|
145
|
-
text: The todo item text
|
146
|
-
checked: Whether the todo item is completed, defaults to False
|
147
|
-
"""
|
148
|
-
self.children.append(TodoMarkdownNode(text=text, checked=checked))
|
149
|
-
return self
|
150
|
-
|
151
|
-
def todo_list(
|
152
|
-
self, items: list[str], completed: Optional[list[bool]] = None
|
153
|
-
) -> Self:
|
154
|
-
"""
|
155
|
-
Add multiple todo items.
|
156
|
-
|
157
|
-
Args:
|
158
|
-
items: List of todo item texts
|
159
|
-
completed: List of completion states for each item, defaults to all False
|
160
|
-
"""
|
161
|
-
if completed is None:
|
162
|
-
completed = [False] * len(items)
|
163
|
-
|
164
|
-
for i, item in enumerate(items):
|
165
|
-
is_done = completed[i] if i < len(completed) else False
|
166
|
-
self.children.append(TodoMarkdownNode(text=item, checked=is_done))
|
167
|
-
return self
|
168
|
-
|
169
|
-
def callout(self, text: str, emoji: Optional[str] = None) -> Self:
|
170
|
-
"""
|
171
|
-
Add a callout block.
|
172
|
-
|
173
|
-
Args:
|
174
|
-
text: The callout text content
|
175
|
-
emoji: Optional emoji for the callout icon
|
176
|
-
"""
|
177
|
-
self.children.append(CalloutMarkdownNode(text=text, emoji=emoji))
|
178
|
-
return self
|
179
|
-
|
180
|
-
def toggle(self, title: str, content: Optional[list[str]] = None) -> Self:
|
181
|
-
"""
|
182
|
-
Add a toggle block.
|
183
|
-
|
184
|
-
Args:
|
185
|
-
title: The toggle title/header text
|
186
|
-
content: Optional list of content items inside the toggle
|
187
|
-
"""
|
188
|
-
self.children.append(ToggleMarkdownNode(title=title, content=content))
|
189
|
-
return self
|
190
|
-
|
191
|
-
def toggleable_heading(
|
192
|
-
self, text: str, level: int = 2, content: Optional[list[str]] = None
|
193
|
-
) -> Self:
|
194
|
-
"""
|
195
|
-
Add a toggleable heading.
|
196
|
-
|
197
|
-
Args:
|
198
|
-
text: The heading text content
|
199
|
-
level: Heading level (1-3), defaults to 2
|
200
|
-
content: Optional list of content items inside the toggleable heading
|
201
|
-
"""
|
202
|
-
self.children.append(
|
203
|
-
ToggleableHeadingMarkdownNode(text=text, level=level, content=content)
|
204
|
-
)
|
205
|
-
return self
|
206
|
-
|
207
|
-
def image(
|
208
|
-
self, url: str, caption: Optional[str] = None, alt: Optional[str] = None
|
209
|
-
) -> Self:
|
210
|
-
"""
|
211
|
-
Add an image.
|
212
|
-
|
213
|
-
Args:
|
214
|
-
url: Image URL or file path
|
215
|
-
caption: Optional image caption text
|
216
|
-
alt: Optional alternative text for accessibility
|
217
|
-
"""
|
218
|
-
self.children.append(ImageMarkdownNode(url=url, caption=caption, alt=alt))
|
219
|
-
return self
|
220
|
-
|
221
|
-
def video(self, url: str, caption: Optional[str] = None) -> Self:
|
222
|
-
"""
|
223
|
-
Add a video.
|
224
|
-
|
225
|
-
Args:
|
226
|
-
url: Video URL or file path
|
227
|
-
caption: Optional video caption text
|
228
|
-
"""
|
229
|
-
self.children.append(VideoMarkdownNode(url=url, caption=caption))
|
230
|
-
return self
|
231
|
-
|
232
|
-
def audio(self, url: str, caption: Optional[str] = None) -> Self:
|
233
|
-
"""
|
234
|
-
Add audio content.
|
235
|
-
|
236
|
-
Args:
|
237
|
-
url: Audio file URL or path
|
238
|
-
caption: Optional audio caption text
|
239
|
-
"""
|
240
|
-
self.children.append(AudioMarkdownNode(url=url, caption=caption))
|
241
|
-
return self
|
242
|
-
|
243
|
-
def document(self, url: str, caption: Optional[str] = None) -> Self:
|
244
|
-
"""
|
245
|
-
Add a document file.
|
246
|
-
|
247
|
-
Args:
|
248
|
-
url: Document file URL or path
|
249
|
-
caption: Optional document caption text
|
250
|
-
"""
|
251
|
-
self.children.append(DocumentMarkdownNode(url=url, caption=caption))
|
252
|
-
return self
|
253
|
-
|
254
|
-
def bookmark(
|
255
|
-
self, url: str, title: Optional[str] = None, description: Optional[str] = None
|
256
|
-
) -> Self:
|
257
|
-
"""
|
258
|
-
Add a bookmark.
|
259
|
-
|
260
|
-
Args:
|
261
|
-
url: Bookmark URL
|
262
|
-
title: Optional bookmark title
|
263
|
-
description: Optional bookmark description text
|
264
|
-
"""
|
265
|
-
self.children.append(
|
266
|
-
BookmarkMarkdownNode(url=url, title=title, description=description)
|
267
|
-
)
|
268
|
-
return self
|
269
|
-
|
270
|
-
def embed(self, url: str, caption: Optional[str] = None) -> Self:
|
271
|
-
"""
|
272
|
-
Add an embed.
|
273
|
-
|
274
|
-
Args:
|
275
|
-
url: URL to embed (e.g., YouTube, Twitter, etc.)
|
276
|
-
caption: Optional embed caption text
|
277
|
-
"""
|
278
|
-
self.children.append(EmbedMarkdownNode(url=url, caption=caption))
|
279
|
-
return self
|
280
|
-
|
281
|
-
def code(
|
282
|
-
self, code: str, language: Optional[str] = None, caption: Optional[str] = None
|
283
|
-
) -> Self:
|
284
|
-
"""
|
285
|
-
Add a code block.
|
286
|
-
|
287
|
-
Args:
|
288
|
-
code: The source code content
|
289
|
-
language: Optional programming language for syntax highlighting
|
290
|
-
caption: Optional code block caption text
|
291
|
-
"""
|
292
|
-
self.children.append(
|
293
|
-
CodeMarkdownNode(code=code, language=language, caption=caption)
|
294
|
-
)
|
295
|
-
return self
|
296
|
-
|
297
|
-
def table(self, headers: list[str], rows: list[list[str]]) -> Self:
|
298
|
-
"""
|
299
|
-
Add a table.
|
300
|
-
|
301
|
-
Args:
|
302
|
-
headers: List of column header texts
|
303
|
-
rows: List of rows, where each row is a list of cell texts
|
304
|
-
"""
|
305
|
-
self.children.append(TableMarkdownNode(headers=headers, rows=rows))
|
306
|
-
return self
|
307
|
-
|
308
|
-
def mention_page(self, page_id: str) -> Self:
|
309
|
-
"""
|
310
|
-
Add a page mention.
|
311
|
-
|
312
|
-
Args:
|
313
|
-
page_id: The ID of the page to mention
|
314
|
-
"""
|
315
|
-
self.children.append(MentionMarkdownNode("page", page_id))
|
316
|
-
return self
|
317
|
-
|
318
|
-
def mention_database(self, database_id: str) -> Self:
|
319
|
-
"""
|
320
|
-
Add a database mention.
|
321
|
-
|
322
|
-
Args:
|
323
|
-
database_id: The ID of the database to mention
|
324
|
-
"""
|
325
|
-
self.children.append(MentionMarkdownNode("database", database_id))
|
326
|
-
return self
|
327
|
-
|
328
|
-
def mention_date(self, date: str) -> Self:
|
329
|
-
"""
|
330
|
-
Add a date mention.
|
331
|
-
|
332
|
-
Args:
|
333
|
-
date: Date in YYYY-MM-DD format
|
334
|
-
"""
|
335
|
-
self.children.append(MentionMarkdownNode("date", date))
|
336
|
-
return self
|
337
|
-
|
338
|
-
def add_custom(self, node: MarkdownNode) -> Self:
|
339
|
-
"""
|
340
|
-
Add a custom MarkdownNode.
|
341
|
-
|
342
|
-
Args:
|
343
|
-
node: A custom MarkdownNode instance
|
344
|
-
"""
|
345
|
-
self.children.append(node)
|
346
|
-
return self
|
347
|
-
|
348
|
-
def space(self) -> Self:
|
349
|
-
"""Add vertical spacing."""
|
350
|
-
return self.paragraph("")
|
351
|
-
|
352
|
-
def build(self) -> str:
|
353
|
-
"""Build and return the final markdown string."""
|
354
|
-
return "\n\n".join(
|
355
|
-
child.to_markdown() for child in self.children if child is not None
|
356
|
-
)
|