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,73 @@
|
|
|
1
|
+
from notionary.blocks.client import NotionBlockHttpClient
|
|
2
|
+
from notionary.page.content.parser.factory import ConverterChainFactory
|
|
3
|
+
from notionary.page.content.parser.post_processing.handlers import RichTextLengthTruncationPostProcessor
|
|
4
|
+
from notionary.page.content.parser.post_processing.service import BlockPostProcessor
|
|
5
|
+
from notionary.page.content.parser.pre_processsing.handlers import (
|
|
6
|
+
ColumnSyntaxPreProcessor,
|
|
7
|
+
IndentationNormalizer,
|
|
8
|
+
WhitespacePreProcessor,
|
|
9
|
+
)
|
|
10
|
+
from notionary.page.content.parser.pre_processsing.service import MarkdownPreProcessor
|
|
11
|
+
from notionary.page.content.parser.service import MarkdownToNotionConverter
|
|
12
|
+
from notionary.page.content.renderer.factory import RendererChainFactory
|
|
13
|
+
from notionary.page.content.renderer.post_processing.handlers import NumberedListPlaceholderReplacerPostProcessor
|
|
14
|
+
from notionary.page.content.renderer.post_processing.service import MarkdownRenderingPostProcessor
|
|
15
|
+
from notionary.page.content.renderer.service import NotionToMarkdownConverter
|
|
16
|
+
from notionary.page.content.service import PageContentService
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PageContentServiceFactory:
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
converter_chain_factory: ConverterChainFactory | None = None,
|
|
23
|
+
renderer_chain_factory: RendererChainFactory | None = None,
|
|
24
|
+
) -> None:
|
|
25
|
+
self._converter_chain_factory = converter_chain_factory or ConverterChainFactory()
|
|
26
|
+
self._renderer_chain_factory = renderer_chain_factory or RendererChainFactory()
|
|
27
|
+
|
|
28
|
+
def create(self, page_id: str, block_client: NotionBlockHttpClient) -> PageContentService:
|
|
29
|
+
markdown_converter = self._create_markdown_to_notion_converter()
|
|
30
|
+
notion_to_markdown_converter = self._create_notion_to_markdown_converter()
|
|
31
|
+
|
|
32
|
+
return PageContentService(
|
|
33
|
+
page_id=page_id,
|
|
34
|
+
block_client=block_client,
|
|
35
|
+
markdown_converter=markdown_converter,
|
|
36
|
+
notion_to_markdown_converter=notion_to_markdown_converter,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def _create_markdown_to_notion_converter(self) -> MarkdownToNotionConverter:
|
|
40
|
+
line_parser = self._converter_chain_factory.create()
|
|
41
|
+
markdown_pre_processor = self._create_markdown_preprocessor()
|
|
42
|
+
block_post_processor = self._create_post_processor()
|
|
43
|
+
|
|
44
|
+
return MarkdownToNotionConverter(
|
|
45
|
+
line_parser=line_parser,
|
|
46
|
+
pre_processor=markdown_pre_processor,
|
|
47
|
+
post_processor=block_post_processor,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def _create_notion_to_markdown_converter(self) -> NotionToMarkdownConverter:
|
|
51
|
+
renderer_chain = self._renderer_chain_factory.create()
|
|
52
|
+
markdown_rendering_post_processor = self._create_markdown_rendering_post_processor()
|
|
53
|
+
return NotionToMarkdownConverter(
|
|
54
|
+
renderer_chain=renderer_chain,
|
|
55
|
+
post_processor=markdown_rendering_post_processor,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def _create_markdown_preprocessor(self) -> MarkdownPreProcessor:
|
|
59
|
+
pre_processor = MarkdownPreProcessor()
|
|
60
|
+
pre_processor.register(ColumnSyntaxPreProcessor())
|
|
61
|
+
pre_processor.register(WhitespacePreProcessor())
|
|
62
|
+
pre_processor.register(IndentationNormalizer())
|
|
63
|
+
return pre_processor
|
|
64
|
+
|
|
65
|
+
def _create_post_processor(self) -> BlockPostProcessor:
|
|
66
|
+
post_processor = BlockPostProcessor()
|
|
67
|
+
post_processor.register(RichTextLengthTruncationPostProcessor())
|
|
68
|
+
return post_processor
|
|
69
|
+
|
|
70
|
+
def _create_markdown_rendering_post_processor(self) -> MarkdownRenderingPostProcessor:
|
|
71
|
+
post_processor = MarkdownRenderingPostProcessor()
|
|
72
|
+
post_processor.register(NumberedListPlaceholderReplacerPostProcessor())
|
|
73
|
+
return post_processor
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from typing import Self
|
|
5
|
+
|
|
6
|
+
from notionary.blocks.enums import CodingLanguage
|
|
7
|
+
from notionary.page.content.markdown.nodes import (
|
|
8
|
+
AudioMarkdownNode,
|
|
9
|
+
BookmarkMarkdownNode,
|
|
10
|
+
BreadcrumbMarkdownNode,
|
|
11
|
+
BulletedListMarkdownNode,
|
|
12
|
+
CalloutMarkdownNode,
|
|
13
|
+
CodeMarkdownNode,
|
|
14
|
+
ColumnListMarkdownNode,
|
|
15
|
+
ColumnMarkdownNode,
|
|
16
|
+
DividerMarkdownNode,
|
|
17
|
+
EmbedMarkdownNode,
|
|
18
|
+
EquationMarkdownNode,
|
|
19
|
+
FileMarkdownNode,
|
|
20
|
+
HeadingMarkdownNode,
|
|
21
|
+
ImageMarkdownNode,
|
|
22
|
+
MarkdownNode,
|
|
23
|
+
NumberedListMarkdownNode,
|
|
24
|
+
ParagraphMarkdownNode,
|
|
25
|
+
PdfMarkdownNode,
|
|
26
|
+
QuoteMarkdownNode,
|
|
27
|
+
SpaceMarkdownNode,
|
|
28
|
+
TableMarkdownNode,
|
|
29
|
+
TableOfContentsMarkdownNode,
|
|
30
|
+
TodoMarkdownNode,
|
|
31
|
+
ToggleMarkdownNode,
|
|
32
|
+
VideoMarkdownNode,
|
|
33
|
+
)
|
|
34
|
+
from notionary.page.content.markdown.nodes.container import flatten_children
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class MarkdownBuilder:
|
|
38
|
+
def __init__(self) -> None:
|
|
39
|
+
self.children: list[MarkdownNode] = []
|
|
40
|
+
|
|
41
|
+
def h1(self, text: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None) -> Self:
|
|
42
|
+
return self._add_heading(text, 1, builder_func)
|
|
43
|
+
|
|
44
|
+
def h2(self, text: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None) -> Self:
|
|
45
|
+
return self._add_heading(text, 2, builder_func)
|
|
46
|
+
|
|
47
|
+
def h3(self, text: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None) -> Self:
|
|
48
|
+
return self._add_heading(text, 3, builder_func)
|
|
49
|
+
|
|
50
|
+
def paragraph(self, text: str) -> Self:
|
|
51
|
+
self.children.append(ParagraphMarkdownNode(text=text))
|
|
52
|
+
return self
|
|
53
|
+
|
|
54
|
+
def space(self) -> Self:
|
|
55
|
+
self.children.append(SpaceMarkdownNode())
|
|
56
|
+
return self
|
|
57
|
+
|
|
58
|
+
def quote(self, text: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None) -> Self:
|
|
59
|
+
children = self._build_children(builder_func)
|
|
60
|
+
self.children.append(QuoteMarkdownNode(text=text, children=children))
|
|
61
|
+
return self
|
|
62
|
+
|
|
63
|
+
def divider(self) -> Self:
|
|
64
|
+
self.children.append(DividerMarkdownNode())
|
|
65
|
+
return self
|
|
66
|
+
|
|
67
|
+
def numbered_list(self, items: list[str]) -> Self:
|
|
68
|
+
self.children.append(NumberedListMarkdownNode(texts=items))
|
|
69
|
+
return self
|
|
70
|
+
|
|
71
|
+
def numbered_list_item(
|
|
72
|
+
self, text: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None
|
|
73
|
+
) -> Self:
|
|
74
|
+
children = self._build_children(builder_func)
|
|
75
|
+
wrapped = flatten_children(children)
|
|
76
|
+
child_nodes = [wrapped] if wrapped else None
|
|
77
|
+
self.children.append(NumberedListMarkdownNode(texts=[text], children=child_nodes))
|
|
78
|
+
return self
|
|
79
|
+
|
|
80
|
+
def bulleted_list(self, items: list[str]) -> Self:
|
|
81
|
+
self.children.append(BulletedListMarkdownNode(texts=items))
|
|
82
|
+
return self
|
|
83
|
+
|
|
84
|
+
def bulleted_list_item(
|
|
85
|
+
self, text: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None
|
|
86
|
+
) -> Self:
|
|
87
|
+
children = self._build_children(builder_func)
|
|
88
|
+
wrapped = flatten_children(children)
|
|
89
|
+
child_nodes = [wrapped] if wrapped else None
|
|
90
|
+
self.children.append(BulletedListMarkdownNode(texts=[text], children=child_nodes))
|
|
91
|
+
return self
|
|
92
|
+
|
|
93
|
+
def todo(
|
|
94
|
+
self,
|
|
95
|
+
text: str,
|
|
96
|
+
checked: bool = False,
|
|
97
|
+
builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None,
|
|
98
|
+
) -> Self:
|
|
99
|
+
children = self._build_children(builder_func)
|
|
100
|
+
self.children.append(TodoMarkdownNode(text=text, checked=checked, children=children))
|
|
101
|
+
return self
|
|
102
|
+
|
|
103
|
+
def checked_todo(self, text: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None) -> Self:
|
|
104
|
+
return self.todo(text, checked=True, builder_func=builder_func)
|
|
105
|
+
|
|
106
|
+
def unchecked_todo(
|
|
107
|
+
self, text: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None
|
|
108
|
+
) -> Self:
|
|
109
|
+
return self.todo(text, checked=False, builder_func=builder_func)
|
|
110
|
+
|
|
111
|
+
def todo_list(self, items: list[str], completed: list[bool] | None = None) -> Self:
|
|
112
|
+
completed = completed or [False] * len(items)
|
|
113
|
+
for i, item in enumerate(items):
|
|
114
|
+
is_done = completed[i] if i < len(completed) else False
|
|
115
|
+
self.children.append(TodoMarkdownNode(text=item, checked=is_done))
|
|
116
|
+
return self
|
|
117
|
+
|
|
118
|
+
def callout(self, text: str, emoji: str | None = None) -> Self:
|
|
119
|
+
self.children.append(CalloutMarkdownNode(text=text, emoji=emoji))
|
|
120
|
+
return self
|
|
121
|
+
|
|
122
|
+
def callout_with_children(
|
|
123
|
+
self,
|
|
124
|
+
text: str,
|
|
125
|
+
emoji: str | None = None,
|
|
126
|
+
builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None,
|
|
127
|
+
) -> Self:
|
|
128
|
+
children = self._build_children(builder_func)
|
|
129
|
+
self.children.append(CalloutMarkdownNode(text=text, emoji=emoji, children=children))
|
|
130
|
+
return self
|
|
131
|
+
|
|
132
|
+
def toggle(self, title: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder]) -> Self:
|
|
133
|
+
children = self._build_children(builder_func)
|
|
134
|
+
self.children.append(ToggleMarkdownNode(title=title, children=children))
|
|
135
|
+
return self
|
|
136
|
+
|
|
137
|
+
def image(self, url: str, caption: str | None = None) -> Self:
|
|
138
|
+
self.children.append(ImageMarkdownNode(url=url, caption=caption))
|
|
139
|
+
return self
|
|
140
|
+
|
|
141
|
+
def video(self, url: str, caption: str | None = None) -> Self:
|
|
142
|
+
self.children.append(VideoMarkdownNode(url=url, caption=caption))
|
|
143
|
+
return self
|
|
144
|
+
|
|
145
|
+
def audio(self, url: str, caption: str | None = None) -> Self:
|
|
146
|
+
self.children.append(AudioMarkdownNode(url=url, caption=caption))
|
|
147
|
+
return self
|
|
148
|
+
|
|
149
|
+
def file(self, url: str, caption: str | None = None) -> Self:
|
|
150
|
+
self.children.append(FileMarkdownNode(url=url, caption=caption))
|
|
151
|
+
return self
|
|
152
|
+
|
|
153
|
+
def pdf(self, url: str, caption: str | None = None) -> Self:
|
|
154
|
+
self.children.append(PdfMarkdownNode(url=url, caption=caption))
|
|
155
|
+
return self
|
|
156
|
+
|
|
157
|
+
def bookmark(self, url: str, title: str | None = None, caption: str | None = None) -> Self:
|
|
158
|
+
self.children.append(BookmarkMarkdownNode(url=url, title=title, caption=caption))
|
|
159
|
+
return self
|
|
160
|
+
|
|
161
|
+
def embed(self, url: str, caption: str | None = None) -> Self:
|
|
162
|
+
self.children.append(EmbedMarkdownNode(url=url, caption=caption))
|
|
163
|
+
return self
|
|
164
|
+
|
|
165
|
+
def code(self, code: str, language: CodingLanguage | None = None, caption: str | None = None) -> Self:
|
|
166
|
+
self.children.append(CodeMarkdownNode(code=code, language=language, caption=caption))
|
|
167
|
+
return self
|
|
168
|
+
|
|
169
|
+
def mermaid(self, diagram: str, caption: str | None = None) -> Self:
|
|
170
|
+
self.children.append(CodeMarkdownNode(code=diagram, language=CodingLanguage.MERMAID.value, caption=caption))
|
|
171
|
+
return self
|
|
172
|
+
|
|
173
|
+
def table(self, headers: list[str], rows: list[list[str]]) -> Self:
|
|
174
|
+
self.children.append(TableMarkdownNode(headers=headers, rows=rows))
|
|
175
|
+
return self
|
|
176
|
+
|
|
177
|
+
def breadcrumb(self) -> Self:
|
|
178
|
+
self.children.append(BreadcrumbMarkdownNode())
|
|
179
|
+
return self
|
|
180
|
+
|
|
181
|
+
def equation(self, expression: str) -> Self:
|
|
182
|
+
self.children.append(EquationMarkdownNode(expression=expression))
|
|
183
|
+
return self
|
|
184
|
+
|
|
185
|
+
def table_of_contents(self) -> Self:
|
|
186
|
+
self.children.append(TableOfContentsMarkdownNode())
|
|
187
|
+
return self
|
|
188
|
+
|
|
189
|
+
def columns(
|
|
190
|
+
self,
|
|
191
|
+
*builder_funcs: Callable[[MarkdownBuilder], MarkdownBuilder],
|
|
192
|
+
width_ratios: list[float] | None = None,
|
|
193
|
+
) -> Self:
|
|
194
|
+
columns = self._build_columns(builder_funcs, width_ratios)
|
|
195
|
+
self.children.append(ColumnListMarkdownNode(columns=columns))
|
|
196
|
+
return self
|
|
197
|
+
|
|
198
|
+
def build(self) -> str:
|
|
199
|
+
return "\n\n".join(child.to_markdown() for child in self.children if child is not None)
|
|
200
|
+
|
|
201
|
+
def _add_heading(
|
|
202
|
+
self, text: str, level: int, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None
|
|
203
|
+
) -> Self:
|
|
204
|
+
children = self._build_children(builder_func)
|
|
205
|
+
self.children.append(HeadingMarkdownNode(text=text, level=level, children=children))
|
|
206
|
+
return self
|
|
207
|
+
|
|
208
|
+
def _build_children(self, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None) -> list[MarkdownNode]:
|
|
209
|
+
if builder_func is None:
|
|
210
|
+
return []
|
|
211
|
+
|
|
212
|
+
builder = MarkdownBuilder()
|
|
213
|
+
builder_func(builder)
|
|
214
|
+
return builder.children
|
|
215
|
+
|
|
216
|
+
def _build_columns(
|
|
217
|
+
self,
|
|
218
|
+
builder_funcs: tuple[Callable[[MarkdownBuilder], MarkdownBuilder], ...],
|
|
219
|
+
width_ratios: list[float] | None,
|
|
220
|
+
) -> list[ColumnMarkdownNode]:
|
|
221
|
+
columns = []
|
|
222
|
+
for i, builder_func in enumerate(builder_funcs):
|
|
223
|
+
width_ratio = width_ratios[i] if width_ratios else None
|
|
224
|
+
children = self._build_children(builder_func)
|
|
225
|
+
columns.append(ColumnMarkdownNode(children=children, width_ratio=width_ratio))
|
|
226
|
+
return columns
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from .audio import AudioMarkdownNode
|
|
2
|
+
from .base import MarkdownNode
|
|
3
|
+
from .bookmark import BookmarkMarkdownNode
|
|
4
|
+
from .breadcrumb import BreadcrumbMarkdownNode
|
|
5
|
+
from .bulleted_list import BulletedListMarkdownNode
|
|
6
|
+
from .callout import CalloutMarkdownNode
|
|
7
|
+
from .code import CodeMarkdownNode
|
|
8
|
+
from .columns import ColumnListMarkdownNode, ColumnMarkdownNode
|
|
9
|
+
from .divider import DividerMarkdownNode
|
|
10
|
+
from .embed import EmbedMarkdownNode
|
|
11
|
+
from .equation import EquationMarkdownNode
|
|
12
|
+
from .file import FileMarkdownNode
|
|
13
|
+
from .heading import HeadingMarkdownNode
|
|
14
|
+
from .image import ImageMarkdownNode
|
|
15
|
+
from .numbered_list import NumberedListMarkdownNode
|
|
16
|
+
from .paragraph import ParagraphMarkdownNode
|
|
17
|
+
from .pdf import PdfMarkdownNode
|
|
18
|
+
from .quote import QuoteMarkdownNode
|
|
19
|
+
from .space import SpaceMarkdownNode
|
|
20
|
+
from .table import TableMarkdownNode
|
|
21
|
+
from .table_of_contents import TableOfContentsMarkdownNode
|
|
22
|
+
from .todo import TodoMarkdownNode
|
|
23
|
+
from .toggle import ToggleMarkdownNode
|
|
24
|
+
from .video import VideoMarkdownNode
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"AudioMarkdownNode",
|
|
28
|
+
"BookmarkMarkdownNode",
|
|
29
|
+
"BreadcrumbMarkdownNode",
|
|
30
|
+
"BulletedListMarkdownNode",
|
|
31
|
+
"CalloutMarkdownNode",
|
|
32
|
+
"CodeMarkdownNode",
|
|
33
|
+
"ColumnListMarkdownNode",
|
|
34
|
+
"ColumnMarkdownNode",
|
|
35
|
+
"DividerMarkdownNode",
|
|
36
|
+
"EmbedMarkdownNode",
|
|
37
|
+
"EquationMarkdownNode",
|
|
38
|
+
"FileMarkdownNode",
|
|
39
|
+
"HeadingMarkdownNode",
|
|
40
|
+
"ImageMarkdownNode",
|
|
41
|
+
"MarkdownNode",
|
|
42
|
+
"NumberedListMarkdownNode",
|
|
43
|
+
"ParagraphMarkdownNode",
|
|
44
|
+
"PdfMarkdownNode",
|
|
45
|
+
"QuoteMarkdownNode",
|
|
46
|
+
"SpaceMarkdownNode",
|
|
47
|
+
"TableMarkdownNode",
|
|
48
|
+
"TableOfContentsMarkdownNode",
|
|
49
|
+
"TodoMarkdownNode",
|
|
50
|
+
"ToggleMarkdownNode",
|
|
51
|
+
"VideoMarkdownNode",
|
|
52
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.markdown.nodes.mixins.caption import CaptionMarkdownNodeMixin
|
|
5
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AudioMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
url: str,
|
|
12
|
+
caption: str | None = None,
|
|
13
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
14
|
+
) -> None:
|
|
15
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
16
|
+
self.url = url
|
|
17
|
+
self.caption = caption
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def to_markdown(self) -> str:
|
|
21
|
+
audio_syntax = self._syntax_registry.get_audio_syntax()
|
|
22
|
+
base_markdown = f"{audio_syntax.start_delimiter}{self.url}{audio_syntax.end_delimiter}"
|
|
23
|
+
return self._append_caption_to_markdown(base_markdown, self.caption)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MarkdownNode(ABC):
|
|
7
|
+
def __init__(self, syntax_registry: SyntaxRegistry | None = None) -> None:
|
|
8
|
+
self._syntax_registry = syntax_registry or SyntaxRegistry()
|
|
9
|
+
|
|
10
|
+
@abstractmethod
|
|
11
|
+
def to_markdown(self) -> str:
|
|
12
|
+
pass
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.markdown.nodes.mixins.caption import CaptionMarkdownNodeMixin
|
|
5
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BookmarkMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
url: str,
|
|
12
|
+
title: str | None = None,
|
|
13
|
+
caption: str | None = None,
|
|
14
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
15
|
+
) -> None:
|
|
16
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
17
|
+
self.url = url
|
|
18
|
+
self.title = title
|
|
19
|
+
self.caption = caption
|
|
20
|
+
|
|
21
|
+
@override
|
|
22
|
+
def to_markdown(self) -> str:
|
|
23
|
+
bookmark_syntax = self._syntax_registry.get_bookmark_syntax()
|
|
24
|
+
base_markdown = f"{bookmark_syntax.start_delimiter}{self.url}{bookmark_syntax.end_delimiter}"
|
|
25
|
+
return self._append_caption_to_markdown(base_markdown, self.caption)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BreadcrumbMarkdownNode(MarkdownNode):
|
|
8
|
+
def __init__(self, syntax_registry: SyntaxRegistry | None = None) -> None:
|
|
9
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
10
|
+
|
|
11
|
+
@override
|
|
12
|
+
def to_markdown(self) -> str:
|
|
13
|
+
breadcrumb_syntax = self._syntax_registry.get_breadcrumb_syntax()
|
|
14
|
+
return breadcrumb_syntax.start_delimiter
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.markdown.nodes.container import ContainerNode
|
|
5
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BulletedListMarkdownNode(ContainerNode):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
texts: list[str],
|
|
12
|
+
children: list[MarkdownNode | None] | None = None,
|
|
13
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
14
|
+
) -> None:
|
|
15
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
16
|
+
self.texts = texts
|
|
17
|
+
self.children = children or []
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def to_markdown(self) -> str:
|
|
21
|
+
list_items = [self._render_list_item(index, text) for index, text in enumerate(self.texts)]
|
|
22
|
+
return "\n".join(list_items)
|
|
23
|
+
|
|
24
|
+
def _render_list_item(self, index: int, text: str) -> str:
|
|
25
|
+
delimiter = self._get_list_delimiter()
|
|
26
|
+
item_line = f"{delimiter}{text}"
|
|
27
|
+
|
|
28
|
+
child = self._get_child_for_item(index)
|
|
29
|
+
if child:
|
|
30
|
+
child_content = self.render_child(child)
|
|
31
|
+
return f"{item_line}\n{child_content}"
|
|
32
|
+
|
|
33
|
+
return item_line
|
|
34
|
+
|
|
35
|
+
def _get_list_delimiter(self) -> str:
|
|
36
|
+
return self._syntax_registry.get_bulleted_list_syntax().start_delimiter
|
|
37
|
+
|
|
38
|
+
def _get_child_for_item(self, index: int) -> MarkdownNode | None:
|
|
39
|
+
if not self.children or index >= len(self.children):
|
|
40
|
+
return None
|
|
41
|
+
return self.children[index]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.markdown.nodes.container import ContainerNode
|
|
5
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CalloutMarkdownNode(ContainerNode):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
text: str,
|
|
12
|
+
emoji: str | None = None,
|
|
13
|
+
children: list[MarkdownNode] | None = None,
|
|
14
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
15
|
+
):
|
|
16
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
17
|
+
self.text = text
|
|
18
|
+
self.emoji = emoji
|
|
19
|
+
self.children = children or []
|
|
20
|
+
|
|
21
|
+
@override
|
|
22
|
+
def to_markdown(self) -> str:
|
|
23
|
+
callout_content = self._format_callout_content()
|
|
24
|
+
start_delimiter = self._syntax_registry.get_callout_syntax().start_delimiter
|
|
25
|
+
result = f"{start_delimiter}({callout_content})"
|
|
26
|
+
|
|
27
|
+
result += self.render_children()
|
|
28
|
+
|
|
29
|
+
return result
|
|
30
|
+
|
|
31
|
+
def _format_callout_content(self) -> str:
|
|
32
|
+
if self.emoji:
|
|
33
|
+
return f'{self.text} "{self.emoji}"'
|
|
34
|
+
return self.text
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.enums import CodingLanguage
|
|
4
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
5
|
+
from notionary.page.content.markdown.nodes.mixins.caption import CaptionMarkdownNodeMixin
|
|
6
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CodeMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
code: str,
|
|
13
|
+
language: CodingLanguage | None = None,
|
|
14
|
+
caption: str | None = None,
|
|
15
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
16
|
+
) -> None:
|
|
17
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
18
|
+
self.code = code
|
|
19
|
+
self.language = language
|
|
20
|
+
self.caption = caption
|
|
21
|
+
|
|
22
|
+
@override
|
|
23
|
+
def to_markdown(self) -> str:
|
|
24
|
+
code_syntax = self._syntax_registry.get_code_syntax()
|
|
25
|
+
lang = self.language or ""
|
|
26
|
+
|
|
27
|
+
base_markdown = f"{code_syntax.start_delimiter}{lang}\n{self.code}\n{code_syntax.end_delimiter}"
|
|
28
|
+
return self._append_caption_to_markdown(base_markdown, self.caption)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.markdown.nodes.container import ContainerNode
|
|
5
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ColumnMarkdownNode(ContainerNode):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
children: list[MarkdownNode] | None = None,
|
|
12
|
+
width_ratio: float | None = None,
|
|
13
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
14
|
+
):
|
|
15
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
16
|
+
self.children = children or []
|
|
17
|
+
self.width_ratio = width_ratio
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def to_markdown(self) -> str:
|
|
21
|
+
start_tag = self._format_column_start_tag()
|
|
22
|
+
result = start_tag + self.render_children()
|
|
23
|
+
return result
|
|
24
|
+
|
|
25
|
+
def _format_column_start_tag(self) -> str:
|
|
26
|
+
delimiter = self._syntax_registry.get_column_syntax().start_delimiter
|
|
27
|
+
|
|
28
|
+
if self.width_ratio is not None:
|
|
29
|
+
return f"{delimiter} {self.width_ratio}"
|
|
30
|
+
return delimiter
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ColumnListMarkdownNode(MarkdownNode):
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
columns: list[ColumnMarkdownNode] | None = None,
|
|
37
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
38
|
+
):
|
|
39
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
40
|
+
self.columns = columns or []
|
|
41
|
+
|
|
42
|
+
@override
|
|
43
|
+
def to_markdown(self) -> str:
|
|
44
|
+
start_delimiter = self._get_column_list_delimiter()
|
|
45
|
+
|
|
46
|
+
if not self.columns:
|
|
47
|
+
return start_delimiter
|
|
48
|
+
|
|
49
|
+
result = start_delimiter + self._render_columns()
|
|
50
|
+
return result
|
|
51
|
+
|
|
52
|
+
def _get_column_list_delimiter(self) -> str:
|
|
53
|
+
return self._syntax_registry.get_column_list_syntax().start_delimiter
|
|
54
|
+
|
|
55
|
+
def _render_columns(self) -> str:
|
|
56
|
+
rendered_columns = []
|
|
57
|
+
|
|
58
|
+
for column in self.columns:
|
|
59
|
+
column_markdown = column.to_markdown()
|
|
60
|
+
if column_markdown:
|
|
61
|
+
indented = self._indent_column(column_markdown)
|
|
62
|
+
rendered_columns.append(indented)
|
|
63
|
+
|
|
64
|
+
return "\n" + "\n".join(rendered_columns) if rendered_columns else ""
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def _indent_column(text: str, indent: str = " ") -> str:
|
|
68
|
+
lines = text.split("\n")
|
|
69
|
+
return "\n".join(f"{indent}{line}" if line.strip() else line for line in lines)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
2
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
3
|
+
from notionary.page.content.syntax.grammar import MarkdownGrammar
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def flatten_children(children: list[MarkdownNode]) -> MarkdownNode | None:
|
|
7
|
+
"""Convert a list of child nodes to a single node for container compatibility."""
|
|
8
|
+
if not children:
|
|
9
|
+
return None
|
|
10
|
+
if len(children) == 1:
|
|
11
|
+
return children[0]
|
|
12
|
+
return _MultiChildWrapper(children)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class _MultiChildWrapper(MarkdownNode):
|
|
16
|
+
def __init__(self, children: list[MarkdownNode]) -> None:
|
|
17
|
+
super().__init__()
|
|
18
|
+
self.children = children
|
|
19
|
+
|
|
20
|
+
def to_markdown(self) -> str:
|
|
21
|
+
return "\n".join(child.to_markdown() for child in self.children if child)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ContainerNode(MarkdownNode):
|
|
25
|
+
children: list[MarkdownNode]
|
|
26
|
+
|
|
27
|
+
def __init__(self, syntax_registry: SyntaxRegistry | None = None) -> None:
|
|
28
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
29
|
+
grammar = self._get_grammar(syntax_registry)
|
|
30
|
+
self._spaces_per_nesting_level = grammar.spaces_per_nesting_level
|
|
31
|
+
|
|
32
|
+
def render_children(self, indent_level: int = 1) -> str:
|
|
33
|
+
if not self.children:
|
|
34
|
+
return ""
|
|
35
|
+
|
|
36
|
+
indent = self._calculate_indent(indent_level)
|
|
37
|
+
rendered = [self._indent_child(child, indent) for child in self.children]
|
|
38
|
+
rendered = [text for text in rendered if text]
|
|
39
|
+
|
|
40
|
+
return f"\n{'\n'.join(rendered)}" if rendered else ""
|
|
41
|
+
|
|
42
|
+
def render_child(self, child: MarkdownNode, indent_level: int = 1) -> str:
|
|
43
|
+
child_markdown = child.to_markdown()
|
|
44
|
+
if not child_markdown:
|
|
45
|
+
return ""
|
|
46
|
+
|
|
47
|
+
indent = self._calculate_indent(indent_level)
|
|
48
|
+
return self._indent_text(child_markdown, indent)
|
|
49
|
+
|
|
50
|
+
def _calculate_indent(self, level: int) -> str:
|
|
51
|
+
return " " * (self._spaces_per_nesting_level * level)
|
|
52
|
+
|
|
53
|
+
def _indent_child(self, child: MarkdownNode, indent: str) -> str:
|
|
54
|
+
child_markdown = child.to_markdown()
|
|
55
|
+
return self._indent_text(child_markdown, indent) if child_markdown else ""
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def _indent_text(text: str, indent: str) -> str:
|
|
59
|
+
lines = text.split("\n")
|
|
60
|
+
return "\n".join(f"{indent}{line}" if line.strip() else line for line in lines)
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def _get_grammar(syntax_registry: SyntaxRegistry | None) -> MarkdownGrammar:
|
|
64
|
+
return syntax_registry._markdown_grammar if syntax_registry else MarkdownGrammar()
|