mkdocs-chess-plugin 0.1.0__py3-none-any.whl
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.
- mkdocs_chess_plugin/__init__.py +86 -0
- mkdocs_chess_plugin/assets/board.css +205 -0
- mkdocs_chess_plugin/assets/board.mjs +565 -0
- mkdocs_chess_plugin/assets/loader.js +13 -0
- mkdocs_chess_plugin/assets/vendor/chess-1.4.0.mjs +3367 -0
- mkdocs_chess_plugin/assets/vendor/chess.js-LICENSE.txt +23 -0
- mkdocs_chess_plugin/assets/xiangqi-rules.mjs +122 -0
- mkdocs_chess_plugin/markdown.py +301 -0
- mkdocs_chess_plugin/themes.py +147 -0
- mkdocs_chess_plugin-0.1.0.dist-info/METADATA +125 -0
- mkdocs_chess_plugin-0.1.0.dist-info/RECORD +14 -0
- mkdocs_chess_plugin-0.1.0.dist-info/WHEEL +5 -0
- mkdocs_chess_plugin-0.1.0.dist-info/entry_points.txt +2 -0
- mkdocs_chess_plugin-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Install and publish the Markdown extension and its self-contained assets."""
|
|
2
|
+
|
|
3
|
+
from importlib.resources import files
|
|
4
|
+
from html import escape
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
from mkdocs.config import config_options
|
|
9
|
+
from mkdocs.exceptions import PluginError
|
|
10
|
+
from mkdocs.plugins import BasePlugin
|
|
11
|
+
from mkdocs.structure.files import File
|
|
12
|
+
from mkdocs.utils import get_relative_url
|
|
13
|
+
|
|
14
|
+
from .markdown import BOARD_TYPES, BoardExtension
|
|
15
|
+
from .themes import ThemeCatalog
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
ASSET_PREFIX = "assets/mkdocs-chess-plugin/"
|
|
19
|
+
THEME_HREF = re.compile(r'href="fen-board-asset:(themes/[a-z0-9-]+/[A-Za-z0-9._-]+)"')
|
|
20
|
+
ASSETS = (
|
|
21
|
+
"board.css", "loader.js", "board.mjs", "xiangqi-rules.mjs",
|
|
22
|
+
"vendor/chess-1.4.0.mjs", "vendor/chess.js-LICENSE.txt",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ChessPlugin(BasePlugin):
|
|
27
|
+
config_scheme = (
|
|
28
|
+
("themes", config_options.Type(dict, default={})),
|
|
29
|
+
("defaults", config_options.Type(dict, default={})),
|
|
30
|
+
("sounds", config_options.Type(dict, default={})),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def on_config(self, config):
|
|
34
|
+
self.catalog = ThemeCatalog()
|
|
35
|
+
root = Path(config.config_file_path).parent if config.config_file_path else Path.cwd()
|
|
36
|
+
self.catalog.load(root, self.config.get("themes", {}), self.config.get("defaults", {}), BOARD_TYPES)
|
|
37
|
+
self.sounds = {}
|
|
38
|
+
for event, source in self.config.get("sounds", {}).items():
|
|
39
|
+
if event not in ("move", "capture") or not isinstance(source, str):
|
|
40
|
+
raise PluginError("chess sounds accepts move and capture file paths")
|
|
41
|
+
path = root / source
|
|
42
|
+
suffix = path.suffix.lower()
|
|
43
|
+
if suffix not in (".mp3", ".ogg", ".wav") or not path.is_file():
|
|
44
|
+
raise PluginError(f"chess sound {event!r} must be an existing MP3, Ogg or WAV file: {source}")
|
|
45
|
+
self.sounds[event] = (event + suffix, path.resolve())
|
|
46
|
+
extensions = config.markdown_extensions
|
|
47
|
+
for extension in ("admonition", "pymdownx.details"):
|
|
48
|
+
if extension not in extensions:
|
|
49
|
+
extensions.append(extension)
|
|
50
|
+
extensions[:] = [extension for extension in extensions if not isinstance(extension, BoardExtension)]
|
|
51
|
+
extensions.append(BoardExtension(self.catalog, {event: name for event, (name, _) in self.sounds.items()}))
|
|
52
|
+
|
|
53
|
+
for assets, name in ((config.extra_css, "board.css"), (config.extra_javascript, "loader.js")):
|
|
54
|
+
uri = ASSET_PREFIX + name
|
|
55
|
+
if not any(str(asset) == uri for asset in assets):
|
|
56
|
+
assets.append(uri)
|
|
57
|
+
return config
|
|
58
|
+
|
|
59
|
+
def on_files(self, site_files, config):
|
|
60
|
+
assets = files(__package__).joinpath("assets")
|
|
61
|
+
for name in ASSETS:
|
|
62
|
+
uri = ASSET_PREFIX + name
|
|
63
|
+
if uri in site_files.src_uris:
|
|
64
|
+
raise PluginError(f"chess asset conflicts with an existing file: {uri}")
|
|
65
|
+
content = assets.joinpath(*name.split("/")).read_bytes()
|
|
66
|
+
site_files.append(File.generated(config, uri, content=content))
|
|
67
|
+
sound_assets = {"sounds/" + name: path for name, path in self.sounds.values()}
|
|
68
|
+
for name, path in {**self.catalog.assets, **sound_assets}.items():
|
|
69
|
+
uri = ASSET_PREFIX + name
|
|
70
|
+
if uri in site_files.src_uris:
|
|
71
|
+
raise PluginError(f"chess asset conflicts with an existing file: {uri}")
|
|
72
|
+
site_files.append(File.generated(config, uri, abs_src_path=str(path)))
|
|
73
|
+
return site_files
|
|
74
|
+
|
|
75
|
+
def on_page_content(self, html, page, **kwargs):
|
|
76
|
+
if not self.catalog.assets:
|
|
77
|
+
return html
|
|
78
|
+
|
|
79
|
+
def resolve(match):
|
|
80
|
+
name = match.group(1)
|
|
81
|
+
if name not in self.catalog.assets:
|
|
82
|
+
return match.group()
|
|
83
|
+
uri = get_relative_url(ASSET_PREFIX + name, page.url)
|
|
84
|
+
return f'href="{escape(uri, quote=True)}"'
|
|
85
|
+
|
|
86
|
+
return THEME_HREF.sub(resolve, html)
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
.fen-board,
|
|
2
|
+
.md-typeset .fen-board {
|
|
3
|
+
border: 1px solid #ab8350;
|
|
4
|
+
border-radius: .35em;
|
|
5
|
+
padding: 0 .8em;
|
|
6
|
+
margin: 1em 0;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.fen-board > :is(summary, .admonition-title),
|
|
10
|
+
.md-typeset .fen-board > :is(summary, .admonition-title) {
|
|
11
|
+
position: relative;
|
|
12
|
+
padding: .55em 2.1em .55em 2.6em;
|
|
13
|
+
background: #ab83501a;
|
|
14
|
+
font-weight: 700;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.fen-board > summary { cursor: pointer; }
|
|
18
|
+
.fen-board > summary::marker { content: ''; }
|
|
19
|
+
.fen-board > summary::-webkit-details-marker { display: none; }
|
|
20
|
+
.fen-board > summary::after,
|
|
21
|
+
.md-typeset .fen-board > summary::after {
|
|
22
|
+
content: '';
|
|
23
|
+
position: absolute;
|
|
24
|
+
top: 50%;
|
|
25
|
+
left: auto;
|
|
26
|
+
right: .85em;
|
|
27
|
+
width: .48em;
|
|
28
|
+
height: .48em;
|
|
29
|
+
border-right: .13em solid currentColor;
|
|
30
|
+
border-bottom: .13em solid currentColor;
|
|
31
|
+
background: none;
|
|
32
|
+
mask: none;
|
|
33
|
+
transform: translateY(-70%) rotate(45deg);
|
|
34
|
+
}
|
|
35
|
+
.fen-board[open] > summary::after,
|
|
36
|
+
.md-typeset .fen-board[open] > summary::after { transform: translateY(-25%) rotate(225deg); }
|
|
37
|
+
.md-typeset .fen-board > :is(summary, .admonition-title)::before { content: none; }
|
|
38
|
+
|
|
39
|
+
.fen-board-icon {
|
|
40
|
+
position: absolute;
|
|
41
|
+
top: 50%;
|
|
42
|
+
left: .65em;
|
|
43
|
+
width: 1.55em;
|
|
44
|
+
height: 1.55em;
|
|
45
|
+
transform: translateY(-50%);
|
|
46
|
+
}
|
|
47
|
+
.fen-board[data-turn="w"] > :first-child > [data-color="b"],
|
|
48
|
+
.fen-board[data-turn="b"] > :first-child > [data-color="w"] {
|
|
49
|
+
display: none;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.fen-board > .fen-board-figure,
|
|
53
|
+
.md-typeset .fen-board > .fen-board-figure {
|
|
54
|
+
display: block;
|
|
55
|
+
width: 100%;
|
|
56
|
+
max-width: 480px;
|
|
57
|
+
margin: 1em auto;
|
|
58
|
+
}
|
|
59
|
+
.fen-board-surface {
|
|
60
|
+
position: relative;
|
|
61
|
+
border-radius: 6px;
|
|
62
|
+
overflow: hidden;
|
|
63
|
+
outline: 1px solid #c5a87c;
|
|
64
|
+
touch-action: manipulation;
|
|
65
|
+
}
|
|
66
|
+
.fen-board-surface > svg {
|
|
67
|
+
display: block;
|
|
68
|
+
width: 100%;
|
|
69
|
+
height: auto;
|
|
70
|
+
background: #e9d7b6;
|
|
71
|
+
color: #866646;
|
|
72
|
+
}
|
|
73
|
+
.fen-board-figure[data-game="chess"] .fen-board-surface { outline-color: #6a8176; }
|
|
74
|
+
.fen-board-figure[data-game="chess"] .fen-board-surface > svg { background: #dce5dc; color: #32453e; }
|
|
75
|
+
.fen-board-figure[data-board-art] .fen-board-surface { outline: 0; border-radius: 0; }
|
|
76
|
+
.fen-board-figure[data-board-art] .fen-board-surface > svg { background: transparent; }
|
|
77
|
+
.fen-board-tile-light { fill: #ede8d6; }
|
|
78
|
+
.fen-board-tile-dark { fill: #789486; }
|
|
79
|
+
.fen-board-xiang-grid { fill: none; stroke: currentColor; stroke-width: 1.2; }
|
|
80
|
+
.fen-board-river, .fen-board-coords {
|
|
81
|
+
fill: currentColor;
|
|
82
|
+
text-anchor: middle;
|
|
83
|
+
font-size: 14px;
|
|
84
|
+
}
|
|
85
|
+
.fen-board-river, .fen-board-character { font-family: STKaiti, KaiTi, "Noto Serif SC", serif; }
|
|
86
|
+
.fen-board-river { font-size: 21px; }
|
|
87
|
+
.fen-board-xiang-light { color: #a52c24; }
|
|
88
|
+
.fen-board-xiang-dark { color: #28343a; }
|
|
89
|
+
.fen-board-disc { fill: #f5dfb7; stroke: #ab8350; }
|
|
90
|
+
.fen-board-ring { fill: none; stroke: currentColor; stroke-width: .8; }
|
|
91
|
+
.fen-board-character {
|
|
92
|
+
fill: currentColor;
|
|
93
|
+
text-anchor: middle;
|
|
94
|
+
dominant-baseline: central;
|
|
95
|
+
font-size: 30px;
|
|
96
|
+
font-weight: bold;
|
|
97
|
+
}
|
|
98
|
+
.fen-board-chess-light, .fen-board-chess-dark {
|
|
99
|
+
stroke-width: 1.65;
|
|
100
|
+
stroke-linecap: round;
|
|
101
|
+
stroke-linejoin: round;
|
|
102
|
+
paint-order: stroke fill;
|
|
103
|
+
}
|
|
104
|
+
.fen-board-chess-light { fill: #fffdf1; stroke: #253432; }
|
|
105
|
+
.fen-board-chess-dark { fill: #233430; stroke: #e2e9d9; }
|
|
106
|
+
|
|
107
|
+
.fen-board-square {
|
|
108
|
+
appearance: none;
|
|
109
|
+
position: absolute;
|
|
110
|
+
display: grid;
|
|
111
|
+
place-items: center;
|
|
112
|
+
transform: translate(-50%, -50%);
|
|
113
|
+
border: 0;
|
|
114
|
+
padding: 0;
|
|
115
|
+
background: transparent;
|
|
116
|
+
cursor: pointer;
|
|
117
|
+
}
|
|
118
|
+
.fen-board-figure[data-game="xiang"] .fen-board-square { border-radius: 50%; }
|
|
119
|
+
.fen-board-square.fen-board-draggable {
|
|
120
|
+
cursor: grab;
|
|
121
|
+
touch-action: none;
|
|
122
|
+
user-select: none;
|
|
123
|
+
-webkit-touch-callout: none;
|
|
124
|
+
}
|
|
125
|
+
.fen-board-dragging,
|
|
126
|
+
.fen-board-dragging .fen-board-square { cursor: grabbing; }
|
|
127
|
+
.fen-board-drag-piece { pointer-events: none; }
|
|
128
|
+
.fen-board-arrows,
|
|
129
|
+
.fen-board-arrow-draft { pointer-events: none; }
|
|
130
|
+
.fen-board-arrow-draft { opacity: .65; }
|
|
131
|
+
.fen-board-arrow-ink { color: #00c83d; fill: currentColor; opacity: .84; }
|
|
132
|
+
.fen-board-arrow[data-side="b"] .fen-board-arrow-ink { color: #8e3dcc; }
|
|
133
|
+
.fen-board-arrow-shaft { stroke: currentColor; stroke-linecap: round; }
|
|
134
|
+
.fen-board-arrow-origin { fill: none; stroke: currentColor; }
|
|
135
|
+
.fen-board-arrow-badge { fill: #f95d0f; }
|
|
136
|
+
.fen-board-arrow text {
|
|
137
|
+
fill: #fff;
|
|
138
|
+
text-anchor: middle;
|
|
139
|
+
dominant-baseline: central;
|
|
140
|
+
font-family: Arial, sans-serif;
|
|
141
|
+
font-weight: 600;
|
|
142
|
+
}
|
|
143
|
+
.fen-board-square.fen-board-last { background: #e2a63142; }
|
|
144
|
+
.fen-board-figure[data-game="xiang"] .fen-board-square.fen-board-last { background: transparent; }
|
|
145
|
+
.fen-board-figure[data-game="xiang"] .fen-board-square.fen-board-last::before {
|
|
146
|
+
content: '';
|
|
147
|
+
position: absolute;
|
|
148
|
+
top: 50%;
|
|
149
|
+
left: 50%;
|
|
150
|
+
transform: translate(-50%, -50%);
|
|
151
|
+
box-sizing: border-box;
|
|
152
|
+
aspect-ratio: 1;
|
|
153
|
+
border: 1.5px solid #fffe;
|
|
154
|
+
border-radius: 50%;
|
|
155
|
+
pointer-events: none;
|
|
156
|
+
}
|
|
157
|
+
.fen-board-figure[data-game="xiang"] .fen-board-last-from::before {
|
|
158
|
+
width: 42%;
|
|
159
|
+
background: radial-gradient(circle, #fff 0 15%, #fffc 25%, #fff0 48%);
|
|
160
|
+
box-shadow: 0 0 4px #fff9;
|
|
161
|
+
}
|
|
162
|
+
.fen-board-figure[data-game="xiang"] .fen-board-last-to::before {
|
|
163
|
+
width: 94%;
|
|
164
|
+
box-shadow: 0 0 4px #fffc, inset 0 0 3px #fff9;
|
|
165
|
+
}
|
|
166
|
+
.fen-board-square[aria-pressed="true"] { box-shadow: inset 0 0 0 3px #036554; }
|
|
167
|
+
.fen-board-square:focus-visible { outline: 3px solid #036554; outline-offset: -3px; z-index: 1; }
|
|
168
|
+
.fen-board-target::after {
|
|
169
|
+
content: '';
|
|
170
|
+
width: 28%;
|
|
171
|
+
aspect-ratio: 1;
|
|
172
|
+
border-radius: 50%;
|
|
173
|
+
background: #096051b8;
|
|
174
|
+
box-shadow: 0 0 0 1.5px #fffdf1;
|
|
175
|
+
}
|
|
176
|
+
.fen-board-controls {
|
|
177
|
+
display: flex;
|
|
178
|
+
flex-wrap: wrap;
|
|
179
|
+
align-items: center;
|
|
180
|
+
gap: .45em;
|
|
181
|
+
margin: .65em 0 0;
|
|
182
|
+
font-size: .9em;
|
|
183
|
+
}
|
|
184
|
+
.fen-board-controls[hidden] { display: none; }
|
|
185
|
+
.fen-board-controls > button {
|
|
186
|
+
color: inherit;
|
|
187
|
+
font: inherit;
|
|
188
|
+
border: 1px solid #ab8350;
|
|
189
|
+
border-radius: .35em;
|
|
190
|
+
background: #ab83501a;
|
|
191
|
+
padding: .32em .65em;
|
|
192
|
+
cursor: pointer;
|
|
193
|
+
}
|
|
194
|
+
.fen-board-controls > button:disabled { opacity: .45; cursor: default; }
|
|
195
|
+
.fen-board-status { margin: 0 .25em; }
|
|
196
|
+
.fen-board-fen-export {
|
|
197
|
+
flex-basis: 100%;
|
|
198
|
+
min-width: 0;
|
|
199
|
+
padding: .32em .65em;
|
|
200
|
+
border: 1px solid #ab8350;
|
|
201
|
+
border-radius: .35em;
|
|
202
|
+
color: inherit;
|
|
203
|
+
font: inherit;
|
|
204
|
+
background: transparent;
|
|
205
|
+
}
|