chatgpt-md-converter 0.3.10__tar.gz → 0.3.12__tar.gz
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.
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/PKG-INFO +1 -1
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/html_markdown/handlers.py +3 -2
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/telegram_markdown/code_blocks.py +1 -1
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/telegram_markdown/inline.py +1 -1
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/telegram_markdown/preprocess.py +4 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/telegram_markdown/renderer.py +1 -2
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter.egg-info/PKG-INFO +1 -1
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/setup.py +1 -1
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/tests/test_parser.py +95 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/tests/test_roundtrip_markdown.py +7 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/LICENSE +0 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/README.md +0 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/__init__.py +0 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/html_markdown/escaping.py +0 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/html_markdown/renderer.py +0 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/html_markdown/state.py +0 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/html_markdown/tree.py +0 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/html_splitter.py +0 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/html_to_markdown.py +0 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/telegram_formatter.py +0 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/telegram_markdown/__init__.py +0 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/telegram_markdown/postprocess.py +0 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter.egg-info/SOURCES.txt +0 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter.egg-info/dependency_links.txt +0 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter.egg-info/top_level.txt +0 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/setup.cfg +0 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/tests/test_html_to_markdown_inline_spacing.py +0 -0
- {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/tests/test_splitter.py +0 -0
|
@@ -169,11 +169,12 @@ def _handle_blockquote(node: Node, state: RenderState) -> str:
|
|
|
169
169
|
expandable = "expandable" in node.attrs
|
|
170
170
|
rendered: list[str] = []
|
|
171
171
|
for index, line in enumerate(lines):
|
|
172
|
-
prefix = "**>" if expandable and index == 0 else ">"
|
|
173
172
|
stripped = line.rstrip("\r")
|
|
174
173
|
if expandable:
|
|
175
|
-
|
|
174
|
+
marker = ">**" if index == 0 else ">"
|
|
175
|
+
rendered.append(f"{marker} {stripped}" if stripped else marker)
|
|
176
176
|
else:
|
|
177
|
+
prefix = ">"
|
|
177
178
|
rendered.append(f"{prefix} {stripped}" if stripped else prefix)
|
|
178
179
|
return "\n".join(rendered)
|
|
179
180
|
|
|
@@ -67,7 +67,7 @@ def extract_and_convert_code_blocks(text: str):
|
|
|
67
67
|
.replace("<", "<")
|
|
68
68
|
.replace(">", ">")
|
|
69
69
|
)
|
|
70
|
-
placeholder = f"
|
|
70
|
+
placeholder = f"CODEBLOCKPLACEHOLDER_{len(placeholders)}_"
|
|
71
71
|
placeholders.append(placeholder)
|
|
72
72
|
if language:
|
|
73
73
|
html_block = f'<pre><code class="language-{language}">{escaped}</code></pre>'
|
|
@@ -60,7 +60,7 @@ def extract_inline_code_snippets(text: str):
|
|
|
60
60
|
|
|
61
61
|
def replacer(match: re.Match[str]) -> str:
|
|
62
62
|
snippet = match.group(1)
|
|
63
|
-
placeholder = f"
|
|
63
|
+
placeholder = f"INLINECODEPLACEHOLDER_{len(placeholders)}_"
|
|
64
64
|
placeholders.append(placeholder)
|
|
65
65
|
snippets[placeholder] = snippet
|
|
66
66
|
return placeholder
|
|
@@ -14,6 +14,10 @@ def combine_blockquotes(text: str) -> str:
|
|
|
14
14
|
in_blockquote = True
|
|
15
15
|
is_expandable = True
|
|
16
16
|
blockquote_lines.append(line[3:].strip())
|
|
17
|
+
elif line.startswith(">**") and (len(line) == 3 or line[3].isspace()):
|
|
18
|
+
in_blockquote = True
|
|
19
|
+
is_expandable = True
|
|
20
|
+
blockquote_lines.append(line[3:].strip())
|
|
17
21
|
elif line.startswith(">"):
|
|
18
22
|
if not in_blockquote:
|
|
19
23
|
in_blockquote = True
|
|
@@ -12,9 +12,8 @@ from .preprocess import combine_blockquotes
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def telegram_format(text: str) -> str:
|
|
15
|
-
text = combine_blockquotes(text)
|
|
16
|
-
|
|
17
15
|
output, block_map = extract_and_convert_code_blocks(text)
|
|
16
|
+
output = combine_blockquotes(output)
|
|
18
17
|
output, inline_snippets = extract_inline_code_snippets(output)
|
|
19
18
|
|
|
20
19
|
output = convert_html_chars(output)
|
|
@@ -716,6 +716,41 @@ Continued</blockquote>"""
|
|
|
716
716
|
assert output == expected_output, "Failed handling spoiler inside blockquote"
|
|
717
717
|
|
|
718
718
|
|
|
719
|
+
def test_blockquote_lines_inside_code_block():
|
|
720
|
+
input_text = """```text
|
|
721
|
+
>** заголовок довгої цитати
|
|
722
|
+
> рядок 2
|
|
723
|
+
> рядок 3
|
|
724
|
+
> рядок 4
|
|
725
|
+
> і ще хоч сто рядків
|
|
726
|
+
```"""
|
|
727
|
+
expected_output = (
|
|
728
|
+
'<pre><code class="language-text">>** заголовок довгої цитати\n'
|
|
729
|
+
'> рядок 2\n'
|
|
730
|
+
'> рядок 3\n'
|
|
731
|
+
'> рядок 4\n'
|
|
732
|
+
'> і ще хоч сто рядків\n'
|
|
733
|
+
"</code></pre>"
|
|
734
|
+
)
|
|
735
|
+
output = telegram_format(input_text)
|
|
736
|
+
assert output == expected_output, f"Got: {output}"
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
def test_blockquote_double_asterisk_prefix():
|
|
740
|
+
input_text = """>** заголовок довгої цитати
|
|
741
|
+
> рядок 2
|
|
742
|
+
> рядок 3
|
|
743
|
+
> рядок 4
|
|
744
|
+
> і ще хоч сто рядків"""
|
|
745
|
+
expected_output = """<blockquote expandable>заголовок довгої цитати
|
|
746
|
+
рядок 2
|
|
747
|
+
рядок 3
|
|
748
|
+
рядок 4
|
|
749
|
+
і ще хоч сто рядків</blockquote>"""
|
|
750
|
+
output = telegram_format(input_text)
|
|
751
|
+
assert output == expected_output, f"Got: {output}"
|
|
752
|
+
|
|
753
|
+
|
|
719
754
|
def test_multiple_spoilers():
|
|
720
755
|
input_text = "First ||spoiler|| and then another ||spoiler with *italic*||"
|
|
721
756
|
expected_output = 'First <span class="tg-spoiler">spoiler</span> and then another <span class="tg-spoiler">spoiler with <i>italic</i></span>'
|
|
@@ -800,6 +835,66 @@ print("hello world ```")
|
|
|
800
835
|
assert output == expected_output, show_output()
|
|
801
836
|
|
|
802
837
|
|
|
838
|
+
def test_inline_code_placeholders_do_not_overlap():
|
|
839
|
+
input_text = """Службова нотатка для тесту.
|
|
840
|
+
|
|
841
|
+
Коли ви запускаєте `alpha.run()`, система піднімає локальний клієнт.
|
|
842
|
+
|
|
843
|
+
У модулі використовується `hook.set()` для реєстрації синхронізації.
|
|
844
|
+
|
|
845
|
+
```python
|
|
846
|
+
from framework import hook
|
|
847
|
+
|
|
848
|
+
async def configure(base_url: str):
|
|
849
|
+
await hook.set(f"{base_url}/sync")
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
**Покроковий план**
|
|
853
|
+
|
|
854
|
+
1. Викликаємо `hook.set()` через менеджер потоків.
|
|
855
|
+
2. `hook.set()` повертає попередження при повторній реєстрації.
|
|
856
|
+
3. Якщо потрібно, `hook.clear()` знімає прив'язку.
|
|
857
|
+
4. Використовуємо `core.loop()` для довготривалих з'єднань.
|
|
858
|
+
5. `hook.set()` запускає фонову синхронізацію.
|
|
859
|
+
|
|
860
|
+
Поточне середовище потребує **TLS**. Для локального доступу підходить `debug.tunnel`.
|
|
861
|
+
|
|
862
|
+
Чи є питання щодо `hook.set()` чи `hook.clear()`?"""
|
|
863
|
+
|
|
864
|
+
expected_output = """Службова нотатка для тесту.
|
|
865
|
+
|
|
866
|
+
Коли ви запускаєте <code>alpha.run()</code>, система піднімає локальний клієнт.
|
|
867
|
+
|
|
868
|
+
У модулі використовується <code>hook.set()</code> для реєстрації синхронізації.
|
|
869
|
+
|
|
870
|
+
<pre><code class="language-python">from framework import hook
|
|
871
|
+
|
|
872
|
+
async def configure(base_url: str):
|
|
873
|
+
await hook.set(f"{base_url}/sync")
|
|
874
|
+
</code></pre>
|
|
875
|
+
|
|
876
|
+
<b>Покроковий план</b>
|
|
877
|
+
|
|
878
|
+
1. Викликаємо <code>hook.set()</code> через менеджер потоків.
|
|
879
|
+
2. <code>hook.set()</code> повертає попередження при повторній реєстрації.
|
|
880
|
+
3. Якщо потрібно, <code>hook.clear()</code> знімає прив'язку.
|
|
881
|
+
4. Використовуємо <code>core.loop()</code> для довготривалих з'єднань.
|
|
882
|
+
5. <code>hook.set()</code> запускає фонову синхронізацію.
|
|
883
|
+
|
|
884
|
+
Поточне середовище потребує <b>TLS</b>. Для локального доступу підходить <code>debug.tunnel</code>.
|
|
885
|
+
|
|
886
|
+
Чи є питання щодо <code>hook.set()</code> чи <code>hook.clear()</code>?"""
|
|
887
|
+
|
|
888
|
+
output = telegram_format(input_text)
|
|
889
|
+
|
|
890
|
+
assert output == expected_output
|
|
891
|
+
assert "<code>hook.set()</code>0" not in output
|
|
892
|
+
assert "<code>hook.set()</code>1" not in output
|
|
893
|
+
assert "<code>hook.set()</code>2" not in output
|
|
894
|
+
assert "<code>hook.set()</code>3" not in output
|
|
895
|
+
assert "<code>hook.set()</code>4" not in output
|
|
896
|
+
|
|
897
|
+
|
|
803
898
|
def test_nested_code_fence_six_backticks():
|
|
804
899
|
input_text = """``````markdown
|
|
805
900
|
`````python
|
{chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/tests/test_roundtrip_markdown.py
RENAMED
|
@@ -30,3 +30,10 @@ def test_markdown_html_markdown_cycle_is_idempotent(_case, markdown_input, _):
|
|
|
30
30
|
assert '<br' not in html_first
|
|
31
31
|
assert '<br' not in html_third
|
|
32
32
|
assert html_first == html_third
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_html_to_markdown_expandable_blockquote():
|
|
36
|
+
html_text = "<blockquote expandable>заголовок\nрядок 2\nрядок 3</blockquote>"
|
|
37
|
+
expected_markdown = ">** заголовок\n> рядок 2\n> рядок 3"
|
|
38
|
+
markdown = html_to_telegram_markdown(html_text)
|
|
39
|
+
assert markdown == expected_markdown
|
|
File without changes
|
|
File without changes
|
{chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/html_splitter.py
RENAMED
|
File without changes
|
{chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/html_to_markdown.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|