notionary 0.2.22__py3-none-any.whl → 0.2.24__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 +1 -1
- notionary/blocks/__init__.py +3 -1
- notionary/blocks/audio/__init__.py +0 -2
- notionary/blocks/audio/audio_element.py +92 -49
- notionary/blocks/audio/audio_markdown_node.py +4 -17
- notionary/blocks/bookmark/__init__.py +0 -2
- notionary/blocks/bookmark/bookmark_markdown_node.py +5 -21
- notionary/blocks/breadcrumbs/__init__.py +0 -2
- notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +2 -21
- notionary/blocks/bulleted_list/__init__.py +0 -2
- notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +3 -17
- notionary/blocks/bulleted_list/bulleted_list_models.py +0 -1
- notionary/blocks/callout/__init__.py +0 -2
- notionary/blocks/callout/callout_markdown_node.py +4 -18
- notionary/blocks/callout/callout_models.py +3 -4
- notionary/blocks/child_database/child_database_element.py +2 -4
- notionary/blocks/code/code_markdown_node.py +5 -19
- notionary/blocks/column/__init__.py +0 -4
- notionary/blocks/column/column_list_markdown_node.py +3 -19
- notionary/blocks/column/column_markdown_node.py +4 -21
- notionary/blocks/divider/__init__.py +0 -2
- notionary/blocks/divider/divider_markdown_node.py +2 -16
- notionary/blocks/embed/__init__.py +0 -2
- notionary/blocks/embed/embed_markdown_node.py +4 -17
- notionary/blocks/equation/__init__.py +0 -1
- notionary/blocks/equation/equation_element_markdown_node.py +3 -15
- notionary/blocks/file/__init__.py +0 -2
- notionary/blocks/file/file_element.py +67 -46
- notionary/blocks/file/file_element_markdown_node.py +4 -17
- notionary/blocks/heading/__init__.py +0 -2
- notionary/blocks/heading/heading_markdown_node.py +5 -19
- notionary/blocks/heading/heading_models.py +3 -3
- notionary/blocks/image_block/__init__.py +0 -2
- notionary/blocks/image_block/image_element.py +66 -25
- notionary/blocks/image_block/image_markdown_node.py +5 -20
- notionary/{markdown → blocks/markdown}/markdown_builder.py +29 -233
- notionary/blocks/markdown/markdown_node.py +25 -0
- notionary/blocks/mixins/file_upload/__init__.py +3 -0
- notionary/blocks/mixins/file_upload/file_upload_mixin.py +320 -0
- notionary/blocks/numbered_list/__init__.py +0 -1
- notionary/blocks/numbered_list/numbered_list_markdown_node.py +3 -17
- notionary/blocks/numbered_list/numbered_list_models.py +3 -3
- notionary/blocks/paragraph/__init__.py +0 -2
- notionary/blocks/paragraph/paragraph_markdown_node.py +3 -13
- notionary/blocks/pdf/__init__.py +0 -2
- notionary/blocks/pdf/pdf_element.py +81 -32
- notionary/blocks/pdf/pdf_markdown_node.py +5 -18
- notionary/blocks/quote/__init__.py +0 -2
- notionary/blocks/quote/quote_markdown_node.py +3 -13
- notionary/blocks/registry/__init__.py +1 -2
- notionary/blocks/registry/block_registry.py +116 -61
- notionary/blocks/rich_text/text_inline_formatter.py +1 -1
- notionary/blocks/table/__init__.py +0 -2
- notionary/blocks/table/table_markdown_node.py +17 -16
- notionary/blocks/table_of_contents/__init__.py +0 -2
- notionary/blocks/table_of_contents/table_of_contents_element.py +27 -15
- notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +3 -17
- notionary/blocks/table_of_contents/table_of_contents_models.py +2 -2
- notionary/blocks/todo/__init__.py +0 -2
- notionary/blocks/todo/todo_markdown_node.py +9 -20
- notionary/blocks/todo/todo_models.py +2 -3
- notionary/blocks/toggle/__init__.py +0 -2
- notionary/blocks/toggle/toggle_markdown_node.py +5 -19
- notionary/blocks/toggleable_heading/__init__.py +0 -2
- notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +6 -23
- notionary/blocks/video/__init__.py +0 -2
- notionary/blocks/video/video_element.py +110 -34
- notionary/blocks/video/video_markdown_node.py +4 -15
- notionary/comments/__init__.py +26 -0
- notionary/comments/client.py +211 -0
- notionary/comments/models.py +129 -0
- notionary/file_upload/client.py +3 -2
- notionary/file_upload/models.py +10 -1
- notionary/file_upload/notion_file_upload.py +5 -5
- notionary/page/client.py +1 -6
- notionary/page/markdown_whitespace_processor.py +129 -0
- notionary/page/notion_page.py +87 -48
- notionary/page/page_content_deleting_service.py +1 -1
- notionary/page/page_content_writer.py +32 -129
- notionary/page/page_context.py +0 -6
- notionary/page/reader/handler/column_list_renderer.py +2 -2
- notionary/page/reader/handler/column_renderer.py +2 -2
- notionary/page/reader/handler/line_renderer.py +2 -2
- notionary/page/reader/handler/toggle_renderer.py +2 -2
- notionary/page/reader/handler/toggleable_heading_renderer.py +2 -2
- notionary/page/writer/handler/toggle_handler.py +8 -4
- notionary/page/writer/handler/toggleable_heading_handler.py +3 -2
- notionary/page/writer/markdown_to_notion_converter.py +74 -30
- notionary/schemas/__init__.py +3 -0
- notionary/schemas/base.py +73 -0
- notionary/shared/__init__.py +3 -0
- notionary/{blocks/rich_text → shared}/name_to_id_resolver.py +0 -2
- {notionary-0.2.22.dist-info → notionary-0.2.24.dist-info}/METADATA +15 -2
- {notionary-0.2.22.dist-info → notionary-0.2.24.dist-info}/RECORD +97 -95
- notionary/blocks/guards.py +0 -22
- notionary/blocks/registry/block_registry_builder.py +0 -264
- notionary/markdown/makdown_document_model.py +0 -0
- notionary/markdown/markdown_document_model.py +0 -228
- notionary/markdown/markdown_node.py +0 -30
- notionary/models/notion_database_response.py +0 -0
- notionary/page/writer/markdown_to_notion_formatting_post_processor.py +0 -73
- notionary/page/writer/markdown_to_notion_post_processor.py +0 -0
- /notionary/{markdown/___init__.py → blocks/markdown/markdown_document_model.py} +0 -0
- {notionary-0.2.22.dist-info → notionary-0.2.24.dist-info}/LICENSE +0 -0
- {notionary-0.2.22.dist-info → notionary-0.2.24.dist-info}/WHEEL +0 -0
notionary/__init__.py
CHANGED
@@ -4,7 +4,7 @@ bootstrap_blocks()
|
|
4
4
|
|
5
5
|
from .database import DatabaseFilterBuilder, NotionDatabase
|
6
6
|
from .file_upload import NotionFileUpload
|
7
|
-
from .markdown.markdown_builder import MarkdownBuilder
|
7
|
+
from .blocks.markdown.markdown_builder import MarkdownBuilder
|
8
8
|
from .page.notion_page import NotionPage
|
9
9
|
from .user import NotionBotUser, NotionUser, NotionUserManager
|
10
10
|
from .workspace import NotionWorkspace
|
notionary/blocks/__init__.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
from notionary.blocks.audio.audio_element import AudioElement
|
2
2
|
from notionary.blocks.audio.audio_markdown_node import (
|
3
|
-
AudioMarkdownBlockParams,
|
4
3
|
AudioMarkdownNode,
|
5
4
|
)
|
6
5
|
from notionary.blocks.audio.audio_models import CreateAudioBlock
|
@@ -9,5 +8,4 @@ __all__ = [
|
|
9
8
|
"AudioElement",
|
10
9
|
"CreateAudioBlock",
|
11
10
|
"AudioMarkdownNode",
|
12
|
-
"AudioMarkdownBlockParams",
|
13
11
|
]
|
@@ -1,46 +1,38 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import re
|
4
|
+
from pathlib import Path
|
4
5
|
from typing import Optional
|
5
6
|
|
6
7
|
from notionary.blocks.audio.audio_models import CreateAudioBlock
|
7
8
|
from notionary.blocks.base_block_element import BaseBlockElement
|
8
|
-
from notionary.blocks.file.file_element_models import
|
9
|
+
from notionary.blocks.file.file_element_models import (
|
10
|
+
ExternalFile,
|
11
|
+
FileBlock,
|
12
|
+
FileType,
|
13
|
+
FileUploadFile,
|
14
|
+
)
|
9
15
|
from notionary.blocks.mixins.captions import CaptionMixin
|
16
|
+
from notionary.blocks.mixins.file_upload.file_upload_mixin import FileUploadMixin
|
10
17
|
from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformation
|
11
18
|
from notionary.blocks.models import Block, BlockCreateResult, BlockType
|
19
|
+
from notionary.util.logging_mixin import LoggingMixin
|
12
20
|
|
13
21
|
|
14
|
-
class AudioElement(BaseBlockElement, CaptionMixin):
|
15
|
-
"""
|
22
|
+
class AudioElement(BaseBlockElement, FileUploadMixin, LoggingMixin, CaptionMixin):
|
23
|
+
r"""
|
16
24
|
Handles conversion between Markdown audio embeds and Notion audio blocks.
|
17
25
|
|
18
|
-
|
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
|
26
|
+
Supports both external URLs and local audio file uploads.
|
22
27
|
|
23
|
-
|
24
|
-
-
|
25
|
-
-
|
28
|
+
Markdown audio syntax:
|
29
|
+
- [audio](https://example.com/audio.mp3) - External URL
|
30
|
+
- [audio](./local/song.mp3) - Local audio file (will be uploaded)
|
31
|
+
- [audio](C:\Music\podcast.wav) - Absolute local path (will be uploaded)
|
32
|
+
- [audio](https://example.com/audio.mp3)(caption:Episode 1) - URL with caption
|
26
33
|
"""
|
27
34
|
|
28
|
-
|
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
|
-
|
35
|
+
AUDIO_PATTERN = re.compile(r"\[audio\]\(([^)]+)\)")
|
44
36
|
SUPPORTED_EXTENSIONS = {".mp3", ".wav", ".ogg", ".oga", ".m4a"}
|
45
37
|
|
46
38
|
@classmethod
|
@@ -49,27 +41,62 @@ class AudioElement(BaseBlockElement, CaptionMixin):
|
|
49
41
|
return block.type == BlockType.AUDIO
|
50
42
|
|
51
43
|
@classmethod
|
52
|
-
async def markdown_to_notion(cls, text: str) -> BlockCreateResult:
|
44
|
+
async def markdown_to_notion(cls, text: str) -> Optional[BlockCreateResult]:
|
53
45
|
"""Convert markdown audio embed to Notion audio block."""
|
54
|
-
#
|
55
|
-
|
56
|
-
if not
|
46
|
+
# Extract the path/URL
|
47
|
+
path = cls._extract_audio_path(text.strip())
|
48
|
+
if not path:
|
57
49
|
return None
|
58
50
|
|
59
|
-
if
|
60
|
-
|
51
|
+
# Check if it's a local file path
|
52
|
+
if cls._is_local_file_path(path):
|
53
|
+
# Verify file exists and has supported extension
|
54
|
+
audio_path = Path(path)
|
55
|
+
if not audio_path.exists():
|
56
|
+
cls.logger.warning(f"Audio file not found: {path}")
|
57
|
+
return None
|
61
58
|
|
62
|
-
|
63
|
-
|
64
|
-
|
59
|
+
if audio_path.suffix.lower() not in cls.SUPPORTED_EXTENSIONS:
|
60
|
+
cls.logger.warning(f"Unsupported audio format: {audio_path.suffix}")
|
61
|
+
return None
|
65
62
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
63
|
+
cls.logger.info(f"Uploading local audio file: {path}")
|
64
|
+
|
65
|
+
# Upload the local audio file
|
66
|
+
file_upload_id = await cls._upload_local_file(path, "audio")
|
67
|
+
if not file_upload_id:
|
68
|
+
cls.logger.error(f"Failed to upload audio file: {path}")
|
69
|
+
return None
|
71
70
|
|
72
|
-
|
71
|
+
cls.logger.info(
|
72
|
+
f"Successfully uploaded audio file with ID: {file_upload_id}"
|
73
|
+
)
|
74
|
+
|
75
|
+
# Use mixin to extract caption (if present anywhere in text)
|
76
|
+
caption_text = cls.extract_caption(text.strip())
|
77
|
+
caption_rich_text = cls.build_caption_rich_text(caption_text or "")
|
78
|
+
|
79
|
+
audio_content = FileBlock(
|
80
|
+
type=FileType.FILE_UPLOAD,
|
81
|
+
file_upload=FileUploadFile(id=file_upload_id),
|
82
|
+
caption=caption_rich_text,
|
83
|
+
)
|
84
|
+
|
85
|
+
return CreateAudioBlock(audio=audio_content)
|
86
|
+
|
87
|
+
else:
|
88
|
+
# Handle external URL - accept any URL (validation happens at API level)
|
89
|
+
# Use mixin to extract caption (if present anywhere in text)
|
90
|
+
caption_text = cls.extract_caption(text.strip())
|
91
|
+
caption_rich_text = cls.build_caption_rich_text(caption_text or "")
|
92
|
+
|
93
|
+
audio_content = FileBlock(
|
94
|
+
type=FileType.EXTERNAL,
|
95
|
+
external=ExternalFile(url=path),
|
96
|
+
caption=caption_rich_text,
|
97
|
+
)
|
98
|
+
|
99
|
+
return CreateAudioBlock(audio=audio_content)
|
73
100
|
|
74
101
|
@classmethod
|
75
102
|
async def notion_to_markdown(cls, block: Block) -> Optional[str]:
|
@@ -78,11 +105,14 @@ class AudioElement(BaseBlockElement, CaptionMixin):
|
|
78
105
|
return None
|
79
106
|
|
80
107
|
audio = block.audio
|
108
|
+
url = None
|
109
|
+
|
110
|
+
# Handle both external URLs and uploaded files
|
111
|
+
if audio.type == FileType.EXTERNAL and audio.external is not None:
|
112
|
+
url = audio.external.url
|
113
|
+
elif audio.type == FileType.FILE_UPLOAD and audio.file_upload is not None:
|
114
|
+
url = audio.file_upload.url
|
81
115
|
|
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
116
|
if not url:
|
87
117
|
return None
|
88
118
|
|
@@ -100,16 +130,29 @@ class AudioElement(BaseBlockElement, CaptionMixin):
|
|
100
130
|
"""Get system prompt information for audio blocks."""
|
101
131
|
return BlockElementMarkdownInformation(
|
102
132
|
block_type=cls.__name__,
|
103
|
-
description="Audio blocks embed audio files from external URLs with optional captions",
|
133
|
+
description="Audio blocks embed audio files from external URLs or local files with optional captions",
|
104
134
|
syntax_examples=[
|
105
135
|
"[audio](https://example.com/song.mp3)",
|
136
|
+
"[audio](./local/podcast.wav)",
|
137
|
+
"[audio](C:\\Music\\interview.mp3)",
|
106
138
|
"[audio](https://example.com/podcast.wav)(caption:Episode 1)",
|
107
|
-
"(caption:Background music)[audio](
|
108
|
-
"[audio](
|
139
|
+
"(caption:Background music)[audio](./song.mp3)",
|
140
|
+
"[audio](./interview.mp3)(caption:**Live** interview)",
|
109
141
|
],
|
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.",
|
142
|
+
usage_guidelines="Use for embedding audio files like music, podcasts, or sound effects. Supports both external URLs and local file uploads. Supports common audio formats (mp3, wav, ogg, m4a). Caption supports rich text formatting and is optional.",
|
111
143
|
)
|
112
144
|
|
113
145
|
@classmethod
|
114
146
|
def _is_likely_audio_url(cls, url: str) -> bool:
|
115
147
|
return any(url.lower().endswith(ext) for ext in cls.SUPPORTED_EXTENSIONS)
|
148
|
+
|
149
|
+
@classmethod
|
150
|
+
def _extract_audio_path(cls, text: str) -> Optional[str]:
|
151
|
+
"""Extract audio path/URL from text, handling caption patterns."""
|
152
|
+
clean_text = cls.remove_caption(text)
|
153
|
+
|
154
|
+
match = cls.AUDIO_PATTERN.search(clean_text)
|
155
|
+
if match:
|
156
|
+
return match.group(1).strip()
|
157
|
+
|
158
|
+
return None
|
@@ -1,30 +1,17 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
1
|
from typing import Optional
|
4
2
|
|
5
|
-
from
|
6
|
-
|
7
|
-
from notionary.markdown.markdown_node import MarkdownNode
|
3
|
+
from notionary.blocks.markdown.markdown_node import MarkdownNode
|
8
4
|
from notionary.blocks.mixins.captions import CaptionMarkdownNodeMixin
|
9
5
|
|
10
6
|
|
11
|
-
class AudioMarkdownBlockParams(BaseModel):
|
12
|
-
url: str
|
13
|
-
caption: Optional[str] = None
|
14
|
-
|
15
|
-
|
16
7
|
class AudioMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
|
17
8
|
"""
|
9
|
+
Enhanced Audio node with Pydantic integration.
|
18
10
|
Programmatic interface for creating Notion-style audio blocks.
|
19
11
|
"""
|
20
12
|
|
21
|
-
|
22
|
-
|
23
|
-
self.caption = caption
|
24
|
-
|
25
|
-
@classmethod
|
26
|
-
def from_params(cls, params: AudioMarkdownBlockParams) -> AudioMarkdownNode:
|
27
|
-
return cls(url=params.url, caption=params.caption)
|
13
|
+
url: str
|
14
|
+
caption: Optional[str] = None
|
28
15
|
|
29
16
|
def to_markdown(self) -> str:
|
30
17
|
"""Return the Markdown representation.
|
@@ -1,6 +1,5 @@
|
|
1
1
|
from notionary.blocks.bookmark.bookmark_element import BookmarkElement
|
2
2
|
from notionary.blocks.bookmark.bookmark_markdown_node import (
|
3
|
-
BookmarkMarkdownBlockParams,
|
4
3
|
BookmarkMarkdownNode,
|
5
4
|
)
|
6
5
|
from notionary.blocks.bookmark.bookmark_models import BookmarkBlock, CreateBookmarkBlock
|
@@ -10,5 +9,4 @@ __all__ = [
|
|
10
9
|
"BookmarkBlock",
|
11
10
|
"CreateBookmarkBlock",
|
12
11
|
"BookmarkMarkdownNode",
|
13
|
-
"BookmarkMarkdownBlockParams",
|
14
12
|
]
|
@@ -1,34 +1,18 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
1
|
from typing import Optional
|
4
2
|
|
5
|
-
from
|
6
|
-
|
7
|
-
from notionary.markdown.markdown_node import MarkdownNode
|
3
|
+
from notionary.blocks.markdown.markdown_node import MarkdownNode
|
8
4
|
from notionary.blocks.mixins.captions import CaptionMarkdownNodeMixin
|
9
5
|
|
10
6
|
|
11
|
-
class BookmarkMarkdownBlockParams(BaseModel):
|
12
|
-
url: str
|
13
|
-
title: Optional[str] = None
|
14
|
-
caption: Optional[str] = None
|
15
|
-
|
16
|
-
|
17
7
|
class BookmarkMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
|
18
8
|
"""
|
9
|
+
Enhanced Bookmark node with Pydantic integration.
|
19
10
|
Programmatic interface for creating Notion-style bookmark Markdown blocks.
|
20
11
|
"""
|
21
12
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
self.url = url
|
26
|
-
self.title = title
|
27
|
-
self.caption = caption
|
28
|
-
|
29
|
-
@classmethod
|
30
|
-
def from_params(cls, params: BookmarkMarkdownBlockParams) -> BookmarkMarkdownNode:
|
31
|
-
return cls(url=params.url, title=params.title, caption=params.caption)
|
13
|
+
url: str
|
14
|
+
title: Optional[str] = None
|
15
|
+
caption: Optional[str] = None
|
32
16
|
|
33
17
|
def to_markdown(self) -> str:
|
34
18
|
"""Return the Markdown representation.
|
@@ -1,6 +1,5 @@
|
|
1
1
|
from notionary.blocks.breadcrumbs.breadcrumb_element import BreadcrumbElement
|
2
2
|
from notionary.blocks.breadcrumbs.breadcrumb_markdown_node import (
|
3
|
-
BreadcrumbMarkdownBlockParams,
|
4
3
|
BreadcrumbMarkdownNode,
|
5
4
|
)
|
6
5
|
from notionary.blocks.breadcrumbs.breadcrumb_models import (
|
@@ -13,5 +12,4 @@ __all__ = [
|
|
13
12
|
"BreadcrumbBlock",
|
14
13
|
"CreateBreadcrumbBlock",
|
15
14
|
"BreadcrumbMarkdownNode",
|
16
|
-
"BreadcrumbMarkdownBlockParams",
|
17
15
|
]
|
@@ -1,32 +1,13 @@
|
|
1
|
-
from
|
2
|
-
|
3
|
-
from pydantic import BaseModel
|
4
|
-
|
5
|
-
from notionary.markdown.markdown_node import MarkdownNode
|
6
|
-
|
7
|
-
|
8
|
-
class BreadcrumbMarkdownBlockParams(BaseModel):
|
9
|
-
"""Parameters for breadcrumb markdown block. No parameters needed."""
|
10
|
-
|
11
|
-
pass
|
1
|
+
from notionary.blocks.markdown.markdown_node import MarkdownNode
|
12
2
|
|
13
3
|
|
14
4
|
class BreadcrumbMarkdownNode(MarkdownNode):
|
15
5
|
"""
|
6
|
+
Enhanced Breadcrumb node with Pydantic integration.
|
16
7
|
Programmatic interface for creating Markdown breadcrumb blocks.
|
17
8
|
Example:
|
18
9
|
[breadcrumb]
|
19
10
|
"""
|
20
11
|
|
21
|
-
def __init__(self):
|
22
|
-
# No parameters needed for breadcrumb
|
23
|
-
pass
|
24
|
-
|
25
|
-
@classmethod
|
26
|
-
def from_params(
|
27
|
-
cls, params: BreadcrumbMarkdownBlockParams
|
28
|
-
) -> BreadcrumbMarkdownNode:
|
29
|
-
return cls()
|
30
|
-
|
31
12
|
def to_markdown(self) -> str:
|
32
13
|
return "[breadcrumb]"
|
@@ -1,6 +1,5 @@
|
|
1
1
|
from notionary.blocks.bulleted_list.bulleted_list_element import BulletedListElement
|
2
2
|
from notionary.blocks.bulleted_list.bulleted_list_markdown_node import (
|
3
|
-
BulletedListMarkdownBlockParams,
|
4
3
|
BulletedListMarkdownNode,
|
5
4
|
)
|
6
5
|
from notionary.blocks.bulleted_list.bulleted_list_models import (
|
@@ -13,5 +12,4 @@ __all__ = [
|
|
13
12
|
"BulletedListItemBlock",
|
14
13
|
"CreateBulletedListItemBlock",
|
15
14
|
"BulletedListMarkdownNode",
|
16
|
-
"BulletedListMarkdownBlockParams",
|
17
15
|
]
|
@@ -1,16 +1,9 @@
|
|
1
|
-
from
|
2
|
-
|
3
|
-
from pydantic import BaseModel
|
4
|
-
|
5
|
-
from notionary.markdown.markdown_node import MarkdownNode
|
6
|
-
|
7
|
-
|
8
|
-
class BulletedListMarkdownBlockParams(BaseModel):
|
9
|
-
texts: list[str]
|
1
|
+
from notionary.blocks.markdown.markdown_node import MarkdownNode
|
10
2
|
|
11
3
|
|
12
4
|
class BulletedListMarkdownNode(MarkdownNode):
|
13
5
|
"""
|
6
|
+
Enhanced BulletedList node with Pydantic integration.
|
14
7
|
Programmatic interface for creating Markdown bulleted list items.
|
15
8
|
Example:
|
16
9
|
- First item
|
@@ -18,14 +11,7 @@ class BulletedListMarkdownNode(MarkdownNode):
|
|
18
11
|
- Third item
|
19
12
|
"""
|
20
13
|
|
21
|
-
|
22
|
-
self.texts = texts
|
23
|
-
|
24
|
-
@classmethod
|
25
|
-
def from_params(
|
26
|
-
cls, params: BulletedListMarkdownBlockParams
|
27
|
-
) -> BulletedListMarkdownNode:
|
28
|
-
return cls(texts=params.texts)
|
14
|
+
texts: list[str]
|
29
15
|
|
30
16
|
def to_markdown(self) -> str:
|
31
17
|
result = []
|
@@ -10,7 +10,6 @@ from notionary.blocks.types import BlockColor
|
|
10
10
|
class BulletedListItemBlock(BaseModel):
|
11
11
|
rich_text: list[RichTextObject]
|
12
12
|
color: BlockColor = BlockColor.DEFAULT
|
13
|
-
children: list[Block] = Field(default_factory=list)
|
14
13
|
|
15
14
|
|
16
15
|
class CreateBulletedListItemBlock(BaseModel):
|
@@ -1,6 +1,5 @@
|
|
1
1
|
from notionary.blocks.callout.callout_element import CalloutElement
|
2
2
|
from notionary.blocks.callout.callout_markdown_node import (
|
3
|
-
CalloutMarkdownBlockParams,
|
4
3
|
CalloutMarkdownNode,
|
5
4
|
)
|
6
5
|
from notionary.blocks.callout.callout_models import CalloutBlock, CreateCalloutBlock
|
@@ -10,5 +9,4 @@ __all__ = [
|
|
10
9
|
"CalloutBlock",
|
11
10
|
"CreateCalloutBlock",
|
12
11
|
"CalloutMarkdownNode",
|
13
|
-
"CalloutMarkdownBlockParams",
|
14
12
|
]
|
@@ -1,30 +1,16 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
1
|
from typing import Optional
|
4
|
-
|
5
|
-
from pydantic import BaseModel
|
6
|
-
|
7
|
-
from notionary.markdown.markdown_node import MarkdownNode
|
8
|
-
|
9
|
-
|
10
|
-
class CalloutMarkdownBlockParams(BaseModel):
|
11
|
-
text: str
|
12
|
-
emoji: Optional[str] = None
|
2
|
+
from notionary.blocks.markdown.markdown_node import MarkdownNode
|
13
3
|
|
14
4
|
|
15
5
|
class CalloutMarkdownNode(MarkdownNode):
|
16
6
|
"""
|
7
|
+
Enhanced Callout node with Pydantic integration.
|
17
8
|
Programmatic interface for creating Notion-style callout Markdown blocks.
|
18
9
|
Example: [callout](This is important "⚠️")
|
19
10
|
"""
|
20
11
|
|
21
|
-
|
22
|
-
|
23
|
-
self.emoji = emoji
|
24
|
-
|
25
|
-
@classmethod
|
26
|
-
def from_params(cls, params: CalloutMarkdownBlockParams) -> CalloutMarkdownNode:
|
27
|
-
return cls(text=params.text, emoji=params.emoji)
|
12
|
+
text: str
|
13
|
+
emoji: Optional[str] = None
|
28
14
|
|
29
15
|
def to_markdown(self) -> str:
|
30
16
|
if self.emoji and self.emoji != "💡":
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from typing import Literal, Optional, Union
|
2
2
|
|
3
|
-
from pydantic import BaseModel, Field
|
3
|
+
from pydantic import BaseModel, Field, model_serializer
|
4
4
|
|
5
5
|
from notionary.blocks.file.file_element_models import FileBlock
|
6
6
|
from notionary.blocks.models import Block
|
@@ -23,10 +23,9 @@ IconObject = Union[EmojiIcon, FileIcon]
|
|
23
23
|
|
24
24
|
class CalloutBlock(BaseModel):
|
25
25
|
rich_text: list[RichTextObject]
|
26
|
-
icon: Optional[IconObject] = None
|
27
26
|
color: BlockColor = BlockColor.DEFAULT
|
28
|
-
|
29
|
-
|
27
|
+
icon: Optional[IconObject] = None
|
28
|
+
children: Optional[list[Block]] = None
|
30
29
|
|
31
30
|
class CreateCalloutBlock(BaseModel):
|
32
31
|
type: Literal["callout"] = "callout"
|
@@ -8,6 +8,7 @@ from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformati
|
|
8
8
|
from notionary.blocks.models import Block, BlockType
|
9
9
|
from notionary.util import LoggingMixin
|
10
10
|
|
11
|
+
|
11
12
|
class ChildDatabaseElement(BaseBlockElement, LoggingMixin):
|
12
13
|
"""
|
13
14
|
Handles conversion between Markdown database references and Notion child database blocks.
|
@@ -23,14 +24,11 @@ class ChildDatabaseElement(BaseBlockElement, LoggingMixin):
|
|
23
24
|
return block.type == BlockType.CHILD_DATABASE and block.child_database
|
24
25
|
|
25
26
|
@classmethod
|
26
|
-
async def markdown_to_notion(
|
27
|
-
cls, text: str
|
28
|
-
) -> Optional[str]:
|
27
|
+
async def markdown_to_notion(cls, text: str) -> Optional[str]:
|
29
28
|
"""
|
30
29
|
Convert markdown database syntax to actual Notion database.
|
31
30
|
Returns the database_id if successful, None otherwise.
|
32
31
|
"""
|
33
|
-
cls.logger.warning("Creating database from markdown is not supported via the block api. Call the create_child_page method in NotionPage instead.s")
|
34
32
|
return None
|
35
33
|
|
36
34
|
@classmethod
|
@@ -1,13 +1,11 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
1
|
from typing import Optional
|
4
2
|
|
5
|
-
from notionary.blocks.
|
6
|
-
from notionary.markdown.markdown_node import MarkdownNode
|
3
|
+
from notionary.blocks.markdown.markdown_node import MarkdownNode
|
7
4
|
|
8
5
|
|
9
6
|
class CodeMarkdownNode(MarkdownNode):
|
10
7
|
"""
|
8
|
+
Enhanced Code node with Pydantic integration.
|
11
9
|
Programmatic interface for creating Notion-style Markdown code blocks.
|
12
10
|
Automatically handles indentation normalization for multiline strings.
|
13
11
|
|
@@ -17,21 +15,9 @@ class CodeMarkdownNode(MarkdownNode):
|
|
17
15
|
```
|
18
16
|
"""
|
19
17
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
language: Optional[str] = None,
|
24
|
-
caption: Optional[str] = None,
|
25
|
-
):
|
26
|
-
self.code = code
|
27
|
-
self.language = language or ""
|
28
|
-
self.caption = caption
|
29
|
-
|
30
|
-
@classmethod
|
31
|
-
def from_params(cls, params: CodeBlock) -> CodeMarkdownNode:
|
32
|
-
return cls(
|
33
|
-
code=params.rich_text, language=params.language, caption=params.caption
|
34
|
-
)
|
18
|
+
code: str
|
19
|
+
language: Optional[str] = None
|
20
|
+
caption: Optional[str] = None
|
35
21
|
|
36
22
|
def to_markdown(self) -> str:
|
37
23
|
lang = self.language or ""
|
@@ -1,11 +1,9 @@
|
|
1
1
|
from notionary.blocks.column.column_element import ColumnElement
|
2
2
|
from notionary.blocks.column.column_list_element import ColumnListElement
|
3
3
|
from notionary.blocks.column.column_list_markdown_node import (
|
4
|
-
ColumnListMarkdownBlockParams,
|
5
4
|
ColumnListMarkdownNode,
|
6
5
|
)
|
7
6
|
from notionary.blocks.column.column_markdown_node import (
|
8
|
-
ColumnMarkdownBlockParams,
|
9
7
|
ColumnMarkdownNode,
|
10
8
|
)
|
11
9
|
from notionary.blocks.column.column_models import (
|
@@ -23,7 +21,5 @@ __all__ = [
|
|
23
21
|
"ColumnListBlock",
|
24
22
|
"CreateColumnListBlock",
|
25
23
|
"ColumnMarkdownNode",
|
26
|
-
"ColumnMarkdownBlockParams",
|
27
24
|
"ColumnListMarkdownNode",
|
28
|
-
"ColumnListMarkdownBlockParams",
|
29
25
|
]
|
@@ -1,19 +1,10 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from pydantic import BaseModel
|
4
|
-
|
5
1
|
from notionary.blocks.column.column_markdown_node import ColumnMarkdownNode
|
6
|
-
from notionary.markdown.
|
7
|
-
from notionary.markdown.markdown_node import MarkdownNode
|
8
|
-
|
9
|
-
|
10
|
-
class ColumnListMarkdownBlockParams(BaseModel):
|
11
|
-
columns: list[list[MarkdownBlock]]
|
12
|
-
model_config = {"arbitrary_types_allowed": True}
|
2
|
+
from notionary.blocks.markdown.markdown_node import MarkdownNode
|
13
3
|
|
14
4
|
|
15
5
|
class ColumnListMarkdownNode(MarkdownNode):
|
16
6
|
"""
|
7
|
+
Enhanced Column List node with Pydantic integration.
|
17
8
|
Programmatic interface for creating a Markdown column list container.
|
18
9
|
This represents the `::: columns` container that holds multiple columns.
|
19
10
|
|
@@ -31,14 +22,7 @@ class ColumnListMarkdownNode(MarkdownNode):
|
|
31
22
|
:::
|
32
23
|
"""
|
33
24
|
|
34
|
-
|
35
|
-
self.columns = columns
|
36
|
-
|
37
|
-
@classmethod
|
38
|
-
def from_params(
|
39
|
-
cls, params: ColumnListMarkdownBlockParams
|
40
|
-
) -> ColumnListMarkdownNode:
|
41
|
-
return cls(columns=params.columns)
|
25
|
+
columns: list[ColumnMarkdownNode] = []
|
42
26
|
|
43
27
|
def to_markdown(self) -> str:
|
44
28
|
if not self.columns:
|
@@ -1,20 +1,10 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
1
|
from typing import Optional
|
4
|
-
|
5
|
-
from pydantic import BaseModel
|
6
|
-
|
7
|
-
from notionary.markdown.markdown_node import MarkdownNode
|
8
|
-
|
9
|
-
|
10
|
-
class ColumnMarkdownBlockParams(BaseModel):
|
11
|
-
children: list[MarkdownNode]
|
12
|
-
width_ratio: Optional[float] = None
|
13
|
-
model_config = {"arbitrary_types_allowed": True}
|
2
|
+
from notionary.blocks.markdown.markdown_node import MarkdownNode
|
14
3
|
|
15
4
|
|
16
5
|
class ColumnMarkdownNode(MarkdownNode):
|
17
6
|
"""
|
7
|
+
Enhanced Column node with Pydantic integration.
|
18
8
|
Programmatic interface for creating a single Markdown column block
|
19
9
|
with nested content and optional width ratio.
|
20
10
|
|
@@ -32,15 +22,8 @@ class ColumnMarkdownNode(MarkdownNode):
|
|
32
22
|
:::
|
33
23
|
"""
|
34
24
|
|
35
|
-
|
36
|
-
|
37
|
-
):
|
38
|
-
self.children = children
|
39
|
-
self.width_ratio = width_ratio
|
40
|
-
|
41
|
-
@classmethod
|
42
|
-
def from_params(cls, params: ColumnMarkdownBlockParams) -> ColumnMarkdownNode:
|
43
|
-
return cls(children=params.children, width_ratio=params.width_ratio)
|
25
|
+
children: list[MarkdownNode] = []
|
26
|
+
width_ratio: Optional[float] = None
|
44
27
|
|
45
28
|
def to_markdown(self) -> str:
|
46
29
|
# Start tag with optional width ratio
|
@@ -1,6 +1,5 @@
|
|
1
1
|
from notionary.blocks.divider.divider_element import DividerElement
|
2
2
|
from notionary.blocks.divider.divider_markdown_node import (
|
3
|
-
DividerMarkdownBlockParams,
|
4
3
|
DividerMarkdownNode,
|
5
4
|
)
|
6
5
|
from notionary.blocks.divider.divider_models import CreateDividerBlock, DividerBlock
|
@@ -10,5 +9,4 @@ __all__ = [
|
|
10
9
|
"DividerBlock",
|
11
10
|
"CreateDividerBlock",
|
12
11
|
"DividerMarkdownNode",
|
13
|
-
"DividerMarkdownBlockParams",
|
14
12
|
]
|