mkdocs2confluence 0.7.8__tar.gz → 0.7.9__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.
Files changed (72) hide show
  1. {mkdocs2confluence-0.7.8/src/mkdocs2confluence.egg-info → mkdocs2confluence-0.7.9}/PKG-INFO +1 -1
  2. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/pyproject.toml +1 -1
  3. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9/src/mkdocs2confluence.egg-info}/PKG-INFO +1 -1
  4. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/preprocess/icons.py +102 -107
  5. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_icons.py +43 -46
  6. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/LICENSE +0 -0
  7. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/README.md +0 -0
  8. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/setup.cfg +0 -0
  9. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs2confluence.egg-info/SOURCES.txt +0 -0
  10. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs2confluence.egg-info/dependency_links.txt +0 -0
  11. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs2confluence.egg-info/entry_points.txt +0 -0
  12. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs2confluence.egg-info/requires.txt +0 -0
  13. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs2confluence.egg-info/top_level.txt +0 -0
  14. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/__init__.py +0 -0
  15. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/cli.py +0 -0
  16. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/emitter/__init__.py +0 -0
  17. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/emitter/xhtml.py +0 -0
  18. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/ir/__init__.py +0 -0
  19. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/ir/document.py +0 -0
  20. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/ir/nodes.py +0 -0
  21. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/ir/treeutil.py +0 -0
  22. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/loader/__init__.py +0 -0
  23. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/loader/config.py +0 -0
  24. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/loader/extra_css.py +0 -0
  25. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/loader/nav.py +0 -0
  26. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/loader/page.py +0 -0
  27. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/parser/__init__.py +0 -0
  28. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/parser/markdown.py +0 -0
  29. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/pdf/__init__.py +0 -0
  30. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/pdf/generator.py +0 -0
  31. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/pdf/render.py +0 -0
  32. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/preprocess/__init__.py +0 -0
  33. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/preprocess/abbrevs.py +0 -0
  34. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/preprocess/fence.py +0 -0
  35. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/preprocess/frontmatter.py +0 -0
  36. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/preprocess/includes.py +0 -0
  37. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/preprocess/linkdefs.py +0 -0
  38. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/preview/__init__.py +0 -0
  39. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/preview/render.py +0 -0
  40. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/preview/server.py +0 -0
  41. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/publisher/__init__.py +0 -0
  42. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/publisher/client.py +0 -0
  43. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/publisher/pipeline.py +0 -0
  44. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/transforms/__init__.py +0 -0
  45. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/transforms/abbrevs.py +0 -0
  46. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/transforms/assets.py +0 -0
  47. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/transforms/editlink.py +0 -0
  48. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/transforms/images.py +0 -0
  49. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/transforms/internallinks.py +0 -0
  50. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/src/mkdocs_to_confluence/transforms/mermaid.py +0 -0
  51. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_abbrevs.py +0 -0
  52. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_cli.py +0 -0
  53. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_editlink.py +0 -0
  54. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_emitter.py +0 -0
  55. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_extra_css.py +0 -0
  56. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_frontmatter.py +0 -0
  57. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_images.py +0 -0
  58. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_internallinks.py +0 -0
  59. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_ir.py +0 -0
  60. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_linkdefs.py +0 -0
  61. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_loader.py +0 -0
  62. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_mermaid.py +0 -0
  63. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_page_loader.py +0 -0
  64. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_parser.py +0 -0
  65. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_pdf.py +0 -0
  66. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_preprocess.py +0 -0
  67. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_preview.py +0 -0
  68. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_publish_client.py +0 -0
  69. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_publish_config.py +0 -0
  70. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_publish_pipeline.py +0 -0
  71. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_server.py +0 -0
  72. {mkdocs2confluence-0.7.8 → mkdocs2confluence-0.7.9}/tests/test_treeutil.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs2confluence
3
- Version: 0.7.8
3
+ Version: 0.7.9
4
4
  Summary: Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more
5
5
  Author: Anders Hybertz
6
6
  License: GPL-3.0-or-later
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mkdocs2confluence"
3
- version = "0.7.8"
3
+ version = "0.7.9"
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" }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs2confluence
3
- Version: 0.7.8
3
+ Version: 0.7.9
4
4
  Summary: Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more
