notionary 0.2.26__py3-none-any.whl → 0.2.28__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 +5 -20
- notionary/blocks/client.py +87 -215
- notionary/blocks/enums.py +167 -0
- notionary/blocks/rich_text/markdown_rich_text_converter.py +266 -0
- notionary/blocks/rich_text/models.py +164 -0
- notionary/blocks/rich_text/name_id_resolver/__init__.py +11 -0
- notionary/blocks/rich_text/name_id_resolver/database.py +31 -0
- notionary/blocks/rich_text/name_id_resolver/page.py +34 -0
- notionary/blocks/rich_text/name_id_resolver/person.py +37 -0
- notionary/blocks/rich_text/name_id_resolver/port.py +11 -0
- notionary/blocks/rich_text/rich_text_markdown_converter.py +132 -0
- notionary/blocks/rich_text/rich_text_patterns.py +39 -0
- notionary/blocks/schemas.py +746 -0
- notionary/comments/client.py +52 -187
- notionary/comments/factory.py +40 -0
- notionary/comments/models.py +5 -127
- notionary/comments/schemas.py +240 -0
- notionary/comments/service.py +34 -0
- notionary/data_source/http/client.py +11 -0
- notionary/data_source/http/data_source_instance_client.py +94 -0
- notionary/data_source/properties/models.py +406 -0
- notionary/data_source/query/builder.py +429 -0
- notionary/data_source/query/resolver.py +114 -0
- notionary/data_source/query/schema.py +304 -0
- notionary/data_source/query/validator.py +73 -0
- notionary/data_source/schemas.py +27 -0
- notionary/data_source/service.py +353 -0
- notionary/database/client.py +30 -135
- notionary/database/database_metadata_update_client.py +19 -0
- notionary/database/schemas.py +29 -0
- notionary/database/service.py +169 -0
- notionary/exceptions/__init__.py +33 -0
- notionary/exceptions/api.py +41 -0
- notionary/exceptions/base.py +2 -0
- notionary/exceptions/block_parsing.py +16 -0
- notionary/exceptions/data_source/__init__.py +6 -0
- notionary/exceptions/data_source/builder.py +182 -0
- notionary/exceptions/data_source/properties.py +34 -0
- notionary/exceptions/properties.py +58 -0
- notionary/exceptions/search.py +33 -0
- notionary/file_upload/client.py +18 -30
- notionary/file_upload/models.py +7 -8
- notionary/file_upload/{notion_file_upload.py → service.py} +29 -64
- notionary/http/client.py +205 -0
- notionary/http/models.py +49 -0
- notionary/page/blocks/client.py +1 -0
- notionary/page/content/factory.py +68 -0
- notionary/page/content/markdown/__init__.py +5 -0
- notionary/page/content/markdown/builder.py +304 -0
- notionary/page/content/markdown/nodes/__init__.py +54 -0
- notionary/page/content/markdown/nodes/audio.py +23 -0
- notionary/page/content/markdown/nodes/base.py +12 -0
- notionary/page/content/markdown/nodes/bookmark.py +25 -0
- notionary/page/content/markdown/nodes/breadcrumb.py +14 -0
- notionary/page/content/markdown/nodes/bulleted_list.py +18 -0
- notionary/page/content/markdown/nodes/callout.py +32 -0
- notionary/page/content/markdown/nodes/code.py +30 -0
- notionary/page/content/markdown/nodes/columns.py +51 -0
- notionary/page/content/markdown/nodes/divider.py +14 -0
- notionary/page/content/markdown/nodes/embed.py +23 -0
- notionary/page/content/markdown/nodes/equation.py +19 -0
- notionary/page/content/markdown/nodes/file.py +23 -0
- notionary/page/content/markdown/nodes/heading.py +16 -0
- notionary/page/content/markdown/nodes/image.py +23 -0
- notionary/page/content/markdown/nodes/mixins/caption.py +12 -0
- notionary/page/content/markdown/nodes/numbered_list.py +15 -0
- notionary/page/content/markdown/nodes/paragraph.py +14 -0
- notionary/page/content/markdown/nodes/pdf.py +23 -0
- notionary/page/content/markdown/nodes/quote.py +15 -0
- notionary/page/content/markdown/nodes/space.py +14 -0
- notionary/page/content/markdown/nodes/table.py +45 -0
- notionary/page/content/markdown/nodes/table_of_contents.py +14 -0
- notionary/page/content/markdown/nodes/todo.py +22 -0
- notionary/page/content/markdown/nodes/toggle.py +28 -0
- notionary/page/content/markdown/nodes/toggleable_heading.py +35 -0
- notionary/page/content/markdown/nodes/video.py +23 -0
- notionary/page/content/parser/context.py +49 -0
- notionary/page/content/parser/factory.py +219 -0
- notionary/page/content/parser/parsers/__init__.py +60 -0
- notionary/page/content/parser/parsers/audio.py +40 -0
- notionary/page/content/parser/parsers/base.py +30 -0
- notionary/page/content/parser/parsers/bookmark.py +33 -0
- notionary/page/content/parser/parsers/breadcrumb.py +33 -0
- notionary/page/content/parser/parsers/bulleted_list.py +41 -0
- notionary/page/content/parser/parsers/callout.py +129 -0
- notionary/page/content/parser/parsers/caption.py +55 -0
- notionary/page/content/parser/parsers/code.py +81 -0
- notionary/page/content/parser/parsers/column.py +117 -0
- notionary/page/content/parser/parsers/column_list.py +81 -0
- notionary/page/content/parser/parsers/divider.py +33 -0
- notionary/page/content/parser/parsers/embed.py +33 -0
- notionary/page/content/parser/parsers/equation.py +65 -0
- notionary/page/content/parser/parsers/file.py +42 -0
- notionary/page/content/parser/parsers/heading.py +58 -0
- notionary/page/content/parser/parsers/image.py +42 -0
- notionary/page/content/parser/parsers/numbered_list.py +45 -0
- notionary/page/content/parser/parsers/paragraph.py +36 -0
- notionary/page/content/parser/parsers/pdf.py +42 -0
- notionary/page/content/parser/parsers/quote.py +65 -0
- notionary/page/content/parser/parsers/space.py +35 -0
- notionary/page/content/parser/parsers/table.py +144 -0
- notionary/page/content/parser/parsers/table_of_contents.py +32 -0
- notionary/page/content/parser/parsers/todo.py +58 -0
- notionary/page/content/parser/parsers/toggle.py +127 -0
- notionary/page/content/parser/parsers/toggleable_heading.py +150 -0
- notionary/page/content/parser/parsers/video.py +42 -0
- notionary/page/content/parser/post_processing/handlers/__init__.py +5 -0
- notionary/page/content/parser/post_processing/handlers/rich_text_length.py +93 -0
- notionary/page/content/parser/post_processing/handlers/rich_text_length_truncation.py +93 -0
- notionary/page/content/parser/post_processing/port.py +9 -0
- notionary/page/content/parser/post_processing/service.py +16 -0
- notionary/page/content/parser/pre_processsing/handlers/__init__.py +9 -0
- notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +80 -0
- notionary/page/content/parser/pre_processsing/handlers/port.py +7 -0
- notionary/page/content/parser/pre_processsing/handlers/whitespace.py +68 -0
- notionary/page/content/parser/pre_processsing/service.py +15 -0
- notionary/page/content/parser/service.py +69 -0
- notionary/page/content/renderer/context.py +48 -0
- notionary/page/content/renderer/factory.py +240 -0
- notionary/page/content/renderer/post_processing/handlers/__init__.py +5 -0
- notionary/page/content/renderer/post_processing/handlers/numbered_list_placeholdere.py +62 -0
- notionary/page/content/renderer/post_processing/port.py +7 -0
- notionary/page/content/renderer/post_processing/service.py +15 -0
- notionary/page/content/renderer/renderers/__init__.py +57 -0
- notionary/page/content/renderer/renderers/audio.py +31 -0
- notionary/page/content/renderer/renderers/base.py +31 -0
- notionary/page/content/renderer/renderers/bookmark.py +25 -0
- notionary/page/content/renderer/renderers/breadcrumb.py +21 -0
- notionary/page/content/renderer/renderers/bulleted_list.py +48 -0
- notionary/page/content/renderer/renderers/callout.py +65 -0
- notionary/page/content/renderer/renderers/captioned_block.py +58 -0
- notionary/page/content/renderer/renderers/code.py +34 -0
- notionary/page/content/renderer/renderers/column.py +44 -0
- notionary/page/content/renderer/renderers/column_list.py +31 -0
- notionary/page/content/renderer/renderers/divider.py +22 -0
- notionary/page/content/renderer/renderers/embed.py +25 -0
- notionary/page/content/renderer/renderers/equation.py +37 -0
- notionary/page/content/renderer/renderers/fallback.py +24 -0
- notionary/page/content/renderer/renderers/file.py +40 -0
- notionary/page/content/renderer/renderers/heading.py +69 -0
- notionary/page/content/renderer/renderers/image.py +31 -0
- notionary/page/content/renderer/renderers/numbered_list.py +41 -0
- notionary/page/content/renderer/renderers/paragraph.py +40 -0
- notionary/page/content/renderer/renderers/pdf.py +31 -0
- notionary/page/content/renderer/renderers/quote.py +49 -0
- notionary/page/content/renderer/renderers/table.py +115 -0
- notionary/page/content/renderer/renderers/table_of_contents.py +26 -0
- notionary/page/content/renderer/renderers/table_row.py +17 -0
- notionary/page/content/renderer/renderers/todo.py +56 -0
- notionary/page/content/renderer/renderers/toggle.py +53 -0
- notionary/page/content/renderer/renderers/toggleable_heading.py +78 -0
- notionary/page/content/renderer/renderers/video.py +31 -0
- notionary/page/content/renderer/service.py +50 -0
- notionary/page/content/service.py +65 -0
- notionary/page/content/syntax/models.py +68 -0
- notionary/page/content/syntax/service.py +453 -0
- notionary/page/page_context.py +7 -16
- notionary/page/page_http_client.py +15 -0
- notionary/page/page_metadata_update_client.py +19 -0
- notionary/page/properties/client.py +144 -0
- notionary/page/properties/factory.py +26 -0
- notionary/page/properties/models.py +307 -0
- notionary/page/properties/service.py +257 -0
- notionary/page/schemas.py +13 -0
- notionary/page/service.py +222 -0
- notionary/shared/entity/client.py +29 -0
- notionary/shared/entity/dto_parsers.py +53 -0
- notionary/shared/entity/entity_metadata_update_client.py +41 -0
- notionary/shared/entity/schemas.py +45 -0
- notionary/shared/entity/service.py +171 -0
- notionary/shared/models/cover.py +20 -0
- notionary/shared/models/file.py +21 -0
- notionary/shared/models/icon.py +28 -0
- notionary/shared/models/parent.py +41 -0
- notionary/shared/properties/type.py +30 -0
- notionary/user/__init__.py +4 -8
- notionary/user/base.py +89 -0
- notionary/user/bot.py +70 -0
- notionary/user/client.py +22 -111
- notionary/user/person.py +41 -0
- notionary/user/schemas.py +67 -0
- notionary/user/service.py +65 -0
- notionary/utils/async_retry.py +39 -0
- notionary/utils/date.py +51 -0
- notionary/utils/fuzzy.py +56 -0
- notionary/{util/logging_mixin.py → utils/mixins/logging.py} +4 -16
- notionary/utils/pagination.py +50 -0
- notionary/utils/singleton.py +13 -0
- notionary/utils/uuid_utils.py +20 -0
- notionary/workspace/__init__.py +3 -0
- notionary/workspace/client.py +62 -0
- notionary/workspace/query/builder.py +60 -0
- notionary/workspace/query/models.py +60 -0
- notionary/workspace/query/service.py +93 -0
- notionary/workspace/schemas.py +21 -0
- notionary/workspace/service.py +116 -0
- {notionary-0.2.26.dist-info → notionary-0.2.28.dist-info}/METADATA +54 -49
- notionary-0.2.28.dist-info/RECORD +200 -0
- {notionary-0.2.26.dist-info → notionary-0.2.28.dist-info}/WHEEL +1 -1
- {notionary-0.2.26.dist-info → notionary-0.2.28.dist-info/licenses}/LICENSE +9 -9
- notionary/base_notion_client.py +0 -219
- notionary/blocks/__init__.py +0 -5
- notionary/blocks/_bootstrap.py +0 -271
- notionary/blocks/audio/__init__.py +0 -11
- notionary/blocks/audio/audio_element.py +0 -158
- notionary/blocks/audio/audio_markdown_node.py +0 -24
- notionary/blocks/audio/audio_models.py +0 -10
- notionary/blocks/base_block_element.py +0 -42
- notionary/blocks/bookmark/__init__.py +0 -12
- notionary/blocks/bookmark/bookmark_element.py +0 -83
- notionary/blocks/bookmark/bookmark_markdown_node.py +0 -28
- notionary/blocks/bookmark/bookmark_models.py +0 -15
- notionary/blocks/breadcrumbs/__init__.py +0 -15
- notionary/blocks/breadcrumbs/breadcrumb_element.py +0 -39
- notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +0 -13
- notionary/blocks/breadcrumbs/breadcrumb_models.py +0 -12
- notionary/blocks/bulleted_list/__init__.py +0 -15
- notionary/blocks/bulleted_list/bulleted_list_element.py +0 -74
- notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +0 -20
- notionary/blocks/bulleted_list/bulleted_list_models.py +0 -17
- notionary/blocks/callout/__init__.py +0 -12
- notionary/blocks/callout/callout_element.py +0 -99
- notionary/blocks/callout/callout_markdown_node.py +0 -19
- notionary/blocks/callout/callout_models.py +0 -33
- notionary/blocks/child_database/__init__.py +0 -14
- notionary/blocks/child_database/child_database_element.py +0 -59
- notionary/blocks/child_database/child_database_models.py +0 -12
- notionary/blocks/child_page/__init__.py +0 -9
- notionary/blocks/child_page/child_page_element.py +0 -94
- notionary/blocks/child_page/child_page_models.py +0 -12
- notionary/blocks/code/__init__.py +0 -11
- notionary/blocks/code/code_element.py +0 -149
- notionary/blocks/code/code_markdown_node.py +0 -80
- notionary/blocks/code/code_models.py +0 -94
- notionary/blocks/column/__init__.py +0 -25
- notionary/blocks/column/column_element.py +0 -65
- notionary/blocks/column/column_list_element.py +0 -52
- notionary/blocks/column/column_list_markdown_node.py +0 -34
- notionary/blocks/column/column_markdown_node.py +0 -42
- notionary/blocks/column/column_models.py +0 -26
- notionary/blocks/divider/__init__.py +0 -12
- notionary/blocks/divider/divider_element.py +0 -41
- notionary/blocks/divider/divider_markdown_node.py +0 -11
- notionary/blocks/divider/divider_models.py +0 -12
- notionary/blocks/embed/__init__.py +0 -12
- notionary/blocks/embed/embed_element.py +0 -98
- notionary/blocks/embed/embed_markdown_node.py +0 -19
- notionary/blocks/embed/embed_models.py +0 -14
- notionary/blocks/equation/__init__.py +0 -13
- notionary/blocks/equation/equation_element.py +0 -133
- notionary/blocks/equation/equation_element_markdown_node.py +0 -23
- notionary/blocks/equation/equation_models.py +0 -11
- notionary/blocks/file/__init__.py +0 -23
- notionary/blocks/file/file_element.py +0 -133
- notionary/blocks/file/file_element_markdown_node.py +0 -24
- notionary/blocks/file/file_element_models.py +0 -39
- notionary/blocks/heading/__init__.py +0 -19
- notionary/blocks/heading/heading_element.py +0 -112
- notionary/blocks/heading/heading_markdown_node.py +0 -16
- notionary/blocks/heading/heading_models.py +0 -29
- notionary/blocks/image_block/__init__.py +0 -11
- notionary/blocks/image_block/image_element.py +0 -130
- notionary/blocks/image_block/image_markdown_node.py +0 -25
- notionary/blocks/image_block/image_models.py +0 -10
- notionary/blocks/markdown/markdown_builder.py +0 -525
- notionary/blocks/markdown/markdown_document_model.py +0 -0
- notionary/blocks/markdown/markdown_node.py +0 -25
- notionary/blocks/mixins/captions/__init__.py +0 -4
- notionary/blocks/mixins/captions/caption_markdown_node_mixin.py +0 -31
- notionary/blocks/mixins/captions/caption_mixin.py +0 -92
- notionary/blocks/mixins/file_upload/__init__.py +0 -3
- notionary/blocks/mixins/file_upload/file_upload_mixin.py +0 -320
- notionary/blocks/models.py +0 -174
- notionary/blocks/numbered_list/__init__.py +0 -16
- notionary/blocks/numbered_list/numbered_list_element.py +0 -65
- notionary/blocks/numbered_list/numbered_list_markdown_node.py +0 -17
- notionary/blocks/numbered_list/numbered_list_models.py +0 -17
- notionary/blocks/paragraph/__init__.py +0 -15
- notionary/blocks/paragraph/paragraph_element.py +0 -58
- notionary/blocks/paragraph/paragraph_markdown_node.py +0 -16
- notionary/blocks/paragraph/paragraph_models.py +0 -16
- notionary/blocks/pdf/__init__.py +0 -11
- notionary/blocks/pdf/pdf_element.py +0 -146
- notionary/blocks/pdf/pdf_markdown_node.py +0 -24
- notionary/blocks/pdf/pdf_models.py +0 -11
- notionary/blocks/quote/__init__.py +0 -14
- notionary/blocks/quote/quote_element.py +0 -75
- notionary/blocks/quote/quote_markdown_node.py +0 -16
- notionary/blocks/quote/quote_models.py +0 -18
- notionary/blocks/registry/__init__.py +0 -3
- notionary/blocks/registry/block_registry.py +0 -150
- notionary/blocks/rich_text/__init__.py +0 -33
- notionary/blocks/rich_text/rich_text_models.py +0 -221
- notionary/blocks/rich_text/text_inline_formatter.py +0 -456
- notionary/blocks/syntax_prompt_builder.py +0 -137
- notionary/blocks/table/__init__.py +0 -19
- notionary/blocks/table/table_element.py +0 -225
- notionary/blocks/table/table_markdown_node.py +0 -42
- notionary/blocks/table/table_models.py +0 -28
- notionary/blocks/table_of_contents/__init__.py +0 -17
- notionary/blocks/table_of_contents/table_of_contents_element.py +0 -80
- notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +0 -21
- notionary/blocks/table_of_contents/table_of_contents_models.py +0 -18
- notionary/blocks/todo/__init__.py +0 -12
- notionary/blocks/todo/todo_element.py +0 -81
- notionary/blocks/todo/todo_markdown_node.py +0 -21
- notionary/blocks/todo/todo_models.py +0 -18
- notionary/blocks/toggle/__init__.py +0 -12
- notionary/blocks/toggle/toggle_element.py +0 -112
- notionary/blocks/toggle/toggle_markdown_node.py +0 -31
- notionary/blocks/toggle/toggle_models.py +0 -17
- notionary/blocks/toggleable_heading/__init__.py +0 -11
- notionary/blocks/toggleable_heading/toggleable_heading_element.py +0 -115
- notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +0 -34
- notionary/blocks/types.py +0 -130
- notionary/blocks/video/__init__.py +0 -11
- notionary/blocks/video/video_element.py +0 -187
- notionary/blocks/video/video_element_models.py +0 -10
- notionary/blocks/video/video_markdown_node.py +0 -26
- notionary/comments/__init__.py +0 -26
- notionary/database/__init__.py +0 -4
- notionary/database/database.py +0 -480
- notionary/database/database_filter_builder.py +0 -173
- notionary/database/database_provider.py +0 -227
- notionary/database/exceptions.py +0 -13
- notionary/database/factory.py +0 -0
- notionary/database/models.py +0 -337
- notionary/database/notion_database.py +0 -487
- notionary/file_upload/__init__.py +0 -7
- notionary/page/client.py +0 -124
- notionary/page/markdown_whitespace_processor.py +0 -129
- notionary/page/models.py +0 -322
- notionary/page/notion_page.py +0 -674
- notionary/page/page_content_deleting_service.py +0 -117
- notionary/page/page_content_writer.py +0 -80
- notionary/page/property_formatter.py +0 -99
- notionary/page/reader/handler/__init__.py +0 -19
- notionary/page/reader/handler/base_block_renderer.py +0 -44
- notionary/page/reader/handler/block_processing_context.py +0 -35
- notionary/page/reader/handler/block_rendering_context.py +0 -48
- notionary/page/reader/handler/column_list_renderer.py +0 -51
- notionary/page/reader/handler/column_renderer.py +0 -60
- notionary/page/reader/handler/equation_renderer.py +0 -0
- notionary/page/reader/handler/line_renderer.py +0 -73
- notionary/page/reader/handler/numbered_list_renderer.py +0 -85
- notionary/page/reader/handler/toggle_renderer.py +0 -69
- notionary/page/reader/handler/toggleable_heading_renderer.py +0 -89
- notionary/page/reader/page_content_retriever.py +0 -81
- notionary/page/search_filter_builder.py +0 -132
- notionary/page/utils.py +0 -60
- notionary/page/writer/handler/__init__.py +0 -24
- notionary/page/writer/handler/code_handler.py +0 -72
- notionary/page/writer/handler/column_handler.py +0 -141
- notionary/page/writer/handler/column_list_handler.py +0 -139
- notionary/page/writer/handler/equation_handler.py +0 -74
- notionary/page/writer/handler/line_handler.py +0 -35
- notionary/page/writer/handler/line_processing_context.py +0 -54
- notionary/page/writer/handler/regular_line_handler.py +0 -86
- notionary/page/writer/handler/table_handler.py +0 -66
- notionary/page/writer/handler/toggle_handler.py +0 -159
- notionary/page/writer/handler/toggleable_heading_handler.py +0 -174
- notionary/page/writer/markdown_to_notion_converter.py +0 -139
- notionary/page/writer/markdown_to_notion_converter_context.py +0 -30
- notionary/page/writer/markdown_to_notion_text_length_post_processor.py +0 -0
- notionary/page/writer/notion_text_length_processor.py +0 -150
- notionary/schemas/__init__.py +0 -3
- notionary/schemas/base.py +0 -73
- notionary/shared/__init__.py +0 -3
- notionary/shared/name_to_id_resolver.py +0 -203
- notionary/telemetry/__init__.py +0 -19
- notionary/telemetry/service.py +0 -136
- notionary/telemetry/views.py +0 -73
- notionary/user/base_notion_user.py +0 -53
- notionary/user/models.py +0 -84
- notionary/user/notion_bot_user.py +0 -226
- notionary/user/notion_user.py +0 -255
- notionary/user/notion_user_manager.py +0 -101
- notionary/util/__init__.py +0 -15
- notionary/util/concurrency_limiter.py +0 -0
- notionary/util/factory_decorator.py +0 -0
- notionary/util/factory_only.py +0 -37
- notionary/util/fuzzy.py +0 -75
- notionary/util/page_id_utils.py +0 -27
- notionary/util/singleton.py +0 -18
- notionary/util/singleton_metaclass.py +0 -22
- notionary/workspace.py +0 -105
- notionary-0.2.26.dist-info/RECORD +0 -202
@@ -1,98 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import re
|
4
|
-
from typing import Optional
|
5
|
-
|
6
|
-
from notionary.blocks.base_block_element import BaseBlockElement
|
7
|
-
from notionary.blocks.embed.embed_models import CreateEmbedBlock, EmbedBlock
|
8
|
-
from notionary.blocks.file.file_element_models import (
|
9
|
-
ExternalFile,
|
10
|
-
FileUploadFile,
|
11
|
-
NotionHostedFile,
|
12
|
-
)
|
13
|
-
from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformation
|
14
|
-
from notionary.blocks.models import Block, BlockCreateResult, BlockType
|
15
|
-
from notionary.blocks.rich_text.rich_text_models import RichTextObject
|
16
|
-
from notionary.blocks.rich_text.text_inline_formatter import TextInlineFormatter
|
17
|
-
|
18
|
-
|
19
|
-
class EmbedElement(BaseBlockElement):
|
20
|
-
"""
|
21
|
-
Handles conversion between Markdown embeds and Notion embed blocks.
|
22
|
-
|
23
|
-
Markdown embed syntax:
|
24
|
-
- [embed](https://example.com) - URL only
|
25
|
-
- [embed](https://example.com "Caption") - URL + caption
|
26
|
-
"""
|
27
|
-
|
28
|
-
PATTERN = re.compile(
|
29
|
-
r"^\[embed\]\(" # prefix
|
30
|
-
r"(https?://[^\s\"]+)" # URL
|
31
|
-
r"(?:\s+\"([^\"]+)\")?" # optional caption
|
32
|
-
r"\)$"
|
33
|
-
)
|
34
|
-
|
35
|
-
@classmethod
|
36
|
-
def match_notion(cls, block: Block) -> bool:
|
37
|
-
return block.type == BlockType.EMBED and block.embed
|
38
|
-
|
39
|
-
@classmethod
|
40
|
-
async def markdown_to_notion(cls, text: str) -> BlockCreateResult:
|
41
|
-
"""Convert markdown embed syntax to Notion EmbedBlock."""
|
42
|
-
match = cls.PATTERN.match(text.strip())
|
43
|
-
if not match:
|
44
|
-
return None
|
45
|
-
|
46
|
-
url, rich_text = match.group(1), match.group(2) or ""
|
47
|
-
|
48
|
-
# Build EmbedBlock
|
49
|
-
embed_block = EmbedBlock(url=url, caption=[])
|
50
|
-
if rich_text.strip():
|
51
|
-
rich_text_obj = RichTextObject.from_plain_text(rich_text.strip())
|
52
|
-
embed_block.caption = [rich_text_obj]
|
53
|
-
|
54
|
-
return CreateEmbedBlock(embed=embed_block)
|
55
|
-
|
56
|
-
@classmethod
|
57
|
-
async def notion_to_markdown(cls, block: Block) -> Optional[str]:
|
58
|
-
if block.type != BlockType.EMBED or not block.embed:
|
59
|
-
return None
|
60
|
-
|
61
|
-
fo = block.embed
|
62
|
-
|
63
|
-
if isinstance(fo, (ExternalFile, NotionHostedFile)):
|
64
|
-
url = fo.url
|
65
|
-
elif isinstance(fo, FileUploadFile):
|
66
|
-
return None
|
67
|
-
else:
|
68
|
-
return None
|
69
|
-
|
70
|
-
if not fo.caption:
|
71
|
-
return f"[embed]({url})"
|
72
|
-
|
73
|
-
text_parts = []
|
74
|
-
for rt in fo.caption:
|
75
|
-
if rt.plain_text:
|
76
|
-
text_parts.append(rt.plain_text)
|
77
|
-
else:
|
78
|
-
formatted_text = await TextInlineFormatter.extract_text_with_formatting(
|
79
|
-
[rt]
|
80
|
-
)
|
81
|
-
text_parts.append(formatted_text)
|
82
|
-
text = "".join(text_parts)
|
83
|
-
|
84
|
-
return f'[embed]({url} "{text}")'
|
85
|
-
|
86
|
-
@classmethod
|
87
|
-
def get_system_prompt_information(cls) -> Optional[BlockElementMarkdownInformation]:
|
88
|
-
"""Get system prompt information for embed blocks."""
|
89
|
-
return BlockElementMarkdownInformation(
|
90
|
-
block_type=cls.__name__,
|
91
|
-
description="Embed blocks display interactive content from external URLs like videos, maps, or widgets",
|
92
|
-
syntax_examples=[
|
93
|
-
"[embed](https://youtube.com/watch?v=123)",
|
94
|
-
'[embed](https://maps.google.com/location "Map Location")',
|
95
|
-
'[embed](https://codepen.io/pen/123 "Interactive Demo")',
|
96
|
-
],
|
97
|
-
usage_guidelines="Use for embedding interactive content that supports iframe embedding. URL must be from a supported platform. Caption describes the embedded content.",
|
98
|
-
)
|
@@ -1,19 +0,0 @@
|
|
1
|
-
from typing import Optional
|
2
|
-
|
3
|
-
from notionary.blocks.markdown.markdown_node import MarkdownNode
|
4
|
-
|
5
|
-
|
6
|
-
class EmbedMarkdownNode(MarkdownNode):
|
7
|
-
"""
|
8
|
-
Enhanced Embed node with Pydantic integration.
|
9
|
-
Programmatic interface for creating Notion-style Markdown embed blocks.
|
10
|
-
Example: [embed](https://example.com "Optional caption")
|
11
|
-
"""
|
12
|
-
|
13
|
-
url: str
|
14
|
-
caption: Optional[str] = None
|
15
|
-
|
16
|
-
def to_markdown(self) -> str:
|
17
|
-
if self.caption:
|
18
|
-
return f'[embed]({self.url} "{self.caption}")'
|
19
|
-
return f"[embed]({self.url})"
|
@@ -1,14 +0,0 @@
|
|
1
|
-
from pydantic import BaseModel, Field
|
2
|
-
from typing_extensions import Literal
|
3
|
-
|
4
|
-
from notionary.blocks.rich_text.rich_text_models import RichTextObject
|
5
|
-
|
6
|
-
|
7
|
-
class EmbedBlock(BaseModel):
|
8
|
-
url: str
|
9
|
-
caption: list[RichTextObject] = Field(default_factory=list)
|
10
|
-
|
11
|
-
|
12
|
-
class CreateEmbedBlock(BaseModel):
|
13
|
-
type: Literal["embed"] = "embed"
|
14
|
-
embed: EmbedBlock
|
@@ -1,13 +0,0 @@
|
|
1
|
-
from notionary.blocks.equation.equation_element import EquationElement
|
2
|
-
from notionary.blocks.equation.equation_element_markdown_node import (
|
3
|
-
EquationMarkdownNode,
|
4
|
-
)
|
5
|
-
from notionary.blocks.equation.equation_models import CreateEquationBlock, EquationBlock
|
6
|
-
|
7
|
-
__all__ = [
|
8
|
-
"EquationElement",
|
9
|
-
"EquationBlock",
|
10
|
-
"CreateEquationBlock",
|
11
|
-
"EquationMarkdownNode",
|
12
|
-
"EquationMarkdownBlockParams",
|
13
|
-
]
|
@@ -1,133 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import re
|
4
|
-
import textwrap
|
5
|
-
from typing import Optional
|
6
|
-
|
7
|
-
from notionary.blocks.base_block_element import BaseBlockElement
|
8
|
-
from notionary.blocks.equation.equation_models import CreateEquationBlock, EquationBlock
|
9
|
-
from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformation
|
10
|
-
from notionary.blocks.models import Block, BlockCreateResult
|
11
|
-
from notionary.blocks.types import BlockType
|
12
|
-
|
13
|
-
|
14
|
-
class EquationElement(BaseBlockElement):
|
15
|
-
"""
|
16
|
-
Supports standard Markdown equation syntax:
|
17
|
-
|
18
|
-
- $$E = mc^2$$ # simple equations
|
19
|
-
- $$E = mc^2 + \\frac{a}{b}$$ # complex equations with LaTeX
|
20
|
-
|
21
|
-
Uses $$...$$ parsing for block equations.
|
22
|
-
"""
|
23
|
-
|
24
|
-
_EQUATION_PATTERN = re.compile(
|
25
|
-
r"^\$\$\s*(?P<expression>.*?)\s*\$\$$",
|
26
|
-
re.DOTALL,
|
27
|
-
)
|
28
|
-
|
29
|
-
@classmethod
|
30
|
-
def match_notion(cls, block: Block) -> bool:
|
31
|
-
return block.type == BlockType.EQUATION and block.equation
|
32
|
-
|
33
|
-
@classmethod
|
34
|
-
async def markdown_to_notion(cls, text: str) -> BlockCreateResult:
|
35
|
-
input_text = text.strip()
|
36
|
-
|
37
|
-
equation_match = cls._EQUATION_PATTERN.match(input_text)
|
38
|
-
if not equation_match:
|
39
|
-
return None
|
40
|
-
|
41
|
-
expression = equation_match.group("expression").strip()
|
42
|
-
if not expression:
|
43
|
-
return None
|
44
|
-
|
45
|
-
return CreateEquationBlock(equation=EquationBlock(expression=expression))
|
46
|
-
|
47
|
-
@classmethod
|
48
|
-
def create_from_markdown_block(
|
49
|
-
cls, opening_line: str, equation_lines: list[str]
|
50
|
-
) -> BlockCreateResult:
|
51
|
-
"""
|
52
|
-
Create a complete equation block from markdown components.
|
53
|
-
Handles multiline equations like:
|
54
|
-
$$
|
55
|
-
some
|
56
|
-
inline formula here
|
57
|
-
$$
|
58
|
-
|
59
|
-
Automatically handles:
|
60
|
-
- Indentation removal from multiline strings
|
61
|
-
- Single backslash conversion to double backslash for LaTeX line breaks
|
62
|
-
"""
|
63
|
-
# Check if opening line is just $$
|
64
|
-
if opening_line.strip() != "$$":
|
65
|
-
return None
|
66
|
-
|
67
|
-
# Process equation lines if any exist
|
68
|
-
if equation_lines:
|
69
|
-
# Remove common indentation from all lines
|
70
|
-
raw_content = "\n".join(equation_lines)
|
71
|
-
dedented_content = textwrap.dedent(raw_content)
|
72
|
-
|
73
|
-
# Fix single backslashes at line ends for LaTeX line breaks
|
74
|
-
fixed_lines = cls._fix_latex_line_breaks(dedented_content.splitlines())
|
75
|
-
expression = "\n".join(fixed_lines).strip()
|
76
|
-
|
77
|
-
if expression:
|
78
|
-
return CreateEquationBlock(
|
79
|
-
equation=EquationBlock(expression=expression)
|
80
|
-
)
|
81
|
-
|
82
|
-
return None
|
83
|
-
|
84
|
-
@classmethod
|
85
|
-
def _fix_latex_line_breaks(cls, lines: list[str]) -> list[str]:
|
86
|
-
"""
|
87
|
-
Fix lines that end with single backslashes by converting them to double backslashes.
|
88
|
-
This makes LaTeX line breaks work correctly when users write single backslashes.
|
89
|
-
|
90
|
-
Examples:
|
91
|
-
- "a = b + c \" -> "a = b + c \\"
|
92
|
-
- "a = b + c \\\\" -> "a = b + c \\\\" (unchanged)
|
93
|
-
"""
|
94
|
-
fixed_lines = []
|
95
|
-
|
96
|
-
for line in lines:
|
97
|
-
# Check if line ends with backslashes
|
98
|
-
backslash_match = re.search(r"(\\+)$", line)
|
99
|
-
if backslash_match:
|
100
|
-
backslashes = backslash_match.group(1)
|
101
|
-
# If odd number of backslashes, the last one needs to be doubled
|
102
|
-
if len(backslashes) % 2 == 1:
|
103
|
-
line = line + "\\"
|
104
|
-
|
105
|
-
fixed_lines.append(line)
|
106
|
-
|
107
|
-
return fixed_lines
|
108
|
-
|
109
|
-
@classmethod
|
110
|
-
async def notion_to_markdown(cls, block: Block) -> Optional[str]:
|
111
|
-
if block.type != BlockType.EQUATION or not block.equation:
|
112
|
-
return None
|
113
|
-
|
114
|
-
expression = (block.equation.expression or "").strip()
|
115
|
-
if not expression:
|
116
|
-
return None
|
117
|
-
|
118
|
-
return f"$${expression}$$"
|
119
|
-
|
120
|
-
@classmethod
|
121
|
-
def get_system_prompt_information(cls) -> Optional[BlockElementMarkdownInformation]:
|
122
|
-
"""Get system prompt information for equation blocks."""
|
123
|
-
return BlockElementMarkdownInformation(
|
124
|
-
block_type=cls.__name__,
|
125
|
-
description="Mathematical equations using standard Markdown LaTeX syntax",
|
126
|
-
syntax_examples=[
|
127
|
-
"$$E = mc^2$$",
|
128
|
-
"$$\\frac{a}{b} + \\sqrt{c}$$",
|
129
|
-
"$$\\int_0^\\infty e^{-x} dx = 1$$",
|
130
|
-
"$$\\sum_{i=1}^n i = \\frac{n(n+1)}{2}$$",
|
131
|
-
],
|
132
|
-
usage_guidelines="Use for mathematical expressions and formulas. Supports LaTeX syntax. Wrap equations in double dollar signs ($$).",
|
133
|
-
)
|
@@ -1,23 +0,0 @@
|
|
1
|
-
from notionary.blocks.markdown.markdown_node import MarkdownNode
|
2
|
-
|
3
|
-
|
4
|
-
class EquationMarkdownNode(MarkdownNode):
|
5
|
-
"""
|
6
|
-
Enhanced Equation node with Pydantic integration.
|
7
|
-
Programmatic interface for creating Markdown equation blocks.
|
8
|
-
Uses standard Markdown equation syntax with double dollar signs.
|
9
|
-
|
10
|
-
Examples:
|
11
|
-
$$E = mc^2$$
|
12
|
-
$$\\frac{a}{b} + \\sqrt{c}$$
|
13
|
-
$$\\int_0^\\infty e^{-x} dx = 1$$
|
14
|
-
"""
|
15
|
-
|
16
|
-
expression: str
|
17
|
-
|
18
|
-
def to_markdown(self) -> str:
|
19
|
-
expr = self.expression.strip()
|
20
|
-
if not expr:
|
21
|
-
return "$$$$"
|
22
|
-
|
23
|
-
return f"$${expr}$$"
|
@@ -1,23 +0,0 @@
|
|
1
|
-
from notionary.blocks.file.file_element import FileElement
|
2
|
-
from notionary.blocks.file.file_element_markdown_node import (
|
3
|
-
FileMarkdownNode,
|
4
|
-
)
|
5
|
-
from notionary.blocks.file.file_element_models import (
|
6
|
-
CreateFileBlock,
|
7
|
-
ExternalFile,
|
8
|
-
FileBlock,
|
9
|
-
FileType,
|
10
|
-
FileUploadFile,
|
11
|
-
NotionHostedFile,
|
12
|
-
)
|
13
|
-
|
14
|
-
__all__ = [
|
15
|
-
"FileElement",
|
16
|
-
"FileType",
|
17
|
-
"ExternalFile",
|
18
|
-
"NotionHostedFile",
|
19
|
-
"FileUploadFile",
|
20
|
-
"FileBlock",
|
21
|
-
"CreateFileBlock",
|
22
|
-
"FileMarkdownNode",
|
23
|
-
]
|
@@ -1,133 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import re
|
4
|
-
from pathlib import Path
|
5
|
-
from typing import Optional
|
6
|
-
|
7
|
-
from notionary.blocks.base_block_element import BaseBlockElement
|
8
|
-
from notionary.blocks.file.file_element_models import (
|
9
|
-
CreateFileBlock,
|
10
|
-
ExternalFile,
|
11
|
-
FileBlock,
|
12
|
-
FileType,
|
13
|
-
FileUploadFile,
|
14
|
-
)
|
15
|
-
from notionary.blocks.mixins.captions import CaptionMixin
|
16
|
-
from notionary.blocks.mixins.file_upload.file_upload_mixin import FileUploadMixin
|
17
|
-
from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformation
|
18
|
-
from notionary.blocks.models import Block, BlockCreateResult, BlockType
|
19
|
-
|
20
|
-
|
21
|
-
class FileElement(BaseBlockElement, CaptionMixin, FileUploadMixin):
|
22
|
-
r"""
|
23
|
-
Handles conversion between Markdown file embeds and Notion file blocks.
|
24
|
-
|
25
|
-
Supports both external URLs and local file uploads.
|
26
|
-
|
27
|
-
Markdown file syntax:
|
28
|
-
- [file](https://example.com/document.pdf) - External URL
|
29
|
-
- [file](./local/document.pdf) - Local file (will be uploaded)
|
30
|
-
- [file](C:\Documents\report.pdf) - Absolute local path (will be uploaded)
|
31
|
-
- [file](https://example.com/document.pdf)(caption:Annual Report) - With caption
|
32
|
-
- (caption:Important document)[file](./doc.pdf) - Caption before URL
|
33
|
-
"""
|
34
|
-
|
35
|
-
FILE_PATTERN = re.compile(r"\[file\]\(([^)]+)\)")
|
36
|
-
|
37
|
-
@classmethod
|
38
|
-
def match_notion(cls, block: Block) -> bool:
|
39
|
-
return bool(block.type == BlockType.FILE and block.file)
|
40
|
-
|
41
|
-
@classmethod
|
42
|
-
async def markdown_to_notion(cls, text: str) -> Optional[BlockCreateResult]:
|
43
|
-
"""Convert markdown file link to Notion FileBlock."""
|
44
|
-
file_path = cls._extract_file_path(text.strip())
|
45
|
-
if not file_path:
|
46
|
-
return None
|
47
|
-
|
48
|
-
cls.logger.info(f"Processing file: {file_path}")
|
49
|
-
|
50
|
-
# Extract caption
|
51
|
-
caption_text = cls.extract_caption(text.strip())
|
52
|
-
caption_rich_text = cls.build_caption_rich_text(caption_text or "")
|
53
|
-
|
54
|
-
# Determine if it's a local file or external URL
|
55
|
-
if cls._is_local_file_path(file_path):
|
56
|
-
cls.logger.debug(f"Detected local file: {file_path}")
|
57
|
-
|
58
|
-
# Upload the local file using mixin method
|
59
|
-
file_upload_id = await cls._upload_local_file(file_path, "file")
|
60
|
-
if not file_upload_id:
|
61
|
-
cls.logger.error(f"Failed to upload file: {file_path}")
|
62
|
-
return None
|
63
|
-
|
64
|
-
# Create FILE_UPLOAD block
|
65
|
-
file_block = FileBlock(
|
66
|
-
type=FileType.FILE_UPLOAD,
|
67
|
-
file_upload=FileUploadFile(id=file_upload_id),
|
68
|
-
caption=caption_rich_text,
|
69
|
-
name=Path(file_path).name,
|
70
|
-
)
|
71
|
-
|
72
|
-
else:
|
73
|
-
cls.logger.debug(f"Using external URL: {file_path}")
|
74
|
-
|
75
|
-
file_block = FileBlock(
|
76
|
-
type=FileType.EXTERNAL,
|
77
|
-
external=ExternalFile(url=file_path),
|
78
|
-
caption=caption_rich_text,
|
79
|
-
)
|
80
|
-
|
81
|
-
return CreateFileBlock(file=file_block)
|
82
|
-
|
83
|
-
@classmethod
|
84
|
-
async def notion_to_markdown(cls, block: Block) -> Optional[str]:
|
85
|
-
if block.type != BlockType.FILE or not block.file:
|
86
|
-
return None
|
87
|
-
|
88
|
-
fb: FileBlock = block.file
|
89
|
-
|
90
|
-
# Determine the source for markdown
|
91
|
-
if fb.type == FileType.EXTERNAL and fb.external:
|
92
|
-
source = fb.external.url
|
93
|
-
elif fb.type == FileType.FILE and fb.file:
|
94
|
-
source = fb.file.url
|
95
|
-
else:
|
96
|
-
return None
|
97
|
-
|
98
|
-
result = f"[file]({source})"
|
99
|
-
|
100
|
-
# Add caption if present
|
101
|
-
caption_markdown = await cls.format_caption_for_markdown(fb.caption or [])
|
102
|
-
if caption_markdown:
|
103
|
-
result += caption_markdown
|
104
|
-
|
105
|
-
return result
|
106
|
-
|
107
|
-
@classmethod
|
108
|
-
def get_system_prompt_information(cls) -> Optional[BlockElementMarkdownInformation]:
|
109
|
-
"""Get system prompt information for file blocks."""
|
110
|
-
return BlockElementMarkdownInformation(
|
111
|
-
block_type=cls.__name__,
|
112
|
-
description="File blocks embed files from external URLs or upload local files with optional captions",
|
113
|
-
syntax_examples=[
|
114
|
-
"[file](https://example.com/document.pdf)",
|
115
|
-
"[file](./local/document.pdf)",
|
116
|
-
"[file](C:\\Documents\\report.xlsx)",
|
117
|
-
"[file](https://example.com/document.pdf)(caption:Annual Report)",
|
118
|
-
"(caption:Q1 Data)[file](./spreadsheet.xlsx)",
|
119
|
-
"[file](./manual.docx)(caption:**User** manual)",
|
120
|
-
],
|
121
|
-
usage_guidelines="Use for both external URLs and local files. Local files will be automatically uploaded to Notion. Supports various file formats including PDFs, documents, spreadsheets, images. Caption supports rich text formatting and should describe the file content or purpose.",
|
122
|
-
)
|
123
|
-
|
124
|
-
@classmethod
|
125
|
-
def _extract_file_path(cls, text: str) -> Optional[str]:
|
126
|
-
"""Extract file path/URL from text, handling caption patterns."""
|
127
|
-
clean_text = cls.remove_caption(text)
|
128
|
-
|
129
|
-
match = cls.FILE_PATTERN.search(clean_text)
|
130
|
-
if match:
|
131
|
-
return match.group(1).strip()
|
132
|
-
|
133
|
-
return None
|
@@ -1,24 +0,0 @@
|
|
1
|
-
from typing import Optional
|
2
|
-
|
3
|
-
from notionary.blocks.markdown.markdown_node import MarkdownNode
|
4
|
-
from notionary.blocks.mixins.captions import CaptionMarkdownNodeMixin
|
5
|
-
|
6
|
-
|
7
|
-
class FileMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
|
8
|
-
"""
|
9
|
-
Enhanced File node with Pydantic integration.
|
10
|
-
Programmatic interface for creating Notion-style Markdown file embeds.
|
11
|
-
"""
|
12
|
-
|
13
|
-
url: str
|
14
|
-
caption: Optional[str] = None
|
15
|
-
|
16
|
-
def to_markdown(self) -> str:
|
17
|
-
"""Return the Markdown representation.
|
18
|
-
|
19
|
-
Examples:
|
20
|
-
- [file](https://example.com/document.pdf)
|
21
|
-
- [file](https://example.com/document.pdf)(caption:User manual)
|
22
|
-
"""
|
23
|
-
base_markdown = f"[file]({self.url})"
|
24
|
-
return self.append_caption_to_markdown(base_markdown, self.caption)
|
@@ -1,39 +0,0 @@
|
|
1
|
-
from enum import Enum
|
2
|
-
from typing import Literal, Optional
|
3
|
-
|
4
|
-
from pydantic import BaseModel, Field
|
5
|
-
|
6
|
-
from notionary.blocks.rich_text.rich_text_models import RichTextObject
|
7
|
-
|
8
|
-
|
9
|
-
class FileType(str, Enum):
|
10
|
-
EXTERNAL = "external"
|
11
|
-
FILE = "file"
|
12
|
-
FILE_UPLOAD = "file_upload"
|
13
|
-
|
14
|
-
|
15
|
-
class ExternalFile(BaseModel):
|
16
|
-
url: str
|
17
|
-
|
18
|
-
|
19
|
-
class NotionHostedFile(BaseModel):
|
20
|
-
url: str
|
21
|
-
expiry_time: str
|
22
|
-
|
23
|
-
|
24
|
-
class FileUploadFile(BaseModel):
|
25
|
-
id: str
|
26
|
-
|
27
|
-
|
28
|
-
class FileBlock(BaseModel):
|
29
|
-
caption: list[RichTextObject] = Field(default_factory=list)
|
30
|
-
type: FileType
|
31
|
-
external: Optional[ExternalFile] = None
|
32
|
-
file: Optional[NotionHostedFile] = None
|
33
|
-
file_upload: Optional[FileUploadFile] = None
|
34
|
-
name: Optional[str] = None
|
35
|
-
|
36
|
-
|
37
|
-
class CreateFileBlock(BaseModel):
|
38
|
-
type: Literal["file"] = "file"
|
39
|
-
file: FileBlock
|
@@ -1,19 +0,0 @@
|
|
1
|
-
from notionary.blocks.heading.heading_element import HeadingElement
|
2
|
-
from notionary.blocks.heading.heading_markdown_node import (
|
3
|
-
HeadingMarkdownNode,
|
4
|
-
)
|
5
|
-
from notionary.blocks.heading.heading_models import (
|
6
|
-
CreateHeading1Block,
|
7
|
-
CreateHeading2Block,
|
8
|
-
CreateHeading3Block,
|
9
|
-
HeadingBlock,
|
10
|
-
)
|
11
|
-
|
12
|
-
__all__ = [
|
13
|
-
"HeadingElement",
|
14
|
-
"HeadingBlock",
|
15
|
-
"CreateHeading1Block",
|
16
|
-
"CreateHeading2Block",
|
17
|
-
"CreateHeading3Block",
|
18
|
-
"HeadingMarkdownNode",
|
19
|
-
]
|
@@ -1,112 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import re
|
4
|
-
from typing import Optional, cast
|
5
|
-
|
6
|
-
from notionary.blocks.base_block_element import BaseBlockElement
|
7
|
-
from notionary.blocks.heading.heading_models import (
|
8
|
-
CreateHeading1Block,
|
9
|
-
CreateHeading2Block,
|
10
|
-
CreateHeading3Block,
|
11
|
-
HeadingBlock,
|
12
|
-
)
|
13
|
-
from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformation
|
14
|
-
from notionary.blocks.models import Block, BlockCreateResult, BlockType
|
15
|
-
from notionary.blocks.rich_text.text_inline_formatter import TextInlineFormatter
|
16
|
-
from notionary.blocks.types import BlockColor
|
17
|
-
|
18
|
-
|
19
|
-
class HeadingElement(BaseBlockElement):
|
20
|
-
"""Handles conversion between Markdown headings and Notion heading blocks."""
|
21
|
-
|
22
|
-
PATTERN = re.compile(r"^(#{1,3})[ \t]+(.+)$")
|
23
|
-
|
24
|
-
@classmethod
|
25
|
-
def match_notion(cls, block: Block) -> bool:
|
26
|
-
return (
|
27
|
-
block.type
|
28
|
-
in (
|
29
|
-
BlockType.HEADING_1,
|
30
|
-
BlockType.HEADING_2,
|
31
|
-
BlockType.HEADING_3,
|
32
|
-
)
|
33
|
-
and getattr(block, block.type.value) is not None
|
34
|
-
)
|
35
|
-
|
36
|
-
@classmethod
|
37
|
-
async def markdown_to_notion(cls, text: str) -> BlockCreateResult:
|
38
|
-
"""Convert markdown headings (#, ##, ###) to Notion HeadingBlock."""
|
39
|
-
match = cls.PATTERN.match(text.strip())
|
40
|
-
if not match:
|
41
|
-
return None
|
42
|
-
|
43
|
-
level = len(match.group(1))
|
44
|
-
if level < 1 or level > 3:
|
45
|
-
return None
|
46
|
-
|
47
|
-
content = match.group(2).strip()
|
48
|
-
if not content:
|
49
|
-
return None
|
50
|
-
|
51
|
-
rich_text = await TextInlineFormatter.parse_inline_formatting(content)
|
52
|
-
heading_content = HeadingBlock(
|
53
|
-
rich_text=rich_text, color=BlockColor.DEFAULT, is_toggleable=False
|
54
|
-
)
|
55
|
-
|
56
|
-
if level == 1:
|
57
|
-
return CreateHeading1Block(heading_1=heading_content)
|
58
|
-
elif level == 2:
|
59
|
-
return CreateHeading2Block(heading_2=heading_content)
|
60
|
-
else:
|
61
|
-
return CreateHeading3Block(heading_3=heading_content)
|
62
|
-
|
63
|
-
@classmethod
|
64
|
-
async def notion_to_markdown(cls, block: Block) -> Optional[str]:
|
65
|
-
# Only handle heading blocks via BlockType enum
|
66
|
-
if block.type not in (
|
67
|
-
BlockType.HEADING_1,
|
68
|
-
BlockType.HEADING_2,
|
69
|
-
BlockType.HEADING_3,
|
70
|
-
):
|
71
|
-
return None
|
72
|
-
|
73
|
-
# Determine heading level from enum
|
74
|
-
if block.type == BlockType.HEADING_1:
|
75
|
-
level = 1
|
76
|
-
elif block.type == BlockType.HEADING_2:
|
77
|
-
level = 2
|
78
|
-
else:
|
79
|
-
level = 3
|
80
|
-
|
81
|
-
heading_obj = getattr(block, block.type.value)
|
82
|
-
if not heading_obj:
|
83
|
-
return None
|
84
|
-
|
85
|
-
heading_data = cast(HeadingBlock, heading_obj)
|
86
|
-
if not heading_data.rich_text:
|
87
|
-
return None
|
88
|
-
|
89
|
-
text = await TextInlineFormatter.extract_text_with_formatting(
|
90
|
-
heading_data.rich_text
|
91
|
-
)
|
92
|
-
if not text:
|
93
|
-
return None
|
94
|
-
|
95
|
-
# Use hash-style for all heading levels
|
96
|
-
return f"{('#' * level)} {text}"
|
97
|
-
|
98
|
-
@classmethod
|
99
|
-
def get_system_prompt_information(cls) -> Optional[BlockElementMarkdownInformation]:
|
100
|
-
"""Get system prompt information for heading blocks."""
|
101
|
-
return BlockElementMarkdownInformation(
|
102
|
-
block_type=cls.__name__,
|
103
|
-
description="Heading blocks create hierarchical document structure with different levels",
|
104
|
-
syntax_examples=[
|
105
|
-
"# Heading Level 1",
|
106
|
-
"## Heading Level 2",
|
107
|
-
"### Heading Level 3",
|
108
|
-
"# Heading with **bold text**",
|
109
|
-
"## Heading with *italic text*",
|
110
|
-
],
|
111
|
-
usage_guidelines="Use # for main titles, ## for sections, ### for subsections. Supports inline formatting. Only levels 1-3 are supported in Notion.",
|
112
|
-
)
|
@@ -1,16 +0,0 @@
|
|
1
|
-
from pydantic import Field
|
2
|
-
from notionary.blocks.markdown.markdown_node import MarkdownNode
|
3
|
-
|
4
|
-
|
5
|
-
class HeadingMarkdownNode(MarkdownNode):
|
6
|
-
"""
|
7
|
-
Enhanced Heading node with Pydantic integration.
|
8
|
-
Programmatic interface for creating Markdown headings (H1-H3).
|
9
|
-
Example: # Heading 1, ## Heading 2, ### Heading 3
|
10
|
-
"""
|
11
|
-
|
12
|
-
text: str
|
13
|
-
level: int = Field(default=1, ge=1, le=3)
|
14
|
-
|
15
|
-
def to_markdown(self) -> str:
|
16
|
-
return f"{'#' * self.level} {self.text}"
|