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
@@ -0,0 +1,62 @@
|
|
1
|
+
import re
|
2
|
+
from typing import override
|
3
|
+
|
4
|
+
from notionary.page.content.renderer.post_processing.port import PostProcessor
|
5
|
+
|
6
|
+
|
7
|
+
class NumberedListPlaceholderReplaceerPostProcessor(PostProcessor):
|
8
|
+
"""
|
9
|
+
Handles post processing of numbered lists in markdown text.
|
10
|
+
Would otherwise require complex state management during initial rendering.
|
11
|
+
"""
|
12
|
+
|
13
|
+
NUMBERED_LIST_PLACEHOLDER = "__NUM__"
|
14
|
+
LIST_ITEM_PATTERN = rf"^\s*{re.escape(NUMBERED_LIST_PLACEHOLDER)}\.\s*(.*)"
|
15
|
+
NUMBERED_ITEM_PATTERN = r"^\d+\.\s+"
|
16
|
+
|
17
|
+
@override
|
18
|
+
def process(self, markdown_text: str) -> str:
|
19
|
+
lines = markdown_text.splitlines()
|
20
|
+
processed_lines = []
|
21
|
+
list_counter = 1
|
22
|
+
|
23
|
+
for line_index, line in enumerate(lines):
|
24
|
+
if self._is_custom_list_item(line):
|
25
|
+
content = self._extract_list_content(line)
|
26
|
+
processed_lines.append(f"{list_counter}. {content}")
|
27
|
+
list_counter += 1
|
28
|
+
elif self._should_skip_blank_line(lines, line_index, processed_lines):
|
29
|
+
continue
|
30
|
+
else:
|
31
|
+
list_counter = 1
|
32
|
+
processed_lines.append(line)
|
33
|
+
|
34
|
+
return "\n".join(processed_lines)
|
35
|
+
|
36
|
+
def _is_custom_list_item(self, line: str) -> bool:
|
37
|
+
return bool(re.match(self.LIST_ITEM_PATTERN, line.strip()))
|
38
|
+
|
39
|
+
def _extract_list_content(self, line: str) -> str:
|
40
|
+
match = re.match(self.LIST_ITEM_PATTERN, line.strip())
|
41
|
+
return match.group(1) if match else ""
|
42
|
+
|
43
|
+
def _is_numbered_list_item(self, line: str) -> bool:
|
44
|
+
return bool(re.match(self.NUMBERED_ITEM_PATTERN, line))
|
45
|
+
|
46
|
+
def _is_blank_line(self, line: str) -> bool:
|
47
|
+
return not line.strip()
|
48
|
+
|
49
|
+
def _should_skip_blank_line(self, lines: list[str], current_index: int, processed_lines: list[str]) -> bool:
|
50
|
+
if not self._is_blank_line(lines[current_index]):
|
51
|
+
return False
|
52
|
+
|
53
|
+
previous_is_list_item = processed_lines and self._is_numbered_list_item(processed_lines[-1])
|
54
|
+
if not previous_is_list_item:
|
55
|
+
return False
|
56
|
+
|
57
|
+
next_index = current_index + 1
|
58
|
+
if next_index >= len(lines):
|
59
|
+
return False
|
60
|
+
|
61
|
+
next_is_list_item = self._is_custom_list_item(lines[next_index])
|
62
|
+
return next_is_list_item
|
@@ -0,0 +1,15 @@
|
|
1
|
+
from notionary.page.content.parser.post_processing.port import PostProcessor
|
2
|
+
|
3
|
+
|
4
|
+
class MarkdownRenderingPostProcessor:
|
5
|
+
def __init__(self) -> None:
|
6
|
+
self._processors: list[PostProcessor] = []
|
7
|
+
|
8
|
+
def register(self, processor: PostProcessor) -> None:
|
9
|
+
self._processors.append(processor)
|
10
|
+
|
11
|
+
def process(self, markdown_text: str) -> str:
|
12
|
+
result = markdown_text
|
13
|
+
for processor in self._processors:
|
14
|
+
result = processor.process(result)
|
15
|
+
return result
|
@@ -0,0 +1,57 @@
|
|
1
|
+
from .audio import AudioRenderer
|
2
|
+
from .base import BlockRenderer
|
3
|
+
from .bookmark import BookmarkRenderer
|
4
|
+
from .breadcrumb import BreadcrumbRenderer
|
5
|
+
from .bulleted_list import BulletedListRenderer
|
6
|
+
from .callout import CalloutRenderer
|
7
|
+
from .code import CodeRenderer
|
8
|
+
from .column import ColumnRenderer
|
9
|
+
from .column_list import ColumnListRenderer
|
10
|
+
from .divider import DividerRenderer
|
11
|
+
from .embed import EmbedRenderer
|
12
|
+
from .equation import EquationRenderer
|
13
|
+
from .fallback import FallbackRenderer
|
14
|
+
from .file import FileRenderer
|
15
|
+
from .heading import HeadingRenderer
|
16
|
+
from .image import ImageRenderer
|
17
|
+
from .numbered_list import NumberedListRenderer
|
18
|
+
from .paragraph import ParagraphRenderer
|
19
|
+
from .pdf import PdfRenderer
|
20
|
+
from .quote import QuoteRenderer
|
21
|
+
from .table import TableRenderer
|
22
|
+
from .table_of_contents import TableOfContentsRenderer
|
23
|
+
from .table_row import TableRowHandler
|
24
|
+
from .todo import TodoRenderer
|
25
|
+
from .toggle import ToggleRenderer
|
26
|
+
from .toggleable_heading import ToggleableHeadingRenderer
|
27
|
+
from .video import VideoRenderer
|
28
|
+
|
29
|
+
__all__ = [
|
30
|
+
"AudioRenderer",
|
31
|
+
"BlockRenderer",
|
32
|
+
"BookmarkRenderer",
|
33
|
+
"BreadcrumbRenderer",
|
34
|
+
"BulletedListRenderer",
|
35
|
+
"CalloutRenderer",
|
36
|
+
"CodeRenderer",
|
37
|
+
"ColumnListRenderer",
|
38
|
+
"ColumnRenderer",
|
39
|
+
"DividerRenderer",
|
40
|
+
"EmbedRenderer",
|
41
|
+
"EquationRenderer",
|
42
|
+
"FallbackRenderer",
|
43
|
+
"FileRenderer",
|
44
|
+
"HeadingRenderer",
|
45
|
+
"ImageRenderer",
|
46
|
+
"NumberedListRenderer",
|
47
|
+
"ParagraphRenderer",
|
48
|
+
"PdfRenderer",
|
49
|
+
"QuoteRenderer",
|
50
|
+
"TableOfContentsRenderer",
|
51
|
+
"TableRenderer",
|
52
|
+
"TableRowHandler",
|
53
|
+
"TodoRenderer",
|
54
|
+
"ToggleRenderer",
|
55
|
+
"ToggleableHeadingRenderer",
|
56
|
+
"VideoRenderer",
|
57
|
+
]
|
@@ -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 AudioRenderer(CaptionedBlockRenderer):
|
8
|
+
@override
|
9
|
+
def _can_handle(self, block: Block) -> bool:
|
10
|
+
return block.type == BlockType.AUDIO
|
11
|
+
|
12
|
+
@override
|
13
|
+
async def _render_main_content(self, block: Block) -> str:
|
14
|
+
url = self._extract_audio_url(block)
|
15
|
+
|
16
|
+
if not url:
|
17
|
+
return ""
|
18
|
+
|
19
|
+
syntax = self._syntax_registry.get_audio_syntax()
|
20
|
+
return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
|
21
|
+
|
22
|
+
def _extract_audio_url(self, block: Block) -> str:
|
23
|
+
if not block.audio:
|
24
|
+
return ""
|
25
|
+
|
26
|
+
if block.audio.external:
|
27
|
+
return block.audio.external.url or ""
|
28
|
+
elif block.audio.file:
|
29
|
+
return block.audio.file.url or ""
|
30
|
+
|
31
|
+
return ""
|
@@ -0,0 +1,31 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from abc import ABC, abstractmethod
|
4
|
+
|
5
|
+
from notionary.blocks.schemas import Block
|
6
|
+
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
7
|
+
from notionary.page.content.syntax.service import SyntaxRegistry
|
8
|
+
|
9
|
+
|
10
|
+
class BlockRenderer(ABC):
|
11
|
+
def __init__(self, syntax_registry: SyntaxRegistry | None = None) -> None:
|
12
|
+
self._syntax_registry = syntax_registry or SyntaxRegistry()
|
13
|
+
self._next_handler: BlockRenderer | None = None
|
14
|
+
|
15
|
+
def set_next(self, handler: BlockRenderer) -> BlockRenderer:
|
16
|
+
self._next_handler = handler
|
17
|
+
return handler
|
18
|
+
|
19
|
+
async def handle(self, context: MarkdownRenderingContext) -> None:
|
20
|
+
if self._can_handle(context.block):
|
21
|
+
await self._process(context)
|
22
|
+
elif self._next_handler:
|
23
|
+
await self._next_handler.handle(context)
|
24
|
+
|
25
|
+
@abstractmethod
|
26
|
+
def _can_handle(self, block: Block) -> bool:
|
27
|
+
pass
|
28
|
+
|
29
|
+
@abstractmethod
|
30
|
+
async def _process(self, context: MarkdownRenderingContext) -> None:
|
31
|
+
pass
|
@@ -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 BookmarkRenderer(CaptionedBlockRenderer):
|
8
|
+
@override
|
9
|
+
def _can_handle(self, block: Block) -> bool:
|
10
|
+
return block.type == BlockType.BOOKMARK
|
11
|
+
|
12
|
+
@override
|
13
|
+
async def _render_main_content(self, block: Block) -> str:
|
14
|
+
url = self._extract_bookmark_url(block)
|
15
|
+
|
16
|
+
if not url:
|
17
|
+
return ""
|
18
|
+
|
19
|
+
syntax = self._syntax_registry.get_bookmark_syntax()
|
20
|
+
return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
|
21
|
+
|
22
|
+
def _extract_bookmark_url(self, block: Block) -> str:
|
23
|
+
if not block.bookmark:
|
24
|
+
return ""
|
25
|
+
return block.bookmark.url or ""
|
@@ -0,0 +1,21 @@
|
|
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 BreadcrumbRenderer(BlockRenderer):
|
9
|
+
@override
|
10
|
+
def _can_handle(self, block: Block) -> bool:
|
11
|
+
return block.type == BlockType.BREADCRUMB
|
12
|
+
|
13
|
+
@override
|
14
|
+
async def _process(self, context: MarkdownRenderingContext) -> None:
|
15
|
+
syntax = self._syntax_registry.get_breadcrumb_syntax()
|
16
|
+
breadcrumb_markdown = syntax.start_delimiter
|
17
|
+
|
18
|
+
if context.indent_level > 0:
|
19
|
+
breadcrumb_markdown = context.indent_text(breadcrumb_markdown)
|
20
|
+
|
21
|
+
context.markdown_result = breadcrumb_markdown
|
@@ -0,0 +1,48 @@
|
|
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.service import SyntaxRegistry
|
8
|
+
|
9
|
+
|
10
|
+
class BulletedListRenderer(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.BULLETED_LIST_ITEM
|
22
|
+
|
23
|
+
@override
|
24
|
+
async def _process(self, context: MarkdownRenderingContext) -> None:
|
25
|
+
markdown = await self._convert_bulleted_list_to_markdown(context.block)
|
26
|
+
|
27
|
+
if not markdown:
|
28
|
+
context.markdown_result = ""
|
29
|
+
return
|
30
|
+
|
31
|
+
syntax = self._syntax_registry.get_bulleted_list_syntax()
|
32
|
+
list_item_markdown = f"{syntax.start_delimiter}{markdown}"
|
33
|
+
|
34
|
+
if context.indent_level > 0:
|
35
|
+
list_item_markdown = context.indent_text(list_item_markdown)
|
36
|
+
|
37
|
+
children_markdown = await context.render_children_with_additional_indent(1)
|
38
|
+
|
39
|
+
if children_markdown:
|
40
|
+
context.markdown_result = f"{list_item_markdown}\n{children_markdown}"
|
41
|
+
else:
|
42
|
+
context.markdown_result = list_item_markdown
|
43
|
+
|
44
|
+
async def _convert_bulleted_list_to_markdown(self, block: Block) -> str | None:
|
45
|
+
if not block.bulleted_list_item or not block.bulleted_list_item.rich_text:
|
46
|
+
return None
|
47
|
+
|
48
|
+
return await self._rich_text_markdown_converter.to_markdown(block.bulleted_list_item.rich_text)
|
@@ -0,0 +1,65 @@
|
|
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.service 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
|
+
icon = await self._extract_callout_icon(context.block)
|
26
|
+
content = await self._extract_callout_content(context.block)
|
27
|
+
|
28
|
+
if not content:
|
29
|
+
context.markdown_result = ""
|
30
|
+
return
|
31
|
+
|
32
|
+
syntax = self._syntax_registry.get_callout_syntax()
|
33
|
+
|
34
|
+
# Build callout structure
|
35
|
+
# Extract just the base part before the regex pattern
|
36
|
+
callout_type = syntax.start_delimiter.split()[1] # Gets "callout" from "::: callout"
|
37
|
+
callout_header = f"{self._syntax_registry.MULTI_LINE_BLOCK_DELIMITER} {callout_type}"
|
38
|
+
if icon:
|
39
|
+
callout_header = f"{self._syntax_registry.MULTI_LINE_BLOCK_DELIMITER} {callout_type} {icon}"
|
40
|
+
|
41
|
+
if context.indent_level > 0:
|
42
|
+
callout_header = context.indent_text(callout_header)
|
43
|
+
|
44
|
+
# Process children if they exist
|
45
|
+
children_markdown = await context.render_children()
|
46
|
+
|
47
|
+
callout_end = syntax.end_delimiter
|
48
|
+
if context.indent_level > 0:
|
49
|
+
callout_end = context.indent_text(callout_end)
|
50
|
+
|
51
|
+
# Combine content
|
52
|
+
if children_markdown:
|
53
|
+
context.markdown_result = f"{callout_header}\n{content}\n{children_markdown}\n{callout_end}"
|
54
|
+
else:
|
55
|
+
context.markdown_result = f"{callout_header}\n{content}\n{callout_end}"
|
56
|
+
|
57
|
+
async def _extract_callout_icon(self, block: Block) -> str:
|
58
|
+
if not block.callout or not block.callout.icon:
|
59
|
+
return ""
|
60
|
+
return block.callout.icon.emoji or ""
|
61
|
+
|
62
|
+
async def _extract_callout_content(self, block: Block) -> str:
|
63
|
+
if not block.callout or not block.callout.rich_text:
|
64
|
+
return ""
|
65
|
+
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.service 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,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 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._extract_column_start(context.block)
|
17
|
+
|
18
|
+
if context.indent_level > 0:
|
19
|
+
column_start = context.indent_text(column_start)
|
20
|
+
|
21
|
+
children_markdown = await context.render_children()
|
22
|
+
|
23
|
+
syntax = self._syntax_registry.get_column_syntax()
|
24
|
+
column_end = syntax.end_delimiter
|
25
|
+
if context.indent_level > 0:
|
26
|
+
column_end = context.indent_text(column_end, spaces=context.indent_level * 4)
|
27
|
+
|
28
|
+
if children_markdown:
|
29
|
+
context.markdown_result = f"{column_start}\n{children_markdown}\n{column_end}"
|
30
|
+
else:
|
31
|
+
context.markdown_result = f"{column_start}\n{column_end}"
|
32
|
+
|
33
|
+
def _extract_column_start(self, block: Block) -> str:
|
34
|
+
syntax = self._syntax_registry.get_column_syntax()
|
35
|
+
base_start = syntax.start_delimiter
|
36
|
+
|
37
|
+
if not block.column:
|
38
|
+
return base_start
|
39
|
+
|
40
|
+
width_ratio = block.column.width_ratio
|
41
|
+
if width_ratio:
|
42
|
+
return f"{base_start} {width_ratio}"
|
43
|
+
else:
|
44
|
+
return base_start
|
@@ -0,0 +1,31 @@
|
|
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
|
+
syntax = self._syntax_registry.get_column_list_syntax()
|
17
|
+
column_list_start = syntax.start_delimiter
|
18
|
+
|
19
|
+
if context.indent_level > 0:
|
20
|
+
column_list_start = context.indent_text(column_list_start)
|
21
|
+
|
22
|
+
children_markdown = await context.render_children()
|
23
|
+
|
24
|
+
column_list_end = syntax.end_delimiter
|
25
|
+
if context.indent_level > 0:
|
26
|
+
column_list_end = context.indent_text(column_list_end)
|
27
|
+
|
28
|
+
if children_markdown:
|
29
|
+
context.markdown_result = f"{column_list_start}\n{children_markdown}\n{column_list_end}"
|
30
|
+
else:
|
31
|
+
context.markdown_result = f"{column_list_start}\n{column_list_end}"
|
@@ -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
|