5
5
  Author: Anders Hybertz
6
6
  License: GPL-3.0-or-later
@@ -1,18 +1,16 @@
1
1
  """Icon shortcode preprocessing.
2
2
 
3
3
  Replaces MkDocs Material / FontAwesome / Octicons / Simple icon shortcodes
4
- (e.g. ``:material-check-circle:``) with the closest Unicode symbol equivalent,
4
+ (e.g. ``:material-check-circle:``) with the closest Unicode emoji equivalent,
5
5
  or strips them silently when no mapping is found.
6
6
 
7
7
  Strategy: split the icon name on ``-`` and test each part against a small
8
8
  keyword→symbol table. The first matching keyword wins. This keeps the
9
9
  mapping set tiny and maintainable without requiring a full icon inventory.
10
10
 
11
- **BMP-only constraint**: all mapped symbols must lie in the Unicode Basic
12
- Multilingual Plane (U+0000–U+FFFF, 3-byte UTF-8). Supplementary-plane
13
- emoji (U+10000+, 4-byte UTF-8) are not stored correctly by Confluence
14
- deployments that use MySQL ``utf8`` (not ``utf8mb4``) and render as ``???``.
15
- Where no good BMP symbol exists the shortcode is stripped silently (``""``).
11
+ **Unicode range**: Confluence Cloud stores pages as UTF-8 with full Unicode
12
+ support (utf8mb4), so supplementary-plane emoji (U+10000+) are safe.
13
+ Symbols without a clear emoji equivalent are still stripped silently (``""``).
16
14
  """
17
15
 
18
16
  from __future__ import annotations
