notionary 0.1.25__py3-none-any.whl → 0.1.27__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 -7
  15. notionary/elements/prompts/synthax_prompt_builder.py +63 -16
  16. notionary/elements/qoute_element.py +72 -74
  17. notionary/elements/registry/block_element_registry_builder.py +6 -9
  18. notionary/elements/table_element.py +49 -36
  19. notionary/elements/text_inline_formatter.py +23 -15
  20. notionary/elements/{todo_lists.py → todo_element.py} +34 -25
  21. notionary/elements/toggle_element.py +184 -108
  22. notionary/elements/toggleable_heading_element.py +269 -0
  23. notionary/elements/video_element.py +37 -28
  24. notionary/page/content/page_content_manager.py +3 -8
  25. notionary/page/markdown_to_notion_converter.py +269 -274
  26. notionary/page/metadata/notion_icon_manager.py +13 -7
  27. notionary/page/notion_page.py +3 -5
  28. notionary/page/notion_to_markdown_converter.py +20 -95
  29. {notionary-0.1.25.dist-info → notionary-0.1.27.dist-info}/METADATA +1 -1
  30. notionary-0.1.27.dist-info/RECORD +58 -0
  31. {notionary-0.1.25.dist-info → notionary-0.1.27.dist-info}/WHEEL +1 -1
  32. notionary/elements/column_element.py +0 -307
  33. notionary-0.1.25.dist-info/RECORD +0 -58
  34. {notionary-0.1.25.dist-info → notionary-0.1.27.dist-info}/licenses/LICENSE +0 -0
  35. {notionary-0.1.25.dist-info → notionary-0.1.27.dist-info}/top_level.txt +0 -0
@@ -2,7 +2,10 @@ import re
2
2
  from typing import Dict, Any, Optional, List, Tuple
3
3
  from notionary.elements.notion_block_element import NotionBlockElement
4
4
  from notionary.elements.text_inline_formatter import TextInlineFormatter
5
- from notionary.elements.prompts.element_prompt_content import ElementPromptContent
5
+ from notionary.elements.prompts.element_prompt_content import (
6
+ ElementPromptBuilder,
7
+ ElementPromptContent,
8
+ )
6
9
 
7
10
 
8
11
  class TableElement(NotionBlockElement):
@@ -21,8 +24,8 @@ class TableElement(NotionBlockElement):
21
24
  ROW_PATTERN = re.compile(r"^\s*\|(.+)\|\s*$")
22
25
  SEPARATOR_PATTERN = re.compile(r"^\s*\|([\s\-:|]+)\|\s*$")
23
26
 
24
- @staticmethod
25
- def match_markdown(text: str) -> bool:
27
+ @classmethod
28
+ def match_markdown(cls, text: str) -> bool:
26
29
  """Check if text contains a markdown table."""
27
30
  lines = text.split("\n")
28
31
 
@@ -39,13 +42,13 @@ class TableElement(NotionBlockElement):
39
42
 
40
43
  return False
41
44
 
42
- @staticmethod
43
- def match_notion(block: Dict[str, Any]) -> bool:
45
+ @classmethod
46
+ def match_notion(cls, block: Dict[str, Any]) -> bool:
44
47
  """Check if block is a Notion table."""
45
48
  return block.get("type") == "table"
46
49
 
47
- @staticmethod
48
- def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
50
+ @classmethod
51
+ def markdown_to_notion(cls, text: str) -> Optional[Dict[str, Any]]:
49
52
  """Convert markdown table to Notion table block."""
50
53
  if not TableElement.match_markdown(text):
51
54
  return None
@@ -76,8 +79,8 @@ class TableElement(NotionBlockElement):
76
79
  },
77
80
  }
78
81
 
79
- @staticmethod
80
- def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
82
+ @classmethod
83
+ def notion_to_markdown(cls, block: Dict[str, Any]) -> Optional[str]:
81
84
  """Convert Notion table block to markdown table."""
82
85
  if block.get("type") != "table":
83
86
  return None
@@ -138,13 +141,13 @@ class TableElement(NotionBlockElement):
138
141
 
139
142
  return "\n".join(table_rows)
140
143
 
141
- @staticmethod
142
- def is_multiline() -> bool:
144
+ @classmethod
145
+ def is_multiline(cls) -> bool:
143
146
  """Indicates if this element handles content that spans multiple lines."""
