pymdownx-mahjong 1.0.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.0.0 → pymdownx_mahjong-1.2.0}/PKG-INFO +9 -3
- pymdownx_mahjong-1.2.0/README.md +13 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/__init__.py +3 -2
- pymdownx_mahjong-1.2.0/pymdownx_mahjong/assets/README.md +3 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/css/mahjong.css +11 -12
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/extension.py +16 -8
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/inline.py +13 -5
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/parser.py +33 -2
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/renderer.py +61 -41
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/superfences.py +41 -3
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/tiles.py +3 -3
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pyproject.toml +2 -2
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/tests/test_extension.py +4 -5
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/tests/test_parser.py +75 -1
- pymdownx_mahjong-1.2.0/tests/test_renderer.py +255 -0
- pymdownx_mahjong-1.2.0/tests/test_superfences.py +147 -0
- pymdownx_mahjong-1.2.0/tests/test_utils.py +219 -0
- pymdownx_mahjong-1.0.0/README.md +0 -7
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/.gitignore +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/LICENSE +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/0m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/0p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/0s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/1m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/1p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/1s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/1z.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/2m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/2p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/2s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/2z.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/3m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/3p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/3s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/3z.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/4m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/4p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/4s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/4z.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/5m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/5p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/5s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/5z.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/6m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/6p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/6s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/6z.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/7m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/7p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/7s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/7z.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/8m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/8p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/8s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/9m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/9p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/9s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/back.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/blank.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/dark/front.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/0m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/0p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/0s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/1m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/1p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/1s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/1z.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/2m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/2p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/2s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/2z.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/3m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/3p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/3s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/3z.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/4m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/4p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/4s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/4z.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/5m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/5p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/5s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/5z.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/6m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/6p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/6s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/6z.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/7m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/7p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/7s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/7z.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/8m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/8p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/8s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/9m.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/9p.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/9s.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/back.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/blank.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/assets/light/front.svg +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/pymdownx_mahjong/utils.py +0 -0
- {pymdownx_mahjong-1.0.0 → pymdownx_mahjong-1.2.0}/tests/__init__.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
|
|
@@ -35,9 +35,15 @@ Requires-Dist: mkdocs-material>=9.0; extra == 'mkdocs'
|
|
|
35
35
|
Requires-Dist: mkdocs>=1.4; extra == 'mkdocs'
|
|
36
36
|
Description-Content-Type: text/markdown
|
|
37
37
|
|
|
38
|
+

