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.
- 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.19.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 -713
- 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/elements/__init__.py +0 -0
- 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 -60
- 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.19.dist-info/RECORD +0 -150
- /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.19.dist-info → notionary-0.2.21.dist-info}/LICENSE +0 -0
- {notionary-0.2.19.dist-info → notionary-0.2.21.dist-info}/WHEEL +0 -0
@@ -1,37 +0,0 @@
|
|
1
|
-
from typing import Optional, Any, TypeAlias, Union
|
2
|
-
from abc import ABC
|
3
|
-
|
4
|
-
from notionary.blocks.prompts.element_prompt_content import ElementPromptContent
|
5
|
-
|
6
|
-
NotionBlock: TypeAlias = dict[str, Any]
|
7
|
-
NotionBlockResult: TypeAlias = Optional[Union[list[dict[str, Any]], dict[str, Any]]]
|
8
|
-
|
9
|
-
|
10
|
-
class NotionBlockElement(ABC):
|
11
|
-
"""Base class for elements that can be converted between Markdown and Notion."""
|
12
|
-
|
13
|
-
@classmethod
|
14
|
-
def markdown_to_notion(cls, text: str) -> NotionBlockResult:
|
15
|
-
"""Convert markdown to Notion blocks (can return multiple blocks or single block)."""
|
16
|
-
|
17
|
-
@classmethod
|
18
|
-
def notion_to_markdown(cls, block: dict[str, any]) -> Optional[str]:
|
19
|
-
"""Convert Notion block to markdown."""
|
20
|
-
|
21
|
-
@classmethod
|
22
|
-
def match_markdown(cls, text: str) -> bool:
|
23
|
-
"""Check if this element can handle the given markdown text."""
|
24
|
-
return bool(cls.markdown_to_notion(text)) # Now calls the class's version
|
25
|
-
|
26
|
-
@classmethod
|
27
|
-
def match_notion(cls, block: dict[str, any]) -> bool:
|
28
|
-
"""Check if this element can handle the given Notion block."""
|
29
|
-
return bool(cls.notion_to_markdown(block)) # Now calls the class's version
|
30
|
-
|
31
|
-
@classmethod
|
32
|
-
def is_multiline(cls) -> bool:
|
33
|
-
return False
|
34
|
-
|
35
|
-
@classmethod
|
36
|
-
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
37
|
-
"""Returns a dictionary with information for LLM prompts about this element."""
|
@@ -1,262 +0,0 @@
|
|
1
|
-
from typing import Any
|
2
|
-
import re
|
3
|
-
|
4
|
-
from notionary.blocks import ElementPromptBuilder, ElementPromptContent
|
5
|
-
|
6
|
-
|
7
|
-
class TextInlineFormatter:
|
8
|
-
"""
|
9
|
-
Handles conversion between Markdown inline formatting and Notion rich text elements.
|
10
|
-
|
11
|
-
Supports various formatting options:
|
12
|
-
- Bold: **text**
|
13
|
-
- Italic: *text* or _text_
|
14
|
-
- Underline: __text__
|
15
|
-
- Strikethrough: ~~text~~
|
16
|
-
- Code: `text`
|
17
|
-
- Links: [text](url)
|
18
|
-
"""
|
19
|
-
|
20
|
-
# Format patterns for matching Markdown formatting
|
21
|
-
FORMAT_PATTERNS = [
|
22
|
-
(r"\*\*(.+?)\*\*", {"bold": True}),
|
23
|
-
(r"\*(.+?)\*", {"italic": True}),
|
24
|
-
(r"_(.+?)_", {"italic": True}),
|
25
|
-
(r"__(.+?)__", {"underline": True}),
|
26
|
-
(r"~~(.+?)~~", {"strikethrough": True}),
|
27
|
-
(r"`(.+?)`", {"code": True}),
|
28
|
-
(r"\[(.+?)\]\((.+?)\)", {"link": True}),
|
29
|
-
(r"@\[([0-9a-f-]+)\]", {"mention": True}),
|
30
|
-
]
|
31
|
-
|
32
|
-
@classmethod
|
33
|
-
def parse_inline_formatting(cls, text: str) -> list[dict[str, Any]]:
|
34
|
-
"""
|
35
|
-
Parse inline text formatting into Notion rich_text format.
|
36
|
-
|
37
|
-
Args:
|
38
|
-
text: Markdown text with inline formatting
|
39
|
-
|
40
|
-
Returns:
|
41
|
-
list of Notion rich_text objects
|
42
|
-
"""
|
43
|
-
if not text:
|
44
|
-
return []
|
45
|
-
|
46
|
-
return cls._split_text_into_segments(text, cls.FORMAT_PATTERNS)
|
47
|
-
|
48
|
-
@classmethod
|
49
|
-
def _split_text_into_segments(
|
50
|
-
cls, text: str, format_patterns: list[tuple]
|
51
|
-
) -> list[dict[str, Any]]:
|
52
|
-
"""
|
53
|
-
Split text into segments by formatting markers and convert to Notion rich_text format.
|
54
|
-
|
55
|
-
Args:
|
56
|
-
text: Text to split
|
57
|
-
format_patterns: list of (regex pattern, formatting dict) tuples
|
58
|
-
|
59
|
-
Returns:
|
60
|
-
list of Notion rich_text objects
|
61
|
-
"""
|
62
|
-
segments = []
|
63
|
-
remaining_text = text
|
64
|
-
|
65
|
-
while remaining_text:
|
66
|
-
earliest_match = None
|
67
|
-
earliest_format = None
|
68
|
-
earliest_pos = len(remaining_text)
|
69
|
-
|
70
|
-
# Find the earliest formatting marker
|
71
|
-
for pattern, formatting in format_patterns:
|
72
|
-
match = re.search(pattern, remaining_text)
|
73
|
-
if match and match.start() < earliest_pos:
|
74
|
-
earliest_match = match
|
75
|
-
earliest_format = formatting
|
76
|
-
earliest_pos = match.start()
|
77
|
-
|
78
|
-
if earliest_match is None:
|
79
|
-
if remaining_text:
|
80
|
-
segments.append(cls._create_text_element(remaining_text, {}))
|
81
|
-
break
|
82
|
-
|
83
|
-
if earliest_pos > 0:
|
84
|
-
segments.append(
|
85
|
-
cls._create_text_element(remaining_text[:earliest_pos], {})
|
86
|
-
)
|
87
|
-
|
88
|
-
if "link" in earliest_format:
|
89
|
-
content = earliest_match.group(1)
|
90
|
-
url = earliest_match.group(2)
|
91
|
-
segments.append(cls._create_link_element(content, url))
|
92
|
-
|
93
|
-
elif "mention" in earliest_format:
|
94
|
-
id = earliest_match.group(1)
|
95
|
-
segments.append(cls._create_mention_element(id))
|
96
|
-
|
97
|
-
else:
|
98
|
-
content = earliest_match.group(1)
|
99
|
-
segments.append(cls._create_text_element(content, earliest_format))
|
100
|
-
|
101
|
-
# Move past the processed segment
|
102
|
-
remaining_text = remaining_text[
|
103
|
-
earliest_pos + len(earliest_match.group(0)) :
|
104
|
-
]
|
105
|
-
|
106
|
-
return segments
|
107
|
-
|
108
|
-
@classmethod
|
109
|
-
def _create_text_element(
|
110
|
-
cls, text: str, formatting: dict[str, Any]
|
111
|
-
) -> dict[str, Any]:
|
112
|
-
"""
|
113
|
-
Create a Notion text element with formatting.
|
114
|
-
|
115
|
-
Args:
|
116
|
-
text: The text content
|
117
|
-
formatting: Dictionary of formatting options
|
118
|
-
|
119
|
-
Returns:
|
120
|
-
Notion rich_text element
|
121
|
-
"""
|
122
|
-
annotations = cls._default_annotations()
|
123
|
-
|
124
|
-
# Apply formatting
|
125
|
-
for key, value in formatting.items():
|
126
|
-
if key == "color":
|
127
|
-
annotations["color"] = value
|
128
|
-
elif key in annotations:
|
129
|
-
annotations[key] = value
|
130
|
-
|
131
|
-
return {
|
132
|
-
"type": "text",
|
133
|
-
"text": {"content": text},
|
134
|
-
"annotations": annotations,
|
135
|
-
"plain_text": text,
|
136
|
-
}
|
137
|
-
|
138
|
-
@classmethod
|
139
|
-
def _create_link_element(cls, text: str, url: str) -> dict[str, Any]:
|
140
|
-
"""
|
141
|
-
Create a Notion link element.
|
142
|
-
|
143
|
-
Args:
|
144
|
-
text: The link text
|
145
|
-
url: The URL
|
146
|
-
|
147
|
-
Returns:
|
148
|
-
Notion rich_text element with link
|
149
|
-
"""
|
150
|
-
return {
|
151
|
-
"type": "text",
|
152
|
-
"text": {"content": text, "link": {"url": url}},
|
153
|
-
"annotations": cls._default_annotations(),
|
154
|
-
"plain_text": text,
|
155
|
-
}
|
156
|
-
|
157
|
-
@classmethod
|
158
|
-
def _create_mention_element(cls, id: str) -> dict[str, Any]:
|
159
|
-
"""
|
160
|
-
Create a Notion mention element.
|
161
|
-
|
162
|
-
Args:
|
163
|
-
id: The page ID
|
164
|
-
|
165
|
-
Returns:
|
166
|
-
Notion rich_text element with mention
|
167
|
-
"""
|
168
|
-
return {
|
169
|
-
"type": "mention",
|
170
|
-
"mention": {"type": "page", "page": {"id": id}},
|
171
|
-
"annotations": cls._default_annotations(),
|
172
|
-
}
|
173
|
-
|
174
|
-
@classmethod
|
175
|
-
def extract_text_with_formatting(cls, rich_text: list[dict[str, Any]]) -> str:
|
176
|
-
"""
|
177
|
-
Convert Notion rich_text elements back to Markdown formatted text.
|
178
|
-
|
179
|
-
Args:
|
180
|
-
rich_text: list of Notion rich_text elements
|
181
|
-
|
182
|
-
Returns:
|
183
|
-
Markdown formatted text
|
184
|
-
"""
|
185
|
-
formatted_parts = []
|
186
|
-
|
187
|
-
for text_obj in rich_text:
|
188
|
-
# Fallback: If plain_text is missing, use text['content']
|
189
|
-
content = text_obj.get("plain_text")
|
190
|
-
if content is None:
|
191
|
-
content = text_obj.get("text", {}).get("content", "")
|
192
|
-
|
193
|
-
annotations = text_obj.get("annotations", {})
|
194
|
-
|
195
|
-
if annotations.get("code", False):
|
196
|
-
content = f"`{content}`"
|
197
|
-
if annotations.get("strikethrough", False):
|
198
|
-
content = f"~~{content}~~"
|
199
|
-
if annotations.get("underline", False):
|
200
|
-
content = f"__{content}__"
|
201
|
-
if annotations.get("italic", False):
|
202
|
-
content = f"*{content}*"
|
203
|
-
if annotations.get("bold", False):
|
204
|
-
content = f"**{content}**"
|
205
|
-
|
206
|
-
text_data = text_obj.get("text", {})
|
207
|
-
link_data = text_data.get("link")
|
208
|
-
if link_data:
|
209
|
-
url = link_data.get("url", "")
|
210
|
-
content = f"[{content}]({url})"
|
211
|
-
|
212
|
-
formatted_parts.append(content)
|
213
|
-
|
214
|
-
return "".join(formatted_parts)
|
215
|
-
|
216
|
-
@classmethod
|
217
|
-
def _default_annotations(cls) -> dict[str, bool]:
|
218
|
-
"""
|
219
|
-
Create default annotations object.
|
220
|
-
|
221
|
-
Returns:
|
222
|
-
Default Notion text annotations
|
223
|
-
"""
|
224
|
-
return {
|
225
|
-
"bold": False,
|
226
|
-
"italic": False,
|
227
|
-
"strikethrough": False,
|
228
|
-
"underline": False,
|
229
|
-
"code": False,
|
230
|
-
"color": "default",
|
231
|
-
}
|
232
|
-
|
233
|
-
@classmethod
|
234
|
-
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
235
|
-
"""
|
236
|
-
Returns structured LLM prompt metadata for inline formatting.
|
237
|
-
"""
|
238
|
-
return (
|
239
|
-
ElementPromptBuilder()
|
240
|
-
.with_description(
|
241
|
-
"Inline formatting can be used within most block types to style your text. You can combine multiple formatting options."
|
242
|
-
)
|
243
|
-
.with_usage_guidelines(
|
244
|
-
"Use inline formatting to highlight important words, provide emphasis, show code or paths, or add hyperlinks. "
|
245
|
-
"This helps create visual hierarchy and improves readability."
|
246
|
-
)
|
247
|
-
.with_syntax(
|
248
|
-
"**bold**, *italic*, `code`, ~~strikethrough~~, __underline__, [text](url)"
|
249
|
-
)
|
250
|
-
.with_examples(
|
251
|
-
[
|
252
|
-
"This text has a **bold** word.",
|
253
|
-
"This text has an *italic* word.",
|
254
|
-
"This text has `code` formatting.",
|
255
|
-
"This text has ~~strikethrough~~ formatting.",
|
256
|
-
"This text has __underlined__ formatting.",
|
257
|
-
"This has a [hyperlink](https://example.com).",
|
258
|
-
"You can **combine *different* formatting** styles.",
|
259
|
-
]
|
260
|
-
)
|
261
|
-
.build()
|
262
|
-
)
|
@@ -1,139 +0,0 @@
|
|
1
|
-
from typing import Optional
|
2
|
-
import re
|
3
|
-
|
4
|
-
# TODO: Use this inline formatting here
|
5
|
-
from notionary.blocks.shared.models import (
|
6
|
-
MentionRichText,
|
7
|
-
RichTextObject,
|
8
|
-
TextAnnotations,
|
9
|
-
TextContent,
|
10
|
-
)
|
11
|
-
|
12
|
-
FORMAT_PATTERNS = [
|
13
|
-
(r"\*\*(.+?)\*\*", {"bold": True}),
|
14
|
-
(r"\*(.+?)\*", {"italic": True}),
|
15
|
-
(r"_(.+?)_", {"italic": True}),
|
16
|
-
(r"__(.+?)__", {"underline": True}),
|
17
|
-
(r"~~(.+?)~~", {"strikethrough": True}),
|
18
|
-
(r"`(.+?)`", {"code": True}),
|
19
|
-
(r"\[(.+?)\]\((.+?)\)", {"link": True}),
|
20
|
-
(r"@\[([0-9a-f-]+)\]", {"mention": True}),
|
21
|
-
]
|
22
|
-
|
23
|
-
|
24
|
-
def parse_inline_formatting(text: str) -> list[dict[str, any]]:
|
25
|
-
"""Parse inline text formatting into Notion rich_text format."""
|
26
|
-
if not text:
|
27
|
-
return []
|
28
|
-
|
29
|
-
return _split_text_into_segments(text)
|
30
|
-
|
31
|
-
|
32
|
-
def _split_text_into_segments(text: str) -> list[dict[str, any]]:
|
33
|
-
"""Split text into segments by formatting markers."""
|
34
|
-
segments = []
|
35
|
-
remaining_text = text
|
36
|
-
|
37
|
-
while remaining_text:
|
38
|
-
match_info = _find_earliest_match(remaining_text)
|
39
|
-
|
40
|
-
# No more formatting found - add remaining text and exit
|
41
|
-
if not match_info:
|
42
|
-
segments.append(_create_plain_text(remaining_text))
|
43
|
-
break
|
44
|
-
|
45
|
-
match, formatting, pos = match_info
|
46
|
-
|
47
|
-
# Add text before match if exists
|
48
|
-
if pos > 0:
|
49
|
-
segments.append(_create_plain_text(remaining_text[:pos]))
|
50
|
-
|
51
|
-
# Add formatted segment
|
52
|
-
segments.append(_create_formatted_segment(match, formatting))
|
53
|
-
|
54
|
-
# Update remaining text
|
55
|
-
remaining_text = remaining_text[pos + len(match.group(0)) :]
|
56
|
-
|
57
|
-
return segments
|
58
|
-
|
59
|
-
|
60
|
-
def _find_earliest_match(text: str) -> Optional[tuple]:
|
61
|
-
"""Find the earliest formatting match in text."""
|
62
|
-
earliest_match = None
|
63
|
-
earliest_format = None
|
64
|
-
earliest_pos = len(text)
|
65
|
-
|
66
|
-
for pattern, formatting in FORMAT_PATTERNS:
|
67
|
-
match = re.search(pattern, text)
|
68
|
-
if match and match.start() < earliest_pos:
|
69
|
-
earliest_match = match
|
70
|
-
earliest_format = formatting
|
71
|
-
earliest_pos = match.start()
|
72
|
-
|
73
|
-
return (earliest_match, earliest_format, earliest_pos) if earliest_match else None
|
74
|
-
|
75
|
-
|
76
|
-
def _create_formatted_segment(match: re.Match, formatting: dict) -> dict[str, any]:
|
77
|
-
"""Create a formatted segment based on match and formatting."""
|
78
|
-
if "link" in formatting:
|
79
|
-
return _create_link_text(match.group(1), match.group(2))
|
80
|
-
elif "mention" in formatting:
|
81
|
-
return _create_mention_text(match.group(1))
|
82
|
-
else:
|
83
|
-
return _create_formatted_text(match.group(1), **formatting)
|
84
|
-
|
85
|
-
|
86
|
-
def _create_plain_text(content: str) -> dict[str, any]:
|
87
|
-
"""Create plain text rich text object."""
|
88
|
-
return RichTextObject.from_plain_text(content).model_dump()
|
89
|
-
|
90
|
-
|
91
|
-
def _create_formatted_text(content: str, **formatting) -> dict[str, any]:
|
92
|
-
"""Create formatted text rich text object."""
|
93
|
-
return RichTextObject.from_plain_text(content, **formatting).model_dump()
|
94
|
-
|
95
|
-
|
96
|
-
def _create_link_text(content: str, url: str) -> dict[str, any]:
|
97
|
-
"""Create link text rich text object."""
|
98
|
-
text_content = TextContent(content=content, link=url)
|
99
|
-
annotations = TextAnnotations()
|
100
|
-
|
101
|
-
rich_text = RichTextObject(
|
102
|
-
text=text_content, annotations=annotations, plain_text=content, href=url
|
103
|
-
)
|
104
|
-
return rich_text.model_dump()
|
105
|
-
|
106
|
-
|
107
|
-
def _create_mention_text(page_id: str) -> dict[str, any]:
|
108
|
-
"""Create mention rich text object."""
|
109
|
-
return MentionRichText.from_page_id(page_id).model_dump()
|
110
|
-
|
111
|
-
|
112
|
-
def extract_text_with_formatting(rich_text: list[dict[str, any]]) -> str:
|
113
|
-
"""Convert Notion rich_text elements back to Markdown."""
|
114
|
-
return "".join(_rich_text_to_markdown(item) for item in rich_text)
|
115
|
-
|
116
|
-
|
117
|
-
def _rich_text_to_markdown(text_obj: dict[str, any]) -> str:
|
118
|
-
"""Convert single rich text object to markdown."""
|
119
|
-
content = text_obj.get("plain_text", text_obj.get("text", {}).get("content", ""))
|
120
|
-
annotations = text_obj.get("annotations", {})
|
121
|
-
|
122
|
-
# Apply formatting in reverse order
|
123
|
-
if annotations.get("code", False):
|
124
|
-
content = f"`{content}`"
|
125
|
-
if annotations.get("strikethrough", False):
|
126
|
-
content = f"~~{content}~~"
|
127
|
-
if annotations.get("underline", False):
|
128
|
-
content = f"__{content}__"
|
129
|
-
if annotations.get("italic", False):
|
130
|
-
content = f"*{content}*"
|
131
|
-
if annotations.get("bold", False):
|
132
|
-
content = f"**{content}**"
|
133
|
-
|
134
|
-
# Handle links
|
135
|
-
link_data = text_obj.get("text", {}).get("link")
|
136
|
-
if link_data and link_data.get("url"):
|
137
|
-
content = f"[{content}]({link_data['url']})"
|
138
|
-
|
139
|
-
return content
|
File without changes
|
notionary/elements/__init__.py
DELETED
File without changes
|