144
147
  return True
145
148
 
146
- @staticmethod
147
- def _find_table_start(lines: List[str]) -> Optional[int]:
149
+ @classmethod
150
+ def _find_table_start(cls, lines: List[str]) -> Optional[int]:
148
151
  """Find the start index of a table in the lines."""
149
152
  for i in range(len(lines) - 2):
150
153
  if (
@@ -155,16 +158,16 @@ class TableElement(NotionBlockElement):
155
158
  return i
156
159
  return None
157
160
 
158
- @staticmethod
159
- def _find_table_end(lines: List[str], start_idx: int) -> int:
161
+ @classmethod
162
+ def _find_table_end(cls, lines: List[str], start_idx: int) -> int:
160
163
  """Find the end index of a table, starting from start_idx."""
161
164
  end_idx = start_idx + 3 # Minimum: Header, Separator, one data row
162
165
  while end_idx < len(lines) and TableElement.ROW_PATTERN.match(lines[end_idx]):
163
166
  end_idx += 1
164
167
  return end_idx
165
168
 
166
- @staticmethod
167
- def _extract_table_rows(table_lines: List[str]) -> List[List[str]]:
169
+ @classmethod
170
+ def _extract_table_rows(cls, table_lines: List[str]) -> List[List[str]]:
168
171
  """Extract row contents from table lines, excluding separator line."""
169
172
  rows = []
170
173
  for i, line in enumerate(table_lines):
@@ -174,8 +177,8 @@ class TableElement(NotionBlockElement):
174
177
  rows.append(cells)
175
178
  return rows
176
179
 
177
- @staticmethod
178
- def _normalize_row_lengths(rows: List[List[str]], column_count: int) -> None:
180
+ @classmethod
181
+ def _normalize_row_lengths(cls, rows: List[List[str]], column_count: int) -> None:
179
182
  """Normalize row lengths to the specified column count."""
180
183
  for row in rows:
181
184
  if len(row) < column_count:
@@ -183,8 +186,8 @@ class TableElement(NotionBlockElement):
183
186
  elif len(row) > column_count:
184
187
  del row[column_count:]
185
188
 
186
- @staticmethod
187
- def _parse_table_row(row_text: str) -> List[str]:
189
+ @classmethod
190
+ def _parse_table_row(cls, row_text: str) -> List[str]:
188
191
  """Convert table row text to cell contents."""
189
192
  row_content = row_text.strip()
190
193
 
@@ -195,8 +198,8 @@ class TableElement(NotionBlockElement):
195
198
 
196
199
  return [cell.strip() for cell in row_content.split("|")]
197
200
 
198
- @staticmethod
199
- def _create_table_rows(rows: List[List[str]]) -> List[Dict[str, Any]]:
201
+ @classmethod
202
+ def _create_table_rows(cls, rows: List[List[str]]) -> List[Dict[str, Any]]:
200
203
  """Create Notion table rows from cell contents."""
201
204
  table_rows = []
202
205
 
@@ -230,8 +233,8 @@ class TableElement(NotionBlockElement):
230
233
 
231
234
  return table_rows
232
235
 
233
- @staticmethod
234
- def find_matches(text: str) -> List[Tuple[int, int, Dict[str, Any]]]:
236
+ @classmethod
237
+ def find_matches(cls, text: str) -> List[Tuple[int, int, Dict[str, Any]]]:
235
238
  """
236
239
  Find all tables in the text and return their positions.
237
240
 
@@ -272,8 +275,8 @@ class TableElement(NotionBlockElement):
272
275
 
273
276
  return matches
274
277
 
275
- @staticmethod
276
- def _calculate_position(lines: List[str], start: int, end: int) -> int:
278
+ @classmethod
279
+ def _calculate_position(cls, lines: List[str], start: int, end: int) -> int:
277
280
  """Calculate the text position in characters from line start to end."""
278
281
  position = 0
279
282
  for i in range(start, end):
@@ -283,12 +286,22 @@ class TableElement(NotionBlockElement):
283
286
  @classmethod
284
287
  def get_llm_prompt_content(cls) -> ElementPromptContent:
285
288
  """Returns information for LLM prompts about this element."""
286
- return {
287
- "description": "Creates formatted tables with rows and columns for structured data.",
288
- "when_to_use": "Use tables to organize and present structured data in a grid format, making information easier to compare and analyze. Tables are ideal for data sets, comparison charts, pricing information, or any content that benefits from columnar organization.",
289
- "syntax": "| Header 1 | Header 2 | Header 3 |\n| -------- | -------- | -------- |\n| Cell 1 | Cell 2 | Cell 3 |",
290
- "examples": [
291
- "| Product | Price | Stock |\n| ------- | ----- | ----- |\n| Widget A | $10.99 | 42 |\n| Widget B | $14.99 | 27 |",
292
- "| Name | Role | Department |\n| ---- | ---- | ---------- |\n| John Smith | Manager | Marketing |\n| Jane Doe | Director | Sales |",
293
- ],
294
- }
289
+ return (
290
+ ElementPromptBuilder()
291
+ .with_description(
292
+ "Creates formatted tables with rows and columns for structured data."
293
+ )
294
+ .with_usage_guidelines(
295
+ "Use tables to organize and present structured data in a grid format, making information easier to compare and analyze. Tables are ideal for data sets, comparison charts, pricing information, or any content that benefits from columnar organization."
296
+ )
297
+ .with_syntax(
298
+ "| Header 1 | Header 2 | Header 3 |\n| -------- | -------- | -------- |\n| Cell 1 | Cell 2 | Cell 3 |"
299
+ )
300
+ .with_examples(
301
+ [
302
+ "| Product | Price | Stock |\n| ------- | ----- | ----- |\n| Widget A | $10.99 | 42 |\n| Widget B | $14.99 | 27 |",
303
+ "| Name | Role | Department |\n| ---- | ---- | ---------- |\n| John Smith | Manager | Marketing |\n| Jane Doe | Director | Sales |",
304
+ ]
305
+ )
306
+ .build()
307
+ )
@@ -1,7 +1,10 @@
1
1
  from typing import Dict, Any, List, Tuple
2
2
  import re
3
3
 
4
- from notionary.elements.prompts.element_prompt_content import ElementPromptContent
4
+ from notionary.elements.prompts.element_prompt_content import (
5
+ ElementPromptBuilder,
6
+ ElementPromptContent,
7
+ )
5
8
 
6
9
 
7
10
  class TextInlineFormatter:
@@ -15,7 +18,6 @@ class TextInlineFormatter:
15
18
  - Strikethrough: ~~text~~
16
19
  - Code: `text`
17
20
  - Links: [text](url)
18
- - Highlights: ==text== (default yellow) or ==color:text== (custom color)
19
21
  """
20
22
 
21
23
  # Format patterns for matching Markdown formatting
@@ -214,18 +216,24 @@ class TextInlineFormatter:
214
216
  """
215
217
  Returns structured LLM prompt metadata for inline formatting.
216
218
  """
217
- return {
218
- "description": "Enables inline formatting like bold, italics, strikethrough, code, links, and underlining for enhanced readability and emphasis.",
219
- "when_to_use": (
219
+ return (
220
+ ElementPromptBuilder()
221
+ .with_description(
222
+ "Enables inline formatting like bold, italics, strikethrough, code, links, and underlining for enhanced readability and emphasis."
223
+ )
224
+ .with_usage_guidelines(
220
225
  "Use inline formatting to highlight important words, provide emphasis, show code or paths, or add hyperlinks. "
221
226
  "Helps create a visual hierarchy and improves scanability of long texts."
222
- ),
223
- "syntax": "**bold**, *italic*, `code`, [text](url)",
224
- "examples": [
225
- "This is a **bold** word.",
226
- "Use *italics* for emphasis.",
227
- "Mark outdated content like ~~this~~.",
228
- "Write `config.json` to reference a file.",
229
- "Visit [Notion](https://notion.so) for more info.",
230
- ],
231
- }
227
+ )
228
+ .with_syntax("**bold**, *italic*, `code`, [text](url)")
229
+ .with_examples(
230
+ [
231
+ "This is a **bold** word.",
232
+ "Use *italics* for emphasis.",
233
+ "Mark outdated content like ~~this~~.",
234
+ "Write `config.json` to reference a file.",
235
+ "Visit [Notion](https://notion.so) for more info.",
236
+ ]
237
+ )
238
+ .build()
239
+ )
@@ -1,7 +1,10 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional
3
3
  from notionary.elements.notion_block_element import NotionBlockElement
4
- from notionary.elements.prompts.element_prompt_content import ElementPromptContent
4
+ from notionary.elements.prompts.element_prompt_content import (
5
+ ElementPromptBuilder,
6
+ ElementPromptContent,
7
+ )
5
8
  from notionary.elements.text_inline_formatter import TextInlineFormatter
6
9
 
7
10
 
@@ -20,20 +23,20 @@ class TodoElement(NotionBlockElement):
20
23
  TODO_PATTERN = re.compile(r"^\s*[-*+]\s+\[\s?\]\s+(.+)$")
21
24
  DONE_PATTERN = re.compile(r"^\s*[-*+]\s+\[x\]\s+(.+)$")
22
25
 
23
- @staticmethod
24
- def match_markdown(text: str) -> bool:
26
+ @classmethod
27
+ def match_markdown(cls, text: str) -> bool:
25
28
  """Check if text is a markdown todo item."""
26
29
  return bool(
27
30
  TodoElement.TODO_PATTERN.match(text) or TodoElement.DONE_PATTERN.match(text)
28
31
  )
29
32
 
30
- @staticmethod
31
- def match_notion(block: Dict[str, Any]) -> bool:
33
+ @classmethod
34
+ def match_notion(cls, block: Dict[str, Any]) -> bool:
32
35
  """Check if block is a Notion to_do block."""
33
36
  return block.get("type") == "to_do"
34
37
 
35
- @staticmethod
36
- def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
38
+ @classmethod
39
+ def markdown_to_notion(cls, text: str) -> Optional[Dict[str, Any]]:
37
40
  """Convert markdown todo item to Notion to_do block."""
38
41
  done_match = TodoElement.DONE_PATTERN.match(text)
39
42
  if done_match:
@@ -47,8 +50,8 @@ class TodoElement(NotionBlockElement):
47
50
 
48
51
  return None
49
52
 
50
- @staticmethod
51
- def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
53
+ @classmethod
54
+ def notion_to_markdown(cls, block: Dict[str, Any]) -> Optional[str]:
52
55
  """Convert Notion to_do block to markdown todo item."""
53
56
  if block.get("type") != "to_do":
54
57
  return None
@@ -64,8 +67,8 @@ class TodoElement(NotionBlockElement):
64
67
  checkbox = "[x]" if checked else "[ ]"
65
68
  return f"- {checkbox} {content}"
66
69
 
67
- @staticmethod
68
- def _create_todo_block(content: str, checked: bool) -> Dict[str, Any]:
70
+ @classmethod
71
+ def _create_todo_block(cls, content: str, checked: bool) -> Dict[str, Any]:
69
72
  """
70
73
  Create a Notion to_do block.
71
74
 
@@ -85,8 +88,8 @@ class TodoElement(NotionBlockElement):
85
88
  },
86
89
  }
87
90
 
88
- @staticmethod
89
- def is_multiline() -> bool:
91
+ @classmethod
92
+ def is_multiline(cls) -> bool:
90
93
  return False
91
94
 
92
95
  @classmethod
@@ -94,17 +97,23 @@ class TodoElement(NotionBlockElement):
94
97
  """
95
98
  Returns structured LLM prompt metadata for the todo element.
96
99
  """
97
- return {
98
- "description": "Creates interactive to-do items with checkboxes that can be marked as complete.",
99
- "when_to_use": (
100
+ return (
101
+ ElementPromptBuilder()
102
+ .with_description(
103
+ "Creates interactive to-do items with checkboxes that can be marked as complete."
104
+ )
105
+ .with_usage_guidelines(
100
106
  "Use to-do items for task lists, checklists, or tracking progress on items that need to be completed. "
101
107
  "Todo items are interactive in Notion and can be checked/unchecked directly."
102
- ),
103
- "syntax": "- [ ] Task to complete",
104
- "examples": [
105
- "- [ ] Draft project proposal",
106
- "- [x] Create initial timeline",
107
- "* [ ] Review code changes",
108
- "+ [x] Finalize handoff checklist",
109
- ],
110
- }
108
+ )
109
+ .with_syntax("- [ ] Task to complete")
110
+ .with_examples(
111
+ [
112
+ "- [ ] Draft project proposal",
113
+ "- [x] Create initial timeline",
114
+ "* [ ] Review code changes",
115
+ "+ [x] Finalize handoff checklist",
116
+ ]
117
+ )
118
+ .build()
119
+ )