|
|
39
|
+
|
|
38
40
|
# PyMdown Mahjong
|
|
39
41
|
|
|
40
|
-
|
|
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
|
+
|
|
44
|
+
## Demo and Documentation
|
|
45
|
+
|
|
46
|
+
Demo and documentation can be found at [https://tylernguyen.github.io/pymdownx-mahjong/](https://tylernguyen.github.io/pymdownx-mahjong/).
|
|
41
47
|
|
|
42
48
|
## License
|
|
43
49
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# PyMdown Mahjong
|
|
4
|
+
|
|
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
|
+
|
|
7
|
+
## Demo and Documentation
|
|
8
|
+
|
|
9
|
+
Demo and documentation can be found at [https://tylernguyen.github.io/pymdownx-mahjong/](https://tylernguyen.github.io/pymdownx-mahjong/).
|
|
10
|
+
|
|
11
|
+
## License
|
|
12
|
+
|
|
13
|
+
License is Creative Commons Attribution-NonCommercial 4.0 International. See [LICENSE](https://github.com/tylernguyen/pymdownx-mahjong/blob/main/LICENSE).
|
|
@@ -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"
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
--mahjong-error-border: #dc2626;
|
|
24
24
|
--mahjong-error-color: #fca5a5;
|
|
25
25
|
--mahjong-tile-bg: #1e1e1e;
|
|
26
|
-
--mahjong-tile-border: #
|
|
26
|
+
--mahjong-tile-border: #47473e;
|
|
27
27
|
--mahjong-tile-shadow: rgba(0, 0, 0, 0.3);
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
display: contents;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
/* Main container */
|
|
51
|
+
/* NOTE Main container */
|
|
52
52
|
.mahjong-hand {
|
|
53
53
|
display: block;
|
|
54
54
|
margin: 1em 0;
|
|
@@ -58,24 +58,24 @@
|
|
|
58
58
|
border-radius: 4px;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
/* Override MkDocs Material theme figure margins */
|
|
61
|
+
/* NOTE Override MkDocs Material/Zensical theme figure margins */
|
|
62
62
|
.md-typeset .mahjong-hand {
|
|
63
63
|
margin-left: 1rem;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
/* Hand row - contains left (tiles/melds), draw, and right (dora) sections */
|
|
66
|
+
/* NOTE Hand row - contains left (tiles/melds), draw, and right (dora) sections */
|
|
67
67
|
.mahjong-hand-row {
|
|
68
68
|
display: flex;
|
|
69
69
|
flex-wrap: nowrap;
|
|
70
70
|
align-items: flex-end;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
/* Left section - closed tiles and melds */
|
|
73
|
+
/* NOTE Left section - closed tiles and melds */
|
|
74
74
|
.mahjong-hand-left {
|
|
75
75
|
flex: 0 0 auto;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
/* Draw tile section
|
|
78
|
+
/* NOTE Draw tile section */
|
|
79
79
|
.mahjong-hand-draw {
|
|
80
80
|
flex: 0 0 auto;
|
|
81
81
|
margin-left: calc(var(--mahjong-meld-gap) * 0.75);
|
|
@@ -96,13 +96,13 @@
|
|
|
96
96
|
pointer-events: none;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
/* Melds section - after draw tile */
|
|
99
|
+
/* NOTE Melds section - after draw tile */
|
|
100
100
|
.mahjong-hand-melds {
|
|
101
101
|
flex: 0 0 auto;
|
|
102
102
|
margin-left: calc(var(--mahjong-meld-gap) * 0.75);
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
/* Tiles container - flexbox layout */
|
|
105
|
+
/* NOTE Tiles container - flexbox layout */
|
|
106
106
|
.mahjong-tiles {
|
|
107
107
|
display: flex;
|
|
108
108
|
flex-wrap: wrap;
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
gap: var(--mahjong-tile-gap);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
/* Individual tile - styled like physical riichi tiles */
|
|
113
|
+
/* NOTE Individual tile - styled like physical riichi tiles */
|
|
114
114
|
.mahjong-tile {
|
|
115
115
|
display: inline-flex;
|
|
116
116
|
align-items: center;
|
|
@@ -122,9 +122,6 @@
|
|
|
122
122
|
background: var(--mahjong-tile-bg);
|
|
123
123
|
border: 2px solid var(--mahjong-tile-border);
|
|
124
124
|
border-radius: 6px;
|
|
125
|
-
box-shadow:
|
|
126
|
-
1px 2px 3px var(--mahjong-tile-shadow),
|
|
127
|
-
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
|
128
125
|
padding: 2px;
|
|
129
126
|
box-sizing: border-box;
|
|
130
127
|
position: relative;
|
|
@@ -144,6 +141,8 @@
|
|
|
144
141
|
/* Rotated tile (called from another player) */
|
|
145
142
|
.mahjong-tile-rotated {
|
|
146
143
|
transform: rotate(90deg);
|
|
144
|
+
will-change: transform;
|
|
145
|
+
backface-visibility: hidden;
|
|
147
146
|
margin-left: calc(var(--mahjong-tile-gap) * 6);
|
|
148
147
|
margin-right: calc(var(--mahjong-tile-gap) * 6);
|
|
149
148
|
position: relative;
|
|
@@ -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
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import TYPE_CHECKING, Any
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Final
|
|
6
6
|
|
|
7
7
|
from markdown.inlinepatterns import InlineProcessor
|
|
8
8
|
|
|
@@ -17,7 +17,16 @@ if TYPE_CHECKING:
|
|
|
17
17
|
# Pattern matches :123m:, :1z:, :0m: (red dora), etc.
|
|
18
18
|
# Must be valid MPSZ: one or more groups of digits followed by m/p/s/z
|
|
19
19
|
# Examples: :1m:, :123p:, :5z:, :0s:, :123m456p:
|
|
20
|
-
INLINE_TILE_PATTERN = r":([0-9]+[mpsz])+:"
|
|
20
|
+
INLINE_TILE_PATTERN: Final[str] = r":([0-9]+[mpsz])+:"
|
|
21
|
+
|
|
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)
|
|
21
30
|
|
|
22
31
|
|
|
23
32
|
class MahjongInlineProcessor(InlineProcessor):
|
|
@@ -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]:
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import re
|
|
6
|
+
from collections import Counter
|
|
6
7
|
from dataclasses import dataclass, field
|
|
7
8
|
from enum import Enum
|
|
9
|
+
from typing import Final, Pattern
|
|
8
10
|
|
|
9
11
|
from .tiles import TileInfo, get_tile_info, is_valid_tile
|
|
10
12
|
|
|
@@ -127,11 +129,11 @@ class MahjongParser:
|
|
|
127
129
|
"""
|
|
128
130
|
|
|
129
131
|
# Pattern for a group of numbers followed by a suit
|
|
130
|
-
TILE_GROUP_PATTERN = re.compile(r"([0-9]+)([mpsz])")
|
|
132
|
+
TILE_GROUP_PATTERN: Final[Pattern[str]] = re.compile(r"([0-9]+)([mpsz])")
|
|
131
133
|
|
|
132
134
|
# Pattern for melds: (tiles<) or [tiles] with optional source marker inside brackets
|
|
133
135
|
# For added kan, use + to mark the added tile: (111+1m<)
|
|
134
|
-
MELD_PATTERN = re.compile(r"(\[|\()([0-9]+)(\+)?([0-9])?([mpsz])([<^>])?(\]|\))")
|
|
136
|
+
MELD_PATTERN: Final[Pattern[str]] = re.compile(r"(\[|\()([0-9]+)(\+)?([0-9])?([mpsz])([<^>])?(\]|\))")
|
|
135
137
|
|
|
136
138
|
def __init__(self) -> None:
|
|
137
139
|
self.errors: list[str] = []
|
|
@@ -163,6 +165,9 @@ class MahjongParser:
|
|
|
163
165
|
# Parse closed tiles
|
|
164
166
|
hand.closed_tiles = self._parse_tiles(closed_part)
|
|
165
167
|
|
|
168
|
+
# Validate tile counts (max 4 of each tile type)
|
|
169
|
+
self._validate_tile_counts(hand)
|
|
170
|
+
|
|
166
171
|
if self.errors:
|
|
167
172
|
raise ParseError("; ".join(self.errors))
|
|
168
173
|
|
|
@@ -336,6 +341,32 @@ class MahjongParser:
|
|
|
336
341
|
|
|
337
342
|
return numbers[1] == numbers[0] + 1 and numbers[2] == numbers[1] + 1
|
|
338
343
|
|
|
344
|
+
def _validate_tile_counts(self, hand: Hand) -> None:
|
|
345
|
+
"""Validate that no tile appears more than 4 times.
|
|
346
|
+
|
|
347
|
+
In Mahjong, there are exactly 4 copies of each tile type.
|
|
348
|
+
Having more than 4 of the same tile is invalid.
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
hand: The Hand object to validate
|
|
352
|
+
"""
|
|
353
|
+
# Count all tiles including melds
|
|
354
|
+
# Note: Red 5 (0) and regular 5 are different tiles, so we count them separately
|
|
355
|
+
all_tiles = list(hand.closed_tiles)
|
|
356
|
+
for meld in hand.melds:
|
|
357
|
+
all_tiles.extend(meld.tiles)
|
|
358
|
+
if hand.draw_tile:
|
|
359
|
+
all_tiles.append(hand.draw_tile)
|
|
360
|
+
|
|
361
|
+
counts = Counter((t.suit, t.number) for t in all_tiles)
|
|
362
|
+
|
|
363
|
+
for (suit, number), count in counts.items():
|
|
364
|
+
if count > 4:
|
|
365
|
+
tile_notation = f"{number}{suit}"
|
|
366
|
+
self.errors.append(
|
|
367
|
+
f"Invalid tile count: {tile_notation} appears {count} times (max 4)"
|
|
368
|
+
)
|
|
369
|
+
|
|
339
370
|
|
|
340
371
|
def parse_hand(notation: str) -> Hand:
|
|
341
372
|
"""Convenience function to parse a hand notation.
|
|
@@ -2,23 +2,47 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import functools
|
|
5
6
|
import html
|
|
6
7
|
import importlib.resources
|
|
7
8
|
import re
|
|
8
9
|
from pathlib import Path
|
|
10
|
+
from typing import Final, Pattern
|
|
9
11
|
|
|
10
12
|
from .parser import Hand, Meld, MeldType, Tile
|
|
11
13
|
from .tiles import TileInfo, get_special_tile
|
|
12
14
|
|
|
13
15
|
# Pre-compiled regex patterns for SVG processing
|
|
14
|
-
_RE_XML_DECL = re.compile(r"<\?xml[^?]*\?>")
|
|
15
|
-
_RE_SODIPODI_SELF = re.compile(r"<sodipodi:namedview[^>]*/>")
|
|
16
|
-
_RE_SODIPODI_FULL = re.compile(r"<sodipodi:namedview[^>]*>.*?</sodipodi:namedview>", re.DOTALL)
|
|
17
|
-
_RE_METADATA = re.compile(r"<metadata[^>]*>.*?</metadata>", re.DOTALL)
|
|
18
|
-
_RE_WIDTH = re.compile(r'width="[^"]*"')
|
|
19
|
-
_RE_HEIGHT = re.compile(r'height="[^"]*"')
|
|
16
|
+
_RE_XML_DECL: Final[Pattern[str]] = re.compile(r"<\?xml[^?]*\?>")
|
|
17
|
+
_RE_SODIPODI_SELF: Final[Pattern[str]] = re.compile(r"<sodipodi:namedview[^>]*/>")
|
|
18
|
+
_RE_SODIPODI_FULL: Final[Pattern[str]] = re.compile(r"<sodipodi:namedview[^>]*>.*?</sodipodi:namedview>", re.DOTALL)
|
|
19
|
+
_RE_METADATA: Final[Pattern[str]] = re.compile(r"<metadata[^>]*>.*?</metadata>", re.DOTALL)
|
|
20
|
+
_RE_WIDTH: Final[Pattern[str]] = re.compile(r'width="[^"]*"')
|
|
21
|
+
_RE_HEIGHT: Final[Pattern[str]] = re.compile(r'height="[^"]*"')
|
|
20
22
|
# Pattern to find IDs in SVGs
|
|
21
|
-
_RE_ID = re.compile(r'id="([^"]+)"')
|
|
23
|
+
_RE_ID: Final[Pattern[str]] = re.compile(r'id="([^"]+)"')
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@functools.lru_cache(maxsize=128)
|
|
27
|
+
def _load_svg_from_package(asset_name: str, theme: str) -> str:
|
|
28
|
+
"""Load SVG content from package resources with caching.
|
|
29
|
+
|
|
30
|
+
This is a module-level cached function to avoid repeated file I/O
|
|
31
|
+
for the same tile assets across multiple renderer instances.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
asset_name: Name of the asset (e.g., '1m', 'back')
|
|
35
|
+
theme: Theme to load ('light' or 'dark')
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Raw SVG content string
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
FileNotFoundError: If the asset doesn't exist
|
|
42
|
+
"""
|
|
43
|
+
assets = importlib.resources.files("pymdownx_mahjong") / "assets" / theme
|
|
44
|
+
svg_file = assets / f"{asset_name}.svg"
|
|
45
|
+
return svg_file.read_text(encoding="utf-8")
|
|
22
46
|
|
|
23
47
|
|
|
24
48
|
class MahjongRenderer:
|
|
@@ -26,9 +50,8 @@ class MahjongRenderer:
|
|
|
26
50
|
|
|
27
51
|
Configuration options:
|
|
28
52
|
theme: 'light', 'dark', or 'auto'
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
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)
|
|
32
55
|
"""
|
|
33
56
|
|
|
34
57
|
DEFAULT_TILE_WIDTH = 45
|
|
@@ -39,19 +62,21 @@ class MahjongRenderer:
|
|
|
39
62
|
def __init__(
|
|
40
63
|
self,
|
|
41
64
|
theme: str = "light",
|
|
42
|
-
css_class: str = "mahjong-hand",
|
|
43
|
-
show_labels: bool = True,
|
|
44
65
|
inline_svg: bool = True,
|
|
45
66
|
assets_path: str | Path | None = None,
|
|
67
|
+
closed_kan_style: str = "outer",
|
|
68
|
+
css_class: str = "mahjong-hand",
|
|
46
69
|
) -> None:
|
|
47
70
|
"""Initialize the renderer.
|
|
48
71
|
|
|
49
72
|
Args:
|
|
50
73
|
theme: Color theme ('light', 'dark', or 'auto')
|
|
51
|
-
css_class: CSS class for the container element
|
|
52
|
-
show_labels: Show tile names as title attributes
|
|
53
74
|
inline_svg: Inline SVG content vs img tags
|
|
54
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)
|
|
55
80
|
"""
|
|
56
81
|
self.theme = theme
|
|
57
82
|
self.tile_width = self.DEFAULT_TILE_WIDTH
|
|
@@ -59,10 +84,9 @@ class MahjongRenderer:
|
|
|
59
84
|
self.tile_gap = self.DEFAULT_TILE_GAP
|
|
60
85
|
self.meld_gap = self.DEFAULT_MELD_GAP
|
|
61
86
|
self.css_class = css_class
|
|
62
|
-
self.show_labels = show_labels
|
|
63
87
|
self.inline_svg = inline_svg
|
|
64
88
|
self.assets_path = Path(assets_path) if assets_path else None
|
|
65
|
-
self.
|
|
89
|
+
self.closed_kan_style = closed_kan_style
|
|
66
90
|
self._svg_id_counter = 0
|
|
67
91
|
|
|
68
92
|
def render(
|
|
@@ -186,7 +210,7 @@ class MahjongRenderer:
|
|
|
186
210
|
classes.append("mahjong-tile-added")
|
|
187
211
|
|
|
188
212
|
class_str = " ".join(classes)
|
|
189
|
-
title_attr = f' title="{info.display_name}"'
|
|
213
|
+
title_attr = f' title="{info.display_name}"'
|
|
190
214
|
|
|
191
215
|
if self.inline_svg:
|
|
192
216
|
svg_content = self._get_themed_svg_content(info)
|
|
@@ -248,9 +272,18 @@ class MahjongRenderer:
|
|
|
248
272
|
parts.append(self._render_tile(meld.tiles[2]))
|
|
249
273
|
else:
|
|
250
274
|
for i, tile in enumerate(meld.tiles):
|
|
251
|
-
# For closed kan, show back tiles
|
|
252
|
-
if meld.meld_type == MeldType.KAN_CLOSED
|
|
253
|
-
|
|
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))
|
|
254
287
|
else:
|
|
255
288
|
parts.append(self._render_tile(tile))
|
|
256
289
|
|
|
@@ -290,17 +323,12 @@ class MahjongRenderer:
|
|
|
290
323
|
SVG content string with unique IDs
|
|
291
324
|
"""
|
|
292
325
|
theme = theme or (self.theme if self.theme != "auto" else "light")
|
|
293
|
-
cache_key = (theme, info.asset_name)
|
|
294
326
|
|
|
295
|
-
#
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
# Process but don't add unique IDs yet - cache the base processed version
|
|
299
|
-
svg_content = self._process_svg(svg_content, unique_prefix=None)
|
|
300
|
-
self._svg_cache[cache_key] = svg_content
|
|
327
|
+
# Load and process SVG (package assets use module-level LRU cache)
|
|
328
|
+
svg_content = self._load_svg(info, theme)
|
|
329
|
+
svg_content = self._process_svg(svg_content)
|
|
301
330
|
|
|
302
|
-
#
|
|
303
|
-
svg_content = self._svg_cache[cache_key]
|
|
331
|
+
# Make IDs unique for this instance
|
|
304
332
|
self._svg_id_counter += 1
|
|
305
333
|
return self._make_ids_unique(svg_content, f"mj{self._svg_id_counter}_")
|
|
306
334
|
|
|
@@ -342,32 +370,28 @@ class MahjongRenderer:
|
|
|
342
370
|
"""
|
|
343
371
|
theme = theme or (self.theme if self.theme != "auto" else "light")
|
|
344
372
|
|
|
345
|
-
# Try custom assets path first
|
|
373
|
+
# Try custom assets path first (not cached since it's user-specific)
|
|
346
374
|
if self.assets_path:
|
|
347
375
|
svg_path = self.assets_path / theme / f"{info.asset_name}.svg"
|
|
348
376
|
if svg_path.exists():
|
|
349
377
|
return svg_path.read_text(encoding="utf-8")
|
|
350
378
|
|
|
351
|
-
# Fall back to package assets
|
|
379
|
+
# Fall back to package assets (uses module-level LRU cache)
|
|
352
380
|
try:
|
|
353
|
-
|
|
354
|
-
svg_file = assets / f"{info.asset_name}.svg"
|
|
355
|
-
return svg_file.read_text(encoding="utf-8")
|
|
381
|
+
return _load_svg_from_package(info.asset_name, theme)
|
|
356
382
|
except (FileNotFoundError, TypeError):
|
|
357
383
|
# Return a placeholder SVG
|
|
358
384
|
return self._placeholder_svg(info)
|
|
359
385
|
|
|
360
|
-
def _process_svg(self, svg_content: str
|
|
386
|
+
def _process_svg(self, svg_content: str) -> str:
|
|
361
387
|
"""Process SVG content for inline use.
|
|
362
388
|
|
|
363
389
|
- Removes XML declaration
|
|
364
390
|
- Removes unnecessary metadata
|
|
365
391
|
- Adds sizing attributes
|
|
366
|
-
- Makes IDs unique to avoid conflicts when multiple SVGs are on the same page
|
|
367
392
|
|
|
368
393
|
Args:
|
|
369
394
|
svg_content: Raw SVG content
|
|
370
|
-
unique_prefix: Optional prefix to make IDs unique
|
|
371
395
|
|
|
372
396
|
Returns:
|
|
373
397
|
Processed SVG content
|
|
@@ -384,10 +408,6 @@ class MahjongRenderer:
|
|
|
384
408
|
svg_content = _RE_WIDTH.sub(f'width="{self.tile_width}"', svg_content, count=1)
|
|
385
409
|
svg_content = _RE_HEIGHT.sub(f'height="{self.tile_height}"', svg_content, count=1)
|
|
386
410
|
|
|
387
|
-
# Make IDs unique if prefix is provided
|
|
388
|
-
if unique_prefix:
|
|
389
|
-
svg_content = self._make_ids_unique(svg_content, unique_prefix)
|
|
390
|
-
|
|
391
411
|
return svg_content.strip()
|
|
392
412
|
|
|
393
413
|
def _make_ids_unique(self, svg_content: str, prefix: str) -> str:
|
|
@@ -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
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import NamedTuple
|
|
5
|
+
from typing import Final, NamedTuple
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class TileInfo(NamedTuple):
|
|
@@ -12,7 +12,7 @@ class TileInfo(NamedTuple):
|
|
|
12
12
|
display_name: str
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
TILE_DATABASE: dict[tuple[str, int], TileInfo] = {
|
|
15
|
+
TILE_DATABASE: Final[dict[tuple[str, int], TileInfo]] = {
|
|
16
16
|
# Manzu
|
|
17
17
|
("m", 1): TileInfo("1m", "1 Man"),
|
|
18
18
|
("m", 2): TileInfo("2m", "2 Man"),
|
|
@@ -57,7 +57,7 @@ TILE_DATABASE: dict[tuple[str, int], TileInfo] = {
|
|
|
57
57
|
("z", 7): TileInfo("7z", "Red Dragon"),
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
SPECIAL_TILES: dict[str, TileInfo] = {
|
|
60
|
+
SPECIAL_TILES: Final[dict[str, TileInfo]] = {
|
|
61
61
|
"back": TileInfo("back", "Face Down"),
|
|
62
62
|
"blank": TileInfo("blank", "Blank"),
|
|
63
63
|
"front": TileInfo("front", "Front"),
|
|
@@ -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
|
|