notionary 0.1.24__py3-none-any.whl → 0.1.26__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.
Files changed (35) hide show
  1. notionary/elements/audio_element.py +41 -38
  2. notionary/elements/bookmark_element.py +36 -27
  3. notionary/elements/bulleted_list_element.py +28 -21
  4. notionary/elements/callout_element.py +39 -31
  5. notionary/elements/code_block_element.py +38 -26
  6. notionary/elements/divider_element.py +29 -18
  7. notionary/elements/embed_element.py +37 -28
  8. notionary/elements/heading_element.py +39 -24
  9. notionary/elements/image_element.py +33 -24
  10. notionary/elements/mention_element.py +40 -29
  11. notionary/elements/notion_block_element.py +13 -31
  12. notionary/elements/numbered_list_element.py +29 -20
  13. notionary/elements/paragraph_element.py +37 -31
  14. notionary/elements/prompts/element_prompt_content.py +91 -8
  15. notionary/elements/prompts/synthax_prompt_builder.py +64 -17
  16. notionary/elements/qoute_element.py +72 -74
  17. notionary/elements/registry/block_element_registry.py +1 -1
  18. notionary/elements/registry/block_element_registry_builder.py +6 -9
  19. notionary/elements/table_element.py +49 -36
  20. notionary/elements/text_inline_formatter.py +23 -15
  21. notionary/elements/{todo_lists.py → todo_element.py} +34 -25
  22. notionary/elements/toggle_element.py +184 -108
  23. notionary/elements/toggleable_heading_element.py +269 -0
  24. notionary/elements/video_element.py +37 -28
  25. notionary/page/content/page_content_manager.py +5 -8
  26. notionary/page/markdown_to_notion_converter.py +269 -274
  27. notionary/page/notion_page.py +1 -1
  28. notionary/page/notion_to_markdown_converter.py +20 -95
  29. {notionary-0.1.24.dist-info → notionary-0.1.26.dist-info}/METADATA +1 -1
  30. notionary-0.1.26.dist-info/RECORD +58 -0
  31. {notionary-0.1.24.dist-info → notionary-0.1.26.dist-info}/WHEEL +1 -1
  32. notionary/elements/column_element.py +0 -307
  33. notionary-0.1.24.dist-info/RECORD +0 -58
  34. {notionary-0.1.24.dist-info → notionary-0.1.26.dist-info}/licenses/LICENSE +0 -0
  35. {notionary-0.1.24.dist-info → notionary-0.1.26.dist-info}/top_level.txt +0 -0
@@ -140,7 +140,7 @@ class NotionPage(LoggingMixin):
140
140
  Returns:
141
141
  str: Status or confirmation message.
142
142
  """
143
- return await self._page_content_manager.append_markdown(markdown)
143
+ return await self._page_content_manager.append_markdown(markdown_text=markdown)
144
144
 
145
145
  async def clear(self) -> str:
146
146
  """
@@ -1,8 +1,6 @@
1
1
  from typing import Dict, Any, List, Optional
2
2
 
3
- from notionary.elements.registry.block_element_registry import (
4
- BlockElementRegistry,
5
- )
3
+ from notionary.elements.registry.block_element_registry import BlockElementRegistry
6
4
  from notionary.elements.registry.block_element_registry_builder import (
7
5
  BlockElementRegistryBuilder,
8
6
  )
