zensical 0.0.3__cp310-abi3-musllinux_1_2_i686.whl → 0.0.9__cp310-abi3-musllinux_1_2_i686.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.
Potentially problematic release.
This version of zensical might be problematic. Click here for more details.
- zensical/__init__.py +2 -2
- zensical/__main__.py +28 -0
- zensical/bootstrap/.github/workflows/docs.yml +9 -3
- zensical/bootstrap/zensical.toml +21 -21
- zensical/config.py +88 -39
- zensical/extensions/__init__.py +2 -2
- zensical/extensions/emoji.py +2 -2
- zensical/extensions/links.py +20 -7
- zensical/extensions/preview.py +2 -2
- zensical/extensions/search.py +2 -2
- zensical/extensions/utilities/__init__.py +2 -2
- zensical/extensions/utilities/filter.py +2 -2
- zensical/main.py +11 -9
- zensical/markdown.py +4 -4
- zensical/templates/assets/javascripts/bundle.21aa498e.min.js +3 -0
- zensical/templates/assets/stylesheets/classic/{main.c5ffb0a9.min.css → main.6eec86b3.min.css} +1 -1
- zensical/templates/assets/stylesheets/modern/main.2644c6b7.min.css +1 -0
- zensical/templates/base.html +3 -3
- zensical/templates/partials/javascripts/base.html +1 -1
- zensical/templates/partials/nav-item.html +1 -1
- zensical/templates/partials/search.html +3 -1
- zensical/zensical.abi3.so +0 -0
- zensical/zensical.pyi +2 -2
- {zensical-0.0.3.dist-info → zensical-0.0.9.dist-info}/METADATA +4 -3
- {zensical-0.0.3.dist-info → zensical-0.0.9.dist-info}/RECORD +28 -27
- {zensical-0.0.3.dist-info → zensical-0.0.9.dist-info}/WHEEL +1 -1
- {zensical-0.0.3.dist-info → zensical-0.0.9.dist-info}/licenses/LICENSE.md +1 -1
- zensical/templates/assets/javascripts/bundle.3c403d54.min.js +0 -3
- zensical/templates/assets/stylesheets/modern/main.1357c24d.min.css +0 -1
- {zensical-0.0.3.dist-info → zensical-0.0.9.dist-info}/entry_points.txt +0 -0
zensical/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# Copyright (c) Zensical
|
|
1
|
+
# Copyright (c) 2025 Zensical and contributors
|
|
2
2
|
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
|
-
# Third-party contributions licensed under
|
|
4
|
+
# Third-party contributions licensed under DCO
|
|
5
5
|
|
|
6
6
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
# of this software and associated documentation files (the "Software"), to
|
zensical/__main__.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Copyright (c) 2025 Zensical and contributors
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
# Third-party contributions licensed under DCO
|
|
5
|
+
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
# of this software and associated documentation files (the "Software"), to
|
|
8
|
+
# deal in the Software without restriction, including without limitation the
|
|
9
|
+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
10
|
+
# sell copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
# furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
# The above copyright notice and this permission notice shall be included in
|
|
14
|
+
# all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
21
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
22
|
+
# IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
# Allow running as a script, with `python -m zensical`.
|
|
25
|
+
from zensical.main import cli
|
|
26
|
+
|
|
27
|
+
if __name__ == "__main__": # pragma: no cover
|
|
28
|
+
cli()
|
|
@@ -1,21 +1,27 @@
|
|
|
1
|
-
name:
|
|
1
|
+
name: Documentation
|
|
2
2
|
on:
|
|
3
3
|
push:
|
|
4
4
|
branches:
|
|
5
5
|
- master
|
|
6
6
|
- main
|
|
7
7
|
permissions:
|
|
8
|
-
contents:
|
|
8
|
+
contents: read
|
|
9
|
+
pages: write
|
|
10
|
+
id-token: write
|
|
9
11
|
jobs:
|
|
10
12
|
deploy:
|
|
13
|
+
environment:
|
|
14
|
+
name: github-pages
|
|
15
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
11
16
|
runs-on: ubuntu-latest
|
|
12
17
|
steps:
|
|
18
|
+
- uses: actions/configure-pages@v5
|
|
13
19
|
- uses: actions/checkout@v5
|
|
14
20
|
- uses: actions/setup-python@v5
|
|
15
21
|
with:
|
|
16
22
|
python-version: 3.x
|
|
17
23
|
- run: pip install zensical
|
|
18
|
-
- run: zensical build
|
|
24
|
+
- run: zensical build --clean
|
|
19
25
|
- uses: actions/upload-pages-artifact@v4
|
|
20
26
|
with:
|
|
21
27
|
path: site
|
zensical/bootstrap/zensical.toml
CHANGED
|
@@ -49,6 +49,24 @@ Copyright © 2025 The authors
|
|
|
49
49
|
# { "Markdown in 5min" = "markdown.md" },
|
|
50
50
|
# ]
|
|
51
51
|
|
|
52
|
+
# With the "extra_css" option you can add your own CSS styling to customize
|
|
53
|
+
# your Zensical project according to your needs. You can add any number of
|
|
54
|
+
# CSS files.
|
|
55
|
+
#
|
|
56
|
+
# The path provided should be relative to the "docs_dir".
|
|
57
|
+
#
|
|
58
|
+
# Read more: https://zensical.org/docs/customization/#additional-css
|
|
59
|
+
#
|
|
60
|
+
#extra_css = ["assets/stylesheets/extra.css"]
|
|
61
|
+
|
|
62
|
+
# With the `extra_javascript` option you can add your own JavaScript to your
|
|
63
|
+
# project to customize the behavior according to your needs.
|
|
64
|
+
#
|
|
65
|
+
# The path provided should be relative to the "docs_dir".
|
|
66
|
+
#
|
|
67
|
+
# Read more: https://zensical.org/docs/customization/#additional-javascript
|
|
68
|
+
#extra_javascript = ["assets/javascript/extra.js"]
|
|
69
|
+
|
|
52
70
|
# ----------------------------------------------------------------------------
|
|
53
71
|
# Section for configuring theme options
|
|
54
72
|
# ----------------------------------------------------------------------------
|
|
@@ -176,7 +194,7 @@ features = [
|
|
|
176
194
|
# In order to provide a better user experience on slow connections when
|
|
177
195
|
# using instant navigation, a progress indicator can be enabled.
|
|
178
196
|
# https://zensical.org/docs/setup/navigation/#progress-indicator
|
|
179
|
-
#"navigation.instant.progress"
|
|
197
|
+
#"navigation.instant.progress",
|
|
180
198
|
|
|
181
199
|
# When navigation paths are activated, a breadcrumb navigation is rendered
|
|
182
200
|
# above the title of each page
|
|
@@ -222,32 +240,14 @@ features = [
|
|
|
222
240
|
# When anchor following for the table of contents is enabled, the sidebar
|
|
223
241
|
# is automatically scrolled so that the active anchor is always visible.
|
|
224
242
|
# https://zensical.org/docs/setup/navigation/#anchor-following
|
|
225
|
-
# "toc.follow"
|
|
243
|
+
# "toc.follow",
|
|
226
244
|
|
|
227
245
|
# When navigation integration for the table of contents is enabled, it is
|
|
228
246
|
# always rendered as part of the navigation sidebar on the left.
|
|
229
247
|
# https://zensical.org/docs/setup/navigation/#navigation-integration
|
|
230
|
-
#"toc.integrate"
|
|
248
|
+
#"toc.integrate",
|
|
231
249
|
]
|
|
232
250
|
|
|
233
|
-
# With the "extra_css" option you can add your own CSS styling to customize
|
|
234
|
-
# your Zensical project according to your needs. You can add any number of
|
|
235
|
-
# CSS files.
|
|
236
|
-
#
|
|
237
|
-
# The path provided should be relative to the "docs_dir".
|
|
238
|
-
#
|
|
239
|
-
# Read more: https://zensical.org/docs/customization/#additional-css
|
|
240
|
-
#
|
|
241
|
-
#extra_css = ["assets/stylesheets/extra.css"]
|
|
242
|
-
|
|
243
|
-
# With the `extra_javascript` option you can add your own JavaScript to your
|
|
244
|
-
# project to customize the behavior according to your needs.
|
|
245
|
-
#
|
|
246
|
-
# The path provided should be relative to the "docs_dir".
|
|
247
|
-
#
|
|
248
|
-
# Read more: https://zensical.org/docs/customization/#additional-javascript
|
|
249
|
-
#extra_javascript = ["assets/javascript/extra.js"]
|
|
250
|
-
|
|
251
251
|
# ----------------------------------------------------------------------------
|
|
252
252
|
# In the "palette" subsection you can configure options for the color scheme.
|
|
253
253
|
# You can configure different color # schemes, e.g., to turn on dark mode,
|
zensical/config.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# Copyright (c) Zensical
|
|
1
|
+
# Copyright (c) 2025 Zensical and contributors
|
|
2
2
|
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
|
-
# Third-party contributions licensed under
|
|
4
|
+
# Third-party contributions licensed under DCO
|
|
5
5
|
|
|
6
6
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
# of this software and associated documentation files (the "Software"), to
|
|
@@ -23,14 +23,19 @@
|
|
|
23
23
|
|
|
24
24
|
from __future__ import annotations
|
|
25
25
|
|
|
26
|
+
import hashlib
|
|
26
27
|
import importlib
|
|
27
28
|
import os
|
|
28
|
-
import
|
|
29
|
+
import pickle
|
|
29
30
|
import yaml
|
|
30
31
|
|
|
32
|
+
try:
|
|
33
|
+
import tomllib
|
|
34
|
+
except ModuleNotFoundError:
|
|
35
|
+
import tomli as tomllib # type: ignore
|
|
36
|
+
|
|
31
37
|
from click import ClickException
|
|
32
38
|
from deepmerge import always_merger
|
|
33
|
-
from functools import partial
|
|
34
39
|
from typing import Any, IO
|
|
35
40
|
from yaml import BaseLoader, Loader, YAMLError
|
|
36
41
|
from yaml.constructor import ConstructorError
|
|
@@ -73,7 +78,9 @@ def parse_config(path: str) -> dict:
|
|
|
73
78
|
"""
|
|
74
79
|
Parse configuration file.
|
|
75
80
|
"""
|
|
76
|
-
|
|
81
|
+
# Decide by extension; no need to convert to Path
|
|
82
|
+
_, ext = os.path.splitext(path)
|
|
83
|
+
if ext.lower() == ".toml":
|
|
77
84
|
return parse_zensical_config(path)
|
|
78
85
|
else:
|
|
79
86
|
return parse_mkdocs_config(path)
|
|
@@ -107,6 +114,13 @@ def parse_mkdocs_config(path: str) -> dict:
|
|
|
107
114
|
return _CONFIG
|
|
108
115
|
|
|
109
116
|
|
|
117
|
+
def get_config():
|
|
118
|
+
"""
|
|
119
|
+
Return configuration.
|
|
120
|
+
"""
|
|
121
|
+
return _CONFIG
|
|
122
|
+
|
|
123
|
+
|
|
110
124
|
def get_theme_dir() -> str:
|
|
111
125
|
"""
|
|
112
126
|
Return the theme directory.
|
|
@@ -131,12 +145,12 @@ def _apply_defaults(config: dict, path: str) -> dict:
|
|
|
131
145
|
raise ConfigurationError("Missing required setting: site_name")
|
|
132
146
|
|
|
133
147
|
# Set site directory
|
|
134
|
-
config
|
|
148
|
+
set_default(config, "site_dir", "site", str)
|
|
135
149
|
if ".." in config.get("site_dir"):
|
|
136
150
|
raise ConfigurationError("site_dir must not contain '..'")
|
|
137
151
|
|
|
138
152
|
# Set docs directory
|
|
139
|
-
config
|
|
153
|
+
set_default(config, "docs_dir", "docs", str)
|
|
140
154
|
if ".." in config.get("docs_dir"):
|
|
141
155
|
raise ConfigurationError("docs_dir must not contain '..'")
|
|
142
156
|
|
|
@@ -157,16 +171,17 @@ def _apply_defaults(config: dict, path: str) -> dict:
|
|
|
157
171
|
# Set defaults for repository name settings
|
|
158
172
|
repo_url = config.get("repo_url")
|
|
159
173
|
if repo_url and not config.get("repo_name"):
|
|
174
|
+
docs_dir = config.get("docs_dir")
|
|
160
175
|
host = urlparse(repo_url).hostname or ""
|
|
161
176
|
if host == "github.com":
|
|
162
|
-
config
|
|
163
|
-
config
|
|
177
|
+
set_default(config, "repo_name", "GitHub", str)
|
|
178
|
+
set_default(config, "edit_uri", f"edit/master/{docs_dir}", str)
|
|
164
179
|
elif host == "gitlab.com":
|
|
165
|
-
config
|
|
166
|
-
config
|
|
180
|
+
set_default(config, "repo_name", "GitLab", str)
|
|
181
|
+
set_default(config, "edit_uri", f"edit/master/{docs_dir}", str)
|
|
167
182
|
elif host == "bitbucket.org":
|
|
168
|
-
config
|
|
169
|
-
config
|
|
183
|
+
set_default(config, "repo_name", "Bitbucket", str)
|
|
184
|
+
set_default(config, "edit_uri", f"src/default/{docs_dir}", str)
|
|
170
185
|
elif host:
|
|
171
186
|
config["repo_name"] = host.split(".")[0].title()
|
|
172
187
|
|
|
@@ -176,7 +191,7 @@ def _apply_defaults(config: dict, path: str) -> dict:
|
|
|
176
191
|
config["edit_uri"] = edit_uri.rstrip("/")
|
|
177
192
|
|
|
178
193
|
# Set defaults for theme font settings
|
|
179
|
-
theme = config
|
|
194
|
+
theme = set_default(config, "theme", {}, dict)
|
|
180
195
|
if isinstance(theme, str):
|
|
181
196
|
theme = {"name": theme}
|
|
182
197
|
config["theme"] = theme
|
|
@@ -212,7 +227,7 @@ def _apply_defaults(config: dict, path: str) -> dict:
|
|
|
212
227
|
set_default(theme["font"], "code", font["code"], str)
|
|
213
228
|
|
|
214
229
|
# Set defaults for theme icons
|
|
215
|
-
icon = theme
|
|
230
|
+
icon = set_default(theme, "icon", {}, dict)
|
|
216
231
|
set_default(icon, "repo", None, str)
|
|
217
232
|
set_default(icon, "annotation", None, str)
|
|
218
233
|
set_default(icon, "tag", {}, dict)
|
|
@@ -242,7 +257,7 @@ def _apply_defaults(config: dict, path: str) -> dict:
|
|
|
242
257
|
set_default(icon, "next", None, str)
|
|
243
258
|
|
|
244
259
|
# Set defaults for theme admonition icons
|
|
245
|
-
admonition = icon
|
|
260
|
+
admonition = set_default(icon, "admonition", {}, dict)
|
|
246
261
|
set_default(admonition, "note", None, str)
|
|
247
262
|
set_default(admonition, "abstract", None, str)
|
|
248
263
|
set_default(admonition, "info", None, str)
|
|
@@ -277,7 +292,7 @@ def _apply_defaults(config: dict, path: str) -> dict:
|
|
|
277
292
|
set_default(toggle, "name", None, str)
|
|
278
293
|
|
|
279
294
|
# Set defaults for extra settings
|
|
280
|
-
extra = config
|
|
295
|
+
extra = set_default(config, "extra", {}, dict)
|
|
281
296
|
set_default(extra, "homepage", None, str)
|
|
282
297
|
set_default(extra, "scope", None, str)
|
|
283
298
|
set_default(extra, "annotate", {}, dict)
|
|
@@ -374,7 +389,7 @@ def _apply_defaults(config: dict, path: str) -> dict:
|
|
|
374
389
|
"md_in_html": {},
|
|
375
390
|
"toc": {"permalink": True},
|
|
376
391
|
"pymdownx.arithmatex": {"generic": True},
|
|
377
|
-
"pymdownx.betterem": {
|
|
392
|
+
"pymdownx.betterem": {},
|
|
378
393
|
"pymdownx.caret": {},
|
|
379
394
|
"pymdownx.details": {},
|
|
380
395
|
"pymdownx.emoji": {
|
|
@@ -416,18 +431,28 @@ def _apply_defaults(config: dict, path: str) -> dict:
|
|
|
416
431
|
if isinstance(emoji.get("emoji_index"), str):
|
|
417
432
|
emoji["emoji_index"] = _resolve(emoji.get("emoji_index"))
|
|
418
433
|
|
|
419
|
-
# Tabbed extension configuration -
|
|
420
|
-
# function.
|
|
434
|
+
# Tabbed extension configuration - resolve slugification function
|
|
421
435
|
tabbed = config["mdx_configs"].get("pymdownx.tabbed", {})
|
|
422
436
|
if isinstance(tabbed.get("slugify"), dict):
|
|
423
437
|
object = tabbed["slugify"].get("object", "pymdownx.slugs.slugify")
|
|
424
|
-
tabbed["slugify"] =
|
|
425
|
-
|
|
426
|
-
|
|
438
|
+
tabbed["slugify"] = _resolve(object)(**tabbed["slugify"].get("kwds"))
|
|
439
|
+
|
|
440
|
+
# Table of contents extension configuration - resolve slugification function
|
|
441
|
+
toc = config["mdx_configs"]["toc"]
|
|
442
|
+
if isinstance(toc.get("slugify"), dict):
|
|
443
|
+
object = toc["slugify"].get("object", "pymdownx.slugs.slugify")
|
|
444
|
+
toc["slugify"] = _resolve(object)(**toc["slugify"].get("kwds"))
|
|
445
|
+
|
|
446
|
+
# Superfences extension configuration - resolve format function
|
|
447
|
+
superfences = config["mdx_configs"].get("pymdownx.superfences", {})
|
|
448
|
+
for fence in superfences.get("custom_fences", []):
|
|
449
|
+
if isinstance(fence.get("format"), str):
|
|
450
|
+
fence["format"] = _resolve(fence.get("format"))
|
|
427
451
|
|
|
428
452
|
# Ensure the table of contents title is initialized, as it's used inside
|
|
429
453
|
# the template, and the table of contents extension is always defined
|
|
430
454
|
config["mdx_configs"]["toc"].setdefault("title", None)
|
|
455
|
+
config["mdx_configs_hash"] = _hash(mdx_configs)
|
|
431
456
|
|
|
432
457
|
# Convert plugins configuration
|
|
433
458
|
config["plugins"] = _convert_plugins(config.get("plugins", []), config)
|
|
@@ -436,10 +461,13 @@ def _apply_defaults(config: dict, path: str) -> dict:
|
|
|
436
461
|
|
|
437
462
|
def set_default(
|
|
438
463
|
entry: dict, key: str, default: Any, data_type: type = None
|
|
439
|
-
) ->
|
|
464
|
+
) -> any:
|
|
440
465
|
"""
|
|
441
466
|
Set a key to a default value if it isn't set, and optionally cast it to the specified data type.
|
|
442
467
|
"""
|
|
468
|
+
if key in entry and entry[key] is None:
|
|
469
|
+
del entry[key]
|
|
470
|
+
|
|
443
471
|
# Set the default value if the key is not present
|
|
444
472
|
entry.setdefault(key, default)
|
|
445
473
|
|
|
@@ -450,6 +478,17 @@ def set_default(
|
|
|
450
478
|
except (ValueError, TypeError) as e:
|
|
451
479
|
raise ValueError(f"Failed to cast key '{key}' to {data_type}: {e}")
|
|
452
480
|
|
|
481
|
+
# Return the resulting value
|
|
482
|
+
return entry[key]
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def _hash(data: any) -> int:
|
|
486
|
+
"""
|
|
487
|
+
Compute a hash for the given data.
|
|
488
|
+
"""
|
|
489
|
+
hash = hashlib.sha1(pickle.dumps(data))
|
|
490
|
+
return int(hash.hexdigest(), 16) % (2**64)
|
|
491
|
+
|
|
453
492
|
|
|
454
493
|
def _convert_extra(data: dict | list) -> dict | list:
|
|
455
494
|
"""
|
|
@@ -594,7 +633,13 @@ def _convert_markdown_extensions(value: any):
|
|
|
594
633
|
if "pymdownx" in value:
|
|
595
634
|
pymdownx = value.pop("pymdownx")
|
|
596
635
|
for ext, config in pymdownx.items():
|
|
597
|
-
|
|
636
|
+
# Special case for blocks extension, which has another level of
|
|
637
|
+
# nesting. This is the only extension that requires this.
|
|
638
|
+
if ext == "blocks":
|
|
639
|
+
for block, config in config.items():
|
|
640
|
+
value[f"pymdownx.{ext}.{block}"] = config
|
|
641
|
+
else:
|
|
642
|
+
value[f"pymdownx.{ext}"] = config
|
|
598
643
|
|
|
599
644
|
# Extensions can be defined as a dict
|
|
600
645
|
if isinstance(value, dict):
|
|
@@ -640,13 +685,15 @@ def _convert_plugins(value: any, config: dict) -> list:
|
|
|
640
685
|
plugins[item] = {}
|
|
641
686
|
|
|
642
687
|
# Define defaults for search plugin
|
|
643
|
-
search = plugins
|
|
644
|
-
search
|
|
645
|
-
|
|
688
|
+
search = set_default(plugins, "search", {}, dict)
|
|
689
|
+
set_default(search, "enabled", True, bool)
|
|
690
|
+
set_default(
|
|
691
|
+
search, "separator", '[\\s\\-_,:!=\\[\\]()\\\\"`/]+|\\.(?!\\d)', str
|
|
692
|
+
)
|
|
646
693
|
|
|
647
694
|
# Define defaults for offline plugin
|
|
648
|
-
offline = plugins
|
|
649
|
-
offline
|
|
695
|
+
offline = set_default(plugins, "offline", {"enabled": False}, dict)
|
|
696
|
+
set_default(offline, "enabled", True, bool)
|
|
650
697
|
|
|
651
698
|
# Ensure correct resolution of links when viewing the site from the
|
|
652
699
|
# file system by disabling directory URLs
|
|
@@ -658,18 +705,20 @@ def _convert_plugins(value: any, config: dict) -> list:
|
|
|
658
705
|
"iframe-worker" in url for url in config["extra"]["polyfills"]
|
|
659
706
|
):
|
|
660
707
|
script = "https://unpkg.com/iframe-worker/shim"
|
|
661
|
-
config["extra"]["polyfills"].append(
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
708
|
+
config["extra"]["polyfills"].append(
|
|
709
|
+
{
|
|
710
|
+
"path": script,
|
|
711
|
+
"type": "text/javascript",
|
|
712
|
+
"async": False,
|
|
713
|
+
"defer": False,
|
|
714
|
+
}
|
|
715
|
+
)
|
|
667
716
|
|
|
668
717
|
# Now, add another level of indirection, by moving all plugin configuration
|
|
669
718
|
# into a `config` property, making it compatible with Material for MkDocs.
|
|
670
|
-
for name,
|
|
671
|
-
if not isinstance(
|
|
672
|
-
plugins[name] = {"config":
|
|
719
|
+
for name, data in plugins.items():
|
|
720
|
+
if not isinstance(data, dict) or "config" not in data:
|
|
721
|
+
plugins[name] = {"config": data}
|
|
673
722
|
|
|
674
723
|
# Return plugins
|
|
675
724
|
return plugins
|
zensical/extensions/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# Copyright (c) Zensical
|
|
1
|
+
# Copyright (c) 2025 Zensical and contributors
|
|
2
2
|
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
|
-
# Third-party contributions licensed under
|
|
4
|
+
# Third-party contributions licensed under DCO
|
|
5
5
|
|
|
6
6
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
# of this software and associated documentation files (the "Software"), to
|
zensical/extensions/emoji.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# Copyright (c) Zensical
|
|
1
|
+
# Copyright (c) 2025 Zensical and contributors
|
|
2
2
|
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
|
-
# Third-party contributions licensed under
|
|
4
|
+
# Third-party contributions licensed under DCO
|
|
5
5
|
|
|
6
6
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
# of this software and associated documentation files (the "Software"), to
|
zensical/extensions/links.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# Copyright (c) Zensical
|
|
1
|
+
# Copyright (c) 2025 Zensical and contributors
|
|
2
2
|
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
|
-
# Third-party contributions licensed under
|
|
4
|
+
# Third-party contributions licensed under DCO
|
|
5
5
|
|
|
6
6
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
# of this software and associated documentation files (the "Software"), to
|
|
@@ -26,6 +26,7 @@ from __future__ import annotations
|
|
|
26
26
|
from markdown import Extension, Markdown
|
|
27
27
|
from markdown.treeprocessors import Treeprocessor
|
|
28
28
|
from markdown.util import AMP_SUBSTITUTE
|
|
29
|
+
from pathlib import PurePosixPath
|
|
29
30
|
from xml.etree.ElementTree import Element
|
|
30
31
|
from urllib.parse import urlparse
|
|
31
32
|
|
|
@@ -52,7 +53,7 @@ class LinksProcessor(Treeprocessor):
|
|
|
52
53
|
def run(self, root: Element):
|
|
53
54
|
# Now, we determine whether the current page is an index page, as we
|
|
54
55
|
# must apply slightly different handling in case of directory URLs
|
|
55
|
-
current_is_index = self.path
|
|
56
|
+
current_is_index = get_name(self.path) in ("index.md", "README.md")
|
|
56
57
|
for el in root.iter():
|
|
57
58
|
# In case the element has a `href` or `src` attribute, we parse it
|
|
58
59
|
# as an URL, so we can analyze and alter its path
|
|
@@ -81,10 +82,9 @@ class LinksProcessor(Treeprocessor):
|
|
|
81
82
|
if path.endswith(".md"):
|
|
82
83
|
path = path.removesuffix(".md") + ".html"
|
|
83
84
|
if self.use_directory_urls:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
path = path[: -len("README.html")]
|
|
85
|
+
name = get_name(path)
|
|
86
|
+
if name in ("index.html", "README.html"):
|
|
87
|
+
path = path[: -len(name)]
|
|
88
88
|
elif path.endswith(".html"):
|
|
89
89
|
path = path[: -len(".html")] + "/"
|
|
90
90
|
|
|
@@ -124,3 +124,16 @@ class LinksExtension(Extension):
|
|
|
124
124
|
# before they are resolved to URLs.
|
|
125
125
|
processor = LinksProcessor(md, self.path, self.use_directory_urls)
|
|
126
126
|
md.treeprocessors.register(processor, "relpath", 0)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# -----------------------------------------------------------------------------
|
|
130
|
+
# Functions
|
|
131
|
+
# -----------------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def get_name(path: str) -> str:
|
|
135
|
+
"""
|
|
136
|
+
Get the name of a file from a given path.
|
|
137
|
+
"""
|
|
138
|
+
path = PurePosixPath(path)
|
|
139
|
+
return path.name
|
zensical/extensions/preview.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# Copyright (c) Zensical
|
|
1
|
+
# Copyright (c) 2025 Zensical and contributors
|
|
2
2
|
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
|
-
# Third-party contributions licensed under
|
|
4
|
+
# Third-party contributions licensed under DCO
|
|
5
5
|
|
|
6
6
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
# of this software and associated documentation files (the "Software"), to
|
zensical/extensions/search.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# Copyright (c) Zensical
|
|
1
|
+
# Copyright (c) 2025 Zensical and contributors
|
|
2
2
|
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
|
-
# Third-party contributions licensed under
|
|
4
|
+
# Third-party contributions licensed under DCO
|
|
5
5
|
|
|
6
6
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
# of this software and associated documentation files (the "Software"), to
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# Copyright (c) Zensical
|
|
1
|
+
# Copyright (c) 2025 Zensical and contributors
|
|
2
2
|
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
|
-
# Third-party contributions licensed under
|
|
4
|
+
# Third-party contributions licensed under DCO
|
|
5
5
|
|
|
6
6
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
# of this software and associated documentation files (the "Software"), to
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# Copyright (c) Zensical
|
|
1
|
+
# Copyright (c) 2025 Zensical and contributors
|
|
2
2
|
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
|
-
# Third-party contributions licensed under
|
|
4
|
+
# Third-party contributions licensed under DCO
|
|
5
5
|
|
|
6
6
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
# of this software and associated documentation files (the "Software"), to
|
zensical/main.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# Copyright (c) Zensical
|
|
1
|
+
# Copyright (c) 2025 Zensical and contributors
|
|
2
2
|
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
|
-
# Third-party contributions licensed under
|
|
4
|
+
# Third-party contributions licensed under DCO
|
|
5
5
|
|
|
6
6
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
# of this software and associated documentation files (the "Software"), to
|
|
@@ -26,11 +26,11 @@ from __future__ import annotations
|
|
|
26
26
|
import click
|
|
27
27
|
import os
|
|
28
28
|
import shutil
|
|
29
|
-
import webbrowser
|
|
30
29
|
|
|
31
30
|
from click import ClickException
|
|
32
31
|
from zensical import build, serve, version
|
|
33
32
|
|
|
33
|
+
|
|
34
34
|
# ----------------------------------------------------------------------------
|
|
35
35
|
# Commands
|
|
36
36
|
# ----------------------------------------------------------------------------
|
|
@@ -122,17 +122,12 @@ def execute_serve(config_file: str | None, **kwargs):
|
|
|
122
122
|
break
|
|
123
123
|
else:
|
|
124
124
|
raise ClickException("No config file found in the current folder.")
|
|
125
|
-
|
|
126
|
-
# Obtain development server address and open in browser, if desired
|
|
127
|
-
dev_addr = kwargs.get("dev_addr") or "localhost:8000"
|
|
128
|
-
if kwargs.get("open", False):
|
|
129
|
-
webbrowser.open(f"http://{dev_addr}")
|
|
130
125
|
if kwargs.get("strict", False):
|
|
131
126
|
print("Warning: Strict mode is currently unsupported.")
|
|
132
127
|
|
|
133
128
|
# Build project in Rust runtime, calling back into Python when necessary,
|
|
134
129
|
# e.g., to parse MkDocs configuration format or render Markdown
|
|
135
|
-
serve(os.path.abspath(config_file),
|
|
130
|
+
serve(os.path.abspath(config_file), kwargs)
|
|
136
131
|
|
|
137
132
|
|
|
138
133
|
@cli.command(name="new")
|
|
@@ -156,6 +151,7 @@ def new_project(directory: str | None, **kwargs):
|
|
|
156
151
|
directory = "."
|
|
157
152
|
docs_dir = os.path.join(directory, "docs")
|
|
158
153
|
config_file = os.path.join(directory, "zensical.toml")
|
|
154
|
+
github_dir = os.path.join(directory, ".github")
|
|
159
155
|
|
|
160
156
|
if os.path.exists(directory):
|
|
161
157
|
if not os.path.isdir(directory):
|
|
@@ -164,6 +160,8 @@ def new_project(directory: str | None, **kwargs):
|
|
|
164
160
|
raise (ClickException(f"{config_file} already exists."))
|
|
165
161
|
if os.path.exists(docs_dir):
|
|
166
162
|
raise (ClickException(f"{docs_dir} already exists."))
|
|
163
|
+
if os.path.exists(github_dir):
|
|
164
|
+
raise (ClickException(f"{github_dir} already exists."))
|
|
167
165
|
else:
|
|
168
166
|
os.makedirs(directory)
|
|
169
167
|
|
|
@@ -173,6 +171,10 @@ def new_project(directory: str | None, **kwargs):
|
|
|
173
171
|
os.path.join(package_dir, "bootstrap/docs"),
|
|
174
172
|
os.path.join(directory, "docs"),
|
|
175
173
|
)
|
|
174
|
+
shutil.copytree(
|
|
175
|
+
os.path.join(package_dir, "bootstrap/.github"),
|
|
176
|
+
os.path.join(directory, ".github"),
|
|
177
|
+
)
|
|
176
178
|
|
|
177
179
|
|
|
178
180
|
# ----------------------------------------------------------------------------
|
zensical/markdown.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# Copyright (c) Zensical
|
|
1
|
+
# Copyright (c) 2025 Zensical and contributors
|
|
2
2
|
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
|
-
# Third-party contributions licensed under
|
|
4
|
+
# Third-party contributions licensed under DCO
|
|
5
5
|
|
|
6
6
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
# of this software and associated documentation files (the "Software"), to
|
|
@@ -30,7 +30,7 @@ from datetime import date, datetime
|
|
|
30
30
|
from markdown import Markdown
|
|
31
31
|
from yaml import SafeLoader
|
|
32
32
|
|
|
33
|
-
from .config import
|
|
33
|
+
from .config import get_config
|
|
34
34
|
from .extensions.links import LinksExtension
|
|
35
35
|
from .extensions.search import SearchExtension
|
|
36
36
|
|
|
@@ -61,7 +61,7 @@ def render(content: str, path: str) -> dict:
|
|
|
61
61
|
in order to support the specific syntax of Python Markdown. We're working
|
|
62
62
|
on moving the entire rendering chain to Rust.
|
|
63
63
|
"""
|
|
64
|
-
config =
|
|
64
|
+
config = get_config()
|
|
65
65
|
|
|
66
66
|
# Initialize Markdown parser
|
|
67
67
|
md = Markdown(
|