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.
@@ -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
+ }