@@ -11,12 +9,12 @@ from notionary.elements.registry.block_element_registry_builder import (
11
9
  class NotionToMarkdownConverter:
12
10
  """Converts Notion blocks to Markdown text with support for nested structures."""
13
11
 
12
+ TOGGLE_ELEMENT_TYPES = ["toggle", "toggleable_heading"]
13
+ LIST_ITEM_TYPES = ["numbered_list_item", "bulleted_list_item"]
14
+
14
15
  def __init__(self, block_registry: Optional[BlockElementRegistry] = None):
15
16
  """
16
17
  Initialize the NotionToMarkdownConverter.
17
-
18
- Args:
19
- block_registry: Optional registry of Notion block elements
20
18
  """
21
19
  self._block_registry = (
22
20
  block_registry or BlockElementRegistryBuilder().create_full_registry()
@@ -25,12 +23,6 @@ class NotionToMarkdownConverter:
25
23
  def convert(self, blocks: List[Dict[str, Any]]) -> str:
26
24
  """
27
25
  Convert Notion blocks to Markdown text, handling nested structures.
28
-
29
- Args:
30
- blocks: List of Notion blocks
31
-
32
- Returns:
33
- Markdown text
34
26
  """
35
27
  if not blocks:
36
28
  return ""
@@ -47,12 +39,6 @@ class NotionToMarkdownConverter:
47
39
  def _convert_single_block_with_children(self, block: Dict[str, Any]) -> str:
48
40
  """
49
41
  Process a single block, including any children.
50
-
51
- Args:
52
- block: Notion block to process
53
-
54
- Returns:
55
- Markdown representation of the block and its children
56
42
  """
57
43
  if not block:
58
44
  return ""
@@ -68,17 +54,14 @@ class NotionToMarkdownConverter:
68
54
 
69
55
  block_type = block.get("type", "")
70
56
 
71
- if block_type == "toggle":
57
+ if block_type in self.TOGGLE_ELEMENT_TYPES:
72
58
  return self._format_toggle_with_children(block_markdown, children_markdown)
73
59
 
74
- if block_type in ["numbered_list_item", "bulleted_list_item"]:
60
+ if block_type in self.LIST_ITEM_TYPES:
75
61
  return self._format_list_item_with_children(
76
62
  block_markdown, children_markdown
77
63
  )
78
64
 
79
- if block_type in ["column_list", "column"]:
80
- return children_markdown
81
-
82
65
  return self._format_standard_block_with_children(
83
66
  block_markdown, children_markdown
84
67
  )
@@ -86,12 +69,6 @@ class NotionToMarkdownConverter:
86
69
  def _has_children(self, block: Dict[str, Any]) -> bool:
87
70
  """
88
71
  Check if block has children that need processing.
89
-
90
- Args:
91
- block: Notion block to check
92
-
93
- Returns:
94
- True if block has children to process
95
72
  """
96
73
  return block.get("has_children", False) and "children" in block
97
74
 
@@ -99,14 +76,7 @@ class NotionToMarkdownConverter:
99
76
  self, toggle_markdown: str, children_markdown: str
100
77
  ) -> str:
101
78
  """
102
- Format toggle block with its children content.
103
-
104
- Args:
105
- toggle_markdown: Markdown for the toggle itself
106
- children_markdown: Markdown for toggle's children
107
-
108
- Returns:
109
- Formatted markdown with indented children
79
+ Format toggle or toggleable_heading block with its children content.
110
80
  """
111
81
  indented_children = self._indent_text(children_markdown)
112
82
  return f"{toggle_markdown}\n{indented_children}"
@@ -116,13 +86,6 @@ class NotionToMarkdownConverter:
116
86
  ) -> str:
117
87
  """
118
88
  Format list item with its children content.
119
-
120
- Args:
121
- item_markdown: Markdown for the list item itself
122
- children_markdown: Markdown for item's children
123
-
124
- Returns:
125
- Formatted markdown with indented children
126
89
  """
127
90
  indented_children = self._indent_text(children_markdown)
128
91
  return f"{item_markdown}\n{indented_children}"
@@ -132,26 +95,12 @@ class NotionToMarkdownConverter:
132
95
  ) -> str:
133
96
  """
134
97
  Format standard block with its children content.
135
-
136
- Args:
137
- block_markdown: Markdown for the block itself
138
- children_markdown: Markdown for block's children
139
-
140
- Returns:
141
- Formatted markdown with children after block
142
98
  """
143
99
  return f"{block_markdown}\n\n{children_markdown}"
144
100
 
145
101
  def _indent_text(self, text: str, spaces: int = 4) -> str:
146
102
  """
147
103
  Indent each line of text with specified number of spaces.
148
-
149
- Args:
150
- text: Text to indent
151
- spaces: Number of spaces to use for indentation
152
-
153
- Returns:
154
- Indented text
155
104
  """
