pymdownx-mahjong 1.1.0__tar.gz → 1.2.0__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.
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/PKG-INFO +3 -3
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/README.md +1 -1
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/__init__.py +3 -2
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/extension.py +16 -8
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/inline.py +11 -3
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/renderer.py +22 -12
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/superfences.py +41 -3
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pyproject.toml +2 -2
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/tests/test_extension.py +4 -5
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/tests/test_renderer.py +42 -17
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/.gitignore +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/LICENSE +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/README.md +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/0m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/0p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/0s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/1m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/1p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/1s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/1z.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/2m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/2p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/2s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/2z.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/3m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/3p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/3s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/3z.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/4m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/4p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/4s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/4z.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/5m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/5p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/5s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/5z.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/6m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/6p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/6s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/6z.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/7m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/7p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/7s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/7z.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/8m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/8p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/8s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/9m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/9p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/9s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/back.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/blank.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/front.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/0m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/0p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/0s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/1m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/1p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/1s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/1z.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/2m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/2p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/2s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/2z.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/3m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/3p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/3s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/3z.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/4m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/4p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/4s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/4z.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/5m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/5p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/5s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/5z.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/6m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/6p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/6s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/6z.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/7m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/7p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/7s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/7z.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/8m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/8p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/8s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/9m.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/9p.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/9s.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/back.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/blank.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/front.svg +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/css/mahjong.css +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/parser.py +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/tiles.py +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/utils.py +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/tests/__init__.py +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/tests/test_parser.py +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/tests/test_superfences.py +0 -0
- {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/tests/test_utils.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pymdownx-mahjong
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Python Markdown extension to render and stylize Mahjong tiles.
|
|
5
5
|
Project-URL: Homepage, https://github.com/tylernguyen/pymdownx-mahjong
|
|
6
|
-
Project-URL: Documentation, https://github.
|
|
6
|
+
Project-URL: Documentation, https://tylernguyen.github.io/pymdownx-mahjong/
|
|
7
7
|
Project-URL: Repository, https://github.com/tylernguyen/pymdownx-mahjong
|
|
8
8
|
Project-URL: Issues, https://github.com/tylernguyen/pymdownx-mahjong/issues
|
|
9
9
|
Author: Tyler Nguyen
|
|
@@ -41,7 +41,7 @@ Description-Content-Type: text/markdown
|
|
|
41
41
|
|
|
42
42
|
[Python Markdown](https://python-markdown.github.io) extension to render and stylize Mahjong tiles. Designed for use with [MkDocs](https://github.com/mkdocs/mkdocs) and [Zensical](https://github.com/zensical/zensical).
|
|
43
43
|
|
|
44
|
-
## Documentation
|
|
44
|
+
## Demo and Documentation
|
|
45
45
|
|
|
46
46
|
Demo and documentation can be found at [https://tylernguyen.github.io/pymdownx-mahjong/](https://tylernguyen.github.io/pymdownx-mahjong/).
|
|
47
47
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[Python Markdown](https://python-markdown.github.io) extension to render and stylize Mahjong tiles. Designed for use with [MkDocs](https://github.com/mkdocs/mkdocs) and [Zensical](https://github.com/zensical/zensical).
|
|
6
6
|
|
|
7
|
-
## Documentation
|
|
7
|
+
## Demo and Documentation
|
|
8
8
|
|
|
9
9
|
Demo and documentation can be found at [https://tylernguyen.github.io/pymdownx-mahjong/](https://tylernguyen.github.io/pymdownx-mahjong/).
|
|
10
10
|
|
|
@@ -4,7 +4,7 @@ from .extension import MahjongExtension, makeExtension
|
|
|
4
4
|
from .inline import INLINE_TILE_PATTERN, MahjongInlineProcessor
|
|
5
5
|
from .parser import Hand, MahjongParser, Meld, Tile
|
|
6
6
|
from .renderer import MahjongRenderer
|
|
7
|
-
from .superfences import superfences_formatter, superfences_validator
|
|
7
|
+
from .superfences import configure_superfences, superfences_formatter, superfences_validator
|
|
8
8
|
|
|
9
9
|
__all__ = [
|
|
10
10
|
"MahjongExtension",
|
|
@@ -18,6 +18,7 @@ __all__ = [
|
|
|
18
18
|
"Hand",
|
|
19
19
|
"superfences_formatter",
|
|
20
20
|
"superfences_validator",
|
|
21
|
+
"configure_superfences",
|
|
21
22
|
]
|
|
22
23
|
|
|
23
|
-
__version__ = "1.
|
|
24
|
+
__version__ = "1.2.0"
|
|
@@ -19,6 +19,15 @@ if TYPE_CHECKING:
|
|
|
19
19
|
from markdown.blockparser import BlockParser
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
def _to_bool(value: Any) -> bool:
|
|
23
|
+
"""Convert a value to boolean, handling string 'true'/'false'."""
|
|
24
|
+
if isinstance(value, bool):
|
|
25
|
+
return value
|
|
26
|
+
if isinstance(value, str):
|
|
27
|
+
return value.lower() in ("true", "1", "yes")
|
|
28
|
+
return bool(value)
|
|
29
|
+
|
|
30
|
+
|
|
22
31
|
class MahjongBlockProcessor(BlockProcessor):
|
|
23
32
|
"""Block processor that handles ```mahjong fenced code blocks.
|
|
24
33
|
|
|
@@ -43,10 +52,9 @@ class MahjongBlockProcessor(BlockProcessor):
|
|
|
43
52
|
self.mj_parser = MahjongParser()
|
|
44
53
|
self.renderer = MahjongRenderer(
|
|
45
54
|
theme=config.get("theme", "light"),
|
|
46
|
-
|
|
47
|
-
show_labels=config.get("show_labels", True),
|
|
48
|
-
inline_svg=config.get("inline_svg", True),
|
|
55
|
+
inline_svg=_to_bool(config.get("inline_svg", True)),
|
|
49
56
|
assets_path=config.get("assets_path"),
|
|
57
|
+
closed_kan_style=config.get("closed_kan_style", "outer"),
|
|
50
58
|
)
|
|
51
59
|
|
|
52
60
|
def test(self, parent: etree.Element, block: str) -> bool:
|
|
@@ -158,13 +166,13 @@ class MahjongExtension(markdown.Extension):
|
|
|
158
166
|
**kwargs: Configuration options
|
|
159
167
|
"""
|
|
160
168
|
# Define configuration options with defaults
|
|
169
|
+
# Note: Use strings for boolean defaults for YAML compatibility
|
|
161
170
|
self.config = {
|
|
162
171
|
"theme": ["auto", "Color theme: 'light', 'dark', or 'auto'"],
|
|
163
|
-
"
|
|
164
|
-
"show_labels": [True, "Show tile names as title attributes"],
|
|
165
|
-
"inline_svg": [True, "Inline SVG content vs img tags"],
|
|
172
|
+
"inline_svg": ["true", "Inline SVG content vs img tags"],
|
|
166
173
|
"assets_path": ["", "Custom path to SVG assets"],
|
|
167
|
-
"enable_inline": [
|
|
174
|
+
"enable_inline": ["true", "Enable inline tile syntax (:1m:)"],
|
|
175
|
+
"closed_kan_style": ["outer", "Closed kan style: 'outer' or 'inner'"],
|
|
168
176
|
}
|
|
169
177
|
super().__init__(**kwargs)
|
|
170
178
|
|
|
@@ -184,7 +192,7 @@ class MahjongExtension(markdown.Extension):
|
|
|
184
192
|
|
|
185
193
|
# Register inline processor if enabled
|
|
186
194
|
# Priority 76 to run before pymdownx.emoji (which uses 75)
|
|
187
|
-
if config.get("enable_inline", True):
|
|
195
|
+
if _to_bool(config.get("enable_inline", True)):
|
|
188
196
|
inline_processor = MahjongInlineProcessor(INLINE_TILE_PATTERN, md, config)
|
|
189
197
|
md.inlinePatterns.register(inline_processor, "mahjong_inline", 76)
|
|
190
198
|
|
|
@@ -20,6 +20,15 @@ if TYPE_CHECKING:
|
|
|
20
20
|
INLINE_TILE_PATTERN: Final[str] = r":([0-9]+[mpsz])+:"
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
def _to_bool(value: Any) -> bool:
|
|
24
|
+
"""Convert a value to boolean, handling string 'true'/'false'."""
|
|
25
|
+
if isinstance(value, bool):
|
|
26
|
+
return value
|
|
27
|
+
if isinstance(value, str):
|
|
28
|
+
return value.lower() in ("true", "1", "yes")
|
|
29
|
+
return bool(value)
|
|
30
|
+
|
|
31
|
+
|
|
23
32
|
class MahjongInlineProcessor(InlineProcessor):
|
|
24
33
|
"""Inline processor for mahjong tile notation.
|
|
25
34
|
|
|
@@ -40,10 +49,9 @@ class MahjongInlineProcessor(InlineProcessor):
|
|
|
40
49
|
self.parser = MahjongParser()
|
|
41
50
|
self.renderer = MahjongRenderer(
|
|
42
51
|
theme=config.get("theme", "auto"),
|
|
43
|
-
|
|
44
|
-
show_labels=config.get("show_labels", True),
|
|
45
|
-
inline_svg=config.get("inline_svg", True),
|
|
52
|
+
inline_svg=_to_bool(config.get("inline_svg", True)),
|
|
46
53
|
assets_path=config.get("assets_path"),
|
|
54
|
+
css_class="mahjong-inline",
|
|
47
55
|
)
|
|
48
56
|
|
|
49
57
|
def handleMatch(self, m: re.Match, data: str) -> tuple[str | None, int | None, int | None]:
|
|
@@ -50,9 +50,8 @@ class MahjongRenderer:
|
|
|
50
50
|
|
|
51
51
|
Configuration options:
|
|
52
52
|
theme: 'light', 'dark', or 'auto'
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
inline_svg: Whether to inline SVG or use img tags
|
|
53
|
+
closed_kan_style: 'outer' (default) or 'inner'
|
|
54
|
+
inline_svg: Whether to inline SVG content (vs using img tags)
|
|
56
55
|
"""
|
|
57
56
|
|
|
58
57
|
DEFAULT_TILE_WIDTH = 45
|
|
@@ -63,19 +62,21 @@ class MahjongRenderer:
|
|
|
63
62
|
def __init__(
|
|
64
63
|
self,
|
|
65
64
|
theme: str = "light",
|
|
66
|
-
css_class: str = "mahjong-hand",
|
|
67
|
-
show_labels: bool = True,
|
|
68
65
|
inline_svg: bool = True,
|
|
69
66
|
assets_path: str | Path | None = None,
|
|
67
|
+
closed_kan_style: str = "outer",
|
|
68
|
+
css_class: str = "mahjong-hand",
|
|
70
69
|
) -> None:
|
|
71
70
|
"""Initialize the renderer.
|
|
72
71
|
|
|
73
72
|
Args:
|
|
74
73
|
theme: Color theme ('light', 'dark', or 'auto')
|
|
75
|
-
css_class: CSS class for the container element
|
|
76
|
-
show_labels: Show tile names as title attributes
|
|
77
74
|
inline_svg: Inline SVG content vs img tags
|
|
78
75
|
assets_path: Custom path to SVG assets
|
|
76
|
+
closed_kan_style: Style for closed kan back tiles:
|
|
77
|
+
'outer' (default) - back tiles on edges (back, front, front, back)
|
|
78
|
+
'inner' - back tiles in middle (front, back, back, front)
|
|
79
|
+
css_class: CSS class for container (internal use)
|
|
79
80
|
"""
|
|
80
81
|
self.theme = theme
|
|
81
82
|
self.tile_width = self.DEFAULT_TILE_WIDTH
|
|
@@ -83,9 +84,9 @@ class MahjongRenderer:
|
|
|
83
84
|
self.tile_gap = self.DEFAULT_TILE_GAP
|
|
84
85
|
self.meld_gap = self.DEFAULT_MELD_GAP
|
|
85
86
|
self.css_class = css_class
|
|
86
|
-
self.show_labels = show_labels
|
|
87
87
|
self.inline_svg = inline_svg
|
|
88
88
|
self.assets_path = Path(assets_path) if assets_path else None
|
|
89
|
+
self.closed_kan_style = closed_kan_style
|
|
89
90
|
self._svg_id_counter = 0
|
|
90
91
|
|
|
91
92
|
def render(
|
|
@@ -209,7 +210,7 @@ class MahjongRenderer:
|
|
|
209
210
|
classes.append("mahjong-tile-added")
|
|
210
211
|
|
|
211
212
|
class_str = " ".join(classes)
|
|
212
|
-
title_attr = f' title="{info.display_name}"'
|
|
213
|
+
title_attr = f' title="{info.display_name}"'
|
|
213
214
|
|
|
214
215
|
if self.inline_svg:
|
|
215
216
|
svg_content = self._get_themed_svg_content(info)
|
|
@@ -271,9 +272,18 @@ class MahjongRenderer:
|
|
|
271
272
|
parts.append(self._render_tile(meld.tiles[2]))
|
|
272
273
|
else:
|
|
273
274
|
for i, tile in enumerate(meld.tiles):
|
|
274
|
-
# For closed kan, show back tiles
|
|
275
|
-
if meld.meld_type == MeldType.KAN_CLOSED
|
|
276
|
-
|
|
275
|
+
# For closed kan, show back tiles based on style setting
|
|
276
|
+
if meld.meld_type == MeldType.KAN_CLOSED:
|
|
277
|
+
if self.closed_kan_style == "inner":
|
|
278
|
+
# 'inner': back tiles in middle: front, back, back, front
|
|
279
|
+
is_back = i in (1, 2)
|
|
280
|
+
else:
|
|
281
|
+
# Default 'outer': back tiles on edges: back, front, front, back
|
|
282
|
+
is_back = i in (0, 3)
|
|
283
|
+
if is_back:
|
|
284
|
+
parts.append(self._render_back_tile())
|
|
285
|
+
else:
|
|
286
|
+
parts.append(self._render_tile(tile))
|
|
277
287
|
else:
|
|
278
288
|
parts.append(self._render_tile(tile))
|
|
279
289
|
|
|
@@ -17,20 +17,32 @@ class _SuperfencesState:
|
|
|
17
17
|
"""Encapsulates global state for superfences integration.
|
|
18
18
|
|
|
19
19
|
Uses lazy initialization to create parser/renderer on first use.
|
|
20
|
+
Allows configuration via configure() method.
|
|
20
21
|
"""
|
|
21
22
|
|
|
22
23
|
def __init__(self) -> None:
|
|
23
24
|
self._renderer: MahjongRenderer | None = None
|
|
24
25
|
self._parser: MahjongParser | None = None
|
|
26
|
+
self._config: dict[str, Any] = {}
|
|
27
|
+
|
|
28
|
+
def configure(self, **kwargs: Any) -> None:
|
|
29
|
+
"""Configure the superfences state.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
**kwargs: Configuration options (theme, closed_kan_style, etc.)
|
|
33
|
+
"""
|
|
34
|
+
self._config.update(kwargs)
|
|
35
|
+
# Reset renderer to apply new config
|
|
36
|
+
self._renderer = None
|
|
25
37
|
|
|
26
38
|
@property
|
|
27
39
|
def renderer(self) -> MahjongRenderer:
|
|
28
40
|
"""Get or create the renderer instance."""
|
|
29
41
|
if self._renderer is None:
|
|
30
42
|
self._renderer = MahjongRenderer(
|
|
31
|
-
theme="auto",
|
|
32
|
-
|
|
33
|
-
|
|
43
|
+
theme=self._config.get("theme", "auto"),
|
|
44
|
+
inline_svg=self._config.get("inline_svg", True),
|
|
45
|
+
closed_kan_style=self._config.get("closed_kan_style", "outer"),
|
|
34
46
|
)
|
|
35
47
|
return self._renderer
|
|
36
48
|
|
|
@@ -46,6 +58,24 @@ class _SuperfencesState:
|
|
|
46
58
|
_state = _SuperfencesState()
|
|
47
59
|
|
|
48
60
|
|
|
61
|
+
def configure_superfences(**kwargs: Any) -> None:
|
|
62
|
+
"""Configure the superfences integration.
|
|
63
|
+
|
|
64
|
+
Call this before using superfences to set options like closed_kan_style.
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
from pymdownx_mahjong import configure_superfences
|
|
68
|
+
configure_superfences(closed_kan_style='outer')
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
**kwargs: Configuration options
|
|
72
|
+
- theme: 'light', 'dark', or 'auto'
|
|
73
|
+
- closed_kan_style: 'outer' or 'inner'
|
|
74
|
+
- inline_svg: bool
|
|
75
|
+
"""
|
|
76
|
+
_state.configure(**kwargs)
|
|
77
|
+
|
|
78
|
+
|
|
49
79
|
def superfences_validator(
|
|
50
80
|
language: str,
|
|
51
81
|
inputs: dict[str, str],
|
|
@@ -65,6 +95,14 @@ def superfences_validator(
|
|
|
65
95
|
Returns:
|
|
66
96
|
True if this is a valid mahjong fence
|
|
67
97
|
"""
|
|
98
|
+
# Try to get config from the markdown instance's extension
|
|
99
|
+
if hasattr(md, 'registeredExtensions'):
|
|
100
|
+
for ext in md.registeredExtensions:
|
|
101
|
+
if hasattr(ext, 'config') and 'closed_kan_style' in ext.config:
|
|
102
|
+
config = {key: ext.getConfig(key) for key in ext.config}
|
|
103
|
+
_state.configure(**config)
|
|
104
|
+
break
|
|
105
|
+
|
|
68
106
|
return language == "mahjong"
|
|
69
107
|
|
|
70
108
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pymdownx-mahjong"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.2.0"
|
|
8
8
|
description = "Python Markdown extension to render and stylize Mahjong tiles."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "CC-BY-NC-4.0"
|
|
@@ -52,7 +52,7 @@ dev = [
|
|
|
52
52
|
|
|
53
53
|
[project.urls]
|
|
54
54
|
Homepage = "https://github.com/tylernguyen/pymdownx-mahjong"
|
|
55
|
-
Documentation = "https://github.
|
|
55
|
+
Documentation = "https://tylernguyen.github.io/pymdownx-mahjong/"
|
|
56
56
|
Repository = "https://github.com/tylernguyen/pymdownx-mahjong"
|
|
57
57
|
Issues = "https://github.com/tylernguyen/pymdownx-mahjong/issues"
|
|
58
58
|
|
|
@@ -109,8 +109,7 @@ invalid notation 8z9z
|
|
|
109
109
|
extensions=["pymdownx_mahjong"],
|
|
110
110
|
extension_configs={
|
|
111
111
|
"pymdownx_mahjong": {
|
|
112
|
-
"
|
|
113
|
-
"show_labels": False,
|
|
112
|
+
"theme": "dark",
|
|
114
113
|
}
|
|
115
114
|
},
|
|
116
115
|
)
|
|
@@ -123,7 +122,7 @@ invalid notation 8z9z
|
|
|
123
122
|
"""
|
|
124
123
|
result = md.convert(source)
|
|
125
124
|
|
|
126
|
-
assert "
|
|
125
|
+
assert "mahjong-hand" in result
|
|
127
126
|
|
|
128
127
|
def test_multiple_blocks(self):
|
|
129
128
|
"""Test converting multiple mahjong blocks."""
|
|
@@ -216,6 +215,6 @@ class TestMakeExtension:
|
|
|
216
215
|
"""Test makeExtension with configuration."""
|
|
217
216
|
from pymdownx_mahjong import makeExtension
|
|
218
217
|
|
|
219
|
-
ext = makeExtension(theme="dark",
|
|
218
|
+
ext = makeExtension(theme="dark", closed_kan_style="inner")
|
|
220
219
|
assert ext.getConfig("theme") == "dark"
|
|
221
|
-
assert ext.getConfig("
|
|
220
|
+
assert ext.getConfig("closed_kan_style") == "inner"
|
|
@@ -113,30 +113,22 @@ class TestMahjongRenderer:
|
|
|
113
113
|
class TestRendererConfiguration:
|
|
114
114
|
"""Tests for renderer configuration options."""
|
|
115
115
|
|
|
116
|
-
def
|
|
117
|
-
"""Test
|
|
118
|
-
renderer = MahjongRenderer(
|
|
116
|
+
def test_default_css_class(self):
|
|
117
|
+
"""Test default CSS class is mahjong-hand."""
|
|
118
|
+
renderer = MahjongRenderer()
|
|
119
119
|
hand = parse_hand("123m")
|
|
120
120
|
html = renderer.render(hand)
|
|
121
121
|
|
|
122
|
-
assert 'class="
|
|
122
|
+
assert 'class="mahjong-hand"' in html
|
|
123
123
|
|
|
124
|
-
def
|
|
125
|
-
"""Test that tile titles are shown
|
|
126
|
-
renderer = MahjongRenderer(
|
|
124
|
+
def test_tile_titles_shown(self):
|
|
125
|
+
"""Test that tile titles are always shown."""
|
|
126
|
+
renderer = MahjongRenderer()
|
|
127
127
|
hand = parse_hand("1m")
|
|
128
128
|
html = renderer.render(hand)
|
|
129
129
|
|
|
130
130
|
assert 'title="1 Man"' in html
|
|
131
131
|
|
|
132
|
-
def test_show_labels_false(self):
|
|
133
|
-
"""Test that tile titles are hidden when show_labels is False."""
|
|
134
|
-
renderer = MahjongRenderer(show_labels=False)
|
|
135
|
-
hand = parse_hand("1m")
|
|
136
|
-
html = renderer.render(hand)
|
|
137
|
-
|
|
138
|
-
assert "title=" not in html
|
|
139
|
-
|
|
140
132
|
def test_inline_svg_true(self):
|
|
141
133
|
"""Test inline SVG mode."""
|
|
142
134
|
renderer = MahjongRenderer(inline_svg=True)
|
|
@@ -174,6 +166,39 @@ class TestRendererConfiguration:
|
|
|
174
166
|
assert 'class="mahjong-tile-light"' in html
|
|
175
167
|
assert 'class="mahjong-tile-dark"' in html
|
|
176
168
|
|
|
169
|
+
def test_closed_kan_style_inner_default(self):
|
|
170
|
+
"""Test that closed kan defaults to inner style (back tiles in middle)."""
|
|
171
|
+
renderer = MahjongRenderer(theme="light")
|
|
172
|
+
hand = parse_hand("[1111z]")
|
|
173
|
+
html = renderer.render(hand)
|
|
174
|
+
|
|
175
|
+
# Inner style: front, back, back, front
|
|
176
|
+
# Count back tiles - should appear in positions 1,2 (middle)
|
|
177
|
+
back_count = html.count('class="mahjong-tile mahjong-tile-back"')
|
|
178
|
+
assert back_count == 2
|
|
179
|
+
|
|
180
|
+
def test_closed_kan_style_outer(self):
|
|
181
|
+
"""Test closed kan with outer style (back tiles on edges)."""
|
|
182
|
+
renderer = MahjongRenderer(theme="light", closed_kan_style="outer")
|
|
183
|
+
hand = parse_hand("[1111z]")
|
|
184
|
+
html = renderer.render(hand)
|
|
185
|
+
|
|
186
|
+
# Outer style: back, front, front, back
|
|
187
|
+
back_count = html.count('class="mahjong-tile mahjong-tile-back"')
|
|
188
|
+
assert back_count == 2
|
|
189
|
+
# Verify the style is set correctly
|
|
190
|
+
assert renderer.closed_kan_style == "outer"
|
|
191
|
+
|
|
192
|
+
def test_closed_kan_style_inner_explicit(self):
|
|
193
|
+
"""Test closed kan with explicit inner style."""
|
|
194
|
+
renderer = MahjongRenderer(theme="light", closed_kan_style="inner")
|
|
195
|
+
hand = parse_hand("[1111z]")
|
|
196
|
+
html = renderer.render(hand)
|
|
197
|
+
|
|
198
|
+
assert renderer.closed_kan_style == "inner"
|
|
199
|
+
back_count = html.count('class="mahjong-tile mahjong-tile-back"')
|
|
200
|
+
assert back_count == 2
|
|
201
|
+
|
|
177
202
|
|
|
178
203
|
class TestRenderTiles:
|
|
179
204
|
"""Tests for render_tiles method."""
|
|
@@ -225,6 +250,6 @@ class TestConvenienceFunction:
|
|
|
225
250
|
def test_render_hand_with_kwargs(self):
|
|
226
251
|
"""Test render_hand with configuration kwargs."""
|
|
227
252
|
hand = parse_hand("123m")
|
|
228
|
-
html = render_hand(hand, theme="dark"
|
|
253
|
+
html = render_hand(hand, theme="dark")
|
|
229
254
|
|
|
230
|
-
assert 'class="
|
|
255
|
+
assert 'class="mahjong-hand"' in html
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|