@@ -29,60 +27,57 @@ _ICON_RE = re.compile(
29
27
  # Must not overlap with _ICON_RE (those all contain a hyphenated prefix).
30
28
  _STANDARD_EMOJI_RE = re.compile(r":([a-z][a-z0-9_]*):")
31
29
 
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.
30
+ # Bare emoji shortcode → Unicode symbol (or "" to strip silently).
35
31
  _STANDARD_EMOJI_MAP: dict[str, str] = {
36
32
  # Alerts / status
37
33
  "warning": "⚠", # U+26A0
38
- "rotating_light": "", # U+26A0 (🚨 U+1F6A8 is supplementary)
34
+ "rotating_light": "🚨", # U+1F6A8
39
35
  "octagonal_sign": "⛔", # U+26D4
40
36
  "no_entry": "⛔", # U+26D4
41
37
  "no_entry_sign": "⛔",
42
38
  "stop_sign": "⛔",
43
39
  "information_source": "ℹ", # U+2139
44
40
  # Checkmarks / marks
45
- "white_check_mark": "✓", # U+2713 (✅ supplementary)
41
+ "white_check_mark": "✓", # U+2713
46
42
  "heavy_check_mark": "✓", # U+2713
47
- "x": "✗", # U+2717 (❌ supplementary)
43
+ "x": "✗", # U+2717
48
44
  "heavy_multiplication_x": "✗",
49
45
  # Objects — tools
50
- "wrench": "", # U+2699 (🔧 supplementary)
46
+ "wrench": "🔧", # U+1F527
51
47
  "gear": "⚙", # U+2699
52
- "hammer": "", # 🔨 supplementary, no BMP equiv — strip
53
- "hammer_and_wrench": "",
48
+ "hammer": "🔨", # U+1F528
49
+ "hammer_and_wrench": "🛠️", # U+1F6E0
54
50
  # Work / business
55
- "briefcase": "", # 💼 supplementary — strip
51
+ "briefcase": "💼", # U+1F4BC
56
52
  # Nature / miscellaneous
57
53
  "star": "★", # U+2605
58
54
  "star2": "★",
59
- "rocket": "", # 🚀 supplementary — strip
60
- "construction": "", # 🚧 supplementary — strip
61
- "tada": "",
62
- "trophy": "",
63
- "thinking": "",
64
- "smile": "",
65
- "laughing": "",
55
+ "rocket": "🚀", # U+1F680
56
+ "construction": "🚧", # U+1F6A7
57
+ "tada": "🎉", # U+1F389
58
+ "trophy": "🏆", # U+1F3C6
59
+ "thinking": "🤔", # U+1F914
60
+ "smile": "😄", # U+1F604
61
+ "laughing": "😆", # U+1F606
66
62
  "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": "",
63
+ "fire": "🔥", # U+1F525
64
+ "zap": "", # U+26A1
65
+ "bulb": "💡", # U+1F4A1
66
+ "computer": "💻", # U+1F4BB
67
+ "notebook": "📓", # U+1F4D3
68
+ "memo": "📝", # U+1F4DD
69
+ "clipboard": "📋", # U+1F4CB
70
+ "link": "🔗", # U+1F517
71
+ "label": "🏷️", # U+1F3F7
72
+ "bookmark": "🔖", # U+1F516
73
+ "chart_with_upwards_trend": "📈", # U+1F4C8
74
+ "bar_chart": "📊", # U+1F4CA
79
75
  }
80
76
 
81
77
  # Keyword → symbol. Keys must be lowercase single words (icon name segments).
82
- # ALL values must be BMP characters (U+0000–U+FFFF) or "" (strip silently).
83
78
  # Ordered so earlier, more-specific entries take priority where ambiguous.
84
79
  _KEYWORD_MAP: dict[str, str | None] = {
85
- # Status / validation (all BMP ✓)
80
+ # Status / validation
86
81
  "check": "✓", # U+2713
87
82
  "done": "✓",
88
83
  "complete": "✓",
@@ -103,98 +98,98 @@ _KEYWORD_MAP: dict[str, str | None] = {
103
98
  # Navigation / directional
104
99
  "arrow": None, # resolved with next segment — see _resolve()
105
100
  "chevron": None,
106
- # Security — no good BMP glyph; strip silently
107
- "lock": "",
108
- "security": "",
109
- "shield": "",
110
- "unlock": "",
111
- "key": "",
101
+ # Security
102
+ "lock": "🔒", # U+1F512
103
+ "security": "🛡️", # U+1F6E0
104
+ "shield": "🛡️",
105
+ "unlock": "🔓", # U+1F513
106
+ "key": "🔑", # U+1F511
112
107
  # Actions
113
108
  "download": "↓", # U+2193
114
109
  "upload": "↑", # U+2191
115
- "refresh": "↻", # U+21BB (BMP)
110
+ "refresh": "↻", # U+21BB
116
111
  "sync": "↻",
117
112
  "reload": "↻",
118
- "search": "", # no reliable BMP magnifier glyph; strip
119
- "magnify": "",
120
- "edit": "✎", # U+270E (BMP pencil)
113
+ "search": "🔍", # U+1F50D
114
+ "magnify": "🔍",
115
+ "edit": "✎", # U+270E
121
116
  "pencil": "✎",
122
117
  "pen": "✎",
123
118
  "copy": "", # strip
124
- "clipboard": "",
125
- "trash": "", # strip
126
- "delete": "",
119
+ "clipboard": "📋", # U+1F4CB
120
+ "trash": "🗑️", # U+1F5D1
121
+ "delete": "🗑️",
127
122
  "add": "+",
128
123
  "plus": "+",
129
- "minus": "−", # U+2212 minus sign
130
- "link": "", # strip — no reliable BMP link-chain glyph
131
- "chain": "",
124
+ "minus": "−", # U+2212
125
+ "link": "🔗", # U+1F517
126
+ "chain": "🔗",
132
127
  # Objects / content
133
- "star": "★", # U+2605 (BMP)
128
+ "star": "★", # U+2605
134
129
  "favorite": "★",
135
- "bookmark": "", # strip
136
- "heart": "♥", # U+2665 (BMP)
137
- "fire": "", # strip
138
- "rocket": "", # strip
139
- "launch": "",
140
- "home": "", # strip — ⌂ U+2302 exists but renders poorly
141
- "settings": "⚙", # U+2699 (BMP)
130
+ "bookmark": "🔖", # U+1F516
131
+ "heart": "♥", # U+2665
132
+ "fire": "🔥", # U+1F525
133
+ "rocket": "🚀", # U+1F680
134
+ "launch": "🚀",
135
+ "home": "🏠", # U+1F3E0
136
+ "settings": "⚙", # U+2699
142
137
  "cog": "⚙",
143
138
  "gear": "⚙",
144
- "wrench": "", # strip
145
- "email": "✉", # U+2709 (BMP envelope)
139
+ "wrench": "🔧", # U+1F527
140
+ "email": "✉", # U+2709
146
141
  "mail": "✉",
147
142
  "envelope": "✉",
148
- "phone": "☎", # U+260E (BMP telephone)
149
- "clock": "", # strip
150
- "time": "",
151
- "calendar": "", # strip
152
- "date": "",
153
- "folder": "", # strip
154
- "file": "",
155
- "document": "",
156
- "code": "", # strip
157
- "terminal": "",
158
- "database": "", # strip
159
- "cloud": "☁", # U+2601 (BMP)
160
- "globe": "", # strip
161
- "world": "",
162
- "earth": "",
163
- "chart": "", # strip
164
- "graph": "",
165
- "book": "", # strip
166
- "docs": "",
167
- "note": "", # strip
168
- "tag": "", # strip
169
- "label": "",
170
- "flag": "", # strip
143
+ "phone": "☎", # U+260E
144
+ "clock": "", # U+23F0
145
+ "time": "",
146
+ "calendar": "📅", # U+1F4C5
147
+ "date": "📅",
148
+ "folder": "📁", # U+1F4C1
149
+ "file": "📄", # U+1F4C4
150
+ "document": "📄",
151
+ "code": "💻", # U+1F4BB
152
+ "terminal": "💻",
153
+ "database": "🗄️", # U+1F5C4
154
+ "cloud": "☁", # U+2601
155
+ "globe": "🌍", # U+1F30D
156
+ "world": "🌍",
157
+ "earth": "🌍",
158
+ "chart": "📊", # U+1F4CA
159
+ "graph": "📊",
160
+ "book": "📖", # U+1F4D6
161
+ "docs": "📖",
162
+ "note": "", # strip — ambiguous; not the 📝 emoji
163
+ "tag": "🏷️", # U+1F3F7
164
+ "label": "🏷️",
165
+ "flag": "🚩", # U+1F6A9
171
166
  "eye": "", # strip — was wrongly matching grid/view icons
172
- "view": "", # strip — semantically ambiguous (grid-view ≠ eye)
173
- "grid": "", # strip — layout/grid icons have no BMP analogue
174
- "user": "", # strip
175
- "account": "",
176
- "person": "",
177
- "group": "", # strip
178
- "people": "",
179
- "team": "",
180
- "robot": "", # strip
181
- "bug": "", # strip
167
+ "view": "", # strip — semantically ambiguous
168
+ "grid": "", # strip — layout/grid icons have no emoji analogue
169
+ "user": "👤", # U+1F464
170
+ "account": "👤",
171
+ "person": "👤",
172
+ "group": "👥", # U+1F465
173
+ "people": "👥",
174
+ "team": "👥",
175
+ "robot": "🤖", # U+1F916
176
+ "bug": "🐛", # U+1F41B
182
177
  "test": "", # strip
183
- "flask": "",
184
- "lightbulb": "", # strip
185
- "idea": "",
186
- "package": "", # strip
187
- "server": "",
178
+ "flask": "", # strip
179
+ "lightbulb": "💡", # U+1F4A1
180
+ "idea": "💡",
181
+ "package": "📦", # U+1F4E6
182
+ "server": "🖥️", # U+1F5A5
188
183
  "network": "", # strip
189
- "wifi": "",
184
+ "wifi": "", # strip
190
185
  "battery": "", # strip
191
- "image": "", # strip
192
- "video": "",
193
- "music": "♪", # U+266A (BMP)
186
+ "image": "🖼️", # U+1F5BC
187
+ "video": "🎥", # U+1F3A5
188
+ "music": "♪", # U+266A
194
189
  "printer": "", # strip
195
- "keyboard": "⌨", # U+2328 (BMP)
190
+ "keyboard": "⌨", # U+2328
196
191
  "mouse": "", # strip
197
- "monitor": "", # strip
192
+ "monitor": "🖥️",
198
193
  # Modifier suffixes — never meaningful on their own; always strip
199
194
  "outline": "",
200
195
  "variant": "",
@@ -1,8 +1,8 @@
1
1
  """Tests for preprocess.icons — icon shortcode stripping/mapping.
2
2
 
3
- All mapped symbols must be BMP characters (U+0000–U+FFFF, ≤ 3-byte UTF-8).
4
- Supplementary-plane emoji (U+10000+) render as ``???`` in Confluence
5
- deployments that use MySQL ``utf8`` rather than ``utf8mb4``.
3
+ Confluence Cloud uses utf8mb4 and supports full Unicode including
4
+ supplementary-plane emoji (U+10000+). Shortcodes with no clear emoji
5
+ equivalent are stripped silently.
6
6
  """
7
7
 
8
8
  from __future__ import annotations
@@ -29,23 +29,20 @@ class TestKnownMappings:
29
29
  def test_close_maps_to_cross(self) -> None:
30
30
  assert strip_icon_shortcodes(":material-close:") == "✗"
31
31
 
32
- def test_lock_stripped(self) -> None:
33
- # 🔒 is non-BMP (U+1F512); strip silently instead
34
- assert strip_icon_shortcodes(":material-lock:") == ""
32
+ def test_lock_maps_to_emoji(self) -> None:
33
+ assert strip_icon_shortcodes(":material-lock:") == "🔒"
35
34
 
36
- def test_shield_stripped(self) -> None:
37
- assert strip_icon_shortcodes(":material-shield:") == ""
35
+ def test_shield_maps_to_emoji(self) -> None:
36
+ assert strip_icon_shortcodes(":material-shield:") == "🛡️"
38
37
 
39
- def test_rocket_stripped(self) -> None:
40
- # 🚀 is non-BMP (U+1F680); strip silently instead
41
- assert strip_icon_shortcodes(":material-rocket-launch:") == ""
38
+ def test_rocket_maps_to_emoji(self) -> None:
39
+ assert strip_icon_shortcodes(":material-rocket-launch:") == "🚀"
42
40
 
43
41
  def test_settings_maps_to_gear(self) -> None:
44
42
  assert strip_icon_shortcodes(":material-cog:") == "⚙"
45
43
 
46
- def test_search_stripped(self) -> None:
47
- # 🔍 is non-BMP (U+1F50D); strip silently instead
48
- assert strip_icon_shortcodes(":material-magnify:") == ""
44
+ def test_search_maps_to_emoji(self) -> None:
45
+ assert strip_icon_shortcodes(":material-magnify:") == "🔍"
49
46
 
50
47
  def test_refresh_maps_to_arrow(self) -> None:
51
48
  assert strip_icon_shortcodes(":material-refresh:") == "↻"
@@ -84,18 +81,17 @@ class TestArrowMappings:
84
81
 
85
82
 
86
83
  class TestBuggedIcons:
87
- """Regression tests for icons that previously rendered as ??? in Confluence."""
84
+ """Regression tests for icons that should not produce wrong output."""
88
85
 
89
- def test_link_variant_stripped(self) -> None:
90
- # Was: 🔗 (U+1F517, non-BMP) ?? in Confluence
91
- assert strip_icon_shortcodes(":material-link-variant:") == ""
86
+ def test_link_variant_maps_to_emoji(self) -> None:
87
+ assert strip_icon_shortcodes(":material-link-variant:") == "🔗"
92
88
 
93
89
  def test_grid_view_outline_stripped(self) -> None:
94
- # Was: 👁️ (wrong semantic + non-BMP) → ??? in Confluence
90
+ # grid/view have no meaningful emoji analogue
95
91
  assert strip_icon_shortcodes(":material-grid-view-outline:") == ""
96
92
 
97
- def test_link_stripped(self) -> None:
98
- assert strip_icon_shortcodes(":material-link:") == ""
93
+ def test_link_maps_to_emoji(self) -> None:
94
+ assert strip_icon_shortcodes(":material-link:") == "🔗"
99
95
 
100
96
  def test_view_stripped(self) -> None:
101
97
  assert strip_icon_shortcodes(":material-view-dashboard:") == ""
@@ -124,21 +120,26 @@ class TestUnknownIcons:
124
120
  assert strip_icon_shortcodes(":fontawesome-brands-github:") == ""
125
121
 
126
122
 
127
- class TestBmpSafety:
128
- """All mapped values must be BMP-only ( U+FFFF, max 3-byte UTF-8)."""
123
+ class TestEmojiMappings:
124
+ """Supplementary-plane emoji now map to real emoji (Cloud utf8mb4 support)."""
129
125
 
130
- def test_all_mapped_values_are_bmp(self) -> None:
131
- from mkdocs_to_confluence.preprocess.icons import _KEYWORD_MAP
126
+ def test_fire_maps_to_emoji(self) -> None:
127
+ assert strip_icon_shortcodes(":material-fire:") == "🔥"
132
128
 
133
- non_bmp = {
134
- kw: val
135
- for kw, val in _KEYWORD_MAP.items()
136
- if val is not None and any(ord(c) > 0xFFFF for c in val)
137
- }
138
- assert non_bmp == {}, (
139
- f"Non-BMP characters found in _KEYWORD_MAP (will render as ??? "
140
- f"in Confluence MySQL utf8): {non_bmp}"
141
- )
129
+ def test_lightbulb_maps_to_emoji(self) -> None:
130
+ assert strip_icon_shortcodes(":material-lightbulb:") == "💡"
131
+
132
+ def test_bug_maps_to_emoji(self) -> None:
133
+ assert strip_icon_shortcodes(":material-bug:") == "🐛"
134
+
135
+ def test_package_maps_to_emoji(self) -> None:
136
+ assert strip_icon_shortcodes(":material-package:") == "📦"
137
+
138
+ def test_account_maps_to_emoji(self) -> None:
139
+ assert strip_icon_shortcodes(":material-account:") == "👤"
140
+
141
+ def test_robot_maps_to_emoji(self) -> None:
142
+ assert strip_icon_shortcodes(":material-robot:") == "🤖"
142
143
 
143
144
 
144
145
  class TestInlineReplacement:
@@ -162,14 +163,14 @@ class TestInlineReplacement:
162
163
  class TestStandardEmoji:
163
164
  """Tests for bare emoji shortcodes like :rotating_light:."""
164
165
 
165
- def test_rotating_light_maps_to_warning(self) -> None:
166
- assert strip_icon_shortcodes(":rotating_light:") == ""
166
+ def test_rotating_light_maps_to_siren(self) -> None:
167
+ assert strip_icon_shortcodes(":rotating_light:") == "🚨"
167
168
 
168
169
  def test_octagonal_sign_maps_to_no_entry(self) -> None:
169
170
  assert strip_icon_shortcodes(":octagonal_sign:") == "⛔"
170
171
 
171
- def test_wrench_maps_to_gear(self) -> None:
172
- assert strip_icon_shortcodes(":wrench:") == ""
172
+ def test_wrench_maps_to_wrench_emoji(self) -> None:
173
+ assert strip_icon_shortcodes(":wrench:") == "🔧"
173
174
 
174
175
  def test_information_source_maps_to_info(self) -> None:
175
176
  assert strip_icon_shortcodes(":information_source:") == "ℹ"
@@ -180,8 +181,8 @@ class TestStandardEmoji:
180
181
  def test_x_maps_to_cross(self) -> None:
181
182
  assert strip_icon_shortcodes(":x:") == "✗"
182
183
 
183
- def test_briefcase_stripped(self) -> None:
184
- assert strip_icon_shortcodes(":briefcase:") == ""
184
+ def test_briefcase_maps_to_emoji(self) -> None:
185
+ assert strip_icon_shortcodes(":briefcase:") == "💼"
185
186
 
186
187
  def test_unknown_shortcode_unchanged(self) -> None:
187
188
  assert strip_icon_shortcodes(":unknown_emoji_xyz:") == ":unknown_emoji_xyz:"
@@ -195,11 +196,7 @@ class TestStandardEmoji:
195
196
  result = strip_icon_shortcodes(":material-check-circle:")
196
197
  assert result == "✓"
197
198
 
198
- def test_all_standard_emoji_values_are_bmp(self) -> None:
199
+ def test_all_standard_emoji_values_are_strings(self) -> None:
199
200
  from mkdocs_to_confluence.preprocess.icons import _STANDARD_EMOJI_MAP
200
201
  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
- )
202
+ assert isinstance(symbol, str), f"_STANDARD_EMOJI_MAP[{name!r}] is not a string"