156
105
  indent = " " * spaces
157
106
  return "\n".join([f"{indent}{line}" for line in text.split("\n")])
@@ -159,12 +108,6 @@ class NotionToMarkdownConverter:
159
108
  def extract_toggle_content(self, blocks: List[Dict[str, Any]]) -> str:
160
109
  """
161
110
  Extract only the content of toggles from blocks.
162
-
163
- Args:
164
- blocks: List of Notion blocks
165
-
166
- Returns:
167
- Markdown text with toggle contents
168
111
  """
169
112
  if not blocks:
170
113
  return ""
@@ -181,12 +124,8 @@ class NotionToMarkdownConverter:
181
124
  ) -> None:
182
125
  """
183
126
  Recursively extract toggle content from a block and its children.
184
-
185
- Args:
186
- block: Block to process
187
- result: List to collect toggle content
188
127
  """
189
- if self._is_toggle_with_children(block):
128
+ if self._is_toggle_or_heading_with_children(block):
190
129
  self._add_toggle_header_to_result(block, result)
191
130
  self._add_toggle_children_to_result(block, result)
192
131
 
@@ -194,31 +133,27 @@ class NotionToMarkdownConverter:
194
133
  for child in block["children"]:
195
134
  self._extract_toggle_content_recursive(child, result)
196
135
 
197
- def _is_toggle_with_children(self, block: Dict[str, Any]) -> bool:
136
+ def _is_toggle_or_heading_with_children(self, block: Dict[str, Any]) -> bool:
198
137
  """
199
- Check if block is a toggle with children.
200
-
201
- Args:
202
- block: Block to check
203
-
204
- Returns:
205
- True if block is a toggle with children
138
+ Check if block is a toggle or toggleable_heading with children.
206
139
  """
207
- return block.get("type") == "toggle" and "children" in block
140
+ return block.get("type") in self.TOGGLE_ELEMENT_TYPES and "children" in block
208
141
 
209
142
  def _add_toggle_header_to_result(
210
143
  self, block: Dict[str, Any], result: List[str]
211
144
  ) -> None:
212
145
  """
213
146
  Add toggle header text to result list.
214
-
215
- Args:
216
- block: Toggle block
217
- result: List to add header to
218
147
  """
219
- toggle_text = self._extract_text_from_rich_text(
220
- block.get("toggle", {}).get("rich_text", [])
221
- )
148
+ block_type = block.get("type")
149
+ rich_text = None
150
+
151
+ if block_type == "toggle":
152
+ rich_text = block.get("toggle", {}).get("rich_text", [])
153
+ elif block_type == "toggleable_heading":
154
+ rich_text = block.get("toggleable_heading", {}).get("rich_text", [])
155
+
156
+ toggle_text = self._extract_text_from_rich_text(rich_text or [])
222
157
 
223
158
  if toggle_text:
224
159
  result.append(f"### {toggle_text}")
@@ -228,10 +163,6 @@ class NotionToMarkdownConverter:
228
163
  ) -> None:
229
164
  """
230
165
  Add formatted toggle children to result list.
231
-
232
- Args:
233
- block: Toggle block with children
234
- result: List to add children content to
235
166
  """
236
167
  for child in block.get("children", []):
237
168
  child_type = child.get("type")
@@ -248,12 +179,6 @@ class NotionToMarkdownConverter:
248
179
  def _extract_text_from_rich_text(self, rich_text: List[Dict[str, Any]]) -> str:
249
180
  """
250
181
  Extract plain text from Notion's rich text array.
251
-
252
- Args:
253
- rich_text: List of rich text objects
254
-
255
- Returns:
256
- Concatenated plain text
257
182
  """
258
183
  if not rich_text:
259
184
  return ""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: notionary
3
- Version: 0.1.24
3
+ Version: 0.1.26
4
4
  Summary: A toolkit to convert between Markdown and Notion blocks
5
5
  Home-page: https://github.com/mathisarends/notionary
6
6
  Author: Mathis Arends
