notionary 0.2.27__py3-none-any.whl → 0.3.0__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/__init__.py +4 -4
- notionary/blocks/client.py +90 -216
- notionary/blocks/enums.py +167 -0
- notionary/blocks/rich_text/markdown_rich_text_converter.py +280 -0
- notionary/blocks/rich_text/models.py +178 -0
- notionary/blocks/rich_text/name_id_resolver/__init__.py +13 -0
- notionary/blocks/rich_text/name_id_resolver/data_source.py +32 -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 +144 -0
- notionary/blocks/rich_text/rich_text_patterns.py +42 -0
- notionary/blocks/schemas.py +778 -0
- notionary/comments/__init__.py +1 -22
- notionary/comments/client.py +52 -187
- notionary/comments/factory.py +38 -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 +104 -0
- notionary/data_source/properties/schemas.py +402 -0
- notionary/data_source/query/builder.py +448 -0
- notionary/data_source/query/resolver.py +114 -0
- notionary/data_source/query/schema.py +302 -0
- notionary/data_source/query/validator.py +73 -0
- notionary/data_source/schema/registry.py +104 -0
- notionary/data_source/schema/service.py +136 -0
- notionary/data_source/schemas.py +27 -0
- notionary/data_source/service.py +377 -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 +168 -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 +57 -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 +204 -0
- notionary/http/models.py +50 -0
- notionary/page/blocks/client.py +1 -0
- notionary/page/content/factory.py +73 -0
- notionary/page/content/markdown/__init__.py +5 -0
- notionary/page/content/markdown/builder.py +226 -0
- notionary/page/content/markdown/nodes/__init__.py +52 -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 +41 -0
- notionary/page/content/markdown/nodes/callout.py +34 -0
- notionary/page/content/markdown/nodes/code.py +28 -0
- notionary/page/content/markdown/nodes/columns.py +69 -0
- notionary/page/content/markdown/nodes/container.py +64 -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 +36 -0
- notionary/page/content/markdown/nodes/image.py +23 -0
- notionary/page/content/markdown/nodes/mixins/__init__.py +5 -0
- notionary/page/content/markdown/nodes/mixins/caption.py +12 -0
- notionary/page/content/markdown/nodes/numbered_list.py +38 -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 +27 -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 +38 -0
- notionary/page/content/markdown/nodes/toggle.py +27 -0
- notionary/page/content/markdown/nodes/video.py +23 -0
- notionary/page/content/parser/context.py +126 -0
- notionary/page/content/parser/factory.py +210 -0
- notionary/page/content/parser/parsers/__init__.py +58 -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 +85 -0
- notionary/page/content/parser/parsers/callout.py +100 -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 +76 -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 +115 -0
- notionary/page/content/parser/parsers/image.py +42 -0
- notionary/page/content/parser/parsers/numbered_list.py +89 -0
- notionary/page/content/parser/parsers/paragraph.py +37 -0
- notionary/page/content/parser/parsers/pdf.py +42 -0
- notionary/page/content/parser/parsers/quote.py +125 -0
- notionary/page/content/parser/parsers/space.py +41 -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 +96 -0
- notionary/page/content/parser/parsers/toggle.py +70 -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 +95 -0
- notionary/page/content/parser/post_processing/handlers/rich_text_length_truncation.py +114 -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 +11 -0
- notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +130 -0
- notionary/page/content/parser/pre_processsing/handlers/indentation.py +84 -0
- notionary/page/content/parser/pre_processsing/handlers/port.py +7 -0
- notionary/page/content/parser/pre_processsing/handlers/whitespace.py +73 -0
- notionary/page/content/parser/pre_processsing/service.py +15 -0
- notionary/page/content/parser/service.py +78 -0
- notionary/page/content/renderer/context.py +51 -0
- notionary/page/content/renderer/factory.py +231 -0
- notionary/page/content/renderer/post_processing/handlers/__init__.py +5 -0
- notionary/page/content/renderer/post_processing/handlers/numbered_list.py +156 -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 +55 -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 +50 -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 +53 -0
- notionary/page/content/renderer/renderers/column_list.py +44 -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 +95 -0
- notionary/page/content/renderer/renderers/image.py +31 -0
- notionary/page/content/renderer/renderers/numbered_list.py +42 -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 +52 -0
- notionary/page/content/renderer/renderers/video.py +31 -0
- notionary/page/content/renderer/service.py +50 -0
- notionary/page/content/service.py +68 -0
- notionary/page/content/syntax/__init__.py +4 -0
- notionary/page/content/syntax/grammar.py +10 -0
- notionary/page/content/syntax/models.py +66 -0
- notionary/page/content/syntax/registry.py +393 -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 +308 -0
- notionary/page/properties/service.py +261 -0
- notionary/page/schemas.py +13 -0
- notionary/page/service.py +225 -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/shared/typings.py +3 -0
- notionary/user/__init__.py +4 -8
- notionary/user/base.py +138 -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/date.py +51 -0
- notionary/utils/decorators.py +122 -0
- notionary/utils/fuzzy.py +68 -0
- notionary/utils/mixins/logging.py +58 -0
- notionary/utils/pagination.py +100 -0
- notionary/utils/uuid_utils.py +20 -0
- notionary/workspace/__init__.py +4 -0
- notionary/workspace/client.py +62 -0
- notionary/workspace/query/__init__.py +3 -0
- notionary/workspace/query/builder.py +60 -0
- notionary/workspace/query/models.py +61 -0
- notionary/workspace/query/service.py +100 -0
- notionary/workspace/schemas.py +21 -0
- notionary/workspace/service.py +116 -0
- notionary-0.3.0.dist-info/METADATA +201 -0
- notionary-0.3.0.dist-info/RECORD +209 -0
- {notionary-0.2.27.dist-info → notionary-0.3.0.dist-info}/WHEEL +1 -1
- {notionary-0.2.27.dist-info → notionary-0.3.0.dist-info/licenses}/LICENSE +9 -9
- notionary/base_notion_client.py +0 -219
- 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/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/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 -712
- 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/logging_mixin.py +0 -59
- 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.27.dist-info/METADATA +0 -270
- notionary-0.2.27.dist-info/RECORD +0 -202
- /notionary/{database → user}/factory.py +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.rich_text.rich_text_markdown_converter import RichTextToMarkdownConverter
|
|
4
|
+
from notionary.blocks.schemas import Block, BlockType
|
|
5
|
+
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
6
|
+
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
7
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CalloutRenderer(BlockRenderer):
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
14
|
+
rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
|
|
15
|
+
) -> None:
|
|
16
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
17
|
+
self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def _can_handle(self, block: Block) -> bool:
|
|
21
|
+
return block.type == BlockType.CALLOUT
|
|
22
|
+
|
|
23
|
+
@override
|
|
24
|
+
async def _process(self, context: MarkdownRenderingContext) -> None:
|
|
25
|
+
content = await self._extract_callout_content(context.block)
|
|
26
|
+
|
|
27
|
+
if not content:
|
|
28
|
+
context.markdown_result = ""
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
icon = await self._extract_callout_icon(context.block)
|
|
32
|
+
|
|
33
|
+
callout_start_delimiter = self._syntax_registry.get_callout_syntax().start_delimiter
|
|
34
|
+
|
|
35
|
+
result = f'{callout_start_delimiter}({content} "{icon}")' if icon else f"{callout_start_delimiter}({content})"
|
|
36
|
+
|
|
37
|
+
if context.indent_level > 0:
|
|
38
|
+
result = context.indent_text(result)
|
|
39
|
+
|
|
40
|
+
context.markdown_result = result
|
|
41
|
+
|
|
42
|
+
async def _extract_callout_icon(self, block: Block) -> str:
|
|
43
|
+
if not block.callout or not block.callout.icon:
|
|
44
|
+
return ""
|
|
45
|
+
return block.callout.icon.emoji or ""
|
|
46
|
+
|
|
47
|
+
async def _extract_callout_content(self, block: Block) -> str:
|
|
48
|
+
if not block.callout or not block.callout.rich_text:
|
|
49
|
+
return ""
|
|
50
|
+
return await self._rich_text_markdown_converter.to_markdown(block.callout.rich_text)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from typing import override
|
|
3
|
+
|
|
4
|
+
from notionary.blocks.rich_text.rich_text_markdown_converter import RichTextToMarkdownConverter
|
|
5
|
+
from notionary.blocks.schemas import Block
|
|
6
|
+
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
7
|
+
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
8
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CaptionedBlockRenderer(BlockRenderer):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
15
|
+
rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
|
|
16
|
+
) -> None:
|
|
17
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
18
|
+
self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
async def _render_main_content(self, block: Block) -> str:
|
|
22
|
+
raise NotImplementedError
|
|
23
|
+
|
|
24
|
+
@override
|
|
25
|
+
async def _process(self, context: MarkdownRenderingContext) -> None:
|
|
26
|
+
main_content = await self._render_main_content(context.block)
|
|
27
|
+
|
|
28
|
+
if not main_content:
|
|
29
|
+
context.markdown_result = ""
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
caption_markdown = await self._render_caption(context.block)
|
|
33
|
+
|
|
34
|
+
final_markdown = f"{main_content}{caption_markdown}"
|
|
35
|
+
|
|
36
|
+
if context.indent_level > 0:
|
|
37
|
+
final_markdown = context.indent_text(final_markdown)
|
|
38
|
+
|
|
39
|
+
children_markdown = await context.render_children_with_additional_indent(1)
|
|
40
|
+
|
|
41
|
+
if children_markdown:
|
|
42
|
+
context.markdown_result = f"{final_markdown}\n{children_markdown}"
|
|
43
|
+
else:
|
|
44
|
+
context.markdown_result = final_markdown
|
|
45
|
+
|
|
46
|
+
async def _render_caption(self, block: Block) -> str:
|
|
47
|
+
block_data_object = getattr(block, block.type.value, None)
|
|
48
|
+
|
|
49
|
+
if not block_data_object or not hasattr(block_data_object, "caption"):
|
|
50
|
+
return ""
|
|
51
|
+
|
|
52
|
+
caption_rich_text = getattr(block_data_object, "caption", [])
|
|
53
|
+
if not caption_rich_text:
|
|
54
|
+
return ""
|
|
55
|
+
|
|
56
|
+
caption_markdown = await self._rich_text_markdown_converter.to_markdown(caption_rich_text)
|
|
57
|
+
|
|
58
|
+
return f"\n[caption] {caption_markdown}"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.enums import BlockType
|
|
4
|
+
from notionary.blocks.schemas import Block
|
|
5
|
+
from notionary.page.content.renderer.renderers.captioned_block import CaptionedBlockRenderer
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CodeRenderer(CaptionedBlockRenderer):
|
|
9
|
+
@override
|
|
10
|
+
def _can_handle(self, block: Block) -> bool:
|
|
11
|
+
return block.type == BlockType.CODE
|
|
12
|
+
|
|
13
|
+
@override
|
|
14
|
+
async def _render_main_content(self, block: Block) -> str:
|
|
15
|
+
language = self._extract_code_language(block)
|
|
16
|
+
code_content = await self._extract_code_content(block)
|
|
17
|
+
|
|
18
|
+
if not code_content:
|
|
19
|
+
return ""
|
|
20
|
+
|
|
21
|
+
syntax = self._syntax_registry.get_code_syntax()
|
|
22
|
+
code_start = f"{syntax.start_delimiter}{language}"
|
|
23
|
+
code_end = syntax.end_delimiter
|
|
24
|
+
return f"{code_start}\n{code_content}\n{code_end}"
|
|
25
|
+
|
|
26
|
+
def _extract_code_language(self, block: Block) -> str:
|
|
27
|
+
if not block.code or not block.code.language:
|
|
28
|
+
return ""
|
|
29
|
+
return block.code.language.value
|
|
30
|
+
|
|
31
|
+
async def _extract_code_content(self, block: Block) -> str:
|
|
32
|
+
if not block.code or not block.code.rich_text:
|
|
33
|
+
return ""
|
|
34
|
+
return await self._rich_text_markdown_converter.to_markdown(block.code.rich_text)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.enums import BlockType
|
|
4
|
+
from notionary.blocks.schemas import Block
|
|
5
|
+
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
6
|
+
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ColumnRenderer(BlockRenderer):
|
|
10
|
+
@override
|
|
11
|
+
def _can_handle(self, block: Block) -> bool:
|
|
12
|
+
return block.type == BlockType.COLUMN
|
|
13
|
+
|
|
14
|
+
@override
|
|
15
|
+
async def _process(self, context: MarkdownRenderingContext) -> None:
|
|
16
|
+
column_start = self._format_column_start(context.block, context.indent_level)
|
|
17
|
+
children_markdown = await self._render_children_with_indentation(context)
|
|
18
|
+
|
|
19
|
+
if children_markdown:
|
|
20
|
+
context.markdown_result = f"{column_start}\n{children_markdown}"
|
|
21
|
+
else:
|
|
22
|
+
context.markdown_result = column_start
|
|
23
|
+
|
|
24
|
+
def _format_column_start(self, block: Block, indent_level: int) -> str:
|
|
25
|
+
column_start = self._build_column_start_tag(block)
|
|
26
|
+
|
|
27
|
+
if indent_level > 0:
|
|
28
|
+
indent = " " * indent_level
|
|
29
|
+
column_start = f"{indent}{column_start}"
|
|
30
|
+
|
|
31
|
+
return column_start
|
|
32
|
+
|
|
33
|
+
def _build_column_start_tag(self, block: Block) -> str:
|
|
34
|
+
delimiter = self._syntax_registry.get_column_syntax().start_delimiter
|
|
35
|
+
|
|
36
|
+
if not block.column:
|
|
37
|
+
return delimiter
|
|
38
|
+
|
|
39
|
+
width_ratio = block.column.width_ratio
|
|
40
|
+
if width_ratio:
|
|
41
|
+
return f"{delimiter} {width_ratio}"
|
|
42
|
+
|
|
43
|
+
return delimiter
|
|
44
|
+
|
|
45
|
+
async def _render_children_with_indentation(self, context: MarkdownRenderingContext) -> str:
|
|
46
|
+
original_indent = context.indent_level
|
|
47
|
+
context.indent_level += 1
|
|
48
|
+
|
|
49
|
+
children_markdown = await context.render_children()
|
|
50
|
+
|
|
51
|
+
context.indent_level = original_indent
|
|
52
|
+
|
|
53
|
+
return children_markdown
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.enums import BlockType
|
|
4
|
+
from notionary.blocks.schemas import Block
|
|
5
|
+
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
6
|
+
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ColumnListRenderer(BlockRenderer):
|
|
10
|
+
@override
|
|
11
|
+
def _can_handle(self, block: Block) -> bool:
|
|
12
|
+
return block.type == BlockType.COLUMN_LIST
|
|
13
|
+
|
|
14
|
+
@override
|
|
15
|
+
async def _process(self, context: MarkdownRenderingContext) -> None:
|
|
16
|
+
column_list_start = self._format_column_list_start(context.indent_level)
|
|
17
|
+
children_markdown = await self._render_children_with_indentation(context)
|
|
18
|
+
|
|
19
|
+
if children_markdown:
|
|
20
|
+
context.markdown_result = f"{column_list_start}\n{children_markdown}"
|
|
21
|
+
else:
|
|
22
|
+
context.markdown_result = column_list_start
|
|
23
|
+
|
|
24
|
+
def _format_column_list_start(self, indent_level: int) -> str:
|
|
25
|
+
delimiter = self._get_column_list_delimiter()
|
|
26
|
+
|
|
27
|
+
if indent_level > 0:
|
|
28
|
+
indent = " " * indent_level
|
|
29
|
+
return f"{indent}{delimiter}"
|
|
30
|
+
|
|
31
|
+
return delimiter
|
|
32
|
+
|
|
33
|
+
def _get_column_list_delimiter(self) -> str:
|
|
34
|
+
return self._syntax_registry.get_column_list_syntax().start_delimiter
|
|
35
|
+
|
|
36
|
+
async def _render_children_with_indentation(self, context: MarkdownRenderingContext) -> str:
|
|
37
|
+
original_indent = context.indent_level
|
|
38
|
+
context.indent_level += 1
|
|
39
|
+
|
|
40
|
+
children_markdown = await context.render_children()
|
|
41
|
+
|
|
42
|
+
context.indent_level = original_indent
|
|
43
|
+
|
|
44
|
+
return children_markdown
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.enums import BlockType
|
|
4
|
+
from notionary.blocks.schemas import Block
|
|
5
|
+
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
6
|
+
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DividerRenderer(BlockRenderer):
|
|
10
|
+
@override
|
|
11
|
+
def _can_handle(self, block: Block) -> bool:
|
|
12
|
+
return block.type == BlockType.DIVIDER
|
|
13
|
+
|
|
14
|
+
@override
|
|
15
|
+
async def _process(self, context: MarkdownRenderingContext) -> None:
|
|
16
|
+
syntax = self._syntax_registry.get_divider_syntax()
|
|
17
|
+
divider_markdown = syntax.start_delimiter
|
|
18
|
+
|
|
19
|
+
if context.indent_level > 0:
|
|
20
|
+
divider_markdown = context.indent_text(divider_markdown)
|
|
21
|
+
|
|
22
|
+
context.markdown_result = divider_markdown
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.schemas import Block, BlockType
|
|
4
|
+
from notionary.page.content.renderer.renderers.captioned_block import CaptionedBlockRenderer
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class EmbedRenderer(CaptionedBlockRenderer):
|
|
8
|
+
@override
|
|
9
|
+
def _can_handle(self, block: Block) -> bool:
|
|
10
|
+
return block.type == BlockType.EMBED
|
|
11
|
+
|
|
12
|
+
@override
|
|
13
|
+
async def _render_main_content(self, block: Block) -> str:
|
|
14
|
+
url = self._extract_embed_url(block)
|
|
15
|
+
|
|
16
|
+
if not url:
|
|
17
|
+
return ""
|
|
18
|
+
|
|
19
|
+
syntax = self._syntax_registry.get_embed_syntax()
|
|
20
|
+
return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
|
|
21
|
+
|
|
22
|
+
def _extract_embed_url(self, block: Block) -> str:
|
|
23
|
+
if not block.embed:
|
|
24
|
+
return ""
|
|
25
|
+
return block.embed.url or ""
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.schemas import Block, BlockType
|
|
4
|
+
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
5
|
+
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EquationRenderer(BlockRenderer):
|
|
9
|
+
@override
|
|
10
|
+
def _can_handle(self, block: Block) -> bool:
|
|
11
|
+
return block.type == BlockType.EQUATION
|
|
12
|
+
|
|
13
|
+
@override
|
|
14
|
+
async def _process(self, context: MarkdownRenderingContext) -> None:
|
|
15
|
+
expression = self._extract_equation_expression(context.block)
|
|
16
|
+
|
|
17
|
+
if not expression:
|
|
18
|
+
context.markdown_result = ""
|
|
19
|
+
return
|
|
20
|
+
|
|
21
|
+
syntax = self._syntax_registry.get_equation_syntax()
|
|
22
|
+
equation_markdown = f"{syntax.start_delimiter}{expression}{syntax.end_delimiter}"
|
|
23
|
+
|
|
24
|
+
if context.indent_level > 0:
|
|
25
|
+
equation_markdown = context.indent_text(equation_markdown)
|
|
26
|
+
|
|
27
|
+
children_markdown = await context.render_children_with_additional_indent(1)
|
|
28
|
+
|
|
29
|
+
if children_markdown:
|
|
30
|
+
context.markdown_result = f"{equation_markdown}\n{children_markdown}"
|
|
31
|
+
else:
|
|
32
|
+
context.markdown_result = equation_markdown
|
|
33
|
+
|
|
34
|
+
def _extract_equation_expression(self, block: Block) -> str:
|
|
35
|
+
if not block.equation:
|
|
36
|
+
return ""
|
|
37
|
+
return block.equation.expression or ""
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.schemas import Block
|
|
4
|
+
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
5
|
+
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
6
|
+
from notionary.utils.mixins.logging import LoggingMixin
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FallbackRenderer(BlockRenderer, LoggingMixin):
|
|
10
|
+
@override
|
|
11
|
+
def _can_handle(self, block: Block) -> bool:
|
|
12
|
+
return True
|
|
13
|
+
|
|
14
|
+
@override
|
|
15
|
+
async def _process(self, context: MarkdownRenderingContext) -> None:
|
|
16
|
+
block_type = context.block.type.value if context.block.type else "unknown"
|
|
17
|
+
self.logger.warning(f"No handler found for block type: {block_type}")
|
|
18
|
+
|
|
19
|
+
fallback_message = f"[Unsupported block type: {block_type}]"
|
|
20
|
+
|
|
21
|
+
if context.indent_level > 0:
|
|
22
|
+
fallback_message = context.indent_text(fallback_message)
|
|
23
|
+
|
|
24
|
+
context.markdown_result = fallback_message
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.schemas import Block, BlockType
|
|
4
|
+
from notionary.page.content.renderer.renderers.captioned_block import CaptionedBlockRenderer
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FileRenderer(CaptionedBlockRenderer):
|
|
8
|
+
@override
|
|
9
|
+
def _can_handle(self, block: Block) -> bool:
|
|
10
|
+
return block.type == BlockType.FILE
|
|
11
|
+
|
|
12
|
+
@override
|
|
13
|
+
async def _render_main_content(self, block: Block) -> str:
|
|
14
|
+
url = self._extract_file_url(block)
|
|
15
|
+
|
|
16
|
+
if not url:
|
|
17
|
+
return ""
|
|
18
|
+
|
|
19
|
+
syntax = self._syntax_registry.get_file_syntax()
|
|
20
|
+
return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
|
|
21
|
+
|
|
22
|
+
def _extract_file_url(self, block: Block) -> str:
|
|
23
|
+
if not block.file:
|
|
24
|
+
return ""
|
|
25
|
+
|
|
26
|
+
if block.file.external:
|
|
27
|
+
return block.file.external.url or ""
|
|
28
|
+
elif block.file.file:
|
|
29
|
+
return block.file.file.url or ""
|
|
30
|
+
|
|
31
|
+
return ""
|
|
32
|
+
|
|
33
|
+
def _extract_file_name(self, block: Block) -> str:
|
|
34
|
+
if not block.file:
|
|
35
|
+
return ""
|
|
36
|
+
|
|
37
|
+
if block.file.name:
|
|
38
|
+
return block.file.name or ""
|
|
39
|
+
|
|
40
|
+
return ""
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.rich_text.rich_text_markdown_converter import RichTextToMarkdownConverter
|
|
4
|
+
from notionary.blocks.schemas import Block, BlockType, HeadingData
|
|
5
|
+
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
6
|
+
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
7
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class HeadingRenderer(BlockRenderer):
|
|
11
|
+
MIN_HEADING_LEVEL = 1
|
|
12
|
+
MAX_HEADING_LEVEL = 3
|
|
13
|
+
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
17
|
+
rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
|
|
18
|
+
) -> None:
|
|
19
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
20
|
+
self._syntax = self._syntax_registry.get_heading_syntax()
|
|
21
|
+
self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
|
|
22
|
+
|
|
23
|
+
@override
|
|
24
|
+
def _can_handle(self, block: Block) -> bool:
|
|
25
|
+
return block.type in (BlockType.HEADING_1, BlockType.HEADING_2, BlockType.HEADING_3)
|
|
26
|
+
|
|
27
|
+
@override
|
|
28
|
+
async def _process(self, context: MarkdownRenderingContext) -> None:
|
|
29
|
+
level = self._get_heading_level(context.block)
|
|
30
|
+
title = await self._get_heading_title(context.block)
|
|
31
|
+
|
|
32
|
+
if not self._is_valid_heading(level, title):
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
heading_markdown = self._format_heading(level, title, context.indent_level)
|
|
36
|
+
|
|
37
|
+
if self._is_toggleable(context.block):
|
|
38
|
+
context.markdown_result = await self._render_toggleable_heading(heading_markdown, context)
|
|
39
|
+
else:
|
|
40
|
+
context.markdown_result = heading_markdown
|
|
41
|
+
|
|
42
|
+
def _is_valid_heading(self, level: int, title: str) -> bool:
|
|
43
|
+
return self.MIN_HEADING_LEVEL <= level <= self.MAX_HEADING_LEVEL and bool(title)
|
|
44
|
+
|
|
45
|
+
def _format_heading(self, level: int, title: str, indent_level: int) -> str:
|
|
46
|
+
heading_prefix = self._syntax.start_delimiter * level
|
|
47
|
+
heading_markdown = f"{heading_prefix} {title}"
|
|
48
|
+
|
|
49
|
+
if indent_level > 0:
|
|
50
|
+
indent = " " * indent_level
|
|
51
|
+
heading_markdown = f"{indent}{heading_markdown}"
|
|
52
|
+
|
|
53
|
+
return heading_markdown
|
|
54
|
+
|
|
55
|
+
async def _render_toggleable_heading(self, heading_markdown: str, context: MarkdownRenderingContext) -> str:
|
|
56
|
+
original_indent = context.indent_level
|
|
57
|
+
context.indent_level += 1
|
|
58
|
+
|
|
59
|
+
children_markdown = await context.render_children()
|
|
60
|
+
|
|
61
|
+
context.indent_level = original_indent
|
|
62
|
+
|
|
63
|
+
if children_markdown:
|
|
64
|
+
return f"{heading_markdown}\n{children_markdown}"
|
|
65
|
+
return heading_markdown
|
|
66
|
+
|
|
67
|
+
def _get_heading_level(self, block: Block) -> int:
|
|
68
|
+
if block.type == BlockType.HEADING_1:
|
|
69
|
+
return 1
|
|
70
|
+
elif block.type == BlockType.HEADING_2:
|
|
71
|
+
return 2
|
|
72
|
+
elif block.type == BlockType.HEADING_3:
|
|
73
|
+
return 3
|
|
74
|
+
return 0
|
|
75
|
+
|
|
76
|
+
def _is_toggleable(self, block: Block) -> bool:
|
|
77
|
+
heading_data = self._get_heading_data(block)
|
|
78
|
+
return heading_data.is_toggleable if heading_data else False
|
|
79
|
+
|
|
80
|
+
async def _get_heading_title(self, block: Block) -> str:
|
|
81
|
+
heading_data = self._get_heading_data(block)
|
|
82
|
+
|
|
83
|
+
if not heading_data or not heading_data.rich_text:
|
|
84
|
+
return ""
|
|
85
|
+
|
|
86
|
+
return await self._rich_text_markdown_converter.to_markdown(heading_data.rich_text)
|
|
87
|
+
|
|
88
|
+
def _get_heading_data(self, block: Block) -> HeadingData | None:
|
|
89
|
+
if block.type == BlockType.HEADING_1:
|
|
90
|
+
return block.heading_1
|
|
91
|
+
elif block.type == BlockType.HEADING_2:
|
|
92
|
+
return block.heading_2
|
|
93
|
+
elif block.type == BlockType.HEADING_3:
|
|
94
|
+
return block.heading_3
|
|
95
|
+
return None
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.schemas import Block, BlockType
|
|
4
|
+
from notionary.page.content.renderer.renderers.captioned_block import CaptionedBlockRenderer
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ImageRenderer(CaptionedBlockRenderer):
|
|
8
|
+
@override
|
|
9
|
+
def _can_handle(self, block: Block) -> bool:
|
|
10
|
+
return block.type == BlockType.IMAGE
|
|
11
|
+
|
|
12
|
+
@override
|
|
13
|
+
async def _render_main_content(self, block: Block) -> str:
|
|
14
|
+
url = self._extract_image_url(block)
|
|
15
|
+
|
|
16
|
+
if not url:
|
|
17
|
+
return ""
|
|
18
|
+
|
|
19
|
+
syntax = self._syntax_registry.get_image_syntax()
|
|
20
|
+
return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
|
|
21
|
+
|
|
22
|
+
def _extract_image_url(self, block: Block) -> str:
|
|
23
|
+
if not block.image:
|
|
24
|
+
return ""
|
|
25
|
+
|
|
26
|
+
if block.image.external:
|
|
27
|
+
return block.image.external.url or ""
|
|
28
|
+
elif block.image.file:
|
|
29
|
+
return block.image.file.url or ""
|
|
30
|
+
|
|
31
|
+
return ""
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.rich_text.rich_text_markdown_converter import (
|
|
4
|
+
RichTextToMarkdownConverter,
|
|
5
|
+
)
|
|
6
|
+
from notionary.blocks.schemas import Block, BlockType
|
|
7
|
+
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
8
|
+
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
9
|
+
from notionary.page.content.syntax import MarkdownGrammar, SyntaxRegistry
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class NumberedListRenderer(BlockRenderer):
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
16
|
+
rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
|
|
17
|
+
markdown_grammar: MarkdownGrammar | None = None,
|
|
18
|
+
) -> None:
|
|
19
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
20
|
+
self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
|
|
21
|
+
|
|
22
|
+
markdown_grammar = markdown_grammar or MarkdownGrammar()
|
|
23
|
+
self._numbered_list_placeholder = markdown_grammar.numbered_list_placeholder
|
|
24
|
+
|
|
25
|
+
@override
|
|
26
|
+
def _can_handle(self, block: Block) -> bool:
|
|
27
|
+
return block.type == BlockType.NUMBERED_LIST_ITEM
|
|
28
|
+
|
|
29
|
+
@override
|
|
30
|
+
async def _process(self, context: MarkdownRenderingContext) -> None:
|
|
31
|
+
list_item_data = context.block.numbered_list_item
|
|
32
|
+
rich_text = list_item_data.rich_text if list_item_data else []
|
|
33
|
+
content = await self._rich_text_markdown_converter.to_markdown(rich_text)
|
|
34
|
+
|
|
35
|
+
item_line = context.indent_text(f"{self._numbered_list_placeholder}. {content}")
|
|
36
|
+
|
|
37
|
+
children_markdown = await context.render_children_with_additional_indent(1)
|
|
38
|
+
|
|
39
|
+
if children_markdown:
|
|
40
|
+
context.markdown_result = f"{item_line}\n{children_markdown}"
|
|
41
|
+
else:
|
|
42
|
+
context.markdown_result = item_line
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.rich_text.rich_text_markdown_converter import RichTextToMarkdownConverter
|
|
4
|
+
from notionary.blocks.schemas import Block, BlockType
|
|
5
|
+
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
6
|
+
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ParagraphRenderer(BlockRenderer):
|
|
10
|
+
def __init__(self, rich_text_markdown_converter: RichTextToMarkdownConverter | None = None) -> None:
|
|
11
|
+
super().__init__()
|
|
12
|
+
self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
|
|
13
|
+
|
|
14
|
+
@override
|
|
15
|
+
def _can_handle(self, block: Block) -> bool:
|
|
16
|
+
return block.type == BlockType.PARAGRAPH
|
|
17
|
+
|
|
18
|
+
@override
|
|
19
|
+
async def _process(self, context: MarkdownRenderingContext) -> None:
|
|
20
|
+
markdown = await self._convert_paragraph_to_markdown(context.block)
|
|
21
|
+
|
|
22
|
+
if not markdown:
|
|
23
|
+
context.markdown_result = ""
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
if context.indent_level > 0:
|
|
27
|
+
markdown = context.indent_text(markdown)
|
|
28
|
+
|
|
29
|
+
children_markdown = await context.render_children_with_additional_indent(1)
|
|
30
|
+
|
|
31
|
+
if children_markdown:
|
|
32
|
+
context.markdown_result = f"{markdown}\n{children_markdown}"
|
|
33
|
+
else:
|
|
34
|
+
context.markdown_result = markdown
|
|
35
|
+
|
|
36
|
+
async def _convert_paragraph_to_markdown(self, block: Block) -> str | None:
|
|
37
|
+
if not block.paragraph or not block.paragraph.rich_text:
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
return await self._rich_text_markdown_converter.to_markdown(block.paragraph.rich_text)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.schemas import Block, BlockType
|
|
4
|
+
from notionary.page.content.renderer.renderers.captioned_block import CaptionedBlockRenderer
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PdfRenderer(CaptionedBlockRenderer):
|
|
8
|
+
@override
|
|
9
|
+
def _can_handle(self, block: Block) -> bool:
|
|
10
|
+
return block.type == BlockType.PDF
|
|
11
|
+
|
|
12
|
+
@override
|
|
13
|
+
async def _render_main_content(self, block: Block) -> str:
|
|
14
|
+
url = self._extract_pdf_url(block)
|
|
15
|
+
|
|
16
|
+
if not url:
|
|
17
|
+
return ""
|
|
18
|
+
|
|
19
|
+
syntax = self._syntax_registry.get_pdf_syntax()
|
|
20
|
+
return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
|
|
21
|
+
|
|
22
|
+
def _extract_pdf_url(self, block: Block) -> str:
|
|
23
|
+
if not block.pdf:
|
|
24
|
+
return ""
|
|
25
|
+
|
|
26
|
+
if block.pdf.external:
|
|
27
|
+
return block.pdf.external.url or ""
|
|
28
|
+
elif block.pdf.file:
|
|
29
|
+
return block.pdf.file.url or ""
|
|
30
|
+
|
|
31
|
+
return ""
|