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.
Files changed (101) hide show
  1. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/PKG-INFO +3 -3
  2. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/README.md +1 -1
  3. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/__init__.py +3 -2
  4. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/extension.py +16 -8
  5. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/inline.py +11 -3
  6. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/renderer.py +22 -12
  7. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/superfences.py +41 -3
  8. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pyproject.toml +2 -2
  9. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/tests/test_extension.py +4 -5
  10. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/tests/test_renderer.py +42 -17
  11. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/.gitignore +0 -0
  12. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/LICENSE +0 -0
  13. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/README.md +0 -0
  14. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/0m.svg +0 -0
  15. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/0p.svg +0 -0
  16. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/0s.svg +0 -0
  17. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/1m.svg +0 -0
  18. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/1p.svg +0 -0
  19. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/1s.svg +0 -0
  20. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/1z.svg +0 -0
  21. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/2m.svg +0 -0
  22. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/2p.svg +0 -0
  23. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/2s.svg +0 -0
  24. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/2z.svg +0 -0
  25. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/3m.svg +0 -0
  26. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/3p.svg +0 -0
  27. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/3s.svg +0 -0
  28. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/3z.svg +0 -0
  29. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/4m.svg +0 -0
  30. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/4p.svg +0 -0
  31. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/4s.svg +0 -0
  32. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/4z.svg +0 -0
  33. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/5m.svg +0 -0
  34. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/5p.svg +0 -0
  35. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/5s.svg +0 -0
  36. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/5z.svg +0 -0
  37. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/6m.svg +0 -0
  38. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/6p.svg +0 -0
  39. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/6s.svg +0 -0
  40. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/6z.svg +0 -0
  41. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/7m.svg +0 -0
  42. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/7p.svg +0 -0
  43. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/7s.svg +0 -0
  44. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/7z.svg +0 -0
  45. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/8m.svg +0 -0
  46. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/8p.svg +0 -0
  47. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/8s.svg +0 -0
  48. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/9m.svg +0 -0
  49. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/9p.svg +0 -0
  50. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/9s.svg +0 -0
  51. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/back.svg +0 -0
  52. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/blank.svg +0 -0
  53. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/front.svg +0 -0
  54. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/0m.svg +0 -0
  55. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/0p.svg +0 -0
  56. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/0s.svg +0 -0
  57. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/1m.svg +0 -0
  58. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/1p.svg +0 -0
  59. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/1s.svg +0 -0
  60. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/1z.svg +0 -0
  61. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/2m.svg +0 -0
  62. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/2p.svg +0 -0
  63. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/2s.svg +0 -0
  64. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/2z.svg +0 -0
  65. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/3m.svg +0 -0
  66. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/3p.svg +0 -0
  67. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/3s.svg +0 -0
  68. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/3z.svg +0 -0
  69. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/4m.svg +0 -0
  70. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/4p.svg +0 -0
  71. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/4s.svg +0 -0
  72. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/4z.svg +0 -0
  73. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/5m.svg +0 -0
  74. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/5p.svg +0 -0
  75. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/5s.svg +0 -0
  76. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/5z.svg +0 -0
  77. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/6m.svg +0 -0
  78. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/6p.svg +0 -0
  79. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/6s.svg +0 -0
  80. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/6z.svg +0 -0
  81. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/7m.svg +0 -0
  82. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/7p.svg +0 -0
  83. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/7s.svg +0 -0
  84. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/7z.svg +0 -0
  85. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/8m.svg +0 -0
  86. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/8p.svg +0 -0
  87. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/8s.svg +0 -0
  88. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/9m.svg +0 -0
  89. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/9p.svg +0 -0
  90. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/9s.svg +0 -0
  91. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/back.svg +0 -0
  92. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/blank.svg +0 -0
  93. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/front.svg +0 -0
  94. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/css/mahjong.css +0 -0
  95. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/parser.py +0 -0
  96. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/tiles.py +0 -0
  97. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/utils.py +0 -0
  98. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/tests/__init__.py +0 -0
  99. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/tests/test_parser.py +0 -0
  100. {pymdownx_mahjong-1.1.0 → pymdownx_mahjong-1.2.0}/tests/test_superfences.py +0 -0
  101. {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.1.0
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.com/tylernguyen/pymdownx-mahjong
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.1.0"
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
- css_class=config.get("css_class", "mahjong-hand"),
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
- "css_class": ["mahjong-hand", "CSS class for container"],
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": [True, "Enable inline tile syntax (:1m:)"],
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
- css_class="mahjong-inline",
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
- css_class: CSS class for the container
54
- show_labels: Whether to show tile names as titles
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}"' if self.show_labels else ""
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 for middle two
275
- if meld.meld_type == MeldType.KAN_CLOSED and i in (1, 2):
276
- parts.append(self._render_back_tile())
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
- show_labels=True,
33
- inline_svg=True,
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.1.0"
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.com/tylernguyen/pymdownx-mahjong"
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
- "css_class": "custom-mahjong",
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 "custom-mahjong" in result
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", css_class="custom-class")
218
+ ext = makeExtension(theme="dark", closed_kan_style="inner")
220
219
  assert ext.getConfig("theme") == "dark"
221
- assert ext.getConfig("css_class") == "custom-class"
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 test_custom_css_class(self):
117
- """Test custom CSS class."""
118
- renderer = MahjongRenderer(css_class="custom-mahjong")
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="custom-mahjong"' in html
122
+ assert 'class="mahjong-hand"' in html
123
123
 
124
- def test_show_labels_true(self):
125
- """Test that tile titles are shown when show_labels is True."""
126
- renderer = MahjongRenderer(show_labels=True)
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", css_class="custom")
253
+ html = render_hand(hand, theme="dark")
229
254
 
230
- assert 'class="custom"' in html
255
+ assert 'class="mahjong-hand"' in html