@@ -0,0 +1,58 @@
1
+ notionary/__init__.py,sha256=ypCkbF1YEcwy5aE2kGFmtJOZq41Vs01lahJNqRO27fU,735
2
+ notionary/notion_client.py,sha256=6YhaDK9UjH18Q0f1bxVVNGMbCvpQhatse6WRVIARGCE,6035
3
+ notionary/database/database_discovery.py,sha256=qDGFhXG9s-_6CXdRg8tMiwX4dvX7jLjgAUFPSNlYtlI,4506
4
+ notionary/database/database_info_service.py,sha256=Ig6gx8jUSPYORJvfgEV5kV6t72pZQsWU8HPMqd43B-o,1336
5
+ notionary/database/notion_database.py,sha256=YCOuzrWfKRJvMiWdVfEt8i3NAEqpKDwHj5SX4GUraEk,8017
6
+ notionary/database/notion_database_factory.py,sha256=Af57yaUHidD8TKJ8uyXOc2nnqHm7on6VGFdDRjxiq9o,6692
7
+ notionary/database/models/page_result.py,sha256=Vmm5_oYpYAkIIJVoTd1ZZGloeC3cmFLMYP255mAmtaw,233
8
+ notionary/elements/audio_element.py,sha256=WpcUpIv4U9grZegiCAmyc-Gj3XM3TQVFKr9PQM-QErg,5352
9
+ notionary/elements/bookmark_element.py,sha256=MS2K3pD-hMmS4Fmk1W8BwNL_-j4baHF42EoOWMNIfXw,8152
10
+ notionary/elements/bulleted_list_element.py,sha256=VdMKlxDlixTeYVQIqwM4vQ55xHqABtqzZDqIUATG9oE,2875
11
+ notionary/elements/callout_element.py,sha256=11qr9XGpF7jWle0wbog5eUrE4SAkThDdK9s4oplOxX0,4499
12
+ notionary/elements/code_block_element.py,sha256=6E-fc9zJegSQ_FjB08HImXL2rDQnmkFoRwoPuN-7PwI,6247
13
+ notionary/elements/divider_element.py,sha256=fLWU7Pn0JXIV8tPcuxvMMua4S54JOi2hQRjLcbVwC7g,2218
14
+ notionary/elements/embed_element.py,sha256=NEQopRLMzMIybVLbTt_y1cgaH1-VZW14xkbGJIDirkE,4586
15
+ notionary/elements/heading_element.py,sha256=Su4OUYeDvb9kvAFCmzagKQCNgkwvxsUn1KXJFTuYTFE,3186
16
+ notionary/elements/image_element.py,sha256=Qo94TavAnmJzGveDfdSkOAZeypij-zqbxsJ14rfbKIc,4765
17
+ notionary/elements/mention_element.py,sha256=B88AcU4XqpEXfhXywS_3K7TJIgoJT_3tWzJ_KcdJ4V4,8234
18
+ notionary/elements/notion_block_element.py,sha256=HN1Bu9wAaFsRIN6jkZSclo6KgB4nNvcVx7hlPWIhmy8,1265
19
+ notionary/elements/numbered_list_element.py,sha256=zW1MjNU_02mJBDGnaQFkAESY_i1rCYgp9o4zl6Au0DQ,2865
20
+ notionary/elements/paragraph_element.py,sha256=JuAN4cFRsJkePD7s7TNhDdSuojZtr6WOaCTGzJsBEGg,3262
21
+ notionary/elements/qoute_element.py,sha256=jG4FkD7Ieec3piBWcOeCZPgvw0GF0KZlxzdx2svQC0M,6129
22
+ notionary/elements/table_element.py,sha256=ujlo6bwrmKrQWRWGuUQebeDS-1T-c_R0SkzaPEPy2m8,11256
23
+ notionary/elements/text_inline_formatter.py,sha256=RdegEHvS5_AzDtqYxKb4b0uDbcpp3UhxHo6GyR84NFc,7780
24
+ notionary/elements/todo_element.py,sha256=cWuu8R55hNj6ubjrLu2yJpoeij7UfWhvmaklqcx8AI8,4131
25
+ notionary/elements/toggle_element.py,sha256=o7Utj0nKZAW5MzF_RMyD36HSWMj1cZmLEmqfGH-BFuY,11089
26
+ notionary/elements/toggleable_heading_element.py,sha256=EAkL9mU6u13JEgUOx6CeAhD8P3L_q6L_M_C8qZ_OTSs,9967
27
+ notionary/elements/video_element.py,sha256=2WwvZuG_UOVBnpEikZFxupe_x321QkczrodZHd5IeCw,5734
28
+ notionary/elements/prompts/element_prompt_content.py,sha256=Snha2FMtWO94kl1KwIJp6n7pY6Z8K-9_zVj1TUyaNNA,3729
29
+ notionary/elements/prompts/synthax_prompt_builder.py,sha256=W3ex5RDszlhMgYPGpemxiEI653hQKl147B-bh_ynR9g,6656
30
+ notionary/elements/registry/block_element_registry.py,sha256=r1V6waYQxZ_4bF6Mx_HmQ42-yhh8E54v-yNRT4PsnLk,3570
31
+ notionary/elements/registry/block_element_registry_builder.py,sha256=C6W64CwFqZBu31hZZqoD-uCZCOMOOXeFVk_SHKSp58U,9048
32
+ notionary/exceptions/database_exceptions.py,sha256=I-Tx6bYRLpi5pjGPtbT-Mqxvz3BFgYTiuZxknJeLxtI,2638
33
+ notionary/exceptions/page_creation_exception.py,sha256=4v7IuZD6GsQLrqhDLriGjuG3ML638gAO53zDCrLePuU,281
34
+ notionary/page/markdown_to_notion_converter.py,sha256=LFnv98uU3QRzxH7sZI0AE5_QxeHkNQZMxVcab-r8Qls,15877
35
+ notionary/page/notion_page.py,sha256=hCQpXhXE0pmgHc6FDjGnBz63wMOHtHjxmCBGOPEPRYI,17663
36
+ notionary/page/notion_page_factory.py,sha256=UUEZ-cyEWL0OMVPrgjc4vJdcplEa1bO2yHCYooACYC8,8189
37
+ notionary/page/notion_to_markdown_converter.py,sha256=EUiHeurQb73zQJ5Gatvf_67vWd-vOI8qg3yFLz8bf8Q,6452
38
+ notionary/page/content/notion_page_content_chunker.py,sha256=xRks74Dqec-De6-AVTxMPnXs-MSJBzSm1HfJfaHiKr8,3330
39
+ notionary/page/content/page_content_manager.py,sha256=0Bl52mEqKuhDGuJhOEMhGjJWHgxCiqGYjrqO1LqYzJM,6403
40
+ notionary/page/metadata/metadata_editor.py,sha256=61uiw8oB25O8ePhytoJvZDetuof5sjPoM6aoHZGo4wc,4949
41
+ notionary/page/metadata/notion_icon_manager.py,sha256=ixZrWsHGVpmF05Ncy9LCt8vZlKAQHYFZW-2yI5JZZDI,1426
42
+ notionary/page/metadata/notion_page_cover_manager.py,sha256=qgQxQE-bx4oWjLFUQvpXD5GzO1Mx7w7htz1xC2BOqUg,1717
43
+ notionary/page/properites/database_property_service.py,sha256=AJuBGahbb53VQa6IGGHxBMoOgCy6vFZg08uR_eDjNUs,11570
44
+ notionary/page/properites/page_property_manager.py,sha256=Xl8Cwn8WVszqpFXT_NvASkmP5igpCTEgRVhG_F45424,6914
45
+ notionary/page/properites/property_formatter.py,sha256=d_Nr5XQxgjB6VIS0u3ey14MOUKY416o_BvdXjbkUNAQ,3667
46
+ notionary/page/properites/property_operation_result.py,sha256=PhxHJJxxG2BdDl7aswhWnMSmf9RQtoinKkRHDoqxwCs,3913
47
+ notionary/page/properites/property_value_extractor.py,sha256=1BfyCYrFzfIUmNTozavrLTjG--6P6Dy2tkewf6rHHwQ,2353
48
+ notionary/page/relations/notion_page_relation_manager.py,sha256=ooN-WxXNPBE_vkNJgbc1wH8iIWBlKa9FHgiMeJjbYdw,13032
49
+ notionary/page/relations/notion_page_title_resolver.py,sha256=XQpa5XY4hoh5UYqpewTYaaftNX52WHyk2XmM4r8B2uY,3016
50
+ notionary/page/relations/page_database_relation.py,sha256=F9aGXFjjL8ZLNbfTGeGm_QAyXhz2AEOw7GgDLdprEcE,2313
51
+ notionary/page/relations/relation_operation_result.py,sha256=NDxBzGntOxc_89ti-HG8xDSqfY6PwyGHKHrrKbCzNjM,5010
52
+ notionary/util/logging_mixin.py,sha256=fKsx9t90bwvL74ZX3dU-sXdC4TZCQyO6qU9I8txkw_U,1369
53
+ notionary/util/page_id_utils.py,sha256=EYNMxgf-7ghzL5K8lKZBZfW7g5CsdY0Xuj4IYmU8RPk,1381
54
+ notionary-0.1.26.dist-info/licenses/LICENSE,sha256=zOm3cRT1qD49eg7vgw95MI79rpUAZa1kRBFwL2FkAr8,1120
55
+ notionary-0.1.26.dist-info/METADATA,sha256=_8qoYLY1oUq-iTJBFkp9FL9SuIt16eFK2DL0wXIemy8,8342
56
+ notionary-0.1.26.dist-info/WHEEL,sha256=7ciDxtlje1X8OhobNuGgi1t-ACdFSelPnSmDPrtlobY,91
57
+ notionary-0.1.26.dist-info/top_level.txt,sha256=fhONa6BMHQXqthx5PanWGbPL0b8rdFqhrJKVLf_adSs,10
58
+ notionary-0.1.26.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.1)
2
+ Generator: setuptools (80.2.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,307 +0,0 @@
1
- import re
2
- from typing import Dict, Any, Optional, List, Tuple, Callable
3
-
4
- from notionary.elements.notion_block_element import NotionBlockElement
5
- from notionary.elements.prompts.element_prompt_content import ElementPromptContent
6
-
7
-
8
- class ColumnElement(NotionBlockElement):
9
- """
10
- Handles conversion between custom Markdown column syntax and Notion column blocks.
11
-
12
- Markdown column syntax:
13
- ::: columns
14
- ::: column
15
- Content for first column
16
- :::
17
- ::: column
18
- Content for second column
19
- :::
20
- :::
21
-
22
- This creates a column layout in Notion with the specified content in each column.
23
- """
24
-
25
- COLUMNS_START = re.compile(r"^:::\s*columns\s*$")
26
- COLUMN_START = re.compile(r"^:::\s*column\s*$")
27
- BLOCK_END = re.compile(r"^:::\s*$")
28
-
29
- _converter_callback = None
30
-
31
- @classmethod
32
- def set_converter_callback(
33
- cls, callback: Callable[[str], List[Dict[str, Any]]]
34
- ) -> None:
35
- """
36
- Setze die Callback-Funktion, die zum Konvertieren von Markdown zu Notion-Blöcken verwendet wird.
37
-
38
- Args:
39
- callback: Funktion, die Markdown-Text annimmt und eine Liste von Notion-Blöcken zurückgibt
40
- """
41
- cls._converter_callback = callback
42
-
43
- @staticmethod
44
- def match_markdown(text: str) -> bool:
45
- """Check if text starts a columns block."""
46
- return bool(ColumnElement.COLUMNS_START.match(text.strip()))
47
-
48
- @staticmethod
49
- def match_notion(block: Dict[str, Any]) -> bool:
50
- """Check if block is a Notion column_list."""
51
- return block.get("type") == "column_list"
52
-
53
- @staticmethod
54
- def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
55
- """
56
- Convert markdown column syntax to Notion column blocks.
57
-
58
- Note: This only processes the first line (columns start).
59
- The full column content needs to be processed separately.
60
- """
61
- if not ColumnElement.COLUMNS_START.match(text.strip()):
62
- return None
63
-
64
- # Create an empty column_list block
65
- # Child columns will be added by the column processor
66
- return {"type": "column_list", "column_list": {"children": []}}
67
-
68
- @staticmethod
69
- def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
70
- """Convert Notion column_list block to markdown column syntax."""
71
- if block.get("type") != "column_list":
72
- return None
73
-
74
- column_children = block.get("column_list", {}).get("children", [])
75
-
76
- # Start the columns block
77
- result = ["::: columns"]
78
-
79
- # Process each column
80
- for column_block in column_children:
81
- if column_block.get("type") == "column":
82
- result.append("::: column")
83
-
84
- for _ in column_block.get("column", {}).get("children", []):
85
- result.append(" [Column content]") # Placeholder
86
-
87
- result.append(":::")
88
-
89
- # End the columns block
90
- result.append(":::")
91
-
92
- return "\n".join(result)
93
-
94
- @staticmethod
95
- def is_multiline() -> bool:
96
- """Column blocks span multiple lines."""
97
- return True
98
-
99
- @classmethod
100
- def find_matches(
101
- cls, text: str, converter_callback: Optional[Callable] = None
102
- ) -> List[Tuple[int, int, Dict[str, Any]]]:
103
- """
104
- Find all column block matches in the text and return their positions and blocks.
105
-
106
- Args:
107
- text: The input markdown text
108
- converter_callback: Optional callback to convert nested content
109
-
110
- Returns:
111
- List of tuples (start_pos, end_pos, block)
112
- """
113
- # Wenn ein Callback übergeben wurde, nutze diesen, sonst die gespeicherte Referenz
114
- converter = converter_callback or cls._converter_callback
115
- if not converter:
116
- raise ValueError(
117
- "No converter callback provided for ColumnElement. Call set_converter_callback first or provide converter_callback parameter."
118
- )
119
-
120
- matches = []
121
- lines = text.split("\n")
122
- i = 0
123
-
124
- while i < len(lines):
125
- # Skip non-column lines
126
- if not ColumnElement.COLUMNS_START.match(lines[i].strip()):
127
- i += 1
128
- continue
129
-
130
- # Process a column block and add to matches
131
- column_block_info = cls._process_column_block(lines, i, converter)
132
- matches.append(column_block_info)
133
-
134
- # Skip to the end of the processed column block
135
- i = column_block_info[3] # i is returned as the 4th element in the tuple
136
-
137
- return [(start, end, block) for start, end, block, _ in matches]
138
-
139
- @classmethod
140
- def _process_column_block(
141
- cls, lines: List[str], start_index: int, converter_callback: Callable
142
- ) -> Tuple[int, int, Dict[str, Any], int]:
143
- """
144
- Process a complete column block structure from the given starting line.
145
-
146
- Args:
147
- lines: All lines of the text
148
- start_index: Index of the column block start line
149
- converter_callback: Callback function to convert markdown to notion blocks
150
-
151
- Returns:
152
- Tuple of (start_pos, end_pos, block, next_line_index)
153
- """
154
- columns_start = start_index
155
- columns_block = cls.markdown_to_notion(lines[start_index].strip())
156
- columns_children = []
157
-
158
- next_index = cls._collect_columns(
159
- lines, start_index + 1, columns_children, converter_callback
160
- )
161
-
162
- # Add columns to the main block
163
- if columns_children:
164
- columns_block["column_list"]["children"] = columns_children
165
-
166
- # Calculate positions
167
- start_pos = sum(len(lines[j]) + 1 for j in range(columns_start))
168
- end_pos = sum(len(lines[j]) + 1 for j in range(next_index))
169
-
170
- return (start_pos, end_pos, columns_block, next_index)
171
-
172
- @classmethod
173
- def _collect_columns(
174
- cls,
175
- lines: List[str],
176
- start_index: int,
177
- columns_children: List[Dict[str, Any]],
178
- converter_callback: Callable,
179
- ) -> int:
180
- """
181
- Collect all columns within a column block structure.
182
-
183
- Args:
184
- lines: All lines of the text
185
- start_index: Index to start collecting from
186
- columns_children: List to append collected columns to
187
- converter_callback: Callback function to convert column content
188
-
189
- Returns:
190
- Next line index after all columns have been processed
191
- """
192
- i = start_index
193
- in_column = False
194
- column_content = []
195
-
196
- while i < len(lines):
197
- current_line = lines[i].strip()
198
-
199
- if cls.COLUMNS_START.match(current_line):
200
- break
201
-
202
- if cls.COLUMN_START.match(current_line):
203
- cls._finalize_column(
204
- column_content, columns_children, in_column, converter_callback
205
- )
206
- column_content = []
207
- in_column = True
208
- i += 1
209
- continue
210
-
211
- if cls.BLOCK_END.match(current_line) and in_column:
212
- cls._finalize_column(
213
- column_content, columns_children, in_column, converter_callback
214
- )
215
- column_content = []
216
- in_column = False
217
- i += 1
218
- continue
219
-
220
- if cls.BLOCK_END.match(current_line) and not in_column:
221
- i += 1
222
- break
223
-
224
- if in_column:
225
- column_content.append(lines[i])
226
-
227
- i += 1
228
-
229
- cls._finalize_column(
230
- column_content, columns_children, in_column, converter_callback
231
- )
232
-
233
- return i
234
-
235
- @staticmethod
236
- def _finalize_column(
237
- column_content: List[str],
238
- columns_children: List[Dict[str, Any]],
239
- in_column: bool,
240
- converter_callback: Callable,
241
- ) -> None:
242
- """
243
- Finalize a column by processing its content and adding it to the columns_children list.
244
-
245
- Args:
246
- column_content: Content lines of the column
247
- columns_children: List to append the column block to
248
- in_column: Whether we're currently in a column (if False, does nothing)
249
- converter_callback: Callback function to convert column content
250
- """
251
- if not (in_column and column_content):
252
- return
253
-
254
- # Process column content using the provided callback
255
- column_blocks = converter_callback("\n".join(column_content))
256
-
257
- # Create column block
258
- column_block = {"type": "column", "column": {"children": column_blocks}}
259
- columns_children.append(column_block)
260
-
261
- @classmethod
262
- def get_llm_prompt_content(cls) -> ElementPromptContent:
263
- """
264
- Returns structured LLM prompt metadata for the column layout element.
265
- """
266
- return {
267
- "description": "Creates a multi-column layout that displays content side by side.",
268
- "when_to_use": (
269
- "Use columns sparingly, only for direct comparisons or when parallel presentation significantly improves readability. "
270
- "Best for pros/cons lists, feature comparisons, or pairing images with descriptions. "
271
- "Avoid overusing as it can complicate document structure."
272
- ),
273
- "syntax": (
274
- "::: columns\n"
275
- "::: column\n"
276
- "Content for first column\n"
277
- ":::\n"
278
- "::: column\n"
279
- "Content for second column\n"
280
- ":::\n"
281
- ":::"
282
- ),
283
- "examples": [
284
- "::: columns\n"
285
- "::: column\n"
286
- "## Features\n"
287
- "- Fast response time\n"
288
- "- Intuitive interface\n"
289
- "- Regular updates\n"
290
- ":::\n"
291
- "::: column\n"
292
- "## Benefits\n"
293
- "- Increased productivity\n"
294
- "- Better collaboration\n"
295
- "- Simplified workflows\n"
296
- ":::\n"
297
- ":::",
298
- "::: columns\n"
299
- "::: column\n"
300
- "![Image placeholder](/api/placeholder/400/320)\n"
301
- ":::\n"
302
- "::: column\n"
303
- "This text appears next to the image, creating a media-with-caption style layout that's perfect for documentation or articles.\n"
304
- ":::\n"
305
- ":::",
306
- ],
307
- }