mkdocs2confluence 0.7.1__tar.gz → 0.7.4__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.
- {mkdocs2confluence-0.7.1/src/mkdocs2confluence.egg-info → mkdocs2confluence-0.7.4}/PKG-INFO +1 -1
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/pyproject.toml +1 -1
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4/src/mkdocs2confluence.egg-info}/PKG-INFO +1 -1
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/emitter/xhtml.py +3 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/ir/__init__.py +2 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/ir/nodes.py +12 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/parser/markdown.py +13 -1
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/pdf/render.py +6 -3
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preprocess/icons.py +68 -1
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preview/render.py +2 -1
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_icons.py +46 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_parser.py +19 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_pdf.py +5 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_preview.py +11 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/LICENSE +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/README.md +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/setup.cfg +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs2confluence.egg-info/SOURCES.txt +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs2confluence.egg-info/dependency_links.txt +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs2confluence.egg-info/entry_points.txt +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs2confluence.egg-info/requires.txt +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs2confluence.egg-info/top_level.txt +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/__init__.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/cli.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/emitter/__init__.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/ir/document.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/ir/treeutil.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/loader/__init__.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/loader/config.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/loader/extra_css.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/loader/nav.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/loader/page.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/parser/__init__.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/pdf/__init__.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/pdf/generator.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preprocess/__init__.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preprocess/abbrevs.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preprocess/fence.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preprocess/frontmatter.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preprocess/includes.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preprocess/linkdefs.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preview/__init__.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preview/server.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/publisher/__init__.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/publisher/client.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/publisher/pipeline.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/transforms/__init__.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/transforms/abbrevs.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/transforms/assets.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/transforms/editlink.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/transforms/images.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/transforms/internallinks.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/transforms/mermaid.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_abbrevs.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_cli.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_editlink.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_emitter.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_extra_css.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_frontmatter.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_images.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_internallinks.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_ir.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_linkdefs.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_loader.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_mermaid.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_page_loader.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_preprocess.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_publish_client.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_publish_config.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_publish_pipeline.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_server.py +0 -0
- {mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/tests/test_treeutil.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mkdocs2confluence"
|
|
3
|
-
version = "0.7.
|
|
3
|
+
version = "0.7.4"
|
|
4
4
|
description = "Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "GPL-3.0-or-later" }
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/emitter/xhtml.py
RENAMED
|
@@ -48,6 +48,7 @@ from mkdocs_to_confluence.ir.nodes import (
|
|
|
48
48
|
OrderedList,
|
|
49
49
|
Paragraph,
|
|
50
50
|
RawHTML,
|
|
51
|
+
RawInlineHtml,
|
|
51
52
|
Section,
|
|
52
53
|
StrikethroughNode,
|
|
53
54
|
SubscriptNode,
|
|
@@ -613,6 +614,8 @@ def _emit_inline(node: IRNode) -> str:
|
|
|
613
614
|
if isinstance(node, InlineHtmlNode):
|
|
614
615
|
open_tag, close_tag = _INLINE_HTML_MAP.get(node.tag, ("<span>", "</span>"))
|
|
615
616
|
return f"{open_tag}{_emit_inlines(node.children)}{close_tag}"
|
|
617
|
+
if isinstance(node, RawInlineHtml):
|
|
618
|
+
return node.html_str
|
|
616
619
|
# Fallback: emit unknown inline nodes as escaped repr
|
|
617
620
|
return html.escape(repr(node))
|
|
618
621
|
|
|
@@ -44,6 +44,7 @@ from mkdocs_to_confluence.ir.nodes import (
|
|
|
44
44
|
OrderedList,
|
|
45
45
|
Paragraph,
|
|
46
46
|
RawHTML,
|
|
47
|
+
RawInlineHtml,
|
|
47
48
|
# Block nodes
|
|
48
49
|
Section,
|
|
49
50
|
StrikethroughNode,
|
|
@@ -81,6 +82,7 @@ __all__ = [
|
|
|
81
82
|
"ImageNode",
|
|
82
83
|
"LineBreakNode",
|
|
83
84
|
"InlineHtmlNode",
|
|
85
|
+
"RawInlineHtml",
|
|
84
86
|
"InsertNode",
|
|
85
87
|
# Block
|
|
86
88
|
"Section",
|
|
@@ -125,6 +125,18 @@ class InlineHtmlNode(IRNode):
|
|
|
125
125
|
children: tuple[IRNode, ...]
|
|
126
126
|
|
|
127
127
|
|
|
128
|
+
@dataclass(frozen=True)
|
|
129
|
+
class RawInlineHtml(IRNode):
|
|
130
|
+
"""A raw inline HTML element passed through verbatim.
|
|
131
|
+
|
|
132
|
+
Used for ``<span class="...">``, ``<abbr>``, and other inline HTML that has
|
|
133
|
+
no semantic IR equivalent. The emitter outputs ``html_str`` unchanged; PDF
|
|
134
|
+
and browser-preview renderers handle it natively.
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
html_str: str
|
|
138
|
+
|
|
139
|
+
|
|
128
140
|
@dataclass(frozen=True)
|
|
129
141
|
class ImageNode(IRNode):
|
|
130
142
|
"""An image reference.
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/parser/markdown.py
RENAMED
|
@@ -49,6 +49,7 @@ a proper library (e.g. ``markdown-it-py``) later without touching the IR.
|
|
|
49
49
|
from __future__ import annotations
|
|
50
50
|
|
|
51
51
|
import contextlib
|
|
52
|
+
import html
|
|
52
53
|
import re
|
|
53
54
|
from dataclasses import dataclass, field
|
|
54
55
|
from typing import Union
|
|
@@ -79,6 +80,7 @@ from mkdocs_to_confluence.ir.nodes import (
|
|
|
79
80
|
MermaidDiagram,
|
|
80
81
|
OrderedList,
|
|
81
82
|
Paragraph,
|
|
83
|
+
RawInlineHtml,
|
|
82
84
|
Section,
|
|
83
85
|
StrikethroughNode,
|
|
84
86
|
SubscriptNode,
|
|
@@ -784,7 +786,7 @@ def _scan_inline(text: str, fn_map: dict[str, int] | None = None) -> list[IRNode
|
|
|
784
786
|
def flush() -> None:
|
|
785
787
|
nonlocal buf
|
|
786
788
|
if buf:
|
|
787
|
-
nodes.append(TextNode(text=buf))
|
|
789
|
+
nodes.append(TextNode(text=html.unescape(buf)))
|
|
788
790
|
buf = ""
|
|
789
791
|
|
|
790
792
|
while i < n:
|
|
@@ -962,6 +964,16 @@ def _scan_inline(text: str, fn_map: dict[str, int] | None = None) -> list[IRNode
|
|
|
962
964
|
i = close_idx + len(close)
|
|
963
965
|
continue
|
|
964
966
|
|
|
967
|
+
# Generic inline HTML with attributes (e.g. <span class="...">) — pass through verbatim
|
|
968
|
+
generic_m = re.match(
|
|
969
|
+
r"<([a-z][a-z0-9]*)\b[^>]*>.*?</\1>", text[i:], re.DOTALL | re.IGNORECASE
|
|
970
|
+
)
|
|
971
|
+
if generic_m:
|
|
972
|
+
flush()
|
|
973
|
+
nodes.append(RawInlineHtml(html_str=generic_m.group(0)))
|
|
974
|
+
i += len(generic_m.group(0))
|
|
975
|
+
continue
|
|
976
|
+
|
|
965
977
|
# Bare URL autolink: https:// or http://
|
|
966
978
|
if text[i : i + 4] in ("http", "ftp:"):
|
|
967
979
|
url_m = re.match(r"(https?://|ftp://)[^\s<>\[\]\"']+", text[i:])
|
|
@@ -20,7 +20,7 @@ from mkdocs_to_confluence.preview.render import render_html
|
|
|
20
20
|
_PDF_CSS = """
|
|
21
21
|
@page {
|
|
22
22
|
size: A4 portrait;
|
|
23
|
-
margin:
|
|
23
|
+
margin: 18mm 15mm 18mm 20mm;
|
|
24
24
|
@bottom-center {
|
|
25
25
|
content: counter(page);
|
|
26
26
|
font-size: 9pt;
|
|
@@ -115,7 +115,10 @@ body {
|
|
|
115
115
|
/* ── Chapters ── */
|
|
116
116
|
article {
|
|
117
117
|
page-break-before: always;
|
|
118
|
-
|
|
118
|
+
}
|
|
119
|
+
.pdf-chapter-title {
|
|
120
|
+
display: none;
|
|
121
|
+
string-set: section-title content();
|
|
119
122
|
}
|
|
120
123
|
article h1 { font-size: 18pt; color: #0052cc; margin-top: 0; }
|
|
121
124
|
article h2 { font-size: 13pt; color: #0052cc; }
|
|
@@ -249,7 +252,7 @@ def build_pdf_html(
|
|
|
249
252
|
|
|
250
253
|
# Chapters
|
|
251
254
|
chapter_html = "".join(
|
|
252
|
-
'<article id="{anchor}"
|
|
255
|
+
'<article id="{anchor}">\n<span class="pdf-chapter-title">{title}</span>\n{body}\n</article>\n'.format(
|
|
253
256
|
anchor=_anchor(title),
|
|
254
257
|
title=_html.escape(title),
|
|
255
258
|
body=render_html(xhtml),
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preprocess/icons.py
RENAMED
|
@@ -24,6 +24,60 @@ _ICON_RE = re.compile(
|
|
|
24
24
|
r":(?:material|fontawesome|octicons|simple|twemoji)-[a-z0-9-]+:"
|
|
25
25
|
)
|
|
26
26
|
|
|
27
|
+
# Matches bare GitHub/Python-Markdown emoji shortcodes: :rotating_light:, :wrench: etc.
|
|
28
|
+
# Requires at least one underscore or is a known single-word name.
|
|
29
|
+
# Must not overlap with _ICON_RE (those all contain a hyphenated prefix).
|
|
30
|
+
_STANDARD_EMOJI_RE = re.compile(r":([a-z][a-z0-9_]*):")
|
|
31
|
+
|
|
32
|
+
# Bare emoji shortcode → BMP Unicode symbol (or "" to strip silently).
|
|
33
|
+
# BMP-only where a reasonable equivalent exists; supplementary-plane emoji
|
|
34
|
+
# are excluded to preserve MySQL utf8 Confluence compatibility.
|
|
35
|
+
_STANDARD_EMOJI_MAP: dict[str, str] = {
|
|
36
|
+
# Alerts / status
|
|
37
|
+
"warning": "⚠", # U+26A0
|
|
38
|
+
"rotating_light": "⚠", # U+26A0 (🚨 U+1F6A8 is supplementary)
|
|
39
|
+
"octagonal_sign": "⛔", # U+26D4
|
|
40
|
+
"no_entry": "⛔", # U+26D4
|
|
41
|
+
"no_entry_sign": "⛔",
|
|
42
|
+
"stop_sign": "⛔",
|
|
43
|
+
"information_source": "ℹ", # U+2139
|
|
44
|
+
# Checkmarks / marks
|
|
45
|
+
"white_check_mark": "✓", # U+2713 (✅ supplementary)
|
|
46
|
+
"heavy_check_mark": "✓", # U+2713
|
|
47
|
+
"x": "✗", # U+2717 (❌ supplementary)
|
|
48
|
+
"heavy_multiplication_x": "✗",
|
|
49
|
+
# Objects — tools
|
|
50
|
+
"wrench": "⚙", # U+2699 (🔧 supplementary)
|
|
51
|
+
"gear": "⚙", # U+2699
|
|
52
|
+
"hammer": "", # 🔨 supplementary, no BMP equiv — strip
|
|
53
|
+
"hammer_and_wrench": "",
|
|
54
|
+
# Work / business
|
|
55
|
+
"briefcase": "", # 💼 supplementary — strip
|
|
56
|
+
# Nature / miscellaneous
|
|
57
|
+
"star": "★", # U+2605
|
|
58
|
+
"star2": "★",
|
|
59
|
+
"rocket": "", # 🚀 supplementary — strip
|
|
60
|
+
"construction": "", # 🚧 supplementary — strip
|
|
61
|
+
"tada": "",
|
|
62
|
+
"trophy": "",
|
|
63
|
+
"thinking": "",
|
|
64
|
+
"smile": "",
|
|
65
|
+
"laughing": "",
|
|
66
|
+
"heart": "♥", # U+2665
|
|
67
|
+
"fire": "", # 🔥 supplementary — strip
|
|
68
|
+
"zap": "", # ⚡ U+26A1 BMP
|
|
69
|
+
"bulb": "",
|
|
70
|
+
"computer": "",
|
|
71
|
+
"notebook": "",
|
|
72
|
+
"memo": "",
|
|
73
|
+
"clipboard": "",
|
|
74
|
+
"link": "",
|
|
75
|
+
"label": "",
|
|
76
|
+
"bookmark": "",
|
|
77
|
+
"chart_with_upwards_trend": "",
|
|
78
|
+
"bar_chart": "",
|
|
79
|
+
}
|
|
80
|
+
|
|
27
81
|
# Keyword → symbol. Keys must be lowercase single words (icon name segments).
|
|
28
82
|
# ALL values must be BMP characters (U+0000–U+FFFF) or "" (strip silently).
|
|
29
83
|
# Ordered so earlier, more-specific entries take priority where ambiguous.
|
|
@@ -175,6 +229,19 @@ def _resolve(parts: list[str]) -> str:
|
|
|
175
229
|
return ""
|
|
176
230
|
|
|
177
231
|
|
|
232
|
+
def _replace_standard_emoji(text: str) -> str:
|
|
233
|
+
"""Replace standard emoji shortcodes (e.g. ``:rotating_light:``) with BMP symbols."""
|
|
234
|
+
|
|
235
|
+
def _replace(m: re.Match[str]) -> str:
|
|
236
|
+
name = m.group(1)
|
|
237
|
+
if name in _STANDARD_EMOJI_MAP:
|
|
238
|
+
return _STANDARD_EMOJI_MAP[name]
|
|
239
|
+
# Unknown shortcode — leave it intact (may be valid Markdown syntax).
|
|
240
|
+
return m.group(0)
|
|
241
|
+
|
|
242
|
+
return _STANDARD_EMOJI_RE.sub(_replace, text)
|
|
243
|
+
|
|
244
|
+
|
|
178
245
|
def strip_icon_shortcodes(text: str) -> str:
|
|
179
246
|
"""Replace icon shortcodes with emoji or strip them silently.
|
|
180
247
|
|
|
@@ -194,4 +261,4 @@ def strip_icon_shortcodes(text: str) -> str:
|
|
|
194
261
|
parts = name.split("-")
|
|
195
262
|
return _resolve(parts)
|
|
196
263
|
|
|
197
|
-
return _ICON_RE.sub(_replace, text)
|
|
264
|
+
return _ICON_RE.sub(_replace, _replace_standard_emoji(text))
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preview/render.py
RENAMED
|
@@ -72,7 +72,8 @@ _AC_ANCHOR_ATTR_RE = re.compile(r'ac:anchor="(?P<anchor>[^"]*)"')
|
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
def _params(macro_body: str) -> dict[str, str]:
|
|
75
|
-
|
|
75
|
+
# Values are XML text content (e.g. "&" for "&"); unescape to plain text.
|
|
76
|
+
return {m.group(1): _html.unescape(m.group(2)) for m in _PARAM_RE.finditer(macro_body)}
|
|
76
77
|
|
|
77
78
|
|
|
78
79
|
def _cdata(macro_body: str) -> str:
|
|
@@ -157,3 +157,49 @@ class TestInlineReplacement:
|
|
|
157
157
|
def test_no_icons_unchanged(self) -> None:
|
|
158
158
|
text = "No icons here, just plain text."
|
|
159
159
|
assert strip_icon_shortcodes(text) == text
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class TestStandardEmoji:
|
|
163
|
+
"""Tests for bare emoji shortcodes like :rotating_light:."""
|
|
164
|
+
|
|
165
|
+
def test_rotating_light_maps_to_warning(self) -> None:
|
|
166
|
+
assert strip_icon_shortcodes(":rotating_light:") == "⚠"
|
|
167
|
+
|
|
168
|
+
def test_octagonal_sign_maps_to_no_entry(self) -> None:
|
|
169
|
+
assert strip_icon_shortcodes(":octagonal_sign:") == "⛔"
|
|
170
|
+
|
|
171
|
+
def test_wrench_maps_to_gear(self) -> None:
|
|
172
|
+
assert strip_icon_shortcodes(":wrench:") == "⚙"
|
|
173
|
+
|
|
174
|
+
def test_information_source_maps_to_info(self) -> None:
|
|
175
|
+
assert strip_icon_shortcodes(":information_source:") == "ℹ"
|
|
176
|
+
|
|
177
|
+
def test_white_check_mark_maps_to_check(self) -> None:
|
|
178
|
+
assert strip_icon_shortcodes(":white_check_mark:") == "✓"
|
|
179
|
+
|
|
180
|
+
def test_x_maps_to_cross(self) -> None:
|
|
181
|
+
assert strip_icon_shortcodes(":x:") == "✗"
|
|
182
|
+
|
|
183
|
+
def test_briefcase_stripped(self) -> None:
|
|
184
|
+
assert strip_icon_shortcodes(":briefcase:") == ""
|
|
185
|
+
|
|
186
|
+
def test_unknown_shortcode_unchanged(self) -> None:
|
|
187
|
+
assert strip_icon_shortcodes(":unknown_emoji_xyz:") == ":unknown_emoji_xyz:"
|
|
188
|
+
|
|
189
|
+
def test_emoji_in_sentence(self) -> None:
|
|
190
|
+
result = strip_icon_shortcodes("Status: :white_check_mark: Done")
|
|
191
|
+
assert result == "Status: ✓ Done"
|
|
192
|
+
|
|
193
|
+
def test_material_icon_not_double_processed(self) -> None:
|
|
194
|
+
# :material-check-circle: should still go through the prefixed path
|
|
195
|
+
result = strip_icon_shortcodes(":material-check-circle:")
|
|
196
|
+
assert result == "✓"
|
|
197
|
+
|
|
198
|
+
def test_all_standard_emoji_values_are_bmp(self) -> None:
|
|
199
|
+
from mkdocs_to_confluence.preprocess.icons import _STANDARD_EMOJI_MAP
|
|
200
|
+
for name, symbol in _STANDARD_EMOJI_MAP.items():
|
|
201
|
+
for ch in symbol:
|
|
202
|
+
assert ord(ch) <= 0xFFFF, (
|
|
203
|
+
f"_STANDARD_EMOJI_MAP[{name!r}] contains supplementary-plane "
|
|
204
|
+
f"character U+{ord(ch):04X} — use BMP or empty string"
|
|
205
|
+
)
|
|
@@ -1181,6 +1181,25 @@ class TestInlineHtmlParsing:
|
|
|
1181
1181
|
raw = "".join(n.text for n in para.children if isinstance(n, TextNode))
|
|
1182
1182
|
assert "<mark>" in raw
|
|
1183
1183
|
|
|
1184
|
+
def test_span_with_class_produces_raw_inline_html(self) -> None:
|
|
1185
|
+
from mkdocs_to_confluence.ir import RawInlineHtml
|
|
1186
|
+
para = first(parse('Text <span class="crit-level">C0</span> end\n'), Paragraph)
|
|
1187
|
+
node = next(n for n in para.children if isinstance(n, RawInlineHtml))
|
|
1188
|
+
assert node.html_str == '<span class="crit-level">C0</span>'
|
|
1189
|
+
|
|
1190
|
+
def test_span_without_attrs_not_treated_as_raw(self) -> None:
|
|
1191
|
+
# Plain <span> (no attributes) is not in the known-tag list either,
|
|
1192
|
+
# so it falls through to generic handler.
|
|
1193
|
+
from mkdocs_to_confluence.ir import RawInlineHtml
|
|
1194
|
+
para = first(parse("<span>plain</span>\n"), Paragraph)
|
|
1195
|
+
assert any(isinstance(n, RawInlineHtml) for n in para.children)
|
|
1196
|
+
|
|
1197
|
+
def test_raw_inline_html_emitted_verbatim(self) -> None:
|
|
1198
|
+
from mkdocs_to_confluence.emitter.xhtml import emit
|
|
1199
|
+
nodes = parse('| Col |\n|-----|\n| <span class="hl">X</span> |\n')
|
|
1200
|
+
xhtml = emit(nodes)
|
|
1201
|
+
assert '<span class="hl">X</span>' in xhtml
|
|
1202
|
+
|
|
1184
1203
|
|
|
1185
1204
|
class TestKeyboardKeys:
|
|
1186
1205
|
def test_single_key(self) -> None:
|
|
@@ -166,6 +166,11 @@ class TestWritePdf:
|
|
|
166
166
|
class TestPdfCli:
|
|
167
167
|
"""Tests for the `pdf` subcommand in cli.py."""
|
|
168
168
|
|
|
169
|
+
@pytest.fixture(autouse=True)
|
|
170
|
+
def _skip_macos_reexec(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
171
|
+
"""Suppress the macOS DYLD re-exec so tests don't try to exec pytest."""
|
|
172
|
+
monkeypatch.setenv("_MK2CONF_DYLD_SET", "1")
|
|
173
|
+
|
|
169
174
|
def _run(self, argv: list[str], config_path: Path) -> None:
|
|
170
175
|
from mkdocs_to_confluence.cli import main
|
|
171
176
|
main(["--"] + argv if argv[0].startswith("-") else argv)
|
|
@@ -87,6 +87,17 @@ class TestRenderHtml:
|
|
|
87
87
|
assert "Show more" in out
|
|
88
88
|
assert "hidden content" in out
|
|
89
89
|
|
|
90
|
+
def test_expand_title_with_ampersand_not_double_escaped(self) -> None:
|
|
91
|
+
xhtml = (
|
|
92
|
+
'<ac:structured-macro ac:name="expand">'
|
|
93
|
+
'<ac:parameter ac:name="title">Architecture & Resilience</ac:parameter>'
|
|
94
|
+
"<ac:rich-text-body><p>content</p></ac:rich-text-body>"
|
|
95
|
+
"</ac:structured-macro>"
|
|
96
|
+
)
|
|
97
|
+
out = render_html(xhtml)
|
|
98
|
+
assert "Architecture & Resilience" in out
|
|
99
|
+
assert "&amp;" not in out
|
|
100
|
+
|
|
90
101
|
def test_nested_code_inside_warning(self) -> None:
|
|
91
102
|
xhtml = (
|
|
92
103
|
'<ac:structured-macro ac:name="warning">'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs2confluence.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs2confluence.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs2confluence.egg-info/requires.txt
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs2confluence.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/emitter/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/loader/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/loader/config.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/loader/extra_css.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/parser/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/pdf/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/pdf/generator.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preprocess/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preprocess/abbrevs.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preprocess/fence.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preprocess/includes.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preprocess/linkdefs.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preview/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/preview/server.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/publisher/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/publisher/client.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/publisher/pipeline.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/transforms/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/transforms/abbrevs.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/transforms/assets.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/transforms/editlink.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/transforms/images.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.7.1 → mkdocs2confluence-0.7.4}/src/mkdocs_to_confluence/transforms/mermaid.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
|
|
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
|