notionary 0.2.10__py3-none-any.whl → 0.2.12__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 +6 -0
- notionary/cli/main.py +184 -0
- notionary/cli/onboarding.py +0 -0
- notionary/database/database_discovery.py +1 -1
- notionary/database/notion_database.py +5 -4
- notionary/database/notion_database_factory.py +10 -5
- notionary/elements/audio_element.py +2 -2
- notionary/elements/bookmark_element.py +2 -2
- notionary/elements/bulleted_list_element.py +2 -2
- notionary/elements/callout_element.py +2 -2
- notionary/elements/code_block_element.py +2 -2
- notionary/elements/column_element.py +51 -44
- notionary/elements/divider_element.py +2 -2
- notionary/elements/embed_element.py +2 -2
- notionary/elements/heading_element.py +3 -3
- notionary/elements/image_element.py +2 -2
- notionary/elements/mention_element.py +2 -2
- notionary/elements/notion_block_element.py +36 -0
- notionary/elements/numbered_list_element.py +2 -2
- notionary/elements/paragraph_element.py +2 -2
- notionary/elements/qoute_element.py +2 -2
- notionary/elements/table_element.py +2 -2
- notionary/elements/text_inline_formatter.py +23 -1
- notionary/elements/todo_element.py +2 -2
- notionary/elements/toggle_element.py +2 -2
- notionary/elements/toggleable_heading_element.py +2 -2
- notionary/elements/video_element.py +2 -2
- notionary/notion_client.py +1 -1
- notionary/page/content/notion_page_content_chunker.py +1 -1
- notionary/page/content/page_content_retriever.py +1 -1
- notionary/page/content/page_content_writer.py +3 -3
- notionary/page/{markdown_to_notion_converter.py → formatting/markdown_to_notion_converter.py} +44 -140
- notionary/page/formatting/spacer_rules.py +483 -0
- notionary/page/metadata/metadata_editor.py +1 -1
- notionary/page/metadata/notion_icon_manager.py +1 -1
- notionary/page/metadata/notion_page_cover_manager.py +1 -1
- notionary/page/notion_page.py +1 -1
- notionary/page/notion_page_factory.py +161 -22
- notionary/page/properites/database_property_service.py +1 -1
- notionary/page/properites/page_property_manager.py +1 -1
- notionary/page/properites/property_formatter.py +1 -1
- notionary/page/properites/property_value_extractor.py +1 -1
- notionary/page/relations/notion_page_relation_manager.py +1 -1
- notionary/page/relations/notion_page_title_resolver.py +1 -1
- notionary/page/relations/page_database_relation.py +1 -1
- notionary/prompting/element_prompt_content.py +1 -0
- notionary/telemetry/__init__.py +7 -0
- notionary/telemetry/telemetry.py +226 -0
- notionary/telemetry/track_usage_decorator.py +76 -0
- notionary/util/__init__.py +5 -0
- notionary/util/logging_mixin.py +3 -0
- notionary/util/singleton.py +18 -0
- {notionary-0.2.10.dist-info → notionary-0.2.12.dist-info}/METADATA +3 -1
- notionary-0.2.12.dist-info/RECORD +70 -0
- {notionary-0.2.10.dist-info → notionary-0.2.12.dist-info}/WHEEL +1 -1
- notionary-0.2.12.dist-info/entry_points.txt +2 -0
- notionary-0.2.10.dist-info/RECORD +0 -61
- {notionary-0.2.10.dist-info → notionary-0.2.12.dist-info}/licenses/LICENSE +0 -0
- {notionary-0.2.10.dist-info → notionary-0.2.12.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,13 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional
|
3
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
3
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
4
4
|
from notionary.prompting.element_prompt_content import (
|
5
5
|
ElementPromptBuilder,
|
6
6
|
ElementPromptContent,
|
7
7
|
)
|
8
8
|
from notionary.elements.text_inline_formatter import TextInlineFormatter
|
9
9
|
|
10
|
-
|
10
|
+
@auto_track_conversions
|
11
11
|
class NumberedListElement(NotionBlockElement):
|
12
12
|
"""Class for converting between Markdown numbered lists and Notion numbered list items."""
|
13
13
|
|
@@ -1,13 +1,13 @@
|
|
1
1
|
from typing import Dict, Any, Optional
|
2
2
|
|
3
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
3
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
4
4
|
from notionary.prompting.element_prompt_content import (
|
5
5
|
ElementPromptBuilder,
|
6
6
|
ElementPromptContent,
|
7
7
|
)
|
8
8
|
from notionary.elements.text_inline_formatter import TextInlineFormatter
|
9
9
|
|
10
|
-
|
10
|
+
@auto_track_conversions
|
11
11
|
class ParagraphElement(NotionBlockElement):
|
12
12
|
"""Handles conversion between Markdown paragraphs and Notion paragraph blocks."""
|
13
13
|
|
@@ -1,12 +1,12 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional, List, Tuple
|
3
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
3
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
4
4
|
from notionary.prompting.element_prompt_content import (
|
5
5
|
ElementPromptBuilder,
|
6
6
|
ElementPromptContent,
|
7
7
|
)
|
8
8
|
|
9
|
-
|
9
|
+
@auto_track_conversions
|
10
10
|
class QuoteElement(NotionBlockElement):
|
11
11
|
"""Class for converting between Markdown blockquotes and Notion quote blocks."""
|
12
12
|
|
@@ -1,13 +1,13 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional, List, Tuple
|
3
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
3
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
4
4
|
from notionary.elements.text_inline_formatter import TextInlineFormatter
|
5
5
|
from notionary.prompting.element_prompt_content import (
|
6
6
|
ElementPromptBuilder,
|
7
7
|
ElementPromptContent,
|
8
8
|
)
|
9
9
|
|
10
|
-
|
10
|
+
@auto_track_conversions
|
11
11
|
class TableElement(NotionBlockElement):
|
12
12
|
"""
|
13
13
|
Handles conversion between Markdown tables and Notion table blocks.
|
@@ -29,6 +29,7 @@ class TextInlineFormatter:
|
|
29
29
|
(r"~~(.+?)~~", {"strikethrough": True}),
|
30
30
|
(r"`(.+?)`", {"code": True}),
|
31
31
|
(r"\[(.+?)\]\((.+?)\)", {"link": True}),
|
32
|
+
(r"@\[([0-9a-f-]+)\]", {"mention": True}),
|
32
33
|
]
|
33
34
|
|
34
35
|
@classmethod
|
@@ -87,11 +88,15 @@ class TextInlineFormatter:
|
|
87
88
|
cls._create_text_element(remaining_text[:earliest_pos], {})
|
88
89
|
)
|
89
90
|
|
90
|
-
|
91
|
+
if "link" in earliest_format:
|
91
92
|
content = earliest_match.group(1)
|
92
93
|
url = earliest_match.group(2)
|
93
94
|
segments.append(cls._create_link_element(content, url))
|
94
95
|
|
96
|
+
elif "mention" in earliest_format:
|
97
|
+
id = earliest_match.group(1)
|
98
|
+
segments.append(cls._create_mention_element(id))
|
99
|
+
|
95
100
|
else:
|
96
101
|
content = earliest_match.group(1)
|
97
102
|
segments.append(cls._create_text_element(content, earliest_format))
|
@@ -152,6 +157,23 @@ class TextInlineFormatter:
|
|
152
157
|
"plain_text": text,
|
153
158
|
}
|
154
159
|
|
160
|
+
@classmethod
|
161
|
+
def _create_mention_element(cls, id: str) -> Dict[str, Any]:
|
162
|
+
"""
|
163
|
+
Create a Notion mention element.
|
164
|
+
|
165
|
+
Args:
|
166
|
+
id: The page ID
|
167
|
+
|
168
|
+
Returns:
|
169
|
+
Notion rich_text element with mention
|
170
|
+
"""
|
171
|
+
return {
|
172
|
+
"type": "mention",
|
173
|
+
"mention": {"type": "page", "page": {"id": id}},
|
174
|
+
"annotations": cls._default_annotations(),
|
175
|
+
}
|
176
|
+
|
155
177
|
@classmethod
|
156
178
|
def extract_text_with_formatting(cls, rich_text: List[Dict[str, Any]]) -> str:
|
157
179
|
"""
|
@@ -1,13 +1,13 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional
|
3
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
3
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
4
4
|
from notionary.prompting.element_prompt_content import (
|
5
5
|
ElementPromptBuilder,
|
6
6
|
ElementPromptContent,
|
7
7
|
)
|
8
8
|
from notionary.elements.text_inline_formatter import TextInlineFormatter
|
9
9
|
|
10
|
-
|
10
|
+
@auto_track_conversions
|
11
11
|
class TodoElement(NotionBlockElement):
|
12
12
|
"""
|
13
13
|
Handles conversion between Markdown todo items and Notion to_do blocks.
|
@@ -1,13 +1,13 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional, List, Tuple, Callable
|
3
3
|
|
4
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
4
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
5
5
|
from notionary.prompting.element_prompt_content import (
|
6
6
|
ElementPromptBuilder,
|
7
7
|
ElementPromptContent,
|
8
8
|
)
|
9
9
|
|
10
|
-
|
10
|
+
@auto_track_conversions
|
11
11
|
class ToggleElement(NotionBlockElement):
|
12
12
|
"""
|
13
13
|
Improved ToggleElement class using pipe syntax instead of indentation.
|
@@ -1,14 +1,14 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional, List, Tuple, Callable
|
3
3
|
|
4
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
4
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
5
5
|
from notionary.prompting.element_prompt_content import (
|
6
6
|
ElementPromptBuilder,
|
7
7
|
ElementPromptContent,
|
8
8
|
)
|
9
9
|
from notionary.elements.text_inline_formatter import TextInlineFormatter
|
10
10
|
|
11
|
-
|
11
|
+
@auto_track_conversions
|
12
12
|
class ToggleableHeadingElement(NotionBlockElement):
|
13
13
|
"""Handles conversion between Markdown collapsible headings and Notion toggleable heading blocks with pipe syntax."""
|
14
14
|
|
@@ -1,12 +1,12 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional, List
|
3
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
3
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
4
4
|
from notionary.prompting.element_prompt_content import (
|
5
5
|
ElementPromptBuilder,
|
6
6
|
ElementPromptContent,
|
7
7
|
)
|
8
8
|
|
9
|
-
|
9
|
+
@auto_track_conversions
|
10
10
|
class VideoElement(NotionBlockElement):
|
11
11
|
"""
|
12
12
|
Handles conversion between Markdown video embeds and Notion video blocks.
|
notionary/notion_client.py
CHANGED
@@ -7,7 +7,7 @@ import httpx
|
|
7
7
|
from dotenv import load_dotenv
|
8
8
|
from notionary.models.notion_database_response import NotionDatabaseResponse
|
9
9
|
from notionary.models.notion_page_response import NotionPageResponse
|
10
|
-
from notionary.util
|
10
|
+
from notionary.util import LoggingMixin
|
11
11
|
|
12
12
|
|
13
13
|
class HttpMethod(Enum):
|
@@ -6,7 +6,7 @@ from notionary.notion_client import NotionClient
|
|
6
6
|
from notionary.page.notion_to_markdown_converter import (
|
7
7
|
NotionToMarkdownConverter,
|
8
8
|
)
|
9
|
-
from notionary.util
|
9
|
+
from notionary.util import LoggingMixin
|
10
10
|
|
11
11
|
|
12
12
|
class PageContentRetriever(LoggingMixin):
|
@@ -4,7 +4,7 @@ from notionary.elements.divider_element import DividerElement
|
|
4
4
|
from notionary.elements.registry.block_registry import BlockRegistry
|
5
5
|
from notionary.notion_client import NotionClient
|
6
6
|
|
7
|
-
from notionary.page.markdown_to_notion_converter import (
|
7
|
+
from notionary.page.formatting.markdown_to_notion_converter import (
|
8
8
|
MarkdownToNotionConverter,
|
9
9
|
)
|
10
10
|
from notionary.page.notion_to_markdown_converter import (
|
@@ -13,7 +13,7 @@ from notionary.page.notion_to_markdown_converter import (
|
|
13
13
|
from notionary.page.content.notion_page_content_chunker import (
|
14
14
|
NotionPageContentChunker,
|
15
15
|
)
|
16
|
-
from notionary.util
|
16
|
+
from notionary.util import LoggingMixin
|
17
17
|
|
18
18
|
|
19
19
|
class PageContentWriter(LoggingMixin):
|
@@ -44,7 +44,7 @@ class PageContentWriter(LoggingMixin):
|
|
44
44
|
)
|
45
45
|
append_divider = False
|
46
46
|
|
47
|
-
# Append divider in markdown format as it will be converted to a Notion divider block
|
47
|
+
# Append divider in markdown format as it will be converted to a Notion divider block
|
48
48
|
if append_divider:
|
49
49
|
markdown_text = markdown_text + "---\n"
|
50
50
|
|
notionary/page/{markdown_to_notion_converter.py → formatting/markdown_to_notion_converter.py}
RENAMED
@@ -1,21 +1,14 @@
|
|
1
|
-
from typing import Dict, Any, List, Optional, Tuple
|
2
1
|
import re
|
2
|
+
from typing import Dict, Any, List, Optional, Tuple
|
3
3
|
|
4
4
|
from notionary.elements.column_element import ColumnElement
|
5
5
|
from notionary.elements.registry.block_registry import BlockRegistry
|
6
|
-
from notionary.elements.registry.block_registry_builder import
|
7
|
-
|
8
|
-
)
|
6
|
+
from notionary.elements.registry.block_registry_builder import BlockRegistryBuilder
|
7
|
+
from notionary.page.formatting.spacer_rules import SpacerRule, SpacerRuleEngine
|
9
8
|
|
10
9
|
|
11
10
|
class MarkdownToNotionConverter:
|
12
|
-
"""
|
13
|
-
|
14
|
-
SPACER_MARKER = "---spacer---"
|
15
|
-
TOGGLE_ELEMENT_TYPES = ["ToggleElement", "ToggleableHeadingElement"]
|
16
|
-
PIPE_CONTENT_PATTERN = r"^\|\s?(.*)$"
|
17
|
-
HEADING_PATTERN = r"^(#{1,6})\s+(.+)$"
|
18
|
-
DIVIDER_PATTERN = r"^-{3,}$"
|
11
|
+
"""Refactored converter mit expliziten Spacer-Regeln"""
|
19
12
|
|
20
13
|
def __init__(self, block_registry: Optional[BlockRegistry] = None):
|
21
14
|
"""Initialize the converter with an optional custom block registry."""
|
@@ -23,6 +16,13 @@ class MarkdownToNotionConverter:
|
|
23
16
|
block_registry or BlockRegistryBuilder().create_full_registry()
|
24
17
|
)
|
25
18
|
|
19
|
+
# Spacer-Engine mit konfigurierbaren Regeln
|
20
|
+
self._spacer_engine = SpacerRuleEngine()
|
21
|
+
|
22
|
+
# Pattern für andere Verarbeitungsschritte
|
23
|
+
self.TOGGLE_ELEMENT_TYPES = ["ToggleElement", "ToggleableHeadingElement"]
|
24
|
+
self.PIPE_CONTENT_PATTERN = r"^\|\s?(.*)$"
|
25
|
+
|
26
26
|
if self._block_registry.contains(ColumnElement):
|
27
27
|
ColumnElement.set_converter_callback(self.convert)
|
28
28
|
|
@@ -31,157 +31,61 @@ class MarkdownToNotionConverter:
|
|
31
31
|
if not markdown_text:
|
32
32
|
return []
|
33
33
|
|
34
|
-
#
|
35
|
-
processed_markdown = self.
|
36
|
-
print("Processed Markdown:", processed_markdown)
|
34
|
+
# Spacer-Verarbeitung mit expliziten Regeln
|
35
|
+
processed_markdown = self._add_spacers_with_rules(markdown_text)
|
37
36
|
|
38
|
-
#
|
37
|
+
# Rest der Pipeline bleibt gleich
|
39
38
|
all_blocks_with_positions = self._collect_all_blocks_with_positions(
|
40
39
|
processed_markdown
|
41
40
|
)
|
42
|
-
|
43
|
-
# Sort all blocks by their position in the text
|
44
41
|
all_blocks_with_positions.sort(key=lambda x: x[0])
|
45
|
-
|
46
|
-
# Extract just the blocks without position information
|
47
42
|
blocks = [block for _, _, block in all_blocks_with_positions]
|
48
43
|
|
49
|
-
# Process spacing between blocks
|
50
44
|
return self._process_block_spacing(blocks)
|
51
45
|
|
52
|
-
def
|
53
|
-
"""
|
54
|
-
but ignore content inside code blocks and consecutive headings."""
|
46
|
+
def _add_spacers_with_rules(self, markdown_text: str) -> str:
|
47
|
+
"""Fügt Spacer mit expliziten Regeln hinzu"""
|
55
48
|
lines = markdown_text.split("\n")
|
56
49
|
processed_lines = []
|
57
|
-
found_first_heading = False
|
58
|
-
in_code_block = False
|
59
|
-
last_line_was_spacer = False
|
60
|
-
last_non_empty_was_heading = False
|
61
|
-
|
62
|
-
i = 0
|
63
|
-
while i < len(lines):
|
64
|
-
line = lines[i]
|
65
|
-
|
66
|
-
# Check for code block boundaries and handle accordingly
|
67
|
-
if self._is_code_block_marker(line):
|
68
|
-
in_code_block = not in_code_block
|
69
|
-
processed_lines.append(line)
|
70
|
-
if line.strip(): # If not empty
|
71
|
-
last_non_empty_was_heading = False
|
72
|
-
last_line_was_spacer = False
|
73
|
-
i += 1
|
74
|
-
continue
|
75
50
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
51
|
+
# Initialer State
|
52
|
+
state = {
|
53
|
+
"in_code_block": False,
|
54
|
+
"last_line_was_spacer": False,
|
55
|
+
"last_non_empty_was_heading": False,
|
56
|
+
"has_content_before": False,
|
57
|
+
"processed_lines": processed_lines,
|
58
|
+
}
|
84
59
|
|
85
|
-
|
86
|
-
|
87
|
-
line,
|
88
|
-
processed_lines,
|
89
|
-
last_line_was_spacer,
|
90
|
-
last_non_empty_was_heading,
|
60
|
+
for line_number, line in enumerate(lines):
|
61
|
+
result_lines, state = self._spacer_engine.process_line(
|
62
|
+
line, line_number, state
|
91
63
|
)
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
# Update tracking of consecutive headings and first heading
|
96
|
-
if line.strip(): # Not empty line
|
97
|
-
is_heading = re.match(self.HEADING_PATTERN, line) is not None
|
98
|
-
if is_heading:
|
99
|
-
if not found_first_heading:
|
100
|
-
found_first_heading = True
|
101
|
-
last_non_empty_was_heading = True
|
102
|
-
elif line.strip() != self.SPACER_MARKER: # Not a spacer or heading
|
103
|
-
last_non_empty_was_heading = False
|
104
|
-
|
105
|
-
i += 1
|
64
|
+
processed_lines.extend(result_lines)
|
65
|
+
state["processed_lines"] = processed_lines
|
106
66
|
|
107
67
|
return "\n".join(processed_lines)
|
108
68
|
|
109
|
-
def
|
110
|
-
"""
|
111
|
-
return line.strip().startswith("```")
|
112
|
-
|
113
|
-
def _process_line_for_spacers(
|
114
|
-
self,
|
115
|
-
line: str,
|
116
|
-
processed_lines: List[str],
|
117
|
-
last_line_was_spacer: bool,
|
118
|
-
last_non_empty_was_heading: bool,
|
119
|
-
) -> Dict[str, bool]:
|
120
|
-
"""
|
121
|
-
Process a single line to add spacers before headings and dividers if needed.
|
69
|
+
def add_custom_spacer_rule(self, rule: SpacerRule, priority: int = -1):
|
70
|
+
"""Fügt eine benutzerdefinierte Spacer-Regel hinzu
|
122
71
|
|
123
72
|
Args:
|
124
|
-
|
125
|
-
|
126
|
-
found_first_heading: Whether the first heading has been found
|
127
|
-
last_line_was_spacer: Whether the last added line was a spacer
|
128
|
-
last_non_empty_was_heading: Whether the last non-empty line was a heading
|
129
|
-
|
130
|
-
Returns:
|
131
|
-
Dictionary with processing results
|
73
|
+
rule: Die hinzuzufügende Regel
|
74
|
+
priority: Position in der Regelliste (-1 = am Ende)
|
132
75
|
"""
|
133
|
-
|
134
|
-
|
135
|
-
is_empty = not line_stripped
|
136
|
-
|
137
|
-
# Skip empty lines
|
138
|
-
if is_empty:
|
139
|
-
processed_lines.append(line)
|
140
|
-
return {"added_spacer": False}
|
141
|
-
|
142
|
-
# Check if line is a heading
|
143
|
-
if re.match(self.HEADING_PATTERN, line):
|
144
|
-
# Check if there's content before this heading (excluding spacers)
|
145
|
-
has_content_before = any(
|
146
|
-
processed_line.strip() and processed_line.strip() != self.SPACER_MARKER
|
147
|
-
for processed_line in processed_lines
|
148
|
-
)
|
149
|
-
|
150
|
-
if (
|
151
|
-
has_content_before
|
152
|
-
and not last_line_was_spacer
|
153
|
-
and not last_non_empty_was_heading
|
154
|
-
):
|
155
|
-
# Add spacer if:
|
156
|
-
# 1. There's content before this heading
|
157
|
-
# 2. Last line was not already a spacer
|
158
|
-
# 3. Last non-empty line was not a heading
|
159
|
-
processed_lines.append(self.SPACER_MARKER)
|
160
|
-
added_spacer = True
|
161
|
-
|
162
|
-
processed_lines.append(line)
|
163
|
-
|
164
|
-
# Check if line is a divider
|
165
|
-
elif re.match(self.DIVIDER_PATTERN, line):
|
166
|
-
if not last_line_was_spacer:
|
167
|
-
# Only add a single spacer line before dividers (no extra line breaks)
|
168
|
-
processed_lines.append(self.SPACER_MARKER)
|
169
|
-
added_spacer = True
|
170
|
-
|
171
|
-
processed_lines.append(line)
|
172
|
-
|
173
|
-
# Check if this line itself is a spacer
|
174
|
-
elif line_stripped == self.SPACER_MARKER:
|
175
|
-
# Never add consecutive spacers
|
176
|
-
if not last_line_was_spacer:
|
177
|
-
processed_lines.append(line)
|
178
|
-
added_spacer = True
|
179
|
-
|
76
|
+
if priority == -1:
|
77
|
+
self._spacer_engine.rules.append(rule)
|
180
78
|
else:
|
181
|
-
|
79
|
+
self._spacer_engine.rules.insert(priority, rule)
|
182
80
|
|
183
|
-
|
81
|
+
def get_spacer_rules_info(self) -> List[Dict[str, str]]:
|
82
|
+
"""Gibt Informationen über alle aktiven Spacer-Regeln zurück"""
|
83
|
+
return [
|
84
|
+
{"name": rule.name, "description": rule.description}
|
85
|
+
for rule in self._spacer_engine.rules
|
86
|
+
]
|
184
87
|
|
88
|
+
# Alle anderen Methoden bleiben unverändert...
|
185
89
|
def _collect_all_blocks_with_positions(
|
186
90
|
self, markdown_text: str
|
187
91
|
) -> List[Tuple[int, int, Dict[str, Any]]]:
|
@@ -416,7 +320,7 @@ class MarkdownToNotionConverter:
|
|
416
320
|
|
417
321
|
def _is_spacer_line(self, line: str) -> bool:
|
418
322
|
"""Check if a line is a spacer marker."""
|
419
|
-
return line.strip() == self.SPACER_MARKER
|
323
|
+
return line.strip() == self._spacer_engine.SPACER_MARKER
|
420
324
|
|
421
325
|
def _process_todo_line(
|
422
326
|
self,
|